// Memory-backed block device. // // Serves an entire SCHFS image out of RAM, so the system can boot and run a // shell with the physical disk untouched. That is what makes destructive work // on the real disk survivable: reformatting, running mkfs, or bringing up a new // on-disk format cannot take away the root you are running from. It is also the // first piece of the installer -- an install environment is exactly "a root in // RAM plus a disk you are allowed to destroy". // // The image is linked into the kernel as a binary blob (see the ramdisk_obj // rule in the Makefile) and is served in place: the blob lives in writable // kernel memory, so writes to the ramdisk simply modify it. They are lost on // reboot, which is the expected behaviour for this kind of root. // // Enabled by building with RAMROOT=1. Without it this file still compiles, the // device reports zero size, and the kernel boots from the IDE disk as before. #include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "spinlock.h" #include "sleeplock.h" #include "fs.h" #include "buf.h" #ifdef RAMDISK_ROOT // Supplied by `ld -r -b binary` over build/fs-ram.img, renamed by objcopy. extern uchar _binary_ramdisk_start[]; extern uchar _binary_ramdisk_size[]; #endif static uchar *rd_base; static uint64 rd_size; // bytes static uint rd_blocks; // rd_size / BSIZE void ramdiskinit(void) { #ifdef RAMDISK_ROOT rd_base = _binary_ramdisk_start; rd_size = (uint64)_binary_ramdisk_size; rd_blocks = (uint)(rd_size / BSIZE); if(rd_blocks == 0) panic("ramdiskinit: embedded image is empty"); // A truncated image is worth catching here rather than as a confusing // "invalid file system" panic later: the superblock lives in block 1. if(rd_blocks < 2) panic("ramdiskinit: embedded image smaller than a superblock"); cprintf("ramdisk: %u blocks (%u KB) at %p\n", rd_blocks, (uint)(rd_size / 1024), rd_base); #else rd_base = 0; rd_size = 0; rd_blocks = 0; #endif } int ramdisk_present(void) { return rd_blocks != 0; } // Sync buf with the in-memory image. Mirrors iderw()'s contract: on a read, // fill b->data and set B_VALID; on a write, copy out and clear B_DIRTY. void ramdiskrw(struct buf *b) { if(!holdingsleep(&b->lock)) panic("ramdiskrw: buf not locked"); if((b->flags & (B_VALID|B_DIRTY)) == B_VALID) panic("ramdiskrw: nothing to do"); if(rd_base == 0) panic("ramdiskrw: no ramdisk"); if(b->blockno >= rd_blocks) panic("ramdiskrw: block out of range"); uchar *p = rd_base + (uint64)b->blockno * BSIZE; if(b->flags & B_DIRTY){ memmove(p, b->data, BSIZE); b->flags &= ~B_DIRTY; } else { memmove(b->data, p, BSIZE); } b->flags |= B_VALID; }