WIP generic bios func wrapper

This commit is contained in:
Thomas Oltmann 2026-02-18 13:40:19 +01:00
parent a4470b4733
commit 0c14013b9d
8 changed files with 201 additions and 75 deletions

View file

@ -25,7 +25,7 @@ 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

@ -3,5 +3,6 @@
CC = gcc
LD = ld
CFLAGS = -no-pie -fno-pic -fno-stack-protector -nostdinc -m32
CFLAGS = -no-pie -fno-pic -fno-stack-protector -nostdinc -ffreestanding -m32
CPPFLAGS = -I3rdparty/include
LDFLAGS = -m elf_i386

View file

@ -9,27 +9,22 @@ bios_write: push %ebp
push %ebx
mov 8+0(%ebp), %edx
mov 8+4(%ebp), %ecx
PROT16
REAL
.bwloop: test %ecx, %ecx
.bwloop: cmpb $0, (%edx)
jz .bwreturn
xor %bx, %bx
mov $0x0E, %ah
mov (%edx), %al
push %ecx
push %edx
int $0x10
pop %edx
pop %ecx
add $1, %edx
sub $1, %ecx
jmp .bwloop
.bwreturn: PROT
@ -77,3 +72,57 @@ _gmdone: add $24, %di
pop %ebp
ret
.global bios_func
bios_func: push %ebp
mov %esp, %ebp
push %ebx
push %ebp
push %edi
push %esi
mov 8+0(%ebp), %eax
mov 8+4(%ebp), %edi
// The interrupt number is given as an immediate value to INT.
// To support any interrupt number, we use self-modifying code.
mov %al, _bf_int+1
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
PROT16
REAL
// This instruction will get written at runtime
_bf_int: .byte 0xCD, 0 // INT imm8
push $0
pushf
PROT
PROT32
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)
pop %eax
pop %esi
pop %edi
pop %ebp
pop %ebx
mov %ebp, %esp
pop %ebp
ret

View file

@ -1,8 +1,6 @@
//#include <stdint.h>
#include "std.h"
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
#include <bootboot.h>
#define COM1_BASE_PORT 0x3F8
@ -20,44 +18,98 @@ 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 serial_write(const char *msg)
{
for (const char *c = msg; *c; c++) {
outb(0xE9, *c);
//serial_write_char(*c);
}
}
extern void bios_write(const char *msg, unsigned length);
extern void bios_write(const char *msg);
extern void *bios_getmap(void *buffer);
unsigned char memmap[24 * 128];
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
main()
{
bios_write("init\r\n", 6);
void *end = bios_getmap(memmap);
bios_write("karlos\r\n", 8);
bios_write("Netboot via fernlader v2 ...\r\n");
memcpy(bootboot.magic, BOOTBOOT_MAGIC, sizeof bootboot.magic);
bootboot.size = 128;
bootboot.protocol = PROTOCOL_MINIMAL | LOADER_BIOS;
bootboot.numcores = 1;
bios_write("init\r\n");
struct e820_entry *end = bios_getmap(memmap);
bios_write("karlos\r\n");
for (int i = 0; ; i++) {
unsigned char *entry = memmap + 24 * i;
if ((void *)entry >= end) break;
unsigned char type = *(entry + 16);
char c = type < 10 ? type + '0' : '?';
bios_write(&c, 1);
if (&memmap[i] >= end) break;
char buf[2];
buf[0] = memmap[i].type < 10 ? memmap[i].type + '0' : '?';
buf[1] = 0;
bios_write(buf);
}
#if 0
char foo[24];
bios_write("bios_func():\r\n");
uint32_t reg[6];
reg[0] = 0xE820;
reg[1] = 24;
reg[2] = 0x534D4150;
reg[3] = 0;
reg[4] = (uint32_t)&foo;
reg[5] = 0;
uint16_t flags = bios_func(0x15, reg);
const char *hex_digits = "0123456789ABCDEF";
char buf[100];
buf[0] = hex_digits[(flags >> 12) & 0xF];
buf[1] = hex_digits[(flags >> 8) & 0xF];
buf[2] = hex_digits[(flags >> 4) & 0xF];
buf[3] = hex_digits[(flags >> 0) & 0xF];
buf[4] = ' ';
buf[5] = hex_digits[(foo[16] >> 0) & 0xF];
buf[6] = ' ';
buf[7] = 0;
bios_write(buf);
#endif
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);
for (;;) {
__asm__ ("hlt" :);
__asm__ ("hlt");
}
}

View file

@ -30,21 +30,12 @@ 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
lgdt GDT_PTR
prot_enter: lgdt GDT_PTR
PROT
PROT32
@ -52,24 +43,6 @@ prot_enter:
.code32
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 real_ss
@ -92,6 +65,3 @@ GDT_PTR: .word GDT_SIZE - 1
.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

@ -15,7 +15,8 @@ SECTIONS {
*(COMMON)
*(.bss)
. = ALIGN(4K);
*(.bootboot)
bootboot = .;
. += 4K;
_bss_end = .;
} :all
}

View file

@ -0,0 +1,12 @@
#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)
:
: "memory");
return dst;
}

View file

@ -0,0 +1,41 @@
#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);
#endif