Initial commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
---
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
BreakAfterReturnType: Automatic
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
AlignArrayOfStructures: None
|
||||
IndentWidth: 4
|
||||
InsertNewlineAtEOF: true
|
||||
IntegerLiteralSeparator:
|
||||
HexMinDigits: 8
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentGotoLabels: false
|
||||
TabWidth: 4
|
||||
Standard: Latest
|
||||
Language: Cpp
|
||||
LineEnding: LF
|
||||
AlignOperands: DontAlign
|
||||
BitFieldColonSpacing: None
|
||||
ColumnLimit: 120
|
||||
MaxEmptyLinesToKeep: 1
|
||||
PointerAlignment: Left
|
||||
QualifierAlignment: Leave
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterCStyleCast: false
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
BreakAfterAttributes: Always
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakBeforeBraces: Attach
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
SpacesInSquareBrackets: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: true
|
||||
AlignCompound: true
|
||||
AlignFunctionPointers: false
|
||||
AlignEscapedNewlines: DontAlign
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
AccessModifierOffset: -4
|
||||
SpacesInParens: Never
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
IncludeBlocks: Merge
|
||||
IncludeCategories:
|
||||
- Regex: "^<zinnia/.*>"
|
||||
Priority: 1
|
||||
- Regex: "^<kernel/.*>"
|
||||
Priority: 2
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 3
|
||||
- Regex: '^".*\.h"'
|
||||
Priority: 4
|
||||
SortIncludes: CaseInsensitive
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
# IDE Files
|
||||
.vscode/
|
||||
.idea/
|
||||
.cache/
|
||||
|
||||
# Build artifacts
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
compile_commands.json
|
||||
|
||||
# Default build dir
|
||||
build/
|
||||
|
||||
# Generated files
|
||||
.config*
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Marvin Friedrich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,42 @@
|
||||
<img src="zinnia.svg" width="10%"/>
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
# zinnia
|
||||
|
||||
zinnia is a lightweight Unix-like microkernel targeting 64-bit devices.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Please note that this project is a work in progress.
|
||||
> Some parts may not work at all and/or will drastically change over time.
|
||||
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
> [!NOTE]
|
||||
> This repository contains only the kernel and drivers.
|
||||
> If you want to get a bootable image, you might want to check out
|
||||
> **https://github.com/zinnia-os/bootstrap** instead.
|
||||
|
||||
To build the kernel you need `meson` and a C23-compatible GNU-like compiler.
|
||||
Currently supported are GCC and Clang toolchains.
|
||||
|
||||
To configure, run:
|
||||
```sh
|
||||
meson setup $build_dir
|
||||
```
|
||||
|
||||
And to build:
|
||||
```sh
|
||||
meson compile -C $build_dir
|
||||
```
|
||||
|
||||
To cross-compile, you should follow the Meson cross-compilation guide.
|
||||
|
||||
## Debugging
|
||||
|
||||
Follow the debugging guide at **https://github.com/zinnia-os/bootstrap**
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef ZINNIA_ARCHCTL_H
|
||||
#define ZINNIA_ARCHCTL_H
|
||||
|
||||
#include <zinnia/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
// Does nothing.
|
||||
ZN_ARCHCTL_NONE = 0,
|
||||
#ifdef __x86_64__
|
||||
// On x86_64, sets the FSBASE register to the value.
|
||||
ZN_ARCHCTL_SET_FSBASE = 1,
|
||||
#endif
|
||||
} zn_archctl_t;
|
||||
|
||||
// Performs an architecture-dependent operation identified by `op`.
|
||||
zn_status_t zn_archctl(zn_archctl_t op, size_t value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef ZINNIA_CHANNEL_H
|
||||
#define ZINNIA_CHANNEL_H
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Handle which always points to a channel connected to the init server.
|
||||
#define ZN_INIT_CHANNEL ((zn_handle_t)(-1))
|
||||
|
||||
enum zn_channel_flags {
|
||||
// Allow sending messages even if one endpoint is not connected.
|
||||
ZN_CHANNEL_NONBLOCK = 1 << 0,
|
||||
};
|
||||
|
||||
// Creates a new channel.
|
||||
zn_status_t zn_channel_create(enum zn_channel_flags flags, zn_handle_t* endpoint0, zn_handle_t* endpoint1);
|
||||
|
||||
// Maps the message buffer in the address space and returns its base address.
|
||||
// There may only be one message buffer per channel and process.
|
||||
zn_status_t zn_channel_open(
|
||||
zn_handle_t channel,
|
||||
size_t num_handles,
|
||||
size_t num_bytes,
|
||||
zn_handle_t** out_handle_buf,
|
||||
void** out_data_buf
|
||||
);
|
||||
|
||||
// Waits for a new message to appear in the channel buffer.
|
||||
zn_status_t zn_channel_read(zn_handle_t channel);
|
||||
|
||||
// Writes a message to the
|
||||
zn_status_t zn_channel_write(zn_handle_t channel);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef ZINNIA_HANDLE_H
|
||||
#define ZINNIA_HANDLE_H
|
||||
|
||||
#include <zinnia/rights.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// A generic object handle.
|
||||
typedef uint32_t zn_handle_t;
|
||||
|
||||
#define ZN_HANDLE_INVALID ((zn_handle_t)(0))
|
||||
|
||||
// Checks an object handle for validity.
|
||||
zn_status_t zn_handle_validate(zn_handle_t handle);
|
||||
|
||||
// Drops a 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.
|
||||
zn_status_t zn_handle_drop(zn_handle_t handle);
|
||||
|
||||
// Creates a new handle pointing to the same object as pointed to
|
||||
// by the original handle. The new handle may obtain new rights.
|
||||
zn_status_t zn_handle_clone(zn_handle_t handle, zn_rights_t cloned_rights, zn_handle_t* cloned);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef ZINNIA_IPC_POSIX_H
|
||||
#define ZINNIA_IPC_POSIX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
ZNI_POSIX_OPEN_REQ,
|
||||
ZNI_POSIX_OPEN_RES,
|
||||
};
|
||||
|
||||
struct zni_posix_open {
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef ZINNIA_LOG_H
|
||||
#define ZINNIA_LOG_H
|
||||
|
||||
#include <zinnia/status.h>
|
||||
|
||||
// Panics and returns an error status to the parent process.
|
||||
void zn_panic(zn_status_t status);
|
||||
|
||||
void zn_log(const char* message);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef ZINNIA_MEM_H
|
||||
#define ZINNIA_MEM_H
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Virtual memory flags.
|
||||
enum zn_vm_flags {
|
||||
ZN_VM_READ = 1 << 0,
|
||||
ZN_VM_WRITE = 1 << 1,
|
||||
ZN_VM_EXEC = 1 << 2,
|
||||
ZN_VM_SHARED = 1 << 3,
|
||||
};
|
||||
|
||||
enum zn_cache_type {
|
||||
// Generic memory
|
||||
ZN_CACHE_NORMAL,
|
||||
// Write combining
|
||||
ZN_CACHE_WC,
|
||||
// Memory-mapped IO
|
||||
ZN_CACHE_MMIO,
|
||||
};
|
||||
|
||||
// Allocates memory
|
||||
zn_status_t zn_mem_alloc(size_t length, zn_handle_t* out);
|
||||
|
||||
zn_status_t zn_mem_map(zn_handle_t mem, void* addr, size_t len, enum zn_vm_flags flags);
|
||||
|
||||
zn_status_t zn_mem_unmap(zn_handle_t mem, void* addr, size_t len, enum zn_vm_flags flags);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef ZINNIA_RIGHTS_H
|
||||
#define ZINNIA_RIGHTS_H
|
||||
|
||||
typedef enum {
|
||||
// Can be read from.
|
||||
ZN_RIGHT_READ = 1u << 0,
|
||||
// Can be written to.
|
||||
ZN_RIGHT_WRITE = 1u << 1,
|
||||
// Can be executed.
|
||||
ZN_RIGHT_EXECUTE = 1u << 2,
|
||||
// Can be mapped in an address space.
|
||||
ZN_RIGHT_MAP = 1u << 3,
|
||||
// Can be moved to another process.
|
||||
ZN_RIGHT_MOVE = 1u << 4,
|
||||
// Can be cloned.
|
||||
ZN_RIGHT_CLONE = 1u << 5,
|
||||
// Can be deleted.
|
||||
ZN_RIGHT_DELETE = 1u << 6,
|
||||
|
||||
// When cloning, use the same rights as the original.
|
||||
ZN_RIGHTS_IDENTICAL = 1u << 31,
|
||||
ZN_RIGHTS_COMMON = (ZN_RIGHT_MOVE | ZN_RIGHT_CLONE),
|
||||
ZN_RIGHTS_RW = (ZN_RIGHT_READ | ZN_RIGHT_WRITE),
|
||||
} zn_rights_t;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef ZINNIA_STATUS_H
|
||||
#define ZINNIA_STATUS_H
|
||||
|
||||
// Status values that may be returned by the kernel.
|
||||
// A status value of 0 means everything is okay.
|
||||
// A negative value indicates an error from the kernel.
|
||||
// Positive values are available to user processes.
|
||||
typedef enum {
|
||||
ZN_OK = 0,
|
||||
// An internal error occured.
|
||||
ZN_ERR_INTERNAL = -1,
|
||||
// Syscall number is not a recognized syscall.
|
||||
ZN_ERR_BAD_SYSCALL = -2,
|
||||
// This operation is not supported.
|
||||
ZN_ERR_UNSUPPORTED = -3,
|
||||
// System does not have enough free memory for this operation.
|
||||
ZN_ERR_NO_MEMORY = -4,
|
||||
// Process can not own any more handles.
|
||||
ZN_ERR_NO_HANDLES = -5,
|
||||
// One or more of the provided arguments is not valid.
|
||||
ZN_ERR_BAD_ARG = -6,
|
||||
// Argument is outside of the range for valid values.
|
||||
ZN_ERR_BAD_RANGE = -7,
|
||||
// Object handle does not name a valid object or correct type.
|
||||
ZN_ERR_BAD_OBJECT = -8,
|
||||
// Object has insufficient permissions for this operation.
|
||||
ZN_ERR_BAD_PERMS = -9,
|
||||
// Buffer is not large enough or doesn't point to a valid memory region.
|
||||
ZN_ERR_BAD_BUFFER = -10,
|
||||
} zn_status_t;
|
||||
|
||||
// Returns a string describing the error code.
|
||||
const char* zn_status_to_string(zn_status_t status);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef ZINNIA_SYSCALL_NUMBERS_H
|
||||
#define ZINNIA_SYSCALL_NUMBERS_H
|
||||
|
||||
enum {
|
||||
ZN_SYSCALL_PANIC,
|
||||
|
||||
ZN_SYSCALL_LOG,
|
||||
|
||||
ZN_SYSCALL_ARCHCTL,
|
||||
|
||||
ZN_SYSCALL_HANDLE_VALIDATE,
|
||||
ZN_SYSCALL_HANDLE_DROP,
|
||||
ZN_SYSCALL_HANDLE_CLONE,
|
||||
|
||||
ZN_SYSCALL_MEM_ALLOC,
|
||||
ZN_SYSCALL_MEM_FREE,
|
||||
|
||||
ZN_SYSCALL_CHANNEL_CREATE,
|
||||
ZN_SYSCALL_CHANNEL_OPEN,
|
||||
ZN_SYSCALL_CHANNEL_READ,
|
||||
ZN_SYSCALL_CHANNEL_WRITE,
|
||||
|
||||
ZN_SYSCALL_THREAD_CREATE,
|
||||
ZN_SYSCALL_THREAD_DESTROY,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef ZINNIA_THREAD_H
|
||||
#define ZINNIA_THREAD_H
|
||||
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/status.h>
|
||||
|
||||
enum zn_thread_flags {
|
||||
ZN_THREAD_NONE,
|
||||
};
|
||||
|
||||
// Creates a new thread.
|
||||
zn_status_t zn_thread_create(const char* name, zn_handle_t address_space, enum zn_thread_flags flags);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <kernel/percpu.h>
|
||||
#include <kernel/syscalls.h>
|
||||
#include <asm.h>
|
||||
#include <defs.h>
|
||||
#include <gdt.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
struct gdt main_gdt = {
|
||||
.null = 0,
|
||||
.kernel_code32 = 0x00cf9b000000ffff,
|
||||
.kernel_data32 = 0x00cf93000000ffff,
|
||||
.kernel_code64 = 0x00a09b0000000000,
|
||||
.kernel_data64 = 0x0000930000000000,
|
||||
.user_code = 0x00cffb000000ffff,
|
||||
.user_data = 0x0000f30000000000,
|
||||
.user_code64 = 0x0000fb0000000000,
|
||||
.tss = {0x0000890000000000, 0},
|
||||
};
|
||||
|
||||
void gdt_init() {
|
||||
// Set initial values.
|
||||
memcpy(&percpu_get()->arch.gdt, &main_gdt, sizeof(struct gdt));
|
||||
|
||||
struct gdtr gdtr = {
|
||||
.limit = sizeof(struct gdt) - 1,
|
||||
.base = &percpu_get()->arch.gdt,
|
||||
};
|
||||
|
||||
asm volatile("lgdt [%0]" ::"r"(&gdtr));
|
||||
|
||||
// Save the contents of MSR_GS_BASE, as they get cleared by a write to `gs`.
|
||||
virt_t gs = asm_rdmsr(MSR_GS_BASE);
|
||||
asm volatile(
|
||||
"push %0\n"
|
||||
"lea rax, [rip + 1f]\n"
|
||||
"push rax\n"
|
||||
"retfq\n"
|
||||
"1:\n"
|
||||
"mov ax, %1\n"
|
||||
"mov ds, ax\n"
|
||||
"mov es, ax\n"
|
||||
"mov fs, ax\n"
|
||||
"mov gs, ax\n"
|
||||
"mov ss, ax\n"
|
||||
:
|
||||
: "i"(offsetof(struct gdt, kernel_code64)), "i"(offsetof(struct gdt, kernel_data64))
|
||||
: "rax", "memory"
|
||||
);
|
||||
asm_wrmsr(MSR_GS_BASE, gs);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <kernel/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct [[__packed]] idtr {
|
||||
uint16_t limit;
|
||||
struct idt* base;
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uint64_t asm_rdmsr(uint32_t msr) {
|
||||
uint32_t eax;
|
||||
uint32_t edx;
|
||||
asm volatile("rdmsr" : "=a"(eax), "=d"(edx) : "c"(msr) : "memory");
|
||||
return ((uint64_t)edx << 32) | eax;
|
||||
}
|
||||
|
||||
// Writes a 64-bit value to a given MSR.
|
||||
static inline void asm_wrmsr(uint32_t msr, uint64_t val) {
|
||||
uint32_t eax = (uint32_t)val;
|
||||
uint32_t edx = val >> 32;
|
||||
asm volatile("wrmsr" : : "a"(eax), "d"(edx), "c"(msr) : "memory");
|
||||
}
|
||||
|
||||
// Writes a 64-bit value to a control register using XSETBV.
|
||||
static inline void asm_wrxcr(uint32_t reg, uint64_t val) {
|
||||
uint32_t eax = val;
|
||||
uint32_t edx = val >> 32;
|
||||
asm volatile("xsetbv" ::"a"(eax), "d"(edx), "c"(reg) : "memory");
|
||||
}
|
||||
|
||||
// Saves the FPU state to a 512-byte region of memory using FXSAVE.
|
||||
// Pointer must be 16-byte aligned.
|
||||
static inline void asm_fxsave(void* mem) {
|
||||
asm volatile("fxsave %0" : "+m"(*(uint8_t*)mem)::"memory");
|
||||
}
|
||||
|
||||
// Restores the FPU state from a 512-byte region of memory using FXRSTOR.
|
||||
// Pointer must be 16-byte aligned.
|
||||
static inline void asm_fxrstor(void* mem) {
|
||||
asm volatile("fxrstor %0" ::"m"(*(uint8_t*)mem) : "memory");
|
||||
}
|
||||
|
||||
// Saves the FPU state to a region of memory using XSAVE.
|
||||
// Pointer must be 16-byte aligned.
|
||||
static inline void asm_xsave(void* mem) {
|
||||
asm volatile("xsave %0" : "+m"(*(uint8_t*)mem)::"memory");
|
||||
}
|
||||
|
||||
// Restores the FPU state from a region of memory using XRSTOR.
|
||||
// Pointer must be 16-byte aligned.
|
||||
static inline void asm_xrstor(void* mem) {
|
||||
asm volatile("xrstor %0" ::"m"(*(uint8_t*)mem) : "memory");
|
||||
}
|
||||
|
||||
static inline uint8_t asm_inb(uint16_t port) {
|
||||
uint8_t result;
|
||||
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline uint16_t asm_inw(uint16_t port) {
|
||||
uint16_t result;
|
||||
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline uint32_t asm_inl(uint16_t port) {
|
||||
uint32_t result;
|
||||
asm volatile("in %0, %1" : "=a"(result) : "Nd"(port));
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void asm_outb(uint16_t port, uint8_t value) {
|
||||
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
|
||||
}
|
||||
|
||||
static inline void asm_outw(uint16_t port, uint16_t value) {
|
||||
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
|
||||
}
|
||||
|
||||
static inline void asm_outl(uint16_t port, uint32_t value) {
|
||||
asm volatile("out %0, %1" : : "Nd"(port), "a"(value));
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#define ELF_ARCH_CLASS ELFCLASS64
|
||||
#define ELF_ARCH_DATA ELFDATA2LSB
|
||||
#define ELF_ARCH_MACHINE EM_X86_64
|
||||
|
||||
#define elf_hdr elf64_hdr
|
||||
#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
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <defs.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static inline bool arch_irq_get_state() {
|
||||
uint64_t flags;
|
||||
asm volatile("pushf; pop %0" : "=r"(flags)::"memory");
|
||||
return flags & RFLAGS_IF;
|
||||
}
|
||||
|
||||
static inline void arch_irq_set_state(bool state) {
|
||||
if (state)
|
||||
asm volatile("sti");
|
||||
else
|
||||
asm volatile("cli");
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/types.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static inline size_t arch_mem_page_bits() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
static inline size_t arch_mem_level_bits() {
|
||||
return 9;
|
||||
}
|
||||
|
||||
static inline size_t arch_mem_num_levels() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
static inline virt_t arch_mem_hhdm_addr() {
|
||||
return 0xFFFF'8000'0000'0000;
|
||||
}
|
||||
|
||||
static inline virt_t arch_mem_pfndb_addr() {
|
||||
return 0xFFFF'A000'0000'0000;
|
||||
}
|
||||
|
||||
static inline virt_t arch_mem_mapping_addr() {
|
||||
return 0xFFFF'C000'0000'0000;
|
||||
}
|
||||
|
||||
typedef uint64_t pte_t;
|
||||
enum pte_flags;
|
||||
enum cache_mode;
|
||||
struct page_table;
|
||||
|
||||
void arch_mem_pte_clear(pte_t* pte);
|
||||
pte_t arch_mem_pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache);
|
||||
bool arch_mem_pte_is_present(pte_t* pte);
|
||||
bool arch_mem_pte_is_dir(pte_t* pte);
|
||||
phys_t arch_mem_pte_address(pte_t* pte);
|
||||
void arch_mem_pt_set(struct page_table* pt);
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <gdt.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static inline struct percpu* arch_percpu_get() {
|
||||
struct percpu* result;
|
||||
asm volatile("mov %0, gs:0" : "=r"(result)::"memory");
|
||||
return result;
|
||||
}
|
||||
|
||||
struct arch_percpu {
|
||||
uint32_t lapic_id;
|
||||
struct gdt gdt;
|
||||
struct tss tss;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct arch_task_context {
|
||||
uint64_t rsp;
|
||||
uint8_t* fpu_region;
|
||||
uint16_t ds, es, fs, gs;
|
||||
uint64_t fs_base, gs_base;
|
||||
};
|
||||
|
||||
struct arch_context {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rsi;
|
||||
uint64_t rdi;
|
||||
uint64_t rbp;
|
||||
uint64_t rdx;
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t rax;
|
||||
uint64_t isr; // Pushed onto the stack by the interrupt handler stubs.
|
||||
uint64_t error; // Pushed onto the stack by the CPU if the interrupt has an error code.
|
||||
uint64_t rip; // The rest is pushed onto the stack by the CPU during an interrupt.
|
||||
uint64_t cs;
|
||||
uint64_t rflags;
|
||||
uint64_t rsp;
|
||||
uint64_t ss;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint64_t phys_t;
|
||||
typedef uint64_t virt_t;
|
||||
typedef uint64_t reg_t;
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/compiler.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool arch_usercopy_read(uint8_t* dst, const __user uint8_t* src, size_t len);
|
||||
bool arch_usercopy_write(__user uint8_t* dst, const uint8_t* src, size_t len);
|
||||
bool arch_usercopy_strlen(const __user uint8_t* str, size_t max, size_t* len);
|
||||
@@ -0,0 +1,191 @@
|
||||
#pragma once
|
||||
|
||||
#define CPUID_1C_SSE3 (1 << 0)
|
||||
#define CPUID_1C_PCLMUL (1 << 1)
|
||||
#define CPUID_1C_DTES64 (1 << 2)
|
||||
#define CPUID_1C_MONITOR (1 << 3)
|
||||
#define CPUID_1C_DS_CPL (1 << 4)
|
||||
#define CPUID_1C_VMX (1 << 5)
|
||||
#define CPUID_1C_SMX (1 << 6)
|
||||
#define CPUID_1C_EST (1 << 7)
|
||||
#define CPUID_1C_TM2 (1 << 8)
|
||||
#define CPUID_1C_SSSE3 (1 << 9)
|
||||
#define CPUID_1C_CID (1 << 10)
|
||||
#define CPUID_1C_SDBG (1 << 11)
|
||||
#define CPUID_1C_FMA (1 << 12)
|
||||
#define CPUID_1C_CX16 (1 << 13)
|
||||
#define CPUID_1C_XTPR (1 << 14)
|
||||
#define CPUID_1C_PDCM (1 << 15)
|
||||
#define CPUID_1C_PCID (1 << 17)
|
||||
#define CPUID_1C_DCA (1 << 18)
|
||||
#define CPUID_1C_SSE4_1 (1 << 19)
|
||||
#define CPUID_1C_SSE4_2 (1 << 20)
|
||||
#define CPUID_1C_X2APIC (1 << 21)
|
||||
#define CPUID_1C_MOVBE (1 << 22)
|
||||
#define CPUID_1C_POPCNT (1 << 23)
|
||||
#define CPUID_1C_TSC (1 << 24)
|
||||
#define CPUID_1C_AES (1 << 25)
|
||||
#define CPUID_1C_XSAVE (1 << 26)
|
||||
#define CPUID_1C_OSXSAVE (1 << 27)
|
||||
#define CPUID_1C_AVX (1 << 28)
|
||||
#define CPUID_1C_F16C (1 << 29)
|
||||
#define CPUID_1C_RDRAND (1 << 30)
|
||||
#define CPUID_1C_HYPERVISOR (1 << 31)
|
||||
|
||||
#define CPUID_1D_FPU (1 << 0)
|
||||
#define CPUID_1D_VME (1 << 1)
|
||||
#define CPUID_1D_DE (1 << 2)
|
||||
#define CPUID_1D_PSE (1 << 3)
|
||||
#define CPUID_1D_TSC (1 << 4)
|
||||
#define CPUID_1D_MSR (1 << 5)
|
||||
#define CPUID_1D_PAE (1 << 6)
|
||||
#define CPUID_1D_MCE (1 << 7)
|
||||
#define CPUID_1D_CX8 (1 << 8)
|
||||
#define CPUID_1D_APIC (1 << 9)
|
||||
#define CPUID_1D_SEP (1 << 11)
|
||||
#define CPUID_1D_MTRR (1 << 12)
|
||||
#define CPUID_1D_PGE (1 << 13)
|
||||
#define CPUID_1D_MCA (1 << 14)
|
||||
#define CPUID_1D_CMOV (1 << 15)
|
||||
#define CPUID_1D_PAT (1 << 16)
|
||||
#define CPUID_1D_PSE36 (1 << 17)
|
||||
#define CPUID_1D_PSN (1 << 18)
|
||||
#define CPUID_1D_CLFLUSH (1 << 19)
|
||||
#define CPUID_1D_DS (1 << 21)
|
||||
#define CPUID_1D_ACPI (1 << 22)
|
||||
#define CPUID_1D_MMX (1 << 23)
|
||||
#define CPUID_1D_FXSR (1 << 24)
|
||||
#define CPUID_1D_SSE (1 << 25)
|
||||
#define CPUID_1D_SSE2 (1 << 26)
|
||||
#define CPUID_1D_SS (1 << 27)
|
||||
#define CPUID_1D_HTT (1 << 28)
|
||||
#define CPUID_1D_TM (1 << 29)
|
||||
#define CPUID_1D_IA64 (1 << 30)
|
||||
#define CPUID_1D_PBE (1 << 31)
|
||||
|
||||
#define CPUID_7B_FSGSBASE (1 << 0)
|
||||
#define CPUID_7B_SGX (1 << 2)
|
||||
#define CPUID_7B_BMI (1 << 3)
|
||||
#define CPUID_7B_HLE (1 << 4)
|
||||
#define CPUID_7B_AVX2 (1 << 5)
|
||||
#define CPUID_7B_SMEP (1 << 7)
|
||||
#define CPUID_7B_BMI2 (1 << 8)
|
||||
#define CPUID_7B_RTM (1 << 11)
|
||||
#define CPUID_7B_AVX512F (1 << 16)
|
||||
#define CPUID_7B_AVX512DQ (1 << 17)
|
||||
#define CPUID_7B_RDSEED (1 << 18)
|
||||
#define CPUID_7B_ADX (1 << 19)
|
||||
#define CPUID_7B_SMAP (1 << 20)
|
||||
#define CPUID_7B_AVX512IFMA (1 << 21)
|
||||
#define CPUID_7B_CLFLUSHOPT (1 << 23)
|
||||
#define CPUID_7B_CLWB (1 << 24)
|
||||
#define CPUID_7B_AVX512CD (1 << 28)
|
||||
#define CPUID_7B_SHA (1 << 29)
|
||||
#define CPUID_7B_AVX512BW (1 << 30)
|
||||
#define CPUID_7B_AVX512VL (1 << 31)
|
||||
|
||||
#define CPUID_7C_AVX512VBMI (1 << 1)
|
||||
#define CPUID_7C_UMIP (1 << 2)
|
||||
#define CPUID_7C_PKU (1 << 3)
|
||||
#define CPUID_7C_OSPKE (1 << 4)
|
||||
#define CPUID_7C_WAITPKG (1 << 5)
|
||||
#define CPUID_7C_AVX512VBMI2 (1 << 6)
|
||||
#define CPUID_7C_SHSTK (1 << 7)
|
||||
#define CPUID_7C_GFNI (1 << 8)
|
||||
#define CPUID_7C_VAES (1 << 9)
|
||||
#define CPUID_7C_VPCLMULQDQ (1 << 10)
|
||||
#define CPUID_7C_AVX512VNNI (1 << 11)
|
||||
#define CPUID_7C_AVX512BITALG (1 << 12)
|
||||
#define CPUID_7C_TME_EN (1 << 13)
|
||||
#define CPUID_7C_AVX512VPOPCNTDQ (1 << 14)
|
||||
#define CPUID_7C_RDPID (1 << 22)
|
||||
#define CPUID_7C_KL (1 << 23)
|
||||
#define CPUID_7C_CLDEMOTE (1 << 25)
|
||||
#define CPUID_7C_MOVDIRI (1 << 27)
|
||||
#define CPUID_7C_MOVDIR64B (1 << 28)
|
||||
#define CPUID_7C_ENQCMD (1 << 29)
|
||||
|
||||
#define CPUID_7D_UINTR (1 << 5)
|
||||
#define CPUID_7D_AVX512VP2INTERSECT (1 << 8)
|
||||
#define CPUID_7D_SERIALIZE (1 << 14)
|
||||
#define CPUID_7D_TSXLDTRK (1 << 16)
|
||||
#define CPUID_7D_PCONFIG (1 << 18)
|
||||
#define CPUID_7D_IBT (1 << 20)
|
||||
#define CPUID_7D_AMX_BF16 (1 << 22)
|
||||
#define CPUID_7D_AVX512FP16 (1 << 23)
|
||||
#define CPUID_7D_AMX_TILE (1 << 24)
|
||||
#define CPUID_7D_AMX_INT8 (1 << 25)
|
||||
|
||||
#define RFLAGS_CF (1u << 0)
|
||||
#define RFLAGS_PF (1u << 2)
|
||||
#define RFLAGS_AF (1u << 4)
|
||||
#define RFLAGS_ZF (1u << 6)
|
||||
#define RFLAGS_SF (1u << 7)
|
||||
#define RFLAGS_TF (1u << 8)
|
||||
#define RFLAGS_IF (1u << 9)
|
||||
#define RFLAGS_DF (1u << 10)
|
||||
#define RFLAGS_OF (1u << 11)
|
||||
#define RFLAGS_IOPL (3u << 12)
|
||||
#define RFLAGS_NT (1u << 14)
|
||||
#define RFLAGS_RF (1u << 16)
|
||||
#define RFLAGS_VM (1u << 17)
|
||||
#define RFLAGS_AC (1u << 18)
|
||||
#define RFLAGS_VIF (1u << 19)
|
||||
#define RFLAGS_VIP (1u << 20)
|
||||
#define RFLAGS_ID (1u << 21)
|
||||
|
||||
#define CR0_PE (1 << 0)
|
||||
#define CR0_MP (1 << 1)
|
||||
#define CR0_EM (1 << 2)
|
||||
#define CR0_TS (1 << 3)
|
||||
#define CR0_ET (1 << 4)
|
||||
#define CR0_NE (1 << 5)
|
||||
#define CR0_WP (1 << 16)
|
||||
#define CR0_AM (1 << 18)
|
||||
#define CR0_NW (1 << 29)
|
||||
#define CR0_CD (1 << 30)
|
||||
#define CR0_PG (1 << 31)
|
||||
|
||||
#define CR4_VME (1 << 0)
|
||||
#define CR4_PVI (1 << 1)
|
||||
#define CR4_TSD (1 << 2)
|
||||
#define CR4_DE (1 << 3)
|
||||
#define CR4_PSE (1 << 4)
|
||||
#define CR4_PAE (1 << 5)
|
||||
#define CR4_MCE (1 << 6)
|
||||
#define CR4_PGE (1 << 7)
|
||||
#define CR4_PCE (1 << 8)
|
||||
#define CR4_OSFXSR (1 << 9)
|
||||
#define CR4_OSXMMEXCPT (1 << 10)
|
||||
#define CR4_UMIP (1 << 11)
|
||||
#define CR4_VMXE (1 << 13)
|
||||
#define CR4_SMXE (1 << 14)
|
||||
#define CR4_FSGSBASE (1 << 16)
|
||||
#define CR4_PCIDE (1 << 17)
|
||||
#define CR4_OSXSAVE (1 << 18)
|
||||
#define CR4_SMEP (1 << 20)
|
||||
#define CR4_SMAP (1 << 21)
|
||||
#define CR4_PKE (1 << 22)
|
||||
#define CR4_CET (1 << 23)
|
||||
#define CR4_PKS (1 << 24)
|
||||
|
||||
#define MSR_EFER_SCE (1 << 0)
|
||||
#define MSR_EFER_LME (1 << 8)
|
||||
#define MSR_EFER_LMA (1 << 10)
|
||||
#define MSR_EFER_NXE (1 << 11)
|
||||
#define MSR_EFER_SVME (1 << 12)
|
||||
#define MSR_EFER_LMSLE (1 << 13)
|
||||
#define MSR_EFER_FFXSR (1 << 14)
|
||||
#define MSR_EFER_TCE (1 << 15)
|
||||
|
||||
#define MSR_EFER (0xC0000080)
|
||||
#define MSR_STAR (0xC0000081)
|
||||
#define MSR_LSTAR (0xC0000082)
|
||||
#define MSR_CSTAR (0xC0000083)
|
||||
#define MSR_SFMASK (0xC0000084)
|
||||
#define MSR_FS_BASE (0xC0000100)
|
||||
#define MSR_GS_BASE (0xC0000101)
|
||||
#define MSR_KERNEL_GS_BASE (0xC0000102)
|
||||
|
||||
#define CPL_USER (0b11)
|
||||
#define CPL_KERNEL (0b00)
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/compiler.h>
|
||||
#include <stdint.h>
|
||||
#include "tss.h"
|
||||
|
||||
struct [[__packed]] gdt {
|
||||
uint64_t null;
|
||||
uint64_t kernel_code32;
|
||||
uint64_t kernel_data32;
|
||||
uint64_t kernel_code64;
|
||||
uint64_t kernel_data64;
|
||||
uint64_t user_code;
|
||||
uint64_t user_data;
|
||||
uint64_t user_code64;
|
||||
uint64_t tss[2];
|
||||
};
|
||||
|
||||
struct [[__packed]] gdtr {
|
||||
uint16_t limit;
|
||||
struct gdt* base;
|
||||
};
|
||||
|
||||
// Initializes a GDT on the local core.
|
||||
void gdt_init();
|
||||
|
||||
// Sets the Task State Segment in the given GDT.
|
||||
void gdt_set_tss(struct gdt* gdt, struct tss* tss);
|
||||
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#define SMP_GDTR_OFFSET 80
|
||||
#define SMP_FARJMP_OFFSET 90
|
||||
#define SMP_TEMP_STACK_OFFSET 98
|
||||
#define SMP_TEMP_CR3_OFFSET 102
|
||||
#define SMP_ENTRY_OFFSET 106
|
||||
#define SMP_HHDM_OFFSET 114
|
||||
#define SMP_KERNEL32_DS 16
|
||||
#define SMP_KERNEL64_CS 24
|
||||
#define SMP_KERNEL64_DS 32
|
||||
#define SMP_INFO_SIZE 127
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <gdt.h>
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct [[__packed]] smp_info {
|
||||
struct gdt gdt;
|
||||
struct gdtr gdtr;
|
||||
uint32_t farjmp_offset;
|
||||
uint32_t farjmp_segment;
|
||||
uint32_t temp_stack;
|
||||
uint32_t temp_cr3;
|
||||
uint64_t entry;
|
||||
uint64_t hhdm_offset;
|
||||
uint32_t lapic_id;
|
||||
uint8_t booted;
|
||||
};
|
||||
|
||||
static_assert(SMP_GDTR_OFFSET == offsetof(struct smp_info, gdtr));
|
||||
static_assert(SMP_FARJMP_OFFSET == offsetof(struct smp_info, farjmp_offset));
|
||||
static_assert(SMP_TEMP_STACK_OFFSET == offsetof(struct smp_info, temp_stack));
|
||||
static_assert(SMP_TEMP_CR3_OFFSET == offsetof(struct smp_info, temp_cr3));
|
||||
static_assert(SMP_ENTRY_OFFSET == offsetof(struct smp_info, entry));
|
||||
static_assert(SMP_HHDM_OFFSET == offsetof(struct smp_info, hhdm_offset));
|
||||
static_assert(SMP_KERNEL32_DS == offsetof(struct gdt, kernel_data32));
|
||||
static_assert(SMP_KERNEL64_CS == offsetof(struct gdt, kernel_code64));
|
||||
static_assert(SMP_KERNEL64_DS == offsetof(struct gdt, kernel_data64));
|
||||
static_assert(SMP_INFO_SIZE == sizeof(struct smp_info));
|
||||
|
||||
// Initializes a LAPIC.
|
||||
void x86_64_smp_init(uint32_t id);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <kernel/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct [[__packed, __aligned(0x10)]] tss {
|
||||
uint32_t reserved0;
|
||||
uint64_t rsp0;
|
||||
uint64_t rsp1;
|
||||
uint64_t rsp2;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
uint64_t ist1;
|
||||
uint64_t ist2;
|
||||
uint64_t ist3;
|
||||
uint64_t ist4;
|
||||
uint64_t ist5;
|
||||
uint64_t ist6;
|
||||
uint64_t ist7;
|
||||
uint32_t reserved3;
|
||||
uint32_t reserved4;
|
||||
uint16_t reserved5;
|
||||
uint16_t iopb;
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/types.h>
|
||||
|
||||
enum : pte_t {
|
||||
ARCH_FLAG_PRESENT = 1 << 0,
|
||||
ARCH_FLAG_READ_WRITE = 1 << 1,
|
||||
ARCH_FLAG_USER_MODE = 1 << 2,
|
||||
ARCH_FLAG_WRITE_THROUGH = 1 << 3,
|
||||
ARCH_FLAG_CACHE_DISABLE = 1 << 4,
|
||||
ARCH_FLAG_ACCESSED = 1 << 5,
|
||||
ARCH_FLAG_DIRTY = 1 << 6,
|
||||
ARCH_FLAG_SIZE = 1 << 7,
|
||||
ARCH_FLAG_GLOBAL = 1 << 8,
|
||||
ARCH_FLAG_AVAILABLE = 1 << 9,
|
||||
ARCH_FLAG_ATTRIBUTE_TABLE = 1 << 10,
|
||||
ARCH_FLAG_EXECUTE_DISABLE = 1LU << 63,
|
||||
|
||||
ARCH_PTE_ADDR_MASK = 0x000F'FFFF'FFFF'F000,
|
||||
};
|
||||
|
||||
void arch_mem_pte_clear(pte_t* pte) {
|
||||
*pte = 0;
|
||||
}
|
||||
|
||||
pte_t arch_mem_pte_build(phys_t addr, enum pte_flags flags, enum cache_mode cache) {
|
||||
pte_t result = ((pte_t)addr & ARCH_PTE_ADDR_MASK) | ARCH_FLAG_PRESENT;
|
||||
|
||||
if (flags & PTE_USER)
|
||||
result |= ARCH_FLAG_USER_MODE;
|
||||
|
||||
if (flags & PTE_DIR)
|
||||
result |= ARCH_FLAG_READ_WRITE;
|
||||
else {
|
||||
if (flags & PTE_WRITE)
|
||||
result |= ARCH_FLAG_READ_WRITE;
|
||||
if (!(flags & PTE_EXEC))
|
||||
result |= ARCH_FLAG_EXECUTE_DISABLE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool arch_mem_pte_is_present(pte_t* pte) {
|
||||
return *pte & ARCH_FLAG_PRESENT;
|
||||
}
|
||||
|
||||
bool arch_mem_pte_is_dir(pte_t* pte) {
|
||||
return *pte & ARCH_FLAG_SIZE;
|
||||
}
|
||||
|
||||
phys_t arch_mem_pte_address(pte_t* pte) {
|
||||
return (phys_t)(*pte & ARCH_PTE_ADDR_MASK);
|
||||
}
|
||||
|
||||
void arch_mem_pt_set(struct page_table* pt) {
|
||||
asm volatile("mov cr3, %0" ::"r"(pt->root) : "memory");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
kernel_c_args += [
|
||||
'-masm=intel',
|
||||
'-mgeneral-regs-only',
|
||||
'-mno-red-zone',
|
||||
'-mcmodel=kernel',
|
||||
]
|
||||
|
||||
kernel_sources += files(
|
||||
'gdt.c',
|
||||
'idt.c',
|
||||
'mem.c',
|
||||
'serial.c',
|
||||
'smp.c',
|
||||
'sys.c',
|
||||
'trampoline.S',
|
||||
'usercopy.c',
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <asm.h>
|
||||
#include <kernel/console.h>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr uint16_t COM1_BASE = 0x3F8;
|
||||
|
||||
static bool is_ready() {
|
||||
return asm_inb(COM1_BASE + 5) & 0x20;
|
||||
}
|
||||
|
||||
static void serial_write(struct console* con, const char* buf, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
while (!is_ready())
|
||||
asm volatile("pause");
|
||||
|
||||
asm_outb(COM1_BASE, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool serial_setup(struct console* con) {
|
||||
asm_outb(COM1_BASE + 1, 0x00); // Disable interrupts
|
||||
asm_outb(COM1_BASE + 3, 0x80); // Enable DLAB (set baud rate divisor)
|
||||
asm_outb(COM1_BASE, 0x01); // Set divisor low byte (115200 baud if 1)
|
||||
asm_outb(COM1_BASE + 1, 0x00); // Set divisor high byte
|
||||
asm_outb(COM1_BASE + 3, 0x03); // 8 bits, no parity, one stop bit (8n1)
|
||||
asm_outb(COM1_BASE + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
|
||||
asm_outb(COM1_BASE + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||
|
||||
asm_outb(COM1_BASE + 4, 0x1E); // Set to loopback mode.
|
||||
asm_outb(COM1_BASE, 0xAE); // Send a test byte.
|
||||
|
||||
// If we don't get the same value back, this serial port doesn't work.
|
||||
if (asm_inb(COM1_BASE) != 0xAE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
asm_outb(COM1_BASE + 4, 0x0F); // Disable loopback mode.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct console serial_con = {
|
||||
.name = "com1",
|
||||
.write = serial_write,
|
||||
.init = serial_setup,
|
||||
};
|
||||
DEFINE_CONSOLE(&serial_con);
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/types.h>
|
||||
#include <smp.h>
|
||||
|
||||
extern uint8_t smp_trampoline_start[];
|
||||
extern uint8_t smp_trampoline_end[];
|
||||
|
||||
[[noreturn]]
|
||||
static void ap_entry(phys_t info) {
|
||||
// TODO
|
||||
|
||||
while (1) {
|
||||
asm volatile("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
void x86_64_smp_init(uint32_t id) {}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <zinnia/archctl.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/percpu.h>
|
||||
#include <gdt.h>
|
||||
#include "asm.h"
|
||||
#include "defs.h"
|
||||
|
||||
[[__init, __naked]]
|
||||
void _start() {
|
||||
asm volatile(
|
||||
"lea rsp, [rip + %0]\n"
|
||||
"jmp %1"
|
||||
:
|
||||
: "i"(__ld_stack_top), "r"(kernel_entry)
|
||||
);
|
||||
}
|
||||
|
||||
void percpu_bsp_early_init() {
|
||||
asm_wrmsr(MSR_GS_BASE, (uint64_t)&percpu_bsp);
|
||||
asm_wrmsr(MSR_FS_BASE, 0);
|
||||
asm_wrmsr(MSR_KERNEL_GS_BASE, 0);
|
||||
|
||||
gdt_init();
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void arch_panic() {
|
||||
asm volatile("cli; hlt");
|
||||
__unreachable();
|
||||
}
|
||||
|
||||
zn_status_t arch_archctl(zn_archctl_t op, uintptr_t arg) {
|
||||
switch (op) {
|
||||
case ZN_ARCHCTL_SET_FSBASE:
|
||||
asm_wrmsr(MSR_FS_BASE, arg);
|
||||
return 0;
|
||||
default:
|
||||
return ZN_ERR_BAD_ARG;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#include <smp.h>
|
||||
#include <defs.h>
|
||||
|
||||
.intel_syntax noprefix
|
||||
|
||||
.global smp_trampoline_start
|
||||
.global smp_trampoline_end
|
||||
|
||||
.section .rodata
|
||||
.code16
|
||||
|
||||
smp_trampoline_start:
|
||||
cli
|
||||
cld
|
||||
jmp 1f
|
||||
|
||||
smp_trampoline_data:
|
||||
.skip SMP_INFO_SIZE
|
||||
|
||||
.set data_offset, (smp_trampoline_data - smp_trampoline_start)
|
||||
.set gdtr_offset, (data_offset + SMP_GDTR_OFFSET)
|
||||
.set farjmp_offset, (data_offset + SMP_FARJMP_OFFSET)
|
||||
.set temp_stack_offset, (data_offset + SMP_TEMP_STACK_OFFSET)
|
||||
.set temp_cr3_offset, (data_offset + SMP_TEMP_CR3_OFFSET)
|
||||
.set entry_offset, (data_offset + SMP_ENTRY_OFFSET)
|
||||
.set hhdm_offset, (data_offset + SMP_HHDM_OFFSET)
|
||||
|
||||
1:
|
||||
mov bx, cs
|
||||
shl ebx, 4
|
||||
|
||||
.set idtr_offset, (invalid_idtr - smp_trampoline_start)
|
||||
lidtd cs:idtr_offset
|
||||
lgdtd cs:gdtr_offset
|
||||
|
||||
.set mode32_offset, (mode32 - smp_trampoline_start)
|
||||
lea eax, [ebx + mode32_offset]
|
||||
mov dword ptr cs:farjmp_offset, eax
|
||||
|
||||
mov eax, CR0_PE | CR0_ET
|
||||
mov cr0, eax
|
||||
jmp fword ptr cs:farjmp_offset
|
||||
|
||||
.code32
|
||||
mode32:
|
||||
mov ax, SMP_KERNEL32_DS
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
xor eax, eax
|
||||
lldt ax
|
||||
|
||||
mov esp, dword ptr [ebx + temp_stack_offset]
|
||||
|
||||
xor eax, eax
|
||||
mov cr4, eax
|
||||
|
||||
or eax, CR4_PAE
|
||||
mov cr4, eax
|
||||
|
||||
mov ecx, MSR_EFER
|
||||
mov eax, MSR_EFER_LME
|
||||
xor edx, edx
|
||||
wrmsr
|
||||
|
||||
mov eax, dword ptr [ebx + temp_cr3_offset]
|
||||
mov cr3, eax
|
||||
|
||||
mov eax, cr0
|
||||
or eax, CR0_PG
|
||||
mov cr0, eax
|
||||
|
||||
.set mode64_offset, (mode64 - smp_trampoline_start)
|
||||
lea eax, [ebx + mode64_offset]
|
||||
push SMP_KERNEL64_CS
|
||||
push eax
|
||||
retf
|
||||
|
||||
.code64
|
||||
mode64:
|
||||
mov ax, SMP_KERNEL64_DS
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
mov ecx, MSR_EFER
|
||||
rdmsr
|
||||
or eax, MSR_EFER_NXE
|
||||
wrmsr
|
||||
|
||||
xor ebp, ebp
|
||||
|
||||
lea rdi, [rbx + data_offset]
|
||||
mov rax, [rbx + entry_offset]
|
||||
add rsp, [rbx + hhdm_offset]
|
||||
jmp rax
|
||||
|
||||
invalid_idtr:
|
||||
.word 0
|
||||
.quad 0
|
||||
|
||||
smp_trampoline_end:
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/usercopy.h>
|
||||
|
||||
extern void x86_64_read_start();
|
||||
extern void x86_64_read_end();
|
||||
extern void x86_64_read_fault();
|
||||
|
||||
static const struct usercopy_region read_region = {
|
||||
.start_ip = x86_64_read_start,
|
||||
.end_ip = x86_64_read_end,
|
||||
.fault_ip = x86_64_read_fault,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_read(uint8_t* dst, const __user uint8_t* src, size_t len) {
|
||||
asm volatile(
|
||||
// Setup regs for `rep movsb`.
|
||||
"xchg rcx, rdx\n"
|
||||
"mov rax, [rip + %0]\n"
|
||||
"mov [rdx], rax\n"
|
||||
|
||||
".global x86_64_read_start\n"
|
||||
"x86_64_read_start:\n"
|
||||
"rep movsb\n"
|
||||
|
||||
".global x86_64_read_end\n"
|
||||
"x86_64_read_end:\n"
|
||||
"xor rax, rax\n"
|
||||
"mov [rdx], rax\n"
|
||||
"mov rax, 1\n"
|
||||
"ret\n"
|
||||
|
||||
".global x86_64_read_fault\n"
|
||||
"x86_64_read_fault:\n"
|
||||
"xor rax, rax\n"
|
||||
"mov [rdx], rax\n"
|
||||
"ret\n"
|
||||
:
|
||||
: "i"(&read_region)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
extern void x86_64_write_start();
|
||||
extern void x86_64_write_end();
|
||||
extern void x86_64_write_fault();
|
||||
|
||||
static const struct usercopy_region write_region = {
|
||||
.start_ip = x86_64_write_start,
|
||||
.end_ip = x86_64_write_end,
|
||||
.fault_ip = x86_64_write_fault,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_write(__user uint8_t* dst, const uint8_t* src, size_t len) {
|
||||
asm volatile(
|
||||
// Setup regs for `rep movsb`.
|
||||
"xchg rcx, rdx\n"
|
||||
"mov rax, [rip + %0]\n"
|
||||
"mov [rdx], rax\n"
|
||||
|
||||
".global x86_64_write_start\n"
|
||||
"x86_64_write_start:\n"
|
||||
"rep movsb\n"
|
||||
|
||||
".global x86_64_write_end\n"
|
||||
"x86_64_write_end:\n"
|
||||
"xor rax, rax\n"
|
||||
"mov [rdx], rax\n"
|
||||
"mov rax, 1\n"
|
||||
"ret\n"
|
||||
|
||||
".global x86_64_write_fault\n"
|
||||
"x86_64_write_fault:\n"
|
||||
"xor rax, rax\n"
|
||||
"mov [rdx], rax\n"
|
||||
"ret\n"
|
||||
:
|
||||
: "i"(&read_region)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
extern void x86_64_strlen_start();
|
||||
extern void x86_64_strlen_end();
|
||||
extern void x86_64_strlen_fault();
|
||||
|
||||
static const struct usercopy_region strlen_region = {
|
||||
.start_ip = x86_64_strlen_start,
|
||||
.end_ip = x86_64_strlen_end,
|
||||
.fault_ip = x86_64_strlen_fault,
|
||||
};
|
||||
|
||||
[[__naked]]
|
||||
bool arch_usercopy_strlen(const __user uint8_t* str, size_t max, size_t* len) {
|
||||
asm volatile(
|
||||
"mov rax, [rip + %0]\n"
|
||||
"mov [rcx], rax\n"
|
||||
|
||||
".global x86_64_strlen_start\n"
|
||||
"x86_64_strlen_start:\n"
|
||||
"xor r8, r8\n"
|
||||
".Lloop:\n"
|
||||
"cmp byte ptr [rdi + r8], 0\n"
|
||||
"je .Lleave\n"
|
||||
"inc r8\n"
|
||||
"cmp rsi, r8\n"
|
||||
"jne .Lloop\n"
|
||||
".Lleave:\n"
|
||||
"mov [rdx], r8\n"
|
||||
|
||||
".global x86_64_strlen_end\n"
|
||||
"x86_64_strlen_end:\n"
|
||||
"xor rax, rax\n"
|
||||
"mov [rcx], rax\n"
|
||||
"mov rax, 1\n"
|
||||
"ret\n"
|
||||
|
||||
".global x86_64_strlen_fault\n"
|
||||
"x86_64_strlen_fault:\n"
|
||||
"xor rax, rax\n"
|
||||
"mov [rcx], rax\n"
|
||||
"ret\n"
|
||||
:
|
||||
: "i"(&read_region)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <kernel/cmdline.h>
|
||||
#include <kernel/init.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// Defined in linker script.
|
||||
extern struct cmdline_option __ld_cmdline_start[];
|
||||
extern struct cmdline_option __ld_cmdline_end[];
|
||||
|
||||
static char cmdline_buf[CMDLINE_MAX];
|
||||
|
||||
[[__init]]
|
||||
void cmdline_parse(const char* cmdline) {
|
||||
struct cmdline_option* opt = __ld_cmdline_start;
|
||||
const size_t len = strnlen(cmdline, CMDLINE_MAX - 1);
|
||||
size_t idx = 0;
|
||||
memcpy(cmdline_buf, cmdline, len);
|
||||
|
||||
while (1) {
|
||||
char* name = nullptr;
|
||||
char* value = nullptr;
|
||||
|
||||
// Skip all leading spaces.
|
||||
while (idx < len && cmdline_buf[idx] == ' ')
|
||||
idx++;
|
||||
if (idx >= len)
|
||||
break;
|
||||
size_t name_idx = idx;
|
||||
name = cmdline_buf + name_idx;
|
||||
|
||||
// Find the next equal sign or space.
|
||||
while (idx < len && cmdline_buf[idx] != '=' && cmdline_buf[idx] != ' ')
|
||||
idx++;
|
||||
if (idx > len)
|
||||
break;
|
||||
|
||||
// Check if the option has a value (=foo).
|
||||
char seperator = cmdline_buf[idx];
|
||||
cmdline_buf[idx++] = 0;
|
||||
if (seperator == '=') {
|
||||
// Check if we need to escape the value.
|
||||
char check;
|
||||
if (cmdline_buf[idx] == '"') {
|
||||
check = '"';
|
||||
cmdline_buf[idx++] = 0;
|
||||
} else {
|
||||
check = ' ';
|
||||
}
|
||||
|
||||
value = cmdline_buf + idx;
|
||||
|
||||
// Skip the value.
|
||||
while (idx < len && cmdline_buf[idx] != check)
|
||||
idx++;
|
||||
if (idx > len)
|
||||
break;
|
||||
cmdline_buf[idx++] = 0;
|
||||
}
|
||||
|
||||
// Find the corresponding option.
|
||||
while (opt < __ld_cmdline_end) {
|
||||
if (!strcmp(opt->name, name))
|
||||
opt->func(value);
|
||||
opt++;
|
||||
}
|
||||
|
||||
if (idx >= len)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/cmdline.h>
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/print.h>
|
||||
#include "limine.h"
|
||||
|
||||
[[__initdata_sorted("limine.0")]]
|
||||
static volatile uint64_t limine_start[] = LIMINE_REQUESTS_START_MARKER;
|
||||
[[__initdata_sorted("limine.1")]]
|
||||
static volatile uint64_t limine_revision[] = LIMINE_BASE_REVISION(4);
|
||||
[[__initdata_sorted("limine.2")]]
|
||||
static volatile uint64_t limine_end[] = LIMINE_REQUESTS_END_MARKER;
|
||||
|
||||
#define LIMINE_REQUEST(request, tag, rev) \
|
||||
[[__initdata_sorted("limine.1")]] \
|
||||
static volatile struct limine_##request request = { \
|
||||
.id = tag, \
|
||||
.revision = rev, \
|
||||
.response = nullptr, \
|
||||
}
|
||||
|
||||
LIMINE_REQUEST(memmap_request, LIMINE_MEMMAP_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(hhdm_request, LIMINE_HHDM_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(executable_address_request, LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(executable_file_request, LIMINE_EXECUTABLE_FILE_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(framebuffer_request, LIMINE_FRAMEBUFFER_REQUEST_ID, 1);
|
||||
LIMINE_REQUEST(module_request, LIMINE_MODULE_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(dtb_request, LIMINE_DTB_REQUEST_ID, 0);
|
||||
LIMINE_REQUEST(rsdp_request, LIMINE_RSDP_REQUEST_ID, 0);
|
||||
|
||||
[[__initdata]]
|
||||
static struct phys_mem mem[128];
|
||||
[[__initdata]]
|
||||
static struct boot_file files[8];
|
||||
|
||||
[[__init]]
|
||||
void kernel_entry() {
|
||||
kernel_early_init();
|
||||
|
||||
struct boot_info info;
|
||||
|
||||
struct limine_memmap_response* mm_res = memmap_request.response;
|
||||
struct limine_module_response* module_res = module_request.response;
|
||||
struct limine_executable_address_response* exec_res = executable_address_request.response;
|
||||
struct limine_executable_file_response* exec_file_res = executable_file_request.response;
|
||||
|
||||
info.cmdline = exec_file_res->executable_file->string;
|
||||
|
||||
info.num_mem_maps = MIN(ARRAY_SIZE(mem), mm_res->entry_count);
|
||||
for (size_t i = 0; i < info.num_mem_maps; i++) {
|
||||
mem[i].address = mm_res->entries[i]->base;
|
||||
mem[i].length = mm_res->entries[i]->length;
|
||||
|
||||
switch (mm_res->entries[i]->type) {
|
||||
case LIMINE_MEMMAP_USABLE:
|
||||
mem[i].usage = PHYS_USABLE;
|
||||
break;
|
||||
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
||||
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
||||
mem[i].usage = PHYS_RECLAIMABLE;
|
||||
break;
|
||||
default:
|
||||
mem[i].usage = PHYS_RESERVED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (module_res) {
|
||||
info.num_files = MIN(ARRAY_SIZE(files), module_res->module_count);
|
||||
for (size_t i = 0; i < info.num_files; i++) {
|
||||
files[i].data = module_res->modules[i]->address;
|
||||
files[i].length = module_res->modules[i]->size;
|
||||
files[i].path = module_res->modules[i]->path;
|
||||
}
|
||||
}
|
||||
|
||||
info.mem_map = mem;
|
||||
info.phys_base = (phys_t)exec_res->physical_base;
|
||||
info.virt_base = (virt_t)exec_res->virtual_base;
|
||||
info.hhdm_base = (virt_t)hhdm_request.response->offset;
|
||||
|
||||
kernel_main(&info);
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
/* BSD Zero Clause License */
|
||||
|
||||
/* Copyright (C) 2022-2025 Mintsuki and contributors.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIMINE_H
|
||||
#define LIMINE_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Misc */
|
||||
|
||||
#ifdef LIMINE_NO_POINTERS
|
||||
#define LIMINE_PTR(TYPE) uint64_t
|
||||
#else
|
||||
#define LIMINE_PTR(TYPE) TYPE
|
||||
#endif
|
||||
|
||||
#define LIMINE_REQUESTS_START_MARKER {0xf6b8f4b39de7d1ae, 0xfab91a6940fcb9cf, 0x785c6ed015d3e316, 0x181e920a7852b9d9}
|
||||
#define LIMINE_REQUESTS_END_MARKER {0xadc0e0531bb10d03, 0x9572709f31764c62}
|
||||
|
||||
#define LIMINE_BASE_REVISION(N) {0xf9562b2d5c95a6c8, 0x6a7b384944536bdc, (N)}
|
||||
|
||||
#define LIMINE_BASE_REVISION_SUPPORTED(VAR) ((VAR)[2] == 0)
|
||||
|
||||
#define LIMINE_LOADED_BASE_REVISION_VALID(VAR) ((VAR)[1] != 0x6a7b384944536bdc)
|
||||
#define LIMINE_LOADED_BASE_REVISION(VAR) ((VAR)[1])
|
||||
|
||||
#define LIMINE_COMMON_MAGIC 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b
|
||||
|
||||
struct limine_uuid {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint16_t c;
|
||||
uint8_t d[8];
|
||||
};
|
||||
|
||||
#define LIMINE_MEDIA_TYPE_GENERIC 0
|
||||
#define LIMINE_MEDIA_TYPE_OPTICAL 1
|
||||
#define LIMINE_MEDIA_TYPE_TFTP 2
|
||||
|
||||
struct limine_file {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(void*) address;
|
||||
uint64_t size;
|
||||
LIMINE_PTR(char*) path;
|
||||
LIMINE_PTR(char*) string;
|
||||
uint32_t media_type;
|
||||
uint32_t unused;
|
||||
uint32_t tftp_ip;
|
||||
uint32_t tftp_port;
|
||||
uint32_t partition_index;
|
||||
uint32_t mbr_disk_id;
|
||||
struct limine_uuid gpt_disk_uuid;
|
||||
struct limine_uuid gpt_part_uuid;
|
||||
struct limine_uuid part_uuid;
|
||||
};
|
||||
|
||||
/* Boot info */
|
||||
|
||||
#define LIMINE_BOOTLOADER_INFO_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xf55038d8e2a1202f, 0x279426fcf5f59740}
|
||||
|
||||
struct limine_bootloader_info_response {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(char*) name;
|
||||
LIMINE_PTR(char*) version;
|
||||
};
|
||||
|
||||
struct limine_bootloader_info_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_bootloader_info_response*) response;
|
||||
};
|
||||
|
||||
/* Executable command line */
|
||||
|
||||
#define LIMINE_EXECUTABLE_CMDLINE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x4b161536e598651e, 0xb390ad4a2f1f303a}
|
||||
|
||||
struct limine_executable_cmdline_response {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(char*) cmdline;
|
||||
};
|
||||
|
||||
struct limine_executable_cmdline_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_executable_cmdline_response*) response;
|
||||
};
|
||||
|
||||
/* Firmware type */
|
||||
|
||||
#define LIMINE_FIRMWARE_TYPE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x8c2f75d90bef28a8, 0x7045a4688eac00c3}
|
||||
|
||||
#define LIMINE_FIRMWARE_TYPE_X86BIOS 0
|
||||
#define LIMINE_FIRMWARE_TYPE_EFI32 1
|
||||
#define LIMINE_FIRMWARE_TYPE_EFI64 2
|
||||
#define LIMINE_FIRMWARE_TYPE_SBI 3
|
||||
|
||||
struct limine_firmware_type_response {
|
||||
uint64_t revision;
|
||||
uint64_t firmware_type;
|
||||
};
|
||||
|
||||
struct limine_firmware_type_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_firmware_type_response*) response;
|
||||
};
|
||||
|
||||
/* Stack size */
|
||||
|
||||
#define LIMINE_STACK_SIZE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d}
|
||||
|
||||
struct limine_stack_size_response {
|
||||
uint64_t revision;
|
||||
};
|
||||
|
||||
struct limine_stack_size_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_stack_size_response*) response;
|
||||
uint64_t stack_size;
|
||||
};
|
||||
|
||||
/* HHDM */
|
||||
|
||||
#define LIMINE_HHDM_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x48dcf1cb8ad2b852, 0x63984e959a98244b}
|
||||
|
||||
struct limine_hhdm_response {
|
||||
uint64_t revision;
|
||||
uint64_t offset;
|
||||
};
|
||||
|
||||
struct limine_hhdm_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_hhdm_response*) response;
|
||||
};
|
||||
|
||||
/* Framebuffer */
|
||||
|
||||
#define LIMINE_FRAMEBUFFER_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x9d5827dcd881dd75, 0xa3148604f6fab11b}
|
||||
|
||||
#define LIMINE_FRAMEBUFFER_RGB 1
|
||||
|
||||
struct limine_video_mode {
|
||||
uint64_t pitch;
|
||||
uint64_t width;
|
||||
uint64_t height;
|
||||
uint16_t bpp;
|
||||
uint8_t memory_model;
|
||||
uint8_t red_mask_size;
|
||||
uint8_t red_mask_shift;
|
||||
uint8_t green_mask_size;
|
||||
uint8_t green_mask_shift;
|
||||
uint8_t blue_mask_size;
|
||||
uint8_t blue_mask_shift;
|
||||
};
|
||||
|
||||
struct limine_framebuffer {
|
||||
LIMINE_PTR(void*) address;
|
||||
uint64_t width;
|
||||
uint64_t height;
|
||||
uint64_t pitch;
|
||||
uint16_t bpp;
|
||||
uint8_t memory_model;
|
||||
uint8_t red_mask_size;
|
||||
uint8_t red_mask_shift;
|
||||
uint8_t green_mask_size;
|
||||
uint8_t green_mask_shift;
|
||||
uint8_t blue_mask_size;
|
||||
uint8_t blue_mask_shift;
|
||||
uint8_t unused[7];
|
||||
uint64_t edid_size;
|
||||
LIMINE_PTR(void*) edid;
|
||||
/* Response revision 1 */
|
||||
uint64_t mode_count;
|
||||
LIMINE_PTR(struct limine_video_mode**) modes;
|
||||
};
|
||||
|
||||
struct limine_framebuffer_response {
|
||||
uint64_t revision;
|
||||
uint64_t framebuffer_count;
|
||||
LIMINE_PTR(struct limine_framebuffer**) framebuffers;
|
||||
};
|
||||
|
||||
struct limine_framebuffer_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_framebuffer_response*) response;
|
||||
};
|
||||
|
||||
/* Paging mode */
|
||||
|
||||
#define LIMINE_PAGING_MODE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x95c1a0edab0944cb, 0xa4e5cb3842f7488a}
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#define LIMINE_PAGING_MODE_X86_64_4LVL 0
|
||||
#define LIMINE_PAGING_MODE_X86_64_5LVL 1
|
||||
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_X86_64_4LVL
|
||||
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_X86_64_4LVL
|
||||
#elif defined(__aarch64__)
|
||||
#define LIMINE_PAGING_MODE_AARCH64_4LVL 0
|
||||
#define LIMINE_PAGING_MODE_AARCH64_5LVL 1
|
||||
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_AARCH64_4LVL
|
||||
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_AARCH64_4LVL
|
||||
#elif defined(__riscv) && (__riscv_xlen == 64)
|
||||
#define LIMINE_PAGING_MODE_RISCV_SV39 0
|
||||
#define LIMINE_PAGING_MODE_RISCV_SV48 1
|
||||
#define LIMINE_PAGING_MODE_RISCV_SV57 2
|
||||
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_RISCV_SV39
|
||||
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_RISCV_SV48
|
||||
#elif defined(__loongarch__) && (__loongarch_grlen == 64)
|
||||
#define LIMINE_PAGING_MODE_LOONGARCH64_4LVL 0
|
||||
#define LIMINE_PAGING_MODE_MIN LIMINE_PAGING_MODE_LOONGARCH64_4LVL
|
||||
#define LIMINE_PAGING_MODE_DEFAULT LIMINE_PAGING_MODE_LOONGARCH64_4LVL
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
struct limine_paging_mode_response {
|
||||
uint64_t revision;
|
||||
uint64_t mode;
|
||||
};
|
||||
|
||||
struct limine_paging_mode_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_paging_mode_response*) response;
|
||||
uint64_t mode;
|
||||
uint64_t max_mode;
|
||||
uint64_t min_mode;
|
||||
};
|
||||
|
||||
/* MP */
|
||||
|
||||
#define LIMINE_MP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x95a67b819a1b857e, 0xa0b61b723b6a73e0}
|
||||
|
||||
struct limine_mp_info;
|
||||
|
||||
typedef void (*limine_goto_address)(struct limine_mp_info*);
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
|
||||
#define LIMINE_MP_X2APIC (1 << 0)
|
||||
|
||||
struct limine_mp_info {
|
||||
uint32_t processor_id;
|
||||
uint32_t lapic_id;
|
||||
uint64_t reserved;
|
||||
LIMINE_PTR(limine_goto_address) goto_address;
|
||||
uint64_t extra_argument;
|
||||
};
|
||||
|
||||
struct limine_mp_response {
|
||||
uint64_t revision;
|
||||
uint32_t flags;
|
||||
uint32_t bsp_lapic_id;
|
||||
uint64_t cpu_count;
|
||||
LIMINE_PTR(struct limine_mp_info**) cpus;
|
||||
};
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
struct limine_mp_info {
|
||||
uint32_t processor_id;
|
||||
uint32_t reserved1;
|
||||
uint64_t mpidr;
|
||||
uint64_t reserved;
|
||||
LIMINE_PTR(limine_goto_address) goto_address;
|
||||
uint64_t extra_argument;
|
||||
};
|
||||
|
||||
struct limine_mp_response {
|
||||
uint64_t revision;
|
||||
uint64_t flags;
|
||||
uint64_t bsp_mpidr;
|
||||
uint64_t cpu_count;
|
||||
LIMINE_PTR(struct limine_mp_info**) cpus;
|
||||
};
|
||||
|
||||
#elif defined(__riscv) && (__riscv_xlen == 64)
|
||||
|
||||
struct limine_mp_info {
|
||||
uint64_t processor_id;
|
||||
uint64_t hartid;
|
||||
uint64_t reserved;
|
||||
LIMINE_PTR(limine_goto_address) goto_address;
|
||||
uint64_t extra_argument;
|
||||
};
|
||||
|
||||
struct limine_mp_response {
|
||||
uint64_t revision;
|
||||
uint64_t flags;
|
||||
uint64_t bsp_hartid;
|
||||
uint64_t cpu_count;
|
||||
LIMINE_PTR(struct limine_mp_info**) cpus;
|
||||
};
|
||||
|
||||
#elif defined(__loongarch__) && (__loongarch_grlen == 64)
|
||||
|
||||
struct limine_mp_info {
|
||||
uint64_t reserved;
|
||||
};
|
||||
|
||||
struct limine_mp_response {
|
||||
uint64_t cpu_count;
|
||||
LIMINE_PTR(struct limine_mp_info**) cpus;
|
||||
};
|
||||
|
||||
#else
|
||||
#error Unknown architecture
|
||||
#endif
|
||||
|
||||
struct limine_mp_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_mp_response*) response;
|
||||
uint64_t flags;
|
||||
};
|
||||
|
||||
/* Memory map */
|
||||
|
||||
#define LIMINE_MEMMAP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x67cf3d9d378a806f, 0xe304acdfc50c3c62}
|
||||
|
||||
#define LIMINE_MEMMAP_USABLE 0
|
||||
#define LIMINE_MEMMAP_RESERVED 1
|
||||
#define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2
|
||||
#define LIMINE_MEMMAP_ACPI_NVS 3
|
||||
#define LIMINE_MEMMAP_BAD_MEMORY 4
|
||||
#define LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE 5
|
||||
#define LIMINE_MEMMAP_EXECUTABLE_AND_MODULES 6
|
||||
#define LIMINE_MEMMAP_FRAMEBUFFER 7
|
||||
#define LIMINE_MEMMAP_ACPI_TABLES 8
|
||||
|
||||
struct limine_memmap_entry {
|
||||
uint64_t base;
|
||||
uint64_t length;
|
||||
uint64_t type;
|
||||
};
|
||||
|
||||
struct limine_memmap_response {
|
||||
uint64_t revision;
|
||||
uint64_t entry_count;
|
||||
LIMINE_PTR(struct limine_memmap_entry**) entries;
|
||||
};
|
||||
|
||||
struct limine_memmap_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_memmap_response*) response;
|
||||
};
|
||||
|
||||
/* Entry point */
|
||||
|
||||
#define LIMINE_ENTRY_POINT_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a}
|
||||
|
||||
typedef void (*limine_entry_point)(void);
|
||||
|
||||
struct limine_entry_point_response {
|
||||
uint64_t revision;
|
||||
};
|
||||
|
||||
struct limine_entry_point_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_entry_point_response*) response;
|
||||
LIMINE_PTR(limine_entry_point) entry;
|
||||
};
|
||||
|
||||
/* Executable File */
|
||||
|
||||
#define LIMINE_EXECUTABLE_FILE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69}
|
||||
|
||||
struct limine_executable_file_response {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_file*) executable_file;
|
||||
};
|
||||
|
||||
struct limine_executable_file_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_executable_file_response*) response;
|
||||
};
|
||||
|
||||
/* Module */
|
||||
|
||||
#define LIMINE_MODULE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x3e7e279702be32af, 0xca1c4f3bd1280cee}
|
||||
|
||||
#define LIMINE_INTERNAL_MODULE_REQUIRED (1 << 0)
|
||||
#define LIMINE_INTERNAL_MODULE_COMPRESSED (1 << 1)
|
||||
|
||||
struct limine_internal_module {
|
||||
LIMINE_PTR(const char*) path;
|
||||
LIMINE_PTR(const char*) string;
|
||||
uint64_t flags;
|
||||
};
|
||||
|
||||
struct limine_module_response {
|
||||
uint64_t revision;
|
||||
uint64_t module_count;
|
||||
LIMINE_PTR(struct limine_file**) modules;
|
||||
};
|
||||
|
||||
struct limine_module_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_module_response*) response;
|
||||
|
||||
/* Request revision 1 */
|
||||
uint64_t internal_module_count;
|
||||
LIMINE_PTR(struct limine_internal_module**) internal_modules;
|
||||
};
|
||||
|
||||
/* RSDP */
|
||||
|
||||
#define LIMINE_RSDP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xc5e77b6b397e7b43, 0x27637845accdcf3c}
|
||||
|
||||
struct limine_rsdp_response {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(void*) address;
|
||||
};
|
||||
|
||||
struct limine_rsdp_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_rsdp_response*) response;
|
||||
};
|
||||
|
||||
/* SMBIOS */
|
||||
|
||||
#define LIMINE_SMBIOS_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x9e9046f11e095391, 0xaa4a520fefbde5ee}
|
||||
|
||||
struct limine_smbios_response {
|
||||
uint64_t revision;
|
||||
uint64_t entry_32;
|
||||
uint64_t entry_64;
|
||||
};
|
||||
|
||||
struct limine_smbios_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_smbios_response*) response;
|
||||
};
|
||||
|
||||
/* EFI system table */
|
||||
|
||||
#define LIMINE_EFI_SYSTEM_TABLE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc}
|
||||
|
||||
struct limine_efi_system_table_response {
|
||||
uint64_t revision;
|
||||
uint64_t address;
|
||||
};
|
||||
|
||||
struct limine_efi_system_table_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_efi_system_table_response*) response;
|
||||
};
|
||||
|
||||
/* EFI memory map */
|
||||
|
||||
#define LIMINE_EFI_MEMMAP_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x7df62a431d6872d5, 0xa4fcdfb3e57306c8}
|
||||
|
||||
struct limine_efi_memmap_response {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(void*) memmap;
|
||||
uint64_t memmap_size;
|
||||
uint64_t desc_size;
|
||||
uint64_t desc_version;
|
||||
};
|
||||
|
||||
struct limine_efi_memmap_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_efi_memmap_response*) response;
|
||||
};
|
||||
|
||||
/* Date at boot */
|
||||
|
||||
#define LIMINE_DATE_AT_BOOT_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x502746e184c088aa, 0xfbc5ec83e6327893}
|
||||
|
||||
struct limine_date_at_boot_response {
|
||||
uint64_t revision;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
struct limine_date_at_boot_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_date_at_boot_response*) response;
|
||||
};
|
||||
|
||||
/* Executable address */
|
||||
|
||||
#define LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x71ba76863cc55f63, 0xb2644a48c516a487}
|
||||
|
||||
struct limine_executable_address_response {
|
||||
uint64_t revision;
|
||||
uint64_t physical_base;
|
||||
uint64_t virtual_base;
|
||||
};
|
||||
|
||||
struct limine_executable_address_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_executable_address_response*) response;
|
||||
};
|
||||
|
||||
/* Device Tree Blob */
|
||||
|
||||
#define LIMINE_DTB_REQUEST_ID {LIMINE_COMMON_MAGIC, 0xb40ddb48fb54bac7, 0x545081493f81ffb7}
|
||||
|
||||
struct limine_dtb_response {
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(void*) dtb_ptr;
|
||||
};
|
||||
|
||||
struct limine_dtb_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_dtb_response*) response;
|
||||
};
|
||||
|
||||
/* RISC-V Boot Hart ID */
|
||||
|
||||
#define LIMINE_RISCV_BSP_HARTID_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x1369359f025525f9, 0x2ff2a56178391bb6}
|
||||
|
||||
struct limine_riscv_bsp_hartid_response {
|
||||
uint64_t revision;
|
||||
uint64_t bsp_hartid;
|
||||
};
|
||||
|
||||
struct limine_riscv_bsp_hartid_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_riscv_bsp_hartid_response*) response;
|
||||
};
|
||||
|
||||
/* Bootloader Performance */
|
||||
|
||||
#define LIMINE_BOOTLOADER_PERFORMANCE_REQUEST_ID {LIMINE_COMMON_MAGIC, 0x6b50ad9bf36d13ad, 0xdc4c7e88fc759e17}
|
||||
|
||||
struct limine_bootloader_performance_response {
|
||||
uint64_t revision;
|
||||
uint64_t reset_usec;
|
||||
uint64_t init_usec;
|
||||
uint64_t exec_usec;
|
||||
};
|
||||
|
||||
struct limine_bootloader_performance_request {
|
||||
uint64_t id[4];
|
||||
uint64_t revision;
|
||||
LIMINE_PTR(struct limine_bootloader_performance_response*) response;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
kernel_sources += files('entry.c')
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/cmdline.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/console.h>
|
||||
#include <kernel/init.h>
|
||||
#include <kernel/irq.h>
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/percpu.h>
|
||||
#include <kernel/print.h>
|
||||
#include <config.h>
|
||||
|
||||
const char zinnia_banner[] =
|
||||
"zinnia " ZINNIA_VERSION " (" ZINNIA_ARCH ", " ZINNIA_COMPILER_ID ", " ZINNIA_LINKER_ID ")";
|
||||
|
||||
[[__init]]
|
||||
void kernel_early_init() {
|
||||
percpu_bsp_early_init();
|
||||
percpu_get()->online = true;
|
||||
irq_lock();
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main(struct boot_info* info) {
|
||||
cmdline_parse(info->cmdline);
|
||||
console_init();
|
||||
kprintf("%s\n", zinnia_banner); // Say hello!
|
||||
kprintf("Command line: \"%s\"\n", info->cmdline);
|
||||
|
||||
mem_init(info->mem_map, info->num_mem_maps, info->virt_base, info->phys_base, info->hhdm_base);
|
||||
|
||||
sched_init();
|
||||
irq_unlock();
|
||||
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void kernel_main_task() {
|
||||
while (1) {}
|
||||
|
||||
ASSERT(false, "Nothing to do");
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
kernel_sources += files(
|
||||
'cmdline.c',
|
||||
'main.c',
|
||||
)
|
||||
|
||||
subdir('limine')
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <kernel/cmdline.h>
|
||||
#include <kernel/console.h>
|
||||
#include <kernel/print.h>
|
||||
#include <kernel/syscalls.h>
|
||||
#include <string.h>
|
||||
|
||||
extern struct console* const __ld_console_start[];
|
||||
extern struct console* const __ld_console_end[];
|
||||
|
||||
struct console* active = nullptr;
|
||||
|
||||
void console_write(const char* buf, size_t len) {
|
||||
if (__likely(active))
|
||||
active->write(active, buf, len);
|
||||
}
|
||||
|
||||
static void console_setup(const char* value) {
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
// Find a suitable boot console.
|
||||
struct console* const* cur = __ld_console_start;
|
||||
while (cur < __ld_console_end) {
|
||||
if (!strcmp(value, (*cur)->name)) {
|
||||
active = *cur;
|
||||
}
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
|
||||
CMDLINE_OPTION("console", console_setup);
|
||||
|
||||
void console_init() {
|
||||
// If we have no console set and at least one set, use that as a default.
|
||||
if (!active && &__ld_console_start[0] != &__ld_console_end[0])
|
||||
active = __ld_console_start[0];
|
||||
|
||||
if (active) {
|
||||
if (active->init(active) == false)
|
||||
active = nullptr;
|
||||
else
|
||||
kprintf("Logging on console \"%s\"\n", active->name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <kernel/elf.h>
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <kernel/irq.h>
|
||||
#include <kernel/percpu.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
void irq_lock() {
|
||||
irq_set_state(false);
|
||||
atomic_fetch_add_explicit(&percpu_get()->irq.level, 1, memory_order_acq_rel);
|
||||
}
|
||||
|
||||
void irq_unlock() {
|
||||
uint32_t old_level = atomic_fetch_sub_explicit(&percpu_get()->irq.level, 1, memory_order_acq_rel);
|
||||
// If it was 1, the new IRQ level is now 0.
|
||||
if (old_level == 1) {
|
||||
irq_set_state(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
kernel_sources += files(
|
||||
'clock.c',
|
||||
'console.c',
|
||||
'elf.c',
|
||||
'irq.c',
|
||||
'panic.c',
|
||||
'percpu.c',
|
||||
'print.c',
|
||||
'sched.c',
|
||||
'spin.c',
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <kernel/panic.h>
|
||||
|
||||
[[noreturn]]
|
||||
void panic() {
|
||||
// TODO
|
||||
while (1) {}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/percpu.h>
|
||||
|
||||
[[__used, __section(".percpu")]]
|
||||
struct percpu percpu_bsp = {
|
||||
.self = &percpu_bsp,
|
||||
.id = 0,
|
||||
.online = false,
|
||||
};
|
||||
|
||||
struct percpu* percpu_new() {
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
#include <kernel/console.h>
|
||||
#include <kernel/panic.h>
|
||||
#include <kernel/print.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
static void reverse(char* s) {
|
||||
int i, j;
|
||||
char c;
|
||||
|
||||
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
|
||||
c = s[i];
|
||||
s[i] = s[j];
|
||||
s[j] = c;
|
||||
}
|
||||
}
|
||||
|
||||
static int pow(int x, unsigned int y) {
|
||||
if (y == 0)
|
||||
return 1;
|
||||
else if ((y % 2) == 0)
|
||||
return pow(x, y / 2) * pow(x, y / 2);
|
||||
else
|
||||
return x * pow(x, y / 2) * pow(x, y / 2);
|
||||
}
|
||||
|
||||
long atol(const char* str, int base) {
|
||||
size_t len = strlen(str);
|
||||
int64_t result = 0;
|
||||
size_t i = 0;
|
||||
if (str[0] == '-')
|
||||
i++;
|
||||
for (; i < len; i++)
|
||||
result += (str[i] - '0') * pow(base, len - i - 1);
|
||||
if (str[0] == '-')
|
||||
result *= -1;
|
||||
return result;
|
||||
};
|
||||
|
||||
unsigned long atolu(const char* str, int base) {
|
||||
size_t len = strlen(str);
|
||||
uint64_t result = 0;
|
||||
size_t i = 0;
|
||||
for (; i < len; i++)
|
||||
result += (str[i] - '0') * pow(base, len - i - 1);
|
||||
return result;
|
||||
};
|
||||
|
||||
char* ltoa(int64_t value, char* str, int base) {
|
||||
size_t i;
|
||||
int64_t sign;
|
||||
sign = value;
|
||||
if (base == 10 && sign < 0)
|
||||
value = -value;
|
||||
i = 0;
|
||||
do {
|
||||
char c = value % base;
|
||||
if (c > 9)
|
||||
c += 7;
|
||||
str[i++] = c + '0';
|
||||
} while ((value /= base) > 0);
|
||||
if (base == 10 && sign < 0)
|
||||
str[i++] = '-';
|
||||
str[i] = '\0';
|
||||
reverse(str);
|
||||
return str;
|
||||
};
|
||||
|
||||
char* lutoa(uint64_t value, char* str, int base) {
|
||||
size_t i = 0;
|
||||
do {
|
||||
char c = value % base;
|
||||
if (c > 9)
|
||||
c += 7;
|
||||
str[i++] = c + '0';
|
||||
} while ((value /= base) > 0);
|
||||
str[i] = '\0';
|
||||
reverse(str);
|
||||
return str;
|
||||
};
|
||||
|
||||
typedef void (*log_fn_t)(void* ctx, const char* buf, size_t len);
|
||||
|
||||
static void kvprintf(log_fn_t callback, void* ctx, const char* restrict fmt, va_list args) {
|
||||
// Amount of bytes written.
|
||||
int32_t written = 0;
|
||||
|
||||
while (*fmt != '\0') {
|
||||
size_t maxrem = INT32_MAX - written;
|
||||
|
||||
if (fmt[0] != '%' || fmt[1] == '%') {
|
||||
if (fmt[0] == '%')
|
||||
fmt++;
|
||||
size_t amount = 1;
|
||||
while (fmt[amount] && fmt[amount] != '%')
|
||||
amount++;
|
||||
if (maxrem < amount) {
|
||||
return;
|
||||
}
|
||||
callback(ctx, fmt, amount);
|
||||
fmt += amount;
|
||||
written += amount;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* format_begun_at = fmt++;
|
||||
bool force_sign = false;
|
||||
bool blank_sign = false;
|
||||
bool write_prefix = false;
|
||||
bool pad_zero = false;
|
||||
bool has_width = false;
|
||||
size_t width = 0;
|
||||
bool has_precision = false;
|
||||
size_t precision = 0;
|
||||
size_t size = sizeof(uint32_t);
|
||||
char number[32];
|
||||
check_fmt:
|
||||
memset(number, 0, sizeof(number));
|
||||
|
||||
switch (*fmt) {
|
||||
// Flags
|
||||
case '+': {
|
||||
force_sign = true;
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
case ' ': {
|
||||
blank_sign = true;
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
case '#': {
|
||||
write_prefix = true;
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
case '0': {
|
||||
pad_zero = true;
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
// Width
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
has_width = true;
|
||||
char number[10 + 1] = {0};
|
||||
size_t idx = 0;
|
||||
while (*fmt >= '0' && *fmt <= '9') {
|
||||
number[idx++] = *fmt;
|
||||
fmt++;
|
||||
}
|
||||
width = atolu(number, 10);
|
||||
|
||||
goto check_fmt;
|
||||
}
|
||||
// Precision
|
||||
case '.': {
|
||||
has_precision = true;
|
||||
fmt++;
|
||||
if (*fmt == '*') {
|
||||
precision = (char)va_arg(args, int32_t);
|
||||
} else {
|
||||
char number[10 + 1] = {0};
|
||||
size_t idx = 0;
|
||||
while (*fmt >= '0' && *fmt <= '9') {
|
||||
number[idx++] = *fmt;
|
||||
fmt++;
|
||||
}
|
||||
precision = atolu(number, 10);
|
||||
}
|
||||
goto check_fmt;
|
||||
}
|
||||
// Length
|
||||
case 'h': {
|
||||
if (fmt[1] == 'h') {
|
||||
size = sizeof(uint8_t);
|
||||
fmt++;
|
||||
} else
|
||||
size = sizeof(uint16_t);
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
case 'l': {
|
||||
// There is no difference between `long int` and `long long int`.
|
||||
if (fmt[1] == 'l')
|
||||
fmt++;
|
||||
fmt++;
|
||||
size = sizeof(uint64_t);
|
||||
|
||||
goto check_fmt;
|
||||
}
|
||||
case 'z': {
|
||||
size = sizeof(size_t);
|
||||
fmt++;
|
||||
goto check_fmt;
|
||||
}
|
||||
// Character
|
||||
case 'c': {
|
||||
char c = (char)va_arg(args, int32_t);
|
||||
|
||||
callback(ctx, &c, sizeof(c));
|
||||
|
||||
written++;
|
||||
fmt++;
|
||||
break;
|
||||
}
|
||||
// String of characters
|
||||
case 's': {
|
||||
const char* str = va_arg(args, const char*);
|
||||
if (!str)
|
||||
str = "(null)";
|
||||
size_t len = 0;
|
||||
if (has_width)
|
||||
len = strnlen(str, width);
|
||||
else
|
||||
len = strlen(str);
|
||||
|
||||
callback(ctx, str, len);
|
||||
|
||||
written += len;
|
||||
fmt++;
|
||||
break;
|
||||
}
|
||||
// Signed decimal integer
|
||||
case 'i':
|
||||
case 'd': {
|
||||
switch (size) {
|
||||
case sizeof(int8_t):
|
||||
ltoa((int8_t)va_arg(args, int32_t), number, 10);
|
||||
break;
|
||||
case sizeof(int16_t):
|
||||
ltoa((int16_t)va_arg(args, int32_t), number, 10);
|
||||
break;
|
||||
case sizeof(int32_t):
|
||||
ltoa((int32_t)va_arg(args, int32_t), number, 10);
|
||||
break;
|
||||
default:
|
||||
ltoa(va_arg(args, int64_t), number, 10);
|
||||
break;
|
||||
}
|
||||
goto print_num;
|
||||
}
|
||||
// Unsigned decimal integer
|
||||
case 'u': {
|
||||
switch (size) {
|
||||
case sizeof(uint8_t):
|
||||
lutoa((uint8_t)va_arg(args, uint32_t), number, 10);
|
||||
break;
|
||||
case sizeof(uint16_t):
|
||||
lutoa((uint16_t)va_arg(args, uint32_t), number, 10);
|
||||
break;
|
||||
case sizeof(uint32_t):
|
||||
lutoa(va_arg(args, uint32_t), number, 10);
|
||||
break;
|
||||
case sizeof(uint64_t):
|
||||
lutoa(va_arg(args, uint64_t), number, 10);
|
||||
break;
|
||||
}
|
||||
goto print_num;
|
||||
}
|
||||
// Unsigned hexadecimal integer
|
||||
case 'X':
|
||||
case 'x': {
|
||||
switch (size) {
|
||||
case sizeof(uint8_t):
|
||||
lutoa((uint8_t)va_arg(args, uint32_t), number, 16);
|
||||
break;
|
||||
case sizeof(uint16_t):
|
||||
lutoa((uint16_t)va_arg(args, uint32_t), number, 16);
|
||||
break;
|
||||
case sizeof(uint32_t):
|
||||
lutoa(va_arg(args, uint32_t), number, 16);
|
||||
break;
|
||||
case sizeof(uint64_t):
|
||||
lutoa(va_arg(args, uint64_t), number, 16);
|
||||
break;
|
||||
}
|
||||
print_num:
|
||||
size_t len = strlen(number);
|
||||
if (has_precision) {
|
||||
for (size_t i = 0; i < precision - len; i++)
|
||||
callback(ctx, " ", 1);
|
||||
}
|
||||
if (has_width) {
|
||||
char c = pad_zero ? '0' : ' ';
|
||||
|
||||
// We don't have to pad anything.
|
||||
if (len < width) {
|
||||
for (size_t i = 0; i < width - len; i++)
|
||||
callback(ctx, &c, 1);
|
||||
}
|
||||
}
|
||||
if (write_prefix) {
|
||||
callback(ctx, "0x", 2);
|
||||
}
|
||||
if (force_sign) {
|
||||
char c = '+';
|
||||
if (number[0] != '-')
|
||||
callback(ctx, &c, 1);
|
||||
}
|
||||
if (blank_sign) {
|
||||
char c = ' ';
|
||||
if (number[0] != '-')
|
||||
callback(ctx, &c, 1);
|
||||
}
|
||||
callback(ctx, number, len);
|
||||
|
||||
written += len;
|
||||
fmt++;
|
||||
break;
|
||||
}
|
||||
// Pointer address
|
||||
case 'p': {
|
||||
const size_t num = va_arg(args, size_t);
|
||||
const size_t buf_size = sizeof(size_t) * 2 + 1;
|
||||
char str[buf_size];
|
||||
lutoa(num, number, 0x10);
|
||||
const size_t len = strlen(number); // Get the length of the final number.
|
||||
for (size_t i = 0; i < buf_size; i++)
|
||||
str[i] = '0';
|
||||
size_t offset = sizeof(str) - len - 1;
|
||||
|
||||
callback(ctx, str, offset);
|
||||
callback(ctx, number, len);
|
||||
|
||||
fmt++;
|
||||
written += len;
|
||||
break;
|
||||
}
|
||||
// The number of characters written so far is stored in the pointed location.
|
||||
case 'n': {
|
||||
int32_t* const ptr = va_arg(args, int32_t*);
|
||||
*ptr = written;
|
||||
fmt++;
|
||||
break;
|
||||
}
|
||||
// No format, just normal text.
|
||||
default: {
|
||||
fmt = format_begun_at;
|
||||
size_t len = strlen(fmt);
|
||||
|
||||
callback(ctx, fmt, len);
|
||||
|
||||
written += len;
|
||||
fmt += len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void printf_console(void*, const char* msg, size_t len) {
|
||||
console_write(msg, len);
|
||||
}
|
||||
|
||||
void kprintf(const char* message, ...) {
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
kvprintf(printf_console, nullptr, message, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <kernel/print.h>
|
||||
#include <kernel/sched.h>
|
||||
|
||||
void sched_init() {
|
||||
kprintf("Scheduler initialized\n");
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#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)) {
|
||||
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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,105 @@
|
||||
ENTRY(_start)
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD FLAGS(5);
|
||||
rodata PT_LOAD FLAGS(4);
|
||||
data PT_LOAD FLAGS(6);
|
||||
dynamic PT_DYNAMIC FLAGS(6);
|
||||
percpu PT_LOAD FLAGS(6);
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0xffffffff80000000;
|
||||
__ld_kernel_start = .;
|
||||
|
||||
.text ALIGN(CONSTANT(MAXPAGESIZE)) : {
|
||||
__ld_text_start = .;
|
||||
|
||||
KEEP(*(.init.text))
|
||||
*(.text .text.*)
|
||||
*(.plt .plt.*)
|
||||
|
||||
__ld_text_end = .;
|
||||
} :text
|
||||
|
||||
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : {
|
||||
__ld_rodata_start = .;
|
||||
|
||||
__ld_init_ctors_start = .;
|
||||
KEEP(*(.initgraph.ctors))
|
||||
__ld_init_ctors_end = .;
|
||||
|
||||
__ld_cmdline_start = .;
|
||||
KEEP(*(.cmdline))
|
||||
__ld_cmdline_end = .;
|
||||
|
||||
*(.rodata .rodata.*)
|
||||
*(.got .got.*)
|
||||
|
||||
__ld_rodata_end = .;
|
||||
} :rodata
|
||||
|
||||
.data ALIGN(CONSTANT(MAXPAGESIZE)) : {
|
||||
__ld_data_start = .;
|
||||
|
||||
__ld_init_start = .;
|
||||
*(.initgraph.nodes)
|
||||
__ld_init_end = .;
|
||||
|
||||
__ld_console_start = .;
|
||||
KEEP(*(.console))
|
||||
__ld_console_end = .;
|
||||
|
||||
KEEP(*(SORT_BY_NAME(.init.data.*)))
|
||||
KEEP(*(.init.data))
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.dynsym : {
|
||||
__ld_dynsym_start = .;
|
||||
*(.dynsym)
|
||||
__ld_dynsym_end = .;
|
||||
} :data
|
||||
|
||||
.dynstr : {
|
||||
__ld_dynstr_start = .;
|
||||
*(.dynstr)
|
||||
__ld_dynstr_end = .;
|
||||
} :data
|
||||
|
||||
.dynamic : {
|
||||
*(.dynamic)
|
||||
} :data :dynamic
|
||||
|
||||
.bss : {
|
||||
/* Small stack to bootstrap the allocator */
|
||||
__ld_stack_bottom = .;
|
||||
. += 0x2000;
|
||||
__ld_stack_top = .;
|
||||
*(.*bss .*bss.*)
|
||||
*(COMMON)
|
||||
} :data
|
||||
|
||||
/* Per-CPU data block. MUST always come last. */
|
||||
.percpu ALIGN(CONSTANT(MAXPAGESIZE)) : {
|
||||
__ld_percpu_start = .;
|
||||
KEEP(*(.percpu))
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
__ld_percpu_end = .;
|
||||
|
||||
__ld_data_end = .;
|
||||
} :percpu
|
||||
|
||||
__ld_kernel_end = .;
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.comment*)
|
||||
*(.eh_frame*)
|
||||
*(.rel.eh_frame*)
|
||||
*(.gcc_except_table*)
|
||||
*(.note*)
|
||||
*(.interp)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
kernel_sources += files(
|
||||
'string.c',
|
||||
)
|
||||
@@ -0,0 +1,164 @@
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
[[__hot]]
|
||||
size_t strlen(const char* str) {
|
||||
size_t result = 0;
|
||||
while (*str++) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[__hot]]
|
||||
size_t strnlen(const char* str, size_t len) {
|
||||
size_t result = 0;
|
||||
while (result < len && *str++) {
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[__hot]]
|
||||
int memcmp(const void* s1, const void* s2, size_t size) {
|
||||
int diff = 0;
|
||||
char* s1ptr = (char*)s1;
|
||||
char* s2ptr = (char*)s2;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (s1ptr[i] != s2ptr[i])
|
||||
diff++;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
[[__hot]]
|
||||
void* memcpy(void* restrict dest, const void* restrict src, size_t n) {
|
||||
if (__unlikely(n == 0))
|
||||
return dest;
|
||||
|
||||
size_t d = (size_t)dest;
|
||||
size_t s = (size_t)src;
|
||||
|
||||
if (d % sizeof(size_t) != 0 || s % sizeof(size_t) != 0) {
|
||||
while (n && (d % sizeof(size_t) != 0) && (s % sizeof(size_t) != 0)) {
|
||||
*((uint8_t*)d) = *((uint8_t*)s);
|
||||
d++;
|
||||
s++;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
size_t* qword_dest = (size_t*)d;
|
||||
const size_t* qword_src = (const size_t*)s;
|
||||
const size_t word_count = n / sizeof(size_t);
|
||||
|
||||
for (size_t i = 0; i < word_count; i++) {
|
||||
qword_dest[i] = qword_src[i];
|
||||
}
|
||||
|
||||
size_t remaining_bytes = n % sizeof(size_t);
|
||||
d = (size_t)(qword_dest + word_count);
|
||||
s = (size_t)(qword_src + word_count);
|
||||
|
||||
while (remaining_bytes--) {
|
||||
*((uint8_t*)d) = *((uint8_t*)s);
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
[[__hot]]
|
||||
void* memmove(void* dstptr, const void* srcptr, size_t size) {
|
||||
uint8_t* dst = (uint8_t*)dstptr;
|
||||
const uint8_t* src = (const uint8_t*)srcptr;
|
||||
if (dst < src) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
dst[i] = src[i];
|
||||
} else {
|
||||
for (size_t i = size; i != 0; i--)
|
||||
dst[i - 1] = src[i - 1];
|
||||
}
|
||||
return dstptr;
|
||||
}
|
||||
|
||||
[[__hot]]
|
||||
void* memset(void* dest, int value, size_t n) {
|
||||
if (__unlikely(n == 0))
|
||||
return dest;
|
||||
|
||||
size_t d = (size_t)dest;
|
||||
|
||||
if (d % sizeof(size_t) != 0) {
|
||||
while (n && (d % sizeof(size_t) != 0)) {
|
||||
*((uint8_t*)d) = value;
|
||||
d++;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
size_t* qword_dest = (size_t*)d;
|
||||
size_t qword_value = value;
|
||||
qword_value |= (qword_value << 8);
|
||||
qword_value |= (qword_value << 16);
|
||||
qword_value |= (qword_value << 32);
|
||||
|
||||
const size_t word_count = n / sizeof(size_t);
|
||||
|
||||
for (size_t i = 0; i < word_count; i++) {
|
||||
qword_dest[i] = qword_value;
|
||||
}
|
||||
|
||||
size_t remaining_bytes = n % sizeof(size_t);
|
||||
d = (size_t)(qword_dest + word_count);
|
||||
|
||||
while (remaining_bytes--) {
|
||||
*((uint8_t*)d) = value;
|
||||
d++;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
char* strncpy(char* restrict dst, const char* restrict src, size_t len) {
|
||||
size_t src_len = strnlen(src, len) + 1;
|
||||
return memcpy(dst, src, MIN(len, src_len));
|
||||
}
|
||||
|
||||
int strncmp(const char* str1, const char* str2, size_t len) {
|
||||
while (len && *str1 && (*str1 == *str2)) {
|
||||
++str1;
|
||||
++str2;
|
||||
--len;
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return (*(unsigned char*)str1 - *(unsigned char*)str2);
|
||||
}
|
||||
}
|
||||
|
||||
int strcmp(const char* str1, const char* str2) {
|
||||
while (*str1 && (*str1 == *str2)) {
|
||||
++str1;
|
||||
++str2;
|
||||
}
|
||||
return (*(unsigned char*)str1 - *(unsigned char*)str2);
|
||||
}
|
||||
|
||||
char* strchr(const char* s, int c) {
|
||||
char ch = (char)c;
|
||||
while (true) {
|
||||
char cur = *s;
|
||||
if (cur == ch)
|
||||
return (char*)s;
|
||||
if (cur == 0)
|
||||
return nullptr;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/common.h>
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/panic.h>
|
||||
#include <kernel/print.h>
|
||||
#include <string.h>
|
||||
|
||||
extern uint8_t __ld_text_start[];
|
||||
extern uint8_t __ld_text_end[];
|
||||
extern uint8_t __ld_rodata_start[];
|
||||
extern uint8_t __ld_rodata_end[];
|
||||
extern uint8_t __ld_data_start[];
|
||||
extern uint8_t __ld_data_end[];
|
||||
extern uint8_t __ld_kernel_start[];
|
||||
|
||||
struct page_table mem_kernel_table = {0};
|
||||
|
||||
void mem_init(struct phys_mem* map, size_t map_len, virt_t kernel_virt, phys_t kernel_phys, virt_t tmp_hhdm) {
|
||||
// This function creates a kernel page table and initializes all memory managers.
|
||||
|
||||
const size_t pgsz = mem_page_size();
|
||||
|
||||
kprintf("Memory map:\n");
|
||||
for (size_t i = 0; i < map_len; i++) {
|
||||
if (map[i].length == 0)
|
||||
continue;
|
||||
|
||||
const char* label = nullptr;
|
||||
switch (map[i].usage) {
|
||||
case PHYS_RESERVED:
|
||||
label = "Reserved";
|
||||
break;
|
||||
case PHYS_USABLE:
|
||||
label = "Usable";
|
||||
break;
|
||||
case PHYS_RECLAIMABLE:
|
||||
label = "Reclaimable";
|
||||
break;
|
||||
}
|
||||
|
||||
kprintf("[%p - %p] %s\n", (void*)map[i].address, (void*)(map[i].address + map[i].length - 1), label);
|
||||
}
|
||||
|
||||
// Set up the bootstrap allocator.
|
||||
struct phys_mem* largest = map;
|
||||
for (size_t i = 0; i < map_len; i++) {
|
||||
if (map[i].length > largest->length && map[i].usage == PHYS_USABLE)
|
||||
largest = &map[i];
|
||||
}
|
||||
mem_phys_bootstrap(largest);
|
||||
kprintf(
|
||||
"Using region [%p - %p] for bootstrap allocator\n",
|
||||
(void*)largest->address,
|
||||
(void*)(largest->address + largest->length - 1)
|
||||
);
|
||||
|
||||
// Set the HHDM base address to the address given by the loader.
|
||||
// We must not keep any virtual addresses to this region,
|
||||
// since we're likely going to map it at a different base address.
|
||||
mem_hhdm_base = tmp_hhdm;
|
||||
|
||||
ASSERT(mem_pt_new_kernel(&mem_kernel_table, 0) == 0, "Unable to allocate the kernel page table");
|
||||
|
||||
// text
|
||||
kprintf("Mapping text segment at %p\n", __ld_text_start);
|
||||
for (uint8_t* p = __ld_text_start; p <= __ld_text_end; p += pgsz) {
|
||||
zn_status_t status = mem_pt_map(
|
||||
&mem_kernel_table,
|
||||
(virt_t)p,
|
||||
(phys_t)(p - __ld_kernel_start + kernel_phys),
|
||||
PTE_READ | PTE_EXEC,
|
||||
CACHE_NONE
|
||||
);
|
||||
ASSERT(!status, "Failed to map %p with error %i", p, status);
|
||||
}
|
||||
|
||||
// rodata
|
||||
kprintf("Mapping rodata segment at %p\n", __ld_rodata_start);
|
||||
for (uint8_t* p = __ld_rodata_start; p < __ld_rodata_end; p += pgsz) {
|
||||
zn_status_t status = mem_pt_map(
|
||||
&mem_kernel_table,
|
||||
(virt_t)p,
|
||||
(phys_t)(p - __ld_kernel_start + kernel_phys),
|
||||
PTE_READ,
|
||||
CACHE_NONE
|
||||
);
|
||||
ASSERT(!status, "Failed to map %p with error %i", p, status);
|
||||
}
|
||||
|
||||
// data
|
||||
kprintf("Mapping data segment at %p\n", __ld_data_start);
|
||||
for (uint8_t* p = __ld_data_start; p < __ld_data_end; p += pgsz) {
|
||||
zn_status_t status = mem_pt_map(
|
||||
&mem_kernel_table,
|
||||
(virt_t)p,
|
||||
(phys_t)(p - __ld_kernel_start + kernel_phys),
|
||||
PTE_READ | PTE_WRITE,
|
||||
CACHE_NONE
|
||||
);
|
||||
ASSERT(!status, "Failed to map %p with error %i", p, status);
|
||||
}
|
||||
|
||||
// The kernel address space is divided into 3 segments (plus kernel).
|
||||
// On x86_64 for example, they live at:
|
||||
// HHDM FFFF'8000'0000'0000
|
||||
// PFNDB FFFF'A000'0000'0000
|
||||
// Mappings FFFF'C000'0000'0000
|
||||
|
||||
// Map all physical memory to the HHDM address.
|
||||
tmp_hhdm = mem_hhdm_addr();
|
||||
for (size_t i = 0; i < map_len; i++) {
|
||||
for (size_t p = 0; p <= map[i].length; p += pgsz) {
|
||||
virt_t vaddr = (virt_t)(map[i].address + p + tmp_hhdm);
|
||||
phys_t paddr = (phys_t)(map[i].address + p);
|
||||
zn_status_t status = mem_pt_map(&mem_kernel_table, vaddr, paddr, PTE_READ | PTE_WRITE, CACHE_NONE);
|
||||
ASSERT(!status, "Failed to map HHDM page %p to %p with error %i", (void*)vaddr, (void*)paddr, status);
|
||||
}
|
||||
}
|
||||
mem_hhdm_base = tmp_hhdm;
|
||||
|
||||
// Switch to our own page table.
|
||||
mem_pt_set(&mem_kernel_table);
|
||||
|
||||
// We record metadata for every single page of available memory in a large array.
|
||||
// This array is contiguous in virtual memory, but is sparsely populated.
|
||||
// Only those array entries which represent usable memory are mapped.
|
||||
for (size_t i = 0; i < map_len; i++) {
|
||||
if (map[i].length == 0 || map[i].usage != PHYS_USABLE)
|
||||
continue;
|
||||
|
||||
size_t length = ALIGN_UP((map[i].length / pgsz) * sizeof(struct page), pgsz);
|
||||
virt_t vaddr = ALIGN_DOWN(mem_pfndb_addr() + (map[i].address / pgsz * sizeof(struct page)), pgsz);
|
||||
|
||||
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!");
|
||||
mem_pt_map(&mem_kernel_table, vaddr + page, paddr, PTE_READ | PTE_WRITE, CACHE_NONE);
|
||||
}
|
||||
}
|
||||
mem_pfndb = (struct page*)mem_pfndb_addr();
|
||||
|
||||
// We don't need the bootstrap allocator from this point on.
|
||||
// Initialize the real page allocator.
|
||||
mem_phys_init(map, map_len);
|
||||
|
||||
kprintf("Memory initialization complete\n");
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
kernel_sources += files(
|
||||
'init.c',
|
||||
'pmm.c',
|
||||
'pt.c',
|
||||
'slab.c',
|
||||
'vmm.c',
|
||||
)
|
||||
@@ -0,0 +1,155 @@
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/print.h>
|
||||
#include <kernel/spin.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
virt_t mem_hhdm_base = 0;
|
||||
struct page* mem_pfndb = nullptr;
|
||||
|
||||
static zn_status_t (*alloc_fn)(size_t num_pages, enum alloc_flags flags, phys_t* out);
|
||||
static zn_status_t (*free_fn)(phys_t start, size_t num_pages);
|
||||
|
||||
zn_status_t mem_phys_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out) {
|
||||
return alloc_fn(num_pages, flags, out);
|
||||
}
|
||||
|
||||
zn_status_t mem_phys_free(phys_t start, size_t num_pages) {
|
||||
return free_fn(start, num_pages);
|
||||
}
|
||||
|
||||
static struct phys_mem bump_mem = {0};
|
||||
static struct phys_mem* bump_region = nullptr;
|
||||
|
||||
static zn_status_t bump_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out) {
|
||||
const size_t bytes = num_pages * mem_page_size();
|
||||
if (((intptr_t)bump_mem.length - bytes) <= 0)
|
||||
return ZN_ERR_NO_MEMORY;
|
||||
|
||||
*out = bump_mem.address;
|
||||
bump_mem.length -= bytes;
|
||||
bump_mem.address += bytes;
|
||||
|
||||
if (!(flags & ALLOC_NOZERO)) {
|
||||
memset(HHDM_PTR(*out), 0, bytes);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zn_status_t bump_free(phys_t addr, size_t num_pages) {
|
||||
// We don't free bootstrap memory.
|
||||
ASSERT(false, "Attempted to free bootstrap memory!");
|
||||
}
|
||||
|
||||
void mem_phys_bootstrap(struct phys_mem* mem) {
|
||||
alloc_fn = bump_alloc;
|
||||
free_fn = bump_free;
|
||||
|
||||
// Save the region by reference and value.
|
||||
// We can't directly modify the region by reference, because that
|
||||
// interferes with e.g. HHDM mapping logic.
|
||||
bump_mem = *mem;
|
||||
bump_region = mem;
|
||||
}
|
||||
|
||||
static struct page* pmm_head = nullptr;
|
||||
static struct spinlock pmm_lock = {0};
|
||||
static size_t pmm_total_free = 0;
|
||||
|
||||
static zn_status_t freelist_alloc(size_t num_pages, enum alloc_flags flags, phys_t* out) {
|
||||
spin_lock(&pmm_lock);
|
||||
|
||||
const size_t bytes = num_pages * mem_page_size();
|
||||
phys_t limit = UINTPTR_MAX;
|
||||
if (flags & ALLOC_MEM20) {
|
||||
limit = (phys_t)1 << 20;
|
||||
} else if (flags & ALLOC_MEM32) {
|
||||
limit = (phys_t)1 << 32;
|
||||
}
|
||||
|
||||
struct page* it = pmm_head;
|
||||
struct page* prev_it = nullptr;
|
||||
|
||||
while (it) {
|
||||
const phys_t page_addr = (((uintptr_t)it - (uintptr_t)mem_pfndb) / sizeof(struct page)) * mem_page_size();
|
||||
|
||||
// If the region is too high up or doesn't have enough pages left, continue searching.
|
||||
if (page_addr + bytes >= limit || __unlikely(it->freelist.count < num_pages)) {
|
||||
prev_it = it;
|
||||
it = it->freelist.next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the current block has exactly enough pages left, consume it entirely.
|
||||
if (__unlikely(it->freelist.count == num_pages)) {
|
||||
if (prev_it) {
|
||||
prev_it->freelist.next = it->freelist.next;
|
||||
} else {
|
||||
pmm_head = it->freelist.next;
|
||||
}
|
||||
|
||||
it->freelist.next = nullptr;
|
||||
it->freelist.count = 0;
|
||||
|
||||
if (__likely(out))
|
||||
*out = page_addr;
|
||||
}
|
||||
// If not, we can just shrink the block.
|
||||
else {
|
||||
it->freelist.count -= num_pages;
|
||||
|
||||
if (__likely(out))
|
||||
*out = page_addr + (it->freelist.count * mem_page_size());
|
||||
}
|
||||
goto success;
|
||||
}
|
||||
|
||||
spin_unlock(&pmm_lock);
|
||||
return ZN_ERR_NO_MEMORY;
|
||||
|
||||
success:
|
||||
// If the NOZERO flag is *not* specified, zero memory.
|
||||
if (__likely(out) && __likely(!(flags & ALLOC_NOZERO))) {
|
||||
memcpy(HHDM_PTR(*out), 0, bytes);
|
||||
}
|
||||
pmm_total_free -= num_pages;
|
||||
spin_unlock(&pmm_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zn_status_t freelist_free(phys_t addr, size_t num_pages) {
|
||||
spin_lock(&pmm_lock);
|
||||
|
||||
struct page* page = &mem_pfndb[addr / mem_page_size()];
|
||||
page->freelist.count = num_pages;
|
||||
page->freelist.next = pmm_head;
|
||||
pmm_head = page;
|
||||
|
||||
spin_unlock(&pmm_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mem_phys_init(struct phys_mem* map, size_t length) {
|
||||
alloc_fn = freelist_alloc;
|
||||
free_fn = freelist_free;
|
||||
bump_region->address = bump_mem.address;
|
||||
bump_region->length = bump_mem.length;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
// Regions smaller than a page are useless.
|
||||
if (map[i].length < mem_page_size() || map[i].usage != PHYS_USABLE)
|
||||
continue;
|
||||
|
||||
struct page* page = &mem_pfndb[map[i].address / mem_page_size()];
|
||||
page->freelist.count = map[i].length / mem_page_size();
|
||||
page->freelist.next = pmm_head;
|
||||
pmm_head = page;
|
||||
pmm_total_free += page->freelist.count;
|
||||
}
|
||||
|
||||
const size_t free_bytes = (pmm_total_free * mem_page_size());
|
||||
kprintf("Total available memory: %zu MiB\n", free_bytes / 1024 / 1024);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <kernel/assert.h>
|
||||
#include <kernel/mem.h>
|
||||
#include <kernel/print.h>
|
||||
#include <kernel/spin.h>
|
||||
|
||||
zn_status_t mem_pt_new_kernel(struct page_table* pt, enum alloc_flags flags) {
|
||||
flags &= ~ALLOC_NOZERO;
|
||||
|
||||
phys_t addr;
|
||||
zn_status_t status = mem_phys_alloc(1, flags, &addr);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
pt->root = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
zn_status_t mem_pt_new_user(struct page_table* pt, enum alloc_flags flags) {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gets a reference to the PTE at the given virtual address.
|
||||
// If `check_only` is set, only checks if the PTE exists,
|
||||
// and doesn't allocate new levels if they don't already exist.
|
||||
// If it can't allocate a page if it has to, returns `nullptr`.
|
||||
static zn_status_t get_pte(struct page_table* pt, virt_t vaddr, bool is_user, bool check_only, pte_t** ret) {
|
||||
pte_t* current_head = HHDM_PTR(pt->root);
|
||||
size_t index = 0;
|
||||
|
||||
for (int8_t level = mem_num_levels() - 1; level >= 0; level--) {
|
||||
const size_t addr_mask = (1 << mem_level_bits()) - 1;
|
||||
const size_t addr_shift = mem_page_bits() + (mem_level_bits() * level);
|
||||
const enum pte_flags level_flags = PTE_DIR | (is_user ? PTE_USER : 0);
|
||||
|
||||
index = (vaddr >> addr_shift) & addr_mask;
|
||||
|
||||
// The last level is used to access the actual PTE, so break the loop then.
|
||||
// We still need to update the index beforehand, that's why we can't just end early.
|
||||
if (level == 0)
|
||||
break;
|
||||
|
||||
pte_t* pte = ¤t_head[index];
|
||||
|
||||
if (mem_pte_is_present(pte)) {
|
||||
// Get the next level.
|
||||
*pte = mem_pte_build(mem_pte_address(pte), level_flags, CACHE_NONE);
|
||||
current_head = HHDM_PTR(mem_pte_address(pte));
|
||||
} else {
|
||||
// If the current level isn't present, we can skip the rest.
|
||||
if (check_only)
|
||||
return ZN_ERR_NO_MEMORY;
|
||||
|
||||
phys_t addr;
|
||||
zn_status_t alloc_status = mem_phys_alloc(1, 0, &addr);
|
||||
if (alloc_status)
|
||||
return alloc_status;
|
||||
|
||||
*pte = mem_pte_build(addr, level_flags, CACHE_NONE);
|
||||
current_head = HHDM_PTR(addr);
|
||||
}
|
||||
}
|
||||
|
||||
*ret = ¤t_head[index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
zn_status_t mem_pt_map(struct page_table* pt, virt_t vaddr, phys_t paddr, enum pte_flags flags, enum cache_mode cache) {
|
||||
spin_lock(&pt->lock);
|
||||
|
||||
pte_t* pte;
|
||||
zn_status_t status = get_pte(pt, vaddr, flags & PTE_USER, false, &pte);
|
||||
if (status) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*pte = mem_pte_build(paddr, flags, cache);
|
||||
|
||||
fail:
|
||||
spin_unlock(&pt->lock);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
#include <kernel/mem.h>
|
||||
@@ -0,0 +1,45 @@
|
||||
kernel_c_args = [
|
||||
'-ffreestanding',
|
||||
'-fno-stack-protector',
|
||||
'-fno-omit-frame-pointer',
|
||||
'-fno-pie',
|
||||
'-nostdinc',
|
||||
'-D__KERNEL__=1',
|
||||
]
|
||||
|
||||
kernel_lds = meson.current_source_dir() / 'kernel.ld'
|
||||
|
||||
kernel_link_args = [
|
||||
'-ffreestanding',
|
||||
'-nostdlib',
|
||||
'-Wl,-nostdlib',
|
||||
'-static',
|
||||
'-T' + kernel_lds,
|
||||
]
|
||||
|
||||
kernel_includes = include_directories(
|
||||
'arch' / target_machine.cpu_family() / 'include',
|
||||
'include',
|
||||
)
|
||||
|
||||
kernel_sources = []
|
||||
|
||||
subdir('arch' / target_machine.cpu_family())
|
||||
subdir('boot')
|
||||
subdir('klibc')
|
||||
subdir('mem')
|
||||
subdir('core')
|
||||
subdir('syscalls')
|
||||
|
||||
configure_file(configuration: common_cfg, output: 'config.h')
|
||||
|
||||
executable(
|
||||
'zinnia',
|
||||
include_directories: [common_includes, kernel_includes],
|
||||
c_args: kernel_c_args,
|
||||
link_args: kernel_link_args,
|
||||
link_depends: kernel_lds,
|
||||
sources: kernel_sources,
|
||||
install: true,
|
||||
install_dir: get_option('datadir') / 'zinnia',
|
||||
)
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <zinnia/channel.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/compiler.h>
|
||||
|
||||
zn_status_t syscall_channel_create(
|
||||
enum zn_channel_flags flags,
|
||||
__user zn_handle_t* endpoint0,
|
||||
__user zn_handle_t* endpoint1
|
||||
) {
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
zn_status_t syscall_channel_open(
|
||||
zn_handle_t channel,
|
||||
size_t num_handles,
|
||||
size_t num_bytes,
|
||||
__user zn_handle_t** out_handle_buf,
|
||||
__user void** out_data_buf
|
||||
) {
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <zinnia/status.h>
|
||||
#include <zinnia/syscall_numbers.h>
|
||||
#include <kernel/compiler.h>
|
||||
#include <kernel/syscalls.h>
|
||||
#include <kernel/types.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) {
|
||||
switch (num) {
|
||||
case ZN_SYSCALL_PANIC:
|
||||
syscall_panic((zn_status_t)a0);
|
||||
case ZN_SYSCALL_LOG:
|
||||
return syscall_log((__user const char*)a0, a1);
|
||||
case ZN_SYSCALL_ARCHCTL:
|
||||
return syscall_archctl(a0, a1);
|
||||
}
|
||||
|
||||
return ZN_ERR_BAD_SYSCALL;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <kernel/percpu.h>
|
||||
#include <kernel/syscalls.h>
|
||||
|
||||
zn_status_t zn_handle_clone(zn_handle_t handle, zn_rights_t cloned_rights, zn_handle_t* cloned) {
|
||||
struct task* current = percpu_get()->sched.current;
|
||||
current->process;
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
zn_status_t zn_handle_drop(zn_handle_t handle) {
|
||||
struct task* current = percpu_get()->sched.current;
|
||||
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
zn_status_t zn_handle_validate(zn_handle_t handle) {
|
||||
struct task* current = percpu_get()->sched.current;
|
||||
|
||||
return ZN_ERR_UNSUPPORTED;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
kernel_sources += files(
|
||||
'channel.c',
|
||||
'dispatch.c',
|
||||
'handle.c',
|
||||
'log.c',
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
subdir('zinnia')
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <zinnia/channel.h>
|
||||
#include <zinnia/syscall_numbers.h>
|
||||
#include "syscall_stubs.h"
|
||||
|
||||
zn_status_t zn_channel_create(enum zn_channel_flags flags, zn_handle_t* endpoint0, zn_handle_t* endpoint1) {
|
||||
return syscall3(ZN_SYSCALL_CHANNEL_CREATE, (arg_t)flags, (arg_t)endpoint0, (arg_t)endpoint1);
|
||||
}
|
||||
|
||||
zn_status_t zn_channel_open(
|
||||
zn_handle_t channel,
|
||||
size_t num_handles,
|
||||
size_t num_bytes,
|
||||
zn_handle_t** out_handle_buf,
|
||||
void** out_data_buf
|
||||
) {
|
||||
return syscall5(
|
||||
ZN_SYSCALL_CHANNEL_OPEN,
|
||||
(arg_t)channel,
|
||||
(arg_t)num_handles,
|
||||
(arg_t)num_bytes,
|
||||
(arg_t)out_handle_buf,
|
||||
(arg_t)out_data_buf
|
||||
);
|
||||
}
|
||||
|
||||
zn_status_t zn_channel_write(zn_handle_t channel) {
|
||||
return syscall1(ZN_SYSCALL_CHANNEL_WRITE, (arg_t)channel);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <zinnia/handle.h>
|
||||
#include <zinnia/syscall_numbers.h>
|
||||
#include "syscall_stubs.h"
|
||||
|
||||
zn_status_t zn_handle_validate(zn_handle_t handle) {
|
||||
return syscall1(ZN_SYSCALL_HANDLE_VALIDATE, (arg_t)handle);
|
||||
}
|
||||
|
||||
zn_status_t zn_handle_drop(zn_handle_t handle) {
|
||||
return syscall1(ZN_SYSCALL_HANDLE_DROP, (arg_t)handle);
|
||||
}
|
||||
|
||||
zn_status_t zn_handle_clone(zn_handle_t object, zn_rights_t cloned_rights, zn_handle_t* cloned) {
|
||||
return syscall3(ZN_SYSCALL_HANDLE_CLONE, (arg_t)object, (arg_t)cloned_rights, (arg_t)cloned);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <zinnia/log.h>
|
||||
#include <zinnia/status.h>
|
||||
#include <zinnia/syscall_numbers.h>
|
||||
#include <string.h>
|
||||
#include "syscall_stubs.h"
|
||||
|
||||
void zn_log(const char* message) {
|
||||
syscall2(ZN_SYSCALL_LOG, (arg_t)message, (arg_t)strlen(message));
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
libzinnia = both_libraries(
|
||||
'zinnia',
|
||||
'channel.c',
|
||||
'handle.c',
|
||||
'log.c',
|
||||
'status.c',
|
||||
include_directories: common_includes,
|
||||
install: true,
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user