Add pxe support

This commit is contained in:
Matteo Semenzato 2020-11-05 01:37:45 +01:00
parent 10bd26962a
commit e7ae195304
17 changed files with 545 additions and 26 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@
.vscode
/limine-install
!/limine.bin
!/limine-pxe.bin

View File

@ -7,6 +7,7 @@ PATH := $(shell pwd)/toolchain/bin:$(PATH)
all: stage2 decompressor
gzip -n -9 < stage2/stage2.bin > stage2/stage2.bin.gz
cd bootsect && nasm bootsect.asm -fbin -o ../limine.bin
cd pxeboot && nasm bootsect.asm -fbin -o ../limine-pxe.bin
clean: stage2-clean decompressor-clean test-clean
rm -f stage2/stage2.bin.gz

View File

@ -3,14 +3,14 @@
#include <gzip/tinf.h>
__attribute__((noreturn))
void entry(uint8_t *compressed_stage2, size_t stage2_size, uint8_t boot_drive) {
void entry(uint8_t *compressed_stage2, size_t stage2_size, uint8_t boot_drive, int pxe) {
// The decompressor should decompress compressed_stage2 to address 0x8000.
uint8_t *dest = (uint8_t *)0x8000;
tinf_gzip_uncompress(dest, compressed_stage2, stage2_size);
__attribute__((noreturn))
void (*stage2)(uint8_t boot_drive) = (void *)dest;
void (*stage2)(uint8_t boot_drive, int pxe) = (void *)dest;
stage2(boot_drive);
stage2(boot_drive, pxe);
}

BIN
limine-pxe.bin Normal file

Binary file not shown.

Binary file not shown.

64
pxeboot/bootsect.asm Normal file
View File

@ -0,0 +1,64 @@
org 0x7c00
bits 16
start:
jmp 0x0000:.initialise_cs
.initialise_cs:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
sti
call load_gdt
cli
mov eax, cr0
bts ax, 0
mov cr0, eax
jmp 0x18:.mode32
bits 32
.mode32:
mov ax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
push 0x1
and edx, 0xff
push edx
push stage2.size
push (stage2 - decompressor) + 0x70000
mov esi, decompressor
mov edi, 0x70000
mov ecx, stage2.fullsize
rep movsb
call 0x70000
bits 16
err:
hlt
jmp err
; Includes
%include '../bootsect/gdt.inc'
; ********************* Stage 2 *********************
decompressor:
incbin '../decompressor/decompressor.bin'
align 16
stage2:
incbin '../stage2/stage2.bin.gz'
.size: equ $ - stage2
.fullsize: equ $ - decompressor

View File

@ -5,6 +5,8 @@
#include <lib/blib.h>
#include <mm/pmm.h>
#include <fs/file.h>
#include <lib/print.h>
#include <pxe/tftp.h>
#define SEPARATOR '\n'
@ -12,7 +14,7 @@ bool config_ready = false;
static char *config_addr;
int init_config(struct part *part) {
int init_config_disk(struct part *part) {
struct file_handle f;
if (fopen(&f, part, "/limine.cfg")) {
@ -26,6 +28,23 @@ int init_config(struct part *part) {
fread(&f, config_addr, 0, f.size);
return init_config(config_size);
}
int init_config_pxe(void) {
struct tftp_file_handle cfg;
if (tftp_open(&cfg, 0, 69, "limine.cfg")) {
return -1;
}
config_addr = conv_mem_alloc(cfg.file_size);
tftp_read(&cfg, config_addr, 0, cfg.file_size);
print("\nconfig: %s\n", config_addr);
return init_config(cfg.file_size);
}
int init_config(size_t config_size) {
// remove windows carriage returns, if any
for (size_t i = 0; i < config_size; i++) {
if (config_addr[i] == '\r') {

View File

@ -7,7 +7,9 @@
extern bool config_ready;
int init_config(struct part *part);
int init_config_disk(struct part *part);
int init_config_pxe(void);
int init_config(size_t config_size);
int config_get_entry_name(char *ret, size_t index, size_t limit);
int config_set_entry(size_t index);
char *config_get_value(char *buf, size_t index, size_t limit, const char *key);

View File

@ -1,6 +1,9 @@
#include <stddef.h>
#include <stdint.h>
#include <lib/libc.h>
#include <limits.h>
#include <stdbool.h>
#include <lib/blib.h>
int toupper(int c) {
if (c >= 'a' && c <= 'z') {
@ -67,3 +70,24 @@ size_t strlen(const char *str) {
return len;
}
int inet_pton(const char *src, void *dst) {
uint8_t array[4] = {};
const char *current = src;
for (int i = 0; i < 4; i++) {
long int value = strtoui(current, 0, 10);
if (value > 255)
return -1;
for (int j = 0; j < 3; j++) {
if (*current != '\0' && *current != '.')
current++;
else
break;
}
current++;
array[i] = value;
}
memcpy(dst, array, 4);
return 0;
}

View File

@ -16,5 +16,6 @@ char *strncpy(char *, const char *, size_t);
size_t strlen(const char *);
int strcmp(const char *, const char *);
int strncmp(const char *, const char *, size_t);
int inet_pton(const char *src, void *dst);
#endif

85
stage2/lib/pxe.asm Normal file
View File

@ -0,0 +1,85 @@
section .realmode
global pxe_call
global set_pxe_fp
set_pxe_fp:
mov eax, [esp + 4]
mov [pxe_call.pxe_fp], eax
ret
pxe_call:
; Save GDT in case BIOS overwrites it
sgdt [.gdt]
; Save non-scratch GPRs
push ebx
push esi
push edi
push ebp
mov ebx, eax
; Jump to real mode
jmp 0x08:.bits16
.bits16:
bits 16
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 0xfe
mov cr0, eax
jmp 0x00:.cszero
.cszero:
xor ax, ax
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
sti
push dx
push cx
push bx
call far [.pxe_fp]
add sp, 6
mov bx, ax
cli
; Restore GDT
lgdt [ss:.gdt]
; Jump back to pmode
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x18:.bits32
.bits32:
bits 32
mov ax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, ebx
; Restore non-scratch GPRs
pop ebp
pop edi
pop esi
pop ebx
; Exit
ret
align 16
.pxe_fp: dd 0
.esp: dd 0
.gdt: dq 0

View File

@ -5,6 +5,9 @@
#include <lib/part.h>
#include <lib/libc.h>
#include <fs/file.h>
#include <mm/pmm.h>
#include <lib/print.h>
#include <pxe/tftp.h>
// A URI takes the form of: resource://root/path
// The following function splits up a URI into its componenets
@ -109,6 +112,28 @@ static bool uri_guid_dispatch(struct file_handle *fd, char *guid_str, char *path
return true;
}
static bool uri_tftp_dispatch(struct file_handle *fd, char *root, char *path) {
uint32_t ip;
if (!strcmp(root, "")) {
ip = 0;
} else {
if (inet_pton(root, &ip)) {
panic("invalid ipv4 address: %s", root);
}
print("\nip: %x\n", ip);
}
struct tftp_file_handle *cfg = conv_mem_alloc(sizeof(struct tftp_file_handle));
if(tftp_open(cfg, ip, 69, path)) {
return false;
}
fd->fd = cfg;
fd->read = tftp_read;
fd->size = cfg->file_size;
return true;
}
bool uri_open(struct file_handle *fd, char *uri) {
char *resource, *root, *path;
uri_resolve(uri, &resource, &root, &path);
@ -117,6 +142,8 @@ bool uri_open(struct file_handle *fd, char *uri) {
return uri_bios_dispatch(fd, root, path);
} else if (!strcmp(resource, "guid")) {
return uri_guid_dispatch(fd, root, path);
} else if (!strcmp(resource, "tftp")) {
return uri_tftp_dispatch(fd, root, path);
} else {
panic("Resource `%s` not valid.", resource);
}

View File

@ -17,8 +17,10 @@
#include <protos/linux.h>
#include <protos/chainload.h>
#include <menu.h>
#include <pxe/pxe.h>
#include <pxe/tftp.h>
void entry(uint8_t _boot_drive) {
void entry(uint8_t _boot_drive, int pxe_boot) {
boot_drive = _boot_drive;
mtrr_save();
@ -30,32 +32,39 @@ void entry(uint8_t _boot_drive) {
if (!a20_enable())
panic("Could not enable A20 line");
print("Boot drive: %x\n", boot_drive);
part_create_index();
// Look for config file.
print("Searching for config file...\n");
struct part parts[4];
for (int i = 0; ; i++) {
if (i == 4) {
panic("Config file not found.");
init_e820();
init_memmap();
if (pxe_boot) {
pxe_init();
if(init_config_pxe()) {
panic("failed to load config file");
}
print("Checking partition %d...\n", i);
int ret = part_get(&parts[i], boot_drive, i);
if (ret) {
print("Partition not found.\n");
} else {
print("Partition found.\n");
if (!init_config(&parts[i])) {
print("Config file found and loaded.\n");
break;
print("config loaded");
} else {
print("Boot drive: %x\n", boot_drive);
// Look for config file.
print("Searching for config file...\n");
struct part parts[4];
for (int i = 0; ; i++) {
if (i == 4) {
panic("Config file not found.");
}
print("Checking partition %d...\n", i);
int ret = part_get(&parts[i], boot_drive, i);
if (ret) {
print("Partition not found.\n");
} else {
print("Partition found.\n");
if (!init_config_disk(&parts[i])) {
print("Config file found and loaded.\n");
break;
}
}
}
}
init_e820();
init_memmap();
char *cmdline = menu();

43
stage2/pxe/pxe.c Normal file
View File

@ -0,0 +1,43 @@
#include <lib/print.h>
#include <lib/real.h>
#include <pxe/pxe.h>
#include <lib/libc.h>
#include <lib/blib.h>
void set_pxe_fp(uint32_t fp);
void pxe_init(void) {
//pxe installation check
struct rm_regs r = { 0 };
r.ebx = 0;
r.ecx = 0;
r.eax = 0x5650;
r.es = 0;
rm_int(0x1a, &r, &r);
if ((r.eax & 0xffff) != 0x564e) {
panic("PXE installation check failed");
}
struct pxenv* pxenv = { 0 };
pxenv = (struct pxenv*)((r.es << 4) + (r.ebx & 0xffff));
if (memcmp(pxenv->signature, PXE_SIGNATURE, sizeof(pxenv->signature)) != 0) {
panic("PXENV structure signature corrupted");
}
if (pxenv->version < 0x201) {
//we won't support pxe < 2.1, grub does this too and it seems to work fine
panic("\npxe version too old");
}
struct bangpxe* bangpxe = (struct bangpxe*)((((pxenv->pxe_ptr & 0xffff0000) >> 16) << 4) + (pxenv->pxe_ptr & 0xffff));
if (memcmp(bangpxe->signature, PXE_BANGPXE_SIGNATURE,
sizeof(bangpxe->signature))
!= 0) {
panic("!pxe signature corrupted");
}
set_pxe_fp(bangpxe->rm_entry);
print("Successfully initialized pxe");
}

95
stage2/pxe/pxe.h Normal file
View File

@ -0,0 +1,95 @@
#ifndef PXE_H
#define PXE_H
#include <stdint.h>
void pxe_init(void);
int pxe_call(uint16_t opcode, uint16_t buf_seg, uint16_t buf_off) __attribute__((regparm(3)));
#define MAC_ADDR_LEN 16
typedef uint8_t MAC_ADDR_t[MAC_ADDR_LEN];
struct bootph {
uint8_t opcode;
uint8_t Hardware;
uint8_t Hardlen;
uint8_t Gatehops;
uint32_t ident;
uint16_t seconds;
uint16_t Flags;
uint32_t cip;
uint32_t yip;
uint32_t sip;
uint32_t gip;
MAC_ADDR_t CAddr;
uint8_t Sname[64];
uint8_t bootfile[128];
union bootph_vendor {
uint8_t d[1024];
struct bootph_vendor_v {
uint8_t magic[4];
uint32_t flags;
uint8_t pad[56];
} v;
} vendor;
};
struct PXENV_UNDI_GET_INFORMATION {
uint16_t Status;
uint16_t BaseIo;
uint16_t IntNumber;
uint16_t MaxTranUnit;
uint16_t HwType;
uint16_t HwAddrLen;
uint8_t CurrentNodeAddress[16];
uint8_t PermNodeAddress[16];
uint16_t ROMAddress;
uint16_t RxBufCt;
uint16_t TxBufCt;
};
#define PXE_SIGNATURE "PXENV+"
struct pxenv {
uint8_t signature[6];
uint16_t version;
uint8_t length;
uint8_t checksum;
uint32_t rm_entry;
uint32_t pm_offset;
uint16_t pm_selector;
uint16_t stack_seg;
uint16_t stack_size;
uint16_t bc_code_seg;
uint16_t bc_code_size;
uint16_t bc_data_seg;
uint16_t bc_data_size;
uint16_t undi_data_seg;
uint16_t undi_data_size;
uint16_t undi_code_seg;
uint16_t undi_code_size;
uint32_t pxe_ptr;
} __attribute__((packed));
#define PXE_BANGPXE_SIGNATURE "!PXE"
struct bangpxe {
uint8_t signature[4];
uint8_t length;
uint8_t chksum;
uint8_t rev;
uint8_t reserved;
uint32_t undiromid;
uint32_t baseromid;
uint32_t rm_entry;
uint32_t pm_entry;
} __attribute__((packed));
#define PXENV_GET_CACHED_INFO 0x0071
struct pxenv_get_cached_info {
uint16_t status;
uint16_t packet_type;
uint16_t buffer_size;
uint32_t buffer;
uint16_t buffer_limit;
} __attribute__((packed));
#endif

97
stage2/pxe/tftp.c Normal file
View File

@ -0,0 +1,97 @@
#include <pxe/tftp.h>
#include <pxe/pxe.h>
#include <lib/real.h>
#include <lib/print.h>
#include <lib/libc.h>
#include <mm/pmm.h>
#include <lib/blib.h>
int tftp_open(struct tftp_file_handle* handle, uint32_t server_ip, uint16_t server_port, const char* name) {
int ret = 0;
if (!server_ip) {
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));
struct bootph *ph = (struct bootph*)(void *) (((((uint32_t)cachedinfo.buffer) >> 16) << 4) + (((uint32_t)cachedinfo.buffer) & 0xFFFF));
server_ip = ph->sip;
}
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) {
return -1;
}
//TODO figure out a more proper way to do this.
uint16_t mtu = undi_info.MaxTranUnit - 48;
handle->server_ip = server_ip;
handle->server_port = server_port;
handle->packet_size = mtu;
struct pxenv_get_file_size fsize = {
.status = 0,
.sip = server_ip,
};
strcpy((char*)fsize.name, name);
ret = pxe_call(TFTP_GET_FILE_SIZE, ((uint16_t)rm_seg(&fsize)), (uint16_t)rm_off(&fsize));
if (ret) {
return -1;
}
handle->file_size = fsize.file_size;
volatile struct pxenv_open open = {
.status = 0,
.sip = server_ip,
.port = (server_port) << 8,
.packet_size = mtu
};
strcpy((char*)open.name, name);
ret = pxe_call(TFTP_OPEN, ((uint16_t)rm_seg(&open)), (uint16_t)rm_off(&open));
if (ret) {
print("failed to open file %x or bad packet size", open.status);
return -1;
}
mtu = open.packet_size;
uint8_t *buf = conv_mem_alloc(mtu);
handle->data = ext_mem_alloc(handle->file_size);
memset(handle->data, 0, handle->file_size);
size_t to_transfer = handle->file_size;
size_t progress = 0;
while (to_transfer > 0) {
volatile struct pxenv_read read = {
.boff = ((uint16_t)rm_off(buf)),
.bseg = ((uint16_t)rm_seg(buf)),
};
ret = pxe_call(TFTP_READ, ((uint16_t)rm_seg(&read)), (uint16_t)rm_off(&read));
if (ret) {
panic("failed reading");
}
memcpy(handle->data + progress, buf, read.bsize);
if (read.bsize < mtu) {
break;
}
to_transfer -= read.bsize;
progress += read.bsize;
}
uint16_t close = 0;
ret = pxe_call(TFTP_CLOSE, ((uint16_t)rm_seg(&close)), (uint16_t)rm_off(&close));
if (ret) {
panic("close failed");
}
return 0;
}
int tftp_read(void* fd, void *buf, uint64_t loc, uint64_t count) {
struct tftp_file_handle *handle = (struct tftp_file_handle*)fd;
if ((loc + count) > handle->file_size) {
return -1;
}
memcpy(buf, handle->data + loc, count);
return 0;
}

51
stage2/pxe/tftp.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef TFTP_H
#define TFTP_H
#include <stdint.h>
#include <stddef.h>
#define UNDI_GET_INFORMATION 0xC
struct tftp_file_handle {
uint32_t server_ip;
uint16_t server_port;
uint16_t packet_size;
size_t file_size;
void *data;
};
#define TFTP_OPEN 0x0020
struct pxenv_open {
uint16_t status;
uint32_t sip;
uint32_t gip;
uint8_t name[128];
uint16_t port;
uint16_t packet_size;
} __attribute__((packed));
#define TFTP_READ 0x22
struct pxenv_read {
uint16_t status;
uint16_t pn;
uint16_t bsize;
uint16_t boff;
uint16_t bseg;
} __attribute__((packed));
#define TFTP_GET_FILE_SIZE 0x25
struct pxenv_get_file_size {
uint16_t status;
uint32_t sip;
uint32_t gip;
uint8_t name[128];
uint32_t file_size;
} __attribute__((packed));
#define TFTP_CLOSE 0x21
//server_ip and server_port can be 0 for default
int tftp_open(struct tftp_file_handle* handle, uint32_t server_ip, uint16_t server_port, const char* name);
int tftp_read(void *fd, void *buf, uint64_t loc, uint64_t count);
#endif