-
Notifications
You must be signed in to change notification settings - Fork 0
Boot Sequence
JNode boots from power-on through BIOS POST, an El Torito CD-ROM, GRUB Multiboot, x86 assembly setup, and into Java's
Main.vmMain(), which initializes the VM and starts system plugins.
The boot sequence has six major phases:
- BIOS POST and CD-ROM Boot — Power-on, BIOS enumerates hardware, loads GRUB via El Torito
- GRUB Multiboot Load — GRUB decompresses and loads the kernel image into memory
-
Protected Mode Assembly —
kernel.asmsets up CPU, paging, interrupts, FPU -
VM Entry — Transition to userspace and jump to
Main.vmMain() -
VM Initialization —
VmSystem.initialize()brings up the Java runtime - Plugin Startup — Plugin manager loads and starts system plugins
- CPU powers on in real mode (16-bit), jumps to reset vector (
0xFFFFFFF0) - BIOS runs Power-On Self-Test (POST): detects RAM, initializes chipset, enumerates PCI buses
- BIOS scans boot devices in configured order (hard disk, CD-ROM, floppy, network)
- For CD-ROM boot, BIOS loads the El Torito boot catalog from the ISO 9660 filesystem
The ISO image (all/build/cdroms/jnode-*.iso) is built by the Boot-CD-ROM-Builder with this layout:
| Component | Location | Purpose |
|---|---|---|
| El Torito boot image | boot/grub/eltorito.s2 |
GRUB stage 2 in no-emulation mode |
| GRUB stage files | boot/grub/ |
Stage 1.5, stage 2, FAT support |
| Boot menu | boot/grub/menu.lst |
GRUB configuration |
| Compressed kernel |
/jnode32.gz or /jnode64.gz
|
The Multiboot kernel image |
| Init JAR |
/default.jgz, /full.jgz, etc. |
Plugin JARs as GRUB modules |
The BIOS reads eltorito.s2 from the CD-ROM into memory and transfers control to it. This is GRUB stage 2 (the core image), which:
- Loads its configuration from
boot/grub/menu.lst - Displays the boot menu (default entry: "JNode (default)")
- Processes the selected boot entry
From all/conf/x86/menu-cdrom.lst:
title JNode (default)
serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
kernel /jnode32.gz mp=no kdb lkd
module /default.jgz
GRUB uses the Multiboot protocol to load the kernel:
-
Locates
/jnode32.gzon the ISO filesystem - Decompresses the gzip image — GRUB has built-in gzip decompression for Multiboot kernels
- Parses the ELF headers of the decompressed binary to determine load segments
- Loads the ELF segments into physical memory at the addresses specified in the ELF program headers
-
Resolves the Multiboot header embedded in
kernel.asmto find the entry point (real_start) -
Passes the init JAR (
/default.jgz) as a Multiboot module — the second line in menu.lst -
Jumps to the entry point with:
-
EAX = 0x2BADB002(Multiboot magic number) -
EBX = pointerto the Multiboot info structure (memory map, module addresses, etc.)
-
NOTE: The gzip-compressed image (jnode32.gz) is created during the build when BootImageBuilder writes bootimage.bin and the Ant gzip task compresses it (all/build-x86.xml:225).
In core/src/native/x86/kernel.asm:20-34:
mb_header:
dd 0x1BADB002 ; Magic
dd 0x00010007 ; Feature flags
dd 0-0x1BADB002-0x00010007 ; Checksum
dd mb_header ; header_addr
dd sys_start ; load_addr
dd 0 ; load_end_addr (patched by BootImageBuilder)
dd 0 ; bss_end_addr (patched by BootImageBuilder)
dd real_start ; entry_addrAll assembly files are assembled as a single unit via the master include file core/src/native/x86/jnode.asm, which includes kernel.asm, cpu.asm, mm32.asm, ints.asm, console.asm, vm.asm, and all other .asm files. See Assembly-Files for the complete breakdown.
core/src/native/x86/kernel.asm:17-18:
sys_start:
jmp real_startThe jmp at the entry point is necessary because the Multiboot header (aligned at offset 4) must appear immediately after the entry point.
core/src/native/x86/kernel.asm:36-206 performs the following steps in order:
-
Set up kernel stack —
mov esp, Lkernel_esp(line 37) -
Clear screen via the
CLEAR_SCREENmacro (line 39) -
Verify Multiboot magic — checks
EAX == 0x2BADB002(lines 41-43); jumps tono_multiboot_loaderand halts if invalid -
Copy Multiboot info block — copies the multiboot structure from
EBXtomultiboot_info(lines 46-51) - Parse command line — copies the kernel command-line string (lines 53-60)
- Parse memory map — copies the BIOS-provided memory map entries (lines 67-96)
- Copy VBE info — copies VESA BIOS Extensions data if available (lines 98-114)
-
Locate init JAR — reads the Multiboot module address and records
initJar_start/initJar_end(lines 117-138) - Check A20 gate — verifies the A20 address line is enabled (lines 143-149); loops forever if not (common BIOS bug)
-
Initialize kernel debugger — calls
kdb_init(line 152) - Print version string — displays the kernel version (line 155)
-
Detect CPU features — calls
test_cpuid(line 163) -
Setup memory manager (paging) — calls
Lsetup_mm(line 168), which is defined inmm32.asm(32-bit) ormm64.asm(64-bit). Sets up GDT, page tables, and enables paging -
Setup interrupts — calls
Lsetup_idt(line 176), building the Interrupt Descriptor Table -
Initialize FPU — calls
init_fpu(line 181) -
Initialize SSE — calls
init_sseif CPU supports SSE (line 183)
core/src/native/x86/kernel.asm:187-206:
; Go into userspace
mov ABX, Luser_esp
mov ACX, go_user_cs
mov ASI, USER_DS
mov ADI, USER_CS
push ASI ; old SS
push ABX ; old ESP
pushf ; old EFLAGS
push ADI ; old CS
push ACX ; old EIP
pushf
pop AAX
and eax, ~F_NT ; Clear nested task flag
push AAX
popf
iretThe iret switches from kernel-mode segment selectors to user-mode segments (USER_CS = 0x1B, USER_DS = 0x23) and jumps to go_user_cs.
core/src/native/x86/kernel.asm:212-257:
- Loads user-mode segment registers (
ds,es,gs,fs) - Sets up the kernel stack end for the boot processor
- Enables interrupts (
sti) - Sets up a minimal stack frame (
ABPis the frame pointer) - Loads the address of
vm_startintoEAX - Adds
BootImageBuilder_JUMP_MAIN_OFFSET32— this offset is the size of an object header, calculated so the jump target lands atMain.vmMain()inside the boot image - Calls via
AAX
The label vm_start at core/src/native/x86/jnode.asm:162 is a 4KB-aligned placeholder. During the build, BootImageBuilder (builder/src/builder/org/jnode/build/x86/BootImageBuilder.java) writes a reference into the boot image so that:
vm_start + JUMP_MAIN_OFFSET32 = Main.vmMain()
The offset (JUMP_MAIN_OFFSET32) equals the size of a Java object header (header slots + alignment), ensuring the first Java method in the image is reached.
core/src/core/org/jnode/boot/Main.java:
@LoadStatics
@Uninterruptible
public static int vmMain() {
Unsafe.debug("Starting JNode\n");
...
VmSystem.initialize();
...
}This is the first Java method ever executed on JNode. It is annotated with:
-
@LoadStatics— static fields must be initialized at runtime, not from build-time values -
@Uninterruptible— must not trigger GC safepoints during early boot
core/src/native/x86/vm.asm provides native runtime support for the VM. While not part of the early boot path per se, it contains the machinery that is called once Java code starts running:
| Routine | Role |
|---|---|
vm_athrow |
Exception throw handler — walks the stack, calls SoftByteCodes.findThrowableHandler
|
vm_print_string |
Print a java.lang.String to the console (used by Unsafe.debug()) |
vm_print_chararray |
Print a char[] to the VGA console |
vmint_print_stack |
Low-level stack trace printer for unhandled interrupts |
vm.asm is included into jnode.asm at line 139, alongside vm-invoke.asm (Java method invocation), vm-ints.asm (interrupt bridge), and vm-jumptable.asm (bytecode dispatch table). These files form the native half of the VM interpreter.
| File | core/src/core/org/jnode/vm/VmSystem.java |
|---|
Called from Main.vmMain(). Sets up:
-
Boot logging —
BootLogInstancefor early console output viaUnsafe.debug() -
System classloader —
VmSystemClassLoaderwith pre-loaded boot classes -
System properties —
java.home,os.name, etc. - Memory manager — Heap initialization, GC setup
-
Scheduler —
VmSchedulerstarts the idle thread and timer -
I/O system — Console streams (
System.out,System.err) -
Log4j — Sets up logging via
ConsoleAppender
After VmSystem.initialize():
-
Load initjar —
InitJarProcessorextracts plugin JARs and descriptors from the init-jar (the Multiboot module passed by GRUB) -
Create PluginManager —
DefaultPluginManagermanages the plugin lifecycle -
Start system plugins —
piMgr.startSystemPlugins(descriptors)activates all plugins in dependency order -
Launch main class — Reads
Main-Classfrom the plugin list manifest (typicallyorg.jnode.shell.CommandShell) and invokes itsmain()method
Plugins are started in dependency order. The default plugin list (all/conf/default-plugin-list.xml) specifies:
- Core classpath extensions (
org.classpath.ext.*) - Driver framework (
org.jnode.driver) - Block devices and partitions
- Filesystem service
- Console and input drivers
- Shell and commands
- Network stack (if included)
- GUI (if included)
| Annotation | Effect |
|---|---|
@Uninterruptible |
Method must not be interrupted (no GC safepoints) |
@LoadStatics |
Static fields are initialized at boot time, not build time |
@SharedStatics |
Static fields are shared across all isolates |
@KernelSpace |
Method runs in kernel context |
Main.vmMain() is annotated with both @LoadStatics and @Uninterruptible because it runs before the scheduler and GC are fully operational.
-
No
start32.asm— Earlier JNode versions may have had a separatestart32.asmfor the protected mode transition, but the current codebase does everything inkernel.asm. The protected mode entry point isreal_startincore/src/native/x86/kernel.asm:36. -
Build time vs. boot time —
BootImageBuilderpre-compiles classes and serializes static fields into the image.@LoadStaticsmarks fields that should be re-initialized at actual boot rather than using build-time values. -
pluginRegistry—Main.pluginRegistryis set byBootImageBuilder.initMain()during image creation, not at runtime. -
No exceptions before VmSystem.initialize() — The exception handling machinery isn't ready until the VM is initialized. Early boot code uses
Unsafe.debug()for output. -
The initjar — This is a nested archive inside the boot image containing all plugin JARs. It's created by the build process and accessed via
VmSystem.getInitJar(). -
Kernel image compression — The kernel is gzip-compressed as
jnode32.gz(orjnode64.gz). GRUB decompresses it before loading. There is no custom decompression routine in JNode's assembly code. -
A20 gate loop — If A20 is not enabled (lines 143-149 of
kernel.asm), the kernel loops forever. This is a known BIOS issue on some hardware.
- Architecture — Where boot fits in the system layers
-
Assembly-Files — Detailed role of each
.asmfile - Kernel-Entry-Point — Deep dive into the assembly-to-Java transition
- Boot-CD-ROM-Builder — How the ISO and El Torito boot image are built
- Boot-Log — The boot logging subsystem
- Plugin-System — How plugins are loaded and started
- Build---BootImageBuilder-Internals — How the boot image is created
- Build-System — How the build produces the final ISO
- VM-Magic — Boot-critical annotations
- NoCloseInputStream — InputStream decorator for shared streams