feat(mm): add early physical page allocator
This commit is contained in:
parent
44bbea1b11
commit
5b69fe1f06
@ -21,12 +21,15 @@
|
||||
|
||||
## 当前约定
|
||||
|
||||
- 详细代码风格和文件组织规则见 `docs/agents/code-style.md`。
|
||||
- 当前代码以“先做最小,但不把架构完全写死”为原则推进。
|
||||
- `arch/` 放架构相关实现。
|
||||
- `mm/` 放架构无关内存管理实现,和 Linux 一样作为顶层核心子系统维护。
|
||||
- `include/tianole/` 放尽量与具体架构无关的共享接口。
|
||||
- 入口文件只负责串联启动流程,不承载文件加载、ELF 解析、内存映射统计等功能细节。
|
||||
- 检查式日志应该放在对应功能模块中,避免把 `kernel_main()` 变成临时测试脚本。
|
||||
- 内部 C API 不加 `tianole_` 前缀;这是单一内核代码库,不把项目名重复进函数名和类型名。
|
||||
- 架构目录内部不重复架构名前缀;跨通用层暴露的架构入口使用 `arch_` 前缀。
|
||||
- 避免 `temp` / `tmp` 这类临时语义进入函数名、类型名和长期变量名;一次性局部变量可以用具体含义命名。
|
||||
- C 代码风格参考 Linux:tabs 缩进,函数左花括号另起一行,`if/for/while` 左花括号留在行尾。
|
||||
- 公共头文件使用 `<...>` include,例如 `<tianole/boot_info.h>`;同目录私有头文件使用 `"..."` include,例如 `"file.h"`。
|
||||
@ -37,4 +40,5 @@
|
||||
- 文本文件使用 LF 行尾。
|
||||
- `.clang-format` 是当前格式化约定;如果环境有 `clang-format`,新增或大改 C/H 文件后应按它格式化。
|
||||
- 根 `Makefile` 只做总控和通用规则;架构配置放在 `arch/<arch>/Makefile`,目录自己的源文件列表放在对应目录的 `Makefile` 中。
|
||||
- 本地维护入口是 `scripts/check.sh`;GitHub Actions 应调用同一个脚本,避免 CI 逻辑和本地逻辑分叉。
|
||||
- 本地维护入口是 `scripts/check.sh`;具体检查放在 `scripts/checks/`,公共脚本函数放在 `scripts/lib/`。
|
||||
- GitHub Actions 应调用 `scripts/check.sh`,避免 CI 逻辑和本地逻辑分叉。
|
||||
|
||||
14
Makefile
14
Makefile
@ -10,6 +10,7 @@ BUILD_DIR := build
|
||||
KERNEL_ELF := $(BUILD_DIR)/image/kernel.elf
|
||||
DEBUG_LOG := $(BUILD_DIR)/debug.log
|
||||
SERIAL_LOG := $(BUILD_DIR)/serial.log
|
||||
KERNEL_TEST_TRAP ?= 0
|
||||
|
||||
ifeq ($(ARCH),x86_64)
|
||||
ARCH_MAKEFILE := arch/x86/Makefile
|
||||
@ -20,6 +21,7 @@ endif
|
||||
include $(ARCH_MAKEFILE)
|
||||
include $(ARCH_DIR)/boot/Makefile
|
||||
include $(ARCH_DIR)/kernel/Makefile
|
||||
include mm/Makefile
|
||||
include kernel/Makefile
|
||||
|
||||
.PHONY: all clean dirs run run-headless
|
||||
@ -27,7 +29,7 @@ include kernel/Makefile
|
||||
all: $(BOOT_EFI) $(KERNEL_ELF)
|
||||
|
||||
dirs:
|
||||
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/kernel $(EFI_DIR)
|
||||
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/kernel $(BUILD_DIR)/mm $(EFI_DIR)
|
||||
|
||||
$(BUILD_DIR)/arch/boot/%.obj: $(ARCH_DIR)/boot/%.c $(ARCH_DIR)/include/efi.h $(ARCH_DIR)/boot/debug_log.h include/tianole/boot_info.h include/tianole/elf.h | dirs
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
@ -38,10 +40,18 @@ $(BOOT_EFI): $(BOOT_OBJS) | dirs
|
||||
$(BUILD_DIR)/arch/kernel/entry.o: $(ARCH_DIR)/kernel/entry.S | dirs
|
||||
$(CC) $(KERNEL_ASFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/arch/kernel/%.o: $(ARCH_DIR)/kernel/%.S | dirs
|
||||
$(CC) $(KERNEL_ASFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/arch/kernel/%.o: $(ARCH_DIR)/kernel/%.c include/tianole/arch.h | dirs
|
||||
$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/kernel/%.o: kernel/%.c include/tianole/boot_info.h include/tianole/kernel_init.h include/tianole/arch.h include/tianole/early_log.h | dirs
|
||||
$(BUILD_DIR)/kernel/%.o: kernel/%.c include/tianole/boot_info.h include/tianole/kernel_init.h include/tianole/arch.h include/tianole/early_log.h include/tianole/mm.h | dirs
|
||||
mkdir -p $(@D)
|
||||
$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD_DIR)/mm/%.o: mm/%.c include/tianole/boot_info.h include/tianole/early_log.h include/tianole/mm.h | dirs
|
||||
mkdir -p $(@D)
|
||||
$(CC) $(KERNEL_CFLAGS) -c $< -o $@
|
||||
|
||||
$(KERNEL_ELF): $(KERNEL_OBJS) $(ARCH_DIR)/kernel/linker.ld | dirs
|
||||
|
||||
@ -21,6 +21,7 @@ tianole/
|
||||
docs/
|
||||
include/
|
||||
kernel/
|
||||
mm/
|
||||
scripts/
|
||||
```
|
||||
|
||||
@ -28,6 +29,7 @@ tianole/
|
||||
|
||||
- `arch/`:架构相关代码
|
||||
- `kernel/`:通用内核主体逐步放这里
|
||||
- `mm/`:架构无关内存管理
|
||||
- `include/tianole/`:项目自有共享接口
|
||||
- `docs/`:正式设计和路线图
|
||||
|
||||
@ -71,7 +73,7 @@ cat build/debug.log
|
||||
scripts/check.sh
|
||||
```
|
||||
|
||||
GitHub Actions 会在 push 和 pull request 时运行同一个检查脚本。
|
||||
GitHub Actions 会在 push 和 pull request 时运行同一个检查入口。具体检查拆在 `scripts/checks/`,共享函数放在 `scripts/lib/`。
|
||||
|
||||
## 文档
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ KERNEL_CFLAGS := \
|
||||
-fno-builtin \
|
||||
-fno-pic \
|
||||
-mno-red-zone \
|
||||
-DKERNEL_TEST_TRAP=$(KERNEL_TEST_TRAP) \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Werror \
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
ARCH_KERNEL_SRCS := \
|
||||
$(ARCH_DIR)/kernel/early_log.c
|
||||
$(ARCH_DIR)/kernel/early_log.c \
|
||||
$(ARCH_DIR)/kernel/gdt.c \
|
||||
$(ARCH_DIR)/kernel/idt.c \
|
||||
$(ARCH_DIR)/kernel/traps.c
|
||||
|
||||
ARCH_KERNEL_OBJS := \
|
||||
$(BUILD_DIR)/arch/kernel/entry.o \
|
||||
$(BUILD_DIR)/arch/kernel/exception_entry.o \
|
||||
$(patsubst $(ARCH_DIR)/kernel/%.c,$(BUILD_DIR)/arch/kernel/%.o,$(ARCH_KERNEL_SRCS))
|
||||
|
||||
71
arch/x86/kernel/cpu.h
Normal file
71
arch/x86/kernel/cpu.h
Normal file
@ -0,0 +1,71 @@
|
||||
#ifndef ARCH_X86_KERNEL_CPU_H
|
||||
#define ARCH_X86_KERNEL_CPU_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define KERNEL_CODE_SELECTOR 0x08
|
||||
#define KERNEL_DATA_SELECTOR 0x10
|
||||
#define TSS_SELECTOR 0x18
|
||||
|
||||
struct trap_frame {
|
||||
uint64_t rax;
|
||||
uint64_t rbx;
|
||||
uint64_t rcx;
|
||||
uint64_t rdx;
|
||||
uint64_t rsi;
|
||||
uint64_t rdi;
|
||||
uint64_t rbp;
|
||||
uint64_t r8;
|
||||
uint64_t r9;
|
||||
uint64_t r10;
|
||||
uint64_t r11;
|
||||
uint64_t r12;
|
||||
uint64_t r13;
|
||||
uint64_t r14;
|
||||
uint64_t r15;
|
||||
uint64_t vector;
|
||||
uint64_t error_code;
|
||||
uint64_t rip;
|
||||
uint64_t cs;
|
||||
uint64_t rflags;
|
||||
};
|
||||
|
||||
void gdt_init(void);
|
||||
void idt_init(void);
|
||||
void idt_set_gate(uint8_t vector, void (*handler)(void));
|
||||
void trap_dispatch(struct trap_frame *frame);
|
||||
|
||||
void exception_0(void);
|
||||
void exception_1(void);
|
||||
void exception_2(void);
|
||||
void exception_3(void);
|
||||
void exception_4(void);
|
||||
void exception_5(void);
|
||||
void exception_6(void);
|
||||
void exception_7(void);
|
||||
void exception_8(void);
|
||||
void exception_9(void);
|
||||
void exception_10(void);
|
||||
void exception_11(void);
|
||||
void exception_12(void);
|
||||
void exception_13(void);
|
||||
void exception_14(void);
|
||||
void exception_15(void);
|
||||
void exception_16(void);
|
||||
void exception_17(void);
|
||||
void exception_18(void);
|
||||
void exception_19(void);
|
||||
void exception_20(void);
|
||||
void exception_21(void);
|
||||
void exception_22(void);
|
||||
void exception_23(void);
|
||||
void exception_24(void);
|
||||
void exception_25(void);
|
||||
void exception_26(void);
|
||||
void exception_27(void);
|
||||
void exception_28(void);
|
||||
void exception_29(void);
|
||||
void exception_30(void);
|
||||
void exception_31(void);
|
||||
|
||||
#endif
|
||||
@ -1,17 +1,18 @@
|
||||
.global _start
|
||||
.global kernel_stack_top
|
||||
.type _start, @function
|
||||
|
||||
.section .text
|
||||
_start:
|
||||
lea kernel_stack_top(%rip), %rsp
|
||||
call kernel_main
|
||||
lea kernel_stack_top(%rip), %rsp
|
||||
call kernel_main
|
||||
|
||||
1:
|
||||
hlt
|
||||
jmp 1b
|
||||
hlt
|
||||
jmp 1b
|
||||
|
||||
.section .bss
|
||||
.balign 16
|
||||
kernel_stack:
|
||||
.skip 16384
|
||||
.skip 16384
|
||||
kernel_stack_top:
|
||||
|
||||
77
arch/x86/kernel/exception_entry.S
Normal file
77
arch/x86/kernel/exception_entry.S
Normal file
@ -0,0 +1,77 @@
|
||||
.macro EXCEPTION_NO_ERROR vector
|
||||
.global exception_\vector
|
||||
.type exception_\vector, @function
|
||||
exception_\vector:
|
||||
pushq $0
|
||||
pushq $\vector
|
||||
jmp exception_common
|
||||
.endm
|
||||
|
||||
.macro EXCEPTION_ERROR vector
|
||||
.global exception_\vector
|
||||
.type exception_\vector, @function
|
||||
exception_\vector:
|
||||
pushq $\vector
|
||||
jmp exception_common
|
||||
.endm
|
||||
|
||||
.section .text
|
||||
|
||||
EXCEPTION_NO_ERROR 0
|
||||
EXCEPTION_NO_ERROR 1
|
||||
EXCEPTION_NO_ERROR 2
|
||||
EXCEPTION_NO_ERROR 3
|
||||
EXCEPTION_NO_ERROR 4
|
||||
EXCEPTION_NO_ERROR 5
|
||||
EXCEPTION_NO_ERROR 6
|
||||
EXCEPTION_NO_ERROR 7
|
||||
EXCEPTION_ERROR 8
|
||||
EXCEPTION_NO_ERROR 9
|
||||
EXCEPTION_ERROR 10
|
||||
EXCEPTION_ERROR 11
|
||||
EXCEPTION_ERROR 12
|
||||
EXCEPTION_ERROR 13
|
||||
EXCEPTION_ERROR 14
|
||||
EXCEPTION_NO_ERROR 15
|
||||
EXCEPTION_NO_ERROR 16
|
||||
EXCEPTION_ERROR 17
|
||||
EXCEPTION_NO_ERROR 18
|
||||
EXCEPTION_NO_ERROR 19
|
||||
EXCEPTION_NO_ERROR 20
|
||||
EXCEPTION_ERROR 21
|
||||
EXCEPTION_NO_ERROR 22
|
||||
EXCEPTION_NO_ERROR 23
|
||||
EXCEPTION_NO_ERROR 24
|
||||
EXCEPTION_NO_ERROR 25
|
||||
EXCEPTION_NO_ERROR 26
|
||||
EXCEPTION_NO_ERROR 27
|
||||
EXCEPTION_NO_ERROR 28
|
||||
EXCEPTION_ERROR 29
|
||||
EXCEPTION_ERROR 30
|
||||
EXCEPTION_NO_ERROR 31
|
||||
|
||||
.type exception_common, @function
|
||||
exception_common:
|
||||
cld
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %r11
|
||||
pushq %r10
|
||||
pushq %r9
|
||||
pushq %r8
|
||||
pushq %rbp
|
||||
pushq %rdi
|
||||
pushq %rsi
|
||||
pushq %rdx
|
||||
pushq %rcx
|
||||
pushq %rbx
|
||||
pushq %rax
|
||||
|
||||
movq %rsp, %rdi
|
||||
call trap_dispatch
|
||||
|
||||
addq $120, %rsp
|
||||
addq $16, %rsp
|
||||
iretq
|
||||
134
arch/x86/kernel/gdt.c
Normal file
134
arch/x86/kernel/gdt.c
Normal file
@ -0,0 +1,134 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#define GDT_PRESENT 0x80
|
||||
#define GDT_RING0 0x00
|
||||
#define GDT_CODE 0x1a
|
||||
#define GDT_DATA 0x12
|
||||
#define GDT_TSS_AVAILABLE 0x09
|
||||
#define GDT_LONG_MODE 0x20
|
||||
#define GDT_GRANULARITY_4K 0x80
|
||||
|
||||
struct gdt_entry {
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
uint8_t access;
|
||||
uint8_t limit_flags;
|
||||
uint8_t base_high;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct tss_entry {
|
||||
uint32_t reserved0;
|
||||
uint64_t rsp0;
|
||||
uint64_t rsp1;
|
||||
uint64_t rsp2;
|
||||
uint64_t reserved1;
|
||||
uint64_t ist[7];
|
||||
uint64_t reserved2;
|
||||
uint16_t reserved3;
|
||||
uint16_t io_map_base;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct tss_descriptor {
|
||||
uint16_t limit_low;
|
||||
uint16_t base_low;
|
||||
uint8_t base_mid;
|
||||
uint8_t access;
|
||||
uint8_t limit_flags;
|
||||
uint8_t base_high;
|
||||
uint32_t base_upper;
|
||||
uint32_t reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gdtr {
|
||||
uint16_t limit;
|
||||
uint64_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct gdt_table {
|
||||
struct gdt_entry null;
|
||||
struct gdt_entry kernel_code;
|
||||
struct gdt_entry kernel_data;
|
||||
struct tss_descriptor tss;
|
||||
} __attribute__((packed));
|
||||
|
||||
extern char kernel_stack_top[];
|
||||
|
||||
static struct tss_entry tss;
|
||||
static struct gdt_table gdt;
|
||||
|
||||
static struct gdt_entry make_gdt_entry(uint8_t access, uint8_t flags)
|
||||
{
|
||||
struct gdt_entry entry = {
|
||||
.limit_low = 0xffff,
|
||||
.base_low = 0,
|
||||
.base_mid = 0,
|
||||
.access = (uint8_t)(GDT_PRESENT | GDT_RING0 | access),
|
||||
.limit_flags = (uint8_t)(0x0f | flags),
|
||||
.base_high = 0,
|
||||
};
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static struct tss_descriptor make_tss_descriptor(uint64_t base, uint32_t limit)
|
||||
{
|
||||
struct tss_descriptor descriptor = {
|
||||
.limit_low = (uint16_t)(limit & 0xffff),
|
||||
.base_low = (uint16_t)(base & 0xffff),
|
||||
.base_mid = (uint8_t)((base >> 16) & 0xff),
|
||||
.access = GDT_PRESENT | GDT_TSS_AVAILABLE,
|
||||
.limit_flags = (uint8_t)((limit >> 16) & 0x0f),
|
||||
.base_high = (uint8_t)((base >> 24) & 0xff),
|
||||
.base_upper = (uint32_t)(base >> 32),
|
||||
.reserved = 0,
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
static void load_gdt(const struct gdtr *gdtr)
|
||||
{
|
||||
__asm__ volatile("lgdt (%0)" : : "r"(gdtr) : "memory");
|
||||
__asm__ volatile("pushq %[code]\n"
|
||||
"leaq 1f(%%rip), %%rax\n"
|
||||
"pushq %%rax\n"
|
||||
"lretq\n"
|
||||
"1:\n"
|
||||
"movw %[data], %%ax\n"
|
||||
"movw %%ax, %%ds\n"
|
||||
"movw %%ax, %%es\n"
|
||||
"movw %%ax, %%ss\n"
|
||||
:
|
||||
: [code] "i"(KERNEL_CODE_SELECTOR),
|
||||
[data] "i"(KERNEL_DATA_SELECTOR)
|
||||
: "rax", "memory");
|
||||
}
|
||||
|
||||
static void load_tss(void)
|
||||
{
|
||||
__asm__ volatile("ltr %0" : : "r"((uint16_t)TSS_SELECTOR) : "memory");
|
||||
}
|
||||
|
||||
void gdt_init(void)
|
||||
{
|
||||
struct gdtr gdtr = {
|
||||
.limit = sizeof(gdt) - 1,
|
||||
.base = (uint64_t)(uintptr_t)&gdt,
|
||||
};
|
||||
|
||||
tss.rsp0 = (uint64_t)(uintptr_t)kernel_stack_top;
|
||||
tss.io_map_base = sizeof(tss);
|
||||
|
||||
gdt.null = (struct gdt_entry){0};
|
||||
gdt.kernel_code =
|
||||
make_gdt_entry(GDT_CODE, GDT_LONG_MODE | GDT_GRANULARITY_4K);
|
||||
gdt.kernel_data = make_gdt_entry(GDT_DATA, GDT_GRANULARITY_4K);
|
||||
gdt.tss =
|
||||
make_tss_descriptor((uint64_t)(uintptr_t)&tss, sizeof(tss) - 1);
|
||||
|
||||
load_gdt(&gdtr);
|
||||
load_tss();
|
||||
}
|
||||
85
arch/x86/kernel/idt.c
Normal file
85
arch/x86/kernel/idt.c
Normal file
@ -0,0 +1,85 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#define IDT_PRESENT 0x80
|
||||
#define IDT_INTERRUPT_GATE 0x0e
|
||||
|
||||
struct idt_entry {
|
||||
uint16_t offset_low;
|
||||
uint16_t selector;
|
||||
uint8_t ist;
|
||||
uint8_t type_attr;
|
||||
uint16_t offset_mid;
|
||||
uint32_t offset_high;
|
||||
uint32_t reserved;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct idtr {
|
||||
uint16_t limit;
|
||||
uint64_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
static struct idt_entry idt[256];
|
||||
|
||||
static void load_idt(const struct idtr *idtr)
|
||||
{
|
||||
__asm__ volatile("lidt (%0)" : : "r"(idtr) : "memory");
|
||||
}
|
||||
|
||||
void idt_set_gate(uint8_t vector, void (*handler)(void))
|
||||
{
|
||||
uint64_t offset = (uint64_t)(uintptr_t)handler;
|
||||
struct idt_entry *entry = &idt[vector];
|
||||
|
||||
entry->offset_low = (uint16_t)(offset & 0xffff);
|
||||
entry->selector = KERNEL_CODE_SELECTOR;
|
||||
entry->ist = 0;
|
||||
entry->type_attr = IDT_PRESENT | IDT_INTERRUPT_GATE;
|
||||
entry->offset_mid = (uint16_t)((offset >> 16) & 0xffff);
|
||||
entry->offset_high = (uint32_t)(offset >> 32);
|
||||
entry->reserved = 0;
|
||||
}
|
||||
|
||||
void idt_init(void)
|
||||
{
|
||||
struct idtr idtr = {
|
||||
.limit = sizeof(idt) - 1,
|
||||
.base = (uint64_t)(uintptr_t)idt,
|
||||
};
|
||||
|
||||
idt_set_gate(0, exception_0);
|
||||
idt_set_gate(1, exception_1);
|
||||
idt_set_gate(2, exception_2);
|
||||
idt_set_gate(3, exception_3);
|
||||
idt_set_gate(4, exception_4);
|
||||
idt_set_gate(5, exception_5);
|
||||
idt_set_gate(6, exception_6);
|
||||
idt_set_gate(7, exception_7);
|
||||
idt_set_gate(8, exception_8);
|
||||
idt_set_gate(9, exception_9);
|
||||
idt_set_gate(10, exception_10);
|
||||
idt_set_gate(11, exception_11);
|
||||
idt_set_gate(12, exception_12);
|
||||
idt_set_gate(13, exception_13);
|
||||
idt_set_gate(14, exception_14);
|
||||
idt_set_gate(15, exception_15);
|
||||
idt_set_gate(16, exception_16);
|
||||
idt_set_gate(17, exception_17);
|
||||
idt_set_gate(18, exception_18);
|
||||
idt_set_gate(19, exception_19);
|
||||
idt_set_gate(20, exception_20);
|
||||
idt_set_gate(21, exception_21);
|
||||
idt_set_gate(22, exception_22);
|
||||
idt_set_gate(23, exception_23);
|
||||
idt_set_gate(24, exception_24);
|
||||
idt_set_gate(25, exception_25);
|
||||
idt_set_gate(26, exception_26);
|
||||
idt_set_gate(27, exception_27);
|
||||
idt_set_gate(28, exception_28);
|
||||
idt_set_gate(29, exception_29);
|
||||
idt_set_gate(30, exception_30);
|
||||
idt_set_gate(31, exception_31);
|
||||
|
||||
load_idt(&idtr);
|
||||
}
|
||||
@ -3,6 +3,7 @@ ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x200000;
|
||||
__kernel_start = .;
|
||||
|
||||
.text : ALIGN(4K) {
|
||||
*(.text .text.*)
|
||||
@ -20,4 +21,6 @@ SECTIONS
|
||||
*(COMMON)
|
||||
*(.bss .bss.*)
|
||||
}
|
||||
|
||||
__kernel_end = .;
|
||||
}
|
||||
|
||||
101
arch/x86/kernel/traps.c
Normal file
101
arch/x86/kernel/traps.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/arch.h>
|
||||
#include <tianole/early_log.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
static const char *const exception_names[32] = {
|
||||
"divide error",
|
||||
"debug",
|
||||
"non-maskable interrupt",
|
||||
"breakpoint",
|
||||
"overflow",
|
||||
"bound range exceeded",
|
||||
"invalid opcode",
|
||||
"device not available",
|
||||
"double fault",
|
||||
"coprocessor segment overrun",
|
||||
"invalid tss",
|
||||
"segment not present",
|
||||
"stack segment fault",
|
||||
"general protection fault",
|
||||
"page fault",
|
||||
"reserved",
|
||||
"x87 floating-point exception",
|
||||
"alignment check",
|
||||
"machine check",
|
||||
"simd floating-point exception",
|
||||
"virtualization exception",
|
||||
"control protection exception",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"reserved",
|
||||
"hypervisor injection exception",
|
||||
"vmm communication exception",
|
||||
"security exception",
|
||||
"reserved",
|
||||
};
|
||||
|
||||
static void log_hex_digit(uint8_t digit)
|
||||
{
|
||||
if (digit < 10) {
|
||||
early_log_putc((char)('0' + digit));
|
||||
return;
|
||||
}
|
||||
|
||||
early_log_putc((char)('a' + digit - 10));
|
||||
}
|
||||
|
||||
static void log_u64_hex(uint64_t value)
|
||||
{
|
||||
int shift;
|
||||
|
||||
early_log_puts("0x");
|
||||
for (shift = 60; shift >= 0; shift -= 4) {
|
||||
log_hex_digit((uint8_t)((value >> shift) & 0x0f));
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t interrupted_rsp(const struct trap_frame *frame)
|
||||
{
|
||||
return (uint64_t)(uintptr_t)&frame->rflags + sizeof(frame->rflags);
|
||||
}
|
||||
|
||||
void arch_traps_init(void)
|
||||
{
|
||||
gdt_init();
|
||||
idt_init();
|
||||
early_log_puts("traps initialized\n");
|
||||
}
|
||||
|
||||
void trap_dispatch(struct trap_frame *frame)
|
||||
{
|
||||
uint64_t vector = frame->vector;
|
||||
const char *name = "unknown exception";
|
||||
|
||||
if (vector < 32) {
|
||||
name = exception_names[vector];
|
||||
}
|
||||
|
||||
early_log_puts("exception: ");
|
||||
early_log_puts(name);
|
||||
early_log_puts("\n");
|
||||
early_log_puts("vector=");
|
||||
early_log_u64_decimal(vector);
|
||||
early_log_puts(" error=");
|
||||
log_u64_hex(frame->error_code);
|
||||
early_log_puts("\n");
|
||||
early_log_puts("rip=");
|
||||
log_u64_hex(frame->rip);
|
||||
early_log_puts(" rsp=");
|
||||
log_u64_hex(interrupted_rsp(frame));
|
||||
early_log_puts(" rflags=");
|
||||
log_u64_hex(frame->rflags);
|
||||
early_log_puts("\n");
|
||||
|
||||
panic("unhandled CPU exception");
|
||||
}
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
建议内容:
|
||||
|
||||
- `code-style.md`:agent 修改代码时必须遵守的代码组织和风格规则。
|
||||
- `session-notes/`:实现日志和阶段结论。
|
||||
- `tasks/`:适合 agent 执行的边界清晰任务,按路线阶段拆分。
|
||||
- `prompts/`:可复用提示词和约束。
|
||||
@ -15,8 +16,9 @@
|
||||
|
||||
当前任务入口:
|
||||
|
||||
- `code-style.md`:代码风格和文件组织规则。
|
||||
- `tasks/README.md`:agent 任务索引。
|
||||
- `tasks/01-early-debug.md`:下一阶段,早期串口和 panic。
|
||||
- `tasks/02-cpu-interrupts.md`:下一阶段,GDT/TSS/IDT、异常入口和 trap frame。
|
||||
- `../roadmap.md`:总路线和阶段依赖。
|
||||
|
||||
当 agent 修改代码且影响下列内容时,应该同步更新这里或 `docs/`:
|
||||
|
||||
74
docs/agents/code-style.md
Normal file
74
docs/agents/code-style.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Code Style
|
||||
|
||||
这个文档记录 agent 修改 Tianole 代码时必须遵守的代码组织和风格规则。
|
||||
|
||||
## 总原则
|
||||
|
||||
- 参考 Linux 的工程风格,但不机械照抄 Linux 的历史实现。
|
||||
- 简化实现可以接受,写死架构边界、设备假设、内存布局和调用路径不接受。
|
||||
- 文件按职责拆分,不按行数拆分。
|
||||
- 入口文件只编排流程,不承载具体功能细节。
|
||||
|
||||
## Include 风格
|
||||
|
||||
- 公共头文件使用 `<...>`,例如 `<tianole/boot_info.h>`。
|
||||
- 同目录私有头文件使用 `"..."`,例如 `"file.h"`。
|
||||
- 不使用 `../foo.h` 这类相对 include。
|
||||
- 如果一个头文件需要跨目录使用,先判断它是否应该成为公共接口。
|
||||
- `include/tianole/` 放尽量架构无关的共享接口。
|
||||
- `arch/<arch>/include/` 放架构公开接口。
|
||||
|
||||
## 文件组织
|
||||
|
||||
- 长文件不是问题,职责混乱才是问题。
|
||||
- 一个文件可以较长,但必须代表一个清晰子系统、算法或驱动边界。
|
||||
- 不为了降低行数拆出 `utils.c`、`helpers.c` 这类无边界文件。
|
||||
- 按职责、生命周期、所有权和调用边界拆分。
|
||||
- `kernel/main.c`、`boot/main.c` 这类入口文件应保持短,只串联阶段。
|
||||
- `mm/` 是顶层内存管理子系统目录,不放在 `kernel/mm/` 下。
|
||||
|
||||
## 命名
|
||||
|
||||
- 内部 C API 不加 `tianole_` 前缀。
|
||||
- 架构目录内部不要重复架构名前缀;例如 `arch/x86/kernel/` 内使用 `gdt_init()`,不要写成 `x86_gdt_init()`。
|
||||
- 跨通用层暴露的架构入口使用 `arch_` 前缀,例如 `arch_traps_init()`。
|
||||
- 描述硬件规格的文档文字可以写 `x86_64`,但代码文件名和内部符号不需要反复带 `x86`。
|
||||
- 避免 `temp`、`tmp` 这类临时语义进入函数名、类型名和长期变量名。
|
||||
- 一次性局部变量也应尽量使用具体含义命名。
|
||||
- 名字应表达长期职责,不表达当前实现的临时状态。
|
||||
|
||||
## 注释
|
||||
|
||||
- 注释优先解释硬件约束、ABI 决策、内存所有权、并发语义和不明显的不变量。
|
||||
- 不写重复描述简单代码行为的注释。
|
||||
- 如果代码依赖硬件手册、启动协议、调用顺序或特殊寄存器状态,应写明约束。
|
||||
- 如果一个实现是临时简化,应写清楚后续替换边界,而不是写成永久接口。
|
||||
|
||||
## C 格式
|
||||
|
||||
- 使用 `.clang-format` 作为当前格式化规则。
|
||||
- 使用 tab 缩进。
|
||||
- 函数左花括号另起一行。
|
||||
- `if/for/while` 左花括号留在行尾。
|
||||
- 文本文件使用 LF 行尾。
|
||||
|
||||
## 构建文件
|
||||
|
||||
- 根 `Makefile` 只做总控和通用规则。
|
||||
- 架构配置放在 `arch/<arch>/Makefile`。
|
||||
- 目录自己的源文件列表放在对应目录的 `Makefile`。
|
||||
- 顶层子系统目录可以有自己的 `Makefile`,例如 `mm/Makefile`。
|
||||
- 不新增独立 `mk/` 目录,除非后续有明确且无法避免的理由。
|
||||
|
||||
## 脚本组织
|
||||
|
||||
- `scripts/check.sh` 是本地和 CI 共用的检查入口,只做编排。
|
||||
- 可独立执行的检查放在 `scripts/checks/`。
|
||||
- 多个检查共享的函数放在 `scripts/lib/`。
|
||||
- 不把所有检查逻辑持续堆进 `scripts/check.sh`。
|
||||
|
||||
## 验证
|
||||
|
||||
- 新增或大改 C/H 文件后运行 `clang-format`。
|
||||
- 提交前至少运行 `scripts/check.sh`。
|
||||
- 如果只是文档修改,至少运行 `git diff --check`。
|
||||
@ -40,15 +40,24 @@
|
||||
|
||||
## 当前状态
|
||||
|
||||
已完成:
|
||||
基础完成:
|
||||
|
||||
- kernel early log 已拆成通用前端和 x86 backend。
|
||||
- x86 backend 已同时写 QEMU debug port 和 COM1。
|
||||
- `panic()` 已接入 early log 和 `arch_halt_forever()`。
|
||||
- `scripts/check.sh` 已验证 `build/debug.log` 和 `build/serial.log` 中的关键启动行。
|
||||
|
||||
后续进入异常阶段时继续补齐:
|
||||
后续扩展:
|
||||
|
||||
- printk/log level/ring buffer。
|
||||
- oops 格式。
|
||||
- 异常栈输出。
|
||||
- 内核符号化输出。
|
||||
- framebuffer console。
|
||||
- crash dump。
|
||||
|
||||
验收依据:
|
||||
|
||||
- 正常启动日志能进入 QEMU debug log。
|
||||
- kernel 日志能进入 COM1 serial log。
|
||||
- `panic()` 已被 `02-cpu-interrupts.md` 的 invalid opcode 测试覆盖。
|
||||
|
||||
@ -39,3 +39,37 @@
|
||||
- 主动触发 invalid opcode 或 divide error。
|
||||
- 日志输出 vector、rip、rsp、error code。
|
||||
- 未处理异常进入 panic。
|
||||
|
||||
## 当前状态
|
||||
|
||||
基础完成:
|
||||
|
||||
- x86_64 GDT 已加载。
|
||||
- 最小 TSS 已建立并通过 `ltr` 加载。
|
||||
- IDT 已建立,当前覆盖 CPU exception vector 0-31。
|
||||
- 异常入口汇编已统一保存通用寄存器现场。
|
||||
- C 层 trap dispatch 已接收统一 `trap_frame`。
|
||||
- 未处理异常进入 `panic("unhandled CPU exception")`。
|
||||
- `KERNEL_TEST_TRAP=1` 会通过 `ud2` 主动触发 invalid opcode。
|
||||
- `scripts/check.sh` 已自动验证 invalid opcode 日志和 panic 路径。
|
||||
|
||||
后续扩展:
|
||||
|
||||
- PIC/APIC 初始化。
|
||||
- 外部 IRQ 分发。
|
||||
- timer interrupt。
|
||||
- page fault 的专门处理策略。
|
||||
- double fault 独立 IST 栈。
|
||||
- 用户态异常返回。
|
||||
- 可恢复异常处理。
|
||||
- oops 格式和符号化输出。
|
||||
|
||||
验收依据:
|
||||
|
||||
- 正常启动日志包含 `traps initialized`。
|
||||
- invalid opcode 测试能输出 vector、error code、rip、rsp、rflags。
|
||||
- 未处理异常能进入 `panic("unhandled CPU exception")`。
|
||||
|
||||
下一阶段:
|
||||
|
||||
- `03-memory.md`,先建立物理页、页表和内核堆。
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
## 建议边界
|
||||
|
||||
- `kernel/mm/`:架构无关内存管理。
|
||||
- `mm/`:架构无关内存管理。
|
||||
- `arch/x86/mm/`:页表格式、地址空间切换、TLB 操作。
|
||||
- `include/tianole/`:通用内存接口。
|
||||
|
||||
@ -42,3 +42,33 @@
|
||||
- page fault 能输出有效诊断。
|
||||
- 内核堆能分配小对象。
|
||||
- 后续能在此基础上加入 COW、`mmap`、换页和内存回收。
|
||||
|
||||
## 当前状态
|
||||
|
||||
基础完成:
|
||||
|
||||
- 已从 boot memory map 扫描 conventional memory。
|
||||
- 已排除 kernel image、`boot_info` 和 boot memory map 占用页。
|
||||
- 已建立最小物理页 free-list allocator。
|
||||
- 已提供 `alloc_page()` 和 `free_page()`。
|
||||
- 已加入物理页分配/释放 selftest。
|
||||
- `scripts/check.sh` 已验证 `physical pages free=` 和 selftest 日志。
|
||||
|
||||
后续扩展:
|
||||
|
||||
- 长期内存区域模型,不直接依赖 UEFI memory type。
|
||||
- page metadata。
|
||||
- buddy allocator。
|
||||
- 页表 map/unmap。
|
||||
- page fault 专门处理。
|
||||
- 最小内核堆。
|
||||
- slab/slub 或等价小对象缓存。
|
||||
|
||||
验收依据:
|
||||
|
||||
- 正常启动日志包含 `physical pages free=`。
|
||||
- 正常启动日志包含 `physical page allocator selftest ok`。
|
||||
|
||||
下一阶段:
|
||||
|
||||
- 继续在 `03-memory.md` 内推进页表管理和内核堆。
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
- `drivers/block/`:块设备抽象和具体设备。
|
||||
- `kernel/fs/`:VFS。
|
||||
- `kernel/mm/`:页缓存或块缓存。
|
||||
- `mm/`:页缓存或块缓存。
|
||||
- `fs/`:具体文件系统实现。
|
||||
|
||||
## 实现内容
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
## 当前状态
|
||||
|
||||
已经完成:
|
||||
基础已具备:
|
||||
|
||||
- 最小 `UEFI -> bootloader -> kernel` 启动链。
|
||||
- bootloader 与 kernel 拆分。
|
||||
@ -13,32 +13,34 @@
|
||||
- 进入 kernel 前调用 `ExitBootServices`。
|
||||
- `memory map` 通过 `boot_info` 传入 kernel。
|
||||
- kernel 能统计 memory map 描述符数量和 conventional memory 页数。
|
||||
- kernel early log 已拆成通用前端和 x86 backend。
|
||||
- x86 early log backend 同时输出到 QEMU debug port 和 COM1 串口。
|
||||
- 最小 `panic()` 已接入 early log 和架构 halt 路径。
|
||||
- early log 基础:通用前端、x86 backend、QEMU debug port、COM1 串口。
|
||||
- panic 基础:最小 `panic()` 已接入 early log 和架构 halt 路径。
|
||||
- CPU exception 基础:x86_64 GDT/TSS/IDT、exception vector 0-31、统一 trap frame。
|
||||
- trap 验证:invalid opcode 已能进入 trap dispatch 并最终进入 panic。
|
||||
- 物理内存基础:已从 boot memory map 建立最小物理页 allocator。
|
||||
- 物理页验证:`alloc_page/free_page` selftest 已接入启动检查。
|
||||
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
|
||||
- `scripts/check.sh` 已验证启动日志和串口日志,GitHub Actions 已接入。
|
||||
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。
|
||||
- `.clang-format` 已用于强制当前 C 代码风格。
|
||||
|
||||
还没有完成:
|
||||
后续未完成:
|
||||
|
||||
- oops 早期错误路径。
|
||||
- GDT/IDT/异常/中断。
|
||||
- 物理页分配器、虚拟内存和内核堆。
|
||||
- 调度、进程、文件系统、用户态和 shell。
|
||||
- 完整日志体系:printk、log level、ring buffer、oops、符号化、crash dump。
|
||||
- 完整中断体系:PIC/APIC、外部 IRQ、timer interrupt。
|
||||
- 内存管理扩展:长期内存区域模型、页表、page fault 策略、内核堆。
|
||||
- 后续主线:调度、进程、文件系统、用户态和 shell。
|
||||
|
||||
## 当前下一步
|
||||
|
||||
下一步执行:
|
||||
|
||||
- `docs/agents/tasks/02-cpu-interrupts.md`
|
||||
- `docs/agents/tasks/03-memory.md`
|
||||
|
||||
目标:
|
||||
|
||||
- 建立 x86_64 GDT/TSS/IDT。
|
||||
- 建立异常入口和 trap frame。
|
||||
- 让未处理异常进入 panic。
|
||||
- 为后续 IRQ、timer 和 page fault 做准备。
|
||||
- 建立内核页表管理。
|
||||
- 建立最小内核堆。
|
||||
- 让 page fault 能进入现有异常路径并输出有效诊断。
|
||||
|
||||
## 任务路由
|
||||
|
||||
|
||||
@ -4,5 +4,6 @@
|
||||
void arch_early_log_init(void);
|
||||
void arch_early_log_putc(char ch);
|
||||
void arch_halt_forever(void) __attribute__((noreturn));
|
||||
void arch_traps_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
16
include/tianole/mm.h
Normal file
16
include/tianole/mm.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef TIANOLE_MM_H
|
||||
#define TIANOLE_MM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/boot_info.h>
|
||||
|
||||
#define PAGE_SIZE 4096u
|
||||
|
||||
typedef uint64_t phys_addr_t;
|
||||
|
||||
void mm_init(const boot_info_t *boot_info);
|
||||
phys_addr_t alloc_page(void);
|
||||
void free_page(phys_addr_t page);
|
||||
|
||||
#endif
|
||||
@ -5,4 +5,5 @@ KERNEL_SRCS := \
|
||||
|
||||
KERNEL_OBJS := \
|
||||
$(ARCH_KERNEL_OBJS) \
|
||||
$(patsubst kernel/%.c,$(BUILD_DIR)/kernel/%.o,$(KERNEL_SRCS))
|
||||
$(patsubst kernel/%.c,$(BUILD_DIR)/kernel/%.o,$(KERNEL_SRCS)) \
|
||||
$(MM_OBJS)
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
#include <tianole/arch.h>
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/kernel_init.h>
|
||||
#include <tianole/mm.h>
|
||||
|
||||
void kernel_main(const boot_info_t *boot_info)
|
||||
{
|
||||
early_log_init();
|
||||
early_log_puts("kernel_main entered\n");
|
||||
arch_traps_init();
|
||||
kernel_report_boot_state(boot_info);
|
||||
mm_init(boot_info);
|
||||
|
||||
#if KERNEL_TEST_TRAP
|
||||
__asm__ volatile("ud2");
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
__asm__ volatile("hlt");
|
||||
|
||||
5
mm/Makefile
Normal file
5
mm/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
MM_SRCS := \
|
||||
mm/page_alloc.c
|
||||
|
||||
MM_OBJS := \
|
||||
$(patsubst mm/%.c,$(BUILD_DIR)/mm/%.o,$(MM_SRCS))
|
||||
164
mm/page_alloc.c
Normal file
164
mm/page_alloc.c
Normal file
@ -0,0 +1,164 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tianole/early_log.h>
|
||||
#include <tianole/mm.h>
|
||||
|
||||
struct page_node {
|
||||
struct page_node *next;
|
||||
};
|
||||
|
||||
extern char __kernel_start[];
|
||||
extern char __kernel_end[];
|
||||
|
||||
static struct page_node *free_pages;
|
||||
static uint64_t free_page_count;
|
||||
|
||||
static uint64_t align_down(uint64_t value, uint64_t alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
static uint64_t align_up(uint64_t value, uint64_t alignment)
|
||||
{
|
||||
return align_down(value + alignment - 1, alignment);
|
||||
}
|
||||
|
||||
static int ranges_overlap(uint64_t start,
|
||||
uint64_t end,
|
||||
uint64_t reserved_start,
|
||||
uint64_t reserved_end)
|
||||
{
|
||||
return start < reserved_end && reserved_start < end;
|
||||
}
|
||||
|
||||
static int page_is_reserved(const boot_info_t *boot_info, uint64_t page)
|
||||
{
|
||||
uint64_t page_end = page + PAGE_SIZE;
|
||||
uint64_t kernel_start =
|
||||
align_down((uint64_t)(uintptr_t)__kernel_start, PAGE_SIZE);
|
||||
uint64_t kernel_end =
|
||||
align_up((uint64_t)(uintptr_t)__kernel_end, PAGE_SIZE);
|
||||
uint64_t boot_info_start =
|
||||
align_down((uint64_t)(uintptr_t)boot_info, PAGE_SIZE);
|
||||
uint64_t boot_info_end = align_up(
|
||||
(uint64_t)(uintptr_t)boot_info + sizeof(*boot_info), PAGE_SIZE);
|
||||
uint64_t map_start = align_down(boot_info->memory_map, PAGE_SIZE);
|
||||
uint64_t map_end = align_up(
|
||||
boot_info->memory_map + boot_info->memory_map_size, PAGE_SIZE);
|
||||
|
||||
if (ranges_overlap(page, page_end, kernel_start, kernel_end)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ranges_overlap(page, page_end, boot_info_start, boot_info_end)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ranges_overlap(page, page_end, map_start, map_end)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_free_page(uint64_t page)
|
||||
{
|
||||
struct page_node *node = (struct page_node *)(uintptr_t)page;
|
||||
|
||||
node->next = free_pages;
|
||||
free_pages = node;
|
||||
free_page_count++;
|
||||
}
|
||||
|
||||
static void add_conventional_range(
|
||||
const boot_info_t *boot_info, uint64_t start, uint64_t pages)
|
||||
{
|
||||
uint64_t page;
|
||||
uint64_t end = start + pages * PAGE_SIZE;
|
||||
|
||||
for (page = align_up(start, PAGE_SIZE); page + PAGE_SIZE <= end;
|
||||
page += PAGE_SIZE) {
|
||||
if (!page_is_reserved(boot_info, page)) {
|
||||
add_free_page(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_free_pages(const boot_info_t *boot_info)
|
||||
{
|
||||
uint64_t offset;
|
||||
|
||||
for (offset = 0; offset + sizeof(boot_memory_descriptor_t) <=
|
||||
boot_info->memory_map_size;
|
||||
offset += boot_info->memory_descriptor_size) {
|
||||
const boot_memory_descriptor_t *descriptor =
|
||||
(const boot_memory_descriptor_t
|
||||
*)(uintptr_t)(boot_info->memory_map +
|
||||
offset);
|
||||
|
||||
if (descriptor->type != BOOT_MEMORY_TYPE_CONVENTIONAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
add_conventional_range(boot_info,
|
||||
descriptor->physical_start,
|
||||
descriptor->number_of_pages);
|
||||
}
|
||||
}
|
||||
|
||||
phys_addr_t alloc_page(void)
|
||||
{
|
||||
struct page_node *node = free_pages;
|
||||
|
||||
if (node == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_pages = node->next;
|
||||
free_page_count--;
|
||||
|
||||
return (phys_addr_t)(uintptr_t)node;
|
||||
}
|
||||
|
||||
void free_page(phys_addr_t page)
|
||||
{
|
||||
if ((page & (PAGE_SIZE - 1)) != 0 || page == 0) {
|
||||
panic("invalid physical page free");
|
||||
}
|
||||
|
||||
add_free_page(page);
|
||||
}
|
||||
|
||||
static void page_allocator_selftest(void)
|
||||
{
|
||||
phys_addr_t first = alloc_page();
|
||||
phys_addr_t second = alloc_page();
|
||||
|
||||
if (first == 0 || second == 0 || first == second) {
|
||||
panic("physical page allocator selftest failed");
|
||||
}
|
||||
|
||||
free_page(second);
|
||||
free_page(first);
|
||||
|
||||
early_log_puts("physical page allocator selftest ok\n");
|
||||
}
|
||||
|
||||
void mm_init(const boot_info_t *boot_info)
|
||||
{
|
||||
if (boot_info == 0 || boot_info->memory_map == 0 ||
|
||||
boot_info->memory_descriptor_size == 0) {
|
||||
panic("memory map unavailable");
|
||||
}
|
||||
|
||||
free_pages = 0;
|
||||
free_page_count = 0;
|
||||
|
||||
init_free_pages(boot_info);
|
||||
|
||||
early_log_puts("physical pages free=");
|
||||
early_log_u64_decimal(free_page_count);
|
||||
early_log_puts("\n");
|
||||
|
||||
page_allocator_selftest();
|
||||
}
|
||||
@ -3,49 +3,7 @@ set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
sources=$(find arch include kernel -name '*.c' -o -name '*.h')
|
||||
|
||||
clang-format --dry-run --Werror $sources
|
||||
|
||||
make clean
|
||||
make
|
||||
|
||||
timeout 30s make run-headless || true
|
||||
|
||||
required_lines=(
|
||||
"Tianole x86 bootloader loaded."
|
||||
"jumping to kernel entry"
|
||||
"kernel_main entered"
|
||||
"boot_info.version ok"
|
||||
"boot services exited"
|
||||
"memory map descriptors="
|
||||
"conventional memory pages="
|
||||
)
|
||||
|
||||
for line in "${required_lines[@]}"; do
|
||||
if ! grep -F "$line" build/debug.log >/dev/null; then
|
||||
echo "missing expected boot log line: $line" >&2
|
||||
echo "--- build/debug.log ---" >&2
|
||||
cat build/debug.log >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
serial_required_lines=(
|
||||
"kernel_main entered"
|
||||
"boot_info.version ok"
|
||||
"boot services exited"
|
||||
"memory map descriptors="
|
||||
"conventional memory pages="
|
||||
)
|
||||
|
||||
for line in "${serial_required_lines[@]}"; do
|
||||
if ! grep -F "$line" build/serial.log >/dev/null; then
|
||||
echo "missing expected serial log line: $line" >&2
|
||||
echo "--- build/serial.log ---" >&2
|
||||
cat build/serial.log >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
cat build/debug.log
|
||||
./scripts/checks/format.sh
|
||||
./scripts/checks/boot.sh
|
||||
./scripts/checks/trap.sh
|
||||
./scripts/checks/default-build.sh
|
||||
|
||||
33
scripts/checks/boot.sh
Executable file
33
scripts/checks/boot.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
. ./scripts/lib/check-common.sh
|
||||
|
||||
build_kernel
|
||||
run_qemu
|
||||
|
||||
check_lines build/debug.log \
|
||||
"Tianole x86 bootloader loaded." \
|
||||
"jumping to kernel entry" \
|
||||
"kernel_main entered" \
|
||||
"traps initialized" \
|
||||
"boot_info.version ok" \
|
||||
"boot services exited" \
|
||||
"memory map descriptors=" \
|
||||
"conventional memory pages=" \
|
||||
"physical pages free=" \
|
||||
"physical page allocator selftest ok"
|
||||
|
||||
check_lines build/serial.log \
|
||||
"kernel_main entered" \
|
||||
"traps initialized" \
|
||||
"boot_info.version ok" \
|
||||
"boot services exited" \
|
||||
"memory map descriptors=" \
|
||||
"conventional memory pages=" \
|
||||
"physical pages free=" \
|
||||
"physical page allocator selftest ok"
|
||||
|
||||
cat build/debug.log
|
||||
8
scripts/checks/default-build.sh
Executable file
8
scripts/checks/default-build.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
. ./scripts/lib/check-common.sh
|
||||
|
||||
build_kernel
|
||||
7
scripts/checks/format.sh
Executable file
7
scripts/checks/format.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
sources=$(find arch include kernel mm -name '*.c' -o -name '*.h')
|
||||
clang-format --dry-run --Werror $sources
|
||||
19
scripts/checks/trap.sh
Executable file
19
scripts/checks/trap.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
. ./scripts/lib/check-common.sh
|
||||
|
||||
build_kernel KERNEL_TEST_TRAP=1
|
||||
run_qemu KERNEL_TEST_TRAP=1
|
||||
|
||||
check_lines build/debug.log \
|
||||
"traps initialized" \
|
||||
"exception: invalid opcode" \
|
||||
"vector=6 error=0x0000000000000000" \
|
||||
"rip=0x" \
|
||||
"rsp=0x" \
|
||||
"panic: unhandled CPU exception"
|
||||
|
||||
cat build/debug.log
|
||||
27
scripts/lib/check-common.sh
Executable file
27
scripts/lib/check-common.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
check_lines()
|
||||
{
|
||||
local log_file=$1
|
||||
shift
|
||||
|
||||
for line in "$@"; do
|
||||
if ! grep -F "$line" "$log_file" >/dev/null; then
|
||||
echo "missing expected log line: $line" >&2
|
||||
echo "--- $log_file ---" >&2
|
||||
cat "$log_file" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
build_kernel()
|
||||
{
|
||||
make clean
|
||||
make "$@"
|
||||
}
|
||||
|
||||
run_qemu()
|
||||
{
|
||||
timeout 30s make run-headless "$@" || true
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user