This commit is contained in:
2026-07-20 01:53:04 +02:00
commit e71245c082
149 changed files with 8571 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
---
AlignAfterOpenBracket: BlockIndent
BinPackArguments: false
BinPackParameters: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowAllArgumentsOnNextLine: false
BreakAfterReturnType: Automatic
PenaltyReturnTypeOnItsOwnLine: 1000
AlignArrayOfStructures: None
IndentWidth: 4
InsertNewlineAtEOF: true
IntegerLiteralSeparator:
HexMinDigits: 8
IndentCaseBlocks: false
IndentCaseLabels: false
IndentPPDirectives: None
IndentGotoLabels: false
TabWidth: 4
Standard: Latest
Language: Cpp
LineEnding: LF
AlignOperands: DontAlign
BitFieldColonSpacing: None
ColumnLimit: 120
MaxEmptyLinesToKeep: 1
PointerAlignment: Left
QualifierAlignment: Leave
SpaceAfterLogicalNot: false
SpaceAfterCStyleCast: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortBlocksOnASingleLine: Empty
AlwaysBreakBeforeMultilineStrings: true
BreakAfterAttributes: Always
BreakConstructorInitializers: BeforeComma
BreakBeforeBraces: Attach
AllowShortLoopsOnASingleLine: false
SpacesInSquareBrackets: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
AlignFunctionPointers: false
AlignEscapedNewlines: DontAlign
BreakBeforeInlineASMColon: OnlyMultiline
AccessModifierOffset: -4
SpacesInParens: Never
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
IncludeBlocks: Merge
IncludeCategories:
- Regex: "^<zinnia/.*>"
Priority: 1
- Regex: "^<common/.*>"
Priority: 2
- Regex: "^<kernel/.*>"
Priority: 3
- Regex: "^<vdso/.*>"
Priority: 4
- Regex: '^<.*\.h>'
Priority: 5
- Regex: '^".*\.h"'
Priority: 6
SortIncludes: CaseInsensitive
+2
View File
@@ -0,0 +1,2 @@
Style:
AngledHeaders: [ "zinnia/.*", "uapi/.*" ]
+6
View File
@@ -0,0 +1,6 @@
build/
.cache/
.wraplock
*.o
*.a
*.so
+3
View File
@@ -0,0 +1,3 @@
[submodule "subprojects/uacpi"]
path = subprojects/uacpi
url = https://github.com/uACPI/uacpi
+25
View File
@@ -0,0 +1,25 @@
<img src="logo.svg" width="10%"/>
# Zinnia
![GitHub License](https://img.shields.io/github/license/zinnia-os/zinnia?style=flat&color=green)
![GitHub Repo stars](https://img.shields.io/github/stars/zinnia-os/zinnia?style=flat)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/zinnia-os/zinnia/ci.yml)
![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/zinnia-os/zinnia?style=flat)
![Discord](https://img.shields.io/discord/1305893510704267327?logo=discord&logoColor=white&label=discord)
Zinnia is a lightweight BSD-like monolithic kernel targeting 64-bit devices.
## Building Zinnia
To build the kernel you need `meson` and a GNU-like C compiler with support for C23.
To configure, run:
```sh
meson setup $build_dir
```
And to build:
```sh
meson compile -C $build_dir
```
+102
View File
@@ -0,0 +1,102 @@
#include <zinnia/clock.h>
#include <zinnia/mmio.h>
#include <zinnia/percpu.h>
#include <assert.h>
#include <stdint.h>
#include <x86_64/apic.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
static uint64_t lapic_read_reg(struct local_apic* lapic, mmio_reg32 reg) {
if (lapic->xapic_regs.base) {
if (reg.offset == APIC_REG_ICR.offset) {
uint64_t lo, hi;
lo = mmio_load(&lapic->xapic_regs, APIC_REG_ICR);
hi = mmio_load(&lapic->xapic_regs, APIC_REG_ICR_HI);
return (hi << 32) | lo;
} else {
return mmio_load(&lapic->xapic_regs, reg);
}
} else {
return asm_rdmsr(0x800 + (reg.offset >> 4));
}
}
static void lapic_write_reg(struct local_apic* lapic, mmio_reg32 reg, uint64_t value) {
if (lapic->xapic_regs.base) {
if (reg.offset == APIC_REG_ICR.offset) {
mmio_store(&lapic->xapic_regs, APIC_REG_ICR_HI, value >> 32);
mmio_store(&lapic->xapic_regs, APIC_REG_ICR, (uint32_t)value);
} else {
mmio_store(&lapic->xapic_regs, reg, (uint32_t)value);
}
} else {
asm_wrmsr(0x800 + (reg.offset >> 4), value);
}
}
void lapic_init(struct local_apic* lapic) {
uint64_t apic_msr = asm_rdmsr(0x1B);
apic_msr |= 1 << 11; // Enable APIC.
struct cpuid cpuid = asm_cpuid(1, 0);
if (cpuid.ecx & CPUID_1C_X2APIC)
apic_msr |= 1 << 10;
else {
lapic->xapic_regs = mmio_new(apic_msr & 0xFFFF'F000, 0x1000);
assert(lapic->xapic_regs.base, "Failed to allocate virtual memory!\n");
}
asm_wrmsr(0x1B, apic_msr);
// Reset the TPR.
lapic_write_reg(lapic, APIC_REG_TPR, 0);
// Enable APIC bit in the SIVR.
lapic_write_reg(lapic, APIC_REG_SIVR, lapic_read_reg(lapic, APIC_REG_SIVR) | 0x100);
if (lapic->xapic_regs.base) {
lapic_write_reg(lapic, APIC_REG_DFR, 0xF000'0000);
// Logical destination = LAPIC ID.
lapic_write_reg(lapic, APIC_REG_LDR, lapic_read_reg(lapic, APIC_REG_ID));
}
// TODO: Parse MADT and setup NMI sources.
// Tell the APIC timer to divide by 16.
lapic_write_reg(lapic, APIC_REG_DCR, 3);
// Set the timer to the highest value possible.
lapic_write_reg(lapic, APIC_REG_ICR_TIMER, 0xFFFF'FFFF);
// Sleep for 10 milliseconds.
clock_spin_ns(10'000'000);
lapic->ticks_per_10ms = 0xFFFF'FFFF - lapic_read_reg(lapic, APIC_REG_CCR);
lapic_write_reg(lapic, APIC_REG_LVT_TR, IDT_IPI_RESCHED | 0x20000);
lapic_write_reg(lapic, APIC_REG_ICR_TIMER, lapic->ticks_per_10ms);
}
void lapic_eoi(struct local_apic* lapic) {
lapic_write_reg(lapic, APIC_REG_EOI, 0);
}
#define PIC1_COMMAND_PORT 0x20
#define PIC1_DATA_PORT 0x21
#define PIC2_COMMAND_PORT 0xA0
#define PIC2_DATA_PORT 0xA1
void pic_disable() {
// We initialize the PIC properly, but completely disable it and use the APIC in favor of it.
// Remap IRQs so they start at 0x20 since interrupts 0x00..0x1F are used by CPU exceptions.
asm_outb(PIC1_COMMAND_PORT, 0x11);
asm_outb(PIC1_DATA_PORT, 0x20);
asm_outb(PIC1_DATA_PORT, 0x04);
asm_outb(PIC1_DATA_PORT, 0x01);
asm_outb(PIC1_DATA_PORT, 0xFF);
// Same for the slave PIC.
asm_outb(PIC2_COMMAND_PORT, 0x11);
asm_outb(PIC2_DATA_PORT, 0x28);
asm_outb(PIC2_DATA_PORT, 0x02);
asm_outb(PIC2_DATA_PORT, 0x01);
asm_outb(PIC2_DATA_PORT, 0xFF);
}
+18
View File
@@ -0,0 +1,18 @@
#include <zinnia/errno.h>
#include <uapi/archctl.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
errno_t arch_archctl(uintptr_t op, uintptr_t arg) {
switch (op) {
case __ARCHCTL_SET_FSBASE:
asm_wrmsr(MSR_FS_BASE, arg);
return 0;
case __ARCHCTL_SET_GSBASE:
// Update the inactive GSBASE MSR, not ours.
asm_wrmsr(MSR_KERNEL_GS_BASE, arg);
return 0;
default:
return EINVAL;
}
}
+76
View File
@@ -0,0 +1,76 @@
#include <zinnia/percpu.h>
#include <stddef.h>
#include <string.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
#include <x86_64/gdt.h>
#include <x86_64/tss.h>
static const struct gdt main_gdt = {
.null = {.val = 0},
.kernel_code32 = {.val = 0x00cf9b000000ffff},
.kernel_data32 = {.val = 0x00cf93000000ffff},
.kernel_code64 = {.val = 0x00a09b0000000000},
.kernel_data64 = {.val = 0x0000930000000000},
.user_code32 = {.val = 0x00cffb000000ffff},
.user_data = {.val = 0x0000f30000000000},
.user_code64 = {.val = 0x00a0fb0000000000},
.tss = {
.limit0 = sizeof(struct tss) - 1,
.access = 0x89, // Present | DPL=0 | System | 64-bit TSS
.limit1_flags = ((sizeof(struct tss) - 1) >> 16) & 0x0F,
.reserved = 0,
},
};
void gdt_load() {
struct gdt* gdt = &percpu_get()->arch.gdt;
memcpy(gdt, &main_gdt, sizeof(main_gdt));
struct tss* tss = &percpu_get()->arch.tss;
tss->iopb = sizeof(struct tss);
uintptr_t tss_addr = (uintptr_t)tss;
gdt->tss.base0 = tss_addr;
gdt->tss.base1 = tss_addr >> 16;
gdt->tss.base2 = tss_addr >> 24;
gdt->tss.base3 = tss_addr >> 32;
struct gdtr gdtr = {
.limit = sizeof(struct gdt) - 1,
.base = gdt,
};
asm volatile("lgdt %0" ::"m"(gdtr));
// Save the contents of MSR_GS_BASE, as they get cleared by a write to `gs`.
uintptr_t gs = asm_rdmsr(MSR_GS_BASE);
// Flush and reload the segment registers.
asm volatile(
"push %0\n"
"lea rax, [rip + 1f]\n"
"push rax\n"
"retfq\n"
"1:\n"
"mov ax, %1\n"
"mov ds, ax\n"
"mov es, ax\n"
"mov fs, ax\n"
"mov gs, ax\n"
"mov ss, ax\n"
:
: "i"(offsetof(struct gdt, kernel_code64)), "i"(offsetof(struct gdt, kernel_data64))
: "rax", "memory"
);
asm_wrmsr(MSR_GS_BASE, gs);
asm volatile(
"mov ax, %c0\n"
"ltr ax"
:
: "i"(offsetof(struct gdt, tss))
: "rax", "memory"
);
}
+69
View File
@@ -0,0 +1,69 @@
#include <zinnia/clock.h>
#include <zinnia/cmdline.h>
#include <zinnia/mmio.h>
#include <zinnia/print.h>
#include <zinnia/utils.h>
#include <assert.h>
#include <uacpi/acpi.h>
#include <uacpi/status.h>
#include <uacpi/tables.h>
#include <uacpi/uacpi.h>
static bool use_hpet = true;
static void hpet_option(bool use) {
use_hpet = use;
}
CMDLINE_OPTION("hpet", hpet_option);
struct hpet {
struct clock clock;
struct mmio_view regs;
uint32_t period;
};
constexpr mmio_reg32 HPET_CAP = {.offset = 0};
constexpr mmio_reg32 HPET_CFG = {.offset = 0x10};
constexpr mmio_reg32 HPET_COUNTER = {.offset = 0xF0};
static void hpet_reset(struct clock* c) {
struct hpet* hpet = containerof(c, struct hpet, clock);
mmio_store(&hpet->regs, HPET_COUNTER, 0u);
}
static uint64_t hpet_get_elapsed_ns(struct clock* c) {
struct hpet* hpet = containerof(c, struct hpet, clock);
uint64_t counter = mmio_load(&hpet->regs, HPET_COUNTER);
return (counter * (uint64_t)hpet->period / 1'000'000);
}
static struct hpet hpet_clock = {
.clock = {
.name = "hpet",
.priority = 128,
.reset = hpet_reset,
.get_elapsed_ns = hpet_get_elapsed_ns,
},
};
static char table_buf[0x1000];
void hpet_init() {
if (!use_hpet)
return;
uacpi_setup_early_table_access(table_buf, sizeof(table_buf));
uacpi_table table = {0};
assert(!uacpi_table_find_by_signature("HPET", &table), "HPET table not found!\n");
struct acpi_hpet* hpet = table.ptr;
hpet_clock.regs = mmio_new(hpet->address.address, 0x1000);
uacpi_table_unref(&table);
printf("HPET regs at %p\n", hpet_clock.regs.base);
mmio_store(&hpet_clock.regs, HPET_CFG, mmio_load(&hpet_clock.regs, HPET_CFG) | 1);
uint64_t caps = mmio_load(&hpet_clock.regs, HPET_CAP);
hpet_clock.period = (caps >> 32) & 0xFFFF'FFFF;
assert(clock_switch(&hpet_clock.clock), "Unable to switch to HPET!\n");
}
+209
View File
@@ -0,0 +1,209 @@
#include <zinnia/compiler.h>
#include <zinnia/irq.h>
#include <zinnia/panic.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <assert.h>
#include <bits/sched.h>
#include <stddef.h>
#include <stdint.h>
#include <x86_64/apic.h>
#include <x86_64/defs.h>
#include <x86_64/gdt.h>
struct [[__packed]] idt_entry {
uint16_t base0;
uint16_t selector;
uint8_t ist;
uint8_t attrib;
uint16_t base1;
uint32_t base2;
uint32_t reserved;
};
static_assert(sizeof(struct idt_entry) == 16);
struct [[__packed]] idtr {
uint16_t limit;
struct idt_entry* base;
};
static_assert(sizeof(struct idtr) == 10);
void interrupt_handler(struct arch_context* ctx) {
struct percpu* cpu = percpu_get();
bool old = irq_set_interrupted(true);
uint64_t isr = ctx->isr;
switch (isr) {
case IDT_PF:
uint64_t cr2;
asm volatile("mov %0, cr2" : "=r"(cr2));
panic("Page fault at %#lx, accessed: %#lx\n", ctx->rip, cr2);
case IDT_IPI_RESCHED:
arch_sched_preempt_disable();
if (arch_sched_preempt_enable()) {
lapic_eoi(&cpu->arch.lapic);
sched_reschedule(&cpu->sched);
}
break;
case 0x20 ...(0x20 + ARRAY_SIZE(cpu->arch.irq_lines)):
struct irq_line* line = cpu->arch.irq_lines[isr - 0x20];
assert(line, "Unhandled interrupt on ISR %lu\n", isr);
// TODO: irq_raise(line);
break;
default:
panic("Unhandled exception on ISR %lu\n", isr);
}
irq_set_interrupted(old);
}
#define CS_OFFSET (sizeof(struct arch_context) - sizeof(uint64_t) - offsetof(struct arch_context, cs))
[[__naked]]
void interrupt_return() {
asm volatile(
"pop r15\n"
"pop r14\n"
"pop r13\n"
"pop r12\n"
"pop r11\n"
"pop r10\n"
"pop r9\n"
"pop r8\n"
"pop rsi\n"
"pop rdi\n"
"pop rbp\n"
"pop rdx\n"
"pop rcx\n"
"pop rbx\n"
"pop rax\n"
// Change GS back if we came from user mode.
"cmp word ptr [rsp + %c0], %c1\n"
"je 2f\n"
"swapgs\n"
"2:\n"
// Skip .error and .isr fields.
"add rsp, 0x10\n"
"iretq\n"
:
: "i"(CS_OFFSET), "i"(offsetof(struct gdt, kernel_code64))
);
}
[[__naked]]
void interrupt_stub_internal() {
asm volatile(
// Load the kernel GS base if we're coming from user space.
"cmp word ptr [rsp + %c0], %c1\n"
"je 2f\n"
"swapgs\n"
"2:\n"
"push rax\n"
"push rbx\n"
"push rcx\n"
"push rdx\n"
"push rbp\n"
"push rdi\n"
"push rsi\n"
"push r8\n"
"push r9\n"
"push r10\n"
"push r11\n"
"push r12\n"
"push r13\n"
"push r14\n"
"push r15\n"
"cld\n"
// Zero out the base pointer since we can't trust it.
"xor rbp, rbp\n"
// Load the frame as first argument.
"mov rdi, rsp\n"
"call %c2\n"
"jmp %c3\n"
:
: "i"(CS_OFFSET), "i"(offsetof(struct gdt, kernel_code64)), "i"(interrupt_handler), "i"(interrupt_return)
);
}
// clang-format off
#define REPEAT_256(M) \
M(0) M(1) M(2) M(3) M(4) M(5) M(6) M(7) \
M(8) M(9) M(10) M(11) M(12) M(13) M(14) M(15) \
M(16) M(17) M(18) M(19) M(20) M(21) M(22) M(23) \
M(24) M(25) M(26) M(27) M(28) M(29) M(30) M(31) \
M(32) M(33) M(34) M(35) M(36) M(37) M(38) M(39) \
M(40) M(41) M(42) M(43) M(44) M(45) M(46) M(47) \
M(48) M(49) M(50) M(51) M(52) M(53) M(54) M(55) \
M(56) M(57) M(58) M(59) M(60) M(61) M(62) M(63) \
M(64) M(65) M(66) M(67) M(68) M(69) M(70) M(71) \
M(72) M(73) M(74) M(75) M(76) M(77) M(78) M(79) \
M(80) M(81) M(82) M(83) M(84) M(85) M(86) M(87) \
M(88) M(89) M(90) M(91) M(92) M(93) M(94) M(95) \
M(96) M(97) M(98) M(99) M(100) M(101) M(102) M(103) \
M(104) M(105) M(106) M(107) M(108) M(109) M(110) M(111) \
M(112) M(113) M(114) M(115) M(116) M(117) M(118) M(119) \
M(120) M(121) M(122) M(123) M(124) M(125) M(126) M(127) \
M(128) M(129) M(130) M(131) M(132) M(133) M(134) M(135) \
M(136) M(137) M(138) M(139) M(140) M(141) M(142) M(143) \
M(144) M(145) M(146) M(147) M(148) M(149) M(150) M(151) \
M(152) M(153) M(154) M(155) M(156) M(157) M(158) M(159) \
M(160) M(161) M(162) M(163) M(164) M(165) M(166) M(167) \
M(168) M(169) M(170) M(171) M(172) M(173) M(174) M(175) \
M(176) M(177) M(178) M(179) M(180) M(181) M(182) M(183) \
M(184) M(185) M(186) M(187) M(188) M(189) M(190) M(191) \
M(192) M(193) M(194) M(195) M(196) M(197) M(198) M(199) \
M(200) M(201) M(202) M(203) M(204) M(205) M(206) M(207) \
M(208) M(209) M(210) M(211) M(212) M(213) M(214) M(215) \
M(216) M(217) M(218) M(219) M(220) M(221) M(222) M(223) \
M(224) M(225) M(226) M(227) M(228) M(229) M(230) M(231) \
M(232) M(233) M(234) M(235) M(236) M(237) M(238) M(239) \
M(240) M(241) M(242) M(243) M(244) M(245) M(246) M(247) \
M(248) M(249) M(250) M(251) M(252) M(253) M(254) M(255)
// clang-format on
#define INTERRUPT_STUB(n) \
[[__naked]] \
static void interrupt_stub_##n() { \
asm volatile( \
".if (%c0 == 8 || (%c0 >= 10 && %c0 <= 14) || %c0 == 17 || %c0 == 21 || %c0 == 29 || %c0 == 30)\n" \
".else\n" \
"push 0\n" \
".endif\n" \
"push %c0\n" \
"jmp %c1\n" \
: \
: "i"(n), "i"(interrupt_stub_internal) \
); \
}
#define INTERRUPT_SETUP(n) \
do { \
uint64_t addr = (uint64_t)interrupt_stub_##n; \
idt[n].base0 = addr & 0xFFFF; \
idt[n].base1 = (addr >> 16) & 0xFFFF; \
idt[n].base2 = (addr >> 32) & 0xFFFF'FFFF; \
idt[n].ist = 0; \
idt[n].attrib = (1 << 7) | 0xE; \
idt[n].selector = offsetof(struct gdt, kernel_code64); \
idt[n].reserved = 0; \
} while (0);
REPEAT_256(INTERRUPT_STUB)
static struct idt_entry idt[256] = {};
void idt_init() {
REPEAT_256(INTERRUPT_SETUP)
}
void idt_load() {
struct idtr idtr = {
.limit = sizeof(idt) - 1,
.base = idt,
};
asm volatile("lidt %0" ::"m"(idtr));
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <zinnia/elf.h>
#define ELF_ARCH_CLASS ELFCLASS64
#define ELF_ARCH_DATA ELFDATA2LSB
#define ELF_ARCH_MACHINE EM_X86_64
#define ELF_R_SYM ELF64_R_SYM
#define ELF_R_TYPE ELF64_R_TYPE
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
#define elf_ehdr elf64_ehdr
#define elf_phdr elf64_phdr
#define elf_dyn elf64_dyn
#define elf_addr elf64_addr
#define elf_off elf64_off
#define elf_nhdr elf64_nhdr
#define elf_auxv elf64_auxv
#define elf_sym elf64_sym
#define elf_rela elf64_rela
#define elf_rel elf64_rel
enum {
R_NONE = 0,
R_COPY = 5,
R_GLOB_DAT = 6,
R_JUMP_SLOT = 7,
R_RELATIVE = 8,
R_IRELATIVE = 37,
};
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <stdint.h>
#include <x86_64/defs.h>
static inline bool arch_irq_get_state() {
uint64_t flags;
asm volatile("pushf; pop %0" : "=r"(flags)::"memory");
return flags & RFLAGS_IF;
}
static inline void arch_irq_set_state(bool state) {
if (state)
asm volatile("sti");
else
asm volatile("cli");
}
static inline void arch_irq_wait() {
asm volatile("hlt");
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <zinnia/types.h>
static inline size_t arch_mem_page_size() {
return 1 << 12;
}
static inline size_t arch_mem_page_bits() {
return 12;
}
static inline size_t arch_mem_level_bits() {
return 9;
}
static inline size_t arch_mem_num_levels() {
return 4;
}
static inline uintptr_t arch_mem_hhdm_addr() {
return 0xFFFF'8000'0000'0000;
}
static inline uintptr_t arch_vm_pfndb_addr() {
return 0xFFFF'A000'0000'0000;
}
static inline uintptr_t arch_mem_mapping_addr() {
return 0xFFFF'C000'0000'0000;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
typedef uint64_t pte_t;
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <stddef.h>
#include <x86_64/apic.h>
#include <x86_64/gdt.h>
#include <x86_64/tss.h>
#include <zinnia/irq.h>
static inline struct percpu* arch_percpu_get() {
struct percpu* result;
asm volatile("mov %0, gs:0" : "=r"(result)::"memory");
return result;
}
struct arch_percpu {
struct gdt gdt;
struct tss tss;
struct local_apic lapic;
struct irq_line* irq_lines[128];
size_t fpu_size;
void (*fpu_save)(void*);
void (*fpu_restore)(const void*);
bool can_smap;
};
void arch_percpu_bsp_init();
void arch_percpu_init();
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <bits/types.h>
#include <stdint.h>
struct arch_task_context {
uint64_t rsp;
uint8_t* fpu_region;
uint16_t ds, es, fs, gs;
uint64_t fs_base, gs_base;
};
struct arch_context {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rsi;
uint64_t rdi;
uint64_t rbp;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t rax;
uint64_t isr; // Pushed onto the stack by the interrupt handler stubs.
uint64_t error; // Pushed onto the stack by the CPU if the interrupt has an error code.
uint64_t rip; // The rest is pushed onto the stack by the CPU during an interrupt.
uint64_t cs;
uint64_t rflags;
uint64_t rsp;
uint64_t ss;
};
#define ARCH_CTX_NUM rax
#define ARCH_CTX_A0 rdi
#define ARCH_CTX_A1 rsi
#define ARCH_CTX_A2 rdx
#define ARCH_CTX_A3 r9
#define ARCH_CTX_A4 r8
#define ARCH_CTX_A5 r10
#define ARCH_CTX_A6 r12
#define ARCH_CTX_A7 r13
#define ARCH_CTX_RET0 rax
#define ARCH_CTX_RET1 rdx
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
typedef uint64_t phys_t;
typedef uint64_t reg_t;
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include <stdint.h>
#include <zinnia/mmio.h>
struct local_apic {
uint32_t id;
uint32_t ticks_per_10ms;
struct mmio_view xapic_regs;
};
enum apic_delivery_mode {
APIC_DELIVERY_FIXED = 0b000,
APIC_DELIVERY_LOWESTPRIO = 0b001,
APIC_DELIVERY_SMI = 0b010,
APIC_DELIVERY_NMI = 0b100,
APIC_DELIVERY_INIT = 0b101,
APIC_DELIVERY_STARTUP = 0b110,
};
enum apic_dest_mode {
APIC_DEST_PHYSICAL = 0,
APIC_DEST_LOGICAL = 1,
};
enum apic_delivery_status {
APIC_DELIVERY_STATUS_IDLE = 0,
APIC_DELIVERY_STATUS_PENDING = 1,
};
enum apic_level {
APIC_LEVEL_DEASSERT = 0,
APIC_LEVEL_ASSERT = 1,
};
enum apic_trigger_mode {
APIC_TRIGGER_EDGE = 0,
APIC_TRIGGER_LEVEL = 1,
};
constexpr mmio_reg32 APIC_REG_ID = {.offset = 0x20};
constexpr mmio_reg32 APIC_REG_TPR = {.offset = 0x80};
constexpr mmio_reg32 APIC_REG_EOI = {.offset = 0xB0};
constexpr mmio_reg32 APIC_REG_LDR = {.offset = 0xD0};
constexpr mmio_reg32 APIC_REG_DFR = {.offset = 0xE0};
constexpr mmio_reg32 APIC_REG_SIVR = {.offset = 0xF0};
constexpr mmio_reg32 APIC_REG_ESR = {.offset = 0x280};
constexpr mmio_reg32 APIC_REG_ICR = {.offset = 0x300};
constexpr mmio_reg32 APIC_REG_ICR_HI = {.offset = 0x310};
constexpr mmio_reg32 APIC_REG_LVT_TR = {.offset = 0x320};
constexpr mmio_reg32 APIC_REG_ICR_TIMER = {.offset = 0x380};
constexpr mmio_reg32 APIC_REG_CCR = {.offset = 0x390};
constexpr mmio_reg32 APIC_REG_DCR = {.offset = 0x3E0};
void lapic_init(struct local_apic* lapic);
void lapic_eoi(struct local_apic* lapic);
void pic_disable();
+135
View File
@@ -0,0 +1,135 @@
#pragma once
#include <stdint.h>
struct cpuid {
uint32_t eax, ebx, ecx, edx;
};
static inline struct cpuid asm_cpuid(uint32_t leaf, uint32_t subleaf) {
struct cpuid out = {0};
asm volatile("cpuid" : "=a"(out.eax), "=b"(out.ebx), "=c"(out.ecx), "=d"(out.edx) : "0"(leaf), "2"(subleaf));
return out;
}
static inline uint64_t asm_rdmsr(uint32_t msr) {
uint32_t eax;
uint32_t edx;
asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(msr) : "memory");
return ((uint64_t)edx << 32) | eax;
}
static inline uint64_t asm_rdtsc() {
uint32_t eax;
uint32_t edx;
asm volatile("rdtsc" : "=a"(eax), "=d"(edx) : : "memory");
return ((uint64_t)edx << 32) | eax;
}
// Writes a 64-bit value to a given MSR.
static inline void asm_wrmsr(uint32_t msr, uint64_t val) {
uint32_t eax = (uint32_t)val;
uint32_t edx = val >> 32;
asm volatile("wrmsr" : : "a"(eax), "d"(edx), "c"(msr) : "memory");
}
// Writes a 64-bit value to a control register using XSETBV.
static inline void asm_wrxcr(uint32_t reg, uint64_t val) {
uint32_t eax = val;
uint32_t edx = val >> 32;
asm volatile("xsetbv" ::"a"(eax), "d"(edx), "c"(reg) : "memory");
}
// Saves the FPU state to a 512-byte region of memory using FXSAVE.
// Pointer must be 16-byte aligned.
static inline void asm_fxsave(void* mem) {
asm volatile("fxsave [%0]" ::"r"(mem) : "memory");
}
// Restores the FPU state from a 512-byte region of memory using FXRSTOR.
// Pointer must be 16-byte aligned.
static inline void asm_fxrstor(const void* mem) {
asm volatile("fxrstor [%0]" ::"r"(mem) : "memory");
}
// Saves the FPU state to a region of memory using XSAVE.
// Pointer must be 16-byte aligned.
static inline void asm_xsave(void* mem) {
asm volatile("xsave [%0]" ::"r"(mem) : "memory");
}
// Restores the FPU state from a region of memory using XRSTOR.
// Pointer must be 16-byte aligned.
static inline void asm_xrstor(const void* mem) {
asm volatile("xrstor [%0]" ::"r"(mem) : "memory");
}
static inline uint8_t asm_inb(uint16_t port) {
uint8_t result;
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
return result;
}
static inline uint16_t asm_inw(uint16_t port) {
uint16_t result;
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
return result;
}
static inline uint32_t asm_inl(uint16_t port) {
uint32_t result;
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
return result;
}
static inline void asm_outb(uint16_t port, uint8_t value) {
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
}
static inline void asm_outw(uint16_t port, uint16_t value) {
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
}
static inline void asm_outl(uint16_t port, uint32_t value) {
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
}
static inline uint16_t asm_read_ds() {
uint16_t value = 0;
asm volatile("mov %0, ds" : "=r"(value));
return value;
}
static inline uint16_t asm_read_es() {
uint16_t value = 0;
asm volatile("mov %0, es" : "=r"(value));
return value;
}
static inline uint16_t asm_read_fs() {
uint16_t value = 0;
asm volatile("mov %0, fs" : "=r"(value));
return value;
}
static inline uint16_t asm_read_gs() {
uint16_t value = 0;
asm volatile("mov %0, gs" : "=r"(value));
return value;
}
static inline void asm_write_ds(uint16_t value) {
asm volatile("mov ds, %0" ::"r"(value));
}
static inline void asm_write_es(uint16_t value) {
asm volatile("mov es, %0" ::"r"(value));
}
static inline void asm_write_fs(uint16_t value) {
asm volatile("mov fs, %0" ::"r"(value));
}
static inline void asm_write_gs(uint16_t value) {
asm volatile("mov gs, %0" ::"r"(value));
}
+201
View File
@@ -0,0 +1,201 @@
#pragma once
#define CPUID_1C_SSE3 (1 << 0)
#define CPUID_1C_PCLMUL (1 << 1)
#define CPUID_1C_DTES64 (1 << 2)
#define CPUID_1C_MONITOR (1 << 3)
#define CPUID_1C_DS_CPL (1 << 4)
#define CPUID_1C_VMX (1 << 5)
#define CPUID_1C_SMX (1 << 6)
#define CPUID_1C_EST (1 << 7)
#define CPUID_1C_TM2 (1 << 8)
#define CPUID_1C_SSSE3 (1 << 9)
#define CPUID_1C_CID (1 << 10)
#define CPUID_1C_SDBG (1 << 11)
#define CPUID_1C_FMA (1 << 12)
#define CPUID_1C_CX16 (1 << 13)
#define CPUID_1C_XTPR (1 << 14)
#define CPUID_1C_PDCM (1 << 15)
#define CPUID_1C_PCID (1 << 17)
#define CPUID_1C_DCA (1 << 18)
#define CPUID_1C_SSE4_1 (1 << 19)
#define CPUID_1C_SSE4_2 (1 << 20)
#define CPUID_1C_X2APIC (1 << 21)
#define CPUID_1C_MOVBE (1 << 22)
#define CPUID_1C_POPCNT (1 << 23)
#define CPUID_1C_TSC (1 << 24)
#define CPUID_1C_AES (1 << 25)
#define CPUID_1C_XSAVE (1 << 26)
#define CPUID_1C_OSXSAVE (1 << 27)
#define CPUID_1C_AVX (1 << 28)
#define CPUID_1C_F16C (1 << 29)
#define CPUID_1C_RDRAND (1 << 30)
#define CPUID_1C_HYPERVISOR (1 << 31)
#define CPUID_1D_FPU (1 << 0)
#define CPUID_1D_VME (1 << 1)
#define CPUID_1D_DE (1 << 2)
#define CPUID_1D_PSE (1 << 3)
#define CPUID_1D_TSC (1 << 4)
#define CPUID_1D_MSR (1 << 5)
#define CPUID_1D_PAE (1 << 6)
#define CPUID_1D_MCE (1 << 7)
#define CPUID_1D_CX8 (1 << 8)
#define CPUID_1D_APIC (1 << 9)
#define CPUID_1D_SEP (1 << 11)
#define CPUID_1D_MTRR (1 << 12)
#define CPUID_1D_PGE (1 << 13)
#define CPUID_1D_MCA (1 << 14)
#define CPUID_1D_CMOV (1 << 15)
#define CPUID_1D_PAT (1 << 16)
#define CPUID_1D_PSE36 (1 << 17)
#define CPUID_1D_PSN (1 << 18)
#define CPUID_1D_CLFLUSH (1 << 19)
#define CPUID_1D_DS (1 << 21)
#define CPUID_1D_ACPI (1 << 22)
#define CPUID_1D_MMX (1 << 23)
#define CPUID_1D_FXSR (1 << 24)
#define CPUID_1D_SSE (1 << 25)
#define CPUID_1D_SSE2 (1 << 26)
#define CPUID_1D_SS (1 << 27)
#define CPUID_1D_HTT (1 << 28)
#define CPUID_1D_TM (1 << 29)
#define CPUID_1D_IA64 (1 << 30)
#define CPUID_1D_PBE (1 << 31)
#define CPUID_7B_FSGSBASE (1 << 0)
#define CPUID_7B_SGX (1 << 2)
#define CPUID_7B_BMI (1 << 3)
#define CPUID_7B_HLE (1 << 4)
#define CPUID_7B_AVX2 (1 << 5)
#define CPUID_7B_SMEP (1 << 7)
#define CPUID_7B_BMI2 (1 << 8)
#define CPUID_7B_RTM (1 << 11)
#define CPUID_7B_AVX512F (1 << 16)
#define CPUID_7B_AVX512DQ (1 << 17)
#define CPUID_7B_RDSEED (1 << 18)
#define CPUID_7B_ADX (1 << 19)
#define CPUID_7B_SMAP (1 << 20)
#define CPUID_7B_AVX512IFMA (1 << 21)
#define CPUID_7B_CLFLUSHOPT (1 << 23)
#define CPUID_7B_CLWB (1 << 24)
#define CPUID_7B_AVX512CD (1 << 28)
#define CPUID_7B_SHA (1 << 29)
#define CPUID_7B_AVX512BW (1 << 30)
#define CPUID_7B_AVX512VL (1 << 31)
#define CPUID_7C_AVX512VBMI (1 << 1)
#define CPUID_7C_UMIP (1 << 2)
#define CPUID_7C_PKU (1 << 3)
#define CPUID_7C_OSPKE (1 << 4)
#define CPUID_7C_WAITPKG (1 << 5)
#define CPUID_7C_AVX512VBMI2 (1 << 6)
#define CPUID_7C_SHSTK (1 << 7)
#define CPUID_7C_GFNI (1 << 8)
#define CPUID_7C_VAES (1 << 9)
#define CPUID_7C_VPCLMULQDQ (1 << 10)
#define CPUID_7C_AVX512VNNI (1 << 11)
#define CPUID_7C_AVX512BITALG (1 << 12)
#define CPUID_7C_TME_EN (1 << 13)
#define CPUID_7C_AVX512VPOPCNTDQ (1 << 14)
#define CPUID_7C_RDPID (1 << 22)
#define CPUID_7C_KL (1 << 23)
#define CPUID_7C_CLDEMOTE (1 << 25)
#define CPUID_7C_MOVDIRI (1 << 27)
#define CPUID_7C_MOVDIR64B (1 << 28)
#define CPUID_7C_ENQCMD (1 << 29)
#define CPUID_7D_UINTR (1 << 5)
#define CPUID_7D_AVX512VP2INTERSECT (1 << 8)
#define CPUID_7D_SERIALIZE (1 << 14)
#define CPUID_7D_TSXLDTRK (1 << 16)
#define CPUID_7D_PCONFIG (1 << 18)
#define CPUID_7D_IBT (1 << 20)
#define CPUID_7D_AMX_BF16 (1 << 22)
#define CPUID_7D_AVX512FP16 (1 << 23)
#define CPUID_7D_AMX_TILE (1 << 24)
#define CPUID_7D_AMX_INT8 (1 << 25)
#define RFLAGS_CF (1u << 0)
#define RFLAGS_PF (1u << 2)
#define RFLAGS_AF (1u << 4)
#define RFLAGS_ZF (1u << 6)
#define RFLAGS_SF (1u << 7)
#define RFLAGS_TF (1u << 8)
#define RFLAGS_IF (1u << 9)
#define RFLAGS_DF (1u << 10)
#define RFLAGS_OF (1u << 11)
#define RFLAGS_IOPL (3u << 12)
#define RFLAGS_NT (1u << 14)
#define RFLAGS_RF (1u << 16)
#define RFLAGS_VM (1u << 17)
#define RFLAGS_AC (1u << 18)
#define RFLAGS_VIF (1u << 19)
#define RFLAGS_VIP (1u << 20)
#define RFLAGS_ID (1u << 21)
#define XCR0_X87 (1 << 0)
#define XCR0_SSE (1 << 1)
#define XCR0_AVX (1 << 2)
#define XCR0_AXV512_OPMASK (1 << 5)
#define XCR0_AVX512_ZMM_HI256 (1 << 6)
#define XCR0_AVX512_HI16_ZMM (1 << 7)
#define CR0_PE (1 << 0)
#define CR0_MP (1 << 1)
#define CR0_EM (1 << 2)
#define CR0_TS (1 << 3)
#define CR0_ET (1 << 4)
#define CR0_NE (1 << 5)
#define CR0_WP (1 << 16)
#define CR0_AM (1 << 18)
#define CR0_NW (1 << 29)
#define CR0_CD (1 << 30)
#define CR0_PG (1 << 31)
#define CR4_VME (1 << 0)
#define CR4_PVI (1 << 1)
#define CR4_TSD (1 << 2)
#define CR4_DE (1 << 3)
#define CR4_PSE (1 << 4)
#define CR4_PAE (1 << 5)
#define CR4_MCE (1 << 6)
#define CR4_PGE (1 << 7)
#define CR4_PCE (1 << 8)
#define CR4_OSFXSR (1 << 9)
#define CR4_OSXMMEXCPT (1 << 10)
#define CR4_UMIP (1 << 11)
#define CR4_VMXE (1 << 13)
#define CR4_SMXE (1 << 14)
#define CR4_FSGSBASE (1 << 16)
#define CR4_PCIDE (1 << 17)
#define CR4_OSXSAVE (1 << 18)
#define CR4_SMEP (1 << 20)
#define CR4_SMAP (1 << 21)
#define CR4_PKE (1 << 22)
#define CR4_CET (1 << 23)
#define CR4_PKS (1 << 24)
#define MSR_EFER_SCE (1 << 0)
#define MSR_EFER_LME (1 << 8)
#define MSR_EFER_LMA (1 << 10)
#define MSR_EFER_NXE (1 << 11)
#define MSR_EFER_SVME (1 << 12)
#define MSR_EFER_LMSLE (1 << 13)
#define MSR_EFER_FFXSR (1 << 14)
#define MSR_EFER_TCE (1 << 15)
#define MSR_EFER (0xC0000080)
#define MSR_STAR (0xC0000081)
#define MSR_LSTAR (0xC0000082)
#define MSR_CSTAR (0xC0000083)
#define MSR_SFMASK (0xC0000084)
#define MSR_FS_BASE (0xC0000100)
#define MSR_GS_BASE (0xC0000101)
#define MSR_KERNEL_GS_BASE (0xC0000102)
#define CPL_USER (0b11)
#define CPL_KERNEL (0b00)
#define IDT_PF (0xE)
#define IDT_IPI_RESCHED (0xFF)
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include <stdint.h>
#include <zinnia/compiler.h>
union gdt_desc {
uint64_t val;
struct {
uint16_t limit0;
uint16_t base0;
uint8_t base1;
uint8_t access;
uint8_t limit1_flags;
uint8_t base2;
};
};
static_assert(sizeof(union gdt_desc) == 0x8);
union gdt_long_desc {
uint64_t val[2];
struct {
uint16_t limit0;
uint16_t base0;
uint8_t base1;
uint8_t access;
uint8_t limit1_flags;
uint8_t base2;
uint32_t base3;
uint32_t reserved;
};
};
static_assert(sizeof(union gdt_long_desc) == 0x10);
struct [[__packed]] gdt {
union gdt_desc null;
union gdt_desc kernel_code32;
union gdt_desc kernel_data32;
union gdt_desc kernel_code64;
union gdt_desc kernel_data64;
union gdt_desc user_code32;
union gdt_desc user_data;
union gdt_desc user_code64;
union gdt_long_desc tss;
};
struct [[__packed]] gdtr {
uint16_t limit;
struct gdt* base;
};
struct percpu;
// Initializes a GDT on the local CPU.
void gdt_load();
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void hpet_init();
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <zinnia/init.h>
[[__init]]
void idt_init();
// Loads the IDT on this CPU.
void idt_load();
void interrupt_return();
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#define SMP_GDTR_OFFSET 80
#define SMP_FARJMP_OFFSET 90
#define SMP_TEMP_STACK_OFFSET 98
#define SMP_TEMP_CR3_OFFSET 102
#define SMP_ENTRY_OFFSET 106
#define SMP_HHDM_OFFSET 114
#define SMP_KERNEL32_DS 16
#define SMP_KERNEL64_CS 24
#define SMP_KERNEL64_DS 32
#define SMP_INFO_SIZE 127
#ifndef __ASSEMBLER__
#include <stddef.h>
#include <stdint.h>
#include <x86_64/gdt.h>
#include <zinnia/compiler.h>
#include <zinnia/utils.h>
struct [[__packed]] smp_info {
struct gdt gdt;
struct gdtr gdtr;
uint32_t farjmp_offset;
uint32_t farjmp_segment;
uint32_t temp_stack;
uint32_t temp_cr3;
uint64_t entry;
uint64_t hhdm_offset;
uint32_t lapic_id;
uint8_t booted;
};
static_assert(SMP_GDTR_OFFSET == offsetof(struct smp_info, gdtr));
static_assert(SMP_FARJMP_OFFSET == offsetof(struct smp_info, farjmp_offset));
static_assert(SMP_TEMP_STACK_OFFSET == offsetof(struct smp_info, temp_stack));
static_assert(SMP_TEMP_CR3_OFFSET == offsetof(struct smp_info, temp_cr3));
static_assert(SMP_ENTRY_OFFSET == offsetof(struct smp_info, entry));
static_assert(SMP_HHDM_OFFSET == offsetof(struct smp_info, hhdm_offset));
static_assert(SMP_KERNEL32_DS == offsetof(struct gdt, kernel_data32));
static_assert(SMP_KERNEL64_CS == offsetof(struct gdt, kernel_code64));
static_assert(SMP_KERNEL64_DS == offsetof(struct gdt, kernel_data64));
static_assert(SMP_INFO_SIZE == sizeof(struct smp_info));
// Initializes a LAPIC.
void x86_64_smp_init(uint32_t id);
#endif
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void arch_syscall_stub();
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void tsc_init();
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
#include <zinnia/compiler.h>
struct [[__packed]] tss {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint32_t reserved1;
uint32_t reserved2;
uint64_t ist1;
uint64_t ist2;
uint64_t ist3;
uint64_t ist4;
uint64_t ist5;
uint64_t ist6;
uint64_t ist7;
uint32_t reserved3;
uint32_t reserved4;
uint16_t reserved5;
uint16_t iopb;
};
static_assert(sizeof(struct tss) == 0x68);
+23
View File
@@ -0,0 +1,23 @@
kernel_c_args += [
'-mgeneral-regs-only',
'-mno-red-zone',
'-mcmodel=kernel',
]
kernel_sources += files(
'apic.c',
'archctl.c',
'gdt.c',
'hpet.c',
'idt.c',
'percpu.c',
'pmap.c',
'sched.c',
'serial.c',
'smp.c',
'sys.c',
'syscall.c',
'trampoline.S',
'tsc.c',
'usercopy.c',
)
+116
View File
@@ -0,0 +1,116 @@
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <stddef.h>
#include <x86_64/apic.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
#include <x86_64/gdt.h>
#include <x86_64/hpet.h>
#include <x86_64/idt.h>
#include <x86_64/syscall.h>
#include <x86_64/tsc.h>
void arch_percpu_bsp_init() {
asm_wrmsr(MSR_GS_BASE, (uint64_t)&percpu_bsp);
asm_wrmsr(MSR_FS_BASE, 0);
asm_wrmsr(MSR_KERNEL_GS_BASE, 0);
pic_disable();
gdt_load();
idt_init();
idt_load();
}
union ia32_star {
uint64_t value;
struct {
uint32_t rsvd;
uint16_t kernel_cs_ss;
uint16_t user_cs_ss;
};
};
static void setup_cpu(struct percpu* cpu) {
asm_wrmsr(MSR_GS_BASE, (uint64_t)cpu);
asm_wrmsr(MSR_FS_BASE, 0);
asm_wrmsr(MSR_KERNEL_GS_BASE, 0);
gdt_load();
idt_load();
// Syscall extension.
asm_wrmsr(MSR_EFER, asm_rdmsr(MSR_EFER) | MSR_EFER_SCE);
union ia32_star star = {0};
star.kernel_cs_ss = offsetof(struct gdt, kernel_code64);
star.user_cs_ss = offsetof(struct gdt, user_code32);
asm_wrmsr(MSR_STAR, star.value);
asm_wrmsr(MSR_LSTAR, (uint64_t)arch_syscall_stub);
asm_wrmsr(MSR_SFMASK, RFLAGS_AC | RFLAGS_DF | RFLAGS_IF);
uint64_t cr0, cr4;
asm volatile("mov %0, cr0" : "=r"(cr0));
asm volatile("mov %0, cr4" : "=r"(cr4));
struct cpuid cpuid1 = asm_cpuid(1, 0);
struct cpuid cpuid7 = asm_cpuid(7, 0);
struct cpuid cpuid13 = asm_cpuid(13, 0);
// Enable SSE.
cr0 &= ~CR0_EM;
cr0 |= CR0_MP;
cr4 |= CR4_OSFXSR | CR4_OSXMMEXCPT;
if (cpuid1.ecx & CPUID_1C_XSAVE) {
cr4 |= CR4_OSXSAVE;
asm volatile("mov cr4, %0" ::"r"(cr4));
uint64_t xcr0 = XCR0_X87 | XCR0_SSE;
// AVX
if (cpuid1.ecx & CPUID_1C_AVX)
xcr0 |= XCR0_AVX;
// AVX-512
if (cpuid7.ebx & CPUID_7B_AVX512F)
xcr0 |= XCR0_AXV512_OPMASK | XCR0_AVX512_ZMM_HI256 | XCR0_AVX512_HI16_ZMM;
asm_wrxcr(0, xcr0);
cpu->arch.fpu_size = cpuid13.ecx;
cpu->arch.fpu_save = asm_xsave;
cpu->arch.fpu_restore = asm_xrstor;
} else {
cpu->arch.fpu_size = 512;
cpu->arch.fpu_save = asm_fxsave;
cpu->arch.fpu_restore = asm_fxrstor;
}
if (cpuid7.ecx & CPUID_7C_UMIP)
cr4 |= CR4_UMIP;
if (cpuid7.ebx & CPUID_7B_SMEP)
cr4 |= CR4_SMEP;
if (cpuid7.ebx & CPUID_7B_SMAP) {
cr4 |= CR4_SMAP;
cpu->arch.can_smap = true;
}
if (cpuid7.ebx & CPUID_7B_FSGSBASE)
cr4 |= CR4_FSGSBASE;
asm volatile("mov %0, cr0" : "=r"(cr0));
asm volatile("mov %0, cr4" : "=r"(cr4));
lapic_init(&cpu->arch.lapic);
cpu->online = true;
}
void arch_percpu_init() {
hpet_init();
tsc_init();
// Init the BSP.
setup_cpu(&percpu_bsp);
// Init any APs.
}
+56
View File
@@ -0,0 +1,56 @@
#include <zinnia/pmap.h>
#include <zinnia/types.h>
enum : pte_t {
ARCH_FLAG_PRESENT = 1 << 0,
ARCH_FLAG_READ_WRITE = 1 << 1,
ARCH_FLAG_USER_MODE = 1 << 2,
ARCH_FLAG_WRITE_THROUGH = 1 << 3,
ARCH_FLAG_CACHE_DISABLE = 1 << 4,
ARCH_FLAG_ACCESSED = 1 << 5,
ARCH_FLAG_DIRTY = 1 << 6,
ARCH_FLAG_SIZE = 1 << 7,
ARCH_FLAG_GLOBAL = 1 << 8,
ARCH_FLAG_AVAILABLE = 1 << 9,
ARCH_FLAG_ATTRIBUTE_TABLE = 1 << 10,
ARCH_FLAG_EXECUTE_DISABLE = 1LU << 63,
ARCH_PTE_ADDR_MASK = 0x000F'FFFF'FFFF'F000,
};
void pte_clear(pte_t* pte) {
*pte = 0;
}
pte_t pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache) {
pte_t result = ((pte_t)addr & ARCH_PTE_ADDR_MASK) | ARCH_FLAG_PRESENT;
if (flags & PTE_USER)
result |= ARCH_FLAG_USER_MODE;
if (flags & PTE_DIR)
result |= ARCH_FLAG_READ_WRITE;
else {
if (flags & PTE_WRITE)
result |= ARCH_FLAG_READ_WRITE;
if (!(flags & PTE_EXEC))
result |= ARCH_FLAG_EXECUTE_DISABLE;
}
return result;
}
bool pte_is_present(pte_t* pte) {
return *pte & ARCH_FLAG_PRESENT;
}
bool pte_is_dir(pte_t* pte) {
return *pte & ARCH_FLAG_SIZE;
}
phys_t pte_address(pte_t* pte) {
return (phys_t)(*pte & ARCH_PTE_ADDR_MASK);
}
void pmap_set(struct pmap* pt) {
asm volatile("mov cr3, %0" ::"r"(pt->root) : "memory");
}
+207
View File
@@ -0,0 +1,207 @@
#include <zinnia/alloc.h>
#include <zinnia/compiler.h>
#include <zinnia/errno.h>
#include <zinnia/percpu.h>
#include <zinnia/pmap.h>
#include <zinnia/sched.h>
#include <zinnia/vas.h>
#include <zinnia/virt.h>
#include <bits/sched.h>
#include <stddef.h>
#include <stdint.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
#include <x86_64/gdt.h>
#include <x86_64/idt.h>
// The task frame consists of registers that the C ABI marks as callee-saved.
// If we don't save them, these registers are lost during a context switch.
struct task_frame {
uint64_t rbx, rbp, r12, r13, r14, r15, rip;
};
[[__naked]]
static void perform_switch(uint64_t* old_rsp, uint64_t new_rsp) {
asm volatile(
"sub rsp, 0x30\n" // Make room for all regs (except RIP).
"mov [rsp + %c0], rbx\n"
"mov [rsp + %c1], rbp\n"
"mov [rsp + %c2], r12\n"
"mov [rsp + %c3], r13\n"
"mov [rsp + %c4], r14\n"
"mov [rsp + %c5], r15\n"
"mov [rdi], rsp\n" // rdi = old_rsp
"mov rsp, rsi\n" // rsi = new_rsp
"mov rbx, [rsp + %c0]\n"
"mov rbp, [rsp + %c1]\n"
"mov r12, [rsp + %c2]\n"
"mov r13, [rsp + %c3]\n"
"mov r14, [rsp + %c4]\n"
"mov r15, [rsp + %c5]\n"
"add rsp, 0x30\n"
"call %c6\n"
"ret" // This will conveniently move us to the RIP we put at this stack entry.
:
: "i"(offsetof(struct task_frame, rbx)),
"i"(offsetof(struct task_frame, rbp)),
"i"(offsetof(struct task_frame, r12)),
"i"(offsetof(struct task_frame, r13)),
"i"(offsetof(struct task_frame, r14)),
"i"(offsetof(struct task_frame, r15)),
"i"(irq_unlock)
: "memory"
);
}
void arch_sched_switch(struct task* from, struct task* to) {
struct arch_percpu* cpu = &percpu_get()->arch;
struct arch_task_context* from_ctx = &from->context;
struct arch_task_context* to_ctx = &to->context;
cpu->tss.rsp0 = to->kernel_stack + KERNEL_STACK_SIZE;
if (from->user_stack) {
cpu->fpu_save(from_ctx->fpu_region);
from_ctx->ds = asm_read_ds();
from_ctx->es = asm_read_es();
from_ctx->fs = asm_read_fs();
from_ctx->gs = asm_read_gs();
from_ctx->fs_base = asm_rdmsr(MSR_FS_BASE);
from_ctx->gs_base = asm_rdmsr(MSR_KERNEL_GS_BASE);
}
if (to->user_stack) {
cpu->fpu_restore(to_ctx->fpu_region);
asm_write_ds(to_ctx->ds);
asm_write_es(to_ctx->es);
asm_write_fs(to_ctx->fs);
// If we have to change the GS segment we need to reload the MSR, otherwise we lose its value.
if (to_ctx->gs != asm_read_gs()) {
uint64_t percpu = asm_rdmsr(MSR_GS_BASE);
asm_write_gs(to_ctx->gs);
asm_wrmsr(MSR_GS_BASE, percpu);
}
asm_wrmsr(MSR_FS_BASE, to_ctx->fs_base);
asm_wrmsr(MSR_KERNEL_GS_BASE, to_ctx->gs_base);
}
perform_switch(&from_ctx->rsp, to_ctx->rsp);
}
[[__naked]]
void task_entry_thunk() {
asm volatile(
"mov rdi, rbx\n"
"mov rsi, r12\n"
"jmp %c0"
:
: "i"(task_entry)
: "memory"
);
}
errno_t arch_task_init(
struct arch_task_context* context,
void* entry,
uintptr_t arg0,
uintptr_t stack_start,
bool is_user
) {
struct arch_percpu* cpu = &percpu_get()->arch;
// Prepare a dummy stack with an entry point function to return to.
struct task_frame* frame = (struct task_frame*)stack_start - 1;
frame->rbx = (uint64_t)entry;
frame->r12 = arg0;
frame->rip = (uint64_t)task_entry_thunk;
context->rsp = (uint64_t)frame;
if (is_user) {
// Allocate FPU space.
const size_t fpu_size = cpu->fpu_size;
void* fpu_base = vm_alloc(fpu_size);
if (!fpu_base)
return ENOMEM;
for (size_t i = 0; i < fpu_size; i += arch_mem_page_size()) {
phys_t page;
errno_t s = mem_phys_alloc(1, 0, &page);
if (s) {
vm_free(fpu_base, fpu_size);
return s;
}
pmap_map(&kernel_vas.pmap, (uintptr_t)fpu_base, page, PTE_WRITE | PTE_READ, CACHE_WRITE_BACK);
}
context->fpu_region = fpu_base;
context->ds = asm_read_ds();
context->es = asm_read_es();
context->fs = asm_read_fs();
context->gs = asm_read_gs();
context->fs_base = asm_rdmsr(MSR_FS_BASE);
context->gs_base = asm_rdmsr(MSR_KERNEL_GS_BASE);
}
return 0;
}
[[__naked]]
void arch_sched_preempt_disable() {
asm volatile(
"inc qword ptr gs:%c0\n"
"ret"
:
: "i"(offsetof(struct percpu, sched.preempt_level))
);
}
[[__naked]]
bool arch_sched_preempt_enable() {
asm volatile(
"dec qword ptr gs:%c0\n"
"setz al\n"
"movzx eax, al\n"
"ret\n"
:
: "i"(offsetof(struct percpu, sched.preempt_level))
: "cc"
);
}
[[__naked]]
void arch_sched_jump_to_context(struct arch_context* context) {
asm volatile(
"mov rsp, rdi\n"
"jmp %c0\n" ::"i"(interrupt_return)
);
}
void arch_sched_jump_to_user(uintptr_t ip, uintptr_t sp, uintptr_t arg) {
struct arch_context context = {
.rip = ip,
.rsp = sp,
.rdi = arg,
.rflags = 0x202,
.cs = offsetof(struct gdt, user_code64) | CPL_USER,
.ss = offsetof(struct gdt, user_data) | CPL_USER,
};
uintptr_t percpu = asm_rdmsr(MSR_GS_BASE);
const uint16_t zero = 0;
asm volatile(
"mov ds, %0\n"
"mov es, %0\n"
"mov fs, %0\n"
"mov gs, %0\n"
:
: "r"(zero)
);
asm_wrmsr(MSR_FS_BASE, 0);
asm_wrmsr(MSR_GS_BASE, percpu);
asm_wrmsr(MSR_KERNEL_GS_BASE, 0);
arch_sched_jump_to_context(&context);
}
+47
View File
@@ -0,0 +1,47 @@
#include <zinnia/console.h>
#include <stdint.h>
#include <x86_64/asm.h>
constexpr uint16_t COM1_BASE = 0x3F8;
static bool is_ready() {
return asm_inb(COM1_BASE + 5) & 0x20;
}
static void serial_write(struct console* con, const char* buf, size_t count) {
for (size_t i = 0; i < count; i++) {
while (!is_ready())
asm volatile("pause");
asm_outb(COM1_BASE, buf[i]);
}
}
bool serial_setup(struct console* con) {
asm_outb(COM1_BASE + 1, 0x00); // Disable interrupts
asm_outb(COM1_BASE + 3, 0x80); // Enable DLAB (set baud rate divisor)
asm_outb(COM1_BASE, 0x01); // Set divisor low byte (115200 baud if 1)
asm_outb(COM1_BASE + 1, 0x00); // Set divisor high byte
asm_outb(COM1_BASE + 3, 0x03); // 8 bits, no parity, one stop bit (8n1)
asm_outb(COM1_BASE + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
asm_outb(COM1_BASE + 4, 0x0B); // IRQs enabled, RTS/DSR set
asm_outb(COM1_BASE + 4, 0x1E); // Set to loopback mode.
asm_outb(COM1_BASE, 0xAE); // Send a test byte.
// If we don't get the same value back, this serial port doesn't work.
if (asm_inb(COM1_BASE) != 0xAE) {
return false;
}
asm_outb(COM1_BASE + 4, 0x0F); // Disable loopback mode.
return true;
}
static struct console serial_con = {
.name = "com1",
.write = serial_write,
.init = serial_setup,
};
DEFINE_CONSOLE(&serial_con);
+17
View File
@@ -0,0 +1,17 @@
#include <zinnia/compiler.h>
#include <zinnia/types.h>
#include <x86_64/smp.h>
extern uint8_t smp_trampoline_start[];
extern uint8_t smp_trampoline_end[];
[[noreturn]]
static void ap_entry(phys_t info) {
// TODO
while (1) {
asm volatile("hlt");
}
}
void x86_64_smp_init(uint32_t id) {}
+21
View File
@@ -0,0 +1,21 @@
#include <zinnia/compiler.h>
#include <zinnia/errno.h>
#include <zinnia/init.h>
#include <zinnia/percpu.h>
#include <uapi/archctl.h>
#include <x86_64/apic.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
#include <x86_64/gdt.h>
[[__init, __naked, __used]]
void _start() {
asm volatile(
"lea rsp, [rip + %0]\n"
"jmp %1"
:
: "i"(__ld_stack_top), "r"(kernel_entry)
);
}
void arch_panic() {}
+68
View File
@@ -0,0 +1,68 @@
#include <zinnia/compiler.h>
#include <zinnia/percpu.h>
#include <zinnia/syscall.h>
#include <bits/sched.h>
#include <stddef.h>
#include <x86_64/defs.h>
[[__naked]]
void arch_syscall_stub() {
asm volatile(
"swapgs\n"
"mov gs:%c0, rsp\n"
"mov rsp, gs:%c1\n"
"cld\n"
// We're pretending to be an interrupt, so fill the bottom fields of `Context`.
"push %c3\n" // SS and CS are not changed during SYSCALL. Use `gdt::user_data | CPL_USER`.
"push gs:%c0\n"
"push r11\n" // RFLAGS is moved into r11 by the CPU.
"push %c2\n" // Same as SS. Use `gdt::user_code64 | CPL_USER`
"push rcx\n" // RIP is moved into rcx by the CPU.
"push 0x00\n" // arch_context::error field
"push 0x00\n" // arch_context::isr field
"push rax\n"
"push rbx\n"
"push rcx\n"
"push rdx\n"
"push rbp\n"
"push rdi\n"
"push rsi\n"
"push r8\n"
"push r9\n"
"push r10\n"
"push r11\n"
"push r12\n"
"push r13\n"
"push r14\n"
"push r15\n"
"xor rbp, rbp\n"
"mov rdi, rsp\n" // Put the trap frame struct as first argument.
"call %c4\n" // Call syscall handler
"cli\n"
"pop r15\n"
"pop r14\n"
"pop r13\n"
"pop r12\n"
"pop r11\n"
"pop r10\n"
"pop r9\n"
"pop r8\n"
"pop rsi\n"
"pop rdi\n"
"pop rbp\n"
"pop rdx\n"
"pop rcx\n"
"pop rbx\n"
"pop rax\n"
"add rsp, 0x10\n" // Skip .error and .isr fields (2 * sizeof(uint64_t))
"mov rsp, gs:%c0\n" // Load user stack from `percpu::user_stack`.
"swapgs\n"
"sysretq\n" // Return to user mode.
:
: "i"(offsetof(struct percpu, user_stack)),
"i"(offsetof(struct percpu, kernel_stack)),
"i"(offsetof(struct gdt, user_code64) | CPL_USER),
"i"(offsetof(struct gdt, user_data) | CPL_USER),
"i"(syscall_dispatch)
);
}
+107
View File
@@ -0,0 +1,107 @@
#include <x86_64/smp.h>
#include <x86_64/defs.h>
.intel_syntax noprefix
.global smp_trampoline_start
.global smp_trampoline_end
.section .rodata
.code16
smp_trampoline_start:
cli
cld
jmp 1f
smp_trampoline_data:
.skip SMP_INFO_SIZE
.set data_offset, (smp_trampoline_data - smp_trampoline_start)
.set gdtr_offset, (data_offset + SMP_GDTR_OFFSET)
.set farjmp_offset, (data_offset + SMP_FARJMP_OFFSET)
.set temp_stack_offset, (data_offset + SMP_TEMP_STACK_OFFSET)
.set temp_cr3_offset, (data_offset + SMP_TEMP_CR3_OFFSET)
.set entry_offset, (data_offset + SMP_ENTRY_OFFSET)
.set hhdm_offset, (data_offset + SMP_HHDM_OFFSET)
1:
mov bx, cs
shl ebx, 4
.set idtr_offset, (invalid_idtr - smp_trampoline_start)
lidtd cs:idtr_offset
lgdtd cs:gdtr_offset
.set mode32_offset, (mode32 - smp_trampoline_start)
lea eax, [ebx + mode32_offset]
mov dword ptr cs:farjmp_offset, eax
mov eax, CR0_PE | CR0_ET
mov cr0, eax
jmp fword ptr cs:farjmp_offset
.code32
mode32:
mov ax, SMP_KERNEL32_DS
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
xor eax, eax
lldt ax
mov esp, dword ptr [ebx + temp_stack_offset]
xor eax, eax
mov cr4, eax
or eax, CR4_PAE
mov cr4, eax
mov ecx, MSR_EFER
mov eax, MSR_EFER_LME
xor edx, edx
wrmsr
mov eax, dword ptr [ebx + temp_cr3_offset]
mov cr3, eax
mov eax, cr0
or eax, CR0_PG
mov cr0, eax
.set mode64_offset, (mode64 - smp_trampoline_start)
lea eax, [ebx + mode64_offset]
push SMP_KERNEL64_CS
push eax
retf
.code64
mode64:
mov ax, SMP_KERNEL64_DS
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ecx, MSR_EFER
rdmsr
or eax, MSR_EFER_NXE
wrmsr
xor ebp, ebp
lea rdi, [rbx + data_offset]
mov rax, [rbx + entry_offset]
add rsp, [rbx + hhdm_offset]
jmp rax
invalid_idtr:
.word 0
.quad 0
smp_trampoline_end:
+54
View File
@@ -0,0 +1,54 @@
#include <zinnia/clock.h>
#include <zinnia/cmdline.h>
#include <zinnia/print.h>
#include <x86_64/asm.h>
#include <x86_64/defs.h>
static bool use_tsc = true;
void tsc_init() {
if (!use_tsc)
return;
if (!(asm_cpuid(1, 0).edx & CPUID_1D_TSC) || !(asm_cpuid(0x8000'0007, 0).edx & (1 << 8))) {
printf("No invariant TSC detected!\n");
return;
}
// Check for the TSC info leaf.
bool has_tsc_leaf = false;
struct cpuid tsc_leaf = {0};
if (asm_cpuid(0x8000'0000, 0).eax >= 0x15) {
tsc_leaf = asm_cpuid(0x15, 0);
}
uint64_t freq = 0;
if (clock_available()) {
// Calibrate using existing clock.
uint64_t t1 = asm_rdtsc();
clock_spin_ns(10'000'000);
uint64_t t2 = asm_rdtsc();
freq = (t2 - t1) * 100;
} else if (has_tsc_leaf) {
// If we have no timer (yet), the only way we can calibrate the TSC is if CPUID gives us the frequency.
// On a normal system, this should usually never be called and is a last resort
// since at this point we have at least the HPET timer.
if (tsc_leaf.ecx != 0 && tsc_leaf.ebx != 0 && tsc_leaf.eax != 0) {
printf("Calibrating TSC using CPUID 0x15");
freq = tsc_leaf.ecx * tsc_leaf.ebx / tsc_leaf.eax;
} else {
printf("Unable to calibrate TSC using CPUID frequency information");
return;
}
} else {
printf("Unable to calibrate the TSC without a clock or static frequency info!\n");
return;
}
printf("Timer frequency is %lu MHz\n", freq / 1'000'000);
}
static void tsc_option(bool opt) {
use_tsc = opt;
}
CMDLINE_OPTION("tsc", tsc_option);
+137
View File
@@ -0,0 +1,137 @@
#include <zinnia/bits/usercopy.h>
#include <zinnia/compiler.h>
extern void x86_64_read_start();
extern void x86_64_read_end();
extern void x86_64_read_fault();
static const struct usercopy_region read_region = {
.start_ip = x86_64_read_start,
.end_ip = x86_64_read_end,
.fault_ip = x86_64_read_fault,
};
bool arch_usercopy_read(void* dst, const __user void* src, size_t len, struct usercopy_region** region) {
asm volatile(
"stac\n"
// Setup regs for `rep movsb`.
"xchg rcx, rdx\n"
"mov rax, [rip + %0]\n"
"mov [rdx], rax\n"
".global x86_64_read_start\n"
"x86_64_read_start:\n"
"rep movsb\n"
".global x86_64_read_end\n"
"x86_64_read_end:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"mov rax, 1\n"
"clac\n"
"ret\n"
".global x86_64_read_fault\n"
"x86_64_read_fault:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"clac\n"
"ret\n"
:
: "i"(&read_region)
: "memory"
);
return false;
}
extern void x86_64_write_start();
extern void x86_64_write_end();
extern void x86_64_write_fault();
static const struct usercopy_region write_region = {
.start_ip = x86_64_write_start,
.end_ip = x86_64_write_end,
.fault_ip = x86_64_write_fault,
};
bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct usercopy_region** region) {
asm volatile(
"stac\n"
// Setup regs for `rep movsb`.
"xchg rcx, rdx\n"
"mov rax, [rip + %0]\n"
"mov [rdx], rax\n"
".global x86_64_write_start\n"
"x86_64_write_start:\n"
"rep movsb\n"
".global x86_64_write_end\n"
"x86_64_write_end:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"mov rax, 1\n"
"clac\n"
"ret\n"
".global x86_64_write_fault\n"
"x86_64_write_fault:\n"
"xor rax, rax\n"
"mov [rdx], rax\n"
"clac\n"
"ret\n"
:
: "i"(&read_region)
: "memory"
);
return false;
}
extern void x86_64_strlen_start();
extern void x86_64_strlen_end();
extern void x86_64_strlen_fault();
static const struct usercopy_region strlen_region = {
.start_ip = x86_64_strlen_start,
.end_ip = x86_64_strlen_end,
.fault_ip = x86_64_strlen_fault,
};
[[__naked]]
bool arch_usercopy_strlen(const __user char* str, size_t max, size_t* len, struct usercopy_region** region) {
asm volatile(
"stac\n"
"mov rax, [rip + %0]\n"
"mov [rcx], rax\n"
".global x86_64_strlen_start\n"
"x86_64_strlen_start:\n"
"xor r8, r8\n"
".Lloop:\n"
"cmp byte ptr [rdi + r8], 0\n"
"je .Lleave\n"
"inc r8\n"
"cmp rsi, r8\n"
"jne .Lloop\n"
".Lleave:\n"
"mov [rdx], r8\n"
".global x86_64_strlen_end\n"
"x86_64_strlen_end:\n"
"xor rax, rax\n"
"mov [rcx], rax\n"
"mov rax, 1\n"
"clac\n"
"ret\n"
".global x86_64_strlen_fault\n"
"x86_64_strlen_fault:\n"
"xor rax, rax\n"
"mov [rcx], rax\n"
"clac\n"
"ret\n"
:
: "i"(&strlen_region)
: "memory"
);
}
+102
View File
@@ -0,0 +1,102 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <zinnia/cmdline.h>
#include <zinnia/init.h>
// Defined in linker script.
extern const struct cmdline_option* __ld_cmdline_start[];
extern const struct cmdline_option* __ld_cmdline_end[];
static char cmdline_buf[0x1000];
[[__init]]
void cmdline_parse(const char* cmdline) {
const size_t len = strnlen(cmdline, sizeof(cmdline_buf) - 1);
size_t idx = 0;
memcpy(cmdline_buf, cmdline, len);
while (1) {
const struct cmdline_option** opt = __ld_cmdline_start;
char* name = nullptr;
char* value = nullptr;
// Skip all leading spaces.
while (idx < len && cmdline_buf[idx] == ' ')
idx++;
if (idx >= len)
break;
size_t name_idx = idx;
name = cmdline_buf + name_idx;
// Find the next equal sign or space.
while (idx < len && cmdline_buf[idx] != '=' && cmdline_buf[idx] != ' ')
idx++;
if (idx > len)
break;
// Check if the option has a value (=foo).
char seperator = cmdline_buf[idx];
cmdline_buf[idx++] = 0;
if (seperator == '=') {
// Check if we need to escape the value.
char check;
if (cmdline_buf[idx] == '"') {
check = '"';
cmdline_buf[idx++] = 0;
} else {
check = ' ';
}
value = cmdline_buf + idx;
// Skip the value.
while (idx < len && cmdline_buf[idx] != check)
idx++;
if (idx > len)
break;
cmdline_buf[idx++] = 0;
}
// Find the corresponding option.
while (opt < __ld_cmdline_end) {
const struct cmdline_option* current = *opt;
if (!strcmp(current->name, name)) {
switch (current->option_type) {
case CMDLINE_STRING:
current->func_str(value);
break;
case CMDLINE_BOOL:
if (!strcmp(value, "on") || !strcmp(value, "yes") || !strcmp(value, "true") || !strcmp(value, "1"))
current->func_bool(true);
else
current->func_bool(false);
break;
case CMDLINE_INTPTR: {
int base = 10;
if (!strncmp(value, "0x", 2)) {
base = 0x10;
value += 2;
}
current->func_intptr(atol(value, base));
break;
}
case CMDLINE_UINTPTR: {
int base = 10;
if (!strncmp(value, "0x", 2)) {
base = 0x10;
value += 2;
}
current->func_uintptr(atolu(value, base));
break;
}
}
break;
}
opt++;
}
if (idx >= len)
break;
}
}
+97
View File
@@ -0,0 +1,97 @@
ENTRY(_start)
PHDRS {
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
dynamic PT_DYNAMIC FLAGS(6);
percpu PT_LOAD FLAGS(6);
}
SECTIONS
{
. = 0xffffffff80000000;
__ld_kernel_start = .;
.text ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_text_start = .;
KEEP(*(.init.text))
*(.text .text.*)
*(.plt .plt.*)
__ld_text_end = .;
} :text
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_rodata_start = .;
__ld_cmdline_start = .;
KEEP(*(.cmdline))
__ld_cmdline_end = .;
*(.rodata .rodata.*)
*(.got .got.*)
__ld_rodata_end = .;
} :rodata
.data ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_data_start = .;
__ld_console_start = .;
KEEP(*(.console))
__ld_console_end = .;
KEEP(*(SORT_BY_NAME(.init.data.*)))
KEEP(*(.init.data))
*(.data .data.*)
} :data
.dynsym : {
__ld_dynsym_start = .;
*(.dynsym)
__ld_dynsym_end = .;
} :data
.dynstr : {
__ld_dynstr_start = .;
*(.dynstr)
__ld_dynstr_end = .;
} :data
.dynamic : {
*(.dynamic)
} :data :dynamic
.bss : {
/* Small stack to bootstrap the allocator */
__ld_stack_bottom = .;
. += 0x2000;
__ld_stack_top = .;
*(.*bss .*bss.*)
*(COMMON)
} :data
/* Per-CPU data block. MUST always come last. */
.percpu ALIGN(CONSTANT(MAXPAGESIZE)) : {
__ld_percpu_start = .;
KEEP(*(.percpu))
. = ALIGN(CONSTANT(MAXPAGESIZE));
__ld_percpu_end = .;
__ld_data_end = .;
} :percpu
__ld_kernel_end = .;
/DISCARD/ :
{
*(.comment*)
*(.eh_frame*)
*(.rel.eh_frame*)
*(.gcc_except_table*)
*(.note.gnu.property)
*(.interp)
}
}
+93
View File
@@ -0,0 +1,93 @@
#include <string.h>
#include <zinnia/alloc.h>
#include <zinnia/cmdline.h>
#include <zinnia/init.h>
#include <zinnia/print.h>
#include <zinnia/utils.h>
#include "limine.h"
[[__initdata_sorted("limine.0")]]
static volatile uint64_t limine_start[] = LIMINE_REQUESTS_START_MARKER;
[[__initdata_sorted("limine.1")]]
static volatile uint64_t limine_revision[] = LIMINE_BASE_REVISION(6);
[[__initdata_sorted("limine.2")]]
static volatile uint64_t limine_end[] = LIMINE_REQUESTS_END_MARKER;
#define LIMINE_REQUEST(request, tag, rev) \
[[__initdata_sorted("limine.1")]] \
static volatile struct limine_##request request = { \
.id = tag, \
.revision = rev, \
.response = nullptr, \
}
LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST_ID, 0);
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST_ID, 0);
LIMINE_REQUEST(executable_address_request, LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, 0);
LIMINE_REQUEST(executable_file_request, LIMINE_EXECUTABLE_FILE_REQUEST_ID, 0);
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST_ID, 1);
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST_ID, 0);
LIMINE_REQUEST(dtb_request, LIMINE_DTB_REQUEST_ID, 0);
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST_ID, 0);
[[__initdata]]
static struct phys_mem mem[128];
[[__initdata]]
static struct boot_file files[8];
[[__init]]
void kernel_entry() {
kernel_early_init();
struct boot_info info;
struct limine_memmap_response* mm_res = memmap_request.response;
struct limine_module_response* module_res = module_request.response;
struct limine_executable_address_response* exec_res = executable_address_request.response;
struct limine_executable_file_response* exec_file_res = executable_file_request.response;
info.cmdline = exec_file_res->executable_file->string;
info.num_mem_maps = MIN(ARRAY_SIZE(mem), mm_res->entry_count);
for (size_t i = 0; i < info.num_mem_maps; i++) {
mem[i].address = mm_res->entries[i]->base;
mem[i].length = mm_res->entries[i]->length;
switch (mm_res->entries[i]->type) {
case LIMINE_MEMMAP_USABLE:
mem[i].usage = PHYS_USABLE;
break;
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
mem[i].usage = PHYS_RECLAIMABLE;
break;
case LIMINE_MEMMAP_EXECUTABLE_AND_MODULES:
mem[i].usage = PHYS_STATIC;
break;
default:
mem[i].usage = PHYS_RESERVED;
break;
}
}
if (module_res) {
info.files = files;
info.num_files = MIN(ARRAY_SIZE(files), module_res->module_count);
for (size_t i = 0; i < info.num_files; i++) {
files[i].data = (phys_t)module_res->modules[i]->address - hhdm_request.response->offset;
files[i].length = module_res->modules[i]->size;
strncpy(files[i].path, module_res->modules[i]->path, sizeof(files[i].path));
}
}
info.mem_map = mem;
info.phys_base = (phys_t)exec_res->physical_base;
info.virt_base = (uintptr_t)exec_res->virtual_base;
info.hhdm_base = (uintptr_t)hhdm_request.response->offset;
info.rsdp = (phys_t)rsdp_request.response->address - info.hhdm_base;
kernel_main(&info);
}
phys_t rsdp_addr;
+669
View File
@@ -0,0 +1,669 @@
/* SPDX-License-Identifier: 0BSD */
/* Copyright (C) 2022-2026 Mintsuki and contributors.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LIMINE_H
#define LIMINE_H 1
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Misc */
#ifdef LIMINE_NO_POINTERS
# define LIMINE_PTR(TYPE) uint64_t
#else
# define LIMINE_PTR(TYPE) TYPE
#endif
#define LIMINE_REQUESTS_START_MARKER { 0xf6b8f4b39de7d1ae, 0xfab91a6940fcb9cf, \
0x785c6ed015d3e316, 0x181e920a7852b9d9 }
#define LIMINE_REQUESTS_END_MARKER { 0xadc0e0531bb10d03, 0x9572709f31764c62 }
#define LIMINE_BASE_REVISION(N) { 0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N) }
#define LIMINE_BASE_REVISION_SUPPORTED(VAR) ((VAR)[2] == 0)
#define LIMINE_LOADED_BASE_REVISION_VALID(VAR) ((VAR)[1] != 0x6a7b384944536bdc)
#define LIMINE_LOADED_BASE_REVISION(VAR) ((VAR)[1])
#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b
struct limine_uuid {
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t d[8];
};
#define LIMINE_MEDIA_TYPE_GENERIC 0
#define LIMINE_MEDIA_TYPE_OPTICAL 1
#define LIMINE_MEDIA_TYPE_TFTP 2
struct limine_file {
uint64_t revision;
LIMINE_PTR(void *) address;
uint64_t size;
LIMINE_PTR(char *) path;
LIMINE_PTR(char *) string;
uint32_t media_type;
uint32_t unused;
uint8_t tftp_ipv4[4];
uint32_t tftp_port;
uint32_t partition_index;
uint32_t mbr_disk_id;
struct limine_uuid gpt_disk_uuid;
struct limine_uuid gpt_part_uuid;
struct limine_uuid part_uuid;
};
/* Boot info */
#define LIMINE_BOOTLOADER_INFO_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740 }
struct limine_bootloader_info_response {
uint64_t revision;
LIMINE_PTR(char *) name;
LIMINE_PTR(char *) version;
};
struct limine_bootloader_info_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_bootloader_info_response *) response;
};
/* Executable command line */
#define LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x4b161536e598651e, 0xb390ad4a2f1f303a }
struct limine_executable_cmdline_response {
uint64_t revision;
LIMINE_PTR(char *) cmdline;
};
struct limine_executable_cmdline_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_executable_cmdline_response *) response;
};
/* Firmware type */
#define LIMINE_FIRMWARE_TYPE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x8c2f75d90bef28a8, 0x7045a4688eac00c3 }
#define LIMINE_FIRMWARE_TYPE_X86BIOS 0
#define LIMINE_FIRMWARE_TYPE_EFI32 1
#define LIMINE_FIRMWARE_TYPE_EFI64 2
#define LIMINE_FIRMWARE_TYPE_SBI 3
struct limine_firmware_type_response {
uint64_t revision;
uint64_t firmware_type;
};
struct limine_firmware_type_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_firmware_type_response *) response;
};
/* Stack size */
#define LIMINE_STACK_SIZE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d }
struct limine_stack_size_response {
uint64_t revision;
};
struct limine_stack_size_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_stack_size_response *) response;
uint64_t stack_size;
};
/* HHDM */
#define LIMINE_HHDM_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b }
struct limine_hhdm_response {
uint64_t revision;
uint64_t offset;
};
struct limine_hhdm_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_hhdm_response *) response;
};
/* Framebuffer */
#define LIMINE_FRAMEBUFFER_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b }
#define LIMINE_FRAMEBUFFER_RGB 1
struct limine_video_mode {
uint64_t pitch;
uint64_t width;
uint64_t height;
uint16_t bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
};
struct limine_framebuffer {
LIMINE_PTR(void *) address;
uint64_t width;
uint64_t height;
uint64_t pitch;
uint16_t bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
uint8_t unused[7];
uint64_t edid_size;
LIMINE_PTR(void *) edid;
/* Response revision 1 */
uint64_t mode_count;
LIMINE_PTR(struct limine_video_mode **) modes;
};
struct limine_framebuffer_response {
uint64_t revision;
uint64_t framebuffer_count;
LIMINE_PTR(struct limine_framebuffer **) framebuffers;
};
struct limine_framebuffer_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_framebuffer_response *) response;
};
/* Flanterm FB init params */
#define LIMINE_FLANTERM_FB_INIT_PARAMS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3259399fe7c5f126, 0xe01c1c8c5db9d1a9 }
#define LIMINE_FLANTERM_FB_ROTATE_0 0
#define LIMINE_FLANTERM_FB_ROTATE_90 1
#define LIMINE_FLANTERM_FB_ROTATE_180 2
#define LIMINE_FLANTERM_FB_ROTATE_270 3
struct limine_flanterm_fb_init_params {
LIMINE_PTR(uint32_t *) canvas;
uint64_t canvas_size;
uint32_t ansi_colours[8];
uint32_t ansi_bright_colours[8];
uint32_t default_bg;
uint32_t default_fg;
uint32_t default_bg_bright;
uint32_t default_fg_bright;
LIMINE_PTR(void *) font;
uint64_t font_width;
uint64_t font_height;
uint64_t font_spacing;
uint64_t font_scale_x;
uint64_t font_scale_y;
uint64_t margin;
uint64_t rotation;
};
struct limine_flanterm_fb_init_params_response {
uint64_t revision;
uint64_t entry_count;
LIMINE_PTR(struct limine_flanterm_fb_init_params **) entries;
};
struct limine_flanterm_fb_init_params_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_flanterm_fb_init_params_response *) response;
};
/* Paging mode */
#define LIMINE_PAGING_MODE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a }
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
#define LIMINE_PAGING_MODE_X86_64_MIN LIMINE_PAGING_MODE_X86_64_4LVL
#define LIMINE_PAGING_MODE_X86_64_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
#define LIMINE_PAGING_MODE_AARCH64_MIN LIMINE_PAGING_MODE_AARCH64_4LVL
#define LIMINE_PAGING_MODE_AARCH64_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
#define LIMINE_PAGING_MODE_RISCV_SV39 0
#define LIMINE_PAGING_MODE_RISCV_SV48 1
#define LIMINE_PAGING_MODE_RISCV_SV57 2
#define LIMINE_PAGING_MODE_RISCV_MIN LIMINE_PAGING_MODE_RISCV_SV39
#define LIMINE_PAGING_MODE_RISCV_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
#define LIMINE_PAGING_MODE_LOONGARCH_4LVL 0
#define LIMINE_PAGING_MODE_LOONGARCH_MIN LIMINE_PAGING_MODE_LOONGARCH_4LVL
#define LIMINE_PAGING_MODE_LOONGARCH_DEFAULT LIMINE_PAGING_MODE_LOONGARCH_4LVL
struct limine_paging_mode_response {
uint64_t revision;
uint64_t mode;
};
struct limine_paging_mode_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_paging_mode_response *) response;
uint64_t mode;
uint64_t max_mode;
uint64_t min_mode;
};
/* MP */
#define LIMINE_MP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0 }
struct limine_mp_info;
typedef void (*limine_goto_address)(struct limine_mp_info *);
#if defined (__x86_64__) || defined (__i386__)
#define LIMINE_MP_RESPONSE_X86_64_X2APIC (1 << 0)
struct limine_mp_info {
uint32_t processor_id;
uint32_t lapic_id;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint32_t flags;
uint32_t bsp_lapic_id;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info **) cpus;
};
#elif defined (__aarch64__)
struct limine_mp_info {
uint32_t processor_id;
uint32_t reserved1;
uint64_t mpidr;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint64_t flags;
uint64_t bsp_mpidr;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info **) cpus;
};
#elif defined (__riscv) && (__riscv_xlen == 64)
struct limine_mp_info {
uint64_t processor_id;
uint64_t hartid;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint64_t flags;
uint64_t bsp_hartid;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info **) cpus;
};
#elif defined (__loongarch__) && (__loongarch_grlen == 64)
struct limine_mp_info {
uint64_t processor_id;
uint64_t phys_id;
uint64_t reserved;
LIMINE_PTR(limine_goto_address) goto_address;
uint64_t extra_argument;
};
struct limine_mp_response {
uint64_t revision;
uint64_t flags;
uint64_t bsp_phys_id;
uint64_t cpu_count;
LIMINE_PTR(struct limine_mp_info **) cpus;
};
#else
#error Unknown architecture
#endif
#define LIMINE_MP_REQUEST_X86_64_X2APIC (1 << 0)
struct limine_mp_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_mp_response *) response;
uint64_t flags;
};
/* Memory map */
#define LIMINE_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62 }
#define LIMINE_MEMMAP_USABLE 0
#define LIMINE_MEMMAP_RESERVED 1
#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2
#define LIMINE_MEMMAP_ACPI_NVS 3
#define LIMINE_MEMMAP_BAD_MEMORY 4
#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5
#define LIMINE_MEMMAP_EXECUTABLE_AND_MODULES 6
#define LIMINE_MEMMAP_FRAMEBUFFER 7
#define LIMINE_MEMMAP_RESERVED_MAPPED 8
struct limine_memmap_entry {
uint64_t base;
uint64_t length;
uint64_t type;
};
struct limine_memmap_response {
uint64_t revision;
uint64_t entry_count;
LIMINE_PTR(struct limine_memmap_entry **) entries;
};
struct limine_memmap_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_memmap_response *) response;
};
/* Entry point */
#define LIMINE_ENTRY_POINT_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a }
typedef void (*limine_entry_point)(void);
struct limine_entry_point_response {
uint64_t revision;
};
struct limine_entry_point_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_entry_point_response *) response;
LIMINE_PTR(limine_entry_point) entry;
};
/* Executable File */
#define LIMINE_EXECUTABLE_FILE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69 }
struct limine_executable_file_response {
uint64_t revision;
LIMINE_PTR(struct limine_file *) executable_file;
};
struct limine_executable_file_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_executable_file_response *) response;
};
/* Module */
#define LIMINE_MODULE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee }
#define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0)
#define LIMINE_INTERNAL_MODULE_COMPRESSED (1 << 1)
struct limine_internal_module {
LIMINE_PTR(const char *) path;
LIMINE_PTR(const char *) string;
uint64_t flags;
};
struct limine_module_response {
uint64_t revision;
uint64_t module_count;
LIMINE_PTR(struct limine_file **) modules;
};
struct limine_module_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_module_response *) response;
/* Request revision 1 */
uint64_t internal_module_count;
LIMINE_PTR(struct limine_internal_module **) internal_modules;
};
/* RSDP */
#define LIMINE_RSDP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c }
struct limine_rsdp_response {
uint64_t revision;
LIMINE_PTR(void *) address;
};
struct limine_rsdp_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_rsdp_response *) response;
};
/* SMBIOS */
#define LIMINE_SMBIOS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee }
struct limine_smbios_response {
uint64_t revision;
LIMINE_PTR(void *) entry_32;
LIMINE_PTR(void *) entry_64;
};
struct limine_smbios_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_smbios_response *) response;
};
/* EFI system table */
#define LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc }
struct limine_efi_system_table_response {
uint64_t revision;
LIMINE_PTR(void *) address;
};
struct limine_efi_system_table_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_efi_system_table_response *) response;
};
/* TPM event log */
#define LIMINE_TPM_EVENT_LOG_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x98e094fc7e76e979, 0xee8d8775c54e1d1f }
#define LIMINE_TPM_EVENT_LOG_FORMAT_TCG_1_2 1
#define LIMINE_TPM_EVENT_LOG_FORMAT_TCG_2 2
struct limine_tpm_event_log_response {
uint64_t revision;
uint64_t format;
uint64_t size;
LIMINE_PTR(void *) address;
};
struct limine_tpm_event_log_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_tpm_event_log_response *) response;
};
/* EFI memory map */
#define LIMINE_EFI_MEMMAP_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8 }
struct limine_efi_memmap_response {
uint64_t revision;
LIMINE_PTR(void *) memmap;
uint64_t memmap_size;
uint64_t desc_size;
uint64_t desc_version;
};
struct limine_efi_memmap_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_efi_memmap_response *) response;
};
/* Date at boot */
#define LIMINE_DATE_AT_BOOT_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893 }
struct limine_date_at_boot_response {
uint64_t revision;
int64_t timestamp;
};
struct limine_date_at_boot_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_date_at_boot_response *) response;
};
/* Executable address */
#define LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487 }
struct limine_executable_address_response {
uint64_t revision;
uint64_t physical_base;
uint64_t virtual_base;
};
struct limine_executable_address_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_executable_address_response *) response;
};
/* Device Tree Blob */
#define LIMINE_DTB_REQUEST_ID { LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7 }
struct limine_dtb_response {
uint64_t revision;
LIMINE_PTR(void *) dtb_ptr;
};
struct limine_dtb_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_dtb_response *) response;
};
/* RISC-V Boot Hart ID */
#define LIMINE_RISCV_BSP_HARTID_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x1369359f025525f9, 0x2ff2a56178391bb6 }
struct limine_riscv_bsp_hartid_response {
uint64_t revision;
uint64_t bsp_hartid;
};
struct limine_riscv_bsp_hartid_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_riscv_bsp_hartid_response *) response;
};
/* Bootloader Performance */
#define LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x6b50ad9bf36d13ad, 0xdc4c7e88fc759e17 }
struct limine_bootloader_performance_response {
uint64_t revision;
uint64_t reset_usec;
uint64_t init_usec;
uint64_t exec_usec;
};
struct limine_bootloader_performance_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_bootloader_performance_response *) response;
};
#define LIMINE_X86_64_KEEP_IOMMU_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x8ebaabe51f490179, 0x2aa86a59ffb4ab0f }
struct limine_x86_64_keep_iommu_response {
uint64_t revision;
};
struct limine_x86_64_keep_iommu_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_x86_64_keep_iommu_response *) response;
};
/* TSC (Timestamp Counter) Frequency */
#define LIMINE_TSC_FREQUENCY_REQUEST_ID { LIMINE_COMMON_MAGIC, 0x10f2ee1d87d195e4, 0xf747a2b78f6ddb31 }
struct limine_tsc_frequency_response {
uint64_t revision;
uint64_t frequency;
};
struct limine_tsc_frequency_request {
uint64_t id[4];
uint64_t revision;
LIMINE_PTR(struct limine_tsc_frequency_response *) response;
};
#ifdef __cplusplus
}
#endif
#endif
+1
View File
@@ -0,0 +1 @@
kernel_sources += files('entry.c')
+76
View File
@@ -0,0 +1,76 @@
#include <zinnia/alloc.h>
#include <zinnia/clock.h>
#include <zinnia/cmdline.h>
#include <zinnia/compiler.h>
#include <zinnia/console.h>
#include <zinnia/elf.h>
#include <zinnia/futex.h>
#include <zinnia/init.h>
#include <zinnia/irq.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/vas.h>
#include <zinnia/vmo.h>
#include <assert.h>
#include <config.h>
#include <stdint.h>
static const char zinnia_banner[] =
"Zinnia " ZINNIA_VERSION " (" ZINNIA_ARCH ", " ZINNIA_COMPILER_ID ", " ZINNIA_LINKER_ID ")";
static void kernel_main_task(uintptr_t arg0) {
struct boot_info* info = (struct boot_info*)arg0;
printf("%s\n", zinnia_banner); // Say hello!
assert(info->num_files >= 1, "No init executable provided\n");
struct vas* init_space;
assert(vma_new(&init_space) == 0, "");
struct boot_file* file_data = &info->files[0];
struct paged_vmo* init_file;
assert(vmo_new_phys(&init_file) == 0, "");
vmo_write(&init_file->object, 0, HHDM_PTR(file_data->data), file_data->length, nullptr);
// Load init.
const char* init_argv[] = {"init", nullptr};
const char* init_envp[] = {nullptr};
struct task* init_task = nullptr;
errno_t status = 0;
assert(status == 0, "Failed to load init process (%i)\n", status);
sched_add_task(&percpu_get()->sched, init_task);
while (true) {
arch_irq_wait();
}
}
[[noreturn]]
void kmain(struct boot_info* info) {
cmdline_parse(info->cmdline);
console_init();
mem_init(info->mem_map, info->num_mem_maps, info->virt_base, info->phys_base, info->hhdm_base);
rsdp_addr = info->rsdp; // TODO
percpu_init();
sched_init(&percpu_get()->sched);
futex_init();
struct task* main_task;
task_create("main", &kernel_vas, nullptr, kernel_main_task, (uintptr_t)info, &main_task);
sched_add_task(&percpu_get()->sched, main_task);
irq_unlock();
sched_yield(&percpu_get()->sched);
unreachable();
}
[[__init]]
void kernel_early_init() {
percpu_bsp_init();
percpu_get()->online = true;
irq_lock();
}
+6
View File
@@ -0,0 +1,6 @@
kernel_sources += files(
'cmdline.c',
'main.c',
)
subdir('limine')
+54
View File
@@ -0,0 +1,54 @@
#include <zinnia/clock.h>
#include <zinnia/compiler.h>
#include <zinnia/print.h>
#include <zinnia/spin.h>
#include <stdatomic.h>
#include <stdint.h>
static struct clock* active_clock = nullptr;
static uint64_t counter_base = 0;
static struct spinlock lock = {};
bool clock_available() {
return active_clock != nullptr;
}
uint64_t clock_get_elapsed_ns() {
struct clock* active = atomic_load(&active_clock);
if (__unlikely(!active))
return 0;
return atomic_load(&counter_base) + active->get_elapsed_ns(active);
}
bool clock_switch(struct clock* clock) {
spin_lock(&lock);
if (active_clock) {
if (clock->priority <= active_clock->priority) {
spin_unlock(&lock);
return false;
}
}
printf("Switching to clock source \"%s\"\n", clock->name);
uint64_t elapsed = clock_get_elapsed_ns();
counter_base += elapsed;
active_clock = clock;
spin_unlock(&lock);
return true;
}
void clock_spin_ns(uint64_t ns) {
if (active_clock == nullptr) {
printf("Unable to sleep for %lu ns, no clock available!\n", ns);
return;
}
uint64_t target = clock_get_elapsed_ns() + ns;
while (clock_get_elapsed_ns() < target) {
// TODO: Maybe pause?
}
}
+43
View File
@@ -0,0 +1,43 @@
#include <zinnia/cmdline.h>
#include <zinnia/console.h>
#include <zinnia/print.h>
#include <string.h>
extern struct console* const __ld_console_start[];
extern struct console* const __ld_console_end[];
struct console* active = nullptr;
void console_write(const char* buf, size_t len) {
if (__likely(active))
active->write(active, buf, len);
}
static void console_setup(const char* value) {
if (!value)
return;
// Find a suitable boot console.
struct console* const* cur = __ld_console_start;
while (cur < __ld_console_end) {
if (!strcmp(value, (*cur)->name)) {
active = *cur;
}
cur++;
}
}
CMDLINE_OPTION("console", console_setup);
void console_init() {
// If we have no console set and at least one set, use that as a default.
if (!active && &__ld_console_start[0] != &__ld_console_end[0])
active = __ld_console_start[0];
if (active) {
if (active->init(active) == false)
active = nullptr;
else
printf("Logging on console \"%s\"\n", active->name);
}
}
+200
View File
@@ -0,0 +1,200 @@
#include <zinnia/elf.h>
#include <zinnia/errno.h>
#include <zinnia/mem/alloc.h>
#include <zinnia/percpu.h>
#include <zinnia/pmap.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/utils.h>
#include <zinnia/vas.h>
#include <zinnia/vdso.h>
#include <zinnia/vmo.h>
#include <assert.h>
#include <bits/elf.h>
#include <string.h>
errno_t elf_load(struct exec_info* info, struct task** out) {
errno_t status;
size_t read;
struct elf_ehdr ehdr = {};
vmo_read(info->file_obj, 0, &ehdr, sizeof(ehdr), &read);
// Check if the file is an ELF.
if (memcmp(ehdr.e_ident, ELF_MAG, sizeof(ELF_MAG)) != 0)
return EINVAL;
if (ehdr.e_ident[EI_VERSION] != EV_CURRENT)
return EINVAL;
if (ehdr.e_ident[EI_CLASS] != ELF_ARCH_CLASS)
return EINVAL;
if (ehdr.e_ident[EI_DATA] != ELF_ARCH_DATA)
return EINVAL;
if (ehdr.e_machine != ELF_ARCH_MACHINE)
return EINVAL;
if (ehdr.e_type != ET_EXEC)
return EINVAL;
const size_t page_size = arch_mem_page_size();
for (size_t i = 0; i < ehdr.e_phnum; i++) {
struct elf_phdr phdr = {};
vmo_read(info->file_obj, (ehdr.e_phoff + (i * ehdr.e_phentsize)), &phdr, sizeof(phdr), &read);
if (phdr.p_type == PT_LOAD) {
enum zn_vm_flags prot = 0;
if (phdr.p_flags & PF_R)
prot |= ZN_VM_MAP_READ;
if (phdr.p_flags & PF_W)
prot |= ZN_VM_MAP_WRITE;
if (phdr.p_flags & PF_X)
prot |= ZN_VM_MAP_EXEC;
assert(phdr.p_offset % phdr.p_align == phdr.p_vaddr % phdr.p_align, "");
const uintptr_t misalign = phdr.p_vaddr & (page_size - 1);
const uintptr_t map_address = phdr.p_vaddr - misalign;
const size_t backed_map_size = (phdr.p_filesz + misalign + page_size - 1) & ~(page_size - 1);
const size_t total_map_size = (phdr.p_memsz + misalign + page_size - 1) & ~(page_size - 1);
// Copy the file data into its own mapping.
struct paged_vmo* phdr_obj;
status = vmo_new_phys(&phdr_obj);
if (status)
return status;
status = vmo_copy(&phdr_obj->object, phdr.p_offset, info->file_obj, phdr.p_offset, phdr.p_filesz, nullptr);
if (status)
return status;
// We map more than we copied so the rest is filled with zeroed pages.
status = vma_map_vmo(info->space, &phdr_obj->object, phdr.p_vaddr, phdr.p_memsz, prot, phdr.p_offset);
if (status)
return status;
}
}
const uintptr_t highest = ((uintptr_t)1 << (mem_high_shift() - 1)) - page_size;
const size_t stack_size = 2 * 1024 * 1024; // 2MiB
const uintptr_t stack_start = highest - stack_size;
// Allocate stack.
struct paged_vmo* stack;
status = vmo_new_phys(&stack);
if (status)
return status;
// Fill in stack info.
uintptr_t stack_off = stack_size;
// TODO: Instead of allocating, calculate it on the fly.
size_t num_envp = 0;
for (const char** ptr = info->envp; *ptr != nullptr; ptr++) {
num_envp++;
}
size_t num_argv = 0;
for (const char** ptr = info->argv; *ptr != nullptr; ptr++) {
num_argv++;
}
uintptr_t* envp_offsets = mem_alloc(num_envp * sizeof(uintptr_t), 0);
uintptr_t* argv_offsets = mem_alloc(num_argv * sizeof(uintptr_t), 0);
for (size_t env = 0; env < num_envp; env++) {
const char nul = 0;
stack_off -= 1;
vmo_write(&stack->object, stack_off, &nul, sizeof(nul), nullptr);
const size_t len = strlen(info->envp[env]);
stack_off -= len;
vmo_write(&stack->object, stack_off, info->envp[env], len, nullptr);
envp_offsets[env] = stack_start + stack_off;
}
for (size_t arg = 0; arg < num_argv; arg++) {
const char nul = 0;
stack_off -= 1;
vmo_write(&stack->object, stack_off, &nul, sizeof(nul), nullptr);
const size_t len = strlen(info->argv[arg]);
stack_off -= len;
vmo_write(&stack->object, stack_off, info->argv[arg], len, nullptr);
argv_offsets[arg] = stack_start + stack_off;
}
stack_off = ALIGN_DOWN(stack_off, 16);
// Align the stack if argc + argv + envp does not add up to 16 byte alignment.
if ((1 + num_argv + num_envp) & 1) {
stack_off -= sizeof(uintptr_t);
uintptr_t zero = 0;
vmo_write(&stack->object, stack_off, &zero, sizeof(zero), nullptr);
}
// Add the init handle to the process namespace.
zn_handle_t init_handle = ZN_HANDLE_INVALID;
if (info->init_handle) {
status = namespace_add_desc(info->ns, *info->init_handle, &init_handle);
if (status)
return status;
}
#define WRITE_AUXV(auxv, value) \
do { \
stack_off -= sizeof(uintptr_t); \
uintptr_t auxv_val = value; \
vmo_write(&stack->object, stack_off, &auxv_val, sizeof(uintptr_t), nullptr); \
stack_off -= sizeof(uintptr_t); \
auxv_val = auxv; \
vmo_write(&stack->object, stack_off, &auxv_val, sizeof(uintptr_t), nullptr); \
} while (0)
// Write auxiliary values.
WRITE_AUXV(AT_NULL, 0);
WRITE_AUXV(AT_SECURE, 0);
WRITE_AUXV(AT_PHNUM, ehdr.e_phnum);
WRITE_AUXV(AT_PHENT, ehdr.e_phentsize);
WRITE_AUXV(AT_ENTRY, ehdr.e_entry);
WRITE_AUXV(AT_INIT_HANDLE, init_handle);
// envp pointers.
stack_off -= sizeof(uintptr_t);
const uintptr_t zero = 0;
vmo_write(&stack->object, stack_off, &zero, sizeof(zero), nullptr);
for (size_t env = 0; env < num_envp; env++) {
stack_off -= sizeof(uintptr_t);
vmo_write(&stack->object, stack_off, &envp_offsets[env], sizeof(envp_offsets[env]), nullptr);
}
// argv pointers.
stack_off -= sizeof(uintptr_t);
vmo_write(&stack->object, stack_off, &zero, sizeof(zero), nullptr);
for (size_t arg = 0; arg < num_argv; arg++) {
stack_off -= sizeof(uintptr_t);
vmo_write(&stack->object, stack_off, &argv_offsets[arg], sizeof(argv_offsets[arg]), nullptr);
}
mem_free(envp_offsets);
mem_free(argv_offsets);
// argc
stack_off -= sizeof(uintptr_t);
uintptr_t argc = num_argv;
vmo_write(&stack->object, stack_off, &argc, sizeof(argc), nullptr);
status = vma_map_vmo(info->space, &stack->object, stack_start, stack_size, ZN_VM_MAP_READ | ZN_VM_MAP_WRITE, 0);
if (status)
return status;
struct task_start_info* start_info = mem_alloc(sizeof(struct task_start_info), 0);
start_info->ip = ehdr.e_entry;
start_info->sp = highest - stack_size + stack_off;
start_info->arg = 0;
struct task* result;
status = task_create(info->argv[0], info->space, info->ns, sched_to_user, (uintptr_t)start_info, &result);
if (status)
return status;
*out = result;
return 0;
}
+120
View File
@@ -0,0 +1,120 @@
#include <zinnia/errno.h>
#include <zinnia/futex.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/spin.h>
#include <zinnia/usercopy.h>
struct futex_bucket {
struct wait_queue wq;
uintptr_t key; // Virtual address used as the futex key.
bool in_use;
};
#define FUTEX_BUCKET_COUNT 64
static struct futex_bucket futex_table[FUTEX_BUCKET_COUNT];
static struct spinlock futex_table_lock;
void futex_init(void) {
futex_table_lock = (struct spinlock){0};
for (size_t i = 0; i < FUTEX_BUCKET_COUNT; i++) {
wait_queue_init(&futex_table[i].wq);
futex_table[i].key = 0;
futex_table[i].in_use = false;
}
}
static size_t futex_hash(uintptr_t addr) {
// Simple hash: mix the address bits and mod by bucket count.
addr = (addr >> 2) ^ (addr >> 12);
return addr % FUTEX_BUCKET_COUNT;
}
static struct futex_bucket* futex_bucket_get(uintptr_t addr) {
size_t idx = futex_hash(addr);
// Linear probing to find a matching or free bucket.
for (size_t i = 0; i < FUTEX_BUCKET_COUNT; i++) {
size_t slot = (idx + i) % FUTEX_BUCKET_COUNT;
struct futex_bucket* b = &futex_table[slot];
if (b->in_use && b->key == addr)
return b;
if (!b->in_use) {
b->key = addr;
b->in_use = true;
return b;
}
}
return nullptr;
}
errno_t futex_wait(__user int* addr, int expected) {
uintptr_t key = (uintptr_t)addr;
spin_lock(&futex_table_lock);
// Read the current value at the userspace address.
int current;
if (!usercopy_read(&current, addr, sizeof(*addr))) {
spin_unlock(&futex_table_lock);
return EFAULT;
}
// If the value has already changed, don't block.
if (current != expected) {
spin_unlock(&futex_table_lock);
return 0;
}
struct futex_bucket* bucket = futex_bucket_get(key);
if (!bucket) {
spin_unlock(&futex_table_lock);
return ENOMEM;
}
spin_unlock(&futex_table_lock);
// Block on the bucket's wait queue.
struct sched_percpu* sched = &percpu_get()->sched;
sched_block(sched, &bucket->wq);
return 0;
}
errno_t futex_wake(__user int* addr, int count) {
uintptr_t key = (uintptr_t)addr;
spin_lock(&futex_table_lock);
size_t idx = futex_hash(key);
struct futex_bucket* bucket = nullptr;
for (size_t i = 0; i < FUTEX_BUCKET_COUNT; i++) {
size_t slot = (idx + i) % FUTEX_BUCKET_COUNT;
struct futex_bucket* b = &futex_table[slot];
if (b->in_use && b->key == key) {
bucket = b;
break;
}
if (!b->in_use)
break;
}
if (!bucket) {
spin_unlock(&futex_table_lock);
return 0; // No waiters, nothing to do.
}
spin_unlock(&futex_table_lock);
struct sched_percpu* sched = &percpu_get()->sched;
if (count == INT32_MAX) {
sched_wake_all(sched, &bucket->wq);
} else {
for (int i = 0; i < count; i++) {
sched_wake(sched, &bucket->wq);
}
}
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
#include <stdatomic.h>
#include <zinnia/irq.h>
#include <zinnia/percpu.h>
void irq_lock() {
struct irq_percpu* cpu = &percpu_get()->irq;
if (atomic_load_explicit(&cpu->in_interrupt, memory_order_acquire))
return;
arch_irq_set_state(false);
atomic_fetch_add_explicit(&percpu_get()->irq.level, 1, memory_order_acq_rel);
}
void irq_unlock() {
struct irq_percpu* cpu = &percpu_get()->irq;
if (atomic_load_explicit(&cpu->in_interrupt, memory_order_acquire))
return;
uint32_t old_level = atomic_fetch_sub_explicit(&cpu->level, 1, memory_order_acq_rel);
// If it was 1, the new IRQ level is now 0.
if (old_level == 1) {
arch_irq_set_state(true);
}
}
bool irq_set_interrupted(bool is_interrupted) {
return atomic_exchange_explicit(&percpu_get()->irq.in_interrupt, is_interrupted, memory_order_release);
}
+16
View File
@@ -0,0 +1,16 @@
kernel_sources += files(
'clock.c',
'console.c',
'elf.c',
'futex.c',
'irq.c',
'panic.c',
'percpu.c',
'print.c',
'spin.c',
'uacpi_glue.c',
)
if get_option('sched') == 'simple'
kernel_sources += files('sched_simple.c')
endif
+32
View File
@@ -0,0 +1,32 @@
#include <zinnia/irq.h>
#include <zinnia/panic.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <stdarg.h>
[[noreturn]]
void panic(const char* msg, ...) {
// TODO: Stop all other CPUs with IPI.
arch_irq_set_state(false);
struct percpu* cpu = percpu_get();
struct task* current = cpu->sched.current;
printf(
"----[ Kernel panic ]----\n"
"In task \"%s\" (TID %zu) on CPU %zu!\n",
current ? current->name : nullptr,
current ? current->id : 0,
cpu->id
);
va_list args;
va_start(args, msg);
kvprintf(msg, args);
va_end(args);
printf("----[ End of panic ]----\n");
while (1) {
arch_irq_wait();
}
}
+14
View File
@@ -0,0 +1,14 @@
#include <zinnia/compiler.h>
#include <zinnia/percpu.h>
[[__used, __section(".percpu")]]
struct percpu percpu_bsp = {
.self = &percpu_bsp,
.id = 0,
.online = false,
};
struct percpu* percpu_new() {
// TODO
return nullptr;
}
+399
View File
@@ -0,0 +1,399 @@
#include <zinnia/clock.h>
#include <zinnia/console.h>
#include <zinnia/panic.h>
#include <zinnia/print.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
static void reverse(char* s) {
int i, j;
char c;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
static int char_to_digit(char c) {
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'z')
return c - 'a' + 10;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 10;
return -1;
}
unsigned long atolu(const char* str, int base) {
unsigned long result = 0;
if (base == 16 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
str += 2;
}
while (*str) {
int digit = char_to_digit(*str++);
if (digit < 0 || digit >= base)
break;
result = result * base + digit;
}
return result;
}
long atol(const char* str, int base) {
int neg = 0;
if (*str == '-') {
neg = 1;
str++;
}
unsigned long val = atolu(str, base);
return neg ? -(long)val : (long)val;
}
char* lutoa(uint64_t value, char* str, int base) {
if (base < 2 || base > 36)
return nullptr;
size_t i = 0;
do {
uint64_t digit = value % base;
str[i++] = (digit < 10) ? '0' + digit : 'a' + (digit - 10);
value /= base;
} while (value);
str[i] = '\0';
reverse(str);
return str;
}
char* ltoa(int64_t value, char* str, int base) {
if (base < 2 || base > 36)
return nullptr;
uint64_t magnitude;
int negative = 0;
if (value < 0) {
negative = 1;
magnitude = (uint64_t)(-(value + 1)) + 1;
// avoids INT64_MIN overflow
} else {
magnitude = (uint64_t)value;
}
size_t i = 0;
do {
uint64_t digit = magnitude % base;
str[i++] = (digit < 10) ? '0' + digit : 'a' + (digit - 10);
magnitude /= base;
} while (magnitude);
if (negative)
str[i++] = '-';
str[i] = '\0';
reverse(str);
return str;
}
typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len);
static void do_print(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
// Amount of bytes written.
int32_t written = 0;
while (*fmt != '\0') {
size_t maxrem = INT32_MAX - written;
if (fmt[0] != '%' || fmt[1] == '%') {
if (fmt[0] == '%')
fmt++;
size_t amount = 1;
while (fmt[amount] && fmt[amount] != '%')
amount++;
if (maxrem < amount) {
return;
}
callback(ctx, fmt, amount);
fmt += amount;
written += amount;
continue;
}
const char* format_begun_at = fmt++;
bool force_sign = false;
bool blank_sign = false;
bool write_prefix = false;
bool pad_zero = false;
bool has_width = false;
size_t width = 0;
bool has_precision = false;
size_t precision = 0;
size_t size = sizeof(uint32_t);
char number[32];
check_fmt:
memset(number, 0, sizeof(number));
switch (*fmt) {
// Flags
case '+': {
force_sign = true;
fmt++;
goto check_fmt;
}
case ' ': {
blank_sign = true;
fmt++;
goto check_fmt;
}
case '#': {
write_prefix = true;
fmt++;
goto check_fmt;
}
case '0': {
pad_zero = true;
fmt++;
goto check_fmt;
}
// Width
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
has_width = true;
char number[10 + 1] = {0};
size_t idx = 0;
while (*fmt >= '0' && *fmt <= '9') {
number[idx++] = *fmt;
fmt++;
}
width = atolu(number, 10);
goto check_fmt;
}
// Precision
case '.': {
has_precision = true;
fmt++;
if (*fmt == '*') {
precision = (char)va_arg(args, int32_t);
} else {
char number[10 + 1] = {0};
size_t idx = 0;
while (*fmt >= '0' && *fmt <= '9') {
number[idx++] = *fmt;
fmt++;
}
precision = atolu(number, 10);
}
goto check_fmt;
}
// Length
case 'h': {
if (fmt[1] == 'h') {
size = sizeof(uint8_t);
fmt++;
} else
size = sizeof(uint16_t);
fmt++;
goto check_fmt;
}
case 'l': {
// There is no difference between `long int` and `long long int`.
if (fmt[1] == 'l')
fmt++;
fmt++;
size = sizeof(uint64_t);
goto check_fmt;
}
case 'z': {
size = sizeof(size_t);
fmt++;
goto check_fmt;
}
// Character
case 'c': {
char c = (char)va_arg(args, int32_t);
callback(ctx, &c, sizeof(c));
written++;
fmt++;
break;
}
// String of characters
case 's': {
const char* str = va_arg(args, const char*);
if (!str)
str = "(null)";
size_t len = 0;
if (has_precision)
len = strnlen(str, precision);
else
len = strlen(str);
callback(ctx, str, len);
written += len;
fmt++;
break;
}
// Signed decimal integer
case 'i':
case 'd': {
switch (size) {
case sizeof(int8_t):
ltoa((int8_t)va_arg(args, int32_t), number, 10);
break;
case sizeof(int16_t):
ltoa((int16_t)va_arg(args, int32_t), number, 10);
break;
case sizeof(int32_t):
ltoa((int32_t)va_arg(args, int32_t), number, 10);
break;
default:
ltoa(va_arg(args, int64_t), number, 10);
break;
}
goto print_num;
}
// Unsigned decimal integer
case 'u': {
switch (size) {
case sizeof(uint8_t):
lutoa((uint8_t)va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint16_t):
lutoa((uint16_t)va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint32_t):
lutoa(va_arg(args, uint32_t), number, 10);
break;
case sizeof(uint64_t):
lutoa(va_arg(args, uint64_t), number, 10);
break;
}
goto print_num;
}
// Unsigned hexadecimal integer
case 'X':
case 'x': {
switch (size) {
case sizeof(uint8_t):
lutoa((uint8_t)va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint16_t):
lutoa((uint16_t)va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint32_t):
lutoa(va_arg(args, uint32_t), number, 16);
break;
case sizeof(uint64_t):
lutoa(va_arg(args, uint64_t), number, 16);
break;
}
print_num:
size_t len = strlen(number);
if (has_precision) {
for (size_t i = 0; i < precision - len; i++)
callback(ctx, " ", 1);
}
if (has_width) {
char c = pad_zero ? '0' : ' ';
// We don't have to pad anything.
if (len < width) {
for (size_t i = 0; i < width - len; i++)
callback(ctx, &c, 1);
}
}
if (write_prefix) {
callback(ctx, "0x", 2);
}
if (force_sign) {
char c = '+';
if (number[0] != '-')
callback(ctx, &c, 1);
}
if (blank_sign) {
char c = ' ';
if (number[0] != '-')
callback(ctx, &c, 1);
}
callback(ctx, number, len);
written += len;
fmt++;
break;
}
// Pointer address
case 'p': {
const size_t num = va_arg(args, size_t);
const size_t buf_size = sizeof(size_t) * 2 + 1;
char str[buf_size];
lutoa(num, number, 0x10);
const size_t len = strlen(number); // Get the length of the final number.
for (size_t i = 0; i < buf_size; i++)
str[i] = '0';
size_t offset = sizeof(str) - len - 1;
callback(ctx, str, offset);
callback(ctx, number, len);
fmt++;
written += len;
break;
}
// The number of characters written so far is stored in the pointed location.
case 'n': {
int32_t* const ptr = va_arg(args, int32_t*);
*ptr = written;
fmt++;
break;
}
// No format, just normal text.
default: {
fmt = format_begun_at;
size_t len = strlen(fmt);
callback(ctx, fmt, len);
written += len;
fmt += len;
break;
}
}
}
}
static void printf_console(void*, const char* msg, size_t len) {
console_write(msg, len);
}
void printf(const char* message, ...) {
va_list args;
va_start(args, message);
do_print(printf_console, nullptr, message, args);
va_end(args);
}
void kvprintf(const char* message, va_list args) {
do_print(printf_console, nullptr, message, args);
}
+184
View File
@@ -0,0 +1,184 @@
#include <zinnia/alloc.h>
#include <zinnia/errno.h>
#include <zinnia/irq.h>
#include <zinnia/list.h>
#include <zinnia/percpu.h>
#include <zinnia/pmap.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/spin.h>
#include <zinnia/tailq.h>
#include <zinnia/vas.h>
#include <assert.h>
#include <bits/irq.h>
#include <stdatomic.h>
#include <stdint.h>
#include <string.h>
static void idle_func(uintptr_t) {
while (1) {
arch_irq_set_state(true);
arch_irq_wait();
}
}
void sched_to_user(uintptr_t info_ptr) {
struct task_start_info* info = (struct task_start_info*)info_ptr;
uintptr_t ip = info->ip;
uintptr_t sp = info->sp;
uintptr_t arg = info->arg;
mem_free(info);
arch_sched_jump_to_user(ip, sp, arg);
}
void sched_to_user_context(uintptr_t ctx) {
arch_sched_jump_to_context((struct arch_context*)ctx);
}
void sched_init(struct sched_percpu* sched) {
struct task* idle_task;
assert(task_create("idle", &kernel_vas, nullptr, idle_func, 0, &idle_task) == 0, "Unable to create idle task!\n");
sched->idle_task = idle_task;
sched->current = idle_task;
tailq_init(&sched->run_queue);
printf("Scheduler initialized\n");
}
void sched_add_task(struct sched_percpu* sched, struct task* task) {
assert(task != nullptr, "Tried to add NULL to the run queue\n");
tailq_insert_tail(&sched->run_queue, task, next);
}
static struct task* next_task(struct sched_percpu* sched) {
struct task* t = tailq_first(&sched->run_queue);
if (t == nullptr)
return sched->idle_task;
tailq_remove(&sched->run_queue, t, next);
return t;
}
static void do_reschedule(struct sched_percpu* sched) {
struct task* from = atomic_load_explicit(&sched->current, memory_order_acquire);
struct task* to = next_task(sched);
if (from == to) {
irq_unlock();
return;
}
atomic_store_explicit(&sched->current, to, memory_order_relaxed);
pmap_set(&to->space->pmap);
struct percpu* cpu = percpu_get();
from->kernel_stack = cpu->kernel_stack;
from->user_stack = cpu->user_stack;
cpu->kernel_stack = to->kernel_stack;
cpu->user_stack = to->user_stack;
arch_sched_switch(from, to);
}
void sched_reschedule(struct sched_percpu* sched) {
irq_lock();
if (sched->current->state != TASK_STATE_DEAD)
sched_add_task(sched, sched->current);
do_reschedule(sched);
}
void sched_yield(struct sched_percpu* sched) {
irq_lock();
do_reschedule(sched);
}
errno_t task_create(
const char* name,
struct vas* space,
struct namespace* ns,
task_fn_t entry,
uintptr_t arg0,
struct task** out
) {
if (!out)
return EINVAL;
struct task* new_task = mem_alloc(sizeof(struct task), 0);
if (new_task == nullptr)
return ENOMEM;
static size_t last_tid = 0;
new_task->id = atomic_fetch_add(&last_tid, 1);
new_task->space = space;
new_task->namespace = ns;
strncpy(new_task->name, name, sizeof(new_task->name));
// Allocate a kernel stack.
new_task->kernel_stack = (uintptr_t)mem_alloc(KERNEL_STACK_SIZE, 0);
if (!new_task->kernel_stack)
return ENOMEM;
new_task->user_stack = 0;
// Initialize the arch-dependent parts.
errno_t s = arch_task_init(&new_task->context, entry, arg0, new_task->kernel_stack + KERNEL_STACK_SIZE, true);
if (s != 0)
return s;
*out = new_task;
return 0;
}
void task_entry(task_fn_t entry, uintptr_t arg0) {
entry(arg0);
struct sched_percpu* sched = &percpu_get()->sched;
sched->current->state = TASK_STATE_DEAD;
sched_yield(sched);
panic("unreachable");
}
void wait_queue_init(struct wait_queue* wq) {
tailq_init(&wq->waiters);
wq->lock = (struct spinlock){0};
}
void sched_block(struct sched_percpu* sched, struct wait_queue* wq) {
irq_lock();
spin_lock(&wq->lock);
sched->current->state = TASK_STATE_BLOCKED;
tailq_insert_tail(&wq->waiters, sched->current, next);
spin_unlock(&wq->lock);
do_reschedule(sched);
}
void sched_wake(struct sched_percpu* sched, struct wait_queue* wq) {
spin_lock(&wq->lock);
struct task* t = tailq_first(&wq->waiters);
if (t != nullptr) {
tailq_remove(&wq->waiters, t, next);
t->state = TASK_STATE_READY;
sched_add_task(sched, t);
}
spin_unlock(&wq->lock);
}
void sched_wake_all(struct sched_percpu* sched, struct wait_queue* wq) {
spin_lock(&wq->lock);
struct task* t;
while ((t = tailq_first(&wq->waiters)) != nullptr) {
tailq_remove(&wq->waiters, t, next);
t->state = TASK_STATE_READY;
sched_add_task(sched, t);
}
spin_unlock(&wq->lock);
}
+15
View File
@@ -0,0 +1,15 @@
#include <zinnia/spin.h>
#include <stdatomic.h>
void spin_lock(struct spinlock* spin) {
uint32_t my = atomic_fetch_add_explicit(&spin->next, 1, memory_order_relaxed);
while (atomic_load_explicit(&spin->owner, memory_order_acquire) != my) {
asm volatile("pause");
}
}
void spin_unlock(struct spinlock* spin) {
uint32_t val = atomic_load_explicit(&spin->owner, memory_order_relaxed);
atomic_store_explicit(&spin->owner, val + 1, memory_order_release);
}
+34
View File
@@ -0,0 +1,34 @@
#include <stdarg.h>
#include <uacpi/kernel_api.h>
#include <uacpi/status.h>
#include <uacpi/types.h>
#include <zinnia/init.h>
#include <zinnia/mmio.h>
#include <zinnia/print.h>
#include <zinnia/utils.h>
uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr* out_rsdp_address) {
*out_rsdp_address = (uacpi_phys_addr)rsdp_addr;
return UACPI_STATUS_OK;
}
void* uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len) {
return mmio_new(addr, len);
}
void uacpi_kernel_unmap(void* addr, uacpi_size len) {
mmio_free(addr, len);
}
void uacpi_kernel_log(uacpi_log_level lvl, const uacpi_char* msg, ...) {
va_list args;
va_start(args, msg);
kvprintf(msg, args);
va_end(args);
}
void uacpi_kernel_vlog(uacpi_log_level lvl, const uacpi_char* msg, uacpi_va_list args) {
kvprintf(msg, args);
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
typedef uint8_t uacpi_u8;
typedef uint16_t uacpi_u16;
typedef uint32_t uacpi_u32;
typedef uint64_t uacpi_u64;
typedef int8_t uacpi_i8;
typedef int16_t uacpi_i16;
typedef int32_t uacpi_i32;
typedef int64_t uacpi_i64;
#define UACPI_TRUE true
#define UACPI_FALSE false
typedef bool uacpi_bool;
#define UACPI_NULL nullptr
typedef uintptr_t uacpi_uintptr;
typedef uacpi_uintptr uacpi_virt_addr;
typedef size_t uacpi_size;
typedef va_list uacpi_va_list;
#define uacpi_va_start va_start
#define uacpi_va_end va_end
#define uacpi_va_arg va_arg
typedef char uacpi_char;
#define uacpi_offsetof offsetof
#define UACPI_PRIu64 "llu"
#define UACPI_PRIx64 "llx"
#define UACPI_PRIX64 "llX"
#define UACPI_FMT64(val) ((unsigned long long)(val))
+10
View File
@@ -0,0 +1,10 @@
# uACPI
uacpi = subproject('uacpi')
uacpi_sources = uacpi.get_variable('sources')
uacpi_includes = uacpi.get_variable('includes')
kernel_c_args += [
'-DUACPI_FORMATTED_LOGGING',
'-DUACPI_BAREBONES_MODE',
'-DUACPI_OVERRIDE_TYPES',
]
kernel_includes += include_directories('include')
+3
View File
@@ -0,0 +1,3 @@
if get_option('acpi')
subdir('acpi')
endif
+1
View File
@@ -0,0 +1 @@
subdir('tmpfs')
+1
View File
@@ -0,0 +1 @@
kernel_sources += files('tmpfs.c')
+1
View File
@@ -0,0 +1 @@
// TODO
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <zinnia/compiler.h>
#include <zinnia/panic.h>
#define assert(expr, msg, ...) \
({ \
if (__unlikely(!(expr))) { \
panic( \
"In function \"%s\" (%s:%u):\n" \
"Assertion \"%s\" failed!\n" msg, \
__FUNCTION__, \
__FILE__, \
__LINE__, \
#expr, \
##__VA_ARGS__ \
); \
} \
})
#ifndef NDEBUG
#define debug_assert(...) assert(__VA_ARGS__)
#else
#define debug_assert(...) ((void)0)
#endif
+7
View File
@@ -0,0 +1,7 @@
#pragma once
typedef __builtin_va_list va_list;
#define va_start(v, l) __builtin_va_start(v, l)
#define va_end(v) __builtin_va_end(v)
#define va_arg(v, T) __builtin_va_arg(v, T)
#define va_copy(d, s) __builtin_va_copy(d, s)
+36
View File
@@ -0,0 +1,36 @@
#pragma once
typedef enum memory_order {
memory_order_relaxed = __ATOMIC_RELAXED,
memory_order_consume = __ATOMIC_CONSUME,
memory_order_acquire = __ATOMIC_ACQUIRE,
memory_order_release = __ATOMIC_RELEASE,
memory_order_acq_rel = __ATOMIC_ACQ_REL,
memory_order_seq_cst = __ATOMIC_SEQ_CST
} memory_order;
#define atomic_store_explicit(ptr, des, ord) __atomic_store_n(ptr, des, ord)
#define atomic_store(ptr, des) atomic_store_explicit(ptr, des, __ATOMIC_SEQ_CST)
#define atomic_load_explicit(ptr, ord) __atomic_load_n(ptr, ord)
#define atomic_load(ptr) atomic_load_explicit(ptr, __ATOMIC_SEQ_CST)
#define atomic_exchange_explicit(ptr, des, ord) __atomic_exchange_n(ptr, des, ord)
#define atomic_exchange(ptr, des) atomic_exchange_explicit(ptr, des, __ATOMIC_SEQ_CST)
#define atomic_fetch_add_explicit(ptr, op, ord) __atomic_fetch_add(ptr, op, ord)
#define atomic_fetch_add(ptr, op) atomic_fetch_add_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_sub_explicit(ptr, op, ord) __atomic_fetch_sub(ptr, op, ord)
#define atomic_fetch_sub(ptr, op) atomic_fetch_sub_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_or_explicit(ptr, op, ord) __atomic_fetch_or(ptr, op, ord)
#define atomic_fetch_or(ptr, op) atomic_fetch_or_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_xor_explicit(ptr, op, ord) __atomic_fetch_xor(ptr, op, ord)
#define atomic_fetch_xor(ptr, op) atomic_fetch_xor_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_fetch_and_explicit(ptr, op, ord) __atomic_fetch_and(ptr, op, ord)
#define atomic_fetch_and(ptr, op) atomic_fetch_and_explicit(ptr, op, __ATOMIC_SEQ_CST)
#define atomic_thread_fence(ord) __atomic_thread_fence(ord)
+7
View File
@@ -0,0 +1,7 @@
#pragma once
typedef unsigned long size_t;
typedef signed long ssize_t;
#define offsetof(type, member) __builtin_offsetof(type, member)
#define unreachable() __builtin_unreachable()
+26
View File
@@ -0,0 +1,26 @@
#pragma once
typedef __INT8_TYPE__ int8_t;
typedef __UINT8_TYPE__ uint8_t;
#define INT8_MAX __INT8_MAX__
#define UINT8_MAX __UINT8_MAX__
typedef __INT16_TYPE__ int16_t;
typedef __UINT16_TYPE__ uint16_t;
#define INT16_MAX __INT16_MAX__
#define UINT16_MAX __UINT16_MAX__
typedef __INT32_TYPE__ int32_t;
typedef __UINT32_TYPE__ uint32_t;
#define INT32_MAX __INT32_MAX__
#define UINT32_MAX __UINT32_MAX__
typedef __INT64_TYPE__ int64_t;
typedef __UINT64_TYPE__ uint64_t;
#define INT64_MAX __INT64_MAX__
#define UINT64_MAX __UINT64_MAX__
typedef __INTPTR_TYPE__ intptr_t;
typedef __UINTPTR_TYPE__ uintptr_t;
#define INTPTR_MAX __INTPTR_MAX__
#define UINTPTR_MAX __UINTPTR_MAX__
+4
View File
@@ -0,0 +1,4 @@
#pragma once
long atol(const char* str, int base);
unsigned long atolu(const char* str, int base);
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stddef.h>
size_t strlen(const char* str);
size_t strnlen(const char* str, size_t len);
int memcmp(const void* s1, const void* s2, size_t size);
void* memcpy(void* restrict dest, const void* restrict src, size_t n);
void* memmove(void* dstptr, const void* srcptr, size_t size);
void* memset(void* dest, int value, size_t n);
char* strncpy(char* restrict dst, const char* restrict src, size_t len);
int strcmp(const char* str1, const char* str2);
int strncmp(const char* str1, const char* str2, size_t len);
char* strchr(const char* c, int s);
+10
View File
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_ARCHCTL_H
#define __ZINNIA_UAPI_ARCHCTL_H
#ifdef __x86_64__
#define __ARCHCTL_SET_FSBASE 1
#define __ARCHCTL_SET_GSBASE 2
#endif
#endif
+39
View File
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_EPOLL_H
#define __ZINNIA_UAPI_EPOLL_H
#include <stdint.h>
#define __EPOLLIN (1 << 0)
#define __EPOLLOUT (1 << 1)
#define __EPOLLPRI (1 << 2)
#define __EPOLLHUP (1 << 3)
#define __EPOLLERR (1 << 4)
#define __EPOLLRDHUP (1 << 5)
#define __EPOLLMSG (1 << 6)
#define __EPOLLRDNORM (1 << 7)
#define __EPOLLRDBAND (1 << 8)
#define __EPOLLWRNORM (1 << 9)
#define __EPOLLWRBAND (1 << 10)
#define __EPOLLEXCLUSIVE (1 << 11)
#define __EPOLLWAKEUP (1 << 12)
#define __EPOLLONESHOT (1 << 13)
#define __EPOLLET (1 << 14)
#define __EPOLL_CTL_ADD 1
#define __EPOLL_CTL_DEL 2
#define __EPOLL_CTL_MOD 3
union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
};
struct epoll_event {
uint32_t events;
union epoll_data data;
};
#endif
+140
View File
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_ERRNO_H
#define __ZINNIA_UAPI_ERRNO_H
#define __EPERM 1
#define __ENOENT 2
#define __ESRCH 3
#define __EINTR 4
#define __EIO 5
#define __ENXIO 6
#define __E2BIG 7
#define __ENOEXEC 8
#define __EBADF 9
#define __ECHILD 10
#define __EAGAIN 11
#define __ENOMEM 12
#define __EACCES 13
#define __EFAULT 14
#define __ENOTBLK 15
#define __EBUSY 16
#define __EEXIST 17
#define __EXDEV 18
#define __ENODEV 19
#define __ENOTDIR 20
#define __EISDIR 21
#define __EINVAL 22
#define __ENFILE 23
#define __EMFILE 24
#define __ENOTTY 25
#define __ETXTBSY 26
#define __EFBIG 27
#define __ENOSPC 28
#define __ESPIPE 29
#define __EROFS 30
#define __EMLINK 31
#define __EPIPE 32
#define __EDOM 33
#define __ERANGE 34
#define __EDEADLK 35
#define __ENAMETOOLONG 36
#define __ENOLCK 37
#define __ENOSYS 38
#define __ENOTEMPTY 39
#define __ELOOP 40
#define __EWOULDBLOCK __EAGAIN
#define __ENOMSG 42
#define __EIDRM 43
#define __ECHRNG 44
#define __EL2NSYNC 45
#define __EL3HLT 46
#define __EL3RST 47
#define __ELNRNG 48
#define __EUNATCH 49
#define __ENOCSI 50
#define __EL2HLT 51
#define __EBADE 52
#define __EBADR 53
#define __EXFULL 54
#define __ENOANO 55
#define __EBADRQC 56
#define __EBADSLT 57
#define __EDEADLOCK __EDEADLK
#define __EBFONT 59
#define __ENOSTR 60
#define __ENODATA 61
#define __ETIME 62
#define __ENOSR 63
#define __ENONET 64
#define __ENOPKG 65
#define __EREMOTE 66
#define __ENOLINK 67
#define __EADV 68
#define __ESRMNT 69
#define __ECOMM 70
#define __EPROTO 71
#define __EMULTIHOP 72
#define __EDOTDOT 73
#define __EBADMSG 74
#define __EOVERFLOW 75
#define __ENOTUNIQ 76
#define __EBADFD 77
#define __EREMCHG 78
#define __ELIBACC 79
#define __ELIBBAD 80
#define __ELIBSCN 81
#define __ELIBMAX 82
#define __ELIBEXEC 83
#define __EILSEQ 84
#define __ERESTART 85
#define __ESTRPIPE 86
#define __EUSERS 87
#define __ENOTSOCK 88
#define __EDESTADDRREQ 89
#define __EMSGSIZE 90
#define __EPROTOTYPE 91
#define __ENOPROTOOPT 92
#define __EPROTONOSUPPORT 93
#define __ESOCKTNOSUPPORT 94
#define __EOPNOTSUPP 95
#define __ENOTSUP __EOPNOTSUPP
#define __EPFNOSUPPORT 96
#define __EAFNOSUPPORT 97
#define __EADDRINUSE 98
#define __EADDRNOTAVAIL 99
#define __ENETDOWN 100
#define __ENETUNREACH 101
#define __ENETRESET 102
#define __ECONNABORTED 103
#define __ECONNRESET 104
#define __ENOBUFS 105
#define __EISCONN 106
#define __ENOTCONN 107
#define __ESHUTDOWN 108
#define __ETOOMANYREFS 109
#define __ETIMEDOUT 110
#define __ECONNREFUSED 111
#define __EHOSTDOWN 112
#define __EHOSTUNREACH 113
#define __EALREADY 114
#define __EINPROGRESS 115
#define __ESTALE 116
#define __EUCLEAN 117
#define __ENOTNAM 118
#define __ENAVAIL 119
#define __EISNAM 120
#define __EREMOTEIO 121
#define __EDQUOT 122
#define __ENOMEDIUM 123
#define __EMEDIUMTYPE 124
#define __ECANCELED 125
#define __ENOKEY 126
#define __EKEYEXPIRED 127
#define __EKEYREVOKED 128
#define __EKEYREJECTED 129
#define __EOWNERDEAD 130
#define __ENOTRECOVERABLE 131
#define __ERFKILL 132
#define __EHWPOISON 133
#endif
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_FCNTL_H
#define __ZINNIA_UAPI_FCNTL_H
#define __F_OK 0
#define __X_OK 1
#define __W_OK 2
#define __R_OK 4
#define __O_RDONLY (1 << 0)
#define __O_WRONLY (1 << 1)
#define __O_RDWR (__O_RDONLY | __O_WRONLY)
#define __O_CREAT (1 << 6)
#define __O_EXCL (1 << 7)
#define __O_NOCTTY (1 << 8)
#define __O_TRUNC (1 << 9)
#define __O_APPEND (1 << 10)
#define __O_NONBLOCK (1 << 11)
#define __O_DSYNC (1 << 12)
#define __O_ASYNC (1 << 13)
#define __O_DIRECT (1 << 14)
#define __O_LARGEFILE (1 << 15)
#define __O_DIRECTORY (1 << 16)
#define __O_NOFOLLOW (1 << 17)
#define __O_NOATIME (1 << 18)
#define __O_CLOEXEC (1 << 19)
#define __O_PATH (1 << 21)
#define __O_TMPFILE (1 << 22)
#define __O_SYNC (__O_DIRECTORY | __O_TMPFILE)
#define __O_RSYNC (__O_SYNC)
#define __O_EXEC (__O_PATH)
#define __O_SEARCH (__O_PATH)
#define __O_ACCMODE (__O_RDWR | __O_PATH)
#define __AT_FDCWD (-100)
#define __AT_SYMLINK_NOFOLLOW (1 << 8)
#define __AT_SYMLINK_FOLLOW (1 << 9)
#define __AT_REMOVEDIR (1 << 10)
#define __AT_EACCESS (1 << 11)
#define __AT_EMPTY_PATH (1 << 12)
#endif
+22
View File
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_MMAN_H
#define __ZINNIA_UAPI_MMAN_H
#define __PROT_NONE 0x00
#define __PROT_READ 0x01
#define __PROT_WRITE 0x02
#define __PROT_EXEC 0x04
#define __MAP_FAILED ((void*)-1)
#define __MAP_FILE 0x00
#define __MAP_SHARED 0x01
#define __MAP_PRIVATE 0x02
#define __MAP_FIXED 0x10
#define __MAP_ANON 0x20
#define __MAP_ANONYMOUS 0x20
#define __MS_ASYNC 0x1
#define __MS_INVALIDATE 0x2
#define __MS_SYNC 0x4
#endif
+13
View File
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_MOUNT_H
#define __ZINNIA_UAPI_MOUNT_H
#define __MNT_RDONLY (1 << 0)
#define __MNT_NOSUID (1 << 1)
#define __MNT_NOEXEC (1 << 2)
#define __MNT_RELATIME (1 << 3)
#define __MNT_NOATIME (1 << 4)
#define __MNT_REMOUNT (1 << 5)
#define __MNT_FORCE (1 << 6)
#endif
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_POLL_H
#define __ZINNIA_UAPI_POLL_H
#include <stdint.h>
#define __POLLIN 0x01
#define __POLLOUT 0x02
#define __POLLPRI 0x04
#define __POLLHUP 0x08
#define __POLLERR 0x10
#define __POLLRDHUP 0x20
#define __POLLNVAL 0x40
#define __POLLRDNORM 0x80
#define __POLLRDBAND 0x100
#define __POLLWRNORM 0x200
#define __POLLWRBAND 0x400
typedef intptr_t nfds_t;
struct pollfd {
int fd;
int16_t events;
int16_t revents;
};
#endif
+51
View File
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_STAT_H
#define __ZINNIA_UAPI_STAT_H
#include <uapi/time.h>
#include <uapi/types.h>
#define __S_IFMT 0x0F000
#define __S_IFBLK 0x06000
#define __S_IFCHR 0x02000
#define __S_IFIFO 0x01000
#define __S_IFREG 0x08000
#define __S_IFDIR 0x04000
#define __S_IFLNK 0x0A000
#define __S_IFSOCK 0x0C000
#define __S_IRWXU 0700
#define __S_IRUSR 0400
#define __S_IWUSR 0200
#define __S_IXUSR 0100
#define __S_IRWXG 070
#define __S_IRGRP 040
#define __S_IWGRP 020
#define __S_IXGRP 010
#define __S_IRWXO 07
#define __S_IROTH 04
#define __S_IWOTH 02
#define __S_IXOTH 01
#define __S_ISUID 04000
#define __S_ISGID 02000
#define __S_ISVTX 01000
#define __S_IREAD __S_IRUSR
#define __S_IWRITE __S_IWUSR
#define __S_IEXEC __S_IXUSR
struct stat {
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
off_t st_size;
struct timespec st_atim;
struct timespec st_mtim;
struct timespec st_ctim;
blksize_t st_blksize;
blkcnt_t st_blocks;
};
#endif
+151
View File
@@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_SYSCALL_NUMBERS_H
#define __ZINNIA_UAPI_SYSCALL_NUMBERS_H
#define __SYS_exit 0
#define __SYS_syslog 1
#define __SYS_getuname 2
#define __SYS_setuname 3
#define __SYS_archctl 4
#define __SYS_reboot 5
#define __SYS_mmap 6
#define __SYS_munmap 7
#define __SYS_mprotect 8
#define __SYS_madvise 9
#define __SYS_sigprocmask 10
#define __SYS_sigsuspend 11
#define __SYS_sigpending 12
#define __SYS_sigaction 13
#define __SYS_sigtimedwait 14
#define __SYS_sigaltstack 15
#define __SYS_sigreturn 16
#define __SYS_sigqueue 17
#define __SYS_execve 18
#define __SYS_fexecve 19
#define __SYS_fork 20
#define __SYS_kill 21
#define __SYS_gettid 22
#define __SYS_getpid 23
#define __SYS_getppid 24
#define __SYS_waitid 25
#define __SYS_waitpid 26
#define __SYS_pread 27
#define __SYS_readv 28
#define __SYS_pwrite 29
#define __SYS_writev 30
#define __SYS_seek 31
#define __SYS_ioctl 32
#define __SYS_openat 33
#define __SYS_close 34
#define __SYS_fstat 35
#define __SYS_fstatat 36
#define __SYS_statvfs 37
#define __SYS_fstatvfs 38
#define __SYS_faccessat 39
#define __SYS_fcntl 40
#define __SYS_ftruncate 41
#define __SYS_truncate 42
#define __SYS_fallocate 43
#define __SYS_fadvise 44
#define __SYS_utimensat 45
#define __SYS_mknodat 46
#define __SYS_getcwd 47
#define __SYS_chdir 48
#define __SYS_fchdir 49
#define __SYS_mkdirat 50
#define __SYS_rmdirat 51
#define __SYS_getdents 52
#define __SYS_renameat 53
#define __SYS_fchmod 54
#define __SYS_fchmodat 55
#define __SYS_fchownat 56
#define __SYS_linkat 57
#define __SYS_symlinkat 58
#define __SYS_unlinkat 59
#define __SYS_readlinkat 60
#define __SYS_flock 61
#define __SYS_ppoll 62
#define __SYS_dup 63
#define __SYS_dup3 64
#define __SYS_sync 65
#define __SYS_fsync 66
#define __SYS_fdatasync 67
#define __SYS_getgroups 68
#define __SYS_setgroups 69
#define __SYS_getsid 70
#define __SYS_setsid 71
#define __SYS_setuid 72
#define __SYS_getuid 73
#define __SYS_setgid 74
#define __SYS_getgid 75
#define __SYS_geteuid 76
#define __SYS_seteuid 77
#define __SYS_getegid 78
#define __SYS_setegid 79
#define __SYS_getpgid 80
#define __SYS_setpgid 81
#define __SYS_getresuid 82
#define __SYS_setresuid 83
#define __SYS_getresgid 84
#define __SYS_setresgid 85
#define __SYS_setreuid 86
#define __SYS_setregid 87
#define __SYS_umask 88
#define __SYS_pipe 89
#define __SYS_futex_wait 90
#define __SYS_futex_wake 91
#define __SYS_thread_create 92
#define __SYS_thread_kill 93
#define __SYS_thread_exit 94
#define __SYS_thread_setname 95
#define __SYS_thread_getname 96
#define __SYS_timer_create 97
#define __SYS_timer_set 98
#define __SYS_timer_get 99
#define __SYS_timer_getoverrun 100
#define __SYS_timer_delete 101
#define __SYS_itimer_get 102
#define __SYS_itimer_set 103
#define __SYS_clock_get 104
#define __SYS_clock_getres 105
#define __SYS_clock_set 106
#define __SYS_sleep 107
#define __SYS_yield 108
#define __SYS_chroot 109
#define __SYS_mount 110
#define __SYS_umount 111
#define __SYS_socket 112
#define __SYS_socketpair 113
#define __SYS_shutdown 114
#define __SYS_bind 115
#define __SYS_connect 116
#define __SYS_accept 117
#define __SYS_listen 118
#define __SYS_getpeername 119
#define __SYS_getsockname 120
#define __SYS_getsockopt 121
#define __SYS_setsockopt 122
#define __SYS_sendmsg 123
#define __SYS_recvmsg 124
#define __SYS_getentropy 125
#define __SYS_getrusage 126
#define __SYS_getrlimit 127
#define __SYS_setrlimit 128
#define __SYS_getpriority 129
#define __SYS_setpriority 130
#define __SYS_sched_getparam 131
#define __SYS_sched_setparam 132
#define __SYS_module_insert 133
#define __SYS_module_remove 134
#define __SYS_epoll_create 135
#define __SYS_epoll_ctl 136
#define __SYS_epoll_pwait 137
#define __SYS_timerfd_create 138
#define __SYS_timerfd_gettime 139
#define __SYS_timerfd_settime 140
#define __SYS_signalfd_create 141
#define __SYS_eventfd_create 142
#define __SYS_msync 143
#define __SYS_sysconf 144
#endif
+47
View File
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_TIME_H
#define __ZINNIA_UAPI_TIME_H
#include <stdint.h>
typedef int64_t time_t;
typedef int64_t suseconds_t;
struct timespec {
time_t tv_sec;
int64_t tv_nsec;
};
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
struct itimerval {
struct timeval it_interval;
struct timeval it_value;
};
struct itimerspec {
struct timespec it_interval;
struct timespec it_value;
};
#define __ITIMER_REAL 0
#define __ITIMER_VIRTUAL 1
#define __ITIMER_PROF 2
#define __CLOCK_REALTIME 0
#define __CLOCK_MONOTONIC 1
#define __CLOCK_PROCESS_CPUTIME_ID 2
#define __CLOCK_THREAD_CPUTIME_ID 3
#define __CLOCK_MONOTONIC_RAW 4
#define __CLOCK_REALTIME_COARSE 5
#define __CLOCK_MONOTONIC_COARSE 6
#define __CLOCK_BOOTTIME 7
#define __TFD_CLOEXEC __O_CLOEXEC
#define __TFD_NONBLOCK __O_NONBLOCK
#define __TFD_TIMER_ABSTIME 1
#endif
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_TYPES_H
#define __ZINNIA_UAPI_TYPES_H
#include <stdint.h>
typedef int64_t off_t;
typedef off_t off64_t;
typedef uintptr_t blksize_t;
typedef uintptr_t blkcnt_t;
typedef uintptr_t clockid_t;
typedef uintptr_t dev_t;
typedef uint32_t gid_t;
typedef uintptr_t ino_t;
typedef uint32_t mode_t;
typedef uintptr_t nlink_t;
typedef int32_t pid_t;
typedef uintptr_t rlim_t;
typedef uint32_t uid_t;
typedef uintptr_t fsblkcnt_t;
typedef uintptr_t fsfilcnt_t;
typedef uintptr_t pthread_attr_t;
#endif
+12
View File
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_UIO_H
#define __ZINNIA_UAPI_UIO_H
#include <stddef.h>
struct iovec {
void* base;
size_t len;
};
#endif
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef __ZINNIA_UAPI_UTSNAME_H
#define __ZINNIA_UAPI_UTSNAME_H
struct utsname {
char sysname[65];
char nodename[65];
char release[65];
char version[65];
char machine[65];
char domainname[65];
};
#endif
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include <uapi/archctl.h>
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include <zinnia/elf.h>
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <zinnia/irq.h>
void arch_irq_set_state(bool state);
bool arch_irq_get_state();
void arch_irq_wait();
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
size_t arch_mem_page_size();
size_t arch_mem_page_bits();
size_t arch_mem_level_bits();
size_t arch_mem_num_levels();
uintptr_t arch_mem_hhdm_addr();
uintptr_t arch_vm_pfndb_addr();
uintptr_t arch_mem_mapping_addr();
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include <stddef.h>
#include <zinnia/compiler.h>
#include <zinnia/usercopy.h>
bool arch_usercopy_read(void* dst, const __user void* src, size_t len, struct usercopy_region** region);
bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct usercopy_region** region);
bool arch_usercopy_strlen(const __user char* str, size_t max, size_t* len, struct usercopy_region** region);
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
struct clock {
const char* name;
uint8_t priority;
void (*reset)(struct clock* c);
uint64_t (*get_elapsed_ns)(struct clock* c);
};
// Attempts to set a new clock. Returns true if the clock was changed.
bool clock_switch(struct clock* clock);
// Returns true, if a clock is available.
bool clock_available();
// Gets the current timestamp in nanoseconds since the last reset.
uint64_t clock_get_elapsed_ns();
// Spin for `ns` nanoseconds.
void clock_spin_ns(uint64_t ns);
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <stdint.h>
#include <zinnia/compiler.h>
#include <zinnia/utils.h>
#define _CMDLINE_OPTION(x, opt_name, opt_func) \
static const struct cmdline_option x = { \
.name = opt_name, \
.option_type = _Generic( \
(opt_func), \
void (*)(const char*): CMDLINE_STRING, \
void (*)(bool): CMDLINE_BOOL, \
void (*)(intptr_t): CMDLINE_INTPTR, \
void (*)(uintptr_t): CMDLINE_UINTPTR \
), \
.func = (void*)(opt_func), \
}; \
[[__used, __section(".cmdline")]] \
static const struct cmdline_option* CONCAT(x, _ptr) = &x;
#define CMDLINE_OPTION(opt_name, opt_func) _CMDLINE_OPTION(UNIQUE_IDENT(cmdline_option), opt_name, opt_func)
enum cmdline_type {
CMDLINE_STRING,
CMDLINE_BOOL,
CMDLINE_INTPTR,
CMDLINE_UINTPTR,
};
struct cmdline_option {
// The name of this option.
const char* name;
// Gets called if this option is present on the command line.
union {
void (*func_str)(const char* value);
void (*func_bool)(bool value);
void (*func_intptr)(intptr_t value);
void (*func_uintptr)(uintptr_t value);
void* func;
};
enum cmdline_type option_type;
};
// Parses the command line and invokes all options.
void cmdline_parse(const char* cmdline);
+27
View File
@@ -0,0 +1,27 @@
#pragma once
// Attributes that affect data layout.
#define __section(x) gnu::section(x)
#define __aligned(x) gnu::aligned(x)
#define __packed gnu::packed
#define __naked gnu::naked
// These hints may improve code generation.
#define __cold gnu::cold
#define __hot gnu::hot
#define __used gnu::used
#define __unused gnu::unused
#define __format(like, ...) gnu::format(like, __VA_ARGS__)
#define __likely(x) __builtin_expect(!!(x), 1)
#define __unlikely(x) __builtin_expect(!!(x), 0)
#define __always_inline gnu::always_inline
// Only clang has these hints, GCC will throw a warning here.
// They also don't support the C23 attribute format.
#ifdef __clang__
#define __user __attribute__((noderef, address_space(1)))
#define __phys __attribute__((noderef, address_space(2)))
#else
#define __user
#define __phys
#endif
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <stddef.h>
#include <zinnia/compiler.h>
#include <zinnia/utils.h>
struct console {
const char* name;
void* private;
// Initializes the console.
bool (*init)(struct console* con);
// Writes to the console.
void (*write)(struct console* con, const char* buf, size_t count);
};
// Initializes a console for logging.
void console_init();
// Writes a message to a console.
void console_write(const char* buf, size_t len);
// Defines a new early console which can be used to log early boot messages.
// An early console must not make any heap allocations.
#define DEFINE_CONSOLE(con) \
[[__used, __section(".console")]] \
static struct console* UNIQUE_IDENT(console) = (con)
+340
View File
@@ -0,0 +1,340 @@
#pragma once
#include <stdint.h>
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
typedef uint64_t elf64_addr_t;
typedef uint64_t elf64_off_t;
typedef uint32_t elf32_addr_t;
typedef uint32_t elf32_off_t;
enum : uint8_t {
EI_MAG0 = 0,
EI_MAG1 = 1,
EI_MAG2 = 2,
EI_MAG3 = 3,
EI_CLASS = 4,
EI_DATA = 5,
EI_VERSION = 6,
EI_OSABI = 7,
EI_ABIVERSION = 8,
EI_PAD = 9,
EI_NIDENT = 16,
};
enum : uint16_t {
EM_X86_64 = 62,
EM_AARCH64 = 183,
EM_RISCV = 243,
EM_LOONGARCH = 258,
};
enum : uint32_t {
ELFCLASS32 = 1,
ELFCLASS64 = 2,
};
enum : uint32_t {
ELFDATA2LSB = 1,
ELFDATA2MSB = 2,
};
enum : uint32_t {
EV_NONE = 0,
EV_CURRENT = 1,
EV_NUM = 2,
};
enum : uint32_t {
ELFOSABI_SYSV = 0,
ELFOSABI_HPUX = 1,
ELFOSABI_STANDALONE = 255,
};
enum : uint32_t {
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
};
enum : uint32_t {
PT_NULL = 0x00000000,
PT_LOAD = 0x00000001,
PT_DYNAMIC = 0x00000002,
PT_INTERP = 0x00000003,
PT_NOTE = 0x00000004,
PT_SHLIB = 0x00000005,
PT_PHDR = 0x00000006,
PT_TLS = 0x00000007,
};
enum : uint32_t {
PF_X = 0x00000001,
PF_W = 0x00000002,
PF_R = 0x00000004,
};
enum : uint32_t {
DT_NULL = 0,
DT_NEEDED = 1,
DT_PLTRELSZ = 2,
DT_PLTGOT = 3,
DT_HASH = 4,
DT_STRTAB = 5,
DT_SYMTAB = 6,
DT_RELA = 7,
DT_RELASZ = 8,
DT_RELAENT = 9,
DT_STRSZ = 10,
DT_SYMENT = 11,
DT_INIT = 12,
DT_FINI = 13,
DT_SONAME = 14,
DT_RPATH = 15,
DT_SYMBOLIC = 16,
DT_REL = 17,
DT_RELSZ = 18,
DT_RELENT = 19,
DT_PLTREL = 20,
DT_DEBUG = 21,
DT_TEXTREL = 22,
DT_JMPREL = 23,
DT_BIND_NOW = 24,
DT_INIT_ARRAY = 25,
DT_FINI_ARRAY = 26,
DT_INIT_ARRAYSZ = 27,
DT_FINI_ARRAYSZ = 28,
DT_LOOS = 0x60000000,
DT_HIOS = 0x6FFFFFFF,
DT_LOPROC = 0x70000000,
DT_HIPROC = 0x7FFFFFFF,
};
enum : uint32_t {
AT_NULL = 0,
AT_IGNORE = 1,
AT_EXECFD = 2,
AT_PHDR = 3,
AT_PHENT = 4,
AT_PHNUM = 5,
AT_PAGESZ = 6,
AT_BASE = 7,
AT_FLAGS = 8,
AT_ENTRY = 9,
AT_NOTELF = 10,
AT_UID = 11,
AT_EUID = 12,
AT_GID = 13,
AT_EGID = 14,
AT_HWCAP = 16,
AT_SECURE = 23,
AT_RANDOM = 25,
AT_EXECFN = 31,
AT_SYSINFO_EHDR = 33,
AT_INIT_HANDLE = 0x1000,
};
struct elf64_ehdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf64_addr_t e_entry;
elf64_off_t e_phoff;
elf64_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct elf32_ehdr {
uint8_t e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
elf32_addr_t e_entry;
elf32_off_t e_phoff;
elf32_off_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct elf64_phdr {
uint32_t p_type;
uint32_t p_flags;
elf64_off_t p_offset;
elf64_addr_t p_vaddr;
elf64_addr_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
};
struct elf32_phdr {
uint32_t p_type;
elf32_off_t p_offset;
elf32_addr_t p_vaddr;
elf32_addr_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct elf64_dyn {
int64_t d_tag;
union {
uint64_t d_val;
elf64_addr_t d_ptr;
} d_un;
};
struct elf32_dyn {
int32_t r_offset;
union {
uint32_t d_val;
elf32_addr_t d_ptr;
} d_un;
};
struct elf64_nhdr {
uint64_t n_namesz;
uint64_t n_descsz;
uint64_t n_type;
};
struct elf32_nhdr {
uint32_t n_namesz;
uint32_t n_descsz;
uint32_t n_type;
};
struct elf64_auxv {
uint64_t atype;
uint64_t avalue;
};
struct elf32_auxv {
uint32_t atype;
uint32_t avalue;
};
struct elf_hash {
uint32_t n_buckets;
uint32_t n_chain;
};
struct elf32_sym {
elf32_off_t st_name;
elf32_addr_t st_value;
elf32_off_t st_size;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
};
struct elf64_sym {
uint32_t st_name;
uint8_t st_info;
uint8_t st_other;
uint16_t st_shndx;
uint64_t st_value;
uint64_t st_size;
};
static uint8_t ELF64_ST_BIND(uint8_t info) {
return info >> 4;
}
static inline uint8_t ELF64_ST_TYPE(uint8_t info) {
return info & 0x0F;
}
static inline uint8_t ELF64_ST_INFO(uint8_t bind, uint8_t type) {
return (bind << 4) | type;
}
typedef struct {
uint16_t si_boundto;
uint16_t si_flags;
} elf64_syminfo;
static inline uint8_t ELF32_ST_BIND(uint8_t info) {
return info >> 4;
}
static inline uint8_t ELF32_ST_TYPE(uint8_t info) {
return info & 0xF;
}
static inline uint8_t ELF32_ST_INFO(uint8_t bind, uint8_t type) {
return (bind << 4) | (type & 0xF);
}
enum {
STB_GLOBAL = 1,
STB_WEAK = 2,
STB_GNU_UNIQUE = 10,
STB_LOPROC = 13,
STB_HIPROC = 15
};
enum {
STT_NOTYPE = 0,
STT_OBJECT = 1,
STT_FUNC = 2,
STT_TLS = 6,
STT_GNU_IFUNC = 10,
STT_LOPROC = 13,
STT_HIPROC = 15
};
struct elf32_rela {
elf32_addr_t r_offset;
uint32_t r_info;
int32_t r_addend;
};
struct elf64_rela {
elf64_addr_t r_offset;
uint64_t r_info;
int64_t r_addend;
};
struct elf32_rel {
elf32_addr_t r_offset;
uint32_t r_info;
};
struct elf64_rel {
elf64_addr_t r_offset;
uint64_t r_info;
};
static inline uint32_t ELF32_R_SYM(uint32_t info) {
return info >> 8;
}
static inline uint32_t ELF32_R_TYPE(uint32_t info) {
return info & 0xF;
}
static inline uint32_t ELF64_R_SYM(uint64_t info) {
return info >> 32;
}
static inline uint32_t ELF64_R_TYPE(uint64_t info) {
return info & 0xFFFFFFFF;
}
+142
View File
@@ -0,0 +1,142 @@
#pragma once
#include <stdint.h>
#include <uapi/errno.h>
typedef enum : int32_t {
EOK = 0, // Not really an error.
EPERM = __EPERM,
ENOENT = __ENOENT,
ESRCH = __ESRCH,
EINTR = __EINTR,
EIO = __EIO,
ENXIO = __ENXIO,
E2BIG = __E2BIG,
ENOEXEC = __ENOEXEC,
EBADF = __EBADF,
ECHILD = __ECHILD,
EAGAIN = __EAGAIN,
ENOMEM = __ENOMEM,
EACCES = __EACCES,
EFAULT = __EFAULT,
ENOTBLK = __ENOTBLK,
EBUSY = __EBUSY,
EEXIST = __EEXIST,
EXDEV = __EXDEV,
ENODEV = __ENODEV,
ENOTDIR = __ENOTDIR,
EISDIR = __EISDIR,
EINVAL = __EINVAL,
ENFILE = __ENFILE,
EMFILE = __EMFILE,
ENOTTY = __ENOTTY,
ETXTBSY = __ETXTBSY,
EFBIG = __EFBIG,
ENOSPC = __ENOSPC,
ESPIPE = __ESPIPE,
EROFS = __EROFS,
EMLINK = __EMLINK,
EPIPE = __EPIPE,
EDOM = __EDOM,
ERANGE = __ERANGE,
EDEADLK = __EDEADLK,
ENAMETOOLONG = __ENAMETOOLONG,
ENOLCK = __ENOLCK,
ENOSYS = __ENOSYS,
ENOTEMPTY = __ENOTEMPTY,
ELOOP = __ELOOP,
EWOULDBLOCK = __EWOULDBLOCK,
ENOMSG = __ENOMSG,
EIDRM = __EIDRM,
ECHRNG = __ECHRNG,
EL2NSYNC = __EL2NSYNC,
EL3HLT = __EL3HLT,
EL3RST = __EL3RST,
ELNRNG = __ELNRNG,
EUNATCH = __EUNATCH,
ENOCSI = __ENOCSI,
EL2HLT = __EL2HLT,
EBADE = __EBADE,
EBADR = __EBADR,
EXFULL = __EXFULL,
ENOANO = __ENOANO,
EBADRQC = __EBADRQC,
EBADSLT = __EBADSLT,
EDEADLOCK = __EDEADLOCK,
EBFONT = __EBFONT,
ENOSTR = __ENOSTR,
ENODATA = __ENODATA,
ETIME = __ETIME,
ENOSR = __ENOSR,
ENONET = __ENONET,
ENOPKG = __ENOPKG,
EREMOTE = __EREMOTE,
ENOLINK = __ENOLINK,
EADV = __EADV,
ESRMNT = __ESRMNT,
ECOMM = __ECOMM,
EPROTO = __EPROTO,
EMULTIHOP = __EMULTIHOP,
EDOTDOT = __EDOTDOT,
EBADMSG = __EBADMSG,
EOVERFLOW = __EOVERFLOW,
ENOTUNIQ = __ENOTUNIQ,
EBADFD = __EBADFD,
EREMCHG = __EREMCHG,
ELIBACC = __ELIBACC,
ELIBBAD = __ELIBBAD,
ELIBSCN = __ELIBSCN,
ELIBMAX = __ELIBMAX,
ELIBEXEC = __ELIBEXEC,
EILSEQ = __EILSEQ,
ERESTART = __ERESTART,
ESTRPIPE = __ESTRPIPE,
EUSERS = __EUSERS,
ENOTSOCK = __ENOTSOCK,
EDESTADDRREQ = __EDESTADDRREQ,
EMSGSIZE = __EMSGSIZE,
EPROTOTYPE = __EPROTOTYPE,
ENOPROTOOPT = __ENOPROTOOPT,
EPROTONOSUPPORT = __EPROTONOSUPPORT,
ESOCKTNOSUPPORT = __ESOCKTNOSUPPORT,
EOPNOTSUPP = __EOPNOTSUPP,
ENOTSUP = __ENOTSUP,
EPFNOSUPPORT = __EPFNOSUPPORT,
EAFNOSUPPORT = __EAFNOSUPPORT,
EADDRINUSE = __EADDRINUSE,
EADDRNOTAVAIL = __EADDRNOTAVAIL,
ENETDOWN = __ENETDOWN,
ENETUNREACH = __ENETUNREACH,
ENETRESET = __ENETRESET,
ECONNABORTED = __ECONNABORTED,
ECONNRESET = __ECONNRESET,
ENOBUFS = __ENOBUFS,
EISCONN = __EISCONN,
ENOTCONN = __ENOTCONN,
ESHUTDOWN = __ESHUTDOWN,
ETOOMANYREFS = __ETOOMANYREFS,
ETIMEDOUT = __ETIMEDOUT,
ECONNREFUSED = __ECONNREFUSED,
EHOSTDOWN = __EHOSTDOWN,
EHOSTUNREACH = __EHOSTUNREACH,
EALREADY = __EALREADY,
EINPROGRESS = __EINPROGRESS,
ESTALE = __ESTALE,
EUCLEAN = __EUCLEAN,
ENOTNAM = __ENOTNAM,
ENAVAIL = __ENAVAIL,
EISNAM = __EISNAM,
EREMOTEIO = __EREMOTEIO,
EDQUOT = __EDQUOT,
ENOMEDIUM = __ENOMEDIUM,
EMEDIUMTYPE = __EMEDIUMTYPE,
ECANCELED = __ECANCELED,
ENOKEY = __ENOKEY,
EKEYEXPIRED = __EKEYEXPIRED,
EKEYREVOKED = __EKEYREVOKED,
EKEYREJECTED = __EKEYREJECTED,
EOWNERDEAD = __EOWNERDEAD,
ENOTRECOVERABLE = __ENOTRECOVERABLE,
ERFKILL = __ERFKILL,
EHWPOISON = __EHWPOISON,
} errno_t;
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <zinnia/compiler.h>
#include <zinnia/errno.h>
#include <zinnia/wait.h>
struct futex {};
// Initializes the global futex table.
void futex_init(void);
// Blocks the calling task if *addr == expected.
errno_t futex_wait(__user int* addr, int expected);
// Wakes up to `count` tasks waiting on the given address.
// Returns the number of tasks woken.
errno_t futex_wake(__user int* addr, int count);
+223
View File
@@ -0,0 +1,223 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <zinnia/alloc.h>
#include <zinnia/errno.h>
// FNV-1a hash over raw bytes.
static inline uint64_t hashmap_hash_default(const void* key, size_t len) {
const uint8_t* data = (const uint8_t*)key;
uint64_t hash = 0xcbf29ce484222325;
for (size_t i = 0; i < len; i++) {
hash ^= data[i];
hash *= 0x100000001b3;
}
return hash;
}
// Byte-wise equality.
static inline bool hashmap_eq_default(const void* a, const void* b, size_t len) {
return memcmp(a, b, len) == 0;
}
// Ensure a hash is never zero (zero is the empty sentinel).
#define _HASHMAP_FIXHASH(h) ((h) == 0 ? (uint64_t)1 : (h))
#define HASHMAP(K, V) \
struct { \
K* keys; \
V* values; \
uint64_t* hashes; \
size_t capacity; \
size_t count; \
}
#define HASHMAP_INIT(map) \
do { \
(map)->keys = nullptr; \
(map)->values = nullptr; \
(map)->hashes = nullptr; \
(map)->capacity = 0; \
(map)->count = 0; \
} while (0)
#define HASHMAP_DESTROY(map) \
do { \
mem_free((map)->keys); \
mem_free((map)->values); \
mem_free((map)->hashes); \
(map)->keys = nullptr; \
(map)->values = nullptr; \
(map)->hashes = nullptr; \
(map)->capacity = 0; \
(map)->count = 0; \
} while (0)
#define HASHMAP_COUNT(map) ((map)->count)
// Internal: probe distance from ideal position.
#define _HASHMAP_PROBE_DIST(hash, pos, cap) (((pos) - ((hash) & ((cap) - 1))) & ((cap) - 1))
// Internal: grow and rehash.
#define _HASHMAP_GROW(map, hash_fn, eq_fn) \
do { \
size_t _old_cap = (map)->capacity; \
size_t _new_cap = _old_cap == 0 ? 16 : _old_cap * 2; \
typeof((map)->keys) _old_keys = (map)->keys; \
typeof((map)->values) _old_values = (map)->values; \
uint64_t* _old_hashes = (map)->hashes; \
(map)->keys = mem_alloc(_new_cap * sizeof(*(map)->keys), ALLOC_NOZERO); \
(map)->values = mem_alloc(_new_cap * sizeof(*(map)->values), ALLOC_NOZERO); \
(map)->hashes = mem_alloc(_new_cap * sizeof(uint64_t), 0); \
(map)->capacity = _new_cap; \
(map)->count = 0; \
for (size_t _i = 0; _i < _old_cap; _i++) { \
if (_old_hashes[_i] != 0) { \
_HASHMAP_INSERT_INNER(map, _old_keys[_i], _old_values[_i], _old_hashes[_i]); \
} \
} \
mem_free(_old_keys); \
mem_free(_old_values); \
mem_free(_old_hashes); \
} while (0)
// Internal: insert with a precomputed hash. Used by grow and the public INSERT macro.
#define _HASHMAP_INSERT_INNER(map, k, v, h) \
do { \
size_t _cap = (map)->capacity; \
size_t _pos = (h) & (_cap - 1); \
typeof(*(map)->keys) _ik = (k); \
typeof(*(map)->values) _iv = (v); \
uint64_t _ih = (h); \
for (;;) { \
if ((map)->hashes[_pos] == 0) { \
(map)->keys[_pos] = _ik; \
(map)->values[_pos] = _iv; \
(map)->hashes[_pos] = _ih; \
(map)->count++; \
break; \
} \
size_t _existing_dist = _HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap); \
size_t _insert_dist = _HASHMAP_PROBE_DIST(_ih, _pos, _cap); \
if (_insert_dist > _existing_dist) { \
typeof(*(map)->keys) _tk = (map)->keys[_pos]; \
typeof(*(map)->values) _tv = (map)->values[_pos]; \
uint64_t _th = (map)->hashes[_pos]; \
(map)->keys[_pos] = _ik; \
(map)->values[_pos] = _iv; \
(map)->hashes[_pos] = _ih; \
_ik = _tk; \
_iv = _tv; \
_ih = _th; \
} \
_pos = (_pos + 1) & (_cap - 1); \
} \
} while (0)
// Insert or update a key-value pair. Returns 0 or ENOMEM.
#define HASHMAP_INSERT(map, key, val, hash_fn, eq_fn) \
({ \
errno_t _status = 0; \
typeof(*(map)->keys) _k = (key); \
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
/* Check for existing key first. */ \
bool _found = false; \
if ((map)->capacity > 0) { \
size_t _cap = (map)->capacity; \
size_t _pos = _h & (_cap - 1); \
for (size_t _d = 0; _d < _cap; _d++) { \
if ((map)->hashes[_pos] == 0) \
break; \
if (_HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap) < _d) \
break; \
if ((map)->hashes[_pos] == _h && (eq_fn)(&(map)->keys[_pos], &_k, sizeof(_k))) { \
(map)->values[_pos] = (val); \
_found = true; \
break; \
} \
_pos = (_pos + 1) & (_cap - 1); \
} \
} \
if (!_found) { \
if ((map)->count * 4 >= (map)->capacity * 3 || (map)->capacity == 0) { \
_HASHMAP_GROW(map, hash_fn, eq_fn); \
if ((map)->keys == nullptr || (map)->values == nullptr || (map)->hashes == nullptr) { \
_status = ENOMEM; \
} \
} \
if (_status == 0) { \
_HASHMAP_INSERT_INNER(map, _k, (val), _h); \
} \
} \
_status; \
})
// Look up a key. Returns a pointer to the value, or nullptr if not found.
#define HASHMAP_GET(map, key, hash_fn, eq_fn) \
({ \
typeof(*(map)->values)* _result = nullptr; \
if ((map)->capacity > 0) { \
typeof(*(map)->keys) _k = (key); \
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
size_t _cap = (map)->capacity; \
size_t _pos = _h & (_cap - 1); \
for (size_t _d = 0; _d < _cap; _d++) { \
if ((map)->hashes[_pos] == 0) \
break; \
if (_HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap) < _d) \
break; \
if ((map)->hashes[_pos] == _h && (eq_fn)(&(map)->keys[_pos], &_k, sizeof(_k))) { \
_result = &(map)->values[_pos]; \
break; \
} \
_pos = (_pos + 1) & (_cap - 1); \
} \
} \
_result; \
})
// Remove a key. Returns true if the key was found and removed.
// Uses backward-shift deletion to avoid tombstones.
#define HASHMAP_REMOVE(map, key, hash_fn, eq_fn) \
({ \
bool _removed = false; \
if ((map)->capacity > 0) { \
typeof(*(map)->keys) _k = (key); \
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
size_t _cap = (map)->capacity; \
size_t _pos = _h & (_cap - 1); \
for (size_t _d = 0; _d < _cap; _d++) { \
if ((map)->hashes[_pos] == 0) \
break; \
if (_HASHMAP_PROBE_DIST((map)->hashes[_pos], _pos, _cap) < _d) \
break; \
if ((map)->hashes[_pos] == _h && (eq_fn)(&(map)->keys[_pos], &_k, sizeof(_k))) { \
(map)->hashes[_pos] = 0; \
(map)->count--; \
/* Backward-shift: move subsequent displaced entries back. */ \
size_t _empty = _pos; \
size_t _next = (_pos + 1) & (_cap - 1); \
while ((map)->hashes[_next] != 0 && _HASHMAP_PROBE_DIST((map)->hashes[_next], _next, _cap) > 0) { \
(map)->keys[_empty] = (map)->keys[_next]; \
(map)->values[_empty] = (map)->values[_next]; \
(map)->hashes[_empty] = (map)->hashes[_next]; \
(map)->hashes[_next] = 0; \
_empty = _next; \
_next = (_next + 1) & (_cap - 1); \
} \
_removed = true; \
break; \
} \
_pos = (_pos + 1) & (_cap - 1); \
} \
} \
_removed; \
})
// Iterate over all occupied entries. `idx` is a size_t loop variable.
// Access keys and values via (map)->keys[idx] and (map)->values[idx].
#define HASHMAP_FOREACH(map, idx) \
for (size_t idx = 0; idx < (map)->capacity; idx++) \
if ((map)->hashes[idx] != 0)
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <zinnia/compiler.h>
#include <zinnia/types.h>
#include <zinnia/utils.h>
#define __init __used, __section(".init.text"), __cold
#define __initdata __used, __section(".init.data")
#define __initdata_sorted(name) __used, __section(".init.data." name)
struct boot_file {
phys_t data;
size_t length;
char path[128];
};
struct boot_info {
char* cmdline;
struct phys_mem* mem_map;
size_t num_mem_maps;
phys_t phys_base;
uintptr_t virt_base;
uintptr_t hhdm_base;
struct boot_file* files;
size_t num_files;
phys_t rsdp;
};
extern uint8_t __ld_stack_top[];
extern uint8_t __ld_stack_bottom[];
extern phys_t rsdp_addr;
// Entry point for the kernel after arch-specific setup has finished.
void kernel_entry();
// Initializes the early parts of the kernel.
void kernel_early_init();
[[noreturn]]
void kernel_main(struct boot_info* info);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <uapi/uio.h>
struct iovec_iter {
struct iovec* iovecs;
size_t iovecs_len;
size_t total_len;
size_t total_offset;
size_t current_idx;
size_t current_offset;
};
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <bits/irq.h>
#include <stdint.h>
#include <zinnia/compiler.h>
#include <zinnia/list.h>
enum irq_status : uint8_t {
IRQ_STATUS_HANDLED,
IRQ_STATUS_IGNORED,
};
enum irq_polarity : uint8_t {
IRQ_POLARITY_LOW,
IRQ_POLARITY_HIGH,
};
enum irq_trigger_mode : uint8_t {
IRQ_TRIGGER_EDGE,
IRQ_TRIGGER_LEVEL,
};
struct irq_handler {
enum irq_status (*handler)(void* self);
list_link(struct irq_handler*) next;
};
struct irq_line {
int (*set_config)(enum irq_trigger_mode mode, enum irq_polarity polarity);
void (*mask)(struct irq_line* self);
void (*unmask)(struct irq_line* self);
void (*eoi)(struct irq_line* self);
list_head(struct irq_handler*) handlers;
bool is_busy;
};
void irq_line_attach(struct irq_line* self, struct irq_handler* handler);
struct irq_percpu {
uint32_t level;
bool in_interrupt;
};
void irq_lock();
void irq_unlock();
bool irq_set_interrupted(bool is_interrupted);
void irq_raise();
+29
View File
@@ -0,0 +1,29 @@
#pragma once
#define list_head(type) \
struct { \
type _sl_head; \
}
#define list_link(type) \
struct { \
type _sl_link; \
}
#define list_is_empty(head) ((head)->_sl_head == NULL)
#define list_first(head) ((head)->_sl_head)
#define list_next(elm, field) ((elm)->field._sl_link)
#define list_insert_end(slistelm, elm, field) \
do { \
list_next((elm), field) = list_next((slistelm), field); \
list_next((slistelm), field) = (elm); \
} while (0)
#define list_insert_head(head, elm, field) \
do { \
list_next((elm), field) = list_first((head)); \
list_first((head)) = (elm); \
} while (0)
#define list_foreach(var, head, field) for ((var) = list_first((head)); (var); (var) = list_next((var), field))

Some files were not shown because too many files have changed in this diff Show More