Repository navigation
Initrd and boot
- GRUB loads
exyde-kernel.elfvia Multiboot2 and jumps to_start. - The assembly entry sets up a bootstrap identity map and calls
kmain(magic, mb_info). -
kmain:- zeroes
.bss, initialises the arch layer, checks Multiboot2 magic - parses the memory map, reserves low memory and the kernel image
- initialises PMM, VMM, heap
- reserves
HEAP_BOOT_RESERVEin the heap - runs kernel self-tests (pmm, vmm, heap, handles)
- initialises the scheduler, registers the timer IRQ, enables interrupts
- runs sync, channel, ring-3 syscall, and userspace self-tests
- enters the idle
hltloop
- zeroes
The userspace self-test is where init is spawned. See below.
The initrd is a cpio newc archive (SVR4 without CRC, magic
070701). It contains:
bin/console
bin/echo
bin/exy-vfs
bin/test
The kernel does not parse it. It maps the archive read-only into
init's address space at INITRD_VA = USER_VA_BASE + 0x02000000
and passes the base and size to init through environment
variables:
EXYDE_INITRD_BASE=0x...
EXYDE_INITRD_SIZE=0x...
init reads these with getenv, validates the magic, and walks the
archive with initrd_find("bin/<name>", &data, &size). It then hands
the blob to SPAWN_ELF.
Each entry:
offset size field
0 6 "070701"
6 8 c_ino (hex ASCII, 8 chars)
14 8 c_mode
22 8 c_uid
30 8 c_gid
38 8 c_nlink
46 8 c_mtime
54 8 c_filesize
62 8 c_devmajor
70 8 c_devminor
78 8 c_rdevmajor
86 8 c_rdevminor
94 8 c_namesize
102 8 c_chksum
Total header: 110 bytes.
Layout of one entry in the stream:
[header 110 B][name (namesize bytes, NUL-terminated)][pad to 4]
[file body (filesize bytes)][pad to 4]
The padding applies to the cumulative offset, not the length.
Both data_off = align4(name_off + namesize) and
next_off = align4(data_off + filesize) use the cumulative form.
An earlier version of the parser used
name_off + align4(namesize), which was off by two bytes for the
first entry and made the kernel see an invalid ELF header. That bug
surfaced as -ENOMEM from SPAWN_ELF.
The archive ends with an entry whose name is TRAILER!!!.
tools/mkinitrd.sh:
- Copies
build/{test,echo,exy-vfs,console}.elfintobuild/initrd-stage/bin/<name>. - Runs
find . -type f -printf '%P\0' | LC_ALL=C sort -z | cpio --null -o -H newc > build/initrd. - Removes the staging directory.
The sort -z is what makes the archive reproducible for a given
set of inputs.
src/kernel/Makefile runs objcopy -I binary -O elf64-x86-64 on
both build/init.elf and build/initrd, producing object files
whose symbols are path-mangled:
build/init.elf -> _binary_build_init_elf_{start,end}
build/initrd -> _binary_build_initrd_{start,end}
include/exyde/init_elf.h and include/exyde/initrd.h expose
these as exyde_init_elf / exyde_init_elf_size and
exyde_initrd / exyde_initrd_size.
Inside init.c:
static exyde_handle_t spawn_from_initrd(const char *path,
const char *name,
exyde_handle_t cap) {
const void *data; size_t size;
if (initrd_find(path, &data, &size) != 0)
return EXYDE_HANDLE_INVALID;
return exyde_spawn_elf(data, size, name, cap);
}
All service spawns go through this helper. The kernel never looks at the archive.