New repo setup

This commit is contained in:
2024-05-28 14:26:46 -05:00
commit 9ee197f57b
97 changed files with 11047 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
*.o
*.elf
*.img
.vscode
*.a
*.bin
testprog
init

87
Makefile Normal file
View File

@@ -0,0 +1,87 @@
CC = aarch64-none-elf-gcc
CXX = aarch64-none-elf-g++
AS = aarch64-none-elf-as
LD = aarch64-none-elf-ld
AR = aarch64-none-elf-ar
OBJCOPY = aarch64-none-elf-objcopy
prefix:=$(HOME)/.cros/root
aarch64_ldscript = src/aarch64/aarch64.ld
aarch64_objs = src/aarch64/boot.o src/aarch64/aarch64.o \
src/aarch64/irq/exceptions.o src/aarch64/irq/irq.o \
src/aarch64/bootstrap.o src/aarch64/sysreg.o src/aarch64/irq/interrupts.o
memory_objs_common = src/memory/addressspace.o src/memory/heap.o src/memory/memorymap.o \
src/memory/mmap.o src/memory/new.o src/memory/pageallocator.o
memory_objs_aarch64 = src/memory/aarch64/mmu.o
fs_objs_common = src/fs/fat32/helpers.o src/fs/fat32/entry_helpers.o src/fs/fat32/entry.o \
src/fs/fat32/fat32.o src/fs/fat32/fs_helpers.o \
src/fs/fat32/disk_interface/disk_interface.o src/fs/fat32/filecontextfat32.o \
src/fs/pipe.o src/fs/filecontext.o
loader_objs_common = src/loader/elf.o
sched_objs_common = src/sched/process.o src/sched/queue.o
sched_objs_aarch64 = src/sched/aarch64/context.o src/sched/aarch64/loadcontext.o
device_objs_common = src/devices/timer.o src/devices/uart.o
util_objs_common = src/util/log.o src/util/string.o src/util/hasrefcount.o
util_objs_aarch64 = src/util/aarch64/hacf.o
objs = src/kernel.o src/irq/interrupts.o src/containers/string.o \
$(memory_objs_common) $(memory_objs_aarch64) $(loader_objs_common) $(fs_objs_common) $(device_objs_common) $(sched_objs_common) $(sched_objs_aarch64) $(util_objs_common) $(util_objs_aarch64)
CRTI_OBJ=src/aarch64/crti.o
CRTBEGIN_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
CRTEND_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
CRTN_OBJ=src/aarch64/crtn.o
kernel_elf = kernel.elf
kernel_binary = kernel8.img
libsyscall = libsyscall.a
libsyscall_obj = src/aarch64/dosyscall.o
testprog_bin = init
testprog_obj = test/entry.o test/main.o
CFLAGS = -Iinclude/ -Isrc/ -ffreestanding -Wall -Wextra -ggdb -O0 -mgeneral-regs-only
CXXFLAGS = -Iinclude/ -Isrc/ -ffreestanding -fpermissive -fno-exceptions -fno-rtti -fno-use-cxa-atexit -Wall -Wextra -ggdb -O0 -mgeneral-regs-only
LDFLAGS = -T $(aarch64_ldscript) -nostdlib
.PHONY: all
all: $(libsyscall) $(testprog_bin) $(kernel_binary)
.PHONY: clean
clean:
rm -f $(CRTI_OBJ) $(CRTN_OBJ) $(objs) $(aarch64_objs) $(kernel_elf) $(kernel_binary) $(libsyscall) $(libsyscall_obj) $(testprog_bin) $(testprog_obj)
.PHONY: install
install:
./../scripts/create_and_mount_img.sh $(prefix)
mkdir -p $(prefix)/include
mkdir -p $(prefix)/boot
mkdir -p $(prefix)/lib
mkdir -p $(prefix)/bin
cp kernel8.img $(prefix)/boot
cp -r include/* $(prefix)/include
cp libsyscall.a $(prefix)/lib
cp $(testprog_bin) $(prefix)/bin
$(kernel_binary): $(kernel_elf)
$(OBJCOPY) $(kernel_elf) -O binary $@
$(kernel_elf): $(CRTI_OBJ) $(CRTBEGIN_OBJ) $(objs) $(aarch64_objs) $(CRTEND_OBJ) $(CRTN_OBJ) $(aarch64_ldscript) $(testprog_bin)
$(CXX) -o $@ $(LDFLAGS) $(CRTI_OBJ) $(CRTBEGIN_OBJ) $(objs) $(aarch64_objs) $(CRTEND_OBJ) $(CRTN_OBJ) -lgcc
$(libsyscall): $(libsyscall_obj)
$(AR) -rcs $@ $^
$(testprog_bin): $(testprog_obj)
$(CC) -o $@ -T test/linker.ld -nostdlib $^ -L. -lgcc -lsyscall
.PHONY: clobber
clobber:
./../scripts/unmount_img.sh $(prefix)

22
README.md Normal file
View File

@@ -0,0 +1,22 @@
# Kernel
## Introduction
## Building
You will need: GCC cross compiler targeting aarch64. See [Arm GNU Toolchain Downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads)
or consult your package manager for help obtaining the correct compiler.
To build the kernel, simply run `make` in this directory. `make` will produce an ELF binary, `kernel.elf`,
as well as a flat binary, `kernel8.img`, for running on the Raspberry Pi.
## Running
This kernel has so far been tested on an emulator, [QEMU](https://www.qemu.org/), as well as the Raspberry Pi 3B.
To run this kernel on QEMU, run:
`qemu-system-aarch64 -M raspi3b -serial stdio -kernel kernel8.img`
To run this kernel on the Raspberry Pi, place `kernel8.img` in the boot partition of your Pi's SD card.
On the Raspberry Pi 3B, you may also need to add the following like to `confix.txt`:
`arm_64bit=1`

222
include/sys/syscall.h Normal file
View File

@@ -0,0 +1,222 @@
#ifndef KERNEL_SYSCALL_H
#define KERNEL_SYSCALL_H
#include "types/syscallid.h"
#include "types/pid.h"
#ifdef __cplusplus
extern "C"
{
#endif
int do_syscall(long id, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4);
/**
* @brief Prints `str` on the kernel log.
* @param str
* @return 0
*/
static inline int printk(const char *str)
{
return do_syscall(SYS_PRINTK, (unsigned long)str, 0, 0, 0);
}
/**
* @brief Map a region in memory to newly allocated page frames
* @param ptr Pointer to the start of the region to map
* @param size Size in bytes of the region to map
* @param flags Access flags for the pages to map
* @return
*/
static inline int mmap(void *ptr, unsigned long size, int flags)
{
return do_syscall(SYS_MMAP, (unsigned long)ptr, size, (unsigned long)flags, 0);
}
/**
* @brief Free the memory in a particular region
* @param ptr Pointer to the start of the region to unmap
* @param size Size in bytes of the region to unmap
* @return
*/
static inline int munmap(void *ptr, unsigned long size)
{
return do_syscall(SYS_MUNMAP, (unsigned long)ptr, size, 0, 0);
}
/**
* @brief Create a new process which shares the current process's address space,
* but has a separate context and is scheduled separately.
*
* @param fn Function pointer to start executing the new process at
* @param stack Stack pointer for the new process
* @param flags
* @return
*/
static inline int clone(int (*fn)(void *), void *stack, void *userdata, int flags)
{
return do_syscall(SYS_CLONE, (unsigned long)fn, (unsigned long)stack, (unsigned long)userdata, (unsigned long)flags);
}
/**
* @brief Completely terminate the current process, freeing all resources that
* belong to it.
*
* @return
*/
static inline int terminate()
{
return do_syscall(SYS_TERMINATE, 0, 0, 0, 0);
}
/**
* @brief Replace the current process's address space with a fresh one, then
* load a new program image from the executable specified by `path`.
*
* @param path Path to the executable to load
* @param argv Program arguments to pass to the new program
* @param envp Environment variables to pass to the new program
* @return
*/
static inline int exec(const char *path, char *const argv[], char *const envp[])
{
return do_syscall(SYS_EXEC, (unsigned long)path, (unsigned long)argv, (unsigned long)envp, 0);
}
/**
* @brief Put current process at the end of the scheduler queue, then switch to
* next process.
*
* @return
*/
static inline int yield()
{
return do_syscall(SYS_YIELD, 0, 0, 0, 0);
}
/**
* @brief Call the specified signal handler on the process with id `pid`.
* @param pid Process to call a signal handler on
* @param signal Signal handler to call
* @return
*/
static inline int sigraise(pid_t pid, int signal)
{
return do_syscall(SYS_SIGRAISE, (unsigned long)pid, (unsigned long)signal, 0, 0);
}
/**
* @brief Return from a signal handler, putting the stack and process context
* back to the state they were in just before the signal was triggered.
* @return
*/
static inline int sigret()
{
return do_syscall(SYS_SIGRET, 0, 0, 0, 0);
}
/**
* @brief Stop scheduling process until a signal occurs.
* @return
*/
static inline int sigwait()
{
return do_syscall(SYS_SIGWAIT, 0, 0, 0, 0);
}
/**
* @brief Sets the handler function to call when a particular signal occurs.
* Kernel will pass the pointer `userdata` to the handler function when it is
* called.
* @param signal Signal to specify handler for
* @param handler Function pointer to signal handler
* @param trampoline Function pointer to trampoline function called when handler returns.
* @param userdata Userdata to pass to handler function (can be NULL)
* @return
*/
static inline int sigaction(int signal, void (*handler)(void *), void (*trampoline)(void), void *userdata)
{
return do_syscall(SYS_SIGACTION, (unsigned long)signal, (unsigned long)handler, (unsigned long)trampoline, (unsigned long)userdata);
}
/**
* @brief Open the file specified by `path`
* @param path Path of the file to open
* @param flags
* @return The file descriptor for the file just opened
*/
static inline int openf(const char *path, int flags)
{
return do_syscall(SYS_OPEN, (unsigned long)path, (unsigned long)flags, 0, 0);
}
/**
* @brief Close a proviously open file
* @param fd File descriptor of the open file to close
* @return
*/
static inline int closef(int fd)
{
return do_syscall(SYS_CLOSE, (unsigned long)fd, 0, 0, 0);
}
/**
* @brief Create a new file at the given path
* @param path Path of the file to create
* @param flags Mode for the new file
* @return
*/
static inline int create(const char *path, int flags)
{
return do_syscall(SYS_CREATE, (unsigned long)path, (unsigned long)flags, 0, 0);
}
/**
* @brief
* @param fd
* @return
*/
static inline int unlink(int fd)
{
return do_syscall(SYS_UNLINK, (unsigned long)fd, 0, 0, 0);
}
/**
* @brief
* @param fd
* @param buffer
* @param size
* @return
*/
static inline int read(int fd, void *buffer, unsigned long size)
{
return do_syscall(SYS_READ, (unsigned long)fd, (unsigned long)buffer, size, 0);
}
/**
* @brief
* @param fd
* @param buffer
* @param size
* @return
*/
static inline int write(int fd, const void *buffer, unsigned long size)
{
return do_syscall(SYS_WRITE, (unsigned long)fd, (unsigned long)buffer, size, 0);
}
static inline int fddup(int oldfd, int newfd)
{
return do_syscall(SYS_FDDUP, (unsigned long)oldfd, (unsigned long)newfd, 0, 0);
}
static inline int create_pipe(int pipefd[2])
{
return do_syscall(SYS_CREATE_PIPE, (unsigned long)pipefd, 0, 0, 0);
}
#ifdef __cplusplus
}
#endif
#endif

6
include/types/physaddr.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef KERNEL_PHYSADDR_H
#define KERNEL_PHYSADDR_H
typedef unsigned long physaddr_t;
#endif

6
include/types/pid.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef KERNEL_PID_H
#define KERNEL_PID_H
typedef unsigned int pid_t;
#endif

28
include/types/status.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef KERNEL_STATUS_H
#define KERNEL_STATUS_H
#ifdef __cplusplus
extern "C"
{
#endif
enum error_t
{
ENONE = 0,
EUNKNOWN = -1,
ENOSYS = -2,
EEOF = -3,
ENOFILE = -4,
ENOMEM = -5,
EINVAL = -6,
EIO = -7,
EEXISTS = -8,
EPIPE = -9,
EFULL = -10
};
#ifdef __cplusplus
}
#endif
#endif

36
include/types/syscallid.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef KERNEL_SYSCALLID_H
#define KERNEL_SYSCALLID_H
#ifdef __cplusplus
extern "C"
{
#endif
typedef enum syscallid_t
{
SYS_PRINTK = 0,
SYS_MMAP,
SYS_MUNMAP,
SYS_CLONE,
SYS_TERMINATE,
SYS_EXEC,
SYS_YIELD,
SYS_SIGRAISE,
SYS_SIGRET,
SYS_SIGWAIT,
SYS_SIGACTION,
SYS_OPEN,
SYS_CLOSE,
SYS_CREATE,
SYS_UNLINK,
SYS_READ,
SYS_WRITE,
SYS_FDDUP,
SYS_CREATE_PIPE
} syscallid_t;
#ifdef __cplusplus
}
#endif
#endif

79
src/aarch64/aarch64.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include <cstdint>
#include "mmgmt.h"
#include "sysreg.h"
#include "irq/exceptions.h"
#include "util/string.h"
#include "devices/uart.h"
#include "devices/timer.h"
#include "util/log.h"
#include "sched/context.h"
#include "irq/interrupts.h"
#include "loader/elf.h"
#include "util/hacf.h"
#include "sched/process.h"
#include "kernel.h"
#include "fs/fat32/fat32.h"
#include "containers/binary_search_tree.h"
#include "util/charstream.h"
using namespace kernel::memory;
using namespace kernel::devices;
using namespace kernel::sched;
using namespace kernel::interrupt;
using namespace kernel::loader;
using namespace kernel::fs;
UART uart;
SystemTimer timer;
extern "C" void aarch64_boot(uint64_t dtb, uint64_t kernelSize)
{
Interrupts::init();
// Construct UART driver
new (&uart) UART((void *)0xFFFFFF803F201000);
Interrupts::insertHandler(57, &uart);
// Initialize log
logInit(&uart);
kernelLog(LogLevel::INFO, "CROS startup...");
kernelLog(LogLevel::DEBUG, "DTB Location = %016x", dtb);
kernelLog(LogLevel::DEBUG, "Kernel size = %i MiB", kernelSize >> 20);
MemoryMap map;
map.place(MemoryMap::MemoryType::AVAILABLE, 0, 0x20000000);
map.place(MemoryMap::MemoryType::MMIO, 0x3f000000, 0x1000000);
map.place(MemoryMap::MemoryType::UNAVAILABLE, (unsigned long)0, kernelSize);
map.place(MemoryMap::MemoryType::UNAVAILABLE, (unsigned long)&__end - (unsigned long)&__high_mem, PageAllocator::mapSize(map, page_size));
map.place(MemoryMap::MemoryType::UNAVAILABLE, (unsigned long)0x8000000, 1 << 26);
kernel::kernel.initMemory(map, kernelSize);
setPageEntry(2, &__high_mem + 0x100000000, 0, PAGE_RW);
kernel::kernel.initRamFS((void *)(&__high_mem + 0x108000000));
new (&timer) SystemTimer(50);
Interrupts::insertHandler(0, &timer);
Interrupts::insertHandler(1, &timer);
Interrupts::insertHandler(2, &timer);
Interrupts::insertHandler(3, &timer);
char *const argv[] = {"/bin/init", nullptr};
char *const envp[] = {"cwd=/", nullptr};
kernelLog(LogLevel::DEBUG, "Creating first process.");
Process *p = new Process();
kernel::kernel.addProcess(*p);
kernel::kernel.switchTask();
if (kernel::kernel.exec("/bin/init", argv, envp))
{
kernelLog(LogLevel::PANIC, "Failed to load /bin/init");
hacf();
}
kernelLog(LogLevel::INFO, "Bootup complete, loading first process...");
load_context(kernel::kernel.getActiveProcess()->getContext());
while (1)
asm("nop");
}

52
src/aarch64/aarch64.ld Normal file
View File

@@ -0,0 +1,52 @@
ENTRY(_start)
SECTIONS
{
__high_mem = 0xFFFFFF8000000000;
__load_addr = 0x80000;
__begin = __high_mem + __load_addr;
. = __load_addr;
.boot :
{
KEEP(*(.boot.text))
KEEP(*(.boot.data))
src/aarch64/bootstrap.o
}
. = __high_mem + ALIGN(4096);
__text_start = .;
.text : AT(ADDR(.text) - __high_mem)
{
*(.text)
}
. = ALIGN(4096);
__text_end = .;
__rodata_start = .;
.rodata : AT(ADDR(.rodata) - __high_mem)
{
*(.rodata)
}
. = ALIGN(4096);
__rodata_end = .;
__data_start = .;
.data : AT(ADDR(.data) - __high_mem)
{
*(.data)
}
. = ALIGN(4096);
__data_end = .;
__bss_start = .;
.bss : AT(ADDR(.bss) - __high_mem)
{
bss = .;
*(.bss)
}
. = ALIGN(4096);
__bss_end = .;
__bss_size = __bss_end - __bss_start;
__end = .;
}

181
src/aarch64/boot.s Normal file
View File

@@ -0,0 +1,181 @@
.section ".boot.data"
.balign 4096
ttl0:
.skip 4096
ttl1:
.skip 4096
ttl2:
.skip 4096
.section ".boot.text"
// Entry point for the kernel. Registers:
// x0 -> 32 bit pointer to DTB in memory (primary core only) / 0 (secondary cores)
.globl _start
_start:
// Check if we're already in EL1
/*ldr x6, =_el1_prepare
bic x6, x6, #0xFFFF << 48
mrs x5, CurrentEL
cmp x5, #8
blt x6*/
// Disable IRQ routing to EL2, set EL1 execution mode to AArch64
mrs x5, HCR_EL2
orr x5, x5, #0x80000000
bic x5, x5, #0x38
msr HCR_EL2, x5
// Set virtual processor ID
mrs x5, MIDR_EL1
msr VPIDR_EL2, x5
// Enable floating-point
mrs x5, CPACR_EL1
orr x5, x5, #3 << 20
msr CPACR_EL1, x5
// Set stack before our code
ldr x5, =_start
msr SP_EL1, x5
// Point exception link register to _el1_begin
ldr x5, =_el1_begin
msr ELR_EL2, x5
// Modify saved program status register to mask exceptions and be in EL1
mrs x5, SPSR_EL2
bic x5, x5, #0xF
mov x6, #0x3C5
orr x5, x5, x6
msr SPSR_EL2, x5
// Fall into EL1, jump to _el1_begin
eret
_el1_prepare:
ldr x5, =_start
mov sp, x5
_el1_begin:
// Save x0 on stack
stp x0, x1, [sp, #-16]!
ldr x0, =bootstrap_vector_table_el1
msr VBAR_EL1, x0
// Prepare arguments for call to mmio_init
ldr x0, =0x2000000
ldr x1, =ttl0
ldr x2, =ttl1
ldr x3, =ttl2
ldr x4, =mmu_init
// Call mmio_init
blr x4
// Restore x0
ldp x0, x1, [sp], #16
ldr x1, =0x2000000
// Initialize stack in high memory
ldr x5, =__begin
mov sp, x5
stp x0, x1, [sp, #-16]!
ldr x5, =_init
blr x5
ldp x0, x1, [sp], #16
ldr x5, =aarch64_boot
blr x5
halt:
wfe
b halt
.balign 0x800
bootstrap_vector_table_el1:
bootstrap__ex_el1_curr_sp0_sync:
mrs x0, ESR_EL1
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_sp0_irq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_sp0_fiq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_sp0_serror:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_spx_sync:
ldr x0, =_start
mov sp, x0
mrs x0, ESR_EL1
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_spx_irq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_spx_fiq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_curr_spx_serror:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch64_sync:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch64_irq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch64_fiq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch64_serror:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch32_sync:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch32_irq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch32_fiq:
bl handle_ex
b halt
.balign 0x80
bootstrap__ex_el1_lower_aarch32_serror:
bl handle_ex
b halt

324
src/aarch64/bootstrap.cpp Normal file
View File

@@ -0,0 +1,324 @@
#include <cstdint>
#include <cstddef>
#include <cstdarg>
#define BASE 0x3f000000
#define MBOX_READ ((volatile unsigned int *)(BASE + 0xB880 + 0x00))
#define MBOX_STATUS ((volatile unsigned int *)(BASE + 0xB880 + 0x18))
#define MBOX_WRITE ((volatile unsigned int *)(BASE + 0xB880 + 0x20))
#define GPPUD ((volatile unsigned int *)(BASE + 0x200000 + 0x94))
#define GPPUDCLK0 ((volatile unsigned int *)(BASE + 0x200000 + 0x98))
#define UART_DR ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x00))
#define UART_FR ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x18))
#define UART_IBRD ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x24))
#define UART_FBRD ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x28))
#define UART_LCRH ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x2C))
#define UART_CR ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x30))
#define UART_IMSC ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x38))
#define UART_ICR ((volatile unsigned int *)(BASE + 0x200000 + 0x1000 + 0x44))
#define TIME_CLO ((volatile unsigned int *)(BASE + 0x3004))
#define TIME_CHI ((volatile unsigned int *)(BASE + 0x3008))
enum format_flags_t
{
FORMAT_PADDING = '0',
FORMAT_WIDTH = '*',
FORMAT_SIGNED_DECIMAL = 'i',
FORMAT_UNSIGNED_DECIMAL = 'u',
FORMAT_UNSIGNED_OCTAL = 'o',
FORMAT_UNSIGNED_HEX = 'x',
FORMAT_STRING = 's',
FORMAT_CHARACTER = 'c',
FORMAT_COUNT = 'n',
FORMAT_PERCENT = '%'
};
// Loop <delay> times in a way that the compiler won't optimize away
static inline void delay(int32_t count)
{
while (count > 0)
{
count--;
}
}
static char *itoa(unsigned long n, unsigned int base, unsigned int width)
{
if (base < 2 || base > 16)
{
return NULL;
}
static const char *digits = "0123456789abcdef";
static char buffer[65];
char *s = &buffer[64];
*s = 0;
unsigned int count = 0;
do
{
*--s = digits[n % base];
n /= base;
count++;
} while (count < width || n != 0);
return s;
}
// A Mailbox message with set clock rate of PL011 to 3MHz tag
volatile unsigned int __attribute__((aligned(16))) mbox[9] = {
9 * 4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0};
static void uart_init()
{
*UART_CR = 0;
*GPPUD = 0;
delay(150);
*GPPUDCLK0 = (1 << 14) | (1 << 15);
delay(150);
*GPPUDCLK0 = 0;
*UART_ICR = 0x7FF;
unsigned int r = (((unsigned int)(&mbox) & ~0xF) | 8);
while (*MBOX_STATUS & 0x80000000)
{
}
*MBOX_WRITE = r;
while ((*MBOX_STATUS & 0x40000000) || *MBOX_READ != r)
{
}
*UART_IBRD = 1;
*UART_FBRD = 40;
*UART_LCRH = (1 << 4) | (1 << 5) | (1 << 6);
//*UART_IMSC = 0; //(1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10);
*UART_CR = (1 << 0) | (1 << 8) | (1 << 9);
}
static void uart_puts(const char *str)
{
for (const char *s = str; *s != '\0'; s++)
{
while (*UART_FR & (1 << 5))
{
}
*UART_DR = *s;
}
}
static void uart_putc(unsigned int c)
{
while (*UART_FR & (1 << 5))
{
}
*UART_DR = c;
}
static unsigned long get_time()
{
return ((unsigned long)*TIME_CHI << 32UL) + (unsigned long)*TIME_CLO;
}
static int vprintf(const char *format, va_list valist)
{
while (*format)
{
if (*format == '%')
{
size_t width = 0;
bool padding = false;
switch (*++format)
{
case FORMAT_PADDING:
padding = true;
format++;
break;
}
while (*format >= '0' && *format <= '9')
{
width = (width * 10) + *format - '0';
format++;
}
switch (*format)
{
case FORMAT_SIGNED_DECIMAL:
{
int n = va_arg(valist, int);
if (n < 0)
{
uart_puts("-");
n *= -1;
}
uart_puts(itoa((unsigned int)n, 10, width));
break;
}
case FORMAT_UNSIGNED_DECIMAL:
uart_puts(itoa(va_arg(valist, unsigned int), 10, width));
break;
case FORMAT_UNSIGNED_OCTAL:
uart_puts(itoa(va_arg(valist, unsigned int), 8, width));
break;
case FORMAT_UNSIGNED_HEX:
uart_puts(itoa(va_arg(valist, unsigned long), 16, width));
break;
case FORMAT_STRING:
uart_puts(va_arg(valist, const char *));
break;
case FORMAT_CHARACTER:
uart_putc(va_arg(valist, unsigned int));
break;
case FORMAT_PERCENT:
uart_putc('%');
break;
}
}
else
{
uart_putc(*format);
}
format++;
}
return 0;
}
static int printf(const char *format, ...)
{
va_list valist;
va_start(valist, format);
vprintf(format, valist);
va_end(valist);
return 0;
}
static void log(const char *fmt, ...)
{
unsigned long time = get_time();
printf("[bootstrap][%i:%03i:%03i]: ", time / 1000000, (time / 1000) % 1000, time % 1000);
va_list valist;
va_start(valist, fmt);
vprintf(fmt, valist);
uart_puts("\r\n");
va_end(valist);
}
extern "C" void handle_ex(uint64_t syndrome)
{
uint64_t far;
asm volatile("mrs %0, far_el1" : "=r"(far));
log("Fatal error during bootstrap:\r\n\tESR_EL1 = %016x\r\n\tFAR_EL1 = %016x", syndrome, far);
while (true)
{
}
}
extern "C" void mmu_init(uint64_t kernel_size, uint64_t *level0, uint64_t *level1, uint64_t *level2)
{
uart_init();
log("Bootstrap start:\r\n\tkernel_size: %i MiB\r\n\tlevel0: %08x\r\n\tlevel1: %08x\r\n\tlevel2: %08x", kernel_size >> 20, level0, level1, level2);
void *linearAddress = (void *)0;
unsigned long offset = (unsigned long)linearAddress & 0x0000FFFFFFFFFFFF;
int level0Index = offset >> 39;
int level1Index = offset >> 30;
int level2Index = offset >> 21;
/*
* Point level 0 entry at level 1 entry; level 1 entry at level 2 entry,
* level 2 entry at physical address 0. Maps first 2MiB in kernel address
* space to first 2 MiB of physical addresses.
*/
level0[level0Index] = (uint64_t)level1 | 1027;
level1[level1Index] = (uint64_t)level2 | 1027;
for (unsigned long p = 0; p < kernel_size; p += 0x200000)
{
level2[level2Index + (p / 0x200000)] = p | 1025;
}
level2[504] = 0x3f000000 | 1025 | 4;
level2[505] = 0x3f200000 | 1025 | 4;
log("Filled kernel table entries.");
/*
* Point last entry of level 0 table at level 0 table.
* Maps all tage tables into virtual address space, greatly simplifying
* process of manipulating tables.
*/
level1[511] = (uint64_t)level1 | 1027;
log("Filled loopback entry.");
/*
* Point both translation table base registers to level0
* (allows creation of temporary identity map)
*/
asm volatile("msr TTBR0_EL1, %0" ::"r"(level1 + 1));
asm volatile("msr TTBR1_EL1, %0" ::"r"(level1 + 1));
log("Set TTBR registers.");
uint64_t mmfr0;
asm volatile("mrs %0, id_aa64mmfr0_el1" : "=r"(mmfr0));
log("ID_AA64MMFR0 = %016x", mmfr0);
unsigned long mair = (0xFF << 0) | // AttrIdx=0: normal, IWBWA, OWBWA, NTR
(0x04 << 8) | // AttrIdx=1: device, nGnRE (must be OSH too)
(0x44 << 16); // AttrIdx=2: non cacheable
asm volatile("msr MAIR_EL1, %0" : : "r"(mair));
log("Set MAIR_EL1 = %016x", mair);
/*
* Configure the translation control register:
* - 4KiB granules for both translation tables
* - 48-bit intermediate physical address size
* - 16 most significant bits select either TTBR0 or TTBR1
*/
uint64_t tcr;
asm volatile("mrs %0, TCR_EL1" : "=r"(tcr));
tcr = (0b00LL << 37) | // TBI=0, no tagging
((mmfr0 & 0xF) << 32) | // IPS=autodetected
(0b10LL << 30) | // TG1=4k
(0b11LL << 28) | // SH1=3 inner
(0b01LL << 26) | // ORGN1=1 write back
(0b01LL << 24) | // IRGN1=1 write back
(0b0LL << 23) | // EPD1 enable higher half
(25LL << 16) | // T1SZ=25, 3 levels (512G)
(0b00LL << 14) | // TG0=4k
(0b11LL << 12) | // SH0=3 inner
(0b01LL << 10) | // ORGN0=1 write back
(0b01LL << 8) | // IRGN0=1 write back
(0b0LL << 7) | // EPD0 enable lower half
(25LL << 0); // T0SZ=25, 3 levels (512G)
asm volatile("msr TCR_EL1, %0" ::"r"(tcr));
asm volatile("isb");
log("Set TCR_EL1 = %016x", tcr);
/*
* Enable MMU by setting bit 0 of SCTLR
*/
uint64_t sctlr;
asm volatile("dsb ish");
asm volatile("isb");
asm volatile("mrs %0, SCTLR_EL1" : "=r"(sctlr));
sctlr |= 0xC00801;
sctlr &= ~((1 << 25) | // clear EE, little endian translation tables
(1 << 24) | // clear E0E
(1 << 19) | // clear WXN
(1 << 12) | // clear I, no instruction cache
(1 << 4) | // clear SA0
(1 << 3) | // clear SA
(1 << 2) | // clear C, no cache at all
(1 << 1)); // clear A, no aligment check
asm volatile("msr SCTLR_EL1, %0" ::"r"(sctlr));
asm volatile("isb");
log("Set SCTLR_EL1 = %016x", sctlr);
log("Enabled MMU.");
}

19
src/aarch64/crti.c Normal file
View File

@@ -0,0 +1,19 @@
typedef void (*func_ptr)(void);
extern func_ptr _init_array_start[0], _init_array_end[0];
extern func_ptr _fini_array_start[0], _fini_array_end[0];
void _init(void)
{
for (func_ptr *func = _init_array_start; func != _init_array_end; func++)
(*func)();
}
void _fini(void)
{
for (func_ptr *func = _fini_array_start; func != _fini_array_end; func++)
(*func)();
}
func_ptr _init_array_start[0] __attribute__((used, section(".init_array"), aligned(sizeof(func_ptr)))) = {};
func_ptr _fini_array_start[0] __attribute__((used, section(".fini_array"), aligned(sizeof(func_ptr)))) = {};

4
src/aarch64/crtn.c Normal file
View File

@@ -0,0 +1,4 @@
typedef void (*func_ptr)(void);
func_ptr _init_array_end[0] __attribute__((used, section(".init_array"), aligned(sizeof(func_ptr)))) = {};
func_ptr _fini_array_end[0] __attribute__((used, section(".fini_array"), aligned(sizeof(func_ptr)))) = {};

6
src/aarch64/dosyscall.s Normal file
View File

@@ -0,0 +1,6 @@
.section ".text"
.global do_syscall
.type do_syscall, "function"
do_syscall:
svc #0
ret

View File

@@ -0,0 +1,42 @@
#ifndef KERNEL_EXCEPTIONCLASS_H
#define KERNEL_EXCEPTIONCLASS_H
enum class ExceptionClass
{
UNKNOWN = 0x00,
TRAPPED_WF = 0x01,
TRAPPED_MCR = 0x03,
TRAPPED_MCRR = 0x04,
TRAPPED_MCR_2 = 0x05,
TRAPPED_LDC = 0x06,
FP_ACCESS = 0x07,
TRAPPED_LD64B = 0x0A,
TRAPPED_MRRC = 0x0C,
BRANCH_TARGET = 0x0D,
ILLEGAL_EXE_STATE = 0x0E,
SVC_AARCH32 = 0x11,
SVC_AARCH64 = 0x15,
TRAPPED_SYS_INST = 0x18,
SVE_ACCESS = 0x19,
POINTER_AUCH = 0x1C,
INST_ABORT_EL0 = 0x20,
INST_ABORT_EL1 = 0x21,
PC_ALIGNMENT = 0x22,
DATA_ABORT_EL0 = 0x24,
DATA_ABORT_EL1 = 0x25,
SP_ALIGNMENT = 0x26,
MEMORY_OP = 0x27,
TRAPPED_FP_AARCH32 = 0x28,
TRAPPED_FP_AARCH64 = 0x2C,
SERROR = 0x2F,
BRK_EL0 = 0x30,
BRK_EL1 = 0x31,
STEP_EL0 = 0x32,
STEP_EL1 = 0x33,
WATCH_EL0 = 0x34,
WATCH_EL1 = 0x35,
BKPT_AARCH32 = 0x38,
BRK_AARCH64 = 0x3C
};
#endif

View File

@@ -0,0 +1,9 @@
#ifndef KERNEL_EXCEPTIONS_H
#define KERNEL_EXCEPTIONS_H
/**
* @brief Symbol located at the start of the interrupt vector table
*/
extern int vector_table_el1;
#endif

View File

@@ -0,0 +1,244 @@
.section ".text"
.macro save_context
// Make room on stack for process context
sub sp, sp, #816
// Save FP registers
st4 {v0.2d, v1.2d, v2.2d, v3.2d}, [sp], #64
st4 {v4.2d, v5.2d, v6.2d, v7.2d}, [sp], #64
st4 {v8.2d, v9.2d, v10.2d, v11.2d}, [sp], #64
st4 {v12.2d, v13.2d, v14.2d, v15.2d}, [sp], #64
st4 {v16.2d, v17.2d, v18.2d, v19.2d}, [sp], #64
st4 {v20.2d, v21.2d, v22.2d, v23.2d}, [sp], #64
st4 {v24.2d, v25.2d, v26.2d, v27.2d}, [sp], #64
st4 {v28.2d, v29.2d, v30.2d, v31.2d}, [sp], #64
// Save X0-X29
stp x0, x1, [sp], #16
stp x2, x3, [sp], #16
stp x4, x5, [sp], #16
stp x6, x7, [sp], #16
stp x8, x9, [sp], #16
stp x10, x11, [sp], #16
stp x12, x13, [sp], #16
stp x14, x15, [sp], #16
stp x16, x17, [sp], #16
stp x18, x19, [sp], #16
stp x20, x21, [sp], #16
stp x22, x23, [sp], #16
stp x24, x25, [sp], #16
stp x26, x27, [sp], #16
stp x28, x29, [sp], #16
// Save x30 and SP
mrs x8, sp_el0
stp x30, x8, [sp], #16
// Save PC and program status
mrs x8, elr_el1
mrs x9, spsr_el1
stp x8, x9, [sp], #16
// Save FPCR and FPSR
mrs x8, fpcr
mrs x9, fpsr
stp x8, x9, [sp], #16
// Save kernel SP
mov x8, sp
add x8, x8, #16
stp x8, x8, [sp], #16
// Set SP to beginning of context struct
sub sp, sp, #816
.endm
_sync_el0_wrapper:
save_context
// Obtain and unpack exception syndrome
mrs x8, esr_el1
mov x9, x8
ldr x10, = 0x1F03FFFFFF
bic x8, x8, x10
lsr x8, x8, #26
mvn x10, x10
bic x9, x9, x10
// Point X5 to process context struct in case we are doing a syscall
mov x5, sp
// If exception class 0x15, we are doing a syscall
cmp x8, #0x15
bne _not_syscall
bl do_syscall
bl load_context
_not_syscall:
// Else, call handle_sync with exception class and ISR
mov x0, x8
mov x1, x9
mov x2, x5
bl handle_sync
// Load next process context returned from handle_sync
bl load_context
_sync_el1_wrapper:
// Save caller-saved registers
stp x0, x1, [sp, #-16]!
stp x2, x3, [sp, #-16]!
stp x4, x5, [sp, #-16]!
stp x6, x7, [sp, #-16]!
stp x8, x9, [sp, #-16]!
stp x10, x11, [sp, #-16]!
stp x12, x13, [sp, #-16]!
stp x14, x15, [sp, #-16]!
stp x16, x17, [sp, #-16]!
stp x18, lr, [sp, #-16]!
// Unpack exception syndrome register
mrs x0, esr_el1
mov x1, x0
ldr x2, = 0x1F03FFFFFF
bic x0, x0, x2
lsr x0, x0, #26
mvn x2, x2
bic x1, x1, x2
// Give handle_sync() a NULL-pointer as a process context, as we came from kernelspace
mov x2, #0
// Call handle_sync()
bl handle_sync
// Restore saved registers
ldp x18, lr, [sp], #16
ldp x16, x17, [sp], #16
ldp x14, x15, [sp], #16
ldp x12, x13, [sp], #16
ldp x10, x11, [sp], #16
ldp x8, x9, [sp], #16
ldp x6, x7, [sp], #16
ldp x4, x5, [sp], #16
ldp x2, x3, [sp], #16
ldp x0, x1, [sp], #16
// Exception return
eret
_irq_el0_wrapper:
save_context
// Call find_irq_source() to obtain an interrupt number
bl find_irq_source
// Point X1 to saved process context, then call handle_irq()
mov x1, sp
bl handle_irq
// Load context returned by handle_irq()
bl load_context
_irq_el1_wrapper:
// Save caller-saved registers
stp x0, x1, [sp, #-16]!
stp x2, x3, [sp, #-16]!
stp x4, x5, [sp, #-16]!
stp x6, x7, [sp, #-16]!
stp x8, x9, [sp, #-16]!
stp x10, x11, [sp, #-16]!
stp x12, x13, [sp, #-16]!
stp x14, x15, [sp, #-16]!
stp x16, x17, [sp, #-16]!
stp x18, lr, [sp, #-16]!
// Call find_irq_source() to obtain an interrupt number
bl find_irq_source
// Set X1 to NULL because we came from kernelspace, then call handle_irq()
mov x1, #0
bl handle_irq
// Restore saved registers
ldp x18, lr, [sp], #16
ldp x16, x17, [sp], #16
ldp x14, x15, [sp], #16
ldp x12, x13, [sp], #16
ldp x10, x11, [sp], #16
ldp x8, x9, [sp], #16
ldp x6, x7, [sp], #16
ldp x4, x5, [sp], #16
ldp x2, x3, [sp], #16
ldp x0, x1, [sp], #16
// Exception return
eret
.balign 0x800
.global vector_table_el1
vector_table_el1:
_ex_el1_curr_sp0_sync:
b hacf
.balign 0x80
_ex_el1_curr_sp0_irq:
b hacf
.balign 0x80
_ex_el1_curr_sp0_fiq:
b hacf
.balign 0x80
_ex_el1_curr_sp0_serror:
b hacf
.balign 0x80
_ex_el1_curr_spx_sync:
b _sync_el1_wrapper
.balign 0x80
_ex_el1_curr_spx_irq:
b _irq_el1_wrapper
.balign 0x80
_ex_el1_curr_spx_fiq:
b _irq_el1_wrapper
.balign 0x80
_ex_el1_curr_spx_serror:
b hacf
.balign 0x80
_ex_el1_lower_aarch64_sync:
b _sync_el0_wrapper
.balign 0x80
_ex_el1_lower_aarch64_irq:
b _irq_el0_wrapper
.balign 0x80
_ex_el1_lower_aarch64_fiq:
b _irq_el0_wrapper
.balign 0x80
_ex_el1_lower_aarch64_serror:
b hacf
.balign 0x80
_ex_el1_lower_aarch32_sync:
b hacf
.balign 0x80
_ex_el1_lower_aarch32_irq:
b hacf
.balign 0x80
_ex_el1_lower_aarch32_fiq:
b hacf
.balign 0x80
_ex_el1_lower_aarch32_serror:
b hacf

View File

@@ -0,0 +1,30 @@
#include "irq/interrupts.h"
#include "exceptions.h"
#include "aarch64/sysreg.h"
#include "devices/mmio.h"
void kernel::interrupt::Interrupts::init()
{
set_vbar_el1(&vector_table_el1);
disable();
for (int i = 0; i < HANDLER_COUNT; i++)
{
handlers[i] = nullptr;
}
mmio_write((void *)MMIOOffset::INTR_IRQ_ENABLE_BASE, 0x80002);
// mmio_write((void *)MMIOOffset::INTR_IRQ_DISABLE_BASE, 0xFE);
mmio_write((void *)MMIOOffset::INTR_IRQ_ENABLE_1, 0x00000002 /*0xFFFFFFFF*/);
// mmio_write((void *)MMIOOffset::INTR_IRQ_DISABLE_1, 0x20000000);
mmio_write((void *)MMIOOffset::INTR_IRQ_ENABLE_2, 0x2000000);
// mmio_write((void *)MMIOOffset::INTR_IRQ_DISABLE_2, 0xFF6800);
}
void kernel::interrupt::Interrupts::enable()
{
set_daif(0);
}
void kernel::interrupt::Interrupts::disable()
{
set_daif(15 << 6);
}

86
src/aarch64/irq/irq.cpp Normal file
View File

@@ -0,0 +1,86 @@
#include <cstdint>
#include "irq/interrupts.h"
#include "util/hacf.h"
#include "exceptionclass.h"
#include "syndromedataabort.h"
#include "devices/mmio.h"
#include "sched/context.h"
#include "../sysreg.h"
#include "util/log.h"
#include "kernel.h"
void handlePageFault(ExceptionClass type, SyndromeDataAbort syndrome);
extern "C" int find_irq_source()
{
unsigned int pending1 = mmio_read((void *)MMIOOffset::INTR_IRQ_PENDING_1);
if (pending1 != 0)
{
return __builtin_ctz(pending1);
}
unsigned int pending2 = mmio_read((void *)MMIOOffset::INTR_IRQ_PENDING_2);
if (pending2 != 0)
{
return 32 + __builtin_ctz(pending2);
}
unsigned int base = mmio_read((void *)MMIOOffset::INTR_BASIC_PENDING) & 255;
if (base != 0)
{
return __builtin_ctz(base);
}
return -1;
}
extern "C" kernel::sched::Context *handle_sync(ExceptionClass type, unsigned long syndrome, kernel::sched::Context *ctx)
{
switch (type)
{
case ExceptionClass::INST_ABORT_EL0:
kernelLog(LogLevel::PANIC, "Unhandled INST_ABORT_EL0, FAR_EL1 = %016x", get_far_el1());
hacf();
case ExceptionClass::INST_ABORT_EL1:
kernelLog(LogLevel::PANIC, "Unhandled INST_ABORT_EL1, FAR_EL1 = %016x", get_far_el1());
hacf();
case ExceptionClass::DATA_ABORT_EL0:
kernelLog(LogLevel::PANIC, "Unhandled DATA_ABORT_EL0, FAR_EL1 = %016x", get_far_el1());
hacf();
break;
case ExceptionClass::DATA_ABORT_EL1:
handlePageFault(type, *(SyndromeDataAbort *)&syndrome);
break;
case ExceptionClass::SVC_AARCH64:
case ExceptionClass::SVC_AARCH32:
break;
default:
kernelLog(LogLevel::PANIC, "Unimplemented exception class");
hacf();
break;
}
return ctx;
}
extern "C" kernel::sched::Context *handle_irq(int source, kernel::sched::Context *ctx)
{
using namespace kernel::interrupt;
if (kernel::kernel.getActiveProcess() != nullptr)
{
kernel::kernel.getActiveProcess()->storeContext(ctx);
}
// kernelLog(LogLevel::DEBUG, "handle_irq(%i, %016x)", source, ctx);
if (source < 0)
{
return ctx;
}
Interrupts::callHandler(source);
if (kernel::kernel.getActiveProcess() != nullptr)
{
return kernel::kernel.getActiveProcess()->getContext();
}
return ctx;
}

View File

@@ -0,0 +1,100 @@
#ifndef KERNEL_SYNDROMEDATAABORT_H
#define KERNEL_SYNDROMEDATAABORT_H
#include <cstdint>
enum class DataAbortStatus
{
ADDR_SIZE_FAULT_0 = 0x00,
ADDR_SIZE_FAULT_1 = 0x01,
ADDR_SIZE_FAULT_2 = 0x02,
ADDR_SIZE_FAULT_3 = 0x03,
TRANSLATE_FAULT_0 = 0x04,
TRANSLATE_FAULT_1 = 0x05,
TRANSLATE_FAULT_2 = 0x06,
TRANSLATE_FAULT_3 = 0x07,
ACCESS_FAULT_0 = 0x08,
ACCESS_FAULT_1 = 0x09,
ACCESS_FAULT_2 = 0x0A,
ACCESS_FAULT_3 = 0x0B,
PERM_FAULT_0 = 0x0C,
PERM_FAULT_1 = 0x0D,
PERM_FAULT_2 = 0x0E,
PERM_FAULT_3 = 0x0F
};
struct SyndromeDataAbort
{
/**
* @brief Data fault status code
*/
DataAbortStatus statusCode : 6;
/**
* @brief Write not read. Set when an abort is caused by an instruction
* writing to a memory location. Clear when an abort is caused by a read.
*/
uint64_t wnr : 1;
/**
* @brief
*/
uint64_t s1ptw : 1;
/**
* @brief Cache maintenance.
*/
uint64_t cm : 1;
/**
* @brief External abort.
*/
uint64_t ea : 1;
/**
* @brief FAR not valid for a synchronous external abort.
*/
uint64_t fnv : 1;
/**
* @brief Synchronous error type.
*/
uint64_t set : 2;
/**
* @brief Indicates that the fault came from use of VNCR_EL2 register.
*/
uint64_t vncr : 1;
/**
* @brief Aquire/release.
*/
uint64_t ar : 1;
/**
* @brief 64-bit GP register transfer.
*/
uint64_t sf : 1;
/**
* @brief Register number of the faulting instruction.
*/
uint64_t srt : 5;
/**
* @brief Syndrome sign extend.
*/
uint64_t sse : 1;
/**
* @brief Syndrome access size.
*/
uint64_t sas : 2;
/**
* @brief Instruction syndrome valid.
*/
uint64_t isv : 1;
};
#endif

76
src/aarch64/sysreg.h Normal file
View File

@@ -0,0 +1,76 @@
#ifndef KERNEL_SYSREG_H
#define KERNEL_SYSREG_H
extern "C"
void *get_vbar_el1();
extern "C"
void *set_vbar_el1(void *vbar_el1);
extern "C"
int get_exception_level();
extern "C"
int get_daif();
extern "C"
int set_daif(int bits);
extern "C"
void *get_far_el1();
extern "C"
unsigned long get_spsr_el1();
extern "C"
unsigned long set_spsr_el1(unsigned long spsr_el1);
extern "C"
void *get_elr_el1();
extern "C"
void *set_elr_el1(void *elr_el1);
extern "C"
unsigned long get_esr_el1();
extern "C"
unsigned long get_fpcr();
extern "C"
unsigned long set_fpcr(unsigned long fpcr);
extern "C"
unsigned long get_fpsr();
extern "C"
unsigned long set_fpsr(unsigned long fpsr);
extern "C"
unsigned long get_cpacr_el1();
extern "C"
unsigned long set_cpacr_el1(unsigned long cpacr_el1);
extern "C"
unsigned long get_sctlr_el1();
extern "C"
unsigned long set_sctlr_el1(unsigned long sctlr_el1);
extern "C"
unsigned long get_midr_el1();
extern "C"
unsigned long get_sp_el0();
extern "C"
unsigned long set_sp_el0(void *sp_el0);
extern "C"
unsigned long get_ttbr0_el1();
extern "C"
unsigned long set_ttbr0_el1(unsigned long ttbr0_el1);
#endif

145
src/aarch64/sysreg.s Normal file
View File

@@ -0,0 +1,145 @@
.section .text
# void *get_vbar_el1();
.global get_vbar_el1
get_vbar_el1:
mrs x0, vbar_el1
ret
# void *set_vbar_el1(void *vbar_el1);
.global set_vbar_el1
set_vbar_el1:
msr vbar_el1, x0
ret
# int get_exception_level();
.global get_exception_level
get_exception_level:
mrs x0, CurrentEL
ret
# int get_daif();
.global get_daif
get_daif:
mrs x0, daif
ret
# int set_daif(int bits);
.global set_daif
set_daif:
msr daif, x0
ret
# void *get_far_el1();
.global get_far_el1
get_far_el1:
mrs x0, far_el1
ret
# unsigned long get_spsr_el1();
.global get_spsr_el1
get_spsr_el1:
mrs x0, spsr_el1
ret
# unsigned long set_spsr_el1(unsigned long spsr_el1);
.global set_spsr_el1
set_spsr_el1:
msr spsr_el1, x0
ret
# void *get_elr_el1();
.global get_elr_el1
get_elr_el1:
mrs x0, elr_el1
ret
# void *set_elr_el1(void *elr_el1);
.global set_elr_el1
set_elr_el1:
msr elr_el1, x0
ret
# unsigned long get_esr_el1();
.global get_esr_el1
get_esr_el1:
mrs x0, esr_el1
ret
# unsigned long get_fpcr();
.global get_fpcr
get_fpcr:
mrs x0, fpcr
ret
# unsigned long set_fpcr(unsigned long fpcr);
.global set_fpcr
set_fpcr:
msr fpcr, x0
ret
# unsigned long get_fpsr();
.global get_fpsr
get_fpsr:
mrs x0, fpsr
ret
# unsigned long set_fpsr(unsigned long fpsr);
.global set_fpsr
set_fpsr:
msr fpsr, x0
ret
# unsigned long get_cpacr_el1();
.global get_cpacr_el1
get_cpacr_el1:
mrs x0, cpacr_el1
ret
# unsigned long set_cpacr_el1(unsigned long cpacr_el1);
.global set_cpacr_el1
set_cpacr_el1:
msr cpacr_el1, x0
ret
# unsigned long get_sctlr_el1();
.global get_sctlr_el1
get_sctlr_el1:
mrs x0, sctlr_el1
ret
# unsigned long set_sctlr_el1(unsigned long sctlr_el1);
.global set_sctlr_el1
set_sctlr_el1:
msr sctlr_el1, x0
ret
# unsigned long get_midr_el1();
.global get_midr_el1
get_midr_el1:
mrs x0, midr_el1
ret
# void *get_sp_el0();
.global get_sp_el0
get_sp_el0:
mrs x0, sp_el0
ret
# void *set_sp_el0(void *sp_el0);
.global set_sp_el0
set_sp_el0:
msr sp_el0, x0
ret
# void *get_ttbr0_el1();
.global get_ttbr0_el1
get_ttbr0_el1:
mrs x0, ttbr0_el1
ret
# void *set_ttbr0_el1(unsigned long ttbr0_el1);
.global set_ttbr0_el1
set_ttbr0_el1:
msr ttbr0_el1, x0
ret

View File

@@ -0,0 +1,706 @@
#ifndef BINARY_SEARCH_TREE_H
#define BINARY_SEARCH_TREE_H
#include "math.h"
#include "string.h"
template <typename K, typename V>
class binary_search_tree
{
public:
class node
{
public:
binary_search_tree *tree;
K key;
V value;
node *parent;
node *left;
node *right;
int height;
node(binary_search_tree *tree, K key, V &val);
int balance() const;
void update_height();
node *successor() const;
void replace(node *other);
void unlink();
private:
};
class tree_iterator
{
public:
tree_iterator();
tree_iterator(node *p);
tree_iterator(tree_iterator &other);
tree_iterator &operator=(tree_iterator &other);
tree_iterator &operator++();
bool operator!=(tree_iterator &other);
K &operator*();
K *operator->();
private:
node *p;
};
binary_search_tree();
~binary_search_tree();
int size() const;
bool empty() const;
void insert(K key, V &val);
void remove(K key);
void clear();
V &get(K key) const;
V &search(K key) const;
bool contains(K key) const;
V &pop_min();
V &pop_max();
V &peek_min() const;
V &peek_max() const;
tree_iterator begin();
tree_iterator end();
string to_string() const;
private:
node *root;
int _size;
node *search_rec(K key) const;
void add_rec(node *cur, K key, V &val, bool &node_added);
void remove_rec(node *cur, K key);
void clear_rec(node *cur);
void rebalance(node *node);
void rotate_right(node *node);
void rotate_left(node *node);
void to_string_rec(K ***grid, node *cur, int row, int col, int height) const;
};
template <typename K, typename V>
binary_search_tree<K, V>::node::node(binary_search_tree *tree, K key, V &val)
: tree(tree), key(key), value(val), parent(nullptr), left(nullptr), right(nullptr), height(1) {}
template <typename K, typename V>
int binary_search_tree<K, V>::node::balance() const
{
if (left == nullptr && right == nullptr)
return 0;
else if (left == nullptr)
return -right->height;
else if (right == nullptr)
return left->height;
else
return left->height - right->height;
}
template <typename K, typename V>
void binary_search_tree<K, V>::node::update_height()
{
if (left == nullptr && right == nullptr)
height = 1;
else if (left == nullptr)
height = 1 + right->height;
else if (right == nullptr)
height = 1 + left->height;
else
height = 1 + max(left->height, right->height);
}
template <typename K, typename V>
typename binary_search_tree<K, V>::node *binary_search_tree<K, V>::node::successor() const
{
if (right != nullptr)
{
node *cur = right;
while (cur->left != nullptr)
cur = cur->left;
return cur;
}
else if (left != nullptr)
{
node *cur = left;
while (cur->right != nullptr)
cur = cur->right;
return cur;
}
else
{
return nullptr;
}
}
template <typename K, typename V>
void binary_search_tree<K, V>::node::replace(node *other)
{
key = other->key;
value = other->value;
node *succ = other->successor();
if (succ == nullptr)
{
node *other_parent = other->parent;
other->unlink();
delete other;
tree->rebalance(other_parent);
}
else
{
node *succ_parent = succ->parent;
other->replace(succ);
tree->rebalance(succ_parent);
}
}
template <typename K, typename V>
void binary_search_tree<K, V>::node::unlink()
{
if (parent != nullptr)
{
if (parent->left == this)
parent->left = nullptr;
else
parent->right = nullptr;
}
}
template <typename K, typename V>
binary_search_tree<K, V>::binary_search_tree()
: root(nullptr), _size(0) {}
template <typename K, typename V>
binary_search_tree<K, V>::~binary_search_tree()
{
clear();
}
template <typename K, typename V>
int binary_search_tree<K, V>::size() const
{
return _size;
}
template <typename K, typename V>
bool binary_search_tree<K, V>::empty() const
{
return _size == 0;
}
template <typename K, typename V>
void binary_search_tree<K, V>::insert(K key, V &val)
{
bool node_added = false;
add_rec(root, key, val, node_added);
}
template <typename K, typename V>
void binary_search_tree<K, V>::remove(K key)
{
remove_rec(root, key);
}
template <typename K, typename V>
V &binary_search_tree<K, V>::get(K key) const
{
binary_search_tree<K, V>::node *node = search_rec(key);
if (node == nullptr || node->key != key)
return root->value;
else
return node->value;
}
template <typename K, typename V>
V &binary_search_tree<K, V>::search(K key) const
{
binary_search_tree<K, V>::node *node = search_rec(key);
if (node == nullptr)
return root->value;
else
return node->value;
}
template <typename K, typename V>
bool binary_search_tree<K, V>::contains(K key) const
{
binary_search_tree<K, V>::node *node = search_rec(key);
if (node == nullptr || node->key != key)
return false;
else
return true;
}
template <typename K, typename V>
void binary_search_tree<K, V>::clear()
{
clear_rec(root);
root = nullptr;
_size = 0;
}
template <typename K, typename V>
V &binary_search_tree<K, V>::peek_min() const
{
node *cur = root;
if (cur == nullptr)
return root->value;
while (cur->left != nullptr)
cur = cur->left;
return cur->value;
}
template <typename K, typename V>
V &binary_search_tree<K, V>::peek_max() const
{
node *cur = root;
if (cur == nullptr)
return root->value;
while (cur->right != nullptr)
cur = cur->right;
return cur->value;
}
template <typename K, typename V>
inline typename binary_search_tree<K, V>::tree_iterator binary_search_tree<K, V>::begin()
{
if (root == nullptr)
{
return tree_iterator();
}
node *p = root;
while (p->left != nullptr)
{
p = p->left;
}
return tree_iterator(p);
}
template <typename K, typename V>
inline typename binary_search_tree<K, V>::tree_iterator binary_search_tree<K, V>::end()
{
return tree_iterator();
}
template <typename K, typename V>
V &binary_search_tree<K, V>::pop_min()
{
node *cur = root;
if (cur == nullptr)
return root->value;
while (cur->left != nullptr)
cur = cur->left;
V &val = cur->value;
remove_rec(root, cur->key);
return val;
}
template <typename K, typename V>
V &binary_search_tree<K, V>::pop_max()
{
node *cur = root;
if (cur == nullptr)
return root->value;
while (cur->right != nullptr)
cur = cur->right;
V &val = cur->value;
remove_rec(root, cur->key);
return val;
}
template <typename K, typename V>
string binary_search_tree<K, V>::to_string() const
{
string str = "";
if (root == nullptr)
return str;
int height = root->height;
int cols = (1 << height) - 1;
K ***grid = new K **[height];
for (int i = 0; i < height; i++)
grid[i] = new K *[cols];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < cols; j++)
{
grid[i][j] = nullptr;
}
}
to_string_rec(grid, root, 0, cols / 2, height);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < cols; j++)
{
if (grid[i][j] == nullptr)
{
str += " ";
}
else
{
str += ('0' + *(grid[i][j]));
str += " ";
}
}
if (i != height - 1)
str += "\n";
}
return str;
}
template <typename K, typename V>
typename binary_search_tree<K, V>::node *binary_search_tree<K, V>::search_rec(K key) const
{
binary_search_tree<K, V>::node *cur = root;
while (cur != nullptr)
{
if (cur->key > key)
{
if (cur->left == nullptr)
return cur;
cur = cur->left;
}
else if (cur->key < key)
{
if (cur->right == nullptr)
return cur;
cur = cur->right;
}
else
{
return cur;
}
}
return nullptr;
}
template <typename K, typename V>
void binary_search_tree<K, V>::add_rec(node *cur, K key, V &val, bool &node_added)
{
if (root == nullptr)
{
_size++;
node_added = true;
root = new node(this, key, val);
cur = root;
}
else if (cur->key > key)
{
if (cur->left != nullptr)
{
add_rec(cur->left, key, val, node_added);
}
else
{
_size++;
node_added = true;
cur->left = new node(this, key, val);
cur->left->parent = cur;
}
}
else if (cur->key < key)
{
if (cur->right != nullptr)
{
add_rec(cur->right, key, val, node_added);
}
else
{
_size++;
node_added = true;
cur->right = new node(this, key, val);
cur->right->parent = cur;
}
}
else
{
if (node_added)
{
return;
}
else if (cur->right != nullptr)
{
add_rec(cur->right, key, val, node_added);
}
else
{
_size++;
node_added = true;
cur->right = new node(this, key, val);
cur->right->parent = cur;
}
}
rebalance(cur);
}
template <typename K, typename V>
void binary_search_tree<K, V>::remove_rec(node *cur, K key)
{
if (cur == nullptr)
return;
if (cur->key > key)
{
remove_rec(cur->left, key);
}
else if (cur->key < key)
{
remove_rec(cur->right, key);
}
else
{
node *succ = cur->successor();
if (succ == nullptr)
{
if (root == cur)
root = nullptr;
node *cur_parent = cur->parent;
cur->unlink();
delete cur;
cur = nullptr;
if (cur_parent != nullptr)
rebalance(cur_parent);
}
else
{
node *succ_parent = succ->parent;
cur->replace(succ);
rebalance(succ_parent);
}
_size--;
}
if (cur == nullptr)
return;
rebalance(cur);
}
template <typename K, typename V>
void binary_search_tree<K, V>::clear_rec(node *cur)
{
if (cur->left != nullptr && cur->right != nullptr)
{
clear_rec(cur->left);
clear_rec(cur->right);
}
else if (cur->left != nullptr)
{
clear_rec(cur->left);
}
else if (cur->right != nullptr)
{
clear_rec(cur->right);
}
delete cur;
}
template <typename K, typename V>
void binary_search_tree<K, V>::rebalance(node *node)
{
node->update_height();
int bal = node->balance();
if (bal > 1)
{
if (node->left != nullptr && node->left->balance() < 0)
rotate_left(node->left);
rotate_right(node);
}
else if (bal < -1)
{
if (node->right != nullptr && node->right->balance() > 0)
rotate_right(node->right);
rotate_left(node);
}
}
template <typename K, typename V>
void binary_search_tree<K, V>::rotate_right(node *node)
{
binary_search_tree<K, V>::node *parent = node->parent;
binary_search_tree<K, V>::node *left = node->left;
binary_search_tree<K, V>::node *left_right = left->right;
left->right = node;
node->left = left_right;
left->parent = parent;
node->parent = left;
if (left_right != nullptr)
left_right->parent = node;
if (parent != nullptr)
{
if (parent->left == node)
parent->left = left;
else
parent->right = left;
}
else
{
root = left;
}
node->update_height();
left->update_height();
}
template <typename K, typename V>
void binary_search_tree<K, V>::rotate_left(node *node)
{
binary_search_tree<K, V>::node *parent = node->parent;
binary_search_tree<K, V>::node *right = node->right;
binary_search_tree<K, V>::node *right_left = right->left;
right->left = node;
node->right = right_left;
right->parent = parent;
node->parent = right;
if (right_left != nullptr)
right_left->parent = node;
if (parent != nullptr)
{
if (parent->left == node)
parent->left = right;
else
parent->right = right;
}
else
{
root = right;
}
node->update_height();
right->update_height();
}
template <typename K, typename V>
void binary_search_tree<K, V>::to_string_rec(K ***grid, node *cur, int row, int col, int height) const
{
if (cur == nullptr)
return;
grid[row][col] = &cur->key;
to_string_rec(grid, cur->left, row + 1, col - pow(2, height - 2), height - 1);
to_string_rec(grid, cur->right, row + 1, col + pow(2, height - 2), height - 1);
}
template <typename K, typename V>
inline binary_search_tree<K, V>::tree_iterator::tree_iterator()
: p(nullptr)
{
}
template <typename K, typename V>
inline binary_search_tree<K, V>::tree_iterator::tree_iterator(node *p)
: p(p)
{
}
template <typename K, typename V>
inline binary_search_tree<K, V>::tree_iterator::tree_iterator(tree_iterator &other)
: p(other.p)
{
}
template <typename K, typename V>
inline typename binary_search_tree<K, V>::tree_iterator &binary_search_tree<K, V>::tree_iterator::operator=(tree_iterator &other)
{
p = other.p;
return *this;
}
template <typename K, typename V>
inline typename binary_search_tree<K, V>::tree_iterator &binary_search_tree<K, V>::tree_iterator::operator++()
{
if (p->right != nullptr)
{
p = p->right;
while (p->left != nullptr)
{
p = p->left;
}
}
else
{
while (true)
{
if (p->parent == nullptr)
{
p = nullptr;
break;
}
else if (p->parent->left == p)
{
p = p->parent;
break;
}
else
{
p = p->parent;
}
}
}
return *this;
}
template <typename K, typename V>
inline bool binary_search_tree<K, V>::tree_iterator::operator!=(tree_iterator &other)
{
return p != other.p;
}
template <typename K, typename V>
inline K &binary_search_tree<K, V>::tree_iterator::operator*()
{
return p->key;
}
template <typename K, typename V>
inline K *binary_search_tree<K, V>::tree_iterator::operator->()
{
return &p->key;
}
#endif

177
src/containers/hashmap.h Normal file
View File

@@ -0,0 +1,177 @@
#ifndef HASHMAP_H
#define HASHMAP_H
#include "pair.h"
#include "string.h"
#include "vector.h"
template <typename K, typename V>
class hashmap {
public:
hashmap();
~hashmap();
int size() const;
bool empty() const;
void insert(const K& key, const V& value);
void remove(const K& key);
const V& get(const K& key) const;
bool contains(const K& key) const;
void clear();
V& operator[](const K& key) const;
private:
static constexpr int hash_prime = 5381;
static constexpr int default_capacity = 16;
static constexpr double load_factor_threshold = 0.75;
static V default_value;
vector<pair<K, V>>* table;
int _size;
int _capacity;
int hash(const int& key) const;
int hash(const double& key) const;
int hash(const string& key) const;
void rehash_table();
};
template <typename K, typename V>
V hashmap<K, V>::default_value = V();
template <typename K, typename V>
hashmap<K, V>::hashmap() {
_size = 0;
_capacity = default_capacity;
table = new vector<pair<K, V>>[_capacity];
for (int i = 0; i < _capacity; i++)
table[i] = vector<pair<K, V>>();
}
template <typename K, typename V>
hashmap<K, V>::~hashmap() {
if (table != nullptr)
// delete[] array; // need to overload delete[] to not call deconstructors of array object, std implementation has unwanted side effects
table = nullptr;
}
template <typename K, typename V>
int hashmap<K, V>::size() const {
return _size;
}
template <typename K, typename V>
bool hashmap<K, V>::empty() const {
return size == 0;
}
template <typename K, typename V>
void hashmap<K, V>::insert(const K& key, const V& value) {
double load_factor = (double)_size / _capacity;
if (load_factor > load_factor_threshold)
rehash_table();
int index = hash(key) % _capacity;
table[index].push_back(pair(key, value));
_size++;
}
template <typename K, typename V>
void hashmap<K, V>::remove(const K& key) {
int index = hash(key) % _capacity;
for (int i = 0; i < table[index].size(); i++) {
if (table[index][i].first == key) {
table[index] = V();
_size--;
break;
}
}
}
template <typename K, typename V>
const V& hashmap<K, V>::get(const K& key) const {
int index = hash(key) % _capacity;
for (int i = 0; i < table[index].size(); i++) {
if (table[index][i].first == key)
return table[index][i].second;
}
return default_value;
}
template <typename K, typename V>
bool hashmap<K, V>::contains(const K& key) const {
int index = hash(key) % table.capacity();
for (int i = 0; i < table[index].size(); i++) {
if (table[index][i].first == key)
return true;
}
return false;
}
template <typename K, typename V>
void hashmap<K, V>::clear() {
_size = 0;
for (int i = 0; i < _capacity; i++)
table[i].clear();
}
template <typename K, typename V>
V& hashmap<K, V>::operator[](const K& key) const {
int index = hash(key) % _capacity;
for (int i = 0; i < table[index].size(); i++) {
if (table[index][i].first == key)
return table[index][i].second;
}
return default_value;
}
template <typename K, typename V>
int hashmap<K, V>::hash(const int& key) const {
int hash = hash_prime;
int size_bytes = sizeof(key);
const char* data = reinterpret_cast<const char*>(&key);
for (int i = 0; i < size_bytes; i++)
hash = (hash << 5) + hash + data[i];
return hash;
}
template <typename K, typename V>
int hashmap<K, V>::hash(const double& key) const {
int hash = hash_prime;
int size_bytes = sizeof(key);
const char* data = reinterpret_cast<const char*>(&key);
for (int i = 0; i < size_bytes; i++)
hash = (hash << 5) + hash + data[i];
return hash;
}
template <typename K, typename V>
int hashmap<K, V>::hash(const string& key) const {
int hash = hash_prime;
for (int i = 0; i < key.size(); i++)
hash = (hash << 5) + hash + key[i];
return hash;
}
template <typename K, typename V>
void hashmap<K, V>::rehash_table() {
int new_capacity = _capacity * 2;
vector<pair<K, V>>* new_table = new vector<pair<K, V>>[new_capacity];
for (int i = 0; i < new_capacity; i++)
new_table[i] = vector<pair<K, V>>();
for (int i = 0; i < _capacity; i++) {
for (int j = 0; j < table[i].size(); j++) {
int new_index = hash(table[i][j].first) % new_capacity;
new_table[new_index].push_back(table[i][j]);
}
}
// delete[] table; // need to overload delete[] to not call deconstructors of array object, std implementation has unwanted side effects
table = new_table;
_capacity = new_capacity;
}
#endif

View File

@@ -0,0 +1,350 @@
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include "string.h"
template <typename T>
class linked_list
{
public:
linked_list();
~linked_list();
bool empty() const;
int size() const;
void push_front(T& data);
void push_back(T& data);
T& pop_front();
T& pop_back();
T& peek_front() const;
T& peek_back() const;
void insert(int index, T &data); // inserts new element before the existing element at the given index
void replace(int index, T &data);
void remove(int index);
void clear();
T &operator[](int index) const;
string to_string() const;
private:
class node {
public:
T& value;
node* prev;
node* next;
node(T &value);
private:
};
static T default_value;
node *head;
node *tail;
int _size;
};
template <typename T>
linked_list<T>::node::node(T &value)
: value(value), prev(nullptr), next(nullptr) {}
template <typename T>
T linked_list<T>::default_value = 0x0;//CHECKME Legal?
template <typename T>
linked_list<T>::linked_list()
: head(nullptr), tail(nullptr), _size(0) {}
template <typename T>
linked_list<T>::~linked_list()
{
}
template <typename T>
bool linked_list<T>::empty() const
{
return _size == 0;
}
template <typename T>
int linked_list<T>::size() const
{
return _size;
}
template <typename T>
void linked_list<T>::push_front(T &data)
{
node *new_node = new node(data);
if (head == nullptr)
{
head = new_node;
tail = new_node;
}
else if (head == tail)
{
head = new_node;
head->next = tail;
tail->prev = head;
}
else
{
head->prev = new_node;
new_node->next = head;
head = new_node;
}
_size++;
}
template <typename T>
void linked_list<T>::push_back(T &data)
{
node *new_node = new node(data);
if (tail == nullptr)
{
tail = new_node;
head = new_node;
}
else if (tail == head)
{
tail = new_node;
tail->prev = head;
head->next = tail;
}
else
{
tail->next = new_node;
new_node->prev = tail;
tail = new_node;
}
_size++;
}
template <typename T>
T& linked_list<T>::pop_front() {
T& data = default_value;
if (head == nullptr)
{
data = default_value;
}
else if (head == tail)
{
data = head->value;
delete head;
head = nullptr;
tail = nullptr;
}
else
{
data = head->value;
head = head->next;
delete head->prev;
head->prev = nullptr;
}
_size--;
return data;
}
template <typename T>
T& linked_list<T>::pop_back() {
T& data = default_value;
if (tail == nullptr)
{
data = default_value;
}
else if (tail == head)
{
data = tail->value;
delete tail;
tail = nullptr;
head = nullptr;
}
else
{
data = tail->value;
tail = tail->prev;
delete tail->next;
tail->next = nullptr;
}
_size--;
return data;
}
template <typename T>
T& linked_list<T>::peek_front() const {
if (head == nullptr)
return default_value;
else
return head->value;
}
template <typename T>
T& linked_list<T>::peek_back() const {
if (tail == nullptr)
return default_value;
else
return tail->value;
}
template <typename T>
void linked_list<T>::insert(int index, T &data)
{
if (index > _size)
return;
node *cur = head;
for (int i = 0; i < index; i++)
cur = cur->next;
node *new_node = new node(data);
if (index == _size && _size == 0)
{
head = new_node;
tail = new_node;
}
else if (index == _size && _size == 1)
{
tail = new_node;
head->next = tail;
tail->prev = head;
}
else if (index == _size && _size > 1)
{
tail->next = new_node;
new_node->prev = tail;
tail = new_node;
}
else if (index == 0)
{
head->prev = new_node;
new_node->next = head;
head = new_node;
}
else
{
cur->prev->next = new_node;
new_node->prev = cur->prev;
cur->prev = new_node;
new_node->next = cur;
}
_size++;
}
template <typename T>
void linked_list<T>::replace(int index, T &data)
{
if (index >= _size)
return;
node *cur = head;
for (int i = 0; i < index; i++)
cur = cur->next;
cur->value = data;
}
template <typename T>
void linked_list<T>::remove(int index)
{
if (index >= _size)
return;
node *cur = head;
for (int i = 0; i < index; i++)
cur = cur->next;
if (head == tail)
{
delete head;
head = nullptr;
tail = nullptr;
}
else if (cur == head)
{
head = head->next;
delete head->prev;
head->prev = nullptr;
}
else if (cur == tail)
{
tail = tail->prev;
delete tail->next;
tail->next = nullptr;
}
else
{
cur->prev->next = cur->next;
cur->next->prev = cur->prev;
delete cur;
}
_size--;
}
template <typename T>
void linked_list<T>::clear()
{
node *cur = head;
while (cur != nullptr)
{
if (cur->next != nullptr)
{
cur = cur->next;
delete cur->prev;
}
else
{
delete cur;
cur = nullptr;
}
}
head = nullptr;
tail = nullptr;
_size = 0;
}
template <typename T>
T &linked_list<T>::operator[](int index) const
{
if (index >= _size)
return default_value;
node *cur = head;
for (int i = 0; i < index; i++)
cur = cur->next;
return cur->value;
}
template <typename T>
string linked_list<T>::to_string() const {
string str;
node* cur = head;
str += "[";
for (int i = 0; i < _size; i++) {
str += '0' + cur->value;
if (i != _size - 1)
str += ", ";
cur = cur->next;
}
str += "]";
return str;
}
#endif

28
src/containers/math.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef MATH_H
#define MATH_H
template <typename T>
const T& min(const T& x, const T& y) {
return (x < y) ? x : y;
}
template <typename T>
const T& max(const T& x, const T& y) {
return (x > y) ? x : y;
}
template <typename T>
const T abs(const T& x) {
return (x < 0) ? -x : x;
}
template <typename T>
const T pow(const T& base, const T& exponent) {
T result = 1;
for (int i = 0; i < abs(exponent); i++) {
result *= base;
}
return (exponent >= 0) ? result : 1 / result;
}
#endif

27
src/containers/pair.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef PAIR_H
#define PAIR_H
template <typename T, typename U>
struct pair
{
T first;
U second;
pair()
: first(T()), second(U()){};
pair(const T &first, const U &second)
: first(first), second(second) {}
bool operator==(const pair<T, U> &other) const
{
return first == other.first && second == other.second;
}
bool operator!=(const pair<T, U> &other) const
{
return !(*this == other);
}
};
#endif

260
src/containers/string.cpp Normal file
View File

@@ -0,0 +1,260 @@
#include "string.h"
string::string()
{
s = strdup("");
}
string::string(const char *other)
{
s = strdup(other);
}
string::string(const string &other)
{
s = strdup(other.s);
}
string::~string()
{
if (s != nullptr)
delete[] s;
}
int string::size() const
{
return strlen(s);
}
bool string::operator==(const string &other) const
{
return strcmp(s, other.s);
}
bool string::operator!=(const string &other) const
{
return strcmp(s, other.s);
}
string &string::operator=(char c)
{
if (s != nullptr)
delete[] s;
char cnvrt[2] = {c, '\0'};
s = strdup(cnvrt);
return *this;
}
string &string::operator=(const char *other)
{
if (s != nullptr)
delete[] s;
s = strdup(other);
return *this;
}
string &string::operator=(const string &other)
{
if (s != nullptr)
delete[] s;
s = strdup(other.s);
return *this;
}
string string::operator+(char c) const
{
string result;
result.s = new char[strlen(s) + 1 + 1];
strcpy(result.s, s);
char cnvrt[2] = {c, '\0'};
strcat(result.s, cnvrt);
return result;
}
string string::operator+(const char *other) const
{
string result;
result.s = new char[strlen(s) + strlen(other) + 1];
strcpy(result.s, s);
strcat(result.s, other);
return result;
}
string string::operator+(const string &other) const
{
return *this + other.s;
}
string &string::operator+=(char c)
{
char *temp = new char[strlen(s) + 1 + 1];
strcpy(temp, s);
char cnvrt[2] = {c, '\0'};
strcat(temp, cnvrt);
if (s != nullptr)
delete[] s;
s = temp;
return *this;
}
string &string::operator+=(const char *other)
{
char *temp = new char[strlen(s) + strlen(other) + 1];
strcpy(temp, s);
strcat(temp, other);
if (s != nullptr)
delete[] s;
s = temp;
return *this;
}
string &string::operator+=(const string &other)
{
return *this += other.s;
}
const char &string::operator[](int index) const
{
return s[index];
}
const char *string::c_str() const
{
return s;
}
string string::substr(int start_index) const
{
if (start_index < 0 || start_index >= strlen(s)) // invalid index
return string();
return string(s + start_index); // pointer arithmatic, eat your heart out rust
}
string string::substr(int start_index, int end_index) const
{
if (start_index < 0 || start_index >= strlen(s) || end_index <= start_index || end_index > strlen(s)) // invalid indices
return string();
int length = end_index - start_index;
char *temp = new char[length + 1];
strncpy(temp, s + start_index, length);
temp[length] = '\0'; // make sure to null-terminate the string
return string(temp);
}
bool string::strcmp(const char *first, const char *second)
{
while (*first && *second && *first == *second)
{
first++;
second++;
}
return *first == *second;
}
char *string::substr(const char *str, int start_index)
{
int len = strlen(str + start_index);
char *substr = new char[len + 1];
strcpy(substr, str + start_index);
return substr;
}
char *string::substr(const char *str, int start_index, int end_index)
{
int len = end_index - start_index;
char *substr = new char[len + 1];
strncpy(substr, str + start_index, len);
substr[len] = '\0';
return substr;
}
char *string::strdup(const char *s)
{
int len = strlen(s);
char *dup = new char[len + 1];
strcpy(dup, s);
return dup;
}
void string::strcpy(char *&dest, const char *src)
{
if (dest != nullptr)
delete[] dest;
int len = strlen(src);
dest = new char[len + 1];
char *temp = dest;
while (*src)
{
*temp = *src;
temp++;
src++;
}
*temp = '\0';
}
void string::strncpy(char *&dest, const char *src, int len)
{
if (dest != nullptr)
delete[] dest;
dest = new char[len + 1];
char *temp = dest;
while (*src && len > 0)
{
*temp = *src;
temp++;
src++;
len--;
}
*temp = '\0';
}
void string::strcat(char *&dest, const char *src)
{
if (dest == nullptr)
{
dest = strdup(src);
return;
}
int len_dest = strlen(dest);
int len_src = strlen(src);
char *temp = new char[len_dest + len_src + 1];
char *temp_ptr = temp;
const char *dest_ptr = dest;
const char *src_ptr = src;
while (*dest_ptr)
{
*temp_ptr = *dest_ptr;
temp_ptr++;
dest_ptr++;
}
while (*src_ptr)
{
*temp_ptr = *src_ptr;
temp_ptr++;
src_ptr++;
}
*temp_ptr = '\0';
delete[] dest;
dest = temp;
}
int string::strlen(const char *s)
{
int len = 0;
while (*s)
{
len++;
s++;
}
return len;
}

47
src/containers/string.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef STRING_H
#define STRING_H
class string
{
public:
string();
string(const char *other);
string(const string &other);
~string();
int size() const;
bool operator==(const string &other) const;
bool operator!=(const string &other) const;
string &operator=(char c);
string &operator=(const char *other);
string &operator=(const string &other);
string operator+(char other) const;
string operator+(const char *other) const;
string operator+(const string &other) const;
string &operator+=(char c);
string &operator+=(const char *other);
string &operator+=(const string &other);
const char &operator[](int index) const;
const char *c_str() const;
string substr(int start_index) const;
string substr(int start_index, int end_index) const;
static bool strcmp(const char *first, const char *second); // differs from standard implemtation
static char *substr(const char *str, int start_index);
static char *substr(const char *str, int start_index, int end_index);
static char *strdup(const char *s);
static void strcpy(char *&dest, const char *src); // differs from standard implemtation, allocates more memory in destination string if its not big enough
static void strncpy(char *&dest, const char *src, int len); // ^
static void strcat(char *&dest, const char *src); // ^
static int strlen(const char *s); // doesn't include the null-terminator
private:
char *s;
};
#endif

137
src/containers/vector.h Normal file
View File

@@ -0,0 +1,137 @@
#ifndef VECTOR_H
#define VECTOR_H
template <typename T>
class vector
{
public:
vector();
vector(int capacity);
vector(const vector<T> &other);
~vector();
bool empty() const;
int size() const;
int capacity() const;
void push_back(const T &data);
void pop_back();
T &back();
void remove(int index);
void clear();
void resize(int new_capacity);
T &operator[](int index) const;
private:
T *array;
int _capacity;
int _size;
};
template <typename T>
vector<T>::vector()
{
_size = 0;
_capacity = 8;
array = new T[_capacity];
}
template <typename T>
vector<T>::vector(int capacity)
{
_size = 0;
_capacity = capacity;
array = new T[_capacity];
}
template <typename T>
vector<T>::vector(const vector<T> &other)
{
_size = other.size();
_capacity = other.capacity();
array = new T[_capacity];
for (int i = 0; i < _size; i++)
array[i] = other[i];
}
template <typename T>
vector<T>::~vector()
{
if (array != nullptr)
delete[] array;
}
template <typename T>
bool vector<T>::empty() const
{
return _size == 0;
}
template <typename T>
int vector<T>::size() const
{
return _size;
}
template <typename T>
int vector<T>::capacity() const
{
return _capacity;
}
template <typename T>
T &vector<T>::operator[](int index) const
{
return array[index];
}
template <typename T>
void vector<T>::push_back(const T &data)
{
if (_size == _capacity)
resize(_capacity * 2);
array[_size] = data;
_size++;
}
template <typename T>
void vector<T>::pop_back()
{
array[_size - 1].~T();
_size--;
}
template <typename T>
T &vector<T>::back()
{
return array[_size - 1];
}
template <typename T>
void vector<T>::remove(int index)
{
return array[index] = T();
}
template <typename T>
void vector<T>::clear()
{
_size = 0;
for (int i = 0; i < _capacity; i++)
array[i].~T();
}
template <typename T>
void vector<T>::resize(int new_capacity)
{
_capacity = new_capacity;
T *temp = new T[_capacity];
for (int i = 0; i < _size; i++)
temp[i] = array[i];
delete[] array;
array = temp;
}
#endif

135
src/devices/disk_driver.c++ Normal file
View File

@@ -0,0 +1,135 @@
#include "disk_driver.h"
void int_to_bytes(unsigned int num, byte* buffer, int offset) {
int size = sizeof(unsigned int);
for (int i = 0; i < size; i++)
buffer[i + offset] = ((num >> (i * 8)) & 0xFF);
return;
}
disk_driver::disk_driver() {
registers = (unsigned int*)0xFFFFFF7FFF300000;
}
disk_driver::disk_driver(unsigned int emmc_registers_base_address) {
registers = (unsigned int*)emmc_registers_base_address;
}
disk_driver::~disk_driver() {}
int disk_driver::read(unsigned int sector_offset, byte*& buffer) {
unsigned int byte_offset = sector_offset * bytes_per_sector;
Status status = wait_for_read_rdy();
if (status != Status::OK)
return status;
registers[BLKSIZECNT] = (1 << 16) | bytes_per_sector;
for (int i = 0; i < bytes_per_sector; i += bytes_per_int) {
unsigned int int_byte_offset = byte_offset + (i * bytes_per_int);
unsigned int response;
Status status = issue_cmd(READ_SINGLE, int_byte_offset, response);
if (status != Status::OK)
return status;
Status status = wait_for_read_rdy();
if (status != Status::OK)
return status;
int_to_bytes(registers[DATA], buffer, i);
}
return Status::OK;
}
int disk_driver::write(unsigned int sector_offset, const byte*& buffer) {
}
disk_driver::Status disk_driver::issue_cmd(COMMAND_MASK cmd, unsigned int arg, unsigned int& response) {
Status status = wait_for_cmd_rdy();
if (status != Status::OK)
return status;
registers[ARG1] = arg;
registers[CMDTM] = cmd;
Status status = wait_for_cmd_done();
if (status != Status::OK)
return status;
response = registers[RESP0];
return Status::OK;
}
disk_driver::Status disk_driver::wait_for_read_rdy() {
int count = 1000000;
while (any_errors() || !can_read()) {
// wait(1);
count--;
}
if (any_errors())
return Status::ERROR;
else if (!can_read())
return Status::TIMEOUT;
else
return Status::OK;
}
disk_driver::Status disk_driver::wait_for_cmd_rdy() {
int count = 1000000;
while (any_errors() || !can_issue_cmd()) {
// wait(1);
count--;
}
if (any_errors())
return Status::ERROR;
else if (!can_issue_cmd())
return Status::TIMEOUT;
else
return Status::OK;
}
disk_driver::Status disk_driver::wait_for_cmd_done() {
int count = 1000000;
while (any_errors() || !cmd_done()) {
// wait(1);
count--;
}
if (any_errors())
return Status::ERROR;
else if (!cmd_done())
return Status::TIMEOUT;
else
return Status::OK;
}
bool disk_driver::any_errors() {
bool any_interrupt_errors = registers[INTERRUPT] & INTERRUPT_MASK::ERROR;
bool any_command_errors = registers[CMDTM] & COMMAND_MASK::ERROR;
return any_interrupt_errors || any_command_errors;
}
bool disk_driver::can_read() {
bool read_available = registers[STATUS] & STATUS_MASK::READ_AVAILABLE;
bool read_ready = registers[INTERRUPT] & INTERRUPT_MASK::READ_READY;
bool prev_transfer_done = !(registers[STATUS] & STATUS_MASK::DATA_INHIBIT);
return read_available && read_ready && prev_transfer_done;
}
bool disk_driver::can_write() {
bool write_avl = registers[STATUS] & STATUS_MASK::WRITE_AVAILABLE;
bool write_rdy = registers[INTERRUPT] & INTERRUPT_MASK::WRITE_READY;
bool prev_transfer_done = !(registers[STATUS] & STATUS_MASK::DATA_INHIBIT);
return write_avl && write_rdy && prev_transfer_done;
}
bool disk_driver::can_issue_cmd() {
return !(registers[STATUS] & STATUS_MASK::CMD_INHIBIT);
}
bool disk_driver::cmd_done() {
return !(registers[INTERRUPT] & INTERRUPT_MASK::CMD_DONE);
}

108
src/devices/disk_driver.h Normal file
View File

@@ -0,0 +1,108 @@
#ifndef KERNEL_DISK_DRIVER_H
#define KERNEL_DISK_DRIVER_H
typedef unsigned char byte;
class disk_driver {
public:
enum Status : int {
OK = 0,
ERROR = 1,
TIMEOUT = 2
};
disk_driver();
disk_driver(unsigned int emmc_registers_base_address);
~disk_driver();
int read(unsigned int sector_offset, byte*& buffer);
int write(unsigned int sector_offset, const byte*& buffer);
private:
enum Register : int {
ARG2 = 0,
BLKSIZECNT = 1,
ARG1 = 2,
CMDTM = 3,
RESP0 = 4,
RESP1 = 5,
RESP2 = 6,
RESP3 = 7,
DATA = 8,
STATUS = 9,
CONTROL0 = 10,
CONTROL1 = 11,
INTERRUPT = 12,
IRPT_MASK = 13,
IRPT_EN = 14,
CONTROL2 = 15,
FORCE_IRPT = 16,
BOOT_TIMEOUT = 17,
DBG_SEL = 18,
EXRDFIFO_CFG = 19,
EXRDFIFO_EN = 20,
TUNE_STEP = 21,
TUNE_STEPS_STD = 22,
TUNE_STEPS_DDR = 23,
SPI_INT_SPT = 24,
SLOTISR_VER = 25
};
enum STATUS_MASK : unsigned int {
READ_AVAILABLE = 0x00000800,
WRITE_AVAILABLE = 0x00000400,
DATA_INHIBIT = 0x00000002,
CMD_INHIBIT = 0x00000001
};
enum INTERRUPT_MASK : unsigned int {
ERROR = 0x017E8000,
DATA_TIMEOUT = 0x00100000,
CMD_TIMEOUT = 0x00010000,
READ_READY = 0x00000020,
WRITE_READY = 0x00000010,
DATA_DONE = 0x00000002,
CMD_DONE = 0x00000001
};
enum COMMAND_MASK : unsigned int {
ERROR = 0xfff9c004,
NEED_APP = 0x80000000,
GO_IDLE = 0x00000000,
ALL_SEND_CID = 0x02010000,
SEND_REL_ADDR = 0x03020000,
CARD_SELECT = 0x07030000,
SEND_IF_COND = 0x08020000,
STOP_TRANS = 0x0C030000,
READ_SINGLE = 0x11220010,
READ_MULTI = 0x12220032,
SET_BLOCKCNT = 0x17020000,
WRITE_SINGLE = 0x18220000,
WRITE_MULTI = 0x19220022,
APP_CMD = 0x37000000,
SET_BUS_WIDTH = (0x06020000 | NEED_APP),
SEND_OP_COND = (0x29020000 | NEED_APP),
SEND_SCR = (0x33220010 | NEED_APP)
};
const unsigned int bytes_per_sector = 512;
const unsigned int bytes_per_int = 4;
const unsigned int ints_per_sector = bytes_per_sector / bytes_per_int;
unsigned int* volatile registers;
Status issue_cmd(COMMAND_MASK cmd, unsigned int arg, unsigned int& response);
Status wait_for_cmd_done();
Status wait_for_cmd_rdy();
Status wait_for_read_rdy();
bool any_errors();
bool can_read();
bool can_write();
bool can_issue_cmd();
bool cmd_done();
};
#endif

64
src/devices/mmio.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef KERNEL_MMIO_H
#define KERNEL_MMIO_H
#include <cstdint>
enum class MMIOOffset : uint64_t
{
/**
* @brief Base address for GPIO registers
*/
GPIO_BASE = 0x200000,
// Controls actuation of pull up/down to ALL GPIO pins.
GPPUD = (GPIO_BASE + 0x94),
// Controls actuation of pull up/down for specific GPIO pin.
GPPUDCLK0 = (GPIO_BASE + 0x98),
/**
* @brief Base address for UART0 registers.
*/
UART0_BASE = (GPIO_BASE + 0x1000), // for raspi4 0xFE201000, raspi2 & 3 0x3F201000, and 0x20201000 for raspi1
INTR_OFFSET = 0xB000,
INTR_BASIC_PENDING = INTR_OFFSET + 0x200,
INTR_IRQ_PENDING_1 = INTR_OFFSET + 0x204,
INTR_IRQ_PENDING_2 = INTR_OFFSET + 0x208,
INTR_FIQ_CTRL = INTR_OFFSET + 0x20C,
INTR_IRQ_ENABLE_1 = INTR_OFFSET + 0x210,
INTR_IRQ_ENABLE_2 = INTR_OFFSET + 0x214,
INTR_IRQ_ENABLE_BASE = INTR_OFFSET + 0x218,
INTR_IRQ_DISABLE_BASE = INTR_OFFSET + 0x224,
INTR_IRQ_DISABLE_1 = INTR_OFFSET + 0x21C,
INTR_IRQ_DISABLE_2 = INTR_OFFSET + 0x220,
MBOX_BASE = 0xB880,
MBOX_READ = (MBOX_BASE + 0x00),
MBOX_STATUS = (MBOX_BASE + 0x18),
MBOX_WRITE = (MBOX_BASE + 0x20)
};
/**
* @brief Write to a 32-bit MMIO register
*
* @param reg pointer to the register to write to
* @param data the data to write to the specified register
*/
static inline void mmio_write(void *reg, uint32_t data)
{
*(volatile uint32_t *)(reg + 0xFFFFFF803F000000) = data;
}
/**
* @brief Read from a 32-bit MMIO register
*
* @param reg pointer to the register to read from
* @return uint32_t the data read from the specified register
*/
static inline uint32_t mmio_read(void *reg)
{
return *(volatile uint32_t *)(reg + 0xFFFFFF803F000000);
}
#endif

31
src/devices/timer.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "timer.h"
#include "kernel.h"
#include "util/log.h"
volatile unsigned int *const kernel::devices::SystemTimer::registers = (unsigned int *)0xFFFFFF803F003000;
kernel::devices::SystemTimer::SystemTimer()
{
this->delta = 50;
reset();
}
kernel::devices::SystemTimer::SystemTimer(unsigned int delta)
{
this->delta = delta;
reset();
}
void kernel::devices::SystemTimer::handleInterrupt(int src)
{
reset();
// kernelLog(LogLevel::DEBUG, "Timer interrupt: %i", registers[CLO]);
kernel::kernel.switchTask();
}
void kernel::devices::SystemTimer::reset()
{
unsigned int currentTime = registers[CLO];
registers[C1] = currentTime + (delta * 1000);
registers[CS] = 0xF;
}

41
src/devices/timer.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef KERNEL_TIMER_H
#define KERNEL_TIMER_H
#include "irq/interrupthandler.h"
namespace kernel::devices
{
class SystemTimer : public kernel::interrupt::InterruptHandler
{
public:
SystemTimer();
SystemTimer(unsigned int delta);
void handleInterrupt(int src);
private:
enum TimerRegisters {
CS = 0,
CLO,
CHI,
C0,
C1,
C2,
C3
};
static volatile unsigned int *const registers;
unsigned int delta;
void reset();
};
} // namespace kernel::devices
#endif

323
src/devices/uart.cpp Normal file
View File

@@ -0,0 +1,323 @@
#include "uart.h"
#include "mmio.h"
#include "util/log.h"
#include "util/string.h"
#include <stdint.h>
namespace kernel::devices
{
enum UARTRegisters
{
/**
* @brief Data Register
*/
DR = 0,
/**
* @brief Receive status register/error clear register
*/
RSRECR = 1,
/**
* @brief Flag register
*/
FR = 6,
/**
* @brief Not in use
*/
ILPR = 8,
/**
* @brief Integer baud rate divisor
*/
IBRD = 9,
/**
* @brief Fractional baud rate divisor
*/
FBRD = 10,
/**
* @brief Line control register
*/
LCRH = 11,
/**
* @brief Control register
*/
CR = 12,
/**
* @brief Interrupt FIFO level select register
*/
IFLS = 13,
/**
* @brief Interrupt mask set/clear register
*/
IMSC = 14,
/**
* @brief Raw interrupt status register
*/
RIS = 15,
/**
* @brief Masked interrupt status register
*/
MIS = 16,
/**
* @brief Interrupt clear register
*/
ICR = 17,
/**
* @brief DMA control register
*/
DMACR = 18,
/**
* @brief Test control register
*/
ITCR = 32,
/**
* @brief Integration test input register
*/
ITIP = 33,
/**
* @brief Integration test output register
*/
ITOP = 34,
/**
* @brief Test data register
*/
IDR = 35
};
volatile unsigned int __attribute__((aligned(16))) mbox[9] = {
9 * 4, 0, 0x38002, 12, 8, 2, 3000000, 0, 0};
// Loop <delay> times in a way that the compiler won't optimize away
static inline void delay(int32_t count)
{
/*asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n"
: "=r"(count) : [count] "0"(count) : "cc");*/
while (count > 0)
{
count--;
}
}
UART::UART()
: registers(nullptr), bufferIndex(0)
{
memset(buffer, bufferSize, 0);
}
UART::UART(void *mmio_offset)
: registers((uint32_t *)mmio_offset), bufferIndex(0)
{
memset(buffer, bufferSize, 0);
/*int raspi = 3;
// Disable UART0.
registers[CR] = 0;
// Setup the GPIO pin 14 && 15.
// Disable pull up/down for all GPIO pins & delay for 150 cycles.
mmio_write((void *)MMIOOffset::GPPUD, 0x00000000);
delay(150);
// Disable pull up/down for pin 14,15 & delay for 150 cycles.
mmio_write((void *)MMIOOffset::GPPUDCLK0, (1 << 14) | (1 << 15));
delay(150);
// Write 0 to GPPUDCLK0 to make it take effect.
mmio_write((void *)MMIOOffset::GPPUDCLK0, 0x00000000);
// Clear pending interrupts.
registers[ICR] = 0x7FF;
// Set integer & fractional part of baud rate.
// Divider = UART_CLOCK/(16 * Baud)
// Fraction part register = (Fractional part * 64) + 0.5
// Baud = 115200.
// For Raspi3 and 4 the UART_CLOCK is system-clock dependent by default.
// Set it to 3Mhz so that we can consistently set the baud rate
if (raspi >= 3)
{
// UART_CLOCK = 30000000;
unsigned int r = (((unsigned int)(&mbox) & ~0xF) | 8);
// wait until we can talk to the VC
while (mmio_read((void *)MMIOOffset::MBOX_STATUS) & 0x80000000)
{
}
// send our message to property channel and wait for the response
mmio_write((void *)MMIOOffset::MBOX_WRITE, r);
while ((mmio_read((void *)MMIOOffset::MBOX_STATUS) & 0x40000000) || mmio_read((void *)MMIOOffset::MBOX_READ) != r)
{
}
}
// Divider = 3000000 / (16 * 115200) = 1.627 = ~1.
registers[IBRD] = 1;
// Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40.
registers[FBRD] = 40;
// Enable FIFO & 8 bit data transmission (1 stop bit, no parity).
registers[LCRH] = (1 << 4) | (1 << 5) | (1 << 6);
// Mask all interrupts.
*/
registers[IMSC] = Receive | /*Transmit |*/ ReceiveTimeout |
Framing | Parity | Break | Overrun
/*0*/
;
// Enable UART0, receive & transfer part of UART.
// registers[CR] = (1 << 0) | (1 << 8) | (1 << 9);
}
UART &UART::operator<<(char c)
{
while (registers[UARTRegisters::FR] & (1 << 5))
;
registers[UARTRegisters::DR] = c;
return *this;
}
UART &UART::operator<<(const char *str)
{
for (const char *s = str; *s != '\0'; s++)
{
*this << *s;
}
return *this;
}
UART &UART::operator>>(char &c)
{
while (registers[FR] & (1 << 4))
{
}
c = registers[DR];
}
kernel::fs::FileContext *UART::open(Mode mode)
{
return new UARTContext(*this);
}
void UART::close(int id)
{
}
void UART::handleInterrupt(int src)
{
int status = registers[UARTRegisters::MIS];
if (status & Receive)
{
// kernelLog(LogLevel::DEBUG, "UART receive interrupt");
readByte();
}
if (status & Transmit)
{
// kernelLog(LogLevel::DEBUG, "UART transmit interrupt");
}
if (status & CTSM)
{
kernelLog(LogLevel::DEBUG, "UART nUARTCTS modem interrupt");
}
if (status & ReceiveTimeout)
{
// kernelLog(LogLevel::DEBUG, "UART receive timeout interrupt");
readByte();
}
if (status & Framing)
{
kernelLog(LogLevel::DEBUG, "UART framing interrupt");
}
if (status & Parity)
{
kernelLog(LogLevel::DEBUG, "UART parity interrupt");
}
if (status & Break)
{
kernelLog(LogLevel::DEBUG, "UART break interrupt");
}
if (status & Overrun)
{
kernelLog(LogLevel::DEBUG, "UART overrun interrupt");
}
registers[UARTRegisters::ICR] = /*0x7F2*/ status;
}
void UART::readByte()
{
while (!(registers[UARTRegisters::FR] & (1 << 4)))
{
buffer[bufferIndex] = registers[UARTRegisters::DR];
bufferIndex++;
if (bufferIndex >= bufferSize)
{
bufferIndex = 0;
}
}
}
}
kernel::devices::UART::UARTContext::UARTContext(UART &uart)
: uart(uart), pos(uart.bufferIndex)
{
}
kernel::devices::UART::UARTContext::~UARTContext()
{
}
int kernel::devices::UART::UARTContext::read(void *buffer, int n)
{
char *s = (char *)buffer;
int c = 0;
while (pos != uart.bufferIndex && c < n)
{
s[c] = uart.buffer[pos];
c++;
pos++;
if (pos >= uart.bufferSize)
{
pos = 0;
}
}
return c;
}
int kernel::devices::UART::UARTContext::write(const void *buffer, int n)
{
for (int i = 0; i < n; i++)
{
uart << ((char *)buffer)[i];
if (((char *)buffer)[i] == '\n')
{
uart << '\r';
}
}
return n;
}
kernel::fs::FileContext *kernel::devices::UART::UARTContext::copy()
{
UARTContext *f = new UARTContext(uart);
if (f != nullptr)
{
f->pos = pos;
}
return f;
}

77
src/devices/uart.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef KERNEL_UART_H
#define KERNEL_UART_H
#include "util/charstream.h"
#include "irq/interrupthandler.h"
#include "containers/binary_search_tree.h"
#include "fs/filecontext.h"
#include <stdint.h>
namespace kernel::devices
{
class UART : public CharStream, public kernel::interrupt::InterruptHandler
{
public:
UART();
UART(void *mmio_offset);
UART &operator<<(char c);
UART &operator<<(const char *str);
UART &operator>>(char &c);
kernel::fs::FileContext *open(Mode mode);
void close(int id);
void handleInterrupt(int src);
private:
enum InterruptBits
{
CTSM = (1 << 1),
Receive = (1 << 4),
Transmit = (1 << 5),
ReceiveTimeout = (1 << 6),
Framing = (1 << 7),
Parity = (1 << 8),
Break = (1 << 9),
Overrun = (1 << 10)
};
class UARTContext : public kernel::fs::FileContext
{
public:
UARTContext(UART &uart);
~UARTContext();
int read(void *buffer, int n);
int write(const void *buffer, int n);
kernel::fs::FileContext *copy();
private:
UART &uart;
int pos;
};
static const int bufferSize = 4096;
uint32_t *volatile registers;
char buffer[bufferSize];
int bufferIndex;
void readByte();
};
}
#endif

View File

@@ -0,0 +1,82 @@
#include "disk_interface.h"
/*#include <filesystem>
#include <fstream>
#include <iostream>*/
#include "containers/string.h"
#include "util/log.h"
DiskInterface::DiskInterface(string disk_filepath, int bytes_per_sector)
: disk_filepath(disk_filepath), bytes_per_sector(bytes_per_sector)
{
/*std::ifstream file(std::string(string::strdup(disk_filepath.c_str())), std::ios::binary);
if (!file.is_open())
{
throw std::runtime_error("Error opening disk file");
}
file.seekg(0, std::ios::end);
std::streampos file_size = file.tellg();
file.seekg(0, std::ios::beg);
size = static_cast<unsigned int>(file_size);
disk = new byte[size];
file.read(reinterpret_cast<char *>(disk), size);
if (!file)
{
throw std::runtime_error("Error reading disk file");
}
file.close();*/
}
DiskInterface::DiskInterface(void *ramfs, int bytes_per_sector)
: disk((byte *)ramfs), bytes_per_sector(bytes_per_sector), disk_filepath("")
{
}
DiskInterface ::~DiskInterface()
{
write_to_disk();
delete[] disk;
}
// must return a copy of the data cuz the buffer may be modified
byte *DiskInterface::read(int sector_index)
{
byte *buffer = new byte[bytes_per_sector];
int offset = bytes_per_sector * sector_index;
for (int i = 0; i < bytes_per_sector; i++)
buffer[i] = disk[i + offset];
return buffer;
}
void DiskInterface::write(int sector_index, const byte *buffer)
{
int offset = bytes_per_sector * sector_index;
for (int i = 0; i < bytes_per_sector; i++)
disk[i + offset] = buffer[i];
return;
}
void DiskInterface::write_to_disk()
{
/*disk_filepath = "disk_modified.img"; // to avoid disk containmination
std::ofstream file(std::string(string::strdup(disk_filepath.c_str())), std::ios::binary);
if (!file.is_open())
{
throw std::runtime_error("Error opening disk file for writing");
}
file.write(reinterpret_cast<char *>(disk), size);
if (!file)
{
throw std::runtime_error("Error writing to disk file");
}
file.close();*/
}

View File

@@ -0,0 +1,25 @@
#ifndef DISK_INTERFACE_H
#define DISK_INTERFACE_H
#include "../helpers.h"
class DiskInterface
{
public:
DiskInterface(string disk_filepath, int bytes_per_sector);
DiskInterface(void *ramfs, int bytes_per_sector);
~DiskInterface();
byte *read(int sector_index);
void write(int sector_index, const byte *buffer);
private:
string disk_filepath;
int bytes_per_sector;
byte *disk;
unsigned int size;
void write_to_disk();
};
#endif

186
src/fs/fat32/entry.cpp Normal file
View File

@@ -0,0 +1,186 @@
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/14/2024
*/
#include "fat32.h"
/* Creates an entry in memory for the FAT32 entry at the given byte offset to keep track of meta-data */
FAT32::Entry::Entry(FAT32 *fs, Entry *parent_dir, int byte_offset)
: byte_offset(byte_offset), parent_dir(parent_dir), fs(fs)
{
// figure out what type of entry this is
// then parse accordingly
type = parse_attribute(byte_offset);
switch (type)
{
case ROOT:
parse_root_entry();
break;
case DIRECTORY:
case FILE:
parse_dir_entry(); // similar enough to do in a single function
break;
case LONG_NAME:
parse_long_name();
break;
default: // must be EMPTY
clear_entry(byte_offset);
break;
}
}
// TODO
FAT32::Entry::~Entry()
{
// will segfault
for (int i = 0; i < sub_dirs.size(); i++)
delete sub_dirs[i];
for (int i = 0; i < long_name_entries.size(); i++)
delete sub_dirs[i];
}
/* Reads the byte data of the entry at the specifed sector offset, will fail by returning null
if: entry isn't a file (e.g. a directory), the sector offset is bigger that the number of sectors
allocated to the file. */
byte *FAT32::Entry::read_data(int sector_offset)
{
if (type != FILE)
return nullptr; // entries not of type FILE don't have readable data
int target_cluster = sector_offset / fs->sectors_per_cluster;
if (target_cluster >= (int)fat_allocations.size())
return nullptr; // can't read data that isn't allocated to the entry
int target_cluster_offset = fat_entry_to_cluster_offset(fat_allocations[target_cluster]);
int target_sector_offset = (target_cluster_offset * fs->sectors_per_cluster) + (sector_offset % fs->sectors_per_cluster);
return fs->di->read(target_sector_offset);
}
/* Writes the given byte data to the given sector offset (using zero-indexing) into the provided data
buffer, will fail if: the entry isn't a file (e.g. a directory), there isn't enough space left on disk
to allocate more clusters if required. */
int FAT32::Entry::write_data(int sector_offset, const byte *data)
{
if (type != FILE)
return failure; // entries not of type FILE can't be written do
int target_cluster = sector_offset / fs->sectors_per_cluster; // cluster the sector is in
int num_clusters = (int)fat_allocations.size();
if (target_cluster >= num_clusters)
{
int num_additional_clusters = (target_cluster + 1) - num_clusters; // need to allocate more clusters
if (!allocate_more_clusters(num_additional_clusters))
return failure; // couldn't allocate more clusters, data region must be full
}
int target_cluster_offset = fat_entry_to_cluster_offset(fat_allocations[target_cluster]);
int target_sector_offset = (target_cluster_offset * fs->sectors_per_cluster) + (sector_offset % fs->sectors_per_cluster);
fs->di->write(target_sector_offset, data); // write data
unsigned int required_size = (sector_offset + 1) * fs->bytes_per_sector; // update file size if needed
if (file_size_bytes < (int)required_size)
{
int data_size_bytes = 0;
for (int i = fs->bytes_per_sector - 1; i >= 0; i--)
{ // data may not fill a sector
if (data[i] != 0)
{
data_size_bytes = i + 1;
break;
}
}
file_size_bytes = sector_offset * fs->bytes_per_sector + data_size_bytes;
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
int_to_bytes(required_size, buffer, 28 + sector_byte_offset); // update file size on disk entry too
fs->di->write(sector_offset, buffer);
delete[] buffer;
}
return success;
}
/* Finds the sub directory with the matching given name, fails by returning null if no match is found */
FAT32::Entry *FAT32::Entry::find_sub_dir(string name)
{
for (int i = 0; i < sub_dirs.size(); i++)
{
Entry *dir = sub_dirs[i];
if (dir->name == name)
return dir;
}
return nullptr; // couldn't find sub dir with matching name
}
/* Used for creating a new directory entry in memory as well as on disk with the given name under the given
parent directory. */
int FAT32::Entry::new_dir_entry(Entry *par_dir, string name, EntryAttribute type)
{
this->parent_dir = par_dir;
this->name = name;
this->type = type;
this->fs = par_dir->fs;
this->order = -1;
int name_size = name.size();
int num_long_name_entries = (name_size / 13) + 1; // conpute the number of required long file name entries for the name
if (name_size % 13 == 0)
num_long_name_entries--; // edge case where the name will fill the last long file name entry - usually it will need an 'end' char but not in this case
int first_entry_byte_offset = parent_dir->find_empty_entries(num_long_name_entries + 1); // find a continous region to store long name entries on disk
if (first_entry_byte_offset == failure) // not enough space in data region for long name entries (at the very least not a continous space)
return failure;
this->byte_offset = first_entry_byte_offset + (num_long_name_entries * fs->bytes_per_dir_entry);
if (!allocate_more_clusters(1)) // allocate a single cluster
return failure;
if (type == FILE)
this->file_size_bytes = 0;
else
this->file_size_bytes = fs->bytes_per_cluster; // idk if directory entries actually need this field
for (int i = 0; i < num_long_name_entries; i++)
{ // create long name entries in memory
string name_part = "";
int start_index = i * 13;
int end_index = (i * 13) + 13;
if (end_index >= (int)name.size())
name_part = name.substr(start_index);
else
name_part = name.substr(start_index, end_index);
Entry *long_name = new Entry();
long_name->fs = fs;
long_name->parent_dir = this;
long_name->name = name_part;
long_name->extension = "";
long_name->type = LONG_NAME;
long_name->byte_offset = first_entry_byte_offset + ((num_long_name_entries - i - 1) * fs->bytes_per_dir_entry);
long_name->order = i + 1;
long_name->last = false;
long_name_entries.push_back(long_name);
}
long_name_entries.back()->last = true;
write_dir_entry(); // write the new directory and all its long name entries to disk
par_dir->sub_dirs.push_back(this);
return success;
}

View File

@@ -0,0 +1,773 @@
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/14/2024
*/
#include "fat32.h"
/* Parses this entry's attribute from disk to find out what type of entry this is. */
FAT32::EntryAttribute FAT32::Entry::parse_attribute(int byte_offset)
{
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
FAT32::EntryAttribute type = EMPTY;
unsigned char attribute = buffer[11 + sector_byte_offset];
attribute &= 0x3F; // upper 2 bits are reserved, so we must mask those 2 bits when reading the value
switch (attribute)
{
case 0x08:
type = ROOT;
break;
case 0x10:
type = DIRECTORY;
break;
case 0x20:
type = FILE;
break;
case 0x0F:
type = LONG_NAME;
break;
default:
type = EMPTY;
break;
}
delete[] buffer;
return type;
}
/* Parses the short file name of this entry from disk. */
string FAT32::Entry::parse_short_file_name(int byte_offset)
{
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
string name = "";
for (int i = 0; i < 8; i++)
{
char c = buffer[i + sector_byte_offset];
if (c != ' ')
name += c;
}
delete[] buffer;
return name;
}
/* Treats this entry as the root directory while parsing its meta-data from disk. */
void FAT32::Entry::parse_root_entry()
{
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
name = "";
extension = "";
find_cluster_allocations((unsigned int)fs->reserved_fat_entries); // just used as an offset, dont read into it too much
file_size_bytes = bytes_to_int(buffer, 28 + sector_byte_offset);
delete[] buffer;
order = -1;
find_sub_dir_entries(); // start of the recursive search of the file system
}
/* Treats this entry as directory while parsing its meta-data from disk - also used
for entries of type file since their actual disk entry structure is identical. */
void FAT32::Entry::parse_dir_entry()
{
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
name = parse_short_file_name(byte_offset);
extension = "";
for (int i = 8; i < 11; i++)
{
char c = buffer[i + sector_byte_offset];
if (c != ' ')
extension += c;
}
unsigned short first_cluster_high = bytes_to_short(buffer, 20 + sector_byte_offset); // first cluster num is split up for some reason
unsigned short first_cluster_low = bytes_to_short(buffer, 26 + sector_byte_offset);
unsigned int first_cluster_offset = (static_cast<unsigned int>(first_cluster_high) << 16) | first_cluster_low;
find_cluster_allocations(first_cluster_offset);
file_size_bytes = bytes_to_int(buffer, 28 + sector_byte_offset);
delete[] buffer;
find_long_name_entries(); // also updates this entries file name if it finds any long file name entries
order = -1;
if (type == DIRECTORY)
find_sub_dir_entries(); // keep searching
}
/* Treats this entry as a long file name while parsing its meta-data from disk. */
void FAT32::Entry::parse_long_name()
{
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
order = buffer[0 + sector_byte_offset] & 0x1F;
last = false;
name = "";
// name pieces are split up for some reason
int offset = 1;
for (int i = 0; i < 10; i += 2)
{
byte first = buffer[i + offset + sector_byte_offset];
byte second = buffer[i + 1 + offset + sector_byte_offset];
wchar_t unicode = chars_to_unicode(first, second);
if (unicode != L'\xFFFF')
name += first;
else
break;
}
offset = 14;
for (int i = 0; i < 12; i += 2)
{
byte first = buffer[i + offset + sector_byte_offset];
byte second = buffer[i + 1 + offset + sector_byte_offset];
wchar_t unicode = chars_to_unicode(first, second);
if (unicode != L'\xFFFF')
name += first;
else
break;
}
offset = 28;
for (int i = 0; i < 4; i += 2)
{
byte first = buffer[i + offset + sector_byte_offset];
byte second = buffer[i + 1 + offset + sector_byte_offset];
wchar_t unicode = chars_to_unicode(first, second);
if (unicode != L'\xFFFF')
name += first;
else
break;
}
extension = "";
file_size_bytes = -1;
delete[] buffer;
}
/* Finds all the clusters allocated to this entry given its first cluster offset in the FAT */
void FAT32::Entry::find_cluster_allocations(unsigned int first_fat_entry_offset)
{
if (first_fat_entry_offset == 0)
return;
unsigned int current_fat_entry_offset = first_fat_entry_offset;
int current_fat_sector_offset = fs->reserved_sectors + (first_fat_entry_offset * fs->bytes_per_fat_entry) / fs->bytes_per_sector;
int current_fat_secotor_int_offset = (current_fat_entry_offset * fs->bytes_per_fat_entry) % fs->bytes_per_sector;
byte *buffer = fs->di->read(current_fat_sector_offset);
FatEntryType type = check_fat_entry_type(bytes_to_int(buffer, current_fat_secotor_int_offset));
while (type == ALLOCATED)
{
fat_allocations.push_back(current_fat_entry_offset);
unsigned int next_fat_entry_offset = bytes_to_int(buffer, current_fat_secotor_int_offset);
int next_fat_sector_offset = fs->reserved_sectors + (next_fat_entry_offset * fs->bytes_per_fat_entry) / fs->bytes_per_sector;
if (check_fat_entry_type(next_fat_entry_offset) == ALLOCATED && next_fat_sector_offset != current_fat_sector_offset)
{ // update the sector being read from if the next cluster is outside the current sector
delete[] buffer;
buffer = fs->di->read(next_fat_sector_offset);
}
current_fat_entry_offset = next_fat_entry_offset;
current_fat_sector_offset = next_fat_sector_offset;
current_fat_secotor_int_offset = (current_fat_entry_offset * fs->bytes_per_fat_entry) % fs->bytes_per_sector;
type = check_fat_entry_type(current_fat_entry_offset);
}
fat_allocations.push_back(current_fat_entry_offset);
delete[] buffer;
}
/* Given the raw value of the fat cluster entry, returns the type - see offical docs for explaintion on the values. */
FAT32::FatEntryType FAT32::Entry::check_fat_entry_type(unsigned int val)
{
unsigned int snipped_val = val & 0xFFFFFFF;
if (val == 0xFFFFFFFF)
return END;
switch (snipped_val)
{
case 0x0000000:
return FREE;
case 0xFFFFFFE:
return DEFECTIVE;
default:
unsigned int total_num_clusters = fs->total_sectors / fs->sectors_per_cluster;
if (val < total_num_clusters)
return ALLOCATED;
else
return RESERVED;
}
}
/* Finds the long file name entries associated with this entry, doesn't search beyond the current cluster, needs
refactoring if you want to support file/dir names longer than 208 characters long. */
void FAT32::Entry::find_long_name_entries()
{
int current_cluster_start_byte_offset = byte_offset / fs->bytes_per_cluster;
int search_byte_offset = byte_offset - fs->bytes_per_dir_entry;
while (search_byte_offset >= current_cluster_start_byte_offset)
{ // only search within current cluster
if (parse_attribute(search_byte_offset) == LONG_NAME)
{
Entry *long_name = new Entry(fs, this, search_byte_offset);
long_name_entries.push_back(long_name);
}
else
{
break;
}
search_byte_offset -= fs->bytes_per_dir_entry;
}
if (!long_name_entries.empty())
{
name = "";
for (int i = 0; i < (int)long_name_entries.size(); i++)
{ // construct the full name of this entry from the long file names
name += long_name_entries[i]->name;
}
long_name_entries.back()->last = true; // mark the last one
}
return;
}
/* Searches through all of this entries clusters and finds all of its sub-directoies and creates memory entries for each, passes over long
file names as they are owned by sub-directors - only call this function once per entry. */
void FAT32::Entry::find_sub_dir_entries()
{
for (int i = 0; i < (int)fat_allocations.size(); i++)
{
int search_cluster_offset = fat_entry_to_cluster_offset(fat_allocations[i]);
int serach_cluster_start_byte_offset = search_cluster_offset * fs->bytes_per_cluster;
int serach_cluster_end_byte_offset = serach_cluster_start_byte_offset + fs->bytes_per_cluster;
int search_byte_offset = serach_cluster_start_byte_offset;
while (search_byte_offset < serach_cluster_end_byte_offset)
{
EntryAttribute type = parse_attribute(search_byte_offset);
string name = parse_short_file_name(search_byte_offset);
bool is_dir_or_file = (type == DIRECTORY || type == FILE);
bool is_dot_or_dotdot = (name == "." || name == "..");
if (is_dir_or_file && !is_dot_or_dotdot)
{ // make sure they aren't the two required copy-entries
Entry *sub_dir = new Entry(fs, this, search_byte_offset);
sub_dirs.push_back(sub_dir);
}
search_byte_offset += fs->bytes_per_dir_entry;
}
}
}
/* Converts the fat entry value to its corresponding cluster offset in the data region. */
int FAT32::Entry::fat_entry_to_cluster_offset(unsigned int fat_entry)
{
return (fat_entry - fs->reserved_fat_entries) + fs->data_region_start_cluster_offset;
}
/* Allocats a given number of new clusters to this entry, will fail if: there are no remaining
free clusters in the data region. */
int FAT32::Entry::allocate_more_clusters(int num_additional_clusters)
{
if (num_additional_clusters <= 0)
return failure; // yeah just don't do that
int fat_start_byte_offset = fs->reserved_sectors * fs->bytes_per_sector;
if (fat_allocations.empty())
{ // allocate first cluster if none are allocated yet
if (!initial_cluster())
return failure;
num_additional_clusters--;
}
int current_entry_offset = fat_allocations[(int)fat_allocations.size() - 1];
int current_entry_byte_offset = fat_start_byte_offset + current_entry_offset * fs->bytes_per_fat_entry;
int current_entry_sector_offset = current_entry_byte_offset / fs->bytes_per_sector;
int current_entry_sector_byte_offset = current_entry_byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(current_entry_sector_offset);
bool no_more_clusters = false;
for (int i = 0; i < num_additional_clusters; i++)
{
int next_free_offset = find_free_cluster();
if (next_free_offset == failure)
{
no_more_clusters = true;
break;
}
int next_entry_offset = next_free_offset;
int next_entry_byte_offset = fat_start_byte_offset + next_entry_offset * fs->bytes_per_fat_entry;
int next_entry_sector_offset = next_entry_byte_offset / fs->bytes_per_sector;
int next_entry_sector_byte_offset = next_entry_byte_offset % fs->bytes_per_sector;
int_to_bytes(next_entry_offset, buffer, current_entry_sector_byte_offset);
fs->di->write(current_entry_sector_offset, buffer);
delete[] buffer;
buffer = fs->di->read(next_entry_sector_offset);
current_entry_offset = next_entry_offset;
current_entry_byte_offset = next_entry_byte_offset;
current_entry_sector_offset = next_entry_sector_offset;
current_entry_sector_byte_offset = next_entry_sector_byte_offset;
if (type == DIRECTORY)
init_dir_cluster(fat_entry_to_cluster_offset(current_entry_offset)); // if this entry is a directory, each cluster allocated to it needs some setup done to it, see init_dir_cluster() for details
fat_allocations.push_back(current_entry_offset);
}
unsigned int end_marker = 0xFFFFFFFF; // mark last fat entry as the end
int_to_bytes(end_marker, buffer, current_entry_sector_byte_offset);
fs->di->write(current_entry_sector_offset, buffer);
delete[] buffer;
if (no_more_clusters)
return failure; // no more space in data region
return success;
}
int FAT32::Entry::find_free_cluster()
{
int fat_start_sector_offset = fs->reserved_sectors;
int fat_start_byte_offset = fat_start_sector_offset * fs->bytes_per_sector;
int clusters_per_fat = fs->sectors_per_fat / fs->sectors_per_cluster;
int current_entry_offset = 2;
int current_entry_byte_offset = fat_start_byte_offset + current_entry_offset * fs->bytes_per_fat_entry;
int current_entry_sector_offset = current_entry_byte_offset / fs->bytes_per_sector;
int current_entry_sector_byte_offset = current_entry_byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(current_entry_sector_offset);
while (current_entry_offset < clusters_per_fat)
{
unsigned int entry_val = bytes_to_int(buffer, current_entry_sector_byte_offset);
FatEntryType type = check_fat_entry_type(entry_val);
if (type == FREE)
{
delete[] buffer;
return current_entry_offset;
}
int next_entry_offset = current_entry_offset + 1;
int next_entry_byte_offset = fat_start_byte_offset + next_entry_offset * fs->bytes_per_fat_entry;
int next_entry_sector_offset = next_entry_byte_offset / fs->bytes_per_sector;
int next_entry_sector_byte_offset = next_entry_byte_offset % fs->bytes_per_sector;
if (next_entry_sector_offset != current_entry_sector_offset)
{
delete[] buffer;
buffer = fs->di->read(next_entry_sector_offset);
}
current_entry_offset = next_entry_offset;
current_entry_byte_offset = next_entry_byte_offset;
current_entry_sector_offset = next_entry_sector_offset;
current_entry_sector_byte_offset = next_entry_sector_byte_offset;
}
delete[] buffer;
return failure;
}
/* Finds a continuous space big enough for the given number of entries within the clusters
allocated to this entry, will allocate more if it can't find a space, will fail if: it needs
to allocate more clusters but there is no more free clusters in the data region. */
int FAT32::Entry::find_empty_entries(int num_entries)
{
int current_byte_offset;
if (parent_dir == nullptr) // root dir
current_byte_offset = parent_dir->byte_offset;
else
current_byte_offset = fat_entry_to_cluster_offset(fat_allocations[0]) * fs->bytes_per_cluster;
int current_cluster_offset = byte_offset / fs->bytes_per_cluster;
int num_empty_found = 0;
int empty_start_offset = 0;
int cluster_num = 1;
int the_sky_is_blue = true;
while (the_sky_is_blue)
{
EntryAttribute type = parse_attribute(current_byte_offset);
if (type == EMPTY)
{
if (num_empty_found == 0)
empty_start_offset = current_byte_offset;
num_empty_found++;
if (num_empty_found == num_entries)
break;
}
else
{
num_empty_found = 0;
}
int next_byte_offset = current_byte_offset += fs->bytes_per_dir_entry;
int next_cluster_offset = byte_offset / fs->bytes_per_cluster;
if (next_cluster_offset != current_cluster_offset)
{
cluster_num++;
if (cluster_num > (int)parent_dir->fat_allocations.size())
{ // can't find a space in the current cluster and this is the last cluster, so allocate another one
if (!parent_dir->allocate_more_clusters(1))
return failure; // out of space in data region
}
next_byte_offset = fat_entry_to_cluster_offset(parent_dir->fat_allocations.back()) + (2 * fs->bytes_per_dir_entry); // skip first two entries, we know they aren't empty
next_cluster_offset = byte_offset / fs->bytes_per_cluster;
}
current_byte_offset = next_byte_offset;
current_cluster_offset = next_cluster_offset;
}
return empty_start_offset;
}
/* Intializes a given cluster of this directory entry with two base entries - '.' which
is a copy of this entry, and '..' which is a copy of this entry's parent entry. */
void FAT32::Entry::init_dir_cluster(int cluster_offset)
{
int cluster_byte_offset = cluster_offset * fs->bytes_per_cluster;
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
clear_entry(cluster_byte_offset);
clear_entry(cluster_byte_offset + fs->bytes_per_dir_entry);
byte *buffer = fs->di->read(sector_offset);
buffer[0 + sector_byte_offset] = '.';
buffer[11 + sector_byte_offset] = 0x10;
unsigned short first_cluster_high = (fat_allocations[0] >> 16) & 0xFFFF;
unsigned short first_cluster_low = fat_allocations[0] & 0xFFFF;
short_to_bytes(first_cluster_high, buffer, 20 + sector_byte_offset);
short_to_bytes(first_cluster_low, buffer, 26 + sector_byte_offset);
int_to_bytes(file_size_bytes, buffer, 28 + sector_byte_offset);
sector_byte_offset += fs->bytes_per_dir_entry;
buffer[0 + sector_byte_offset] = '.';
buffer[0 + sector_byte_offset] = '.';
buffer[11 + sector_byte_offset] = 0x10;
first_cluster_high = (parent_dir->fat_allocations[0] >> 16) & 0xFFFF;
first_cluster_low = parent_dir->fat_allocations[0] & 0xFFFF;
short_to_bytes(first_cluster_high, buffer, 20 + sector_byte_offset);
short_to_bytes(first_cluster_low, buffer, 26 + sector_byte_offset);
int_to_bytes(parent_dir->file_size_bytes, buffer, 28 + sector_byte_offset);
fs->di->write(sector_offset, buffer);
delete[] buffer;
}
/* Clears the entry specified by the given byte offset, removes any garbage data that may be there. */
void FAT32::Entry::clear_entry(int byte_offset)
{
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
for (int i = 0; i < fs->bytes_per_dir_entry; i++)
buffer[i + sector_byte_offset] = 0x0;
fs->di->write(sector_offset, buffer);
delete[] buffer;
}
/* Allocates the first cluster of this entry, assumes that there are no clusters already allocated to this
entry, will fail if: there are no free clusters left in the data region. */
int FAT32::Entry::initial_cluster()
{
int fat_start_byte_offset = fs->reserved_sectors * fs->bytes_per_sector;
int current_entry_offset = find_free_cluster();
if (current_entry_offset == failure) // no free clusters left
return failure;
int current_entry_byte_offset = fat_start_byte_offset + current_entry_offset * fs->bytes_per_fat_entry;
int current_entry_sector_offset = current_entry_byte_offset / fs->bytes_per_sector;
int current_entry_sector_byte_offset = current_entry_byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(current_entry_sector_offset);
unsigned int end_marker = 0xFFFFFFFF;
int_to_bytes(end_marker, buffer, current_entry_sector_byte_offset);
fs->di->write(current_entry_sector_offset, buffer);
delete[] buffer;
fat_allocations.push_back(current_entry_offset);
if (type == DIRECTORY)
init_dir_cluster(fat_entry_to_cluster_offset(current_entry_offset)); // if directory, perform cluster setup
return success;
}
/* Writes the meta-data stored in this file/directory memory entry to the corresponding entry on disk. */
void FAT32::Entry::write_dir_entry()
{
clear_entry(byte_offset); // clear old data
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
string short_name = long_to_short_name(name, 8);
for (int i = 0; i < 8; i++)
{
buffer[i + sector_byte_offset] = short_name[i];
}
string short_ext = long_to_short_name(extension, 3);
for (int i = 0; i < 3; i++)
{
buffer[8 + i + sector_byte_offset] = short_ext[i];
}
if (type == FILE)
buffer[sector_byte_offset + 11] = 0x20;
else
buffer[sector_byte_offset + 11] = 0x10;
unsigned int first_cluster = fat_allocations[0];
unsigned short cluster_high = (first_cluster >> 16) & 0xFFFF;
unsigned short cluster_low = first_cluster & 0xFFFF;
short_to_bytes(cluster_high, buffer, 20 + sector_byte_offset);
short_to_bytes(cluster_low, buffer, 26 + sector_byte_offset);
int_to_bytes(file_size_bytes, buffer, 28 + sector_byte_offset);
fs->di->write(sector_offset, buffer);
delete[] buffer;
for (int i = 0; i < long_name_entries.size(); i++)
long_name_entries[i]->write_long_name_entry(); // also update long file name disk entries
}
/* Writes the meta-data stored in this long file name memory entry to the corresponding entry on disk,
this function probably doesn't calcuate the checksum properly and it probably the reason why it isn't
comforming to the FAT32 standard fully. */
void FAT32::Entry::write_long_name_entry()
{
clear_entry(byte_offset); // clear old data
int sector_offset = byte_offset / fs->bytes_per_sector;
int sector_byte_offset = byte_offset % fs->bytes_per_sector;
byte *buffer = fs->di->read(sector_offset);
if (last)
buffer[sector_byte_offset] = (unsigned char)order | 0x40;
else
buffer[sector_byte_offset] = (unsigned char)order;
bool done = false;
string name_part_1 = "";
int start_index = 0;
int end_index = 5;
if (end_index >= (int)name.size())
{
name_part_1 = name.substr(start_index);
done = true;
}
else
{
name_part_1 = name.substr(start_index, end_index);
}
string name_part_2 = "";
start_index = 5;
end_index = 11;
if (!done)
{
if (end_index >= (int)name.size())
{
name_part_2 = name.substr(start_index);
done = true;
}
else
{
name_part_2 = name.substr(start_index, end_index);
}
}
string name_part_3 = "";
start_index = 11;
end_index = 13;
if (!done)
{
if (end_index >= (int)name.size())
{
name_part_3 = name.substr(start_index);
}
else
{
name_part_3 = name.substr(start_index, end_index);
}
}
for (int i = 0; i < 5; i++)
{
if (i == (int)name_part_1.size())
{
buffer[sector_byte_offset + 1 + i * 2] = 0x0;
}
else if (i > name_part_1.size())
{
buffer[sector_byte_offset + 1 + i * 2] = 0xFF;
buffer[sector_byte_offset + 1 + i * 2 + 1] = 0xFF;
}
else
{
buffer[sector_byte_offset + 1 + i * 2] = name_part_1[i];
}
}
for (int i = 0; i < 6; i++)
{
if (i == (int)name_part_2.size())
{
buffer[sector_byte_offset + 14 + i * 2] = 0x0;
}
else if (i > name_part_2.size())
{
buffer[sector_byte_offset + 14 + i * 2] = 0xFF;
buffer[sector_byte_offset + 14 + i * 2 + 1] = 0xFF;
}
else
{
buffer[sector_byte_offset + 14 + i * 2] = name_part_2[i];
}
}
for (int i = 0; i < 2; i++)
{
if (i == name_part_3.size())
{
buffer[sector_byte_offset + 28 + i * 2] = 0x0;
}
else if (i > name_part_3.size())
{
buffer[sector_byte_offset + 28 + i * 2] = 0xFF;
buffer[sector_byte_offset + 28 + i * 2 + 1] = 0xFF;
}
else
{
buffer[sector_byte_offset + 28 + i * 2] = name_part_3[i];
}
}
buffer[sector_byte_offset + 11] = 0x0F;
unsigned char checksum = 0;
string short_name = long_to_short_name(parent_dir->name, 8);
short_name += long_to_short_name(extension, 3);
for (int i = 0; i < short_name.size(); i++)
{
char c = short_name[i];
checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + c; // calculate checksum
// TODO: this probably isnt correct
}
buffer[sector_byte_offset + 13] = checksum;
fs->di->write(sector_offset, buffer);
delete[] buffer;
}
/* Converts the given long file name to the short name format of a given length used by FAT32 file/directory disk entries. */
string FAT32::Entry::long_to_short_name(string long_name, int len)
{
string short_name = "";
for (int i = 0; i < len; i++)
{
char c = long_name[i];
if (c >= 'A' && c <= 'Z')
{
short_name += c;
}
else if (c >= 'a' && c <= 'z')
{
c -= 'a' - 'A';
short_name += c;
}
else if (
c == '$' || c == '%' || c == '\'' || c == '-' || c == '_' ||
c == '@' || c == '~' || c == '`' || c == '!' || c == '(' ||
c == ')' || c == '{' || c == '}' || c == '^' || c == '#' ||
c == '&' || c == '.')
{
short_name += c;
}
}
for (int i = short_name.size(); i < len; i++)
{
short_name += ' ';
}
return short_name;
}

255
src/fs/fat32/fat32.cpp Normal file
View File

@@ -0,0 +1,255 @@
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/14/2024
*/
#include "fat32.h"
#include "util/log.h"
/* Constructs the file system tree in memeory to keep track of meta-data */
FAT32::FAT32(string disk_filepath)
{
di = new DiskInterface(disk_filepath, 512);
// read and save required boot sector info
byte *buffer = di->read(0);
init(buffer);
delete[] buffer;
}
FAT32::FAT32(void *ramfs)
{
di = new DiskInterface(ramfs, 512);
// read and save required boot sector info
byte *buffer = di->read(0);
init(buffer);
delete[] buffer;
}
// TODO
FAT32::~FAT32()
{
// will segfaults if you uncomment
// delete root_dir;
// delete di;
}
void FAT32::init(byte *root_sector)
{
bytes_per_sector = bytes_to_short(root_sector, 11);
sectors_per_cluster = root_sector[13];
reserved_sectors = bytes_to_short(root_sector, 14);
fat_count = root_sector[16];
total_sectors = bytes_to_int(root_sector, 32);
if (total_sectors == 0)
{
total_sectors = bytes_to_short(root_sector, 19);
}
sectors_per_fat = bytes_to_int(root_sector, 36);
bytes_per_fat_entry = sizeof(unsigned int);
bytes_per_dir_entry = 32;
bytes_per_cluster = bytes_per_sector * sectors_per_cluster;
data_region_start_cluster_offset = (reserved_sectors + (fat_count * sectors_per_fat)) / sectors_per_cluster;
reserved_fat_entries = 2;
// start recursive search of the filesystem
root_dir = new Entry(this, nullptr, data_region_start_cluster_offset * bytes_per_cluster);
}
/* Finds the file specified by the given file path and fills the given type parameter
with the type of the file (file or directory), will fail if it can't find the file. */
int FAT32::file_type(string file_path, FileType &type)
{
Entry *entry = find_entry(file_path);
if (entry == nullptr)
return failure; // couldn't find file
type = (FileType)entry->type;
return success;
}
/* Finds the file specified by the given file path and fills the given size parameter
with the size of the file, will fail if it can't find the file. */
int FAT32::file_size(string file_path, int &size)
{
Entry *entry = find_entry(file_path);
if (entry == nullptr)
return failure; // couldn't find file
size = entry->file_size_bytes;
return success;
}
/* Finds the file specified by the given file path and fills the given buffer parameter
with the byte data of the file at the given sector offset (using zero-indexing), will fail
if it can't find the file, or if the sector offset is bigger than the number of sectors the
file has. */
int FAT32::read_file(string file_path, int sector_offset, byte *&buffer)
{
buffer = nullptr;
Entry *entry = find_entry(file_path);
if (entry == nullptr)
return failure; // couldn't find file
buffer = entry->read_data(sector_offset);
if (buffer == nullptr)
return failure; // couldn't read data
return success;
}
/* Finds the file specified by the given file path and writes the byte data at the given sector
offset (using zero-indexing) into the provided data buffer, will allocate space for the buffer
so don't allocate space for the buffer before passing, will fail if: it can't find the file,
there isn't enough space left on disk to allocate more space for the write. */
int FAT32::write_file(string file_path, int sector_offset, const byte *data)
{
Entry *entry = find_entry(file_path);
if (entry == nullptr)
return failure; // couldn't find file
if (!entry->write_data(sector_offset, data))
return failure; // couldn't write data
return success;
}
/* Will create a new file at the given file path, will fail if: file path doesn't exist, the parent
directory isn't a directory or, file name already exits in parent directory, there isn't enough space
left on disk to allocate more space for a new file entry. */
int FAT32::create_file(string file_path)
{
string par_dir_path = snip_file_path(file_path);
Entry *par_dir = find_entry(par_dir_path);
if (par_dir == nullptr)
return failure; // invalid file path
if (par_dir->type != EntryAttribute::DIRECTORY)
return failure; // parent 'directory' isnt a directory
Entry *existing_entry = find_entry(file_path);
if (existing_entry != nullptr)
return failure; // file with name already exists in directory
Entry *new_entry = new Entry();
string name = parse_file_path(file_path).back();
if (!new_entry->new_dir_entry(par_dir, name, FILE))
{
delete new_entry;
return failure; // not enough space in data region
}
return success;
}
// TODO
int FAT32::remove_file(string file_name)
{
// TODO
return failure;
}
// TODO
int FAT32::copy_file(string file_name, string dest_dir)
{
// TODO
return failure;
}
// TODO
int FAT32::move_file(string file_name, string dest_dir)
{
// TODO
return failure;
}
/* Finds the directory specified by the given file path and fill the given list with the directory's
sub-directory names, the given list will be emptied upon being passed, will fail if: it can't find the
directory, the 'directory' isn't a directory.*/
int FAT32::list_dir(string dir_path, vector<string> &list)
{
list.clear();
Entry *entry = find_entry(dir_path);
if (entry == nullptr)
return failure; // couldn't find directory
if (entry->type != EntryAttribute::DIRECTORY)
return failure; // 'directory' isnt a directory
for (int i = 0; i < (int)entry->sub_dirs.size(); i++)
{
list.push_back(entry->sub_dirs[i]->name);
}
return success;
}
/* Will create a new directory at the given file path, will fail if: file path doesn't exist, the parent
directory isn't a directory or, file name already exits in parent directory, there isn't enough space
left on disk to allocate more space for a new directory entry. */
int FAT32::create_dir(string dir_path)
{
string par_dir_path = snip_file_path(dir_path);
Entry *par_dir = find_entry(par_dir_path);
if (par_dir == nullptr)
return failure; // invalid file path
Entry *existing_entry = find_entry(dir_path);
if (existing_entry != nullptr)
return failure; // directory with name already exists in parent directory
Entry *new_entry = new Entry();
string name = parse_file_path(dir_path).back();
if (!new_entry->new_dir_entry(par_dir, name, DIRECTORY))
{
delete new_entry;
return failure; // not enough space in data region
}
return success;
}
// TODO
int FAT32::remove_dir(string name)
{
// TODO
return failure;
}
// TODO
int FAT32::copy_dir(string file_name, string dest_dir)
{
// TODO
return failure;
}
// TODO
int FAT32::move_dir(string file_name, string dest_dir)
{
// TODO
return failure;
}
int FAT32::get_sector_size()
{
return bytes_per_sector;
}

158
src/fs/fat32/fat32.h Normal file
View File

@@ -0,0 +1,158 @@
#ifndef FILE_SYSTEM_H
#define FILE_SYSTEM_H
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/15/2024
*/
#include "disk_interface/disk_interface.h"
#include "helpers.h"
#define success 1
#define failure 0
enum FileType
{
File = 0,
Directory = 2,
};
/* A FAT32 file system interface which constructs a tree of the file system strucuture storing all the
* neccesary meta-data and is used to read/write disk entries/data. */
class FAT32
{
public:
FAT32() = default;
FAT32(string disk_filepath);
FAT32(void *ramfs);
~FAT32();
void init(byte *root_sector);
int file_type(string file_path, FileType &type);
int file_size(string file_path, int &size);
int read_file(string file_path, int sector_offset, byte *&buffer);
int write_file(string file_path, int sector_offset, const byte *data);
int create_file(string file_path);
int remove_file(string file_path);
int copy_file(string file_path, string dest_dir);
int move_file(string file_path, string dest_dir);
int list_dir(string dir_path, vector<string> &list);
int create_dir(string dir_path);
int remove_dir(string dir_path);
int copy_dir(string src_dir_path, string dest_dir_path);
int move_dir(string src_dir_path, string dest_dir_path);
int get_sector_size();
// temp functions for testing/debugging, remove when merged with kernel
void dump_fs_structure();
void fat_dump(int cluster_offset);
void mem_dump(int cluster_offset);
int test();
private:
enum EntryAttribute
{
FILE,
EMPTY,
DIRECTORY,
LONG_NAME,
ROOT
};
enum FatEntryType
{
END,
FREE,
RESERVED,
ALLOCATED,
DEFECTIVE
};
/* Memory representation of a disk entry, only storing the required/useful meta-data */
class Entry
{
public:
Entry() = default;
Entry(FAT32 *fs, Entry *parent_dir, int byte_offset);
~Entry();
string name;
string extension; // may not have one
EntryAttribute type;
int file_size_bytes; // idk if directories or long file name entries really need this but I keep it updated anyway
int byte_offset; // actual byte offset of the corresponding disk entry
int order; // only used for entries of type LONG_NAME
bool last; // only used for entries of type LONG_NAME
Entry *parent_dir; // when entry type is LONG_NAME, this is used to link back to its associated FILE or DIRECTORY entry
vector<Entry *> sub_dirs; // only for entries of type DIRECTORY
vector<Entry *> long_name_entries;
vector<int> fat_allocations;
byte *read_data(int sector_offset);
int write_data(int sector_offset, const byte *data);
int new_dir_entry(Entry *par_dir, string name, EntryAttribute type);
Entry *find_sub_dir(string name);
private:
FAT32 *fs;
// these functions use explicit parameters and return types because they are used to inspect an entry without creating an entry object
EntryAttribute parse_attribute(int byte_offset);
string parse_short_file_name(int byte_offset);
void parse_root_entry();
void parse_dir_entry();
void parse_long_name();
void clear_entry(int byte_offset);
void write_dir_entry();
void write_long_name_entry();
int allocate_more_clusters(int num_additional_clusters);
int find_free_cluster();
int initial_cluster();
int find_empty_entries(int num_entries);
void init_dir_cluster(int cluster_offset);
void find_cluster_allocations(unsigned int first_fat_cluster_offset);
void find_long_name_entries();
void find_sub_dir_entries(); // use only once per entry
FatEntryType check_fat_entry_type(unsigned int cluster_val);
int fat_entry_to_cluster_offset(unsigned int fat_entry_val);
string long_to_short_name(string long_name, int len);
};
DiskInterface *di;
Entry *root_dir;
int data_region_start_cluster_offset;
int bytes_per_fat_entry;
int bytes_per_dir_entry;
int bytes_per_cluster;
int bytes_per_sector;
int sectors_per_cluster;
int sectors_per_fat;
int reserved_sectors;
int reserved_fat_entries;
int total_sectors;
int fat_count;
Entry *find_entry(string file_path);
vector<string> parse_file_path(string file_path);
string snip_file_path(string file_path);
};
#endif

View File

@@ -0,0 +1,78 @@
#include "filecontextfat32.h"
#include "util/string.h"
#include "util/log.h"
#include "types/status.h"
kernel::fs::FileContextFAT32::FileContextFAT32(FAT32 &fs, string path)
: fs(fs), path(path), sectorBuffer(nullptr), pos(0), lastSector(-1)
{
}
kernel::fs::FileContextFAT32::~FileContextFAT32()
{
if (sectorBuffer != nullptr)
{
delete sectorBuffer;
}
}
int kernel::fs::FileContextFAT32::read(void *buffer, int n)
{
int filesize;
if (fs.file_size(path, filesize) == failure)
{
kernelLog(LogLevel::ERROR, "Failed to obtain file size of %s during read.", path.c_str());
return EIO;
}
if (pos >= filesize)
{
// kernelLog(LogLevel::DEBUG, "Hit EOF on %s during read.", path.c_str());
return EEOF;
}
else if (pos + n > filesize)
{
n = filesize - pos;
}
unsigned int count = 0;
while (count < n)
{
int sector = pos / fs.get_sector_size();
int offset = pos % fs.get_sector_size();
int bytesLeft = fs.get_sector_size() - offset;
if (sector != lastSector)
{
if (sectorBuffer != nullptr)
{
delete sectorBuffer;
}
if (fs.read_file(path, sector, sectorBuffer) == failure)
{
kernelLog(LogLevel::WARNING, "I/O error reading sector %i of %s", sector, path.c_str());
break;
}
}
int c = (n > bytesLeft) ? bytesLeft : n;
memcpy(buffer + count, sectorBuffer + offset, c);
count += c;
pos += c;
}
return count;
}
int kernel::fs::FileContextFAT32::write(const void *buffer, int n)
{
return EIO;
}
kernel::fs::FileContext *kernel::fs::FileContextFAT32::copy()
{
FileContextFAT32 *f = new FileContextFAT32(fs, path);
if (f != nullptr)
{
f->pos = pos;
f->fileSize = fileSize;
}
return f;
}

View File

@@ -0,0 +1,38 @@
#ifndef KERNEL_FILECONTEXTFAT32_H
#define KERNEL_FILECONTEXTFAT32_H
#include "../filecontext.h"
#include "fat32.h"
#include "containers/string.h"
namespace kernel::fs
{
class FileContextFAT32 : public FileContext
{
public:
FileContextFAT32(FAT32 &fs, string path);
~FileContextFAT32();
int read(void *buffer, int n);
int write(const void *buffer, int n);
FileContext *copy();
private:
FAT32 &fs;
string path;
byte *sectorBuffer;
unsigned int fileSize;
unsigned int pos;
int lastSector;
};
}
#endif

213
src/fs/fat32/fs_helpers.cpp Normal file
View File

@@ -0,0 +1,213 @@
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/14/2024
*/
#include "util/log.h"
#include "containers/vector.h"
#include "containers/pair.h"
#include "fat32.h"
/* Finds the entry base on a given file path, fails if: the path is invalid, couldn't find entry via the path. */
FAT32::Entry *FAT32::find_entry(string file_path)
{
if (file_path == "/")
return root_dir;
vector<string> path = parse_file_path(file_path);
if (path.empty())
return nullptr; // couldn't parse file_path
Entry *current = root_dir;
for (int i = 0; i < (int)path.size(); i++)
{
current = current->find_sub_dir(path[i]);
if (current == nullptr)
return nullptr; // couldn't find sub dir with matching name
}
return current;
}
/* Parses a given file path into its corresponding sequence of entry string names. */
vector<string> FAT32::parse_file_path(string file_path)
{
vector<string> path;
if (file_path[0] != '/')
return path;
string part = "";
for (int i = 1; i < (int)file_path.size(); i++)
{
char c = file_path[i];
if (c == '/')
{
path.push_back(part);
part = "";
}
else
{
part += c;
}
}
path.push_back(part);
return path;
}
/* Cuts off the last entry name of the file path, used for getting the parent directory file path. */
string FAT32::snip_file_path(string file_path)
{
int last_slash_index = 0;
for (int i = 1; i < (int)file_path.size(); i++)
{
if (file_path[i] == '/')
last_slash_index = i;
}
string snipped = "/";
if (last_slash_index != 0)
snipped = file_path.substr(0, last_slash_index);
return snipped;
}
// below this are temp functions used for testing/debugging, remove when merged with kernel
void FAT32::dump_fs_structure()
{
// Use a stack to perform iterative depth-first traversal
vector<pair<Entry *, int>> s;
s.push_back({root_dir, 0});
while (!s.empty())
{
Entry *current = s.back().first;
vector<Entry *> sub_dirs = current->sub_dirs;
int num_sub_dirs = (int)sub_dirs.size();
int depth = s.back().second;
s.pop_back();
// Print current directory name
for (int i = 0; i < depth; ++i)
{
printf("| ");
}
printf("|-- %s\n", current->name.c_str());
// Push subdirectories onto the stack in reverse order
for (int i = num_sub_dirs - 1; i >= 0; i--)
{
s.push_back({sub_dirs[i], depth + 1});
}
}
}
void FAT32::fat_dump(int cluster_offset)
{
int sector_offset = reserved_sectors + (sectors_per_cluster * cluster_offset);
printf("\n");
for (int i = 0; i < sectors_per_cluster; i++)
{
byte *sector = di->read(i + sector_offset);
for (int j = 0; j < bytes_per_sector; j += 4)
{
printf("%i ", bytes_to_int(sector, j));
}
delete[] sector;
}
printf("\n");
}
void FAT32::mem_dump(int cluster_offset)
{
int sector_offset = (sectors_per_cluster * cluster_offset);
printf("\n");
for (int i = 0; i < sectors_per_cluster; i++)
{
byte *sector = di->read(i + sector_offset);
for (int j = 0; j < bytes_per_sector * sectors_per_cluster; j++)
{
if (sector[j] == 0)
{
printf("_ ");
}
else
{
printf("%02x ", sector[j]);
}
}
delete[] sector;
}
printf("\n");
}
int FAT32::test()
{
byte *buffer;
if (!read_file("/folder2/file6.txt", 0, buffer))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: read failure");
delete[] buffer;
return failure;
}
vector<string> list;
if (!list_dir("/folder2", list))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: list failure");
return failure;
}
if (!write_file("/file2.txt", 0, buffer))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: write failure");
return failure;
}
if (!create_file("/folder2/griddy_uncanny.ohi"))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: file creation failure");
return failure;
}
if (!write_file("/folder2/griddy_uncanny.ohi", 0, buffer))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: write failure");
return failure;
}
if (!create_dir("/folder1/skibidi_gyatt_rizzler"))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: directory creation failure");
return failure;
}
if (!create_file("/folder1/skibidi_gyatt_rizzler/yolo.txt"))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: file creation failure");
return failure;
}
if (!write_file("/folder1/skibidi_gyatt_rizzler/yolo.txt", 0, buffer))
{
kernelLog(LogLevel::WARNING, "FAT32 Test: write failure");
return failure;
}
delete[] buffer;
return success;
}

76
src/fs/fat32/helpers.cpp Normal file
View File

@@ -0,0 +1,76 @@
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/14/2024
*/
#include "helpers.h"
/* Will convert the sequence of bytes in a given byte array starting at the given start index to an
unsigned short, will only read enough bytes required by an unsigned short. */
unsigned short bytes_to_short(const byte *bytes, int index)
{
int size = sizeof(unsigned short);
byte temp[size];
for (int i = 0; i < size; i++)
temp[i] = bytes[i + index];
return *reinterpret_cast<unsigned short *>(temp);
}
/* Will convert the sequence of bytes in a given byte array starting at the given start index to an
unsigned int, will only read enough bytes required by an unsigned int. */
unsigned int bytes_to_int(const byte *bytes, int index)
{
int size = sizeof(unsigned int);
byte temp[size];
for (int i = 0; i < size; i++)
temp[i] = bytes[i + index];
return *reinterpret_cast<unsigned int *>(temp);
}
/* Will convert the sequence of bytes in a given byte array starting at the given start index to an
unsigned long, will only read enough bytes required by an unsigned long. */
unsigned long bytes_to_long(const byte *bytes, int index)
{
int size = sizeof(unsigned long);
byte temp[size];
for (int i = 0; i < size; i++)
temp[i] = bytes[i + index];
return *reinterpret_cast<unsigned long *>(temp);
}
/* Will convert the given unsigned short to a sequence of bytes in a given byte array starting at a
given index. Will only convert enough bytes required by an unsigned short. */
void short_to_bytes(unsigned short num, byte *buffer, int offset)
{
int size = sizeof(unsigned short);
for (int i = 0; i < size; i++)
buffer[i + offset] = ((num >> (i * 8)) & 0xFF);
return;
}
/* Will convert the given unsigned int to a sequence of bytes in a given byte array starting at a
given index. Will only convert enough bytes required by an unsigned int. */
void int_to_bytes(unsigned int num, byte *buffer, int offset)
{
int size = sizeof(unsigned int);
for (int i = 0; i < size; i++)
buffer[i + offset] = ((num >> (i * 8)) & 0xFF);
return;
}
/* Will convert the given unsigned long to a sequence of bytes in a given byte array starting at a
given index. Will only convert enough bytes required by an unsigned long. */
void long_to_bytes(unsigned long num, byte *buffer, int offset)
{
int size = sizeof(unsigned long);
for (int i = 0; i < size; i++)
buffer[i + offset] = ((num >> (i * 8)) & 0xFF);
return;
}
/* Given two characters, will return the unicode character of them slapped together. */
wchar_t chars_to_unicode(unsigned char first, unsigned char second)
{
return (static_cast<wchar_t>(first) << 8) | static_cast<wchar_t>(second);
}

26
src/fs/fat32/helpers.h Executable file
View File

@@ -0,0 +1,26 @@
#ifndef MAIN_H
#define MAIN_H
/*
* Author: Hunter Overstake
* Email: huntstake@gmail.com
* Date: 5/14/2024
*/
#include "containers/string.h"
#include "containers/vector.h"
typedef unsigned char byte;
unsigned short bytes_to_short(const byte *bytes, int index);
unsigned int bytes_to_int(const byte *bytes, int index);
unsigned long bytes_to_long(const byte *bytes, int index);
void short_to_bytes(unsigned short num, byte *buffer, int offset);
void int_to_bytes(unsigned int num, byte *buffer, int offset);
void long_to_bytes(unsigned long num, byte *buffer, int offset);
wchar_t chars_to_unicode(unsigned char first, unsigned char second);
bool string_compare(string first, string second);
#endif

5
src/fs/filecontext.cpp Normal file
View File

@@ -0,0 +1,5 @@
#include "filecontext.h"
kernel::fs::FileContext::~FileContext()
{
}

21
src/fs/filecontext.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef KERNEL_FILECONTEXT_H
#define KERNEL_FILECONTEXT_H
#include "util/hasrefcount.h"
namespace kernel::fs
{
class FileContext : public HasRefcount
{
public:
virtual ~FileContext() = 0;
virtual int read(void *buffer, int n) = 0;
virtual int write(const void *buffer, int n) = 0;
virtual FileContext *copy() = 0;
};
}
#endif

167
src/fs/pipe.cpp Normal file
View File

@@ -0,0 +1,167 @@
#include "pipe.h"
#include "filecontext.h"
#include "types/status.h"
kernel::fs::Pipe::Pipe()
: writePos(0), readPos(0), readerCount(0), writerCount(0)
{
}
kernel::fs::Pipe::~Pipe()
{
}
int kernel::fs::Pipe::put(void *data, int n)
{
if (getReaderCount() == 0)
{
return EPIPE;
}
char *s = (char *)data;
int c = 0;
while (c < n && !(writePos == PIPE_SIZE - 1 && readPos == 0) && !(writePos + 1 == readPos))
{
buffer[writePos] = s[c];
c++;
writePos++;
if (writePos >= PIPE_SIZE)
{
writePos = 0;
}
}
if (n > 0 && c == 0)
{
return EFULL;
}
else
{
return c;
}
}
int kernel::fs::Pipe::read(void *data, int n)
{
char *s = (char *)data;
int c = 0;
while (readPos != writePos && c < n)
{
s[c] = buffer[readPos];
c++;
readPos++;
if (readPos >= PIPE_SIZE)
{
readPos = 0;
}
}
return c;
}
kernel::fs::FileContext *kernel::fs::Pipe::createReader()
{
return new PipeReader(this);
}
kernel::fs::FileContext *kernel::fs::Pipe::createWriter()
{
return new PipeWriter(this);
}
void kernel::fs::Pipe::addReader()
{
readerCount++;
}
void kernel::fs::Pipe::removeReader()
{
readerCount--;
}
void kernel::fs::Pipe::addWriter()
{
writerCount++;
}
void kernel::fs::Pipe::removeWriter()
{
writerCount--;
}
int kernel::fs::Pipe::getReaderCount() const
{
return readerCount;
}
int kernel::fs::Pipe::getWriterCount() const
{
return writerCount;
}
kernel::fs::Pipe::PipeReader::PipeReader(Pipe *pipe)
: pipe(pipe)
{
pipe->addReader();
}
kernel::fs::Pipe::PipeReader::~PipeReader()
{
pipe->removeReader();
if (pipe->getReaderCount() == 0 && pipe->getWriterCount() == 0)
{
delete pipe;
}
}
int kernel::fs::Pipe::PipeReader::read(void *buffer, int n)
{
int c = pipe->read(buffer, n);
if (c == 0 && pipe->getWriterCount() == 0)
{
return EEOF;
}
else
{
return c;
}
}
int kernel::fs::Pipe::PipeReader::write(const void *buffer, int n)
{
return EIO;
}
kernel::fs::FileContext *kernel::fs::Pipe::PipeReader::copy()
{
return new PipeReader(pipe);
}
kernel::fs::Pipe::PipeWriter::PipeWriter(Pipe *pipe)
: pipe(pipe)
{
pipe->addWriter();
}
kernel::fs::Pipe::PipeWriter::~PipeWriter()
{
pipe->removeWriter();
if (pipe->getReaderCount() == 0 && pipe->getWriterCount() == 0)
{
delete pipe;
}
}
int kernel::fs::Pipe::PipeWriter::read(void *buffer, int n)
{
return EIO;
}
int kernel::fs::Pipe::PipeWriter::write(const void *buffer, int n)
{
return pipe->put(buffer, n);
}
kernel::fs::FileContext *kernel::fs::Pipe::PipeWriter::copy()
{
return new PipeWriter(pipe);
}

83
src/fs/pipe.h Normal file
View File

@@ -0,0 +1,83 @@
#ifndef KERNEL_PIPE_H
#define KERNEL_PIPE_H
#include "util/hasrefcount.h"
#include "filecontext.h"
namespace kernel::fs
{
class Pipe
{
public:
Pipe();
~Pipe();
int put(void *data, int n);
int read(void *data, int n);
FileContext *createReader();
FileContext *createWriter();
void addReader();
void removeReader();
void addWriter();
void removeWriter();
int getReaderCount() const;
int getWriterCount() const;
private:
static const int PIPE_SIZE = 4096;
char buffer[PIPE_SIZE];
int writePos, readPos;
int readerCount, writerCount;
class PipeReader : public FileContext
{
public:
PipeReader(Pipe *pipe);
~PipeReader();
int read(void *buffer, int n);
int write(const void *buffer, int n);
FileContext *copy();
private:
Pipe *pipe;
};
class PipeWriter : public FileContext
{
public:
PipeWriter(Pipe *pipe);
~PipeWriter();
int read(void *buffer, int n);
int write(const void *buffer, int n);
FileContext *copy();
private:
Pipe *pipe;
};
};
}
#endif

View File

@@ -0,0 +1,27 @@
#ifndef KERNEL_INTERRUPTHANDLER_H
#define KERNEL_INTERRUPTHANDLER_H
namespace kernel::interrupt
{
/**
* @brief An interface which drivers that wish to handle hardware interrupts
* must implement. The handleInterrupt() method will be called when the associated
* interrupt number fires.
*/
class InterruptHandler
{
public:
/**
* @brief Method to be called when the interrupt number associated with this
* handler fires. `source` is the interrupt number which caused the handler
* to be called. A handler object may choose to handle multiple interrupt
* numbers.
* @param source
*/
virtual void handleInterrupt(int source) = 0;
};
}
#endif

19
src/irq/interrupts.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include "interrupts.h"
kernel::interrupt::InterruptHandler *kernel::interrupt::Interrupts::handlers[256];
void kernel::interrupt::Interrupts::insertHandler(int id, InterruptHandler *handler)
{
if(id < HANDLER_COUNT)
{
handlers[id] = handler;
}
}
void kernel::interrupt::Interrupts::callHandler(int id)
{
if(handlers[id] != nullptr)
{
handlers[id]->handleInterrupt(id);
}
}

64
src/irq/interrupts.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef KERNEL_INTERRUPTS_H
#define KERNEL_INTERRUPTS_H
#include "interrupthandler.h"
namespace kernel::interrupt
{
/**
* @brief Static class which handles the enabling and disabling of interrupts,
* as well as calling the appropriate handler for a particuler interrupt number.
*/
class Interrupts
{
public:
/**
* @brief Initialize interrupts. Must be called before interrupts can work
* properly. This function disables interrupts. When the caller is ready for
* interrupts to fire, they must call `enable()`.
*/
static void init();
/**
* @brief Enable interrupts.
*/
static void enable();
/**
* @brief Disable interrupts.
*/
static void disable();
/**
* @brief Insert a handler object to handle interrupts with the given id.
* Only one handler may be associated with a given interrupt. If a handler
* is already present for `id`, this function will overwrite it.
* @param id
* @param handler
*/
static void insertHandler(int id, InterruptHandler *handler);
/**
* @param id ler associated with the given interrupt id.
* @param id
*/
static void callHandler(int id);
private:
/**
* @brief Maximum size of the interrupt handler array. Must be at least as
* big as the largest possible interrupt number.
*/
static const int HANDLER_COUNT = 256;
/**
* @brief An array of pointers to handler objects. Each index in the array
* corresponds to an interrupt number.
*/
static InterruptHandler *handlers[HANDLER_COUNT];
};
}
#endif

547
src/kernel.cpp Normal file
View File

@@ -0,0 +1,547 @@
#include "kernel.h"
#include "util/log.h"
#include "util/hacf.h"
#include "util/string.h"
#include "mmgmt.h"
#include "types/physaddr.h"
#include "sched/process.h"
#include "loader/elf.h"
#include "fs/fat32/filecontextfat32.h"
#include "types/status.h"
#include "fs/pipe.h"
kernel::Kernel kernel::kernel;
class PipeReadContext : public kernel::fs::FileContext
{
int read(void *buffer, int n);
int write(const void *buffer, int n);
};
void (*syscall_table[])(long, long, long, long) = {
(void (*)(long, long, long, long))kernel::syscall_printk,
(void (*)(long, long, long, long))kernel::syscall_mmap,
(void (*)(long, long, long, long))kernel::syscall_munmap,
(void (*)(long, long, long, long))kernel::syscall_clone,
(void (*)(long, long, long, long))kernel::syscall_terminate,
(void (*)(long, long, long, long))kernel::syscall_exec,
(void (*)(long, long, long, long))kernel::syscall_yield,
(void (*)(long, long, long, long))kernel::syscall_sigraise,
(void (*)(long, long, long, long))kernel::syscall_sigret,
(void (*)(long, long, long, long))kernel::syscall_sigwait,
(void (*)(long, long, long, long))kernel::syscall_sigaction,
(void (*)(long, long, long, long))kernel::syscall_open,
(void (*)(long, long, long, long))kernel::syscall_close,
(void (*)(long, long, long, long))kernel::syscall_create,
(void (*)(long, long, long, long))kernel::syscall_unlink,
(void (*)(long, long, long, long))kernel::syscall_read,
(void (*)(long, long, long, long))kernel::syscall_write,
(void (*)(long, long, long, long))kernel::syscall_fddup,
(void (*)(long, long, long, long))kernel::syscall_create_pipe};
kernel::sched::Context *do_syscall(unsigned long id, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, kernel::sched::Context *ctx)
{
using namespace kernel::sched;
kernel::kernel.getActiveProcess()->storeContext(ctx);
syscall_table[id](arg1, arg2, arg3, arg4);
// kernelLog(LogLevel::DEBUG, "Returning from call %i:\n\tpc = %016x\n\tsp=%016x", id, ctx->getProgramCounter(), ctx->getStackPointer());
return kernel::kernel.getActiveProcess()->getContext();
}
void kernel::syscall_printk(const char *str)
{
printf(str);
kernel.setCallerReturn(ENONE);
}
void kernel::syscall_mmap(void *ptr, unsigned long size, int flags)
{
using namespace kernel::memory;
physaddr_t frame = pageAllocator.reserve(size);
if (frame == PageAllocator::NOMEM)
{
kernel.setCallerReturn(ENOMEM);
}
else
{
kernel.setCallerReturn(map_region(ptr, size, frame, flags | PAGE_USER));
}
}
void kernel::syscall_munmap(void *ptr, unsigned long size)
{
using namespace kernel::memory;
unmap_region(ptr, size);
kernel.setCallerReturn(ENONE);
}
void kernel::syscall_clone(int (*fn)(void *), void *stack, void *userdata, int flags)
{
using namespace kernel::sched;
Process *newProcess = kernel.getActiveProcess()->clone(kernel.nextPid(), (void *)fn, stack, userdata);
kernel.addProcess(*newProcess);
kernel.setCallerReturn(ENONE);
}
void kernel::syscall_terminate()
{
kernel.raiseSignal(kernel.getActiveProcess()->getParent(), 17);
kernel.deleteActiveProcess();
kernel.switchTask();
}
void kernel::syscall_exec(const char *path, char *const argv[], char *const envp[])
{
if (path == nullptr || argv == nullptr || envp == nullptr)
{
kernel.setCallerReturn(EINVAL);
return;
}
int argc = 0;
while (argv[argc] != nullptr)
{
argc++;
}
int envc = 0;
while (envp[envc] != nullptr)
{
envc++;
}
char **argvCopy = new char *[argc + 1];
for (int i = 0; i <= argc; i++)
{
if (argv[i] != nullptr)
{
argvCopy[i] = new char[strlen(argv[i]) + 1];
strcpy(argvCopy[i], argv[i]);
}
else
{
argvCopy[i] = nullptr;
}
}
char **envpCopy = new char *[envc + 1];
for (int i = 0; i <= envc; i++)
{
if (envp[i] != nullptr)
{
envpCopy[i] = new char[strlen(envp[i]) + 1];
strcpy(envpCopy[i], envp[i]);
}
else
{
envpCopy[i] = nullptr;
}
}
char *pathCopy = (char *)rmalloc(strlen(path) + 1);
strcpy(pathCopy, path);
int status;
if ((status = kernel.exec(path, argvCopy, envpCopy)) != ENONE)
{
kernel.setCallerReturn(status);
}
for (int i = 0; argvCopy[i] != nullptr; i++)
{
delete argvCopy[i];
}
for (int i = 0; envpCopy[i] != nullptr; i++)
{
delete envpCopy[i];
}
delete envpCopy;
delete argvCopy;
// kernelLog(LogLevel::DEBUG, "Returning exec() into %s", pathCopy);
rfree(pathCopy);
}
void kernel::syscall_yield()
{
kernel.setCallerReturn(ENONE);
kernel.switchTask();
}
void kernel::syscall_sigraise(pid_t pid, int signal)
{
int status = kernel.raiseSignal(pid, signal);
if (status <= 0)
{
kernel.setCallerReturn(status);
}
}
void kernel::syscall_sigret()
{
kernel.getActiveProcess()->signalReturn();
}
void kernel::syscall_sigwait()
{
using namespace kernel::sched;
kernel.getActiveProcess()->setState(Process::State::SIGWAIT);
kernel.sleepActiveProcess();
kernel.switchTask();
}
void kernel::syscall_sigaction(int signal, void (*handler)(void *), void (*trampoline)(void), void *userdata)
{
kernel.getActiveProcess()->setSignalAction(signal, handler, trampoline, userdata);
kernel.setCallerReturn(ENONE);
}
void kernel::syscall_open(const char *path, int flags)
{
using namespace kernel::fs;
if (kernel.getRamFS() == nullptr)
{
kernel.setCallerReturn(EIO);
return;
}
FileType type;
if (kernel.getRamFS()->file_type(path, type) == failure || type != File)
{
kernel.setCallerReturn(ENOFILE);
return;
}
FileContext *fc = new FileContextFAT32(*kernel.getRamFS(), path);
int fd = kernel.getActiveProcess()->storeFileContext(fc);
kernel.setCallerReturn(fd);
}
void kernel::syscall_close(int fd)
{
kernel.setCallerReturn(kernel.getActiveProcess()->closeFileContext(fd));
}
void kernel::syscall_create(const char *path, int flags)
{
kernel.setCallerReturn(ENOSYS);
}
void kernel::syscall_unlink(int fd)
{
kernel.setCallerReturn(ENOSYS);
}
void kernel::syscall_read(int fd, void *buffer, unsigned long size)
{
using namespace kernel::fs;
// kernelLog(LogLevel::DEBUG, "pid %i: read(%i, %016x, %i)", kernel.getActiveProcess()->getPid(), fd, buffer, size);
FileContext *fc = kernel.getActiveProcess()->getFileContext(fd);
if (fc == nullptr)
{
kernel.setCallerReturn(ENOFILE);
return;
}
int count = fc->read(buffer, size);
kernel.setCallerReturn(count);
}
void kernel::syscall_write(int fd, const void *buffer, unsigned long size)
{
using namespace kernel::fs;
// kernelLog(LogLevel::DEBUG, "pid %i: write(%i, %016x, %i)", kernel.getActiveProcess()->getPid(), fd, buffer, size);
FileContext *fc = kernel.getActiveProcess()->getFileContext(fd);
if (fc == nullptr)
{
kernelLog(LogLevel::WARNING, "Failed to write on fd %i", fd);
kernel.setCallerReturn(ENOFILE);
return;
}
kernel.setCallerReturn(fc->write(buffer, size));
}
void kernel::syscall_fddup(int oldfd, int newfd)
{
using namespace kernel::fs;
// kernelLog(LogLevel::DEBUG, "pid %i: fddup(%i, %i)", kernel.getActiveProcess()->getPid(), oldfd, newfd);
FileContext *fc = kernel.getActiveProcess()->getFileContext(oldfd);
if (fc == nullptr)
{
kernel.setCallerReturn(ENOFILE);
return;
}
if (kernel.getActiveProcess()->getFileContext(newfd) != nullptr && kernel.getActiveProcess()->closeFileContext(newfd))
{
kernelLog(LogLevel::ERROR, "fddup() failed to close newfd=%i", newfd);
kernel.setCallerReturn(EUNKNOWN);
return;
}
if (kernel.getActiveProcess()->storeFileContext(fc, newfd) != ENONE)
{
kernelLog(LogLevel::ERROR, "fddup() failed to store newfd=%i", newfd);
kernel.setCallerReturn(EUNKNOWN);
return;
}
kernel.setCallerReturn(ENONE);
}
void kernel::syscall_create_pipe(int pipefd[2])
{
using namespace kernel::fs;
// kernelLog(LogLevel::DEBUG, "pid %i: pipe", kernel.getActiveProcess()->getPid());
Pipe *pipe = new Pipe();
if (pipe == nullptr)
{
kernel.setCallerReturn(ENOMEM);
return;
}
FileContext *reader = pipe->createReader();
FileContext *writer = pipe->createWriter();
if (reader == nullptr || writer == nullptr)
{
if (reader)
{
delete reader;
}
if (writer)
{
delete writer;
}
kernel.setCallerReturn(ENOMEM);
return;
}
pipefd[0] = kernel.getActiveProcess()->storeFileContext(reader);
pipefd[1] = kernel.getActiveProcess()->storeFileContext(writer);
kernel.setCallerReturn(ENONE);
}
kernel::Kernel::Kernel()
: scheduler(), processTable(), fs(nullptr), currPid(1)
{
}
pid_t kernel::Kernel::nextPid()
{
pid_t v = currPid;
currPid++;
return v;
}
int kernel::Kernel::initMemory(memory::MemoryMap &memoryMap, unsigned long kernelSize)
{
using namespace memory;
kernelLog(LogLevel::DEBUG, "Constructing page allocator at %016x", &__end);
new (&pageAllocator) memory::PageAllocator(memoryMap, &__end, page_size);
unsigned long pageMapEnd = (unsigned long)&__end + PageAllocator::mapSize(memoryMap, page_size);
pageMapEnd = (pageMapEnd + (page_size - 1)) & ~(page_size - 1);
unsigned long heapSize = ((unsigned long)&__high_mem + kernelSize) - pageMapEnd;
kernelLog(LogLevel::DEBUG, "Constructing kernel heap at %016x with size %016x", pageMapEnd, heapSize);
init_heap((void *)pageMapEnd, heapSize);
return 0;
}
int kernel::Kernel::initRamFS(void *ramfs)
{
kernelLog(LogLevel::DEBUG, "Creating ramfs %016x", ramfs);
if (fs != nullptr)
{
kernelLog(LogLevel::INFO, "Deleting previous ramfs %016x", fs);
delete fs;
}
fs = new FAT32(ramfs);
return 0;
}
FAT32 *kernel::Kernel::getRamFS()
{
return fs;
}
void kernel::Kernel::switchTask()
{
scheduler.sched_next();
memory::loadAddressSpace(*scheduler.get_cur_process()->getAddressSpace());
// kernelLog(LogLevel::DEBUG, "Switched to pid %i", getActiveProcess()->getPid());
}
void kernel::Kernel::setCallerReturn(unsigned long v)
{
scheduler.get_cur_process()->getContext()->setReturnValue(v);
}
void kernel::Kernel::addProcess(sched::Process &p)
{
using namespace sched;
processTable.insert(p.getPid(), p);
if (p.getState() == Process::State::ACTIVE)
{
scheduler.enqueue(&processTable.get(p.getPid()));
}
}
kernel::sched::Process *kernel::Kernel::getActiveProcess()
{
return scheduler.get_cur_process();
}
void kernel::Kernel::sleepActiveProcess()
{
scheduler.set_cur_process(nullptr);
}
void kernel::Kernel::deleteActiveProcess()
{
processTable.remove(scheduler.get_cur_process()->getPid());
scheduler.set_cur_process(nullptr);
}
int kernel::Kernel::raiseSignal(pid_t pid, int signal)
{
using namespace kernel::sched;
if (!processTable.contains(pid))
{
kernelLog(LogLevel::WARNING, "Attempt to raise signal %i on non-existent pid %i", signal, pid);
return -1;
}
else if (processTable.get(pid).getState() != Process::State::ACTIVE && processTable.get(pid).getState() != Process::State::SIGWAIT)
{
kernelLog(LogLevel::WARNING, "Process %i cannot acccept signal: invalid state.", pid);
return -1;
}
bool schedule = processTable.get(pid).getState() == Process::State::SIGWAIT;
int status = processTable.get(pid).signalTrigger(signal);
if (status > 0)
{
kernelLog(LogLevel::DEBUG, "Killing process %i due to signal.", pid);
if (processTable.get(pid).getState() == Process::State::ACTIVE)
{
scheduler.remove(pid);
if (getActiveProcess()->getPid() == pid)
{
scheduler.set_cur_process(nullptr);
switchTask();
}
}
processTable.remove(pid);
}
else if (status == 0 && schedule)
{
// kernelLog(LogLevel::DEBUG, "Placing process %i back on schedule queue.", pid);
scheduler.enqueue(&processTable.get(pid));
}
return status;
}
int kernel::Kernel::exec(const char *path, char *const argv[], char *const envp[])
{
using namespace memory;
using namespace loader;
using namespace sched;
using namespace fs;
// kernelLog(LogLevel::DEBUG, "exec %s", path);
if (getActiveProcess() == nullptr || getActiveProcess()->getState() != Process::State::ACTIVE || argv == nullptr || envp == nullptr)
{
return -1;
}
FileType type;
int size;
if (fs->file_type(path, type) == failure || fs->file_size(path, size) == failure)
{
kernelLog(LogLevel::INFO, "kernel.exec() failure: %s not found.", path);
return ENOFILE;
}
void *exeData = rmalloc(size);
for (int i = 0; i < size; i += 512)
{
byte *data;
if (fs->read_file(path, i / 512, data) == failure)
{
kernelLog(LogLevel::WARNING, "kernel.exec() failure: I/O error.");
return EIO;
}
memcpy((char *)exeData + i, data, size - i >= 512 ? 512 : size - i);
delete data;
data = nullptr;
}
AddressSpace *addressSpace = createAddressSpace();
loadAddressSpace(*addressSpace);
ELF exe(exeData);
buildProgramImage(exe);
map_region((void *)0x7FBFFF0000, 0x10000, pageAllocator.reserve(0x10000), PAGE_USER | PAGE_RW);
void *base = rmalloc(1UL << 16);
if (base == nullptr)
{
rfree(exeData);
return ENOMEM;
}
void *kernelStack = (void *)((unsigned long)base + (1UL << 16));
getActiveProcess()->exec(exe.fileHeader().entry, (void *)0x7FC0000000, kernelStack, addressSpace);
getActiveProcess()->storeProgramArgs(argv, envp);
if (getActiveProcess()->getFileContext(0) == nullptr)
{
FileContext *in = getLogStream()->open(CharStream::Mode::RO);
if (in != nullptr)
{
if (getActiveProcess()->storeFileContext(in, 0))
{
delete in;
}
}
}
if (getActiveProcess()->getFileContext(1) == nullptr)
{
FileContext *out = getLogStream()->open(CharStream::Mode::W);
if (out != nullptr)
{
if (getActiveProcess()->storeFileContext(out, 1))
{
delete out;
}
}
}
if (getActiveProcess()->getFileContext(1) == nullptr)
{
FileContext *out = getLogStream()->open(CharStream::Mode::W);
if (out != nullptr)
{
if (getActiveProcess()->storeFileContext(out, 1))
{
delete out;
}
}
}
if (getActiveProcess()->getFileContext(2) == nullptr)
{
FileContext *err = getLogStream()->open(CharStream::Mode::W);
if (err != nullptr)
{
if (getActiveProcess()->storeFileContext(err, 2))
{
delete err;
}
}
}
rfree(exeData);
return ENONE;
}

234
src/kernel.h Normal file
View File

@@ -0,0 +1,234 @@
#ifndef KERNEL_H
#define KERNEL_H
#include "memory/memorymap.h"
#include "fs/fat32/fat32.h"
#include "sched/queue.h"
#include "containers/binary_search_tree.h"
#include "types/pid.h"
/**
* @brief Symbol located at the beginning of the kernel binary in memory.
*/
extern char __begin;
/**
* @brief Symbol located at the end of the kernel binary in memory.
*/
extern char __end;
/**
* @brief Symbol located at the start of high memory.
*/
extern char __high_mem;
extern "C" void (*syscall_table[])(long, long, long, long);
extern "C" kernel::sched::Context *do_syscall(unsigned long id, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, kernel::sched::Context *ctx);
namespace kernel
{
void initialize(memory::MemoryMap &memoryMap, unsigned long kernelSize);
class Kernel
{
public:
Kernel();
pid_t nextPid();
int initMemory(memory::MemoryMap &memoryMap, unsigned long kernelSize);
int initRamFS(void *ramfs);
FAT32 *getRamFS();
void switchTask();
void setCallerReturn(unsigned long v);
void addProcess(sched::Process &p);
sched::Process *getActiveProcess();
void sleepActiveProcess();
void deleteActiveProcess();
int raiseSignal(pid_t pid, int signal);
int exec(const char *path, char *const argv[], char *const envp[]);
void loadInitProgram();
private:
queue scheduler;
binary_search_tree<pid_t, sched::Process> processTable;
FAT32 *fs;
pid_t currPid;
};
extern Kernel kernel;
/**
* @brief Prints `str` on the kernel log.
* @param str
* @return 0
*/
void syscall_printk(const char *str);
/**
* @brief Map a region in memory to newly allocated page frames
* @param ptr Pointer to the start of the region to map
* @param size Size in bytes of the region to map
* @param flags Access flags for the pages to map
* @return
*/
void syscall_mmap(void *ptr, unsigned long size, int flags);
/**
* @brief Free the memory in a particular region
* @param ptr Pointer to the start of the region to unmap
* @param size Size in bytes of the region to unmap
* @return
*/
void syscall_munmap(void *ptr, unsigned long size);
/**
* @brief Create a new process which shares the current process's address space,
* but has a separate context and is scheduled separately.
*
* @param fn Function pointer to start executing the new process at
* @param stack Stack pointer for the new process
* @param flags
* @return
*/
void syscall_clone(int (*fn)(void *), void *stack, void *userdata, int flags);
/**
* @brief Completely terminate the current process, freeing all resources that
* belong to it.
*
* @return
*/
void syscall_terminate();
/**
* @brief Replace the current process's address space with a fresh one, then
* load a new program image from the executable specified by `path`.
*
* @param path Path to the executable to load
* @param argv Program arguments to pass to the new program
* @param envp Environment variables to pass to the new program
* @return
*/
void syscall_exec(const char *path, char *const argv[], char *const envp[]);
/**
* @brief Put current process at the end of the scheduler queue, then switch to
* next process.
*
* @return
*/
void syscall_yield();
/**
* @brief Call the specified signal handler on the process with id `pid`.
* @param pid Process to call a signal handler on
* @param signal Signal handler to call
* @return
*/
void syscall_sigraise(pid_t pid, int signal);
/**
* @brief Return from a signal handler, putting the stack and process context
* back to the state they were in just before the signal was triggered.
* @return
*/
void syscall_sigret();
/**
* @brief Stop scheduling process until a signal occurs.
* @return
*/
void syscall_sigwait();
/**
* @brief Sets the handler function to call when a particular signal occurs.
* Kernel will pass the pointer `userdata` to the handler function when it is
* called.
* @param signal Signal to specify handler for
* @param handler Function pointer to signal handler
* @param trampoline Function pointer to trampoline function called when handler returns.
* @param userdata Userdata to pass to handler function (can be NULL)
* @return
*/
void syscall_sigaction(int signal, void (*handler)(void *), void (*trampoline)(void), void *userdata);
/**
* @brief Open the file specified by `path`
* @param path Path of the file to open
* @param flags
* @return The file descriptor for the file just opened
*/
void syscall_open(const char *path, int flags);
/**
* @brief Close a proviously open file
* @param fd File descriptor of the open file to close
* @return
*/
void syscall_close(int fd);
/**
* @brief Create a new file at the given path
* @param path Path of the file to create
* @param flags Mode for the new file
* @return
*/
void syscall_create(const char *path, int flags);
/**
* @brief
* @param fd
* @return
*/
void syscall_unlink(int fd);
/**
* @brief
* @param fd
* @param buffer
* @param size
* @return
*/
void syscall_read(int fd, void *buffer, unsigned long size);
/**
* @brief
* @param fd
* @param buffer
* @param size
* @return
*/
void syscall_write(int fd, const void *buffer, unsigned long size);
/**
* @brief Duplicate a file descriptor. `newfd` will serve as an alias for
* `oldfd`.
* @param oldfd
* @param newfd
*/
void syscall_fddup(int oldfd, int newfd);
/**
* @brief Creates a new pipe.
*/
void syscall_create_pipe(int pipefd[2]);
}
#endif

87
src/loader/elf.cpp Normal file
View File

@@ -0,0 +1,87 @@
/**
* Author: Nathan Giddings
*/
#include "elf.h"
#include "memory/mmap.h"
#include "memory/pageallocator.h"
#include "util/string.h"
kernel::loader::ELF::ELF(void *file)
{
header = (ELFFileHeader *)file;
sectionIndex = 0;
}
const void *kernel::loader::ELF::fileLocation() const
{
return (void *)header;
}
bool kernel::loader::ELF::isValid() const
{
return header->magic == 0x464c457f && header->phcount > 0;
}
const kernel::loader::ELFFileHeader &kernel::loader::ELF::fileHeader() const
{
return *header;
}
const kernel::loader::ELFProgramHeader &kernel::loader::ELF::currentSection() const
{
ELFProgramHeader *sectionHeader = (ELFProgramHeader *)((void *)header + header->phoffset);
return sectionHeader[sectionIndex];
}
bool kernel::loader::ELF::nextSection()
{
sectionIndex++;
if (sectionIndex >= header->phcount)
{
sectionIndex = 0;
}
return (sectionIndex > 0);
}
int kernel::loader::buildProgramImage(ELF &elf)
{
using namespace kernel::memory;
// Abort if binary isn't valid format
if (!elf.isValid())
{
return -1;
}
do
{
// Check if section should be loaded
if ((ELFSegmentType)elf.currentSection().type != ELFSegmentType::LOAD)
{
continue;
}
// Try to allocate enough physical memory
unsigned long sectionSize = elf.currentSection().memsize;
physaddr_t frame = pageAllocator.reserve(sectionSize);
if (frame == PageAllocator::NOMEM)
{
return -1;
}
unsigned long fileSize = elf.currentSection().filesize;
// Map virtual memory, then copy section data
void *dest = elf.currentSection().vaddr;
const void *src = elf.fileLocation() + elf.currentSection().offset;
unsigned long diff = (unsigned long)dest % page_size;
void *mapPtr = getPageFrame(dest) == 0 ? (dest - diff) : (dest + (page_size - diff));
unsigned long mapSize = getPageFrame(dest) == 0 ? (sectionSize + diff) : (sectionSize - (page_size - diff));
map_region(mapPtr, mapSize, frame, PAGE_RW | PAGE_USER | PAGE_EXE);
memset(dest, sectionSize, 0);
memcpy(dest, src, fileSize);
} while (elf.nextSection());
return 0;
}

131
src/loader/elf.h Normal file
View File

@@ -0,0 +1,131 @@
/**
* Author: Nathan Giddings
*/
#ifndef KERNEL_ELF_H
#define KERNEL_ELF_H
#include "types/physaddr.h"
#include <cstdint>
namespace kernel::loader
{
enum class ELFEndianness
{
LITTLE = 1,
BIG = 2
};
enum class ELFISA
{
NA = 0x00,
x86 = 0x03,
MIPS = 0x08,
PPC = 0x14,
PPC64 = 0x15,
ARM = 0x28,
x86_64 = 0x3E,
AARCH64 = 0xB7
};
enum class ELFSegmentType
{
UNUSED = 0,
LOAD = 1,
DYNAMIC = 2
};
class ELFFileHeader
{
public:
uint32_t magic;
char size;
char endianness;
char version;
char abi;
char abi_version;
char reserved[7];
uint16_t type;
uint16_t machine;
uint32_t _version;
void *entry;
#if defined __i386__ || defined __arm__
uint32_t phoffset;
uint32_t shoffset;
#elif defined __x86_64__ || defined __aarch64__
uint64_t phoffset;
uint64_t shoffset;
#endif
uint32_t flags;
uint16_t header_size;
uint16_t phsize;
uint16_t phcount;
uint16_t shsize;
uint16_t shcount;
uint16_t shstrndx;
};
class ELFProgramHeader
{
public:
uint32_t type;
#if defined __i386__ || defined __arm__
uint32_t offset;
void *vaddr;
physaddr_t paddr;
uint32_t filesize;
uint32_t memsize;
uint32_t flags;
uint32_t align;
#elif defined __x86_64__ || defined __aarch64__
uint32_t flags;
uint64_t offset;
void *vaddr;
physaddr_t paddr;
uint64_t filesize;
uint64_t memsize;
uint64_t align;
#endif
};
class ELF
{
public:
ELF(void *file);
const void *fileLocation() const;
bool isValid() const;
const ELFFileHeader &fileHeader() const;
const ELFProgramHeader &currentSection() const;
bool nextSection();
private:
ELFFileHeader *header;
int sectionIndex;
};
/**
* @brief Creates a program image from the given binary in the current address space.
* @param elf Binary to unpack
* @return zero upon success, nonzero upon failure
*/
int buildProgramImage(ELF &elf);
#if defined __i386__
static const ELFISA HOST_ISA = ELFISA::x86;
#elif defined __x86_64__
static const ELFISA HOST_ISA = ELFISA::x86_64;
#elif defined __arm__
static const ELFISA HOST_ISA = ELFISA::ARM;
#elif defined __aarch64__
static const ELFISA HOST_ISA = ELFISA::AARCH64;
#endif
}
#endif

448
src/memory/aarch64/mmu.cpp Normal file
View File

@@ -0,0 +1,448 @@
#include "memory/mmap.h"
#include "memory/pageallocator.h"
#include "types/physaddr.h"
#include "aarch64/irq/exceptionclass.h"
#include "aarch64/irq/syndromedataabort.h"
#include "aarch64/sysreg.h"
#include "util/hacf.h"
#include "kernel.h"
#include "util/log.h"
#include <cstdint>
using namespace kernel::memory;
class PageTableEntry
{
public:
/**
* @brief Set when descriptor points to valid page or table
*/
uint64_t present : 1;
/**
* @brief Descriptor type: 1 for table/page entry, 0 for block entry
*/
uint64_t type : 1;
/**
* @brief Index into MAIR_ELn register
*/
uint64_t attrIndex : 3;
/**
* @brief Non-secure
*/
uint64_t ns : 1;
/**
* @brief Access permission[1]: when set, accessable by EL0
*
*/
uint64_t apEL0 : 1;
/**
* @brief Access permission[2]: when set, memory is read-only
*
*/
uint64_t apReadOnly : 1;
/**
* @brief Sharability
*/
uint64_t sh : 2;
/**
* @brief Access flag
*/
uint64_t af : 1;
/**
* @brief No global
*/
uint64_t ng : 1;
/**
* @brief Significant bits of physical address of next table or page.
*/
uint64_t outputAddress : 40;
/**
* @brief Privileged execute-never
*/
uint64_t pxn : 1;
/**
* @brief Execute-never
*/
uint64_t xn : 1;
/**
* @brief Copy-on-write
*/
uint64_t cow : 1;
/**
* @brief Free for software use
*/
uint64_t reserved : 3;
/**
* @brief When set, all child tables will be treated as if pxn=1
*/
uint64_t pxnTable : 1;
/**
* @brief When set, all child tables will be treated as if xn=1
*/
uint64_t xnTable : 1;
/**
* @brief When set, all child tables will be treated as if ap=1
*/
uint64_t apTable : 2;
/**
* @brief when set, all child tables will be treated as if ns=1
*/
uint64_t nsTable : 1;
void clear()
{
*(uint64_t *)this = 0;
}
void makeTableDescriptor(physaddr_t nextLevel)
{
clear();
present = 1;
type = 1;
af = 1;
physicalAddress(nextLevel);
}
void makeBlockDescriptor(physaddr_t frame, int permissions)
{
clear();
present = 1;
apEL0 = (permissions & PAGE_USER) ? 1 : 0;
apReadOnly = (permissions & PAGE_RW) ? 0 : 1;
xn = (permissions & PAGE_EXE) ? 0 : 1;
af = 1;
physicalAddress(frame);
}
void makePageDescriptor(physaddr_t frame, int permissions)
{
clear();
present = 1;
type = 1;
apEL0 = (permissions & PAGE_USER) ? 1 : 0;
apReadOnly = (permissions & PAGE_RW) ? 0 : 1;
xn = (permissions & PAGE_EXE) ? 0 : 1;
af = 1;
physicalAddress(frame);
}
void physicalAddress(physaddr_t addr)
{
outputAddress = addr >> 12;
}
physaddr_t physicalAddress()
{
return outputAddress << 12;
}
};
PageTableEntry *const kernelTables[] = {
(PageTableEntry *)0xFFFFFFFFFFFFF000,
(PageTableEntry *)0xFFFFFFFFFFE00000,
(PageTableEntry *)0xFFFFFFFFC0000000};
PageTableEntry *const userTables[] = {
(PageTableEntry *)0x0000007FFFFFF000,
(PageTableEntry *)0x0000007FFFE00000,
(PageTableEntry *)0x0000007FC0000000};
const unsigned long kernel::memory::page_size = 4096;
void kernel::memory::loadAddressSpace(AddressSpace &addressSpace)
{
unsigned long ttbr0 = addressSpace.getTableFrame() | ((unsigned long)addressSpace.getId() << 48);
set_ttbr0_el1(ttbr0);
asm volatile("DSB ISH");
asm volatile("TLBI VMALLE1");
asm volatile("DSB ISH");
asm volatile("ISB");
}
void kernel::memory::initializeTopTable(physaddr_t frame)
{
static PageTableEntry *scratchAddr = (PageTableEntry *)0xFFFFFFFFBFFFF000;
physaddr_t buffer = getPageFrame(scratchAddr);
setPageEntry(0, scratchAddr, frame, PAGE_RW);
for (int i = 0; i < 511; i++)
{
((unsigned long *)scratchAddr)[i] = 0;
}
scratchAddr[511].makeTableDescriptor(frame);
setPageEntry(0, scratchAddr, buffer, PAGE_RW);
}
size_t kernel::memory::getBlockSize(int level)
{
switch (level)
{
case 0:
return (1UL << 12);
case 1:
return (1UL << 21);
default:
return 0;
}
}
physaddr_t kernel::memory::getPageFrame(void *page)
{
PageTableEntry *const *tables = (unsigned long)page > (unsigned long)&__high_mem ? kernelTables : userTables;
unsigned long linearAddr = (unsigned long)page & 0x0000007FFFFFFFFF;
for (int i = 0; i < 2; i++)
{
int index = linearAddr >> (30 - i * 9);
if (!tables[i][index].present)
{
return 0;
}
else if (!tables[i][index].type)
{
return tables[i][index].physicalAddress();
}
}
if (!tables[2][linearAddr >> 12].present)
{
return 0;
}
return tables[2][linearAddr >> 12].physicalAddress();
}
void kernel::memory::setPageEntry(int level, void *page, physaddr_t frame, int flags)
{
if (level > 2)
{
return;
}
PageTableEntry *entry;
if ((unsigned long)page >= (unsigned long)&__high_mem)
{
page = (void *)((unsigned long)page - (unsigned long)&__high_mem);
unsigned long index = (unsigned long)page >> (12 + 9 * level);
entry = &kernelTables[2 - level][index];
}
else if ((unsigned long)page < 0x0000008000000000)
{
unsigned long index = (unsigned long)page >> (12 + 9 * level);
entry = &userTables[2 - level][index];
}
if (level == 0)
{
entry->makePageDescriptor(frame, flags);
}
else
{
entry->makeBlockDescriptor(frame, flags);
}
asm volatile("DSB ISH");
asm volatile("TLBI VMALLE1");
asm volatile("DSB ISH");
asm volatile("ISB");
}
void kernel::memory::setTableEntry(int level, void *page, physaddr_t table)
{
if (level == 0)
{
return;
}
PageTableEntry *entry;
if ((unsigned long)page >= (unsigned long)&__high_mem)
{
page = (void *)((unsigned long)page - (unsigned long)&__high_mem);
unsigned long index = (unsigned long)page >> (12 + 9 * level);
entry = &kernelTables[2 - level][index];
}
else if ((unsigned long)page < 0x0000008000000000)
{
unsigned long index = (unsigned long)page >> (12 + 9 * level);
entry = &userTables[2 - level][index];
}
entry->makeTableDescriptor(table);
asm volatile("DSB ISH");
asm volatile("TLBI VMALLE1");
asm volatile("DSB ISH");
asm volatile("ISB");
}
void kernel::memory::clearEntry(int level, void *page)
{
PageTableEntry *entry;
if ((unsigned long)page >= (unsigned long)&__high_mem)
{
page = (void *)((unsigned long)page - (unsigned long)&__high_mem);
unsigned long index = (unsigned long)page >> (12 + 9 * level);
entry = &kernelTables[2 - level][index];
}
else if ((unsigned long)page < 0x0000008000000000)
{
unsigned long index = (unsigned long)page >> (12 + 9 * level);
entry = &userTables[2 - level][index];
}
entry->clear();
asm volatile("DSB ISH");
asm volatile("TLBI VMALLE1");
asm volatile("DSB ISH");
asm volatile("ISB");
}
void fillTranslationTable(int faultLevel, int targetLevel, void *far)
{
// kernelLog(LogLevel::DEBUG, "fillTranslationTable(%i, %i, %016x)", faultLevel, targetLevel, far);
PageTableEntry *const *tables = nullptr;
if (far >= &__high_mem)
{
tables = kernelTables;
}
else
{
tables = userTables;
}
unsigned long farOffset = (unsigned long)far - (unsigned long)tables[targetLevel];
for (int i = (targetLevel + faultLevel - 4); i < targetLevel; i++)
{
unsigned long tableIndex = farOffset >> (12 + 9 * (targetLevel - i - 1));
physaddr_t tableFrame = pageAllocator.reserve(page_size);
if (tableFrame == PageAllocator::NOMEM)
{
kernelLog(LogLevel::PANIC, "Out of memory while allocating page table");
hacf();
}
tables[i][tableIndex].makeTableDescriptor(tableFrame);
asm volatile("DSB ISH");
asm volatile("TLBI VMALLE1");
asm volatile("DSB ISH");
asm volatile("ISB");
}
}
void handlePageFault(ExceptionClass type, SyndromeDataAbort syndrome)
{
void *far = get_far_el1();
switch (syndrome.statusCode)
{
case DataAbortStatus::ACCESS_FAULT_0:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ACCESS_FAULT_0), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ACCESS_FAULT_1:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ACCESS_FAULT_1), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ACCESS_FAULT_2:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ACCESS_FAULT_2), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ACCESS_FAULT_3:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ACCESS_FAULT_3), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ADDR_SIZE_FAULT_0:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ADDR_SIZE_FAULT_0), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ADDR_SIZE_FAULT_1:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ADDR_SIZE_FAULT_1), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ADDR_SIZE_FAULT_2:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ADDR_SIZE_FAULT_2), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::ADDR_SIZE_FAULT_3:
kernelLog(LogLevel::PANIC, "Unhandled page fault (ADDR_SIZE_FAULT_3), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::PERM_FAULT_0:
kernelLog(LogLevel::PANIC, "Unhandled page fault (PERM_FAULT_0), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::PERM_FAULT_1:
kernelLog(LogLevel::PANIC, "Unhandled page fault (PERM_FAULT_1), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::PERM_FAULT_2:
kernelLog(LogLevel::PANIC, "Unhandled page fault (PERM_FAULT_2), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::PERM_FAULT_3:
kernelLog(LogLevel::PANIC, "Unhandled page fault (PERM_FAULT_3), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::TRANSLATE_FAULT_0:
kernelLog(LogLevel::PANIC, "Unhandled page fault (TRANSLATE_FAULT_0), FAR_EL1 = %016x", far);
hacf();
break;
case DataAbortStatus::TRANSLATE_FAULT_1:
case DataAbortStatus::TRANSLATE_FAULT_2:
case DataAbortStatus::TRANSLATE_FAULT_3:
int level = (int)syndrome.statusCode & 3;
if (!syndrome.wnr)
{
kernelLog(LogLevel::PANIC, "Unhandled page fault (TRANSLATE_FAULT_%c), FAR_EL1 = %016x", '0' + level, get_far_el1());
hacf();
}
if (far >= kernelTables[0])
{
kernelLog(LogLevel::PANIC, "Unhandled page fault (TRANSLATE_FAULT_%c), FAR_EL1 = %016x", '0' + level, get_far_el1());
hacf();
}
else if (far >= kernelTables[1])
{
fillTranslationTable(level, 1, far);
}
else if (far >= kernelTables[2])
{
fillTranslationTable(level, 2, far);
}
else if (far >= userTables[0])
{
kernelLog(LogLevel::PANIC, "Unhandled page fault (TRANSLATE_FAULT_%c), FAR_EL1 = %016x", '0' + level, get_far_el1());
hacf();
}
else if (far >= userTables[1])
{
fillTranslationTable(level, 1, far);
}
else if (far >= userTables[2])
{
fillTranslationTable(level, 2, far);
}
else if (far == nullptr)
{
kernelLog(LogLevel::PANIC, "Null pointer exception.");
hacf();
}
else
{
kernelLog(LogLevel::PANIC, "Unhandled page fault (TRANSLATE_FAULT_%c), FAR_EL1 = %016x", '0' + level, get_far_el1());
hacf();
}
break;
}
}

View File

@@ -0,0 +1,18 @@
#include "addressspace.h"
using namespace kernel::memory;
AddressSpace::AddressSpace(physaddr_t frame, int id)
: topLevelTable(frame), id(id)
{
}
physaddr_t AddressSpace::getTableFrame() const
{
return topLevelTable;
}
int AddressSpace::getId() const
{
return id;
}

27
src/memory/addressspace.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef KERNEL_ADDRESSSPACE_H
#define KERNEL_ADDRESSSPACE_H
#include "util/hasrefcount.h"
#include "types/physaddr.h"
namespace kernel::memory
{
class AddressSpace : public HasRefcount
{
public:
AddressSpace(physaddr_t frame, int id);
physaddr_t getTableFrame() const;
int getId() const;
private:
physaddr_t topLevelTable;
int id;
};
};
#endif

344
src/memory/heap.cpp Normal file
View File

@@ -0,0 +1,344 @@
#include "heap.h"
#include "mmap.h"
#include "pageallocator.h"
#include "util/string.h"
#include "../containers/math.h"
#include <cstdint>
#include "util/log.h"
unsigned long *heap;
unsigned long *linked_list_start = nullptr; // Actually last block in heap, but start of ll
unsigned long *search_start = nullptr; // Set at start of search, don't maintain
unsigned long *search_previous = nullptr; // Must maintain for search
unsigned long *search_current = nullptr; // Must maintain for search
unsigned long *heap_end = nullptr;
// unsigned long num_free_blocks;
void init_heap(void *heap_ptr, unsigned long mem_size)
{
linked_list_start = nullptr;
search_start = nullptr;
search_previous = nullptr;
search_current = nullptr;
int overhead_adj_size = mem_size - (WORD_SIZE * OVERHEAD);
heap = (unsigned long *)heap_ptr;
heap[0] = overhead_adj_size / SIZE_T_SIZE; // Header size var
heap[1] = (unsigned long)nullptr;
heap[2] = 0; // Header flag var
heap[(mem_size / SIZE_T_SIZE) - 2] = heap[0];
heap[(mem_size / SIZE_T_SIZE) - 1] = heap[1];
linked_list_start = search_start = search_current = heap;
// num_free_blocks = 1;
heap_end = &heap[(mem_size / SIZE_T_SIZE)];
}
unsigned long next_power_of_2(unsigned long n)
{
unsigned count = 0;
// First n in the below condition
// is for the case where n is 0
if (n && !(n & (n - 1)))
{
return n;
}
while (n != 0)
{
n >>= 1;
count += 1;
}
return 1 << count;
}
void expand_heap(unsigned long size)
{
physaddr_t new_frame;
unsigned long adj_size = next_power_of_2(size);
if (size <= 4096)
{ // Min size
new_frame = kernel::memory::pageAllocator.reserve(4096);
}
else
{
new_frame = kernel::memory::pageAllocator.reserve(adj_size);
}
kernel::memory::map_region(heap_end, adj_size, new_frame, kernel::memory::PAGE_RW);
heap_end = heap_end + adj_size;
rebuild_list(search_current);
}
unsigned long *split_blk(unsigned long size, unsigned long *blk_to_split)
{
unsigned long *return_block = blk_to_split;
unsigned long *split_block = blk_to_split + (size / SIZE_T_SIZE) + OVERHEAD; // Current address + size + overhead to get address of new block
split_block[0] = blk_to_split[0] - (size / SIZE_T_SIZE) - OVERHEAD; // Set header size
split_block[1] = blk_to_split[1]; // What ever blk_to_split points to, split block now points to
split_block[2] = 0; // Flag
split_block[split_block[0] + OVERHEAD - 2] = split_block[0]; // Set footer size
split_block[split_block[0] + OVERHEAD - 1] = 0; // Set footer flag
// Maintain Linked list pointers
if (search_previous && search_previous[1])
{
search_previous[1] = (unsigned long)split_block;
}
if (blk_to_split == linked_list_start)
{
linked_list_start = split_block;
}
if (blk_to_split == search_previous)
{
search_previous = split_block;
}
if (blk_to_split == search_current)
{
search_current = split_block;
}
return_block[0] = size / SIZE_T_SIZE; // Set header size
return_block[1] = 0; // Reset next address to null
return_block[2] = 1; // Set flag
return_block[return_block[0] + OVERHEAD - 2] = return_block[0]; // Setting footer size
return_block[return_block[0] + OVERHEAD - 1] = 1; // Setting footer flag
return &return_block[3];
}
unsigned long *find_fit(unsigned long size)
{
search_start = search_current;
do
{
if ((search_current[0] * SIZE_T_SIZE) == size + OVERHEAD)
{ // Block is correct size, use it
unsigned long *return_block = search_current;
if (search_previous && search_current != search_start)
{
search_previous[1] = return_block[1]; // Update linked list, removing search next as it is now in use
}
search_current = (unsigned long *)return_block[1]; // Go to next block for future calls
return_block[2] = 1; // Set flag
return_block[return_block[0] + 2] = 1; // Set footer flag
return &return_block[3]; // Return address of start of useable memory
}
else if ((search_current[0] * SIZE_T_SIZE) > ALIGN(size + ((OVERHEAD + 1) * SIZE_T_SIZE * 2)))
{ // Block is oversized, split
return split_blk(size, search_current);
}
else if (search_current[1])
{ // Block is not right size and not end of linked list, iterate
search_previous = search_current;
search_current = (unsigned long *)search_current[1];
}
else
{ // End of list, go to start and iterate
search_previous = search_current;
search_current = linked_list_start;
}
} while (search_previous && search_start != search_current && search_current < heap_end);
return nullptr; // Failed to find block big enough
}
void *rmalloc(unsigned long size)
{
unsigned long blk_size = ALIGN(size);
unsigned long *block = find_fit(blk_size);
if (!block)
{ // Out of space, request more;
expand_heap(size);
block = find_fit(blk_size); // Try allocation again
if (!block)
{
return nullptr; // Panic, mem expansion failed
}
}
return block;
}
void rebuild_list(unsigned long *current)
{
unsigned long *ll_cur, *ll_prev;
ll_prev = linked_list_start; // First block in ll
ll_cur = heap; // First block in physical mem
// Rebuild ll WATCHME High computation since traversing whole ll?
while (ll_cur < heap_end)
{
while (ll_cur[2] != 0)
{
ll_cur = ll_cur + ll_cur[0] + OVERHEAD; // Start of next block
}
if (ll_cur != linked_list_start)
{
if (ll_cur == current)
{ // Set previous and current
search_previous = ll_prev;
search_current = ll_cur;
}
ll_prev[1] = (unsigned long)ll_cur;
ll_prev = ll_cur;
ll_cur = ll_cur + ll_cur[0] + OVERHEAD; // Start of next block
}
else
{
break; // End of heap
}
}
if (current == linked_list_start && linked_list_start[1])
{ // Edge Case when linked_list_start is current, move current to next node
search_previous = linked_list_start;
search_current = (unsigned long *)linked_list_start[1];
}
ll_prev[1] = 0; // End of list, set next addr to 0 so list doesn't loop
}
void merge(unsigned long *current)
{
unsigned long *previous, *next, *ll_cur, *ll_prev;
previous = next = nullptr;
if (current != heap)
{
if ((current - 1)[0] == 0)
{ // Previous block is free, merge
previous = (current - ((current - 2)[0] + OVERHEAD));
}
}
if ((current + current[0] + OVERHEAD + 2)[0] == 0 && (current + current[0] + OVERHEAD + 2) < heap_end)
{ // Next block is free, merge NOTE 2 to get to the flag on the next block
next = (current + current[0] + OVERHEAD);
}
if (previous)
{ // Merge previous
previous[0] = previous[0] + current[0] + OVERHEAD; // Set size
current[1] = 0; // NOTE Not required, just cleans up heap for debugging
if (current == linked_list_start)
{ // Need to maintain, otherwise rebuild will fail, all other pointers set in rebuild
linked_list_start = previous;
}
if (current == search_current)
{
search_current = previous;
}
previous[previous[0] + OVERHEAD - 2] = previous[0]; // Set footer size, flag is already set
current = previous; // Set this incase next is also being merged
// num_free_blocks--;
}
if (next)
{ // Merge next
current[0] = current[0] + next[0] + OVERHEAD; // Set size
next[1] = 0; // NOTE Not required, just cleans up heap
if (next == linked_list_start)
{ // Need to maintain, otherwise rebuild will fail, all other pointers set in rebuild
linked_list_start = current;
}
if (next == search_current)
{
search_current = current;
}
current[current[0] + OVERHEAD - 2] = current[0]; // Set footer size, flag is already set
// num_free_blocks--;
}
if (previous || next)
{ // Only rebuild list if merge occured
rebuild_list(current);
}
}
void rfree(void *ptr)
{
unsigned long *blk_free = (unsigned long *)ptr - 3; // Subtract 3 to get from start of data field to start of header
blk_free[2] = 0; // Set flag
blk_free[blk_free[0] + OVERHEAD - 1] = 0; // Set footer flag
// Insert into linked list
if (search_previous)
{ // Already a list, insert
blk_free[1] = search_previous[1]; // search_previous-> blk_free -> search_next
search_previous[1] = (unsigned long)blk_free;
}
else
{ // No list, start one
search_previous = linked_list_start;
search_previous[1] = (unsigned long)blk_free;
}
search_current = blk_free;
// num_free_blocks++;
// Check for merging
merge(blk_free);
}
void *realloc(void *ptr, unsigned long new_size)
{
unsigned long *return_blk, *blk_old;
int flag = 1; // Tries mem expansion twice before failing
blk_old = (unsigned long *)ptr - 3; // Subtract 3 to get from start of data field to start of header
if (!ptr)
{ // If pointer null, same as malloc
return rmalloc(new_size);
}
else if (blk_old[0] > new_size + OVERHEAD + 1)
{ // Need to shrink and free FIXME Potential Fragmentation, merge not called on freed block
return_blk = split_blk(new_size, blk_old);
rebuild_list(return_blk);
return return_blk;
}
// Need to expand, check if space in current heap
do
{ // CLEANME
return_blk = find_fit(new_size);
if (return_blk)
{ // Found space in current heap, copy data;
memcpy(return_blk, ptr, blk_old[0]);
}
// No mem left, expand
expand_heap(new_size);
flag--;
} while (flag >= 0);
return NULL; // Panic, expansion failed
}
/*Debug Fuctions*/
uint64_t xorshift64(uint64_t state) // Psuedo Random Number Generator
{
uint64_t x = state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return state = x;
}
uint64_t randinrange(uint64_t lower, uint64_t upper, uint64_t rand)
{
return (rand % (upper - lower + 1)) + lower;
}
void debug_bounds_check(unsigned long *a, unsigned long *b, uint64_t size_b)
{
unsigned long *blk1 = a - 3;
unsigned long *blk2 = b - 3;
if (blk1 == blk2)
{
return;
}
if (blk1 >= blk2 && blk1 < blk2 + size_b)
{
while (1)
{
}
}
}

137
src/memory/heap.h Normal file
View File

@@ -0,0 +1,137 @@
/*
Author: Sam Lane
*/
#ifndef _KERNEL_HEAP_H
#define _KERNEL_HEAP_H
#include <cstdint>
#define WORD_SIZE sizeof(unsigned long) // Either 4 or 8, we're 64 bit so 8
#define OVERHEAD 5
#define ALIGN(size) (((size) + (WORD_SIZE - 1)) & ~(WORD_SIZE - 1))
#define SIZE_T_SIZE (ALIGN(sizeof(unsigned long))) // header size
/**
* @brief Initialize heap at the memory address pointed to by
* heap_ptr of size mem_size. Sets initial block up with flags
* and all pointers used for traversing memory.
*
* @param heap_ptr
* @param mem_size
*/
void init_heap(void *heap_ptr, unsigned long mem_size);
/**
* @brief Expands avaliable heap memory by allocating and mapping
* additional pages.
*
* @param size
*/
void expand_heap(unsigned long size);
/**
* @brief Iterates through all of memory, adding free blocks
* to the linked list for later allocation.
*
* @param current
*/
void rebuild_list(unsigned long *current);
/**
* @brief Checks previous and next blocks of memory to see
* if they are free. If they are, a single block is created
* from them and rebuild list is called to fix the linked
* list pointers.
*
* @param current
*/
void merge(unsigned long *current);
/**
* @brief Frees memory by setting the free bit flag and calling
* merge to check surrounding blocks of memory. Ptr is the pointer
* to writable memory, so it is subtracted by 3 to get to the start
* of the memory block header.
*
* @param ptr
*/
void rfree(void *ptr);
/**
* @brief Allocates a memory block of size +5 for the header and footer
* info. The header bytes are, in order, size of the block in bytes, pointer to
* the next free block if in linked list, and in use flag. The footer
* bytes are, in order, size of block in bytes and in use flag. Returns a ptr
* to the user of the fourth byte in the allocated block, which is the first
* free byte of memory after the header.
*
* @param size
* @return void*
*/
void *rmalloc(unsigned long size);
/**
* @brief Takes a pointer to allocated memory. If pointer is null, operates the
* same as malloc. Otherwise, it either shrinks the allocated memory to fit in
* the new size, copying all memory up to the new size. If new size is larger,
* the heap is checked for a block of sufficient size, which is then allocated
* and the memory copied. If no block of sufficient size is found, memory is
* expanded and then the allocation is made.
*
* @param ptr
* @param new_size
* @return void*
*/
void *realloc(void *ptr, unsigned long new_size);
/**
* @brief Helper function used when expanding memory. Finds the next power
* of 2 larger than n and returns it.
*
* @param n
* @return unsigned long
*/
unsigned long next_power_of_2(unsigned long n);
/**
* @brief Takes a large block of memory and splits it into two smaller
* blocks, one of size size, and the other with the remaining size minus
* the overhead for the header and footers. The block of size size is
* returned
*
* @param size
* @param blk_to_split
* @return unsigned long*
*/
unsigned long *split_blk(unsigned long size, unsigned long *blk_to_split);
/**
* @brief Find fit traverses memory looking for free blocks of sufficient
* size. If a block is oversized, it is split into two via split_blk.
* Find_fit iterates over all free blocks of memory in the linked list
* until the starting free block is reached. It then fails to find a block
* of sufficient size and returns a nullptr.
*
* @param size
* @return unsigned long*
*/
unsigned long *find_fit(unsigned long size);
// Testing functions KILLME
#define SIZE 30
void debug_bounds_check(unsigned long *a, unsigned long *b, uint64_t size_b);
uint64_t xorshift64(uint64_t state);
uint64_t randinrange(uint64_t lower, uint64_t upper, uint64_t rand);
struct test_struct
{
unsigned long *pointer;
int size;
};
#endif

190
src/memory/memorymap.cpp Normal file
View File

@@ -0,0 +1,190 @@
/**
* Author: Nathan Giddings
*/
#include "memorymap.h"
using namespace kernel::memory;
MemoryMap::MemoryMap() : mapSize(0)
{}
int MemoryMap::place(MemoryType type, unsigned long location, unsigned long size)
{
if(mapSize <= capacity - 2)
{
insert(type, location, size);
int i = 0;
while(i >= 0)
{
i = trim(i);
}
return 0;
}
else
{
return -1;
}
}
int MemoryMap::size() const
{
return mapSize;
}
const MemoryMap::MemoryRegion& MemoryMap::operator[](int index) const
{
return map[index];
}
int MemoryMap::trim(int index)
{
if(index + 1 >= mapSize)
{
return -1;
}
MemoryRegion& left = map[index];
MemoryRegion& right = map[index + 1];
if(left.overlaps(right) && left.type == right.type)
{
left.size = (right.end() > left.end() ? right.end() : left.end()) - left.location;
remove(index + 1);
return index;
}
else if(left.overlaps(right) && left.type < right.type && right.contains(left))
{
remove(index);
return index;
}
else if(left.overlaps(right) && left.type < right.type && left.end() <= right.end())
{
left.size = (right.location > left.location) ? right.location - left.location : 0;
return index + 1;
}
else if(left.overlaps(right) && left.type < right.type)
{
MemoryRegion newRight(left.type, right.end(), left.end() - right.end());
left.size = (right.location > left.location) ? right.location - left.location : 0;
if (left.size == 0)
{
remove(index);
}
insert(newRight.type, newRight.location, newRight.size);
return index + 2;
}
else if(left.overlaps(right) && left.contains(right))
{
remove(index + 1);
return index;
}
else if(left.overlaps(right))
{
right.size = right.end() - left.end();
right.location = left.end();
return index + 1;
}
else if((left.end() == right.location) && left.type == right.type)
{
left.size = right.end() - left.location;
remove(index + 1);
return index;
}
else
{
return index + 1;
}
}
void MemoryMap::remove(int index)
{
if(index >= 0 && index < mapSize)
{
for(int i = index; i < mapSize - 1; i++)
{
map[i] = map[i + 1];
}
mapSize--;
}
}
void MemoryMap::insert(MemoryType type, unsigned long location, unsigned long size)
{
MemoryRegion newRegion(type, location, size);
unsigned int i = 0;
while(i < mapSize)
{
if(newRegion < map[i])
{
MemoryRegion buffer = newRegion;
newRegion = map[i];
map[i] = buffer;
}
i++;
}
map[i] = newRegion;
mapSize++;
}
MemoryMap::MemoryRegion::MemoryRegion()
: type(MemoryType::UNAVAILABLE), location(0), size(0)
{}
MemoryMap::MemoryRegion::MemoryRegion(MemoryType type, unsigned long location, unsigned long size)
: type(type), location(location), size(size)
{}
bool MemoryMap::MemoryRegion::operator<(const MemoryRegion& rhs) const
{
if(location == rhs.location)
{
return size < rhs.size;
}
else
{
return location < rhs.location;
}
}
bool MemoryMap::MemoryRegion::operator>(const MemoryRegion& rhs) const
{
if(location == rhs.location)
{
return size > rhs.size;
}
else
{
return location > rhs.location;
}
}
bool MemoryMap::MemoryRegion::overlaps(const MemoryRegion& rhs) const
{
if(rhs.location < location)
{
return rhs.end() > location;
}
else
{
return rhs.location < end();
}
}
bool MemoryMap::MemoryRegion::contains(const MemoryRegion& rhs) const
{
return (rhs.location >= location) && (rhs.end() <= end());
}
unsigned long MemoryMap::MemoryRegion::end() const
{
return location + size;
}
MemoryMap::MemoryType MemoryMap::MemoryRegion::getType() const
{
return type;
}
unsigned long MemoryMap::MemoryRegion::getLocation() const
{
return location;
}

220
src/memory/memorymap.h Normal file
View File

@@ -0,0 +1,220 @@
/**
* Author: Nathan Giddings
*/
#ifndef KERNEL_MEMORYMAP_H
#define KERNEL_MEMORYMAP_H
namespace kernel::memory
{
/**
* @brief Describes in broad terms the layout of an address space
* (or parts(s) of an address space). Most useful for getting an initial
* layout of the physical address space.
*/
class MemoryMap
{
public:
/**
* @brief Enumerator declaring the various memory types
*/
enum class MemoryType
{
/**
* @brief Memory at the described location is available for use as RAM.
*/
AVAILABLE = 1,
/**
* @brief Memory at the described location is unavailable for use for
* some unspecified reason.
*/
UNAVAILABLE = 2,
/**
* @brief Memory at this region is used for MMIO and should not be used
* as RAM.
*/
MMIO = 3,
/**
* @brief Memory at the described region is defective and cannot be used.
*/
DEFECTIVE = 4
};
/**
* @brief Describes a particular region of memory starting at `location`
* and extending `size` bytes, with its use indicated by `type`.
*/
class MemoryRegion
{
friend class MemoryMap;
public:
/**
* @brief Default contructor
*/
MemoryRegion();
/**
* @brief Contructs a MemoryRegion object
* @param type
* @param location
* @param size
*/
MemoryRegion(MemoryType type, unsigned long location, unsigned long size);
/**
* @brief Compares the location and size of two MemoryRegions
* @param rhs object to compare to
* @return true if `rhs` should be placed after `lhs` in a MemoryMap.
*/
bool operator<(const MemoryRegion& rhs) const;
/**
* @brief Compares the location and size of two MemoryRegions
* @param rhs object to compare to
* @return true if `rhs` should be placed before `lhs` in a MemoryMap.
*/
bool operator>(const MemoryRegion& rhs) const;
/**
* @brief Checks if this object overlaps the region described by `rhs`
* @param rhs object to compare to
* @return true if the two regions overlap
*/
bool overlaps(const MemoryRegion& rhs) const;
/**
* @brief Checks if this object completely contains the region
* described by `rhs`
*
* @param rhs object to compare to
* @return true if `rhs` is completely contained within this region
*/
bool contains(const MemoryRegion& rhs) const;
/**
* @brief Computes the location at the end of this memory region,
* given by (`location` + `size`).
*
* @return The first location after this memory region.
*/
unsigned long end() const;
/**
* @return An enumerator representing the type of memory in this region.
*/
MemoryType getType() const;
/**
* @return The starting location of this region.
*/
unsigned long getLocation() const;
private:
/**
* @brief The type or use of the memory in this region. Higher values
* overwrite lower values as the memory map is built.
*/
MemoryType type;
/**
* @brief The location of the beginning of this memory region.
*/
unsigned long location;
/**
* @brief The size of this memory region in bytes.
*/
unsigned long size;
};
/**
* @brief Default constructor.
*/
MemoryMap();
/**
* @brief Places a new region into the memory map. Regions with higher type
* values overwrite overlapping regions with lower type values. The map's
* array is modified so that no two regions overlap, and the regions are
* by location in ascending order.
*
* @param type the type of new new region to place
* @param location the location of the new region to place
* @param size the size of the new region to place
* @return Nonzero if there is insufficient room left in the map's internal
* array.
*/
int place(MemoryType type, unsigned long location, unsigned long size);
/**
* @brief Gets the size of the MemoryRegion array
* @return the number of MemoryRegions inside this map.
*/
int size() const;
/**
* @brief Access a particular entry in the memory map
* @param index the index of the entry to access
* @return a reference to the requested entry
*/
const MemoryRegion& operator[](int index) const;
private:
/**
* @brief Maximum number of different regions that can be inside a memory map
*/
static const unsigned long capacity = 16;
/**
* @brief Current number of regions inside this memory map. Any objects
* in `map` at or after index `mapSize` will be uninitialized.
*/
int mapSize;
/**
* @brief Array of memory regions describing the layout of an address space.
*/
MemoryRegion map[capacity];
/**
* @brief Modifies the map array starting at `index` to ensure that
* subsequent regions do not overlap, and that adjacent regions of the
* same type are merged.
* @param index the array index to start operation on
* @return the next index at which this method should be called, or -1 if
* the end of the array has been reached
*/
int trim(int index);
/**
* @brief Removes a particular map entry from the array and moves
* subsequent entries backwards one space.
*
* @param index the index of the element to be removed
*/
void remove(int index);
/**
* @brief Inserts a new region into the map such that the array remains
* sorted in ascending order
*
* @param type the type of the new region to insert
* @param location the location of the new region to insert
* @param size the size of the new region to insert
*/
void insert(MemoryType type, unsigned long location, unsigned long size);
};
}
#endif

46
src/memory/mmap.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "mmap.h"
#include "pageallocator.h"
#include "util/log.h"
using namespace kernel::memory;
int idCounter = 1;
AddressSpace *kernel::memory::createAddressSpace()
{
physaddr_t frame = pageAllocator.reserve(getBlockSize(0));
if (frame == PageAllocator::NOMEM)
{
return nullptr;
}
initializeTopTable(frame);
AddressSpace *obj = new AddressSpace(frame, idCounter);
idCounter++;
return obj;
}
void kernel::memory::destoryAddressSpace(AddressSpace &addressSpace)
{
// kernelLog(LogLevel::WARNING, "destroyAddressSpace(id = %i) called: not implemented.", addressSpace.getId());
// TODO
}
int kernel::memory::map_region(void *addr, size_t size, physaddr_t frame, int flags)
{
// TODO: Make this more efficient. Map larger blocks when possible.
for (unsigned long p = 0; p < size; p += getBlockSize(0))
{
setPageEntry(0, addr + p, frame + p, flags);
}
return 0;
}
physaddr_t kernel::memory::unmap_region(void *addr, size_t size)
{
for (unsigned long p = 0; p < size; p += getBlockSize(0))
{
clearEntry(0, addr + p);
}
return 0;
}

152
src/memory/mmap.h Normal file
View File

@@ -0,0 +1,152 @@
#ifndef KERNEL_MMAP_H
#define KERNEL_MMAP_H
#include "addressspace.h"
#include "types/physaddr.h"
#include <cstddef>
namespace kernel::memory
{
/**
* @brief Size in bytes of a single page
*/
extern const unsigned long page_size;
/**
* @brief Permission flags for controlling memory access at page level
*/
enum PageFlags
{
/**
* @brief When set, page is writable. When clear, page is read-only.
*/
PAGE_RW = (1 << 0),
/**
* @brief When set, page is accesable from usermode. When clear, page is
* only accessable in kernelmode.
*/
PAGE_USER = (1 << 1),
/**
* @brief When set, page can contain executable code. When clear, code
* in this page cannot be executed.
*/
PAGE_EXE = (1 << 2)
};
/**
* @brief Creates and initialzes a new address space, the constructs a
* corresponding AddressSpace object.
*
* @return a pointer to a newly allocated AddressSpace object
*/
AddressSpace *createAddressSpace();
/**
* @brief Frees all physical memory associated with the given address space.
* The address space will no longer be usable, and the caller is expected to
* delete the relevant AddressSpace object.
*
* @param addressSpace object describing the address space to destory.
*/
void destoryAddressSpace(AddressSpace &addressSpace);
/**
* @brief Switches to the provided address space. Calls to `map_region` and
* other such functions will modify only the currently active address space
* (details on this may be platform specific).
*
* @param addressSpace address space to load
*/
void loadAddressSpace(AddressSpace &addressSpace);
/**
* @brief Map the region of memory starting at `addr` to frames starting at `frame`.
* @param addr linear address of the region to map
* @param size size in bytes of the region to map (should be multiple of page size)
* @param frame starting physical address to map this region to
* @param flags permission flags for the pages to map
* @return 0 upon success, nonzero upon failure
*/
int map_region(void *addr, size_t size, physaddr_t frame, int flags);
/**
* @brief Unmap the region of memory starting at `addr`. Does not free any
* frames; this must be done by caller.
*
* @param addr Starting address of region to unmap
* @param size Size in bytes of region to unmap
* @return Frame pointed to by `addr`
*/
physaddr_t unmap_region(void *addr, size_t size);
/**
* @brief Initializes the given frame as a top-level translation table.
*
* Implementation of this function is platform-dependent.
*
* @param frame Frame to store new top table in
*/
void initializeTopTable(physaddr_t frame);
/**
* @brief Gets the size of a page or block at the given level. Level 0 is
* the smallest available granularity.
*
* Implementation of this function is platform-dependent.
*
* @param level Translation level to use
* @return Size in bytes of block at given level
*/
size_t getBlockSize(int level);
/**
* @brief Gets the frame referenced by virtual address `page`, if it exists.
*
* Implementation of this function is platform-dependent.
*
* @param page Virtual address for with to get corresponding frame
* @return Frame pointed to by `page`, or 0 if no frame exists.
*/
physaddr_t getPageFrame(void *page);
/**
* @brief Writes a new page entry for the given page, pointing to the given
* frame. The size of the page is implied by `level`.
*
* Implementation of this function is platform-dependent.
*
* @param level Translation level to use
* @param page Virtual address to map
* @param frame Frame to point the new page to
* @param flags Permission flags for the new page
*/
void setPageEntry(int level, void *page, physaddr_t frame, int flags);
/**
* @brief Writes a new translation table entry for the virtual memory region
* starting at `page`. The new table is backed by `table`.
*
* Implementation of this function is platform-dependent.
*
* @param level Translation level of the new table entry. 0 is invalid, as it
* is always the final translation level.
* @param page Virtual address for the new table entry
* @param table Frame to store the new translation table
*/
void setTableEntry(int level, void *page, physaddr_t table);
/**
* @brief Clears the translation table entry corresponding to this location,
* and marks it as 'not present'.
*
* @param level Translation level of the entry to clear
* @param page Virtual address of the page corresponding to the entry to clear
*/
void clearEntry(int level, void *page);
}
#endif

32
src/memory/new.cpp Normal file
View File

@@ -0,0 +1,32 @@
#include "new.h"
#include "heap.h"
void *operator new(size_t size)
{
return rmalloc(size);
}
void operator delete(void *ptr)
{
rfree(ptr);
}
void *operator new[](size_t size)
{
return rmalloc(size);
}
void operator delete[](void *ptr)
{
return rfree(ptr);
}
void operator delete(void *ptr, unsigned long sz)
{
return rfree(ptr);
}
void operator delete[](void *ptr, unsigned long sz)
{
return rfree(ptr);
}

34
src/memory/new.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef KERNEL_NEW_H
#define KERNEL_NEW_H
#include <stddef.h>
void *operator new(size_t size);
void operator delete(void* ptr);
void *operator new[](size_t size);
void operator delete[](void *ptr);
void operator delete(void *ptr, unsigned long sz);
void operator delete[](void *ptr, unsigned long sz);
inline void *operator new(size_t, void *p) throw()
{
return p;
}
inline void *operator new[](size_t, void *p) throw()
{
return p;
}
inline void operator delete(void *, void *) throw()
{ }
inline void operator delete[](void *, void *) throw()
{ }
#endif

View File

@@ -0,0 +1,135 @@
#include "pageallocator.h"
#include "util/math.h"
using namespace kernel::memory;
PageAllocator kernel::memory::pageAllocator;
unsigned long PageAllocator::mapSize(MemoryMap &map, unsigned long blockSize)
{
int index = map.size() - 1;
while (map[index].getType() != MemoryMap::MemoryType::AVAILABLE)
{
index--;
}
return 1UL << llog2(sizeof(PageAllocator::Block) * map[index].end() / blockSize);
}
PageAllocator::PageAllocator()
{
this->blockMap = nullptr;
this->blockMapSize = 0;
this->maxKVal = 0;
this->blockSize = 0;
this->offset = 0;
this->freeBlockCount = 0;
}
PageAllocator::PageAllocator(MemoryMap &map, void *mapBase, unsigned long blockSize)
{
this->blockMap = (Block *)mapBase;
this->blockSize = blockSize;
this->blockMapSize = mapSize(map, blockSize);
this->offset = 0;
this->freeBlockCount = 0;
this->maxKVal = llog2(blockMapSize / sizeof(Block));
for (int i = 0; i <= maxKVal; i++)
{
availList[i].linkf = &availList[i];
availList[i].linkb = &availList[i];
}
for (int i = 0; i < blockMapSize / sizeof(Block); i++)
{
blockMap[i] = Block(nullptr, nullptr, 0, Block::RESERVED);
}
for (int i = 0; i < map.size(); i++)
{
if (map[i].getType() != MemoryMap::MemoryType::AVAILABLE)
{
continue;
}
unsigned long location = map[i].getLocation() + blockSize - 1;
location -= location % blockSize;
while (location + blockSize <= map[i].end())
{
insert(location / blockSize, 0);
location += blockSize;
freeBlockCount++;
}
}
}
physaddr_t PageAllocator::reserve(unsigned long size)
{
unsigned long k = llog2((size - 1) / blockSize + 1);
for (unsigned long j = k; j <= maxKVal; j++)
{
if (availList[j].linkf != &availList[j])
{
Block *block = availList[j].linkb;
availList[j].linkb = block->linkb;
availList[j].linkb->linkf = &availList[j];
block->tag = Block::RESERVED;
while (j > k)
{
j--;
Block *buddy = block + (1UL << j);
*buddy = Block(&availList[j], &availList[j], j, Block::FREE);
block->kval = j;
availList[j].linkb = buddy;
availList[j].linkf = buddy;
}
unsigned long index = block - blockMap;
freeBlockCount -= 1UL << k;
return offset + index * blockSize;
}
}
return NOMEM;
}
unsigned long PageAllocator::free(physaddr_t location)
{
unsigned long index = (location - offset) / blockSize;
unsigned long k = blockMap[index].kval;
insert(index, k);
return (1UL << k) * blockSize;
}
void PageAllocator::insert(unsigned long index, unsigned long k)
{
freeBlockCount += 1UL << k;
while (k < maxKVal)
{
unsigned long buddyIndex = index ^ (1UL << k);
if (blockMap[buddyIndex].tag != Block::FREE || blockMap[buddyIndex].kval != k)
{
break;
}
blockMap[buddyIndex].linkb->linkf = blockMap[buddyIndex].linkf;
blockMap[buddyIndex].linkf->linkb = blockMap[buddyIndex].linkb;
blockMap[buddyIndex].tag = Block::RESERVED;
k++;
if (buddyIndex < index)
{
index = buddyIndex;
}
}
Block *p = availList[k].linkf;
blockMap[index] = Block(p, &availList[k], k, Block::FREE);
p->linkb = &blockMap[index];
availList[k].linkf = &blockMap[index];
}
PageAllocator::Block::Block()
: linkf(nullptr), linkb(nullptr), kval(0), tag(RESERVED)
{
}
PageAllocator::Block::Block(Block *linkf, Block *linkb, unsigned long kval, unsigned long tag)
: linkf(linkf), linkb(linkb), kval(kval), tag(tag)
{
}

116
src/memory/pageallocator.h Normal file
View File

@@ -0,0 +1,116 @@
/**
* Author: Nathan Giddings
*
* Code adapted from personal project: https://github.com/ngiddings/quark-libmalloc/blob/master/include/libmalloc/buddy_alloc.h
*/
#ifndef PAGE_ALLOCATOR_H
#define PAGE_ALLOCATOR_H
#include "memorymap.h"
#include "types/physaddr.h"
namespace kernel::memory
{
/**
* @brief Manages the reservation and freeing of physical memory pages.
*/
class PageAllocator
{
public:
static const physaddr_t NOMEM = (physaddr_t)(~0);
static unsigned long mapSize(MemoryMap& map, unsigned long blockSize);
PageAllocator();
/**
* @brief Constructs the page allocator's internal data structures from an
* existing description of the memory layout.
*
* @param map a description of the physical memory layout
* @param mapBase location
* @param blockSize the size in bytes of a single block of memory
*/
PageAllocator(MemoryMap& map, void *mapBase, unsigned long blockSize);
/**
* @brief Reserves a chunk of memory at least `size` bytes long. The
* reserved physical memory will be contiguous; therefore, the allocator
* can potentially fail due to fragmentation.
*
* @param size the minimum number of bytes to reserve
* @return the physical address of the newly allocated chunk of memory, or
* NOMEM upon failure.
*/
physaddr_t reserve(unsigned long size);
/**
* @brief
* @param location
* @return
*/
unsigned long free(physaddr_t location);
private:
class Block
{
public:
static const unsigned long RESERVED = 0;
static const unsigned long FREE = 1;
Block();
Block(Block *linkf, Block *linkb, unsigned long kval, unsigned long tag);
Block *linkb;
Block *linkf;
unsigned long kval;
unsigned long tag;
};
/**
* @brief The number of distinct block sizes that are supported. Serves
* as the maximum possible value of `maxKVal`, and defines the size of
* the largest possible block: (2 ^ (availListSize-1)) * blockSize.
*/
static const int availListSize = 32;
Block availList[availListSize];
Block *blockMap;
unsigned long blockMapSize;
unsigned long maxKVal;
unsigned long blockSize;
unsigned long offset;
unsigned long freeBlockCount;
/**
* @brief Inserts a new block into the appropriate linked list, performing
* mergers with buddy blocks as needed.
*
* @param index
* @param k
*/
void insert(unsigned long index, unsigned long k);
};
extern PageAllocator pageAllocator;
}
#endif

10
src/mmgmt.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef KERNEL_MEMORY
#define KERNEL_MEMORY
#include "memory/heap.h"
#include "memory/pageallocator.h"
#include "memory/new.h"
#include "memory/mmap.h"
#include "memory/addressspace.h"
#endif

View File

@@ -0,0 +1,138 @@
#include "sched/context.h"
#include "util/string.h"
kernel::sched::Context::Context()
{
for (int i = 0; i < 64; i++)
{
fpRegs[i] = 0;
}
for (int i = 0; i < 31; i++)
{
gpRegs[i] = i;
}
stackPointer = 0;
programCounter = 0;
programStatus = 0;
fpcr = 0;
fpsr = 0;
}
kernel::sched::Context::Context(void *pc)
{
for (int i = 0; i < 64; i++)
{
fpRegs[i] = 0;
}
for (int i = 0; i < 31; i++)
{
gpRegs[i] = i;
}
stackPointer = 0;
programCounter = (uint64_t)pc;
programStatus = 0;
fpcr = 0;
fpsr = 0;
kernelStack = 0;
}
kernel::sched::Context::Context(void *pc, void *sp)
{
for (int i = 0; i < 64; i++)
{
fpRegs[i] = 0;
}
for (int i = 0; i < 31; i++)
{
gpRegs[i] = i;
}
stackPointer = (uint64_t)sp;
programCounter = (uint64_t)pc;
programStatus = 0;
fpcr = 0;
fpsr = 0;
kernelStack = 0;
}
kernel::sched::Context::Context(void *pc, void *sp, void *ksp)
{
for (int i = 0; i < 64; i++)
{
fpRegs[i] = 0;
}
for (int i = 0; i < 31; i++)
{
gpRegs[i] = i;
}
stackPointer = (uint64_t)sp;
programCounter = (uint64_t)pc;
programStatus = 0;
fpcr = 0;
fpsr = 0;
kernelStack = (uint64_t)ksp;
}
void kernel::sched::Context::functionCall(void *func_ptr, void *returnLoc, unsigned long arg)
{
programCounter = (unsigned long)func_ptr;
gpRegs[30] = (unsigned long)returnLoc;
gpRegs[0] = arg;
}
void kernel::sched::Context::setProgramCounter(void *pc)
{
programCounter = (uint64_t)pc;
}
void *kernel::sched::Context::getProgramCounter() const
{
return (void *)programCounter;
}
void kernel::sched::Context::setStackPointer(void *sp)
{
stackPointer = (uint64_t)sp;
}
void *kernel::sched::Context::getStackPointer() const
{
return (void *)stackPointer;
}
void kernel::sched::Context::setKernelStack(void *sp)
{
kernelStack = (uint64_t)sp;
}
void *kernel::sched::Context::getKernelStack() const
{
return (void *)kernelStack;
}
void kernel::sched::Context::setProcessArgs(int argc, char **argv, char **envp)
{
gpRegs[0] = (uint64_t)argc;
gpRegs[1] = (uint64_t)argv;
gpRegs[2] = (uint64_t)envp;
}
void kernel::sched::Context::setReturnValue(unsigned long v)
{
gpRegs[0] = v;
}
void kernel::sched::Context::pushLong(unsigned long v)
{
unsigned long *sp = (unsigned long *)stackPointer;
*--sp = v;
stackPointer = (unsigned long)sp;
}
void kernel::sched::Context::pushString(const char *str)
{
int len = strlen(str) + 1;
len += 15;
len -= len % 16;
stackPointer -= len;
strcpy((char *)stackPointer, str);
}

View File

@@ -0,0 +1,58 @@
.section ".text"
.global load_context
load_context:
// Load FP registers from (*x0)
ld4 {v0.2d, v1.2d, v2.2d, v3.2d}, [x0], #64
ld4 {v4.2d, v5.2d, v6.2d, v7.2d}, [x0], #64
ld4 {v8.2d, v9.2d, v10.2d, v11.2d}, [x0], #64
ld4 {v12.2d, v13.2d, v14.2d, v15.2d}, [x0], #64
ld4 {v16.2d, v17.2d, v18.2d, v19.2d}, [x0], #64
ld4 {v20.2d, v21.2d, v22.2d, v23.2d}, [x0], #64
ld4 {v24.2d, v25.2d, v26.2d, v27.2d}, [x0], #64
ld4 {v28.2d, v29.2d, v30.2d, v31.2d}, [x0], #64
// Skip X0-X3 (we need to use them as scratch registers)
add x0, x0, #32
// Load X4-X30, SP
ldp x4, x5, [x0], #16
ldp x6, x7, [x0], #16
ldp x8, x9, [x0], #16
ldp x10, x11, [x0], #16
ldp x12, x13, [x0], #16
ldp x14, x15, [x0], #16
ldp x16, x17, [x0], #16
ldp x18, x19, [x0], #16
ldp x20, x21, [x0], #16
ldp x22, x23, [x0], #16
ldp x24, x25, [x0], #16
ldp x26, x27, [x0], #16
ldp x28, x29, [x0], #16
ldp x30, x1, [x0], #16
msr sp_el0, x1
// Load PC, status register
ldp x2, x3, [x0], #16
msr elr_el1, x2
msr spsr_el1, x3
// Load FP control & status
ldp x2, x3, [x0], #16
msr fpcr, x2
msr fpsr, x3
// Load kernel SP
ldp x2, x3, [x0, #-8]
mov sp, x3
// Go back to X0-X3
sub x0, x0, #288
// Load X2 & X3
ldp x2, x3, [x0, #16]
// Load X0 & X1
ldp x0, x1, [x0]
eret

14
src/sched/actiontype.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef KERNEL_ACTIONTYPE_H
#define KERNEL_ACTIONTYPE_H
namespace kernel::sched
{
enum class ActionType
{
NONE,
HANDLER,
KILL
};
}
#endif

70
src/sched/context.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef KERNEL_CONTEXT_H
#define KERNEL_CONTEXT_H
#include <cstdint>
namespace kernel::sched
{
class Context
{
public:
Context();
Context(void *pc);
Context(void *pc, void *sp);
Context(void *pc, void *sp, void *ksp);
void functionCall(void *func_ptr, void *returnLoc, unsigned long arg);
void setProgramCounter(void *pc);
void *getProgramCounter() const;
void setStackPointer(void *sp);
void *getStackPointer() const;
void setKernelStack(void *sp);
void *getKernelStack() const;
void setProcessArgs(int argc, char **argv, char **envp);
void setReturnValue(unsigned long v);
void pushLong(unsigned long v);
void pushString(const char *str);
private:
#if defined __aarch64__
uint64_t fpRegs[64];
uint64_t gpRegs[31];
uint64_t stackPointer;
uint64_t programCounter;
uint64_t programStatus;
uint64_t fpcr;
uint64_t fpsr;
uint64_t kernelStack;
#else
#error "Platform not supported"
#endif
};
extern "C" void load_context(const Context *ctx);
}
#endif

380
src/sched/process.cpp Normal file
View File

@@ -0,0 +1,380 @@
#include "process.h"
#include "memory/new.h"
#include "memory/mmap.h"
#include "types/status.h"
#include "util/log.h"
#include "memory/heap.h"
pid_t kernel::sched::Process::nextPidVal = 1;
pid_t kernel::sched::Process::nextPid()
{
pid_t v = nextPidVal;
nextPidVal++;
return v;
}
kernel::sched::Process::Process()
: pid(0), parent(0), state(State::ACTIVE), ctx(), addressSpace(nullptr), backupCtx(nullptr), files()
{
for (int i = 0; i < MAX_SIGNAL; i++)
{
signalHandlers[i].type = ActionType::NONE;
}
}
kernel::sched::Process::Process(pid_t pid, pid_t parent, void *entry, void *stack, void *kernelStack, kernel::memory::AddressSpace *addressSpace)
: pid(pid), parent(parent), state(State::ACTIVE), ctx(entry, stack, kernelStack), addressSpace(addressSpace), backupCtx(nullptr), files()
{
addressSpace->addReference();
for (int i = 0; i < MAX_SIGNAL; i++)
{
signalHandlers[i].type = ActionType::NONE;
}
}
kernel::sched::Process::Process(Process &other)
: files()
{
pid = other.pid;
parent = other.parent;
ctx = other.ctx;
state = other.state;
addressSpace = other.addressSpace;
if (addressSpace != nullptr)
{
addressSpace->addReference();
}
if (other.backupCtx == nullptr)
{
backupCtx = nullptr;
}
else
{
backupCtx = new Context(*other.backupCtx);
}
for (int i = 0; i < MAX_SIGNAL; i++)
{
signalHandlers[i].type = other.signalHandlers[i].type;
signalHandlers[i].handler = other.signalHandlers[i].handler;
signalHandlers[i].trampoline = other.signalHandlers[i].trampoline;
signalHandlers[i].userdata = other.signalHandlers[i].userdata;
}
for (int fd : other.files)
{
storeFileContext(other.files.get(fd), fd);
}
}
kernel::sched::Process::~Process()
{
addressSpace->removeReference();
if (addressSpace->getRefCount() <= 0)
{
kernel::memory::destoryAddressSpace(*addressSpace);
delete addressSpace;
}
if (backupCtx != nullptr)
{
delete backupCtx;
}
for (int fd : files)
{
closeFileContext(fd);
}
}
int kernel::sched::Process::exec(void *pc, void *stack, void *kernelStack, kernel::memory::AddressSpace *addressSpace)
{
if (state != State::ACTIVE)
{
// This will probably break if called during a signal handler
return -1;
}
if (this->addressSpace != nullptr)
{
this->addressSpace->removeReference();
if (this->addressSpace->getRefCount() <= 0)
{
kernel::memory::destoryAddressSpace(*this->addressSpace);
delete this->addressSpace;
}
}
this->addressSpace = addressSpace;
this->addressSpace->addReference();
ctx.setProgramCounter(pc);
ctx.setStackPointer(stack);
ctx.setKernelStack(kernelStack);
return 0;
}
kernel::sched::Process *kernel::sched::Process::clone(pid_t pid, void *pc, void *stack, void *userdata)
{
using namespace kernel::fs;
void *base = rmalloc(1UL << 16);
if (base == nullptr)
{
return nullptr;
}
void *kernelStack = (void *)((unsigned long)base + (1UL << 16));
Process *copy = new Process(pid, this->pid, pc, stack, kernelStack, this->addressSpace);
if (copy == nullptr)
{
return nullptr;
}
copy->getContext()->functionCall(pc, nullptr, (unsigned long)userdata);
for (int fd : files)
{
copy->storeFileContext(files.get(fd)->copy(), fd);
}
return copy;
}
kernel::sched::Context *kernel::sched::Process::getContext()
{
return &ctx;
}
void kernel::sched::Process::storeContext(kernel::sched::Context *newCtx)
{
ctx = *newCtx;
}
kernel::sched::Process::State kernel::sched::Process::getState() const
{
return state;
}
void kernel::sched::Process::setState(State newState)
{
state = newState;
}
pid_t kernel::sched::Process::getPid() const
{
return pid;
}
pid_t kernel::sched::Process::getParent() const
{
return parent;
}
kernel::memory::AddressSpace *kernel::sched::Process::getAddressSpace()
{
return addressSpace;
}
void kernel::sched::Process::setSignalAction(int signal, void (*handler)(void *), void (*trampoline)(void), void *userdata)
{
if (signal < 0 || signal >= MAX_SIGNAL)
{
return;
}
if (handler == nullptr)
{
signalHandlers[signal].type = ActionType::NONE;
signalHandlers[signal].handler = nullptr;
signalHandlers[signal].trampoline = nullptr;
signalHandlers[signal].userdata = nullptr;
}
else
{
signalHandlers[signal].type = ActionType::HANDLER;
signalHandlers[signal].handler = handler;
signalHandlers[signal].trampoline = trampoline;
signalHandlers[signal].userdata = userdata;
}
}
int kernel::sched::Process::signalTrigger(int sig)
{
if (sig < 0 || sig >= MAX_SIGNAL)
{
return 0;
}
switch (signalHandlers[sig].type)
{
case ActionType::NONE:
return 0;
case ActionType::HANDLER:
if (state == State::SIGNAL)
{
return -1;
}
backupCtx = new Context(ctx);
ctx.functionCall((void *)signalHandlers[sig].handler,
(void *)signalHandlers[sig].trampoline,
(unsigned long)signalHandlers[sig].userdata);
state = State::SIGNAL;
return 0;
case ActionType::KILL:
return 1;
}
}
void kernel::sched::Process::signalReturn()
{
if (state != State::SIGNAL)
{
return;
}
ctx = *backupCtx;
delete backupCtx;
backupCtx = nullptr;
state = State::ACTIVE;
}
void kernel::sched::Process::storeProgramArgs(char *const argv[], char *const envp[])
{
int envc = 0;
while (envp[envc] != nullptr)
{
envc++;
}
int argc = 0;
while (argv[argc] != nullptr)
{
argc++;
}
char **argArray = new char *[argc];
char **envArray = new char *[envc + 1];
for (int i = argc - 1; i >= 0; i--)
{
ctx.pushString(argv[i]);
argArray[i] = (char *)ctx.getStackPointer();
}
for (int i = envc - 1; i >= 0; i--)
{
ctx.pushString(envp[i]);
envArray[i] = (char *)ctx.getStackPointer();
}
envArray[envc] = nullptr;
if (argc % 2 == 1)
{
ctx.pushLong(0);
}
for (int i = argc - 1; i >= 0; i--)
{
ctx.pushLong((unsigned long)argArray[i]);
}
void *argPtr = ctx.getStackPointer();
if ((envc + 1) % 2 == 1)
{
ctx.pushLong(0);
}
for (int i = envc; i >= 0; i--)
{
ctx.pushLong((unsigned long)envArray[i]);
}
void *envPtr = ctx.getStackPointer();
ctx.setProcessArgs(argc, (char **)argPtr, (char **)envPtr);
}
kernel::fs::FileContext *kernel::sched::Process::getFileContext(int fd) const
{
if (files.contains(fd))
{
return files.get(fd);
}
else
{
return nullptr;
}
}
int kernel::sched::Process::storeFileContext(kernel::fs::FileContext *f)
{
int fd = files.size();
files.insert(fd, f);
f->addReference();
return fd;
}
int kernel::sched::Process::storeFileContext(kernel::fs::FileContext *f, int fd)
{
if (files.contains(fd))
{
return EEXISTS;
}
else
{
files.insert(fd, f);
f->addReference();
return ENONE;
}
}
int kernel::sched::Process::closeFileContext(int fd)
{
using namespace kernel::fs;
if (!files.contains(fd))
{
return ENOFILE;
}
FileContext *fc = files.get(fd);
files.remove(fd);
fc->removeReference();
if (fc->getRefCount() <= 0)
{
// kernelLog(LogLevel::DEBUG, "Freeing file context %i", fd);
delete fc;
}
return ENONE;
}
kernel::sched::Process &kernel::sched::Process::operator=(Process &other)
{
pid = other.pid;
parent = other.parent;
ctx = other.ctx;
state = other.state;
if (addressSpace != nullptr && addressSpace != other.addressSpace)
{
addressSpace->removeReference();
if (addressSpace->getRefCount() <= 0)
{
kernel::memory::destoryAddressSpace(*addressSpace);
delete addressSpace;
}
}
addressSpace = other.addressSpace;
if (addressSpace != nullptr)
{
addressSpace->addReference();
}
if (other.backupCtx == nullptr)
{
backupCtx = nullptr;
}
else
{
backupCtx = new Context(*other.backupCtx);
}
for (int i = 0; i < MAX_SIGNAL; i++)
{
signalHandlers[i].type = other.signalHandlers[i].type;
signalHandlers[i].handler = other.signalHandlers[i].handler;
signalHandlers[i].trampoline = other.signalHandlers[i].trampoline;
signalHandlers[i].userdata = other.signalHandlers[i].userdata;
}
for (int fd : files)
{
closeFileContext(fd);
}
for (int fd : other.files)
{
storeFileContext(other.files.get(fd), fd);
}
return *this;
}

90
src/sched/process.h Normal file
View File

@@ -0,0 +1,90 @@
#ifndef KERNEL_PROCESS_H
#define KERNEL_PROCESS_H
#include "memory/addressspace.h"
#include "context.h"
#include "types/pid.h"
#include "signalaction.h"
#include "containers/binary_search_tree.h"
#include "fs/filecontext.h"
namespace kernel::sched
{
class Process
{
public:
enum class State
{
ACTIVE,
SIGNAL,
SIGWAIT
};
static pid_t nextPid();
Process();
Process(pid_t pid, pid_t parent, void *entry, void *stack, void *kernelStack, kernel::memory::AddressSpace *addressSpace);
Process(Process &other);
~Process();
Process &operator=(Process &other);
int exec(void *pc, void *stack, void *kernelStack, kernel::memory::AddressSpace *addressSpace);
Process *clone(pid_t pid, void *pc, void *stack, void *userdata);
Context *getContext();
void storeContext(Context *newCtx);
State getState() const;
void setState(State newState);
pid_t getPid() const;
pid_t getParent() const;
kernel::memory::AddressSpace *getAddressSpace();
void setSignalAction(int signal, void (*handler)(void *), void (*trampoline)(void), void *userdata);
int signalTrigger(int sig);
void signalReturn();
void storeProgramArgs(char *const argv[], char *const envp[]);
kernel::fs::FileContext *getFileContext(int fd) const;
int storeFileContext(kernel::fs::FileContext *f);
int storeFileContext(kernel::fs::FileContext *f, int fd);
int closeFileContext(int fd);
private:
static pid_t nextPidVal;
static const int MAX_SIGNAL = 64;
pid_t pid, parent;
Context ctx, *backupCtx;
State state;
kernel::memory::AddressSpace *addressSpace;
SignalAction signalHandlers[MAX_SIGNAL];
binary_search_tree<int, kernel::fs::FileContext *> files;
};
}
#endif

104
src/sched/queue.cpp Normal file
View File

@@ -0,0 +1,104 @@
#include "queue.h"
#include "process.h"
node::node(kernel::sched::Process *value) : value(value), prev(nullptr), next(nullptr){};
queue::queue()
{
queue_size = 0;
linked_list_front = linked_list_back = nullptr;
cur_Process = nullptr;
};
void queue::enqueue(kernel::sched::Process *process)
{
node *new_node = new node(process);
if (empty())
{
linked_list_front = new_node;
}
else
{
node *last_node = linked_list_back;
last_node->next = new_node;
new_node->prev = last_node;
}
linked_list_back = new_node;
queue_size++;
}
kernel::sched::Process *queue::dequeue()
{
if (queue_size == 0)
{
return nullptr;
}
node *return_node = linked_list_front;
linked_list_front = linked_list_front->next;
if (linked_list_back == return_node)
{
linked_list_back = nullptr;
}
kernel::sched::Process *p = return_node->value;
delete return_node;
queue_size--;
return p;
}
kernel::sched::Process *queue::remove(pid_t pid)
{
node *cur_node = linked_list_front;
kernel::sched::Process *p;
while (cur_node->next)
{
if (cur_node->value->getPid() == pid)
{
cur_node->prev->next = cur_node->next;
cur_node->next->prev = cur_node->prev;
p = cur_node->value;
delete cur_node;
return p;
}
cur_node = cur_node->next;
}
}
kernel::sched::Process *queue::peek()
{
return linked_list_front->value;
}
kernel::sched::Process *queue::sched_next()
{
if (cur_Process != nullptr)
{
enqueue(cur_Process);
}
if (!empty())
{
cur_Process = dequeue();
return cur_Process;
}
return nullptr;
}
int queue::size() const
{
return queue_size;
}
bool queue::empty() const
{
return queue_size == 0 ? true : false;
}
kernel::sched::Process *queue::get_cur_process()
{
return cur_Process;
}
void queue::set_cur_process(kernel::sched::Process *proc)
{
cur_Process = proc;
}

37
src/sched/queue.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef QUEUE_H
#define QUEUE_H
#include "containers/linked_list.h"
#include "process.h"
class node
{
public:
node(kernel::sched::Process *value);
kernel::sched::Process *value;
node *prev;
node *next;
};
class queue
{
public:
queue();
void enqueue(kernel::sched::Process *process);
kernel::sched::Process *dequeue();
kernel::sched::Process *remove(pid_t pid);
kernel::sched::Process *peek();
kernel::sched::Process *sched_next();
kernel::sched::Process *get_cur_process();
void set_cur_process(kernel::sched::Process *proc);
bool empty() const;
int size() const;
private:
int queue_size;
node *linked_list_front;
node *linked_list_back;
kernel::sched::Process *cur_Process;
};
#endif

19
src/sched/signalaction.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef KERNEL_SIGNALACTION_H
#define KERNEL_SIGNALACTION_H
#include "actiontype.h"
namespace kernel::sched
{
struct SignalAction
{
ActionType type;
void (*handler)(void *);
void (*trampoline)(void);
void *userdata;
};
}
#endif

33
src/sched/signaltype.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef KERNEL_SIGNALTYPE_H
#define KERNEL_SIGNALTYPE_H
namespace kernel::sched
{
enum class SignalType
{
Unknown = 0,
Hangup,
Interrupt,
Quit,
IllegalInstruction,
Trap,
Abort,
Bus,
FloatingPoint,
Kill,
User_1,
Segfault,
User_2,
Pipe,
Alarm,
Terminate,
StackFault,
Child,
Continue,
Stop
};
}
#endif

12
src/util/aarch64/hacf.cpp Normal file
View File

@@ -0,0 +1,12 @@
#include "../hacf.h"
#include <stdint.h>
extern "C" void hacf()
{
uint64_t daif = 0xF << 6;
asm("msr daif, %0" ::"r"(daif));
while (1)
{
asm("wfi");
}
}

46
src/util/charstream.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef _CHARSTREAM_H
#define _CHARSTREAM_H
#include "fs/filecontext.h"
namespace kernel
{
/**
* @brief Abstract class representing an object that accepts character data
* for output.
*/
class CharStream
{
public:
enum class Mode
{
RO,
RW,
W
};
/**
* @brief Outputs a single character
* @param c The character to output
* @return A reference to this object
*/
virtual CharStream &operator<<(char c) = 0;
/**
* @brief Outputs an entire null-terminated string.
* @param str The string to output
* @return A reference to this object
*/
virtual CharStream &operator<<(const char *str) = 0;
virtual CharStream &operator>>(char &c) = 0;
virtual kernel::fs::FileContext *open(Mode mode) = 0;
virtual void close(int id) = 0;
};
}
#endif

10
src/util/hacf.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef _HACF_H
#define _HACF_H
extern "C"
/**
* @brief Cease all meaningful execution. Implementation is platform-dependent.
*/
void hacf() __attribute__((noreturn));
#endif

21
src/util/hasrefcount.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include "hasrefcount.h"
HasRefcount::HasRefcount()
: refcount(0)
{
}
int HasRefcount::getRefCount() const
{
return refcount;
}
void HasRefcount::addReference()
{
refcount++;
}
void HasRefcount::removeReference()
{
refcount--;
}

19
src/util/hasrefcount.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef _KERNEL_HASREFCOUNT_H
#define _KERNEL_HASREFCOUNT_H
class HasRefcount
{
public:
HasRefcount();
virtual int getRefCount() const;
virtual void addReference();
virtual void removeReference();
protected:
int refcount;
};
#endif

183
src/util/log.cpp Normal file
View File

@@ -0,0 +1,183 @@
#include "log.h"
#include <stddef.h>
#include <stdbool.h>
using namespace kernel;
enum format_flags_t
{
FORMAT_PADDING = '0',
FORMAT_WIDTH = '*',
FORMAT_SIGNED_DECIMAL = 'i',
FORMAT_UNSIGNED_DECIMAL = 'u',
FORMAT_UNSIGNED_OCTAL = 'o',
FORMAT_UNSIGNED_HEX = 'x',
FORMAT_STRING = 's',
FORMAT_CHARACTER = 'c',
FORMAT_COUNT = 'n',
FORMAT_PERCENT = '%'
};
static const char *FG_BLUE = "\x1b[34m";
static const char *FG_YELLOW = "\x1b[33m";
static const char *FG_RED = "\x1b[31m";
static const char *BOLD_RED = "\x1b[1;31m";
static const char *FG_FAINT = "\x1b[1;2m";
static const char *MODE_RESET = "\x1b[0m";
CharStream *stream = nullptr;
static char *itoa(unsigned long n, unsigned int base, unsigned int width)
{
if (base < 2 || base > 16)
{
return NULL;
}
static const char *digits = "0123456789abcdef";
static char buffer[65];
char *s = &buffer[64];
*s = 0;
unsigned int count = 0;
do
{
*--s = digits[n % base];
n /= base;
count++;
} while (count < width || n != 0);
return s;
}
int logInit(CharStream *outStream)
{
stream = outStream;
return 0;
}
kernel::CharStream *getLogStream()
{
return stream;
}
int vprintf(const char *format, va_list valist)
{
if (stream == nullptr)
{
return 0;
}
while (*format)
{
if (*format == '%')
{
size_t width = 0;
switch (*++format)
{
case FORMAT_PADDING:
format++;
break;
}
while (*format >= '0' && *format <= '9')
{
width = (width * 10) + *format - '0';
format++;
}
switch (*format)
{
case FORMAT_SIGNED_DECIMAL:
{
int n = va_arg(valist, int);
if (n < 0)
{
*stream << '-';
n *= -1;
}
*stream << itoa((unsigned int)n, 10, width);
break;
}
case FORMAT_UNSIGNED_DECIMAL:
*stream << itoa(va_arg(valist, unsigned long), 10, width);
break;
case FORMAT_UNSIGNED_OCTAL:
*stream << itoa(va_arg(valist, unsigned long), 8, width);
break;
case FORMAT_UNSIGNED_HEX:
*stream << itoa(va_arg(valist, unsigned long), 16, width);
break;
case FORMAT_STRING:
*stream << va_arg(valist, const char *);
break;
case FORMAT_CHARACTER:
*stream << va_arg(valist, int);
break;
case FORMAT_PERCENT:
*stream << '%';
break;
}
}
else
{
*stream << *format;
if (*format == '\n')
{
*stream << '\r';
}
}
format++;
}
}
int printf(const char *format, ...)
{
if (stream == nullptr)
{
return 0;
}
va_list valist;
va_start(valist, format);
vprintf(format, valist);
va_end(valist);
return 0;
}
void kernelLog(LogLevel level, const char *fmt, ...)
{
va_list valist;
va_start(valist, fmt);
switch (level)
{
#if !defined KERNEL_NODEBUG
case LogLevel::DEBUG:
printf("%s[Debug]: ", FG_FAINT);
vprintf(fmt, valist);
printf("%s\r\n", MODE_RESET);
break;
#endif
case LogLevel::INFO:
printf("[%sInfo%s]: ", FG_BLUE, MODE_RESET);
vprintf(fmt, valist);
printf("\r\n");
break;
case LogLevel::WARNING:
printf("[%sWarning%s]: ", FG_YELLOW, MODE_RESET);
vprintf(fmt, valist);
printf("\r\n");
break;
case LogLevel::ERROR:
printf("[%sError%s]: ", FG_RED, MODE_RESET);
vprintf(fmt, valist);
printf("\r\n");
break;
case LogLevel::PANIC:
printf("[%sPANIC%s]: ", BOLD_RED, MODE_RESET);
vprintf(fmt, valist);
printf("\r\n");
break;
default:
break;
}
va_end(valist);
}

50
src/util/log.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef LOG_H
#define LOG_H
#include "charstream.h"
#include <stdarg.h>
enum class LogLevel
{
DEBUG,
INFO,
WARNING,
ERROR,
PANIC
};
/**
* @brief Initialize the logger to output characters to `outStream`.
* @param outStream
* @return Zero upon success, nonzero upon failure.
*/
int logInit(kernel::CharStream *outStream);
/**
* @return the stream being used for the kernel log
*/
kernel::CharStream *getLogStream();
/**
* @brief C-style printf function, accepting commonly used formatting flags.
* Outputs to the stream provided to `logInit`. Calls to this function before
* a call to `logInit` will result in no data being outputted.
*
* @param format
* @param
* @return zero
*/
int printf(const char *format, ...);
/**
* @brief Logs a formatted message tagged with the log level. Appends a newline
* and carriage return after each message, so log messages need not include
* them.
*
* @param level The log level of the message
* @param fmt Format string of the log message
* @param
*/
void kernelLog(LogLevel level, const char *fmt, ...);
#endif

84
src/util/math.h Normal file
View File

@@ -0,0 +1,84 @@
/**
* Author: Nathan Giddings
*
* Source: https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers
*/
#ifndef MATH_H
#define MATH_H
/**
* @brief Quickly compute log2 of an int, rounding up.
* @param x
* @return ceil(log2(x))
*/
static inline int ilog2(unsigned int x)
{
#if defined __GNUC__
if(x <= 1)
return 0;
return 32 - __builtin_clz(x - 1);
#else
static const int table[32] = {
0, 9, 1, 10, 13, 21, 2, 29,
11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7,
19, 27, 23, 6, 26, 5, 4, 31};
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return table[(x * 0x07C4ACDD) >> 27];
#endif
}
/**
* @brief Quickly compute log2 of a long, rounding up.
* @param x
* @return ceil(log2(x))
*/
static inline int llog2(unsigned long x)
{
#if (defined __GNUC__) && (__SIZEOF_LONG__ == 4)
if(x <= 1)
return 0;
return 32 - __builtin_clzl(x - 1);
#elif (defined __GNUC__) && (__SIZEOF_LONG__ == 8)
if(x <= 1)
return 0;
return 64 - __builtin_clzl(x - 1);
#elif __SIZEOF_LONG__ == 8
static const int table[64] = {
0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, 61,
51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, 62,
57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21, 56,
45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5, 63};
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x |= x >> 32;
return table[(x * 0x03f6eaf2cd271461) >> 58];
#else
static const int table[32] = {
0, 9, 1, 10, 13, 21, 2, 29,
11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7,
19, 27, 23, 6, 26, 5, 4, 31};
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return table[(x * 0x07C4ACDD) >> 27];
#endif
}
#endif

49
src/util/string.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "string.h"
extern "C" void *memcpy(void *dest, const void *src, size_t count)
{
char *d = (char *)dest;
const char *s = (const char *)src;
for (int i = 0; i < count; i++)
{
d[i] = s[i];
}
return dest;
}
void memset(char *s, int size, int v)
{
for (int i = 0; i < size; i++)
{
s[i] = (char)v;
}
}
int strlen(const char *s)
{
int c = 0;
if (s == nullptr)
{
return 0;
}
while (s[c] != '\0')
{
c++;
}
return c;
}
void strcpy(char *dest, const char *src)
{
if (dest == nullptr || src == nullptr)
{
return;
}
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = '\0';
}

22
src/util/string.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef KERNEL_STRING_H
#define KERNEL_STRING_H
#include <cstddef>
/**
* @brief
* @see https://en.cppreference.com/w/c/string/byte/memcpy
* @param dest
* @param src
* @param count
* @return
*/
extern "C" void *memcpy(void *dest, const void *src, size_t count);
extern "C" void memset(char *s, int size, int v);
extern "C" int strlen(const char *s);
extern "C" void strcpy(char *dest, const char *src);
#endif

6
test/entry.s Normal file
View File

@@ -0,0 +1,6 @@
.section ".text"
.global _start
_start:
bl main
b _start

43
test/linker.ld Normal file
View File

@@ -0,0 +1,43 @@
ENTRY(_start)
SECTIONS
{
. = 0x200000;
__begin = .;
__text_start = .;
.text :
{
*(.text)
}
. = ALIGN(4096);
__text_end = .;
__rodata_start = .;
.rodata :
{
*(.rodata)
}
. = ALIGN(4096);
__rodata_end = .;
__data_start = .;
.data :
{
*(.data)
}
. = ALIGN(4096);
__data_end = .;
__bss_start = .;
.bss :
{
bss = .;
*(.bss)
}
. = ALIGN(4096);
__bss_end = .;
__bss_size = __bss_end - __bss_start;
__end = .;
}

32
test/main.c Normal file
View File

@@ -0,0 +1,32 @@
#include <stdint.h>
#include "sys/syscall.h"
void trampoline()
{
sigret();
}
void handler(void *userdata)
{
printk("Handler called.\n");
}
void thread(void *data)
{
printk("Thread 2\n");
terminate();
}
int main(int argc, char **argv, char **envp)
{
printk("Process start: ");
printk(argv[0]);
printk("\n");
mmap((void *)0, 0x10000, 1);
sigaction(17, handler, trampoline, (void *)0);
while (1)
{
clone(thread, (void *)0x10000, 0, 0);
sigwait();
}
}