test
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
build/
|
||||
.cache/
|
||||
.wraplock
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "subprojects/uacpi"]
|
||||
path = subprojects/uacpi
|
||||
url = https://github.com/uACPI/uacpi
|
||||
@@ -0,0 +1,25 @@
|
||||
<img src="logo.svg" width="10%"/>
|
||||
|
||||
# Zinnia
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
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
|
||||
```
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint64_t pte_t;
|
||||
@@ -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();
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint64_t phys_t;
|
||||
typedef uint64_t reg_t;
|
||||
@@ -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();
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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();
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void hpet_init();
|
||||
@@ -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();
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void arch_syscall_stub();
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void tsc_init();
|
||||
@@ -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);
|
||||
@@ -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',
|
||||
)
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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) {}
|
||||
@@ -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() {}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
kernel_sources += files('entry.c')
|
||||
+76
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
kernel_sources += files(
|
||||
'cmdline.c',
|
||||
'main.c',
|
||||
)
|
||||
|
||||
subdir('limine')
|
||||
@@ -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?
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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(¤t, 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
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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))
|
||||
@@ -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')
|
||||
@@ -0,0 +1,3 @@
|
||||
if get_option('acpi')
|
||||
subdir('acpi')
|
||||
endif
|
||||
@@ -0,0 +1 @@
|
||||
subdir('tmpfs')
|
||||
@@ -0,0 +1 @@
|
||||
kernel_sources += files('tmpfs.c')
|
||||
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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__
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
long atol(const char* str, int base);
|
||||
unsigned long atolu(const char* str, int base);
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <uapi/archctl.h>
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/elf.h>
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
@@ -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
Reference in New Issue
Block a user