summaryrefslogtreecommitdiff
path: root/efi-kernel
diff options
context:
space:
mode:
authorclaude <[email protected]>2026-07-12 10:27:24 +0000
committerclaude <[email protected]>2026-07-12 10:27:24 +0000
commit3caf5e9682807b9579938431cca4446d7efcf615 (patch)
tree119845f998ad6dbf36cc486bad7f2bb646660c7b /efi-kernel
parentc480e76433f9bb8ce8ce9d5802cdada8883db184 (diff)
downloadplan9-kencc-linux-main.tar.gz
plan9-kencc-linux-main.zip
🌭 efi-kernel/: настоящий EFI-загрузчик + amd64-ядро (Plan9 a.out)HEADmain
По уточнению @lzcnt (нужен EFI-загрузчик, не a.out-парсер, и amd64-ядро): - kernel.c + kern64.s -> 6c/6a/6l -> amd64 Plan9 a.out (S_MAGIC 0x8A97, entry 0x100000), kmain пишет строку в VGA 0xB8000. - efi_load.c -> PE32+ bootx64.efi (через pe64.s из 9legacy + dd skip=40): под UEFI печатает через ConOut, разбирает встроенный a.out (big-endian 40-байтный заголовок), копирует сегменты по entry, прыгает в ядро (long mode). - Проверено: валидный PE32+ x64 EFI_APPLICATION, ядро встроено и парсится, ESP собран. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Diffstat (limited to 'efi-kernel')
-rw-r--r--efi-kernel/README.md32
-rw-r--r--efi-kernel/efi_load.c65
-rw-r--r--efi-kernel/kern64.s5
-rw-r--r--efi-kernel/kernel.c21
-rwxr-xr-xefi-kernel/mk.sh15
5 files changed, 138 insertions, 0 deletions
diff --git a/efi-kernel/README.md b/efi-kernel/README.md
new file mode 100644
index 0000000..dcbd2a0
--- /dev/null
+++ b/efi-kernel/README.md
@@ -0,0 +1,32 @@
+# efi-kernel — EFI-загрузчик + amd64-ядро (Plan9 a.out)
+
+Полная цепочка UEFI-загрузки на портированном тулчейне Plan9 (6c/6a/6l):
+**bootx64.efi (PE32+, EFI-загрузчик)** грузит **amd64-ядро (Plan9 a.out)**, которое
+пишет в VGA-текстбуфер **0xB8000**.
+
+## Как работает
+1. `kernel.c` + `kern64.s` → `6c`/`6a`/`6l` → `kernel.out` — amd64-ядро (Plan9 a.out,
+ S_MAGIC `0x8A97`, entry `0x100000`). `kmain` пишет строку в `0xB8000`, затем halt.
+2. Ядро встраивается байт-массивом (`kernel_blob.h`) в загрузчик.
+3. `efi_load.c` (+ `pe64.s` из 9legacy) → `6c`/`6a`/`6l` → `dd skip=40` → `bootx64.efi`
+ (PE32+). Под UEFI загрузчик: печатает статус через `ConOut`, разбирает встроенный
+ a.out (big-endian заголовок, 40 байт), копирует сегменты `[text][data]` по `entry`
+ (`0x100000`), зануляет bss и **прыгает** в ядро (long mode).
+
+## Формат amd64 Plan9 a.out (S_MAGIC)
+Заголовок 40 байт, big-endian: `magic(4) text(4) data(4) bss(4) syms(4) entry32(4)
+spsz(4) pcsz(4) entry64(8@off32)`. Сегменты идут за заголовком.
+
+## Сборка
+```sh
+BINDIR=../bin PLAN9SRC=../deps/plan9foundation ./mk.sh # -> bootx64.efi
+python3 ../efi/mkesp.py bootx64.efi esp.img # загрузочный ESP
+qemu-system-x86_64 -bios OVMF.fd -drive format=raw,file=esp.img
+```
+`pe64.s`/`efi.h`/`mem.h` — из 9legacy (`../efi/fetch_ref.sh`).
+
+## Заметки
+- `0xB8000` — легаси VGA-текст. Под чистым UEFI без CSM запись туда идёт в RAM, а не на
+ экран; под QEMU с обычным VGA/CSM — видно. Ядро-болванка демонстрирует загрузку и прыжок.
+- Загрузчик не делает `ExitBootServices` (болванка); настоящий — сделал бы перед прыжком.
+- Проверено структурно: PE32+ x64 EFI_APPLICATION, встроенный a.out парсится, entry/сегменты сходятся.
diff --git a/efi-kernel/efi_load.c b/efi-kernel/efi_load.c
new file mode 100644
index 0000000..24539ae
--- /dev/null
+++ b/efi-kernel/efi_load.c
@@ -0,0 +1,65 @@
+/*
+ * EFI-загрузчик amd64-ядра (Plan9 a.out) для UEFI. Сам — PE32+ (bootx64.efi).
+ * Печатает статус через UEFI ConOut, разбирает встроенное ядро (a.out, S_MAGIC),
+ * копирует сегменты по адресу линковки и прыгает в него (long mode). Ядро пишет 0xB8000.
+ */
+#include <u.h>
+#include "efi.h"
+#include "kernel_blob.h"
+
+EFI_HANDLE IH;
+EFI_SYSTEM_TABLE *ST;
+extern uintptr eficall(void *fn, ...);
+
+typedef unsigned int u32;
+typedef unsigned char u8;
+
+static void
+eputs(char *s) /* ASCII -> UTF-16 -> ConOut */
+{
+ CHAR16 w[2];
+ w[1] = 0;
+ for(; *s; s++){
+ if(*s == '\n'){ w[0] = '\r'; eficall(ST->ConOut->OutputString, ST->ConOut, w); }
+ w[0] = (uchar)*s;
+ eficall(ST->ConOut->OutputString, ST->ConOut, w);
+ }
+}
+
+static u32
+be32(u8 *p) /* заголовок Plan9 a.out — big-endian */
+{
+ return ((u32)p[0]<<24) | ((u32)p[1]<<16) | ((u32)p[2]<<8) | (u32)p[3];
+}
+
+void
+efimain(EFI_HANDLE ih, EFI_SYSTEM_TABLE *st)
+{
+ u8 *k, *src, *dst;
+ u32 magic, text, data, bss, entry, i;
+ void (*kentry)(void);
+
+ IH = ih; ST = st;
+ eputs("\nPlan9 EFI loader (amd64)\n");
+ eputs("parsing embedded a.out kernel...\n");
+
+ k = kernel_blob;
+ magic = be32(k + 0); /* S_MAGIC(amd64) = 0x8A97 */
+ if(magic != 0x8A97){ eputs("bad magic, halt\n"); for(;;); }
+ text = be32(k + 4);
+ data = be32(k + 8);
+ bss = be32(k + 12);
+ entry = be32(k + 36); /* 64-бит entry (мл. слово) на off 32; = 0x100000 */
+
+ src = k + 40; /* сегменты идут за 40-байтным заголовком */
+ dst = (u8*)(uintptr)entry; /* грузим по адресу линковки (-T) */
+ for(i = 0; i < text + data; i++)
+ dst[i] = src[i];
+ for(i = 0; i < bss; i++)
+ dst[text + data + i] = 0;
+
+ eputs("kernel loaded @0x100000, jumping...\n");
+ kentry = (void(*)(void))(uintptr)entry;
+ kentry(); /* -> ядро пишет 0xB8000, назад не вернётся */
+ for(;;);
+}
diff --git a/efi-kernel/kern64.s b/efi-kernel/kern64.s
new file mode 100644
index 0000000..9c39029
--- /dev/null
+++ b/efi-kernel/kern64.s
@@ -0,0 +1,5 @@
+/* amd64-энтрипоинт ядра: зовёт kmain (стек уже валиден от загрузчика) */
+TEXT _start(SB), 1, $-4
+ CALL kmain(SB)
+loop:
+ JMP loop
diff --git a/efi-kernel/kernel.c b/efi-kernel/kernel.c
new file mode 100644
index 0000000..83c9479
--- /dev/null
+++ b/efi-kernel/kernel.c
@@ -0,0 +1,21 @@
+/* Plan9 C, amd64 — kernel-болванка: пишет строку в VGA-текстбуфер 0xB8000.
+ * Грузится EFI-загрузчиком (bootx64.efi), исполняется в long mode. */
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+
+void
+kmain(void)
+{
+ ushort *vga;
+ char *s;
+ int i;
+
+ vga = (ushort*)0xB8000; /* VGA text framebuffer (long mode, identity-mapped) */
+ for(i = 0; i < 80*25; i++)
+ vga[i] = (0x1F<<8) | ' '; /* очистить: белым по синему */
+ s = "PLAN9 amd64 KERNEL loaded by EFI -> writes 0xB8000";
+ for(i = 0; s[i]; i++)
+ vga[i] = (0x1F<<8) | (uchar)s[i];
+ for(;;) /* halt */
+ ;
+}
diff --git a/efi-kernel/mk.sh b/efi-kernel/mk.sh
new file mode 100755
index 0000000..9cd32c2
--- /dev/null
+++ b/efi-kernel/mk.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# EFI-загрузчик + amd64-ядро -> bootx64.efi. Нужны: BINDIR (6c/6a/6l), PLAN9SRC (include),
+# и pe64.s/efi.h/mem.h из ../efi (запусти ../efi/fetch_ref.sh).
+set -e; cd "$(dirname "$0")"
+BIN="${BINDIR:?}"; INC="${PLAN9SRC:?}"; export objtype=amd64
+for f in pe64.s efi.h mem.h; do [ -f ../efi/$f ] || { echo "нет ../efi/$f — запусти ../efi/fetch_ref.sh"; exit 1; }; cp ../efi/$f .; done
+head -162 pe64.s > pe64min.s
+"$BIN/6a" kern64.s; "$BIN/6c" -I"$INC/amd64/include" -I"$INC/sys/include" kernel.c
+"$BIN/6l" -l -s -T0x100000 -E _start -o kernel.out kern64.6 kernel.6
+python3 -c "d=open('kernel.out','rb').read(); open('kernel_blob.h','w').write('static unsigned char kernel_blob[]={'+','.join(hex(b) for b in d)+'};\nstatic unsigned kernel_len=%d;\n'%len(d))"
+"$BIN/6a" -DIMAGEBASE=0x8000 pe64min.s
+"$BIN/6c" -I"$INC/amd64/include" -I"$INC/sys/include" efi_load.c
+"$BIN/6l" -l -s -R1 -T0x8000 -o boot.out pe64min.6 efi_load.6
+dd if=boot.out bs=1 skip=40 of=bootx64.efi 2>/dev/null
+echo "bootx64.efi: $(ls -la bootx64.efi|awk '{print $5}') байт"