limine: Add UEFI PXE support

based on @qookei's original patch with small changes.
This commit is contained in:
Qwinci 2023-01-17 18:40:43 +02:00
parent ac1c5d1b95
commit 3dd0a6fb41
No known key found for this signature in database
GPG Key ID: D55C7A11244BE1A1
11 changed files with 180 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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