stuff2
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
// These hints may improve code generation, but are optional and can be stubbed.
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
// Attributes that affect data layout.
|
||||
#define __section(x) gnu::section(x)
|
||||
#define __aligned(x) gnu::aligned(x)
|
||||
#define __packed gnu::packed
|
||||
#define __naked gnu::naked
|
||||
|
||||
// These hints may improve code generation.
|
||||
#define __cold gnu::cold
|
||||
#define __hot gnu::hot
|
||||
#define __used gnu::used
|
||||
@@ -9,17 +14,6 @@
|
||||
#define __format(like, ...) gnu::format(like, __VA_ARGS__)
|
||||
#define __likely(x) __builtin_expect(!!(x), 1)
|
||||
#define __unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define __always_inline gnu::always_inline
|
||||
#else
|
||||
#define __cold
|
||||
#define __hot
|
||||
#define __used
|
||||
#define __unused
|
||||
#define __format(like, ...)
|
||||
#define __likely(x) x
|
||||
#define __unlikely(x) x
|
||||
#define __always_inline
|
||||
#endif
|
||||
|
||||
// Only clang has these hints, GCC will throw a warning here.
|
||||
// They also don't support the C23 attribute format.
|
||||
@@ -30,18 +24,3 @@
|
||||
#define __user
|
||||
#define __phys
|
||||
#endif
|
||||
|
||||
// Attributes that affect data layout. Not optional.
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define __weak gnu::weak
|
||||
#define __section(x) gnu::section(x)
|
||||
#define __aligned(x) gnu::aligned(x)
|
||||
#define __packed gnu::packed
|
||||
#define __naked gnu::naked
|
||||
#define __alias(x) gnu::alias(x)
|
||||
#else
|
||||
#error "Unsupported compiler!"
|
||||
#endif
|
||||
|
||||
// Intrinsics
|
||||
#define __unreachable() __builtin_unreachable()
|
||||
|
||||
+33
-31
@@ -2,37 +2,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define ELF_ARCH_CLASS ELFCLASS64
|
||||
#define ELF_ARCH_DATA ELFDATA2LSB
|
||||
#define ELF_ARCH_MACHINE EM_X86_64
|
||||
|
||||
#define ELF_R_SYM ELF64_R_SYM
|
||||
#define ELF_R_TYPE ELF64_R_TYPE
|
||||
#define ELF_ST_BIND ELF64_ST_BIND
|
||||
#define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
|
||||
#define elf_ehdr elf64_ehdr
|
||||
#define elf_phdr elf64_phdr
|
||||
#define elf_dyn elf64_dyn
|
||||
#define elf_addr elf64_addr
|
||||
#define elf_off elf64_off
|
||||
#define elf_nhdr elf64_nhdr
|
||||
#define elf_auxv elf64_auxv
|
||||
#define elf_sym elf64_sym
|
||||
#define elf_rela elf64_rela
|
||||
#define elf_rel elf64_rel
|
||||
|
||||
enum {
|
||||
R_NONE = 0,
|
||||
R_COPY = 5,
|
||||
R_GLOB_DAT = 6,
|
||||
R_JUMP_SLOT = 7,
|
||||
R_RELATIVE = 8,
|
||||
R_IRELATIVE = 37,
|
||||
};
|
||||
#endif
|
||||
|
||||
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
|
||||
|
||||
typedef uint64_t elf64_addr_t;
|
||||
@@ -369,3 +338,36 @@ static inline uint32_t ELF64_R_SYM(uint64_t info) {
|
||||
static inline uint32_t ELF64_R_TYPE(uint64_t info) {
|
||||
return info & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
// Architecture specific bits.
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define ELF_ARCH_CLASS ELFCLASS64
|
||||
#define ELF_ARCH_DATA ELFDATA2LSB
|
||||
#define ELF_ARCH_MACHINE EM_X86_64
|
||||
|
||||
#define ELF_R_SYM ELF64_R_SYM
|
||||
#define ELF_R_TYPE ELF64_R_TYPE
|
||||
#define ELF_ST_BIND ELF64_ST_BIND
|
||||
#define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
|
||||
#define elf_ehdr elf64_ehdr
|
||||
#define elf_phdr elf64_phdr
|
||||
#define elf_dyn elf64_dyn
|
||||
#define elf_addr elf64_addr
|
||||
#define elf_off elf64_off
|
||||
#define elf_nhdr elf64_nhdr
|
||||
#define elf_auxv elf64_auxv
|
||||
#define elf_sym elf64_sym
|
||||
#define elf_rela elf64_rela
|
||||
#define elf_rel elf64_rel
|
||||
|
||||
enum {
|
||||
R_NONE = 0,
|
||||
R_COPY = 5,
|
||||
R_GLOB_DAT = 6,
|
||||
R_JUMP_SLOT = 7,
|
||||
R_RELATIVE = 8,
|
||||
R_IRELATIVE = 37,
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <zinnia/syscall_stubs.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -10,12 +11,12 @@ extern "C" {
|
||||
|
||||
// Blocks the calling thread if *addr == expected.
|
||||
// Returns immediately if the value at addr has changed.
|
||||
static inline zn_status_t zn_futex_wait(int* addr, int expected) {
|
||||
static inline zn_status_t zn_futex_wait(int32_t* addr, int32_t expected) {
|
||||
return zn_syscall2(addr, expected, ZN_SYSCALL_FUTEX_WAIT);
|
||||
}
|
||||
|
||||
// Wakes up to `count` threads waiting on the given futex address.
|
||||
static inline zn_status_t zn_futex_wake(int* addr, int count) {
|
||||
static inline zn_status_t zn_futex_wake(int32_t* addr, int32_t count) {
|
||||
return zn_syscall2(addr, count, ZN_SYSCALL_FUTEX_WAKE);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,8 @@ static inline zn_status_t zn_handle_validate(zn_handle_t handle) {
|
||||
return zn_syscall1(handle, ZN_SYSCALL_HANDLE_VALIDATE);
|
||||
}
|
||||
|
||||
// Drops an object handle.
|
||||
// All further references using this handle are invalid. The numerical value
|
||||
// may become valid again, but it is an error to keep using it.
|
||||
// Drops an object handle. All further references using this handle are invalid.
|
||||
// The numerical value may become valid again, but it is an error to keep using it.
|
||||
// The underlying object gets freed only once no other handle references it.
|
||||
static inline zn_status_t zn_handle_drop(zn_handle_t handle) {
|
||||
return zn_syscall1(handle, ZN_SYSCALL_HANDLE_DROP);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef ZINNIA_IPC_H
|
||||
#define ZINNIA_IPC_H
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/rights.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <zinnia/syscall_stubs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Defines an IPC window.
|
||||
struct zn_ipcw {
|
||||
zn_handle_t handles[16];
|
||||
};
|
||||
|
||||
static inline zn_status_t zn_ipcw_init(struct zn_ipcw** ipcw) {
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define IPC_GET_STRING(data, str, buf) \
|
||||
do { \
|
||||
memcpy(buf, (data) + (str)->offset, (str)->len); \
|
||||
} while (0)
|
||||
|
||||
#define IPC_SET_STRING(cursor, data, field, string) \
|
||||
do { \
|
||||
const size_t _len = strlen(string); \
|
||||
(field)->offset = cursor; \
|
||||
(field)->len = _len; \
|
||||
memcpy(data + cursor, string, _len); \
|
||||
cursor += _len; \
|
||||
} while (0)
|
||||
|
||||
enum ipc_msg_id {
|
||||
IPC_MSG_FOO,
|
||||
IPC_MSG_BAR,
|
||||
IPC_MSG_BAZ,
|
||||
};
|
||||
|
||||
struct ipc_array {
|
||||
uint16_t offset;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct ipc_option {
|
||||
uint16_t offset;
|
||||
};
|
||||
|
||||
struct ipc_init_register_rq {
|
||||
enum ipc_msg_id id;
|
||||
struct ipc_array name;
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct ipc_kernel_msg {
|
||||
enum ipc_msg_id id;
|
||||
struct ipc_array name;
|
||||
char data[];
|
||||
};
|
||||
@@ -25,7 +25,7 @@ enum zn_vm_flags {
|
||||
// Map at a fixed address, replacing whatever is in the way.
|
||||
ZN_VM_MAP_FIXED = 1 << 8,
|
||||
// Map at a fixed address, failing if anything is in the way.
|
||||
ZB_VM_MAP_FIXED_NOREPLACE = 1 << 9,
|
||||
ZN_VM_MAP_FIXED_NOREPLACE = 1 << 9,
|
||||
// Map the entire range immediately.
|
||||
ZN_VM_MAP_POPULATE = 1 << 10,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,9 @@ static inline zn_status_t zn_ns_create(zn_handle_t* out) {
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
static inline zn_status_t zn_ns_move(zn_handle_t handle, zn_handle_t target_namespace) {
|
||||
// Moves a handle to another namespace.
|
||||
// The handle must have the `ZN_RIGHT_MOVE` right.
|
||||
static inline zn_status_t zn_ns_move(zn_handle_t handle, zn_handle_t target_ns) {
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -45,7 +45,7 @@ void lapic_init(struct local_apic* lapic) {
|
||||
apic_msr |= 1 << 10;
|
||||
else {
|
||||
lapic->xapic_regs = mmio_new(apic_msr & 0xFFFF'F000, 0x1000);
|
||||
ASSERT(lapic->xapic_regs, "Failed to allocate virtual memory!\n");
|
||||
assert(lapic->xapic_regs, "Failed to allocate virtual memory!\n");
|
||||
}
|
||||
|
||||
asm_wrmsr(0x1B, apic_msr);
|
||||
@@ -86,18 +86,18 @@ void lapic_eoi(struct local_apic* lapic) {
|
||||
#define PIC2_DATA_PORT 0xA1
|
||||
|
||||
void pic_disable() {
|
||||
// Note: We initialize the PIC properly, but completely disable it and use the APIC in favor of it.
|
||||
// We initialize the PIC properly, but completely disable it and use the APIC in favor of it.
|
||||
// Remap IRQs so they start at 0x20 since interrupts 0x00..0x1F are used by CPU exceptions.
|
||||
asm_outb(PIC1_COMMAND_PORT, 0x11); // ICW1: Begin initialization and set cascade mode.
|
||||
asm_outb(PIC1_DATA_PORT, 0x20); // ICW2: Set where interrupts should be mapped to (0x20-0x27).
|
||||
asm_outb(PIC1_DATA_PORT, 0x04); // ICW3: Connect IRQ2 (0x04) to the slave PIC.
|
||||
asm_outb(PIC1_DATA_PORT, 0x01); // ICW4: Set the PIC to operate in 8086/88 mode.
|
||||
asm_outb(PIC1_DATA_PORT, 0xFF); // Mask all interrupts.
|
||||
asm_outb(PIC1_COMMAND_PORT, 0x11);
|
||||
asm_outb(PIC1_DATA_PORT, 0x20);
|
||||
asm_outb(PIC1_DATA_PORT, 0x04);
|
||||
asm_outb(PIC1_DATA_PORT, 0x01);
|
||||
asm_outb(PIC1_DATA_PORT, 0xFF);
|
||||
|
||||
// Same for the slave PIC.
|
||||
asm_outb(PIC2_COMMAND_PORT, 0x11); // ICW1: Begin initialization.
|
||||
asm_outb(PIC2_DATA_PORT, 0x28); // ICW2: Set where interrupts should be mapped to (0x28-0x2F).
|
||||
asm_outb(PIC2_DATA_PORT, 0x02); // ICW3: Connect to master PIC at IRQ2.
|
||||
asm_outb(PIC2_DATA_PORT, 0x01); // ICW4: Set the PIC to operate in 8086/88 mode.
|
||||
asm_outb(PIC2_DATA_PORT, 0xFF); // Mask all interrupts.
|
||||
asm_outb(PIC2_COMMAND_PORT, 0x11);
|
||||
asm_outb(PIC2_DATA_PORT, 0x28);
|
||||
asm_outb(PIC2_DATA_PORT, 0x02);
|
||||
asm_outb(PIC2_DATA_PORT, 0x01);
|
||||
asm_outb(PIC2_DATA_PORT, 0xFF);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ void hpet_init() {
|
||||
|
||||
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");
|
||||
assert(!uacpi_table_find_by_signature("HPET", &table), "HPET table not found!\n");
|
||||
|
||||
struct acpi_hpet* hpet = table.ptr;
|
||||
hpet_clock.regs = mmio_new(hpet->address.address, 0x1000);
|
||||
@@ -65,5 +65,5 @@ void hpet_init() {
|
||||
uint64_t caps = hpet_clock.regs[HPET_CAP];
|
||||
hpet_clock.period = (caps >> 32) & 0xFFFF'FFFF;
|
||||
|
||||
ASSERT(clock_switch(&hpet_clock.clock), "Unable to switch to HPET!\n");
|
||||
assert(clock_switch(&hpet_clock.clock), "Unable to switch to HPET!\n");
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ void interrupt_handler(struct arch_context* ctx) {
|
||||
break;
|
||||
case 0x20 ...(0x20 + ARRAY_SIZE(cpu->arch.irq_lines)):
|
||||
struct irq_line* line = cpu->arch.irq_lines[isr - 0x20];
|
||||
ASSERT(line, "Unhandled interrupt on ISR %lu\n", isr);
|
||||
assert(line, "Unhandled interrupt on ISR %lu\n", isr);
|
||||
// TODO: irq_raise(line);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -10,22 +10,27 @@ struct arch_task_context {
|
||||
uint64_t fs_base, gs_base;
|
||||
};
|
||||
|
||||
#define ARCH_ABI(typ, reg, ...) \
|
||||
union { \
|
||||
typ reg, __VA_ARGS__; \
|
||||
}
|
||||
|
||||
struct arch_context {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
ARCH_ABI(uint64_t, r13, a7);
|
||||
ARCH_ABI(uint64_t, r12, a6);
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rsi;
|
||||
uint64_t rdi;
|
||||
ARCH_ABI(uint64_t, r10, a5);
|
||||
ARCH_ABI(uint64_t, r9, a3);
|
||||
ARCH_ABI(uint64_t, r8, a4);
|
||||
ARCH_ABI(uint64_t, rsi, a1);
|
||||
ARCH_ABI(uint64_t, rdi, a0);
|
||||
uint64_t rbp;
|
||||
uint64_t rdx;
|
||||
ARCH_ABI(uint64_t, rdx, a2, ret1);
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t rax;
|
||||
ARCH_ABI(uint64_t, rax, num, ret0);
|
||||
uint64_t isr; // Pushed onto the stack by the interrupt handler stubs.
|
||||
uint64_t error; // Pushed onto the stack by the CPU if the interrupt has an error code.
|
||||
uint64_t rip; // The rest is pushed onto the stack by the CPU during an interrupt.
|
||||
@@ -35,6 +40,8 @@ struct arch_context {
|
||||
uint64_t ss;
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct arch_context) == 176);
|
||||
|
||||
#define ARCH_CTX_NUM rax
|
||||
#define ARCH_CTX_A0 rdi
|
||||
#define ARCH_CTX_A1 rsi
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
// The task frame consists of registers that the C ABI marks as callee-saved.
|
||||
// If we don't save them, these registers are lost during a context switch.
|
||||
// The order of these fields is important.
|
||||
struct task_frame {
|
||||
uint64_t rbx, rbp, r12, r13, r14, r15, rip;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ static const struct usercopy_region read_region = {
|
||||
.fault_ip = x86_64_read_fault,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_read(void* dst, const __user void* src, size_t len, struct usercopy_region** region) {
|
||||
asm volatile(
|
||||
"stac\n"
|
||||
@@ -41,7 +42,6 @@ bool arch_usercopy_read(void* dst, const __user void* src, size_t len, struct us
|
||||
: "i"(&read_region)
|
||||
: "memory"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
extern void x86_64_write_start();
|
||||
@@ -54,6 +54,7 @@ static const struct usercopy_region write_region = {
|
||||
.fault_ip = x86_64_write_fault,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct usercopy_region** region) {
|
||||
asm volatile(
|
||||
"stac\n"
|
||||
@@ -84,7 +85,6 @@ bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct u
|
||||
: "i"(&read_region)
|
||||
: "memory"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
extern void x86_64_strlen_start();
|
||||
|
||||
+8
-15
@@ -1,4 +1,3 @@
|
||||
#include <zinnia/ipc/init.h>
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/assert.h>
|
||||
@@ -62,22 +61,22 @@ static void kernel_main_task(uintptr_t arg0) {
|
||||
struct boot_info* info = (struct boot_info*)arg0;
|
||||
kprintf("%s\n", zinnia_banner); // Say hello!
|
||||
|
||||
ASSERT(info->num_files >= 1, "No init executable provided\n");
|
||||
assert(info->num_files >= 1, "No init executable provided\n");
|
||||
|
||||
struct channel* init_ep;
|
||||
struct channel* other_ep;
|
||||
ASSERT(channel_create(&init_ep, &other_ep) == ZN_OK, "Failed to create init channel\n");
|
||||
assert(channel_create(&init_ep, &other_ep) == ZN_OK, "Failed to create init channel\n");
|
||||
|
||||
struct vas* init_space;
|
||||
ASSERT(vma_new(&init_space) == ZN_OK, "");
|
||||
assert(vma_new(&init_space) == ZN_OK, "");
|
||||
|
||||
struct boot_file* file_data = &info->files[0];
|
||||
struct paged_vmo* init_file;
|
||||
ASSERT(vmo_new_phys(&init_file) == ZN_OK, "");
|
||||
assert(vmo_new_phys(&init_file) == ZN_OK, "");
|
||||
vmo_write(&init_file->object, 0, HHDM_PTR(file_data->data), file_data->length, nullptr);
|
||||
|
||||
struct namespace* ns;
|
||||
ASSERT(namespace_new(&ns) == ZN_OK, "");
|
||||
assert(namespace_new(&ns) == ZN_OK, "");
|
||||
|
||||
struct namespace_desc init_ch_desc = {
|
||||
.type = NAMESPACE_DESC_CHANNEL,
|
||||
@@ -91,7 +90,7 @@ static void kernel_main_task(uintptr_t arg0) {
|
||||
|
||||
struct task* init_task;
|
||||
zn_status_t status = load_server(&info->files[0], init_argv, init_envp, &init_ch_desc, &init_task);
|
||||
ASSERT(status == ZN_OK, "Failed to load init process (%i)\n", status);
|
||||
assert(status == ZN_OK, "Failed to load init process (%i)\n", status);
|
||||
sched_add_task(&percpu_get()->sched, init_task);
|
||||
|
||||
// Load other daemons.
|
||||
@@ -107,16 +106,10 @@ static void kernel_main_task(uintptr_t arg0) {
|
||||
|
||||
struct task* task;
|
||||
status = load_server(&info->files[i], argv, envp, &ch_desc, &task);
|
||||
ASSERT(status == ZN_OK, "Failed to load server \"%s\": (%i)\n", info->files[i].path, status);
|
||||
assert(status == ZN_OK, "Failed to load server \"%s\": (%i)\n", info->files[i].path, status);
|
||||
sched_add_task(&percpu_get()->sched, task);
|
||||
}
|
||||
|
||||
struct ipc_kernel_msg* msg = mem_alloc(0x40, 0);
|
||||
msg->id = IPC_MSG_BAR;
|
||||
memcpy(msg->data, "This message is from the kernel!", 33);
|
||||
|
||||
channel_write(other_ep, msg, 0x40, nullptr, 0);
|
||||
|
||||
while (true) {
|
||||
arch_irq_wait();
|
||||
}
|
||||
@@ -140,7 +133,7 @@ void kernel_main(struct boot_info* info) {
|
||||
irq_unlock();
|
||||
sched_yield(&percpu_get()->sched);
|
||||
|
||||
__unreachable();
|
||||
unreachable();
|
||||
}
|
||||
|
||||
[[__init]]
|
||||
|
||||
@@ -21,12 +21,12 @@ zn_status_t channel_create(struct channel** ep0, struct channel** ep1) {
|
||||
|
||||
a->peer = b;
|
||||
a->lock = (struct spinlock){0};
|
||||
TAILQ_INIT(&a->messages);
|
||||
tailq_init(&a->messages);
|
||||
wait_queue_init(&a->waiters);
|
||||
|
||||
b->peer = a;
|
||||
b->lock = (struct spinlock){0};
|
||||
TAILQ_INIT(&b->messages);
|
||||
tailq_init(&b->messages);
|
||||
wait_queue_init(&b->waiters);
|
||||
|
||||
*ep0 = a;
|
||||
@@ -64,7 +64,7 @@ zn_status_t channel_write(
|
||||
|
||||
// Enqueue on the peer's message queue and wake a waiter.
|
||||
spin_lock(&peer->lock);
|
||||
TAILQ_INSERT_TAIL(&peer->messages, msg, next);
|
||||
tailq_insert_tail(&peer->messages, msg, next);
|
||||
spin_unlock(&peer->lock);
|
||||
|
||||
struct sched_percpu* sched = &percpu_get()->sched;
|
||||
@@ -75,10 +75,10 @@ zn_status_t channel_write(
|
||||
|
||||
zn_status_t channel_read(struct channel* ch, struct channel_msg** msg_out) {
|
||||
spin_lock(&ch->lock);
|
||||
struct channel_msg* msg = TAILQ_FIRST(&ch->messages);
|
||||
struct channel_msg* msg = tailq_first(&ch->messages);
|
||||
|
||||
if (msg != nullptr) {
|
||||
TAILQ_REMOVE(&ch->messages, msg, next);
|
||||
tailq_remove(&ch->messages, msg, next);
|
||||
spin_unlock(&ch->lock);
|
||||
*msg_out = msg;
|
||||
return ZN_OK;
|
||||
@@ -97,7 +97,7 @@ zn_status_t channel_read(struct channel* ch, struct channel_msg** msg_out) {
|
||||
zn_status_t channel_poll(struct channel* ch) {
|
||||
spin_lock(&ch->lock);
|
||||
|
||||
if (!TAILQ_EMPTY(&ch->messages)) {
|
||||
if (!tailq_empty(&ch->messages)) {
|
||||
spin_unlock(&ch->lock);
|
||||
return ZN_OK;
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ zn_status_t elf_load(struct exec_info* info, struct task** out) {
|
||||
if (phdr.p_flags & PF_X)
|
||||
prot |= ZN_VM_MAP_EXEC;
|
||||
|
||||
ASSERT(phdr.p_offset % phdr.p_align == phdr.p_vaddr % phdr.p_align, "");
|
||||
assert(phdr.p_offset % phdr.p_align == phdr.p_vaddr % phdr.p_align, "");
|
||||
|
||||
const uintptr_t misalign = phdr.p_vaddr & (page_size - 1);
|
||||
const uintptr_t map_address = phdr.p_vaddr - misalign;
|
||||
|
||||
@@ -15,7 +15,7 @@ zn_status_t namespace_new(struct namespace** out) {
|
||||
|
||||
namespace->mutex = (struct mutex){0};
|
||||
namespace->next_handle = 1;
|
||||
HASHMAP_INIT(&namespace->descriptors);
|
||||
hashmap_init(&namespace->descriptors);
|
||||
|
||||
*out = namespace;
|
||||
return ZN_OK;
|
||||
@@ -31,10 +31,10 @@ zn_status_t namespace_add_desc(struct namespace* namespace, struct namespace_des
|
||||
handle = atomic_fetch_add_explicit(&namespace->next_handle, 1, memory_order_acq_rel);
|
||||
if (handle <= ZN_HANDLE_INVALID)
|
||||
continue;
|
||||
} while (HASHMAP_GET(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default) != nullptr);
|
||||
} while (hashmap_get(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default) != nullptr);
|
||||
|
||||
zn_status_t status =
|
||||
HASHMAP_INSERT(&namespace->descriptors, handle, desc, hashmap_hash_default, hashmap_eq_default);
|
||||
hashmap_insert(&namespace->descriptors, handle, desc, hashmap_hash_default, hashmap_eq_default);
|
||||
if (status != ZN_OK)
|
||||
return status;
|
||||
|
||||
@@ -49,10 +49,10 @@ zn_status_t namespace_add_desc_at(struct namespace* namespace, struct namespace_
|
||||
return ZN_ERR_BAD_HANDLE;
|
||||
|
||||
// Fail if a descriptor already exists at this handle.
|
||||
if (HASHMAP_GET(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default) != nullptr)
|
||||
if (hashmap_get(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default) != nullptr)
|
||||
return ZN_ERR_ALREADY_EXISTS;
|
||||
|
||||
return HASHMAP_INSERT(&namespace->descriptors, handle, desc, hashmap_hash_default, hashmap_eq_default);
|
||||
return hashmap_insert(&namespace->descriptors, handle, desc, hashmap_hash_default, hashmap_eq_default);
|
||||
}
|
||||
|
||||
zn_status_t namespace_get(struct namespace* namespace, zn_handle_t handle, struct namespace_desc* out) {
|
||||
@@ -65,7 +65,7 @@ zn_status_t namespace_get(struct namespace* namespace, zn_handle_t handle, struc
|
||||
return ZN_ERR_BAD_HANDLE;
|
||||
|
||||
struct namespace_desc* desc =
|
||||
HASHMAP_GET(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default);
|
||||
hashmap_get(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default);
|
||||
if (!desc)
|
||||
return ZN_ERR_BAD_HANDLE;
|
||||
|
||||
@@ -80,7 +80,7 @@ zn_status_t namespace_del_desc(struct namespace* namespace, zn_handle_t handle)
|
||||
if (handle <= ZN_HANDLE_INVALID)
|
||||
return ZN_ERR_BAD_HANDLE;
|
||||
|
||||
if (!HASHMAP_REMOVE(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default))
|
||||
if (!hashmap_remove(&namespace->descriptors, handle, hashmap_hash_default, hashmap_eq_default))
|
||||
return ZN_ERR_BAD_HANDLE;
|
||||
|
||||
return ZN_OK;
|
||||
|
||||
+13
-13
@@ -1,6 +1,5 @@
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/irq.h>
|
||||
#include <kernel/list.h>
|
||||
#include <kernel/percpu.h>
|
||||
@@ -8,6 +7,7 @@
|
||||
#include <kernel/sched.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <kernel/tailq.h>
|
||||
#include <assert.h>
|
||||
#include <bits/irq.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
@@ -35,30 +35,30 @@ void sched_to_user_context(uintptr_t ctx) {
|
||||
|
||||
void sched_init(struct sched_percpu* sched) {
|
||||
struct task* idle_task;
|
||||
ASSERT(
|
||||
assert(
|
||||
task_create("idle", &kernel_vas, nullptr, idle_func, 0, &idle_task) == ZN_OK,
|
||||
"Unable to create idle task!\n"
|
||||
);
|
||||
sched->idle_task = idle_task;
|
||||
sched->current = idle_task;
|
||||
|
||||
TAILQ_INIT(&sched->run_queue);
|
||||
tailq_init(&sched->run_queue);
|
||||
|
||||
kprintf("Scheduler initialized\n");
|
||||
}
|
||||
|
||||
void sched_add_task(struct sched_percpu* sched, struct task* task) {
|
||||
ASSERT(task != nullptr, "Tried to add NULL to the run queue\n");
|
||||
TAILQ_INSERT_TAIL(&sched->run_queue, task, next);
|
||||
assert(task != nullptr, "Tried to add NULL to the run queue\n");
|
||||
tailq_insert_tail(&sched->run_queue, task, next);
|
||||
}
|
||||
|
||||
static struct task* next_task(struct sched_percpu* sched) {
|
||||
struct task* t = TAILQ_FIRST(&sched->run_queue);
|
||||
struct task* t = tailq_first(&sched->run_queue);
|
||||
|
||||
if (t == nullptr)
|
||||
return sched->idle_task;
|
||||
|
||||
TAILQ_REMOVE(&sched->run_queue, t, next);
|
||||
tailq_remove(&sched->run_queue, t, next);
|
||||
|
||||
return t;
|
||||
}
|
||||
@@ -143,7 +143,7 @@ void task_entry(task_fn_t entry, uintptr_t arg0) {
|
||||
}
|
||||
|
||||
void wait_queue_init(struct wait_queue* wq) {
|
||||
TAILQ_INIT(&wq->waiters);
|
||||
tailq_init(&wq->waiters);
|
||||
wq->lock = (struct spinlock){0};
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ void sched_block(struct sched_percpu* sched, struct wait_queue* wq) {
|
||||
spin_lock(&wq->lock);
|
||||
|
||||
sched->current->state = TASK_STATE_BLOCKED;
|
||||
TAILQ_INSERT_TAIL(&wq->waiters, sched->current, next);
|
||||
tailq_insert_tail(&wq->waiters, sched->current, next);
|
||||
|
||||
spin_unlock(&wq->lock);
|
||||
do_reschedule(sched);
|
||||
@@ -161,9 +161,9 @@ void sched_block(struct sched_percpu* sched, struct wait_queue* wq) {
|
||||
void sched_wake(struct sched_percpu* sched, struct wait_queue* wq) {
|
||||
spin_lock(&wq->lock);
|
||||
|
||||
struct task* t = TAILQ_FIRST(&wq->waiters);
|
||||
struct task* t = tailq_first(&wq->waiters);
|
||||
if (t != nullptr) {
|
||||
TAILQ_REMOVE(&wq->waiters, t, next);
|
||||
tailq_remove(&wq->waiters, t, next);
|
||||
t->state = TASK_STATE_READY;
|
||||
sched_add_task(sched, t);
|
||||
}
|
||||
@@ -175,8 +175,8 @@ void sched_wake_all(struct sched_percpu* sched, struct wait_queue* wq) {
|
||||
spin_lock(&wq->lock);
|
||||
|
||||
struct task* t;
|
||||
while ((t = TAILQ_FIRST(&wq->waiters)) != nullptr) {
|
||||
TAILQ_REMOVE(&wq->waiters, t, next);
|
||||
while ((t = tailq_first(&wq->waiters)) != nullptr) {
|
||||
tailq_remove(&wq->waiters, t, next);
|
||||
t->state = TASK_STATE_READY;
|
||||
sched_add_task(sched, t);
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,16 +1,15 @@
|
||||
#include <kernel/spin.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
// TODO: Ticketing system
|
||||
|
||||
void spin_lock(struct spinlock* spin) {
|
||||
while (atomic_exchange_explicit(&spin->locked, true, memory_order_acquire)) {
|
||||
uint32_t my = atomic_fetch_add_explicit(&spin->next, 1, memory_order_relaxed);
|
||||
|
||||
while (atomic_load_explicit(&spin->owner, memory_order_acquire) != my) {
|
||||
asm volatile("pause");
|
||||
}
|
||||
|
||||
atomic_store_explicit(&spin->locked, true, memory_order_release);
|
||||
}
|
||||
|
||||
void spin_unlock(struct spinlock* spin) {
|
||||
atomic_store_explicit(&spin->locked, false, memory_order_release);
|
||||
uint32_t val = atomic_load_explicit(&spin->owner, memory_order_relaxed);
|
||||
atomic_store_explicit(&spin->owner, val + 1, memory_order_release);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <common/compiler.h>
|
||||
#include <kernel/panic.h>
|
||||
|
||||
#define ASSERT(expr, msg, ...) \
|
||||
// Like the standard C assert, but also active in debug builds.
|
||||
#define assert(expr, msg, ...) \
|
||||
({ \
|
||||
if (__unlikely(!(expr))) { \
|
||||
panic( \
|
||||
@@ -19,7 +20,7 @@
|
||||
})
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEBUG_ASSERT(...) ASSERT(__VA_ARGS__)
|
||||
#define debug_assert(...) assert(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_ASSERT(...) ((void)0)
|
||||
#define debug_assert(...) ((void)0)
|
||||
#endif
|
||||
@@ -13,13 +13,13 @@ struct channel_msg {
|
||||
size_t num_bytes;
|
||||
struct namespace_desc* descs;
|
||||
size_t num_handles;
|
||||
TAILQ_LINK(struct channel_msg) next;
|
||||
tailq_link(struct channel_msg) next;
|
||||
};
|
||||
|
||||
struct channel {
|
||||
struct channel* peer;
|
||||
struct spinlock lock;
|
||||
TAILQ_HEAD(struct channel_msg) messages;
|
||||
tailq_head(struct channel_msg) messages;
|
||||
struct wait_queue waiters;
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
// Ensure a hash is never zero (zero is the empty sentinel).
|
||||
#define _HASHMAP_FIXHASH(h) ((h) == 0 ? (uint64_t)1 : (h))
|
||||
|
||||
#define HASHMAP(K, V) \
|
||||
#define hashmap(K, V) \
|
||||
struct { \
|
||||
K* keys; \
|
||||
V* values; \
|
||||
@@ -34,7 +34,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
size_t count; \
|
||||
}
|
||||
|
||||
#define HASHMAP_INIT(map) \
|
||||
#define hashmap_init(map) \
|
||||
do { \
|
||||
(map)->keys = nullptr; \
|
||||
(map)->values = nullptr; \
|
||||
@@ -43,7 +43,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
(map)->count = 0; \
|
||||
} while (0)
|
||||
|
||||
#define HASHMAP_DESTROY(map) \
|
||||
#define hashmap_destroy(map) \
|
||||
do { \
|
||||
mem_free((map)->keys); \
|
||||
mem_free((map)->values); \
|
||||
@@ -55,7 +55,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
(map)->count = 0; \
|
||||
} while (0)
|
||||
|
||||
#define HASHMAP_COUNT(map) ((map)->count)
|
||||
#define hashmap_count(map) ((map)->count)
|
||||
|
||||
// Internal: probe distance from ideal position.
|
||||
#define _HASHMAP_PROBE_DIST(hash, pos, cap) (((pos) - ((hash) & ((cap) - 1))) & ((cap) - 1))
|
||||
@@ -116,10 +116,10 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Insert or update a key-value pair. Returns ZN_OK or ZN_ERR_NO_MEMORY.
|
||||
#define HASHMAP_INSERT(map, key, val, hash_fn, eq_fn) \
|
||||
// Insert or update a key-value pair. Returns 0 or ZN_ERR_NO_MEMORY.
|
||||
#define hashmap_insert(map, key, val, hash_fn, eq_fn) \
|
||||
({ \
|
||||
zn_status_t _status = ZN_OK; \
|
||||
zn_status_t _status = 0; \
|
||||
typeof(*(map)->keys) _k = (key); \
|
||||
uint64_t _h = _HASHMAP_FIXHASH((hash_fn)(&_k, sizeof(_k))); \
|
||||
/* Check for existing key first. */ \
|
||||
@@ -147,7 +147,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
_status = ZN_ERR_NO_MEMORY; \
|
||||
} \
|
||||
} \
|
||||
if (_status == ZN_OK) { \
|
||||
if (_status == 0) { \
|
||||
_HASHMAP_INSERT_INNER(map, _k, (val), _h); \
|
||||
} \
|
||||
} \
|
||||
@@ -155,7 +155,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
})
|
||||
|
||||
// Look up a key. Returns a pointer to the value, or nullptr if not found.
|
||||
#define HASHMAP_GET(map, key, hash_fn, eq_fn) \
|
||||
#define hashmap_get(map, key, hash_fn, eq_fn) \
|
||||
({ \
|
||||
typeof(*(map)->values)* _result = nullptr; \
|
||||
if ((map)->capacity > 0) { \
|
||||
@@ -180,7 +180,7 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
|
||||
// Remove a key. Returns true if the key was found and removed.
|
||||
// Uses backward-shift deletion to avoid tombstones.
|
||||
#define HASHMAP_REMOVE(map, key, hash_fn, eq_fn) \
|
||||
#define hashmap_remove(map, key, hash_fn, eq_fn) \
|
||||
({ \
|
||||
bool _removed = false; \
|
||||
if ((map)->capacity > 0) { \
|
||||
@@ -218,6 +218,6 @@ static inline bool hashmap_eq_default(const void* a, const void* b, size_t len)
|
||||
|
||||
// Iterate over all occupied entries. `idx` is a size_t loop variable.
|
||||
// Access keys and values via (map)->keys[idx] and (map)->values[idx].
|
||||
#define HASHMAP_FOREACH(map, idx) \
|
||||
#define hashmap_foreach(map, idx) \
|
||||
for (size_t idx = 0; idx < (map)->capacity; idx++) \
|
||||
if ((map)->hashes[idx] != 0)
|
||||
|
||||
@@ -22,7 +22,7 @@ enum irq_trigger_mode : uint8_t {
|
||||
|
||||
struct irq_handler {
|
||||
enum irq_status (*handler)(void* self);
|
||||
SLIST_LINK(struct irq_handler*) next;
|
||||
list_link(struct irq_handler*) next;
|
||||
};
|
||||
|
||||
struct irq_line {
|
||||
@@ -31,7 +31,7 @@ struct irq_line {
|
||||
void (*unmask)(struct irq_line* self);
|
||||
void (*eoi)(struct irq_line* self);
|
||||
|
||||
SLIST_HEAD(struct irq_handler*) handlers;
|
||||
list_head(struct irq_handler*) handlers;
|
||||
bool is_busy;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#define SLIST_HEAD(type) \
|
||||
#define list_head(type) \
|
||||
struct { \
|
||||
type sl_head; \
|
||||
type _sl_head; \
|
||||
}
|
||||
|
||||
#define SLIST_LINK(type) \
|
||||
#define list_link(type) \
|
||||
struct { \
|
||||
type sl_link; \
|
||||
type _sl_link; \
|
||||
}
|
||||
|
||||
#define SLIST_EMPTY(head) ((head)->sl_head == NULL)
|
||||
#define SLIST_FIRST(head) ((head)->sl_head)
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sl_link)
|
||||
#define list_is_empty(head) ((head)->_sl_head == NULL)
|
||||
#define list_first(head) ((head)->_sl_head)
|
||||
#define list_next(elm, field) ((elm)->field._sl_link)
|
||||
|
||||
#define SLIST_INSERT_END(slistelm, elm, field) \
|
||||
#define list_insert_end(slistelm, elm, field) \
|
||||
do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
list_next((elm), field) = list_next((slistelm), field); \
|
||||
list_next((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) \
|
||||
#define list_insert_head(head, elm, field) \
|
||||
do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
list_next((elm), field) = list_first((head)); \
|
||||
list_first((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) for ((var) = SLIST_FIRST((head)); (var); (var) = SLIST_NEXT((var), field))
|
||||
#define list_foreach(var, head, field) for ((var) = list_first((head)); (var); (var) = list_next((var), field))
|
||||
|
||||
@@ -32,7 +32,7 @@ struct namespace_desc {
|
||||
struct namespace {
|
||||
zn_handle_t next_handle;
|
||||
struct mutex mutex;
|
||||
HASHMAP(zn_handle_t, struct namespace_desc) descriptors;
|
||||
hashmap(zn_handle_t, struct namespace_desc) descriptors;
|
||||
};
|
||||
|
||||
// Creates a new namespace with no contents.
|
||||
|
||||
@@ -38,7 +38,7 @@ struct task {
|
||||
size_t time_slice;
|
||||
int8_t priority;
|
||||
|
||||
TAILQ_LINK(struct task) next;
|
||||
tailq_link(struct task) next;
|
||||
};
|
||||
|
||||
// Per-CPU data for scheduling.
|
||||
@@ -46,7 +46,7 @@ struct sched_percpu {
|
||||
struct task* current;
|
||||
struct task* idle_task;
|
||||
size_t preempt_level;
|
||||
TAILQ_HEAD(struct task) run_queue;
|
||||
tailq_head(struct task) run_queue;
|
||||
};
|
||||
|
||||
// Blocks the current task on the given wait queue.
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Busy-waits in a loop until the lock is freed.
|
||||
// Does not put the CPU to sleep.
|
||||
struct spinlock {
|
||||
bool locked;
|
||||
uint32_t owner;
|
||||
uint32_t next;
|
||||
};
|
||||
|
||||
// Attempts to lock a spinlock. If it's already locked, waits until it's freed.
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#define TAILQ_HEAD(type) \
|
||||
#define tailq_head(type) \
|
||||
struct { \
|
||||
type* tqh_first; \
|
||||
type** tqh_last; \
|
||||
}
|
||||
|
||||
#define TAILQ_LINK(type) \
|
||||
#define tailq_link(type) \
|
||||
struct { \
|
||||
type* tqe_next; \
|
||||
type** tqe_prev; \
|
||||
}
|
||||
|
||||
#define TAILQ_INIT(head) \
|
||||
#define tailq_init(head) \
|
||||
do { \
|
||||
(head)->tqh_first = nullptr; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define tailq_first(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define tailq_next(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == nullptr)
|
||||
#define tailq_empty(head) (tailq_first(head) == nullptr)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) \
|
||||
#define tailq_insert_tail(head, elm, field) \
|
||||
do { \
|
||||
(elm)->field.tqe_next = nullptr; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
@@ -32,7 +32,7 @@
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) \
|
||||
#define tailq_insert_head(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != nullptr) \
|
||||
(head)->tqh_first->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
@@ -42,7 +42,7 @@
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) \
|
||||
#define tailq_remove(head, elm, field) \
|
||||
do { \
|
||||
if (((elm)->field.tqe_next) != nullptr) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
|
||||
+25
-26
@@ -1,25 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h> // IWYU pragma: keep
|
||||
#include <string.h> // IWYU pragma: keep
|
||||
|
||||
#define VEC(T) \
|
||||
#define vec(T) \
|
||||
struct { \
|
||||
T* data; \
|
||||
size_t length; \
|
||||
size_t capacity; \
|
||||
}
|
||||
|
||||
#define VEC_INIT(vec) \
|
||||
#define vec_init(vec) \
|
||||
do { \
|
||||
(vec)->data = nullptr; \
|
||||
(vec)->length = 0; \
|
||||
(vec)->capacity = 0; \
|
||||
} while (0)
|
||||
|
||||
#define VEC_DESTROY(vec) \
|
||||
#define vec_destroy(vec) \
|
||||
do { \
|
||||
mem_free((vec)->data); \
|
||||
(vec)->data = nullptr; \
|
||||
@@ -27,13 +26,13 @@
|
||||
(vec)->capacity = 0; \
|
||||
} while (0)
|
||||
|
||||
#define VEC_LENGTH(vec) ((vec)->length)
|
||||
#define VEC_CAPACITY(vec) ((vec)->capacity)
|
||||
#define VEC_EMPTY(vec) ((vec)->length == 0)
|
||||
#define VEC_AT(vec, i) ((vec)->data[i])
|
||||
#define vec_length(vec) ((vec)->length)
|
||||
#define vec_capacity(vec) ((vec)->capacity)
|
||||
#define vec_empty(vec) ((vec)->length == 0)
|
||||
#define vec_at(vec, i) ((vec)->data[i])
|
||||
|
||||
// Internal: grow to at least `needed` capacity.
|
||||
#define _VEC_GROW(vec, needed) \
|
||||
#define _vec_grow(vec, needed) \
|
||||
do { \
|
||||
size_t _new_cap = (vec)->capacity == 0 ? 8 : (vec)->capacity; \
|
||||
while (_new_cap < (needed)) \
|
||||
@@ -46,34 +45,34 @@
|
||||
(vec)->capacity = _new_cap; \
|
||||
} while (0)
|
||||
|
||||
// Append an element. Returns ZN_OK or ZN_ERR_NO_MEMORY.
|
||||
#define VEC_PUSH(vec, val) \
|
||||
// Append an element. Returns 0 or ZN_ERR_NO_MEMORY.
|
||||
#define vec_push(vec, val) \
|
||||
({ \
|
||||
zn_status_t _status = ZN_OK; \
|
||||
zn_status_t _status = 0; \
|
||||
if ((vec)->length >= (vec)->capacity) { \
|
||||
_VEC_GROW(vec, (vec)->length + 1); \
|
||||
_vec_grow(vec, (vec)->length + 1); \
|
||||
if ((vec)->data == nullptr) \
|
||||
_status = ZN_ERR_NO_MEMORY; \
|
||||
} \
|
||||
if (_status == ZN_OK) \
|
||||
if (_status == 0) \
|
||||
(vec)->data[(vec)->length++] = (val); \
|
||||
_status; \
|
||||
})
|
||||
|
||||
// Remove and return the last element. Undefined if empty.
|
||||
#define VEC_POP(vec) ((vec)->data[--(vec)->length])
|
||||
#define vec_pop(vec) ((vec)->data[--(vec)->length])
|
||||
|
||||
// Insert at index, shifting subsequent elements right. Returns ZN_OK or ZN_ERR_NO_MEMORY.
|
||||
#define VEC_INSERT(vec, i, val) \
|
||||
// Insert at index, shifting subsequent elements right. Returns 0 or ZN_ERR_NO_MEMORY.
|
||||
#define vec_insert(vec, i, val) \
|
||||
({ \
|
||||
zn_status_t _status = ZN_OK; \
|
||||
zn_status_t _status = 0; \
|
||||
size_t _idx = (i); \
|
||||
if ((vec)->length >= (vec)->capacity) { \
|
||||
_VEC_GROW(vec, (vec)->length + 1); \
|
||||
_vec_grow(vec, (vec)->length + 1); \
|
||||
if ((vec)->data == nullptr) \
|
||||
_status = ZN_ERR_NO_MEMORY; \
|
||||
} \
|
||||
if (_status == ZN_OK) { \
|
||||
if (_status == 0) { \
|
||||
memmove(&(vec)->data[_idx + 1], &(vec)->data[_idx], ((vec)->length - _idx) * sizeof(*(vec)->data)); \
|
||||
(vec)->data[_idx] = (val); \
|
||||
(vec)->length++; \
|
||||
@@ -82,7 +81,7 @@
|
||||
})
|
||||
|
||||
// Remove element at index, shifting subsequent elements left.
|
||||
#define VEC_REMOVE(vec, i) \
|
||||
#define vec_remove(vec, i) \
|
||||
do { \
|
||||
size_t _idx = (i); \
|
||||
(vec)->length--; \
|
||||
@@ -91,7 +90,7 @@
|
||||
} while (0)
|
||||
|
||||
// Remove element at index by swapping with the last element (O(1), unordered).
|
||||
#define VEC_SWAP_REMOVE(vec, i) \
|
||||
#define vec_swap_remove(vec, i) \
|
||||
do { \
|
||||
size_t _idx = (i); \
|
||||
(vec)->length--; \
|
||||
@@ -100,6 +99,6 @@
|
||||
} while (0)
|
||||
|
||||
// Clear all elements without freeing memory.
|
||||
#define VEC_CLEAR(vec) ((vec)->length = 0)
|
||||
#define vec_clear(vec) ((vec)->length = 0)
|
||||
|
||||
#define VEC_FOREACH(vec, idx) for (size_t idx = 0; idx < (vec)->length; idx++)
|
||||
#define vec_foreach(vec, idx) for (size_t idx = 0; idx < (vec)->length; idx++)
|
||||
|
||||
@@ -30,13 +30,13 @@ struct pager_ops {
|
||||
struct page_list {
|
||||
uintptr_t offset;
|
||||
struct page* value;
|
||||
SLIST_LINK(struct page_list*) next;
|
||||
list_link(struct page_list*) next;
|
||||
};
|
||||
|
||||
struct paged_vmo {
|
||||
struct vmo object;
|
||||
struct pager_ops source;
|
||||
SLIST_HEAD(struct page_list*) cache;
|
||||
list_head(struct page_list*) cache;
|
||||
struct spinlock lock;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ struct task;
|
||||
|
||||
// A queue of tasks waiting for an event.
|
||||
struct wait_queue {
|
||||
TAILQ_HEAD(struct task) waiters;
|
||||
tailq_head(struct task) waiters;
|
||||
struct spinlock lock;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
#ifndef ZINNIA_STDARG_H
|
||||
#define ZINNIA_STDARG_H
|
||||
#pragma once
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
#define va_start(v, l) __builtin_va_start(v, l)
|
||||
#define va_end(v) __builtin_va_end(v)
|
||||
#define va_arg(v, T) __builtin_va_arg(v, T)
|
||||
#define va_copy(d, s) __builtin_va_copy(d, s)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef ZINNIA_STDATOMIC_H
|
||||
#define ZINNIA_STDATOMIC_H
|
||||
#pragma once
|
||||
|
||||
typedef enum memory_order {
|
||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||
@@ -35,5 +34,3 @@ typedef enum memory_order {
|
||||
#define atomic_fetch_and(ptr, op) atomic_fetch_and_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_thread_fence(ord) __atomic_thread_fence(ord)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#ifndef ZINNIA_STDDEF_H
|
||||
#define ZINNIA_STDDEF_H
|
||||
#pragma once
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef signed long ssize_t;
|
||||
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
|
||||
#endif
|
||||
#define unreachable() __builtin_unreachable()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef ZINNIA_STDINT_H
|
||||
#define ZINNIA_STDINT_H
|
||||
#pragma once
|
||||
|
||||
typedef __INT8_TYPE__ int8_t;
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
@@ -25,5 +24,3 @@ typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
#define INTPTR_MAX __INTPTR_MAX__
|
||||
#define UINTPTR_MAX __UINTPTR_MAX__
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#ifndef ZINNIA_STDLIB_H
|
||||
#define ZINNIA_STDLIB_H
|
||||
#pragma once
|
||||
|
||||
long atol(const char* str, int base);
|
||||
unsigned long atolu(const char* str, int base);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#ifndef ZINNIA_STRING_H
|
||||
#define ZINNIA_STRING_H
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -13,5 +12,3 @@ 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);
|
||||
|
||||
#endif
|
||||
|
||||
+7
-7
@@ -1,11 +1,11 @@
|
||||
#include <common/utils.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/panic.h>
|
||||
#include <kernel/print.h>
|
||||
#include <kernel/vas.h>
|
||||
#include <kernel/virt.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
extern uint8_t __ld_text_start[];
|
||||
@@ -66,7 +66,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
// since we're likely going to map it at a different base address.
|
||||
mem_hhdm_base = tmp_hhdm;
|
||||
|
||||
ASSERT(pmap_new_kernel(&kernel_vas.pmap, 0) == 0, "Unable to allocate the kernel page table\n");
|
||||
assert(pmap_new_kernel(&kernel_vas.pmap, 0) == 0, "Unable to allocate the kernel page table\n");
|
||||
|
||||
// text
|
||||
kprintf("Mapping text segment at %p\n", __ld_text_start);
|
||||
@@ -78,7 +78,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
PTE_READ | PTE_EXEC,
|
||||
CACHE_NONE
|
||||
);
|
||||
ASSERT(!status, "Failed to map %p with error %i\n", p, status);
|
||||
assert(!status, "Failed to map %p with error %i\n", p, status);
|
||||
}
|
||||
|
||||
// rodata
|
||||
@@ -91,7 +91,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
PTE_READ,
|
||||
CACHE_NONE
|
||||
);
|
||||
ASSERT(!status, "Failed to map %p with error %i\n", p, status);
|
||||
assert(!status, "Failed to map %p with error %i\n", p, status);
|
||||
}
|
||||
|
||||
// data
|
||||
@@ -104,7 +104,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
PTE_READ | PTE_WRITE,
|
||||
CACHE_NONE
|
||||
);
|
||||
ASSERT(!status, "Failed to map %p with error %i\n", p, status);
|
||||
assert(!status, "Failed to map %p with error %i\n", p, status);
|
||||
}
|
||||
|
||||
// The kernel address space is divided into 3 segments (plus kernel).
|
||||
@@ -122,7 +122,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
uintptr_t vaddr = (uintptr_t)(map[i].address + p + tmp_hhdm);
|
||||
phys_t paddr = (phys_t)(map[i].address + p);
|
||||
zn_status_t status = pmap_map(&kernel_vas.pmap, vaddr, paddr, PTE_READ | PTE_WRITE, CACHE_NONE);
|
||||
ASSERT(!status, "Failed to map HHDM page %p to %p with error %i\n", (void*)vaddr, (void*)paddr, status);
|
||||
assert(!status, "Failed to map HHDM page %p to %p with error %i\n", (void*)vaddr, (void*)paddr, status);
|
||||
}
|
||||
}
|
||||
mem_hhdm_base = tmp_hhdm;
|
||||
@@ -150,7 +150,7 @@ void mem_init(struct phys_mem* map, size_t map_len, uintptr_t kernel_virt, phys_
|
||||
|
||||
for (size_t page = 0; page < length; page += pgsz) {
|
||||
phys_t paddr;
|
||||
ASSERT(!mem_phys_alloc(1, 0, &paddr), "Failed to allocate memory for PFNDB!\n");
|
||||
assert(!mem_phys_alloc(1, 0, &paddr), "Failed to allocate memory for PFNDB!\n");
|
||||
pmap_map(&kernel_vas.pmap, vaddr + page, paddr, PTE_READ | PTE_WRITE, CACHE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ kernel_sources += files(
|
||||
'slab.c',
|
||||
'usercopy.c',
|
||||
'virt.c',
|
||||
'vas.c',
|
||||
'vma.c',
|
||||
'vmo.c',
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include <zinnia/status.h>
|
||||
#include <common/utils.h>
|
||||
#include <kernel/alloc.h>
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/vas.h>
|
||||
#include <assert.h>
|
||||
|
||||
// Try to create a new, empty virtual address space.
|
||||
zn_status_t vma_new(struct vas** out) {
|
||||
DEBUG_ASSERT(out != nullptr, "Missing out");
|
||||
debug_assert(out != nullptr, "Missing out");
|
||||
|
||||
zn_status_t res;
|
||||
if (!out)
|
||||
+2
-2
@@ -105,7 +105,7 @@ static zn_status_t paged_get_page(struct vmo* vmo, uintptr_t offset_idx, struct
|
||||
|
||||
// Try to find the requested page in the cache.
|
||||
struct page_list* iter;
|
||||
SLIST_FOREACH(iter, &paged->cache, next) {
|
||||
list_foreach(iter, &paged->cache, next) {
|
||||
if (iter->offset != offset_idx)
|
||||
continue;
|
||||
|
||||
@@ -128,7 +128,7 @@ static zn_status_t paged_get_page(struct vmo* vmo, uintptr_t offset_idx, struct
|
||||
entry->value = new_page;
|
||||
|
||||
// Add page to the object cache.
|
||||
SLIST_INSERT_HEAD(&paged->cache, entry, next);
|
||||
list_insert_head(&paged->cache, entry, next);
|
||||
|
||||
*out = new_page;
|
||||
return ZN_OK;
|
||||
|
||||
@@ -33,12 +33,12 @@ static const syscall_fn_t dispatch_table[] = {
|
||||
};
|
||||
|
||||
void syscall_dispatch(struct arch_context* ctx) {
|
||||
const size_t num = ctx->ARCH_CTX_NUM;
|
||||
const size_t num = ctx->num;
|
||||
|
||||
if (num >= ARRAY_SIZE(dispatch_table)) {
|
||||
ctx->ARCH_CTX_RET0 = ZN_ERR_BAD_SYSCALL;
|
||||
ctx->ret0 = ZN_ERR_BAD_SYSCALL;
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->ARCH_CTX_RET0 = dispatch_table[num](ctx);
|
||||
ctx->ret0 = dispatch_table[num](ctx);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <kernel/sched.h>
|
||||
#include <kernel/syscalls.h>
|
||||
#include <kernel/usercopy.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
zn_status_t syscall_thread_create(struct arch_context* ctx) {
|
||||
@@ -114,5 +115,5 @@ zn_status_t syscall_thread_exit(struct arch_context* ctx) {
|
||||
struct sched_percpu* sched = &percpu_get()->sched;
|
||||
sched->current->state = TASK_STATE_DEAD;
|
||||
sched_yield(sched);
|
||||
__unreachable();
|
||||
unreachable();
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'zinnia',
|
||||
license: 'MIT',
|
||||
version: '0.1.0',
|
||||
version: '0.3.0',
|
||||
meson_version: '>=1.1.0',
|
||||
default_options: [
|
||||
'c_std=gnu23',
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
#include <zinnia/channel.h>
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/ipc/init.h>
|
||||
#include <zinnia/system.h>
|
||||
#include <common/compiler.h>
|
||||
#include <stdio.h>
|
||||
@@ -22,7 +21,7 @@ static void zn_printf(const char* msg, ...) {
|
||||
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
zn_printf("devd: starting...\n");
|
||||
zn_handle_t channel = getauxval(AT_INIT_HANDLE);
|
||||
zn_handle_t channel = getauxval(0x1000);
|
||||
|
||||
// while (true) {}
|
||||
uacpi_status status = uacpi_initialize(0);
|
||||
|
||||
+4
-24
@@ -1,6 +1,5 @@
|
||||
#include <zinnia/channel.h>
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/ipc/init.h>
|
||||
#include <zinnia/mem.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <zinnia/system.h>
|
||||
@@ -12,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ZN_ASSERT(expr) \
|
||||
do { \
|
||||
@@ -31,38 +31,18 @@ static void zn_printf(const char* msg, ...) {
|
||||
int32_t len = vsnprintf(buf, sizeof(buf), msg, args);
|
||||
va_end(args);
|
||||
|
||||
zn_log(buf, len);
|
||||
write(1, buf, len);
|
||||
// zn_log(buf, len);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
zn_printf("initd: starting...\n");
|
||||
zn_handle_t channel = getauxval(AT_INIT_HANDLE);
|
||||
zn_handle_t channel = getauxval(0x1000);
|
||||
|
||||
while (true) {
|
||||
static thread_local zn_handle_t handles[32];
|
||||
static thread_local char buf[1024];
|
||||
|
||||
ZN_ASSERT(zn_channel_read(channel, handles, buf, ARRAY_SIZE(handles), ARRAY_SIZE(buf), nullptr, nullptr));
|
||||
|
||||
enum ipc_msg_id* msg = (enum ipc_msg_id*)buf;
|
||||
switch (*msg) {
|
||||
case IPC_MSG_FOO: {
|
||||
struct ipc_init_register_rq* foo = (struct ipc_init_register_rq*)msg;
|
||||
|
||||
char buf[32] = {0};
|
||||
IPC_GET_STRING(foo->data, &foo->name, buf);
|
||||
|
||||
zn_printf("initd: ipc_init_register_rq, peer = \"%s\"\n", buf);
|
||||
} break;
|
||||
case IPC_MSG_BAR: {
|
||||
struct ipc_kernel_msg* foo = (struct ipc_kernel_msg*)msg;
|
||||
|
||||
char buf[32] = {0};
|
||||
IPC_GET_STRING(foo->data, &foo->name, buf);
|
||||
zn_printf("initd: ipc_kernel_msg, msg = \"%s\"\n", buf);
|
||||
} break;
|
||||
case IPC_MSG_BAZ:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
subdir('initd')
|
||||
subdir('devd')
|
||||
subdir('posixd')
|
||||
subdir('unix')
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <zinnia/channel.h>
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/ipc/init.h>
|
||||
#include <zinnia/mem.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <zinnia/system.h>
|
||||
@@ -29,38 +28,45 @@ static void zn_printf(const char* msg, ...) {
|
||||
|
||||
zn_handle_t channel;
|
||||
|
||||
int other_fn(void* arg) {
|
||||
zn_printf("posixd: Hello from the other thread named \"%s\"!\n", (const char*)arg);
|
||||
channel = getauxval(AT_INIT_HANDLE);
|
||||
|
||||
struct ipc_init_register_rq msg = {
|
||||
.id = IPC_MSG_FOO,
|
||||
struct message {
|
||||
size_t id;
|
||||
};
|
||||
|
||||
#define MSG_HI \
|
||||
IPC_START \
|
||||
IPC_STRING \
|
||||
IPC_END
|
||||
|
||||
int other_fn(void* arg) {
|
||||
zn_printf("unix: Hello from the other thread named \"%s\"!\n", (const char*)arg);
|
||||
channel = getauxval(0x1000);
|
||||
|
||||
struct message msg = {.id = 1};
|
||||
char name[] = "posixd";
|
||||
|
||||
zn_status_t status = zn_channel_write(channel, nullptr, &msg, 0, sizeof(msg) + sizeof(name));
|
||||
|
||||
if (status != ZN_OK) {
|
||||
zn_printf("posixd: %s\n", zn_status_to_string(status));
|
||||
zn_printf("unix: %s\n", zn_status_to_string(status));
|
||||
}
|
||||
|
||||
return 69;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
zn_printf("posixd: starting...\n");
|
||||
const char* thread_name = "posix_second_thread";
|
||||
zn_printf("unix: starting...\n");
|
||||
const char* thread_name = "unix_second_thread";
|
||||
|
||||
thrd_t thread;
|
||||
int res = thrd_create(&thread, other_fn, (void*)thread_name);
|
||||
if (res != thrd_success)
|
||||
zn_printf("posixd: errno: %s\n", strerror(errno));
|
||||
zn_printf("posixd: second thread created\n");
|
||||
zn_printf("unix: errno: %s\n", strerror(errno));
|
||||
zn_printf("unix: second thread created\n");
|
||||
|
||||
int res2;
|
||||
zn_printf("posixd: waiting on second thread...\n");
|
||||
zn_printf("unix: waiting on second thread...\n");
|
||||
thrd_join(thread, &res2);
|
||||
zn_printf("posixd: second thread exited with: %i\n", res2);
|
||||
zn_printf("unix: second thread exited with: %i\n", res2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
configure_file(configuration: cfg, output: 'config.h')
|
||||
|
||||
executable(
|
||||
'posixd',
|
||||
'unix',
|
||||
'main.c',
|
||||
'uname.c',
|
||||
link_args: '-static',
|
||||
+1
-1
Submodule subprojects/uacpi updated: e05715b2e6...9c9b26d629
Reference in New Issue
Block a user