SchOS/kernel/dev/ramdisk.c
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

92 lines
2.8 KiB
C

// 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 "core/types.h"
#include "core/defs.h"
#include "core/param.h"
#include "arch/memlayout.h"
#include "arch/mmu.h"
#include "core/spinlock.h"
#include "core/sleeplock.h"
#include "fs/fs.h"
#include "fs/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;
}