I'm not aware of any disk operating system that works with a TRS-80 that has Level I BASIC ROMs. It's certainly possible to roll your own, though, if you're ambitious. I happen to have just been thinking recently about what that would take. There are a few notable obstacles:
(1) The Level I BASIC ROM contains no code to load a boot sector off a floppy drive. You could nevertheless get a reasonably convenient boot procedure that consisted of turning on the power and typing the four-key sequence "CL.[ENTER]". For this to work, you'd have to have a cassette player hooked up with a tape in it that had a short boot loader. The loader would only take a few seconds of tape, so you could fill both sides of a tape with copies of the loader and only need to deal with flipping the tape over once every hundred boots or so. (The TRS-80 hardware already deals with starting and stopping the tape player automatically for you.)
(2) (This one's a non-issue if you're only trying to run CP/M.) A major feature of most TRS-80 DOSes is a "Disk BASIC" implementation that's really a set of extensions to the Level II BASIC in ROM. You would have to modify the Disk BASIC to load a copy of Level II BASIC off of a disk and into RAM. That would leave you with 12k less available RAM. Most disk applications only required 32k RAM systems, though, so if you filled your RAM slots to get a 48k system, you could still run most Disk BASIC applications.
(3) On a Level II BASIC disk system, the Z-80 interrupt mode 1 is used, in which an interrupt input (on the INT line) causes a call to a fixed address (38h), which the TRS-80 maps to ROM. The Level II BASIC ROMs have an instruction at this address that jumps to a RAM address, where a DOS can place an interrupt handler. Level I BASIC doesn't do this. You could deal with this by instead using the Z-80 interrupt mode 2. Mode 2 is designed for use with devices that send a byte to the CPU identifying themselves. The TRS-80 hardware doesn't do this, but you can use mode 2 just as a way of bypassing ROM and having interrupts vectored through a table in RAM pointed to by the I register. (I think you can count on the interrupt byte being read by the CPU as all ones, and therefore you don't need to dedicate a whole page of memory to the interrupt table -- just the last byte of some page and the first byte of the next page.)
(You could also just leave interrupts disabled: the disk drive, keyboard, and serial input device drivers normally just use polling, anyway. You only need the interrupt if you want to: (a) keep track of the time of day; (b) have an interrupt-driven keyboard poller that can handle type-ahead; or (c) have an interrupt-driven RS-232 serial port input routine. Such serial input routines don't work by getting interrupts from the RS-232 interface when a byte comes in (because the RS-232 interface has no such feature), but instead work by polling the RS-232 input once every 1/40th of a second when the clock interrupt comes in. This just barely suffices for doing 300-baud input in the background. For faster baud rates, you need to use a non-interrupt-driven program that constantly polls.)
That's all I've thought of so far. Let us know if you get something working!