[Sorry, couldn't split this one up any further.]
* Images preloaded by the boot loader had to be modules to be of any use to the kernel. Extended the mechanism so that any images not accepted by the module code would later be tried to be added as drivers by the devfs. This is a little hacky ATM, since the devfs manages the drivers using a hash map keyed by the drivers inode ID, which those drivers obviously don't have. * The devfs emulates read_pages() using read(), if the device driver doesn't implement the former (all old-style drivers), thus making it possible to BFS, which uses the file cache which in turn requires read_pages(), on the device. write_pages() emulation is still missing. * Replaced the kernel_args::boot_disk structure by a KMessage, which can more flexibly be extended and deals more gracefully with arbitrarily-size data. The disk_identifier structure still exists, though. It is added as message field in cases where needed (non net boot). Moved the boot_drive_number field of the bios_ia32 platform specific args into the message. * Made the stage 1 PXE boot loader superfluous. Moved the relevant initialization code into the stage 2 loader, which can now be loaded directly via PXE. * The PXE boot loader does now download a boot tgz archive via TFTP. It does no longer use the RemoteDisk protocol (it could actually be removed from the boot loader). It also parses the DHCP options in the DHCPACK packet provided by PXE and extracts the root path to be mounted by the kernel. * Reorganized the boot volume search in the kernel (vfs_boot.cpp) and added support for network boot. In this case the net stack is initialized and the network interface the boot loader used is brought up and configured. Since NBD and RemoteDisk are our only options for net boot (and those aren't really configurable dynamically) ATM, the the boot device is found automatically by the disk device manager. Booting via PXE does work to some degree now. The most grievous problem is that loading certain drivers or kernel modules (or related activity) causes a reboot (likely a triple fault, though one wonders where our double fault handler is on vacation). Namely the keyboard and mouse input server add-ons need to be deactivated as well as the media server. A smaller problem is the net server, which apparently tries to (re-)configure the network interface we're using to boot, which obviously doesn't work out that well. So, if all this stuff is disabled Haiku does fully boot, when using the RemoteDisk protocol (not being able to use keyboard or mouse doesn't make this a particular fascinating experience, though ;-)). I had no luck with NBD -- it seemed to have protocol problems with the servers I tried. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21611 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
018cf36396
commit
9e8dc2a9bb
@ -23,7 +23,6 @@ enum device_types {
|
|||||||
USB_DEVICE,
|
USB_DEVICE,
|
||||||
FIREWIRE_DEVICE,
|
FIREWIRE_DEVICE,
|
||||||
FIBRE_DEVICE,
|
FIBRE_DEVICE,
|
||||||
NETWORK_DEVICE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NUM_DISK_CHECK_SUMS 5
|
#define NUM_DISK_CHECK_SUMS 5
|
||||||
@ -62,11 +61,6 @@ typedef struct disk_identifier {
|
|||||||
struct {
|
struct {
|
||||||
uint64 wwd;
|
uint64 wwd;
|
||||||
} fibre;
|
} fibre;
|
||||||
struct {
|
|
||||||
uint32 client_ip;
|
|
||||||
uint32 server_ip;
|
|
||||||
uint16 server_port;
|
|
||||||
} network;
|
|
||||||
struct {
|
struct {
|
||||||
off_t size;
|
off_t size;
|
||||||
struct {
|
struct {
|
||||||
|
@ -35,6 +35,8 @@ struct preloaded_image {
|
|||||||
ino_t inode;
|
ino_t inode;
|
||||||
image_id id;
|
image_id id;
|
||||||
// the ID field will be filled out in the kernel
|
// the ID field will be filled out in the kernel
|
||||||
|
bool is_module;
|
||||||
|
// set by the module initialization code
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -18,9 +18,26 @@
|
|||||||
#include <platform_kernel_args.h>
|
#include <platform_kernel_args.h>
|
||||||
#include <arch_kernel_args.h>
|
#include <arch_kernel_args.h>
|
||||||
|
|
||||||
|
#include <util/KMessage.h>
|
||||||
|
|
||||||
#define CURRENT_KERNEL_ARGS_VERSION 1
|
#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 {
|
typedef struct kernel_args {
|
||||||
uint32 kernel_args_size;
|
uint32 kernel_args_size;
|
||||||
@ -41,14 +58,7 @@ typedef struct kernel_args {
|
|||||||
uint32 num_cpus;
|
uint32 num_cpus;
|
||||||
addr_range cpu_kstack[MAX_BOOT_CPUS];
|
addr_range cpu_kstack[MAX_BOOT_CPUS];
|
||||||
|
|
||||||
struct {
|
KMessage boot_volume;
|
||||||
disk_identifier identifier;
|
|
||||||
off_t partition_offset;
|
|
||||||
bool user_selected;
|
|
||||||
bool booted_from_image;
|
|
||||||
bool booted_from_network;
|
|
||||||
bool cd;
|
|
||||||
} boot_disk;
|
|
||||||
|
|
||||||
struct driver_settings_file *driver_settings;
|
struct driver_settings_file *driver_settings;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint16 serial_base_ports[MAX_SERIAL_PORTS];
|
uint16 serial_base_ports[MAX_SERIAL_PORTS];
|
||||||
|
|
||||||
uint16 boot_drive_number;
|
|
||||||
bios_drive *drives; // this does not contain the boot drive
|
bios_drive *drives; // this does not contain the boot drive
|
||||||
|
|
||||||
apm_info apm;
|
apm_info apm;
|
||||||
|
@ -73,6 +73,26 @@ class ConsoleNode : public Node {
|
|||||||
virtual ssize_t Write(const void *buffer, size_t bufferSize);
|
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 */
|
/* function prototypes */
|
||||||
|
|
||||||
extern status_t vfs_init(stage2_args *args);
|
extern status_t vfs_init(stage2_args *args);
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void devfs_add_preloaded_drivers(struct kernel_args* args);
|
||||||
|
|
||||||
status_t devfs_add_driver(const char *path);
|
status_t devfs_add_driver(const char *path);
|
||||||
|
|
||||||
status_t devfs_unpublish_file_device(const char *path);
|
status_t devfs_unpublish_file_device(const char *path);
|
||||||
|
@ -22,7 +22,9 @@ UsePrivateHeaders kernel shared storage ;
|
|||||||
BOOT_SUPPORT_FILE_SYSTEM_BFS
|
BOOT_SUPPORT_FILE_SYSTEM_BFS
|
||||||
BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS
|
BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS
|
||||||
BOOT_SUPPORT_FILE_SYSTEM_TARFS
|
BOOT_SUPPORT_FILE_SYSTEM_TARFS
|
||||||
;
|
|
||||||
|
KMESSAGE_CONTAINER_ONLY
|
||||||
|
;
|
||||||
|
|
||||||
defines = [ FDefines $(defines) ] ;
|
defines = [ FDefines $(defines) ] ;
|
||||||
SubDirCcFlags $(defines) -Wall -Wno-multichar ;
|
SubDirCcFlags $(defines) -Wall -Wno-multichar ;
|
||||||
@ -46,8 +48,9 @@ KernelStaticLibrary boot_loader :
|
|||||||
driver_settings.c
|
driver_settings.c
|
||||||
|
|
||||||
# utils
|
# utils
|
||||||
list.c
|
|
||||||
kernel_cpp.cpp
|
kernel_cpp.cpp
|
||||||
|
KMessage.cpp
|
||||||
|
list.c
|
||||||
|
|
||||||
: -fno-pic
|
: -fno-pic
|
||||||
;
|
;
|
||||||
@ -69,6 +72,9 @@ KernelStaticLibrary boot_partitions :
|
|||||||
SEARCH on [ FGristFiles kernel_cpp.cpp list.c ]
|
SEARCH on [ FGristFiles kernel_cpp.cpp list.c ]
|
||||||
= [ FDirName $(HAIKU_TOP) src system kernel util ] ;
|
= [ 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 ]
|
SEARCH on [ FGristFiles driver_settings.c ]
|
||||||
= [ FDirName $(HAIKU_TOP) src system libroot os ] ;
|
= [ FDirName $(HAIKU_TOP) src system libroot os ] ;
|
||||||
|
|
||||||
|
@ -193,7 +193,8 @@ load_modules(stage2_args *args, Directory *volume)
|
|||||||
// and now load all partitioning and file system modules
|
// and now load all partitioning and file system modules
|
||||||
// needed to identify the boot volume
|
// 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
|
// iterate over the mounted volumes and load their file system
|
||||||
Partition *partition;
|
Partition *partition;
|
||||||
if (gRoot->GetPartitionFor(volume, &partition) == B_OK) {
|
if (gRoot->GetPartitionFor(volume, &partition) == B_OK) {
|
||||||
|
@ -35,6 +35,9 @@ main(stage2_args *args)
|
|||||||
|
|
||||||
TRACE(("boot(): heap initialized...\n"));
|
TRACE(("boot(): heap initialized...\n"));
|
||||||
|
|
||||||
|
// construct boot_volume KMessage explicitely
|
||||||
|
new(&gKernelArgs.boot_volume) KMessage;
|
||||||
|
|
||||||
platform_init_video();
|
platform_init_video();
|
||||||
|
|
||||||
// the main platform dependent initialisation
|
// the main platform dependent initialisation
|
||||||
@ -107,6 +110,20 @@ main(stage2_args *args)
|
|||||||
gKernelArgs.kernel_args_size = sizeof(kernel_args);
|
gKernelArgs.kernel_args_size = sizeof(kernel_args);
|
||||||
gKernelArgs.version = CURRENT_KERNEL_ARGS_VERSION;
|
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.
|
// ToDo: cleanup, heap_release() etc.
|
||||||
platform_start_kernel();
|
platform_start_kernel();
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ user_menu_boot_volume(Menu *menu, MenuItem *item)
|
|||||||
bootItem->Select(true);
|
bootItem->Select(true);
|
||||||
bootItem->SetData(item->Data());
|
bootItem->SetData(item->Data());
|
||||||
|
|
||||||
gKernelArgs.boot_disk.user_selected = true;
|
gKernelArgs.boot_volume.SetBool(BOOT_VOLUME_USER_SELECTED, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ add_boot_volume_menu(Directory *bootVolume)
|
|||||||
menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
|
menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
|
||||||
item->SetType(MENU_ITEM_NO_CHOICE);
|
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");
|
menu->SetChoiceText("CD-ROM or hard drive");
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
@ -210,8 +210,10 @@ Partition::_Mount(file_system_module_info *module, Directory **_fileSystem)
|
|||||||
status_t
|
status_t
|
||||||
Partition::Mount(Directory **_fileSystem, bool isBootDevice)
|
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);
|
return _Mount(&gTarFileSystemModule, _fileSystem);
|
||||||
|
}
|
||||||
|
|
||||||
for (int32 i = 0; i < sNumFileSystemModules; i++) {
|
for (int32 i = 0; i < sNumFileSystemModules; i++) {
|
||||||
status_t status = _Mount(sFileSystemModules[i], _fileSystem);
|
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
|
// 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
|
// the device we were booted from (which is likely to be a slow
|
||||||
// floppy or CD)
|
// 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;
|
return B_ENTRY_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
const partition_module_info *bestModule = NULL;
|
const partition_module_info *bestModule = NULL;
|
||||||
void *bestCookie = NULL;
|
void *bestCookie = NULL;
|
||||||
|
@ -198,6 +198,58 @@ Directory::Type() const
|
|||||||
// #pragma mark -
|
// #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)
|
Descriptor::Descriptor(Node *node, void *cookie)
|
||||||
:
|
:
|
||||||
fNode(node),
|
fNode(node),
|
||||||
@ -307,7 +359,8 @@ register_boot_file_system(Directory *volume)
|
|||||||
return status;
|
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());
|
Node *device = get_node_from(partition->FD());
|
||||||
if (device == NULL) {
|
if (device == NULL) {
|
||||||
|
@ -166,7 +166,7 @@ static bool sBlockDevicesAdded = false;
|
|||||||
static void
|
static void
|
||||||
check_cd_boot(BIOSDrive *drive)
|
check_cd_boot(BIOSDrive *drive)
|
||||||
{
|
{
|
||||||
gKernelArgs.boot_disk.cd = false;
|
gKernelArgs.boot_volume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK);
|
||||||
|
|
||||||
if (drive->DriveID() != 0)
|
if (drive->DriveID() != 0)
|
||||||
return;
|
return;
|
||||||
@ -183,7 +183,8 @@ check_cd_boot(BIOSDrive *drive)
|
|||||||
// we obviously were booted from CD!
|
// we obviously were booted from CD!
|
||||||
|
|
||||||
specification_packet *packet = (specification_packet *)kDataSegmentScratch;
|
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
|
#if 0
|
||||||
dprintf("got CD boot spec:\n");
|
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()));
|
TRACE(("boot drive size: %Ld bytes\n", drive->Size()));
|
||||||
gKernelArgs.boot_disk.booted_from_image = gBootedFromImage;
|
gKernelArgs.boot_volume.SetInt32(BOOT_VOLUME_BOOTED_FROM_IMAGE,
|
||||||
gKernelArgs.boot_disk.booted_from_network = false;
|
gBootedFromImage);
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@ -803,8 +804,9 @@ platform_register_boot_device(Node *device)
|
|||||||
|
|
||||||
check_cd_boot(drive);
|
check_cd_boot(drive);
|
||||||
|
|
||||||
gKernelArgs.platform_args.boot_drive_number = drive->DriveID();
|
gKernelArgs.boot_volume.SetInt64("boot drive number", drive->DriveID());
|
||||||
gKernelArgs.boot_disk.identifier = drive->Identifier();
|
gKernelArgs.boot_volume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
|
||||||
|
&drive->Identifier(), sizeof(disk_identifier));
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ KernelMergeObject boot_platform_pxe_ia32.o :
|
|||||||
pxe_undi.cpp
|
pxe_undi.cpp
|
||||||
$(bios_ia32_src)
|
$(bios_ia32_src)
|
||||||
$(bios_ia32_edid_src)
|
$(bios_ia32_edid_src)
|
||||||
|
|
||||||
# generic
|
# generic
|
||||||
text_menu.cpp
|
text_menu.cpp
|
||||||
: -fno-pic
|
: -fno-pic
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <boot/net/NetStack.h>
|
#include <boot/net/NetStack.h>
|
||||||
#include <boot/net/RemoteDisk.h>
|
#include <boot/net/RemoteDisk.h>
|
||||||
#include <util/kernel_cpp.h>
|
#include <util/kernel_cpp.h>
|
||||||
|
#include <util/KMessage.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -27,26 +28,90 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//extern unsigned char* gBuiltinBootArchive;
|
||||||
|
//extern long long gBuiltinBootArchiveSize;
|
||||||
|
|
||||||
|
static TFTP sTFTP;
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
|
platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
|
||||||
{
|
{
|
||||||
TRACE("platform_add_boot_device\n");
|
TRACE("platform_add_boot_device\n");
|
||||||
|
|
||||||
status_t error = net_stack_init();
|
// get the boot archive containing kernel and drivers via TFTP
|
||||||
if (error != B_OK)
|
status_t error = sTFTP.Init();
|
||||||
return error;
|
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
|
// get the file
|
||||||
RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk();
|
error = sTFTP.ReceiveFile(fileName, &data, &size);
|
||||||
if (!remoteDisk) {
|
if (error == B_OK) {
|
||||||
unsigned ip = NetStack::Default()->GetEthernetInterface()->IPAddress();
|
char name[64];
|
||||||
panic("PXE boot: can't find remote disk on server %u.%u.%u.%u\n",
|
ip_addr_t serverAddress = sTFTP.ServerIPAddress();
|
||||||
(ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
|
snprintf(name, sizeof(name), "%lu.%lu.%lu.%lu:%s",
|
||||||
return B_ENTRY_NOT_FOUND;
|
(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_ENTRY_NOT_FOUND;
|
||||||
return B_OK;
|
|
||||||
|
// // 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");
|
TRACE("platform_register_boot_device\n");
|
||||||
|
|
||||||
gKernelArgs.platform_args.boot_drive_number = 0xffff;
|
// get the root path -- chop off the file name of the archive we loaded
|
||||||
gKernelArgs.platform_args.drives = NULL;
|
const char* rootPath = sTFTP.RootPath();
|
||||||
|
if (rootPath) {
|
||||||
|
if (char* fileNameEnd = strchr(rootPath, ';'))
|
||||||
|
rootPath = fileNameEnd + 1;
|
||||||
|
}
|
||||||
|
|
||||||
RemoteDisk *rd = static_cast<RemoteDisk *>(device);
|
KMessage& bootVolume = gKernelArgs.boot_volume;
|
||||||
UNDI *undi = static_cast<UNDI *>(NetStack::Default()->GetEthernetInterface());
|
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;
|
// RemoteDisk *rd = static_cast<RemoteDisk *>(device);
|
||||||
gKernelArgs.boot_disk.identifier.device_type = NETWORK_DEVICE;
|
// UNDI *undi = static_cast<UNDI *>(NetStack::Default()->GetEthernetInterface());
|
||||||
gKernelArgs.boot_disk.identifier.device.network.client_ip = undi->IPAddress();
|
//
|
||||||
gKernelArgs.boot_disk.identifier.device.network.server_ip = rd->ServerIPAddress();
|
// gKernelArgs.boot_disk.identifier.bus_type = UNKNOWN_BUS;
|
||||||
gKernelArgs.boot_disk.identifier.device.network.server_port = rd->ServerPort();
|
// gKernelArgs.boot_disk.identifier.device_type = NETWORK_DEVICE;
|
||||||
gKernelArgs.boot_disk.partition_offset = 0;
|
// gKernelArgs.boot_disk.identifier.device.network.client_ip = undi->IPAddress();
|
||||||
gKernelArgs.boot_disk.user_selected = false;
|
// gKernelArgs.boot_disk.identifier.device.network.server_ip = rd->ServerIPAddress();
|
||||||
gKernelArgs.boot_disk.booted_from_image = false;
|
// gKernelArgs.boot_disk.identifier.device.network.server_port = rd->ServerPort();
|
||||||
gKernelArgs.boot_disk.booted_from_network = true;
|
// gKernelArgs.boot_disk.partition_offset = 0;
|
||||||
gKernelArgs.boot_disk.cd = false;
|
// 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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <boot/platform.h>
|
||||||
|
|
||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "pxe_undi.h"
|
#include "pxe_undi.h"
|
||||||
|
|
||||||
@ -49,18 +51,110 @@ hex_dump(const void *_data, int length)
|
|||||||
#endif // !TRACE_NETWORK
|
#endif // !TRACE_NETWORK
|
||||||
|
|
||||||
|
|
||||||
UNDI::UNDI()
|
// #pragma mark - PXEService
|
||||||
: fMACAddress()
|
|
||||||
, fRxFinished(true)
|
|
||||||
, fPxeData(NULL)
|
|
||||||
{
|
|
||||||
TRACE("UNDI::UNDI\n");
|
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
fPxeData = pxe_undi_find_data();
|
||||||
if (!fPxeData)
|
if (!fPxeData) {
|
||||||
panic("can't find !PXE structure");
|
panic("can't find !PXE structure");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
dprintf("PXE API entrypoint at %04x:%04x\n", fPxeData->EntryPointSP.seg, fPxeData->EntryPointSP.ofs);
|
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");
|
TRACE("UNDI::Init\n");
|
||||||
|
|
||||||
PXENV_GET_CACHED_INFO cached_info;
|
|
||||||
PXENV_UNDI_GET_INFORMATION get_info;
|
PXENV_UNDI_GET_INFORMATION get_info;
|
||||||
PXENV_UNDI_GET_STATE get_state;
|
PXENV_UNDI_GET_STATE get_state;
|
||||||
PXENV_UNDI_OPEN undi_open;
|
PXENV_UNDI_OPEN undi_open;
|
||||||
uint16 res;
|
uint16 res;
|
||||||
|
|
||||||
cached_info.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
|
status_t error = PXEService::Init();
|
||||||
cached_info.BufferSize = 0;
|
if (error != B_OK)
|
||||||
cached_info.BufferLimit = 0;
|
return error;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
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,
|
(fClientIP >> 24) & 0xff, (fClientIP >> 16) & 0xff, (fClientIP >> 8) & 0xff, fClientIP & 0xff,
|
||||||
(ipServer >> 24) & 0xff, (ipServer >> 16) & 0xff, (ipServer >> 8) & 0xff, ipServer & 0xff);
|
(fServerIP >> 24) & 0xff, (fServerIP >> 16) & 0xff, (fServerIP >> 8) & 0xff, fServerIP & 0xff);
|
||||||
|
|
||||||
SetIPAddress(ipClient);
|
SetIPAddress(fClientIP);
|
||||||
|
|
||||||
undi_open.OpenFlag = 0;
|
undi_open.OpenFlag = 0;
|
||||||
undi_open.PktFilter = FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS;
|
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
|
status_t
|
||||||
platform_net_stack_init()
|
platform_net_stack_init()
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define _PXE_NETWORK_H
|
#define _PXE_NETWORK_H
|
||||||
/*
|
/*
|
||||||
* Copyright 2006, Marcus Overhagen <marcus@overhagen.de. All rights reserved.
|
* Copyright 2006, Marcus Overhagen <marcus@overhagen.de. All rights reserved.
|
||||||
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
* Copyright 2005-2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -12,7 +12,30 @@
|
|||||||
|
|
||||||
struct PXE_STRUCT;
|
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:
|
public:
|
||||||
UNDI();
|
UNDI();
|
||||||
@ -20,7 +43,6 @@ public:
|
|||||||
|
|
||||||
status_t Init();
|
status_t Init();
|
||||||
|
|
||||||
ip_addr_t ServerIPAddress() const;
|
|
||||||
virtual mac_addr_t MACAddress() const;
|
virtual mac_addr_t MACAddress() const;
|
||||||
|
|
||||||
virtual void * AllocateSendReceiveBuffer(size_t size);
|
virtual void * AllocateSendReceiveBuffer(size_t size);
|
||||||
@ -30,9 +52,25 @@ public:
|
|||||||
virtual ssize_t Receive(void *buffer, size_t size);
|
virtual ssize_t Receive(void *buffer, size_t size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mac_addr_t fMACAddress;
|
|
||||||
bool fRxFinished;
|
bool fRxFinished;
|
||||||
PXE_STRUCT * fPxeData;
|
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
|
#endif
|
||||||
|
@ -1,30 +1,198 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||||
* Copyright 2006, Marcus Overhagen, marcus@overhagen.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.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/** This file contains the stage 2 boot loader (haiku-pxe-loader) for the PXE
|
// system state according to PXE specification:
|
||||||
* (preboot execution environment) API
|
// CS:IP 0000:7C00
|
||||||
* It gets loaded by the PXE stage1 loader to 0x10000, and it starts execution
|
// ES:BX address of the PXENV+ structure
|
||||||
* in 32 bit protected mode. Entry point is at offset 0.
|
// SS:[SP+4] address of the !PXE structure.
|
||||||
* This code will directly call the entry function of the embedded ELF part
|
// SS:SP at least 1.5KB of free stack
|
||||||
* of the loader.
|
|
||||||
*/
|
// 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
|
#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
|
.text
|
||||||
.code32
|
.code16
|
||||||
.org 0
|
|
||||||
|
|
||||||
pxe_start:
|
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
|
|
||||||
|
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -14,6 +14,11 @@ extern "C" uint16 call_pxe_bios(void *pxe, uint16 opcode, void *param);
|
|||||||
#define UNDI_ISR 0x0014
|
#define UNDI_ISR 0x0014
|
||||||
#define UNDI_GET_STATE 0x0015
|
#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 GET_CACHED_INFO 0x0071
|
||||||
|
|
||||||
#define SEG(ptr) ((uint16)(((uint32)(ptr)) >> 4))
|
#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_RECEIVE 3
|
||||||
#define PXENV_UNDI_ISR_OUT_BUSY 4
|
#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();
|
PXE_STRUCT * pxe_undi_find_data();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,6 +12,7 @@ KernelMergeObject kernel_fs.o :
|
|||||||
fd.c
|
fd.c
|
||||||
vfs.cpp
|
vfs.cpp
|
||||||
vfs_boot.cpp
|
vfs_boot.cpp
|
||||||
|
vfs_net_boot.cpp
|
||||||
vfs_select.cpp
|
vfs_select.cpp
|
||||||
node_monitor.cpp
|
node_monitor.cpp
|
||||||
IOScheduler.cpp
|
IOScheduler.cpp
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
#include <vm.h>
|
#include <vm.h>
|
||||||
#include <arch/cpu.h>
|
#include <arch/cpu.h>
|
||||||
|
#include <boot/kernel_args.h>
|
||||||
|
|
||||||
#include <devfs.h>
|
#include <devfs.h>
|
||||||
|
|
||||||
@ -181,9 +182,12 @@ load_driver(driver_entry *driver)
|
|||||||
status_t status;
|
status_t status;
|
||||||
|
|
||||||
// load the module
|
// load the module
|
||||||
image_id image = load_kernel_add_on(driver->path);
|
image_id image = driver->image;
|
||||||
if (image < B_OK)
|
if (image < 0) {
|
||||||
return image;
|
image = load_kernel_add_on(driver->path);
|
||||||
|
if (image < 0)
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
// for prettier debug output
|
// for prettier debug output
|
||||||
const char *name = strrchr(driver->path, '/');
|
const char *name = strrchr(driver->path, '/');
|
||||||
@ -292,13 +296,71 @@ error2:
|
|||||||
}
|
}
|
||||||
|
|
||||||
error1:
|
error1:
|
||||||
unload_kernel_add_on(image);
|
if (driver->image < 0) {
|
||||||
driver->image = status;
|
unload_kernel_add_on(image);
|
||||||
|
driver->image = status;
|
||||||
|
}
|
||||||
|
|
||||||
return 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
|
/*! This is no longer part of the public kernel API, so we just export the
|
||||||
symbol
|
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
|
// all went fine, let's initialize the node
|
||||||
node->stream.type = S_IFCHR | 0644;
|
node->stream.type = S_IFCHR | 0644;
|
||||||
|
|
||||||
if (deviceNode != NULL)
|
if (deviceNode == NULL) {
|
||||||
node->stream.u.dev.info = info;
|
info = create_new_driver_info(ops, apiVersion);
|
||||||
else
|
if (!info)
|
||||||
node->stream.u.dev.info = create_new_driver_info(ops, apiVersion);
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->stream.u.dev.info = info;
|
||||||
node->stream.u.dev.node = deviceNode;
|
node->stream.u.dev.node = deviceNode;
|
||||||
node->stream.u.dev.driver = driver;
|
node->stream.u.dev.driver = driver;
|
||||||
node->stream.u.dev.ops = ops;
|
node->stream.u.dev.ops = ops;
|
||||||
@ -1804,7 +1868,8 @@ devfs_can_page(fs_volume _fs, fs_vnode _vnode, fs_cookie cookie)
|
|||||||
|| cookie == NULL)
|
|| cookie == NULL)
|
||||||
return false;
|
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));
|
//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)
|
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)
|
|| cookie == NULL)
|
||||||
return B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
|
|
||||||
@ -1827,8 +1893,37 @@ devfs_read_pages(fs_volume _fs, fs_vnode _vnode, fs_cookie _cookie, off_t pos,
|
|||||||
*_numBytes);
|
*_numBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vnode->stream.u.dev.info->read_pages(cookie->device_cookie, pos,
|
if (vnode->stream.u.dev.info->read_pages) {
|
||||||
vecs, count, _numBytes);
|
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
|
// #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
|
extern "C" status_t
|
||||||
devfs_add_driver(const char *path)
|
devfs_add_driver(const char *path)
|
||||||
{
|
{
|
||||||
// see if we already know this driver
|
return add_driver(path, -1);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de.
|
||||||
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de.
|
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
@ -6,6 +7,9 @@
|
|||||||
* Distributed under the terms of the NewOS License.
|
* Distributed under the terms of the NewOS License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "vfs_boot.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
#include <fs_info.h>
|
#include <fs_info.h>
|
||||||
@ -20,9 +24,10 @@
|
|||||||
#include <KPath.h>
|
#include <KPath.h>
|
||||||
#include <syscalls.h>
|
#include <syscalls.h>
|
||||||
#include <boot/kernel_args.h>
|
#include <boot/kernel_args.h>
|
||||||
|
#include <util/KMessage.h>
|
||||||
#include <util/Stack.h>
|
#include <util/Stack.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include "vfs_net_boot.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_VFS
|
//#define TRACE_VFS
|
||||||
@ -53,8 +58,7 @@ dev_t gBootDevice = -1;
|
|||||||
|
|
||||||
/** No image was chosen - prefer disks with names like "Haiku", or "System"
|
/** No image was chosen - prefer disks with names like "Haiku", or "System"
|
||||||
*/
|
*/
|
||||||
|
int
|
||||||
static int
|
|
||||||
compare_image_boot(const void *_a, const void *_b)
|
compare_image_boot(const void *_a, const void *_b)
|
||||||
{
|
{
|
||||||
KPartition *a = *(KPartition **)_a;
|
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
|
* is no CD, fall back to the standard mechanism (as implemented by
|
||||||
* compare_image_boot().
|
* compare_image_boot().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compare_cd_boot(const void *_a, const void *_b)
|
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
|
* Note, this must use the same method as the one used in
|
||||||
* boot/platform/bios_ia32/devices.cpp (or similar solutions).
|
* boot/platform/bios_ia32/devices.cpp (or similar solutions).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static uint32
|
static uint32
|
||||||
compute_check_sum(KDiskDevice *device, off_t offset)
|
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
|
// #pragma mark - BootMethod
|
||||||
* boot loader.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 PCI_BUS:
|
||||||
case LEGACY_BUS:
|
case LEGACY_BUS:
|
||||||
// TODO: implement this! (and then enable this feature in the boot loader)
|
// 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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (disk.device_type) {
|
switch (disk->device_type) {
|
||||||
case UNKNOWN_DEVICE:
|
case UNKNOWN_DEVICE:
|
||||||
// test if the size of the device matches
|
// test if the size of the device matches
|
||||||
// (the BIOS might have given us the wrong value here, though)
|
// (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;
|
return false;
|
||||||
|
|
||||||
// check if the check sums match, too
|
// check if the check sums match, too
|
||||||
for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
|
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;
|
continue;
|
||||||
|
|
||||||
if (compute_check_sum(device, disk.device.unknown.check_sums[i].offset)
|
if (compute_check_sum(device,
|
||||||
!= disk.device.unknown.check_sums[i].sum)
|
disk->device.unknown.check_sums[i].offset)
|
||||||
|
!= disk->device.unknown.check_sums[i].sum) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -184,7 +228,6 @@ is_boot_device(kernel_args *args, KDiskDevice *device, bool strict)
|
|||||||
case USB_DEVICE:
|
case USB_DEVICE:
|
||||||
case FIREWIRE_DEVICE:
|
case FIREWIRE_DEVICE:
|
||||||
case FIBRE_DEVICE:
|
case FIBRE_DEVICE:
|
||||||
case NETWORK_DEVICE:
|
|
||||||
// TODO: implement me!
|
// TODO: implement me!
|
||||||
break;
|
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.
|
/** Make the boot partition (and probably others) available.
|
||||||
* The partitions that are a boot candidate a put into the /a partitions
|
* 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
|
* 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.
|
* entry in this stack; if not, the most likely is put up first.
|
||||||
* The boot code should then just try them one by one.
|
* The boot code should then just try them one by one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
get_boot_partitions(kernel_args *args, PartitionStack &partitions)
|
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::CreateDefault();
|
||||||
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
|
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
|
||||||
|
|
||||||
status_t status = manager->InitialDeviceScan();
|
status = manager->InitialDeviceScan();
|
||||||
if (status != B_OK) {
|
if (status != B_OK) {
|
||||||
dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n", strerror(status));
|
dprintf("KDiskDeviceManager::InitialDeviceScan() failed: %s\n", strerror(status));
|
||||||
return 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 {
|
struct BootPartitionVisitor : KPartitionVisitor {
|
||||||
BootPartitionVisitor(kernel_args &args, PartitionStack &stack)
|
BootPartitionVisitor(BootMethod* bootMethod, PartitionStack &stack)
|
||||||
: fArgs(args), fPartitions(stack) {}
|
: fPartitions(stack),
|
||||||
|
fBootMethod(bootMethod)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool VisitPre(KPartition *partition)
|
virtual bool VisitPre(KPartition *partition)
|
||||||
{
|
{
|
||||||
if (!partition->ContainsFileSystem())
|
if (!partition->ContainsFileSystem())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!fArgs.boot_disk.booted_from_image) {
|
bool foundForSure = false;
|
||||||
// the simple case: we can just boot from the selected boot device
|
if (fBootMethod->IsBootPartition(partition, foundForSure))
|
||||||
if (partition->Offset() == fArgs.boot_disk.partition_offset) {
|
fPartitions.Push(partition);
|
||||||
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;
|
|
||||||
|
|
||||||
if (partition->ContentType() != NULL
|
// if found for sure, we can terminate the search
|
||||||
&& !strcmp(partition->ContentType(), "Be File System"))
|
return foundForSure;
|
||||||
fPartitions.Push(partition);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
kernel_args &fArgs;
|
|
||||||
PartitionStack &fPartitions;
|
PartitionStack &fPartitions;
|
||||||
} visitor(*args, partitions);
|
BootMethod* fBootMethod;
|
||||||
|
} visitor(bootMethod, partitions);
|
||||||
|
|
||||||
bool strict = true;
|
bool strict = true;
|
||||||
|
|
||||||
@ -257,7 +399,7 @@ get_boot_partitions(kernel_args *args, PartitionStack &partitions)
|
|||||||
KDiskDevice *device;
|
KDiskDevice *device;
|
||||||
int32 cookie = 0;
|
int32 cookie = 0;
|
||||||
while ((device = manager->NextDevice(&cookie)) != NULL) {
|
while ((device = manager->NextDevice(&cookie)) != NULL) {
|
||||||
if (!is_boot_device(args, device, strict))
|
if (!bootMethod->IsBootDevice(device, strict))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (device->VisitEachDescendant(&visitor) != NULL)
|
if (device->VisitEachDescendant(&visitor) != NULL)
|
||||||
@ -271,11 +413,10 @@ get_boot_partitions(kernel_args *args, PartitionStack &partitions)
|
|||||||
strict = false;
|
strict = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args->boot_disk.user_selected) {
|
// sort partition list (e.g.. when booting from CD, CDs should come first in
|
||||||
// sort partition list (ie. when booting from CD, CDs should come first in the list)
|
// the list)
|
||||||
qsort(partitions.Array(), partitions.CountItems(), sizeof(KPartition *),
|
if (!args->boot_volume.GetBool(BOOT_VOLUME_USER_SELECTED, false))
|
||||||
args->boot_disk.cd ? compare_cd_boot : compare_image_boot);
|
bootMethod->SortPartitions(partitions.Array(), partitions.CountItems());
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
30
src/system/kernel/fs/vfs_boot.h
Normal file
30
src/system/kernel/fs/vfs_boot.h
Normal file
@ -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 <disk_device_manager/KDiskDevice.h>
|
||||||
|
#include <util/KMessage.h>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
397
src/system/kernel/fs/vfs_net_boot.cpp
Normal file
397
src/system/kernel/fs/vfs_net_boot.cpp
Normal file
@ -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 <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#include <net/if_types.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
|
||||||
|
#include <DiskDeviceTypes.h>
|
||||||
|
|
||||||
|
#include <disk_device_manager/KDiskDevice.h>
|
||||||
|
|
||||||
|
#include <socket_interface.h>
|
||||||
|
|
||||||
|
#include <KPath.h>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
28
src/system/kernel/fs/vfs_net_boot.h
Normal file
28
src/system/kernel/fs/vfs_net_boot.h
Normal file
@ -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
|
@ -17,6 +17,7 @@
|
|||||||
#include <cpu.h>
|
#include <cpu.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
|
#include <fs/devfs.h>
|
||||||
#include <int.h>
|
#include <int.h>
|
||||||
#include <kdevice_manager.h>
|
#include <kdevice_manager.h>
|
||||||
#include <kdriver_settings.h>
|
#include <kdriver_settings.h>
|
||||||
@ -243,6 +244,9 @@ main2(void *unused)
|
|||||||
TRACE("Init Device Manager\n");
|
TRACE("Init Device Manager\n");
|
||||||
device_manager_init(&sKernelArgs);
|
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,
|
// ToDo: device manager starts here, bus_init()/dev_init() won't be necessary anymore,
|
||||||
// but instead, the hardware and drivers are rescanned then.
|
// but instead, the hardware and drivers are rescanned then.
|
||||||
|
|
||||||
|
@ -909,6 +909,8 @@ register_preloaded_module_image(struct preloaded_image *image)
|
|||||||
|
|
||||||
TRACE(("register_preloaded_module_image(image = \"%s\")\n", image->name));
|
TRACE(("register_preloaded_module_image(image = \"%s\")\n", image->name));
|
||||||
|
|
||||||
|
image->is_module = false;
|
||||||
|
|
||||||
if (image->id < 0)
|
if (image->id < 0)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
@ -922,6 +924,8 @@ register_preloaded_module_image(struct preloaded_image *image)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
image->is_module = true;
|
||||||
|
|
||||||
moduleImage->dependencies = NULL;
|
moduleImage->dependencies = NULL;
|
||||||
get_image_symbol(image->id, "module_dependencies", B_SYMBOL_TYPE_DATA,
|
get_image_symbol(image->id, "module_dependencies", B_SYMBOL_TYPE_DATA,
|
||||||
(void **)&moduleImage->dependencies);
|
(void **)&moduleImage->dependencies);
|
||||||
@ -966,8 +970,10 @@ register_preloaded_module_image(struct preloaded_image *image)
|
|||||||
error:
|
error:
|
||||||
free(moduleImage);
|
free(moduleImage);
|
||||||
|
|
||||||
// we don't need this image anymore
|
// We don't need this image anymore. We keep it, if it doesn't look like
|
||||||
unload_kernel_add_on(image->id);
|
// a module at all. It might be an old-style driver.
|
||||||
|
if (image->is_module)
|
||||||
|
unload_kernel_add_on(image->id);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user