Basically: Both. The programmer can decide whether they want to re-load the CCP after the program has finished or preserve it and simply return. Depends on your memory requirements:
You can perform a warm start by coding a
rst 0 or
jmp 0
This re-initializes the system and re-loads the transient part of the CCP (which obviously must be present on the logged-in disk). If you do that, you can consider the memory occupied by that transient part "yours" and use it for your program.
If you left the original stack intact that was present when your program was first executed, you can restore that stack pointer and simply return to the CCP
; end of program, return to CCP
LHLD OLDSP ; restore initial stack pointer
; (must have been saved before)
SPHL
RET ; to the CCP
This saves the end user waiting for a warm start (which normally really isn't long). It does, however, limit your program's available memory (not really significantly, but on CP/M, every byte might count), because you need to preserve the CCP's environment (code and stack) while your program is running.
Smaller utilities typically used the latter way, while bigger, memory-hungry programs used the first approach.
To find the lowest address occupied by the CCP, you would typically consult BDOS function 6 (if you want to preserve it) - I would assume most small utilities didn't even do that, but rather used trial and error to find out whether the CCP is still "alive" after they're done - or use the system's base address (at $0006) if you intend to use CCP memory for your program.