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
