Merge pull request #246 from Qwinci/trunk

limine: Add UEFI PXE support
This commit is contained in:
mint 2023-01-17 22:56:35 +01:00 committed by GitHub
commit 41d4b3af54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 180 additions and 43 deletions

View File

@ -124,7 +124,7 @@ make install # (or gmake where applicable)
## How to use ## How to use
### UEFI ### 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 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 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. 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. `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 ### Configuration
The `limine.cfg` file contains Limine's configuration. The `limine.cfg` file contains Limine's configuration.

View File

@ -14,6 +14,7 @@
#include <lib/rand.h> #include <lib/rand.h>
#include <mm/pmm.h> #include <mm/pmm.h>
#include <sys/cpu.h> #include <sys/cpu.h>
#include <pxe/pxe.h>
#define DEFAULT_FASTEST_XFER_SIZE 64 #define DEFAULT_FASTEST_XFER_SIZE 64
#define MAX_FASTEST_XFER_SIZE 512 #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]; static alignas(4096) uint8_t unique_sector_pool[4096];
struct volume *disk_volume_from_efi_handle(EFI_HANDLE efi_handle) { 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); status = gBS->HandleProtocol(efi_handle, &block_io_guid, (void **)&block_io);
if (status) { if (status) {
return NULL; return pxe_from_efi_handle(efi_handle);
} }
block_io->Media->WriteCaching = false; 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; struct file_handle *ret;
#if defined (BIOS)
if (part->pxe) { if (part->pxe) {
if ((ret = tftp_open(0, 69, filename)) == NULL) { if ((ret = tftp_open(part, "", filename)) == NULL) {
return NULL; return NULL;
} }
goto success; goto success;
} }
#endif
if ((ret = ext2_open(part, filename)) != NULL) { if ((ret = ext2_open(part, filename)) != NULL) {
goto success; goto success;

View File

@ -43,22 +43,6 @@ int init_config_disk(struct volume *part) {
return init_config(config_size); 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 NOT_CHILD (-1)
#define DIRECT_CHILD 0 #define DIRECT_CHILD 0
#define INDIRECT_CHILD 1 #define INDIRECT_CHILD 1

View File

@ -26,7 +26,6 @@ struct conf_tuple {
extern struct menu_entry *menu_tree; extern struct menu_entry *menu_tree;
int init_config_disk(struct volume *part); int init_config_disk(struct volume *part);
int init_config_pxe(void);
int init_config(size_t config_size); int init_config(size_t config_size);
char *config_get_value(const char *config, size_t index, const char *key); char *config_get_value(const char *config, size_t index, const char *key);

View File

@ -17,9 +17,14 @@
struct volume { struct volume {
#if defined (UEFI) #if defined (UEFI)
EFI_HANDLE efi_handle; EFI_HANDLE efi_handle;
// Block storage
EFI_HANDLE efi_part_handle; EFI_HANDLE efi_part_handle;
EFI_BLOCK_IO *block_io; EFI_BLOCK_IO *block_io;
// PXE
EFI_PXE_BASE_CODE_PROTOCOL *pxe_base_code;
bool unique_sector_valid; bool unique_sector_valid;
uint64_t unique_sector; uint64_t unique_sector;
uint8_t unique_sector_b2b[BLAKE2B_OUT_BYTES]; 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); return fopen(volume, path);
} }
#if defined (BIOS)
static struct file_handle *uri_tftp_dispatch(char *root, char *path) { static struct file_handle *uri_tftp_dispatch(char *root, char *path) {
uint32_t ip; uint32_t ip;
if (!strcmp(root, "")) { if (!strcmp(root, "")) {
@ -176,19 +175,16 @@ static struct file_handle *uri_tftp_dispatch(char *root, char *path) {
} }
struct file_handle *ret; struct file_handle *ret;
if ((ret = tftp_open(ip, 69, path)) == NULL) { if ((ret = tftp_open(boot_volume, root, path)) == NULL) {
return NULL; return NULL;
} }
return ret; return ret;
} }
#endif
static struct file_handle *uri_boot_dispatch(char *s_part, char *path) { static struct file_handle *uri_boot_dispatch(char *s_part, char *path) {
#if defined (BIOS)
if (boot_volume->pxe) if (boot_volume->pxe)
return uri_tftp_dispatch(s_part, path); return uri_tftp_dispatch(s_part, path);
#endif
int partition; int partition;
@ -242,10 +238,8 @@ struct file_handle *uri_open(char *uri) {
ret = uri_guid_dispatch(root, path); ret = uri_guid_dispatch(root, path);
} else if (!strcmp(resource, "fslabel")) { } else if (!strcmp(resource, "fslabel")) {
ret = uri_fslabel_dispatch(root, path); ret = uri_fslabel_dispatch(root, path);
#if defined (BIOS)
} else if (!strcmp(resource, "tftp")) { } else if (!strcmp(resource, "tftp")) {
ret = uri_tftp_dispatch(root, path); ret = uri_tftp_dispatch(root, path);
#endif
} else { } else {
panic(true, "Resource `%s` not valid.", resource); panic(true, "Resource `%s` not valid.", resource);
} }

View File

@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <lib/part.h> #include <lib/part.h>
#if defined (BIOS)
struct volume *pxe_bind_volume(void); struct volume *pxe_bind_volume(void);
void pxe_init(void); void pxe_init(void);
int pxe_call(uint16_t opcode, uint16_t buf_seg, uint16_t buf_off); 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; uint16_t buffer_limit;
} __attribute__((packed)); } __attribute__((packed));
#elif defined (UEFI)
struct volume *pxe_bind_volume(EFI_HANDLE efi_handle, EFI_PXE_BASE_CODE *pxe_base_code);
#endif
#endif #endif

View File

@ -1,11 +1,15 @@
#if defined (BIOS)
#include <lib/print.h> #include <lib/print.h>
#include <lib/real.h>
#include <pxe/pxe.h> #include <pxe/pxe.h>
#include <lib/libc.h> #include <lib/libc.h>
#include <lib/misc.h> #include <lib/misc.h>
#include <mm/pmm.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); void set_pxe_fp(uint32_t fp);
@ -53,4 +57,16 @@ void pxe_init(void) {
printv("pxe: Successfully initialized\n"); 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 #endif

View File

@ -5,6 +5,8 @@
#include <stddef.h> #include <stddef.h>
#include <fs/file.h> #include <fs/file.h>
#if defined (BIOS)
#define UNDI_GET_INFORMATION 0xC #define UNDI_GET_INFORMATION 0xC
#define TFTP_OPEN 0x0020 #define TFTP_OPEN 0x0020
@ -37,9 +39,8 @@ struct pxenv_get_file_size {
#define TFTP_CLOSE 0x21 #define TFTP_CLOSE 0x21
//server_ip and server_port can be 0 for default #endif
struct file_handle *tftp_open(uint32_t server_ip, uint16_t server_port, const char *name);
uint32_t get_boot_server_info(void); struct file_handle *tftp_open(struct volume *part, const char *server_addr, const char *name);
#endif #endif

View File

@ -1,14 +1,18 @@
#if defined (BIOS)
#include <pxe/tftp.h> #include <pxe/tftp.h>
#include <pxe/pxe.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/print.h>
#include <lib/libc.h> #include <lib/libc.h>
#include <mm/pmm.h> #include <mm/pmm.h>
#include <lib/misc.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 }; struct pxenv_get_cached_info cachedinfo = { 0 };
cachedinfo.packet_type = 2; cachedinfo.packet_type = 2;
pxe_call(PXENV_GET_CACHED_INFO, ((uint16_t)rm_seg(&cachedinfo)), (uint16_t)rm_off(&cachedinfo)); 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; 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; int ret = 0;
if (!server_ip) { (void)part;
server_ip = get_boot_server_info();
}
struct PXENV_UNDI_GET_INFORMATION undi_info = { 0 }; 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)); 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; 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 #endif