load userspace from disk
This commit is contained in:
parent
47f1d3f8ab
commit
2b32908029
20
Makefile
20
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
|
||||
|
@ -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 (;;)
|
||||
|
@ -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));
|
||||
|
@ -3,4 +3,6 @@
|
||||
#define SYSCALL_INTR_NB 0x42
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int syscallSetup();
|
||||
#endif
|
||||
|
245
core/main.c
245
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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
21
userspace/assert.h
Normal file
21
userspace/assert.h
Normal file
@ -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)
|
9
userspace/crt.c
Normal file
9
userspace/crt.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include "libc.h"
|
||||
|
||||
void _start()
|
||||
{
|
||||
/* This starter function expects a main() function somewhere */
|
||||
extern int main();
|
||||
|
||||
_exit(main());
|
||||
}
|
8
userspace/kernel/swintr.h
Normal file
8
userspace/kernel/swintr.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define SYSCALL_INTR_NB 0x42
|
||||
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int syscallSetup();
|
||||
#endif
|
12
userspace/kernel/syscall.h
Normal file
12
userspace/kernel/syscall.h
Normal file
@ -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
|
514
userspace/libc.c
Normal file
514
userspace/libc.c
Normal file
@ -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 s1<s2, 0 if s1==s2, >0 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);
|
||||
}
|
39
userspace/libc.h
Normal file
39
userspace/libc.h
Normal file
@ -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();
|
33
userspace/linker.ld
Normal file
33
userspace/linker.ld
Normal file
@ -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)
|
||||
}
|
||||
}
|
12
userspace/main_user.c
Normal file
12
userspace/main_user.c
Normal file
@ -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;
|
||||
}
|
9
userspace/math.c
Normal file
9
userspace/math.c
Normal file
@ -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;
|
||||
}
|
5
userspace/math.h
Normal file
5
userspace/math.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "stdarg.h"
|
||||
#include "types.h"
|
||||
|
||||
uint32_t log2(uint32_t x);
|
169
userspace/minmax.h
Normal file
169
userspace/minmax.h
Normal file
@ -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 <Martin.Uecker@med.uni-goettingen.de>
|
||||
*/
|
||||
#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 */
|
54
userspace/stdarg.h
Normal file
54
userspace/stdarg.h
Normal file
@ -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)))
|
38
userspace/types.h
Normal file
38
userspace/types.h
Normal file
@ -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;
|
Loading…
Reference in New Issue
Block a user