Compare commits

...

10 commits

15 changed files with 544 additions and 118 deletions

6
.gitignore vendored
View file

@ -1,3 +1,5 @@
*.o
boot.bin
boot.elf
*.swp
/boot.bin
/boot.elf
/serve

View file

@ -1,11 +1,19 @@
-include config.mk
OBJS=\
src/nbp.o \
src/bios.o \
src/pxe.o \
src/std.o \
src/main.o \
# end of object list
.PHONY: all clean
all: boot.bin
clean:
rm -f *.o boot.elf boot.bin
rm -f $(OBJS) boot.elf boot.bin
config.mk: | config.default.mk
cp config.default.mk $@
@ -14,11 +22,11 @@ boot.bin: boot.elf
objcopy -O binary -j .text -j .data boot.elf $@
wc -c $@
boot.elf: Rnbp.o Ptftp.o Qcommon.o fernlader.ld
$(LD) $(LDFLAGS) -o $@ Rnbp.o Ptftp.o Qcommon.o
boot.elf: $(OBJS) src/nbp.ld
$(LD) $(LDFLAGS) -T src/nbp.ld -o $@ $(OBJS)
%.o: %.S
$(CC) $(CFLAGS) -c -o $@ $(@:.o=.S)
$(CC) $(CFLAGS) -c -o $@ $(@:.o=.S) $(CPPFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -O0 -c -o $@ $(@:.o=.c)
$(CC) $(CFLAGS) -O0 -c -o $@ $(@:.o=.c) $(CPPFLAGS)

View file

View file

@ -1,38 +0,0 @@
#include <stdint.h>
#define COM1_BASE_PORT 0x3F8
static inline uint8_t
inb(uint16_t port)
{
uint8_t data;
__asm__ ("inb %%dx, %%al" : "=a"(data) : "d"(port));
return data;
}
static inline void
outb(uint16_t port, uint8_t data)
{
__asm__ ("outb %%al, %%dx" :: "a"(data), "d"(port));
}
void serial_write_char(char c)
{
while (1) {
int line_status = inb(COM1_BASE_PORT + 5);
if (line_status & 0x20) {
break;
}
}
outb(COM1_BASE_PORT, c);
}
void
main()
{
outb(0xE9, 'X');
serial_write_char('X');
for (;;) {
__asm__ ("hlt" :);
}
}

View file

@ -1,5 +1,8 @@
CC = cc
#CC = /usr/local/x86_64-karlos-toolchain/bin/x86_64-karlos-gcc
#LD = /usr/local/x86_64-karlos-toolchain/bin/x86_64-karlos-ld
CC = gcc
LD = ld
CFLAGS = -no-pie -fno-pic -fno-stack-protector
LDFLAGS = -Tfernlader.ld
CFLAGS = -no-pie -fno-pic -fno-stack-protector -nostdinc -ffreestanding -m32
CPPFLAGS = -I3rdparty/include
LDFLAGS = -m elf_i386

2
run-dnsmasq.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
dnsmasq -u "$USER" --conf-file=dnsmasq.conf "$@"

125
src/bios.S Normal file
View file

@ -0,0 +1,125 @@
// vim: ft=gas:et:sw=12:ts=12:sts=12
.include "src/mode.S"
.global bios_write
.code32
bios_write: push %ebp
mov %esp, %ebp
push %ebx
mov 8+0(%ebp), %edx
PROT16
REAL
.bwloop: cmpb $0, (%edx)
jz .bwreturn
xor %bx, %bx
mov $0x0E, %ah
mov (%edx), %al
push %edx
int $0x10
pop %edx
add $1, %edx
jmp .bwloop
.bwreturn: PROT
PROT32
pop %ebx
leave
ret
.global bios_getmap
.code32
bios_getmap:push %ebp
mov %esp, %ebp
push %ebx
push %edi
mov 8+0(%ebp), %edi
PROT16
REAL
xor %ebx, %ebx
_gmnext: movl $0, 20(%di)
mov $0x534D4150, %edx // e820 magic number
mov $24, %ecx
mov $0xE820, %eax
int $0x15
jc _gmdone
test %ebx, %ebx
jz _gmdone
add $24, %di
jmp _gmnext
_gmdone: add $24, %di
PROT
PROT32
mov %edi, %eax
pop %edi
pop %ebx
leave
ret
.global bios_func
bios_func: push %ebp
mov %esp, %ebp
push %ebx
push %ebp
push %edi
push %esi
// The interrupt number is given as an immediate value to INT.
// To support any interrupt number, we use self-modifying code.
mov 8+0(%ebp), %eax
mov %al, _bf_int+1
PROT16
REAL
mov %ds:8+4(%ebp), %edi
mov 0(%edi), %eax
mov 4(%edi), %ecx
mov 8(%edi), %edx
mov 12(%edi), %ebx
mov 16(%edi), %ebp
mov 20(%edi), %esi
mov 24(%edi), %edi
// This instruction will get written at runtime
_bf_int: .byte 0xCD, 0 // INT imm8
push $0
pushf
// FIXME broken BP
push %edi
mov 8+4(%ebp), %edi
mov %eax, 0(%edi)
mov %ecx, 4(%edi)
mov %edx, 8(%edi)
mov %ebx, 12(%edi)
mov %ebp, 16(%edi)
mov %esi, 20(%edi)
pop %eax
mov %eax, 24(%edi)
PROT
PROT32
pop %eax
pop %esi
pop %edi
pop %ebp
pop %ebx
leave
ret

197
src/main.c Normal file
View file

@ -0,0 +1,197 @@
#include "std.h"
#include <bootboot.h>
#define COM1_BASE_PORT 0x3F8
static inline uint8_t
inb(uint16_t port)
{
uint8_t data;
__asm__ ("inb %%dx, %%al" : "=a"(data) : "d"(port));
return data;
}
static inline void
outb(uint16_t port, uint8_t data)
{
__asm__ ("outb %%al, %%dx" :: "a"(data), "d"(port));
}
extern void bios_write(const char *msg);
extern void *bios_getmap(void *buffer);
extern BOOTBOOT bootboot;
#define E820_TYPE_FREE 0x1
#define E820_TYPE_RESERVED 0x2
#define E820_TYPE_RECLAIMABLE 0x3
#define E820_TYPE_NONVOLATILE 0x4
#define E820_TYPE_BADMEM 0x5
struct e820_entry {
uint64_t base;
uint64_t length;
uint32_t type;
uint32_t xattr;
};
struct e820_entry memmap[128];
struct vesa_info {
char signature[4];
uint16_t version;
uint16_t oem_name[2];
uint8_t capab[4];
uint16_t video_mode_offset;
uint16_t video_mode_segment;
uint16_t num_64k_blocks;
uint8_t reserved[492];
};
extern uint16_t bios_func(uint8_t inum, uint32_t reg[]);
void
blank_screen(uint16_t attr)
{
uint32_t reg[7];
reg[0] = (0x06 << 8);
reg[1] = 0;
reg[2] = 0xFFFF;
reg[3] = attr << 8;
reg[4] = 0;
reg[5] = 0;
reg[6] = 0;
bios_func(0x10, reg);
}
void
move_cursor(uint8_t row, uint8_t col)
{
uint32_t reg[7];
reg[0] = (0x02 << 8);
reg[1] = 0;
reg[2] = (row << 8) | col;
reg[3] = 0;
reg[4] = 0;
reg[5] = 0;
reg[6] = 0;
bios_func(0x10, reg);
}
void
display_string(uint16_t attr, const char *str)
{
uint32_t reg[7];
reg[0] = (0x13 << 8) | 0x01;
reg[1] = strlen(str);
reg[2] = 0xFFFF;
reg[3] = attr;
reg[4] = (uint32_t)str;
reg[5] = 0;
reg[6] = 0;
bios_func(0x10, reg);
}
#define PG_PRESENT 0x001
#define PG_WRITE 0x002
#define PG_USER 0x004
#define PG_SIZE 0x080
#define PG_GLOBAL 0x100
__attribute__((aligned(4096))) uint64_t pml4[512];
__attribute__((aligned(4096))) uint64_t pdp[512];
__attribute__((aligned(4096))) uint64_t pd[512];
uint64_t
pg_setup()
{
pml4[0] = (uint64_t)(uint32_t)pdp | PG_WRITE | PG_PRESENT;
pdp[0] = (uint64_t)(uint32_t)pd | PG_WRITE | PG_PRESENT;
for (unsigned i = 0; i < 512; i++) {
pd[i] = (i * (uint64_t)0x200000) | PG_WRITE | PG_PRESENT | PG_SIZE;
}
uint64_t cr3 = (uint64_t)(uint32_t)pml4;
return cr3;
}
struct PXENV {
uint8_t signature[6];
uint16_t version;
uint8_t length;
uint8_t checksum;
uint32_t rmentry;
uint32_t pmoffset;
};
struct exPXE {
uint8_t signature[4];
uint8_t length;
uint8_t checksum;
uint8_t revision;
uint8_t reserved;
uint32_t undiromid;
uint32_t bcromid;
uint32_t rmentry;
uint32_t pmentry;
};
//extern uint32_t pxe_call(uint16_t func, offset, segment);
extern struct PXENV *PXENV;
extern struct exPXE *exPXE;
void
main()
{
blank_screen(0x10);
move_cursor(0, 0);
//display_string(0x14, "Netboot via fernlader v2 ...\r\n");
bios_write("Going well ...\r\n");
if (memcmp(PXENV->signature, "PXENV+", 6) != 0) {
bios_write("missing PXENV+ signature\r\n");
}
if (PXENV->version >= 0x0201) {
bios_write("!PXE version\r\n");
if (memcmp(exPXE->signature, "!PXE", 4) != 0) {
bios_write("missing !PXE signature\r\n");
}
}
memcpy(bootboot.magic, BOOTBOOT_MAGIC, sizeof bootboot.magic);
bootboot.size = 128;
bootboot.protocol = PROTOCOL_MINIMAL | LOADER_BIOS;
bootboot.numcores = 1;
//display_string(0x0E, "init\r\n");
struct e820_entry *end = bios_getmap(memmap);
//display_string(0x0E, "karlos\r\n");
for (int i = 0; ; i++) {
if (&memmap[i] >= end) break;
char buf[2];
buf[0] = memmap[i].type < 10 ? memmap[i].type + '0' : '?';
buf[1] = 0;
bios_write(buf);
}
pg_setup();
#if 0
uint32_t reg[7];
reg[0] = (0x0E << 8) + 'J';
reg[1] = 0;
reg[2] = 0;
reg[3] = 0;
reg[4] = 0;
reg[5] = 0;
reg[6] = 0;
bios_func(0x10, reg);
bios_write("what?\r\n");
#endif
for (;;) {
__asm__ ("hlt");
}
}

57
src/mode.S Normal file
View file

@ -0,0 +1,57 @@
// vim: ft=gas:et:sw=12:ts=12:sts=12
.set SS_CODE16, 0x08
.set SS_DATA16, 0x10
.set SS_CODE32, 0x18
.set SS_DATA32, 0x20
.macro PROT16
.code32
mov $SS_DATA16, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
ljmp $SS_CODE16, $9f
9: .code16
.endm
.macro PROT32
.code16
mov $SS_DATA32, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
ljmp $SS_CODE32, $9f
9: .code32
.endm
.macro REAL
mov %cr0, %eax
and $0xFFFE, %ax
mov %eax, %cr0
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov real_ss, %ax
mov %ax, %ss
shl $4, %eax
sub %eax, %esp
ljmp $0x0000, $9f
9:
.endm
.macro PROT
mov %cr0, %eax
or $1, %eax
mov %eax, %cr0
xor %eax, %eax
mov $SS_DATA16, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov real_ss, %ax
shl $4, %eax
add %eax, %esp
.endm

View file

@ -1,34 +1,45 @@
// vim: et:sw=12:ts=12:sts=12
// vim: ft=gas:et:sw=12:ts=12:sts=12
.include "src/mode.S"
.global _start
.text
.code16
.set SS_CODE16, 0x08
.set SS_DATA16, 0x10
.set SS_CODE32, 0x18
.set SS_DATA32, 0x20
// _start: entry point
_start: cli
cld
mov %ss, %ax
mov %ax, %cs:real_ss
xor %eax, %eax
mov %es, %ax
shl $4, %eax
movzwl %bx, %ebx
add %eax, %ebx
mov %ebx, %cs:PXENV
xor %eax, %eax
xor %ebx, %ebx
mov %sp, %bx
mov %ss:6(%bx), %ax
mov %ss:4(%bx), %bx
shl $4, %eax
add %eax, %ebx
mov %ebx, %cs:exPXE
mov %sp, %bp
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
push $msg_start
call dbgmsg
add $2, %sp
// initialize our own BSS section
mov $_bss_start, %di
mov $_bss_end, %cx
sub %di, %cx
xor %al, %al
rep stosb
rep stosb
// a20_enable: Allow use of 'high' (>1Mb) memory
a20_enable: // Of all the ways to toggle A20, we only try the Fast A20 Gate.
@ -36,69 +47,31 @@ a20_enable: // Of all the ways to toggle A20, we only try the Fast A20 Gate.
// but as our bootloader exclusively runs on 64-bit machines,
// we should not run into any of those systems.
// Modern machines apparently don't even have the A20 line anymore.
push $msg_a20
call dbgmsg
add $2, %sp
inb $0x92, %al
or $2, %al
outb %al, $0x92
// prot_enter: Set up GDT, switch into 32-bit protected mode.
prot_enter:
push $msg_prot
call dbgmsg
add $2, %sp
prot_enter: lgdt GDT_PTR
lgdt GDT_PTR
// Set Protection Enable (PE) bit
mov %cr0, %eax
or $1, %al
mov %eax, %cr0
ljmp $SS_CODE32, $prot_trampo
PROT
PROT32
.code32
prot_trampo:
mov $SS_DATA32, %eax
mov %eax, %ds
mov %eax, %es
mov %eax, %fs
mov %eax, %gs
mov %eax, %ss
mov 'Y', %al
outb %al, $0xE9
// TODO load proper stack pointer
mov $0x90000, %ebp
mov %ebp, %esp
jmp main
.code16
dbgmsg: push %bp
mov %sp, %bp
push %ax
push %si
mov 4(%bp), %si
1: lodsb
test %al, %al
jz 2f
outb %al, $0xE9
jmp 1b
2: pop %si
pop %ax
pop %bp
ret
.data
.global GDT
.global PXENV
PXENV: .long 0
.global exPXE
exPXE: .long 0
.global real_ss
real_ss: .word 0
.global GDT
GDT: // entry 0: null descriptor
.space 8, 0
// entry 1: 16-bit code segment
@ -112,8 +85,6 @@ GDT: // entry 0: null descriptor
// TODO: 32-bit TSS
.set GDT_SIZE, . - GDT
GDT_PTR: .word GDT_SIZE - 1
.quad GDT
.word GDT
.word 0, 0, 0
msg_start: .asciz "Netboot via fernlader v2 ...\r\n"
msg_a20: .asciz " * Enabling A20 Gate\r\n"
msg_prot: .asciz " * Protected Mode\r\n"

View file

@ -4,7 +4,7 @@ PHDRS {
}
SECTIONS {
.text 0x7C00: {
Rnbp.o(.text)
src/nbp.o(.text)
*(.text*, .rodata*)
} :all
.data : {
@ -15,7 +15,8 @@ SECTIONS {
*(COMMON)
*(.bss)
. = ALIGN(4K);
*(.bootboot)
bootboot = .;
. += 4K;
_bss_end = .;
} :all
}

24
src/pxe.S Normal file
View file

@ -0,0 +1,24 @@
// vim: ft=gas:et:sw=12:ts=12:sts=12
.include "src/mode.S"
.data
pxe_entry: .word 0
.text
.global pxe_call
.code32
pxe_call: push %ebp
mov %esp, %ebp
PROT16
call (pxe_entry)
push %eax
PROT32
pop %eax
leave
ret

31
src/std.c Normal file
View file

@ -0,0 +1,31 @@
#include "std.h"
void *
memcpy(void *dst, const void *src, size_t n)
{
void *di = dst;
__asm__ ("rep movsb"
: "+D"(di), "+S"(src), "+c"(n)
:
: "cc", "memory");
return dst;
}
int
memcmp(const void *src1, const void *src2, size_t n)
{
__asm__ ("repe cmpsb"
: "+D"(src2), "+S"(src1), "+c"(n)
:
: "cc", "memory");
if (n == 0) return 0;
return src1 < src2 ? -1 : 1;
}
size_t
strlen(const char *s)
{
size_t l = 0;
while (s[l]) l++;
return l;
}

43
src/std.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef FERNLADER_STD_H
#define FERNLADER_STD_H
/* Since fernlader may be built with bog-standard GCC C compiler
* (not a freestanding cross compiler), we can't rely on any of
* the compiler-provided definitions like those in stdint.h.
*/
// stddef.h
#define NULL ((void *)0)
typedef unsigned long size_t;
// stdint.h
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
// stdbool.h
#define true ((_Bool)1)
#define false ((_Bool)0)
typedef _Bool bool;
// string.h
void *memcpy (void *dst, const void *src, size_t n);
void *memmove(void *dst, const void *src, size_t n);
void *memset (void *dst, int c, size_t n);
int memcmp (const void *src1, const void *src2, size_t n);
size_t strlen(const char *s);
#endif