[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:
Ingo Weinhold 2007-07-15 02:10:15 +00:00
parent 018cf36396
commit 9e8dc2a9bb
27 changed files with 1568 additions and 210 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 ] ;

View File

@ -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) {

View File

@ -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();
} }

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;
} }

View File

@ -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

View File

@ -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;
} }

View File

@ -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()
{ {

View File

@ -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

View File

@ -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
//-------------------------------------------------------------- //--------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View File

@ -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;
} }

View 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

View 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);
}

View 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

View File

@ -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.

View File

@ -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;
} }