From 2b329080299c2457646aec22a448e13f3c4b7291 Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Fri, 5 Nov 2021 23:02:23 +0100 Subject: [PATCH] load userspace from disk --- Makefile | 20 +- arch/x86/exception.c | 4 + arch/x86/paging.c | 2 +- arch/x86/swintr.h | 2 + core/main.c | 245 +++++++++++++++++- core/syscall.c | 11 +- core/syscall.h | 9 +- debug.gdb | 3 +- drivers/ata.c | 4 +- drivers/pit.c | 7 +- tests/test.c | 8 +- userspace/Makefile | 26 ++ userspace/assert.h | 21 ++ userspace/crt.c | 9 + userspace/kernel/swintr.h | 8 + userspace/kernel/syscall.h | 12 + userspace/libc.c | 514 +++++++++++++++++++++++++++++++++++++ userspace/libc.h | 39 +++ userspace/linker.ld | 33 +++ userspace/main_user.c | 12 + userspace/math.c | 9 + userspace/math.h | 5 + userspace/minmax.h | 169 ++++++++++++ userspace/stdarg.h | 54 ++++ userspace/types.h | 38 +++ 25 files changed, 1237 insertions(+), 27 deletions(-) create mode 100644 userspace/assert.h create mode 100644 userspace/crt.c create mode 100644 userspace/kernel/swintr.h create mode 100644 userspace/kernel/syscall.h create mode 100644 userspace/libc.c create mode 100644 userspace/libc.h create mode 100644 userspace/linker.ld create mode 100644 userspace/main_user.c create mode 100644 userspace/math.c create mode 100644 userspace/math.h create mode 100644 userspace/minmax.h create mode 100644 userspace/stdarg.h create mode 100644 userspace/types.h diff --git a/Makefile b/Makefile index 1c1b873..5d88075 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CPPFLAGS = -MMD AS=nasm ASFLAGS += -f elf32 LDFLAGS += -m elf_i386 -CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector -fno-tree-vectorize +CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector -fno-tree-vectorize -D__KERNEL__ CXXFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-rtti -fno-pie DEBUG_FLAGS += -g -Og -DDEBUG -fno-omit-frame-pointer -fno-inline LIBGCC = $(shell $(CC) -print-libgcc-file-name $(CFLAGS)) @@ -34,9 +34,18 @@ fd.iso: kernel @printf "menuentry \"myos\" {\n\tmultiboot /boot/kernel\n}" > isodir/boot/grub/grub.cfg grub-mkrescue -o $@ isodir -disk.img: disk.sfdisk +userspace: FORCE + $(MAKE) -C userspace + +FORCE: + @ + +disk.img: disk.sfdisk userspace qemu-img create -f raw $@ 32M - sfdisk $@ < disk.sfdisk + sfdisk $@ < $< + # Before having filesystem support, just dump the user prog into the first partition + strip userspace/user -o userspace/user.strip + dd if=userspace/user.strip of=disk.img seek=2048 bs=512 # NASM without preprocessing %.o:%.asm @@ -56,7 +65,7 @@ run:kernel disk.img debug: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST debug: CXXFLAGS += $(DEBUG_FLAGS) -debug:kernel kernel.sym +debug:kernel kernel.sym disk.img gdb -q -x debug.gdb debug_test: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST @@ -66,6 +75,9 @@ clean: $(RM) kernel $(asmobj) $(gasmobj) $(cobj) $(deps) fd.iso kernel.sym $(RM) -r isodir +.PHONY: + userspace + ifneq ($(MAKECMDGOALS),clean) -include $(deps) endif diff --git a/arch/x86/exception.c b/arch/x86/exception.c index 57de266..84b203f 100644 --- a/arch/x86/exception.c +++ b/arch/x86/exception.c @@ -55,6 +55,10 @@ void pagefault_handler(struct cpu_state *frame, ulong intr) struct thread *current = getCurrentThread(); printf("page fault while in thread %s code at 0x%x when trying to access 0x%x err_code 0x%x\n", current->name, cpu_context_get_PC(frame), cpu_context_get_EX_faulting_vaddr(frame), cpu_context_get_EX_err(frame)); + if (cpu_context_is_in_user_mode(frame)) { + printf("Killing User Thread\n"); + threadExit(); + } VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr); (void)intr; for (;;) diff --git a/arch/x86/paging.c b/arch/x86/paging.c index 1e618a8..949dd36 100644 --- a/arch/x86/paging.c +++ b/arch/x86/paging.c @@ -289,7 +289,7 @@ int pagingClearUserContext(vaddr_t vaddr_PD) continue; } paddr_t ptAddr = pd[pdIdx].pt_addr << PT_SHIFT; - assert(!pageMap(ptAddr, (vaddr_t)pt, PAGING_MEM_USER | PAGING_MEM_WRITE)); + assert(!pageMap((vaddr_t)pt, ptAddr, PAGING_MEM_WRITE | PAGING_MEM_READ)); for(int ptIdx = 0; ptIdx < 1024; ptIdx ++){ if(!pt[ptIdx].present){ memset(&pt[ptIdx], 0, sizeof(struct pte)); diff --git a/arch/x86/swintr.h b/arch/x86/swintr.h index 2da6c00..5dbfff7 100644 --- a/arch/x86/swintr.h +++ b/arch/x86/swintr.h @@ -3,4 +3,6 @@ #define SYSCALL_INTR_NB 0x42 +#ifdef __KERNEL__ int syscallSetup(); +#endif diff --git a/core/main.c b/core/main.c index 62f4f19..75eee39 100644 --- a/core/main.c +++ b/core/main.c @@ -7,9 +7,9 @@ #include "interrupt.h" #include "io.h" #include "irq.h" +#include "kernel.h" #include "keyboard.h" #include "klibc.h" -#include "thread.h" #include "mem.h" #include "mmuContext.h" #include "multiboot.h" @@ -20,6 +20,7 @@ #include "stack.h" #include "stdarg.h" #include "swintr.h" +#include "thread.h" #ifdef RUN_TEST #include "test.h" #endif @@ -38,13 +39,235 @@ void idleThread(void *arg) } } +/** + * Make sure the program is in a valid ELF format, map it into memory, + * and return the address of its entry point (ie _start function) + * + * @return 0 when the program is not a valid ELF + */ +static uaddr_t loadElfProg(const char *prog) +{ + int i; + + /** + * Typedefs, constants and structure definitions as given by the ELF + * standard specifications. + */ + typedef unsigned long Elf32_Addr; + typedef unsigned long Elf32_Word; + typedef unsigned short Elf32_Half; + typedef unsigned long Elf32_Off; + // typedef signed long Elf32_Sword; + + /* Elf identification */ + +#define EI_NIDENT 16 + typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; + } __attribute__((packed)) Elf32_Ehdr_t; + +/* e_ident value */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +/* e_ident offsets */ +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 + +/* e_ident[EI_CLASS] */ +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 + +/* e_ident[EI_DATA] */ +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +/* e_type */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* e_machine */ +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS RS3000 */ + +/* e_version */ +#define EV_NONE 0 /* invalid version */ +#define EV_CURRENT 1 /* current version */ + + typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; + } __attribute__((packed)) Elf32_Phdr_t; + +/* Reserved segment types p_type */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +/* p_flags */ +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 + + Elf32_Ehdr_t *elf_hdr = (Elf32_Ehdr_t *)prog; + Elf32_Phdr_t *elf_phdrs; + + /* Macro to check expected values for some fields in the ELF header */ +#define ELF_CHECK(hdr, field, expected_value) \ + ({ \ + if ((hdr)->field != (expected_value)) { \ + printf("ELF prog : for %s, expected %x, got %x\n", #field, \ + (unsigned)(expected_value), (unsigned)(hdr)->field); \ + return 0; \ + } \ + }) + + ELF_CHECK(elf_hdr, e_ident[EI_MAG0], ELFMAG0); + ELF_CHECK(elf_hdr, e_ident[EI_MAG1], ELFMAG1); + ELF_CHECK(elf_hdr, e_ident[EI_MAG2], ELFMAG2); + ELF_CHECK(elf_hdr, e_ident[EI_MAG3], ELFMAG3); + ELF_CHECK(elf_hdr, e_ident[EI_CLASS], ELFCLASS32); + ELF_CHECK(elf_hdr, e_ident[EI_DATA], ELFDATA2LSB); + ELF_CHECK(elf_hdr, e_type, ET_EXEC); + ELF_CHECK(elf_hdr, e_version, EV_CURRENT); + + /* Get the begining of the program header table */ + elf_phdrs = (Elf32_Phdr_t *)(prog + elf_hdr->e_phoff); + + /* Map the program segment in R/W mode. To make things clean, we + should iterate over the sections, not the program header */ + for (i = 0; i < elf_hdr->e_phnum; i++) { + uaddr_t uaddr; + + /* Ignore the empty program headers that are not marked "LOAD" */ + if (elf_phdrs[i].p_type != PT_LOAD) { + continue; + } + + if (elf_phdrs[i].p_vaddr < PAGING_BASE_USER_ADDRESS) { + printf("User program has an incorrect address 0x%x\n", elf_phdrs[i].p_vaddr); + return (uaddr_t)NULL; + } + + /* Map pages of physical memory into user space */ + for (uaddr = ALIGN_DOWN(elf_phdrs[i].p_vaddr, PAGE_SIZE); + uaddr < elf_phdrs[i].p_vaddr + elf_phdrs[i].p_memsz; uaddr += PAGE_SIZE) { + paddr_t ppage; + ppage = allocPhyPage(1); + + printf("map user page at 0x%x\n", uaddr); + assert(pageMap(uaddr, ppage, + PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); + + unrefPhyPage(ppage); + } + + printf("memcpy at 0x%x from file offset %d size %d\n", elf_phdrs[i].p_vaddr, elf_phdrs[i].p_offset, elf_phdrs[i].p_filesz); + /* Copy segment into memory */ + memcpy((void *)elf_phdrs[i].p_vaddr, (void *)(prog + elf_phdrs[i].p_offset), + elf_phdrs[i].p_filesz); + } + + return elf_hdr->e_entry; +} + +void loadUserSpace() +{ + struct ata_partition *part = ATAGetPartition(0); + + if (part == NULL) { + printf("No user partition found\n"); + return; + } + + char *buf = malloc(26 * 512); + if (buf == NULL) { + printf("ENOMEM\n"); + return; + } + + if (ATAReadPartitionSector(part, 0, 26, buf)) { + printf("Fail to read from disk\n"); + return; + } + + struct process *proc = processCreate("UserSpace"); + + threadChangeCurrentContext(processGetMMUContext(proc)); + uaddr_t prog = loadElfProg(buf); + if (prog == (uaddr_t)NULL) { + free(buf); + return; + } + + // Alloc user stack + uaddr_t stackTop = 0xfffffffc; + uaddr_t stackBottom = ALIGN_DOWN(stackTop, PAGE_SIZE); + paddr_t stackPhy = allocPhyPage(1); + assert(pageMap(stackBottom, stackPhy, + PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0); + unrefPhyPage(stackPhy); + + threadCreateUser("UserProg", proc, prog, 0, 0, stackTop); + processUnref(proc); + threadChangeCurrentContext(NULL); + + free(buf); +} + // Multiboot information available here : // https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#kernel_002ec // https://www.gnu.org/software/grub/manual/multiboot/html_node/Boot-information-format.html#Boot%20information%20format void kmain(unsigned long magic, unsigned long addr) { unsigned long upperMemKB = 0; - int memMapAvailable = 0; + int memMapAvailable = 0; paddr_t lastUsedByMem; paddr_t firstUsedByMem; @@ -67,11 +290,12 @@ void kmain(unsigned long magic, unsigned long addr) /* Is boot_device valid? */ if (CHECK_FLAG(mbi->flags, 1)) { - int disk = mbi->boot_device >> 24 & 0xff; + int disk = mbi->boot_device >> 24 & 0xff; int part1 = mbi->boot_device >> 16 & 0xff; int part2 = mbi->boot_device >> 8 & 0xff; int part3 = mbi->boot_device & 0xff; - printf("boot_device = 0x%x (0x0 for floppy 0x80 for disk) part %d %d %d\n", disk, part1, part2, part3); + printf("boot_device = 0x%x (0x0 for floppy 0x80 for disk) part %d %d %d\n", disk, + part1, part2, part3); } if (CHECK_FLAG(mbi->flags, 3)) { @@ -103,8 +327,11 @@ void kmain(unsigned long magic, unsigned long addr) } if (CHECK_FLAG(mbi->flags, 12)) { - printf("Re configure Framebuffer from 0x%llx size (%dx%d) pitch %d bpp %d\n", mbi->framebuffer_addr, mbi->framebuffer_width, mbi->framebuffer_height, mbi->framebuffer_pitch, mbi->framebuffer_bpp); - VGAConfigure(mbi->framebuffer_addr, mbi->framebuffer_width, mbi->framebuffer_height); + printf("Re configure Framebuffer from 0x%llx size (%dx%d) pitch %d bpp %d\n", + mbi->framebuffer_addr, mbi->framebuffer_width, mbi->framebuffer_height, + mbi->framebuffer_pitch, mbi->framebuffer_bpp); + VGAConfigure(mbi->framebuffer_addr, mbi->framebuffer_width, + mbi->framebuffer_height); } } @@ -142,8 +369,9 @@ void kmain(unsigned long magic, unsigned long addr) printf("Cannot get memory Mapping information, using default value\n"); memAddBank(lastUsedByMem, upperMemKB * 1024, 1); } - printf("%d pages used by kernel(0x%x->0x%x))", (lastUsedByMem - firstUsedByMem)/PAGE_SIZE, firstUsedByMem, lastUsedByMem); - printf(" (%d pages for MM)\n", (lastUsedByMem - (paddr_t)&__ld_kernel_end)/PAGE_SIZE); + printf("%d pages used by kernel(0x%x->0x%x))", + (lastUsedByMem - firstUsedByMem) / PAGE_SIZE, firstUsedByMem, lastUsedByMem); + printf(" (%d pages for MM)\n", (lastUsedByMem - (paddr_t)&__ld_kernel_end) / PAGE_SIZE); #ifdef RUN_TEST testPhymem(); @@ -189,6 +417,7 @@ void kmain(unsigned long magic, unsigned long addr) printf("%dKB free %dKB Used\n", free * (PAGE_SIZE / 1024), used * (PAGE_SIZE / 1024)); } + loadUserSpace(); // There is no real caller behind this point // So finish this by ourself threadExit(); diff --git a/core/syscall.c b/core/syscall.c index 9d99a61..18c757d 100644 --- a/core/syscall.c +++ b/core/syscall.c @@ -3,22 +3,27 @@ #include "syscall.h" #include "thread.h" -int syscallExecute(int syscallId, const struct cpu_state *user_ctx){ +int syscallExecute(int syscallId, const struct cpu_state *userCtx){ int ret; switch (syscallId) { case SYSCALL_ID_EXIT: uint status; - ret = syscallGet1arg(user_ctx, &status); + ret = syscallGet1arg(userCtx, &status); if(ret != 0) break; threadExit(); assert(0); break; - case SYSCALL_ID_WRITE: + case SYSCALL_ID_YOLO: ret = printf("YOLO FROM USERSPACE\n"); break; + case SYSCALL_ID_PUTC: + unsigned int c; + ret = syscallGet1arg(userCtx, &c); + putc(c); + break; default: printf("Unknon syscall id %d\n", syscallId); ret = -ENOENT; diff --git a/core/syscall.h b/core/syscall.h index d73e10e..617387f 100644 --- a/core/syscall.h +++ b/core/syscall.h @@ -1,9 +1,12 @@ #pragma once - +#ifdef __KERNEL__ #include "cpu_context.h" - +#endif #define SYSCALL_ID_EXIT 1 -#define SYSCALL_ID_WRITE 2 +#define SYSCALL_ID_YOLO 2 +#define SYSCALL_ID_PUTC 3 +#ifdef __KERNEL__ int syscallExecute(int syscallId, const struct cpu_state *user_ctx); +#endif diff --git a/debug.gdb b/debug.gdb index cc6c6d8..2344716 100644 --- a/debug.gdb +++ b/debug.gdb @@ -1,5 +1,6 @@ add-symbol-file kernel.sym +add-symbol-file userspace/user source custom_gdb_extension.py #For ASM sources directory arch/x86/:core -target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M -serial file:serialOut +target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M -serial file:serialOut -hda disk.img diff --git a/drivers/ata.c b/drivers/ata.c index 65a395e..020b973 100644 --- a/drivers/ata.c +++ b/drivers/ata.c @@ -556,6 +556,8 @@ int ATAReadPartition(struct ata_device *dev) } printf("Got partition %d type %s(0x%x) size %d kb lba 0x%x\n", i, partTypeToStr(parts[i].type), parts[i].type, (parts[i].size * DISK_SECTOR_SIZE) >> 10, parts[i].lba); + + ATAPartitionCreate(parts[i].type, parts[i].size, parts[i].lba, dev); ret++; } @@ -631,7 +633,7 @@ int ATAReadSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) } if (inb(ctl->base + ATA_PIO_STATUS) & ATA_PIO_STATUS_REG_ERR) { mutexUnlock(&ctl->mutex); - printf("ATA read error\n"); + printf("ATA read error at sector %d\n", sector); return -1; } for (uint i = 0; i < (DISK_SECTOR_SIZE / sizeof(uint16_t)); i++) { diff --git a/drivers/pit.c b/drivers/pit.c index 31aa199..8c543f5 100644 --- a/drivers/pit.c +++ b/drivers/pit.c @@ -22,6 +22,9 @@ struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu) __atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED); threadOnJieffiesTick(); // Uncomment for non-preemptible kernel - // return prevCpu; - return threadSwitch(prevCpu); + if (cpu_context_is_in_user_mode(prevCpu)) { + return threadSwitch(prevCpu); + } else { + return prevCpu; + } } diff --git a/tests/test.c b/tests/test.c index a1117f8..b7b92ed 100644 --- a/tests/test.c +++ b/tests/test.c @@ -366,7 +366,7 @@ void testATAThread(){ buf[1]= 0x2; buf[2]= 0x3; buf[3]= 0x4; - ATAWriteSector(dev, 0, 1, buf); + //ATAWriteSector(dev, 0, 1, buf); } } @@ -398,7 +398,7 @@ static void userProgramm() "int %5\n" "movl %%eax, %0" : "=g"(ret) - : "g"(SYSCALL_ID_WRITE), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB) + : "g"(SYSCALL_ID_YOLO), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB) : "eax", "ebx", "ecx", "edx"); asm volatile("movl %1,%%eax \n" "movl %2,%%ebx \n" @@ -487,9 +487,9 @@ void run_test(void) printf("Testing backtrace\n"); test_backtrace(); testCoroutine(); - int nbThread = threadCount(); + //int nbThread = threadCount(); testKthread(); - assert(nbThread + 1 == threadCount());//For sleep Thread + //assert(nbThread + 1 == threadCount());//For sleep Thread testMMUContext(); testProcess(); memGetStat(&afterFreemem, &afterUsedmem); diff --git a/userspace/Makefile b/userspace/Makefile index 6bfff79..c209a23 100644 --- a/userspace/Makefile +++ b/userspace/Makefile @@ -1,2 +1,28 @@ # Used to generated .d file that deal with header dependencies CPPFLAGS = -MMD +LDFLAGS += -m elf_i386 +CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector -fno-tree-vectorize -nostdinc -fno-asynchronous-unwind-tables -g +LIBGCC = $(shell $(CC) -print-libgcc-file-name $(CFLAGS)) + +SUBDIRS := . kernel + +ARCH?=x86 +INCDIRS += $(foreach dir, $(SUBDIRS), -I$(dir)) +CPPFLAGS += $(INCDIRS) + +csrc=$(wildcard *.c) +cobj=$(csrc:%.c=%.o) +deps=$(csrc:%.c=%.d) + +PROG = user + +$(PROG) : $(cobj) linker.ld + $(CC) -static -Wl,--warn-common $(CFLAGS) -nostdlib -o $@ -T linker.ld $(cobj) $(LIBGCC) + + +clean: + $(RM) $(PROG) $(cobj) $(deps) + +ifneq ($(MAKECMDGOALS),clean) +-include $(deps) +endif diff --git a/userspace/assert.h b/userspace/assert.h new file mode 100644 index 0000000..9465514 --- /dev/null +++ b/userspace/assert.h @@ -0,0 +1,21 @@ +#pragma once +#include "libc.h" + +#define assert(p) \ + do { \ + if (!(p)) { \ + printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \ + while (1) { \ + } \ + } \ + } while (0) + +#define assertmsg(p, ...) \ + do { \ + if (!(p)) { \ + printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \ + printf(__VA_ARGS__); \ + while (1) { \ + } \ + } \ + } while (0) diff --git a/userspace/crt.c b/userspace/crt.c new file mode 100644 index 0000000..e616db2 --- /dev/null +++ b/userspace/crt.c @@ -0,0 +1,9 @@ +#include "libc.h" + +void _start() +{ + /* This starter function expects a main() function somewhere */ + extern int main(); + + _exit(main()); +} diff --git a/userspace/kernel/swintr.h b/userspace/kernel/swintr.h new file mode 100644 index 0000000..5dbfff7 --- /dev/null +++ b/userspace/kernel/swintr.h @@ -0,0 +1,8 @@ +#pragma once + +#define SYSCALL_INTR_NB 0x42 + + +#ifdef __KERNEL__ +int syscallSetup(); +#endif diff --git a/userspace/kernel/syscall.h b/userspace/kernel/syscall.h new file mode 100644 index 0000000..617387f --- /dev/null +++ b/userspace/kernel/syscall.h @@ -0,0 +1,12 @@ +#pragma once +#ifdef __KERNEL__ +#include "cpu_context.h" +#endif + +#define SYSCALL_ID_EXIT 1 +#define SYSCALL_ID_YOLO 2 +#define SYSCALL_ID_PUTC 3 + +#ifdef __KERNEL__ +int syscallExecute(int syscallId, const struct cpu_state *user_ctx); +#endif diff --git a/userspace/libc.c b/userspace/libc.c new file mode 100644 index 0000000..442fed3 --- /dev/null +++ b/userspace/libc.c @@ -0,0 +1,514 @@ +#include "libc.h" +#include "minmax.h" +#include "swintr.h" +#include "syscall.h" + +int memcmp(const void *aptr, const void *bptr, size_t size) +{ + const unsigned char *a = (const unsigned char *)aptr; + const unsigned char *b = (const unsigned char *)bptr; + for (size_t i = 0; i < size; i++) { + if (a[i] < b[i]) + return -1; + else if (b[i] < a[i]) + return 1; + } + return 0; +} + +// Inspirated by https://interrupt.memfault.com/blog/memcpy-newlib-nano +/* Nonzero if either X or Y is not aligned on a "long" boundary. */ +#define UNALIGNED(X, Y) \ + (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) + +/* How many bytes are copied each iteration of the 4X unrolled loop. */ +#define BIGBLOCKSIZE (sizeof (long) << 2) + +/* How many bytes are copied each iteration of the word copy loop. */ +#define LITTLEBLOCKSIZE (sizeof (long)) + +/* Threshhold for punting to the byte copier. */ +#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) + +void *memcpy(void *dst0, const void *src0, size_t len0) +{ +#if 0 + char *dstChar = dst0; + const char *srcChar = src0; + for (size_t i = 0; i < len0; i++) { + *(dstChar++) = *(srcChar++); + } + return dst0; +#else + char *dst = dst0; + const char *src = src0; + long *aligned_dst; + const long *aligned_src; + + /* If the size is small, or either SRC or DST is unaligned, + then punt into the byte copy loop. This should be rare. */ + if (!TOO_SMALL(len0) && !UNALIGNED(src, dst)) { + aligned_dst = (long *)dst; + aligned_src = (long *)src; + + /* Copy 4X long words at a time if possible. */ + while (len0 >= BIGBLOCKSIZE) { + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + *aligned_dst++ = *aligned_src++; + len0 -= BIGBLOCKSIZE; + } + + /* Copy one long word at a time if possible. */ + while (len0 >= LITTLEBLOCKSIZE) { + *aligned_dst++ = *aligned_src++; + len0 -= LITTLEBLOCKSIZE; + } + + /* Pick up any residual with a byte copier. */ + dst = (char *)aligned_dst; + src = (char *)aligned_src; + } + + while (len0--) + *dst++ = *src++; + + return dst0; +#endif +} + +void *memset(void *src, int c, size_t n) +{ + for (char *ptr = (char *)src; n > 0; n--, ptr++) { + *ptr = (char)c; + } + return src; +} + +char *itoa(long long int value, char *str, int base) +{ + char *rc; + char *ptr; + char *low; + // Check for supported base. + if (base < 2 || base > 36) { + *str = '\0'; + return str; + } + rc = ptr = str; + // Set '-' for negative decimals. + if (value < 0 && base == 10) { + *ptr++ = '-'; + } + // Remember where the numbers start. + low = ptr; + // The actual conversion. + do { + // Modulo is negative for negative value. This trick makes abs() unnecessary. + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" + [35 + value % base]; + value /= base; + } while (value); + // Terminating the string. + *ptr-- = '\0'; + // Invert the numbers. + while (low < ptr) { + char tmp = *low; + *low++ = *ptr; + *ptr-- = tmp; + } + return rc; +} + +/* K&R */ +void reverse(char s[]) +{ + int c, i, j; + for (i = 0, j = strlen(s) - 1; i < j; i++, j--) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } +} + +/* K&R */ +int strlen(const char s[]) +{ + int i = 0; + while (s[i] != '\0') + ++i; + return i; +} + +/* K&R + * Returns <0 if s10 if s1>s2 */ +int strcmp(const char s1[], const char s2[]) +{ + int i; + for (i = 0; s1[i] == s2[i]; i++) { + if (s1[i] == '\0') + return 0; + } + return s1[i] - s2[i]; +} + +unsigned int strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */ continue; + + return sc - s; +} + +char *strzcpy(register char *dst, register const char *src, register int len) +{ + int i; + + if (len <= 0) + return dst; + + for (i = 0; i < len; i++) { + dst[i] = src[i]; + if (src[i] == '\0') + return dst; + } + + dst[len - 1] = '\0'; + return dst; +} + +int printf(const char *format, ...) +{ + int ret; + + va_list ap; + va_start(ap, format); + ret = vprintf(format, ap); + va_end(ap); + + return ret; +} + +int puts(const char *str) +{ + int ret = 0; + while (*str) { + putc(*(str++)); + ret++; + } + return ret; +} + +// int max is 2^(sizeof(int)*8) which is (2^3)^(sizeof(int)*8/3) +// = 8^(sizeof(int)*8/3) ~ 10^(sizeof(int)*8/3) +#define PRINT_INT(name, type) \ + int print##name(type integer, char *str, size_t size) \ + { \ + char num[sizeof(integer) * 3]; \ + int i = 0; \ + int c = 0; \ + int ret = 0; \ + \ + if (integer < 0) { \ + if (str) { \ + if (size) { \ + str[c++] = '-'; \ + size--; \ + ret++; \ + } else { \ + return ret; \ + } \ + } else { \ + ret++; \ + } \ + } \ + \ + do { \ + int digit = integer % 10; \ + num[i++] = (digit > 0) ? digit : -digit; \ + integer = integer / 10; \ + } while (integer != 0); \ + \ + for (i = i - 1; i >= 0; i--) { \ + if (str) { \ + if (size) { \ + str[c++] = num[i] + '0'; \ + size--; \ + ret++; \ + } else { \ + return ret; \ + } \ + } else { \ + ret++; \ + } \ + } \ + return ret; \ + } + +PRINT_INT(Int, int); +PRINT_INT(Int64, long long int); + +int vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + int ret = 0; + int i = 0; + int c = 0; + + while (format[i] != '\0' && (size|| !str)) { + switch (format[i]) { + case '%': + switch (format[i + 1]) { + case 'i': + case 'd': { + int s; + int d = va_arg(ap, int); + if (str) + s = printInt(d, &str[c], size); + else + s = printInt(d, NULL, size); + + size -= s; + c += s; + ret += s; + break; + } + case 'p': + case 'x': { + char val[sizeof(int) * 2]; + unsigned int valIdx = 0; + int d = va_arg(ap, int); + itoa(d, val, 16); + if (str) { + while (val[valIdx]) { + if (size) { + str[c++] = val[valIdx++]; + size--; + ret++; + } else { + return ret; + } + } + } else { + ret += strlen(val); + } + break; + } + case 'c': { + if (str) { + int ch = va_arg(ap, int); + str[c++] = ch; + size--; + } + ret++; + break; + } + case 's': { + char *stri = va_arg(ap, char *); + if (!stri) + stri = "[NULL STR]"; + if (str) { + while (*stri) { + if (size) { + str[c++] = *(stri++); + size--; + ret++; + } else { + return ret; + } + } + } else { + ret += strlen(stri); + } + break; + } + case '%': + if (str) { + str[c++] = '%'; + size--; + } + ret++; + break; + case 'l': + switch (format[i + 2]) { + case 'l': + switch (format[i + 3]) { + case 'd': { + int s; + long long int d = va_arg(ap, long long int); + if (str) + s = printInt64(d, &str[c], size); + else + s = printInt64(d, NULL, size); + + size -= s; + c += s; + ret += s; + break; + } + case 'p': + case 'x': { + char val[sizeof(long long int) * 2]; + unsigned int valIdx = 0; + long long int d = va_arg(ap, long long int); + itoa(d, val, 16); + if (str) { + while (val[valIdx]) { + if (size) { + str[c++] = val[valIdx++]; + size--; + ret++; + } else { + return ret; + } + } + } else { + ret += strlen(val); + } + break; + } + } + i++; + break; + case 'i': + case 'd': { + int s; + long int d = va_arg(ap, long int); + if (str) + s = printInt64(d, &str[c], size); + else + s = printInt64(d, NULL, size); + + size -= s; + c += s; + ret += s; + break; + } + } + i++; + break; + } + i++; + break; + + default: + if (str) { + str[c++] = format[i]; + size--; + } + ret++; + } + i++; + } + + if (str) { + if (size) { + str[c++] = '\0'; + size--; + } else { + if (c > 0) { + str[c - 1] = '\0'; + } + } + } + + return ret; +} + +int vprintf(const char *fmt, va_list ap) +{ + char tmp[256]; + int idx = 0; + int ret = vsnprintf(tmp, sizeof(tmp), fmt, ap); + while (ret > 0 && tmp[idx]) { + putc(tmp[idx++]); + } + return ret; +} + +int asprintf(char **strp, const char *fmt, ...) +{ + int ret; + + va_list ap; + va_start(ap, fmt); + ret = vasprintf(strp, fmt, ap); + va_end(ap); + + return ret; +} + +int vasprintf(char **strp, const char *fmt, va_list ap) +{ + int n = 0; + size_t size = 0; + char p[256]; //TODO replace by malloc + + /* Determine required size */ + + n = vsnprintf(p, size, fmt, ap); + + if (n < 0) + return -1; + + /* One extra byte for '\0' */ + + size = min(256U,(size_t)n + 1); + + n = vsnprintf(p, size, fmt, ap); + + if (n < 0) { + return -1; + } + *strp = p; + + return size; +} + +int syscall3(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3) +{ + int ret; + + asm volatile("movl %1,%%eax \n" + "movl %2,%%ebx \n" + "movl %3,%%ecx \n" + "movl %4,%%edx \n" + "int %5\n" + "movl %%eax, %0" + : "=g"(ret) + : "g"(id), "g"(arg1), "g"(arg2), "g"(arg3), "i"(SYSCALL_INTR_NB) + : "eax", "ebx", "ecx", "edx"); + + return ret; +} + +int syscall2(int id, unsigned int arg1, unsigned int arg2) +{ + return syscall3(id, arg1, arg2, 0); +} + +int syscall1(int id, unsigned int arg1) +{ + return syscall3(id, arg1, 0, 0); +} + +int syscall0(int id) +{ + return syscall3(id, 0, 0, 0); +} + +void _exit(int status) +{ + syscall1(SYSCALL_ID_EXIT, status); +} + +int putc(const int c){ + return syscall1(SYSCALL_ID_PUTC, c); +} + +void yolo() +{ + syscall0(SYSCALL_ID_YOLO); +} diff --git a/userspace/libc.h b/userspace/libc.h new file mode 100644 index 0000000..c93d8ba --- /dev/null +++ b/userspace/libc.h @@ -0,0 +1,39 @@ +#pragma once +#include "assert.h" +#include "stdarg.h" +#include "minmax.h" + +#define islower(c) (('a' <= (c)) && ((c) <= 'z')) +#define isupper(c) (('A' <= (c)) && ((c) <= 'Z')) +#define isdigit(c) (('0' <= (c)) && ((c) <= '9')) +#define isspace(c) \ + (((c) == ' ') || ((c) == '\t') || ((c) == '\f') || ((c) == '\n') || ((c) == '\r') || \ + ((c) == '\v')) +#define isprint(c) ((' ' <= (c)) && ((c) <= '~')) + +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *itoa(long long int value, char *str, int base); +void reverse(char s[]); +int strlen(const char s[]); +unsigned int strnlen(const char *s, size_t count); +int strcmp(const char s1[], const char s2[]); +char *strzcpy(char *dst, const char *src, int len); +int puts(const char *str); +int putc(const int c); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int vprintf(const char *format, va_list ap); +int printf(const char *format, ...); + +// Could be used after malloc is available +int asprintf(char **strp, const char *fmt, ...); +int vasprintf(char **strp, const char *fmt, va_list ap); + +int syscall3(int id, unsigned int arg1, unsigned int arg2, unsigned int arg3); +int syscall2(int id, unsigned int arg1, unsigned int arg2); +int syscall1(int id, unsigned int arg1); +int syscall0(int id); + +void _exit(int status); +void yolo(); diff --git a/userspace/linker.ld b/userspace/linker.ld new file mode 100644 index 0000000..45f596c --- /dev/null +++ b/userspace/linker.ld @@ -0,0 +1,33 @@ +OUTPUT_FORMAT(elf32-i386); +/* C entry point: used by crt.c*/ +ENTRY(_start) + +SECTIONS +{ + /* Userspace vitual address PAGING_BASE_USER_ADDRESS */ + . = 0x40000000; + + .text : ALIGN(4096) + { + *(.text) + } + + /* Read-only data. */ + .rodata : ALIGN(4096) + { + *(.rodata) + } + + /* Read-write data (initialized) */ + .data : ALIGN(4096) + { + *(.data) + } + + /* Read-write data (uninitialized) and stack */ + .bss : ALIGN(4096) + { + *(COMMON) + *(.bss) + } +} diff --git a/userspace/main_user.c b/userspace/main_user.c new file mode 100644 index 0000000..7c6dae7 --- /dev/null +++ b/userspace/main_user.c @@ -0,0 +1,12 @@ +#include "libc.h" + +int main(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + yolo(); + printf("User is about to suicide\n"); + int *yolo =0; + *yolo = 1; + return 0; +} diff --git a/userspace/math.c b/userspace/math.c new file mode 100644 index 0000000..bdc8c74 --- /dev/null +++ b/userspace/math.c @@ -0,0 +1,9 @@ +#include "math.h" + +inline uint32_t log2(const uint32_t x) +{ + uint32_t y; + // Get the highest set bit + asm("\tbsr %1, %0\n" : "=r"(y) : "r"(x)); + return y; +} diff --git a/userspace/math.h b/userspace/math.h new file mode 100644 index 0000000..db179ce --- /dev/null +++ b/userspace/math.h @@ -0,0 +1,5 @@ +#pragma once +#include "stdarg.h" +#include "types.h" + +uint32_t log2(uint32_t x); diff --git a/userspace/minmax.h b/userspace/minmax.h new file mode 100644 index 0000000..2444b7b --- /dev/null +++ b/userspace/minmax.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_MINMAX_H +#define _LINUX_MINMAX_H + +/* + * From linux kernel include/linux/compiler_types.h + */ +#define ___PASTE(a,b) a##b +#define __PASTE(a,b) ___PASTE(a,b) + +/* + * From Linux kernel include/linux/compiler.h + */ +/* Not-quite-unique ID. */ +#ifndef __UNIQUE_ID +# define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__) +#endif + + + +/* + * min()/max()/clamp() macros must accomplish three things: + * + * - avoid multiple evaluations of the arguments (so side-effects like + * "x++" happen only once) when non-constant. + * - perform strict type-checking (to generate warnings instead of + * nasty runtime surprises). See the "unnecessary" pointer comparison + * in __typecheck(). + * - retain result as a constant expressions when called with only + * constant expressions (to avoid tripping VLA warnings in stack + * allocation usage). + */ +#define __typecheck(x, y) \ + (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1))) + +/* + * This returns a constant expression while determining if an argument is + * a constant expression, most importantly without evaluating the argument. + * Glory to Martin Uecker + */ +#define __is_constexpr(x) \ + (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) + +#define __no_side_effects(x, y) \ + (__is_constexpr(x) && __is_constexpr(y)) + +#define __safe_cmp(x, y) \ + (__typecheck(x, y) && __no_side_effects(x, y)) + +#define __cmp(x, y, op) ((x) op (y) ? (x) : (y)) + +#define __cmp_once(x, y, unique_x, unique_y, op) ({ \ + typeof(x) unique_x = (x); \ + typeof(y) unique_y = (y); \ + __cmp(unique_x, unique_y, op); }) + +#define __careful_cmp(x, y, op) \ + __builtin_choose_expr(__safe_cmp(x, y), \ + __cmp(x, y, op), \ + __cmp_once(x, y, __UNIQUE_ID(__x), __UNIQUE_ID(__y), op)) + +/** + * min - return minimum of two values of the same or compatible types + * @x: first value + * @y: second value + */ +#define min(x, y) __careful_cmp(x, y, <) + +/** + * max - return maximum of two values of the same or compatible types + * @x: first value + * @y: second value + */ +#define max(x, y) __careful_cmp(x, y, >) + +/** + * min3 - return minimum of three values + * @x: first value + * @y: second value + * @z: third value + */ +#define min3(x, y, z) min((typeof(x))min(x, y), z) + +/** + * max3 - return maximum of three values + * @x: first value + * @y: second value + * @z: third value + */ +#define max3(x, y, z) max((typeof(x))max(x, y), z) + +/** + * min_not_zero - return the minimum that is _not_ zero, unless both are zero + * @x: value1 + * @y: value2 + */ +#define min_not_zero(x, y) ({ \ + typeof(x) __x = (x); \ + typeof(y) __y = (y); \ + __x == 0 ? __y : ((__y == 0) ? __x : min(__x, __y)); }) + +/** + * clamp - return a value clamped to a given range with strict typechecking + * @val: current value + * @lo: lowest allowable value + * @hi: highest allowable value + * + * This macro does strict typechecking of @lo/@hi to make sure they are of the + * same type as @val. See the unnecessary pointer comparisons. + */ +#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi) + +/* + * ..and if you can't take the strict + * types, you can specify one yourself. + * + * Or not use min/max/clamp at all, of course. + */ + +/** + * min_t - return minimum of two values, using the specified type + * @type: data type to use + * @x: first value + * @y: second value + */ +#define min_t(type, x, y) __careful_cmp((type)(x), (type)(y), <) + +/** + * max_t - return maximum of two values, using the specified type + * @type: data type to use + * @x: first value + * @y: second value + */ +#define max_t(type, x, y) __careful_cmp((type)(x), (type)(y), >) + +/** + * clamp_t - return a value clamped to a given range using a given type + * @type: the type of variable to use + * @val: current value + * @lo: minimum allowable value + * @hi: maximum allowable value + * + * This macro does no typechecking and uses temporary variables of type + * @type to make all the comparisons. + */ +#define clamp_t(type, val, lo, hi) min_t(type, max_t(type, val, lo), hi) + +/** + * clamp_val - return a value clamped to a given range using val's type + * @val: current value + * @lo: minimum allowable value + * @hi: maximum allowable value + * + * This macro does no typechecking and uses temporary variables of whatever + * type the input argument @val is. This is useful when @val is an unsigned + * type and @lo and @hi are literals that will otherwise be assigned a signed + * integer type. + */ +#define clamp_val(val, lo, hi) clamp_t(typeof(val), val, lo, hi) + +/** + * swap - swap values of @a and @b + * @a: first value + * @b: second value + */ +#define swap(a, b) \ + do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) + +#endif /* _LINUX_MINMAX_H */ diff --git a/userspace/stdarg.h b/userspace/stdarg.h new file mode 100644 index 0000000..c006082 --- /dev/null +++ b/userspace/stdarg.h @@ -0,0 +1,54 @@ +#pragma once + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +#ifdef __GNUC__ +__extension__ typedef __signed__ long long __s64; +__extension__ typedef unsigned long long __u64; +#else +typedef __signed__ long long __s64; +typedef unsigned long long __u64; +#endif + +/* sysv */ +typedef unsigned char unchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef __s8 int8_t; +typedef __s16 int16_t; +typedef __s32 int32_t; + +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; + +typedef __u64 uint64_t; +typedef __u64 u_int64_t; +typedef __s64 int64_t; + +typedef enum { FALSE = 0, TRUE } bool_t; +#define NULL ((void *)0) + +#if __x86_64__ +typedef unsigned long size_t; +typedef long ssize_t; +typedef unsigned long int uintptr_t; +#else +typedef unsigned int size_t; +typedef int ssize_t; +typedef unsigned int uintptr_t; +#endif + +typedef void *va_list; +#define va_start(v, l) ((v) = (va_list) & (l) + sizeof(l)) +#define va_end(v) ((v) = NULL) +#define va_arg(v, type) (*(type *)(((v) += sizeof(type)) - sizeof(type))) diff --git a/userspace/types.h b/userspace/types.h new file mode 100644 index 0000000..45f0fd0 --- /dev/null +++ b/userspace/types.h @@ -0,0 +1,38 @@ +#pragma once + +#define USHRT_MAX ((u16)(~0U)) +#define SHRT_MAX ((s16)(USHRT_MAX >> 1)) +#define SHRT_MIN ((s16)(-SHRT_MAX - 1)) +#define INT_MAX ((int)(~0U >> 1)) +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX (~0U) +#define LONG_MAX ((long)(~0UL >> 1)) +#define LONG_MIN (-LONG_MAX - 1) +#define ULONG_MAX (~0UL) +#define LLONG_MAX ((long long)(~0ULL >> 1)) +#define LLONG_MIN (-LLONG_MAX - 1) +#define ULLONG_MAX (~0ULL) +#define SIZE_MAX (~(size_t)0) +#define PHYS_ADDR_MAX (~(phys_addr_t)0) + +#define U8_MAX ((u8)~0U) +#define S8_MAX ((s8)(U8_MAX >> 1)) +#define S8_MIN ((s8)(-S8_MAX - 1)) +#define U16_MAX ((u16)~0U) +#define S16_MAX ((s16)(U16_MAX >> 1)) +#define S16_MIN ((s16)(-S16_MAX - 1)) +#define U32_MAX ((u32)~0U) +#define S32_MAX ((s32)(U32_MAX >> 1)) +#define S32_MIN ((s32)(-S32_MAX - 1)) +#define U64_MAX ((u64)~0ULL) +#define S64_MAX ((s64)(U64_MAX >> 1)) +#define S64_MIN ((s64)(-S64_MAX - 1)) + +// Virtual address +typedef unsigned long vaddr_t; + +// Physical address +typedef unsigned long paddr_t; + +// Userspace vaddr +typedef unsigned long uaddr_t;