Receiving a whole file over PXE
This commit is contained in:
parent
1c0b8a53d6
commit
c13946c7dd
3 changed files with 160 additions and 94 deletions
|
|
@ -4,7 +4,7 @@ PHDRS {
|
|||
SECTIONS {
|
||||
.text 0x7C00: {
|
||||
lboot.o(.text)
|
||||
*(.text)
|
||||
*(.text, .rodata*)
|
||||
*(.data)
|
||||
*(.bss, COMMON)
|
||||
} :all
|
||||
|
|
|
|||
226
lboot.S
226
lboot.S
|
|
@ -1,32 +1,59 @@
|
|||
// vim: et:sw=12:ts=12:sts=12
|
||||
// lboot.S: boot code for legacy BIOS boot over PXE.
|
||||
|
||||
/* The assembly code in this project follows its own
|
||||
* made up calling convention, because it is more convenient
|
||||
* than the conventional ones for working in unreal mode.
|
||||
*
|
||||
* Callees preserve all registers,
|
||||
* except those which they use to return values.
|
||||
* All 32 bits of a register are preserved.
|
||||
* Saved registers are push to the stack before the frame pointer,
|
||||
* as this simplifies frame cleanup.
|
||||
* Addresses/pointers are returned in EBX,
|
||||
* all other values are returned in EAX.
|
||||
*
|
||||
* DS, ES have a 4Gb limit, and an offset of 0.
|
||||
* CS has an offset of 0. SS is unspecified.
|
||||
*/
|
||||
|
||||
.global _start
|
||||
.text
|
||||
.code16
|
||||
|
||||
.set PACKET_SIZE, 1024
|
||||
.set PACKET_SIZE, 512
|
||||
|
||||
.macro pxe_call, opcode
|
||||
push %eax
|
||||
push %ebx
|
||||
push %ecx
|
||||
push %edx
|
||||
|
||||
mov %sp, %bx
|
||||
add $16, %bx
|
||||
|
||||
push %ss
|
||||
push %bx
|
||||
push $\opcode
|
||||
lcall *pxe_api
|
||||
add $6, %sp
|
||||
mov %sp, %bx
|
||||
|
||||
or %ss:(%bx), %ax
|
||||
jnz _pcerr
|
||||
|
||||
pop %edx
|
||||
pop %ecx
|
||||
pop %ebx
|
||||
pop %eax
|
||||
.endm
|
||||
|
||||
// _start: entry point
|
||||
_start: cli
|
||||
cld
|
||||
mov %sp, %bp
|
||||
|
||||
// we keep our text and data close to each other
|
||||
xor %ax, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
|
||||
call init_com1
|
||||
|
||||
|
|
@ -35,14 +62,14 @@ _start: cli
|
|||
|
||||
mov %ss:4(%bp), %si
|
||||
mov %ss:6(%bp), %ax
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
.set PXE_MAGIC, 0x45585021
|
||||
cmpl $PXE_MAGIC, %es:(%si)
|
||||
cmpl $PXE_MAGIC, %fs:(%si)
|
||||
je 1f
|
||||
mov $msg_pserr, %si
|
||||
call print
|
||||
jmp hang
|
||||
1: mov %es:16(%si), %eax
|
||||
1: mov %fs:16(%si), %eax
|
||||
mov %eax, pxe_api
|
||||
|
||||
mov $msg_a20, %si
|
||||
|
|
@ -79,7 +106,9 @@ _start: cli
|
|||
|
||||
mov $msg_read, %si
|
||||
call print
|
||||
mov $fn_initrd, %esi
|
||||
call read_file
|
||||
mov %ebx, initrd_ptr
|
||||
|
||||
mov $msg_long, %si
|
||||
call print
|
||||
|
|
@ -115,6 +144,7 @@ enable_a20: // Of all the ways to toggle A20, we only try the Fast A20 Gate.
|
|||
|
||||
// unreal: Enter unreal mode
|
||||
unreal: push %ds
|
||||
push %es
|
||||
lgdt gdt16_ptr
|
||||
|
||||
mov %cr0, %eax
|
||||
|
|
@ -124,18 +154,18 @@ unreal: push %ds
|
|||
|
||||
_urprot: mov $0x10, %cx
|
||||
mov %cx, %ds
|
||||
mov %cx, %es
|
||||
|
||||
and $0xFE, %al
|
||||
mov %eax, %cr0
|
||||
ljmp $0x0, $_urunreal
|
||||
|
||||
_urunreal: pop %ds
|
||||
_urunreal: pop %es
|
||||
pop %ds
|
||||
ret
|
||||
|
||||
// get_map: Retrieve memory map using e820 BIOS function
|
||||
get_map: mov %ds, %ax
|
||||
mov %ax, %es
|
||||
mov $memmap, %di
|
||||
get_map: mov $memmap, %di
|
||||
xor %ebx, %ebx
|
||||
mov $0x534D4150, %edx // e820 magic number
|
||||
_gmnext: movl $0, 20(%di)
|
||||
|
|
@ -152,14 +182,16 @@ _gmdone: add $24, %di
|
|||
ret
|
||||
|
||||
// paging: Set up initial page tables for long mode
|
||||
paging: mov $4*4096, %ecx
|
||||
call alloc
|
||||
paging:
|
||||
mov heap_ptr, %eax
|
||||
add $6*4096, %eax
|
||||
cmp heap_end, %eax
|
||||
ja out_of_mem
|
||||
|
||||
mov %eax, pd_ptr
|
||||
mov $4096, %ecx
|
||||
call alloc
|
||||
add $4*4096, %eax
|
||||
mov %eax, pdp_ptr
|
||||
mov $4096, %ecx
|
||||
call alloc
|
||||
add $4096, %eax
|
||||
mov %eax, pml4_ptr
|
||||
|
||||
// fill PDEs with identity map < 4Gb
|
||||
|
|
@ -209,15 +241,14 @@ _mhnext: add $24, %si
|
|||
|
||||
cmpl $1, 16(%si)
|
||||
jne _mhnext
|
||||
|
||||
cmpl $0, 4(%si)
|
||||
ja _mhnext
|
||||
|
||||
mov 0(%si), %ebx
|
||||
mov 0(%si), %eax
|
||||
mov 8(%si), %ecx
|
||||
|
||||
// find end of range, clip to 4Gb
|
||||
add %ebx, %ecx
|
||||
add %eax, %ecx
|
||||
jnc 1f
|
||||
mov $0xFFFFFFFF, %ecx
|
||||
|
||||
|
|
@ -227,94 +258,116 @@ _mhnext: add $24, %si
|
|||
mov $0xFFFFFFFF, %ecx
|
||||
|
||||
// adjust base to above 1Mb, above the bootloader
|
||||
1: cmp $0x10000, %ebx
|
||||
1: cmp $0x10000, %eax
|
||||
jae 1f
|
||||
mov $0x10000, %ebx
|
||||
mov $0x10000, %eax
|
||||
|
||||
// align to 4Kb boundaries
|
||||
1: add $0xFFF, %ebx
|
||||
and $0xFFFFF000, %ebx
|
||||
and $0xFFFFF000, %ecx
|
||||
// align start to 4Kb boundary
|
||||
1: add $0xFFF, %eax
|
||||
and $0xFFFFF000, %eax
|
||||
|
||||
sub %ebx, %ecx
|
||||
cmp heap_size, %ecx
|
||||
// Is this entry larger than current heap?
|
||||
mov %ecx, %edx
|
||||
sub %eax, %edx
|
||||
mov heap_end, %ebx
|
||||
sub heap_ptr, %ebx
|
||||
cmp %ebx, %edx
|
||||
jbe _mhnext
|
||||
|
||||
mov %ebx, heap_start
|
||||
mov %ecx, heap_size
|
||||
// If so, switch to it.
|
||||
mov %eax, heap_ptr
|
||||
mov %ecx, heap_end
|
||||
jmp _mhnext
|
||||
|
||||
_mhdone: ret
|
||||
|
||||
// alloc: take ECX bytes from heap, return ptr in EAX
|
||||
// The allocation does not get marked in the memmap.
|
||||
// No realignment is performed, so only alloc aligned sizes.
|
||||
alloc: cmp heap_size, %ecx
|
||||
ja _aerr
|
||||
mov heap_start, %eax
|
||||
add %ecx, heap_start
|
||||
sub %ecx, heap_size
|
||||
ret
|
||||
_aerr: mov $msg_aerr, %si
|
||||
call print
|
||||
jmp hang
|
||||
|
||||
read_file:
|
||||
.set PXE_TFTP_OPEN, 0x0020
|
||||
.set PXE_TFTP_CLOSE, 0x0021
|
||||
.set PXE_TFTP_READ, 0x0022
|
||||
|
||||
read_file: push %bp
|
||||
mov %sp, %bp
|
||||
|
||||
call tftp_open
|
||||
mov heap_ptr, %ebx
|
||||
mov %ebx, %edi
|
||||
|
||||
_rdloop: call tftp_read
|
||||
mov $tx_buf, %esi
|
||||
mov $PACKET_SIZE, %ecx
|
||||
addr32 rep movsb
|
||||
cmp heap_end, %edi
|
||||
ja out_of_mem
|
||||
cmp $PACKET_SIZE, %ax
|
||||
je _rdloop
|
||||
|
||||
mov %edi, heap_ptr
|
||||
call tftp_close
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
// tftp_open: Open a file stream over TFTP.
|
||||
// A pointer to the filename is passed in ESI.
|
||||
tftp_open: push %ecx
|
||||
push %esi
|
||||
push %edi
|
||||
push %bp
|
||||
mov %sp, %bp
|
||||
|
||||
push $PACKET_SIZE
|
||||
push $69<<8
|
||||
sub $128, %sp
|
||||
|
||||
mov $fn_config, %esi
|
||||
mov %ss, %ax
|
||||
mov %ax, %es
|
||||
mov %esp, %edi
|
||||
mov $fn_config_l, %ecx
|
||||
rep movsb
|
||||
|
||||
mov $128, %ecx
|
||||
sub %cx, %sp
|
||||
mov %ss, %edi
|
||||
shl $4, %edi
|
||||
add %esp, %edi
|
||||
addr32 rep movsb
|
||||
push $0
|
||||
push $0
|
||||
|
||||
sub $4, %sp
|
||||
mov %sp, %di
|
||||
mov server_ip+0, %al
|
||||
mov %al, %ss:0(%di)
|
||||
mov server_ip+1, %al
|
||||
mov %al, %ss:1(%di)
|
||||
mov server_ip+2, %al
|
||||
mov %al, %ss:2(%di)
|
||||
mov server_ip+3, %al
|
||||
mov %al, %ss:3(%di)
|
||||
|
||||
push server_ip+2
|
||||
push server_ip
|
||||
push $0
|
||||
pxe_call PXE_TFTP_OPEN
|
||||
add $14+128, %sp
|
||||
|
||||
leave
|
||||
pop %edi
|
||||
pop %esi
|
||||
pop %ecx
|
||||
ret
|
||||
|
||||
// tftp_close: Close the TFTP stream.
|
||||
tftp_close: push %bp
|
||||
mov %sp, %bp
|
||||
|
||||
push $0
|
||||
pxe_call PXE_TFTP_CLOSE
|
||||
|
||||
leave
|
||||
ret
|
||||
|
||||
// tftp_read: Read the next TFTP packet into tx_buf, return size in AX.
|
||||
tftp_read: push %ebx
|
||||
push %bp
|
||||
mov %sp, %bp
|
||||
|
||||
.set PXE_TFTP_READ, 0x0022
|
||||
push %cs
|
||||
push $tx_buf
|
||||
push $0
|
||||
push $0
|
||||
push $0
|
||||
pxe_call PXE_TFTP_READ
|
||||
add $10, %sp
|
||||
|
||||
|
||||
|
||||
.set PXE_TFTP_CLOSE, 0x0021
|
||||
push $0
|
||||
pxe_call PXE_TFTP_CLOSE
|
||||
add $2, %sp
|
||||
|
||||
|
||||
|
||||
mov $tx_buf, %si
|
||||
call print
|
||||
mov %sp, %bx
|
||||
mov %ss:4(%bx), %ax
|
||||
|
||||
leave
|
||||
pop %ebx
|
||||
ret
|
||||
|
||||
out_of_mem: mov $msg_memerr, %si
|
||||
call print
|
||||
jmp hang
|
||||
|
||||
// print: print NUL-terminated string pointed to by SI
|
||||
print: xor %bx, %bx
|
||||
|
||||
|
|
@ -435,16 +488,17 @@ msg_read: .asciz " * Retrieving\r\n"
|
|||
msg_long: .asciz " * Long Mode\r\n"
|
||||
msg_pserr: .asciz "panic: Missing !PXE structure.\r\n"
|
||||
msg_pcerr: .asciz "panic: PXE call failed.\r\n"
|
||||
msg_aerr: .asciz "panic: Out of heap space.\r\n"
|
||||
msg_memerr: .asciz "panic: Out of heap space.\r\n"
|
||||
|
||||
fn_config: .asciz "fernlader.cfg"
|
||||
.set fn_config_l, .-fn_config
|
||||
fn_initrd: .asciz "initrd"
|
||||
|
||||
pxe_api: .long 0
|
||||
server_ip: .space 4
|
||||
|
||||
heap_start: .long 0
|
||||
heap_size: .long 0
|
||||
heap_ptr: .long 0
|
||||
heap_end: .long 0
|
||||
|
||||
initrd_ptr: .long 0
|
||||
|
||||
// Long mode initial page tables
|
||||
pd_ptr: .long 0
|
||||
|
|
@ -466,6 +520,8 @@ trampo64:
|
|||
mov %eax, %fs
|
||||
mov %eax, %gs
|
||||
mov %eax, %ss
|
||||
|
||||
mov initrd_ptr, %edi
|
||||
jmp loader_main
|
||||
|
||||
// ToDo List:
|
||||
|
|
|
|||
26
loader.c
26
loader.c
|
|
@ -128,9 +128,19 @@ typedef struct {
|
|||
} Elf64_Phdr;
|
||||
|
||||
static void
|
||||
panic(void)
|
||||
panic(const char *message)
|
||||
{
|
||||
for (;;) {}
|
||||
for (const char *c = message; *c; c++) {
|
||||
uint8_t status = 0;
|
||||
do {
|
||||
__asm__ ("inb\t%%dx" : "=a"(status) : "d"(0x3F8 + 5));
|
||||
} while (!(status & 0x20));
|
||||
__asm__ __volatile__ ("outb\t%%dx" : : "a"(*c), "d"(0x3F8));
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
__asm__ __volatile__ ("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -175,21 +185,21 @@ load_elf(file_t file, uintptr_t *entry)
|
|||
}
|
||||
|
||||
void
|
||||
loader_main(void)
|
||||
loader_main(void *initrd)
|
||||
{
|
||||
uint64_t entry = 0;
|
||||
file_t elf = { 0, 0 };
|
||||
file_t elf = { initrd, 1024 };
|
||||
if (load_elf(elf, &entry) < 0) {
|
||||
panic();
|
||||
panic("panic: Malformed ELF64 executable");
|
||||
}
|
||||
|
||||
uint64_t stack = 0;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"mov\t%1, %rbp\n\t"
|
||||
"mov\t%rbp, %rsp\n\t"
|
||||
"mov\t%1, %%rbp\n\t"
|
||||
"mov\t%%rbp, %%rsp\n\t"
|
||||
"call\t*%0"
|
||||
: : "r"(entry), "r"(stack) : "memory");
|
||||
|
||||
panic();
|
||||
panic("panic: Ran past kernel invocation");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue