summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/README.md40
-rw-r--r--kernel/kern.s6
-rw-r--r--kernel/kernel.c20
-rw-r--r--kernel/loader.c63
-rw-r--r--kernel/loader_test.c12
-rw-r--r--kernel/mkboot.sh25
6 files changed, 166 insertions, 0 deletions
diff --git a/kernel/README.md b/kernel/README.md
new file mode 100644
index 0000000..1e711ae
--- /dev/null
+++ b/kernel/README.md
@@ -0,0 +1,40 @@
+# kernel — болваночное ядро + загрузчик (Plan9 a.out)
+
+Ядро-болванка, которое пишет строку в VGA-текстбуфер **0xB8000**, и **загрузчик**,
+который грузит его. Всё в формате **Plan9 a.out** (не ELF) — потому что наш kencc-выхлоп
+именно a.out; загрузчик разбирает a.out-заголовок, а не ELF.
+
+## Файлы
+* `kern.s` — энтрипоинт на 386-асме: ставит стек, зовёт `kmain`.
+* `kernel.c` — ядро: пишет `PLAN9 KERNEL STUB -> writes to 0xB8000` в 0xB8000.
+* `loader.c` — загрузчик Plan9 a.out: разбор заголовка (big-endian), загрузка
+ сегментов `[text][data]` по адресу линковки, обнуление bss, прыжок в entry.
+* `loader_test.c` — хостовый тест: прогоняет `aout_parse` из настоящего загрузчика на
+ собранном ядре и печатает поля (magic/entry/размеры).
+* `mkboot.sh` — сборка: `8a`+`8c`+`8l` -> `kernel` (a.out), `8c` -> `loader.8`.
+
+## Сборка
+```sh
+BINDIR=../bin ./mkboot.sh
+```
+
+## Формат Plan9 a.out (386)
+32-байтный заголовок, **big-endian**, 8 полей по 4 байта:
+```
+magic text data bss syms entry spsz pcsz
+```
+`magic == I_MAGIC == 0x1EB` (intel 386). Сегменты идут сразу за заголовком:
+`file[32 .. 32+text]` = text, дальше data. Загрузчик копирует их по `entry`
+(равен `-T` линкера, тут `0x100000`), зануляет bss и прыгает в `entry`.
+
+## Что проверено
+```
+kernel: Plan9 a.out, magic=0x1EB (I_MAGIC 386), entry=0x100000,
+ константа 0xB8000 присутствует в коде.
+loader: aout_parse на реальном kernel -> OK, entry=0x100000, text/data сходятся.
+```
+Реальный запуск в QEMU/Bochs не гонялся (в контейнере нет эмулятора), но kernel —
+валидный a.out, а загрузчик корректно его разбирает и знает, куда грузить и куда прыгать.
+
+> Хочешь под GRUB/multiboot — можно докинуть a.out→ELF-обёртку; здесь же честный
+> a.out-путь под твоё «выхлоп у нас в а.аут».
diff --git a/kernel/kern.s b/kernel/kern.s
new file mode 100644
index 0000000..28d0d3e
--- /dev/null
+++ b/kernel/kern.s
@@ -0,0 +1,6 @@
+/* энтрипоинт: ставит стек и зовёт kmain */
+TEXT _start(SB), $0
+ MOVL $0x10000, SP
+ CALL kmain(SB)
+loop:
+ JMP loop
diff --git a/kernel/kernel.c b/kernel/kernel.c
new file mode 100644
index 0000000..0440677
--- /dev/null
+++ b/kernel/kernel.c
@@ -0,0 +1,20 @@
+/* Plan9 C, 386 — kernel-болванка: пишет строку в VGA-текстбуфер 0xB8000. */
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+
+void
+kmain(void)
+{
+ ushort *vga;
+ char *s;
+ int i;
+
+ vga = (ushort*)0xB8000; /* VGA text mode framebuffer */
+ for(i = 0; i < 80*25; i++)
+ vga[i] = (0x1F<<8) | ' '; /* очистить: белым по синему */
+ s = "PLAN9 KERNEL STUB -> writes to 0xB8000";
+ for(i = 0; s[i]; i++)
+ vga[i] = (0x1F<<8) | (uchar)s[i];
+ for(;;) /* halt */
+ ;
+}
diff --git a/kernel/loader.c b/kernel/loader.c
new file mode 100644
index 0000000..0246628
--- /dev/null
+++ b/kernel/loader.c
@@ -0,0 +1,63 @@
+/*
+ * Загрузчик ядра в формате Plan9 a.out (386, I_MAGIC).
+ * Чистый C без хедеров: собирается и нашим 8c (часть a.out-тулчейна),
+ * и хостовым gcc (тест разбора заголовка). "Выхлоп у нас в а.аут" —
+ * поэтому разбираем именно Plan9 a.out, а не ELF.
+ */
+typedef unsigned int u32; /* 4 байта и на 386, и на x86_64 */
+typedef unsigned char u8;
+
+typedef struct Exec {
+ u32 magic, text, data, bss, syms, entry, spsz, pcsz;
+} Exec;
+
+#define I_MAGIC 0x1EB /* intel 386 = _MAGIC(0,11) */
+#define HDRSZ 32 /* 8 x 4 байта, big-endian */
+
+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];
+}
+
+/* разбор заголовка. 0 = ок (это наш 386 a.out), заполняет e. */
+int
+aout_parse(u8 *img, Exec *e)
+{
+ e->magic = be32(img+0); e->text = be32(img+4);
+ e->data = be32(img+8); e->bss = be32(img+12);
+ e->syms = be32(img+16); e->entry= be32(img+20);
+ e->spsz = be32(img+24); e->pcsz = be32(img+28);
+ return e->magic == I_MAGIC ? 0 : -1;
+}
+
+/* загрузить сегменты [text][data] по адресу линковки и обнулить bss;
+ * вернуть адрес входа. freestanding — пишет по абсолютному адресу. */
+void *
+aout_load(u8 *img)
+{
+ Exec e;
+ u8 *src, *dst;
+ u32 i;
+
+ if(aout_parse(img, &e) < 0)
+ return (void*)0;
+ src = img + HDRSZ; /* сегменты идут сразу за заголовком */
+ dst = (u8*)e.entry; /* грузим по entry (= -T линкера) */
+ for(i = 0; i < e.text + e.data; i++)
+ dst[i] = src[i];
+ for(i = 0; i < e.bss; i++)
+ dst[e.text + e.data + i] = 0;
+ return (void*)e.entry;
+}
+
+/* точка входа загрузчика: грузит ядро и прыгает в него (назад не вернётся). */
+void
+bootkernel(u8 *kern)
+{
+ void (*entry)(void);
+
+ entry = (void(*)(void))aout_load(kern);
+ if(entry)
+ entry();
+}
diff --git a/kernel/loader_test.c b/kernel/loader_test.c
new file mode 100644
index 0000000..1241a39
--- /dev/null
+++ b/kernel/loader_test.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "loader.c" /* берём aout_parse из настоящего загрузчика */
+int main(int c, char**v){
+ FILE*f=fopen(v[1],"rb"); fseek(f,0,SEEK_END); long n=ftell(f); rewind(f);
+ u8*b=malloc(n); fread(b,1,n,f); fclose(f);
+ Exec e; int r=aout_parse(b,&e);
+ printf("aout_parse: %s\n", r==0?"OK (I_MAGIC 386)":"НЕ наш формат");
+ printf(" magic=0x%X text=%u data=%u bss=%u entry=0x%X\n",e.magic,e.text,e.data,e.bss,e.entry);
+ printf(" сегменты @ file+%d, грузятся -> 0x%X\n",HDRSZ,e.entry);
+ return r;
+}
diff --git a/kernel/mkboot.sh b/kernel/mkboot.sh
new file mode 100644
index 0000000..27544f8
--- /dev/null
+++ b/kernel/mkboot.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# Собирает kernel-болванку (пишет в VGA 0xB8000) и загрузчик — всё в Plan9 a.out.
+# Тулчейн: 8a/8c/8l (386) из ../bin. "Выхлоп у нас в а.аут" — потому и формат a.out.
+set -e
+cd "$(dirname "$0")"
+BINDIR="${BINDIR:-$PWD/../bin}"
+export objtype=386
+
+echo "1) 8a kern.s — энтрипоинт (стек + вызов kmain)"; "$BINDIR/8a" kern.s
+echo "2) 8c kernel.c — ядро (пишет строку в 0xB8000)"; "$BINDIR/8c" kernel.c
+echo "3) 8l -H2 -T1M — линк в Plan9 a.out, entry=_start"; "$BINDIR/8l" -o kernel -H2 -T0x100000 -E _start kern.8 kernel.8
+echo "4) 8c loader.c — загрузчик a.out (тоже a.out)"; "$BINDIR/8c" loader.c
+
+echo
+echo "=== kernel: $(ls -la kernel | awk '{print $5}') байт (Plan9 a.out) ==="
+python3 - <<'PY'
+import struct
+d=open('kernel','rb').read()
+m,t,da,b,s,e,sp,pc=struct.unpack('>8L',d[:32])
+IM=(((4*11)+0)*11)+7
+print(f" magic=0x{m:X} {'= I_MAGIC(386) OK' if m==IM else '(?)'} text={t} data={da} bss={b} entry=0x{e:X}")
+print(" константа 0xB8000 в коде:", "да" if d.find(bytes([0,0x80,0x0B,0]),32)>=0 else "нет")
+PY
+echo
+echo "проверка разбора загрузчиком (хостовый gcc): gcc -w -o loader_test loader_test.c && ./loader_test kernel"