From 3dd0a6fb4102ab72b10a45a95682f6b7f8773418 Mon Sep 17 00:00:00 2001 From: Qwinci <32550582+Qwinci@users.noreply.github.com> Date: Tue, 17 Jan 2023 18:40:43 +0200 Subject: [PATCH] limine: Add UEFI PXE support based on @qookei's original patch with small changes. --- README.md | 7 ++- common/drivers/disk.s2.c | 35 ++++++++++++- common/fs/file.s2.c | 4 +- common/lib/config.c | 16 ------ common/lib/config.h | 1 - common/lib/part.h | 5 ++ common/lib/uri.c | 8 +-- common/pxe/pxe.h | 8 +++ common/pxe/pxe.s2.c | 22 ++++++-- common/pxe/tftp.h | 7 +-- common/pxe/tftp.s2.c | 110 ++++++++++++++++++++++++++++++++++++--- 11 files changed, 180 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index bae80cfa..0c0223be 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ make install # (or gmake where applicable) ## How to use ### UEFI -The `BOOT{IA32,X64,AA64}.EFI` files are vaild EFI applications that can be simply copied to +The `BOOT{IA32,X64,AA64}.EFI` files are valid EFI applications that can be simply copied to the `/EFI/BOOT` directory of a FAT formatted EFI system partition. These files can be installed there and coexist with a BIOS installation of Limine (see below) so that the disk will be bootable on both BIOS and UEFI systems. @@ -211,6 +211,11 @@ server or your existing DHCP server and a proxy DHCP server such as dnsmasq. `limine.cfg` and `limine.sys` are expected to be on the server used for boot. +### UEFI/PXE boot +The `BOOT{IA32,X64,AA64}.EFI` files are compatible with UEFI PXE. +The steps needed to boot Limine are the same as with BIOS PXE, +except that you don't need `limine.sys` in the server. + ### Configuration The `limine.cfg` file contains Limine's configuration. diff --git a/common/drivers/disk.s2.c b/common/drivers/disk.s2.c index ef3d0506..ed014db6 100644 --- a/common/drivers/disk.s2.c +++ b/common/drivers/disk.s2.c @@ -14,6 +14,7 @@ #include #include #include +#include #define DEFAULT_FASTEST_XFER_SIZE 64 #define MAX_FASTEST_XFER_SIZE 512 @@ -313,6 +314,38 @@ int disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t c } } +static struct volume *pxe_from_efi_handle(EFI_HANDLE efi_handle) { + static struct volume *vol = NULL; + + // There's only one PXE volume + if (vol) { + return vol; + } + + EFI_STATUS status; + + EFI_GUID pxe_base_code_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID; + EFI_PXE_BASE_CODE *pxe_base_code = NULL; + + status = gBS->HandleProtocol(efi_handle, &pxe_base_code_guid, (void **)&pxe_base_code); + if (status) { + return NULL; + } + + if (!pxe_base_code->Mode->DhcpDiscoverValid) { + print("PXE somehow didn't use DHCP?\n"); + return NULL; + } + + if (pxe_base_code->Mode->UsingIpv6) { + print("Sorry, unsupported: PXE IPv6\n"); + return NULL; + } + + vol = pxe_bind_volume(efi_handle, pxe_base_code); + return vol; +} + static alignas(4096) uint8_t unique_sector_pool[4096]; struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle) { @@ -323,7 +356,7 @@ struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle) { status = gBS->HandleProtocol(efi_handle, &block_io_guid, (void **)&block_io); if (status) { - return NULL; + return pxe_from_efi_handle(efi_handle); } block_io->Media->WriteCaching = false; diff --git a/common/fs/file.s2.c b/common/fs/file.s2.c index df413f32..f109f605 100644 --- a/common/fs/file.s2.c +++ b/common/fs/file.s2.c @@ -49,14 +49,12 @@ struct file_handle *fopen(struct volume *part, const char *filename) { struct file_handle *ret; -#if defined (BIOS) if (part->pxe) { - if ((ret = tftp_open(0, 69, filename)) == NULL) { + if ((ret = tftp_open(part, "", filename)) == NULL) { return NULL; } goto success; } -#endif if ((ret = ext2_open(part, filename)) != NULL) { goto success; diff --git a/common/lib/config.c b/common/lib/config.c index ac3ad2a5..a53320ff 100644 --- a/common/lib/config.c +++ b/common/lib/config.c @@ -43,22 +43,6 @@ int init_config_disk(struct volume *part) { return init_config(config_size); } -#if defined (BIOS) -int init_config_pxe(void) { - struct file_handle *f; - if ((f = tftp_open(0, 69, "limine.cfg")) == NULL) { - return -1; - } - - size_t config_size = f->size + 2; - config_addr = ext_mem_alloc(config_size); - - fread(f, config_addr, 0, f->size); - - return init_config(config_size); -} -#endif - #define NOT_CHILD (-1) #define DIRECT_CHILD 0 #define INDIRECT_CHILD 1 diff --git a/common/lib/config.h b/common/lib/config.h index 6ae5794a..5edda9b0 100644 --- a/common/lib/config.h +++ b/common/lib/config.h @@ -26,7 +26,6 @@ struct conf_tuple { extern struct menu_entry *menu_tree; int init_config_disk(struct volume *part); -int init_config_pxe(void); int init_config(size_t config_size); char *config_get_value(const char *config, size_t index, const char *key); diff --git a/common/lib/part.h b/common/lib/part.h index 5f18a36c..37c08954 100644 --- a/common/lib/part.h +++ b/common/lib/part.h @@ -17,9 +17,14 @@ struct volume { #if defined (UEFI) EFI_HANDLE efi_handle; + + // Block storage EFI_HANDLE efi_part_handle; EFI_BLOCK_IO *block_io; + // PXE + EFI_PXE_BASE_CODE_PROTOCOL *pxe_base_code; + bool unique_sector_valid; uint64_t unique_sector; uint8_t unique_sector_b2b[BLAKE2B_OUT_BYTES]; diff --git a/common/lib/uri.c b/common/lib/uri.c index 9771c2a3..734bb897 100644 --- a/common/lib/uri.c +++ b/common/lib/uri.c @@ -164,7 +164,6 @@ static struct file_handle *uri_fslabel_dispatch(char *fslabel, char *path) { return fopen(volume, path); } -#if defined (BIOS) static struct file_handle *uri_tftp_dispatch(char *root, char *path) { uint32_t ip; if (!strcmp(root, "")) { @@ -176,19 +175,16 @@ static struct file_handle *uri_tftp_dispatch(char *root, char *path) { } struct file_handle *ret; - if ((ret = tftp_open(ip, 69, path)) == NULL) { + if ((ret = tftp_open(boot_volume, root, path)) == NULL) { return NULL; } return ret; } -#endif static struct file_handle *uri_boot_dispatch(char *s_part, char *path) { -#if defined (BIOS) if (boot_volume->pxe) return uri_tftp_dispatch(s_part, path); -#endif int partition; @@ -242,10 +238,8 @@ struct file_handle *uri_open(char *uri) { ret = uri_guid_dispatch(root, path); } else if (!strcmp(resource, "fslabel")) { ret = uri_fslabel_dispatch(root, path); -#if defined (BIOS) } else if (!strcmp(resource, "tftp")) { ret = uri_tftp_dispatch(root, path); -#endif } else { panic(true, "Resource `%s` not valid.", resource); } diff --git a/common/pxe/pxe.h b/common/pxe/pxe.h index db148d2c..bbb9d087 100644 --- a/common/pxe/pxe.h +++ b/common/pxe/pxe.h @@ -4,6 +4,8 @@ #include #include +#if defined (BIOS) + struct volume *pxe_bind_volume(void); void pxe_init(void); int pxe_call(uint16_t opcode, uint16_t buf_seg, uint16_t buf_off); @@ -94,4 +96,10 @@ struct pxenv_get_cached_info { uint16_t buffer_limit; } __attribute__((packed)); +#elif defined (UEFI) + +struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code); + +#endif + #endif diff --git a/common/pxe/pxe.s2.c b/common/pxe/pxe.s2.c index 363ade0c..43762ad0 100644 --- a/common/pxe/pxe.s2.c +++ b/common/pxe/pxe.s2.c @@ -1,11 +1,15 @@ -#if defined (BIOS) - #include -#include #include #include #include #include +#if defined (BIOS) +#include +#elif defined (UEFI) +#include +#endif + +#if defined (BIOS) void set_pxe_fp(uint32_t fp); @@ -53,4 +57,16 @@ void pxe_init(void) { printv("pxe: Successfully initialized\n"); } +#elif defined (UEFI) + +struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code) { + struct volume *volume = ext_mem_alloc(sizeof(struct volume)); + + volume->efi_handle = efi_handle; + volume->pxe_base_code = pxe_base_code; + volume->pxe = true; + + return volume; +} + #endif diff --git a/common/pxe/tftp.h b/common/pxe/tftp.h index 5fae5f0c..99c58612 100644 --- a/common/pxe/tftp.h +++ b/common/pxe/tftp.h @@ -5,6 +5,8 @@ #include #include +#if defined (BIOS) + #define UNDI_GET_INFORMATION 0xC #define TFTP_OPEN 0x0020 @@ -37,9 +39,8 @@ struct pxenv_get_file_size { #define TFTP_CLOSE 0x21 -//server_ip and server_port can be 0 for default -struct file_handle *tftp_open(uint32_t server_ip, uint16_t server_port, const char *name); +#endif -uint32_t get_boot_server_info(void); +struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name); #endif diff --git a/common/pxe/tftp.s2.c b/common/pxe/tftp.s2.c index 991aa9d5..dc6c14d0 100644 --- a/common/pxe/tftp.s2.c +++ b/common/pxe/tftp.s2.c @@ -1,14 +1,18 @@ -#if defined (BIOS) - #include #include -#include +#if defined (BIOS) +# include +#elif defined (UEFI) +# include +#endif #include #include #include #include -uint32_t get_boot_server_info(void) { +#if defined (BIOS) + +static uint32_t get_boot_server_info(void) { struct pxenv_get_cached_info cachedinfo = { 0 }; cachedinfo.packet_type = 2; pxe_call(PXENV_GET_CACHED_INFO, ((uint16_t)rm_seg(&cachedinfo)), (uint16_t)rm_off(&cachedinfo)); @@ -16,12 +20,26 @@ uint32_t get_boot_server_info(void) { return ph->sip; } -struct file_handle *tftp_open(uint32_t server_ip, uint16_t server_port, const char *name) { +static uint32_t parse_ip_addr(const char *server_addr) { + uint32_t out; + + if (!server_addr || !strlen(server_addr)) { + return get_boot_server_info(); + } + + if (inet_pton(server_addr, &out)) { + panic(true, "tftp: Invalid IPv4 address: \"%s\"", server_addr); + } + + return out; +} + +struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name) { + uint32_t server_ip = parse_ip_addr(server_addr); + const uint16_t server_port = 69; // This couldn't be changed previously either int ret = 0; - if (!server_ip) { - server_ip = get_boot_server_info(); - } + (void)part; struct PXENV_UNDI_GET_INFORMATION undi_info = { 0 }; ret = pxe_call(UNDI_GET_INFORMATION, ((uint16_t)rm_seg(&undi_info)), (uint16_t)rm_off(&undi_info)); @@ -106,4 +124,80 @@ struct file_handle *tftp_open(uint32_t server_ip, uint16_t server_port, const ch return handle; } +#elif defined (UEFI) + +static EFI_IP_ADDRESS *parse_ip_addr(struct volume *part, const char *server_addr) { + static EFI_IP_ADDRESS out; + + if (!server_addr || !strlen(server_addr)) { + EFI_PXE_BASE_CODE_PACKET* packet; + if (part->pxe_base_code->Mode->PxeReplyReceived) packet = &part->pxe_base_code->Mode->PxeReply; + else if (part->pxe_base_code->Mode->ProxyOfferReceived) packet = &part->pxe_base_code->Mode->ProxyOffer; + else packet = &part->pxe_base_code->Mode->DhcpAck; + memcpy(out.Addr, packet->Dhcpv4.BootpSiAddr, 4); + } else { + if (inet_pton(server_addr, &out.Addr)) { + panic(true, "tftp: Invalid IPv4 address: \"%s\"", server_addr); + } + } + + return &out; +} + +struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name) { + if (!part->pxe_base_code) { + return NULL; + } + + EFI_IP_ADDRESS *ip = parse_ip_addr(part, server_addr); + + uint64_t file_size; + EFI_STATUS status; + + status = part->pxe_base_code->Mtftp( + part->pxe_base_code, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + false, + &file_size, + NULL, + ip, + (uint8_t *)name, + NULL, + false); + + if (status) { + return NULL; + } + + struct file_handle *handle = ext_mem_alloc(sizeof(struct file_handle)); + + handle->size = file_size; + handle->is_memfile = true; + + handle->pxe = true; + handle->pxe_ip = *(uint32_t *)&ip; + handle->pxe_port = 69; + + handle->fd = ext_mem_alloc(handle->size); + + status = part->pxe_base_code->Mtftp( + part->pxe_base_code, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + handle->fd, + false, + &file_size, + NULL, + ip, + (uint8_t *)name, + NULL, + false); + + if (status) { + return NULL; + } + + return handle; +} + #endif