:: commit 5efd4357dceec097bd681d157b58ecb94c37b78c

Qwinci <32550582+Qwinci@users.noreply.github.com> — 2023-01-17 16:40

parents: 2ef15c15e4

limine: Add UEFI PXE support based on @qookei's original patch with small changes.

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 <lib/rand.h>
 #include <mm/pmm.h>
 #include <sys/cpu.h>
+#include <pxe/pxe.h>
 
 #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 <stdint.h>
 #include <lib/part.h>
 
+#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 <lib/print.h>
-#include <lib/real.h>
 #include <pxe/pxe.h>
 #include <lib/libc.h>
 #include <lib/misc.h>
 #include <mm/pmm.h>
+#if defined (BIOS)
+#include <lib/real.h>
+#elif defined (UEFI)
+#include <efi.h>
+#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 <stddef.h>
 #include <fs/file.h>
 
+#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 <pxe/tftp.h>
 #include <pxe/pxe.h>
-#include <lib/real.h>
+#if defined (BIOS)
+#  include <lib/real.h>
+#elif defined (UEFI)
+#  include <efi.h>
+#endif
 #include <lib/print.h>
 #include <lib/libc.h>
 #include <mm/pmm.h>
 #include <lib/misc.h>
 
-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,13 +20,27 @@ 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) {
-    int ret = 0;
+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 (!server_ip) {
-        server_ip = 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;
+
+    (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));
     if (ret) {
@@ -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
tab: 248 wrap: offon