Initial commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/panic.h>
|
||||
#include <kernel/print.h>
|
||||
|
||||
#define ASSERT(expr, msg, ...) \
|
||||
({ \
|
||||
if (__unlikely(!(expr))) { \
|
||||
kprintf( \
|
||||
"\e[31mKernel panic - Environment is unsound!\n" \
|
||||
"In function \"%s\" (%s:%u):\n" \
|
||||
"Assertion \"%s\" failed! " msg "\n", \
|
||||
__FUNCTION__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
#expr, \
|
||||
##__VA_ARGS__ \
|
||||
); \
|
||||
panic(); \
|
||||
} \
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/compiler.h>
|
||||
|
||||
#define CMDLINE_MAX 0x1000
|
||||
|
||||
#define CMDLINE_OPTION(opt_name, opt_func) \
|
||||
[[__used, __section(".cmdline")]] \
|
||||
static const struct cmdline_option UNIQUE_IDENT(__cmline_option_) = { \
|
||||
.name = opt_name, \
|
||||
.func = opt_func, \
|
||||
}
|
||||
|
||||
struct cmdline_option {
|
||||
// The name of this option.
|
||||
const char* name;
|
||||
// Gets called if this option is present on the command line.
|
||||
// If an option is specified as `name=value`, then the `value` is passed as well.
|
||||
// Otherwise, it's NULL.
|
||||
void (*func)(const char* value);
|
||||
};
|
||||
|
||||
// Parses the command line and invokes all options.
|
||||
void cmdline_parse(const char* cmdline);
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
#define ROUND_UP(value, to) (((value) + ((to) - 1)) / (to))
|
||||
#define ALIGN_DOWN(value, align) (((value) / (align)) * (align))
|
||||
#define ALIGN_UP(value, align) (ROUND_UP(value, align) * align)
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define BIT_TEST(value, bit) (((value) & (1 << bit)) == (1 << bit))
|
||||
|
||||
#define CONTAINER_OF(value, p, field) ((p*)((char*)(&(value)) + offsetof(p, field)))
|
||||
|
||||
#define CONCAT(A, B) _CONCAT(A, B)
|
||||
#define _CONCAT(A, B) A##B
|
||||
|
||||
// Creates a unique identifier for a compilation unit.
|
||||
#define UNIQUE_IDENT(ident) CONCAT(__unique_##ident, __COUNTER__)
|
||||
|
||||
// Ensures that a type is properly defined at this point.
|
||||
// This is used to ensure that e.g. architecture-specific structures are defined.
|
||||
#define ASSERT_TYPE(type) static_assert(sizeof(type) == sizeof(type))
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
// These hints may improve code generation, but are optional and can be stubbed.
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define __cold gnu::cold
|
||||
#define __hot gnu::hot
|
||||
#define __used gnu::used
|
||||
#define __unused gnu::unused
|
||||
#define __format(like, ...) gnu::format(like, __VA_ARGS__)
|
||||
#define __likely(x) __builtin_expect(!!(x), 1)
|
||||
#define __unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define __always_inline gnu::always_inline
|
||||
#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.
|
||||
#ifdef __clang__
|
||||
#define __user __attribute__((noderef, address_space(1)))
|
||||
#define __phys __attribute__((noderef, address_space(2)))
|
||||
#else
|
||||
#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
|
||||
#else
|
||||
#error "Unsupported compiler!"
|
||||
#endif
|
||||
|
||||
// Intrinsics
|
||||
#define __unreachable() __builtin_unreachable()
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct console {
|
||||
const char* name;
|
||||
void* private;
|
||||
|
||||
// Initializes the console.
|
||||
bool (*init)(struct console* con);
|
||||
// Writes to the console.
|
||||
void (*write)(struct console* con, const char* buf, size_t count);
|
||||
};
|
||||
|
||||
// Initializes a console for logging.
|
||||
void console_init();
|
||||
|
||||
// Writes a message to a console.
|
||||
void console_write(const char* buf, size_t len);
|
||||
|
||||
// Defines a new early console which can be used to log early boot messages.
|
||||
// An early console must not make any heap allocations.
|
||||
#define DEFINE_CONSOLE(con) \
|
||||
[[__used, __section(".console")]] \
|
||||
static struct console* UNIQUE_IDENT(console) = (con)
|
||||
@@ -0,0 +1,227 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr char ELF_MAG[4] = {0x7F, 'E', 'L', 'F'};
|
||||
|
||||
typedef uint64_t elf64_addr_t;
|
||||
typedef uint64_t elf64_off_t;
|
||||
typedef uint32_t elf32_addr_t;
|
||||
typedef uint32_t elf32_off_t;
|
||||
|
||||
enum : uint8_t {
|
||||
EI_MAG0 = 0,
|
||||
EI_MAG1 = 1,
|
||||
EI_MAG2 = 2,
|
||||
EI_MAG3 = 3,
|
||||
EI_CLASS = 4,
|
||||
EI_DATA = 5,
|
||||
EI_VERSION = 6,
|
||||
EI_OSABI = 7,
|
||||
EI_ABIVERSION = 8,
|
||||
EI_PAD = 9,
|
||||
EI_NIDENT = 16,
|
||||
};
|
||||
|
||||
enum : uint16_t {
|
||||
EM_X86_64 = 62,
|
||||
EM_AARCH64 = 183,
|
||||
EM_RISCV = 243,
|
||||
EM_LOONGARCH = 258,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
ELFCLASS32 = 1,
|
||||
ELFCLASS64 = 2,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
ELFDATA2LSB = 1,
|
||||
ELFDATA2MSB = 2,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
EV_NONE = 0,
|
||||
EV_CURRENT = 1,
|
||||
EV_NUM = 2,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
ELFOSABI_SYSV = 0,
|
||||
ELFOSABI_HPUX = 1,
|
||||
ELFOSABI_STANDALONE = 255,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
ET_NONE = 0,
|
||||
ET_REL = 1,
|
||||
ET_EXEC = 2,
|
||||
ET_DYN = 3,
|
||||
ET_CORE = 4,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
PT_NULL = 0x00000000,
|
||||
PT_LOAD = 0x00000001,
|
||||
PT_DYNAMIC = 0x00000002,
|
||||
PT_INTERP = 0x00000003,
|
||||
PT_NOTE = 0x00000004,
|
||||
PT_SHLIB = 0x00000005,
|
||||
PT_PHDR = 0x00000006,
|
||||
PT_TLS = 0x00000007,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
PF_X = 0x00000001,
|
||||
PF_W = 0x00000002,
|
||||
PF_R = 0x00000004,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
DT_NULL = 0,
|
||||
DT_NEEDED = 1,
|
||||
DT_PLTRELSZ = 2,
|
||||
DT_PLTGOT = 3,
|
||||
DT_HASH = 4,
|
||||
DT_STRTAB = 5,
|
||||
DT_SYMTAB = 6,
|
||||
DT_RELA = 7,
|
||||
DT_RELASZ = 8,
|
||||
DT_RELAENT = 9,
|
||||
DT_STRSZ = 10,
|
||||
DT_SYMENT = 11,
|
||||
DT_INIT = 12,
|
||||
DT_FINI = 13,
|
||||
DT_SONAME = 14,
|
||||
DT_RPATH = 15,
|
||||
DT_SYMBOLIC = 16,
|
||||
DT_REL = 17,
|
||||
DT_RELSZ = 18,
|
||||
DT_RELENT = 19,
|
||||
DT_PLTREL = 20,
|
||||
DT_DEBUG = 21,
|
||||
DT_TEXTREL = 22,
|
||||
DT_JMPREL = 23,
|
||||
DT_BIND_NOW = 24,
|
||||
DT_INIT_ARRAY = 25,
|
||||
DT_FINI_ARRAY = 26,
|
||||
DT_INIT_ARRAYSZ = 27,
|
||||
DT_FINI_ARRAYSZ = 28,
|
||||
DT_LOOS = 0x60000000,
|
||||
DT_HIOS = 0x6FFFFFFF,
|
||||
DT_LOPROC = 0x70000000,
|
||||
DT_HIPROC = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
AT_NULL = 0,
|
||||
AT_IGNORE = 1,
|
||||
AT_EXECFD = 2,
|
||||
AT_PHDR = 3,
|
||||
AT_PHENT = 4,
|
||||
AT_PHNUM = 5,
|
||||
AT_PAGESZ = 6,
|
||||
AT_BASE = 7,
|
||||
AT_FLAGS = 8,
|
||||
AT_ENTRY = 9,
|
||||
AT_NOTELF = 10,
|
||||
AT_UID = 11,
|
||||
AT_EUID = 12,
|
||||
AT_GID = 13,
|
||||
AT_EGID = 14,
|
||||
};
|
||||
|
||||
struct elf64_ehdr {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
elf64_addr_t e_entry;
|
||||
elf64_off_t e_phoff;
|
||||
elf64_off_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct elf32_ehdr {
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
elf32_addr_t e_entry;
|
||||
elf32_off_t e_phoff;
|
||||
elf32_off_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
};
|
||||
|
||||
struct elf64_phdr {
|
||||
uint32_t p_type;
|
||||
uint32_t p_flags;
|
||||
elf64_off_t p_offset;
|
||||
elf64_addr_t p_vaddr;
|
||||
elf64_addr_t p_paddr;
|
||||
uint64_t p_filesz;
|
||||
uint64_t p_memsz;
|
||||
uint64_t p_align;
|
||||
};
|
||||
|
||||
struct elf32_phdr {
|
||||
uint32_t p_type;
|
||||
elf32_off_t p_offset;
|
||||
elf32_addr_t p_vaddr;
|
||||
elf32_addr_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
};
|
||||
|
||||
struct elf64_dyn {
|
||||
int64_t d_tag;
|
||||
union {
|
||||
uint64_t d_val;
|
||||
elf64_addr_t d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
struct elf32_dyn {
|
||||
int32_t r_offset;
|
||||
union {
|
||||
uint32_t d_val;
|
||||
elf32_addr_t d_ptr;
|
||||
} d_un;
|
||||
};
|
||||
|
||||
struct elf64_nhdr {
|
||||
uint64_t n_namesz;
|
||||
uint64_t n_descsz;
|
||||
uint64_t n_type;
|
||||
};
|
||||
|
||||
struct elf32_nhdr {
|
||||
uint32_t n_namesz;
|
||||
uint32_t n_descsz;
|
||||
uint32_t n_type;
|
||||
};
|
||||
|
||||
struct elf64_auxv {
|
||||
uint64_t atype;
|
||||
uint64_t avalue;
|
||||
};
|
||||
|
||||
struct elf32_auxv {
|
||||
uint32_t atype;
|
||||
uint32_t avalue;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define __init __used, __section(".init.text"), __cold
|
||||
#define __initdata __used, __section(".init.data")
|
||||
#define __initdata_sorted(name) __used, __section(".init.data." name)
|
||||
|
||||
struct boot_file {
|
||||
uint8_t* data;
|
||||
size_t length;
|
||||
const char* path;
|
||||
};
|
||||
|
||||
struct boot_info {
|
||||
char* cmdline;
|
||||
struct phys_mem* mem_map;
|
||||
size_t num_mem_maps;
|
||||
phys_t phys_base;
|
||||
virt_t virt_base;
|
||||
virt_t hhdm_base;
|
||||
struct boot_file* files;
|
||||
size_t num_files;
|
||||
};
|
||||
|
||||
extern uint8_t __ld_stack_top[];
|
||||
extern uint8_t __ld_stack_bottom[];
|
||||
|
||||
// Entry point for the kernel after arch-specific setup has finished.
|
||||
void kernel_entry();
|
||||
|
||||
// Initializes the early parts of the kernel.
|
||||
void kernel_early_init();
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main(struct boot_info* info);
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main_task();
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/irq.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/list.h>
|
||||
#include <kernel/vector.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum irq_status : uint8_t {
|
||||
IRQ_STATUS_HANDLED,
|
||||
IRQ_STATUS_IGNORED,
|
||||
};
|
||||
|
||||
enum irq_polarity : uint8_t {
|
||||
IRQ_POLARITY_LOW,
|
||||
IRQ_POLARITY_HIGH,
|
||||
};
|
||||
|
||||
enum irq_trigger_mode : uint8_t {
|
||||
IRQ_TRIGGER_EDGE,
|
||||
IRQ_TRIGGER_LEVEL,
|
||||
};
|
||||
|
||||
struct irq_handler {
|
||||
enum irq_status (*handler)(void* self);
|
||||
SLIST_LINK(struct irq_handler*) next;
|
||||
};
|
||||
|
||||
struct irq_line {
|
||||
int (*set_config)(enum irq_trigger_mode mode, enum irq_polarity polarity);
|
||||
void (*mask)(struct irq_line* self);
|
||||
void (*unmask)(struct irq_line* self);
|
||||
void (*eoi)(struct irq_line* self);
|
||||
|
||||
SLIST_HEAD(struct irq_handler*) handlers;
|
||||
bool is_busy;
|
||||
};
|
||||
|
||||
void irq_line_attach(struct irq_line* self, struct irq_handler* handler);
|
||||
|
||||
struct irq_percpu {
|
||||
uint32_t level;
|
||||
};
|
||||
|
||||
void irq_lock();
|
||||
|
||||
void irq_unlock();
|
||||
|
||||
// Enables or disables interrupts on this CPU.
|
||||
// Don't use this function directly.
|
||||
static inline void irq_set_state(bool state) {
|
||||
return arch_irq_set_state(state);
|
||||
}
|
||||
|
||||
// Returns true if interrupts are enabled on this CPU.
|
||||
// Don't use this function directly.
|
||||
static inline bool irq_get_state() {
|
||||
return arch_irq_get_state();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#define SLIST_HEAD(type) \
|
||||
struct { \
|
||||
type sl_head; \
|
||||
}
|
||||
|
||||
#define SLIST_LINK(type) \
|
||||
struct { \
|
||||
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 SLIST_INSERT_END(slistelm, elm, field) \
|
||||
do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) \
|
||||
do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) for ((var) = SLIST_FIRST((head)); (var); (var) = SLIST_NEXT((var), field))
|
||||
@@ -0,0 +1,180 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <kernel/types.h>
|
||||
#include <bits/mem.h>
|
||||
#include <stddef.h>
|
||||
|
||||
ASSERT_TYPE(pte_t);
|
||||
|
||||
enum pte_flags {
|
||||
PTE_READ = 1 << 0, // Can read from this page.
|
||||
PTE_WRITE = 1 << 1, // Can write to this page.
|
||||
PTE_EXEC = 1 << 2, // Can execute code on this page.
|
||||
PTE_USER = 1 << 3, // Can be accessed by the user.
|
||||
PTE_DIR = 1 << 4, // Is a non-leaf page.
|
||||
};
|
||||
|
||||
enum cache_mode {
|
||||
CACHE_NONE,
|
||||
CACHE_WRITE_COMBINE,
|
||||
CACHE_WRITE_THROUGH,
|
||||
CACHE_WRITE_BACK,
|
||||
CACHE_MMIO,
|
||||
};
|
||||
|
||||
struct page_table {
|
||||
phys_t root;
|
||||
struct spinlock lock;
|
||||
};
|
||||
|
||||
enum alloc_flags {
|
||||
ALLOC_NOZERO = 1 << 0, // Don't zero out the allocated memory.
|
||||
ALLOC_MEM32 = 1 << 1, // Allocated memory needs to fit inside 32 bits.
|
||||
ALLOC_MEM20 = 1 << 2, // Allocated memory needs to fit inside 20 bits.
|
||||
};
|
||||
|
||||
enum phys_mem_usage {
|
||||
PHYS_RESERVED,
|
||||
PHYS_USABLE,
|
||||
PHYS_RECLAIMABLE,
|
||||
};
|
||||
|
||||
struct phys_mem {
|
||||
phys_t address;
|
||||
size_t length;
|
||||
enum phys_mem_usage usage;
|
||||
};
|
||||
|
||||
enum page_type {
|
||||
PAGE_PHYS = 0, // Regular physical memory.
|
||||
};
|
||||
|
||||
struct page {
|
||||
enum page_type type;
|
||||
int32_t flags;
|
||||
size_t refcount;
|
||||
union {
|
||||
struct {
|
||||
struct page* next; // Pointer to the next chunk.
|
||||
size_t count; // Amount of free pages.
|
||||
} freelist;
|
||||
};
|
||||
};
|
||||
static_assert(0x1000 % sizeof(struct page) == 0, "struct must be a multiple of the page size!");
|
||||
static_assert(sizeof(struct page) <= 64, "struct must be smaller than 64 bytes!");
|
||||
|
||||
struct address_space {
|
||||
// TODO
|
||||
};
|
||||
|
||||
extern struct page_table mem_kernel_table;
|
||||
|
||||
// Base address of the `struct page` array.
|
||||
extern struct page* mem_pfndb;
|
||||
|
||||
// Base address of the HHDM.
|
||||
extern virt_t mem_hhdm_base;
|
||||
#define HHDM_PTR(paddr) (void*)((paddr) + mem_hhdm_base)
|
||||
|
||||
void mem_init(struct phys_mem* map, size_t length, virt_t kernel_virt, phys_t kernel_phys, virt_t tmp_hhdm);
|
||||
|
||||
void mem_phys_bootstrap(struct phys_mem* mem);
|
||||
void mem_phys_init(struct phys_mem* map, size_t length);
|
||||
|
||||
// Allocates a region of memory which can be smaller than the page size.
|
||||
// Returns `nullptr` if the allocator cannot provide an allocation for the
|
||||
// given `length` + `flags`. Always returns `nullptr` if `length` is 0.
|
||||
zn_status_t mem_alloc(size_t length, enum alloc_flags flags, void** out);
|
||||
|
||||
// Frees an allocation created by `mem_alloc`.
|
||||
// Passing `nullptr` is a no-op.
|
||||
zn_status_t mem_free(void* mem);
|
||||
|
||||
// Allocates an amount of contiguous physical pages.
|
||||
zn_status_t mem_phys_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out);
|
||||
|
||||
// Frees an allocated region of physical pages.
|
||||
zn_status_t mem_phys_free(phys_t start, size_t num_pages);
|
||||
|
||||
// Creates a new page table for the kernel.
|
||||
zn_status_t mem_pt_new_kernel(struct page_table* pt, enum alloc_flags flags);
|
||||
|
||||
// Creates a new page table for a user process.
|
||||
zn_status_t mem_pt_new_user(struct page_table* pt, enum alloc_flags flags);
|
||||
|
||||
// Sets a page table on the current processor.
|
||||
static inline void mem_pt_set(struct page_table* pt) {
|
||||
arch_mem_pt_set(pt);
|
||||
}
|
||||
|
||||
// Maps a single page to a virtual address in the given page table.
|
||||
zn_status_t mem_pt_map(struct page_table* pt, virt_t vaddr, phys_t paddr, enum pte_flags flags, enum cache_mode cache);
|
||||
|
||||
// Changes the protection of a page.
|
||||
zn_status_t mem_pt_protect(struct page_table* pt, virt_t vaddr, enum pte_flags flags);
|
||||
|
||||
// Unmaps a page.
|
||||
zn_status_t mem_pt_unmap(struct page_table* pt, virt_t vaddr);
|
||||
|
||||
// Base address used to access physical memory.
|
||||
static inline virt_t mem_hhdm_addr() {
|
||||
return arch_mem_hhdm_addr();
|
||||
}
|
||||
|
||||
// Base address used to access the page array.
|
||||
static inline virt_t mem_pfndb_addr() {
|
||||
return arch_mem_pfndb_addr();
|
||||
}
|
||||
|
||||
// Base address used to access kernel memory mappings.
|
||||
static inline virt_t mem_mapping_addr() {
|
||||
return arch_mem_mapping_addr();
|
||||
}
|
||||
|
||||
// Bit shift in a page.
|
||||
static inline size_t mem_page_bits() {
|
||||
return arch_mem_page_bits();
|
||||
}
|
||||
|
||||
// Gets the page size in bytes.
|
||||
static inline size_t mem_page_size() {
|
||||
return 1 << mem_page_bits();
|
||||
}
|
||||
|
||||
// The amount of bits in a level.
|
||||
static inline size_t mem_level_bits() {
|
||||
return arch_mem_level_bits();
|
||||
}
|
||||
|
||||
// The amount of levels in an address.
|
||||
static inline size_t mem_num_levels() {
|
||||
return arch_mem_num_levels();
|
||||
}
|
||||
|
||||
// Clears out a page table entry.
|
||||
static inline void mem_pte_clear(pte_t* pte) {
|
||||
return arch_mem_pte_clear(pte);
|
||||
}
|
||||
|
||||
// Builds a page table entry from the given info.
|
||||
static inline pte_t mem_pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache) {
|
||||
return arch_mem_pte_build(addr, flags, cache);
|
||||
}
|
||||
|
||||
// Returns true if the given PTE is present and valid.
|
||||
static inline bool mem_pte_is_present(pte_t* pte) {
|
||||
return arch_mem_pte_is_present(pte);
|
||||
}
|
||||
|
||||
// Returns true if the given PTE contains another level.
|
||||
static inline bool mem_pte_is_dir(pte_t* pte) {
|
||||
return arch_mem_pte_is_dir(pte);
|
||||
}
|
||||
|
||||
// Returns the address component of the page table entry.
|
||||
static inline phys_t mem_pte_address(pte_t* pte) {
|
||||
return arch_mem_pte_address(pte);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/sched.h>
|
||||
|
||||
struct mutex {
|
||||
struct task* owner;
|
||||
bool flag;
|
||||
};
|
||||
|
||||
void mutex_lock(struct mutex* mutex);
|
||||
void mutex_unlock(struct mutex* mutex);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
// Stop all execution upon panic.
|
||||
[[noreturn]]
|
||||
void panic();
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/percpu.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/irq.h>
|
||||
#include <kernel/sched.h>
|
||||
#include <stddef.h>
|
||||
|
||||
ASSERT_TYPE(struct arch_percpu);
|
||||
|
||||
// CPU-relative information.
|
||||
struct percpu {
|
||||
struct percpu* self; // A pointer to this structure.
|
||||
size_t id; // The virtual ID of this CPU.
|
||||
bool online; // Whether this CPU is initialized and active.
|
||||
virt_t kernel_stack; // The kernel mode stack.
|
||||
virt_t user_stack; // The user mode stack.
|
||||
struct arch_percpu arch; // Architecture-specific fields.
|
||||
struct irq_percpu irq; // IRQ information.
|
||||
struct sched_percpu sched; // Scheduler information.
|
||||
};
|
||||
|
||||
// Per-CPU data for the bootstrap processor.
|
||||
extern struct percpu percpu_bsp;
|
||||
|
||||
// Gets the per-CPU data on the current CPU.
|
||||
static inline struct percpu* percpu_get() {
|
||||
return arch_percpu_get();
|
||||
}
|
||||
|
||||
// Allocates a block of memory for a new CPU.
|
||||
struct percpu* percpu_new();
|
||||
|
||||
// Initializes the bootstrap processor.
|
||||
void percpu_bsp_early_init();
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/compiler.h>
|
||||
|
||||
[[__format(printf, 1, 2)]]
|
||||
void kprintf(const char* message, ...);
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/sched.h>
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
ASSERT_TYPE(struct arch_context);
|
||||
ASSERT_TYPE(struct arch_task_context);
|
||||
|
||||
typedef size_t tid_t;
|
||||
|
||||
enum task_state {
|
||||
TASK_STATE_RUNNING, // Task is active and running.
|
||||
TASK_STATE_READY, // Task is active, but not currently scheduled.
|
||||
TASK_STATE_BLOCKED, // Task is waiting on another object.
|
||||
};
|
||||
|
||||
// A task is the smallest unit of the scheduler.
|
||||
struct task {
|
||||
tid_t id;
|
||||
struct process* process;
|
||||
enum task_state state;
|
||||
struct arch_context context;
|
||||
virt_t kernel_stack;
|
||||
virt_t user_stack;
|
||||
size_t time_slice;
|
||||
int8_t priority;
|
||||
};
|
||||
|
||||
// Per-CPU data for scheduling.
|
||||
struct sched_percpu {
|
||||
struct task* current;
|
||||
};
|
||||
|
||||
void sched_init();
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Busy-waits in a loop until the lock is freed.
|
||||
// Does not put the CPU to sleep.
|
||||
struct spinlock {
|
||||
bool locked;
|
||||
};
|
||||
|
||||
// Attempts to lock a spinlock. If it's already locked, waits until it's freed.
|
||||
void spin_lock(struct spinlock* spin);
|
||||
|
||||
// Unlocks a spinlock.
|
||||
void spin_unlock(struct spinlock* spin);
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
zn_status_t syscall_dispatch(reg_t num, reg_t a0, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6);
|
||||
|
||||
[[noreturn]]
|
||||
void syscall_panic(zn_status_t err);
|
||||
|
||||
zn_status_t syscall_log(__user const char* msg, size_t len);
|
||||
|
||||
zn_status_t syscall_archctl(uint32_t op, size_t value);
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/types.h>
|
||||
#include <kernel/common.h>
|
||||
|
||||
ASSERT_TYPE(phys_t);
|
||||
ASSERT_TYPE(virt_t);
|
||||
ASSERT_TYPE(reg_t);
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <uapi/uio.h>
|
||||
|
||||
struct iovec_iter {};
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <bits/usercopy.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct usercopy_region {
|
||||
void (*start_ip)();
|
||||
void (*end_ip)();
|
||||
void (*fault_ip)();
|
||||
};
|
||||
|
||||
// Copies a block of data from user to kernel memory.
|
||||
bool usercopy_read(uint8_t* dst, const __user uint8_t* src, size_t len) {
|
||||
return arch_usercopy_read(dst, src, len);
|
||||
}
|
||||
|
||||
// Copies a block of data from kernel to user memory.
|
||||
bool usercopy_write(__user uint8_t* dst, const uint8_t* src, size_t len) {
|
||||
return arch_usercopy_write(dst, src, len);
|
||||
}
|
||||
|
||||
// Performs a strlen() on a user string.
|
||||
bool usercopy_strlen(const __user uint8_t* str, size_t max, size_t* len) {
|
||||
return arch_usercopy_strlen(str, max, len);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/mem.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#define VEC(type) \
|
||||
struct { \
|
||||
type* items; \
|
||||
size_t capacity; \
|
||||
size_t length; \
|
||||
}
|
||||
|
||||
// Initializes a vector that can hold `cap` entries.
|
||||
#define VEC_INIT(vec, cap) \
|
||||
do { \
|
||||
*(vec) = (typeof(*(vec))){ \
|
||||
.items = nullptr, \
|
||||
.capacity = (cap), \
|
||||
.length = 0, \
|
||||
}; \
|
||||
} while (0)
|
||||
|
||||
// Frees the memory associated with the `vec`.
|
||||
#define VEC_FREE(vec) \
|
||||
do { \
|
||||
kfree((vec)->items); \
|
||||
(vec)->items = NULL; \
|
||||
(vec)->capacity = 0; \
|
||||
(vec)->length = 0; \
|
||||
} while (0)
|
||||
|
||||
// Pushes a new `item` to the `vec`.
|
||||
#define VEC_PUSH(vec, item) \
|
||||
do { \
|
||||
auto __vec = (vec); \
|
||||
/* Default capacity. */ \
|
||||
if (__vec->capacity == 0) \
|
||||
__vec->capacity = 16; \
|
||||
/* If no container was allocated, do it now.*/ \
|
||||
if (__vec->items == nullptr) { \
|
||||
__vec->items = kmalloc(sizeof(typeof(*(__vec->items))) * __vec->capacity, KMF_ZEROED); \
|
||||
__vec->length = 0; \
|
||||
} \
|
||||
/* If the new entry would overflow the buffer, double the capacity. */ \
|
||||
if (__vec->length + 1 > __vec->capacity) { \
|
||||
__vec->capacity *= 2; \
|
||||
__vec->items = krealloc(__vec->items, sizeof(typeof(*(__vec->items))) * __vec->capacity); \
|
||||
} \
|
||||
__vec->items[__vec->length] = (item); \
|
||||
__vec->length++; \
|
||||
} while (0)
|
||||
|
||||
// Removes the element at index `idx` and moves all other members so the vec is contiguous again.
|
||||
#define VEC_POP(vec, idx) \
|
||||
do { \
|
||||
auto __vec = (vec); \
|
||||
if ((idx) >= __vec->length) \
|
||||
break; \
|
||||
/* Move all data back by one entry. */ \
|
||||
memmove( \
|
||||
__vec->items + (idx), \
|
||||
__vec->items + (idx) + 1, \
|
||||
sizeof(typeof(*(__vec->items))) * (__vec->length - (idx)) \
|
||||
); \
|
||||
__vec->length--; \
|
||||
} while (0)
|
||||
|
||||
// Iterates over `vec` with iterator variable `var_name`.
|
||||
#define VEC_ITER(vec, var_name) \
|
||||
for (typeof(((vec)->items)) var_name = (vec)->items + 0; (var_name) < (vec)->items + (vec)->length; var_name++)
|
||||
|
||||
// Looks for a `value` in the `vec` and returns the index of the first match in `result`.
|
||||
// If unsuccessful, returns -1.
|
||||
#define VEC_FIND(vec, result, value) \
|
||||
do { \
|
||||
result = -1; \
|
||||
for (usize __i = 0; __i < (vec)->length; __i++) { \
|
||||
if ((vec)->items[__i] == value) { \
|
||||
result = __i; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
true; \
|
||||
} while (0)
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef ZINNIA_STDARG_H
|
||||
#define ZINNIA_STDARG_H
|
||||
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef ZINNIA_STDATOMIC_H
|
||||
#define ZINNIA_STDATOMIC_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_store_explicit(ptr, des, ord) __atomic_store_n(ptr, des, ord)
|
||||
#define atomic_store(ptr, des) atomic_store_explicit(ptr, des, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_load_explicit(ptr, ord) __atomic_load_n(ptr, ord)
|
||||
#define atomic_load(ptr) atomic_load_explicit(ptr, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_exchange_explicit(ptr, des, ord) __atomic_exchange_n(ptr, des, ord)
|
||||
#define atomic_exchange(ptr, des) atomic_exchange_explicit(ptr, des, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_add_explicit(ptr, op, ord) __atomic_fetch_add(ptr, op, ord)
|
||||
#define atomic_fetch_add(ptr, op) atomic_fetch_add_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_sub_explicit(ptr, op, ord) __atomic_fetch_sub(ptr, op, ord)
|
||||
#define atomic_fetch_sub(ptr, op) atomic_fetch_sub_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_or_explicit(ptr, op, ord) __atomic_fetch_or(ptr, op, ord)
|
||||
#define atomic_fetch_or(ptr, op) atomic_fetch_or_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_xor_explicit(ptr, op, ord) __atomic_fetch_xor(ptr, op, ord)
|
||||
#define atomic_fetch_xor(ptr, op) atomic_fetch_xor_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#define atomic_fetch_and_explicit(ptr, op, ord) __atomic_fetch_and(ptr, op, ord)
|
||||
#define atomic_fetch_and(ptr, op) atomic_fetch_and_explicit(ptr, op, __ATOMIC_SEQ_CST)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef ZINNIA_STDDEF_H
|
||||
#define ZINNIA_STDDEF_H
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef signed long ssize_t;
|
||||
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef ZINNIA_STDINT_H
|
||||
#define ZINNIA_STDINT_H
|
||||
|
||||
typedef __INT8_TYPE__ int8_t;
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
#define INT8_MAX __INT8_MAX__
|
||||
#define UINT8_MAX __UINT8_MAX__
|
||||
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
typedef __UINT16_TYPE__ uint16_t;
|
||||
#define INT16_MAX __INT16_MAX__
|
||||
#define UINT16_MAX __UINT16_MAX__
|
||||
|
||||
typedef __INT32_TYPE__ int32_t;
|
||||
typedef __UINT32_TYPE__ uint32_t;
|
||||
#define INT32_MAX __INT32_MAX__
|
||||
#define UINT32_MAX __UINT32_MAX__
|
||||
|
||||
typedef __INT64_TYPE__ int64_t;
|
||||
typedef __UINT64_TYPE__ uint64_t;
|
||||
#define INT64_MAX __INT64_MAX__
|
||||
#define UINT64_MAX __UINT64_MAX__
|
||||
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
#define INTPTR_MAX __INTPTR_MAX__
|
||||
#define UINTPTR_MAX __UINTPTR_MAX__
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef ZINNIA_STRING_H
|
||||
#define ZINNIA_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
size_t strlen(const char* str);
|
||||
size_t strnlen(const char* str, size_t len);
|
||||
int memcmp(const void* s1, const void* s2, size_t size);
|
||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n);
|
||||
void* memmove(void* dstptr, const void* srcptr, size_t size);
|
||||
void* memset(void* dest, int value, size_t n);
|
||||
char* strncpy(char* restrict dst, const char* restrict src, size_t len);
|
||||
int strcmp(const char* str1, const char* str2);
|
||||
int strncmp(const char* str1, const char* str2, size_t len);
|
||||
char* strchr(const char* c, int s);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user