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 {
|
SECTIONS {
|
||||||
.text 0x7C00: {
|
.text 0x7C00: {
|
||||||
lboot.o(.text)
|
lboot.o(.text)
|
||||||
*(.text)
|
*(.text, .rodata*)
|
||||||
*(.data)
|
*(.data)
|
||||||
*(.bss, COMMON)
|
*(.bss, COMMON)
|
||||||
} :all
|
} :all
|
||||||
|
|
|
||||||
226
lboot.S
226
lboot.S
|
|
@ -1,32 +1,59 @@
|
||||||
// vim: et:sw=12:ts=12:sts=12
|
// vim: et:sw=12:ts=12:sts=12
|
||||||
// lboot.S: boot code for legacy BIOS boot over PXE.
|
// 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
|
.global _start
|
||||||
.text
|
.text
|
||||||
.code16
|
.code16
|
||||||
|
|
||||||
.set PACKET_SIZE, 1024
|
.set PACKET_SIZE, 512
|
||||||
|
|
||||||
.macro pxe_call, opcode
|
.macro pxe_call, opcode
|
||||||
|
push %eax
|
||||||
|
push %ebx
|
||||||
|
push %ecx
|
||||||
|
push %edx
|
||||||
|
|
||||||
mov %sp, %bx
|
mov %sp, %bx
|
||||||
|
add $16, %bx
|
||||||
|
|
||||||
push %ss
|
push %ss
|
||||||
push %bx
|
push %bx
|
||||||
push $\opcode
|
push $\opcode
|
||||||
lcall *pxe_api
|
lcall *pxe_api
|
||||||
add $6, %sp
|
add $6, %sp
|
||||||
mov %sp, %bx
|
|
||||||
or %ss:(%bx), %ax
|
or %ss:(%bx), %ax
|
||||||
jnz _pcerr
|
jnz _pcerr
|
||||||
|
|
||||||
|
pop %edx
|
||||||
|
pop %ecx
|
||||||
|
pop %ebx
|
||||||
|
pop %eax
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
// _start: entry point
|
// _start: entry point
|
||||||
_start: cli
|
_start: cli
|
||||||
cld
|
cld
|
||||||
mov %sp, %bp
|
mov %sp, %bp
|
||||||
|
|
||||||
// we keep our text and data close to each other
|
|
||||||
xor %ax, %ax
|
xor %ax, %ax
|
||||||
mov %ax, %ds
|
mov %ax, %ds
|
||||||
|
mov %ax, %es
|
||||||
|
|
||||||
call init_com1
|
call init_com1
|
||||||
|
|
||||||
|
|
@ -35,14 +62,14 @@ _start: cli
|
||||||
|
|
||||||
mov %ss:4(%bp), %si
|
mov %ss:4(%bp), %si
|
||||||
mov %ss:6(%bp), %ax
|
mov %ss:6(%bp), %ax
|
||||||
mov %ax, %es
|
mov %ax, %fs
|
||||||
.set PXE_MAGIC, 0x45585021
|
.set PXE_MAGIC, 0x45585021
|
||||||
cmpl $PXE_MAGIC, %es:(%si)
|
cmpl $PXE_MAGIC, %fs:(%si)
|
||||||
je 1f
|
je 1f
|
||||||
mov $msg_pserr, %si
|
mov $msg_pserr, %si
|
||||||
call print
|
call print
|
||||||
jmp hang
|
jmp hang
|
||||||
1: mov %es:16(%si), %eax
|
1: mov %fs:16(%si), %eax
|
||||||
mov %eax, pxe_api
|
mov %eax, pxe_api
|
||||||
|
|
||||||
mov $msg_a20, %si
|
mov $msg_a20, %si
|
||||||
|
|
@ -79,7 +106,9 @@ _start: cli
|
||||||
|
|
||||||
mov $msg_read, %si
|
mov $msg_read, %si
|
||||||
call print
|
call print
|
||||||
|
mov $fn_initrd, %esi
|
||||||
call read_file
|
call read_file
|
||||||
|
mov %ebx, initrd_ptr
|
||||||
|
|
||||||
mov $msg_long, %si
|
mov $msg_long, %si
|
||||||
call print
|
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: Enter unreal mode
|
||||||
unreal: push %ds
|
unreal: push %ds
|
||||||
|
push %es
|
||||||
lgdt gdt16_ptr
|
lgdt gdt16_ptr
|
||||||
|
|
||||||
mov %cr0, %eax
|
mov %cr0, %eax
|
||||||
|
|
@ -124,18 +154,18 @@ unreal: push %ds
|
||||||
|
|
||||||
_urprot: mov $0x10, %cx
|
_urprot: mov $0x10, %cx
|
||||||
mov %cx, %ds
|
mov %cx, %ds
|
||||||
|
mov %cx, %es
|
||||||
|
|
||||||
and $0xFE, %al
|
and $0xFE, %al
|
||||||
mov %eax, %cr0
|
mov %eax, %cr0
|
||||||
ljmp $0x0, $_urunreal
|
ljmp $0x0, $_urunreal
|
||||||
|
|
||||||
_urunreal: pop %ds
|
_urunreal: pop %es
|
||||||
|
pop %ds
|
||||||
ret
|
ret
|
||||||
|
|
||||||
// get_map: Retrieve memory map using e820 BIOS function
|
// get_map: Retrieve memory map using e820 BIOS function
|
||||||
get_map: mov %ds, %ax
|
get_map: mov $memmap, %di
|
||||||
mov %ax, %es
|
|
||||||
mov $memmap, %di
|
|
||||||
xor %ebx, %ebx
|
xor %ebx, %ebx
|
||||||
mov $0x534D4150, %edx // e820 magic number
|
mov $0x534D4150, %edx // e820 magic number
|
||||||
_gmnext: movl $0, 20(%di)
|
_gmnext: movl $0, 20(%di)
|
||||||
|
|
@ -152,14 +182,16 @@ _gmdone: add $24, %di
|
||||||
ret
|
ret
|
||||||
|
|
||||||
// paging: Set up initial page tables for long mode
|
// paging: Set up initial page tables for long mode
|
||||||
paging: mov $4*4096, %ecx
|
paging:
|
||||||
call alloc
|
mov heap_ptr, %eax
|
||||||
|
add $6*4096, %eax
|
||||||
|
cmp heap_end, %eax
|
||||||
|
ja out_of_mem
|
||||||
|
|
||||||
mov %eax, pd_ptr
|
mov %eax, pd_ptr
|
||||||
mov $4096, %ecx
|
add $4*4096, %eax
|
||||||
call alloc
|
|
||||||
mov %eax, pdp_ptr
|
mov %eax, pdp_ptr
|
||||||
mov $4096, %ecx
|
add $4096, %eax
|
||||||
call alloc
|
|
||||||
mov %eax, pml4_ptr
|
mov %eax, pml4_ptr
|
||||||
|
|
||||||
// fill PDEs with identity map < 4Gb
|
// fill PDEs with identity map < 4Gb
|
||||||
|
|
@ -209,15 +241,14 @@ _mhnext: add $24, %si
|
||||||
|
|
||||||
cmpl $1, 16(%si)
|
cmpl $1, 16(%si)
|
||||||
jne _mhnext
|
jne _mhnext
|
||||||
|
|
||||||
cmpl $0, 4(%si)
|
cmpl $0, 4(%si)
|
||||||
ja _mhnext
|
ja _mhnext
|
||||||
|
|
||||||
mov 0(%si), %ebx
|
mov 0(%si), %eax
|
||||||
mov 8(%si), %ecx
|
mov 8(%si), %ecx
|
||||||
|
|
||||||
// find end of range, clip to 4Gb
|
// find end of range, clip to 4Gb
|
||||||
add %ebx, %ecx
|
add %eax, %ecx
|
||||||
jnc 1f
|
jnc 1f
|
||||||
mov $0xFFFFFFFF, %ecx
|
mov $0xFFFFFFFF, %ecx
|
||||||
|
|
||||||
|
|
@ -227,94 +258,116 @@ _mhnext: add $24, %si
|
||||||
mov $0xFFFFFFFF, %ecx
|
mov $0xFFFFFFFF, %ecx
|
||||||
|
|
||||||
// adjust base to above 1Mb, above the bootloader
|
// adjust base to above 1Mb, above the bootloader
|
||||||
1: cmp $0x10000, %ebx
|
1: cmp $0x10000, %eax
|
||||||
jae 1f
|
jae 1f
|
||||||
mov $0x10000, %ebx
|
mov $0x10000, %eax
|
||||||
|
|
||||||
// align to 4Kb boundaries
|
// align start to 4Kb boundary
|
||||||
1: add $0xFFF, %ebx
|
1: add $0xFFF, %eax
|
||||||
and $0xFFFFF000, %ebx
|
and $0xFFFFF000, %eax
|
||||||
and $0xFFFFF000, %ecx
|
|
||||||
|
|
||||||
sub %ebx, %ecx
|
// Is this entry larger than current heap?
|
||||||
cmp heap_size, %ecx
|
mov %ecx, %edx
|
||||||
|
sub %eax, %edx
|
||||||
|
mov heap_end, %ebx
|
||||||
|
sub heap_ptr, %ebx
|
||||||
|
cmp %ebx, %edx
|
||||||
jbe _mhnext
|
jbe _mhnext
|
||||||
|
|
||||||
mov %ebx, heap_start
|
// If so, switch to it.
|
||||||
mov %ecx, heap_size
|
mov %eax, heap_ptr
|
||||||
|
mov %ecx, heap_end
|
||||||
jmp _mhnext
|
jmp _mhnext
|
||||||
|
|
||||||
_mhdone: ret
|
_mhdone: ret
|
||||||
|
|
||||||
// alloc: take ECX bytes from heap, return ptr in EAX
|
.set PXE_TFTP_OPEN, 0x0020
|
||||||
// The allocation does not get marked in the memmap.
|
.set PXE_TFTP_CLOSE, 0x0021
|
||||||
// No realignment is performed, so only alloc aligned sizes.
|
.set PXE_TFTP_READ, 0x0022
|
||||||
alloc: cmp heap_size, %ecx
|
|
||||||
ja _aerr
|
read_file: push %bp
|
||||||
mov heap_start, %eax
|
mov %sp, %bp
|
||||||
add %ecx, heap_start
|
|
||||||
sub %ecx, heap_size
|
call tftp_open
|
||||||
ret
|
mov heap_ptr, %ebx
|
||||||
_aerr: mov $msg_aerr, %si
|
mov %ebx, %edi
|
||||||
call print
|
|
||||||
jmp hang
|
_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
|
||||||
|
|
||||||
read_file:
|
|
||||||
.set PXE_TFTP_OPEN, 0x0020
|
|
||||||
push $PACKET_SIZE
|
push $PACKET_SIZE
|
||||||
push $69<<8
|
push $69<<8
|
||||||
sub $128, %sp
|
mov $128, %ecx
|
||||||
|
sub %cx, %sp
|
||||||
mov $fn_config, %esi
|
mov %ss, %edi
|
||||||
mov %ss, %ax
|
shl $4, %edi
|
||||||
mov %ax, %es
|
add %esp, %edi
|
||||||
mov %esp, %edi
|
addr32 rep movsb
|
||||||
mov $fn_config_l, %ecx
|
|
||||||
rep movsb
|
|
||||||
|
|
||||||
push $0
|
push $0
|
||||||
push $0
|
push $0
|
||||||
|
push server_ip+2
|
||||||
sub $4, %sp
|
push server_ip
|
||||||
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 $0
|
push $0
|
||||||
pxe_call PXE_TFTP_OPEN
|
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 %cs
|
||||||
push $tx_buf
|
push $tx_buf
|
||||||
push $0
|
push $0
|
||||||
push $0
|
push $0
|
||||||
push $0
|
push $0
|
||||||
pxe_call PXE_TFTP_READ
|
pxe_call PXE_TFTP_READ
|
||||||
add $10, %sp
|
mov %sp, %bx
|
||||||
|
mov %ss:4(%bx), %ax
|
||||||
|
|
||||||
|
|
||||||
.set PXE_TFTP_CLOSE, 0x0021
|
|
||||||
push $0
|
|
||||||
pxe_call PXE_TFTP_CLOSE
|
|
||||||
add $2, %sp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mov $tx_buf, %si
|
|
||||||
call print
|
|
||||||
|
|
||||||
|
leave
|
||||||
|
pop %ebx
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
out_of_mem: mov $msg_memerr, %si
|
||||||
|
call print
|
||||||
|
jmp hang
|
||||||
|
|
||||||
// print: print NUL-terminated string pointed to by SI
|
// print: print NUL-terminated string pointed to by SI
|
||||||
print: xor %bx, %bx
|
print: xor %bx, %bx
|
||||||
|
|
||||||
|
|
@ -435,16 +488,17 @@ msg_read: .asciz " * Retrieving\r\n"
|
||||||
msg_long: .asciz " * Long Mode\r\n"
|
msg_long: .asciz " * Long Mode\r\n"
|
||||||
msg_pserr: .asciz "panic: Missing !PXE structure.\r\n"
|
msg_pserr: .asciz "panic: Missing !PXE structure.\r\n"
|
||||||
msg_pcerr: .asciz "panic: PXE call failed.\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"
|
fn_initrd: .asciz "initrd"
|
||||||
.set fn_config_l, .-fn_config
|
|
||||||
|
|
||||||
pxe_api: .long 0
|
pxe_api: .long 0
|
||||||
server_ip: .space 4
|
server_ip: .space 4
|
||||||
|
|
||||||
heap_start: .long 0
|
heap_ptr: .long 0
|
||||||
heap_size: .long 0
|
heap_end: .long 0
|
||||||
|
|
||||||
|
initrd_ptr: .long 0
|
||||||
|
|
||||||
// Long mode initial page tables
|
// Long mode initial page tables
|
||||||
pd_ptr: .long 0
|
pd_ptr: .long 0
|
||||||
|
|
@ -466,6 +520,8 @@ trampo64:
|
||||||
mov %eax, %fs
|
mov %eax, %fs
|
||||||
mov %eax, %gs
|
mov %eax, %gs
|
||||||
mov %eax, %ss
|
mov %eax, %ss
|
||||||
|
|
||||||
|
mov initrd_ptr, %edi
|
||||||
jmp loader_main
|
jmp loader_main
|
||||||
|
|
||||||
// ToDo List:
|
// ToDo List:
|
||||||
|
|
|
||||||
26
loader.c
26
loader.c
|
|
@ -128,9 +128,19 @@ typedef struct {
|
||||||
} Elf64_Phdr;
|
} Elf64_Phdr;
|
||||||
|
|
||||||
static void
|
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
|
static int
|
||||||
|
|
@ -175,21 +185,21 @@ load_elf(file_t file, uintptr_t *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
loader_main(void)
|
loader_main(void *initrd)
|
||||||
{
|
{
|
||||||
uint64_t entry = 0;
|
uint64_t entry = 0;
|
||||||
file_t elf = { 0, 0 };
|
file_t elf = { initrd, 1024 };
|
||||||
if (load_elf(elf, &entry) < 0) {
|
if (load_elf(elf, &entry) < 0) {
|
||||||
panic();
|
panic("panic: Malformed ELF64 executable");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t stack = 0;
|
uint64_t stack = 0;
|
||||||
|
|
||||||
__asm__ __volatile__ (
|
__asm__ __volatile__ (
|
||||||
"mov\t%1, %rbp\n\t"
|
"mov\t%1, %%rbp\n\t"
|
||||||
"mov\t%rbp, %rsp\n\t"
|
"mov\t%%rbp, %%rsp\n\t"
|
||||||
"call\t*%0"
|
"call\t*%0"
|
||||||
: : "r"(entry), "r"(stack) : "memory");
|
: : "r"(entry), "r"(stack) : "memory");
|
||||||
|
|
||||||
panic();
|
panic("panic: Ran past kernel invocation");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue