status_t register_boot_file_system(BootVolume& bootVolume) { Directory* rootDirectory = bootVolume.RootDirectory(); gRoot->AddLink("boot", rootDirectory); Partition *partition; status_t status = gRoot->GetPartitionFor(rootDirectory, &partition); if (status != B_OK) { dprintf("register_boot_file_system(): could not locate boot volume in " "root!\n"); return status; } gBootVolume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET, partition->offset); if (bootVolume.IsPackaged()) { gBootVolume.SetBool(BOOT_VOLUME_PACKAGED, true); PackageVolumeState* state = bootVolume.GetPackageVolumeState(); if (state->Name() != NULL) gBootVolume.AddString(BOOT_VOLUME_PACKAGES_STATE, state->Name()); } Node *device = get_node_from(partition->FD()); if (device == NULL) { dprintf("register_boot_file_system(): could not get boot device!\n"); return B_ERROR; } return platform_register_boot_device(device); }
static int open_maybe_packaged(BootVolume& volume, const char* path, int openMode) { if (strncmp(path, kSystemDirectoryPrefix, strlen(kSystemDirectoryPrefix)) == 0) { path += strlen(kSystemDirectoryPrefix); return open_from(volume.SystemDirectory(), path, openMode); } return open_from(volume.RootDirectory(), path, openMode); }
bool is_bootable(Directory *volume) { if (volume->IsEmpty()) return false; BootVolume bootVolume; if (bootVolume.SetTo(volume) != B_OK) return false; // check for the existance of a kernel (for our platform) int fd = find_kernel(bootVolume); if (fd < 0) return false; close(fd); return true; }
status_t load_modules(stage2_args* args, BootVolume& volume) { int32 failed = 0; // ToDo: this should be mostly replaced by a hardware oriented detection mechanism int32 i = 0; for (; sAddonPaths[i]; i++) { char path[B_FILE_NAME_LENGTH]; snprintf(path, sizeof(path), "%s/boot", sAddonPaths[i]); if (load_modules_from(volume, path) != B_OK) failed++; } if (failed == i) { // couldn't load any boot modules // fall back to load all modules (currently needed by the boot floppy) const char *paths[] = { "bus_managers", "busses/ide", "busses/scsi", "generic", "partitioning_systems", "drivers/bin", NULL}; for (int32 i = 0; paths[i]; i++) { char path[B_FILE_NAME_LENGTH]; snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], paths[i]); load_modules_from(volume, path); } } // and now load all partitioning and file system modules // needed to identify the boot volume if (!gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) { // iterate over the mounted volumes and load their file system Partition *partition; if (gRoot->GetPartitionFor(volume.RootDirectory(), &partition) == B_OK) { while (partition != NULL) { load_module(volume, partition->ModuleName()); partition = partition->Parent(); } } } else { // The boot image should only contain the file system // needed to boot the system, so we just load it. // ToDo: this is separate from the fall back from above // as this piece will survive a more intelligent module // loading approach... char path[B_FILE_NAME_LENGTH]; snprintf(path, sizeof(path), "%s/%s", sAddonPaths[0], "file_systems"); load_modules_from(volume, path); } return B_OK; }
/*! Gets the boot device, scans all of its partitions, gets the boot partition, and mounts its file system. \param args The stage 2 arguments. \param _bootVolume On success set to the boot volume. \return \c B_OK on success, another error code otherwise. */ status_t get_boot_file_system(stage2_args* args, BootVolume& _bootVolume) { status_t error = platform_add_boot_device(args, &gBootDevices); if (error != B_OK) return error; NodeIterator iterator = gBootDevices.GetIterator(); while (iterator.HasNext()) { Node *device = iterator.Next(); error = add_partitions_for(device, false, true); if (error != B_OK) continue; NodeList bootPartitions; error = platform_get_boot_partitions(args, device, &gPartitions, &bootPartitions); if (error != B_OK) continue; NodeIterator partitionIterator = bootPartitions.GetIterator(); while (partitionIterator.HasNext()) { Partition *partition = (Partition*)partitionIterator.Next(); Directory *fileSystem; error = partition->Mount(&fileSystem, true); if (error != B_OK) { // this partition doesn't contain any known file system; we // don't need it anymore gPartitions.Remove(partition); delete partition; continue; } // init the BootVolume error = _bootVolume.SetTo(fileSystem); if (error != B_OK) continue; sBootDevice = device; return B_OK; } } return B_ERROR; }
/*! Gets the boot device, scans all of its partitions, gets the boot partition, and mounts its file system. \param args The stage 2 arguments. \param _bootVolume On success set to the boot volume. \return \c B_OK on success, another error code otherwise. */ status_t get_boot_file_system(stage2_args* args, BootVolume& _bootVolume) { Node *device; status_t error = platform_add_boot_device(args, &gBootDevices); if (error != B_OK) return error; // the boot device must be the first device in the list device = gBootDevices.First(); error = add_partitions_for(device, false, true); if (error != B_OK) return error; Partition *partition; error = platform_get_boot_partition(args, device, &gPartitions, &partition); if (error != B_OK) return error; Directory *fileSystem; error = partition->Mount(&fileSystem, true); if (error != B_OK) { // this partition doesn't contain any known file system; we // don't need it anymore gPartitions.Remove(partition); delete partition; return error; } // init the BootVolume error = _bootVolume.SetTo(fileSystem); if (error != B_OK) return error; sBootDevice = device; return B_OK; }
extern "C" int main(stage2_args *args) { TRACE(("boot(): enter\n")); if (heap_init(args) < B_OK) panic("Could not initialize heap!\n"); TRACE(("boot(): heap initialized...\n")); // set debug syslog default #if KDEBUG_ENABLE_DEBUG_SYSLOG gKernelArgs.keep_debug_output_buffer = true; #endif add_stage2_driver_settings(args); platform_init_video(); // the main platform dependent initialisation // has already taken place at this point. if (vfs_init(args) < B_OK) panic("Could not initialize VFS!\n"); dprintf("Welcome to the Haiku boot loader!\n"); bool mountedAllVolumes = false; BootVolume bootVolume; PathBlacklist pathBlacklist; if (get_boot_file_system(args, bootVolume) != B_OK || (platform_boot_options() & BOOT_OPTION_MENU) != 0) { if (!bootVolume.IsValid()) puts("\tno boot path found, scan for all partitions...\n"); if (mount_file_systems(args) < B_OK) { // That's unfortunate, but we still give the user the possibility // to insert a CD-ROM or just rescan the available devices puts("Could not locate any supported boot devices!\n"); } // ToDo: check if there is only one bootable volume! mountedAllVolumes = true; if (user_menu(bootVolume, pathBlacklist) < B_OK) { // user requested to quit the loader goto out; } } if (bootVolume.IsValid()) { // we got a volume to boot from! 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, pathBlacklist) != 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_blacklist(bootVolume.SystemDirectory(), pathBlacklist); } register_boot_file_system(bootVolume); if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) == 0) platform_switch_to_logo(); load_modules(args, bootVolume); load_driver_settings(args, bootVolume.RootDirectory()); // apply boot settings apply_boot_settings(); // set up kernel args version info gKernelArgs.kernel_args_size = sizeof(kernel_args); gKernelArgs.version = CURRENT_KERNEL_ARGS_VERSION; // clone the boot_volume KMessage into kernel accessible memory // note, that we need to 4 byte align the buffer and thus allocate // 3 more bytes void* buffer = kernel_args_malloc(gBootVolume.ContentSize() + 3); if (!buffer) { panic("Could not allocate memory for the boot volume kernel " "arguments"); } buffer = (void*)(((addr_t)buffer + 3) & ~(addr_t)0x3); memcpy(buffer, gBootVolume.Buffer(), gBootVolume.ContentSize()); gKernelArgs.boot_volume = buffer; gKernelArgs.boot_volume_size = gBootVolume.ContentSize(); // ToDo: cleanup, heap_release() etc. heap_print_statistics(); platform_start_kernel(); } } out: heap_release(args); return 0; }
status_t user_menu(BootVolume& _bootVolume, PathBlacklist& _pathBlacklist) { Menu* menu = new(std::nothrow) Menu(MAIN_MENU); sMainMenu = menu; sBootVolume = &_bootVolume; sPathBlacklist = &_pathBlacklist; Menu* safeModeMenu = NULL; Menu* debugMenu = NULL; MenuItem* item; TRACE(("user_menu: enter\n")); // Add boot volume menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume", add_boot_volume_menu())); // Add safe mode menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options", safeModeMenu = add_safe_mode_menu())); // add debug menu menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options", debugMenu = add_debug_menu())); // Add platform dependent menus platform_add_menus(menu); menu->AddSeparatorItem(); menu->AddItem(item = new(std::nothrow) MenuItem("Reboot")); item->SetTarget(user_menu_reboot); item->SetShortcut('r'); menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting")); if (!_bootVolume.IsValid()) { item->SetEnabled(false); menu->ItemAt(0)->Select(true); } else item->SetShortcut('b'); menu->Run(); apply_safe_mode_options(safeModeMenu); apply_safe_mode_options(debugMenu); apply_safe_mode_path_blacklist(); add_safe_mode_settings(sSafeModeOptionsBuffer.String()); delete menu; TRACE(("user_menu: leave\n")); sMainMenu = NULL; sBlacklistRootMenu = NULL; sBootVolume = NULL; sPathBlacklist = NULL; return B_OK; }