New repo setup
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
*.o
|
||||
*.elf
|
||||
*.img
|
||||
.vscode
|
||||
*.a
|
||||
*.bin
|
||||
testprog
|
||||
init
|
||||
87
Makefile
Normal file
87
Makefile
Normal 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
22
README.md
Normal 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
222
include/sys/syscall.h
Normal 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
6
include/types/physaddr.h
Normal 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
6
include/types/pid.h
Normal 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
28
include/types/status.h
Normal 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
36
include/types/syscallid.h
Normal 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
79
src/aarch64/aarch64.cpp
Normal 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
52
src/aarch64/aarch64.ld
Normal 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
181
src/aarch64/boot.s
Normal 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
324
src/aarch64/bootstrap.cpp
Normal 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
19
src/aarch64/crti.c
Normal 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
4
src/aarch64/crtn.c
Normal 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
6
src/aarch64/dosyscall.s
Normal file
@@ -0,0 +1,6 @@
|
||||
.section ".text"
|
||||
.global do_syscall
|
||||
.type do_syscall, "function"
|
||||
do_syscall:
|
||||
svc #0
|
||||
ret
|
||||
42
src/aarch64/irq/exceptionclass.h
Normal file
42
src/aarch64/irq/exceptionclass.h
Normal 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
|
||||
9
src/aarch64/irq/exceptions.h
Normal file
9
src/aarch64/irq/exceptions.h
Normal 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
|
||||
244
src/aarch64/irq/exceptions.s
Normal file
244
src/aarch64/irq/exceptions.s
Normal 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
|
||||
30
src/aarch64/irq/interrupts.cpp
Normal file
30
src/aarch64/irq/interrupts.cpp
Normal 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
86
src/aarch64/irq/irq.cpp
Normal 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;
|
||||
}
|
||||
100
src/aarch64/irq/syndromedataabort.h
Normal file
100
src/aarch64/irq/syndromedataabort.h
Normal 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
76
src/aarch64/sysreg.h
Normal 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
145
src/aarch64/sysreg.s
Normal 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
|
||||
706
src/containers/binary_search_tree.h
Normal file
706
src/containers/binary_search_tree.h
Normal 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
177
src/containers/hashmap.h
Normal 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
|
||||
350
src/containers/linked_list.h
Normal file
350
src/containers/linked_list.h
Normal 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
28
src/containers/math.h
Normal 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
27
src/containers/pair.h
Normal 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
260
src/containers/string.cpp
Normal 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
47
src/containers/string.h
Normal 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
137
src/containers/vector.h
Normal 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
135
src/devices/disk_driver.c++
Normal 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
108
src/devices/disk_driver.h
Normal 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
64
src/devices/mmio.h
Normal 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
31
src/devices/timer.cpp
Normal 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
41
src/devices/timer.h
Normal 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
323
src/devices/uart.cpp
Normal 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
77
src/devices/uart.h
Normal 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
|
||||
82
src/fs/fat32/disk_interface/disk_interface.cpp
Normal file
82
src/fs/fat32/disk_interface/disk_interface.cpp
Normal 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();*/
|
||||
}
|
||||
25
src/fs/fat32/disk_interface/disk_interface.h
Normal file
25
src/fs/fat32/disk_interface/disk_interface.h
Normal 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
186
src/fs/fat32/entry.cpp
Normal 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;
|
||||
}
|
||||
773
src/fs/fat32/entry_helpers.cpp
Normal file
773
src/fs/fat32/entry_helpers.cpp
Normal 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
255
src/fs/fat32/fat32.cpp
Normal 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
158
src/fs/fat32/fat32.h
Normal 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
|
||||
78
src/fs/fat32/filecontextfat32.cpp
Normal file
78
src/fs/fat32/filecontextfat32.cpp
Normal 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;
|
||||
}
|
||||
38
src/fs/fat32/filecontextfat32.h
Normal file
38
src/fs/fat32/filecontextfat32.h
Normal 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
213
src/fs/fat32/fs_helpers.cpp
Normal 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
76
src/fs/fat32/helpers.cpp
Normal 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
26
src/fs/fat32/helpers.h
Executable 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
5
src/fs/filecontext.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "filecontext.h"
|
||||
|
||||
kernel::fs::FileContext::~FileContext()
|
||||
{
|
||||
}
|
||||
21
src/fs/filecontext.h
Normal file
21
src/fs/filecontext.h
Normal 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
167
src/fs/pipe.cpp
Normal 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
83
src/fs/pipe.h
Normal 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
|
||||
27
src/irq/interrupthandler.h
Normal file
27
src/irq/interrupthandler.h
Normal 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
19
src/irq/interrupts.cpp
Normal 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
64
src/irq/interrupts.h
Normal 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
547
src/kernel.cpp
Normal 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
234
src/kernel.h
Normal 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
87
src/loader/elf.cpp
Normal 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
131
src/loader/elf.h
Normal 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 ¤tSection() 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
448
src/memory/aarch64/mmu.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
18
src/memory/addressspace.cpp
Normal file
18
src/memory/addressspace.cpp
Normal 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
27
src/memory/addressspace.h
Normal 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
344
src/memory/heap.cpp
Normal 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
137
src/memory/heap.h
Normal 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
190
src/memory/memorymap.cpp
Normal 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
220
src/memory/memorymap.h
Normal 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
46
src/memory/mmap.cpp
Normal 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
152
src/memory/mmap.h
Normal 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
32
src/memory/new.cpp
Normal 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
34
src/memory/new.h
Normal 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
|
||||
135
src/memory/pageallocator.cpp
Normal file
135
src/memory/pageallocator.cpp
Normal 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
116
src/memory/pageallocator.h
Normal 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
10
src/mmgmt.h
Normal 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
|
||||
138
src/sched/aarch64/context.cpp
Normal file
138
src/sched/aarch64/context.cpp
Normal 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);
|
||||
}
|
||||
58
src/sched/aarch64/loadcontext.s
Normal file
58
src/sched/aarch64/loadcontext.s
Normal 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
14
src/sched/actiontype.h
Normal 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
70
src/sched/context.h
Normal 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
380
src/sched/process.cpp
Normal 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
90
src/sched/process.h
Normal 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
104
src/sched/queue.cpp
Normal 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
37
src/sched/queue.h
Normal 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
19
src/sched/signalaction.h
Normal 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
33
src/sched/signaltype.h
Normal 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
12
src/util/aarch64/hacf.cpp
Normal 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
46
src/util/charstream.h
Normal 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
10
src/util/hacf.h
Normal 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
21
src/util/hasrefcount.cpp
Normal 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
19
src/util/hasrefcount.h
Normal 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
183
src/util/log.cpp
Normal 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
50
src/util/log.h
Normal 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
84
src/util/math.h
Normal 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
49
src/util/string.cpp
Normal 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
22
src/util/string.h
Normal 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
6
test/entry.s
Normal file
@@ -0,0 +1,6 @@
|
||||
.section ".text"
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
bl main
|
||||
b _start
|
||||
43
test/linker.ld
Normal file
43
test/linker.ld
Normal 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
32
test/main.c
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user