CLOSE
Loader in Haiku Continued

Loader in Haiku Continued

Updated on 30 Jun, 2026 37 mins read 1,574 views

Introduction

In this chapter we complete the remaining code part of the loader.

if (bootVolume.IsValid()) {
		// we got a volume to boot from!

		// TODO: fix for riscv64
#ifndef __riscv
		load_driver_settings(args, bootVolume.RootDirectory());
#endif
		status_t status;
		while ((status = load_kernel(args, bootVolume)) < B_OK) {
			// loading the kernel failed, so let the user choose another
			// volume to boot from until it works
			bootVolume.Unset();

			if (!mountedAllVolumes) {
				// mount all other file systems, if not already happened
				if (mount_file_systems(args) < B_OK)
					panic("Could not locate any supported boot devices!\n");

				mountedAllVolumes = true;
			}

			if (user_menu(bootVolume, pathBlocklist) != B_OK
				|| !bootVolume.IsValid()) {
				// user requested to quit the loader
				goto out;
			}
		}

		// if everything is okay, continue booting; the kernel
		// is already loaded at this point and we definitely
		// know our boot volume, too
		if (status == B_OK) {
			if (bootVolume.IsPackaged()) {
				packagefs_apply_path_blocklist(bootVolume.SystemDirectory(),
					pathBlocklist);
			}

			register_boot_file_system(bootVolume);

			if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) == 0)
				platform_switch_to_logo();

			load_modules(args, bootVolume);

			gKernelArgs.ucode_data = NULL;
			gKernelArgs.ucode_data_size = 0;
			platform_load_ucode(bootVolume);

			// TODO: fix for riscv64
#ifndef __riscv
			// apply boot settings
			apply_boot_settings();
#endif

			// set up kernel args version info
			gKernelArgs.kernel_args_size = sizeof(kernel_args);
			gKernelArgs.version = CURRENT_KERNEL_ARGS_VERSION;
			if (gKernelArgs.ucode_data == NULL)
				gKernelArgs.kernel_args_size = kernel_args_size_v1;

			// clone the boot_volume KMessage into kernel accessible memory
			// note, that we need to 8-byte align the buffer and thus allocate
			// 7 more bytes
			void* buffer = kernel_args_malloc(gBootVolume.ContentSize() + 7);
			if (!buffer) {
				panic("Could not allocate memory for the boot volume kernel "
					"arguments");
			}

			buffer = (void*)(((addr_t)buffer + 7) & ~(addr_t)0x7);
			memcpy(buffer, gBootVolume.Buffer(), gBootVolume.ContentSize());
			gKernelArgs.boot_volume = buffer;
			gKernelArgs.boot_volume_size = gBootVolume.ContentSize();

			platform_cleanup_devices();
			// TODO: cleanup, heap_release() etc.
			heap_print_statistics();
			platform_start_kernel();
		}
	}

Boot Volume Validation

if (bootVolume.IsValid()) {
   // code inside this block executes if a valid boot volume is present
}
  • Checks if bootVolume is valid. If it is, the code proceeds to boot from it.

Load Driver Settings

#ifndef __riscv
   load_driver_settings(args, bootVolume.RootDirectory());
#endif
  • Loads driver settings from the root directory of the boot volume. This section is excluded for the RISC-V architecture.
  • This function is defined at: src/system/boot/loader/load_driver_settings.cpp.

load_driver_settings(…)

status_t
load_driver_settings(stage2_args* /*args*/, Directory* volume)
{
	int fd = open_from(volume, "home/config/settings/kernel/drivers", O_RDONLY);
	if (fd < B_OK)
		return fd;

	Directory* settings = (Directory*)get_node_from(fd);
	if (settings == NULL)
		return B_ENTRY_NOT_FOUND;

	void* cookie;
	if (settings->Open(&cookie, O_RDONLY) == B_OK) {
		char name[B_FILE_NAME_LENGTH];
		while (settings->GetNextEntry(cookie, name, sizeof(name)) == B_OK) {
			if (!strcmp(name, ".") || !strcmp(name, ".."))
				continue;

			status_t status = load_driver_settings_file(settings, name);
			if (status != B_OK)
				dprintf("Could not load \"%s\" error %" B_PRIx32 "\n", name, status);
		}

		settings->Close(cookie);
	}

	return B_OK;
}

Let's break down the code:

Function Signature

status_t load_driver_settings(stage2_args* /*args*/, Directory* volume)
  • Parameters:
    • args : A pointer to an object of type stage2_args. (Note: Not used in the function)
    • volume : A pointer to a Directory object representing the volume from which driver settings should be loaded.
  • Return Type: status_t - A type commonly used in Haiku OS for error handling. B_OK indicate success, while other values represent specific error codes.

Open the Driver Settings File:

int fd = open_from(volume, "home/config/settings/kernel/drivers", O_RDONLY);
if (fd < B_OK)
    return fd;
  • Attempts to open the driver settings file located at “home/config/settings/kernel/drivers” on the specified volume.
  • If unsuccessful, returns the error code.

Get the Settings Directory:

Directory* settings = (Directory*)get_node_from(fd);
if (settings == NULL)
    return B_ENTRY_NOT_FOUND;
  • Retrieves the Directory object corresponding to the opened file descriptor (fd).
  • If the directory is not found, returns B_ENTRY_NOT_FOUND.

Iterate Over Entries in the Settings Directory:

void* cookie;
if (settings->Open(&cookie, O_RDONLY) == B_OK) {
    char name[B_FILE_NAME_LENGTH];
    while (settings->GetNextEntry(cookie, name, sizeof(name)) == B_OK) {
        // ...
    }
    settings->Close(cookie);
}
  • Opens the setting directory for reading and initializes an iteration cookie.
  • Iterates over entries in the directory.

Load Driver Settings From Each File:

if (!strcmp(name, ".") || !strcmp(name, ".."))
    continue;

status_t status = load_driver_settings_file(settings, name);
if (status != B_OK)
    dprintf("Could not load \"%s\" error %" B_PRIx32 "\n", name, status);
  • Skips entries with the name “.” and “..”.
  • Calls load_driver_settings_file function to load settings from each file.
  • Prints an error message if the loading fails.

Close the Settings Directory and Return Success:

settings->Close(cookie);
return B_OK;

Load Kernel in Loop

status_t status;
while ((status = load_kernel(args, bootVolume)) < B_OK) {
   // loading the kernel failed
   // let the user choose another volume to boot from until it works
   bootVolume.Unset();

   // code to mount file systems if not already happened
   // and prompt the user to choose another volume
   // ...
}
  • Uses a loop to attempt loading the kernel from the boot volume.
  • If loading fails, the loop allows the user to choose another volume until successful.
  • load_kernel(args, bootVolume) function is defined at location: src/system/boot/loader/loader.cpp.
status_t
load_kernel(stage2_args* args, BootVolume& volume)
{
	const char *name;
	int fd = find_kernel(volume, &name);
	if (fd < B_OK)
		return fd;

	dprintf("load kernel %s...\n", name);

	elf_init();
	preloaded_image *image;
	status_t status = elf_load_image(fd, &image);

	close(fd);

	if (status < B_OK) {
		dprintf("loading kernel failed: %" B_PRIx32 "!\n", status);
		return status;
	}

	gKernelArgs.kernel_image = image;

	status = elf_relocate_image(gKernelArgs.kernel_image);
	if (status < B_OK) {
		dprintf("relocating kernel failed: %" B_PRIx32 "!\n", status);
		return status;
	}

	gKernelArgs.kernel_image->name = kernel_args_strdup(name);

	return B_OK;
}

Find Kernel and Open File Descriptor:

const char *name;
int fd = find_kernel(volume, &name);
if (fd < B_OK)
    return fd;
  • Calls find_kernel function to locate the kernel on the specified BootVolume.
  • If unsuccessful (returns an error code), the function returns the error.

Print Debug Message:

dprintf("load kernel %s...\n", name);

// Ouput = load kernel kernel_x86_64...
  • Prints a debug message indicating that the kernel is being loaded.

Initialize ELF and Load Kernel Image:

elf_init();
preloaded_image *image;
status_t status = elf_load_image(fd, &image);
close(fd);
  • Initializes ELF (Executable and Linkable Format) processing.
  • Calls elf_load_image to load the kernel image from the file descriptor (fd).
  • Closes the file descriptor after loading.

Handle Loading Errors:

if (status < B_OK) {
    dprintf("loading kernel failed: %" B_PRIx32 "!\n", status);
    return status;
}
  • Prints an error message and returns the error code if loading the kernel image fails.

Set Kernel Image in Global Structure:

gKernelArgs.kernel_image = image;
  • Sets the kernel image in the global gKernelArgs structure.

Relocate Kernel Image:

status = elf_relocate_image(gKernelArgs.kernel_image);
if (status < B_OK) {
    dprintf("relocating kernel failed: %" B_PRIx32 "!\n", status);
    return status;
}
  • Calls elf_relocate_image to relocate the loaded kernel image.
  • Prints an error message and returns the error code if relocation fails.

Set Kernel Image Name in Global Structure:

gKernelArgs.kernel_image->name = kernel_args_strdup(name);
  • Allocates memory for and sets the name of the kernel image in the global gKernelArgs structure.

Return Success:

return B_OK;
  • Returns B_OK to indicate successful loading and relocation of the kernel image.

Continue Booting

if (status == B_OK) {
   // code to continue booting after successful kernel loading
   // ...
}
  • If kernel loading is successful, it proceeds to continue the boot process.

Handle Packaged Boot Volume

if (bootVolume.IsPackaged()) {
   packagefs_apply_path_blocklist(bootVolume.SystemDirectory(), pathBlocklist);
}
  • If the boot volume is packaged, it applied a path blocklist to the system directory.

Register Boot File System

register_boot_file_system(bootVolume);
  • Registers the boot file system.

Switch to Logo or Debug Output

if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) == 0)
   platform_switch_to_logo();
  • Switches to logo display unless debug output is requested.

Load Modules

load_modules(args, bootVolume);
  • Load modules needed for the operating system.

Load Microcode

gKernelArgs.ucode_data = NULL;
gKernelArgs.ucode_data_size = 0;
platform_load_ucode(bootVolume);
  • Prepares to load microcode. Sets microcode data and size to zero and calls a platform-specific function to load microcode from the boot volume.

Apply Boot Settings

#ifndef __riscv
   apply_boot_settings();
#endif
  • Applies boot settings. This section is excluded for the RISC-V architecture.

Set Up Kernel Args Version Info

#ifndef __riscv
   apply_boot_settings();
#endif
  • Sets up version information for kernel arguments.

Clone Boot Volume KMessage into Kernel Accessible Memory

void* buffer = kernel_args_malloc(gBootVolume.ContentSize() + 7);
// ...
memcpy(buffer, gBootVolume.Buffer(), gBootVolume.ContentSize());
gKernelArgs.boot_volume = buffer;
gKernelArgs.boot_volume_size = gBootVolume.ContentSize();
  • Allocates memory for boot volume kernel arguments, aligns the buffer, and clones the boot volume information into this memory.

Cleanup Devices and Start Kernel

platform_cleanup_devices();
// TODO: cleanup, heap_release() etc.
heap_print_statistics();
platform_start_kernel();
  • Cleans up devices, prints heap statistics, and starts the kernel.

 

 

Buy Me A Coffee

Leave a comment

Your experience on this site will be improved by allowing cookies Cookie Policy