mirror of
https://github.com/limine-bootloader/limine
synced 2024-12-23 22:36:48 +03:00
Add pxe support
This commit is contained in:
parent
10bd26962a
commit
e7ae195304
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,3 +10,4 @@
|
||||
.vscode
|
||||
/limine-install
|
||||
!/limine.bin
|
||||
!/limine-pxe.bin
|
||||
|
1
Makefile
1
Makefile
@ -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
|
||||
|
@ -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
BIN
limine-pxe.bin
Normal file
Binary file not shown.
BIN
limine.bin
BIN
limine.bin
Binary file not shown.
64
pxeboot/bootsect.asm
Normal file
64
pxeboot/bootsect.asm
Normal 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
|
@ -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') {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
85
stage2/lib/pxe.asm
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
43
stage2/pxe/pxe.c
Normal 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
95
stage2/pxe/pxe.h
Normal 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
97
stage2/pxe/tftp.c
Normal 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
51
stage2/pxe/tftp.h
Normal 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
|
Loading…
Reference in New Issue
Block a user