load userspace from disk

This commit is contained in:
Mathieu Maret 2021-11-05 23:02:23 +01:00
parent 47f1d3f8ab
commit 2b32908029
25 changed files with 1237 additions and 27 deletions

View File

@ -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

View File

@ -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 (;;)

View File

@ -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));

View File

@ -3,4 +3,6 @@
#define SYSCALL_INTR_NB 0x42
#ifdef __KERNEL__
int syscallSetup();
#endif

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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++) {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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
View 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
View File

@ -0,0 +1,9 @@
#include "libc.h"
void _start()
{
/* This starter function expects a main() function somewhere */
extern int main();
_exit(main());
}

View File

@ -0,0 +1,8 @@
#pragma once
#define SYSCALL_INTR_NB 0x42
#ifdef __KERNEL__
int syscallSetup();
#endif

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;