Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 09ab2f1a83 | |||
| 9e5ed517bc | |||
| cf3f1f9e99 | |||
| ac9d47fa16 | |||
| 40cea4dedd | |||
| 5ee30664c5 | |||
| 0e2d4ed8c6 | |||
| cf33867e68 |
@@ -1,2 +1,2 @@
|
||||
Style:
|
||||
AngledHeaders: [ "zinnia/.*", "uapi/.*" ]
|
||||
AngledHeaders: [ "zinnia/.*", " kernel/.*" ]
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
.config*
|
||||
build/
|
||||
.cache/
|
||||
.wraplock
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
__pycache__/
|
||||
@@ -1,9 +0,0 @@
|
||||
mainmenu "Zinnia Kernel Configuration"
|
||||
|
||||
config KERNEL_FLAVOR
|
||||
string "Kernel flavor name"
|
||||
default "vanilla"
|
||||
|
||||
source "arch/$(ARCH)/Kconfig"
|
||||
source "core/Kconfig"
|
||||
source "dev/Kconfig"
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Marvin Friedrich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,7 +0,0 @@
|
||||
ISC License
|
||||
|
||||
Copyright 2026 Marvin Friedrich and contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
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.
|
||||
@@ -1,10 +0,0 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
@@ -1,27 +1,20 @@
|
||||
<img src="logo.svg" width="10%"/>
|
||||
|
||||
# Zinnia
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Zinnia is a lightweight Unix-like monolithic kernel targeting 64-bit devices.
|
||||
Zinnia is a lightweight Unix-like microkernel targeting 64-bit devices.
|
||||
|
||||
## Building Zinnia
|
||||
|
||||
To build the kernel you need:
|
||||
- `meson`
|
||||
- a `kconfig` frontend (like `kconfiglib` via `pip`)
|
||||
- a GNU-like C compiler with support for C23 (e.g. `gcc` or `clang`)
|
||||
To build the kernel and the servers you need `meson` and a C23-compatible
|
||||
GNU-like compiler targeting Zinnia.
|
||||
Currently supported are GCC and Clang toolchains.
|
||||
|
||||
To configure, run:
|
||||
```sh
|
||||
alldefconfig
|
||||
meson setup $build_dir
|
||||
meson setup -Dbuild_kernel=true -Dbuild_servers=true $build_dir
|
||||
```
|
||||
|
||||
And to build:
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
menu "x86_64 Architecture options"
|
||||
|
||||
config X86_64
|
||||
bool
|
||||
default y
|
||||
select ACPI
|
||||
select HAVE_MMU
|
||||
select HAVE_MMU_GENERIC
|
||||
help
|
||||
64-bit x86 CPU support for AMD and Intel CPUs.
|
||||
|
||||
config TSC
|
||||
bool "Invariant Timestamp Counter support"
|
||||
default y
|
||||
|
||||
endmenu
|
||||
@@ -1,32 +0,0 @@
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/syscall.h>
|
||||
#include <arch/archctl.h>
|
||||
#include <arch/mem.h>
|
||||
#include <uapi/archctl.h>
|
||||
#include <x86_64/asm.h>
|
||||
#include <x86_64/defs.h>
|
||||
|
||||
struct syscall_ret arch_archctl(uintptr_t op, uintptr_t arg) {
|
||||
struct syscall_ret ret = {};
|
||||
|
||||
switch (op) {
|
||||
case __ARCHCTL_SET_FSBASE:
|
||||
if (!arch_mem_addr_is_user(arg)) {
|
||||
ret.error = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
asm_wrmsr(MSR_FS_BASE, arg);
|
||||
return ret;
|
||||
case __ARCHCTL_SET_GSBASE:
|
||||
if (!arch_mem_addr_is_user(arg)) {
|
||||
ret.error = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
// Update the inactive GSBASE MSR, not ours.
|
||||
asm_wrmsr(MSR_KERNEL_GS_BASE, arg);
|
||||
return ret;
|
||||
default:
|
||||
ret.error = EINVAL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
#include <zinnia/acpi.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/mmio.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.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("x86_64.hpet", "Enable High Precision Event Timer clock", hpet_option);
|
||||
|
||||
struct hpet {
|
||||
struct clock clock;
|
||||
void* regs;
|
||||
uint32_t period;
|
||||
};
|
||||
|
||||
constexpr uint32_t HPET_CAP = 0x0;
|
||||
constexpr uint32_t HPET_CFG = 0x10;
|
||||
constexpr uint32_t HPET_COUNTER = 0xF0;
|
||||
|
||||
static void hpet_reset(struct clock* c) {
|
||||
struct hpet* hpet = containerof(c, struct hpet, clock);
|
||||
mmio_write64(hpet->regs + HPET_COUNTER, 0);
|
||||
}
|
||||
|
||||
static uint64_t hpet_get_elapsed_ns(struct clock* c) {
|
||||
struct hpet* hpet = containerof(c, struct hpet, clock);
|
||||
uint64_t counter = mmio_read64(hpet->regs + HPET_COUNTER);
|
||||
return (uint64_t)((__uint128_t)counter * (__uint128_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 void hpet_init(struct boot_info*) {
|
||||
if (!use_hpet)
|
||||
return;
|
||||
|
||||
uacpi_table table = {0};
|
||||
if (uacpi_table_find_by_signature("HPET", &table) != UACPI_STATUS_OK) {
|
||||
log("x86_64/hpet: No HPET table found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct acpi_hpet* hpet = table.ptr;
|
||||
|
||||
uintptr_t vaddr = vm_alloc(0x1000);
|
||||
pmap_map(&kernel_vma.pmap, vaddr, hpet->address.address, PTE_READ | PTE_WRITE, CACHE_MMIO);
|
||||
hpet_clock.regs = (void*)vaddr;
|
||||
|
||||
uacpi_table_unref(&table);
|
||||
log("x86_64/hpet: HPET registers mapped at %p\n", hpet_clock.regs);
|
||||
|
||||
mmio_write64(hpet_clock.regs + HPET_CFG, mmio_read64(hpet_clock.regs + HPET_CFG) | 1);
|
||||
|
||||
uint64_t caps = mmio_read64(hpet_clock.regs + HPET_CAP);
|
||||
hpet_clock.period = (caps >> 32) & 0xFFFF'FFFF;
|
||||
|
||||
assert(clock_switch(&hpet_clock.clock), "Unable to switch to HPET!\n");
|
||||
}
|
||||
INIT_TASK(hpet_stage, "arch.x86_64.hpet", hpet_init);
|
||||
INIT_DEPENDS(hpet_stage, acpi_stage);
|
||||
INIT_ENTAILS(hpet_stage, percpu_stage);
|
||||
@@ -1,34 +0,0 @@
|
||||
#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
|
||||
|
||||
typedef elf64_ehdr_t elf_ehdr_t;
|
||||
typedef elf64_phdr_t elf_phdr_t;
|
||||
typedef elf64_dyn_t elf_dyn_t;
|
||||
typedef elf64_addr_t elf_addr_t;
|
||||
typedef elf64_off_t elf_off_t;
|
||||
typedef elf64_nhdr_t elf_nhdr_t;
|
||||
typedef elf64_auxv_t elf_auxv_t;
|
||||
typedef elf64_sym_t elf_sym_t;
|
||||
typedef elf64_rela_t elf_rela_t;
|
||||
typedef elf64_rel_t elf_rel_t;
|
||||
typedef elf64_relr_t elf_relr_t;
|
||||
|
||||
enum {
|
||||
R_NONE = 0,
|
||||
R_64 = 1,
|
||||
R_COPY = 5,
|
||||
R_GLOB_DAT = 6,
|
||||
R_JUMP_SLOT = 7,
|
||||
R_RELATIVE = 8,
|
||||
R_IRELATIVE = 37,
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include <zinnia/pmap.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 << 7,
|
||||
ARCH_FLAG_EXECUTE_DISABLE = 1LU << 63,
|
||||
|
||||
ARCH_PTE_ADDR_MASK = 0x000F'FFFF'FFFF'F000,
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <x86_64/gdt.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;
|
||||
uint64_t percpu;
|
||||
uint32_t lapic_id;
|
||||
atomic_bool booted;
|
||||
};
|
||||
|
||||
// Discovers all APs and starts them.
|
||||
void x86_64_smp_init();
|
||||
@@ -1,50 +0,0 @@
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/pci.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <x86_64/asm.h>
|
||||
|
||||
constexpr uint16_t PCI_CONFIG_ADDRESS = 0xcf8;
|
||||
constexpr uint16_t PCI_CONFIG_DATA = 0xcfc;
|
||||
|
||||
static struct spinlock port_lock = {};
|
||||
|
||||
static void port_select(const struct pci_address* addr, uint16_t offset) {
|
||||
asm_outl(
|
||||
PCI_CONFIG_ADDRESS,
|
||||
1u << 31 | (uint32_t)addr->bus << 16 | (uint32_t)(addr->device & 0x1f) << 11 |
|
||||
(uint32_t)(addr->function & 0x7) << 8 | (offset & 0xfc)
|
||||
);
|
||||
}
|
||||
|
||||
static uint32_t port_read32(struct pci_access*, const struct pci_address* addr, uint16_t offset) {
|
||||
spin_lock(&port_lock);
|
||||
port_select(addr, offset);
|
||||
uint32_t value = asm_inl(PCI_CONFIG_DATA);
|
||||
spin_unlock(&port_lock);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void port_write32(struct pci_access*, const struct pci_address* addr, uint16_t offset, uint32_t value) {
|
||||
spin_lock(&port_lock);
|
||||
port_select(addr, offset);
|
||||
asm_outl(PCI_CONFIG_DATA, value);
|
||||
spin_unlock(&port_lock);
|
||||
}
|
||||
|
||||
static struct pci_access port_access = {
|
||||
.name = "port",
|
||||
.domain = 0,
|
||||
.start_bus = 0,
|
||||
.end_bus = 255,
|
||||
.priority = PCI_ACCESS_PRIO_LEGACY,
|
||||
.read32 = port_read32,
|
||||
.write32 = port_write32,
|
||||
};
|
||||
|
||||
[[__init]]
|
||||
static void pci_x86_stage_init(struct boot_info*) {
|
||||
pci_access_register(&port_access);
|
||||
}
|
||||
INIT_TASK(pci_x86_stage, "arch.x86_64.pci", pci_x86_stage_init);
|
||||
INIT_DEPENDS(pci_x86_stage, mem_stage);
|
||||
INIT_ENTAILS(pci_x86_stage, pci_stage);
|
||||
@@ -1,356 +0,0 @@
|
||||
#define pr_fmt(fmt) "smp: " fmt
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/irq.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <uacpi/acpi.h>
|
||||
#include <uacpi/tables.h>
|
||||
#include <x86_64/apic.h>
|
||||
#include <x86_64/defs.h>
|
||||
#include <x86_64/percpu.h>
|
||||
#include <x86_64/pmap.h>
|
||||
#include <x86_64/smp.h>
|
||||
|
||||
extern uint8_t smp_trampoline_start[];
|
||||
extern uint8_t smp_trampoline_data[];
|
||||
extern uint8_t smp_trampoline_end[];
|
||||
|
||||
[[__naked, __used]]
|
||||
static void smp_trampoline() {
|
||||
asm volatile(
|
||||
".pushsection .rodata\n"
|
||||
".code16\n"
|
||||
".global smp_trampoline_start\n"
|
||||
"smp_trampoline_start:\n"
|
||||
" cli\n"
|
||||
" cld\n"
|
||||
" jmp .Lmode16\n"
|
||||
|
||||
".org smp_trampoline_start + %c[data_off]\n"
|
||||
".global smp_trampoline_data\n"
|
||||
"smp_trampoline_data:\n"
|
||||
" .skip %c[info_size]\n"
|
||||
|
||||
".set .Ldata_offset, %c[data_off]\n"
|
||||
".set .Lgdtr_offset, (.Ldata_offset + %c[gdtr])\n"
|
||||
".set .Lfarjmp_offset, (.Ldata_offset + %c[farjmp])\n"
|
||||
".set .Ltemp_stack_offset, (.Ldata_offset + %c[temp_stack])\n"
|
||||
".set .Ltemp_cr3_offset, (.Ldata_offset + %c[temp_cr3])\n"
|
||||
".set .Lentry_offset, (.Ldata_offset + %c[entry])\n"
|
||||
".set .Lhhdm_offset, (.Ldata_offset + %c[hhdm])\n"
|
||||
|
||||
".Lmode16:\n"
|
||||
" mov bx, cs\n"
|
||||
" shl ebx, 4\n"
|
||||
|
||||
" lidtd cs:(.Linvalid_idtr - smp_trampoline_start)\n"
|
||||
" lgdtd cs:.Lgdtr_offset\n"
|
||||
|
||||
" lea eax, [ebx + (.Lmode32 - smp_trampoline_start)]\n"
|
||||
" mov dword ptr cs:.Lfarjmp_offset, eax\n"
|
||||
|
||||
" mov eax, %c[cr0]\n"
|
||||
" mov cr0, eax\n"
|
||||
" jmp fword ptr cs:.Lfarjmp_offset\n"
|
||||
|
||||
".code32\n"
|
||||
".Lmode32:\n"
|
||||
" mov ax, %c[kernel32_ds]\n"
|
||||
" mov ds, ax\n"
|
||||
" mov es, ax\n"
|
||||
" mov fs, ax\n"
|
||||
" mov gs, ax\n"
|
||||
" mov ss, ax\n"
|
||||
|
||||
" xor eax, eax\n"
|
||||
" lldt ax\n"
|
||||
|
||||
" mov esp, dword ptr [ebx + .Ltemp_stack_offset]\n"
|
||||
|
||||
" xor eax, eax\n"
|
||||
" mov cr4, eax\n"
|
||||
|
||||
" or eax, %c[cr4_pae]\n"
|
||||
" mov cr4, eax\n"
|
||||
|
||||
" mov ecx, %c[msr_efer]\n"
|
||||
" mov eax, %c[efer_lme]\n"
|
||||
" xor edx, edx\n"
|
||||
" wrmsr\n"
|
||||
|
||||
" mov eax, dword ptr [ebx + .Ltemp_cr3_offset]\n"
|
||||
" mov cr3, eax\n"
|
||||
|
||||
" mov eax, cr0\n"
|
||||
" or eax, %c[cr0_pg]\n"
|
||||
" mov cr0, eax\n"
|
||||
|
||||
" lea eax, [ebx + (.Lmode64 - smp_trampoline_start)]\n"
|
||||
" push %c[kernel64_cs]\n"
|
||||
" push eax\n"
|
||||
" retf\n"
|
||||
|
||||
".code64\n"
|
||||
".Lmode64:\n"
|
||||
" mov ax, %c[kernel64_ds]\n"
|
||||
" mov ds, ax\n"
|
||||
" mov es, ax\n"
|
||||
" mov fs, ax\n"
|
||||
" mov gs, ax\n"
|
||||
" mov ss, ax\n"
|
||||
|
||||
" mov ecx, %c[msr_efer]\n"
|
||||
" rdmsr\n"
|
||||
" or eax, %c[efer_nxe]\n"
|
||||
" wrmsr\n"
|
||||
|
||||
" xor ebp, ebp\n"
|
||||
|
||||
" lea rdi, [rbx + .Ldata_offset]\n"
|
||||
" mov rax, [rbx + .Lentry_offset]\n"
|
||||
" add rsp, [rbx + .Lhhdm_offset]\n"
|
||||
" jmp rax\n"
|
||||
|
||||
".Linvalid_idtr:\n"
|
||||
" .word 0\n"
|
||||
" .quad 0\n"
|
||||
|
||||
".global smp_trampoline_end\n"
|
||||
"smp_trampoline_end:\n"
|
||||
".popsection\n"
|
||||
" ud2\n"
|
||||
:
|
||||
: [data_off] "i"(8),
|
||||
[info_size] "i"(sizeof(struct smp_info)),
|
||||
[gdtr] "i"(offsetof(struct smp_info, gdtr)),
|
||||
[farjmp] "i"(offsetof(struct smp_info, farjmp_offset)),
|
||||
[temp_stack] "i"(offsetof(struct smp_info, temp_stack)),
|
||||
[temp_cr3] "i"(offsetof(struct smp_info, temp_cr3)),
|
||||
[entry] "i"(offsetof(struct smp_info, entry)),
|
||||
[hhdm] "i"(offsetof(struct smp_info, hhdm_offset)),
|
||||
[kernel32_ds] "i"(offsetof(struct gdt, kernel_data32)),
|
||||
[kernel64_cs] "i"(offsetof(struct gdt, kernel_code64)),
|
||||
[kernel64_ds] "i"(offsetof(struct gdt, kernel_data64)),
|
||||
[cr0] "i"(CR0_PE | CR0_ET),
|
||||
[cr0_pg] "i"(CR0_PG),
|
||||
[cr4_pae] "i"(CR4_PAE),
|
||||
[msr_efer] "i"(MSR_EFER),
|
||||
[efer_lme] "i"(MSR_EFER_LME),
|
||||
[efer_nxe] "i"(MSR_EFER_NXE)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr size_t AP_BOOT_STACK_SIZE = 0x8000;
|
||||
constexpr uint64_t AP_BOOT_TIMEOUT_NS = 1'000'000'000;
|
||||
|
||||
[[noreturn]]
|
||||
static void ap_entry(phys_t info_addr) {
|
||||
// We're still running on the temporary page table.
|
||||
pmap_set(&kernel_vma.pmap);
|
||||
|
||||
struct smp_info* info = HHDM_PTR(info_addr);
|
||||
struct percpu* cpu = (struct percpu*)info->percpu;
|
||||
|
||||
x86_64_cpu_setup(cpu);
|
||||
irq_lock();
|
||||
|
||||
// This task keeps running on the boot stack, but is never scheduled again after the first yield.
|
||||
sched_init(&cpu->sched);
|
||||
|
||||
// Let the BSP know that we're alive.
|
||||
// `info` must not be touched after this point.
|
||||
atomic_store_explicit(&info->booted, 1, memory_order_release);
|
||||
|
||||
log("CPU %zu online\n", cpu->id);
|
||||
|
||||
sched_yield(&cpu->sched);
|
||||
unreachable();
|
||||
}
|
||||
|
||||
static bool start_ap(phys_t trampoline, uint32_t temp_cr3, uint32_t lapic_id) {
|
||||
const size_t trampoline_size = smp_trampoline_end - smp_trampoline_start;
|
||||
|
||||
phys_t stack;
|
||||
if (mem_phys_alloc(AP_BOOT_STACK_SIZE / arch_mem_page_size(), ALLOC_MEM32 | ALLOC_NOZERO, &stack)) {
|
||||
log_err("Failed to allocate a boot stack for LAPIC %u\n", lapic_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct percpu* cpu = percpu_new();
|
||||
if (!cpu) {
|
||||
log_err("Failed to allocate per-CPU data for LAPIC %u\n", lapic_id);
|
||||
mem_phys_free(stack, AP_BOOT_STACK_SIZE / arch_mem_page_size());
|
||||
return false;
|
||||
}
|
||||
|
||||
log("Starting CPU %zu\n", cpu->id);
|
||||
|
||||
memcpy(HHDM_PTR(trampoline), smp_trampoline_start, trampoline_size);
|
||||
|
||||
const phys_t info_addr = trampoline + (smp_trampoline_data - smp_trampoline_start);
|
||||
struct smp_info* info = HHDM_PTR(info_addr);
|
||||
memcpy(&info->gdt, &gdt_base, sizeof(info->gdt));
|
||||
info->gdtr.limit = sizeof(struct gdt) - 1;
|
||||
info->gdtr.base = (struct gdt*)(info_addr + offsetof(struct smp_info, gdt));
|
||||
info->farjmp_offset = 0; // Filled in by the trampoline.
|
||||
info->farjmp_segment = offsetof(struct gdt, kernel_code32);
|
||||
info->temp_stack = stack + AP_BOOT_STACK_SIZE;
|
||||
info->temp_cr3 = temp_cr3;
|
||||
info->entry = (uint64_t)ap_entry;
|
||||
info->hhdm_offset = mem_hhdm_base;
|
||||
info->percpu = (uint64_t)cpu;
|
||||
info->lapic_id = lapic_id;
|
||||
atomic_init(&info->booted, 0);
|
||||
|
||||
struct local_apic* lapic = &percpu_ref(arch_data)->lapic;
|
||||
|
||||
lapic_send_ipi(lapic, lapic_id, 0, APIC_DELIVERY_INIT, APIC_DEST_PHYSICAL, APIC_LEVEL_ASSERT, APIC_TRIGGER_EDGE);
|
||||
clock_spin_ns(10'000'000);
|
||||
|
||||
lapic_send_ipi(
|
||||
lapic,
|
||||
lapic_id,
|
||||
trampoline >> 12,
|
||||
APIC_DELIVERY_STARTUP,
|
||||
APIC_DEST_PHYSICAL,
|
||||
APIC_LEVEL_ASSERT,
|
||||
APIC_TRIGGER_EDGE
|
||||
);
|
||||
clock_spin_ns(10'000'000);
|
||||
|
||||
for (uint64_t waited = 0; !atomic_load_explicit(&info->booted, memory_order_acquire); waited += 1'000'000) {
|
||||
if (waited >= AP_BOOT_TIMEOUT_NS) {
|
||||
// The per-CPU data and boot stack are leaked on purpose, in case the AP still comes up.
|
||||
log_err("CPU %zu (LAPIC %u) did not respond\n", cpu->id, lapic_id);
|
||||
return false;
|
||||
}
|
||||
clock_spin_ns(1'000'000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static errno_t build_temp_table(phys_t* out) {
|
||||
const size_t pgsz = arch_mem_page_size();
|
||||
|
||||
phys_t mem;
|
||||
errno_t status = mem_phys_alloc(6, ALLOC_MEM32, &mem);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
uint64_t* l4 = HHDM_PTR(mem);
|
||||
uint64_t* l3 = HHDM_PTR(mem + pgsz);
|
||||
const phys_t l2_base = mem + 2 * pgsz;
|
||||
|
||||
// Use 2 MiB pages.
|
||||
l4[0] = (mem + pgsz) | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE;
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
uint64_t* l2 = HHDM_PTR(l2_base + i * pgsz);
|
||||
l3[i] = (l2_base + i * pgsz) | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE;
|
||||
for (size_t j = 0; j < 512; j++)
|
||||
l2[j] = ((i << 30) + (j << 21)) | ARCH_FLAG_PRESENT | ARCH_FLAG_READ_WRITE | ARCH_FLAG_SIZE;
|
||||
}
|
||||
|
||||
// Copy over the higher half from the kernel page table.
|
||||
const uint64_t* kernel_l4 = HHDM_PTR(kernel_vma.pmap.root);
|
||||
for (size_t i = 256; i < 512; i++)
|
||||
l4[i] = kernel_l4[i];
|
||||
|
||||
*out = mem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x86_64_smp_init() {
|
||||
const size_t trampoline_size = smp_trampoline_end - smp_trampoline_start;
|
||||
assert(trampoline_size <= arch_mem_page_size(), "SMP trampoline doesn't fit in a page\n");
|
||||
|
||||
const size_t max_cpus = percpu_max_cpus();
|
||||
if (max_cpus <= 1)
|
||||
return;
|
||||
|
||||
uacpi_table table;
|
||||
if (uacpi_table_find_by_signature(ACPI_MADT_SIGNATURE, &table) != UACPI_STATUS_OK) {
|
||||
log_warn("No MADT found, not starting any APs\n");
|
||||
return;
|
||||
}
|
||||
|
||||
phys_t trampoline;
|
||||
if (mem_phys_alloc(1, ALLOC_MEM20, &trampoline)) {
|
||||
log_err("Failed to allocate 20-bit memory for the AP trampoline\n");
|
||||
uacpi_table_unref(&table);
|
||||
return;
|
||||
}
|
||||
|
||||
phys_t temp_table;
|
||||
if (build_temp_table(&temp_table)) {
|
||||
log_err("Failed to allocate a temporary page table\n");
|
||||
mem_phys_free(trampoline, 1);
|
||||
uacpi_table_unref(&table);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t bsp_id = percpu_ref(arch_data)->lapic.id;
|
||||
bool all_booted = true;
|
||||
size_t num_cpus = 1;
|
||||
|
||||
const struct acpi_madt* madt = table.ptr;
|
||||
const uint8_t* entry = (const uint8_t*)madt->entries;
|
||||
const uint8_t* end = (const uint8_t*)madt + madt->hdr.length;
|
||||
|
||||
while (entry + sizeof(struct acpi_entry_hdr) <= end && num_cpus < max_cpus) {
|
||||
const struct acpi_entry_hdr* hdr = (const struct acpi_entry_hdr*)entry;
|
||||
|
||||
// Don't trust malformed firmware tables.
|
||||
if (hdr->length < sizeof(struct acpi_entry_hdr) || entry + hdr->length > end) {
|
||||
log_warn("MADT contains a malformed entry\n");
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t lapic_id = 0;
|
||||
bool enabled = false;
|
||||
switch (hdr->type) {
|
||||
case ACPI_MADT_ENTRY_TYPE_LAPIC:
|
||||
const struct acpi_madt_lapic* lapic = (const struct acpi_madt_lapic*)hdr;
|
||||
lapic_id = lapic->id;
|
||||
enabled = lapic->flags & ACPI_PIC_ENABLED;
|
||||
break;
|
||||
case ACPI_MADT_ENTRY_TYPE_LOCAL_X2APIC:
|
||||
const struct acpi_madt_x2apic* x2apic = (const struct acpi_madt_x2apic*)hdr;
|
||||
lapic_id = x2apic->id;
|
||||
enabled = x2apic->flags & ACPI_PIC_ENABLED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (enabled && lapic_id != bsp_id) {
|
||||
if (start_ap(trampoline, temp_table, lapic_id))
|
||||
num_cpus++;
|
||||
else
|
||||
all_booted = false;
|
||||
}
|
||||
|
||||
entry += hdr->length;
|
||||
}
|
||||
|
||||
uacpi_table_unref(&table);
|
||||
|
||||
// An AP which didn't respond might still be using these.
|
||||
if (all_booted) {
|
||||
mem_phys_free(temp_table, 6);
|
||||
mem_phys_free(trampoline, 1);
|
||||
}
|
||||
|
||||
log("%zu CPU(s) online\n", num_cpus);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#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(
|
||||
"mov rsp, %0\n"
|
||||
"jmp %1"
|
||||
:
|
||||
: "r"(__ld_stack_top), "r"(kernel_entry)
|
||||
);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/syscall.h>
|
||||
#include <bits/sched.h>
|
||||
#include <stddef.h>
|
||||
#include <x86_64/defs.h>
|
||||
#include <x86_64/gdt.h>
|
||||
|
||||
// TODO: Refactor to only push callee-saved registers.
|
||||
|
||||
[[__naked]]
|
||||
void arch_syscall_stub() {
|
||||
asm volatile(
|
||||
" swapgs\n"
|
||||
" mov gs:%c[user_stack], rsp\n"
|
||||
" mov rsp, gs:%c[kernel_stack]\n"
|
||||
" cld\n"
|
||||
"1:\n"
|
||||
" push %c[user_data]\n" // ss
|
||||
" push gs:%c[user_stack]\n"
|
||||
" push r11\n" // rflags
|
||||
" push %c[user_code64]\n" // cs
|
||||
" push rcx\n" // rip
|
||||
" push 0x00\n" // arch_context::error
|
||||
" push 0x00\n" // arch_context::isr
|
||||
" 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" // SysV ABI first argument.
|
||||
" call syscall_dispatch\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
|
||||
" pop rcx\n" // rip
|
||||
" add rsp, 0x08\n" // Skip cs
|
||||
" pop r11\n" // rflags
|
||||
" pop rsp\n" // rsp
|
||||
" swapgs\n"
|
||||
" sysretq\n"
|
||||
:
|
||||
: [user_stack] "i"(offsetof(struct percpu, user_stack)),
|
||||
[kernel_stack] "i"(offsetof(struct percpu, kernel_stack)),
|
||||
[user_code64] "i"(offsetof(struct gdt, user_code64) | CPL_USER),
|
||||
[user_data] "i"(offsetof(struct gdt, user_data) | CPL_USER)
|
||||
);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#define pr_fmt(fmt) "x86_64/tsc: " fmt
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stdint.h>
|
||||
#include <x86_64/asm.h>
|
||||
#include <x86_64/defs.h>
|
||||
|
||||
static bool use_tsc = true;
|
||||
static void tsc_option(bool opt) {
|
||||
use_tsc = opt;
|
||||
}
|
||||
CMDLINE_OPTION("x86_64.tsc", "Enable timestamp counter", tsc_option);
|
||||
|
||||
constexpr uint64_t nanos_per_sec = 1'000'000'000;
|
||||
static uint64_t tsc_freq = 0;
|
||||
static uint64_t tsc_base_cycles = 0;
|
||||
|
||||
static uint64_t cycles_to_ns(uint64_t cycles, uint64_t freq) {
|
||||
uint64_t seconds = cycles / freq;
|
||||
uint64_t remaining_cycles = cycles % freq;
|
||||
uint64_t fractional_ns = (uint64_t)((__uint128_t)remaining_cycles * (__uint128_t)nanos_per_sec / (__uint128_t)freq);
|
||||
uint64_t ns = seconds * nanos_per_sec + fractional_ns;
|
||||
|
||||
return MIN(ns, UINT64_MAX);
|
||||
}
|
||||
|
||||
static void tsc_reset(struct clock* clock) {
|
||||
tsc_base_cycles = asm_rdtsc();
|
||||
}
|
||||
|
||||
static uint64_t tsc_get_elapsed_ns(struct clock* clock) {
|
||||
uint64_t cycles = asm_rdtsc() - tsc_base_cycles;
|
||||
return cycles_to_ns(cycles, tsc_freq);
|
||||
}
|
||||
|
||||
static struct clock tsc_clock = {
|
||||
.name = "tsc",
|
||||
.priority = 255,
|
||||
.reset = tsc_reset,
|
||||
.get_elapsed_ns = tsc_get_elapsed_ns,
|
||||
};
|
||||
|
||||
void tsc_init() {
|
||||
if (!use_tsc) {
|
||||
log_warn("TSC disabled on the command line");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(asm_cpuid(1, 0).edx & CPUID_1D_TSC) || !(asm_cpuid(0x8000'0007, 0).edx & (1 << 8))) {
|
||||
log_warn("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(0, 0).eax >= 0x15) {
|
||||
tsc_leaf = asm_cpuid(0x15, 0);
|
||||
has_tsc_leaf = true;
|
||||
}
|
||||
|
||||
if (clock_available()) {
|
||||
// Calibrate using existing clock.
|
||||
uint64_t t1 = asm_rdtsc();
|
||||
clock_spin_ns(10'000'000);
|
||||
uint64_t t2 = asm_rdtsc();
|
||||
tsc_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) {
|
||||
log("Calibrating TSC using CPUID 0x15");
|
||||
tsc_freq = (uint64_t)tsc_leaf.ecx * tsc_leaf.ebx / tsc_leaf.eax;
|
||||
} else {
|
||||
log_err("Unable to calibrate TSC using CPUID frequency information");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log_err("Unable to calibrate the TSC without a clock or static frequency info!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Timer frequency is %lu MHz\n", tsc_freq / 1'000'000);
|
||||
clock_switch(&tsc_clock);
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <arch/usercopy.h>
|
||||
#include <x86_64/percpu.h>
|
||||
|
||||
void arch_usercopy_allow(bool state) {
|
||||
if (!percpu_ref(arch_data)->can_smap)
|
||||
return;
|
||||
|
||||
if (state) {
|
||||
asm volatile("stac" ::: "memory");
|
||||
} else {
|
||||
asm volatile("clac" ::: "memory");
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_read(void* dst, const __user void* src, size_t len, struct usercopy_region** region) {
|
||||
asm volatile(
|
||||
// Setup regs for `rep movsb`.
|
||||
" xchg rcx, rdx\n"
|
||||
" lea rax, [rip + %0]\n"
|
||||
" xchg [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"
|
||||
" xchg [rdx], rax\n"
|
||||
" mov rax, 1\n"
|
||||
" ret\n"
|
||||
|
||||
".global x86_64_read_fault\n"
|
||||
"x86_64_read_fault:\n"
|
||||
" xor rax, rax\n"
|
||||
" mov [rdx], rax\n"
|
||||
" ret\n"
|
||||
:
|
||||
: "i"(&read_region)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct usercopy_region** region) {
|
||||
asm volatile(
|
||||
// Setup regs for `rep movsb`.
|
||||
" xchg rcx, rdx\n"
|
||||
" lea 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"
|
||||
" xchg [rdx], rax\n"
|
||||
" mov rax, 1\n"
|
||||
" ret\n"
|
||||
|
||||
".global x86_64_write_fault\n"
|
||||
"x86_64_write_fault:\n"
|
||||
" xor rax, rax\n"
|
||||
" mov [rdx], rax\n"
|
||||
" ret\n"
|
||||
:
|
||||
: "i"(&write_region)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
" lea rax, [rip + %0]\n"
|
||||
" xchg [rcx], rax\n"
|
||||
|
||||
".global x86_64_strlen_start\n"
|
||||
" x86_64_strlen_start:\n"
|
||||
" xor r8, r8\n"
|
||||
".Lloop:\n"
|
||||
" cmp r8, rsi\n"
|
||||
" je .Lleave\n"
|
||||
" cmp byte ptr [rdi + r8], 0\n"
|
||||
" je .Lleave\n"
|
||||
" inc r8\n"
|
||||
" jmp .Lloop\n"
|
||||
".Lleave:\n"
|
||||
" mov [rdx], r8\n"
|
||||
|
||||
".global x86_64_strlen_end\n"
|
||||
"x86_64_strlen_end:\n"
|
||||
" xor rax, rax\n"
|
||||
" xchg [rcx], rax\n"
|
||||
" mov rax, 1\n"
|
||||
" ret\n"
|
||||
|
||||
".global x86_64_strlen_fault\n"
|
||||
"x86_64_strlen_fault:\n"
|
||||
" xor rax, rax\n"
|
||||
" mov [rcx], rax\n"
|
||||
" ret\n"
|
||||
:
|
||||
: "i"(&strlen_region)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
@@ -1,669 +0,0 @@
|
||||
/* 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
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/elf.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/irq.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <zinnia/vmo.h>
|
||||
#include <config.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const char zinnia_banner[] = "Zinnia " ZINNIA_VERSION " " CONFIG_KERNEL_FLAVOR " (" ZINNIA_ARCH
|
||||
", " ZINNIA_COMPILER_ID ", " ZINNIA_LINKER_ID ")";
|
||||
|
||||
static void kernel_main_task(uintptr_t arg0) {
|
||||
errno_t status = 0;
|
||||
struct boot_info* info = (struct boot_info*)arg0;
|
||||
|
||||
log("main: Hello from main task!\n");
|
||||
|
||||
// Finish the initgraph.
|
||||
initgraph_run(nullptr, info);
|
||||
|
||||
// TODO: Try to run init.
|
||||
|
||||
assert(info->num_files >= 1, "No init executable provided");
|
||||
|
||||
struct vma* init_space;
|
||||
assert(vma_new(&init_space) == 0, "");
|
||||
|
||||
struct boot_file* file_data = &info->files[0];
|
||||
struct task* init_task = nullptr;
|
||||
|
||||
struct paged_vmo* init_file;
|
||||
status = vmo_new_phys(&init_file);
|
||||
assert(status == 0, "Failed to allocate a memory object");
|
||||
|
||||
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 exec_info exec = {
|
||||
.space = init_space,
|
||||
.file_obj = &init_file->object,
|
||||
.argv = init_argv,
|
||||
.envp = init_envp,
|
||||
};
|
||||
|
||||
status = elf_load(&exec, &init_task);
|
||||
vmo_unref(&init_file->object);
|
||||
|
||||
assert(status == 0, "Failed to load init process (%i)", status);
|
||||
sched_add_task(&percpu_get()->sched, init_task);
|
||||
|
||||
while (true) {
|
||||
irq_wait();
|
||||
}
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
void kernel_early_init() {
|
||||
percpu_bsp_init();
|
||||
percpu_get()->online = true;
|
||||
irq_lock();
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main(struct boot_info* info) {
|
||||
cmdline_parse(info->cmdline);
|
||||
|
||||
log("%s\n", zinnia_banner); // Say hello!
|
||||
log("Command line: %s\n", info->cmdline);
|
||||
|
||||
initgraph_run(sched_stage, info);
|
||||
|
||||
struct task* main_task;
|
||||
task_create("main", &kernel_vma, kernel_main_task, (uintptr_t)info, &main_task);
|
||||
sched_add_task(&percpu_get()->sched, main_task);
|
||||
|
||||
irq_unlock();
|
||||
sched_yield(&percpu_get()->sched);
|
||||
|
||||
unreachable();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
menu "Kernel Core options"
|
||||
|
||||
source "core/mem/Kconfig"
|
||||
|
||||
choice SCHED_ALG
|
||||
prompt "Scheduler algorithm"
|
||||
default SCHED_DUMB
|
||||
help
|
||||
The algorithm to use for scheduling.
|
||||
|
||||
config SCHED_DUMB
|
||||
bool "Dumb Scheduler"
|
||||
help
|
||||
Very basic scheduler, without any priorities.
|
||||
|
||||
endchoice
|
||||
|
||||
endmenu
|
||||
@@ -1,67 +0,0 @@
|
||||
#define pr_fmt(fmt) "clock: " fmt
|
||||
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static struct clock* active_clock = nullptr;
|
||||
static uint64_t counter_base = 0;
|
||||
static struct spinlock lock = {};
|
||||
|
||||
static uint64_t clock_get_elapsed_ns_locked() {
|
||||
if (__unlikely(!active_clock))
|
||||
return 0;
|
||||
|
||||
return counter_base + active_clock->get_elapsed_ns(active_clock);
|
||||
}
|
||||
|
||||
bool clock_available() {
|
||||
spin_lock(&lock);
|
||||
bool available = active_clock != nullptr;
|
||||
spin_unlock(&lock);
|
||||
return available;
|
||||
}
|
||||
|
||||
uint64_t clock_get_elapsed_ns() {
|
||||
spin_lock(&lock);
|
||||
uint64_t elapsed = clock_get_elapsed_ns_locked();
|
||||
spin_unlock(&lock);
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
bool clock_switch(struct clock* clock) {
|
||||
spin_lock(&lock);
|
||||
|
||||
if (active_clock) {
|
||||
if (clock->priority <= active_clock->priority) {
|
||||
spin_unlock(&lock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t elapsed = clock_get_elapsed_ns_locked();
|
||||
clock->reset(clock);
|
||||
|
||||
counter_base = elapsed;
|
||||
active_clock = clock;
|
||||
|
||||
spin_unlock(&lock);
|
||||
|
||||
log("Switching to clock source \"%s\"\n", clock->name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clock_spin_ns(uint64_t ns) {
|
||||
if (!clock_available()) {
|
||||
log_err("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?
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/spin.h>
|
||||
|
||||
static struct console* registered = nullptr;
|
||||
static struct spinlock console_lock;
|
||||
|
||||
void console_write(const char* buf, size_t len) {
|
||||
spin_lock(&console_lock);
|
||||
for (struct console* con = registered; con; con = con->next)
|
||||
con->write(con, buf, len);
|
||||
spin_unlock(&console_lock);
|
||||
}
|
||||
|
||||
static void console_replay_write(void* ctx, const char* buf, size_t len) {
|
||||
struct console* con = ctx;
|
||||
con->write(con, buf, len);
|
||||
}
|
||||
|
||||
bool console_register(struct console* con) {
|
||||
if (!con->init(con))
|
||||
return false;
|
||||
|
||||
con->write(con, "\ec", 2);
|
||||
log_replay(console_replay_write, con);
|
||||
spin_lock(&console_lock);
|
||||
con->next = registered;
|
||||
registered = con;
|
||||
spin_unlock(&console_lock);
|
||||
|
||||
log("console: Registered console \"%s\"\n", con->name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void console_unregister(struct console* con) {
|
||||
spin_lock(&console_lock);
|
||||
for (struct console** link = ®istered; *link; link = &(*link)->next) {
|
||||
if (*link == con) {
|
||||
*link = con->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&console_lock);
|
||||
}
|
||||
|
||||
INIT_TASK(console_stage, "core.console");
|
||||
@@ -1,3 +0,0 @@
|
||||
#include <zinnia/dpc.h>
|
||||
|
||||
// TODO
|
||||
-205
@@ -1,205 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static const uint8_t fbcon_font[] = {
|
||||
#embed "fbcon_font.bin"
|
||||
};
|
||||
|
||||
constexpr size_t FONT_WIDTH = 8;
|
||||
constexpr size_t FONT_HEIGHT = 12;
|
||||
constexpr uint32_t FG_COLOR = 0xaaaaaa;
|
||||
constexpr uint32_t BG_COLOR = 0x000000;
|
||||
|
||||
enum esc_state {
|
||||
ESC_NONE,
|
||||
ESC_START,
|
||||
ESC_CSI,
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct boot_framebuffer fb;
|
||||
uint32_t* back;
|
||||
uint32_t fg, bg;
|
||||
size_t cols, rows;
|
||||
size_t offset_x, offset_y;
|
||||
size_t cx, cy;
|
||||
enum esc_state esc;
|
||||
} fbcon;
|
||||
|
||||
static inline uint8_t* fb_ptr() {
|
||||
return HHDM_PTR(fbcon.fb.base);
|
||||
}
|
||||
|
||||
static inline uint32_t* back_row(size_t y) {
|
||||
return fbcon.back + y * fbcon.fb.width;
|
||||
}
|
||||
|
||||
static void flush_rect(size_t x, size_t y, size_t w, size_t h) {
|
||||
for (size_t j = 0; j < h; j++)
|
||||
memcpy((uint32_t*)(fb_ptr() + (y + j) * fbcon.fb.pitch) + x, back_row(y + j) + x, w * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static uint32_t pack_color(uint32_t rgb) {
|
||||
const struct boot_framebuffer* fb = &fbcon.fb;
|
||||
uint32_t r = (rgb >> 16) & 0xff;
|
||||
uint32_t g = (rgb >> 8) & 0xff;
|
||||
uint32_t b = rgb & 0xff;
|
||||
return ((r >> (8 - fb->red.size)) << fb->red.shift) | ((g >> (8 - fb->green.size)) << fb->green.shift) |
|
||||
((b >> (8 - fb->blue.size)) << fb->blue.shift);
|
||||
}
|
||||
|
||||
static void fill_rect(size_t x, size_t y, size_t w, size_t h, uint32_t color) {
|
||||
for (size_t j = 0; j < h; j++) {
|
||||
uint32_t* row = back_row(y + j) + x;
|
||||
for (size_t i = 0; i < w; i++)
|
||||
row[i] = color;
|
||||
}
|
||||
flush_rect(x, y, w, h);
|
||||
}
|
||||
|
||||
static void clear_screen() {
|
||||
fill_rect(0, 0, fbcon.fb.width, fbcon.fb.height, fbcon.bg);
|
||||
fbcon.cx = fbcon.cy = 0;
|
||||
}
|
||||
|
||||
static void draw_glyph(size_t col, size_t row, uint8_t ch) {
|
||||
const uint8_t* glyph = &fbcon_font[ch * FONT_HEIGHT];
|
||||
size_t x = fbcon.offset_x + col * FONT_WIDTH;
|
||||
size_t y = fbcon.offset_y + row * FONT_HEIGHT;
|
||||
|
||||
for (size_t j = 0; j < FONT_HEIGHT; j++) {
|
||||
uint32_t* line = back_row(y + j) + x;
|
||||
for (size_t i = 0; i < FONT_WIDTH; i++)
|
||||
line[i] = (glyph[j] & (0x80 >> i)) ? fbcon.fg : fbcon.bg;
|
||||
}
|
||||
flush_rect(x, y, FONT_WIDTH, FONT_HEIGHT);
|
||||
}
|
||||
|
||||
static void scroll_up() {
|
||||
const size_t width = fbcon.fb.width;
|
||||
const size_t text_y = fbcon.offset_y;
|
||||
const size_t text_h = fbcon.rows * FONT_HEIGHT;
|
||||
|
||||
memmove(back_row(text_y), back_row(text_y + FONT_HEIGHT), (text_h - FONT_HEIGHT) * width * sizeof(uint32_t));
|
||||
for (size_t j = text_y + text_h - FONT_HEIGHT; j < text_y + text_h; j++) {
|
||||
uint32_t* row = back_row(j);
|
||||
for (size_t i = 0; i < width; i++)
|
||||
row[i] = fbcon.bg;
|
||||
}
|
||||
flush_rect(0, text_y, width, text_h);
|
||||
}
|
||||
|
||||
static void newline() {
|
||||
fbcon.cx = 0;
|
||||
if (++fbcon.cy >= fbcon.rows) {
|
||||
fbcon.cy = fbcon.rows - 1;
|
||||
scroll_up();
|
||||
}
|
||||
}
|
||||
|
||||
static void put_char(uint8_t ch) {
|
||||
switch (fbcon.esc) {
|
||||
case ESC_START:
|
||||
fbcon.esc = ch == '[' ? ESC_CSI : ESC_NONE;
|
||||
if (ch == 'c')
|
||||
clear_screen();
|
||||
return;
|
||||
case ESC_CSI:
|
||||
if (ch >= 0x40 && ch <= 0x7e)
|
||||
fbcon.esc = ESC_NONE;
|
||||
return;
|
||||
case ESC_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ch) {
|
||||
case 0x1b:
|
||||
fbcon.esc = ESC_START;
|
||||
break;
|
||||
case '\n':
|
||||
newline();
|
||||
break;
|
||||
case '\r':
|
||||
fbcon.cx = 0;
|
||||
break;
|
||||
case '\b':
|
||||
if (fbcon.cx > 0)
|
||||
fbcon.cx--;
|
||||
break;
|
||||
case '\t':
|
||||
fbcon.cx = (fbcon.cx + 8) & ~(size_t)7;
|
||||
if (fbcon.cx >= fbcon.cols)
|
||||
newline();
|
||||
break;
|
||||
default:
|
||||
if (fbcon.cx >= fbcon.cols)
|
||||
newline();
|
||||
draw_glyph(fbcon.cx++, fbcon.cy, ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void fbcon_write(struct console* con, const char* buf, size_t count) {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
put_char((uint8_t)buf[i]);
|
||||
}
|
||||
|
||||
static bool fbcon_setup(struct console* con) {
|
||||
const struct boot_framebuffer* fb = &fbcon.fb;
|
||||
if (fb->bytes_per_pixel != 4 || fb->red.size > 8 || fb->green.size > 8 || fb->blue.size > 8)
|
||||
return false;
|
||||
|
||||
fbcon.fg = pack_color(FG_COLOR);
|
||||
fbcon.bg = pack_color(BG_COLOR);
|
||||
fbcon.cols = fb->width / FONT_WIDTH;
|
||||
fbcon.rows = fb->height / FONT_HEIGHT;
|
||||
fbcon.offset_x = (fb->width % FONT_WIDTH) / 2;
|
||||
fbcon.offset_y = (fb->height % FONT_HEIGHT) / 2;
|
||||
if (!fbcon.cols || !fbcon.rows)
|
||||
return false;
|
||||
|
||||
clear_screen();
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct console fbcon_con = {
|
||||
.name = "fbcon",
|
||||
.init = fbcon_setup,
|
||||
.write = fbcon_write,
|
||||
};
|
||||
|
||||
[[__init]]
|
||||
void fbcon_init(struct boot_info* info) {
|
||||
const struct boot_framebuffer* fb = &info->fb;
|
||||
if (!fb->base)
|
||||
return;
|
||||
|
||||
fbcon.fb = *fb;
|
||||
|
||||
const size_t pgsz = arch_mem_page_size();
|
||||
const phys_t start = ALIGN_DOWN(fb->base, pgsz);
|
||||
const phys_t end = ALIGN_UP(fb->base + fb->pitch * fb->height, pgsz);
|
||||
|
||||
for (phys_t p = start; p < end; p += pgsz) {
|
||||
errno_t status =
|
||||
pmap_map(&kernel_vma.pmap, (uintptr_t)HHDM_PTR(p), p, PTE_READ | PTE_WRITE, CACHE_WRITE_COMBINE);
|
||||
if (status)
|
||||
return;
|
||||
}
|
||||
|
||||
fbcon.back = mem_alloc(fb->width * fb->height * sizeof(uint32_t), ALLOC_NOZERO);
|
||||
if (!fbcon.back)
|
||||
return;
|
||||
|
||||
console_register(&fbcon_con);
|
||||
}
|
||||
INIT_TASK(fbcon_stage, "core.fbcon", fbcon_init);
|
||||
INIT_DEPENDS(fbcon_stage, sched_stage);
|
||||
Binary file not shown.
@@ -1,194 +0,0 @@
|
||||
#define pr_fmt(fmt) "init: " fmt
|
||||
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/print.h>
|
||||
|
||||
extern struct init_node* const __ld_initgraph_nodes_start[];
|
||||
extern struct init_node* const __ld_initgraph_nodes_end[];
|
||||
extern const struct init_edges* const __ld_initgraph_edges_start[];
|
||||
extern const struct init_edges* const __ld_initgraph_edges_end[];
|
||||
|
||||
static bool dump_graph = false;
|
||||
static void initgraph_option(bool value) {
|
||||
dump_graph = value;
|
||||
}
|
||||
CMDLINE_OPTION("initgraph", "Print the init graph in DOT format after running", initgraph_option);
|
||||
|
||||
static bool edges_linked = false;
|
||||
static struct init_node* failed_node = nullptr;
|
||||
|
||||
struct init_queue {
|
||||
struct init_node* head;
|
||||
struct init_node* tail;
|
||||
};
|
||||
|
||||
static void queue_push(struct init_queue* queue, struct init_node* node) {
|
||||
node->queue_next = nullptr;
|
||||
if (queue->tail)
|
||||
queue->tail->queue_next = node;
|
||||
else
|
||||
queue->head = node;
|
||||
queue->tail = node;
|
||||
}
|
||||
|
||||
static struct init_node* queue_pop(struct init_queue* queue) {
|
||||
struct init_node* node = queue->head;
|
||||
if (node) {
|
||||
queue->head = node->queue_next;
|
||||
if (!queue->head)
|
||||
queue->tail = nullptr;
|
||||
node->queue_next = nullptr;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static void link_edges() {
|
||||
for (auto cur = __ld_initgraph_edges_start; cur < __ld_initgraph_edges_end; cur++) {
|
||||
const struct init_edges* group = *cur;
|
||||
for (size_t i = 0; i < group->count; i++) {
|
||||
struct init_edge* edge = &group->edges[i];
|
||||
edge->source = group->depends ? group->nodes[i] : group->node;
|
||||
edge->target = group->depends ? group->node : group->nodes[i];
|
||||
edge->propagates_disable = group->depends;
|
||||
|
||||
edge->next_out = edge->source->out_edges;
|
||||
edge->source->out_edges = edge;
|
||||
edge->next_in = edge->target->in_edges;
|
||||
edge->target->in_edges = edge;
|
||||
edge->target->unsatisfied++;
|
||||
}
|
||||
}
|
||||
edges_linked = true;
|
||||
}
|
||||
|
||||
static void mark_wanted(struct init_node* goal) {
|
||||
if (!goal) {
|
||||
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++)
|
||||
(*cur)->wanted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
struct init_queue queue = {};
|
||||
|
||||
if (!goal->wanted) {
|
||||
goal->wanted = true;
|
||||
queue_push(&queue, goal);
|
||||
}
|
||||
|
||||
struct init_node* node;
|
||||
while ((node = queue_pop(&queue))) {
|
||||
for (struct init_edge* edge = node->in_edges; edge; edge = edge->next_in) {
|
||||
if (!edge->source->wanted) {
|
||||
edge->source->wanted = true;
|
||||
queue_push(&queue, edge->source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_ready(struct init_node* node) {
|
||||
return node->wanted && !node->done && node->unsatisfied == 0;
|
||||
}
|
||||
|
||||
static void reach_node(struct init_node* node, struct boot_info* info) {
|
||||
if (node->disabled) {
|
||||
log_status("Skipping stage \"%s\"\n", node->name);
|
||||
} else {
|
||||
log_status("Running stage \"%s\"\n", node->name);
|
||||
failed_node = node;
|
||||
switch (node->action) {
|
||||
case INIT_EMPTY:
|
||||
break;
|
||||
case INIT_CALLBACK:
|
||||
node->callback(info);
|
||||
break;
|
||||
case INIT_GATE:
|
||||
if (!node->gate(info)) {
|
||||
node->disabled = true;
|
||||
node->failed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
failed_node = nullptr;
|
||||
node->executed = true;
|
||||
}
|
||||
node->done = true;
|
||||
}
|
||||
|
||||
static void dump() {
|
||||
kprintf("digraph initgraph {\n");
|
||||
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++) {
|
||||
struct init_node* node = *cur;
|
||||
const char* style = "";
|
||||
if (node->failed || node == failed_node)
|
||||
style = ", color=red, fontcolor=red";
|
||||
else if (!node->executed)
|
||||
style = ", color=gray, fontcolor=gray";
|
||||
kprintf("\tn%p [label=\"%s\"%s];\n", node, node->name, style);
|
||||
for (struct init_edge* edge = node->in_edges; edge; edge = edge->next_in)
|
||||
kprintf(
|
||||
"\tn%p -> n%p%s;\n",
|
||||
edge->source,
|
||||
edge->target,
|
||||
edge->source->executed && edge->target->executed ? "" : " [color=gray]"
|
||||
);
|
||||
}
|
||||
kprintf("}\n");
|
||||
}
|
||||
|
||||
void initgraph_panic() {
|
||||
static bool dumping = false;
|
||||
if (!dump_graph || !failed_node || dumping)
|
||||
return;
|
||||
dumping = true;
|
||||
dump();
|
||||
}
|
||||
|
||||
void initgraph_run(struct init_node* goal, struct boot_info* info) {
|
||||
if (!edges_linked)
|
||||
link_edges();
|
||||
|
||||
mark_wanted(goal);
|
||||
|
||||
struct init_queue pending = {};
|
||||
|
||||
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++) {
|
||||
if (is_ready(*cur))
|
||||
queue_push(&pending, *cur);
|
||||
}
|
||||
|
||||
struct init_node* node;
|
||||
while ((node = queue_pop(&pending))) {
|
||||
reach_node(node, info);
|
||||
|
||||
for (struct init_edge* edge = node->out_edges; edge; edge = edge->next_out) {
|
||||
struct init_node* successor = edge->target;
|
||||
assert(successor->unsatisfied != 0, "Stage \"%s\" has no unsatisfied dependencies left", successor->name);
|
||||
successor->unsatisfied--;
|
||||
|
||||
if (node->disabled && edge->propagates_disable)
|
||||
successor->disabled = true;
|
||||
|
||||
if (is_ready(successor))
|
||||
queue_push(&pending, successor);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cur = __ld_initgraph_nodes_start; cur < __ld_initgraph_nodes_end; cur++) {
|
||||
if ((*cur)->wanted && !(*cur)->done)
|
||||
failed_node = *cur;
|
||||
assert(
|
||||
!(*cur)->wanted || (*cur)->done,
|
||||
"The dependencies for stage \"%s\" could not be resolved",
|
||||
(*cur)->name
|
||||
);
|
||||
}
|
||||
|
||||
if (!goal) {
|
||||
log_status("All stages are complete!\n");
|
||||
if (dump_graph)
|
||||
dump();
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/irq.h>
|
||||
#include <zinnia/list.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <arch/irq.h>
|
||||
|
||||
[[__percpu]]
|
||||
struct irq_percpu irq_data;
|
||||
|
||||
bool irq_set_state(bool state) {
|
||||
return arch_irq_set_state(state);
|
||||
}
|
||||
|
||||
bool irq_get_state() {
|
||||
return arch_irq_get_state();
|
||||
}
|
||||
|
||||
void irq_wait() {
|
||||
arch_irq_wait();
|
||||
}
|
||||
|
||||
void irq_lock() {
|
||||
bool prev = arch_irq_set_state(false);
|
||||
struct irq_percpu* state = percpu_ref(irq_data);
|
||||
if (state->depth++ == 0)
|
||||
state->saved_state = prev;
|
||||
}
|
||||
|
||||
void irq_unlock() {
|
||||
struct irq_percpu* state = percpu_ref(irq_data);
|
||||
if (--state->depth == 0 && state->saved_state)
|
||||
arch_irq_set_state(true);
|
||||
}
|
||||
|
||||
void irq_line_program(struct irq_line* line, enum irq_trigger_mode trigger, enum irq_polarity polarity) {
|
||||
irq_lock();
|
||||
line->mode = line->set_config(line, trigger, polarity);
|
||||
irq_unlock();
|
||||
}
|
||||
|
||||
void irq_line_raise(struct irq_line* line) {
|
||||
assert(!arch_irq_get_state(), "Raised an IRQ line with interrupts enabled");
|
||||
|
||||
irq_lock();
|
||||
line->is_busy = true;
|
||||
|
||||
bool claimed = false;
|
||||
struct irq_handler* handler;
|
||||
list_foreach(handler, &line->handlers, next) {
|
||||
if (handler->raise(handler) == IRQ_STATUS_HANDLED)
|
||||
claimed = true;
|
||||
}
|
||||
|
||||
if (!claimed)
|
||||
log("irq: Spurious interrupt\n");
|
||||
|
||||
if ((line->mode & IRQ_MODE_EOI) && line->eoi)
|
||||
line->eoi(line);
|
||||
|
||||
line->is_busy = false;
|
||||
irq_unlock();
|
||||
}
|
||||
|
||||
void irq_line_attach(struct irq_line* line, struct irq_handler* handler) {
|
||||
irq_lock();
|
||||
list_insert_head(&line->handlers, handler, next);
|
||||
irq_unlock();
|
||||
}
|
||||
-135
@@ -1,135 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/elf.h>
|
||||
#include <zinnia/hashmap.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/ksyms.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <bits/elf.h>
|
||||
#include <string.h>
|
||||
|
||||
[[__visibility("hidden")]]
|
||||
extern const elf_sym_t __ld_dynsym_start[];
|
||||
[[__visibility("hidden")]]
|
||||
extern const elf_sym_t __ld_dynsym_end[];
|
||||
[[__visibility("hidden")]]
|
||||
extern const char __ld_dynstr_start[];
|
||||
[[__visibility("hidden")]]
|
||||
extern const char __ld_dynstr_end[];
|
||||
[[__visibility("hidden")]]
|
||||
extern const elf_dyn_t _DYNAMIC[];
|
||||
|
||||
static uintptr_t slide;
|
||||
static bool slide_known;
|
||||
static const char* kernel_name;
|
||||
static hashmap(const char*, uintptr_t) symbols;
|
||||
|
||||
static uint64_t sym_hash(const void* key, size_t) {
|
||||
const char* name = *(const char* const*)key;
|
||||
return hashmap_hash_default(name, strlen(name));
|
||||
}
|
||||
|
||||
static bool sym_eq(const void* a, const void* b, size_t) {
|
||||
return !strcmp(*(const char* const*)a, *(const char* const*)b);
|
||||
}
|
||||
|
||||
static size_t num_syms() {
|
||||
return __ld_dynsym_end - __ld_dynsym_start;
|
||||
}
|
||||
|
||||
static const char* dynstr(uint64_t offset) {
|
||||
if (offset >= (size_t)(__ld_dynstr_end - __ld_dynstr_start))
|
||||
return nullptr;
|
||||
return &__ld_dynstr_start[offset];
|
||||
}
|
||||
|
||||
static uintptr_t kernel_slide() {
|
||||
if (slide_known)
|
||||
return slide;
|
||||
|
||||
for (size_t i = 0; i < num_syms(); i++) {
|
||||
const char* name = dynstr(__ld_dynsym_start[i].st_name);
|
||||
if (name != nullptr && !strcmp(name, "ksym_lookup")) {
|
||||
slide = (uintptr_t)ksym_lookup - __ld_dynsym_start[i].st_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
slide_known = true;
|
||||
return slide;
|
||||
}
|
||||
|
||||
void ksym_init() {
|
||||
const uintptr_t base = kernel_slide();
|
||||
|
||||
for (const elf_dyn_t* dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) {
|
||||
if (dyn->d_tag == DT_SONAME)
|
||||
kernel_name = dynstr(dyn->d_un.d_val);
|
||||
}
|
||||
|
||||
hashmap_init(&symbols);
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < num_syms(); i++) {
|
||||
const elf_sym_t* sym = &__ld_dynsym_start[i];
|
||||
if (sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
|
||||
const char* name = dynstr(sym->st_name);
|
||||
if (name == nullptr || *name == 0)
|
||||
continue;
|
||||
|
||||
const errno_t status = hashmap_insert(&symbols, name, sym->st_value + base, sym_hash, sym_eq);
|
||||
assert(status == 0, "Failed to register kernel symbol \"%s\" (%i)", name, status);
|
||||
count++;
|
||||
}
|
||||
|
||||
log("ksyms: Registered %zu kernel symbols\n", count);
|
||||
}
|
||||
|
||||
bool ksym_find(const char* name, uintptr_t* addr) {
|
||||
uintptr_t* value = hashmap_get(&symbols, name, sym_hash, sym_eq);
|
||||
if (value == nullptr)
|
||||
return false;
|
||||
*addr = *value;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* ksym_kernel_name() {
|
||||
return kernel_name;
|
||||
}
|
||||
|
||||
bool ksym_lookup(uintptr_t addr, const char** name, size_t* offset) {
|
||||
addr -= kernel_slide();
|
||||
for (size_t i = 0; i < num_syms(); i++) {
|
||||
const elf_sym_t* sym = &__ld_dynsym_start[i];
|
||||
if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_shndx == SHN_UNDEF)
|
||||
continue;
|
||||
if (addr >= sym->st_value && addr - sym->st_value < sym->st_size) {
|
||||
const char* sym_name = dynstr(sym->st_name);
|
||||
if (sym_name == nullptr)
|
||||
return false;
|
||||
*name = sym_name;
|
||||
*offset = addr - sym->st_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ksym_format(uintptr_t addr, char* buf, size_t size) {
|
||||
const char* name;
|
||||
size_t offset;
|
||||
if (ksym_lookup(addr, &name, &offset))
|
||||
ksnprintf(buf, size, "%s+0x%zx", name, offset);
|
||||
else
|
||||
ksnprintf(buf, size, "<\?\?\?>");
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
static void ksyms_stage_init(struct boot_info*) {
|
||||
ksym_init();
|
||||
}
|
||||
INIT_TASK(ksyms_stage, "core.ksyms", ksyms_stage_init);
|
||||
INIT_DEPENDS(ksyms_stage, mem_stage);
|
||||
@@ -1,93 +0,0 @@
|
||||
// Compiler runtime support.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t __udivdi3(uint64_t dividend, uint64_t divisor) {
|
||||
if (divisor == 0)
|
||||
return 0;
|
||||
|
||||
if (dividend < divisor)
|
||||
return 0;
|
||||
|
||||
unsigned shift = __builtin_clzll(divisor) - __builtin_clzll(dividend);
|
||||
|
||||
divisor <<= shift;
|
||||
|
||||
uint64_t quotient = 0;
|
||||
|
||||
for (;;) {
|
||||
if (dividend >= divisor) {
|
||||
dividend -= divisor;
|
||||
quotient |= (uint64_t)1 << shift;
|
||||
}
|
||||
|
||||
if (shift == 0)
|
||||
break;
|
||||
|
||||
shift--;
|
||||
divisor >>= 1;
|
||||
}
|
||||
|
||||
return quotient;
|
||||
}
|
||||
|
||||
static unsigned clz128(__uint128_t value) {
|
||||
uint64_t high = (uint64_t)(value >> 64);
|
||||
|
||||
if (high != 0)
|
||||
return (unsigned)__builtin_clzll(high);
|
||||
|
||||
return 64 + (unsigned)__builtin_clzll((uint64_t)value);
|
||||
}
|
||||
|
||||
__uint128_t __udivmodti4(__uint128_t dividend, __uint128_t divisor, __uint128_t* remainder) {
|
||||
if (divisor == 0) {
|
||||
if (remainder)
|
||||
*remainder = dividend;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dividend < divisor) {
|
||||
if (remainder)
|
||||
*remainder = dividend;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned shift = clz128(divisor) - clz128(dividend);
|
||||
|
||||
divisor <<= shift;
|
||||
|
||||
__uint128_t quotient = 0;
|
||||
|
||||
for (;;) {
|
||||
if (dividend >= divisor) {
|
||||
dividend -= divisor;
|
||||
quotient |= (__uint128_t)1 << shift;
|
||||
}
|
||||
|
||||
if (shift == 0)
|
||||
break;
|
||||
|
||||
shift--;
|
||||
divisor >>= 1;
|
||||
}
|
||||
|
||||
if (remainder)
|
||||
*remainder = dividend;
|
||||
|
||||
return quotient;
|
||||
}
|
||||
|
||||
__uint128_t __udivti3(__uint128_t dividend, __uint128_t divisor) {
|
||||
return __udivmodti4(dividend, divisor, nullptr);
|
||||
}
|
||||
|
||||
__uint128_t __umodti3(__uint128_t dividend, __uint128_t divisor) {
|
||||
__uint128_t remainder;
|
||||
|
||||
__udivmodti4(dividend, divisor, &remainder);
|
||||
|
||||
return remainder;
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
#include <zinnia/stdlib.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;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
menu "Memory Management options"
|
||||
|
||||
config HAVE_MMU
|
||||
bool
|
||||
config HAVE_MMU_GENERIC
|
||||
bool
|
||||
|
||||
endmenu
|
||||
@@ -1,17 +0,0 @@
|
||||
kernel_sources += files(
|
||||
'fault.c',
|
||||
'init.c',
|
||||
'pmm.c',
|
||||
'slab.c',
|
||||
'stack.c',
|
||||
'usercopy.c',
|
||||
'virt.c',
|
||||
'vma.c',
|
||||
'vmo.c',
|
||||
)
|
||||
|
||||
if kconfig.get('CONFIG_HAVE_MMU', 'n') == 'y'
|
||||
kernel_sources += files(
|
||||
'pmap.c',
|
||||
)
|
||||
endif
|
||||
@@ -1,60 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/stack.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/virt.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <arch/mem.h>
|
||||
|
||||
errno_t stack_init(struct stack* ks, size_t size) {
|
||||
if (!ks || size == 0)
|
||||
return EINVAL;
|
||||
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
size = ALIGN_UP(size, page_size);
|
||||
|
||||
uintptr_t va = vm_alloc(size + page_size);
|
||||
if (!va)
|
||||
return ENOMEM;
|
||||
|
||||
phys_t phys;
|
||||
errno_t status = mem_phys_alloc(size / page_size, 0, &phys);
|
||||
if (status)
|
||||
goto err_vm;
|
||||
|
||||
const uintptr_t base = va + page_size;
|
||||
size_t mapped = 0;
|
||||
for (; mapped < size; mapped += page_size) {
|
||||
status = pmap_map(&kernel_vma.pmap, base + mapped, phys + mapped, PTE_READ | PTE_WRITE, CACHE_WRITE_BACK);
|
||||
if (status)
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
ks->base = base;
|
||||
ks->phys = phys;
|
||||
ks->size = size;
|
||||
return 0;
|
||||
|
||||
err_map:
|
||||
for (size_t i = 0; i < mapped; i += page_size)
|
||||
pmap_unmap(&kernel_vma.pmap, base + i);
|
||||
mem_phys_free(phys, size / page_size);
|
||||
err_vm:
|
||||
vm_free(va, size + page_size);
|
||||
return status;
|
||||
}
|
||||
|
||||
void stack_destroy(struct stack* ks) {
|
||||
if (!ks || !ks->base)
|
||||
return;
|
||||
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
for (size_t i = 0; i < ks->size; i += page_size)
|
||||
pmap_unmap(&kernel_vma.pmap, ks->base + i);
|
||||
|
||||
mem_phys_free(ks->phys, ks->size / page_size);
|
||||
vm_free(ks->base - page_size, ks->size + page_size);
|
||||
|
||||
*ks = (struct stack){0};
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/usercopy.h>
|
||||
#include <arch/mem.h>
|
||||
#include <arch/usercopy.h>
|
||||
|
||||
static bool is_user_range(uintptr_t addr, size_t len) {
|
||||
if (len == 0)
|
||||
return arch_mem_addr_is_user(addr);
|
||||
return addr + len > addr && arch_mem_addr_is_user(addr) && arch_mem_addr_is_user(addr + len - 1);
|
||||
}
|
||||
|
||||
bool usercopy_read(void* dst, const __user void* src, size_t len) {
|
||||
if (!is_user_range((uintptr_t)src, len))
|
||||
return false;
|
||||
|
||||
arch_usercopy_allow(true);
|
||||
bool success = arch_usercopy_read(dst, src, len, &percpu_get()->sched.current->usercopy_region);
|
||||
arch_usercopy_allow(false);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool usercopy_write(__user void* dst, const void* src, size_t len) {
|
||||
if (!is_user_range((uintptr_t)dst, len))
|
||||
return false;
|
||||
|
||||
arch_usercopy_allow(true);
|
||||
bool success = arch_usercopy_write(dst, src, len, &percpu_get()->sched.current->usercopy_region);
|
||||
arch_usercopy_allow(false);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool usercopy_strlen(const __user char* str, size_t max, size_t* len) {
|
||||
if (!arch_mem_addr_is_user((uintptr_t)str))
|
||||
return false;
|
||||
|
||||
size_t unused;
|
||||
if (len == nullptr)
|
||||
len = &unused;
|
||||
|
||||
arch_usercopy_allow(true);
|
||||
bool success = arch_usercopy_strlen(str, max, len, &percpu_get()->sched.current->usercopy_region);
|
||||
arch_usercopy_allow(false);
|
||||
return success;
|
||||
}
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/mman.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <stdckdint.h>
|
||||
|
||||
// Try to create a new, empty virtual address space.
|
||||
errno_t vma_new(struct vma** out) {
|
||||
debug_assert(out != nullptr, "Missing out");
|
||||
|
||||
errno_t res;
|
||||
if (!out)
|
||||
return EINVAL;
|
||||
|
||||
struct vma* vmspace = mem_alloc(sizeof(struct vma), 0);
|
||||
if (!vmspace)
|
||||
return ENOMEM;
|
||||
|
||||
res = pmap_new_user(&vmspace->pmap, 0);
|
||||
if (res)
|
||||
goto err1;
|
||||
|
||||
tailq_init(&vmspace->mappings);
|
||||
|
||||
*out = vmspace;
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
mem_free(vmspace);
|
||||
err0:
|
||||
return res;
|
||||
}
|
||||
|
||||
// Delete a virtual address space.
|
||||
void vma_free(struct vma* v) {
|
||||
struct vma_mapping* mapping;
|
||||
while ((mapping = tailq_first(&v->mappings)) != nullptr) {
|
||||
tailq_remove(&v->mappings, mapping, link);
|
||||
vmo_unref(mapping->object);
|
||||
mem_free(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
errno_t vma_unmap(struct vma* v, uintptr_t vaddr, size_t size) {
|
||||
panic("TODO");
|
||||
}
|
||||
|
||||
errno_t vma_protect(struct vma* v, uintptr_t vaddr, size_t size, enum prot_flags flags) {
|
||||
panic("TODO");
|
||||
}
|
||||
|
||||
static struct vma_mapping* find_mapping(struct vma* v, size_t page) {
|
||||
for (struct vma_mapping* m = tailq_first(&v->mappings); m; m = tailq_next(m, link)) {
|
||||
if (page >= m->start_page && page < m->end_page)
|
||||
return m;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool can_access(enum prot_flags prot, const struct vma_fault_info* info) {
|
||||
if (info->caused_by_write)
|
||||
return prot & PROT_WRITE;
|
||||
if (info->caused_by_fetch)
|
||||
return prot & PROT_EXEC;
|
||||
return prot & (PROT_READ | PROT_WRITE);
|
||||
}
|
||||
|
||||
static enum pte_flags prot_to_pte(struct vma* v, enum prot_flags prot) {
|
||||
enum pte_flags pf = 0;
|
||||
if (prot & PROT_READ)
|
||||
pf |= PTE_READ;
|
||||
if (prot & PROT_WRITE)
|
||||
pf |= PTE_WRITE;
|
||||
if (prot & PROT_EXEC)
|
||||
pf |= PTE_EXEC;
|
||||
if (v->pmap.is_user)
|
||||
pf |= PTE_USER;
|
||||
return pf;
|
||||
}
|
||||
|
||||
bool vma_page_fault(struct vma* v, const struct vma_fault_info* info) {
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
const size_t faulty_page = info->addr / page_size;
|
||||
|
||||
spin_lock(&v->lock);
|
||||
struct vma_mapping* mapping = find_mapping(v, faulty_page);
|
||||
if (!mapping || !can_access(mapping->prot, info)) {
|
||||
spin_unlock(&v->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct vmo* object = mapping->object;
|
||||
const size_t object_page = (faulty_page - mapping->start_page) + mapping->offset_page;
|
||||
const enum prot_flags prot = mapping->prot;
|
||||
vmo_ref(object);
|
||||
spin_unlock(&v->lock);
|
||||
|
||||
struct page* page;
|
||||
if (object->get_page(object, object_page, &page)) {
|
||||
vmo_unref(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
spin_lock(&v->lock);
|
||||
mapping = find_mapping(v, faulty_page);
|
||||
const bool still_valid = mapping && mapping->object == object &&
|
||||
(faulty_page - mapping->start_page) + mapping->offset_page == object_page && can_access(mapping->prot, info);
|
||||
|
||||
if (still_valid) {
|
||||
errno_t status =
|
||||
pmap_map(&v->pmap, faulty_page * page_size, vm_page_addr(page), prot_to_pte(v, prot), CACHE_ANY);
|
||||
assert(status == 0, "Failed to map a demand-loaded page\n");
|
||||
}
|
||||
spin_unlock(&v->lock);
|
||||
|
||||
vmo_unref(object);
|
||||
return true;
|
||||
}
|
||||
|
||||
errno_t vma_map(
|
||||
struct vma* vma,
|
||||
struct vmo* vmo,
|
||||
uintptr_t addr,
|
||||
size_t len,
|
||||
enum prot_flags flags,
|
||||
uintptr_t vmo_offset
|
||||
) {
|
||||
if (!vma || !vmo)
|
||||
return EINVAL;
|
||||
|
||||
uintptr_t end;
|
||||
if (ckd_add(&end, addr, len))
|
||||
return ENOMEM;
|
||||
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
if (addr % page_size != vmo_offset % page_size)
|
||||
return EINVAL;
|
||||
|
||||
struct vma_mapping* mapping = mem_alloc(sizeof(struct vma_mapping), 0);
|
||||
if (!mapping)
|
||||
return ENOMEM;
|
||||
|
||||
vmo_ref(vmo);
|
||||
mapping->start_page = addr / page_size;
|
||||
mapping->end_page = ALIGN_UP(end, page_size) / page_size;
|
||||
mapping->offset_page = vmo_offset / page_size;
|
||||
mapping->object = vmo;
|
||||
mapping->prot = flags;
|
||||
|
||||
spin_lock(&vma->lock);
|
||||
tailq_insert_head(&vma->mappings, mapping, link);
|
||||
spin_unlock(&vma->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
-613
@@ -1,613 +0,0 @@
|
||||
#define pr_fmt(fmt) "module: " fmt
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/elf.h>
|
||||
#include <zinnia/ksyms.h>
|
||||
#include <zinnia/module.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/virt.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <bits/elf.h>
|
||||
#include <stdckdint.h>
|
||||
#include <string.h>
|
||||
|
||||
extern errno_t (*__ld_mod_entry_start[])(struct module*);
|
||||
extern errno_t (*__ld_mod_entry_end[])(struct module*);
|
||||
|
||||
static struct spinlock module_lock;
|
||||
static tailq_head(struct module) module_list;
|
||||
|
||||
static bool in_range(size_t total, uint64_t offset, uint64_t size) {
|
||||
return offset <= total && size <= total - offset;
|
||||
}
|
||||
|
||||
static void* image_ptr(struct module* mod, uint64_t vaddr, uint64_t size) {
|
||||
uintptr_t addr, end;
|
||||
if (ckd_add(&addr, mod->bias, vaddr) || ckd_add(&end, addr, size))
|
||||
return nullptr;
|
||||
|
||||
for (size_t i = 0; i < mod->num_segments; i++) {
|
||||
const struct module_segment* seg = &mod->segments[i];
|
||||
if (addr >= seg->virt && end <= seg->virt + seg->size)
|
||||
return (void*)addr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char* image_cstr(struct module* mod, uint64_t vaddr, uint64_t size) {
|
||||
const char* str = image_ptr(mod, vaddr, size);
|
||||
if (str == nullptr || size == 0 || strnlen(str, size) >= size)
|
||||
return nullptr;
|
||||
return str;
|
||||
}
|
||||
|
||||
static const char* table_str(const char* table, size_t size, uint64_t offset) {
|
||||
if (offset >= size)
|
||||
return nullptr;
|
||||
const char* str = table + offset;
|
||||
if (strnlen(str, size - offset) >= size - offset)
|
||||
return nullptr;
|
||||
return str;
|
||||
}
|
||||
|
||||
static const char* sym_name(struct module* mod, uint64_t offset) {
|
||||
return table_str(mod->strtab, mod->strtab_size, offset);
|
||||
}
|
||||
|
||||
static struct module* find_locked(const char* name) {
|
||||
for (struct module* m = tailq_first(&module_list); m != nullptr; m = tailq_next(m, link)) {
|
||||
if (!strcmp(m->name, name))
|
||||
return m;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void module_init(struct boot_info*) {
|
||||
spin_init(&module_lock);
|
||||
tailq_init(&module_list);
|
||||
|
||||
for (auto start = __ld_mod_entry_start; start < __ld_mod_entry_end; start += 1) {
|
||||
errno_t mod_status = (*start)(nullptr);
|
||||
assert(mod_status == 0, "Built-in entry point returned %i", mod_status);
|
||||
}
|
||||
}
|
||||
INIT_TASK(module_stage, "core.module", module_init);
|
||||
INIT_DEPENDS(module_stage, sched_stage, ksyms_stage);
|
||||
|
||||
static errno_t resolve_symbol(struct module* mod, uint32_t index, uintptr_t* out) {
|
||||
if (index >= mod->num_syms)
|
||||
return ENOEXEC;
|
||||
|
||||
const elf_sym_t* sym = &((const elf_sym_t*)mod->symtab)[index];
|
||||
if (sym->st_shndx != SHN_UNDEF) {
|
||||
*out = mod->bias + sym->st_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* name = sym_name(mod, sym->st_name);
|
||||
if (name == nullptr)
|
||||
return ENOEXEC;
|
||||
|
||||
if (ksym_find(name, out))
|
||||
return 0;
|
||||
|
||||
if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
|
||||
*out = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_err("%s: Unresolved symbol \"%s\"\n", mod->name, name);
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
static errno_t store_word(struct module* mod, uint64_t vaddr, uintptr_t value) {
|
||||
uintptr_t* where = image_ptr(mod, vaddr, sizeof(uintptr_t));
|
||||
if (where == nullptr)
|
||||
return ENOEXEC;
|
||||
*where = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static errno_t relocate_relative(struct module* mod, uint64_t vaddr) {
|
||||
uintptr_t* where = image_ptr(mod, vaddr, sizeof(uintptr_t));
|
||||
if (where == nullptr)
|
||||
return ENOEXEC;
|
||||
*where += mod->bias;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static errno_t apply_rela(struct module* mod, uint64_t addr, uint64_t size, uint64_t entsize) {
|
||||
if (entsize == 0)
|
||||
entsize = sizeof(elf_rela_t);
|
||||
if (entsize < sizeof(elf_rela_t))
|
||||
return ENOEXEC;
|
||||
|
||||
const uint8_t* table = image_ptr(mod, addr, size);
|
||||
if (table == nullptr)
|
||||
return ENOEXEC;
|
||||
|
||||
for (uint64_t off = 0; off + sizeof(elf_rela_t) <= size; off += entsize) {
|
||||
elf_rela_t rela;
|
||||
memcpy(&rela, table + off, sizeof(rela));
|
||||
|
||||
errno_t status = 0;
|
||||
uintptr_t value;
|
||||
switch (ELF_R_TYPE(rela.r_info)) {
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_64:
|
||||
case R_GLOB_DAT:
|
||||
case R_JUMP_SLOT:
|
||||
status = resolve_symbol(mod, ELF_R_SYM(rela.r_info), &value);
|
||||
if (status == 0)
|
||||
status = store_word(mod, rela.r_offset, value + rela.r_addend);
|
||||
break;
|
||||
case R_RELATIVE:
|
||||
status = store_word(mod, rela.r_offset, mod->bias + rela.r_addend);
|
||||
break;
|
||||
default:
|
||||
log_err("%s: Unsupported relocation type %u\n", mod->name, ELF_R_TYPE(rela.r_info));
|
||||
status = ENOEXEC;
|
||||
break;
|
||||
}
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static errno_t apply_relr(struct module* mod, uint64_t addr, uint64_t size, uint64_t entsize) {
|
||||
if (entsize != 0 && entsize != sizeof(elf_relr_t))
|
||||
return ENOEXEC;
|
||||
|
||||
const elf_relr_t* table = image_ptr(mod, addr, size);
|
||||
if (table == nullptr)
|
||||
return ENOEXEC;
|
||||
|
||||
uint64_t where = 0;
|
||||
for (size_t i = 0; i < size / sizeof(elf_relr_t); i++) {
|
||||
elf_relr_t entry = table[i];
|
||||
errno_t status;
|
||||
|
||||
if ((entry & 1) == 0) {
|
||||
status = relocate_relative(mod, entry);
|
||||
if (status)
|
||||
return status;
|
||||
where = entry + sizeof(elf_relr_t);
|
||||
} else {
|
||||
for (size_t bit = 0; (entry >>= 1) != 0; bit++) {
|
||||
if ((entry & 1) == 0)
|
||||
continue;
|
||||
status = relocate_relative(mod, where + bit * sizeof(elf_relr_t));
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
where += (sizeof(elf_relr_t) * 8 - 1) * sizeof(elf_relr_t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void module_free(struct module* mod) {
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
|
||||
mem_free(mod->cmdline);
|
||||
|
||||
for (size_t i = 0; i < mod->num_segments; i++) {
|
||||
const struct module_segment* seg = &mod->segments[i];
|
||||
for (size_t off = 0; off < seg->size; off += page_size)
|
||||
pmap_unmap(&kernel_vma.pmap, seg->virt + off);
|
||||
mem_phys_free(seg->phys, seg->size / page_size);
|
||||
}
|
||||
mem_free(mod->segments);
|
||||
|
||||
if (mod->base != 0)
|
||||
vm_free(mod->base, mod->size);
|
||||
|
||||
mem_free(mod);
|
||||
}
|
||||
|
||||
static enum pte_flags prot_from_phdr(const elf_phdr_t* phdr) {
|
||||
enum pte_flags prot = 0;
|
||||
if (phdr->p_flags & PF_R)
|
||||
prot |= PTE_READ;
|
||||
if (phdr->p_flags & PF_W)
|
||||
prot |= PTE_WRITE;
|
||||
if (phdr->p_flags & PF_X)
|
||||
prot |= PTE_EXEC;
|
||||
return prot;
|
||||
}
|
||||
|
||||
static errno_t read_headers(const void* data, size_t length, elf_ehdr_t* ehdr, elf_phdr_t** out) {
|
||||
const uint8_t* file = data;
|
||||
|
||||
if (data == nullptr || length < sizeof(elf_ehdr_t))
|
||||
return ENOEXEC;
|
||||
|
||||
memcpy(ehdr, file, sizeof(*ehdr));
|
||||
|
||||
if (memcmp(ehdr->e_ident, ELF_MAG, sizeof(ELF_MAG)) != 0 || ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
|
||||
ehdr->e_ident[EI_CLASS] != ELF_ARCH_CLASS || ehdr->e_ident[EI_DATA] != ELF_ARCH_DATA ||
|
||||
ehdr->e_machine != ELF_ARCH_MACHINE || ehdr->e_type != ET_DYN)
|
||||
return ENOEXEC;
|
||||
|
||||
if (ehdr->e_phentsize < sizeof(elf_phdr_t) || ehdr->e_phnum == 0 ||
|
||||
!in_range(length, ehdr->e_phoff, (uint64_t)ehdr->e_phnum * ehdr->e_phentsize))
|
||||
return ENOEXEC;
|
||||
|
||||
elf_phdr_t* phdrs = mem_alloc(ehdr->e_phnum * sizeof(elf_phdr_t), 0);
|
||||
if (phdrs == nullptr)
|
||||
return ENOMEM;
|
||||
for (size_t i = 0; i < ehdr->e_phnum; i++)
|
||||
memcpy(&phdrs[i], file + ehdr->e_phoff + i * ehdr->e_phentsize, sizeof(elf_phdr_t));
|
||||
|
||||
*out = phdrs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno_t module_load(const void* data, size_t length, const char* cmdline, struct module** out) {
|
||||
const uint8_t* file = data;
|
||||
const size_t page_size = arch_mem_page_size();
|
||||
|
||||
elf_ehdr_t ehdr;
|
||||
elf_phdr_t* phdrs;
|
||||
errno_t header_status = read_headers(data, length, &ehdr, &phdrs);
|
||||
if (header_status)
|
||||
return header_status;
|
||||
|
||||
struct module* mod = mem_alloc(sizeof(struct module), 0);
|
||||
if (mod == nullptr) {
|
||||
mem_free(phdrs);
|
||||
return ENOMEM;
|
||||
}
|
||||
mod->version = "";
|
||||
mod->author = "";
|
||||
mod->desc = "";
|
||||
mod->license = "";
|
||||
|
||||
errno_t status = ENOEXEC;
|
||||
bool listed = false;
|
||||
|
||||
uintptr_t load_min = UINTPTR_MAX;
|
||||
uintptr_t load_end = 0;
|
||||
size_t num_loads = 0;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
const elf_phdr_t* phdr = &phdrs[i];
|
||||
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0)
|
||||
continue;
|
||||
if (phdr->p_filesz > phdr->p_memsz || !in_range(length, phdr->p_offset, phdr->p_filesz))
|
||||
goto fail;
|
||||
|
||||
uintptr_t end;
|
||||
if (ckd_add(&end, phdr->p_vaddr, phdr->p_memsz) || end > UINTPTR_MAX - page_size)
|
||||
goto fail;
|
||||
|
||||
const uintptr_t start = ALIGN_DOWN(phdr->p_vaddr, page_size);
|
||||
if (start < load_end)
|
||||
goto fail;
|
||||
if (load_min == UINTPTR_MAX)
|
||||
load_min = start;
|
||||
load_end = ALIGN_UP(end, page_size);
|
||||
num_loads++;
|
||||
}
|
||||
|
||||
if (num_loads == 0)
|
||||
goto fail;
|
||||
|
||||
status = ENOMEM;
|
||||
mod->segments = mem_alloc(num_loads * sizeof(struct module_segment), 0);
|
||||
if (mod->segments == nullptr)
|
||||
goto fail;
|
||||
|
||||
mod->base = vm_alloc(load_end - load_min);
|
||||
if (mod->base == 0)
|
||||
goto fail;
|
||||
mod->size = load_end - load_min;
|
||||
mod->bias = mod->base - load_min;
|
||||
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
const elf_phdr_t* phdr = &phdrs[i];
|
||||
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0)
|
||||
continue;
|
||||
|
||||
const uintptr_t start = ALIGN_DOWN(phdr->p_vaddr, page_size);
|
||||
const size_t size = ALIGN_UP(phdr->p_vaddr + phdr->p_memsz, page_size) - start;
|
||||
|
||||
phys_t phys;
|
||||
status = mem_phys_alloc(size / page_size, 0, &phys);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
struct module_segment* seg = &mod->segments[mod->num_segments++];
|
||||
*seg = (struct module_segment){.phys = phys, .virt = mod->bias + start, .size = size};
|
||||
|
||||
for (size_t off = 0; off < size; off += page_size) {
|
||||
status = pmap_map(&kernel_vma.pmap, seg->virt + off, phys + off, PTE_READ | PTE_WRITE, CACHE_WRITE_BACK);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy((void*)(mod->bias + phdr->p_vaddr), file + phdr->p_offset, phdr->p_filesz);
|
||||
}
|
||||
|
||||
status = ENOEXEC;
|
||||
|
||||
const elf_dyn_t* dyn = nullptr;
|
||||
size_t num_dyn = 0;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
if (phdrs[i].p_type != PT_DYNAMIC)
|
||||
continue;
|
||||
dyn = image_ptr(mod, phdrs[i].p_vaddr, phdrs[i].p_memsz);
|
||||
num_dyn = phdrs[i].p_memsz / sizeof(elf_dyn_t);
|
||||
break;
|
||||
}
|
||||
if (dyn == nullptr)
|
||||
goto fail;
|
||||
|
||||
uint64_t dt_strtab = 0, dt_strsz = 0, dt_symtab = 0, dt_hash = 0, dt_soname = 0;
|
||||
uint64_t dt_rela = 0, dt_relasz = 0, dt_relaent = 0;
|
||||
uint64_t dt_jmprel = 0, dt_pltrelsz = 0, dt_pltrel = DT_RELA;
|
||||
uint64_t dt_relr = 0, dt_relrsz = 0, dt_relrent = 0;
|
||||
bool has_strtab = false, has_symtab = false, has_hash = false, has_soname = false;
|
||||
bool has_rela = false, has_jmprel = false, has_relr = false, has_needed = false;
|
||||
|
||||
for (size_t i = 0; i < num_dyn && dyn[i].d_tag != DT_NULL; i++) {
|
||||
const uint64_t val = dyn[i].d_un.d_val;
|
||||
switch (dyn[i].d_tag) {
|
||||
case DT_STRTAB:
|
||||
dt_strtab = val;
|
||||
has_strtab = true;
|
||||
break;
|
||||
case DT_STRSZ:
|
||||
dt_strsz = val;
|
||||
break;
|
||||
case DT_SYMTAB:
|
||||
dt_symtab = val;
|
||||
has_symtab = true;
|
||||
break;
|
||||
case DT_HASH:
|
||||
dt_hash = val;
|
||||
has_hash = true;
|
||||
break;
|
||||
case DT_SONAME:
|
||||
dt_soname = val;
|
||||
has_soname = true;
|
||||
break;
|
||||
case DT_NEEDED:
|
||||
has_needed = true;
|
||||
break;
|
||||
case DT_RELA:
|
||||
dt_rela = val;
|
||||
has_rela = true;
|
||||
break;
|
||||
case DT_RELASZ:
|
||||
dt_relasz = val;
|
||||
break;
|
||||
case DT_RELAENT:
|
||||
dt_relaent = val;
|
||||
break;
|
||||
case DT_JMPREL:
|
||||
dt_jmprel = val;
|
||||
has_jmprel = true;
|
||||
break;
|
||||
case DT_PLTRELSZ:
|
||||
dt_pltrelsz = val;
|
||||
break;
|
||||
case DT_PLTREL:
|
||||
dt_pltrel = val;
|
||||
break;
|
||||
case DT_RELR:
|
||||
dt_relr = val;
|
||||
has_relr = true;
|
||||
break;
|
||||
case DT_RELRSZ:
|
||||
dt_relrsz = val;
|
||||
break;
|
||||
case DT_RELRENT:
|
||||
dt_relrent = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_strtab || !has_symtab || !has_hash || !has_soname || dt_strsz == 0 || dt_pltrel != DT_RELA)
|
||||
goto fail;
|
||||
|
||||
const elf_hash_t* hash = image_ptr(mod, dt_hash, sizeof(elf_hash_t));
|
||||
if (hash == nullptr)
|
||||
goto fail;
|
||||
|
||||
mod->num_syms = hash->n_chain;
|
||||
mod->symtab = image_ptr(mod, dt_symtab, (uint64_t)mod->num_syms * sizeof(elf_sym_t));
|
||||
mod->strtab = image_ptr(mod, dt_strtab, dt_strsz);
|
||||
mod->strtab_size = dt_strsz;
|
||||
if (mod->symtab == nullptr || mod->strtab == nullptr) {
|
||||
mod->num_syms = 0;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mod->name = sym_name(mod, dt_soname);
|
||||
if (mod->name == nullptr || *mod->name == 0)
|
||||
goto fail;
|
||||
|
||||
for (size_t i = 0; has_needed && i < num_dyn && dyn[i].d_tag != DT_NULL; i++) {
|
||||
if (dyn[i].d_tag != DT_NEEDED)
|
||||
continue;
|
||||
|
||||
// TODO: Modules can technically claim they have dependencies on other modules, but we don't support that yet.
|
||||
const char* needed = sym_name(mod, dyn[i].d_un.d_val);
|
||||
const char* kernel_name = ksym_kernel_name();
|
||||
if (needed == nullptr || kernel_name == nullptr || strcmp(needed, kernel_name) != 0) {
|
||||
log_err("%s: Modules can only depend on the kernel, not \"%s\"\n", mod->name, needed ? needed : "?");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_relr) {
|
||||
status = apply_relr(mod, dt_relr, dt_relrsz, dt_relrent);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
if (has_rela) {
|
||||
status = apply_rela(mod, dt_rela, dt_relasz, dt_relaent);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
if (has_jmprel) {
|
||||
status = apply_rela(mod, dt_jmprel, dt_pltrelsz, dt_relaent);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spin_lock(&module_lock);
|
||||
if (find_locked(mod->name) != nullptr) {
|
||||
spin_unlock(&module_lock);
|
||||
status = EEXIST;
|
||||
goto fail;
|
||||
}
|
||||
tailq_insert_tail(&module_list, mod, link);
|
||||
listed = true;
|
||||
spin_unlock(&module_lock);
|
||||
|
||||
const struct cmdline_option* const* opts_start = nullptr;
|
||||
const struct cmdline_option* const* opts_end = nullptr;
|
||||
|
||||
status = ENOEXEC;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
const elf_phdr_t* phdr = &phdrs[i];
|
||||
const char* str;
|
||||
switch (phdr->p_type) {
|
||||
case PT_MOD_VERSION:
|
||||
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
|
||||
mod->version = str;
|
||||
break;
|
||||
case PT_MOD_AUTHOR:
|
||||
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
|
||||
mod->author = str;
|
||||
break;
|
||||
case PT_MOD_DESC:
|
||||
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
|
||||
mod->desc = str;
|
||||
break;
|
||||
case PT_MOD_LICENSE:
|
||||
if ((str = image_cstr(mod, phdr->p_vaddr, phdr->p_memsz)) != nullptr)
|
||||
mod->license = str;
|
||||
break;
|
||||
case PT_MOD_CMDLINE:
|
||||
if (phdr->p_memsz < sizeof(void*))
|
||||
break;
|
||||
opts_start = image_ptr(mod, phdr->p_vaddr, phdr->p_memsz);
|
||||
if (opts_start == nullptr)
|
||||
goto fail;
|
||||
opts_end = opts_start + (phdr->p_memsz / sizeof(void*));
|
||||
break;
|
||||
case PT_MOD_EXIT: {
|
||||
if (phdr->p_memsz < sizeof(void*))
|
||||
break;
|
||||
const void* slot = image_ptr(mod, phdr->p_vaddr, sizeof(void*));
|
||||
if (slot == nullptr)
|
||||
goto fail;
|
||||
memcpy(&mod->exit, slot, sizeof(void*));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (image_ptr(mod, ehdr.e_entry, 1) == nullptr)
|
||||
goto fail;
|
||||
|
||||
size_t seg_idx = 0;
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
const elf_phdr_t* phdr = &phdrs[i];
|
||||
if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0)
|
||||
continue;
|
||||
|
||||
const struct module_segment* seg = &mod->segments[seg_idx++];
|
||||
const enum pte_flags prot = prot_from_phdr(phdr);
|
||||
for (size_t off = 0; off < seg->size; off += page_size) {
|
||||
pmap_unmap(&kernel_vma.pmap, seg->virt + off);
|
||||
status = pmap_map(&kernel_vma.pmap, seg->virt + off, seg->phys + off, prot, CACHE_WRITE_BACK);
|
||||
if (status)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdline == nullptr)
|
||||
cmdline = "";
|
||||
const size_t cmdline_len = strlen(cmdline);
|
||||
mod->cmdline = mem_alloc(cmdline_len + 1, 0);
|
||||
if (mod->cmdline == nullptr) {
|
||||
status = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
memcpy(mod->cmdline, cmdline, cmdline_len);
|
||||
if (opts_start != nullptr)
|
||||
cmdline_parse_buf(mod->cmdline, cmdline_len, opts_start, opts_end);
|
||||
|
||||
log("Loaded module \"%s\" (%s, %s) at %p\n", mod->name, mod->desc, mod->version, (void*)mod->base);
|
||||
|
||||
errno_t (*entry)(struct module*) = (errno_t (*)(struct module*))(mod->bias + ehdr.e_entry);
|
||||
status = entry(mod);
|
||||
if (status) {
|
||||
log_err("%s: Entry point returned %i\n", mod->name, status);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mem_free(phdrs);
|
||||
|
||||
if (out != nullptr)
|
||||
*out = mod;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (listed) {
|
||||
spin_lock(&module_lock);
|
||||
tailq_remove(&module_list, mod, link);
|
||||
spin_unlock(&module_lock);
|
||||
}
|
||||
module_free(mod);
|
||||
mem_free(phdrs);
|
||||
return status;
|
||||
}
|
||||
|
||||
errno_t module_unload(struct module* mod) {
|
||||
if (mod == nullptr)
|
||||
return EINVAL;
|
||||
|
||||
if (mod->exit == nullptr)
|
||||
return ENOTSUP;
|
||||
|
||||
spin_lock(&module_lock);
|
||||
tailq_remove(&module_list, mod, link);
|
||||
spin_unlock(&module_lock);
|
||||
|
||||
errno_t status = mod->exit(mod);
|
||||
if (status) {
|
||||
log_err("%s: Exit function returned %i\n", mod->name, status);
|
||||
spin_lock(&module_lock);
|
||||
tailq_insert_tail(&module_list, mod, link);
|
||||
spin_unlock(&module_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
log("Unloaded module \"%s\"\n", mod->name);
|
||||
module_free(mod);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct module* module_find(const char* name) {
|
||||
spin_lock(&module_lock);
|
||||
struct module* mod = find_locked(name);
|
||||
spin_unlock(&module_lock);
|
||||
return mod;
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/irq.h>
|
||||
#include <zinnia/ksyms.h>
|
||||
#include <zinnia/panic.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define panic_log(msg, ...) kprintf("[ !!!! ] \e[31m" msg "\e[0m", ##__VA_ARGS__)
|
||||
|
||||
static void print_backtrace() {
|
||||
constexpr size_t max_depth = 32;
|
||||
void** fp = __builtin_frame_address(0);
|
||||
|
||||
panic_log("Backtrace:\n");
|
||||
for (size_t i = 0; i < max_depth; i++) {
|
||||
if (!fp || (uintptr_t)fp < mem_hhdm_base || (uintptr_t)fp % alignof(void*))
|
||||
break;
|
||||
|
||||
void* ret = fp[1];
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
const char* name;
|
||||
size_t offset;
|
||||
// The return address may already point past the end of a noreturn call.
|
||||
if (ksym_lookup((uintptr_t)ret - 1, &name, &offset))
|
||||
panic_log(" #%zu [%p] %s+0x%zx\n", i, ret, name, offset + 1);
|
||||
else
|
||||
panic_log(" #%zu [%p] <\?\?\?>\n", i, ret);
|
||||
|
||||
void** next = fp[0];
|
||||
if (next <= fp)
|
||||
break;
|
||||
fp = next;
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void panic(const char* msg, ...) {
|
||||
// TODO: Stop all other CPUs with IPI.
|
||||
irq_set_state(false);
|
||||
|
||||
struct percpu* cpu = percpu_get();
|
||||
struct task* current = cpu->sched.current;
|
||||
kprintf(
|
||||
"[ !!!! ] \e[31m"
|
||||
"Kernel panic on CPU %zu (T%zu, \"%s\"):\e[0m\n",
|
||||
cpu->id,
|
||||
current ? current->id : 0,
|
||||
current ? current->name : nullptr
|
||||
);
|
||||
|
||||
kprintf("[ !!!! ] \e[31m");
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
kvprintf(msg, args);
|
||||
va_end(args);
|
||||
|
||||
kprintf("\e[0m");
|
||||
|
||||
print_backtrace();
|
||||
panic_log("End of panic message\n");
|
||||
|
||||
initgraph_panic();
|
||||
|
||||
while (true) {
|
||||
irq_wait();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <arch/percpu.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
extern uint8_t __ld_percpu_start[];
|
||||
extern uint8_t __ld_percpu_end[];
|
||||
|
||||
static _Atomic(size_t) num_cpus = 1;
|
||||
|
||||
static size_t max_cpus = SIZE_MAX;
|
||||
static void smp_option(uintptr_t value) {
|
||||
max_cpus = value;
|
||||
}
|
||||
CMDLINE_OPTION("smp", "Maximum number of CPUs to use", smp_option);
|
||||
|
||||
[[__used, __section(".percpu.init")]]
|
||||
struct percpu percpu_bsp = {
|
||||
.self = &percpu_bsp,
|
||||
.id = 0,
|
||||
.online = false,
|
||||
};
|
||||
|
||||
struct percpu* percpu_new() {
|
||||
const size_t size = __ld_percpu_end - __ld_percpu_start;
|
||||
const size_t num_pages = ALIGN_UP(size, arch_mem_page_size()) / arch_mem_page_size();
|
||||
|
||||
// TODO: Map pages past the end of the kernel.
|
||||
// TODO: Document how allocations work.
|
||||
|
||||
phys_t mem;
|
||||
if (mem_phys_alloc(num_pages, 0, &mem))
|
||||
return nullptr;
|
||||
|
||||
struct percpu* cpu = HHDM_PTR(mem);
|
||||
cpu->self = cpu;
|
||||
cpu->id = atomic_fetch_add_explicit(&num_cpus, 1, memory_order_relaxed);
|
||||
cpu->online = false;
|
||||
return cpu;
|
||||
}
|
||||
|
||||
size_t percpu_max_cpus() {
|
||||
return max_cpus;
|
||||
}
|
||||
|
||||
struct percpu* percpu_get() {
|
||||
return arch_percpu_get();
|
||||
}
|
||||
|
||||
void percpu_bsp_init() {
|
||||
arch_percpu_bsp_init();
|
||||
}
|
||||
|
||||
void percpu_init(struct boot_info*) {
|
||||
arch_percpu_init();
|
||||
}
|
||||
INIT_TASK(percpu_stage, "core.percpu", percpu_init);
|
||||
INIT_DEPENDS(percpu_stage, mem_stage);
|
||||
-496
@@ -1,496 +0,0 @@
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/panic.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static void print_pad(log_fn_t callback, void* ctx, char c, size_t count) {
|
||||
for (size_t i = 0; i < count; i++)
|
||||
callback(ctx, &c, 1);
|
||||
}
|
||||
|
||||
void print_fmt(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 left_align = 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;
|
||||
}
|
||||
case '-': {
|
||||
left_align = true;
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
// Width
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
width = 0;
|
||||
while (*fmt >= '0' && *fmt <= '9') {
|
||||
width = width * 10 + (*fmt - '0');
|
||||
fmt++;
|
||||
}
|
||||
|
||||
goto check_fmt;
|
||||
}
|
||||
// Precision
|
||||
case '.': {
|
||||
has_precision = true;
|
||||
fmt++;
|
||||
precision = 0;
|
||||
if (*fmt == '*') {
|
||||
const int32_t arg = va_arg(args, int32_t);
|
||||
if (arg < 0)
|
||||
has_precision = false;
|
||||
else
|
||||
precision = arg;
|
||||
fmt++;
|
||||
} else {
|
||||
while (*fmt >= '0' && *fmt <= '9') {
|
||||
precision = precision * 10 + (*fmt - '0');
|
||||
fmt++;
|
||||
}
|
||||
}
|
||||
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);
|
||||
const size_t pad = width > 1 ? width - 1 : 0;
|
||||
|
||||
if (!left_align)
|
||||
print_pad(callback, ctx, ' ', pad);
|
||||
callback(ctx, &c, sizeof(c));
|
||||
if (left_align)
|
||||
print_pad(callback, ctx, ' ', pad);
|
||||
|
||||
written += 1 + pad;
|
||||
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);
|
||||
const size_t pad = width > len ? width - len : 0;
|
||||
|
||||
if (!left_align)
|
||||
print_pad(callback, ctx, ' ', pad);
|
||||
callback(ctx, str, len);
|
||||
if (left_align)
|
||||
print_pad(callback, ctx, ' ', pad);
|
||||
|
||||
written += len + pad;
|
||||
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:
|
||||
const char* digits = number;
|
||||
char sign = 0;
|
||||
if (digits[0] == '-') {
|
||||
sign = '-';
|
||||
digits++;
|
||||
} else if (force_sign) {
|
||||
sign = '+';
|
||||
} else if (blank_sign) {
|
||||
sign = ' ';
|
||||
}
|
||||
|
||||
const size_t len = strlen(digits);
|
||||
size_t zeros = (has_precision && precision > len) ? precision - len : 0;
|
||||
const size_t total = (sign ? 1 : 0) + (write_prefix ? 2 : 0) + zeros + len;
|
||||
size_t pad = width > total ? width - total : 0;
|
||||
if (pad_zero && !left_align && !has_precision) {
|
||||
zeros += pad;
|
||||
pad = 0;
|
||||
}
|
||||
|
||||
if (!left_align)
|
||||
print_pad(callback, ctx, ' ', pad);
|
||||
if (sign)
|
||||
callback(ctx, &sign, 1);
|
||||
if (write_prefix)
|
||||
callback(ctx, "0x", 2);
|
||||
print_pad(callback, ctx, '0', zeros);
|
||||
callback(ctx, digits, len);
|
||||
if (left_align)
|
||||
print_pad(callback, ctx, ' ', pad);
|
||||
|
||||
written += width > total ? width : total;
|
||||
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: {
|
||||
size_t len = fmt - format_begun_at;
|
||||
if (*fmt)
|
||||
len++;
|
||||
|
||||
callback(ctx, format_begun_at, len);
|
||||
|
||||
written += len;
|
||||
fmt = format_begun_at + len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void printf_console(void*, const char* msg, size_t len) {
|
||||
console_write(msg, len);
|
||||
}
|
||||
|
||||
static struct spinlock print_lock = {};
|
||||
|
||||
void kprintf(const char* message, ...) {
|
||||
spin_lock(&print_lock);
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
print_fmt(printf_console, nullptr, message, args);
|
||||
va_end(args);
|
||||
|
||||
spin_unlock(&print_lock);
|
||||
}
|
||||
|
||||
struct snprintf_ctx {
|
||||
char* buf;
|
||||
size_t size;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void snprintf_write(void* ctx, const char* str, size_t len) {
|
||||
struct snprintf_ctx* c = ctx;
|
||||
// Always keep room for the terminator.
|
||||
if (c->len < c->size) {
|
||||
size_t room = c->size - c->len - 1;
|
||||
memcpy(c->buf + c->len, str, len < room ? len : room);
|
||||
}
|
||||
c->len += len;
|
||||
}
|
||||
|
||||
size_t kvsnprintf(char* buf, size_t size, const char* format, va_list args) {
|
||||
struct snprintf_ctx ctx = {.buf = buf, .size = size};
|
||||
print_fmt(snprintf_write, &ctx, format, args);
|
||||
if (size > 0)
|
||||
buf[ctx.len < size ? ctx.len : size - 1] = '\0';
|
||||
return ctx.len;
|
||||
}
|
||||
|
||||
size_t ksnprintf(char* buf, size_t size, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
size_t len = kvsnprintf(buf, size, format, args);
|
||||
va_end(args);
|
||||
return len;
|
||||
}
|
||||
|
||||
void kvprintf(const char* message, va_list args) {
|
||||
spin_lock(&print_lock);
|
||||
|
||||
print_fmt(printf_console, nullptr, message, args);
|
||||
|
||||
spin_unlock(&print_lock);
|
||||
}
|
||||
|
||||
struct log_record {
|
||||
uint64_t time_ns;
|
||||
uint32_t tid;
|
||||
uint16_t len;
|
||||
uint8_t level;
|
||||
};
|
||||
|
||||
#define LOG_BUFFER_SIZE (64 * 1024)
|
||||
#define LOG_MESSAGE_MAX 255
|
||||
|
||||
static uint8_t log_buffer[LOG_BUFFER_SIZE];
|
||||
|
||||
// Protected by print_lock.
|
||||
static size_t log_head = 0;
|
||||
static size_t log_tail = 0;
|
||||
|
||||
static void log_ring_copy_in(const void* src, size_t len) {
|
||||
for (size_t i = 0; i < len; i++)
|
||||
log_buffer[(log_head + i) % LOG_BUFFER_SIZE] = ((const uint8_t*)src)[i];
|
||||
log_head += len;
|
||||
}
|
||||
|
||||
static void log_ring_peek(size_t offset, void* dst, size_t len) {
|
||||
for (size_t i = 0; i < len; i++)
|
||||
((uint8_t*)dst)[i] = log_buffer[(offset + i) % LOG_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
static void log_store(const struct log_record* rec, const char* msg) {
|
||||
const size_t total = sizeof(*rec) + rec->len;
|
||||
|
||||
// Drop the oldest records until the new one fits.
|
||||
while (LOG_BUFFER_SIZE - (log_head - log_tail) < total) {
|
||||
struct log_record old;
|
||||
log_ring_peek(log_tail, &old, sizeof(old));
|
||||
log_tail += sizeof(old) + old.len;
|
||||
}
|
||||
|
||||
log_ring_copy_in(rec, sizeof(*rec));
|
||||
log_ring_copy_in(msg, rec->len);
|
||||
}
|
||||
|
||||
struct log_line {
|
||||
char buf[LOG_MESSAGE_MAX];
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void log_line_append(void* ctx, const char* str, size_t len) {
|
||||
struct log_line* line = ctx;
|
||||
if (len > sizeof(line->buf) - line->len)
|
||||
len = sizeof(line->buf) - line->len;
|
||||
memcpy(line->buf + line->len, str, len);
|
||||
line->len += len;
|
||||
}
|
||||
|
||||
// ANSI colors for each level.
|
||||
static const char* const log_level_colors[] = {
|
||||
[LOG_DEBUG] = "\e[90m",
|
||||
[LOG_STATUS] = "\e[32m",
|
||||
[LOG_INFO] = "",
|
||||
[LOG_WARN] = "\e[33m",
|
||||
[LOG_ERR] = "\e[31m",
|
||||
[LOG_PANIC] = "\e[1;31m",
|
||||
};
|
||||
|
||||
static void log_cb_printf(log_fn_t callback, void* ctx, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
print_fmt(callback, ctx, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Formats a single record and passes it to `callback`.
|
||||
static void log_emit(log_fn_t callback, void* ctx, const struct log_record* rec, const char* msg) {
|
||||
log_cb_printf(
|
||||
callback,
|
||||
ctx,
|
||||
"[%5lu.%06lu] [%5lu] %s",
|
||||
rec->time_ns / 1'000'000'000,
|
||||
rec->time_ns / 1'000 % 1'000'000,
|
||||
(uint64_t)rec->tid,
|
||||
log_level_colors[rec->level]
|
||||
);
|
||||
callback(ctx, msg, rec->len);
|
||||
callback(ctx, "\e[0m\n", 5);
|
||||
}
|
||||
|
||||
void log_replay(log_fn_t callback, void* ctx) {
|
||||
char msg[LOG_MESSAGE_MAX];
|
||||
for (size_t offset = log_tail; offset != log_head;) {
|
||||
struct log_record rec;
|
||||
log_ring_peek(offset, &rec, sizeof(rec));
|
||||
log_ring_peek(offset + sizeof(rec), msg, rec.len);
|
||||
log_emit(callback, ctx, &rec, msg);
|
||||
offset += sizeof(rec) + rec.len;
|
||||
}
|
||||
}
|
||||
|
||||
void log_vwrite(enum log_level level, const char* message, va_list args) {
|
||||
log_vwrite_prefixed(level, "", message, args);
|
||||
}
|
||||
|
||||
void log_vwrite_prefixed(enum log_level level, const char* prefix, const char* message, va_list args) {
|
||||
struct log_line line = {};
|
||||
log_line_append(&line, prefix, strlen(prefix));
|
||||
print_fmt(log_line_append, &line, message, args);
|
||||
if (line.len > 0 && line.buf[line.len - 1] == '\n')
|
||||
line.len--;
|
||||
|
||||
auto cur = percpu_get()->sched.current;
|
||||
const struct log_record rec = {
|
||||
.time_ns = clock_get_elapsed_ns(),
|
||||
.tid = cur ? cur->id : 0,
|
||||
.len = line.len,
|
||||
.level = level,
|
||||
};
|
||||
|
||||
spin_lock(&print_lock);
|
||||
log_store(&rec, line.buf);
|
||||
log_emit(printf_console, nullptr, &rec, line.buf);
|
||||
spin_unlock(&print_lock);
|
||||
}
|
||||
|
||||
void log_write(enum log_level level, const char* message, ...) {
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
log_vwrite(level, message, args);
|
||||
va_end(args);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
#include <zinnia/sched.h>
|
||||
@@ -1,7 +0,0 @@
|
||||
kernel_sources += files(
|
||||
'core.c',
|
||||
)
|
||||
|
||||
if kconfig.get('CONFIG_SCHED_DUMB', 'n') == 'y'
|
||||
kernel_sources += files('sched_dumb.c')
|
||||
endif
|
||||
@@ -1,194 +0,0 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.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/stack.h>
|
||||
#include <zinnia/tailq.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <arch/sched.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static void idle_func(uintptr_t) {
|
||||
while (1) {
|
||||
irq_set_state(true);
|
||||
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 scheduler* sched) {
|
||||
struct task* idle_task;
|
||||
assert(task_create("idle", &kernel_vma, idle_func, 0, &idle_task) == 0, "Unable to create idle task!\n");
|
||||
sched->idle_task = idle_task;
|
||||
|
||||
struct task* boot_task = mem_alloc(sizeof(struct task), 0);
|
||||
assert(boot_task != nullptr, "Unable to create boot task!\n");
|
||||
strncpy(boot_task->name, "boot", sizeof(boot_task->name));
|
||||
boot_task->space = &kernel_vma;
|
||||
sched->current = boot_task;
|
||||
|
||||
tailq_init(&sched->run_queue);
|
||||
|
||||
log("sched: Scheduler initialized\n");
|
||||
}
|
||||
|
||||
void sched_add_task(struct scheduler* sched, struct task* task) {
|
||||
assert(task != nullptr, "Tried to add NULL to the run queue\n");
|
||||
task->state = TASK_STATE_READY;
|
||||
tailq_insert_tail(&sched->run_queue, task, next);
|
||||
}
|
||||
|
||||
static struct task* next_task(struct scheduler* sched) {
|
||||
struct task* t = tailq_first(&sched->run_queue);
|
||||
|
||||
if (t == nullptr)
|
||||
return sched->idle_task;
|
||||
|
||||
tailq_remove(&sched->run_queue, t, next);
|
||||
t->state = TASK_STATE_RUNNING;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void finish_switch(struct task* prev) {
|
||||
if (prev->state == TASK_STATE_DEAD)
|
||||
task_destroy(prev);
|
||||
}
|
||||
|
||||
static void do_reschedule(struct scheduler* sched) {
|
||||
struct task* from = sched->current;
|
||||
struct task* to = next_task(sched);
|
||||
|
||||
// We'd end up on the same task, skip switching altogether.
|
||||
if (from == to) {
|
||||
irq_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
sched->current = to;
|
||||
pmap_set(&to->space->pmap);
|
||||
|
||||
struct percpu* cpu = percpu_get();
|
||||
from->user_stack = cpu->user_stack;
|
||||
cpu->kernel_stack = kernel_stack_top(&to->kernel_stack);
|
||||
cpu->user_stack = to->user_stack;
|
||||
|
||||
struct irq_percpu irq_state = *percpu_ref(irq_data);
|
||||
struct task* prev = arch_sched_switch(from, to);
|
||||
*percpu_ref(irq_data) = irq_state;
|
||||
finish_switch(prev);
|
||||
irq_unlock();
|
||||
}
|
||||
|
||||
void sched_reschedule(struct scheduler* sched) {
|
||||
irq_lock();
|
||||
if (sched->current != sched->idle_task && sched->current->state != TASK_STATE_DEAD)
|
||||
sched_add_task(sched, sched->current);
|
||||
do_reschedule(sched);
|
||||
}
|
||||
|
||||
void sched_yield(struct scheduler* sched) {
|
||||
irq_lock();
|
||||
do_reschedule(sched);
|
||||
}
|
||||
|
||||
void sched_block(struct scheduler* sched) {
|
||||
irq_lock();
|
||||
if (atomic_exchange_explicit(&sched->current->wake_pending, false, memory_order_acq_rel)) {
|
||||
irq_unlock();
|
||||
return;
|
||||
}
|
||||
sched->current->state = TASK_STATE_BLOCKED;
|
||||
do_reschedule(sched);
|
||||
}
|
||||
|
||||
void sched_wake(struct task* task) {
|
||||
irq_lock();
|
||||
if (task->state == TASK_STATE_BLOCKED)
|
||||
sched_add_task(&percpu_get()->sched, task);
|
||||
else
|
||||
atomic_store_explicit(&task->wake_pending, true, memory_order_release);
|
||||
irq_unlock();
|
||||
}
|
||||
|
||||
errno_t task_create(const char* name, struct vma* space, 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 atomic_size_t last_tid = 0;
|
||||
new_task->id = atomic_fetch_add_explicit(&last_tid, 1, memory_order_acquire);
|
||||
new_task->space = space;
|
||||
strncpy(new_task->name, name, sizeof(new_task->name));
|
||||
|
||||
// Allocate a kernel stack.
|
||||
errno_t s = stack_init(&new_task->kernel_stack, KERNEL_STACK_SIZE);
|
||||
if (s != 0) {
|
||||
mem_free(new_task);
|
||||
return s;
|
||||
}
|
||||
new_task->user_stack = 0;
|
||||
|
||||
// Initialize the arch-dependent parts.
|
||||
s = arch_task_init(&new_task->context, entry, arg0, kernel_stack_top(&new_task->kernel_stack), true);
|
||||
if (s != 0) {
|
||||
stack_destroy(&new_task->kernel_stack);
|
||||
mem_free(new_task);
|
||||
return s;
|
||||
}
|
||||
|
||||
*out = new_task;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void task_destroy(struct task* task) {
|
||||
arch_task_fini(&task->context);
|
||||
stack_destroy(&task->kernel_stack);
|
||||
mem_free(task);
|
||||
}
|
||||
|
||||
void task_entry(task_fn_t entry, uintptr_t arg0, struct task* prev) {
|
||||
finish_switch(prev);
|
||||
|
||||
*percpu_ref(irq_data) = (struct irq_percpu){
|
||||
.depth = 1,
|
||||
.saved_state = true,
|
||||
};
|
||||
irq_unlock();
|
||||
|
||||
entry(arg0);
|
||||
|
||||
struct scheduler* sched = &percpu_get()->sched;
|
||||
sched->current->state = TASK_STATE_DEAD;
|
||||
sched_yield(sched);
|
||||
panic("unreachable");
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
static void sched_stage_init(struct boot_info*) {
|
||||
sched_init(&percpu_get()->sched);
|
||||
}
|
||||
INIT_TASK(sched_stage, "core.sched", sched_stage_init);
|
||||
INIT_DEPENDS(sched_stage, percpu_stage);
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
#include <zinnia/waitq.h>
|
||||
|
||||
void waitq_init(struct waitq* wq) {
|
||||
tailq_init(&wq->waiters);
|
||||
}
|
||||
|
||||
void waitq_wait(struct waitq* wq) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void waitq_wake_one(struct waitq* wq) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void waitq_wake_all(struct waitq* wq) {
|
||||
// TODO
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
menu "Device Drivers"
|
||||
|
||||
config OF
|
||||
bool "Support for OpenFirmware"
|
||||
default y
|
||||
help
|
||||
Adds support for flattened device trees and OF platform drivers.
|
||||
|
||||
config ACPI
|
||||
bool "Support for ACPI firmware"
|
||||
default y
|
||||
help
|
||||
Adds support for ACPI firmware, tables and devices.
|
||||
|
||||
source "dev/block/Kconfig"
|
||||
source "dev/pci/Kconfig"
|
||||
|
||||
endmenu
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
INIT_EXTERN(acpi_stage);
|
||||
|
||||
extern phys_t acpi_rsdp_addr;
|
||||
|
||||
struct acpi_match {
|
||||
const char* hid;
|
||||
uintptr_t data;
|
||||
};
|
||||
|
||||
#define ACPI_MATCH_HID(id) .hid = (id)
|
||||
@@ -1,45 +0,0 @@
|
||||
#define pr_fmt(fmt) "acpi: " fmt
|
||||
|
||||
#include <zinnia/acpi.h>
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <uacpi/status.h>
|
||||
#include <uacpi/tables.h>
|
||||
#include <uacpi/uacpi.h>
|
||||
|
||||
phys_t acpi_rsdp_addr = 0;
|
||||
|
||||
static char table_buf[0x1000];
|
||||
|
||||
static bool use_acpi = true;
|
||||
void use_acpi_option(bool use) {
|
||||
use_acpi = use;
|
||||
}
|
||||
CMDLINE_OPTION("acpi", "Enable ACPI firmware subsystem", use_acpi_option);
|
||||
|
||||
[[__init]]
|
||||
static bool acpi_stage_init(struct boot_info* info) {
|
||||
if (!use_acpi) {
|
||||
log_warn("ACPI disabled on the command line!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info->rsdp) {
|
||||
log_warn("No RSDP was passed, unable to start ACPI subsystem!\n");
|
||||
return false;
|
||||
}
|
||||
acpi_rsdp_addr = info->rsdp;
|
||||
|
||||
if (uacpi_setup_early_table_access(table_buf, sizeof(table_buf)) != UACPI_STATUS_OK) {
|
||||
log_err("Failed to set up early table access!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
INIT_TASK(acpi_stage, "dev.acpi", acpi_stage_init);
|
||||
INIT_DEPENDS(acpi_stage, mem_stage);
|
||||
INIT_ENTAILS(acpi_stage, percpu_stage);
|
||||
@@ -1,32 +0,0 @@
|
||||
#define pr_fmt(fmt) "acpi: " fmt
|
||||
|
||||
#include <zinnia/acpi.h>
|
||||
#include <zinnia/pci.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <uacpi/acpi.h>
|
||||
#include <uacpi/status.h>
|
||||
#include <uacpi/tables.h>
|
||||
|
||||
[[__init]]
|
||||
static void acpi_mcfg_stage_init(struct boot_info*) {
|
||||
uacpi_table table = {};
|
||||
if (uacpi_table_find_by_signature(ACPI_MCFG_SIGNATURE, &table) != UACPI_STATUS_OK)
|
||||
return;
|
||||
|
||||
struct acpi_mcfg* mcfg = table.ptr;
|
||||
size_t count = 0;
|
||||
if (mcfg->hdr.length > sizeof(*mcfg))
|
||||
count = (mcfg->hdr.length - sizeof(*mcfg)) / sizeof(struct acpi_mcfg_allocation);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
struct acpi_mcfg_allocation* entry = &mcfg->entries[i];
|
||||
errno_t status = pci_ecam_register(entry->address, entry->segment, entry->start_bus, entry->end_bus);
|
||||
if (status != 0)
|
||||
log_warn("Failed to register ECAM for segment %hu: %i\n", entry->segment, status);
|
||||
}
|
||||
|
||||
uacpi_table_unref(&table);
|
||||
}
|
||||
INIT_TASK(acpi_mcfg_stage, "dev.acpi.mcfg", acpi_mcfg_stage_init);
|
||||
INIT_DEPENDS(acpi_mcfg_stage, acpi_stage);
|
||||
INIT_ENTAILS(acpi_mcfg_stage, pci_stage);
|
||||
@@ -1,20 +0,0 @@
|
||||
uacpi = subproject('uacpi')
|
||||
|
||||
kernel_sources += uacpi.get_variable('sources')
|
||||
kernel_sources += files(
|
||||
'init.c',
|
||||
'uacpi_glue.c',
|
||||
)
|
||||
|
||||
if kconfig.get('CONFIG_PCI', 'n') == 'y'
|
||||
kernel_sources += files('mcfg.c')
|
||||
endif
|
||||
|
||||
kernel_includes += uacpi.get_variable('includes')
|
||||
kernel_includes += include_directories('include')
|
||||
|
||||
kernel_c_args += [
|
||||
'-DUACPI_FORMATTED_LOGGING',
|
||||
'-DUACPI_BAREBONES_MODE', # TODO: Remove
|
||||
'-DUACPI_OVERRIDE_TYPES',
|
||||
]
|
||||
@@ -1,66 +0,0 @@
|
||||
#include <zinnia/acpi.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/mmio.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/virt.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <arch/mem.h>
|
||||
#include <stdarg.h>
|
||||
#include <uacpi/kernel_api.h>
|
||||
#include <uacpi/status.h>
|
||||
#include <uacpi/types.h>
|
||||
|
||||
uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr* out_rsdp_address) {
|
||||
*out_rsdp_address = (uacpi_phys_addr)acpi_rsdp_addr;
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
void* uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len) {
|
||||
uintptr_t aligned_addr = ALIGN_DOWN(addr, arch_mem_page_size());
|
||||
uintptr_t difference = addr - aligned_addr;
|
||||
size_t aligned_len = ALIGN_UP(len + difference, arch_mem_page_size());
|
||||
|
||||
uintptr_t vaddr = vm_alloc(aligned_len);
|
||||
if (!vaddr)
|
||||
return nullptr;
|
||||
|
||||
for (size_t i = 0; i < aligned_len; i += arch_mem_page_size()) {
|
||||
pmap_map(&kernel_vma.pmap, vaddr + i, aligned_addr + i, PROT_READ | PROT_WRITE, CACHE_ANY);
|
||||
}
|
||||
|
||||
return (void*)(vaddr + difference);
|
||||
}
|
||||
|
||||
void uacpi_kernel_unmap(void* addr, uacpi_size len) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
static enum log_level acpi_log_level(uacpi_log_level lvl) {
|
||||
switch (lvl) {
|
||||
case UACPI_LOG_ERROR:
|
||||
return LOG_ERR;
|
||||
|
||||
case UACPI_LOG_WARN:
|
||||
return LOG_WARN;
|
||||
|
||||
case UACPI_LOG_INFO:
|
||||
return LOG_INFO;
|
||||
|
||||
default:
|
||||
return LOG_DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
void uacpi_kernel_log(uacpi_log_level lvl, const uacpi_char* msg, ...) {
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
log_vwrite_prefixed(acpi_log_level(lvl), "acpi: ", msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void uacpi_kernel_vlog(uacpi_log_level lvl, const uacpi_char* msg, uacpi_va_list args) {
|
||||
log_vwrite_prefixed(acpi_log_level(lvl), "acpi: ", msg, args);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
menu "Block device drivers"
|
||||
|
||||
source "dev/block/nvme/Kconfig"
|
||||
|
||||
endmenu
|
||||
@@ -1,3 +0,0 @@
|
||||
if kconfig.get('CONFIG_NVME_CORE', 'n') != 'n'
|
||||
subdir('nvme')
|
||||
endif
|
||||
@@ -1,13 +0,0 @@
|
||||
menu "NVMe Support"
|
||||
|
||||
config NVME_CORE
|
||||
bool
|
||||
default y
|
||||
|
||||
config NVME_PCI
|
||||
bool "NVMe via PCI transport"
|
||||
default y
|
||||
depends on PCI
|
||||
depends on NVME_CORE
|
||||
|
||||
endmenu
|
||||
@@ -1,41 +0,0 @@
|
||||
#define pr_fmt(fmt) "nvme: " fmt
|
||||
|
||||
#include <zinnia/fdt.h>
|
||||
#include <zinnia/module.h>
|
||||
#include <zinnia/pci.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <config.h>
|
||||
|
||||
#if CONFIG_NVME_PCI
|
||||
#include "nvme_pci.h"
|
||||
#endif
|
||||
|
||||
struct nvme_dev {
|
||||
#if CONFIG_NVME_PCI
|
||||
struct pci_device pci_dev;
|
||||
#endif
|
||||
};
|
||||
|
||||
errno_t nvme_init(struct module* mod) {
|
||||
errno_t status = 0;
|
||||
|
||||
#if CONFIG_NVME_PCI
|
||||
status = pci_driver_register(&nvme_driver);
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
errno_t nvme_exit(struct module* mod) {
|
||||
// TODO: Unregister
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ENTRY(nvme_init);
|
||||
MODULE_EXIT(nvme_exit);
|
||||
|
||||
MODULE_DESC("NVMe Block Devices");
|
||||
MODULE_AUTHOR("Marvin Friedrich");
|
||||
MODULE_LICENSE("ISC");
|
||||
MODULE_VERSION("1.0");
|
||||
@@ -1,12 +0,0 @@
|
||||
nvme_sources = files('core.c')
|
||||
|
||||
if kconfig.get('CONFIG_NVME_PCI', 'n') == 'y'
|
||||
nvme_sources += files('pci.c')
|
||||
endif
|
||||
|
||||
modules += {
|
||||
'nvme': {
|
||||
'modular': kconfig.get('CONFIG_NVME_CORE') == 'm',
|
||||
'sources': nvme_sources,
|
||||
},
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
extern struct pci_driver nvme_driver;
|
||||
@@ -1,23 +0,0 @@
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/module.h>
|
||||
#include <zinnia/pci.h>
|
||||
|
||||
static errno_t nvme_pci_probe(struct pci_device* dev, const struct pci_match* match) {
|
||||
pci_log(dev, "Detected controller \n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_match nvme_match[] = {
|
||||
// Generic NVMe device. Assume no quirks or any funny business.
|
||||
{PCI_MATCH_CLASS(1, 8, 2), .data = 0},
|
||||
};
|
||||
|
||||
MATCH_TABLE("pci", nvme_match);
|
||||
|
||||
struct pci_driver nvme_driver = {
|
||||
.name = "nvme_pci",
|
||||
.match = nvme_match,
|
||||
.num_matches = ARRAY_SIZE(nvme_match),
|
||||
.probe = nvme_pci_probe,
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
#pragma once
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
kernel_sources += files(
|
||||
'memfiles.c',
|
||||
)
|
||||
-235
@@ -1,235 +0,0 @@
|
||||
#include <zinnia/fdt.h>
|
||||
#include <string.h>
|
||||
|
||||
uint32_t fdt_read_u32(const void* data) {
|
||||
const uint8_t* p = data;
|
||||
return (uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3];
|
||||
}
|
||||
|
||||
uint64_t fdt_read_u64(const void* data) {
|
||||
const uint8_t* p = data;
|
||||
return (uint64_t)fdt_read_u32(p) << 32 | fdt_read_u32(p + 4);
|
||||
}
|
||||
|
||||
static bool range_valid(size_t offset, size_t length, size_t total) {
|
||||
return offset <= total && length <= total - offset;
|
||||
}
|
||||
|
||||
struct token {
|
||||
uint32_t kind;
|
||||
size_t next;
|
||||
const char* name;
|
||||
const void* data;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
static bool read_token(struct fdt* tree, size_t offset, struct token* token) {
|
||||
if (!range_valid(offset, 4, tree->structs_len) || offset % 4)
|
||||
return false;
|
||||
*token = (struct token){.kind = fdt_read_u32(tree->structs + offset), .next = offset + 4};
|
||||
if (token->kind == FDT_BEGIN_NODE) {
|
||||
token->name = (const char*)tree->structs + token->next;
|
||||
size_t left = tree->structs_len - token->next;
|
||||
size_t length = strnlen(token->name, left);
|
||||
if (length == left)
|
||||
return false;
|
||||
token->next += (length + 1 + 3) & ~(size_t)3;
|
||||
} else if (token->kind == FDT_PROP) {
|
||||
if (!range_valid(token->next, 8, tree->structs_len))
|
||||
return false;
|
||||
token->length = fdt_read_u32(tree->structs + token->next);
|
||||
size_t name_offset = fdt_read_u32(tree->structs + token->next + 4);
|
||||
token->next += 8;
|
||||
if (name_offset >= tree->strings_len || !range_valid(token->next, token->length, tree->structs_len))
|
||||
return false;
|
||||
token->name = (const char*)tree->strings + name_offset;
|
||||
size_t left = tree->strings_len - name_offset;
|
||||
if (strnlen(token->name, left) == left || !*token->name)
|
||||
return false;
|
||||
token->data = tree->structs + token->next;
|
||||
token->next += (token->length + 3) & ~(size_t)3;
|
||||
} else if (token->kind != FDT_END_NODE && token->kind != FDT_NOP && token->kind != FDT_END) {
|
||||
return false;
|
||||
}
|
||||
return token->next <= tree->structs_len;
|
||||
}
|
||||
|
||||
bool fdt_init(struct fdt* tree, const void* data, size_t length) {
|
||||
if (!tree)
|
||||
return false;
|
||||
*tree = (struct fdt){};
|
||||
if (!data || length < sizeof(struct fdt_header))
|
||||
return false;
|
||||
const uint8_t* blob = data;
|
||||
size_t total = fdt_read_u32(blob + 4);
|
||||
size_t structs = fdt_read_u32(blob + 8);
|
||||
size_t strings = fdt_read_u32(blob + 12);
|
||||
size_t reserves = fdt_read_u32(blob + 16);
|
||||
size_t strings_len = fdt_read_u32(blob + 32);
|
||||
size_t structs_len = fdt_read_u32(blob + 36);
|
||||
uint32_t version = fdt_read_u32(blob + 20);
|
||||
uint32_t compat = fdt_read_u32(blob + 24);
|
||||
if (fdt_read_u32(blob) != FDT_MAGIC || total > length || total < sizeof(struct fdt_header) || version < 17 ||
|
||||
compat > 17 || compat > version || structs % 4 || reserves % 8 || structs < sizeof(struct fdt_header) ||
|
||||
strings < sizeof(struct fdt_header) || reserves < sizeof(struct fdt_header) ||
|
||||
!range_valid(structs, structs_len, total) || !range_valid(strings, strings_len, total) ||
|
||||
(structs < strings + strings_len && strings < structs + structs_len))
|
||||
return false;
|
||||
size_t reserved_end = reserves;
|
||||
for (;;) {
|
||||
if (!range_valid(reserved_end, 16, total))
|
||||
return false;
|
||||
bool end = !fdt_read_u64(blob + reserved_end) && !fdt_read_u64(blob + reserved_end + 8);
|
||||
reserved_end += 16;
|
||||
if (end)
|
||||
break;
|
||||
}
|
||||
if ((reserves < structs + structs_len && structs < reserved_end) ||
|
||||
(reserves < strings + strings_len && strings < reserved_end))
|
||||
return false;
|
||||
struct fdt candidate = {
|
||||
.blob = blob,
|
||||
.total_size = total,
|
||||
.version = version,
|
||||
.compat_version = compat,
|
||||
.boot_cpuid = fdt_read_u32(blob + 28),
|
||||
.structs = blob + structs,
|
||||
.structs_len = structs_len,
|
||||
.strings = blob + strings,
|
||||
.strings_len = strings_len,
|
||||
};
|
||||
size_t depth = 0;
|
||||
bool started = false;
|
||||
bool children[64] = {};
|
||||
for (size_t offset = 0; offset < structs_len;) {
|
||||
struct token token;
|
||||
if (!read_token(&candidate, offset, &token))
|
||||
return false;
|
||||
if (token.kind == FDT_BEGIN_NODE) {
|
||||
if (depth == 64 || (!depth && (started || *token.name)) || (depth && !*token.name))
|
||||
return false;
|
||||
if (depth)
|
||||
children[depth - 1] = true;
|
||||
children[depth++] = false;
|
||||
started = true;
|
||||
} else if (token.kind == FDT_END_NODE) {
|
||||
if (!depth)
|
||||
return false;
|
||||
depth--;
|
||||
} else if (token.kind == FDT_PROP) {
|
||||
if (!depth || children[depth - 1])
|
||||
return false;
|
||||
} else if (token.kind == FDT_END) {
|
||||
if (!started || depth || token.next != structs_len)
|
||||
return false;
|
||||
*tree = candidate;
|
||||
return true;
|
||||
}
|
||||
offset = token.next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct fdt_node fdt_root(struct fdt* tree) {
|
||||
struct token token;
|
||||
size_t offset = 0;
|
||||
if (tree && tree->blob) {
|
||||
while (read_token(tree, offset, &token)) {
|
||||
if (token.kind == FDT_BEGIN_NODE)
|
||||
return (struct fdt_node){tree, offset};
|
||||
if (token.kind != FDT_NOP)
|
||||
break;
|
||||
offset = token.next;
|
||||
}
|
||||
}
|
||||
return (struct fdt_node){};
|
||||
}
|
||||
|
||||
static struct fdt_node find_child(struct fdt_node node, const char* name, size_t length) {
|
||||
struct token token;
|
||||
if (!node.tree || !read_token(node.tree, node.offset, &token) || token.kind != FDT_BEGIN_NODE)
|
||||
return (struct fdt_node){};
|
||||
size_t depth = 0;
|
||||
for (size_t offset = token.next; read_token(node.tree, offset, &token); offset = token.next) {
|
||||
if (token.kind == FDT_BEGIN_NODE) {
|
||||
if (!depth && strlen(token.name) == length && !memcmp(token.name, name, length))
|
||||
return (struct fdt_node){node.tree, offset};
|
||||
depth++;
|
||||
} else if (token.kind == FDT_END_NODE) {
|
||||
if (!depth)
|
||||
break;
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
return (struct fdt_node){};
|
||||
}
|
||||
|
||||
struct fdt_node fdt_find(struct fdt* tree, const char* path) {
|
||||
if (!path || *path != '/')
|
||||
return (struct fdt_node){};
|
||||
struct fdt_node node = fdt_root(tree);
|
||||
path++;
|
||||
while (*path && node.tree) {
|
||||
size_t length = 0;
|
||||
while (path[length] && path[length] != '/')
|
||||
length++;
|
||||
if (!length)
|
||||
return (struct fdt_node){};
|
||||
node = find_child(node, path, length);
|
||||
path += length;
|
||||
if (*path == '/')
|
||||
path++;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
struct fdt_prop fdt_property(struct fdt_node node, const char* name) {
|
||||
struct token token;
|
||||
if (!node.tree || !name || !read_token(node.tree, node.offset, &token) || token.kind != FDT_BEGIN_NODE)
|
||||
return (struct fdt_prop){};
|
||||
for (size_t offset = token.next; read_token(node.tree, offset, &token); offset = token.next) {
|
||||
if (token.kind == FDT_PROP && !strcmp(token.name, name))
|
||||
return (struct fdt_prop){node.tree, token.data, token.length};
|
||||
if (token.kind == FDT_BEGIN_NODE || token.kind == FDT_END_NODE)
|
||||
break;
|
||||
}
|
||||
return (struct fdt_prop){};
|
||||
}
|
||||
|
||||
bool fdt_property_u32(struct fdt_node node, const char* name, uint32_t* value) {
|
||||
struct fdt_prop property = fdt_property(node, name);
|
||||
if (!property.tree || property.length != 4 || !value)
|
||||
return false;
|
||||
*value = fdt_read_u32(property.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fdt_property_u64(struct fdt_node node, const char* name, uint64_t* value) {
|
||||
struct fdt_prop property = fdt_property(node, name);
|
||||
if (!property.tree || property.length != 8 || !value)
|
||||
return false;
|
||||
*value = fdt_read_u64(property.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct fdt_node fdt_find_compatible(struct fdt* tree, const char* compatible) {
|
||||
if (!tree || !tree->blob || !compatible)
|
||||
return (struct fdt_node){};
|
||||
struct token token;
|
||||
for (size_t offset = 0; read_token(tree, offset, &token); offset = token.next) {
|
||||
if (token.kind != FDT_BEGIN_NODE)
|
||||
continue;
|
||||
struct fdt_node node = {tree, offset};
|
||||
struct fdt_prop property = fdt_property(node, "compatible");
|
||||
const char* data = property.data;
|
||||
for (size_t i = 0; i < property.length;) {
|
||||
size_t length = strnlen(data + i, property.length - i);
|
||||
if (length == property.length - i)
|
||||
break;
|
||||
if (!strcmp(data + i, compatible))
|
||||
return node;
|
||||
i += length + 1;
|
||||
}
|
||||
}
|
||||
return (struct fdt_node){};
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
kernel_sources += files('fdt.c')
|
||||
@@ -1,13 +0,0 @@
|
||||
subdir('common')
|
||||
subdir('block')
|
||||
subdir('dt')
|
||||
|
||||
if kconfig.get('CONFIG_PCI', 'n') == 'y'
|
||||
subdir('pci')
|
||||
endif
|
||||
|
||||
if kconfig.get('CONFIG_ACPI', 'n') == 'y'
|
||||
subdir('acpi')
|
||||
endif
|
||||
|
||||
module_lds = meson.current_source_dir() / 'module.ld'
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
/* Linker script for kernel modules */
|
||||
|
||||
ENTRY(__module_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(6);
|
||||
modversion 0x60000001;
|
||||
modauthor 0x60000002;
|
||||
moddesc 0x60000003;
|
||||
modlicense 0x60000004;
|
||||
modcmdline 0x60000005;
|
||||
modexit 0x60000006;
|
||||
modmatch 0x60000007;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
/* Code */
|
||||
.text : {
|
||||
*(.init .init.*)
|
||||
*(.text .text.*)
|
||||
*(.fini .fini.*)
|
||||
*(.plt .plt.*)
|
||||
} :text
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
/* Read-only data */
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
*(.hash .hash.*)
|
||||
*(.got .got.*)
|
||||
*(.got.plt .got.plt.*)
|
||||
} :rodata
|
||||
|
||||
.mod.version : {
|
||||
KEEP(*(.mod.version))
|
||||
} :rodata :modversion
|
||||
|
||||
.mod.author : {
|
||||
KEEP(*(.mod.author))
|
||||
} :rodata :modauthor
|
||||
|
||||
.mod.desc : {
|
||||
KEEP(*(.mod.desc))
|
||||
} :rodata :moddesc
|
||||
|
||||
.mod.license : {
|
||||
KEEP(*(.mod.license))
|
||||
} :rodata :modlicense
|
||||
|
||||
.cmdline : {
|
||||
KEEP(*(.cmdline))
|
||||
} :rodata :modcmdline
|
||||
|
||||
.mod.exit : {
|
||||
KEEP(*(.mod.exit))
|
||||
} :rodata :modexit
|
||||
|
||||
.mod.match : {
|
||||
KEEP(*(.mod.match))
|
||||
} :rodata :modmatch
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
/* Read-Write data */
|
||||
.data : {
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
} :data
|
||||
|
||||
/* Zero-initialized memory */
|
||||
.bss : {
|
||||
*(.*bss .*bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
.dynsym : {
|
||||
*(.dynsym .dynsym.*)
|
||||
} :data
|
||||
|
||||
.dynstr : {
|
||||
*(.dynstr .dynstr.*)
|
||||
} :data
|
||||
|
||||
.rela.dyn : {
|
||||
*(.rela.dyn .rela.dyn.*)
|
||||
} :data
|
||||
|
||||
.relr.dyn : {
|
||||
*(.relr.dyn .relr.dyn.*)
|
||||
} :data
|
||||
|
||||
/* Dynamic tags */
|
||||
.dynamic : {
|
||||
*(.dynamic)
|
||||
} :data :dynamic
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.comment*)
|
||||
*(.eh_frame*)
|
||||
*(.rel.eh_frame*)
|
||||
*(.gcc_except_table*)
|
||||
*(.note*)
|
||||
*(.interp)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
menu "PCI bus drivers"
|
||||
|
||||
config PCI
|
||||
bool "PCI core drivers"
|
||||
default y
|
||||
|
||||
endmenu
|
||||
-376
@@ -1,376 +0,0 @@
|
||||
#define pr_fmt(fmt) "pci: " fmt
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/module.h>
|
||||
#include <zinnia/pci.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/vec.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct spinlock access_lock = {};
|
||||
static vec(struct pci_access*) accesses = {};
|
||||
|
||||
static struct spinlock device_lock = {};
|
||||
static vec(struct pci_device*) devices = {};
|
||||
static vec(struct pci_driver*) drivers = {};
|
||||
|
||||
errno_t pci_access_register(struct pci_access* access) {
|
||||
spin_lock(&access_lock);
|
||||
errno_t status = vec_push(&accesses, access);
|
||||
spin_unlock(&access_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool pci_access_decodes(const struct pci_access* access, const struct pci_address* addr) {
|
||||
return access->domain == addr->domain && addr->bus >= access->start_bus && addr->bus <= access->end_bus;
|
||||
}
|
||||
|
||||
struct pci_access* pci_access_find(const struct pci_address* addr) {
|
||||
struct pci_access* found = nullptr;
|
||||
|
||||
spin_lock(&access_lock);
|
||||
vec_foreach(&accesses, i) {
|
||||
struct pci_access* access = vec_elem_get(&accesses, i);
|
||||
if (!pci_access_decodes(access, addr))
|
||||
continue;
|
||||
if (found == nullptr || access->priority > found->priority)
|
||||
found = access;
|
||||
}
|
||||
spin_unlock(&access_lock);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static uint32_t cfg_read32(struct pci_access* access, const struct pci_address* addr, uint16_t offset) {
|
||||
return access->read32(access, addr, offset & ~3);
|
||||
}
|
||||
|
||||
static void cfg_write32(struct pci_access* access, const struct pci_address* addr, uint16_t offset, uint32_t value) {
|
||||
access->write32(access, addr, offset & ~3, value);
|
||||
}
|
||||
|
||||
uint32_t pci_read32(struct pci_device* dev, uint16_t offset) {
|
||||
return cfg_read32(dev->access, &dev->address, offset);
|
||||
}
|
||||
|
||||
uint16_t pci_read16(struct pci_device* dev, uint16_t offset) {
|
||||
return pci_read32(dev, offset) >> ((offset & 2) * 8);
|
||||
}
|
||||
|
||||
uint8_t pci_read8(struct pci_device* dev, uint16_t offset) {
|
||||
return pci_read32(dev, offset) >> ((offset & 3) * 8);
|
||||
}
|
||||
|
||||
void pci_write32(struct pci_device* dev, uint16_t offset, uint32_t value) {
|
||||
cfg_write32(dev->access, &dev->address, offset, value);
|
||||
}
|
||||
|
||||
void pci_write16(struct pci_device* dev, uint16_t offset, uint16_t value) {
|
||||
uint32_t shift = (offset & 2) * 8;
|
||||
uint32_t reg = pci_read32(dev, offset);
|
||||
reg &= ~(0xffffu << shift);
|
||||
reg |= (uint32_t)value << shift;
|
||||
pci_write32(dev, offset, reg);
|
||||
}
|
||||
|
||||
void pci_write8(struct pci_device* dev, uint16_t offset, uint8_t value) {
|
||||
uint32_t shift = (offset & 3) * 8;
|
||||
uint32_t reg = pci_read32(dev, offset);
|
||||
reg &= ~(0xffu << shift);
|
||||
reg |= (uint32_t)value << shift;
|
||||
pci_write32(dev, offset, reg);
|
||||
}
|
||||
|
||||
errno_t pci_bar_get(struct pci_device* dev, size_t index, struct pci_bar* out) {
|
||||
if (index >= PCI_NUM_BARS)
|
||||
return EINVAL;
|
||||
|
||||
uint16_t bar_offset = PCI_BAR0 + index * sizeof(uint32_t);
|
||||
uint16_t high_offset = bar_offset + sizeof(uint32_t);
|
||||
uint32_t bar = pci_read32(dev, bar_offset);
|
||||
|
||||
bool is_mmio = (bar & 0x1) == 0;
|
||||
bool is_mmio64 = is_mmio && ((bar >> 1) & 0x3) == 0x2;
|
||||
bool is_prefetchable = is_mmio && (bar & (1 << 3));
|
||||
bool is_64bit_pair = is_mmio64 && index + 1 < PCI_NUM_BARS;
|
||||
|
||||
uint16_t command = pci_read16(dev, PCI_COMMAND);
|
||||
pci_write16(dev, PCI_COMMAND, command & ~(PCI_COMMAND_IO | PCI_COMMAND_MEM));
|
||||
|
||||
uint32_t high = is_64bit_pair ? pci_read32(dev, high_offset) : 0;
|
||||
|
||||
pci_write32(dev, bar_offset, 0xffff'ffff);
|
||||
if (is_64bit_pair)
|
||||
pci_write32(dev, high_offset, 0xffff'ffff);
|
||||
|
||||
uint32_t mask_low = pci_read32(dev, bar_offset);
|
||||
uint32_t mask_high = is_64bit_pair ? pci_read32(dev, high_offset) : 0;
|
||||
|
||||
pci_write32(dev, bar_offset, bar);
|
||||
if (is_64bit_pair)
|
||||
pci_write32(dev, high_offset, high);
|
||||
|
||||
pci_write16(dev, PCI_COMMAND, command);
|
||||
|
||||
uint64_t addr_mask = is_mmio ? ~0xfull : ~0x3ull;
|
||||
uint64_t raw = (uint64_t)mask_high << 32 | mask_low;
|
||||
uint64_t masked = raw & addr_mask & (is_64bit_pair ? UINT64_MAX : UINT32_MAX);
|
||||
|
||||
struct pci_bar result = {
|
||||
.size = masked == 0 ? 0 : (size_t)1 << __builtin_ctzll(masked),
|
||||
.prefetchable = is_prefetchable,
|
||||
};
|
||||
|
||||
if (is_mmio && is_64bit_pair) {
|
||||
result.type = PCI_BAR_MMIO64;
|
||||
result.address = (phys_t)high << 32 | (bar & 0xffff'fff0);
|
||||
} else if (is_mmio) {
|
||||
result.type = PCI_BAR_MMIO32;
|
||||
result.address = bar & 0xffff'fff0;
|
||||
} else {
|
||||
result.type = PCI_BAR_IO;
|
||||
result.address = (uint16_t)(bar & 0xffff'fffc);
|
||||
}
|
||||
|
||||
if (result.address == 0)
|
||||
return ENOENT;
|
||||
|
||||
*out = result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t pci_cap_next(struct pci_device* dev, uint8_t cur) {
|
||||
uint8_t ptr;
|
||||
if (cur == 0) {
|
||||
if (!(pci_read16(dev, PCI_STATUS) & PCI_STATUS_CAP_LIST))
|
||||
return 0;
|
||||
ptr = pci_read8(dev, PCI_CAP_PTR) & ~3;
|
||||
} else {
|
||||
ptr = (pci_read32(dev, cur) >> 8) & ~3;
|
||||
}
|
||||
|
||||
if (ptr == 0)
|
||||
return 0;
|
||||
|
||||
if ((pci_read32(dev, ptr) & 0xff) == 0xff)
|
||||
return 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint8_t pci_cap_find(struct pci_device* dev, uint8_t id) {
|
||||
for (uint8_t cap = pci_cap_next(dev, 0); cap != 0; cap = pci_cap_next(dev, cap)) {
|
||||
if (pci_read8(dev, cap) == id)
|
||||
return cap;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_device_name(const struct pci_device* dev, char buf[static 18]) {
|
||||
ksnprintf(buf, 18, "pci-" PCI_ADDR_FMT, PCI_ADDR_ARGS(&dev->address));
|
||||
}
|
||||
|
||||
static bool scan_function(struct pci_access* access, struct pci_address addr) {
|
||||
uint32_t reg0 = cfg_read32(access, &addr, PCI_VENDOR_ID);
|
||||
uint16_t vendor_id = reg0;
|
||||
uint16_t device_id = reg0 >> 16;
|
||||
|
||||
if (vendor_id == 0xffff || vendor_id == 0)
|
||||
return false;
|
||||
if (device_id == 0xffff || device_id == 0)
|
||||
return false;
|
||||
|
||||
struct pci_device* dev = mem_alloc(sizeof(*dev), 0);
|
||||
if (dev == nullptr) {
|
||||
log_err("Out of memory while scanning " PCI_ADDR_FMT "\n", PCI_ADDR_ARGS(&addr));
|
||||
return false;
|
||||
}
|
||||
|
||||
dev->address = addr;
|
||||
dev->access = access;
|
||||
dev->id.vendor_id = vendor_id;
|
||||
dev->id.device_id = device_id;
|
||||
dev->class.prog_if = pci_read8(dev, PCI_PROG_IF);
|
||||
dev->class.subclass = pci_read8(dev, PCI_SUBCLASS);
|
||||
dev->class.class = pci_read8(dev, PCI_CLASS);
|
||||
dev->header_type = pci_read8(dev, PCI_HEADER_TYPE);
|
||||
|
||||
if ((dev->header_type & PCI_HEADER_TYPE_MASK) == 0) {
|
||||
dev->id.subvendor_id = pci_read16(dev, PCI_SUBSYS_VENDOR_ID);
|
||||
dev->id.subdevice_id = pci_read16(dev, PCI_SUBSYS_ID);
|
||||
}
|
||||
|
||||
pci_log(
|
||||
dev,
|
||||
"<id = %.4hx:%.4hx, class = %.2hhx:%.2hhx:%.2hhx>\n",
|
||||
dev->id.vendor_id,
|
||||
dev->id.device_id,
|
||||
dev->class.class,
|
||||
dev->class.subclass,
|
||||
dev->class.prog_if
|
||||
);
|
||||
|
||||
if (vec_push(&devices, dev) != 0) {
|
||||
mem_free(dev);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void scan_slot(struct pci_access* access, struct pci_address addr) {
|
||||
if (!scan_function(access, addr))
|
||||
return;
|
||||
|
||||
if (!(vec_elem_get(&devices, vec_length(&devices) - 1)->header_type & PCI_HEADER_TYPE_MULTIFUNCTION))
|
||||
return;
|
||||
|
||||
for (addr.function = 1; addr.function < PCI_NUM_FUNCTIONS; addr.function++)
|
||||
scan_function(access, addr);
|
||||
}
|
||||
|
||||
static void format_match_id(char buf[static 5], uint16_t value) {
|
||||
if (value == PCI_ANY_ID)
|
||||
ksnprintf(buf, 5, "****");
|
||||
else
|
||||
ksnprintf(buf, 5, "%.4hx", value);
|
||||
}
|
||||
|
||||
static void format_match_class(char buf[static 3], uint8_t value) {
|
||||
if (value == PCI_ANY_CLASS)
|
||||
ksnprintf(buf, 3, "**");
|
||||
else
|
||||
ksnprintf(buf, 3, "%.2hhx", value);
|
||||
}
|
||||
|
||||
static void format_match(const struct pci_match* match, char* buf, size_t size) {
|
||||
char vendor[5], device[5], class[3], subclass[3], prog_if[3];
|
||||
format_match_id(vendor, match->vendor_id);
|
||||
format_match_id(device, match->device_id);
|
||||
format_match_class(class, match->class);
|
||||
format_match_class(subclass, match->subclass);
|
||||
format_match_class(prog_if, match->prog_if);
|
||||
ksnprintf(buf, size, "<id = %s:%s, class = %s:%s:%s>", vendor, device, class, subclass, prog_if);
|
||||
}
|
||||
|
||||
static bool pci_match_device(const struct pci_match* m, const struct pci_device* dev) {
|
||||
return (m->vendor_id == PCI_ANY_ID || m->vendor_id == dev->id.vendor_id) &&
|
||||
(m->device_id == PCI_ANY_ID || m->device_id == dev->id.device_id) &&
|
||||
(m->class == PCI_ANY_CLASS || m->class == dev->class.class) &&
|
||||
(m->subclass == PCI_ANY_CLASS || m->subclass == dev->class.subclass) &&
|
||||
(m->prog_if == PCI_ANY_CLASS || m->prog_if == dev->class.prog_if);
|
||||
}
|
||||
|
||||
// Returns the first entry that matches, or nullptr.
|
||||
static const struct pci_match* pci_match_table(
|
||||
const struct pci_match* table,
|
||||
size_t num_matches,
|
||||
const struct pci_device* dev
|
||||
) {
|
||||
for (size_t i = 0; i < num_matches; i++) {
|
||||
if (pci_match_device(&table[i], dev))
|
||||
return &table[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
errno_t pci_driver_register(struct pci_driver* driver) {
|
||||
if (driver->match == nullptr || driver->num_matches == 0) {
|
||||
log_warn("Driver \"%s\" does not define any matches, ignoring\n", driver->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock(&device_lock);
|
||||
|
||||
vec_foreach(&drivers, i) {
|
||||
if (strcmp(vec_elem_get(&drivers, i)->name, driver->name) == 0) {
|
||||
spin_unlock(&device_lock);
|
||||
log_warn("Driver \"%s\" is already registered\n", driver->name);
|
||||
return EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
errno_t status = vec_push(&drivers, driver);
|
||||
spin_unlock(&device_lock);
|
||||
if (status != 0)
|
||||
return status;
|
||||
|
||||
log("Registered PCI driver \"%s\" with %zu match%s\n",
|
||||
driver->name,
|
||||
driver->num_matches,
|
||||
driver->num_matches == 1 ? "" : "es");
|
||||
for (size_t i = 0; i < driver->num_matches; i++) {
|
||||
char rule[48];
|
||||
format_match(&driver->match[i], rule, sizeof(rule));
|
||||
log(" [%zu] = %s\n", i, rule);
|
||||
}
|
||||
|
||||
for (size_t i = 0;; i++) {
|
||||
spin_lock(&device_lock);
|
||||
if (i >= vec_length(&devices)) {
|
||||
spin_unlock(&device_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
struct pci_device* dev = vec_elem_get(&devices, i);
|
||||
const struct pci_match* match = nullptr;
|
||||
if (dev->driver == nullptr) {
|
||||
match = pci_match_table(driver->match, driver->num_matches, dev);
|
||||
if (match != nullptr)
|
||||
dev->driver = driver;
|
||||
}
|
||||
spin_unlock(&device_lock);
|
||||
|
||||
if (match == nullptr)
|
||||
continue;
|
||||
|
||||
pci_log(dev, "Matched driver \"%s\" on rule [%zu]\n", driver->name, (size_t)(match - driver->match));
|
||||
|
||||
status = driver->probe(dev, match);
|
||||
if (status != 0) {
|
||||
pci_log_warn(dev, "%s failed to probe: %i\n", driver->name, status);
|
||||
spin_lock(&device_lock);
|
||||
dev->driver = nullptr;
|
||||
spin_unlock(&device_lock);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
static void pci_stage_init(struct boot_info*) {
|
||||
if (vec_is_empty(&accesses)) {
|
||||
log_warn("No configuration space access method available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Scanning PCI devices\n");
|
||||
|
||||
spin_lock(&device_lock);
|
||||
vec_foreach(&accesses, i) {
|
||||
struct pci_access* access = vec_elem_get(&accesses, i);
|
||||
for (uint32_t bus = access->start_bus; bus <= access->end_bus; bus++) {
|
||||
for (uint8_t slot = 0; slot < PCI_NUM_SLOTS; slot++) {
|
||||
struct pci_address addr = {
|
||||
.domain = access->domain,
|
||||
.bus = bus,
|
||||
.device = slot,
|
||||
};
|
||||
|
||||
if (pci_access_find(&addr) != access)
|
||||
continue;
|
||||
|
||||
scan_slot(access, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&device_lock);
|
||||
|
||||
log("Found %zu device(s)\n", vec_length(&devices));
|
||||
}
|
||||
INIT_TASK(pci_stage, "dev.pci", pci_stage_init);
|
||||
INIT_DEPENDS(pci_stage, mem_stage);
|
||||
INIT_ENTAILS(pci_stage, module_stage);
|
||||
@@ -1,76 +0,0 @@
|
||||
#define pr_fmt(fmt) "pci: " fmt
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/mmio.h>
|
||||
#include <zinnia/pci.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/virt.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <arch/mem.h>
|
||||
|
||||
struct pci_ecam {
|
||||
struct pci_access access;
|
||||
void* base;
|
||||
};
|
||||
|
||||
static void* ecam_ptr(struct pci_access* self, const struct pci_address* addr, uint16_t offset) {
|
||||
struct pci_ecam* ecam = containerof(self, struct pci_ecam, access);
|
||||
size_t index = (size_t)(addr->bus - self->start_bus) << 20 | (size_t)(addr->device & 0x1f) << 15 |
|
||||
(size_t)(addr->function & 0x7) << 12 | (offset & 0xffc);
|
||||
return (uint8_t*)ecam->base + index;
|
||||
}
|
||||
|
||||
static uint32_t ecam_read32(struct pci_access* self, const struct pci_address* addr, uint16_t offset) {
|
||||
return mmio_read32(ecam_ptr(self, addr, offset));
|
||||
}
|
||||
|
||||
static void ecam_write32(struct pci_access* self, const struct pci_address* addr, uint16_t offset, uint32_t value) {
|
||||
mmio_write32(ecam_ptr(self, addr, offset), value);
|
||||
}
|
||||
|
||||
errno_t pci_ecam_register(phys_t base, uint16_t domain, uint8_t start_bus, uint8_t end_bus) {
|
||||
if (end_bus < start_bus)
|
||||
return EINVAL;
|
||||
|
||||
size_t length = ((size_t)(end_bus - start_bus) + 1) << 20;
|
||||
phys_t phys = base + ((phys_t)start_bus << 20);
|
||||
|
||||
uintptr_t vaddr = vm_alloc(length);
|
||||
if (!vaddr)
|
||||
return ENOMEM;
|
||||
|
||||
for (size_t i = 0; i < length; i += arch_mem_page_size()) {
|
||||
errno_t status = pmap_map(&kernel_vma.pmap, vaddr + i, phys + i, PTE_READ | PTE_WRITE, CACHE_MMIO);
|
||||
if (status != 0)
|
||||
return status;
|
||||
}
|
||||
|
||||
struct pci_ecam* ecam = mem_alloc(sizeof(*ecam), 0);
|
||||
if (ecam == nullptr)
|
||||
return ENOMEM;
|
||||
|
||||
*ecam = (struct pci_ecam){
|
||||
.access =
|
||||
{
|
||||
.name = "ecam",
|
||||
.domain = domain,
|
||||
.start_bus = start_bus,
|
||||
.end_bus = end_bus,
|
||||
.priority = PCI_ACCESS_PRIO_ECAM,
|
||||
.read32 = ecam_read32,
|
||||
.write32 = ecam_write32,
|
||||
},
|
||||
.base = (void*)vaddr,
|
||||
};
|
||||
|
||||
errno_t status = pci_access_register(&ecam->access);
|
||||
if (status != 0) {
|
||||
mem_free(ecam);
|
||||
return status;
|
||||
}
|
||||
|
||||
log("ECAM for %.4hx:%.2hhx - %.4hx:%.2hhx at %p\n", domain, start_bus, domain, end_bus, (void*)phys);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
kernel_sources += files(
|
||||
'core.c',
|
||||
'ecam.c',
|
||||
)
|
||||
@@ -1,3 +0,0 @@
|
||||
kernel_sources += files(
|
||||
'elf.c',
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
subdir('exec')
|
||||
@@ -1,7 +0,0 @@
|
||||
#include <zinnia/ident.h>
|
||||
|
||||
static const struct ident kernel_identity = {0};
|
||||
|
||||
const struct ident* ident_kernel() {
|
||||
return &kernel_identity;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
#include <zinnia/vfs.h>
|
||||
|
||||
void vfs_init() {}
|
||||
@@ -1,4 +0,0 @@
|
||||
kernel_sources += files(
|
||||
'ident.c',
|
||||
'init.c',
|
||||
)
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct syscall_ret arch_archctl(uintptr_t op, uintptr_t value);
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: Ensure bits are defining values right.
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/irq.h>
|
||||
|
||||
bool arch_irq_set_state(bool state);
|
||||
bool arch_irq_get_state();
|
||||
void arch_irq_wait();
|
||||
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
[[__pure]]
|
||||
bool arch_mem_addr_is_user(uintptr_t addr);
|
||||
|
||||
[[__pure]]
|
||||
size_t arch_mem_page_size();
|
||||
|
||||
[[__pure]]
|
||||
size_t arch_mem_page_bits();
|
||||
|
||||
[[__pure]]
|
||||
size_t arch_mem_level_bits();
|
||||
|
||||
[[__pure]]
|
||||
size_t arch_mem_num_levels();
|
||||
|
||||
[[__pure]]
|
||||
uintptr_t arch_mem_hhdm_addr();
|
||||
|
||||
[[__pure]]
|
||||
uintptr_t arch_mem_pfndb_addr();
|
||||
|
||||
[[__pure]]
|
||||
uintptr_t arch_mem_mapping_addr();
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/percpu.h>
|
||||
|
||||
struct percpu* arch_percpu_get();
|
||||
void arch_percpu_bsp_init();
|
||||
void arch_percpu_init();
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/sched.h>
|
||||
|
||||
void arch_sched_preempt_disable();
|
||||
bool arch_sched_preempt_enable();
|
||||
struct task* arch_sched_switch(struct task* from, struct task* to);
|
||||
errno_t arch_task_init(
|
||||
struct arch_task_context* context,
|
||||
void* entry,
|
||||
uintptr_t arg0,
|
||||
uintptr_t stack_start,
|
||||
bool is_user
|
||||
);
|
||||
void arch_task_fini(struct arch_task_context* context);
|
||||
[[noreturn]]
|
||||
void arch_sched_jump_to_context(struct arch_context* context);
|
||||
[[noreturn]]
|
||||
void arch_sched_jump_to_user(uintptr_t ip, uintptr_t sp, uintptr_t arg);
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/usercopy.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void arch_usercopy_allow(bool state);
|
||||
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);
|
||||
@@ -1,10 +1,11 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
// Attributes that affect data layout.
|
||||
#define __section(x) gnu::section(x)
|
||||
#define __aligned(x) gnu::aligned(x)
|
||||
#define __packed gnu::packed
|
||||
#define __may_alias gnu::may_alias
|
||||
#define __naked gnu::naked
|
||||
|
||||
// These hints may improve code generation.
|
||||
@@ -13,12 +14,11 @@
|
||||
#define __used gnu::used
|
||||
#define __unused gnu::unused
|
||||
#define __format(like, ...) gnu::format(like, __VA_ARGS__)
|
||||
#define __force_inline gnu::always_inline
|
||||
#define __visibility gnu::visibility
|
||||
#define __pure gnu::pure
|
||||
#define __likely(x) __builtin_expect(!!(x), 1)
|
||||
#define __unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
// Only clang has these hints, GCC would throw a warning here.
|
||||
// clang::noderef also doesn't support the C23 attribute format.
|
||||
// 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)))
|
||||
@@ -26,6 +26,3 @@
|
||||
#define __user
|
||||
#define __phys
|
||||
#endif
|
||||
|
||||
#define __likely(x) __builtin_expect(!!(x), 1)
|
||||
#define __unlikely(x) __builtin_expect(!!(x), 0)
|
||||
@@ -1,8 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/vfs.h>
|
||||
#include <arch/elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
|
||||
@@ -74,20 +73,6 @@ enum : uint32_t {
|
||||
PT_TLS = 0x00000007,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
PT_MOD_VERSION = 0x60000001,
|
||||
PT_MOD_AUTHOR = 0x60000002,
|
||||
PT_MOD_DESC = 0x60000003,
|
||||
PT_MOD_LICENSE = 0x60000004,
|
||||
PT_MOD_CMDLINE = 0x60000005,
|
||||
PT_MOD_EXIT = 0x60000006,
|
||||
PT_MOD_MATCH = 0x60000007,
|
||||
};
|
||||
|
||||
enum : uint16_t {
|
||||
SHN_UNDEF = 0,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
PF_X = 0x00000001,
|
||||
PF_W = 0x00000002,
|
||||
@@ -124,9 +109,6 @@ enum : uint32_t {
|
||||
DT_FINI_ARRAY = 26,
|
||||
DT_INIT_ARRAYSZ = 27,
|
||||
DT_FINI_ARRAYSZ = 28,
|
||||
DT_RELRSZ = 35,
|
||||
DT_RELR = 36,
|
||||
DT_RELRENT = 37,
|
||||
DT_LOOS = 0x60000000,
|
||||
DT_HIOS = 0x6FFFFFFF,
|
||||
DT_LOPROC = 0x70000000,
|
||||
@@ -157,7 +139,7 @@ enum : uint32_t {
|
||||
AT_INIT_HANDLE = 0x1000,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_ehdr {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
@@ -172,9 +154,9 @@ typedef struct {
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} elf64_ehdr_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf32_ehdr {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
@@ -189,9 +171,9 @@ typedef struct {
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} elf32_ehdr_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_phdr {
|
||||
uint32_t p_type;
|
||||
uint32_t p_flags;
|
||||
elf64_off_t p_offset;
|
||||
@@ -200,9 +182,9 @@ typedef struct {
|
||||
uint64_t p_filesz;
|
||||
uint64_t p_memsz;
|
||||
uint64_t p_align;
|
||||
} elf64_phdr_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf32_phdr {
|
||||
uint32_t p_type;
|
||||
elf32_off_t p_offset;
|
||||
elf32_addr_t p_vaddr;
|
||||
@@ -211,68 +193,68 @@ typedef struct {
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
} elf32_phdr_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_dyn {
|
||||
int64_t d_tag;
|
||||
union {
|
||||
uint64_t d_val;
|
||||
elf64_addr_t d_ptr;
|
||||
} d_un;
|
||||
} elf64_dyn_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf32_dyn {
|
||||
int32_t r_offset;
|
||||
union {
|
||||
uint32_t d_val;
|
||||
elf32_addr_t d_ptr;
|
||||
} d_un;
|
||||
} elf32_dyn_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_nhdr {
|
||||
uint64_t n_namesz;
|
||||
uint64_t n_descsz;
|
||||
uint64_t n_type;
|
||||
} elf64_nhdr_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf32_nhdr {
|
||||
uint32_t n_namesz;
|
||||
uint32_t n_descsz;
|
||||
uint32_t n_type;
|
||||
} elf32_nhdr_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_auxv {
|
||||
uint64_t atype;
|
||||
uint64_t avalue;
|
||||
} elf64_auxv_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf32_auxv {
|
||||
uint32_t atype;
|
||||
uint32_t avalue;
|
||||
} elf32_auxv_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf_hash {
|
||||
uint32_t n_buckets;
|
||||
uint32_t n_chain;
|
||||
} elf_hash_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
} elf32_sym_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
} elf64_sym_t;
|
||||
};
|
||||
|
||||
static uint8_t ELF64_ST_BIND(uint8_t info) {
|
||||
return info >> 4;
|
||||
@@ -289,7 +271,7 @@ static inline uint8_t ELF64_ST_INFO(uint8_t bind, uint8_t type) {
|
||||
typedef struct {
|
||||
uint16_t si_boundto;
|
||||
uint16_t si_flags;
|
||||
} elf64_syminfo_t;
|
||||
} elf64_syminfo;
|
||||
|
||||
static inline uint8_t ELF32_ST_BIND(uint8_t info) {
|
||||
return info >> 4;
|
||||
@@ -321,30 +303,27 @@ enum {
|
||||
STT_HIPROC = 15
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf32_rela {
|
||||
elf32_addr_t r_offset;
|
||||
uint32_t r_info;
|
||||
int32_t r_addend;
|
||||
} elf32_rela_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_rela {
|
||||
elf64_addr_t r_offset;
|
||||
uint64_t r_info;
|
||||
int64_t r_addend;
|
||||
} elf64_rela_t;
|
||||
};
|
||||
|
||||
typedef uint64_t elf64_relr_t;
|
||||
typedef uint32_t elf32_relr_t;
|
||||
|
||||
typedef struct {
|
||||
struct elf32_rel {
|
||||
elf32_addr_t r_offset;
|
||||
uint32_t r_info;
|
||||
} elf32_rel_t;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct elf64_rel {
|
||||
elf64_addr_t r_offset;
|
||||
uint64_t r_info;
|
||||
} elf64_rel_t;
|
||||
};
|
||||
|
||||
static inline uint32_t ELF32_R_SYM(uint32_t info) {
|
||||
return info >> 8;
|
||||
@@ -362,4 +341,35 @@ static inline uint32_t ELF64_R_TYPE(uint64_t info) {
|
||||
return info & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
errno_t elf_load(struct exec_info* info, struct task** out);
|
||||
// Architecture specific bits.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#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,
|
||||
};
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
@@ -20,13 +22,13 @@
|
||||
__builtin_types_compatible_p(typeof(*(ptr)), typeof(((type*)0)->member)), \
|
||||
"Incompatible types for containerof" \
|
||||
); \
|
||||
(type*)((size_t)(ptr) - offsetof(type, member)); \
|
||||
(type*)((size_t)ptr - offsetof(type, member)); \
|
||||
})
|
||||
#endif
|
||||
#endif
|
||||
#ifndef containerof
|
||||
// Get pointer to parent object given pointer to a struct or union member.
|
||||
#define containerof(ptr, type, member) (type*)((size_t)(ptr) - offsetof(type, member))
|
||||
#define containerof(ptr, type, member) (type*)((size_t)ptr - offsetof(type, member))
|
||||
#endif
|
||||
|
||||
#define CONCAT(A, B) _CONCAT(A, B)
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/channel.h>
|
||||
#include <zinnia/handle.h>
|
||||
#include <common/utils.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define ZN_IPC_NUM(group, num) ((uint32_t)(group) << 24u) | ((uint32_t)(num) & 0xffffffu)
|
||||
|
||||
#define ZN_IPC(name, group, num, num_handles) \
|
||||
name##_inner; \
|
||||
constexpr uint32_t name##_num = ZN_IPC_NUM(group, num); \
|
||||
typedef struct { \
|
||||
uint32_t id; \
|
||||
zn_handle_t handles[num_handles]; \
|
||||
name##_inner inner; \
|
||||
} name
|
||||
|
||||
#define zn_ipc_write(channel, obj) \
|
||||
zn_channel_write(channel, (obj)->handles, &(obj)->inner, ARRAY_SIZE((obj)->handles), sizeof((obj)->inner))
|
||||
|
||||
#define zn_ipc_read(channel, obj) \
|
||||
zn_channel_read( \
|
||||
channel, \
|
||||
(obj)->handles, \
|
||||
&(obj)->inner, \
|
||||
ARRAY_SIZE((obj)->handles), \
|
||||
sizeof((obj)->inner), \
|
||||
nullptr, \
|
||||
nullptr \
|
||||
)
|
||||
|
||||
#define zn_ipc_new(name) \
|
||||
(zn_ipc_fw_rsdp) { \
|
||||
.id = name##_num \
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#undef va_start
|
||||
#define va_start(v, ...) __builtin_va_start(v, 0)
|
||||
|
||||
#undef va_end
|
||||
#define va_end(v) __builtin_va_end(v)
|
||||
|
||||
#undef va_arg
|
||||
#define va_arg(v, l) __builtin_va_arg(v, l)
|
||||
|
||||
#undef va_copy
|
||||
#define va_copy(d, s) __builtin_va_copy(d, s)
|
||||
@@ -1,163 +0,0 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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_thread_fence(order) __atomic_thread_fence(order)
|
||||
#define atomic_signal_fence(order) __atomic_signal_fence(order)
|
||||
|
||||
#ifdef __clang__
|
||||
|
||||
#define atomic_init(obj, value) __c11_atomic_init(obj, value)
|
||||
|
||||
#define atomic_store_explicit(obj, val, order) __c11_atomic_store(obj, val, order)
|
||||
#define atomic_load_explicit(obj, order) __c11_atomic_load(obj, order)
|
||||
|
||||
#define atomic_exchange_explicit(obj, val, order) __c11_atomic_exchange(obj, val, order)
|
||||
|
||||
#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \
|
||||
__c11_atomic_compare_exchange_strong(obj, expected, desired, succ, fail)
|
||||
#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \
|
||||
__c11_atomic_compare_exchange_weak(obj, expected, desired, succ, fail)
|
||||
|
||||
#define atomic_fetch_add_explicit(obj, arg, order) __c11_atomic_fetch_add(obj, arg, order)
|
||||
#define atomic_fetch_sub_explicit(obj, arg, order) __c11_atomic_fetch_sub(obj, arg, order)
|
||||
#define atomic_fetch_or_explicit(obj, arg, order) __c11_atomic_fetch_or(obj, arg, order)
|
||||
#define atomic_fetch_xor_explicit(obj, arg, order) __c11_atomic_fetch_xor(obj, arg, order)
|
||||
#define atomic_fetch_and_explicit(obj, arg, order) __c11_atomic_fetch_and(obj, arg, order)
|
||||
|
||||
#define atomic_is_lock_free(obj) __c11_atomic_is_lock_free(sizeof(*(obj)))
|
||||
|
||||
#else /* GCC */
|
||||
|
||||
#define atomic_init(obj, value) \
|
||||
do { \
|
||||
*(obj) = (value); \
|
||||
} while (0)
|
||||
|
||||
#define atomic_store_explicit(obj, val, order) \
|
||||
({ \
|
||||
auto __ao = (obj); \
|
||||
typeof(*__ao) __av = (val); \
|
||||
__atomic_store(__ao, &__av, order); \
|
||||
})
|
||||
|
||||
#define atomic_load_explicit(obj, order) \
|
||||
({ \
|
||||
auto __ao = (obj); \
|
||||
typeof(*__ao) __ar; \
|
||||
__atomic_load(__ao, &__ar, order); \
|
||||
__ar; \
|
||||
})
|
||||
|
||||
#define atomic_exchange_explicit(obj, val, order) \
|
||||
({ \
|
||||
auto __ao = (obj); \
|
||||
typeof(*__ao) __av = (val); \
|
||||
typeof(*__ao) __ar; \
|
||||
__atomic_exchange(__ao, &__av, &__ar, order); \
|
||||
__ar; \
|
||||
})
|
||||
|
||||
#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \
|
||||
({ \
|
||||
auto __ao = (obj); \
|
||||
auto __ae = (expected); \
|
||||
typeof(*__ao) __ad = (desired); \
|
||||
__atomic_compare_exchange(__ao, __ae, &__ad, 0, succ, fail); \
|
||||
})
|
||||
|
||||
#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \
|
||||
({ \
|
||||
auto __ao = (obj); \
|
||||
auto __ae = (expected); \
|
||||
typeof(*__ao) __ad = (desired); \
|
||||
__atomic_compare_exchange(__ao, __ae, &__ad, 1, succ, fail); \
|
||||
})
|
||||
|
||||
#define atomic_fetch_add_explicit(obj, arg, order) __atomic_fetch_add(obj, arg, order)
|
||||
#define atomic_fetch_sub_explicit(obj, arg, order) __atomic_fetch_sub(obj, arg, order)
|
||||
#define atomic_fetch_or_explicit(obj, arg, order) __atomic_fetch_or(obj, arg, order)
|
||||
#define atomic_fetch_xor_explicit(obj, arg, order) __atomic_fetch_xor(obj, arg, order)
|
||||
#define atomic_fetch_and_explicit(obj, arg, order) __atomic_fetch_and(obj, arg, order)
|
||||
|
||||
#define atomic_is_lock_free(obj) __atomic_is_lock_free(sizeof(*(obj)), (obj))
|
||||
|
||||
#endif
|
||||
|
||||
#define atomic_store(obj, val) atomic_store_explicit(obj, val, memory_order_seq_cst)
|
||||
#define atomic_load(obj) atomic_load_explicit(obj, memory_order_seq_cst)
|
||||
|
||||
#define atomic_exchange(obj, val) atomic_exchange_explicit(obj, val, memory_order_seq_cst)
|
||||
|
||||
#define atomic_compare_exchange_strong(obj, expected, desired) \
|
||||
atomic_compare_exchange_strong_explicit(obj, expected, desired, memory_order_seq_cst, memory_order_seq_cst)
|
||||
#define atomic_compare_exchange_weak(obj, expected, desired) \
|
||||
atomic_compare_exchange_weak_explicit(obj, expected, desired, memory_order_seq_cst, memory_order_seq_cst)
|
||||
|
||||
#define atomic_fetch_add(obj, arg) atomic_fetch_add_explicit(obj, arg, memory_order_seq_cst)
|
||||
#define atomic_fetch_sub(obj, arg) atomic_fetch_sub_explicit(obj, arg, memory_order_seq_cst)
|
||||
#define atomic_fetch_or(obj, arg) atomic_fetch_or_explicit(obj, arg, memory_order_seq_cst)
|
||||
#define atomic_fetch_xor(obj, arg) atomic_fetch_xor_explicit(obj, arg, memory_order_seq_cst)
|
||||
#define atomic_fetch_and(obj, arg) atomic_fetch_and_explicit(obj, arg, memory_order_seq_cst)
|
||||
|
||||
// atomic_flag
|
||||
typedef struct atomic_flag {
|
||||
_Atomic(bool) __val;
|
||||
} atomic_flag;
|
||||
|
||||
#define ATOMIC_FLAG_INIT {0}
|
||||
|
||||
#define atomic_flag_test_and_set_explicit(obj, order) atomic_exchange_explicit(&(obj)->__val, 1, order)
|
||||
#define atomic_flag_test_and_set(obj) atomic_flag_test_and_set_explicit(obj, memory_order_seq_cst)
|
||||
#define atomic_flag_clear_explicit(obj, order) atomic_store_explicit(&(obj)->__val, 0, order)
|
||||
#define atomic_flag_clear(obj) atomic_flag_clear_explicit(obj, memory_order_seq_cst)
|
||||
|
||||
typedef _Atomic(bool) atomic_bool;
|
||||
typedef _Atomic(char) atomic_char;
|
||||
typedef _Atomic(signed char) atomic_schar;
|
||||
typedef _Atomic(unsigned char) atomic_uchar;
|
||||
typedef _Atomic(short) atomic_short;
|
||||
typedef _Atomic(unsigned short) atomic_ushort;
|
||||
typedef _Atomic(int) atomic_int;
|
||||
typedef _Atomic(unsigned int) atomic_uint;
|
||||
typedef _Atomic(long) atomic_long;
|
||||
typedef _Atomic(unsigned long) atomic_ulong;
|
||||
typedef _Atomic(long long) atomic_llong;
|
||||
typedef _Atomic(unsigned long long) atomic_ullong;
|
||||
typedef _Atomic(__CHAR16_TYPE__) atomic_char16_t;
|
||||
typedef _Atomic(__CHAR32_TYPE__) atomic_char32_t;
|
||||
typedef _Atomic(__WCHAR_TYPE__) atomic_wchar_t;
|
||||
|
||||
typedef _Atomic(int_least8_t) atomic_int_least8_t;
|
||||
typedef _Atomic(uint_least8_t) atomic_uint_least8_t;
|
||||
typedef _Atomic(int_least16_t) atomic_int_least16_t;
|
||||
typedef _Atomic(uint_least16_t) atomic_uint_least16_t;
|
||||
typedef _Atomic(int_least32_t) atomic_int_least32_t;
|
||||
typedef _Atomic(uint_least32_t) atomic_uint_least32_t;
|
||||
typedef _Atomic(int_least64_t) atomic_int_least64_t;
|
||||
typedef _Atomic(uint_least64_t) atomic_uint_least64_t;
|
||||
typedef _Atomic(int_fast8_t) atomic_int_fast8_t;
|
||||
typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t;
|
||||
typedef _Atomic(int_fast16_t) atomic_int_fast16_t;
|
||||
typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t;
|
||||
typedef _Atomic(int_fast32_t) atomic_int_fast32_t;
|
||||
typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t;
|
||||
typedef _Atomic(int_fast64_t) atomic_int_fast64_t;
|
||||
typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t;
|
||||
typedef _Atomic(intptr_t) atomic_intptr_t;
|
||||
typedef _Atomic(uintptr_t) atomic_uintptr_t;
|
||||
typedef _Atomic(size_t) atomic_size_t;
|
||||
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
|
||||
typedef _Atomic(intmax_t) atomic_intmax_t;
|
||||
typedef _Atomic(uintmax_t) atomic_uintmax_t;
|
||||
@@ -1,6 +0,0 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
#define ckd_add(result, a, b) __builtin_add_overflow(a, b, result)
|
||||
#define ckd_sub(result, a, b) __builtin_sub_overflow(a, b, result)
|
||||
#define ckd_mul(result, a, b) __builtin_mul_overflow(a, b, result)
|
||||
@@ -1,14 +0,0 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
||||
|
||||
typedef __WCHAR_TYPE__ wchar_t;
|
||||
typedef typeof(nullptr) nullptr_t;
|
||||
|
||||
#undef offsetof
|
||||
#define offsetof(s, m) __builtin_offsetof(s, m)
|
||||
|
||||
#undef unreachable
|
||||
#define unreachable() __builtin_unreachable()
|
||||
@@ -1,354 +0,0 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
#ifdef __UINT8_TYPE__
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
#endif
|
||||
#ifdef __UINT16_TYPE__
|
||||
typedef __UINT16_TYPE__ uint16_t;
|
||||
#endif
|
||||
#ifdef __UINT32_TYPE__
|
||||
typedef __UINT32_TYPE__ uint32_t;
|
||||
#endif
|
||||
#ifdef __UINT64_TYPE__
|
||||
typedef __UINT64_TYPE__ uint64_t;
|
||||
#endif
|
||||
|
||||
typedef __UINT_LEAST8_TYPE__ uint_least8_t;
|
||||
typedef __UINT_LEAST16_TYPE__ uint_least16_t;
|
||||
typedef __UINT_LEAST32_TYPE__ uint_least32_t;
|
||||
typedef __UINT_LEAST64_TYPE__ uint_least64_t;
|
||||
|
||||
typedef __UINT_FAST8_TYPE__ uint_fast8_t;
|
||||
typedef __UINT_FAST16_TYPE__ uint_fast16_t;
|
||||
typedef __UINT_FAST32_TYPE__ uint_fast32_t;
|
||||
typedef __UINT_FAST64_TYPE__ uint_fast64_t;
|
||||
|
||||
#ifdef __INT8_TYPE__
|
||||
typedef __INT8_TYPE__ int8_t;
|
||||
#endif
|
||||
#ifdef __INT16_TYPE__
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
#endif
|
||||
#ifdef __INT32_TYPE__
|
||||
typedef __INT32_TYPE__ int32_t;
|
||||
#endif
|
||||
#ifdef __INT64_TYPE__
|
||||
typedef __INT64_TYPE__ int64_t;
|
||||
#endif
|
||||
|
||||
typedef __INT_LEAST8_TYPE__ int_least8_t;
|
||||
typedef __INT_LEAST16_TYPE__ int_least16_t;
|
||||
typedef __INT_LEAST32_TYPE__ int_least32_t;
|
||||
typedef __INT_LEAST64_TYPE__ int_least64_t;
|
||||
|
||||
typedef __INT_FAST8_TYPE__ int_fast8_t;
|
||||
typedef __INT_FAST16_TYPE__ int_fast16_t;
|
||||
typedef __INT_FAST32_TYPE__ int_fast32_t;
|
||||
typedef __INT_FAST64_TYPE__ int_fast64_t;
|
||||
|
||||
#ifdef __UINTPTR_TYPE__
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
#endif
|
||||
#ifdef __INTPTR_TYPE__
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
#endif
|
||||
|
||||
typedef __UINTMAX_TYPE__ uintmax_t;
|
||||
typedef __INTMAX_TYPE__ intmax_t;
|
||||
|
||||
// Clang and GCC have different mechanisms for INT32_C and friends.
|
||||
#ifdef __clang__
|
||||
#ifndef __FREESTANDING_C_HDRS_C_JOIN
|
||||
#define __FREESTANDING_C_HDRS_C_EXPAND_JOIN(x, suffix) x##suffix
|
||||
#define __FREESTANDING_C_HDRS_C_JOIN(x, suffix) __FREESTANDING_C_HDRS_C_EXPAND_JOIN(x, suffix)
|
||||
#endif
|
||||
|
||||
#undef INT8_C
|
||||
#define INT8_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __INT8_C_SUFFIX__)
|
||||
#undef INT16_C
|
||||
#define INT16_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __INT16_C_SUFFIX__)
|
||||
#undef INT32_C
|
||||
#define INT32_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __INT32_C_SUFFIX__)
|
||||
#undef INT64_C
|
||||
#define INT64_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __INT64_C_SUFFIX__)
|
||||
|
||||
#undef UINT8_C
|
||||
#define UINT8_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __UINT8_C_SUFFIX__)
|
||||
#undef UINT16_C
|
||||
#define UINT16_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __UINT16_C_SUFFIX__)
|
||||
#undef UINT32_C
|
||||
#define UINT32_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __UINT32_C_SUFFIX__)
|
||||
#undef UINT64_C
|
||||
#define UINT64_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __UINT64_C_SUFFIX__)
|
||||
|
||||
#undef INTMAX_C
|
||||
#define INTMAX_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __INTMAX_C_SUFFIX__)
|
||||
#undef UINTMAX_C
|
||||
#define UINTMAX_C(x) __FREESTANDING_C_HDRS_C_JOIN(x, __UINTMAX_C_SUFFIX__)
|
||||
#else
|
||||
#undef INT8_C
|
||||
#define INT8_C(x) __INT8_C(x)
|
||||
#undef INT16_C
|
||||
#define INT16_C(x) __INT16_C(x)
|
||||
#undef INT32_C
|
||||
#define INT32_C(x) __INT32_C(x)
|
||||
#undef INT64_C
|
||||
#define INT64_C(x) __INT64_C(x)
|
||||
|
||||
#undef UINT8_C
|
||||
#define UINT8_C(x) __UINT8_C(x)
|
||||
#undef UINT16_C
|
||||
#define UINT16_C(x) __UINT16_C(x)
|
||||
#undef UINT32_C
|
||||
#define UINT32_C(x) __UINT32_C(x)
|
||||
#undef UINT64_C
|
||||
#define UINT64_C(x) __UINT64_C(x)
|
||||
|
||||
#undef INTMAX_C
|
||||
#define INTMAX_C(x) __INTMAX_C(x)
|
||||
#undef UINTMAX_C
|
||||
#define UINTMAX_C(x) __UINTMAX_C(x)
|
||||
#endif
|
||||
|
||||
#ifdef __UINT8_MAX__
|
||||
#undef UINT8_MAX
|
||||
#define UINT8_MAX __UINT8_MAX__
|
||||
#endif
|
||||
#ifdef __UINT16_MAX__
|
||||
#undef UINT16_MAX
|
||||
#define UINT16_MAX __UINT16_MAX__
|
||||
#endif
|
||||
#ifdef __UINT32_MAX__
|
||||
#undef UINT32_MAX
|
||||
#define UINT32_MAX __UINT32_MAX__
|
||||
#endif
|
||||
#ifdef __UINT64_MAX__
|
||||
#undef UINT64_MAX
|
||||
#define UINT64_MAX __UINT64_MAX__
|
||||
#endif
|
||||
|
||||
#ifdef __INT8_MAX__
|
||||
#undef INT8_MAX
|
||||
#define INT8_MAX __INT8_MAX__
|
||||
#endif
|
||||
#ifdef __INT16_MAX__
|
||||
#undef INT16_MAX
|
||||
#define INT16_MAX __INT16_MAX__
|
||||
#endif
|
||||
#ifdef __INT32_MAX__
|
||||
#undef INT32_MAX
|
||||
#define INT32_MAX __INT32_MAX__
|
||||
#endif
|
||||
#ifdef __INT64_MAX__
|
||||
#undef INT64_MAX
|
||||
#define INT64_MAX __INT64_MAX__
|
||||
#endif
|
||||
|
||||
#ifdef __INT8_MAX__
|
||||
#undef INT8_MIN
|
||||
#define INT8_MIN (-INT8_MAX - 1)
|
||||
#endif
|
||||
#ifdef __INT16_MAX__
|
||||
#undef INT16_MIN
|
||||
#define INT16_MIN (-INT16_MAX - 1)
|
||||
#endif
|
||||
#ifdef __INT32_MAX__
|
||||
#undef INT32_MIN
|
||||
#define INT32_MIN (-INT32_MAX - 1)
|
||||
#endif
|
||||
#ifdef __INT64_MAX__
|
||||
#undef INT64_MIN
|
||||
#define INT64_MIN (-INT64_MAX - 1)
|
||||
#endif
|
||||
|
||||
#undef UINT_LEAST8_MAX
|
||||
#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__
|
||||
#undef UINT_LEAST16_MAX
|
||||
#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__
|
||||
#undef UINT_LEAST32_MAX
|
||||
#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__
|
||||
#undef UINT_LEAST64_MAX
|
||||
#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__
|
||||
|
||||
#undef INT_LEAST8_MAX
|
||||
#define INT_LEAST8_MAX __INT_LEAST8_MAX__
|
||||
#undef INT_LEAST16_MAX
|
||||
#define INT_LEAST16_MAX __INT_LEAST16_MAX__
|
||||
#undef INT_LEAST32_MAX
|
||||
#define INT_LEAST32_MAX __INT_LEAST32_MAX__
|
||||
#undef INT_LEAST64_MAX
|
||||
#define INT_LEAST64_MAX __INT_LEAST64_MAX__
|
||||
|
||||
#undef INT_LEAST8_MIN
|
||||
#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1)
|
||||
#undef INT_LEAST16_MIN
|
||||
#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1)
|
||||
#undef INT_LEAST32_MIN
|
||||
#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1)
|
||||
#undef INT_LEAST64_MIN
|
||||
#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1)
|
||||
|
||||
#undef UINT_FAST8_MAX
|
||||
#define UINT_FAST8_MAX __UINT_FAST8_MAX__
|
||||
#undef UINT_FAST16_MAX
|
||||
#define UINT_FAST16_MAX __UINT_FAST16_MAX__
|
||||
#undef UINT_FAST32_MAX
|
||||
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
|
||||
#undef UINT_FAST64_MAX
|
||||
#define UINT_FAST64_MAX __UINT_FAST64_MAX__
|
||||
|
||||
#undef INT_FAST8_MAX
|
||||
#define INT_FAST8_MAX __INT_FAST8_MAX__
|
||||
#undef INT_FAST16_MAX
|
||||
#define INT_FAST16_MAX __INT_FAST16_MAX__
|
||||
#undef INT_FAST32_MAX
|
||||
#define INT_FAST32_MAX __INT_FAST32_MAX__
|
||||
#undef INT_FAST64_MAX
|
||||
#define INT_FAST64_MAX __INT_FAST64_MAX__
|
||||
|
||||
#undef INT_FAST8_MIN
|
||||
#define INT_FAST8_MIN (-INT_FAST8_MAX - 1)
|
||||
#undef INT_FAST16_MIN
|
||||
#define INT_FAST16_MIN (-INT_FAST16_MAX - 1)
|
||||
#undef INT_FAST32_MIN
|
||||
#define INT_FAST32_MIN (-INT_FAST32_MAX - 1)
|
||||
#undef INT_FAST64_MIN
|
||||
#define INT_FAST64_MIN (-INT_FAST64_MAX - 1)
|
||||
|
||||
#ifdef __UINTPTR_MAX__
|
||||
#undef UINTPTR_MAX
|
||||
#define UINTPTR_MAX __UINTPTR_MAX__
|
||||
#endif
|
||||
#ifdef __INTPTR_MAX__
|
||||
#undef INTPTR_MAX
|
||||
#define INTPTR_MAX __INTPTR_MAX__
|
||||
#undef INTPTR_MIN
|
||||
#define INTPTR_MIN (-INTPTR_MAX - 1)
|
||||
#endif
|
||||
|
||||
#undef UINTMAX_MAX
|
||||
#define UINTMAX_MAX __UINTMAX_MAX__
|
||||
#undef INTMAX_MAX
|
||||
#define INTMAX_MAX __INTMAX_MAX__
|
||||
#undef INTMAX_MIN
|
||||
#define INTMAX_MIN (-INTMAX_MAX - 1)
|
||||
|
||||
#undef PTRDIFF_MAX
|
||||
#define PTRDIFF_MAX __PTRDIFF_MAX__
|
||||
#undef PTRDIFF_MIN
|
||||
#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)
|
||||
|
||||
#undef SIG_ATOMIC_MAX
|
||||
#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
|
||||
#undef SIG_ATOMIC_MIN
|
||||
#define SIG_ATOMIC_MIN (-SIG_ATOMIC_MAX - 1)
|
||||
|
||||
#undef SIZE_MAX
|
||||
#define SIZE_MAX __SIZE_MAX__
|
||||
|
||||
#undef WCHAR_MAX
|
||||
#define WCHAR_MAX __WCHAR_MAX__
|
||||
#undef WCHAR_MIN
|
||||
#define WCHAR_MIN (-WCHAR_MAX - 1)
|
||||
|
||||
#undef WINT_MAX
|
||||
#define WINT_MAX __WINT_MAX__
|
||||
#undef WINT_MIN
|
||||
#define WINT_MIN (-WINT_MAX - 1)
|
||||
|
||||
#ifdef __INT8_TYPE__
|
||||
#undef INT8_WIDTH
|
||||
#define INT8_WIDTH 8
|
||||
#endif
|
||||
#ifdef __UINT8_TYPE__
|
||||
#undef UINT8_WIDTH
|
||||
#define UINT8_WIDTH 8
|
||||
#endif
|
||||
#ifdef __INT16_TYPE__
|
||||
#undef INT16_WIDTH
|
||||
#define INT16_WIDTH 16
|
||||
#endif
|
||||
#ifdef __UINT16_TYPE__
|
||||
#undef UINT16_WIDTH
|
||||
#define UINT16_WIDTH 16
|
||||
#endif
|
||||
#ifdef __INT32_TYPE__
|
||||
#undef INT32_WIDTH
|
||||
#define INT32_WIDTH 32
|
||||
#endif
|
||||
#ifdef __UINT32_TYPE__
|
||||
#undef UINT32_WIDTH
|
||||
#define UINT32_WIDTH 32
|
||||
#endif
|
||||
#ifdef __INT64_TYPE__
|
||||
#undef INT64_WIDTH
|
||||
#define INT64_WIDTH 64
|
||||
#endif
|
||||
#ifdef __UINT64_TYPE__
|
||||
#undef UINT64_WIDTH
|
||||
#define UINT64_WIDTH 64
|
||||
#endif
|
||||
|
||||
#undef INT_LEAST8_WIDTH
|
||||
#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
||||
#undef UINT_LEAST8_WIDTH
|
||||
#define UINT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
||||
#undef INT_LEAST16_WIDTH
|
||||
#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
||||
#undef UINT_LEAST16_WIDTH
|
||||
#define UINT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
||||
#undef INT_LEAST32_WIDTH
|
||||
#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
||||
#undef UINT_LEAST32_WIDTH
|
||||
#define UINT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
||||
#undef INT_LEAST64_WIDTH
|
||||
#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
||||
#undef UINT_LEAST64_WIDTH
|
||||
#define UINT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
||||
|
||||
#undef INT_FAST8_WIDTH
|
||||
#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
||||
#undef UINT_FAST8_WIDTH
|
||||
#define UINT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
||||
#undef INT_FAST16_WIDTH
|
||||
#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
||||
#undef UINT_FAST16_WIDTH
|
||||
#define UINT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
||||
#undef INT_FAST32_WIDTH
|
||||
#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
||||
#undef UINT_FAST32_WIDTH
|
||||
#define UINT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
||||
#undef INT_FAST64_WIDTH
|
||||
#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
||||
#undef UINT_FAST64_WIDTH
|
||||
#define UINT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
||||
|
||||
#ifdef __INTPTR_TYPE__
|
||||
#undef INTPTR_WIDTH
|
||||
#define INTPTR_WIDTH __INTPTR_WIDTH__
|
||||
#endif
|
||||
#ifdef __UINTPTR_TYPE__
|
||||
#undef UINTPTR_WIDTH
|
||||
#define UINTPTR_WIDTH __INTPTR_WIDTH__
|
||||
#endif
|
||||
|
||||
#undef INTMAX_WIDTH
|
||||
#define INTMAX_WIDTH __INTMAX_WIDTH__
|
||||
#undef UINTMAX_WIDTH
|
||||
#define UINTMAX_WIDTH __INTMAX_WIDTH__
|
||||
|
||||
#undef PTRDIFF_WIDTH
|
||||
#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__
|
||||
|
||||
#undef SIG_ATOMIC_WIDTH
|
||||
#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__
|
||||
|
||||
#undef SIZE_WIDTH
|
||||
#define SIZE_WIDTH __SIZE_WIDTH__
|
||||
|
||||
#undef WCHAR_WIDTH
|
||||
#define WCHAR_WIDTH __WCHAR_WIDTH__
|
||||
|
||||
#undef WINT_WIDTH
|
||||
#define WINT_WIDTH __WINT_WIDTH__
|
||||
@@ -1,12 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#ifndef __ZINNIA_UAPI_ARCHCTL_H
|
||||
#define __ZINNIA_UAPI_ARCHCTL_H
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define __ARCHCTL_SET_FSBASE 0
|
||||
#define __ARCHCTL_SET_GSBASE 1
|
||||
#define __ARCHCTL_GET_FSBASE 2
|
||||
#define __ARCHCTL_GET_GSBASE 3
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#ifndef __ZINNIA_UAPI_DIRENT_H
|
||||
#define __ZINNIA_UAPI_DIRENT_H
|
||||
|
||||
#include <uapi/types.h>
|
||||
|
||||
#define __DT_UNKNOWN 0
|
||||
#define __DT_FIFO 1
|
||||
#define __DT_CHR 2
|
||||
#define __DT_DIR 4
|
||||
#define __DT_BLK 6
|
||||
#define __DT_REG 8
|
||||
#define __DT_LNK 10
|
||||
#define __DT_SOCK 12
|
||||
#define __DT_WHT 14
|
||||
|
||||
struct __dirent {
|
||||
__ino_t d_ino;
|
||||
__off_t d_off;
|
||||
uint16_t d_reclen;
|
||||
uint8_t d_type;
|
||||
char d_name[256];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#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
|
||||
@@ -1,140 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#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
|
||||
@@ -1,87 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#ifndef __ZINNIA_UAPI_FCNTL_H
|
||||
#define __ZINNIA_UAPI_FCNTL_H
|
||||
|
||||
#include <uapi/types.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)
|
||||
|
||||
#define __F_DUPFD 0
|
||||
#define __F_GETFD 1
|
||||
#define __F_SETFD 2
|
||||
#define __F_GETFL 3
|
||||
#define __F_SETFL 4
|
||||
#define __F_GETLK 5
|
||||
#define __F_SETLK 6
|
||||
#define __F_SETLKW 7
|
||||
#define __F_SETOWN 8
|
||||
#define __F_GETOWN 9
|
||||
#define __F_SETOWN_EX 15
|
||||
#define __F_GETOWN_EX 16
|
||||
#define __F_OFD_GETLK 36
|
||||
#define __F_OFD_SETLK 37
|
||||
#define __F_OFD_SETLKW 38
|
||||
#define __F_DUPFD_CLOEXEC 1030
|
||||
|
||||
#define __F_RDLCK 0
|
||||
#define __F_WRLCK 1
|
||||
#define __F_UNLCK 2
|
||||
|
||||
#define __FD_CLOEXEC 1
|
||||
|
||||
#define __SEEK_SET 0
|
||||
#define __SEEK_CUR 1
|
||||
#define __SEEK_END 2
|
||||
|
||||
#define __POSIX_FADV_NORMAL 0
|
||||
#define __POSIX_FADV_RANDOM 1
|
||||
#define __POSIX_FADV_SEQUENTIAL 2
|
||||
#define __POSIX_FADV_WILLNEED 3
|
||||
#define __POSIX_FADV_DONTNEED 4
|
||||
#define __POSIX_FADV_NOREUSE 5
|
||||
|
||||
struct __flock {
|
||||
int16_t l_type;
|
||||
int16_t l_whence;
|
||||
__off_t l_start;
|
||||
__off_t l_len;
|
||||
__pid_t l_pid;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,7 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#ifndef __ZINNIA_UAPI_IOCTLS_H
|
||||
#define __ZINNIA_UAPI_IOCTLS_H
|
||||
|
||||
#define __FIONBIO 0x5421
|
||||
|
||||
#endif
|
||||
@@ -1,9 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#ifndef __ZINNIA_UAPI_LIMITS_H
|
||||
#define __ZINNIA_UAPI_LIMITS_H
|
||||
|
||||
#define __PATH_MAX 4096
|
||||
#define __NAME_MAX 255
|
||||
#define __SYMLINK_MAX 4096
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#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 __MS_ASYNC 0x1
|
||||
#define __MS_INVALIDATE 0x2
|
||||
#define __MS_SYNC 0x4
|
||||
|
||||
#endif
|
||||
@@ -1,13 +0,0 @@
|
||||
/* SPDX-License-Identifier: Unlicense */
|
||||
#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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user