diff --git a/headers/private/kernel/platform/efi/protocol/device-path.h b/headers/private/kernel/platform/efi/protocol/device-path.h index e31215e024..e91bbf6ec3 100644 --- a/headers/private/kernel/platform/efi/protocol/device-path.h +++ b/headers/private/kernel/platform/efi/protocol/device-path.h @@ -13,7 +13,7 @@ extern efi_guid DevicePathProtocol; typedef struct efi_device_path_protocol { uint8_t Type; uint8_t SubType; - uint8_t Length[2]; + uint16_t Length; } efi_device_path_protocol; #define DEVICE_PATH_HARDWARE 0x01 diff --git a/headers/private/kernel/platform/efi/protocol/loaded-image.h b/headers/private/kernel/platform/efi/protocol/loaded-image.h index 800d269107..d7091c8c5c 100644 --- a/headers/private/kernel/platform/efi/protocol/loaded-image.h +++ b/headers/private/kernel/platform/efi/protocol/loaded-image.h @@ -12,6 +12,9 @@ {0x5b1b31a1, 0x9562, 0x11d2, {0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b}} extern efi_guid LoadedImageProtocol; +#define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID \ + {0xbc62157e, 0x3e33, 0x4fec, {0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf}} + #define EFI_LOADED_IMAGE_PROTOCOL_REVISION 0x1000 typedef struct { diff --git a/src/system/boot/loader/Jamfile b/src/system/boot/loader/Jamfile index 64b2fe3397..b0fed7f700 100644 --- a/src/system/boot/loader/Jamfile +++ b/src/system/boot/loader/Jamfile @@ -94,8 +94,8 @@ for platform in [ MultiBootSubDirSetup ] { } } - local kernelC++Header = [ FDirName $(HAIKU_TOP) headers private kernel util - kernel_cpp.h ] ; + local kernelC++Header = [ FDirName $(HAIKU_TOP) headers private + kernel util kernel_cpp.h ] ; SubDirC++Flags -fno-rtti -include $(kernelC++Header) ; } @@ -123,6 +123,7 @@ for platform in [ MultiBootSubDirSetup ] { driver_settings.cpp # utils + convertutf.cpp kernel_cpp.cpp KMessage.cpp list.cpp @@ -153,7 +154,8 @@ for platform in [ MultiBootSubDirSetup ] { ; # Tell Jam where to find the utility sources - SEARCH on [ FGristFiles kernel_cpp.cpp list.cpp ring_buffer.cpp StringHash.cpp ] + SEARCH on [ FGristFiles convertutf.cpp kernel_cpp.cpp list.cpp + ring_buffer.cpp StringHash.cpp ] = [ FDirName $(HAIKU_TOP) src system kernel util ] ; SEARCH on [ FGristFiles KMessage.cpp ] @@ -166,19 +168,25 @@ for platform in [ MultiBootSubDirSetup ] { = [ FDirName $(HAIKU_TOP) src system libroot os ] ; SEARCH on [ FGristFiles amiga_rdb.cpp ] - = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems amiga ] ; + = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems + amiga ] ; SEARCH on [ FGristFiles apple.cpp ] - = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems apple ] ; + = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems + apple ] ; SEARCH on [ FGristFiles gpt.cpp Header.cpp crc32.cpp utility.cpp ] - = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems gpt ] ; + = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems + gpt ] ; - SEARCH on [ FGristFiles intel.cpp PartitionMap.cpp PartitionMapParser.cpp ] - = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems intel ] ; + SEARCH on [ FGristFiles intel.cpp PartitionMap.cpp + PartitionMapParser.cpp ] + = [ FDirName $(HAIKU_TOP) src add-ons kernel partitioning_systems + intel ] ; SEARCH on [ FGristFiles stage2_crt0.S ] - = [ FDirName $(HAIKU_TOP) src system boot arch $(TARGET_KERNEL_ARCH_DIR) ] ; + = [ FDirName $(HAIKU_TOP) src system boot arch + $(TARGET_KERNEL_ARCH_DIR) ] ; SEARCH on [ FGristFiles DataIO.cpp Referenceable.cpp ] = [ FDirName $(HAIKU_TOP) src kits support ] ; diff --git a/src/system/boot/platform/efi/Jamfile b/src/system/boot/platform/efi/Jamfile index 909473c340..98c63c9855 100644 --- a/src/system/boot/platform/efi/Jamfile +++ b/src/system/boot/platform/efi/Jamfile @@ -32,6 +32,7 @@ local platform_src = video.cpp debug.cpp mmu.cpp + network.cpp heap.cpp acpi.cpp timer.cpp diff --git a/src/system/boot/platform/efi/devices.cpp b/src/system/boot/platform/efi/devices.cpp index e52ef71280..88dff6398c 100644 --- a/src/system/boot/platform/efi/devices.cpp +++ b/src/system/boot/platform/efi/devices.cpp @@ -8,6 +8,9 @@ #include #include +#include +#include + #include "Header.h" #include "efi_platform.h" @@ -181,6 +184,20 @@ platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) efi_block_io_protocol *blockIo; size_t memSize = 0; + // If the bootloader was started from the network (net_stack_init will find + // the ip= parameter in the LoadOptions), add network booting as the first + // entry if available + status_t error = net_stack_init(); + if (error != B_OK) { + TRACE("Can't init network...\n"); + } else { + TRACE("Network is initialized! Search for remote disk...\n"); + RemoteDisk *remoteDisk = RemoteDisk::FindAnyRemoteDisk(); + if (remoteDisk != NULL) { + devicesList->Add(remoteDisk); + } + } + // Read to zero sized buffer to get memory needed for handles if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 0) != EFI_BUFFER_TOO_SMALL) @@ -225,6 +242,7 @@ platform_add_boot_device(struct stage2_args *args, NodeList *devicesList) panic("Can't allocate memory for block devices!"); devicesList->Insert(device); } + return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND; } diff --git a/src/system/boot/platform/efi/network.cpp b/src/system/boot/platform/efi/network.cpp new file mode 100644 index 0000000000..f7e566a64b --- /dev/null +++ b/src/system/boot/platform/efi/network.cpp @@ -0,0 +1,412 @@ +/* + * Copyright 2005, Ingo Weinhold . + * Copyright 2010, Andreas Faerber + * Copyright 2025, Adrien Destugues + * All rights reserved. Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include "efi_platform.h" +#include +#include + + +static efi_guid sNetworkProtocolGUID = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; +static efi_guid sLoadedImageProtocolGUID = EFI_LOADED_IMAGE_PROTOCOL_GUID; + +#if 0 +static efi_guid sDevicePathProtocolGUID = EFI_DEVICE_PATH_PROTOCOL_GUID; +static efi_guid sLoadedImageDevicePathProtocolGUID = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; +#endif + + +//#define TRACE_NETWORK +#ifdef TRACE_NETWORK +# define TRACE(x...) dprintf("efi/network: " x) +# define CALLED(x...) dprintf("efi/network: CALLED %s\n", __func__) +#else +# define TRACE(x...) ; +# define CALLED(x...) ; +#endif + +#define TRACE_ALWAYS(x...) dprintf("efi/network: " x) + + +class EFIEthernetInterface : public EthernetInterface { +public: + EFIEthernetInterface(); + virtual ~EFIEthernetInterface(); + + status_t Init(); + + virtual mac_addr_t MACAddress() const; + + virtual void * AllocateSendReceiveBuffer(size_t size); + virtual void FreeSendReceiveBuffer(void *buffer); + + virtual ssize_t Send(const void *buffer, size_t size); + virtual ssize_t Receive(void *buffer, size_t size); + +private: + status_t FindMACAddress(); + +private: + efi_simple_network_protocol* fNetwork; + mac_addr_t fMACAddress; +}; + + +#ifdef TRACE_NETWORK + +static void +hex_dump(const void *_data, int length) +{ + uint8 *data = (uint8*)_data; + for (int i = 0; i < length; i++) { + if (i % 4 == 0) { + if (i % 32 == 0) { + if (i != 0) + dprintf("\n"); + dprintf("%03x: ", i); + } else + dprintf(" "); + } + + dprintf("%02x", data[i]); + } + dprintf("\n"); +} + +#else // !TRACE_NETWORK + +#define hex_dump(data, length) + +#endif // !TRACE_NETWORK + + +// #pragma mark - + + +EFIEthernetInterface::EFIEthernetInterface() + : + EthernetInterface(), + fNetwork(NULL), + fMACAddress(kNoMACAddress) +{ +} + + +EFIEthernetInterface::~EFIEthernetInterface() +{ + if (fNetwork) { + fNetwork->Shutdown(fNetwork); + fNetwork->Stop(fNetwork); + } +} + + +status_t +EFIEthernetInterface::FindMACAddress() +{ + if (fNetwork == NULL) + return B_ERROR; + + memcpy(&fMACAddress, &fNetwork->Mode->CurrentAddress, + sizeof(fMACAddress)); + + if (fMACAddress == kNoMACAddress) { + memcpy(&fMACAddress, &fNetwork->Mode->PermanentAddress, + sizeof(fMACAddress)); + } + + if (fMACAddress == kNoMACAddress) + return B_ENTRY_NOT_FOUND; + + return B_OK; +} + + +status_t +EFIEthernetInterface::Init() +{ + CALLED(); + + efi_status status = kSystemTable->BootServices->LocateProtocol( + &sNetworkProtocolGUID, NULL, (void**)&fNetwork); + + if (status != EFI_SUCCESS || fNetwork == NULL) { + fNetwork = NULL; + TRACE_ALWAYS("EFI reports network unavaiable\n"); + return B_ERROR; + } + + TRACE_ALWAYS("EFI reports network avaiable\n"); + + // Attempt to locate IP address from command line arguments passed by + // the bootloader (for example, U-Boot bootargs variable) + // Recognize a subset of the ip= parameter as defined for Linux: + // https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt + efi_loaded_image_protocol* loadedImageProtocol; + status = kSystemTable->BootServices->LocateProtocol( + &sLoadedImageProtocolGUID, NULL, (void**)&loadedImageProtocol); + + if (status == EFI_SUCCESS) { + char buffer[256]; + utf16le_to_utf8((uint16*)loadedImageProtocol->LoadOptions, + loadedImageProtocol->LoadOptionsSize / 2, buffer, sizeof(buffer)); + TRACE("Load options: %s\n", buffer); + + char* base = buffer; + char* ptr = base; + while (*ptr != '\0') { + if (*ptr == '=') { + TRACE("Key at %ld: %s\n", ptr - base, base); + // Found a key-value separator, see if it's a known option + if (ptr - base != 2) + continue; + + if (strncmp(base, "ip", 2) == 0) { + TRACE("Found ip configuration in command line\n"); + break; + } + } + + // At any space, reset the base pointer to the start of the next + // key + if (*ptr == ' ') { + base = ptr + 1; + } + ptr++; + } + + if (*ptr != '=') { + TRACE("No keys found in load options\n"); + return B_ERROR; + } + + // Point after the = sign + ptr++; + + // Try to parse next part of the string as an IP address + // TODO also recognize autoconfig parameters (such as 'ip=dhcp') + TRACE("Found IP address from load options: %s\n", ptr); + ip_addr_t address = ip_parse_address(ptr); + SetIPAddress(address); + } + +#if 0 + // TODO u-boot does not provide the IP address or the TFTP server URL in + // "loaded image" and "device path" protocols. That would allow to detect + // network booting without having to specify the ip in bootargs. Enable + // this if the information is available when network booting with other + // EFI implementations or if U-Boot grows support for it. + bool bootFromNetwork = false; + + // Try the basic "device path" protocol + efi_device_path_protocol* devicePathProtocol; + status = kSystemTable->BootServices->LocateProtocol( + &sDevicePathProtocolGUID, NULL, (void**)&devicePathProtocol); + + if (status == EFI_SUCCESS) { + for (;;) { + TRACE("Now scanning: Type %d SubType %d\n", devicePathProtocol->Type, devicePathProtocol->SubType); + if (devicePathProtocol->Type == DEVICE_PATH_END + && devicePathProtocol->SubType == DEVICE_PATH_ENTIRE_END) { + break; + } + + if (devicePathProtocol->Type == DEVICE_PATH_MESSAGING + && devicePathProtocol->SubType == DEVICE_PATH_MESSAGING_MAC) { + bootFromNetwork = true; + } + + if (devicePathProtocol->Type == DEVICE_PATH_MEDIA + && devicePathProtocol->SubType == MEDIA_FILEPATH_DP) { + TRACE("Found device path, length %d: %s\n", devicePathProtocol->Length, ((char*)devicePathProtocol) + sizeof(efi_device_path_protocol)); + } + + if (devicePathProtocol->Type == DEVICE_PATH_MESSAGING + && devicePathProtocol->SubType == DEVICE_PATH_MESSAGING_IPV4) { + bootFromNetwork = true; + ip_addr_t* address = (ip_addr_t*)(devicePathProtocol + 1); + SetIPAddress(*address); + return B_OK; + } + + devicePathProtocol = (efi_device_path_protocol*)(((uint8_t*)devicePathProtocol) + devicePathProtocol->Length); + } + } else { + TRACE("No device path protocol\n"); + } + + TRACE_ALWAYS("IP address not found in device path protocol, try in loaded image\n"); + + // Try the "loaded image device path protocol" which provides more specific + // information + status = kSystemTable->BootServices->LocateProtocol( + &sLoadedImageDevicePathProtocolGUID, NULL, (void**)&devicePathProtocol); + + if (status == EFI_SUCCESS) { + for (;;) { + TRACE("Now scanning: Type %d SubType %d\n", devicePathProtocol->Type, devicePathProtocol->SubType); + if (devicePathProtocol->Type == DEVICE_PATH_END + && devicePathProtocol->SubType == DEVICE_PATH_ENTIRE_END) { + break; + } + + if (devicePathProtocol->Type == DEVICE_PATH_MESSAGING + && devicePathProtocol->SubType == DEVICE_PATH_MESSAGING_MAC) { + bootFromNetwork = true; + } + + if (devicePathProtocol->Type == DEVICE_PATH_MEDIA + && devicePathProtocol->SubType == MEDIA_FILEPATH_DP) { + TRACE("Found device path, length %d: %s\n", devicePathProtocol->Length, ((char*)devicePathProtocol) + sizeof(efi_device_path_protocol)); + } + + if (devicePathProtocol->Type == DEVICE_PATH_MESSAGING + && devicePathProtocol->SubType == DEVICE_PATH_MESSAGING_IPV4) { + bootFromNetwork = true; + ip_addr_t* address = (ip_addr_t*)(devicePathProtocol + 1); + SetIPAddress(*address); + return B_OK; + } + + devicePathProtocol = (efi_device_path_protocol*)(((uint8_t*)devicePathProtocol) + devicePathProtocol->Length); + } + } else { + TRACE("No loaded image device path protocol\n"); + } + + if (!bootFromNetwork) + return B_ENTRY_NOT_FOUND; +#endif + + status = fNetwork->Start(fNetwork); + + if (status != EFI_SUCCESS) { + TRACE_ALWAYS("error starting network\n"); + return B_ERROR; + } + + status = fNetwork->Initialize(fNetwork, 0, 0); + + if (status != EFI_SUCCESS) { + TRACE_ALWAYS("error initializing network\n"); + return B_ERROR; + } + + if (FindMACAddress() != B_OK) { + TRACE_ALWAYS("failed to get MAC address\n"); + return B_ERROR; + } + + return B_OK; +} + + +mac_addr_t +EFIEthernetInterface::MACAddress() const +{ + return fMACAddress; +} + + +void * +EFIEthernetInterface::AllocateSendReceiveBuffer(size_t size) +{ + return malloc(size); +} + + +void +EFIEthernetInterface::FreeSendReceiveBuffer(void *buffer) +{ + free(buffer); +} + + +ssize_t +EFIEthernetInterface::Send(const void *buffer, size_t size) +{ + TRACE("EFIEthernetInterface::Send(%p, %lu)\n", buffer, size); + + if (!buffer) + return B_BAD_VALUE; + + hex_dump(buffer, size); + + efi_status status = fNetwork->Transmit(fNetwork, 0, size, + const_cast(buffer), NULL, NULL, NULL); + + if (status != EFI_SUCCESS) { + TRACE("Send: Result is %lx\n", status); + return B_ERROR; + } + return size; +} + + +ssize_t +EFIEthernetInterface::Receive(void *buffer, size_t size) +{ + if (!buffer) + return B_BAD_VALUE; + + efi_status status = fNetwork->Receive(fNetwork, NULL, &size, buffer, + NULL, NULL, NULL); + + if (status == EFI_NOT_READY) + return B_WOULD_BLOCK; + + if (status != EFI_SUCCESS) { + TRACE("EFIEthernetInterface::Receive(%p, %lu): %ld\n", buffer, size, status); + return B_ERROR; + } + + hex_dump(buffer, size); + + return size; +} + + +// #pragma mark - + + +status_t +platform_net_stack_init() +{ + // create an EthernetInterface object for the device + EFIEthernetInterface *interface = new(nothrow) EFIEthernetInterface; + if (!interface) + return B_NO_MEMORY; + + status_t error = interface->Init(); + if (error != B_OK) { + delete interface; + return error; + } + + // add it to the net stack + error = NetStack::Default()->AddEthernetInterface(interface); + if (error != B_OK) { + delete interface; + return error; + } + + return B_OK; +}