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 typestage2_args
. (Note: Not used in the function)volume
: A pointer to aDirectory
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.