2026-02-18 13:40:19 +01:00
|
|
|
#include "std.h"
|
2026-02-18 00:57:22 +01:00
|
|
|
|
2026-02-18 13:40:19 +01:00
|
|
|
#include <bootboot.h>
|
2026-02-17 19:44:29 +01:00
|
|
|
|
|
|
|
|
#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));
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-18 13:40:19 +01:00
|
|
|
extern void bios_write(const char *msg);
|
|
|
|
|
extern void *bios_getmap(void *buffer);
|
2026-02-17 19:44:29 +01:00
|
|
|
|
2026-05-21 14:44:16 +02:00
|
|
|
void
|
|
|
|
|
debug_write(const char *msg)
|
|
|
|
|
{
|
|
|
|
|
for (const char *c = msg; *c; c++) {
|
|
|
|
|
outb(0xE9, *c);
|
|
|
|
|
}
|
|
|
|
|
bios_write(msg);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-18 13:40:19 +01:00
|
|
|
extern BOOTBOOT bootboot;
|
2026-02-18 00:39:02 +01:00
|
|
|
|
2026-02-18 13:40:19 +01:00
|
|
|
#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];
|
2026-02-18 01:27:49 +01:00
|
|
|
|
2026-02-18 13:40:19 +01:00
|
|
|
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];
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-21 16:45:39 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2026-02-18 00:39:02 +01:00
|
|
|
|
2026-05-21 14:44:16 +02:00
|
|
|
void
|
|
|
|
|
debug_write_uint(unsigned v)
|
|
|
|
|
{
|
|
|
|
|
char buf[20], *p = buf + sizeof buf;
|
|
|
|
|
p--;
|
|
|
|
|
*p = 0;
|
|
|
|
|
do {
|
|
|
|
|
p--;
|
|
|
|
|
*p = (v % 10) + '0';
|
|
|
|
|
v /= 10;
|
|
|
|
|
} while (v);
|
|
|
|
|
debug_write(p);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-21 14:13:24 +01:00
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-28 15:57:51 +01:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-21 15:57:46 +02:00
|
|
|
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;
|
|
|
|
|
uint8_t caddr[16];
|
|
|
|
|
uint8_t sname[64];
|
|
|
|
|
uint8_t bootfile[128];
|
|
|
|
|
uint8_t vendor[64];
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-21 14:44:16 +02:00
|
|
|
extern uint32_t pxe_entry;
|
|
|
|
|
extern uint32_t pxe_call(uint16_t func, uint16_t segment, uint16_t offset);
|
2026-02-28 15:57:51 +01:00
|
|
|
|
2026-05-21 14:44:16 +02:00
|
|
|
extern unsigned char pxe_cmd_buf[512];
|
2026-06-24 21:32:19 +02:00
|
|
|
extern unsigned char pxe_dat_buf[512];
|
2026-02-28 15:57:51 +01:00
|
|
|
extern struct PXENV *PXENV;
|
|
|
|
|
extern struct exPXE *exPXE;
|
|
|
|
|
|
2026-05-21 14:44:16 +02:00
|
|
|
#define PXENV_GET_CACHED 0x0071
|
|
|
|
|
#define PXENV_TFTP_OPEN 0x0020
|
2026-06-24 21:32:19 +02:00
|
|
|
#define PXENV_TFTP_CLOSE 0x0021
|
2026-06-24 20:37:07 +02:00
|
|
|
#define PXENV_TFTP_READ 0x0022
|
2026-05-21 14:44:16 +02:00
|
|
|
|
|
|
|
|
typedef struct __attribute__((packed)) s_PXENV_GET_CACHED {
|
|
|
|
|
uint16_t status;
|
|
|
|
|
uint16_t packet_type;
|
|
|
|
|
uint16_t buffer_size;
|
|
|
|
|
uint16_t buffer_offset;
|
|
|
|
|
uint16_t buffer_seg;
|
|
|
|
|
uint16_t buffer_limit;
|
|
|
|
|
} t_PXENV_GET_CACHED;
|
|
|
|
|
|
|
|
|
|
typedef struct __attribute__((packed)) s_PXENV_TFTP_OPEN {
|
|
|
|
|
uint16_t status;
|
|
|
|
|
uint32_t server_ip;
|
|
|
|
|
uint32_t gateway_ip;
|
|
|
|
|
unsigned char filename[128];
|
|
|
|
|
uint16_t tftp_port;
|
|
|
|
|
uint16_t packet_size;
|
|
|
|
|
} t_PXENV_TFTP_OPEN;
|
|
|
|
|
|
2026-06-24 20:37:07 +02:00
|
|
|
typedef struct __attribute__((packed)) s_PXENV_TFTP_READ {
|
|
|
|
|
uint16_t status;
|
|
|
|
|
uint16_t packet_number;
|
|
|
|
|
uint16_t buffer_size;
|
|
|
|
|
uint16_t buffer_offset;
|
|
|
|
|
uint16_t buffer_seg;
|
|
|
|
|
} t_PXENV_TFTP_READ;
|
|
|
|
|
|
2026-06-24 21:32:19 +02:00
|
|
|
typedef struct __attribute__((packed)) s_PXENV_TFTP_CLOSE {
|
|
|
|
|
uint16_t status;
|
|
|
|
|
} t_PXENV_TFTP_CLOSE;
|
|
|
|
|
|
2026-06-03 22:58:10 +02:00
|
|
|
/**
|
|
|
|
|
* return type for fs drivers
|
|
|
|
|
*/
|
|
|
|
|
typedef struct {
|
|
|
|
|
uint8_t *ptr;
|
|
|
|
|
uint32_t size;
|
|
|
|
|
} file_t;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* convert ascii octal number to binary number
|
|
|
|
|
*/
|
|
|
|
|
int octbin(unsigned char *str,int size)
|
|
|
|
|
{
|
|
|
|
|
int s=0;
|
|
|
|
|
unsigned char *c=str;
|
|
|
|
|
while(size-->0){
|
|
|
|
|
s*=8;
|
|
|
|
|
s+=*c-'0';
|
|
|
|
|
c++;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* convert ascii hex number to binary number
|
|
|
|
|
*/
|
|
|
|
|
int hexbin(unsigned char *str, int size)
|
|
|
|
|
{
|
|
|
|
|
int v=0;
|
|
|
|
|
while(size-->0){
|
|
|
|
|
v <<= 4;
|
|
|
|
|
if(*str>='0' && *str<='9')
|
|
|
|
|
v += (int)((unsigned char)(*str)-'0');
|
|
|
|
|
else if(*str >= 'A' && *str <= 'F')
|
|
|
|
|
v += (int)((unsigned char)(*str)-'A'+10);
|
|
|
|
|
str++;
|
|
|
|
|
}
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define PRINTFS(fsname,kernel) do {\
|
|
|
|
|
debug_write(fsname);\
|
|
|
|
|
debug_write(" ");\
|
|
|
|
|
debug_write(kernel);\
|
|
|
|
|
debug_write("\r\n");\
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#include "fs.h"
|
|
|
|
|
|
2026-06-24 20:37:07 +02:00
|
|
|
uint32_t pxe_sip;
|
|
|
|
|
uint32_t pxe_gip;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
get_cached()
|
|
|
|
|
{
|
|
|
|
|
t_PXENV_GET_CACHED *t_cached = (void *)pxe_cmd_buf;
|
|
|
|
|
t_cached->status = 0;
|
|
|
|
|
t_cached->packet_type = 2;
|
|
|
|
|
t_cached->buffer_size = 0;
|
|
|
|
|
t_cached->buffer_offset = 0;
|
|
|
|
|
t_cached->buffer_seg = 0;
|
|
|
|
|
t_cached->buffer_limit = 0xFFFFu;
|
|
|
|
|
debug_write("Performing PXE call\r\n");
|
|
|
|
|
unsigned ret = pxe_call(PXENV_GET_CACHED, (uint16_t)(uintptr_t)pxe_cmd_buf, 0x0000);
|
|
|
|
|
debug_write("Still alive!\r\n");
|
|
|
|
|
debug_write("ret=");
|
|
|
|
|
debug_write_uint(ret);
|
|
|
|
|
debug_write("\r\nstatus=");
|
|
|
|
|
debug_write_uint(t_cached->status);
|
|
|
|
|
debug_write("\r\n");
|
|
|
|
|
|
|
|
|
|
debug_write("buffer_off=");
|
|
|
|
|
debug_write_uint(t_cached->buffer_offset);
|
|
|
|
|
debug_write("\r\nbuffer_seg=");
|
|
|
|
|
debug_write_uint(t_cached->buffer_seg);
|
|
|
|
|
struct bootph *bootph = (void *)(t_cached->buffer_offset + 16 * t_cached->buffer_seg);
|
|
|
|
|
pxe_sip = bootph->sip;
|
|
|
|
|
pxe_gip = bootph->gip;
|
|
|
|
|
debug_write("\r\nsip=");
|
|
|
|
|
debug_write_uint(pxe_sip);
|
|
|
|
|
debug_write("\r\ngip=");
|
|
|
|
|
debug_write_uint(pxe_gip);
|
|
|
|
|
debug_write("\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
receive_file(void)
|
|
|
|
|
{
|
|
|
|
|
unsigned ret;
|
|
|
|
|
|
|
|
|
|
t_PXENV_TFTP_OPEN *t_open = (void *)pxe_cmd_buf;
|
|
|
|
|
t_open->status = 0;
|
|
|
|
|
t_open->server_ip = pxe_sip;
|
|
|
|
|
t_open->gateway_ip = pxe_gip;
|
|
|
|
|
memcpy(t_open->filename, "/initrd", 8);
|
|
|
|
|
t_open->tftp_port = 69 << 8;
|
|
|
|
|
t_open->packet_size = 512;
|
|
|
|
|
debug_write("Performing PXE OPEN call\r\n");
|
|
|
|
|
ret = pxe_call(PXENV_TFTP_OPEN, (uint16_t)(uintptr_t)pxe_cmd_buf, 0x0000);
|
|
|
|
|
debug_write("ret=");
|
|
|
|
|
debug_write_uint(ret);
|
|
|
|
|
debug_write(" status=");
|
|
|
|
|
debug_write_uint(t_open->status);
|
|
|
|
|
debug_write("\r\n");
|
|
|
|
|
|
|
|
|
|
uint16_t packet_size = t_open->packet_size;
|
2026-06-24 21:32:19 +02:00
|
|
|
debug_write("packet_size=");
|
|
|
|
|
debug_write_uint(t_open->packet_size);
|
|
|
|
|
debug_write("\r\n");
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
__asm__ ("cli");
|
|
|
|
|
for (;;) {}
|
|
|
|
|
*/
|
2026-06-24 20:37:07 +02:00
|
|
|
|
2026-06-24 21:32:19 +02:00
|
|
|
#if 1
|
2026-06-24 20:37:07 +02:00
|
|
|
for (;;) {
|
|
|
|
|
t_PXENV_TFTP_READ *t_read = (void *)pxe_cmd_buf;
|
|
|
|
|
t_read->status = 0;
|
2026-06-24 21:32:19 +02:00
|
|
|
uintptr_t lin_addr = (uintptr_t)pxe_dat_buf;
|
|
|
|
|
t_read->buffer_seg = lin_addr >> 4;
|
|
|
|
|
t_read->buffer_offset = lin_addr - 16 * t_read->buffer_seg;
|
|
|
|
|
t_read->buffer_size = 512;
|
2026-06-24 20:37:07 +02:00
|
|
|
|
|
|
|
|
ret = pxe_call(PXENV_TFTP_READ, (uint16_t)(uintptr_t)pxe_cmd_buf, 0x0000);
|
|
|
|
|
debug_write("ret=");
|
|
|
|
|
debug_write_uint(ret);
|
|
|
|
|
debug_write(" status=");
|
|
|
|
|
debug_write_uint(t_read->status);
|
|
|
|
|
debug_write("\r\n");
|
|
|
|
|
|
|
|
|
|
if (t_read->buffer_size < packet_size) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-06-24 21:32:19 +02:00
|
|
|
|
|
|
|
|
// TEMP
|
|
|
|
|
break;
|
2026-06-24 20:37:07 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
2026-06-24 21:32:19 +02:00
|
|
|
|
|
|
|
|
t_PXENV_TFTP_CLOSE *t_close = (void *)pxe_cmd_buf;
|
|
|
|
|
t_close->status = 0;
|
|
|
|
|
debug_write("Calling TFTP_CLOSE\r\n");
|
|
|
|
|
ret = pxe_call(PXENV_TFTP_CLOSE, (uint16_t)(uintptr_t)pxe_cmd_buf, 0x0000);
|
|
|
|
|
debug_write("ret=");
|
|
|
|
|
debug_write_uint(ret);
|
|
|
|
|
debug_write(" status=");
|
|
|
|
|
debug_write_uint(t_open->status);
|
|
|
|
|
debug_write("\r\n");
|
2026-06-24 20:37:07 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-17 19:44:29 +01:00
|
|
|
void
|
|
|
|
|
main()
|
|
|
|
|
{
|
2026-06-24 21:32:19 +02:00
|
|
|
/*
|
2026-02-28 15:57:51 +01:00
|
|
|
blank_screen(0x10);
|
|
|
|
|
move_cursor(0, 0);
|
2026-06-24 21:32:19 +02:00
|
|
|
*/
|
2026-02-28 15:57:51 +01:00
|
|
|
//display_string(0x14, "Netboot via fernlader v2 ...\r\n");
|
2026-05-21 14:44:16 +02:00
|
|
|
debug_write("Going well ...\r\n");
|
2026-02-18 13:40:19 +01:00
|
|
|
|
2026-02-28 15:57:51 +01:00
|
|
|
if (memcmp(PXENV->signature, "PXENV+", 6) != 0) {
|
2026-05-21 14:44:16 +02:00
|
|
|
debug_write("missing PXENV+ signature\r\n");
|
2026-02-28 15:57:51 +01:00
|
|
|
}
|
|
|
|
|
if (PXENV->version >= 0x0201) {
|
2026-05-21 14:44:16 +02:00
|
|
|
debug_write("!PXE version\r\n");
|
2026-02-28 15:57:51 +01:00
|
|
|
if (memcmp(exPXE->signature, "!PXE", 4) != 0) {
|
2026-05-21 14:44:16 +02:00
|
|
|
debug_write("missing !PXE signature\r\n");
|
2026-02-28 15:57:51 +01:00
|
|
|
}
|
2026-05-21 14:44:16 +02:00
|
|
|
pxe_entry = exPXE->rmentry;
|
2026-02-28 15:57:51 +01:00
|
|
|
}
|
|
|
|
|
|
2026-06-24 21:32:19 +02:00
|
|
|
/*
|
2026-02-18 13:40:19 +01:00
|
|
|
memcpy(bootboot.magic, BOOTBOOT_MAGIC, sizeof bootboot.magic);
|
|
|
|
|
bootboot.size = 128;
|
|
|
|
|
bootboot.protocol = PROTOCOL_MINIMAL | LOADER_BIOS;
|
|
|
|
|
bootboot.numcores = 1;
|
|
|
|
|
|
|
|
|
|
struct e820_entry *end = bios_getmap(memmap);
|
2026-02-18 01:27:49 +01:00
|
|
|
for (int i = 0; ; i++) {
|
2026-02-18 13:40:19 +01:00
|
|
|
if (&memmap[i] >= end) break;
|
|
|
|
|
char buf[2];
|
|
|
|
|
buf[0] = memmap[i].type < 10 ? memmap[i].type + '0' : '?';
|
|
|
|
|
buf[1] = 0;
|
2026-05-21 14:44:16 +02:00
|
|
|
debug_write(buf);
|
2026-02-18 01:27:49 +01:00
|
|
|
}
|
2026-06-24 21:32:19 +02:00
|
|
|
*/
|
2026-02-18 13:40:19 +01:00
|
|
|
|
2026-06-24 21:32:19 +02:00
|
|
|
//pg_setup();
|
2026-02-21 14:13:24 +01:00
|
|
|
|
2026-06-24 20:37:07 +02:00
|
|
|
get_cached();
|
|
|
|
|
receive_file();
|
|
|
|
|
|
|
|
|
|
__asm__ ("cli");
|
2026-02-17 19:44:29 +01:00
|
|
|
for (;;) {
|
2026-06-24 21:32:19 +02:00
|
|
|
__asm__ ("hlt");
|
2026-02-17 19:44:29 +01:00
|
|
|
}
|
|
|
|
|
}
|