initgraph, whole lot of refactor
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <x86_64/asm.h>
|
||||
#include <x86_64/defs.h>
|
||||
#include <x86_64/gdt.h>
|
||||
|
||||
+11
-5
@@ -1,7 +1,10 @@
|
||||
#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>
|
||||
@@ -47,15 +50,15 @@ static struct hpet hpet_clock = {
|
||||
},
|
||||
};
|
||||
|
||||
static char table_buf[0x1000];
|
||||
|
||||
void hpet_init() {
|
||||
static void hpet_init(struct boot_info*) {
|
||||
if (!use_hpet)
|
||||
return;
|
||||
|
||||
uacpi_setup_early_table_access(table_buf, sizeof(table_buf));
|
||||
uacpi_table table = {0};
|
||||
assert(!uacpi_table_find_by_signature("HPET", &table), "HPET table not found!\n");
|
||||
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;
|
||||
|
||||
@@ -73,3 +76,6 @@ void hpet_init() {
|
||||
|
||||
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);
|
||||
|
||||
@@ -11,19 +11,21 @@
|
||||
#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
|
||||
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,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void hpet_init();
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <x86_64/gdt.h>
|
||||
|
||||
@@ -17,7 +17,7 @@ struct [[__packed]] smp_info {
|
||||
uint64_t hhdm_offset;
|
||||
uint64_t percpu;
|
||||
uint32_t lapic_id;
|
||||
_Atomic(uint8_t) booted;
|
||||
atomic_bool booted;
|
||||
};
|
||||
|
||||
// Discovers all APs and starts them.
|
||||
|
||||
@@ -23,3 +23,7 @@ kernel_sources += files(
|
||||
'tsc.c',
|
||||
'usercopy.c',
|
||||
)
|
||||
|
||||
if kconfig.get('CONFIG_PCI', 'n') == 'y'
|
||||
kernel_sources += files('pci.c')
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#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,3 +1,4 @@
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <config.h>
|
||||
@@ -6,7 +7,6 @@
|
||||
#include <x86_64/asm.h>
|
||||
#include <x86_64/defs.h>
|
||||
#include <x86_64/gdt.h>
|
||||
#include <x86_64/hpet.h>
|
||||
#include <x86_64/idt.h>
|
||||
#include <x86_64/percpu.h>
|
||||
#include <x86_64/smp.h>
|
||||
@@ -132,7 +132,6 @@ void x86_64_cpu_setup(struct percpu* cpu) {
|
||||
}
|
||||
|
||||
void arch_percpu_init() {
|
||||
hpet_init();
|
||||
#if CONFIG_TSC
|
||||
tsc_init();
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <stdint.h>
|
||||
#include <x86_64/asm.h>
|
||||
|
||||
@@ -44,4 +46,10 @@ static struct console serial_con = {
|
||||
.write = serial_write,
|
||||
.init = serial_setup,
|
||||
};
|
||||
DEFINE_CONSOLE(&serial_con);
|
||||
|
||||
[[__init]]
|
||||
static void serial_init(struct boot_info*) {
|
||||
console_register(&serial_con);
|
||||
}
|
||||
INIT_TASK(serial_stage, "arch.x86_64.serial", serial_init);
|
||||
INIT_ENTAILS(serial_stage, console_stage);
|
||||
|
||||
+17
-13
@@ -1,3 +1,5 @@
|
||||
#define pr_fmt(fmt) "smp: " fmt
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/clock.h>
|
||||
@@ -7,13 +9,13 @@
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/string.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>
|
||||
@@ -37,11 +39,12 @@ static void smp_trampoline() {
|
||||
" 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, (smp_trampoline_data - smp_trampoline_start)\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"
|
||||
@@ -130,7 +133,8 @@ static void smp_trampoline() {
|
||||
".popsection\n"
|
||||
" ud2\n"
|
||||
:
|
||||
: [info_size] "i"(sizeof(struct smp_info)),
|
||||
: [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)),
|
||||
@@ -170,7 +174,7 @@ static void ap_entry(phys_t info_addr) {
|
||||
// `info` must not be touched after this point.
|
||||
atomic_store_explicit(&info->booted, 1, memory_order_release);
|
||||
|
||||
log("smp: CPU %zu online\n", cpu->id);
|
||||
log("CPU %zu online\n", cpu->id);
|
||||
|
||||
sched_yield(&cpu->sched);
|
||||
unreachable();
|
||||
@@ -181,18 +185,18 @@ static bool start_ap(phys_t trampoline, uint32_t temp_cr3, uint32_t lapic_id) {
|
||||
|
||||
phys_t stack;
|
||||
if (mem_phys_alloc(AP_BOOT_STACK_SIZE / arch_mem_page_size(), ALLOC_MEM32 | ALLOC_NOZERO, &stack)) {
|
||||
log("smp: Failed to allocate a boot stack for LAPIC %u\n", lapic_id);
|
||||
log_err("Failed to allocate a boot stack for LAPIC %u\n", lapic_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct percpu* cpu = percpu_new();
|
||||
if (!cpu) {
|
||||
log("smp: Failed to allocate per-CPU data for LAPIC %u\n", lapic_id);
|
||||
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("smp: Starting CPU %zu\n", cpu->id);
|
||||
log("Starting CPU %zu\n", cpu->id);
|
||||
|
||||
memcpy(HHDM_PTR(trampoline), smp_trampoline_start, trampoline_size);
|
||||
|
||||
@@ -230,7 +234,7 @@ static bool start_ap(phys_t trampoline, uint32_t temp_cr3, uint32_t lapic_id) {
|
||||
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("smp: CPU %zu (LAPIC %u) did not respond\n", cpu->id, lapic_id);
|
||||
log_err("CPU %zu (LAPIC %u) did not respond\n", cpu->id, lapic_id);
|
||||
return false;
|
||||
}
|
||||
clock_spin_ns(1'000'000);
|
||||
@@ -279,20 +283,20 @@ void x86_64_smp_init() {
|
||||
|
||||
uacpi_table table;
|
||||
if (uacpi_table_find_by_signature(ACPI_MADT_SIGNATURE, &table) != UACPI_STATUS_OK) {
|
||||
log("smp: No MADT found, not starting any APs\n");
|
||||
log_warn("No MADT found, not starting any APs\n");
|
||||
return;
|
||||
}
|
||||
|
||||
phys_t trampoline;
|
||||
if (mem_phys_alloc(1, ALLOC_MEM20, &trampoline)) {
|
||||
log("smp: Failed to allocate 20-bit memory for the AP trampoline\n");
|
||||
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("smp: Failed to allocate a temporary page table\n");
|
||||
log_err("Failed to allocate a temporary page table\n");
|
||||
mem_phys_free(trampoline, 1);
|
||||
uacpi_table_unref(&table);
|
||||
return;
|
||||
@@ -311,7 +315,7 @@ void x86_64_smp_init() {
|
||||
|
||||
// Don't trust malformed firmware tables.
|
||||
if (hdr->length < sizeof(struct acpi_entry_hdr) || entry + hdr->length > end) {
|
||||
log("smp: MADT contains a malformed entry\n");
|
||||
log_warn("MADT contains a malformed entry\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -348,5 +352,5 @@ void x86_64_smp_init() {
|
||||
mem_phys_free(trampoline, 1);
|
||||
}
|
||||
|
||||
log("smp: %zu CPU(s) online\n", num_cpus);
|
||||
log("%zu CPU(s) online\n", num_cpus);
|
||||
}
|
||||
|
||||
+9
-6
@@ -1,3 +1,4 @@
|
||||
#define pr_fmt(fmt) "x86_64/tsc: " fmt
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/print.h>
|
||||
@@ -42,11 +43,13 @@ static struct clock tsc_clock = {
|
||||
};
|
||||
|
||||
void tsc_init() {
|
||||
if (!use_tsc)
|
||||
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("x86_64/tsc: No invariant TSC detected!\n");
|
||||
log_warn("No invariant TSC detected!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,17 +72,17 @@ void tsc_init() {
|
||||
// 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("x86_64/tsc: Calibrating TSC using CPUID 0x15");
|
||||
log("Calibrating TSC using CPUID 0x15");
|
||||
tsc_freq = (uint64_t)tsc_leaf.ecx * tsc_leaf.ebx / tsc_leaf.eax;
|
||||
} else {
|
||||
log("x86_64/tsc: Unable to calibrate TSC using CPUID frequency information");
|
||||
log_err("Unable to calibrate TSC using CPUID frequency information");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log("x86_64/tsc: Unable to calibrate the TSC without a clock or static frequency info!\n");
|
||||
log_err("Unable to calibrate the TSC without a clock or static frequency info!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
log("x86_64/tsc: Timer frequency is %lu MHz\n", tsc_freq / 1'000'000);
|
||||
log("Timer frequency is %lu MHz\n", tsc_freq / 1'000'000);
|
||||
clock_switch(&tsc_clock);
|
||||
}
|
||||
|
||||
+20
-11
@@ -1,8 +1,8 @@
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <stddef.h>
|
||||
#include <zinnia/stdlib.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// Defined in linker script.
|
||||
extern const struct cmdline_option* __ld_cmdline_start[];
|
||||
@@ -10,17 +10,16 @@ extern const struct cmdline_option* __ld_cmdline_end[];
|
||||
|
||||
static char cmdline_buf[0x1000];
|
||||
|
||||
[[__init]]
|
||||
void cmdline_parse(const char* cmdline) {
|
||||
if (cmdline == nullptr)
|
||||
cmdline = "";
|
||||
|
||||
const size_t len = strnlen(cmdline, sizeof(cmdline_buf) - 1);
|
||||
void cmdline_parse_buf(
|
||||
char* cmdline_buf,
|
||||
size_t len,
|
||||
const struct cmdline_option* const* start,
|
||||
const struct cmdline_option* const* end
|
||||
) {
|
||||
size_t idx = 0;
|
||||
memcpy(cmdline_buf, cmdline, len);
|
||||
|
||||
while (1) {
|
||||
const struct cmdline_option** opt = __ld_cmdline_start;
|
||||
const struct cmdline_option* const* opt = start;
|
||||
char* name = nullptr;
|
||||
char* value = nullptr;
|
||||
|
||||
@@ -62,7 +61,7 @@ void cmdline_parse(const char* cmdline) {
|
||||
}
|
||||
|
||||
// Find the corresponding option.
|
||||
while (opt < __ld_cmdline_end) {
|
||||
while (opt < end) {
|
||||
const struct cmdline_option* current = *opt;
|
||||
if (!strcmp(current->name, name)) {
|
||||
if (value == nullptr)
|
||||
@@ -105,3 +104,13 @@ void cmdline_parse(const char* cmdline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
void cmdline_parse(const char* cmdline) {
|
||||
if (cmdline == nullptr)
|
||||
cmdline = "";
|
||||
|
||||
const size_t len = strnlen(cmdline, sizeof(cmdline_buf) - 1);
|
||||
memcpy(cmdline_buf, cmdline, len);
|
||||
cmdline_parse_buf(cmdline_buf, len, __ld_cmdline_start, __ld_cmdline_end);
|
||||
}
|
||||
|
||||
+11
-7
@@ -6,6 +6,7 @@ PHDRS {
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(6);
|
||||
percpu PT_LOAD FLAGS(6);
|
||||
cmdline 0x60000005;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -28,7 +29,7 @@ SECTIONS
|
||||
__ld_cmdline_start = .;
|
||||
KEEP(*(.cmdline))
|
||||
__ld_cmdline_end = .;
|
||||
} :rodata
|
||||
} :rodata :cmdline
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
@@ -39,6 +40,15 @@ SECTIONS
|
||||
KEEP(*(.mod.entry .mod.entry.*))
|
||||
__ld_mod_entry_end = .;
|
||||
|
||||
. = ALIGN(8);
|
||||
__ld_initgraph_nodes_start = .;
|
||||
KEEP(*(.initgraph.nodes))
|
||||
__ld_initgraph_nodes_end = .;
|
||||
|
||||
__ld_initgraph_edges_start = .;
|
||||
KEEP(*(.initgraph.edges))
|
||||
__ld_initgraph_edges_end = .;
|
||||
|
||||
*(.got .got.*)
|
||||
|
||||
__ld_rodata_end = .;
|
||||
@@ -52,12 +62,6 @@ SECTIONS
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.console : {
|
||||
__ld_console_start = .;
|
||||
KEEP(*(.console))
|
||||
__ld_console_end = .;
|
||||
} :data
|
||||
|
||||
.dynsym : {
|
||||
__ld_dynsym_start = .;
|
||||
KEEP(*(.dynsym*))
|
||||
|
||||
+3
-1
@@ -3,8 +3,8 @@
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <string.h>
|
||||
#include "limine.h"
|
||||
|
||||
[[__initdata_sorted("limine.0")]]
|
||||
@@ -53,6 +53,8 @@ void kernel_entry() {
|
||||
assert(hhdm_res, "hhdm_request returned no response!");
|
||||
|
||||
info.cmdline = exec_file_res->executable_file->string;
|
||||
info.kernel_file.data = (phys_t)((uintptr_t)exec_file_res->executable_file->address - hhdm_res->offset);
|
||||
info.kernel_file.length = exec_file_res->executable_file->size;
|
||||
|
||||
info.num_mem_maps = MIN(ARRAY_SIZE(mem), mm_res->entry_count);
|
||||
for (size_t i = 0; i < info.num_mem_maps; i++) {
|
||||
|
||||
+8
-19
@@ -6,8 +6,8 @@
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/elf.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/irq.h>
|
||||
#include <zinnia/module.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
@@ -17,10 +17,6 @@
|
||||
#include <config.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if CONFIG_ACPI
|
||||
#include <zinnia/acpi.h>
|
||||
#endif
|
||||
|
||||
static const char zinnia_banner[] = "Zinnia " ZINNIA_VERSION " " CONFIG_KERNEL_FLAVOR " (" ZINNIA_ARCH
|
||||
", " ZINNIA_COMPILER_ID ", " ZINNIA_LINKER_ID ")";
|
||||
|
||||
@@ -30,20 +26,22 @@ static void kernel_main_task(uintptr_t arg0) {
|
||||
|
||||
log("main: Hello from main task!\n");
|
||||
|
||||
// Initialize built-in modules.
|
||||
module_init();
|
||||
// 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) == EOK, "");
|
||||
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 == EOK, "Failed to allocate a memory object");
|
||||
assert(status == 0, "Failed to allocate a memory object");
|
||||
|
||||
vmo_write(&init_file->object, 0, HHDM_PTR(file_data->data), file_data->length, nullptr);
|
||||
|
||||
@@ -83,17 +81,8 @@ void kernel_main(struct boot_info* info) {
|
||||
kprintf("Command line: %s\n", info->cmdline);
|
||||
|
||||
cmdline_parse(info->cmdline);
|
||||
console_init();
|
||||
kprintf("\ec");
|
||||
|
||||
mem_init(info->mem_map, info->num_mem_maps, info->virt_base, info->phys_base, info->hhdm_base);
|
||||
|
||||
#if CONFIG_ACPI
|
||||
acpi_init(info);
|
||||
#endif
|
||||
|
||||
percpu_init();
|
||||
sched_init(&percpu_get()->sched);
|
||||
initgraph_run(sched_stage, info);
|
||||
|
||||
struct task* main_task;
|
||||
task_create("main", &kernel_vma, kernel_main_task, (uintptr_t)info, &main_task);
|
||||
|
||||
+4
-2
@@ -1,3 +1,5 @@
|
||||
#define pr_fmt(fmt) "clock: " fmt
|
||||
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/print.h>
|
||||
@@ -47,14 +49,14 @@ bool clock_switch(struct clock* clock) {
|
||||
|
||||
spin_unlock(&lock);
|
||||
|
||||
log("clock: Switching to clock source \"%s\"\n", clock->name);
|
||||
log("Switching to clock source \"%s\"\n", clock->name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clock_spin_ns(uint64_t ns) {
|
||||
if (!clock_available()) {
|
||||
log("clock: Unable to sleep for %lu ns, no clock available!\n", ns);
|
||||
log_err("Unable to sleep for %lu ns, no clock available!\n", ns);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+31
-32
@@ -1,45 +1,44 @@
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/spin.h>
|
||||
|
||||
extern struct console* const __ld_console_start[];
|
||||
extern struct console* const __ld_console_end[];
|
||||
|
||||
struct console* active = nullptr;
|
||||
static struct console* registered = nullptr;
|
||||
static struct spinlock console_lock;
|
||||
|
||||
void console_write(const char* buf, size_t len) {
|
||||
if (__likely(active))
|
||||
active->write(active, buf, 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_setup(const char* value) {
|
||||
if (!value)
|
||||
return;
|
||||
bool console_register(struct console* con) {
|
||||
if (!con->init(con))
|
||||
return false;
|
||||
|
||||
// Find a suitable boot console.
|
||||
struct console* const* cur = __ld_console_start;
|
||||
while (cur < __ld_console_end) {
|
||||
if (!strcmp(value, (*cur)->name)) {
|
||||
active = *cur;
|
||||
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;
|
||||
}
|
||||
cur++;
|
||||
}
|
||||
spin_unlock(&console_lock);
|
||||
}
|
||||
|
||||
CMDLINE_OPTION("console", "Set a default console", console_setup);
|
||||
|
||||
void console_init() {
|
||||
// If we have no console set and at least one set, use that as a default.
|
||||
if (!active && &__ld_console_start[0] != &__ld_console_end[0])
|
||||
active = __ld_console_start[0];
|
||||
|
||||
if (active) {
|
||||
if (active->init(active) == false) {
|
||||
active = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
log("console: Logging on console \"%s\"\n", active->name);
|
||||
}
|
||||
[[__init]]
|
||||
static void console_stage_init(struct boot_info*) {
|
||||
kprintf("\ec");
|
||||
}
|
||||
INIT_TASK(console_stage, "core.console", console_stage_init);
|
||||
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
#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 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 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 = (uint32_t*)(fb_ptr() + (y + j) * fbcon.fb.pitch) + x;
|
||||
for (size_t i = 0; i < w; i++)
|
||||
row[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
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 = (uint32_t*)(fb_ptr() + (y + j) * fbcon.fb.pitch) + x;
|
||||
for (size_t i = 0; i < FONT_WIDTH; i++)
|
||||
line[i] = (glyph[j] & (0x80 >> i)) ? fbcon.fg : fbcon.bg;
|
||||
}
|
||||
}
|
||||
|
||||
static void scroll_up() {
|
||||
const size_t pitch = fbcon.fb.pitch;
|
||||
const size_t text_y = fbcon.offset_y;
|
||||
const size_t text_h = fbcon.rows * FONT_HEIGHT;
|
||||
|
||||
memmove(fb_ptr() + text_y * pitch, fb_ptr() + (text_y + FONT_HEIGHT) * pitch, (text_h - FONT_HEIGHT) * pitch);
|
||||
fill_rect(0, text_y + text_h - FONT_HEIGHT, fbcon.fb.width, FONT_HEIGHT, fbcon.bg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
console_register(&fbcon_con);
|
||||
}
|
||||
INIT_TASK(fbcon_stage, "core.fbcon", fbcon_init);
|
||||
INIT_DEPENDS(fbcon_stage, sched_stage);
|
||||
Binary file not shown.
@@ -0,0 +1,168 @@
|
||||
#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;
|
||||
|
||||
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);
|
||||
switch (node->action) {
|
||||
case INIT_EMPTY:
|
||||
break;
|
||||
case INIT_CALLBACK:
|
||||
node->callback(info);
|
||||
break;
|
||||
case INIT_GATE:
|
||||
if (!node->gate(info))
|
||||
node->disabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
kprintf("\tn%p [label=\"%s\"];\n", node, node->name);
|
||||
for (struct init_edge* edge = node->in_edges; edge; edge = edge->next_in)
|
||||
kprintf("\tn%p -> n%p;\n", edge->source, edge->target);
|
||||
}
|
||||
kprintf("}\n");
|
||||
}
|
||||
|
||||
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++) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
#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);
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
#include <stdint.h>
|
||||
#include <zinnia/stdlib.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static void reverse(char* s) {
|
||||
int i, j;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
+17
-8
@@ -1,9 +1,11 @@
|
||||
#define pr_fmt(fmt) "mem: " fmt
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/assert.h>
|
||||
#include <zinnia/console.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/panic.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vma.h>
|
||||
|
||||
@@ -23,7 +25,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
|
||||
const size_t pgsz = arch_mem_page_size();
|
||||
|
||||
log("mem: Memory map:\n");
|
||||
log("Memory map:\n");
|
||||
for (size_t i = 0; i < map_len; i++) {
|
||||
if (map[i].length == 0)
|
||||
continue;
|
||||
@@ -44,7 +46,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
break;
|
||||
}
|
||||
|
||||
log("mem: [%p - %p] %s\n", (void*)map[i].address, (void*)(map[i].address + map[i].length - 1), label);
|
||||
log("[%p - %p] %s\n", (void*)map[i].address, (void*)(map[i].address + map[i].length - 1), label);
|
||||
}
|
||||
|
||||
// Set up the bootstrap allocator.
|
||||
@@ -55,7 +57,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
}
|
||||
assert(largest && largest->length >= pgsz, "No usable memory for bootstrap allocator");
|
||||
mem_phys_bootstrap(largest);
|
||||
log("mem: Using region [%p - %p] for bootstrap allocator\n",
|
||||
log("Using region [%p - %p] for bootstrap allocator\n",
|
||||
(void*)largest->address,
|
||||
(void*)(largest->address + largest->length - 1));
|
||||
|
||||
@@ -68,7 +70,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
tailq_init(&kernel_vma.mappings);
|
||||
|
||||
// text
|
||||
log("mem: Mapping text segment at %p\n", __ld_text_start);
|
||||
log("Mapping text segment at %p\n", __ld_text_start);
|
||||
for (uint8_t* p = __ld_text_start; p < __ld_text_end; p += pgsz) {
|
||||
errno_t status = pmap_map(
|
||||
&kernel_vma.pmap,
|
||||
@@ -81,7 +83,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
}
|
||||
|
||||
// rodata
|
||||
log("mem: Mapping rodata segment at %p\n", __ld_rodata_start);
|
||||
log("Mapping rodata segment at %p\n", __ld_rodata_start);
|
||||
for (uint8_t* p = __ld_rodata_start; p < __ld_rodata_end; p += pgsz) {
|
||||
errno_t status = pmap_map(
|
||||
&kernel_vma.pmap,
|
||||
@@ -94,7 +96,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
}
|
||||
|
||||
// data
|
||||
log("mem: Mapping data segment at %p\n", __ld_data_start);
|
||||
log("Mapping data segment at %p\n", __ld_data_start);
|
||||
for (uint8_t* p = __ld_data_start; p < __ld_data_end; p += pgsz) {
|
||||
errno_t status = pmap_map(
|
||||
&kernel_vma.pmap,
|
||||
@@ -165,5 +167,12 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
|
||||
slab_init();
|
||||
|
||||
log("mem: Memory initialization complete\n");
|
||||
log("Memory initialization complete\n");
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
static void mem_stage_init(struct boot_info* info) {
|
||||
mem_init(info->mem_map, info->num_mem_maps, info->virt_base, info->phys_base, info->hhdm_base);
|
||||
}
|
||||
INIT_TASK(mem_stage, "core.mem", mem_stage_init);
|
||||
INIT_DEPENDS(mem_stage, console_stage);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <config.h>
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <zinnia/panic.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/virt.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
size_t num_pages; // Amount of pages connected to this slab.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/list.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vmo.h>
|
||||
|
||||
|
||||
+3
-1
@@ -1,13 +1,15 @@
|
||||
kernel_sources += files(
|
||||
'clock.c',
|
||||
'console.c',
|
||||
'fbcon.c',
|
||||
'initgraph.c',
|
||||
'irq.c',
|
||||
'ksyms.c',
|
||||
'module.c',
|
||||
'panic.c',
|
||||
'percpu.c',
|
||||
'print.c',
|
||||
'spin.c',
|
||||
'wait.c',
|
||||
)
|
||||
|
||||
subdir('lib')
|
||||
|
||||
+603
-2
@@ -1,12 +1,613 @@
|
||||
#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*);
|
||||
|
||||
void module_init() {
|
||||
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 == EOK, "Built-in entry point returned %i", mod_status);
|
||||
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;
|
||||
}
|
||||
|
||||
+36
-2
@@ -1,11 +1,42 @@
|
||||
#include <zinnia/alloc.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.
|
||||
@@ -15,12 +46,14 @@ void panic(const char* msg, ...) {
|
||||
struct task* current = cpu->sched.current;
|
||||
kprintf(
|
||||
"[ !!!! ] \e[31m"
|
||||
"Kernel panic on CPU %zu (T%zu, \"%s\"): ",
|
||||
"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);
|
||||
@@ -28,9 +61,10 @@ void panic(const char* msg, ...) {
|
||||
|
||||
kprintf("\e[0m");
|
||||
|
||||
print_backtrace();
|
||||
panic_log("End of panic message\n");
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
irq_wait();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -2,7 +2,6 @@
|
||||
#include <zinnia/cmdline.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/percpu.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <arch/percpu.h>
|
||||
#include <stdatomic.h>
|
||||
@@ -55,6 +54,8 @@ void percpu_bsp_init() {
|
||||
arch_percpu_bsp_init();
|
||||
}
|
||||
|
||||
void percpu_init() {
|
||||
void percpu_init(struct boot_info*) {
|
||||
arch_percpu_init();
|
||||
}
|
||||
INIT_TASK(percpu_stage, "core.percpu", percpu_init);
|
||||
INIT_DEPENDS(percpu_stage, mem_stage);
|
||||
|
||||
+149
-6
@@ -1,22 +1,22 @@
|
||||
#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 <zinnia/string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len);
|
||||
#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);
|
||||
}
|
||||
|
||||
static void do_print(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
|
||||
void print_fmt(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
|
||||
// Amount of bytes written.
|
||||
int32_t written = 0;
|
||||
|
||||
@@ -320,16 +320,159 @@ void kprintf(const char* message, ...) {
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
do_print(printf_console, nullptr, message, args);
|
||||
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);
|
||||
|
||||
do_print(printf_console, nullptr, message, args);
|
||||
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_console_printf(const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
print_fmt(printf_console, nullptr, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
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_console_printf(
|
||||
"[%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[level]
|
||||
);
|
||||
console_write(line.buf, line.len);
|
||||
console_write("\e[0m\n", 5);
|
||||
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);
|
||||
}
|
||||
|
||||
+29
-2
@@ -9,7 +9,6 @@
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/stack.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/tailq.h>
|
||||
#include <zinnia/vma.h>
|
||||
#include <arch/sched.h>
|
||||
@@ -54,6 +53,7 @@ void sched_init(struct scheduler* sched) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ static struct task* next_task(struct scheduler* sched) {
|
||||
return sched->idle_task;
|
||||
|
||||
tailq_remove(&sched->run_queue, t, next);
|
||||
t->state = TASK_STATE_RUNNING;
|
||||
|
||||
return t;
|
||||
}
|
||||
@@ -110,6 +111,25 @@ void sched_yield(struct scheduler* sched) {
|
||||
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;
|
||||
@@ -118,7 +138,7 @@ errno_t task_create(const char* name, struct vma* space, task_fn_t entry, uintpt
|
||||
if (new_task == nullptr)
|
||||
return ENOMEM;
|
||||
|
||||
static _Atomic(size_t) last_tid = 0;
|
||||
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));
|
||||
@@ -165,3 +185,10 @@ void task_entry(task_fn_t entry, uintptr_t arg0, struct task* prev) {
|
||||
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);
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void acpi_init(struct boot_info* info);
|
||||
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)
|
||||
|
||||
+25
-5
@@ -1,25 +1,45 @@
|
||||
#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);
|
||||
|
||||
void acpi_init(struct boot_info* info) {
|
||||
[[__init]]
|
||||
static bool acpi_stage_init(struct boot_info* info) {
|
||||
if (!use_acpi) {
|
||||
log("acpi: ACPI disabled on the command line!\n");
|
||||
return;
|
||||
log_warn("ACPI disabled on the command line!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!info->rsdp) {
|
||||
log("acpi: No RSDP was passed, unable to start ACPI subsystem!\n");
|
||||
return;
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#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);
|
||||
@@ -6,6 +6,10 @@ kernel_sources += files(
|
||||
'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')
|
||||
|
||||
|
||||
+18
-6
@@ -38,17 +38,29 @@ void uacpi_kernel_unmap(void* addr, uacpi_size len) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void uacpi_kernel_log(uacpi_log_level lvl, const uacpi_char* msg, ...) {
|
||||
log("acpi: ");
|
||||
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);
|
||||
|
||||
kvprintf(msg, args);
|
||||
|
||||
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) {
|
||||
kvprintf(msg, args);
|
||||
log_vwrite_prefixed(acpi_log_level(lvl), "acpi: ", msg, args);
|
||||
}
|
||||
|
||||
+23
-5
@@ -1,16 +1,34 @@
|
||||
#include <zinnia/cmdline.h>
|
||||
#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) {
|
||||
log("Hello world from NVMe!\n");
|
||||
errno_t status = 0;
|
||||
|
||||
return 0;
|
||||
#if CONFIG_NVME_PCI
|
||||
status = pci_driver_register(&nvme_driver);
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
errno_t nvme_exit(struct module* mod) {
|
||||
log("Good bye from NVMe!\n");
|
||||
|
||||
// TODO: Unregister
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern struct pci_driver nvme_driver;
|
||||
@@ -0,0 +1,23 @@
|
||||
#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,
|
||||
};
|
||||
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
#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){};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
kernel_sources += files('fdt.c')
|
||||
|
||||
@@ -2,6 +2,10 @@ 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
|
||||
|
||||
+12
-7
@@ -1,5 +1,7 @@
|
||||
/* Linker script for kernel modules */
|
||||
|
||||
ENTRY(__module_entry)
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5);
|
||||
@@ -11,8 +13,8 @@ PHDRS
|
||||
moddesc 0x60000003;
|
||||
modlicense 0x60000004;
|
||||
modcmdline 0x60000005;
|
||||
modentry 0x60000006;
|
||||
modexit 0x60000007;
|
||||
modexit 0x60000006;
|
||||
modmatch 0x60000007;
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -57,14 +59,14 @@ SECTIONS
|
||||
KEEP(*(.cmdline))
|
||||
} :rodata :modcmdline
|
||||
|
||||
.mod.entry : {
|
||||
KEEP(*(.mod.entry))
|
||||
} :rodata :modentry
|
||||
|
||||
.mod.exit : {
|
||||
KEEP(*(.mod.exit))
|
||||
} :rodata :modexit
|
||||
|
||||
.mod.match : {
|
||||
KEEP(*(.mod.match))
|
||||
} :rodata :modmatch
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
/* Read-Write data */
|
||||
@@ -91,6 +93,10 @@ SECTIONS
|
||||
*(.rela.dyn .rela.dyn.*)
|
||||
} :data
|
||||
|
||||
.relr.dyn : {
|
||||
*(.relr.dyn .relr.dyn.*)
|
||||
} :data
|
||||
|
||||
/* Dynamic tags */
|
||||
.dynamic : {
|
||||
*(.dynamic)
|
||||
@@ -101,7 +107,6 @@ SECTIONS
|
||||
*(.comment*)
|
||||
*(.eh_frame*)
|
||||
*(.rel.eh_frame*)
|
||||
*(.relr.dyn .relr.dyn.*)
|
||||
*(.gcc_except_table*)
|
||||
*(.note*)
|
||||
*(.interp)
|
||||
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
#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);
|
||||
@@ -0,0 +1,76 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
kernel_sources += files(
|
||||
'core.c',
|
||||
'ecam.c',
|
||||
)
|
||||
+3
-4
@@ -7,7 +7,6 @@
|
||||
#include <zinnia/pmap.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <zinnia/vfs.h>
|
||||
#include <zinnia/vma.h>
|
||||
@@ -19,7 +18,7 @@ errno_t elf_load(struct exec_info* info, struct task** out) {
|
||||
errno_t err;
|
||||
|
||||
size_t read;
|
||||
struct elf_ehdr ehdr = {};
|
||||
elf_ehdr_t ehdr = {};
|
||||
err = vmo_read(info->file_obj, 0, &ehdr, sizeof(ehdr), &read);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -44,7 +43,7 @@ errno_t elf_load(struct exec_info* info, struct task** out) {
|
||||
|
||||
const size_t phdrs_size = (size_t)ehdr.e_phnum * ehdr.e_phentsize;
|
||||
uintptr_t phdrs_end;
|
||||
if (ehdr.e_phentsize < sizeof(struct elf_phdr) || ehdr.e_phnum == 0 || phdrs_size > 64 * 1024)
|
||||
if (ehdr.e_phentsize < sizeof(elf_phdr_t) || ehdr.e_phnum == 0 || phdrs_size > 64 * 1024)
|
||||
return ENOEXEC;
|
||||
if (ckd_add(&phdrs_end, ehdr.e_phoff, phdrs_size))
|
||||
return ENOEXEC;
|
||||
@@ -53,7 +52,7 @@ errno_t elf_load(struct exec_info* info, struct task** out) {
|
||||
uintptr_t phdr_addr = 0;
|
||||
|
||||
for (size_t i = 0; i < ehdr.e_phnum; i++) {
|
||||
struct elf_phdr phdr = {};
|
||||
elf_phdr_t phdr = {};
|
||||
err = vmo_read(info->file_obj, (ehdr.e_phoff + (i * ehdr.e_phentsize)), &phdr, sizeof(phdr), &read);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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)
|
||||
@@ -0,0 +1,163 @@
|
||||
// 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;
|
||||
@@ -0,0 +1,6 @@
|
||||
// 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)
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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()
|
||||
@@ -0,0 +1,354 @@
|
||||
// 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,3 +1,4 @@
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -11,4 +12,3 @@ void* memset(void* dest, int value, size_t n);
|
||||
char* strncpy(char* restrict dst, const char* restrict src, size_t len);
|
||||
int strcmp(const char* str1, const char* str2);
|
||||
int strncmp(const char* str1, const char* str2, size_t len);
|
||||
char* strchr(const char* c, int s);
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <arch/mem.h>
|
||||
#include <stddef.h>
|
||||
@@ -51,3 +52,5 @@ void mem_phys_init(struct phys_mem* map, size_t length);
|
||||
static inline size_t mem_high_shift() {
|
||||
return arch_mem_level_bits() * arch_mem_num_levels() + arch_mem_page_bits();
|
||||
}
|
||||
|
||||
INIT_EXTERN(mem_stage);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/mman.h>
|
||||
|
||||
// TODO
|
||||
|
||||
enum bio_type : uint8_t {
|
||||
BLOCK_READ,
|
||||
BLOCK_WRITE,
|
||||
};
|
||||
|
||||
struct bio {
|
||||
enum bio_type type;
|
||||
uint32_t num_lbas;
|
||||
uint64_t lba;
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define _CMDLINE_OPTION(x, opt_name, opt_desc, opt_func) \
|
||||
@@ -48,3 +49,10 @@ struct cmdline_option {
|
||||
|
||||
// Parses the command line and invokes all options.
|
||||
void cmdline_parse(const char* cmdline);
|
||||
|
||||
void cmdline_parse_buf(
|
||||
char* buf,
|
||||
size_t len,
|
||||
const struct cmdline_option* const* start,
|
||||
const struct cmdline_option* const* end
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -10,16 +11,16 @@ struct console {
|
||||
bool (*init)(struct console* con);
|
||||
// Writes to the console.
|
||||
void (*write)(struct console* con, const char* buf, size_t count);
|
||||
struct console* next;
|
||||
};
|
||||
|
||||
// Initializes a console for logging.
|
||||
void console_init();
|
||||
INIT_EXTERN(console_stage);
|
||||
|
||||
// Registers a console.
|
||||
bool console_register(struct console* con);
|
||||
|
||||
// Unregisters a regular console.
|
||||
void console_unregister(struct console* con);
|
||||
|
||||
// Writes a message to a console.
|
||||
void console_write(const char* buf, size_t len);
|
||||
|
||||
// Defines a new early console which can be used to log early boot messages.
|
||||
// An early console must not make any heap allocations.
|
||||
#define DEFINE_CONSOLE(con) \
|
||||
[[__used, __section(".console")]] \
|
||||
static struct console* UNIQUE_IDENT(console) = (con)
|
||||
|
||||
+56
-36
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/vfs.h>
|
||||
#include <bits/elf.h>
|
||||
#include <arch/elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
|
||||
@@ -74,6 +74,20 @@ 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,
|
||||
@@ -110,6 +124,9 @@ 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,
|
||||
@@ -140,7 +157,7 @@ enum : uint32_t {
|
||||
AT_INIT_HANDLE = 0x1000,
|
||||
};
|
||||
|
||||
struct elf64_ehdr {
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
@@ -155,9 +172,9 @@ struct elf64_ehdr {
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
} elf64_ehdr_t;
|
||||
|
||||
struct elf32_ehdr {
|
||||
typedef struct {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
@@ -172,9 +189,9 @@ struct elf32_ehdr {
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
} elf32_ehdr_t;
|
||||
|
||||
struct elf64_phdr {
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
uint32_t p_flags;
|
||||
elf64_off_t p_offset;
|
||||
@@ -183,9 +200,9 @@ struct elf64_phdr {
|
||||
uint64_t p_filesz;
|
||||
uint64_t p_memsz;
|
||||
uint64_t p_align;
|
||||
};
|
||||
} elf64_phdr_t;
|
||||
|
||||
struct elf32_phdr {
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
elf32_off_t p_offset;
|
||||
elf32_addr_t p_vaddr;
|
||||
@@ -194,68 +211,68 @@ struct elf32_phdr {
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
} elf32_phdr_t;
|
||||
|
||||
struct elf64_dyn {
|
||||
typedef struct {
|
||||
int64_t d_tag;
|
||||
union {
|
||||
uint64_t d_val;
|
||||
elf64_addr_t d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
} elf64_dyn_t;
|
||||
|
||||
struct elf32_dyn {
|
||||
typedef struct {
|
||||
int32_t r_offset;
|
||||
union {
|
||||
uint32_t d_val;
|
||||
elf32_addr_t d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
} elf32_dyn_t;
|
||||
|
||||
struct elf64_nhdr {
|
||||
typedef struct {
|
||||
uint64_t n_namesz;
|
||||
uint64_t n_descsz;
|
||||
uint64_t n_type;
|
||||
};
|
||||
} elf64_nhdr_t;
|
||||
|
||||
struct elf32_nhdr {
|
||||
typedef struct {
|
||||
uint32_t n_namesz;
|
||||
uint32_t n_descsz;
|
||||
uint32_t n_type;
|
||||
};
|
||||
} elf32_nhdr_t;
|
||||
|
||||
struct elf64_auxv {
|
||||
typedef struct {
|
||||
uint64_t atype;
|
||||
uint64_t avalue;
|
||||
};
|
||||
} elf64_auxv_t;
|
||||
|
||||
struct elf32_auxv {
|
||||
typedef struct {
|
||||
uint32_t atype;
|
||||
uint32_t avalue;
|
||||
};
|
||||
} elf32_auxv_t;
|
||||
|
||||
struct elf_hash {
|
||||
typedef struct {
|
||||
uint32_t n_buckets;
|
||||
uint32_t n_chain;
|
||||
};
|
||||
} elf_hash_t;
|
||||
|
||||
struct elf32_sym {
|
||||
typedef struct {
|
||||
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;
|
||||
|
||||
struct elf64_sym {
|
||||
typedef struct {
|
||||
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;
|
||||
@@ -272,7 +289,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;
|
||||
} elf64_syminfo_t;
|
||||
|
||||
static inline uint8_t ELF32_ST_BIND(uint8_t info) {
|
||||
return info >> 4;
|
||||
@@ -304,27 +321,30 @@ enum {
|
||||
STT_HIPROC = 15
|
||||
};
|
||||
|
||||
struct elf32_rela {
|
||||
typedef struct {
|
||||
elf32_addr_t r_offset;
|
||||
uint32_t r_info;
|
||||
int32_t r_addend;
|
||||
};
|
||||
} elf32_rela_t;
|
||||
|
||||
struct elf64_rela {
|
||||
typedef struct {
|
||||
elf64_addr_t r_offset;
|
||||
uint64_t r_info;
|
||||
int64_t r_addend;
|
||||
};
|
||||
} elf64_rela_t;
|
||||
|
||||
struct elf32_rel {
|
||||
typedef uint64_t elf64_relr_t;
|
||||
typedef uint32_t elf32_relr_t;
|
||||
|
||||
typedef struct {
|
||||
elf32_addr_t r_offset;
|
||||
uint32_t r_info;
|
||||
};
|
||||
} elf32_rel_t;
|
||||
|
||||
struct elf64_rel {
|
||||
typedef struct {
|
||||
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;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <uapi/errno.h>
|
||||
|
||||
typedef enum : int32_t {
|
||||
EOK = 0, // Success! Not really an error though...
|
||||
EPERM = __EPERM,
|
||||
ENOENT = __ENOENT,
|
||||
ESRCH = __ESRCH,
|
||||
|
||||
@@ -21,8 +21,18 @@ constexpr uint32_t FDT_END_NODE = 0x00000002;
|
||||
constexpr uint32_t FDT_PROP = 0x00000003;
|
||||
constexpr uint32_t FDT_NOP = 0x00000004;
|
||||
constexpr uint32_t FDT_END = 0x00000009;
|
||||
constexpr uint32_t FDT_MAGIC = 0xD00DFEED;
|
||||
|
||||
struct fdt_match {
|
||||
const char* compatible;
|
||||
uintptr_t data;
|
||||
};
|
||||
|
||||
#define FDT_MATCH_COMPATIBLE(compat) .compatible = (compat)
|
||||
|
||||
struct fdt {
|
||||
const uint8_t* blob;
|
||||
size_t total_size;
|
||||
uint32_t version;
|
||||
uint32_t compat_version;
|
||||
uint32_t boot_cpuid;
|
||||
@@ -35,11 +45,23 @@ struct fdt {
|
||||
|
||||
struct fdt_node {
|
||||
struct fdt* tree;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
struct fdt_prop {
|
||||
struct fdt* tree;
|
||||
const void* data;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
uint32_t fdt_read_u32(const void* data);
|
||||
uint64_t fdt_read_u64(const void* data);
|
||||
bool fdt_init(struct fdt* tree, const void* data, size_t length);
|
||||
struct fdt_node fdt_find(struct fdt* tree, const char* path);
|
||||
struct fdt_node fdt_find_compatible(struct fdt* tree, const char* compatible);
|
||||
struct fdt_prop fdt_property(struct fdt_node node, const char* name);
|
||||
bool fdt_property_u32(struct fdt_node node, const char* name, uint32_t* value);
|
||||
bool fdt_property_u64(struct fdt_node node, const char* name, uint64_t* value);
|
||||
|
||||
// Root of the device tree.
|
||||
struct fdt_node fdt_root(struct fdt* tree);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <zinnia/errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <string.h>
|
||||
|
||||
#define hashmap(K, V) \
|
||||
struct { \
|
||||
|
||||
@@ -38,6 +38,7 @@ struct boot_info {
|
||||
struct boot_file* files;
|
||||
size_t num_files;
|
||||
struct boot_framebuffer fb;
|
||||
struct boot_file kernel_file;
|
||||
void* dtb;
|
||||
size_t dtb_size;
|
||||
#if CONFIG_ACPI
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
|
||||
enum init_action {
|
||||
INIT_EMPTY,
|
||||
INIT_CALLBACK,
|
||||
INIT_GATE,
|
||||
};
|
||||
|
||||
struct boot_info;
|
||||
struct init_node;
|
||||
|
||||
struct init_edge {
|
||||
struct init_node* source;
|
||||
struct init_node* target;
|
||||
bool propagates_disable;
|
||||
struct init_edge* next_in;
|
||||
struct init_edge* next_out;
|
||||
};
|
||||
|
||||
struct init_edges {
|
||||
struct init_node* node;
|
||||
bool depends;
|
||||
size_t count;
|
||||
struct init_node* const* nodes;
|
||||
struct init_edge* edges;
|
||||
};
|
||||
|
||||
struct init_node {
|
||||
const char* name;
|
||||
enum init_action action;
|
||||
union {
|
||||
void (*callback)(struct boot_info* info);
|
||||
bool (*gate)(struct boot_info* info);
|
||||
void* func;
|
||||
};
|
||||
size_t unsatisfied;
|
||||
bool wanted;
|
||||
bool done;
|
||||
bool disabled;
|
||||
struct init_edge* in_edges;
|
||||
struct init_edge* out_edges;
|
||||
struct init_node* queue_next;
|
||||
};
|
||||
|
||||
#define INIT_EXTERN(node) extern struct init_node node[1]
|
||||
|
||||
#define INIT_TASK(node, display, ...) \
|
||||
struct init_node node[1] = {{ \
|
||||
.name = (display), \
|
||||
__VA_OPT__( \
|
||||
.action = _Generic( \
|
||||
(__VA_ARGS__), \
|
||||
void (*)(struct boot_info*): INIT_CALLBACK, \
|
||||
bool (*)(struct boot_info*): INIT_GATE \
|
||||
), \
|
||||
.func = (void*)(__VA_ARGS__), \
|
||||
) \
|
||||
}}; \
|
||||
[[__used, __section(".initgraph.nodes")]] \
|
||||
static struct init_node* const CONCAT(node, _initgraph_node) = node
|
||||
|
||||
#define _INIT_EDGES(x, from, is_depends, ...) \
|
||||
static struct init_node* const CONCAT(x, _nodes)[] = {__VA_ARGS__}; \
|
||||
static struct init_edge CONCAT(x, _edges)[ARRAY_SIZE(CONCAT(x, _nodes))]; \
|
||||
static const struct init_edges x = { \
|
||||
.node = (from), \
|
||||
.depends = (is_depends), \
|
||||
.count = ARRAY_SIZE(CONCAT(x, _nodes)), \
|
||||
.nodes = CONCAT(x, _nodes), \
|
||||
.edges = CONCAT(x, _edges), \
|
||||
}; \
|
||||
[[__used, __section(".initgraph.edges")]] \
|
||||
static const struct init_edges* const CONCAT(x, _ptr) = &x
|
||||
|
||||
#define INIT_DEPENDS(node, ...) _INIT_EDGES(UNIQUE_IDENT(init_edges), node, true, __VA_ARGS__)
|
||||
#define INIT_ENTAILS(node, ...) _INIT_EDGES(UNIQUE_IDENT(init_edges), node, false, __VA_ARGS__)
|
||||
|
||||
void initgraph_run(struct init_node* goal, struct boot_info* info);
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/usercopy.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void ksym_init();
|
||||
|
||||
// Finds the function containing `addr`. Returns false if no symbol is known.
|
||||
bool ksym_lookup(uintptr_t addr, const char** name, size_t* offset);
|
||||
|
||||
// Writes `name+0xoffset` for `addr` to `buf`, or `<???>` if the address can't be resolved.
|
||||
void ksym_format(uintptr_t addr, char* buf, size_t size);
|
||||
|
||||
bool ksym_find(const char* name, uintptr_t* addr);
|
||||
|
||||
const char* ksym_kernel_name();
|
||||
|
||||
INIT_EXTERN(ksyms_stage);
|
||||
+65
-14
@@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/tailq.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <zinnia/utils.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MODULE_PROPERTY(name, data) \
|
||||
@@ -13,36 +17,83 @@
|
||||
#define MODULE_VERSION(version) MODULE_PROPERTY("version", version)
|
||||
#define MODULE_LICENSE(license) MODULE_PROPERTY("license", license)
|
||||
|
||||
#ifdef IN_MODULE
|
||||
struct match_table {
|
||||
const char* name;
|
||||
const void* entries;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#ifdef __IN_MODULE
|
||||
#define MATCH_TABLE(match_name, table) \
|
||||
[[__used, __section(".mod.match"), __aligned(8)]] \
|
||||
static const struct match_table CONCAT(table, _match_table) = { \
|
||||
.name = (match_name), \
|
||||
.entries = (table), \
|
||||
.size = ARRAY_SIZE(table), \
|
||||
}
|
||||
#else
|
||||
#define MATCH_TABLE(match_name, table) static_assert(true)
|
||||
#endif
|
||||
|
||||
// Module entries are used
|
||||
#ifdef __IN_MODULE
|
||||
#define MODULE_ENTRY(func) \
|
||||
[[__used]] \
|
||||
[[__used, __visibility("hidden")]] \
|
||||
errno_t __module_entry(struct module* __module) { \
|
||||
return func(__module); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define MODULE_ENTRY(func) \
|
||||
[[__used, __section(".mod.entry")]] \
|
||||
static const void* __entry_fn = &__module_entry
|
||||
static errno_t (*const UNIQUE_IDENT(module_entry))(struct module*) = &(func)
|
||||
#endif
|
||||
|
||||
#ifdef __IN_MODULE
|
||||
#define MODULE_EXIT(func) \
|
||||
[[__used]] \
|
||||
[[__used, __visibility("hidden")]] \
|
||||
errno_t __module_exit(struct module* __module) { \
|
||||
return func(__module); \
|
||||
} \
|
||||
[[__used, __section(".mod.exit")]] \
|
||||
static const void* __exit_fn = &__module_exit
|
||||
static errno_t (*const __exit_fn)(struct module*) = &__module_exit
|
||||
#else
|
||||
#define MODULE_ENTRY(func) \
|
||||
[[__used, __section(".mod.entry")]] \
|
||||
static const void* UNIQUE_IDENT(module_entry) = &(func)
|
||||
|
||||
#define MODULE_EXIT(func) \
|
||||
[[__used, __section(".mod.exit")]] \
|
||||
static const void* UNIQUE_IDENT(module_exit) = &(func)
|
||||
// Built-in drivers can't be unloaded.
|
||||
#define MODULE_EXIT(func) static_assert(&(func) != nullptr)
|
||||
#endif
|
||||
|
||||
struct module_segment {
|
||||
phys_t phys;
|
||||
uintptr_t virt;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct module {
|
||||
const char* name;
|
||||
const char* version;
|
||||
const char* author;
|
||||
const char* desc;
|
||||
const char* license;
|
||||
uintptr_t base;
|
||||
size_t size;
|
||||
uintptr_t bias;
|
||||
const void* symtab;
|
||||
size_t num_syms;
|
||||
const char* strtab;
|
||||
size_t strtab_size;
|
||||
struct module_segment* segments;
|
||||
size_t num_segments;
|
||||
errno_t (*exit)(struct module*);
|
||||
char* cmdline;
|
||||
tailq_link(struct module) link;
|
||||
};
|
||||
|
||||
#define THIS_MODULE (struct module*)nullptr
|
||||
|
||||
void module_init();
|
||||
struct boot_info;
|
||||
void module_init(struct boot_info* info);
|
||||
|
||||
errno_t module_load(const void* data, size_t length, const char* cmdline, struct module** out);
|
||||
errno_t module_unload(struct module* mod);
|
||||
struct module* module_find(const char* name);
|
||||
|
||||
INIT_EXTERN(module_stage);
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct pci_address {
|
||||
uint16_t domain;
|
||||
uint8_t bus;
|
||||
uint8_t device;
|
||||
uint8_t function;
|
||||
};
|
||||
|
||||
#define PCI_ADDR_FMT "%.4hx:%.2hhx:%.2hhx.%hhx"
|
||||
#define PCI_ADDR_ARGS(a) (a)->domain, (a)->bus, (a)->device, (a)->function
|
||||
|
||||
struct pci_id {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t subvendor_id;
|
||||
uint16_t subdevice_id;
|
||||
};
|
||||
|
||||
struct pci_class {
|
||||
uint8_t class;
|
||||
uint8_t subclass;
|
||||
uint8_t prog_if;
|
||||
};
|
||||
|
||||
enum pci_cfg : uint16_t {
|
||||
PCI_VENDOR_ID = 0x00,
|
||||
PCI_DEVICE_ID = 0x02,
|
||||
PCI_COMMAND = 0x04,
|
||||
PCI_STATUS = 0x06,
|
||||
PCI_PROG_IF = 0x09,
|
||||
PCI_SUBCLASS = 0x0a,
|
||||
PCI_CLASS = 0x0b,
|
||||
PCI_HEADER_TYPE = 0x0e,
|
||||
PCI_BAR0 = 0x10,
|
||||
PCI_SECONDARY_BUS = 0x19,
|
||||
PCI_SUBSYS_VENDOR_ID = 0x2c,
|
||||
PCI_SUBSYS_ID = 0x2e,
|
||||
PCI_CAP_PTR = 0x34,
|
||||
PCI_INTERRUPT_LINE = 0x3c,
|
||||
PCI_INTERRUPT_PIN = 0x3d,
|
||||
};
|
||||
|
||||
enum pci_command : uint16_t {
|
||||
PCI_COMMAND_IO = 1 << 0,
|
||||
PCI_COMMAND_MEM = 1 << 1,
|
||||
PCI_COMMAND_MASTER = 1 << 2,
|
||||
PCI_COMMAND_INTX_DISABLE = 1 << 10,
|
||||
};
|
||||
|
||||
constexpr uint16_t PCI_STATUS_CAP_LIST = 1 << 4;
|
||||
|
||||
constexpr uint8_t PCI_HEADER_TYPE_MASK = 0x7f;
|
||||
constexpr uint8_t PCI_HEADER_TYPE_MULTIFUNCTION = 0x80;
|
||||
constexpr uint8_t PCI_HEADER_TYPE_BRIDGE = 0x01;
|
||||
|
||||
constexpr size_t PCI_NUM_BARS = 6;
|
||||
constexpr uint8_t PCI_NUM_SLOTS = 32;
|
||||
constexpr uint8_t PCI_NUM_FUNCTIONS = 8;
|
||||
|
||||
enum pci_cap_id : uint8_t {
|
||||
PCI_CAP_MSI = 0x05,
|
||||
PCI_CAP_PCIE = 0x10,
|
||||
PCI_CAP_MSIX = 0x11,
|
||||
};
|
||||
|
||||
struct pci_access {
|
||||
const char* name;
|
||||
uint16_t domain;
|
||||
uint8_t start_bus;
|
||||
uint8_t end_bus;
|
||||
uint8_t priority;
|
||||
uint32_t (*read32)(struct pci_access* self, const struct pci_address* addr, uint16_t offset);
|
||||
void (*write32)(struct pci_access* self, const struct pci_address* addr, uint16_t offset, uint32_t value);
|
||||
};
|
||||
|
||||
enum pci_access_priority : uint8_t {
|
||||
PCI_ACCESS_PRIO_LEGACY = 0,
|
||||
PCI_ACCESS_PRIO_ECAM = 128,
|
||||
};
|
||||
|
||||
static bool pci_access_decodes(const struct pci_access* access, const struct pci_address* addr);
|
||||
|
||||
errno_t pci_access_register(struct pci_access* access);
|
||||
struct pci_access* pci_access_find(const struct pci_address* addr);
|
||||
|
||||
errno_t pci_ecam_register(phys_t base, uint16_t domain, uint8_t start_bus, uint8_t end_bus);
|
||||
|
||||
struct pci_driver;
|
||||
|
||||
struct pci_device {
|
||||
struct pci_address address;
|
||||
struct pci_id id;
|
||||
struct pci_class class;
|
||||
struct pci_access* access;
|
||||
uint8_t header_type;
|
||||
struct pci_driver* driver;
|
||||
};
|
||||
|
||||
uint8_t pci_read8(struct pci_device* dev, uint16_t offset);
|
||||
uint16_t pci_read16(struct pci_device* dev, uint16_t offset);
|
||||
uint32_t pci_read32(struct pci_device* dev, uint16_t offset);
|
||||
void pci_write8(struct pci_device* dev, uint16_t offset, uint8_t value);
|
||||
void pci_write16(struct pci_device* dev, uint16_t offset, uint16_t value);
|
||||
void pci_write32(struct pci_device* dev, uint16_t offset, uint32_t value);
|
||||
|
||||
enum pci_bar_type : uint8_t {
|
||||
PCI_BAR_NONE,
|
||||
PCI_BAR_MMIO32,
|
||||
PCI_BAR_MMIO64,
|
||||
PCI_BAR_IO,
|
||||
};
|
||||
|
||||
struct pci_bar {
|
||||
enum pci_bar_type type;
|
||||
bool prefetchable;
|
||||
phys_t address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
errno_t pci_bar_get(struct pci_device* dev, size_t index, struct pci_bar* out);
|
||||
|
||||
uint8_t pci_cap_next(struct pci_device* dev, uint8_t cur);
|
||||
uint8_t pci_cap_find(struct pci_device* dev, uint8_t id);
|
||||
|
||||
#define PCI_ANY_ID 0xffff
|
||||
#define PCI_ANY_CLASS 0xff
|
||||
|
||||
// Matches a device by ID and class.
|
||||
struct pci_match {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint8_t class;
|
||||
uint8_t subclass;
|
||||
uint8_t prog_if;
|
||||
|
||||
// Not used to match, but can be used to pass device-specific context data.
|
||||
uintptr_t data;
|
||||
};
|
||||
|
||||
#define PCI_MATCH_DEVICE(vendor, device) \
|
||||
.vendor_id = (vendor), .device_id = (device), .class = PCI_ANY_CLASS, .subclass = PCI_ANY_CLASS, \
|
||||
.prog_if = PCI_ANY_CLASS
|
||||
|
||||
#define PCI_MATCH_CLASS(c, sc, pi) \
|
||||
.vendor_id = PCI_ANY_ID, .device_id = PCI_ANY_ID, .class = (c), .subclass = (sc), .prog_if = (pi)
|
||||
|
||||
// Writes the address to `buf`.
|
||||
static void pci_device_name(const struct pci_device* dev, char buf[static 18]);
|
||||
|
||||
struct pci_driver {
|
||||
const char* name;
|
||||
|
||||
// Match table, array of `num_matches` entries, checked sequentially.
|
||||
// When writing a match table, the most specific matches should come first, then more generic cases.
|
||||
const struct pci_match* match;
|
||||
const size_t num_matches;
|
||||
|
||||
errno_t (*probe)(struct pci_device* dev, const struct pci_match* match);
|
||||
};
|
||||
|
||||
errno_t pci_driver_register(struct pci_driver* driver);
|
||||
|
||||
INIT_EXTERN(pci_stage);
|
||||
|
||||
#ifdef __MODULE_NAME
|
||||
#define PCI_LOG_NAME __MODULE_NAME
|
||||
#else
|
||||
#define PCI_LOG_NAME "pci"
|
||||
#endif
|
||||
|
||||
#define pci_log_level(level, dev, message, ...) \
|
||||
log_write(level, PCI_LOG_NAME " " PCI_ADDR_FMT ": " message, PCI_ADDR_ARGS(&(dev)->address), ##__VA_ARGS__)
|
||||
|
||||
#define pci_log_debug(dev, message, ...) pci_log_level(LOG_DEBUG, dev, message, ##__VA_ARGS__)
|
||||
#define pci_log(dev, message, ...) pci_log_level(LOG_INFO, dev, message, ##__VA_ARGS__)
|
||||
#define pci_log_warn(dev, message, ...) pci_log_level(LOG_WARN, dev, message, ##__VA_ARGS__)
|
||||
#define pci_log_err(dev, message, ...) pci_log_level(LOG_ERR, dev, message, ##__VA_ARGS__)
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/sched.h>
|
||||
#include <zinnia/usercopy.h>
|
||||
#include <stddef.h>
|
||||
@@ -40,7 +41,9 @@ struct percpu* percpu_new();
|
||||
size_t percpu_max_cpus();
|
||||
struct percpu* percpu_get();
|
||||
void percpu_bsp_init();
|
||||
void percpu_init();
|
||||
void percpu_init(struct boot_info* info);
|
||||
|
||||
INIT_EXTERN(percpu_stage);
|
||||
|
||||
// Per-CPU data for the bootstrap processor.
|
||||
extern struct percpu percpu_bsp;
|
||||
|
||||
+35
-9
@@ -2,19 +2,45 @@
|
||||
|
||||
#include <zinnia/compiler.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
[[__format(printf, 1, 2)]]
|
||||
void kprintf(const char* message, ...);
|
||||
|
||||
void kvprintf(const char* message, va_list args);
|
||||
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/percpu.h>
|
||||
[[__format(printf, 3, 4)]]
|
||||
size_t ksnprintf(char* buf, size_t size, const char* format, ...);
|
||||
|
||||
#define log(message, ...) \
|
||||
do { \
|
||||
auto _time = clock_get_elapsed_ns(); \
|
||||
auto _cur = percpu_get()->sched.current; \
|
||||
auto _tid = _cur ? _cur->id : 0; \
|
||||
kprintf("[%5lu.%06lu] [%5lu] " message, _time / 1'000'000'000, _time / 1'000 % 1'000'000, _tid, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
size_t kvsnprintf(char* buf, size_t size, const char* format, va_list args);
|
||||
|
||||
typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len);
|
||||
void print_fmt(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args);
|
||||
|
||||
enum log_level {
|
||||
LOG_DEBUG,
|
||||
LOG_STATUS,
|
||||
LOG_INFO,
|
||||
LOG_WARN,
|
||||
LOG_ERR,
|
||||
LOG_PANIC,
|
||||
};
|
||||
|
||||
[[__format(printf, 2, 3)]]
|
||||
void log_write(enum log_level level, const char* message, ...);
|
||||
|
||||
void log_vwrite(enum log_level level, const char* message, va_list args);
|
||||
|
||||
// Like log_vwrite(), but prepends a `prefix` to the message.
|
||||
void log_vwrite_prefixed(enum log_level level, const char* prefix, const char* message, va_list args);
|
||||
|
||||
#ifndef pr_fmt
|
||||
#define pr_fmt(fmt) fmt
|
||||
#endif
|
||||
|
||||
#define log(message, ...) log_write(LOG_INFO, pr_fmt(message), ##__VA_ARGS__)
|
||||
#define log_debug(message, ...) log_write(LOG_DEBUG, pr_fmt(message), ##__VA_ARGS__)
|
||||
#define log_status(message, ...) log_write(LOG_STATUS, pr_fmt(message), ##__VA_ARGS__)
|
||||
#define log_warn(message, ...) log_write(LOG_WARN, pr_fmt(message), ##__VA_ARGS__)
|
||||
#define log_err(message, ...) log_write(LOG_ERR, pr_fmt(message), ##__VA_ARGS__)
|
||||
#define log_panic(message, ...) log_write(LOG_PANIC, pr_fmt(message), ##__VA_ARGS__)
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
#include <zinnia/errno.h>
|
||||
#include <zinnia/init.h>
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/stack.h>
|
||||
#include <zinnia/tailq.h>
|
||||
#include <zinnia/types.h>
|
||||
#include <bits/sched.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -34,6 +36,7 @@ struct task {
|
||||
uintptr_t user_stack;
|
||||
size_t time_slice;
|
||||
int8_t priority;
|
||||
atomic_bool wake_pending;
|
||||
|
||||
tailq_link(struct task) next;
|
||||
};
|
||||
@@ -57,6 +60,13 @@ void sched_init(struct scheduler* sched);
|
||||
void sched_reschedule(struct scheduler* sched);
|
||||
void sched_yield(struct scheduler* sched);
|
||||
void sched_add_task(struct scheduler* sched, struct task* task);
|
||||
|
||||
// Blocks the current task until sched_wake() is called on it.
|
||||
// Returns immediately if a wake is already pending. Must not hold any spinlock.
|
||||
void sched_block(struct scheduler* sched);
|
||||
|
||||
// Makes a blocked task runnable again, or marks the wake as pending if it hasn't blocked yet.
|
||||
void sched_wake(struct task* task);
|
||||
[[noreturn]]
|
||||
void sched_to_user(uintptr_t ctx);
|
||||
[[noreturn]]
|
||||
@@ -68,3 +78,5 @@ struct task_start_info {
|
||||
uintptr_t sp;
|
||||
uintptr_t arg;
|
||||
};
|
||||
|
||||
INIT_EXTERN(sched_stage);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Busy-waits in a loop until the lock is freed.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_ANY_ID 0xffff
|
||||
#define USB_ANY_CLASS 0xff
|
||||
|
||||
struct usb_match {
|
||||
uint16_t vendor_id;
|
||||
uint16_t product_id;
|
||||
uint8_t class;
|
||||
uint8_t subclass;
|
||||
uint8_t protocol;
|
||||
uintptr_t data;
|
||||
};
|
||||
|
||||
#define USB_MATCH_DEVICE(vendor, product) \
|
||||
.vendor_id = (vendor), .product_id = (product), .class = USB_ANY_CLASS, .subclass = USB_ANY_CLASS, \
|
||||
.protocol = USB_ANY_CLASS
|
||||
|
||||
#define USB_MATCH_CLASS(c, sc, p) \
|
||||
.vendor_id = USB_ANY_ID, .product_id = USB_ANY_ID, .class = (c), .subclass = (sc), .protocol = (p)
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <zinnia/alloc.h>
|
||||
#include <zinnia/errno.h>
|
||||
#include <stddef.h> // IWYU pragma: keep
|
||||
#include <zinnia/string.h> // IWYU pragma: keep
|
||||
#include <string.h> // IWYU pragma: keep
|
||||
|
||||
#define vec(T) \
|
||||
struct { \
|
||||
|
||||
+93
-52
@@ -29,22 +29,25 @@ assert(c_compiler.has_define('__GNUC__'))
|
||||
add_project_arguments(
|
||||
'-Wno-unused',
|
||||
'-Wno-unused-parameter',
|
||||
'-fno-stack-protector',
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
'-ffreestanding',
|
||||
'-fno-stack-protector',
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
add_project_link_arguments(
|
||||
common_c_args = [
|
||||
'-fno-stack-protector',
|
||||
'-fno-omit-frame-pointer',
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
'-ffreestanding',
|
||||
'-nostdinc',
|
||||
]
|
||||
|
||||
common_link_args = [
|
||||
'-ffreestanding',
|
||||
'-nostdlib',
|
||||
'-znoexecstack',
|
||||
'-Wl,--gc-sections',
|
||||
'-Wl,--build-id=none',
|
||||
language: 'c',
|
||||
)
|
||||
]
|
||||
|
||||
common_includes = [
|
||||
include_directories('include'),
|
||||
@@ -70,12 +73,14 @@ foreach k, v : kconfig
|
||||
endif
|
||||
endforeach
|
||||
|
||||
kernel_c_args = ['-DIN_KERNEL']
|
||||
kernel_c_args = ['-D__IN_KERNEL']
|
||||
|
||||
kernel_lds = meson.current_source_dir() / 'boot' / 'kernel.ld'
|
||||
kernel_link_args = [
|
||||
'-T' + kernel_lds,
|
||||
'-Wl,--export-dynamic',
|
||||
'-shared',
|
||||
'-Wl,-Bsymbolic',
|
||||
'-Wl,-soname,zinnia',
|
||||
]
|
||||
|
||||
kernel_includes = []
|
||||
@@ -92,71 +97,107 @@ subdir('syscalls')
|
||||
subdir('dev')
|
||||
subdir('fs')
|
||||
|
||||
subdir('tests')
|
||||
|
||||
configure_file(
|
||||
configuration: cfg,
|
||||
output: 'config.h',
|
||||
)
|
||||
|
||||
zinnia_datadir = get_option('datadir') / 'zinnia'
|
||||
|
||||
mod_summary = {}
|
||||
modular_modules = {}
|
||||
builtin_libs = []
|
||||
|
||||
foreach name, v : modules
|
||||
is_modular = v.get('modular', false)
|
||||
mod_sources = v.get('sources')
|
||||
mod_c_args = v.get('c_args', [])
|
||||
mod_link_args = v.get('link_args', [])
|
||||
mod_includes = v.get('include_directories', [])
|
||||
mod_link = v.get('link_with', [])
|
||||
|
||||
if is_modular
|
||||
shared_library(
|
||||
name,
|
||||
sources: mod_sources,
|
||||
c_args: ['-fPIC', '-fPIE', '-DIN_MODULE', mod_c_args],
|
||||
link_args: [
|
||||
'-T' + module_lds, mod_link_args,
|
||||
],
|
||||
link_depends: module_lds,
|
||||
include_directories: [
|
||||
common_includes,
|
||||
kernel_includes, # We want this, actually
|
||||
mod_includes,
|
||||
],
|
||||
name_prefix: '',
|
||||
name_suffix: 'kso',
|
||||
link_with: mod_link,
|
||||
override_options: ['b_lundef=false'],
|
||||
install_dir: get_option('datadir') / 'zinnia' / 'modules',
|
||||
#link_depends: kernel.full_path(),
|
||||
foreach dep : v.get('depends', [])
|
||||
assert(
|
||||
dep in modules,
|
||||
'Module "' + name + '" depends on unknown module "' + dep + '"',
|
||||
)
|
||||
else
|
||||
kernel_sources += mod_sources
|
||||
kernel_c_args += mod_c_args
|
||||
kernel_includes += mod_includes
|
||||
kernel_link_args += mod_link_args
|
||||
assert(
|
||||
not modules[dep].get('modular', false),
|
||||
'Module "'
|
||||
+ name
|
||||
+ '" can\'t depend on modular module "'
|
||||
+ dep
|
||||
+ '"',
|
||||
)
|
||||
endforeach
|
||||
|
||||
if v.get('modular', false)
|
||||
modular_modules += {name: v}
|
||||
mod_summary += {name: 'module'}
|
||||
continue
|
||||
endif
|
||||
|
||||
if is_modular
|
||||
mod_summary += {name: 'mod'}
|
||||
else
|
||||
mod_summary += {name: 'builtin'}
|
||||
endif
|
||||
mod_c_args = v.get('c_args', [])
|
||||
mod_includes = v.get('include_directories', [])
|
||||
builtin_libs += static_library(
|
||||
name,
|
||||
sources: v.get('sources'),
|
||||
c_args: [
|
||||
common_c_args,
|
||||
kernel_c_args,
|
||||
mod_c_args,
|
||||
'-D__MODULE_NAME="' + name + '"',
|
||||
],
|
||||
include_directories: [common_includes, kernel_includes, mod_includes],
|
||||
pic: true,
|
||||
)
|
||||
kernel_c_args += mod_c_args
|
||||
kernel_includes += mod_includes
|
||||
kernel_link_args += v.get('link_args', [])
|
||||
mod_summary += {name: 'builtin'}
|
||||
endforeach
|
||||
|
||||
# Kernel
|
||||
executable(
|
||||
kernel = executable(
|
||||
'zinnia',
|
||||
include_directories: [
|
||||
common_includes,
|
||||
kernel_includes,
|
||||
],
|
||||
c_args: [kernel_c_args],
|
||||
link_args: kernel_link_args,
|
||||
c_args: [common_c_args, kernel_c_args],
|
||||
link_args: [common_link_args, kernel_link_args],
|
||||
link_depends: kernel_lds,
|
||||
sources: kernel_sources,
|
||||
install: true,
|
||||
install_dir: get_option('datadir') / 'zinnia',
|
||||
link_whole: builtin_libs,
|
||||
pie: true,
|
||||
install: true,
|
||||
install_dir: zinnia_datadir,
|
||||
)
|
||||
|
||||
foreach name, v : modular_modules
|
||||
shared_library(
|
||||
name,
|
||||
sources: v.get('sources'),
|
||||
c_args: [
|
||||
common_c_args,
|
||||
'-fPIC',
|
||||
'-D__IN_MODULE',
|
||||
'-D__MODULE_NAME="' + name + '"',
|
||||
v.get('c_args', []),
|
||||
],
|
||||
link_args: [
|
||||
common_link_args,
|
||||
'-T' + module_lds, kernel.full_path(),
|
||||
v.get('link_args', []),
|
||||
],
|
||||
link_depends: [module_lds, kernel],
|
||||
include_directories: [
|
||||
common_includes,
|
||||
kernel_includes, # We want this, actually
|
||||
v.get('include_directories', []),
|
||||
],
|
||||
name_prefix: '',
|
||||
name_suffix: 'kso',
|
||||
link_with: v.get('link_with', []),
|
||||
install_dir: zinnia_datadir / 'modules',
|
||||
)
|
||||
endforeach
|
||||
|
||||
# Print a summary
|
||||
summary(mod_summary, section: 'Driver configuration')
|
||||
summary(kconfig, bool_yn: true, section: 'Kernel configuration')
|
||||
|
||||
+137
-2
@@ -1,14 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
# Small script to print all options available inside a built kernel.
|
||||
# Small script to print all module information available.
|
||||
# fmt: off
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from elftools.elf.dynamic import DynamicSegment
|
||||
from elftools.elf.elffile import ELFFile
|
||||
from elftools.elf.relocation import RelrRelocationTable
|
||||
|
||||
SIZEOF_PTR = 8
|
||||
SIZEOF_STRUCT = 40
|
||||
ENDIAN = "little"
|
||||
|
||||
MATCH_SEGMENT = 0x60000007
|
||||
TABLE_FORMAT = "QQQ"
|
||||
PTR_SIZE = 8
|
||||
|
||||
|
||||
def format_id(value):
|
||||
return "****" if value == 0xFFFF else f"{value:04x}"
|
||||
|
||||
|
||||
def format_class(value):
|
||||
return "**" if value == 0xFF else f"{value:02x}"
|
||||
|
||||
|
||||
def format_ids(module, addr):
|
||||
first, second, cls, subcls, iface = struct.unpack(
|
||||
module.endian + "HHBBB", module.read(addr, 7)
|
||||
)
|
||||
ids = f"{format_id(first)}:{format_id(second)}"
|
||||
classes = f"{format_class(cls)}:{format_class(subcls)}:{format_class(iface)}"
|
||||
return f"<id = {ids}, class = {classes}>"
|
||||
|
||||
|
||||
def format_fdt(module, addr):
|
||||
compatible = module.read_string(module.read_pointer(addr))
|
||||
if compatible is None:
|
||||
return "<compatible = *>"
|
||||
return f'<compatible = "{compatible}">'
|
||||
|
||||
|
||||
def format_acpi(module, addr):
|
||||
hid = module.read_string(module.read_pointer(addr))
|
||||
if hid is None:
|
||||
return "<hid = *>"
|
||||
return f'<hid = "{hid}">'
|
||||
|
||||
|
||||
KINDS = {
|
||||
"pci": {"size": 16, "format": format_ids},
|
||||
"usb": {"size": 16, "format": format_ids},
|
||||
"fdt": {"size": 16, "format": format_fdt},
|
||||
"acpi": {"size": 16, "format": format_acpi},
|
||||
}
|
||||
|
||||
|
||||
class Module:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
self.file = ELFFile(stream)
|
||||
self.byteorder = "little" if self.file.little_endian else "big"
|
||||
self.endian = "<" if self.file.little_endian else ">"
|
||||
self.addends = {}
|
||||
for segment in self.file.iter_segments():
|
||||
if isinstance(segment, DynamicSegment):
|
||||
for table in segment.get_relocation_tables().values():
|
||||
if not isinstance(table, RelrRelocationTable) and table.is_RELA():
|
||||
for reloc in table.iter_relocations():
|
||||
self.addends[reloc["r_offset"]] = reloc["r_addend"]
|
||||
|
||||
def match_region(self):
|
||||
for segment in self.file.iter_segments():
|
||||
if segment["p_type"] == MATCH_SEGMENT:
|
||||
return segment["p_vaddr"], segment.data()
|
||||
return 0, b""
|
||||
|
||||
def read(self, addr, size):
|
||||
self.stream.seek(next(self.file.address_offsets(addr, size)))
|
||||
return self.stream.read(size)
|
||||
|
||||
def pointer(self, raw, addr):
|
||||
return raw if raw != 0 else self.addends.get(addr, 0)
|
||||
|
||||
def read_pointer(self, addr):
|
||||
return self.pointer(
|
||||
int.from_bytes(self.read(addr, PTR_SIZE), self.byteorder), addr
|
||||
)
|
||||
|
||||
def read_string(self, addr):
|
||||
if addr == 0:
|
||||
return None
|
||||
self.stream.seek(next(self.file.address_offsets(addr)))
|
||||
raw = b""
|
||||
while (byte := self.stream.read(1)) not in (b"", b"\0"):
|
||||
raw += byte
|
||||
return raw.decode(errors="replace")
|
||||
|
||||
|
||||
def parse_matches(module):
|
||||
base, data = module.match_region()
|
||||
table_size = struct.calcsize(module.endian + TABLE_FORMAT)
|
||||
|
||||
tables = []
|
||||
offset = 0
|
||||
while offset + table_size <= len(data):
|
||||
addr = base + offset
|
||||
raw_name, raw_entries, count = struct.unpack_from(
|
||||
module.endian + TABLE_FORMAT, data, offset
|
||||
)
|
||||
name_addr = module.pointer(raw_name, addr)
|
||||
if name_addr == 0:
|
||||
offset += PTR_SIZE
|
||||
continue
|
||||
offset += table_size
|
||||
|
||||
name = module.read_string(name_addr)
|
||||
kind = KINDS.get(name)
|
||||
if kind is None:
|
||||
tables.append((name, count, None))
|
||||
continue
|
||||
|
||||
entries_addr = module.pointer(raw_entries, addr + PTR_SIZE)
|
||||
entries = [
|
||||
kind["format"](module, entries_addr + i * kind["size"])
|
||||
for i in range(count)
|
||||
]
|
||||
tables.append((name, count, entries))
|
||||
return tables
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
@@ -16,7 +136,8 @@ if __name__ == "__main__":
|
||||
sys.exit(1)
|
||||
|
||||
with open(sys.argv[1], "rb") as elffile:
|
||||
file = ELFFile(elffile)
|
||||
module = Module(elffile)
|
||||
file = module.file
|
||||
|
||||
version = "?"
|
||||
author = "?"
|
||||
@@ -35,3 +156,17 @@ if __name__ == "__main__":
|
||||
print(f"Version:\t{version}")
|
||||
print(f"Author(s):\t{author}")
|
||||
print(f"License:\t{license}")
|
||||
|
||||
tables = parse_matches(module)
|
||||
if not tables:
|
||||
print("Devices:\tnone")
|
||||
else:
|
||||
print("Devices:")
|
||||
for name, count, entries in tables:
|
||||
rules = f"{count} rule{'' if count == 1 else 's'}"
|
||||
if entries is None:
|
||||
print(f" {name} ({rules}, unknown type)")
|
||||
continue
|
||||
print(f" {name} ({rules})")
|
||||
for j, entry in enumerate(entries):
|
||||
print(f" [{j}] = {entry}")
|
||||
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
#include <zinnia/spin.h>
|
||||
#include <zinnia/string.h>
|
||||
#include <zinnia/syscall.h>
|
||||
#include <zinnia/usercopy.h>
|
||||
#include <config.h>
|
||||
#include <string.h>
|
||||
#include <uapi/utsname.h>
|
||||
|
||||
static struct spinlock uname_lock = {};
|
||||
|
||||
Reference in New Issue
Block a user