diff --git a/headers/private/kernel/boot/disk_identifier.h b/headers/private/kernel/boot/disk_identifier.h index 7c8a5651e0..52f2cebf37 100644 --- a/headers/private/kernel/boot/disk_identifier.h +++ b/headers/private/kernel/boot/disk_identifier.h @@ -23,7 +23,6 @@ enum device_types { USB_DEVICE, FIREWIRE_DEVICE, FIBRE_DEVICE, - NETWORK_DEVICE, }; #define NUM_DISK_CHECK_SUMS 5 @@ -62,11 +61,6 @@ typedef struct disk_identifier { struct { uint64 wwd; } fibre; - struct { - uint32 client_ip; - uint32 server_ip; - uint16 server_port; - } network; struct { off_t size; struct { diff --git a/headers/private/kernel/boot/elf.h b/headers/private/kernel/boot/elf.h index 0165b87ac4..d825d440a7 100644 --- a/headers/private/kernel/boot/elf.h +++ b/headers/private/kernel/boot/elf.h @@ -35,6 +35,8 @@ struct preloaded_image { ino_t inode; image_id id; // the ID field will be filled out in the kernel + bool is_module; + // set by the module initialization code }; #ifdef __cplusplus diff --git a/headers/private/kernel/boot/kernel_args.h b/headers/private/kernel/boot/kernel_args.h index bd52047d5f..0b3a67eab9 100644 --- a/headers/private/kernel/boot/kernel_args.h +++ b/headers/private/kernel/boot/kernel_args.h @@ -18,9 +18,26 @@ #include #include +#include #define CURRENT_KERNEL_ARGS_VERSION 1 -#define MAX_KERNEL_ARGS_RANGE 16 +#define MAX_KERNEL_ARGS_RANGE 32 + +// names of common boot_volume fields +#define BOOT_METHOD "boot method" +#define BOOT_VOLUME_USER_SELECTED "user selected" +#define BOOT_VOLUME_BOOTED_FROM_IMAGE "booted from image" +#define BOOT_VOLUME_PARTITION_OFFSET "partition offset" +#define BOOT_VOLUME_DISK_IDENTIFIER "disk identifier" + +// boot methods +enum { + BOOT_METHOD_HARD_DISK = 0, + BOOT_METHOD_CD = 1, + BOOT_METHOD_NET = 2, + + BOOT_METHOD_DEFAULT = BOOT_METHOD_HARD_DISK +}; typedef struct kernel_args { uint32 kernel_args_size; @@ -41,14 +58,7 @@ typedef struct kernel_args { uint32 num_cpus; addr_range cpu_kstack[MAX_BOOT_CPUS]; - struct { - disk_identifier identifier; - off_t partition_offset; - bool user_selected; - bool booted_from_image; - bool booted_from_network; - bool cd; - } boot_disk; + KMessage boot_volume; struct driver_settings_file *driver_settings; diff --git a/headers/private/kernel/boot/platform/bios_ia32/platform_kernel_args.h b/headers/private/kernel/boot/platform/bios_ia32/platform_kernel_args.h index f78efc56cd..3073dfb2d7 100644 --- a/headers/private/kernel/boot/platform/bios_ia32/platform_kernel_args.h +++ b/headers/private/kernel/boot/platform/bios_ia32/platform_kernel_args.h @@ -25,7 +25,6 @@ typedef struct { uint16 serial_base_ports[MAX_SERIAL_PORTS]; - uint16 boot_drive_number; bios_drive *drives; // this does not contain the boot drive apm_info apm; diff --git a/headers/private/kernel/boot/vfs.h b/headers/private/kernel/boot/vfs.h index ae0e835873..e6a6fb045e 100644 --- a/headers/private/kernel/boot/vfs.h +++ b/headers/private/kernel/boot/vfs.h @@ -73,6 +73,26 @@ class ConsoleNode : public Node { virtual ssize_t Write(const void *buffer, size_t bufferSize); }; + +class MemoryDisk : public Node { + public: + MemoryDisk(const uint8* data, size_t size, const char* name); + + virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, + size_t bufferSize); + virtual ssize_t WriteAt(void* cookie, off_t pos, const void* buffer, + size_t bufferSize); + + virtual off_t Size() const; + virtual status_t GetName(char *nameBuffer, size_t bufferSize) const; + + private: + const uint8* fData; + size_t fSize; + char fName[64]; +}; + + /* function prototypes */ extern status_t vfs_init(stage2_args *args); diff --git a/headers/private/kernel/fs/devfs.h b/headers/private/kernel/fs/devfs.h index f032774978..b7a4e96082 100644 --- a/headers/private/kernel/fs/devfs.h +++ b/headers/private/kernel/fs/devfs.h @@ -16,6 +16,8 @@ extern "C" { #endif +void devfs_add_preloaded_drivers(struct kernel_args* args); + status_t devfs_add_driver(const char *path); status_t devfs_unpublish_file_device(const char *path); diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index 99eccdd16e..8cbe3b673c 100644 --- a/src/system/boot/loader/Jamfile +++ b/src/system/boot/loader/Jamfile @@ -22,7 +22,9 @@ UsePrivateHeaders kernel shared storage ; BOOT_SUPPORT_FILE_SYSTEM_BFS BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS BOOT_SUPPORT_FILE_SYSTEM_TARFS - ; + + KMESSAGE_CONTAINER_ONLY + ; defines = [ FDefines $(defines) ] ; SubDirCcFlags $(defines) -Wall -Wno-multichar ; @@ -46,8 +48,9 @@ KernelStaticLibrary boot_loader : driver_settings.c # utils - list.c kernel_cpp.cpp + KMessage.cpp + list.c : -fno-pic ; @@ -69,6 +72,9 @@ KernelStaticLibrary boot_partitions : SEARCH on [ FGristFiles kernel_cpp.cpp list.c ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; +SEARCH on [ FGristFiles KMessage.cpp ] + = [ FDirName $(HAIKU_TOP) src system kernel messaging ] ; + SEARCH on [ FGristFiles driver_settings.c ] = [ FDirName $(HAIKU_TOP) src system libroot os ] ; diff --git a/src/system/boot/loader/loader.cpp b/src/system/boot/loader/loader.cpp index e8d28cbd3d..0bf41f65df 100644 --- a/src/system/boot/loader/loader.cpp +++ b/src/system/boot/loader/loader.cpp @@ -193,7 +193,8 @@ load_modules(stage2_args *args, Directory *volume) // and now load all partitioning and file system modules // needed to identify the boot volume - if (!gKernelArgs.boot_disk.booted_from_image) { + if (!gKernelArgs.boot_volume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, + false)) { // iterate over the mounted volumes and load their file system Partition *partition; if (gRoot->GetPartitionFor(volume, &partition) == B_OK) { diff --git a/src/system/boot/loader/main.cpp b/src/system/boot/loader/main.cpp index ad38dec9b7..76a5683b95 100644 --- a/src/system/boot/loader/main.cpp +++ b/src/system/boot/loader/main.cpp @@ -35,6 +35,9 @@ main(stage2_args *args) TRACE(("boot(): heap initialized...\n")); + // construct boot_volume KMessage explicitely + new(&gKernelArgs.boot_volume) KMessage; + platform_init_video(); // the main platform dependent initialisation @@ -107,6 +110,20 @@ main(stage2_args *args) 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 + KMessage& bootVolume = gKernelArgs.boot_volume; + void* buffer = kernel_args_malloc(bootVolume.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, bootVolume.Buffer(), bootVolume.ContentSize()); + bootVolume.SetTo(buffer, bootVolume.ContentSize()); + // ToDo: cleanup, heap_release() etc. platform_start_kernel(); } diff --git a/src/system/boot/loader/menu.cpp b/src/system/boot/loader/menu.cpp index 8ad3768175..0dcf765610 100644 --- a/src/system/boot/loader/menu.cpp +++ b/src/system/boot/loader/menu.cpp @@ -360,7 +360,7 @@ user_menu_boot_volume(Menu *menu, MenuItem *item) bootItem->Select(true); bootItem->SetData(item->Data()); - gKernelArgs.boot_disk.user_selected = true; + gKernelArgs.boot_volume.SetBool(BOOT_VOLUME_USER_SELECTED, true); return true; } @@ -416,7 +416,7 @@ add_boot_volume_menu(Directory *bootVolume) menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); item->SetType(MENU_ITEM_NO_CHOICE); - if (gKernelArgs.boot_disk.booted_from_image) + if (gKernelArgs.boot_volume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) menu->SetChoiceText("CD-ROM or hard drive"); return menu; diff --git a/src/system/boot/loader/partitions.cpp b/src/system/boot/loader/partitions.cpp index 5165b62582..465f21165f 100644 --- a/src/system/boot/loader/partitions.cpp +++ b/src/system/boot/loader/partitions.cpp @@ -210,8 +210,10 @@ Partition::_Mount(file_system_module_info *module, Directory **_fileSystem) status_t Partition::Mount(Directory **_fileSystem, bool isBootDevice) { - if (isBootDevice && gKernelArgs.boot_disk.booted_from_image) + if (isBootDevice && gKernelArgs.boot_volume.GetBool( + BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) { return _Mount(&gTarFileSystemModule, _fileSystem); + } for (int32 i = 0; i < sNumFileSystemModules; i++) { status_t status = _Mount(sFileSystemModules[i], _fileSystem); @@ -233,8 +235,10 @@ Partition::Scan(bool mountFileSystems, bool isBootDevice) // if we were not booted from the real boot device, we won't scan // the device we were booted from (which is likely to be a slow // floppy or CD) - if (isBootDevice && gKernelArgs.boot_disk.booted_from_image) + if (isBootDevice && gKernelArgs.boot_volume.GetBool( + BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) { return B_ENTRY_NOT_FOUND; + } const partition_module_info *bestModule = NULL; void *bestCookie = NULL; diff --git a/src/system/boot/loader/vfs.cpp b/src/system/boot/loader/vfs.cpp index 8b5145dd18..88e64df08f 100644 --- a/src/system/boot/loader/vfs.cpp +++ b/src/system/boot/loader/vfs.cpp @@ -198,6 +198,58 @@ Directory::Type() const // #pragma mark - +MemoryDisk::MemoryDisk(const uint8* data, size_t size, const char* name) + : Node(), + fData(data), + fSize(size) +{ + strlcpy(fName, name, sizeof(fName)); +} + + +ssize_t +MemoryDisk::ReadAt(void* cookie, off_t pos, void* buffer, size_t bufferSize) +{ + if (pos >= fSize) + return 0; + + if (pos + bufferSize > fSize) + bufferSize = fSize - pos; + + memcpy(buffer, fData + pos, bufferSize); + return bufferSize; +} + + +ssize_t +MemoryDisk::WriteAt(void* cookie, off_t pos, const void* buffer, + size_t bufferSize) +{ + return B_NOT_ALLOWED; +} + + +off_t +MemoryDisk::Size() const +{ + return fSize; +} + + +status_t +MemoryDisk::GetName(char *nameBuffer, size_t bufferSize) const +{ + if (!nameBuffer) + return B_BAD_VALUE; + + strlcpy(nameBuffer, fName, bufferSize); + return B_OK; +} + + +// #pragma mark - + + Descriptor::Descriptor(Node *node, void *cookie) : fNode(node), @@ -307,7 +359,8 @@ register_boot_file_system(Directory *volume) return status; } - gKernelArgs.boot_disk.partition_offset = partition->offset; + gKernelArgs.boot_volume.SetInt64(BOOT_VOLUME_PARTITION_OFFSET, + partition->offset); Node *device = get_node_from(partition->FD()); if (device == NULL) { diff --git a/src/system/boot/platform/bios_ia32/devices.cpp b/src/system/boot/platform/bios_ia32/devices.cpp index a9f9878f2a..174320854d 100644 --- a/src/system/boot/platform/bios_ia32/devices.cpp +++ b/src/system/boot/platform/bios_ia32/devices.cpp @@ -166,7 +166,7 @@ static bool sBlockDevicesAdded = false; static void check_cd_boot(BIOSDrive *drive) { - gKernelArgs.boot_disk.cd = false; + gKernelArgs.boot_volume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK); if (drive->DriveID() != 0) return; @@ -183,7 +183,8 @@ check_cd_boot(BIOSDrive *drive) // we obviously were booted from CD! specification_packet *packet = (specification_packet *)kDataSegmentScratch; - gKernelArgs.boot_disk.cd = packet->media_type != 0; + if (packet->media_type != 0) + gKernelArgs.boot_volume.SetInt32(BOOT_METHOD, BOOT_METHOD_CD); #if 0 dprintf("got CD boot spec:\n"); @@ -756,8 +757,8 @@ platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) } TRACE(("boot drive size: %Ld bytes\n", drive->Size())); - gKernelArgs.boot_disk.booted_from_image = gBootedFromImage; - gKernelArgs.boot_disk.booted_from_network = false; + gKernelArgs.boot_volume.SetInt32(BOOT_VOLUME_BOOTED_FROM_IMAGE, + gBootedFromImage); return B_OK; } @@ -803,8 +804,9 @@ platform_register_boot_device(Node *device) check_cd_boot(drive); - gKernelArgs.platform_args.boot_drive_number = drive->DriveID(); - gKernelArgs.boot_disk.identifier = drive->Identifier(); + gKernelArgs.boot_volume.SetInt64("boot drive number", drive->DriveID()); + gKernelArgs.boot_volume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, + &drive->Identifier(), sizeof(disk_identifier)); return B_OK; } diff --git a/src/system/boot/platform/pxe_ia32/Jamfile b/src/system/boot/platform/pxe_ia32/Jamfile index 5f484df721..e6e008e66f 100644 --- a/src/system/boot/platform/pxe_ia32/Jamfile +++ b/src/system/boot/platform/pxe_ia32/Jamfile @@ -48,7 +48,7 @@ KernelMergeObject boot_platform_pxe_ia32.o : pxe_undi.cpp $(bios_ia32_src) $(bios_ia32_edid_src) - + # generic text_menu.cpp : -fno-pic diff --git a/src/system/boot/platform/pxe_ia32/devices.cpp b/src/system/boot/platform/pxe_ia32/devices.cpp index 8961529bca..7b2f9ac4e3 100644 --- a/src/system/boot/platform/pxe_ia32/devices.cpp +++ b/src/system/boot/platform/pxe_ia32/devices.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -27,26 +28,90 @@ #endif +//extern unsigned char* gBuiltinBootArchive; +//extern long long gBuiltinBootArchiveSize; + +static TFTP sTFTP; + + status_t platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) { TRACE("platform_add_boot_device\n"); - status_t error = net_stack_init(); - if (error != B_OK) - return error; + // get the boot archive containing kernel and drivers via TFTP + status_t error = sTFTP.Init(); + if (error == B_OK) { + uint8* data; + size_t size; + // The root path in the DHCP packet from the server might contain the + // name of the archive. It would come first, then separated by semicolon + // the actual root path. + const char* fileName = "haiku-netboot.tgz"; // default + char stackFileName[1024]; + const char* rootPath = sTFTP.RootPath(); + if (rootPath) { + if (char* fileNameEnd = strchr(rootPath, ';')) { + size_t len = min_c(fileNameEnd - rootPath, + (int)sizeof(stackFileName) - 1); + memcpy(stackFileName, rootPath, len); + stackFileName[len] = '\0'; + fileName = stackFileName; + } + } - // init a remote disk, if possible - RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk(); - if (!remoteDisk) { - unsigned ip = NetStack::Default()->GetEthernetInterface()->IPAddress(); - panic("PXE boot: can't find remote disk on server %u.%u.%u.%u\n", - (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); - return B_ENTRY_NOT_FOUND; + // get the file + error = sTFTP.ReceiveFile(fileName, &data, &size); + if (error == B_OK) { + char name[64]; + ip_addr_t serverAddress = sTFTP.ServerIPAddress(); + snprintf(name, sizeof(name), "%lu.%lu.%lu.%lu:%s", + (serverAddress >> 24), (serverAddress >> 16) & 0xff, + (serverAddress >> 8) & 0xff, serverAddress & 0xff, fileName); + + MemoryDisk* disk = new(nothrow) MemoryDisk(data, size, name); + if (!disk) { + dprintf("platform_add_boot_device(): Out of memory!\n"); + platform_free_region(data, size); + return B_NO_MEMORY; + } + + devicesList->Add(disk); + return B_OK; + } else { + dprintf("platform_add_boot_device(): Failed to load file \"%s\" " + "via TFTP\n", fileName); + } } - devicesList->Add(remoteDisk); - return B_OK; + return B_ENTRY_NOT_FOUND; + +// // built-in boot archive? +// if (gBuiltinBootArchiveSize > 0) { +// MemoryDisk* disk = new(nothrow) MemoryDisk(gBuiltinBootArchive, +// gBuiltinBootArchiveSize); +// if (!disk) +// return B_NO_MEMORY; +// +// devicesList->Add(disk); +// return B_OK; +// } + +// error = net_stack_init(); +// if (error != B_OK) +// return error; +// +// // init a remote disk, if possible +// RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk(); +// if (!remoteDisk) { +// unsigned ip = NetStack::Default()->GetEthernetInterface()->IPAddress(); +// panic("PXE boot: can't find remote disk on server %u.%u.%u.%u\n", +// (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); +// return B_ENTRY_NOT_FOUND; +// } +// +// devicesList->Add(remoteDisk); +// return B_OK; } @@ -80,22 +145,39 @@ platform_register_boot_device(Node *device) { TRACE("platform_register_boot_device\n"); - gKernelArgs.platform_args.boot_drive_number = 0xffff; - gKernelArgs.platform_args.drives = NULL; + // get the root path -- chop off the file name of the archive we loaded + const char* rootPath = sTFTP.RootPath(); + if (rootPath) { + if (char* fileNameEnd = strchr(rootPath, ';')) + rootPath = fileNameEnd + 1; + } - RemoteDisk *rd = static_cast(device); - UNDI *undi = static_cast(NetStack::Default()->GetEthernetInterface()); + KMessage& bootVolume = gKernelArgs.boot_volume; + if (bootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_NET) != B_OK + || bootVolume.AddInt64("client MAC", + sTFTP.MACAddress().ToUInt64()) != B_OK + || bootVolume.AddInt32("client IP", sTFTP.IPAddress()) != B_OK + || bootVolume.AddInt32("server IP", sTFTP.ServerIPAddress()) != B_OK + || bootVolume.AddInt32("server port", sTFTP.ServerPort()) != B_OK + || (sTFTP.RootPath() + && bootVolume.AddString("net root path", rootPath) + != B_OK)) { + return B_NO_MEMORY; + } - gKernelArgs.boot_disk.identifier.bus_type = UNKNOWN_BUS; - gKernelArgs.boot_disk.identifier.device_type = NETWORK_DEVICE; - gKernelArgs.boot_disk.identifier.device.network.client_ip = undi->IPAddress(); - gKernelArgs.boot_disk.identifier.device.network.server_ip = rd->ServerIPAddress(); - gKernelArgs.boot_disk.identifier.device.network.server_port = rd->ServerPort(); - gKernelArgs.boot_disk.partition_offset = 0; - gKernelArgs.boot_disk.user_selected = false; - gKernelArgs.boot_disk.booted_from_image = false; - gKernelArgs.boot_disk.booted_from_network = true; - gKernelArgs.boot_disk.cd = false; +// RemoteDisk *rd = static_cast(device); +// UNDI *undi = static_cast(NetStack::Default()->GetEthernetInterface()); +// +// gKernelArgs.boot_disk.identifier.bus_type = UNKNOWN_BUS; +// gKernelArgs.boot_disk.identifier.device_type = NETWORK_DEVICE; +// gKernelArgs.boot_disk.identifier.device.network.client_ip = undi->IPAddress(); +// gKernelArgs.boot_disk.identifier.device.network.server_ip = rd->ServerIPAddress(); +// gKernelArgs.boot_disk.identifier.device.network.server_port = rd->ServerPort(); +// gKernelArgs.boot_disk.partition_offset = 0; +// gKernelArgs.boot_disk.user_selected = false; +// gKernelArgs.boot_disk.booted_from_image = false; +// gKernelArgs.boot_disk.booted_from_network = true; +// gKernelArgs.boot_disk.cd = false; return B_OK; -} +} diff --git a/src/system/boot/platform/pxe_ia32/network.cpp b/src/system/boot/platform/pxe_ia32/network.cpp index f522b94794..ffacab2611 100644 --- a/src/system/boot/platform/pxe_ia32/network.cpp +++ b/src/system/boot/platform/pxe_ia32/network.cpp @@ -11,6 +11,8 @@ #include +#include + #include "network.h" #include "pxe_undi.h" @@ -49,18 +51,110 @@ hex_dump(const void *_data, int length) #endif // !TRACE_NETWORK -UNDI::UNDI() - : fMACAddress() - , fRxFinished(true) - , fPxeData(NULL) -{ - TRACE("UNDI::UNDI\n"); +// #pragma mark - PXEService + +PXEService::PXEService() + : fPxeData(NULL), + fClientIP(0), + fServerIP(0), + fRootPath(NULL) +{ +} + + +PXEService::~PXEService() +{ + free(fRootPath); +} + + +status_t +PXEService::Init() +{ + // get !PXE struct fPxeData = pxe_undi_find_data(); - if (!fPxeData) + if (!fPxeData) { panic("can't find !PXE structure"); + return B_ERROR; + } dprintf("PXE API entrypoint at %04x:%04x\n", fPxeData->EntryPointSP.seg, fPxeData->EntryPointSP.ofs); + + // get cached info + PXENV_GET_CACHED_INFO cached_info; + cached_info.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY; + cached_info.BufferSize = 0; + cached_info.BufferLimit = 0; + cached_info.Buffer.seg = 0; + cached_info.Buffer.ofs = 0; + uint16 res = call_pxe_bios(fPxeData, GET_CACHED_INFO, &cached_info); + if (res != 0 || cached_info.Status != 0) { + char s[100]; + snprintf(s, sizeof(s), "Can't determine IP address! PXENV_GET_CACHED_INFO res %x, status %x\n", res, cached_info.Status); + panic(s); + return B_ERROR; + } + + char *buf = (char *)(cached_info.Buffer.seg * 16 + cached_info.Buffer.ofs); + fClientIP = ntohl(*(ip_addr_t *)(buf + 16)); + fServerIP = ntohl(*(ip_addr_t *)(buf + 20)); + fMACAddress = mac_addr_t((uint8*)(buf + 28)); + + uint8* options = (uint8*)buf + 236; + int optionsLen = int(cached_info.BufferSize) - 236; + + // check magic + if (optionsLen < 4 || options[0] != 0x63 || options[1] != 0x82 + || options[2] != 0x53 || options[3] != 0x63) { + return B_OK; + } + options += 4; + optionsLen -= 4; + + // parse DHCP options + while (optionsLen > 0) { + int option = *(options++); + optionsLen--; + + // check end or pad option + if (option == 0xff || optionsLen < 0) + break; + if (option == 0x00) + continue; + + // other options have a len field + int len = *(options++); + optionsLen--; + if (len > optionsLen) + break; + + // root path option + if (option == 17) { + dprintf("root path option: \"%.*s\"\n", len, (char*)options); + free(fRootPath); + fRootPath = (char*)malloc(len + 1); + if (!fRootPath) + return B_NO_MEMORY; + memcpy(fRootPath, options, len); + fRootPath[len] = '\0'; + } + + options += len; + optionsLen -= len; + } + + return B_OK; +} + + +// #pragma mark - UNDI + + +UNDI::UNDI() + : fRxFinished(true) +{ + TRACE("UNDI::UNDI\n"); } @@ -75,33 +169,20 @@ UNDI::Init() { TRACE("UNDI::Init\n"); - PXENV_GET_CACHED_INFO cached_info; PXENV_UNDI_GET_INFORMATION get_info; PXENV_UNDI_GET_STATE get_state; PXENV_UNDI_OPEN undi_open; uint16 res; - cached_info.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY; - cached_info.BufferSize = 0; - cached_info.BufferLimit = 0; - cached_info.Buffer.seg = 0; - cached_info.Buffer.ofs = 0; - res = call_pxe_bios(fPxeData, GET_CACHED_INFO, &cached_info); - if (res != 0 || cached_info.Status != 0) { - char s[100]; - snprintf(s, sizeof(s), "Can't determine IP address! PXENV_GET_CACHED_INFO res %x, status %x\n", res, cached_info.Status); - panic(s); - } + status_t error = PXEService::Init(); + if (error != B_OK) + return error; - char *buf = (char *)(cached_info.Buffer.seg * 16 + cached_info.Buffer.ofs); - ip_addr_t ipClient = ntohl(*(ip_addr_t *)(buf + 16)); - ip_addr_t ipServer = ntohl(*(ip_addr_t *)(buf + 20)); - dprintf("client-ip: %lu.%lu.%lu.%lu, server-ip: %lu.%lu.%lu.%lu\n", - (ipClient >> 24) & 0xff, (ipClient >> 16) & 0xff, (ipClient >> 8) & 0xff, ipClient & 0xff, - (ipServer >> 24) & 0xff, (ipServer >> 16) & 0xff, (ipServer >> 8) & 0xff, ipServer & 0xff); + (fClientIP >> 24) & 0xff, (fClientIP >> 16) & 0xff, (fClientIP >> 8) & 0xff, fClientIP & 0xff, + (fServerIP >> 24) & 0xff, (fServerIP >> 16) & 0xff, (fServerIP >> 8) & 0xff, fServerIP & 0xff); - SetIPAddress(ipClient); + SetIPAddress(fClientIP); undi_open.OpenFlag = 0; undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS; @@ -297,6 +378,159 @@ UNDI::Receive(void *buffer, size_t size) } +// #pragma mark - TFTP + +TFTP::TFTP() +{ +} + + +TFTP::~TFTP() +{ +} + + +status_t +TFTP::Init() +{ + status_t error = PXEService::Init(); + if (error != B_OK) + return error; + + + + return B_OK; +} + + +uint16 +TFTP::ServerPort() const +{ + return 69; +} + + +status_t +TFTP::ReceiveFile(const char* fileName, uint8** data, size_t* size) +{ + // get file size + pxenv_tftp_get_fsize getFileSize; + getFileSize.server_ip.num = htonl(fServerIP); + getFileSize.gateway_ip.num = 0; + strlcpy(getFileSize.file_name, fileName, sizeof(getFileSize.file_name)); + + uint16 res = call_pxe_bios(fPxeData, TFTP_GET_FILE_SIZE, &getFileSize); + if (res != 0 || getFileSize.status != 0) { + dprintf("TFTP_GET_FILE_SIZE failed, res %x, status %x\n", res, + getFileSize.status); + + return B_ERROR; + } + + uint32 fileSize = getFileSize.file_size; + dprintf("size of boot archive \"%s\": %lu\n", fileName, fileSize); + + // allocate memory for the data + uint8* fileData = NULL; + if (platform_allocate_region((void**)&fileData, fileSize, + B_READ_AREA | B_WRITE_AREA, false) != B_OK) { + TRACE(("TFTP: allocating memory for file data failed\n")); + return B_NO_MEMORY; + } + + // open TFTP connection + pxenv_tftp_open openConnection; + openConnection.server_ip.num = htonl(fServerIP); + openConnection.gateway_ip.num = 0; + strlcpy(openConnection.file_name, fileName, sizeof(getFileSize.file_name)); + openConnection.port = htons(ServerPort()); + openConnection.packet_size = 1456; + + res = call_pxe_bios(fPxeData, TFTP_OPEN, &openConnection); + if (res != 0 || openConnection.status != 0) { + dprintf("TFTP_OPEN failed, res %x, status %x\n", res, + openConnection.status); + + platform_free_region(fileData, fileSize); + return B_ERROR; + } + + uint16 packetSize = openConnection.packet_size; + dprintf("successfully opened TFTP connection, packet size %u\n", + packetSize); + + // check, if the file is too big for the TFTP protocol + if (fileSize > 0xffff * (uint32)packetSize) { + dprintf("TFTP: File is too big to be transferred via TFTP\n"); + _Close(); + platform_free_region(fileData, fileSize); + return B_ERROR; + } + + // transfer the file + status_t error = B_OK; + uint32 remainingBytes = fileSize; + uint8* buffer = fileData; + while (remainingBytes > 0) { + void* scratchBuffer = (void*)0x07C00; + pxenv_tftp_read readPacket; + readPacket.buffer.seg = SEG(scratchBuffer); + readPacket.buffer.ofs = OFS(scratchBuffer); + + res = call_pxe_bios(fPxeData, TFTP_READ, &readPacket); + if (res != 0 || readPacket.status != 0) { + dprintf("TFTP_READ failed, res %x, status %x\n", res, + readPacket.status); + error = B_ERROR; + break; + } + + uint32 bytesRead = readPacket.buffer_size; + if (bytesRead > remainingBytes) { + dprintf("TFTP: Read more bytes than should be remaining!"); + error = B_ERROR; + break; + } + + memcpy(buffer, scratchBuffer, bytesRead); + buffer += bytesRead; + remainingBytes -= bytesRead; + } + + // close TFTP connection + _Close(); + + if (error == B_OK) { + dprintf("TFTP: Successfully received file\n"); + *data = fileData; + *size = fileSize; + } else { + platform_free_region(fileData, fileSize); + } + + return error; +} + +status_t +TFTP::_Close() +{ + // close TFTP connection + pxenv_tftp_close closeConnection; + uint16 res = call_pxe_bios(fPxeData, TFTP_CLOSE, &closeConnection); + if (res != 0 || closeConnection.status != 0) { + dprintf("TFTP_CLOSE failed, res %x, status %x\n", res, + closeConnection.status); + return B_ERROR; + } + + return B_OK; +} + + + +// #pragma mark - + + status_t platform_net_stack_init() { diff --git a/src/system/boot/platform/pxe_ia32/network.h b/src/system/boot/platform/pxe_ia32/network.h index 15a34999f8..90db43f6fc 100644 --- a/src/system/boot/platform/pxe_ia32/network.h +++ b/src/system/boot/platform/pxe_ia32/network.h @@ -2,7 +2,7 @@ #define _PXE_NETWORK_H /* * Copyright 2006, Marcus Overhagen . + * Copyright 2005-2007, Ingo Weinhold . * Distributed under the terms of the MIT License. */ @@ -12,7 +12,30 @@ struct PXE_STRUCT; -class UNDI : public EthernetInterface + +class PXEService { +protected: + PXEService(); + ~PXEService(); + + status_t Init(); + +public: + mac_addr_t MACAddress() const { return fMACAddress; } + ip_addr_t IPAddress() const { return fClientIP; } + ip_addr_t ServerIPAddress() const { return fServerIP; } + const char* RootPath() const { return fRootPath; } + +protected: + PXE_STRUCT* fPxeData; + ip_addr_t fClientIP; + ip_addr_t fServerIP; + mac_addr_t fMACAddress; + char* fRootPath; +}; + + +class UNDI : public EthernetInterface, public PXEService { public: UNDI(); @@ -20,7 +43,6 @@ public: status_t Init(); - ip_addr_t ServerIPAddress() const; virtual mac_addr_t MACAddress() const; virtual void * AllocateSendReceiveBuffer(size_t size); @@ -30,9 +52,25 @@ public: virtual ssize_t Receive(void *buffer, size_t size); private: - mac_addr_t fMACAddress; bool fRxFinished; PXE_STRUCT * fPxeData; }; + +class TFTP : public PXEService { +public: + TFTP(); + ~TFTP(); + + status_t Init(); + + uint16 ServerPort() const; + + status_t ReceiveFile(const char* fileName, + uint8** data, size_t* size); + +private: + status_t _Close(); +}; + #endif diff --git a/src/system/boot/platform/pxe_ia32/pxe_stage2.S b/src/system/boot/platform/pxe_ia32/pxe_stage2.S index 87fc40644f..c4e95835fd 100644 --- a/src/system/boot/platform/pxe_ia32/pxe_stage2.S +++ b/src/system/boot/platform/pxe_ia32/pxe_stage2.S @@ -1,30 +1,198 @@ /* * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Copyright 2006, Marcus Overhagen, marcus@overhagen.de. All rights reserved. + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. * Distributed under the terms of the MIT License. */ -/** This file contains the stage 2 boot loader (haiku-pxe-loader) for the PXE - * (preboot execution environment) API - * It gets loaded by the PXE stage1 loader to 0x10000, and it starts execution - * in 32 bit protected mode. Entry point is at offset 0. - * This code will directly call the entry function of the embedded ELF part - * of the loader. - */ +// system state according to PXE specification: +// CS:IP 0000:7C00 +// ES:BX address of the PXENV+ structure +// SS:[SP+4] address of the !PXE structure. +// SS:SP at least 1.5KB of free stack + +// memory map: +// 0x00000 - 0x07bff real mode IDT, stack +// 0x07C00 - 0x08FFF original boot loader location +// 0x10000 - 0x8AFFF relocated boot loader +// 0x8B000 - 0x8CFFF used by stage2 trampoline code +// 0x8D000 - 0x9F7FF PXE and UNDI code and data segments +// 0x9F800 - 0xA0000 extended BIOS data area #define GLOBAL(x) .globl x ; x +.equ LOAD_ADDRESS, 0x10000 +.equ INITIAL_LOAD_ADDRESS, 0x07C00 +.equ INITIAL_LOAD_OFFSET, INITIAL_LOAD_ADDRESS - LOAD_ADDRESS + .text -.code32 -.org 0 +.code16 pxe_start: + // setup segments + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw $0x7c00, %sp + + cld + + // print start banner + mov $kStartMessage + INITIAL_LOAD_OFFSET, %esi + call puts + + // switch to unreal mode + cli + call enable_a20 + call go_unreal + sti + movl $kUnrealMessage + INITIAL_LOAD_OFFSET, %esi + call puts + + // relocate boot loader code to expected start address + movl $_end, %ecx // desired end address + addl $3, %ecx // long word align + andb $0xfc, %cl // (should be page aligned anyway, though) + movl %ecx, %edi + subl $LOAD_ADDRESS, %ecx // number of bytes to copy + movl $INITIAL_LOAD_ADDRESS, %esi + addl %ecx, %esi // current end address + shrl $2, %ecx // number of long words +_copy: subl $4, %esi + subl $4, %edi + movl (%esi), %eax + movl %eax, (%edi) + decl %ecx + jnz _copy + + // jump into the relocated boot loader +.code32 + .byte 0x66 + ljmp $0x1000, $relocated_start - LOAD_ADDRESS +.code16 + +relocated_start: + cli + + // switch to PM + .code32 + .byte 0x66 + .byte 0x67 + lgdt pm_gdt_descriptor + .code16 + + movl %cr0, %eax + orb $0x1, %al + movl %eax, %cr0 + +.code32 + .byte 0x66 + ljmp $0x8, $pm_start +pm_start: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + call _start + + +.code16 +stop: hlt + jmp stop + + + +puts: pushal +_puts2: lodsb + testb %al, %al + jnz putc + popal + ret +putc: movw $0x7, %bx + movb $0xe, %ah + int $0x10 + jmp _puts2 + + +enable_a20: inb $0x92, %al + testb $0x02, %al + jnz _a20_out + orb $0x02, %al + andb $0xfe, %al + outb %al, $0x92 +_a20_out: ret + + +go_unreal: pushw %ds + pushw %es + pushw %bx + .code32 + .byte 0x66 + .byte 0x67 + lgdt unreal_gdt_descriptor + INITIAL_LOAD_OFFSET + .code16 + movl %cr0, %eax + orb $1, %al + movl %eax, %cr0 + movw $8, %bx + movw %bx, %ds + movw %bx, %es + decb %al + movl %eax, %cr0 + popw %bx + popw %es + popw %ds + ret + + +kStartMessage: .asciz "\r\nHaiku PXE bootloader version 1.0\r\n\r\n" +kUnrealMessage: .asciz "Switch to unreal mode done\r\n" + + +.balign 8 +unreal_gdt: + .long 0 + .long 0 + .long 0x0000ffff + .long 0x00cf9200 + +unreal_gdt_descriptor: + .word 0x10 + .long unreal_gdt + INITIAL_LOAD_OFFSET + +.balign 8 +pm_gdt: + // null descriptor + .long 0 + .long 0 + + // kernel code segment + .long 0x0000ffff + .long 0x00cf9e00 + // kernel data and stack segment + .long 0x0000ffff + .long 0x00cf9200 + + // real mode 16 bit code segment + .long 0x0000ffff + .long 0x00009e01 + // real mode 16 bit data and stack segment + .long 0x0000ffff + .long 0x00009201 + // real mode 16 bit stack segment + .long 0x0000ffff + .long 0x00009200 + +pm_gdt_descriptor: + .word 0x2f + .long pm_gdt - mov $0x10000, %ebp // setup new stack - mov %ebp, %esp - call _start //-------------------------------------------------------------- diff --git a/src/system/boot/platform/pxe_ia32/pxe_undi.h b/src/system/boot/platform/pxe_ia32/pxe_undi.h index 665c9a24e6..3005e65091 100644 --- a/src/system/boot/platform/pxe_ia32/pxe_undi.h +++ b/src/system/boot/platform/pxe_ia32/pxe_undi.h @@ -14,6 +14,11 @@ extern "C" uint16 call_pxe_bios(void *pxe, uint16 opcode, void *param); #define UNDI_ISR 0x0014 #define UNDI_GET_STATE 0x0015 +#define TFTP_OPEN 0x0020 +#define TFTP_CLOSE 0x0021 +#define TFTP_READ 0x0022 +#define TFTP_GET_FILE_SIZE 0x0025 + #define GET_CACHED_INFO 0x0071 #define SEG(ptr) ((uint16)(((uint32)(ptr)) >> 4)) @@ -183,6 +188,41 @@ struct PXENV_UNDI_ISR #define PXENV_UNDI_ISR_OUT_RECEIVE 3 #define PXENV_UNDI_ISR_OUT_BUSY 4 + +typedef union { + uint32 num; + uint8 array[4]; +} pxenv_ip4; + +struct pxenv_tftp_open { + uint16 status; + pxenv_ip4 server_ip; + pxenv_ip4 gateway_ip; + char file_name[128]; + uint16 port; + uint16 packet_size; +} __attribute__((packed)); + +struct pxenv_tftp_close { + uint16 status; +} __attribute__((packed)); + +struct pxenv_tftp_read { + uint16 status; + uint16 packet_number; + uint16 buffer_size; + SEGOFF16 buffer; +} __attribute__((packed)); + +struct pxenv_tftp_get_fsize { + uint16 status; + pxenv_ip4 server_ip; + pxenv_ip4 gateway_ip; + char file_name[128]; + uint32 file_size; +} __attribute__((packed)); + + PXE_STRUCT * pxe_undi_find_data(); #endif diff --git a/src/system/kernel/fs/Jamfile b/src/system/kernel/fs/Jamfile index c2868f9885..e97ca2b1aa 100644 --- a/src/system/kernel/fs/Jamfile +++ b/src/system/kernel/fs/Jamfile @@ -12,6 +12,7 @@ KernelMergeObject kernel_fs.o : fd.c vfs.cpp vfs_boot.cpp + vfs_net_boot.cpp vfs_select.cpp node_monitor.cpp IOScheduler.cpp diff --git a/src/system/kernel/fs/devfs.cpp b/src/system/kernel/fs/devfs.cpp index 9235368730..15caf2a6ad 100644 --- a/src/system/kernel/fs/devfs.cpp +++ b/src/system/kernel/fs/devfs.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -181,9 +182,12 @@ load_driver(driver_entry *driver) status_t status; // load the module - image_id image = load_kernel_add_on(driver->path); - if (image < B_OK) - return image; + image_id image = driver->image; + if (image < 0) { + image = load_kernel_add_on(driver->path); + if (image < 0) + return image; + } // for prettier debug output const char *name = strrchr(driver->path, '/'); @@ -292,13 +296,71 @@ error2: } error1: - unload_kernel_add_on(image); - driver->image = status; + if (driver->image < 0) { + unload_kernel_add_on(image); + driver->image = status; + } return status; } +static status_t +add_driver(const char *path, image_id image) +{ + // see if we already know this driver + + struct stat stat; + if (image >= 0) { + // TODO: Unfortunately only the node ID is the key for the hash table. + // The image ID should be a small number and hopefully the boot FS + // doesn't use small negative values -- if it is inode based, we should + // be relatively safe. + stat.st_dev = -1; + stat.st_ino = -image; + } else { + if (::stat(path, &stat) != 0) + return errno; + } + + RecursiveLocker locker(&sDeviceFileSystem->lock); + + driver_entry *driver = (driver_entry *)hash_lookup( + sDeviceFileSystem->driver_hash, &stat.st_ino); + if (driver != NULL) { + // we know this driver + // ToDo: test for changes here? Although node monitoring should be enough. + if (driver->image < B_OK) + return driver->image; + + return B_OK; + } + + // we don't know this driver, create a new entry for it + + driver = (driver_entry *)malloc(sizeof(driver_entry)); + if (driver == NULL) + return B_NO_MEMORY; + + driver->path = strdup(path); + if (driver->path == NULL) { + free(driver); + return B_NO_MEMORY; + } + + driver->device = stat.st_dev; + driver->node = stat.st_ino; + driver->image = image; + driver->last_modified = stat.st_mtime; + + hash_insert(sDeviceFileSystem->driver_hash, driver); + + // Even if loading the driver fails - its entry will stay with us + // so that we don't have to go through it again + return load_driver(driver); +} + + /*! This is no longer part of the public kernel API, so we just export the symbol */ @@ -900,11 +962,13 @@ publish_device(struct devfs *fs, const char *path, device_node_info *deviceNode, // all went fine, let's initialize the node node->stream.type = S_IFCHR | 0644; - if (deviceNode != NULL) - node->stream.u.dev.info = info; - else - node->stream.u.dev.info = create_new_driver_info(ops, apiVersion); + if (deviceNode == NULL) { + info = create_new_driver_info(ops, apiVersion); + if (!info) + return B_NO_MEMORY; + } + node->stream.u.dev.info = info; node->stream.u.dev.node = deviceNode; node->stream.u.dev.driver = driver; node->stream.u.dev.ops = ops; @@ -1804,7 +1868,8 @@ devfs_can_page(fs_volume _fs, fs_vnode _vnode, fs_cookie cookie) || cookie == NULL) return false; - return vnode->stream.u.dev.info->read_pages != NULL; + return vnode->stream.u.dev.info->read_pages != NULL + || vnode->stream.u.dev.info->read != NULL; } @@ -1818,7 +1883,8 @@ devfs_read_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, //TRACE(("devfs_read_pages: vnode %p, vecs %p, count = %lu, pos = %Ld, size = %lu\n", vnode, vecs, count, pos, *_numBytes)); if (!S_ISCHR(vnode->stream.type) - || vnode->stream.u.dev.info->read_pages == NULL + || (vnode->stream.u.dev.info->read_pages == NULL + && vnode->stream.u.dev.info->read == NULL) || cookie == NULL) return B_NOT_ALLOWED; @@ -1827,8 +1893,37 @@ devfs_read_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos, *_numBytes); } - return vnode->stream.u.dev.info->read_pages(cookie->device_cookie, pos, - vecs, count, _numBytes); + if (vnode->stream.u.dev.info->read_pages) { + return vnode->stream.u.dev.info->read_pages(cookie->device_cookie, pos, + vecs, count, _numBytes); + } + + // emulate read_pages() using read() + + status_t error = B_OK; + size_t bytesTransferred = 0; + + size_t remainingBytes = *_numBytes; + for (size_t i = 0; i < count && remainingBytes > 0; i++) { + size_t toRead = min_c(vecs[i].iov_len, remainingBytes); + size_t length = toRead; + + error = vnode->stream.u.dev.info->read(cookie->device_cookie, pos, + vecs[i].iov_base, &length); + if (error != B_OK) + break; + + pos += length; + bytesTransferred += length; + remainingBytes -= length; + + if (length < toRead) + break; + } + + *_numBytes = bytesTransferred; + + return (bytesTransferred > 0 ? B_OK : error); } @@ -2196,49 +2291,33 @@ driver_module_info gDeviceForDriversModule = { // #pragma mark - kernel private API +extern "C" void +devfs_add_preloaded_drivers(kernel_args* args) +{ + struct preloaded_image* image; + for (image = args->preloaded_images; image != NULL; image = image->next) { + if (!image->is_module && image->id >= 0) { + // fake an absolute path + char path[B_PATH_NAME_LENGTH]; + strlcpy(path, "/boot/beos/system/add-ons/kernel/", sizeof(path)); + strlcat(path, image->name, sizeof(path)); + + // try to add the driver + status_t error = add_driver(path, image->id); + if (error != B_OK) { + dprintf("devfs_add_preloaded_drivers: Failed to add \"%s\"\n", + image->name); + unload_kernel_add_on(image->id); + } + } + } +} + + extern "C" status_t devfs_add_driver(const char *path) { - // see if we already know this driver - - struct stat stat; - if (::stat(path, &stat) != 0) - return errno; - - RecursiveLocker locker(&sDeviceFileSystem->lock); - - driver_entry *driver = (driver_entry *)hash_lookup( - sDeviceFileSystem->driver_hash, &stat.st_ino); - if (driver != NULL) { - // we know this driver - // ToDo: test for changes here? Although node monitoring should be enough. - if (driver->image < B_OK) - return driver->image; - - return B_OK; - } - - // we don't know this driver, create a new entry for it - - driver = (driver_entry *)malloc(sizeof(driver_entry)); - if (driver == NULL) - return B_NO_MEMORY; - - driver->path = strdup(path); - if (driver->path == NULL) { - free(driver); - return B_NO_MEMORY; - } - - driver->device = stat.st_dev; - driver->node = stat.st_ino; - driver->last_modified = stat.st_mtime; - - hash_insert(sDeviceFileSystem->driver_hash, driver); - - // Even if loading the driver fails - its entry will stay with us - // so that we don't have to go through it again - return load_driver(driver); + return add_driver(path, -1); } diff --git a/src/system/kernel/fs/vfs_boot.cpp b/src/system/kernel/fs/vfs_boot.cpp index 574d85ce01..1082ddcb3f 100644 --- a/src/system/kernel/fs/vfs_boot.cpp +++ b/src/system/kernel/fs/vfs_boot.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. * @@ -6,6 +7,9 @@ * Distributed under the terms of the NewOS License. */ +#include "vfs_boot.h" + +#include #include #include @@ -20,9 +24,10 @@ #include #include #include +#include #include -#include +#include "vfs_net_boot.h" //#define TRACE_VFS @@ -53,8 +58,7 @@ dev_t gBootDevice = -1; /** No image was chosen - prefer disks with names like "Haiku", or "System" */ - -static int +int compare_image_boot(const void *_a, const void *_b) { KPartition *a = *(KPartition **)_a; @@ -89,7 +93,6 @@ compare_image_boot(const void *_a, const void *_b) * is no CD, fall back to the standard mechanism (as implemented by * compare_image_boot(). */ - static int compare_cd_boot(const void *_a, const void *_b) { @@ -113,7 +116,6 @@ compare_cd_boot(const void *_a, const void *_b) * Note, this must use the same method as the one used in * boot/platform/bios_ia32/devices.cpp (or similar solutions). */ - static uint32 compute_check_sum(KDiskDevice *device, off_t offset) { @@ -136,19 +138,59 @@ compute_check_sum(KDiskDevice *device, off_t offset) } -/** Checks if the device matches the boot device as specified by the - * boot loader. - */ +// #pragma mark - BootMethod -static bool -is_boot_device(kernel_args *args, KDiskDevice *device, bool strict) + +BootMethod::BootMethod(const KMessage& bootVolume, int32 method) + : fBootVolume(bootVolume), + fMethod(method) { - disk_identifier &disk = args->boot_disk.identifier; +} - TRACE(("boot device: bus %ld, device %ld\n", disk.bus_type, - disk.device_type)); - switch (disk.bus_type) { +BootMethod::~BootMethod() +{ +} + + +status_t +BootMethod::Init() +{ + return B_OK; +} + + +// #pragma mark - DiskBootMethod + + +class DiskBootMethod : public BootMethod { +public: + DiskBootMethod(const KMessage& bootVolume, int32 method) + : BootMethod(bootVolume, method) + { + } + + virtual bool IsBootDevice(KDiskDevice* device, bool strict); + virtual bool IsBootPartition(KPartition* partition, bool& foundForSure); + virtual void SortPartitions(KPartition** partitions, int32 count); +}; + + +bool +DiskBootMethod::IsBootDevice(KDiskDevice* device, bool strict) +{ + disk_identifier *disk; + int32 diskIdentifierSize; + if (fBootVolume.FindData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE, + (const void**)&disk, &diskIdentifierSize) != B_OK) { + dprintf("DiskBootMethod::IsBootDevice(): no disk identifier!\n"); + return false; + } + + TRACE(("boot device: bus %ld, device %ld\n", disk->bus_type, + disk->device_type)); + + switch (disk->bus_type) { case PCI_BUS: case LEGACY_BUS: // TODO: implement this! (and then enable this feature in the boot loader) @@ -160,21 +202,23 @@ is_boot_device(kernel_args *args, KDiskDevice *device, bool strict) break; } - switch (disk.device_type) { + switch (disk->device_type) { case UNKNOWN_DEVICE: // test if the size of the device matches // (the BIOS might have given us the wrong value here, though) - if (strict && device->Size() != disk.device.unknown.size) + if (strict && device->Size() != disk->device.unknown.size) return false; // check if the check sums match, too for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) { - if (disk.device.unknown.check_sums[i].offset == -1) + if (disk->device.unknown.check_sums[i].offset == -1) continue; - if (compute_check_sum(device, disk.device.unknown.check_sums[i].offset) - != disk.device.unknown.check_sums[i].sum) + if (compute_check_sum(device, + disk->device.unknown.check_sums[i].offset) + != disk->device.unknown.check_sums[i].sum) { return false; + } } break; @@ -184,7 +228,6 @@ is_boot_device(kernel_args *args, KDiskDevice *device, bool strict) case USB_DEVICE: case FIREWIRE_DEVICE: case FIBRE_DEVICE: - case NETWORK_DEVICE: // TODO: implement me! break; } @@ -193,63 +236,162 @@ is_boot_device(kernel_args *args, KDiskDevice *device, bool strict) } +bool +DiskBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure) +{ + if (!fBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) { + // the simple case: we can just boot from the selected boot + // device + if (partition->Offset() == fBootVolume.GetBool( + BOOT_VOLUME_PARTITION_OFFSET, 0)) { + foundForSure = true; + return true; + } + } else { + // for now, we will just collect all BFS volumes + if (fMethod == BOOT_METHOD_CD + && fBootVolume.GetBool(BOOT_VOLUME_USER_SELECTED, false) + && partition->Type() != NULL + && strcmp(partition->Type(), kPartitionTypeDataSession)) { + return false; + } + + if (partition->ContentType() != NULL + && !strcmp(partition->ContentType(), "Be File System")) { + return true; + } + } + + return false; +} + + +void +DiskBootMethod::SortPartitions(KPartition** partitions, int32 count) +{ + qsort(partitions, count, sizeof(KPartition *), + fMethod == BOOT_METHOD_CD ? compare_cd_boot : compare_image_boot); +} + + +// #pragma mark - + + /** Make the boot partition (and probably others) available. * The partitions that are a boot candidate a put into the /a partitions * stack. If the user selected a boot device, there is will only be one * entry in this stack; if not, the most likely is put up first. * The boot code should then just try them one by one. */ - static status_t get_boot_partitions(kernel_args *args, PartitionStack &partitions) { + const KMessage& bootVolume = args->boot_volume; + + dprintf("get_boot_partitions(): boot volume message:\n"); + KMessageField field; + while (bootVolume.GetNextField(&field) == B_OK) { + type_code type = field.TypeCode(); + uint32 bigEndianType = B_HOST_TO_BENDIAN_INT32(type); + dprintf("field: \"%s\", type: %.4s (0x%lx):\n", field.Name(), + (char*)&bigEndianType, type); + + if (field.CountElements() == 0) + dprintf("\n"); + + int32 size; + for (int i = 0; const void* data = field.ElementAt(i, &size); i++) { + dprintf(" [%2d] ", i); + bool isIntType = false; + int64 intData = 0; + switch (type) { + case B_BOOL_TYPE: + dprintf("%s\n", (*(bool*)data ? "true" : "false")); + break; + case B_INT8_TYPE: + isIntType = true; + intData = *(int8*)data; + break; + case B_INT16_TYPE: + isIntType = true; + intData = *(int16*)data; + break; + case B_INT32_TYPE: + isIntType = true; + intData = *(int32*)data; + break; + case B_INT64_TYPE: + isIntType = true; + intData = *(int64*)data; + break; + case B_STRING_TYPE: + dprintf("\"%s\"\n", (char*)data); + break; + default: + dprintf("data: \"%p\", %ld bytes\n", (char*)data, size); + break; + } + if (isIntType) + dprintf("%lld (0x%llx)\n", intData, intData); + } + } + + + // create boot method + int32 bootMethodType = bootVolume.GetInt32(BOOT_METHOD, + BOOT_METHOD_DEFAULT); +dprintf("get_boot_partitions(): boot method type: %ld\n", bootMethodType); + BootMethod* bootMethod = NULL; + switch (bootMethodType) { + case BOOT_METHOD_NET: + bootMethod = new(nothrow) NetBootMethod(bootVolume, bootMethodType); + break; + + case BOOT_METHOD_HARD_DISK: + case BOOT_METHOD_CD: + default: + bootMethod = new(nothrow) DiskBootMethod(bootVolume, + bootMethodType); + break; + } + + status_t status = bootMethod->Init(); + if (status != B_OK) + return status; + KDiskDeviceManager::CreateDefault(); KDiskDeviceManager *manager = KDiskDeviceManager::Default(); - status_t status = manager->InitialDeviceScan(); + status = manager->InitialDeviceScan(); if (status != B_OK) { dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n", strerror(status)); return status; } - if (args->boot_disk.booted_from_network) { - panic("get_boot_partitions: boot from network, server %08lx, client %08lx\n", - args->boot_disk.identifier.device.network.server_ip, - args->boot_disk.identifier.device.network.client_ip); - } - struct BootPartitionVisitor : KPartitionVisitor { - BootPartitionVisitor(kernel_args &args, PartitionStack &stack) - : fArgs(args), fPartitions(stack) {} + BootPartitionVisitor(BootMethod* bootMethod, PartitionStack &stack) + : fPartitions(stack), + fBootMethod(bootMethod) + { + } virtual bool VisitPre(KPartition *partition) { if (!partition->ContainsFileSystem()) return false; - if (!fArgs.boot_disk.booted_from_image) { - // the simple case: we can just boot from the selected boot device - if (partition->Offset() == fArgs.boot_disk.partition_offset) { - fPartitions.Push(partition); - return true; - } - } else { - // for now, we will just collect all BFS volumes - if (fArgs.boot_disk.cd && fArgs.boot_disk.user_selected - && partition->Type() != NULL - && strcmp(partition->Type(), kPartitionTypeDataSession)) - return false; + bool foundForSure = false; + if (fBootMethod->IsBootPartition(partition, foundForSure)) + fPartitions.Push(partition); - if (partition->ContentType() != NULL - && !strcmp(partition->ContentType(), "Be File System")) - fPartitions.Push(partition); - } - return false; + // if found for sure, we can terminate the search + return foundForSure; } + private: - kernel_args &fArgs; PartitionStack &fPartitions; - } visitor(*args, partitions); + BootMethod* fBootMethod; + } visitor(bootMethod, partitions); bool strict = true; @@ -257,7 +399,7 @@ get_boot_partitions(kernel_args *args, PartitionStack &partitions) KDiskDevice *device; int32 cookie = 0; while ((device = manager->NextDevice(&cookie)) != NULL) { - if (!is_boot_device(args, device, strict)) + if (!bootMethod->IsBootDevice(device, strict)) continue; if (device->VisitEachDescendant(&visitor) != NULL) @@ -271,11 +413,10 @@ get_boot_partitions(kernel_args *args, PartitionStack &partitions) strict = false; } - if (!args->boot_disk.user_selected) { - // sort partition list (ie. when booting from CD, CDs should come first in the list) - qsort(partitions.Array(), partitions.CountItems(), sizeof(KPartition *), - args->boot_disk.cd ? compare_cd_boot : compare_image_boot); - } + // sort partition list (e.g.. when booting from CD, CDs should come first in + // the list) + if (!args->boot_volume.GetBool(BOOT_VOLUME_USER_SELECTED, false)) + bootMethod->SortPartitions(partitions.Array(), partitions.CountItems()); return B_OK; } diff --git a/src/system/kernel/fs/vfs_boot.h b/src/system/kernel/fs/vfs_boot.h new file mode 100644 index 0000000000..54aea4119b --- /dev/null +++ b/src/system/kernel/fs/vfs_boot.h @@ -0,0 +1,30 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _VFS_BOOT_H +#define _VFS_BOOT_H + + +#include +#include + + +class BootMethod { +public: + BootMethod(const KMessage& bootVolume, int32 method); + virtual ~BootMethod(); + + virtual status_t Init(); + + virtual bool IsBootDevice(KDiskDevice* device, bool strict) = 0; + virtual bool IsBootPartition(KPartition* partition, bool& foundForSure) = 0; + virtual void SortPartitions(KPartition** partitions, int32 count) = 0; + +protected: + const KMessage& fBootVolume; + int32 fMethod; +}; + + +#endif // _VFS_BOOT_H diff --git a/src/system/kernel/fs/vfs_net_boot.cpp b/src/system/kernel/fs/vfs_net_boot.cpp new file mode 100644 index 0000000000..57e59caf88 --- /dev/null +++ b/src/system/kernel/fs/vfs_net_boot.cpp @@ -0,0 +1,397 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include "vfs_net_boot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + + +static bool +string_starts_with(const char* string, const char* prefix) +{ + size_t stringLen = strlen(string); + size_t prefixLen = strlen(prefix); + return (stringLen >= prefixLen && strncmp(string, prefix, prefixLen) == 0); +} + + +static bool +is_net_device(KDiskDevice* device) +{ + const char* path = device->Path(); + return (string_starts_with(path, "/dev/disk/virtual/nbd/") + || string_starts_with(path, "/dev/disk/virtual/remote_disk/")); +} + + +static int +compare_partitions_net_devices(const void *_a, const void *_b) +{ + KPartition* a = *(KPartition**)_a; + KPartition* b = *(KPartition**)_b; + + bool aIsCD = is_net_device(a->Device()); + bool bIsCD = is_net_device(b->Device()); + + int compare = (int)aIsCD - (int)bIsCD; + if (compare != 0) + return compare; + + return compare_image_boot(_a, _b); +} + + +class NetStackInitializer { +public: + NetStackInitializer(uint64 clientMAC, uint32 clientIP, uint32 netMask) + : fSocketModule(NULL), + fSocket(-1), + fLinkSocket(-1), + fClientMAC(clientMAC), + fClientIP(clientIP), + fNetMask(netMask), + fFoundInterface(false), + fConfiguredInterface(false) + { + } + + ~NetStackInitializer() + { + // close control sockets + if (fSocket >= 0) + close(fSocket); + + if (fLinkSocket >= 0) + close(fLinkSocket); + + // put socket module + if (fSocketModule) + put_module(fSocketModule->info.name); + } + + status_t Init() + { + // get the socket module + status_t error = get_module(B_SOCKET_MODULE_NAME, + (module_info**)&fSocketModule); + if (error != B_OK) { + dprintf("NetStackInitializer: Failed to load socket module: %s\n", + strerror(error)); + return error; + } + + // open a control socket for playing with the stack + fSocket = fSocketModule->socket(AF_INET, SOCK_DGRAM, 0); + if (fSocket < 0) { + dprintf("NetStackInitializer: Failed to open socket: %s\n", + strerror(errno)); + return errno; + } + + // ... and a link level socket + fLinkSocket = fSocketModule->socket(AF_LINK, SOCK_DGRAM, 0); + if (fLinkSocket < 0) { + dprintf("NetStackInitializer: Failed to open link level socket:" + " %s\n", strerror(errno)); + return errno; + } + + + // now iterate through the existing network devices + KPath path; + error = path.SetTo("/dev/net"); + if (error != B_OK) + return error; + + _ScanDevices(path); + + return (fConfiguredInterface ? B_OK : B_ERROR); + } + +private: + void _ScanDevices(KPath& path) + { + DIR* dir = opendir(path.Path()); + if (!dir) { + dprintf("NetStackInitializer: Failed to opendir() \"%s\": %s\n", + path.Path(), strerror(errno)); + return; + } + + status_t error = B_OK; + + while (dirent* entry = readdir(dir)) { + // skip "." and ".." + if (strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) { + continue; + } + + path.Append(entry->d_name); + + struct stat st; + if (stat(path.Path(), &st) == 0) { + if (S_ISDIR(st.st_mode)) + _ScanDevices(path); + else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) + _ScanDevice(path.Path()); + } + + path.RemoveLeaf(); + + if (fFoundInterface) + break; + } + + closedir(dir); + } + + void _ScanDevice(const char* path) + { + dprintf("NetStackInitializer: scanning device %s\n", path); + + // check if this interface is already known + ifreq request; + if (strlen(path) >= IF_NAMESIZE) + return; + strcpy(request.ifr_name, path); + + if (ioctl(fSocket, SIOCGIFINDEX, &request, sizeof(request)) < 0) { + // not known yet -- add it + request.ifr_parameter.base_name[0] = '\0'; + request.ifr_parameter.device[0] = '\0'; + request.ifr_parameter.sub_type = 0; + + if (ioctl(fSocket, SIOCAIFADDR, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: adding interface failed for " + "device %s: %s\n", path, strerror(errno)); + return; + } + } + + // bring the interface up (get flags, add IFF_UP) + if (ioctl(fSocket, SIOCGIFFLAGS, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: getting flags failed for interface " + "%s: %s\n", path, strerror(errno)); + return; + } + + int interfaceFlags = request.ifr_flags; + if (!(interfaceFlags & IFF_UP)) { + interfaceFlags |= IFF_UP; + request.ifr_flags = interfaceFlags; + if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: failed to bring interface up " + "%s: %s\n", path, strerror(errno)); + return; + } + } + + // get the MAC address + if (ioctl(fLinkSocket, SIOCGIFADDR, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: Getting MAC addresss failed for " + "interface %s: %s\n", path, strerror(errno)); + return; + } + + sockaddr_dl& link = *(sockaddr_dl*)&request.ifr_addr; + if (link.sdl_type != IFT_ETHER) + return; + + if (link.sdl_alen == 0) + return; + + uint8* macBytes = (uint8 *)LLADDR(&link); + uint64 macAddress = ((uint64)macBytes[0] << 40) + | ((uint64)macBytes[1] << 32) + | ((uint64)macBytes[2] << 24) + | ((uint64)macBytes[3] << 16) + | ((uint64)macBytes[4] << 8) + | (uint64)macBytes[5]; + + dprintf("NetStackInitializer: found ethernet interface with MAC " + "address %02x:%02x:%02x:%02x:%02x:%02x; which is%s the one we're " + "looking for\n", macBytes[0], macBytes[1], macBytes[2], macBytes[3], + macBytes[4], macBytes[5], (macAddress == fClientMAC ? "" : "n't")); + + if (macAddress != fClientMAC) + return; + + fFoundInterface = true; + + // configure the interface + + // set IP address + sockaddr_in& address = *(sockaddr_in*)&request.ifr_addr; + address.sin_family = AF_INET; + address.sin_len = sizeof(sockaddr_in); + address.sin_port = 0; + address.sin_addr.s_addr = htonl(fClientIP); + memset(&address.sin_zero[0], 0, sizeof(address.sin_zero)); + if (ioctl(fSocket, SIOCSIFADDR, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: Setting IP addresss failed for " + "interface %s: %s\n", path, strerror(errno)); + return; + } + + // set net mask + address.sin_addr.s_addr = htonl(fNetMask); + if (ioctl(fSocket, SIOCSIFNETMASK, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: Setting net mask failed for " + "interface %s: %s\n", path, strerror(errno)); + return; + } + + // set broadcast address + address.sin_addr.s_addr = htonl(fClientIP | ~fNetMask); + if (ioctl(fSocket, SIOCSIFBRDADDR, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: Setting broadcast address failed for " + "interface %s: %s\n", path, strerror(errno)); + return; + } + + // set IFF_BROADCAST + if (!(interfaceFlags & IFF_BROADCAST)) { + interfaceFlags |= IFF_BROADCAST; + request.ifr_flags = interfaceFlags; + if (ioctl(fSocket, SIOCSIFFLAGS, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: failed to set IFF_BROADCAST flag " + "for interface %s: %s\n", path, strerror(errno)); + return; + } + } + + // set default route; remove previous one, if any + route_entry route; + memset(&route, 0, sizeof(route_entry)); + route.flags = RTF_STATIC | RTF_DEFAULT; + + request.ifr_route = route; + ioctl(fSocket, SIOCDELRT, &request, sizeof(request)); + if (ioctl(fSocket, SIOCADDRT, &request, sizeof(request)) < 0) { + dprintf("NetStackInitializer: Failed to set default route: %s\n", + strerror(errno)); + return; + } + + fConfiguredInterface = true; + + dprintf("NetStackInitializer: successfully configured boot network " + "interface\n"); + } + +private: + socket_module_info* fSocketModule; + int fSocket; + int fLinkSocket; + uint64 fClientMAC; + uint32 fClientIP; + uint32 fNetMask; + bool fFoundInterface; + bool fConfiguredInterface; +}; + + +// #pragma mark - NetBootMethod + + +NetBootMethod::NetBootMethod(const KMessage& bootVolume, int32 method) + : BootMethod(bootVolume, method) +{ +} + + +NetBootMethod::~NetBootMethod() +{ +} + + +status_t +NetBootMethod::Init() +{ + // We need to bring up the net stack. + status_t status; + + uint64 clientMAC; + uint32 clientIP; + uint32 netMask; + if (fBootVolume.FindInt64("client MAC", (int64*)&clientMAC) != B_OK + || fBootVolume.FindInt32("client IP", (int32*)&clientIP) != B_OK) { + panic("no client MAC or IP address or net mask\n"); + return B_ERROR; + } + + if (fBootVolume.FindInt32("net mask", (int32*)&netMask) != B_OK) { + // choose default netmask depending on the class of the address + in_addr_t net = htonl(clientIP); + if (IN_CLASSA(net) + || (ntohl(net) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { + // class A, or loopback + netMask = IN_CLASSA_NET; + } else if (IN_CLASSB(net)) { + // class B + netMask = IN_CLASSB_NET; + } else { + // class C and rest + netMask = IN_CLASSC_NET; + } + } + + NetStackInitializer initializer(clientMAC, clientIP, netMask); + status = initializer.Init(); + if (status != B_OK) + return status; + + // TODO: "net root path" should be used for finding the boot device/FS, + // but ATM neither the remote_disk nor the nbd driver are configurable + // at this point. + const char* rootPath = fBootVolume.GetString("net root path", NULL); + dprintf("NetBootMethod::Init(): net stack initialized; root path is: %s\n", + rootPath); + + return B_OK; +} + + +bool +NetBootMethod::IsBootDevice(KDiskDevice* device, bool strict) +{ + // We support only NBD and RemoteDisk at the moment, so we accept any + // device under /dev/disk/virtual/{nbd,remote_disk}/. + return is_net_device(device); +} + + +bool +NetBootMethod::IsBootPartition(KPartition* partition, bool& foundForSure) +{ + // as long as it's BFS, we're fine + return (partition->ContentType() + && strcmp(partition->ContentType(), kPartitionTypeBFS) == 0); +} + + +void +NetBootMethod::SortPartitions(KPartition** partitions, int32 count) +{ + qsort(partitions, count, sizeof(KPartition*), + compare_partitions_net_devices); +} diff --git a/src/system/kernel/fs/vfs_net_boot.h b/src/system/kernel/fs/vfs_net_boot.h new file mode 100644 index 0000000000..c2f16d0b47 --- /dev/null +++ b/src/system/kernel/fs/vfs_net_boot.h @@ -0,0 +1,28 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _VFS_NET_BOOT_H +#define _VFS_NET_BOOT_H + + +#include "vfs_boot.h" + + +int compare_image_boot(const void *_a, const void *_b); + + +class NetBootMethod : public BootMethod { +public: + NetBootMethod(const KMessage& bootVolume, int32 method); + virtual ~NetBootMethod(); + + virtual status_t Init(); + + virtual bool IsBootDevice(KDiskDevice* device, bool strict); + virtual bool IsBootPartition(KPartition* partition, bool& foundForSure); + virtual void SortPartitions(KPartition** partitions, int32 count); +}; + + +#endif // _VFS_NET_BOOT_H diff --git a/src/system/kernel/main.c b/src/system/kernel/main.c index 5397f8055f..ed87d6ec73 100644 --- a/src/system/kernel/main.c +++ b/src/system/kernel/main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -243,6 +244,9 @@ main2(void *unused) TRACE("Init Device Manager\n"); device_manager_init(&sKernelArgs); + TRACE("Add preloaded old-style drivers\n"); + devfs_add_preloaded_drivers(&sKernelArgs); + // ToDo: device manager starts here, bus_init()/dev_init() won't be necessary anymore, // but instead, the hardware and drivers are rescanned then. diff --git a/src/system/kernel/module.cpp b/src/system/kernel/module.cpp index 2577c8d90f..4655c50724 100644 --- a/src/system/kernel/module.cpp +++ b/src/system/kernel/module.cpp @@ -909,6 +909,8 @@ register_preloaded_module_image(struct preloaded_image *image) TRACE(("register_preloaded_module_image(image = \"%s\")\n", image->name)); + image->is_module = false; + if (image->id < 0) return B_BAD_VALUE; @@ -922,6 +924,8 @@ register_preloaded_module_image(struct preloaded_image *image) goto error; } + image->is_module = true; + moduleImage->dependencies = NULL; get_image_symbol(image->id, "module_dependencies", B_SYMBOL_TYPE_DATA, (void **)&moduleImage->dependencies); @@ -966,8 +970,10 @@ register_preloaded_module_image(struct preloaded_image *image) error: free(moduleImage); - // we don't need this image anymore - unload_kernel_add_on(image->id); + // We don't need this image anymore. We keep it, if it doesn't look like + // a module at all. It might be an old-style driver. + if (image->is_module) + unload_kernel_add_on(image->id); return status; }