user_space #4

Merged
mathieu merged 40 commits from user_space into master 2021-11-04 16:17:36 +01:00
45 changed files with 2259 additions and 580 deletions

View File

@ -12,16 +12,19 @@ QEMU_OPT += -hda disk.img
ARCH?=x86 ARCH?=x86
SUBDIRS := core drivers tests arch/$(ARCH) SUBDIRS := core drivers tests arch/$(ARCH)
CPPFLAGS += $(foreach dir, $(SUBDIRS), -I$(dir)) INCDIRS += $(foreach dir, $(SUBDIRS), -I$(dir))
CPPFLAGS += $(INCDIRS)
asmsrc=$(wildcard arch/$(ARCH)/boot/*.asm) asmsrc=
asmobj=$(asmsrc:%.asm=%.o) asmobj=$(asmsrc:%.asm=%.o)
gasmsrc=$(wildcard arch/$(ARCH)/*.S arch/$(ARCH)/boot/*.S)
gasmobj=$(gasmsrc:%.S=%.o)
csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c) csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c)
cobj=$(csrc:%.c=%.o) arch/$(ARCH)/cpu_context_switch.o arch/$(ARCH)/irq_pit.o arch/$(ARCH)/irq_wrappers.o arch/$(ARCH)/exception_wrappers.o cobj=$(csrc:%.c=%.o)
deps = $(csrc:%.c=%.d) deps=$(csrc:%.c=%.d) $(gasmsrc:%.S=%.d)
kernel kernel.sym &: $(asmobj) $(cobj) linker.ld kernel kernel.sym &: $(asmobj) $(gasmobj) $(cobj) linker.ld
$(CC) -m32 -ffreestanding -nostdlib $(cobj) $(asmobj) -o kernel -T linker.ld -lgcc $(CC) -m32 -ffreestanding -nostdlib $(cobj) $(gasmobj) $(asmobj) -o kernel -T linker.ld -lgcc
objcopy --only-keep-debug kernel kernel.sym objcopy --only-keep-debug kernel kernel.sym
objcopy --strip-debug kernel objcopy --strip-debug kernel
@ -38,7 +41,7 @@ disk.img:
$(AS) $(ASFLAGS) -o $@ $< $(AS) $(ASFLAGS) -o $@ $<
%.o: %.S %.o: %.S
$(CC) "-I$(PWD)" -c "$<" $(CFLAGS) -o "$@" $(CC) $(CFLAGS) $(CPPFLAGS) -c "$<" -o "$@"
test: CFLAGS += -DRUN_TEST test: CFLAGS += -DRUN_TEST
@ -60,7 +63,7 @@ debug_test: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST
debug_test: debug debug_test: debug
clean: clean:
$(RM) kernel $(asmobj) $(cobj) $(deps) fd.iso kernel.sym $(RM) kernel $(asmobj) $(gasmobj) $(cobj) $(deps) fd.iso kernel.sym
$(RM) -r isodir $(RM) -r isodir
ifneq ($(MAKECMDGOALS),clean) ifneq ($(MAKECMDGOALS),clean)

View File

@ -31,7 +31,7 @@ stack is properly aligned and failure to align the stack will result in
undefined behavior. undefined behavior.
*/ */
.section .bss .section .bss
.align 16 .align 4096
stack_bottom: stack_bottom:
.skip 16384 # 16 KiB .skip 16384 # 16 KiB
stack_top: stack_top:
@ -117,3 +117,7 @@ Set the size of the _start symbol to the current location '.' minus its start.
This is useful when debugging or when you implement call tracing. This is useful when debugging or when you implement call tracing.
*/ */
.size _start, . - _start .size _start, . - _start
.global _stack_bottom
_stack_bottom: stack_bottom
.global _stack_stop
_stack_stop: stack_top

View File

@ -28,7 +28,7 @@ align 4
; stack is properly aligned and failure to align the stack will result in ; stack is properly aligned and failure to align the stack will result in
; undefined behavior. ; undefined behavior.
section .bss section .bss
align 16 align 4096
stack_bottom: stack_bottom:
resb 16384 ; 16 KiB resb 16384 ; 16 KiB
stack_top: stack_top:

View File

@ -1,15 +1,16 @@
/* Copyright (C) 2005 David Decotigny /* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2005 David Decotigny
Copyright (C) 2000-2004, The KOS team Copyright (C) 2000-2004, The KOS team
Initially taken from SOS Initially taken from SOS
*/ */
#include "cpu_context.h"
#include "assert.h" #include "assert.h"
#include "gdt.h"
#include "klibc.h" #include "klibc.h"
#include "segment.h" #include "segment.h"
#include "cpu_context.h"
/** /**
* Here is the definition of a CPU context for IA32 processors. This * Here is the definition of a CPU context for IA32 processors. This
* is a Matos/SOS convention, not a specification given by the IA32 * is a Matos/SOS convention, not a specification given by the IA32
@ -37,12 +38,11 @@ struct cpu_state {
uint16_t alignment_padding; /* unused */ uint16_t alignment_padding; /* unused */
uint32_t edi; uint32_t edi;
uint32_t esi; uint32_t esi;
uint32_t esp;
uint32_t ebp;
uint32_t ebx; uint32_t ebx;
uint32_t edx; uint32_t edx;
uint32_t ecx; uint32_t ecx;
uint32_t eax; uint32_t eax;
uint32_t ebp;
/* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */ /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
uint32_t error_code; uint32_t error_code;
@ -76,6 +76,22 @@ struct cpu_kstate {
struct cpu_state regs; struct cpu_state regs;
} __attribute__((packed)); } __attribute__((packed));
/**
* Structure of an interrupted User thread's context. This is almost
* the same as a kernel context, except that 2 additional values are
* pushed on the stack before the eflags/cs/eip of the interrupted
* context: the stack configuration of the interrupted user context.
*
* @see Section 6.4.1 of Intel x86 vol 1
*/
struct cpu_ustate {
struct cpu_state regs;
struct {
uint32_t cpl3_esp;
uint16_t cpl3_ss;
};
} __attribute__((packed));
/** /**
* THE main operation of a kernel thread. This routine calls the * THE main operation of a kernel thread. This routine calls the
* kernel thread function start_func and calls exit_func when * kernel thread function start_func and calls exit_func when
@ -96,6 +112,123 @@ static void core_routine(cpu_kstate_function_arg1_t *start_func, void *start_arg
; ;
} }
/*
* Structure of a Task State Segment on the x86 Architecture.
*
* @see Intel x86 spec vol 3, figure 6-2
*
* @note Such a data structure should not cross any page boundary (see
* end of section 6.2.1 of Intel spec vol 3). This is the reason why
* we tell gcc to align it on a 128B boundary (its size is 104B, which
* is <= 128).
*/
struct x86_tss {
/**
* Intel provides a way for a task to switch to another in an
* automatic way (call gates). In this case, the back_link field
* stores the source TSS of the context switch. This allows to
* easily implement coroutines, task backtracking, ... In Matos/SOS we
* don't use TSS for the context switch purpouse, so we always
* ignore this field.
* (+0)
*/
uint16_t back_link;
uint16_t reserved1;
/* CPL0 saved context. (+4) */
vaddr_t esp0;
uint16_t ss0;
uint16_t reserved2;
/* CPL1 saved context. (+12) */
vaddr_t esp1;
uint16_t ss1;
uint16_t reserved3;
/* CPL2 saved context. (+20) */
vaddr_t esp2;
uint16_t ss2;
uint16_t reserved4;
/* Interrupted context's saved registers. (+28) */
vaddr_t cr3;
vaddr_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
/* +72 */
uint16_t es;
uint16_t reserved5;
/* +76 */
uint16_t cs;
uint16_t reserved6;
/* +80 */
uint16_t ss;
uint16_t reserved7;
/* +84 */
uint16_t ds;
uint16_t reserved8;
/* +88 */
uint16_t fs;
uint16_t reserved9;
/* +92 */
uint16_t gs;
uint16_t reserved10;
/* +96 */
uint16_t ldtr;
uint16_t reserved11;
/* +100 */
uint16_t debug_trap_flag : 1;
uint16_t reserved12 : 15;
uint16_t iomap_base_addr;
/* 104 */
} __attribute__((packed, aligned(128)));
static struct x86_tss kernel_tss;
int cpu_context_subsystem_setup()
{
/* Reset the kernel TSS */
memset(&kernel_tss, 0x0, sizeof(kernel_tss));
/**
* Now setup the kernel TSS.
*
* Considering the privilege change method we choose (cpl3 -> cpl0
* through a software interrupt), we don't need to initialize a
* full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually,
* only a correct value for the kernel esp and ss are required (aka
* "ss0" and "esp0" fields). Since the esp0 will have to be updated
* at privilege change time, we don't have to set it up now.
*/
kernel_tss.ss0 = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA);
/* Register this TSS into the gdt */
gdtRegisterTSS((vaddr_t)&kernel_tss);
return 0;
}
int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func, int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func,
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size, vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg) cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg)
@ -177,6 +310,43 @@ int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_f
return 0; return 0;
} }
int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2,
uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize)
{
// The user context is stacked above the usual cpu state by the CPU on context switch.
// So store it when the cpu expect it (See cpu_kstate_init for more details)
struct cpu_ustate *uctx =
(struct cpu_ustate *)(kernelStackBottom + kernelStackSize - sizeof(struct cpu_ustate));
/* If needed, poison the stack */
#ifdef CPU_STATE_DETECT_UNINIT_KERNEL_VARS
memset((void *)kernelStackBottom, CPU_STATE_STACK_POISON, kernelStackSize);
#elif defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size);
#endif
memset(uctx, 0, sizeof(struct cpu_ustate));
uctx->regs.eip = startPC;
uctx->regs.eax = arg1;
uctx->regs.ebx = arg2;
uctx->regs.cs = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE); // Code
uctx->regs.ds = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // Data
uctx->regs.es = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // Data
uctx->regs.cpl0_ss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); // Kernel Stack
uctx->cpl3_ss = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // User Stack
uctx->cpl3_esp = startSP;
/* The newly created context is initially interruptible */
uctx->regs.eflags = (1 << 9); /* set IF bit */
*ctx = (struct cpu_state *)uctx;
return 0;
}
#if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) #if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt, void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
vaddr_t stack_bottom, size_t stack_size) vaddr_t stack_bottom, size_t stack_size)
@ -213,6 +383,30 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_
/* ======================================================================= /* =======================================================================
* Public Accessor functions * Public Accessor functions
*/ */
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt)
{
/* An interrupted user thread has its CS register set to that of the
User code segment */
switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs)) {
case BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE):
return TRUE;
break;
case BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE):
return FALSE;
break;
default:
pr_err("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !",
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs),
BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE),
BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE));
break;
}
/* Should never get here */
return -1;
}
vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt) vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt)
{ {
@ -233,6 +427,27 @@ vaddr_t cpu_context_get_SP(const struct cpu_state *ctxt)
return (vaddr_t)ctxt; return (vaddr_t)ctxt;
} }
uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt)
{
assert(NULL != ctxt);
/* This is the Err_code of the interrupted context (ie kernel or user
context). */
return ctxt->error_code;
}
vaddr_t cpu_context_get_EX_faulting_vaddr(const struct cpu_state *ctxt)
{
assert(NULL != ctxt);
// A page fault has occurred.
// The faulting address is stored in the CR2 register.
vaddr_t faulting_address;
asm volatile("mov %%cr2, %0" : "=r"(faulting_address));
return faulting_address;
}
void cpu_context_dump(const struct cpu_state *ctxt) void cpu_context_dump(const struct cpu_state *ctxt)
{ {
printf("CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", (unsigned)ctxt->eip, printf("CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", (unsigned)ctxt->eip,
@ -240,3 +455,68 @@ void cpu_context_dump(const struct cpu_state *ctxt)
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds, (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds,
(unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code); (unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code);
} }
/* *************************************************************
* Function to manage the TSS. This function is not really "public":
* it is reserved to the assembler routines defined in
* cpu_context_switch.S
*
* Update the kernel stack address so that the IRQ, syscalls and
* exception return in a correct stack location when coming back into
* kernel mode.
*/
void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt)
{
/* next_ctxt corresponds to an interrupted user thread ? */
if (cpu_context_is_in_user_mode(next_ctxt)) {
/*
* Yes: "next_ctxt" is an interrupted user thread => we are
* going to switch to user mode ! Setup the stack address so
* that the user thread "next_ctxt" can come back to the correct
* stack location when returning in kernel mode.
*
* This stack location corresponds to the SP of the next user
* thread once its context has been transferred on the CPU, ie
* once the CPU has executed all the pop/iret instruction of the
* context switch with privilege change.
*/
kernel_tss.esp0 = ((vaddr_t)next_ctxt) + sizeof(struct cpu_ustate);
/* Note: no need to protect this agains IRQ because IRQs are not
allowed to update it by themselves, and they are not allowed
to block */
} else {
/* No: No need to update kernel TSS when we stay in kernel
mode */
}
}
inline
int syscallGet3args(const struct cpu_state *user_ctxt,
/* out */unsigned int *arg1,
/* out */unsigned int *arg2,
/* out */unsigned int *arg3)
{
*arg1 = user_ctxt->ebx;
*arg2 = user_ctxt->ecx;
*arg3 = user_ctxt->edx;
return 0;
}
int syscallGet1arg(const struct cpu_state *user_ctxt,
/* out */unsigned int *arg1)
{
unsigned int unused;
return syscallGet3args(user_ctxt, arg1, & unused, & unused);
}
int _syscallGet2args(const struct cpu_state *user_ctxt,
/* out */unsigned int *arg1,
/* out */unsigned int *arg2)
{
unsigned int unused;
return syscallGet3args(user_ctxt, arg1, arg2, & unused);
}

View File

@ -2,18 +2,35 @@
.text .text
/**
* C Function called by the routines below in order to tell the CPU
* where will be the kernel stack (needed by the interrupt handlers)
* when next_ctxt will come back into kernel mode.
*
* void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt)
*
* @see end of cpu_context.c
*/
.extern cpu_context_update_kernel_tss
.globl cpu_context_switch .globl cpu_context_switch
.type cpu_context_switch, @function .type cpu_context_switch, @function
cpu_context_switch: cpu_context_switch:
// arg2= to_context -- esp+68 // arg2= to_context -- esp+64
// arg1= from_context -- esp+64 // arg1= from_context -- esp+60
// caller ip -- esp+60 // caller ip -- esp+56
pushf // (eflags) esp+56 pushf // (eflags) esp+52
pushl %cs // (cs) esp+52 pushl %cs // (cs) esp+48
pushl $resume_pc // (ip) esp+48 pushl $resume_pc // (ip) esp+44
pushl $0 // (error code) esp+12+8x4 pushl $0 // (error code) esp+12+7x4
pushal // (general reg) esp+12 pushl %ebp
pushl %eax
pushl %ecx
pushl %edx
pushl %ebx
pushl %esi
pushl %edi
subl $2, %esp // (alignment) esp+10 subl $2, %esp // (alignment) esp+10
pushw %ss // esp+8 pushw %ss // esp+8
pushw %ds // esp+6 pushw %ds // esp+6
@ -26,11 +43,19 @@ cpu_context_switch:
*/ */
/* Store the address of the saved context */ /* Store the address of the saved context */
movl 64(%esp), %ebx movl 60(%esp), %ebx
movl %esp, (%ebx) movl %esp, (%ebx)
/* This is the proper context switch ! We change the stack here */ /* This is the proper context switch ! We change the stack here */
movl 68(%esp), %esp movl 64(%esp), %esp
/* Prepare kernel TSS in case we are switching to a user thread: we
make sure that we will come back into the kernel at a correct
stack location */
pushl %esp /* Pass the location of the context we are
restoring to the function */
call cpu_context_update_kernel_tss
addl $4, %esp
/* Restore the CPU context */ /* Restore the CPU context */
popw %gs popw %gs
@ -39,7 +64,13 @@ cpu_context_switch:
popw %ds popw %ds
popw %ss popw %ss
addl $2,%esp addl $2,%esp
popal popl %edi
popl %esi
popl %ebx
popl %edx
popl %ecx
popl %eax
popl %ebp
addl $4, %esp /* Ignore "error code" */ addl $4, %esp /* Ignore "error code" */
/* This restores the eflags, the cs and the eip registers */ /* This restores the eflags, the cs and the eip registers */
@ -75,6 +106,14 @@ cpu_context_exit_to:
call *8(%eax) call *8(%eax)
addl $4, %esp addl $4, %esp
/* Prepare kernel TSS in case we are switching to a user thread: we
make sure that we will come back into the kernel at a correct
stack location */
pushl %esp /* Pass the location of the context we are
restoring to the function */
call cpu_context_update_kernel_tss
addl $4, %esp
/* Restore the CPU context */ /* Restore the CPU context */
popw %gs popw %gs
popw %fs popw %fs
@ -82,7 +121,13 @@ cpu_context_exit_to:
popw %ds popw %ds
popw %ss popw %ss
addl $2,%esp addl $2,%esp
popal popl %edi
popl %esi
popl %ebx
popl %edx
popl %ecx
popl %eax
popl %ebp
addl $4, %esp /* Ignore "error code" */ addl $4, %esp /* Ignore "error code" */
/* This restores the eflags, the cs and the eip registers */ /* This restores the eflags, the cs and the eip registers */

View File

@ -4,7 +4,7 @@
#include "interrupt.h" #include "interrupt.h"
#include "irq.h" #include "irq.h"
#include "klibc.h" #include "klibc.h"
#include "kthread.h" #include "thread.h"
#include "vga.h" #include "vga.h"
exception_handler exception_handler_array[EXCEPTION_NUM] = { exception_handler exception_handler_array[EXCEPTION_NUM] = {
@ -51,14 +51,10 @@ void print_handler(struct cpu_state *frame, ulong intr)
void pagefault_handler(struct cpu_state *frame, ulong intr) void pagefault_handler(struct cpu_state *frame, ulong intr)
{ {
// A page fault has occurred.
// The faulting address is stored in the CR2 register.
uint32_t faulting_address;
asm volatile("mov %%cr2, %0" : "=r"(faulting_address));
struct kthread *current = getCurrentThread(); struct thread *current = getCurrentThread();
printf("page fault while in thread %s at 0x%x 0x%x\n", current->name, faulting_address, 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_PC(frame), cpu_context_get_EX_faulting_vaddr(frame), cpu_context_get_EX_err(frame));
VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr); VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr);
(void)intr; (void)intr;
for (;;) for (;;)

View File

@ -1,10 +1,19 @@
#define ASM_SOURCE 1 #define ASM_SOURCE 1
#include "segment.h"
.file "irq_wrappers.S" .file "irq_wrappers.S"
.text .text
.extern exception_handler_wrap .extern exception_handler_wrap
.globl exception_handler_wrapper_array .globl exception_handler_wrapper_array
/** Update the kernel TSS in case we are switching to a thread in user
mode in order to come back into the correct kernel stack */
.extern cpu_context_update_kernel_tss
/* The address of the function to call to set back the user thread's
MMU configuration upon return to user context */
.extern threadPrepareExceptionSwitchBack
.altmacro .altmacro
@ -13,61 +22,156 @@
.type exception_wrapper_\id,@function .type exception_wrapper_\id,@function
/* INTERRUPT FRAME START */ /* INTERRUPT FRAME START */
/* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */ /* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */
/* uint32_t ip */
/* uint32_t cs; */
/* uint32_t flags */ /* uint32_t flags */
/* uint32_t cs; */
/* uint32_t ip */
/* Pushes the other reg to save same and look like a struct cpu_state*/ /* Pushes the other reg to save same and look like a struct cpu_state*/
/* Fake error code */ /* Fake error code */
pushl $0 pushl $0
/* Backup the actual context */ /* Backup the actual context */
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
pushl %edi pushl %eax
pushl %esi pushl %ecx
pushl %edx pushl %edx
pushl %ecx pushl %ebx
pushl %ebx pushl %esi
pushl %eax pushl %edi
subl $2,%esp subl $2,%esp
pushw %ss pushw %ss
pushw %ds pushw %ds
pushw %es pushw %es
pushw %fs pushw %fs
pushw %gs pushw %gs
/* Set correct kernel segment descriptors' value */
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
pushw %di ; popw %ds
pushw %di ; popw %es
pushw %di ; popw %fs
pushw %di ; popw %gs
push %esp push %esp
pushl $\id pushl $\id
call exception_handler_wrap call exception_handler_wrap
addl $8, %esp addl $8, %esp
/* Restore the context */ /* Reconfigure the MMU if needed */
popw %gs pushl %esp /* cpu_ctxt */
popw %fs call threadPrepareExceptionSwitchBack
popw %es addl $4, %esp /* Unallocate the stack */
popw %ds
popw %ss
addl $2,%esp
popl %eax
popl %ebx
popl %ecx
popl %edx
popl %esi
popl %edi
popl %ebp
/* Remove fake error code */ /* Prepare kernel TSS in case we are switching to a
addl $4, %esp user thread: we make sure that we will come back
into the kernel at a correct stack location */
pushl %esp /* Pass the location of the context we are
restoring to the function */
call cpu_context_update_kernel_tss
addl $4, %esp
/* Restore the context */
popw %gs
popw %fs
popw %es
popw %ds
popw %ss
addl $2,%esp
popl %edi
popl %esi
popl %ebx
popl %edx
popl %ecx
popl %eax
popl %ebp
/* Remove fake error code */
addl $4, %esp
iret iret
.endm .endm
.macro exception_mac_with_errcode id
exception_wrapper_\id:
.type exception_wrapper_\id,@function
/* INTERRUPT FRAME START */
/* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */
/* uint32_t flags */
/* uint32_t cs; */
/* uint32_t ip */
/* uint32_t errcode */
/* Pushes the other reg to save same and look like a struct cpu_state*/
.set i, 0 /* Backup the actual context */
.rept 0x20 pushl %ebp
exception_mac %i movl %esp, %ebp
.set i, i+1
pushl %eax
pushl %ecx
pushl %edx
pushl %ebx
pushl %esi
pushl %edi
subl $2,%esp
pushw %ss
pushw %ds
pushw %es
pushw %fs
pushw %gs
/* Set correct kernel segment descriptors' value */
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
pushw %di ; popw %ds
pushw %di ; popw %es
pushw %di ; popw %fs
pushw %di ; popw %gs
push %esp
pushl $\id
call exception_handler_wrap
addl $8, %esp
/* Reconfigure the MMU if needed */
pushl %esp /* cpu_ctxt */
call threadPrepareExceptionSwitchBack
addl $4, %esp /* Unallocate the stack */
/* Prepare kernel TSS in case we are switching to a
user thread: we make sure that we will come back
into the kernel at a correct stack location */
pushl %esp /* Pass the location of the context we are
restoring to the function */
call cpu_context_update_kernel_tss
addl $4, %esp
/* Restore the context */
popw %gs
popw %fs
popw %es
popw %ds
popw %ss
addl $2,%esp
popl %edi
popl %esi
popl %ebx
popl %edx
popl %ecx
popl %eax
popl %ebp
/* Error code isn't compatible with iretd */
addl $4, %esp
iret
.endm
/*List of exception w or w/o err codehttps://wiki.osdev.org/Exceptions*/
.irp exception_id, 8, 10, 11, 12, 13, 14, 17, 30
exception_mac_with_errcode exception_id
.endr
.irp exception_id, 0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16, 18, 19, 20 21, 22, 23, 24, 25, 26, 27, 28, 29, 31
exception_mac exception_id
.endr .endr
.macro ref_exception_wrapper id .macro ref_exception_wrapper id

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2004 David Decotigny /* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2004 David Decotigny
Copyright (C) 1999 Free Software Foundation, Inc. Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -17,7 +18,6 @@
USA. USA.
*/ */
#include "segment.h" #include "segment.h"
#include "gdt.h" #include "gdt.h"
/** /**
@ -111,6 +111,10 @@ static struct x86_segment_descriptor gdt[] = {
}, },
[SEG_KCODE] = BUILD_GDTE(0, 1), [SEG_KCODE] = BUILD_GDTE(0, 1),
[SEG_KDATA] = BUILD_GDTE(0, 0), [SEG_KDATA] = BUILD_GDTE(0, 0),
[SEG_UCODE] = BUILD_GDTE(3, 1),
[SEG_UDATA] = BUILD_GDTE(3, 0),
[SEG_K_TSS] = {0,}, // Used by syscall, IRQ while in user space
// initialized by gdtRegisterTSS
}; };
int gdtSetup(void) int gdtSetup(void)
@ -144,3 +148,29 @@ int gdtSetup(void)
return 0; return 0;
} }
int gdtRegisterTSS(vaddr_t tss_vaddr)
{
uint16_t regval_tss;
/* Initialize the GDT entry */
gdt[SEG_K_TSS] = (struct x86_segment_descriptor){
.limit_15_0 = 0x67, /* See Intel x86 vol 3 section 6.2.2 */
.base_paged_addr_15_0 = (tss_vaddr)&0xffff,
.base_paged_addr_23_16 = (tss_vaddr >> 16) & 0xff,
.segment_type = 0x9, /* See Intel x86 vol 3 figure 6-3 */
.descriptor_type = 0, /* (idem) */
.dpl = 3, /* Allowed for CPL3 tasks */
.present = 1,
.limit_19_16 = 0, /* Size of a TSS is < 2^16 ! */
.custom = 0, /* Unused */
.op_size = 0, /* See Intel x86 vol 3 figure 6-3 */
.granularity = 1, /* limit is in Bytes */
.base_paged_addr_31_24 = (tss_vaddr >> 24) & 0xff};
/* Load the TSS register into the processor */
regval_tss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_K_TSS);
asm("ltr %0" : : "r"(regval_tss));
return 0;
}

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2004 David Decotigny /* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2004 David Decotigny
Copyright (C) 1999 Free Software Foundation, Inc. Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -17,7 +18,8 @@
USA. USA.
*/ */
#pragma once #pragma once
#include "types.h"
#include "stdarg.h"
/** /**
* @file gdt.h * @file gdt.h
* *
@ -34,3 +36,4 @@
* address space (ie "flat" virtual space). * address space (ie "flat" virtual space).
*/ */
int gdtSetup(void); int gdtSetup(void);
int gdtRegisterTSS(vaddr_t tss_vaddr);

View File

@ -1,3 +1,5 @@
#define ASM_SOURCE 1
#include "segment.h"
.file "irq_pit.S" .file "irq_pit.S"
.text .text
@ -7,8 +9,16 @@
.globl pit_handler .globl pit_handler
.type pit_handler, @function .type pit_handler, @function
pit_handler: // already got eflags, cs and eip on stack thanks to CPU pit_handler: // already got eflags, cs and eip on stack thanks to CPU
pushl $0 // err_code esp+12+8*4=44 pushl $0 // err_code esp+12+7*4=40
pushal // (general reg) esp+12 pushl %ebp
movl %esp, %ebp
pushl %eax
pushl %ecx
pushl %edx
pushl %ebx
pushl %esi
pushl %edi
subl $2, %esp // (alignment) esp+10 subl $2, %esp // (alignment) esp+10
pushw %ss // esp+8 pushw %ss // esp+8
pushw %ds // esp+6 pushw %ds // esp+6
@ -16,12 +26,20 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU
pushw %fs // esp+2 pushw %fs // esp+2
pushw %gs // esp pushw %gs // esp
/* Set correct kernel segment descriptors' value */
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
pushw %di ; popw %ds
pushw %di ; popw %es
pushw %di ; popw %fs
pushw %di ; popw %gs
/* Send EOI to PIC */ /* Send EOI to PIC */
movb $0x20, %al movb $0x20, %al
outb %al, $0x20 outb %al, $0x20
pushl %esp pushl %esp
call pitIrqHandler call pitIrqHandler
addl $4, %esp
movl %eax,%esp movl %eax,%esp
/* Restore the CPU context */ /* Restore the CPU context */
@ -31,7 +49,13 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU
popw %ds popw %ds
popw %ss popw %ss
addl $2,%esp addl $2,%esp
popal popl %edi
popl %esi
popl %ebx
popl %edx
popl %ecx
popl %eax
popl %ebp
addl $4, %esp /* Ignore "error code" */ addl $4, %esp /* Ignore "error code" */
/* This restores the eflags, the cs and the eip registers */ /* This restores the eflags, the cs and the eip registers */

View File

@ -1,9 +1,17 @@
#define ASM_SOURCE 1 #define ASM_SOURCE 1
#include "segment.h"
.file "irq_wrappers.S" .file "irq_wrappers.S"
.text .text
.extern interrupt_handler_pic .extern interrupt_handler_pic
.globl irq_handler_wrapper_array .globl irq_handler_wrapper_array
/** Update the kernel TSS in case we are switching to a thread in user
mode in order to come back into the correct kernel stack */
.extern cpu_context_update_kernel_tss
/* The address of the function to call to set back the user thread's
MMU configuration upon return to user context */
.extern threadPrepareIrqSwitchBack
.altmacro .altmacro
@ -24,12 +32,12 @@
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax pushl %eax
pushl %ecx
pushl %edx
pushl %ebx
pushl %esi
pushl %edi
subl $2,%esp subl $2,%esp
pushw %ss pushw %ss
pushw %ds pushw %ds
@ -37,25 +45,45 @@
pushw %fs pushw %fs
pushw %gs pushw %gs
/* Set correct kernel segment descriptors' value */
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
pushw %di ; popw %ds
pushw %di ; popw %es
pushw %di ; popw %fs
pushw %di ; popw %gs
push %esp push %esp
pushl $\irq pushl $\irq
call interrupt_handler_pic call interrupt_handler_pic
addl $8, %esp addl $8, %esp
/* Reconfigure the MMU if needed */
pushl %esp /* cpu_ctxt */
call threadPrepareIrqSwitchBack
addl $4, %esp /* Unallocate the stack */
/* Prepare kernel TSS in case we are switching to a
user thread: we make sure that we will come back
into the kernel at a correct stack location */
pushl %esp /* Pass the location of the context we are
restoring to the function */
call cpu_context_update_kernel_tss
addl $4, %esp
/* Restore the context */ /* Restore the context */
popw %gs popw %gs
popw %fs popw %fs
popw %es popw %es
popw %ds popw %ds
popw %ss popw %ss
addl $2,%esp addl $2,%esp
popl %eax popl %edi
popl %ebx popl %esi
popl %ecx popl %ebx
popl %edx popl %edx
popl %esi popl %ecx
popl %edi popl %eax
popl %ebp popl %ebp
/* Remove fake error code */ /* Remove fake error code */
addl $4, %esp addl $4, %esp

170
arch/x86/mmuContext.c Normal file
View File

@ -0,0 +1,170 @@
#include "alloc.h"
#include "allocArea.h"
#include "errno.h"
#include "irq.h"
#include "klibc.h"
#include "list.h"
#include "mem.h"
#include "mmuContext.h"
#include "paging.h"
#include "stdarg.h"
#include "types.h"
struct mmu_context {
paddr_t paddr_PD;
vaddr_t vaddr_PD;
uint32_t ref;
struct mmu_context *next, *prev;
};
static struct mmu_context *listContext = NULL;
static struct mmu_context *currentContext = NULL;
struct mmu_context * mmuContextGetCurrent(){
return currentContext;
}
int mmuContextSetup()
{
struct mmu_context *initialCtx;
int ret = 0;
allocBookSlab(sizeof(struct mmu_context), PAGE_SIZE * 3, 0, 0);
initialCtx = malloc(sizeof(struct mmu_context));
if (initialCtx == NULL)
return -ENOMEM;
initialCtx->paddr_PD = pagingGetCurrentPDPaddr();
initialCtx->vaddr_PD = areaAlloc(1, 0);
ret = pageMap(initialCtx->vaddr_PD, initialCtx->paddr_PD,
PAGING_MEM_WRITE | PAGING_MEM_READ);
if (ret)
return ret;
list_singleton(listContext, initialCtx);
currentContext = initialCtx;
// We create the context and we are using it
initialCtx->ref = 2;
return 0;
}
struct mmu_context *mmuContextCreate()
{
struct mmu_context *ctx;
uint32_t flags;
ctx = malloc(sizeof(struct mmu_context));
if (ctx == NULL)
return NULL;
ctx->vaddr_PD = areaAlloc(1, AREA_PHY_MAP);
if (ctx->vaddr_PD == (vaddr_t)NULL) {
pr_info("Fail to allocate MMU Context\n");
free(ctx);
return NULL;
}
ctx->paddr_PD = pagingGetPaddr(ctx->vaddr_PD);
ctx->ref = 1;
if (pagingCopyKernelSpace(ctx->vaddr_PD, ctx->paddr_PD, currentContext->vaddr_PD)) {
pr_err("Fail to copy Kernel space\n");
free(ctx);
return NULL;
}
disable_IRQs(flags);
list_add_tail(listContext, ctx);
restore_IRQs(flags);
return ctx;
}
int mmuContextRef(struct mmu_context *ctx)
{
uint32_t flags;
disable_IRQs(flags);
// ref == 0 => suppression
assert(ctx->ref > 0);
ctx->ref++;
restore_IRQs(flags);
return 0;
}
int mmuContextUnref(struct mmu_context *ctx)
{
uint32_t flags;
disable_IRQs(flags);
assert(ctx->ref > 0);
ctx->ref--;
if (ctx->ref == 0) {
list_delete(listContext, ctx);
pagingClearUserContext(ctx->vaddr_PD);
areaFree(ctx->vaddr_PD);
free(ctx);
}
restore_IRQs(flags);
return 0;
}
int mmuContextSwitch(struct mmu_context *ctx)
{
uint32_t flags;
disable_IRQs(flags);
assert(ctx->ref > 0);
assert(currentContext->ref > 0);
if (ctx != currentContext) {
struct mmu_context *prev = currentContext;
ctx->ref++;
currentContext = ctx;
pagingSetCurrentPDPaddr(ctx->paddr_PD);
mmuContextUnref(prev);
}
restore_IRQs(flags);
return 0;
}
int mmuContextSyncKernelPDE(int pdEntry, void *pde, size_t pdeSize)
{
uint32_t flags;
struct mmu_context *destContext;
int nbContexts;
disable_IRQs(flags);
list_foreach_forward(listContext, destContext, nbContexts)
{
void *dest_pd;
assert(destContext->ref > 0);
dest_pd = (void *)destContext->vaddr_PD;
memcpy(dest_pd + pdEntry * pdeSize, pde, pdeSize);
}
restore_IRQs(flags);
return 0;
}

View File

@ -1,7 +1,10 @@
#include "allocArea.h"
#include "paging.h" #include "paging.h"
#include "errno.h" #include "errno.h"
#include "kernel.h"
#include "klibc.h" #include "klibc.h"
#include "mem.h" #include "mem.h"
#include "mmuContext.h"
#include "stdarg.h" #include "stdarg.h"
// In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry // In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry
@ -107,9 +110,10 @@ int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr)
} }
// Setup mirroring // Setup mirroring
pd[PD_MIRROR_PAGE_IDX].present = 1; pd[PAGING_MIRROR_VADDR >> PD_SHIFT].present = 1;
pd[PD_MIRROR_PAGE_IDX].write = 1; pd[PAGING_MIRROR_VADDR >> PD_SHIFT].write = 1;
pd[PD_MIRROR_PAGE_IDX].pt_addr = ((paddr_t)pd >> PT_SHIFT); pd[PAGING_MIRROR_VADDR >> PD_SHIFT].pt_addr = ((paddr_t)pd >> PT_SHIFT);
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].user = 0;
// Loading of the PDBR in the MMU: // Loading of the PDBR in the MMU:
asm volatile("movl %0,%%cr3\n\t" asm volatile("movl %0,%%cr3\n\t"
@ -130,25 +134,36 @@ int pageMap(vaddr_t vaddr, paddr_t paddr, int flags)
uint pdEntry = vaddr >> (PD_SHIFT); uint pdEntry = vaddr >> (PD_SHIFT);
uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK; uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK;
if ((vaddr >= PAGING_MIRROR_VADDR) && (vaddr < PAGING_MIRROR_VADDR + PAGING_MIRROR_SIZE))
return -EINVAL;
// Thank to mirroring, we can access the PD // Thank to mirroring, we can access the PD
struct pde *pd = struct pde *pd =
(struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT)); (struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT));
struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT)); struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE));
if (!pd[pdEntry].present) { if (!pd[pdEntry].present) {
paddr_t ptPhy = allocPhyPage(1); paddr_t ptPhy = allocPhyPage(1);
if (ptPhy == (vaddr_t)NULL) if (ptPhy == (vaddr_t)NULL)
return -ENOMEM; return -ENOMEM;
pd[pdEntry].user = (flags & PAGING_MEM_USER) ? 1 : 0;
pd[pdEntry].present = 1; pd[pdEntry].present = 1;
pd[pdEntry].write = 1; pd[pdEntry].write = 1;
pd[pdEntry].pt_addr = (ptPhy >> PT_SHIFT); pd[pdEntry].pt_addr = (ptPhy >> PT_SHIFT);
if (vaddr < PAGING_BASE_USER_ADDRESS) {
pd[pdEntry].user = 0;
mmuContextSyncKernelPDE(pdEntry, &pd[pdEntry], sizeof(struct pde));
} else {
assert(flags & PAGING_MEM_USER);
pd[pdEntry].user = 1;
}
__native_flush_tlb_single((vaddr_t)pt); __native_flush_tlb_single((vaddr_t)pt);
memset((void *)pt, 0, PAGE_SIZE); memset((void *)pt, 0, PAGE_SIZE);
} else { }
{
// Already mapped ? Remove old mapping // Already mapped ? Remove old mapping
if (pt[ptEntry].present) { if (pt[ptEntry].present) {
@ -175,11 +190,14 @@ int pageUnmap(vaddr_t vaddr)
uint pdEntry = vaddr >> (PD_SHIFT); uint pdEntry = vaddr >> (PD_SHIFT);
uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK; uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK;
if ((vaddr >= PAGING_MIRROR_VADDR) && (vaddr < PAGING_MIRROR_VADDR + PAGING_MIRROR_SIZE))
return -EINVAL;
// Thank to mirroring, we can access the PD // Thank to mirroring, we can access the PD
struct pde *pd = struct pde *pd =
(struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT)); (struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT));
struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT)); struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE));
if (!pd[pdEntry].present) if (!pd[pdEntry].present)
return -EINVAL; return -EINVAL;
if (!pt[ptEntry].present) if (!pt[ptEntry].present)
@ -191,6 +209,9 @@ int pageUnmap(vaddr_t vaddr)
// PTE not used. Decrease refcount on it. Is PT not used anymore ? // PTE not used. Decrease refcount on it. Is PT not used anymore ?
if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 0) { if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 0) {
pd[pdEntry].present = 0; pd[pdEntry].present = 0;
if (vaddr < PAGING_BASE_USER_ADDRESS) {
mmuContextSyncKernelPDE(pdEntry, &pd[pdEntry], sizeof(struct pde));
}
__native_flush_tlb_single((vaddr_t)pt); __native_flush_tlb_single((vaddr_t)pt);
} }
__native_flush_tlb_single(vaddr); __native_flush_tlb_single(vaddr);
@ -198,7 +219,133 @@ int pageUnmap(vaddr_t vaddr)
return 0; return 0;
} }
paddr_t pagingGetPaddr(vaddr_t vaddr)
{
/* Get the page directory entry and table entry index for this
address */
unsigned pdEntry = vaddr >> PD_SHIFT;
unsigned ptEntry = vaddr >> PT_SHIFT & PTE_MASK;
unsigned pageOffset = vaddr & PAGE_MASK;
// Thank to mirroring, we can access the PD
struct pde *pd =
(struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT));
struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE));
/* No page mapped at this address ? */
if (!pd[pdEntry].present)
return (paddr_t)NULL;
if (!pt[ptEntry].present)
return (paddr_t)NULL;
return (pt[ptEntry].paddr << PT_SHIFT) + pageOffset;
}
unsigned long getNbMappedPage(void) unsigned long getNbMappedPage(void)
{ {
return mappedPage; return mappedPage;
} }
paddr_t pagingGetCurrentPDPaddr()
{
struct pdbr pdbr;
asm volatile("movl %%cr3, %0\n" : "=r"(pdbr));
return (pdbr.pd_paddr << 12);
}
int pagingSetCurrentPDPaddr(paddr_t paddrPD)
{
struct pdbr pdbr;
assert(paddrPD != 0);
assert(IS_ALIGNED(paddrPD, PAGE_SIZE));
/* Setup the value of the PDBR */
memset(&pdbr, 0x0, sizeof(struct pdbr)); /* Reset the PDBR */
pdbr.pd_paddr = (paddrPD >> 12);
/* Configure the MMU according to the PDBR */
asm volatile("movl %0,%%cr3\n" ::"r"(pdbr));
return 0;
}
// unmap page inside this MMU context
int pagingClearUserContext(vaddr_t vaddr_PD)
{
struct pde *pd = (struct pde *)vaddr_PD;
//Tmp pt to unref page they reference
struct pte *pt = (struct pte *)areaAlloc(1, 0);
if(pt == NULL)
return -ENOMEM;
for (int pdIdx = PAGING_BASE_USER_ADDRESS >> PD_SHIFT; pdIdx < 1024; pdIdx++) {
if(!pd[pdIdx].present){
memset(&pd[pdIdx], 0, sizeof(struct pde));
continue;
}
paddr_t ptAddr = pd[pdIdx].pt_addr << PT_SHIFT;
assert(!pageMap(ptAddr, (vaddr_t)pt, PAGING_MEM_USER | PAGING_MEM_WRITE));
for(int ptIdx = 0; ptIdx < 1024; ptIdx ++){
if(!pt[ptIdx].present){
memset(&pt[ptIdx], 0, sizeof(struct pte));
continue;
}
unrefPhyPage(pt[ptIdx].paddr);
memset(&pt[ptIdx], 0, sizeof(struct pte));
}
assert(!pageUnmap((vaddr_t)pt));
memset(&pd[pdIdx], 0, sizeof(struct pde));
unrefPhyPage(ptAddr);
}
areaFree((vaddr_t)pt);
return 0;
}
int pagingCopyKernelSpace(vaddr_t destVaddrPD, paddr_t destPaddrPD, vaddr_t srcVaddrPD)
{
struct pde *src_pd = (struct pde *)srcVaddrPD;
struct pde *dest_pd = (struct pde *)destVaddrPD;
struct pde mirror_pde;
uint index_in_pd;
/* Fill destination PD with zeros */
memset((void *)destVaddrPD, 0x0, PAGE_SIZE);
/* Synchronize it with the master Kernel MMU context. Stop just
before the mirroring ! */
for (index_in_pd = 0; index_in_pd < (PAGING_MIRROR_VADDR >> 22); /* 1 PDE = 1 PT
= 1024 Pages
= 4MB */
index_in_pd++) {
/* Copy the master's configuration */
dest_pd[index_in_pd] = src_pd[index_in_pd];
/* We DON'T mark the underlying PT and pages as referenced
because all the PD are equivalent in the kernel space: as
soon as a page is mapped in the kernel, it is mapped by X
address spaces, and as soon as it is unmapped by 1 address
space, it is unmapped in all the others. So that for X
address spaces, the reference counter will be either 0 or X,
and not something else: using the reference counter correctly
won't be of any use and would consume some time in updating it. */
}
/* Setup the mirroring for the new address space */
mirror_pde.present = TRUE;
mirror_pde.write = 1;
mirror_pde.user = 0; /* This is a KERNEL PDE */
mirror_pde.pt_addr = (destPaddrPD >> 12);
dest_pd[PAGING_MIRROR_VADDR >> 22] = mirror_pde;
return 0;
}

View File

@ -1,7 +1,20 @@
#pragma once #pragma once
#include "types.h" #include "types.h"
#define PAGING_MEM_USER 1 /** Frontier between kernel and user space virtual addresses */
#define PAGING_BASE_USER_ADDRESS (0x40000000) /* 1GB (must be 4MB-aligned) */
#define PAGING_TOP_USER_ADDRESS (0xFFFFFFFF) /* 4GB - 1B */
#define PAGING_USER_SPACE_SIZE (0xc0000000) /* 3GB */
/** Length of the space reserved for the mirroring in the kernel
virtual space */
#define PAGING_MIRROR_SIZE (PAGE_SIZE << 10) /* 1 PD = 1024 Page Tables = 4MB */
/** Virtual address where the mirroring takes place */
#define PAGING_MIRROR_VADDR \
(PAGING_BASE_USER_ADDRESS - PAGING_MIRROR_SIZE)
#define PAGING_MEM_USER (1U << 0)
#define PAGING_MEM_READ (1U << 1) #define PAGING_MEM_READ (1U << 1)
#define PAGING_MEM_WRITE (1U << 2) #define PAGING_MEM_WRITE (1U << 2)
@ -10,3 +23,9 @@ int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr);
int pageMap(vaddr_t vaddr, paddr_t paddr, int flags); int pageMap(vaddr_t vaddr, paddr_t paddr, int flags);
int pageUnmap(vaddr_t vaddr); int pageUnmap(vaddr_t vaddr);
unsigned long getNbMappedPage(void); unsigned long getNbMappedPage(void);
int pagingSetCurrentPDPaddr(paddr_t paddrPD);
paddr_t pagingGetPaddr(vaddr_t vaddr);
paddr_t pagingGetCurrentPDPaddr();
int pagingCopyKernelSpace(vaddr_t destVaddrPD, paddr_t destPaddrPD, vaddr_t srcVaddrPD);
int pagingClearUserContext(vaddr_t vaddr_PD);

18
arch/x86/swintr.c Normal file
View File

@ -0,0 +1,18 @@
#include "assert.h"
#include "swintr.h"
#include "irq.h"
#include "idt.h"
extern void syscallHandler();
int syscallSetup(){
uint32_t flags, ret;
disable_IRQs(flags);
ret = idt_set_handler(SYSCALL_INTR_NB, (vaddr_t)syscallHandler, 3);
restore_IRQs(flags);
assert(ret == 0);
return ret;
}

6
arch/x86/swintr.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#define SYSCALL_INTR_NB 0x42
int syscallSetup();

View File

@ -0,0 +1,91 @@
#define ASM_SOURCE
#include "segment.h"
.file "syscall_wrappers.S"
.text
/* The address of the real "C" syscall function */
.extern syscallExecute
/** Update the kernel TSS in case we are switching to a thread in user
mode in order to come back into the correct kernel stack */
.extern cpu_context_update_kernel_tss
/* The address of the function to call to set back the user thread's
MMU configuration upon return to user context */
.extern threadPrepareSyscallSwitchBack
.p2align 2, 0x90
.globl syscallHandler
syscallHandler:
.type syscallHandler,@function
/* Fake error code */
pushl $0
/* Backup the context */
pushl %ebp
movl %esp, %ebp
pushl %eax
pushl %ecx
pushl %edx
pushl %ebx
pushl %esi
pushl %edi
subl $2,%esp
pushw %ss
pushw %ds
pushw %es
pushw %fs
pushw %gs
/* Set correct kernel segment descriptors' value */
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
pushw %di ; popw %ds
pushw %di ; popw %es
pushw %di ; popw %fs
pushw %di ; popw %gs
/* Prepare the call to do_syscall */
pushl %esp /* user_ctxt */
pushl %eax /* syscall ID */
call syscallExecute
/* Unallocate the stack used by the
do_syscall arguments */
addl $8, %esp
/* store the do_syscall return value into interrupted context */
movl %eax, 12(%esp)
/* Set the MMU configuration to that of the user thread's process */
pushl %esp /* user_ctxt */
call threadPrepareSyscallSwitchBack
addl $4, %esp /* Unallocate the stack */
/* Prepare kernel TSS because we are switching back to a user
thread: we make sure that we will come back into the kernel at a
correct stack location */
pushl %esp /* Pass the location of the context we are
restoring to the function */
call cpu_context_update_kernel_tss
addl $4, %esp
/* Restore the user context */
popw %gs
popw %fs
popw %es
popw %ds
popw %ss
addl $2,%esp
popl %edi
popl %esi
popl %ebx
popl %edx
popl %ecx
popl %eax
popl %ebp
/* Remove fake error code */
addl $4, %esp
iret

View File

@ -17,7 +17,7 @@ static struct slabDesc *slub;
static int allocInitialized = FALSE; static int allocInitialized = FALSE;
static int allocSlab(struct slabDesc **desc, size_t sizeEl, size_t sizeSlab, static int allocSlab(struct slabDesc **desc, size_t sizeEl, size_t sizeSlab,
int self_containing); int self_containing, int neverEmpty);
static int allocSlabEntry(struct slabEntry **desc, size_t sizeEl, size_t sizeSlab, static int allocSlabEntry(struct slabEntry **desc, size_t sizeEl, size_t sizeSlab,
int selfContained); int selfContained);
static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained); static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained);
@ -28,27 +28,28 @@ static struct {
size_t elementSize; size_t elementSize;
size_t slabSize; size_t slabSize;
unsigned char isSelf; unsigned char isSelf;
} initSlab[] = {{4, PAGE_SIZE, 0}, unsigned char neverEmpty;
{8, PAGE_SIZE, 0}, } initSlab[] = {{4, PAGE_SIZE, 0, 0},
{16, PAGE_SIZE, 0}, {8, PAGE_SIZE, 0, 0},
{32, PAGE_SIZE, 0}, {16, PAGE_SIZE, 0, 0},
{64, PAGE_SIZE, 0}, {32, PAGE_SIZE, 0, 0},
{128, PAGE_SIZE, 0}, {64, PAGE_SIZE, 0, 0},
{256, 2 * PAGE_SIZE, 0}, {128, PAGE_SIZE, 0, 0},
{1024, 2 * PAGE_SIZE, 0}, {256, 2 * PAGE_SIZE, 0, 0},
{2048, 3 * PAGE_SIZE, 0}, {1024, 2 * PAGE_SIZE, 0, 0},
{4096, 4 * PAGE_SIZE, 0}, {2048, 3 * PAGE_SIZE, 0, 0},
{0, 0, 0}}; {4096, 4 * PAGE_SIZE, 0, 0},
{0, 0, 0, 0}};
int allocSetup(size_t sizeOfArea, vaddr_t *areaAddr, vaddr_t *descAddr, vaddr_t *entryAddr) int allocSetup(size_t sizeOfArea, vaddr_t *areaAddr, vaddr_t *descAddr, vaddr_t *entryAddr)
{ {
list_init(slub); list_init(slub);
assert(allocBookSlab(sizeof(struct slabDesc), PAGE_SIZE, TRUE) == 0); assert(allocBookSlab(sizeof(struct slabDesc), PAGE_SIZE, TRUE, FALSE) == 0);
*descAddr = (vaddr_t)allocGetSlab(sizeof(struct slabDesc)); *descAddr = (vaddr_t)allocGetSlab(sizeof(struct slabDesc));
assert(allocBookSlab(sizeof(struct slabEntry), PAGE_SIZE, TRUE) == 0); assert(allocBookSlab(sizeof(struct slabEntry), PAGE_SIZE, TRUE, FALSE) == 0);
*entryAddr = (vaddr_t)allocGetSlab(sizeof(struct slabEntry)); *entryAddr = (vaddr_t)allocGetSlab(sizeof(struct slabEntry));
assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE) == 0); assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE, TRUE) == 0);
*areaAddr = (vaddr_t)allocGetSlab(sizeOfArea); *areaAddr = (vaddr_t)allocGetSlab(sizeOfArea);
allocInitialized = TRUE; allocInitialized = TRUE;
@ -61,7 +62,7 @@ int allocPopulate()
int ret; int ret;
if ((ret = allocBookSlab(initSlab[i].elementSize, initSlab[i].slabSize, if ((ret = allocBookSlab(initSlab[i].elementSize, initSlab[i].slabSize,
initSlab[i].isSelf))) { initSlab[i].isSelf, initSlab[i].neverEmpty))) {
if (ret == -EEXIST) if (ret == -EEXIST)
continue; continue;
pr_err("Fail to allocBookSlab %d for %d \n", ret, (1U << i)); pr_err("Fail to allocBookSlab %d for %d \n", ret, (1U << i));
@ -73,7 +74,7 @@ int allocPopulate()
return 0; return 0;
} }
int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained) int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained, int neverEmpty)
{ {
struct slabDesc *slab = NULL; struct slabDesc *slab = NULL;
struct slabDesc *newSlab = NULL; struct slabDesc *newSlab = NULL;
@ -95,7 +96,7 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained)
} }
} }
if ((ret = allocSlab(&newSlab, sizeEl, sizeSlab, selfContained))) { if ((ret = allocSlab(&newSlab, sizeEl, sizeSlab, selfContained, neverEmpty))) {
pr_devel("Failed to alloc Slab\n"); pr_devel("Failed to alloc Slab\n");
restore_IRQs(flags); restore_IRQs(flags);
return ret; return ret;
@ -112,7 +113,8 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained)
return 0; return 0;
} }
static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int selfContained) static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int selfContained,
int neverEmpty)
{ {
uint nbPage, i; uint nbPage, i;
vaddr_t alloc; vaddr_t alloc;
@ -127,7 +129,7 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s
nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE); nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE);
if (allocInitialized) { if (allocInitialized) {
alloc = areaAlloc(nbPage); alloc = areaAlloc(nbPage, AREA_PHY_MAP);
if (alloc == (paddr_t)NULL) if (alloc == (paddr_t)NULL)
return -ENOMEM; return -ENOMEM;
} else { } else {
@ -153,10 +155,11 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s
struct slabEntry *slab = &(*desc)->slab; struct slabEntry *slab = &(*desc)->slab;
list_singleton(slab, slab); list_singleton(slab, slab);
slab->page = (vaddr_t)alloc; slab->page = (vaddr_t)alloc;
slab->full = 0; slab->full = 0;
slab->size = sizeSlab; slab->size = sizeSlab;
(*desc)->size = size; (*desc)->neverEmpty = neverEmpty;
(*desc)->size = size;
return formatPage(&(*desc)->slab, size, sizeSlab, selfContained); return formatPage(&(*desc)->slab, size, sizeSlab, selfContained);
@ -182,7 +185,7 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab,
} }
nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE); nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE);
vaddr_t alloc = areaAlloc(nbPage); vaddr_t alloc = areaAlloc(nbPage, AREA_PHY_MAP);
if (alloc == (paddr_t)NULL) if (alloc == (paddr_t)NULL)
return -ENOMEM; return -ENOMEM;
@ -202,8 +205,6 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab,
(*desc)->size = sizeSlab; (*desc)->size = sizeSlab;
return formatPage((*desc), size, sizeSlab, selfContained); return formatPage((*desc), size, sizeSlab, selfContained);
return -ENOMEM;
} }
static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained) static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained)
@ -261,8 +262,8 @@ void *malloc(size_t size)
disable_IRQs(flags); disable_IRQs(flags);
if (size >= PAGE_SIZE){ if (size >= PAGE_SIZE) {
vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE)); vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE), AREA_PHY_MAP);
return (void *)area; return (void *)area;
} }
@ -271,12 +272,36 @@ void *malloc(size_t size)
return NULL; return NULL;
} }
struct slabEntry *slabList = &slab->slab;
list_foreach(&slab->slab, slabEntry, slabIdx) list_foreach(&slab->slab, slabEntry, slabIdx)
{ {
if (!slabEntry->full) { if (!slabEntry->full) {
// pr_devel("found place in slub %d at idx %d for size %d\n", slubIdx, // pr_devel("found place in slab idx %d for size %d\n", slabIdx, slab->size);
// slabIdx, size);
ret = allocFromSlab(slabEntry); ret = allocFromSlab(slabEntry);
if (slabEntry->full && slab->neverEmpty) {
pr_devel("Prealloc place for slab for object size %d\n", slab->size);
assert(IS_SELF_CONTAINED(slabEntry));
size_t slabSize = slabEntry->size;
int nbPages = DIV_ROUND_UP(slabSize, PAGE_SIZE);
struct slabEntry *entry = (struct slabEntry *)areaBook(nbPages, AREA_PHY_MAP);
if (entry == NULL) {
restore_IRQs(flags);
return NULL;
}
(entry)->freeEl = (char *)(entry) + sizeof(struct slabEntry);
list_singleton(entry, entry);
entry->page = (vaddr_t)entry;
entry->full = 0;
entry->size = slabSize;
formatPage(entry, slab->size, slabSize, 1);
list_add_tail(slabList, entry);
areaAdd((vaddr_t)entry, (vaddr_t)entry + nbPages * PAGE_SIZE, FALSE);
}
restore_IRQs(flags); restore_IRQs(flags);
return ret; return ret;
} }
@ -284,8 +309,7 @@ void *malloc(size_t size)
// No room found // No room found
struct slabEntry *newSlabEntry; struct slabEntry *newSlabEntry;
struct slabEntry *slabList = &slab->slab; size_t slabSize = MAX(PAGE_SIZE, size);
size_t slabSize = MAX(PAGE_SIZE, size);
int retSlab; int retSlab;
if ((retSlab = allocSlabEntry(&newSlabEntry, slab->size, slabSize, if ((retSlab = allocSlabEntry(&newSlabEntry, slab->size, slabSize,
@ -304,7 +328,8 @@ void *malloc(size_t size)
return ret; return ret;
} }
void *zalloc(size_t size){ void *zalloc(size_t size)
{
void *alloc = malloc(size); void *alloc = malloc(size);
if (alloc != NULL) if (alloc != NULL)
@ -333,7 +358,8 @@ static int freeFromSlab(void *ptr, struct slabEntry *slab)
return 0; return 0;
} }
int freeSlabAllocated(void *ptr){ int freeSlabAllocated(void *ptr)
{
struct slabDesc *slab; struct slabDesc *slab;
int slabIdx; int slabIdx;
int flags; int flags;
@ -361,8 +387,8 @@ void free(void *ptr)
{ {
if (!ptr) if (!ptr)
return; return;
if(!freeSlabAllocated(ptr)) if (!freeSlabAllocated(ptr))
return; return;
if(areaFree((vaddr_t)ptr)) if (areaFree((vaddr_t)ptr))
pr_err("free: cannot found origin\n"); pr_err("free: cannot found origin\n");
} }

View File

@ -12,7 +12,7 @@ int allocPopulate();
* Allow malloc to allocate elements of this precise size. * Allow malloc to allocate elements of this precise size.
* Otherwise the allocation will be in the closest biggest pool. * Otherwise the allocation will be in the closest biggest pool.
* */ * */
int allocBookSlab(size_t size, size_t sizeSlab, int selfContained); int allocBookSlab(size_t size, size_t sizeSlab, int selfContained, int neverEmpty);
void *malloc(size_t size); void *malloc(size_t size);
void *zalloc(size_t size); void *zalloc(size_t size);
@ -22,9 +22,9 @@ void free(void *ptr);
*/ */
struct slabEntry { struct slabEntry {
vaddr_t page; vaddr_t page;
size_t size;
void *freeEl; void *freeEl;
char full; size_t size;
bool_t full;//TODO replace by freeEl == NULL
struct slabEntry *next; struct slabEntry *next;
struct slabEntry *prev; struct slabEntry *prev;
}; };
@ -32,6 +32,7 @@ struct slabEntry {
struct slabDesc { struct slabDesc {
struct slabEntry slab; struct slabEntry slab;
size_t size; size_t size;
bool_t neverEmpty;
struct slabDesc *next; struct slabDesc *next;
struct slabDesc *prev; struct slabDesc *prev;
}; };

View File

@ -1,33 +1,43 @@
#include "allocArea.h" #include "allocArea.h"
#include "alloc.h" #include "alloc.h"
#include "assert.h" #include "assert.h"
#include "irq.h"
#include "kernel.h" #include "kernel.h"
#include "list.h" #include "list.h"
#include "mem.h" #include "mem.h"
#include "stdarg.h" #include "stdarg.h"
static struct memArea *freeArea; static struct memArea *freeArea;
static struct memArea *usedArea; static struct memArea *usedArea;
static int areaMergeFreeArea(struct memArea *prev, struct memArea *next); static int areaMergeFreeArea(struct memArea *prev, struct memArea *next);
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed) void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stackBottom, vaddr_t stackTop)
{ {
list_init(freeArea); list_init(freeArea);
list_init(usedArea); list_init(usedArea);
vaddr_t areaAddr, descAddr, entryAddr; vaddr_t areaAddr, descAddr, entryAddr;
allocSetup(sizeof(struct memArea), &areaAddr, &descAddr, &entryAddr); allocSetup(sizeof(struct memArea), &areaAddr, &descAddr, &entryAddr);
areaAdd(descAddr, PAGE_SIZE, FALSE); areaAdd(descAddr, descAddr + PAGE_SIZE, FALSE);
if (entryAddr != descAddr) if (entryAddr != descAddr)
areaAdd(entryAddr, PAGE_SIZE, FALSE); areaAdd(entryAddr, entryAddr + PAGE_SIZE, FALSE);
if (areaAddr != descAddr && areaAddr != entryAddr) if (areaAddr != descAddr && areaAddr != entryAddr)
areaAdd(areaAddr, PAGE_SIZE, FALSE); areaAdd(areaAddr, areaAddr + PAGE_SIZE, FALSE);
int nbPages = DIV_ROUND_UP((lastUsed - firstMemUsed), PAGE_SIZE); // kernel bootstrap part
areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), nbPages, FALSE); areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), stackBottom, FALSE);
areaAdd(ALIGN_DOWN(areaAddr + PAGE_SIZE, PAGE_SIZE), 300 , TRUE);
// Initial kernel stack
areaAdd(stackBottom, stackTop, FALSE);
// Rest of kernel code
areaAdd(stackTop, lastUsed, FALSE);
// Rest of virtual mem is free
areaAdd(areaAddr + PAGE_SIZE, AREA_MEM_TOP, TRUE);
// Create allocBank for the rest of the system
allocPopulate(); allocPopulate();
} }
@ -62,61 +72,137 @@ static void insertSorted(struct memArea **list, struct memArea *item)
list_add_tail(*list, item); list_add_tail(*list, item);
} }
vaddr_t areaAlloc(unsigned int nbPages) vaddr_t areaBook(unsigned int nbPages, uint32_t flags)
{ {
struct memArea *area, *allocated;
struct memArea *area;
vaddr_t allocated;
uint32_t irqFlags;
disable_IRQs(irqFlags);
area = areaFindFit(nbPages); area = areaFindFit(nbPages);
if (!area) if (!area) {
printf("NULL<\n");
restore_IRQs(irqFlags);
return (vaddr_t)NULL; return (vaddr_t)NULL;
}
if (area->nbPages == nbPages) {
list_delete(freeArea, area);
allocated = area->startAddr;
} else {
allocated = area->startAddr;
area->nbPages -= nbPages;
area->startAddr += nbPages * PAGE_SIZE;
}
if (flags & AREA_PHY_MAP) {
for (uint i = 0; i < nbPages; i++) {
paddr_t page = allocPhyPage(1);
if (page) {
if (pageMap(allocated + i * PAGE_SIZE, page, PAGING_MEM_WRITE)) {
unrefPhyPage(page);
page = (paddr_t)NULL;
} else {
unrefPhyPage(page);
}
}
if (page == (paddr_t)NULL) {
areaFree(allocated);
restore_IRQs(irqFlags);
return (vaddr_t)NULL;
}
}
}
restore_IRQs(irqFlags);
return allocated;
}
vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags)
{
struct memArea *area, *allocated;
uint32_t irqFlags;
disable_IRQs(irqFlags);
area = areaFindFit(nbPages);
if (!area) {
restore_IRQs(irqFlags);
return (vaddr_t)NULL;
}
if (area->nbPages == nbPages) { if (area->nbPages == nbPages) {
list_delete(freeArea, area); list_delete(freeArea, area);
insertSorted(&usedArea, area); insertSorted(&usedArea, area);
allocated = area; allocated = area;
} else {
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
if (!newArea) {
pr_devel("Failed to allocated area of %d pages\n", nbPages);
restore_IRQs(irqFlags);
return (vaddr_t)NULL;
}
// Avoid insertSorted(freeArea, newArea) call by modifying area
newArea->nbPages = nbPages;
newArea->startAddr = area->startAddr;
area->nbPages -= nbPages;
area->startAddr += nbPages * PAGE_SIZE;
insertSorted(&usedArea, newArea);
allocated = newArea;
} }
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea)); if (flags & AREA_PHY_MAP) {
if (!newArea){ for (uint i = 0; i < nbPages; i++) {
pr_devel("Failed to allocated area of %d pages\n", nbPages); paddr_t page = allocPhyPage(1);
return (vaddr_t)NULL; if (page) {
} if (pageMap(allocated->startAddr + i * PAGE_SIZE, page, PAGING_MEM_WRITE)) {
page = (paddr_t)NULL;
} else {
unrefPhyPage(page);
}
}
if (page == (paddr_t)NULL) {
areaFree(allocated->startAddr);
restore_IRQs(irqFlags);
// Avoid insertSorted(freeArea, newArea) call by modifying area return (vaddr_t)NULL;
}
newArea->nbPages = nbPages;
newArea->startAddr = area->startAddr;
area->nbPages -= nbPages;
area->startAddr += nbPages * PAGE_SIZE;
insertSorted(&usedArea, newArea);
allocated = newArea;
for (uint i = 0; i < nbPages; i++) {
paddr_t page = allocPhyPage(1);
if (page) {
pageMap(newArea->startAddr + i * PAGE_SIZE, page, PAGING_MEM_WRITE);
} else {
// TODO
assert(1);
} }
} }
restore_IRQs(irqFlags);
return allocated->startAddr; return allocated->startAddr;
} }
int areaAdd(vaddr_t addr, uint nbPages, int isFree) int areaAdd(vaddr_t start, vaddr_t end, int isFree)
{ {
struct memArea **area; struct memArea **area;
int nbPages = (end - start) / PAGE_SIZE;
pr_devel("Add %s area 0x%x->0x%x (%d)\n", isFree ? "free" : "used", start, end, nbPages);
assert(nbPages > 0);
assert(IS_ALIGNED(start, PAGE_SIZE));
assert(IS_ALIGNED(end, PAGE_SIZE));
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea)); struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
if (!newArea) if (!newArea)
return (vaddr_t)NULL; return (vaddr_t)NULL;
newArea->nbPages = nbPages; newArea->nbPages = nbPages;
newArea->startAddr = addr; newArea->startAddr = start;
if (isFree) { if (isFree) {
area = &freeArea; area = &freeArea;
@ -158,7 +244,7 @@ static struct memArea *areaFindMemArea(struct memArea *list, vaddr_t addr)
int count; int count;
list_foreach(list, area, count) list_foreach(list, area, count)
{ {
if (area->startAddr <= addr && addr <= area->startAddr + area->nbPages * PAGE_SIZE) if (area->startAddr <= addr && addr < area->startAddr + area->nbPages * PAGE_SIZE)
return area; return area;
} }
@ -174,6 +260,11 @@ int areaFree(vaddr_t addr)
pr_info("Cannot find memArea associated to %p\n", addr); pr_info("Cannot find memArea associated to %p\n", addr);
return -1; return -1;
} }
for (uint i = 0; i < area->nbPages; i++) {
pageUnmap(area->startAddr + i * PAGE_SIZE);
}
list_delete(usedArea, area); list_delete(usedArea, area);
insertSorted(&freeArea, area); insertSorted(&freeArea, area);

View File

@ -2,6 +2,12 @@
#include "paging.h" #include "paging.h"
#include "stdarg.h" #include "stdarg.h"
/* Pure Virtual Memory Allocation */
// areaAlloc map vmem to phy pages
#define AREA_PHY_MAP (1<<0)
#define AREA_MEM_TOP PAGING_MIRROR_VADDR
struct memArea { struct memArea {
vaddr_t startAddr; vaddr_t startAddr;
uint nbPages; uint nbPages;
@ -10,7 +16,11 @@ struct memArea {
struct memArea *prev; struct memArea *prev;
}; };
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed); void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stack_bottom, vaddr_t stack_top);
vaddr_t areaAlloc(unsigned int nbPages); vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags);
// Remove an area from the free ones but do not add it into used ones.
// This area should be latter added woth areaAdd.
// Used by malloc to avoid recursivity issue
vaddr_t areaBook(unsigned int nbPages, uint32_t flags);
int areaFree(vaddr_t addr); int areaFree(vaddr_t addr);
int areaAdd(vaddr_t addr, uint nbPages, int isFree); int areaAdd(vaddr_t begin, vaddr_t end, int isFree);

View File

@ -6,7 +6,7 @@
do { \ do { \
if (!(p)) { \ if (!(p)) { \
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \ printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
printStackTrace(5); \ printStackTrace(3); \
while (1) { \ while (1) { \
} \ } \
} \ } \
@ -17,7 +17,7 @@
if (!(p)) { \ if (!(p)) { \
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \ printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
printf(__VA_ARGS__); \ printf(__VA_ARGS__); \
printStackTrace(5); \ printStackTrace(3); \
while (1) { \ while (1) { \
} \ } \
} \ } \

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2005 David Decotigny /* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2005 David Decotigny
Copyright (C) 2000-2004, The KOS team Copyright (C) 2000-2004, The KOS team
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -84,6 +85,11 @@ int cpu_kstate_init(struct cpu_state **kctxt, cpu_kstate_function_arg1_t *start_
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size, vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg); cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg);
/**
* Prepare the system to deal with multiple CPU execution contexts
*/
int cpu_context_subsystem_setup();
/** /**
* Function that performs an immediate context-switch from one * Function that performs an immediate context-switch from one
* kernel/user thread to another one. It stores the current executing * kernel/user thread to another one. It stores the current executing
@ -119,6 +125,14 @@ void cpu_context_exit_to(struct cpu_state *switch_to_ctxt,
* Public Accessor functions * Public Accessor functions
*/ */
/**
* Return whether the saved context was in kernel or user context
*
* @return TRUE when context was interrupted when in user mode, FALSE
* when in kernel mode, < 0 on error.
*/
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt);
/** /**
* Return Program Counter stored in the saved kernel/user context * Return Program Counter stored in the saved kernel/user context
*/ */
@ -142,7 +156,7 @@ void cpu_context_dump(const struct cpu_state *ctxt);
* Return the argument passed by the CPU upon exception, as stored in the * Return the argument passed by the CPU upon exception, as stored in the
* saved context * saved context
*/ */
uint32_t cpu_context_get_EX_info(const struct cpu_state *ctxt); uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt);
/** /**
* Return the faulting address of the exception * Return the faulting address of the exception
@ -181,7 +195,23 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
vaddr_t kernel_stack_bottom, vaddr_t kernel_stack_bottom,
size_t kernel_stack_size); size_t kernel_stack_size);
#else #else
#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop \ #define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) \
({/* nop \
*/}) */})
#define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */}) #define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */})
#endif #endif
int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2,
uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize);
int syscallGet3args(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1,
/* out */ unsigned int *arg2,
/* out */ unsigned int *arg3);
int syscallGet1arg(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1);
int syscallGet2args(const struct cpu_state *user_ctxt,
/* out */ unsigned int *arg1,
/* out */ unsigned int *arg2);

View File

@ -1,260 +0,0 @@
#include "kthread.h"
#include "alloc.h"
#include "assert.h"
#include "irq.h"
#include "klibc.h"
#include "list.h"
#include "time.h"
#include "vga.h"
static struct kthread *currentThread;
static struct kthread *threadWithTimeout;
void kthreadExit()
{
uint32_t flags;
disable_IRQs(flags);
struct kthread *current = currentThread;
struct kthread *next = kthreadSelectNext();
if (next == current)
assert("cannot exit thread");
currentThread->state = EXITING;
currentThread = next;
currentThread->state = RUNNING;
cpu_context_exit_to(next->cpuState, (cpu_kstate_function_arg1_t *)kthreadDelete,
(uint32_t)current);
restore_IRQs(flags);
return;
}
int kthreadSetup(vaddr_t mainStack, size_t mainStackSize)
{
struct kthread *current = (struct kthread *)malloc(sizeof(struct kthread));
strzcpy(current->name, "[KINIT]", KTHREAD_NAME_MAX_LENGTH);
current->stackAddr = mainStack;
current->stackSize = mainStackSize;
current->state = RUNNING;
list_singleton(currentThread, current);
list_init_named(threadWithTimeout, timePrev, timeNext);
return 0;
}
struct kthread *kthreadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args)
{
struct kthread *thread = (struct kthread *)malloc(sizeof(struct kthread));
if (!thread)
return NULL;
thread->stackAddr = (vaddr_t)malloc(KTHREAD_DEFAULT_STACK_SIZE);
#ifdef DEBUG
printf("Alloc stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread);
#endif
thread->stackSize = KTHREAD_DEFAULT_STACK_SIZE;
if (!thread->stackAddr)
goto free_mem;
if (name)
strzcpy(thread->name, name, KTHREAD_NAME_MAX_LENGTH);
else
strzcpy(thread->name, "[UNKNOW]", KTHREAD_NAME_MAX_LENGTH);
if (cpu_kstate_init(&thread->cpuState, (cpu_kstate_function_arg1_t *)func, (vaddr_t)args,
thread->stackAddr, thread->stackSize,
(cpu_kstate_function_arg1_t *)kthreadExit, 0))
goto free_mem;
thread->state = READY;
uint32_t flags;
disable_IRQs(flags);
list_add_tail(currentThread, thread);
restore_IRQs(flags);
return thread;
free_mem:
free((void *)thread->stackAddr);
free((void *)thread);
return NULL;
}
void kthreadDelete(struct kthread *thread)
{
uint32_t flags;
disable_IRQs(flags);
list_delete(currentThread, thread);
#ifdef DEBUG
printf("Free stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread);
#endif
free((void *)thread->stackAddr);
free((void *)thread);
restore_IRQs(flags);
}
struct kthread *kthreadSelectNext()
{
struct kthread *nextThread;
int idx;
list_foreach(currentThread->next, nextThread, idx)
{
if (nextThread->state == READY) {
return nextThread;
}
}
return currentThread;
}
struct cpu_state *kthreadSwitch(struct cpu_state *prevCpu)
{
uint32_t flags;
struct kthread *nextThread;
disable_IRQs(flags);
nextThread = kthreadSelectNext();
currentThread->cpuState = prevCpu;
currentThread->state = READY;
currentThread = nextThread;
currentThread->state = RUNNING;
restore_IRQs(flags);
return nextThread->cpuState;
}
int kthreadOnJieffiesTick()
{
struct kthread *nextThread;
int idx;
uint32_t flags;
disable_IRQs(flags);
list_foreach(currentThread, nextThread, idx)
{
if (nextThread->state == SLEEPING && nextThread->jiffiesSleeping) {
nextThread->jiffiesSleeping--;
if (!nextThread->jiffiesSleeping) {
nextThread->state = READY;
}
}
}
list_foreach_named(threadWithTimeout, nextThread, idx, timePrev, timeNext)
{
if (nextThread->state == WAITING && nextThread->jiffiesSleeping) {
nextThread->jiffiesSleeping--;
if (!nextThread->jiffiesSleeping) {
nextThread->sleepHaveTimeouted = 1;
list_delete_named(threadWithTimeout, nextThread, timePrev, timeNext);
kthreadAddThread(nextThread);
}
}
}
restore_IRQs(flags);
return 0;
}
int kthreadUnsched(struct kthread *th)
{
list_delete(currentThread, th);
return 0;
}
// Must be called with IRQ disabled
int kthreadWait(struct kthread *current, struct kthread *next, unsigned long msec)
{
if (current == next) {
assertmsg(0, "Cannot yield from %s to %s\n", current->name, next->name);
return 0;
}
assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state);
current->jiffiesSleeping = msecs_to_jiffies(msec);
current->sleepHaveTimeouted = 0;
if (current->jiffiesSleeping)
list_add_tail_named(threadWithTimeout, current, timePrev, timeNext);
currentThread = next;
currentThread->state = RUNNING;
cpu_context_switch(&current->cpuState, next->cpuState);
return current->sleepHaveTimeouted;
}
int kthreadYield()
{
uint32_t flags;
disable_IRQs(flags);
struct kthread *next = kthreadSelectNext();
struct kthread *current = currentThread;
if (current == next) {
restore_IRQs(flags);
return 0;
}
assert(current->state == RUNNING);
assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state);
if (current->state == RUNNING)
current->state = READY;
currentThread = next;
currentThread->state = RUNNING;
cpu_context_switch(&current->cpuState, next->cpuState);
restore_IRQs(flags);
return 0;
}
int kthreadMsleep(unsigned long msec)
{
uint32_t flags;
struct kthread *next, *current;
disable_IRQs(flags);
current = currentThread;
assertmsg(current->state == RUNNING, "thread %s is in state %d for %d\n", current->name,
current->state, msec);
current->state = SLEEPING;
current->sleepHaveTimeouted = 0;
current->jiffiesSleeping = msecs_to_jiffies(msec);
next = kthreadSelectNext();
assert(next != current);
assert(next->state == READY);
currentThread = next;
currentThread->state = RUNNING;
cpu_context_switch(&current->cpuState, next->cpuState);
restore_IRQs(flags);
return current->sleepHaveTimeouted == 1;
}
struct kthread *getCurrentThread()
{
return currentThread;
}
int kthreadAddThread(struct kthread *th)
{
if (th->state == READY)
return 0;
th->state = READY;
list_add_tail(currentThread, th);
return 0;
}

View File

@ -1,47 +0,0 @@
#pragma once
#include "cpu_context.h"
#include "mem.h"
#define KTHREAD_NAME_MAX_LENGTH 32
#define KTHREAD_DEFAULT_STACK_SIZE PAGE_SIZE
typedef enum {
RUNNING,
READY,
SLEEPING,
WAITING,
EXITING
} kthread_state;
struct kthread {
char name[KTHREAD_NAME_MAX_LENGTH];
struct cpu_state *cpuState;
kthread_state state;
vaddr_t stackAddr;
size_t stackSize;
unsigned long jiffiesSleeping;
int sleepHaveTimeouted;
struct kthread *next;
struct kthread *prev;
struct kthread *timeNext;
struct kthread *timePrev;
};
int kthreadSetup(vaddr_t mainStack, size_t mainStackSize);
void kthreadExit();
struct kthread *kthreadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args);
void kthreadDelete(struct kthread *thread);
struct kthread *kthreadSelectNext();
struct cpu_state *kthreadSwitch(struct cpu_state *prevCpu);
int kthreadYield();
int kthreadWait(struct kthread *current, struct kthread *next, unsigned long msec);
int kthreadUnsched(struct kthread *th);
int kthreadMsleep(unsigned long msec);
int kthreadOnJieffiesTick();
struct kthread *getCurrentThread();
int kthreadAddThread(struct kthread *th);

View File

@ -9,14 +9,17 @@
#include "irq.h" #include "irq.h"
#include "keyboard.h" #include "keyboard.h"
#include "klibc.h" #include "klibc.h"
#include "kthread.h" #include "thread.h"
#include "mem.h" #include "mem.h"
#include "mmuContext.h"
#include "multiboot.h" #include "multiboot.h"
#include "paging.h" #include "paging.h"
#include "pit.h" #include "pit.h"
#include "process.h"
#include "serial.h" #include "serial.h"
#include "stack.h" #include "stack.h"
#include "stdarg.h" #include "stdarg.h"
#include "swintr.h"
#ifdef RUN_TEST #ifdef RUN_TEST
#include "test.h" #include "test.h"
#endif #endif
@ -31,7 +34,7 @@ void idleThread(void *arg)
(void)arg; (void)arg;
while (1) { while (1) {
VGAPrintf(GREEN, BLACK, 0, VGA_HEIGHT - 1, "%d", (jiffies / HZ)); VGAPrintf(GREEN, BLACK, 0, VGA_HEIGHT - 1, "%d", (jiffies / HZ));
kthreadYield(); threadYield();
} }
} }
@ -129,6 +132,7 @@ void kmain(unsigned long magic, unsigned long addr)
// Turns out linux and windows do the same ! // Turns out linux and windows do the same !
// https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/ // https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/
if (mmap[i].addr < 0x100000) { if (mmap[i].addr < 0x100000) {
printf(" -> skipping\n");
continue; continue;
} }
memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem), memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem),
@ -160,12 +164,16 @@ void kmain(unsigned long magic, unsigned long addr)
serialSetup(115200); serialSetup(115200);
printf("[Setup] allocation system\n"); printf("[Setup] allocation system\n");
areaInit(firstUsedByMem, lastUsedByMem); areaInit(firstUsedByMem, lastUsedByMem, _stack_bottom, _stack_top);
//allocSetup();
mmuContextSetup();
cpu_context_subsystem_setup();
printf("[Setup] thread system\n"); printf("[Setup] thread system\n");
kthreadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1)); threadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1));
kthreadCreate("idle ", idleThread, NULL); threadCreate("idle ", idleThread, NULL);
processSetup();
syscallSetup();
irqSetRoutine(IRQ_TIMER, pit_handler); irqSetRoutine(IRQ_TIMER, pit_handler);
@ -183,5 +191,5 @@ void kmain(unsigned long magic, unsigned long addr)
// There is no real caller behind this point // There is no real caller behind this point
// So finish this by ourself // So finish this by ourself
kthreadExit(); threadExit();
} }

View File

@ -1,8 +1,9 @@
#include "mem.h"
#include "assert.h" #include "assert.h"
#include "errno.h"
#include "kernel.h" #include "kernel.h"
#include "klibc.h" #include "klibc.h"
#include "list.h" #include "list.h"
#include "mem.h"
#include "types.h" #include "types.h"
static struct phyMemDesc *pageDesc = (struct phyMemDesc *)&__ld_kernel_end; static struct phyMemDesc *pageDesc = (struct phyMemDesc *)&__ld_kernel_end;
@ -132,6 +133,7 @@ paddr_t allocPhyPage(uint nbPage)
next = mem->next; next = mem->next;
} }
allocatedPage += nbPage; allocatedPage += nbPage;
return head->phy_addr; return head->phy_addr;
} }
@ -139,9 +141,10 @@ int unrefPhyPage(paddr_t addr)
{ {
struct phyMemDesc *mem = addr2memDesc(addr); struct phyMemDesc *mem = addr2memDesc(addr);
if (!mem) { if (!mem) {
return -1; return -EINVAL;
} }
assert(mem->ref > 0); if(mem->ref <= 0)
return -EINVAL;
mem->ref--; mem->ref--;
if (mem->ref == 0) { if (mem->ref == 0) {
allocatedPage--; allocatedPage--;

View File

@ -7,6 +7,8 @@
#define PAGE_SHIFT 12U #define PAGE_SHIFT 12U
#define PAGE_SIZE (1U << PAGE_SHIFT) #define PAGE_SIZE (1U << PAGE_SHIFT)
#define PAGE_MASK (PAGE_SIZE - 1)
// Defined in linker.ld script // Defined in linker.ld script
extern uint32_t __ld_kernel_begin; extern uint32_t __ld_kernel_begin;

12
core/mmuContext.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "stdarg.h"
struct mmu_context;
int mmuContextSetup();
struct mmu_context *mmuContextCreate();
int mmuContextSyncKernelPDE(int pdEntry, void *pde, size_t pdeSize);
int mmuContextSwitch(struct mmu_context *ctx);
struct mmu_context *mmuContextGetCurrent();
int mmuContextRef(struct mmu_context *ctx);
int mmuContextUnref(struct mmu_context *ctx);

168
core/process.c Normal file
View File

@ -0,0 +1,168 @@
#include "process.h"
#include "alloc.h"
#include "irq.h"
#include "klibc.h"
#include "list.h"
#include "mmuContext.h"
struct process {
char name[PROCESS_NAME_MAX_LENGTH];
int ref;
int pid;
struct mmu_context *context;
struct thread *thList;
struct process *prev, *next;
};
static struct process *processList;
static int nextPid;
int processSetup()
{
list_init(processList);
allocBookSlab(sizeof(struct process), PAGE_SIZE * 3, 0, 0);
nextPid = 0;
return 0;
}
struct process *processCreate(char *name)
{
uint32_t flags;
struct process *new = (struct process *)malloc(sizeof(struct process));
if (new == NULL)
return NULL;
new->context = mmuContextCreate();
if (new->context == NULL) {
free(new);
return NULL;
}
strzcpy(new->name, name, PROCESS_NAME_MAX_LENGTH);
new->ref = 1;
disable_IRQs(flags);
new->pid = nextPid++;
list_add_tail(processList, new);
restore_IRQs(flags);
return new;
}
void processListPrint()
{
struct process *proc;
int nbProcs;
struct thread *cur = getCurrentThread();
printf("PID NAME NBTHREAD REF\n");
list_foreach(processList, proc, nbProcs)
{
struct thread *th;
int nbTh;
printf("%d %s %d %d\n", proc->pid, proc->name, processCountThread(proc), proc->ref);
list_foreach_named(proc->thList, th, nbTh, prevInProcess, nextInProcess)
{
if (th == cur) {
printf(" th: 0x%x Current\n", th);
} else {
printf(" th: 0x%x in 0x%x\n", th, cpu_context_get_PC(th->cpuState));
}
}
}
}
int processCountThread(struct process *proc)
{
int count;
struct thread *th;
list_foreach_named(proc->thList, th, count, prevInProcess, nextInProcess) {}
return count;
}
int processRef(struct process *proc)
{
uint32_t flags;
// ref == 0 -> delete
assert(proc->ref > 0);
disable_IRQs(flags);
proc->ref++;
restore_IRQs(flags);
return 0;
}
int processUnref(struct process *proc)
{
uint32_t flags;
assert(proc->ref > 0);
disable_IRQs(flags);
proc->ref--;
if (proc->ref > 0) {
restore_IRQs(flags);
return -EBUSY;
}
list_delete(processList, proc);
restore_IRQs(flags);
mmuContextUnref(proc->context);
free(proc);
return 0;
}
int processAddThread(struct process *proc, struct thread *th)
{
uint32_t flags;
assert(proc->ref > 0);
th->process = proc;
disable_IRQs(flags);
processRef(proc);
list_add_tail_named(proc->thList, th, prevInProcess, nextInProcess);
restore_IRQs(flags);
return 0;
}
int processRemoveThread(struct thread *th)
{
uint32_t flags;
struct process *proc;
disable_IRQs(flags);
proc = th->process;
list_delete_named(proc->thList, th, prevInProcess, nextInProcess);
restore_IRQs(flags);
processUnref(proc);
return 0;
}
int processSetName(struct process *proc, char *name)
{
assert(name != NULL);
strzcpy(proc->name, name, PROCESS_NAME_MAX_LENGTH);
return 0;
}
struct mmu_context *processGetMMUContext(struct process *proc)
{
return proc->context;
}

17
core/process.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "thread.h"
#define PROCESS_NAME_MAX_LENGTH 32
struct process;
int processSetup();
struct process *processCreate(char *name);
int processCountThread(struct process *proc);
void processListPrint();
int processRef(struct process *proc);
int processUnref(struct process *proc);
int processSetName(struct process *proc, char *name);
int processAddThread(struct process *proc, struct thread *th);
int processRemoveThread(struct thread *th);
struct mmu_context *processGetMMUContext(struct process *th);

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2004 The SOS Team /* Copyright (C) 2021 Mathieu Maret
Copyright (C) 2004 The SOS Team
Copyright (C) 1999 Free Software Foundation, Inc. Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -29,8 +30,6 @@
* @see Intel x86 doc, vol 3 chapter 3. * @see Intel x86 doc, vol 3 chapter 3.
*/ */
#include "stdarg.h"
/* /*
* Global segment selectors (GDT) for SOS/x86. * Global segment selectors (GDT) for SOS/x86.
* *
@ -39,13 +38,23 @@
#define SEG_NULL 0 /* NULL segment, unused by the procesor */ #define SEG_NULL 0 /* NULL segment, unused by the procesor */
#define SEG_KCODE 1 /* Kernel code segment */ #define SEG_KCODE 1 /* Kernel code segment */
#define SEG_KDATA 2 /* Kernel data segment */ #define SEG_KDATA 2 /* Kernel data segment */
#define SEG_UCODE 3 /* User code segment */
#define SEG_UDATA 4 /* User data segment */
#define SEG_K_TSS 5 /* Kernel TSS for priviledge change (user to kernel) */
/** /**
* Helper macro that builds a segment register's value * Helper macro that builds a segment register's value
*/ */
#ifdef ASM_SOURCE
#define BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \
( (((desc_privilege) & 0x3) << 0) \
| ((in_ldt & 1) << 2) \
| ((seg_index) << 3) )
#else
#define BUILD_SEGMENT_REG_VALUE(desc_privilege, in_ldt, seg_index) \ #define BUILD_SEGMENT_REG_VALUE(desc_privilege, in_ldt, seg_index) \
((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | ((seg_index) << 3)) ((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | ((seg_index) << 3))
#endif
/* /*
* Local segment selectors (LDT) for SOS/x86 * Local segment selectors (LDT) for SOS/x86
*/ */

View File

@ -51,7 +51,7 @@ int mutexFree(struct mutex *m)
int mutexLock(struct mutex *m) int mutexLock(struct mutex *m)
{ {
uint32_t flags; uint32_t flags;
struct kthread *current; struct thread *current;
disable_IRQs(flags); disable_IRQs(flags);
current = getCurrentThread(); current = getCurrentThread();

27
core/syscall.c Normal file
View File

@ -0,0 +1,27 @@
#include "klibc.h"
#include "stdarg.h"
#include "syscall.h"
#include "thread.h"
int syscallExecute(int syscallId, const struct cpu_state *user_ctx){
int ret;
switch (syscallId) {
case SYSCALL_ID_EXIT:
uint status;
ret = syscallGet1arg(user_ctx, &status);
if(ret != 0)
break;
threadExit();
assert(0);
break;
case SYSCALL_ID_WRITE:
ret = printf("YOLO FROM USERSPACE\n");
break;
default:
printf("Unknon syscall id %d\n", syscallId);
ret = -ENOENT;
}
return ret;
}

9
core/syscall.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "cpu_context.h"
#define SYSCALL_ID_EXIT 1
#define SYSCALL_ID_WRITE 2
int syscallExecute(int syscallId, const struct cpu_state *user_ctx);

391
core/thread.c Normal file
View File

@ -0,0 +1,391 @@
#include "thread.h"
#include "alloc.h"
#include "assert.h"
#include "irq.h"
#include "klibc.h"
#include "list.h"
#include "mmuContext.h"
#include "time.h"
#include "types.h"
#include "vga.h"
static struct thread *currentThread;
static struct thread *threadWithTimeout;
static void threadPrepareContext(struct thread *th);
void threadExit()
{
uint32_t flags;
disable_IRQs(flags);
struct thread *current = currentThread;
struct thread *next = threadSelectNext();
if (next == current)
assert("cannot exit thread");
currentThread->state = EXITING;
currentThread = next;
currentThread->state = RUNNING;
threadPrepareContext(next);
cpu_context_exit_to(next->cpuState, (cpu_kstate_function_arg1_t *)threadDelete,
(uint32_t)current);
restore_IRQs(flags);
return;
}
int threadSetup(vaddr_t mainStack, size_t mainStackSize)
{
struct thread *current = (struct thread *)malloc(sizeof(struct thread));
strzcpy(current->name, "[KINIT]", THREAD_NAME_MAX_LENGTH);
current->stackAddr = mainStack;
current->stackSize = mainStackSize;
current->state = RUNNING;
list_singleton(currentThread, current);
list_init_named(threadWithTimeout, timePrev, timeNext);
return 0;
}
struct thread *threadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args)
{
struct thread *thread = (struct thread *)malloc(sizeof(struct thread));
if (!thread)
return NULL;
thread->stackAddr = (vaddr_t)malloc(THREAD_DEFAULT_STACK_SIZE);
if (!thread->stackAddr){
free(thread);
return NULL;
}
#ifdef DEBUG
printf("Alloc stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread);
#endif
thread->stackSize = THREAD_DEFAULT_STACK_SIZE;
if (name)
strzcpy(thread->name, name, THREAD_NAME_MAX_LENGTH);
else
strzcpy(thread->name, "[UNKNOW]", THREAD_NAME_MAX_LENGTH);
if (cpu_kstate_init(&thread->cpuState, (cpu_kstate_function_arg1_t *)func, (vaddr_t)args,
thread->stackAddr, thread->stackSize,
(cpu_kstate_function_arg1_t *)threadExit, 0))
goto free_mem;
thread->state = READY;
uint32_t flags;
disable_IRQs(flags);
list_add_tail(currentThread, thread);
restore_IRQs(flags);
return thread;
free_mem:
free((void *)thread->stackAddr);
free((void *)thread);
return NULL;
}
struct thread *threadCreateUser(const char *name, struct process *proc, uaddr_t startPc,
uint32_t arg1, uint32_t arg2, uaddr_t startSP)
{
struct thread *thread = malloc(sizeof(struct thread));
if (thread == NULL)
return NULL;
thread->stackAddr = (vaddr_t)malloc(THREAD_DEFAULT_STACK_SIZE);
if (!thread->stackAddr) {
free(thread);
return NULL;
}
thread->stackSize = THREAD_DEFAULT_STACK_SIZE;
if (name)
strzcpy(thread->name, name, THREAD_NAME_MAX_LENGTH);
else
strzcpy(thread->name, "[UNKNOW]", THREAD_NAME_MAX_LENGTH);
if (cpu_ustate_init(&thread->cpuState, startPc, arg1, arg2, startSP, thread->stackAddr,
thread->stackSize)) {
goto free_mem;
}
if(processAddThread(proc, thread))
goto free_mem;
thread->state = READY;
uint32_t flags;
disable_IRQs(flags);
list_add_tail(currentThread, thread);
restore_IRQs(flags);
return thread;
free_mem:
free((void *)thread->stackAddr);
free((void *)thread);
return NULL;
}
void threadDelete(struct thread *thread)
{
uint32_t flags;
disable_IRQs(flags);
list_delete(currentThread, thread);
restore_IRQs(flags);
assert(thread->state == EXITING);
if (thread->squattedContext) {
threadChangeCurrentContext(NULL);
}
if (thread->process)
processRemoveThread(thread);
#ifdef DEBUG
printf("Free stack at 0x%x struct at 0x%x\n", thread->stackAddr, thread);
#endif
free((void *)thread->stackAddr);
free((void *)thread);
}
struct thread *threadSelectNext()
{
struct thread *nextThread;
int idx;
list_foreach(currentThread->next, nextThread, idx)
{
if (nextThread->state == READY) {
return nextThread;
}
}
return currentThread;
}
struct cpu_state *threadSwitch(struct cpu_state *prevCpu)
{
uint32_t flags;
struct thread *nextThread;
disable_IRQs(flags);
nextThread = threadSelectNext();
currentThread->cpuState = prevCpu;
if (nextThread != currentThread) {
currentThread->state = READY;
// printf(" Switch from %s to %s\n", currentThread->name, nextThread->name);
currentThread = nextThread;
currentThread->state = RUNNING;
threadPrepareContext(nextThread);
}
restore_IRQs(flags);
return nextThread->cpuState;
}
int threadCount()
{
struct thread *nextThread;
int idx;
uint32_t flags;
disable_IRQs(flags);
list_foreach(currentThread, nextThread, idx){
}
return idx;
}
int threadOnJieffiesTick()
{
struct thread *nextThread;
int idx;
uint32_t flags;
disable_IRQs(flags);
list_foreach(currentThread, nextThread, idx)
{
if (nextThread->state == SLEEPING && nextThread->jiffiesSleeping) {
nextThread->jiffiesSleeping--;
if (!nextThread->jiffiesSleeping) {
nextThread->state = READY;
}
}
}
list_foreach_named(threadWithTimeout, nextThread, idx, timePrev, timeNext)
{
if (nextThread->state == WAITING && nextThread->jiffiesSleeping) {
nextThread->jiffiesSleeping--;
if (!nextThread->jiffiesSleeping) {
nextThread->sleepHaveTimeouted = 1;
list_delete_named(threadWithTimeout, nextThread, timePrev, timeNext);
threadAddThread(nextThread);
}
}
}
restore_IRQs(flags);
return 0;
}
int threadUnsched(struct thread *th)
{
list_delete(currentThread, th);
return 0;
}
// Must be called with IRQ disabled
int threadWait(struct thread *current, struct thread *next, unsigned long msec)
{
if (current == next) {
assertmsg(0, "Cannot yield from %s to %s\n", current->name, next->name);
return 0;
}
assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state);
current->jiffiesSleeping = msecs_to_jiffies(msec);
current->sleepHaveTimeouted = 0;
if (current->jiffiesSleeping)
list_add_tail_named(threadWithTimeout, current, timePrev, timeNext);
currentThread = next;
currentThread->state = RUNNING;
threadPrepareContext(next);
cpu_context_switch(&current->cpuState, next->cpuState);
return current->sleepHaveTimeouted;
}
int threadYield()
{
uint32_t flags;
disable_IRQs(flags);
struct thread *next = threadSelectNext();
struct thread *current = currentThread;
if (current == next) {
restore_IRQs(flags);
return 0;
}
assert(current->state == RUNNING);
assertmsg(next->state == READY, "thread %s is in state %d\n", next->name, next->state);
if (current->state == RUNNING)
current->state = READY;
// printf(" Yield from %s to %s\n", currentThread->name, next->name);
currentThread = next;
currentThread->state = RUNNING;
threadPrepareContext(next);
cpu_context_switch(&current->cpuState, next->cpuState);
restore_IRQs(flags);
return 0;
}
int threadMsleep(unsigned long msec)
{
uint32_t flags;
struct thread *next, *current;
disable_IRQs(flags);
current = currentThread;
assertmsg(current->state == RUNNING, "thread %s is in state %d for %d\n", current->name,
current->state, msec);
current->state = SLEEPING;
current->sleepHaveTimeouted = 0;
current->jiffiesSleeping = msecs_to_jiffies(msec);
next = threadSelectNext();
assert(next != current);
assert(next->state == READY);
currentThread = next;
currentThread->state = RUNNING;
threadPrepareContext(next);
cpu_context_switch(&current->cpuState, next->cpuState);
restore_IRQs(flags);
return current->sleepHaveTimeouted == 1;
}
struct thread *getCurrentThread()
{
return currentThread;
}
int threadAddThread(struct thread *th)
{
if (th->state == READY)
return 0;
th->state = READY;
list_add_tail(currentThread, th);
return 0;
}
static void threadPrepareContext(struct thread *th)
{
if (cpu_context_is_in_user_mode(th->cpuState)) {
assert(th->process != NULL);
assert(th->squattedContext == NULL);
mmuContextSwitch(processGetMMUContext(th->process));
} else if (th->squattedContext) {
mmuContextSwitch(th->squattedContext);
}
}
int threadChangeCurrentContext(struct mmu_context *ctx)
{
uint32_t flags;
struct mmu_context *prev = currentThread->squattedContext;
if (ctx != NULL) {
assert(prev == NULL);
} else {
assert(prev != NULL);
}
disable_IRQs(flags);
currentThread->squattedContext = ctx;
if (ctx != NULL) {
mmuContextRef(ctx);
mmuContextSwitch(ctx);
} else {
mmuContextUnref(prev);
}
restore_IRQs(flags);
return 0;
}
void threadPrepareSyscallSwitchBack(struct cpu_state *cpuState)
{
currentThread->cpuState = cpuState;
threadPrepareContext(currentThread);
}
void threadPrepareExceptionSwitchBack(struct cpu_state *cpuState)
{
currentThread->cpuState = cpuState;
threadPrepareContext(currentThread);
}
void threadPrepareIrqServicing(struct cpu_state *cpuState)
{
currentThread->cpuState = cpuState;
}
void threadPrepareIrqSwitchBack(struct cpu_state *cpuState)
{
currentThread->cpuState = cpuState;
threadPrepareContext(currentThread);
}

90
core/thread.h Normal file
View File

@ -0,0 +1,90 @@
#pragma once
struct thread;
#include "cpu_context.h"
#include "mem.h"
#include "process.h"
#define THREAD_NAME_MAX_LENGTH 32
#define THREAD_DEFAULT_STACK_SIZE PAGE_SIZE
typedef enum {
RUNNING,
READY,
SLEEPING,
WAITING,
EXITING
} thread_state;
struct thread {
char name[THREAD_NAME_MAX_LENGTH];
struct cpu_state *cpuState;
thread_state state;
vaddr_t stackAddr;
size_t stackSize;
unsigned long jiffiesSleeping;
int sleepHaveTimeouted;
struct thread *next, *prev;
struct thread *timeNext, *timePrev;
// For User thread only
struct thread *nextInProcess, *prevInProcess;
struct process *process;
/**
* Address space currently "squatted" by the thread, or used to be
* active when the thread was interrupted/preempted. This is the MMU
* configuration expected before the cpu_state of the thread is
* restored on CPU.
* - For kernel threads: should normally be NULL, meaning that the
* thread will squat the current mm_context currently set in the
* MMU. Might be NON NULL when a kernel thread squats a given
* process to manipulate its address space.
* - For user threads: should normally be NULL. More precisely:
* - in user mode: the thread->process.mm_context is ALWAYS
* set on MMU. squatted_mm_context is ALWAYS NULL in this
* situation, meaning that the thread in user mode uses its
* process-space as expected
* - in kernel mode: NULL means that we keep on using the
* mm_context currently set on MMU, which might be the
* mm_context of another process. This is natural since a
* thread in kernel mode normally only uses data in kernel
* space. BTW, this limits the number of TLB flushes. However,
* there are exceptions where this squatted_mm_context will
* NOT be NULL. One is the copy_from/to_user API, which can
* force the effective mm_context so that the MMU will be
* (re)configured upon every context to the thread to match
* the squatted_mm_context. Another exception is when a parent
* thread creates the address space of a child process, in
* which case the parent thread might temporarilly decide to
* switch to the child's process space.
*
* This is the SOS/matos implementation of the Linux "Lazy TLB" and
* address-space loaning.
*/
struct mmu_context *squattedContext;
};
int threadSetup(vaddr_t mainStack, size_t mainStackSize);
void threadExit();
struct thread *threadCreate(const char *name, cpu_kstate_function_arg1_t func, void *args);
struct thread *threadCreateUser(const char *name, struct process *proc, uaddr_t startPc,
uint32_t arg1, uint32_t arg2, uaddr_t startSP);
void threadDelete(struct thread *thread);
struct thread *threadSelectNext();
struct cpu_state *threadSwitch(struct cpu_state *prevCpu);
int threadYield();
int threadWait(struct thread *current, struct thread *next, unsigned long msec);
int threadUnsched(struct thread *th);
int threadMsleep(unsigned long msec);
int threadOnJieffiesTick();
struct thread *getCurrentThread();
int threadAddThread(struct thread *th);
int threadChangeCurrentContext(struct mmu_context *ctx);
int threadCount();

View File

@ -33,3 +33,6 @@ typedef unsigned long vaddr_t;
// Physical address // Physical address
typedef unsigned long paddr_t; typedef unsigned long paddr_t;
// Userspace vaddr
typedef unsigned long uaddr_t;

View File

@ -1,5 +1,5 @@
#include "irq.h" #include "irq.h"
#include "kthread.h" #include "thread.h"
#include "list.h" #include "list.h"
#include "wait.h" #include "wait.h"
@ -25,13 +25,13 @@ int waitQueueFree(struct wait_queue *wq)
int wakeUp(struct wait_queue *wq) int wakeUp(struct wait_queue *wq)
{ {
struct kthread *th; struct thread *th;
uint32_t flags; uint32_t flags;
disable_IRQs(flags); disable_IRQs(flags);
list_collapse(wq->thread, th) list_collapse(wq->thread, th)
{ {
kthreadAddThread(th); threadAddThread(th);
} }
restore_IRQs(flags); restore_IRQs(flags);
@ -46,7 +46,7 @@ int wait(struct wait_queue *wq)
int waitTimeout(struct wait_queue *wq, unsigned long msec) int waitTimeout(struct wait_queue *wq, unsigned long msec)
{ {
struct kthread *current, *next; struct thread *current, *next;
uint32_t flags; uint32_t flags;
int ret; int ret;
@ -54,11 +54,11 @@ int waitTimeout(struct wait_queue *wq, unsigned long msec)
current = getCurrentThread(); current = getCurrentThread();
current->state = WAITING; current->state = WAITING;
next = kthreadSelectNext(); next = threadSelectNext();
kthreadUnsched(current); threadUnsched(current);
list_add_tail(wq->thread, current); list_add_tail(wq->thread, current);
ret = kthreadWait(current, next, msec); ret = threadWait(current, next, msec);
restore_IRQs(flags); restore_IRQs(flags);
return ret; return ret;

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include "kthread.h" #include "thread.h"
struct wait_queue { struct wait_queue {
struct kthread *thread; struct thread *thread;
struct wait_queue *next; struct wait_queue *next;
struct wait_queue *prev; struct wait_queue *prev;
}; };
@ -35,6 +35,6 @@ struct semaphore {
}; };
struct mutex { struct mutex {
struct kthread *owner; struct thread *owner;
struct wait_queue *wait; struct wait_queue *wait;
}; };

View File

@ -39,32 +39,32 @@ class CustomPrettyPrinterLocator(PrettyPrinter):
if typename is None: if typename is None:
typename = val.type.name typename = val.type.name
if typename == "kthread": if typename == "thread":
return KthreadPrettyPrinter(val) return KthreadPrettyPrinter(val)
class KthreadListDumpCmd(gdb.Command): class threadListDumpCmd(gdb.Command):
"""Prints the kthread list""" """Prints the thread list"""
def __init__(self): def __init__(self):
super(KthreadListDumpCmd, self).__init__( super(threadListDumpCmd, self).__init__(
"kthread_list_dump", gdb.COMMAND_USER "thread_list_dump", gdb.COMMAND_USER
) )
def _kthread_list_to_str(self, val): def _thread_list_to_str(self, val):
"""Walk through the Kthread list. """Walk through the Kthread list.
We will simply follow the 'next' pointers until we encounter the HEAD again We will simply follow the 'next' pointers until we encounter the HEAD again
""" """
idx = 0 idx = 0
head = val head = val
kthread_ptr = val thread_ptr = val
result = "" result = ""
while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head): while thread_ptr != 0 and (idx == 0 or thread_ptr != head):
result += "\n%d: %s" % (idx, KthreadPrettyPrinter(kthread_ptr).to_string()) result += "\n%d: %s" % (idx, KthreadPrettyPrinter(thread_ptr).to_string())
kthread_ptr = kthread_ptr["next"] thread_ptr = thread_ptr["next"]
idx += 1 idx += 1
result = ("Found a Linked List with %d kthread:" % idx) + result result = ("Found a Linked List with %d thread:" % idx) + result
return result return result
def complete(self, text, word): def complete(self, text, word):
@ -77,14 +77,14 @@ class KthreadListDumpCmd(gdb.Command):
# to do argument parsing # to do argument parsing
print("Args Passed: %s" % args) print("Args Passed: %s" % args)
if args: if args:
kthread_ptr_val = gdb.parse_and_eval(args) thread_ptr_val = gdb.parse_and_eval(args)
else: else:
kthread_ptr_val = gdb.parse_and_eval("currentThread") thread_ptr_val = gdb.parse_and_eval("currentThread")
if str(kthread_ptr_val.type) != "struct kthread *": if str(thread_ptr_val.type) != "struct thread *":
print("Expected pointer argument of type (struct kthread *)") print("Expected pointer argument of type (struct thread *)")
return return
print(self._kthread_list_to_str(kthread_ptr_val)) print(self._thread_list_to_str(thread_ptr_val))
class PhyMemDescListDumpCmd(gdb.Command): class PhyMemDescListDumpCmd(gdb.Command):
@ -175,6 +175,7 @@ class PrintStructC99Cmd(gdb.Command):
rr_rval = rr_s[1].strip() rr_rval = rr_s[1].strip()
print(' ' * hs + '.' + rr_s[0] + '= ' + rr_rval) print(' ' * hs + '.' + rr_s[0] + '= ' + rr_rval)
class ListDumpCmd(gdb.Command): class ListDumpCmd(gdb.Command):
"""Prints a linked list""" """Prints a linked list"""
@ -190,13 +191,13 @@ class ListDumpCmd(gdb.Command):
""" """
idx = 0 idx = 0
head = val head = val
kthread_ptr = val thread_ptr = val
result = "" result = ""
while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head): while thread_ptr != 0 and (idx == 0 or thread_ptr != head):
result += gdb.execute('p *({}){}'.format(str(kthread_ptr.type),kthread_ptr), to_string=True) result += gdb.execute('p *({}){}'.format(str(thread_ptr.type),thread_ptr), to_string=True)
kthread_ptr = kthread_ptr["next"] thread_ptr = thread_ptr["next"]
idx += 1 idx += 1
result = ("Found a Linked List with %d items:" % idx) + "\n"+ result result = ("Found a Linked List with %d items:" % idx) + "\n" + result
return result return result
def complete(self, text, word): def complete(self, text, word):
@ -220,8 +221,9 @@ class ListDumpCmd(gdb.Command):
print(self._print_list(ptr_val)) print(self._print_list(ptr_val))
register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True) register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True)
KthreadListDumpCmd() threadListDumpCmd()
PhyMemDescListDumpCmd() PhyMemDescListDumpCmd()
PrintStructC99Cmd() PrintStructC99Cmd()
ListDumpCmd() ListDumpCmd()

View File

@ -1,3 +1,5 @@
add-symbol-file kernel.sym add-symbol-file kernel.sym
source custom_gdb_extension.py source custom_gdb_extension.py
target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M #For ASM sources
directory arch/x86/:core
target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M -serial file:serialOut

View File

@ -2,7 +2,7 @@
#include "io.h" #include "io.h"
#include "kernel.h" #include "kernel.h"
#include "klibc.h" #include "klibc.h"
#include "kthread.h" #include "thread.h"
// from // from
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data
@ -536,6 +536,7 @@ int ATAReadSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf) int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
{ {
struct ata_controller *ctl = dev->ctl; struct ata_controller *ctl = dev->ctl;
int ret = 0;
mutexLock(&ctl->mutex); mutexLock(&ctl->mutex);
@ -561,7 +562,8 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
printf("ATA write error\n"); printf("ATA write error\n");
return -1; return -1;
} }
while (1) { int retry = 100;
while (retry) {
int status = inb(ctl->base + ATA_PIO_STATUS); int status = inb(ctl->base + ATA_PIO_STATUS);
if (status & ATA_PIO_STATUS_REG_ERR) { if (status & ATA_PIO_STATUS_REG_ERR) {
mutexUnlock(&ctl->mutex); mutexUnlock(&ctl->mutex);
@ -571,12 +573,19 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
if ((status & ATA_PIO_STATUS_DRQ) && if ((status & ATA_PIO_STATUS_DRQ) &&
!(status & ATA_PIO_STATUS_DRIVE_BUSY)) !(status & ATA_PIO_STATUS_DRIVE_BUSY))
break; break;
retry--;
}
if(retry == 0){
printf("ATA write timeout error\n");
ret=-1;
break;
} }
for (uint i = 0; i < (DISK_SECTOR_SIZE / sizeof(uint16_t)); i++) { for (uint i = 0; i < (DISK_SECTOR_SIZE / sizeof(uint16_t)); i++) {
outw(ctl->base + ATA_PIO_DATA, *ptr++); outw(ctl->base + ATA_PIO_DATA, *ptr++);
} }
retry = 1000;
// Wait for the device to receive the data // Wait for the device to receive the data
while (1) { while (retry) {
int status = inb(ctl->base + ATA_PIO_STATUS); int status = inb(ctl->base + ATA_PIO_STATUS);
if (status & ATA_PIO_STATUS_REG_ERR) { if (status & ATA_PIO_STATUS_REG_ERR) {
mutexUnlock(&ctl->mutex); mutexUnlock(&ctl->mutex);
@ -586,12 +595,18 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
if (!(status & ATA_PIO_STATUS_DRQ) && if (!(status & ATA_PIO_STATUS_DRQ) &&
!(status & ATA_PIO_STATUS_DRIVE_BUSY)) !(status & ATA_PIO_STATUS_DRIVE_BUSY))
break; break;
retry --;
}
if(retry == 0){
printf("ATA write data timeout error\n");
ret=-1;
break;
} }
} }
mutexUnlock(&ctl->mutex); mutexUnlock(&ctl->mutex);
return 0; return ret;
} }
struct ata_device *ATAGetDevice(int ctlId, int devId) struct ata_device *ATAGetDevice(int ctlId, int devId)

View File

@ -2,7 +2,7 @@
#include "io.h" #include "io.h"
#include "irq.h" #include "irq.h"
#include "klibc.h" #include "klibc.h"
#include "kthread.h" #include "thread.h"
#include "time.h" #include "time.h"
int pitSetup(unsigned int freq) int pitSetup(unsigned int freq)
@ -20,8 +20,8 @@ int pitSetup(unsigned int freq)
struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu) struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu)
{ {
__atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED); __atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED);
kthreadOnJieffiesTick(); threadOnJieffiesTick();
// Uncomment for non-preemptible kernel // Uncomment for non-preemptible kernel
// return prevCpu; // return prevCpu;
return kthreadSwitch(prevCpu); return threadSwitch(prevCpu);
} }

View File

@ -1,15 +1,21 @@
#include "alloc.h" #include "alloc.h"
#include "allocArea.h"
#include "ata.h" #include "ata.h"
#include "assert.h" #include "assert.h"
#include "cpu_context.h" #include "cpu_context.h"
#include "kernel.h"
#include "klibc.h" #include "klibc.h"
#include "kthread.h" #include "thread.h"
#include "list.h" #include "list.h"
#include "mem.h" #include "mem.h"
#include "mmuContext.h"
#include "paging.h" #include "paging.h"
#include "process.h"
#include "serial.h" #include "serial.h"
#include "stack.h" #include "stack.h"
#include "synchro.h" #include "synchro.h"
#include "syscall.h"
#include "swintr.h"
#include "time.h" #include "time.h"
void testMemcpyPerf() void testMemcpyPerf()
@ -84,14 +90,22 @@ static void *testAllocNSet(size_t size)
return allocated; return allocated;
} }
static void testAlloc(void) void testAllocArea(){
vaddr_t area = areaAlloc(1, 0);
vaddr_t area2 = areaAlloc(1, AREA_PHY_MAP);
assert(area != area2);
areaFree(area);
areaFree(area2);
}
void testAlloc(void)
{ {
assert(malloc(1410065407) == NULL); assert(malloc(1410065407) == NULL);
for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabEntry)); i++) { for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabEntry)); i++) {
malloc(sizeof(struct slabEntry)); assert(malloc(sizeof(struct slabEntry)) != NULL);
} }
for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabDesc)); i++) { for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabDesc)); i++) {
malloc(sizeof(struct slabDesc)); assert(malloc(sizeof(struct slabDesc)) != NULL);
} }
assert(malloc(1)); assert(malloc(1));
assert(malloc(2)); assert(malloc(2));
@ -103,6 +117,8 @@ static void testAlloc(void)
free(malloc2); free(malloc2);
void *malloc3 = malloc(sizeof(void *)); void *malloc3 = malloc(sizeof(void *));
assertmsg((char *)malloc2 == (char *)malloc3, " %d %d\n", malloc2, malloc3); assertmsg((char *)malloc2 == (char *)malloc3, " %d %d\n", malloc2, malloc3);
free(malloc1);
free(malloc3);
void *alloc1 = testAllocNSet(1024); void *alloc1 = testAllocNSet(1024);
void *alloc2 = testAllocNSet(1024); void *alloc2 = testAllocNSet(1024);
void *alloc3 = testAllocNSet(1024); void *alloc3 = testAllocNSet(1024);
@ -133,7 +149,7 @@ static void testAlloc(void)
free(alloc16); free(alloc16);
} }
static void testPaging(void) void testPaging(void)
{ {
printf("Testing paging\n"); printf("Testing paging\n");
struct phyMemDesc *allocated_page_list; struct phyMemDesc *allocated_page_list;
@ -143,9 +159,7 @@ static void testPaging(void)
int allocCount = 0; int allocCount = 0;
int freeCount = 0; int freeCount = 0;
while ((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL) { while ((page = (struct phyMemDesc *)areaAlloc(1, AREA_PHY_MAP)) != NULL) {
assertmsg(pageMap((vaddr_t)page, (paddr_t)page, PAGING_MEM_WRITE) == 0,
"Fail to map page %d\n", allocCount);
memset(page, allocCount, PAGE_SIZE); memset(page, allocCount, PAGE_SIZE);
allocCount++; allocCount++;
list_add_tail(allocated_page_list, page); list_add_tail(allocated_page_list, page);
@ -156,12 +170,12 @@ static void testPaging(void)
(page = list_pop_head(allocated_page_list)) != NULL) { (page = list_pop_head(allocated_page_list)) != NULL) {
assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %d\n", assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %d\n",
freeCount, page->phy_addr); freeCount, page->phy_addr);
assertmsg(unrefPhyPage((ulong)page) >= 0, "Failed to free page %d\n", (ulong)page); areaFree((vaddr_t)page);
pageUnmap((vaddr_t)page);
freeCount++; freeCount++;
} }
printf("%d pages freed\n", freeCount); printf("%d pages freed\n", freeCount);
assert(freeCount == allocCount);
assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL, assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL,
"Cannot allocate memory\n"); "Cannot allocate memory\n");
unrefPhyPage((ulong)page); unrefPhyPage((ulong)page);
@ -202,6 +216,7 @@ static void reclaim_stack(void *stack_vaddr)
static void exit_hello12(void *stack_vaddr) static void exit_hello12(void *stack_vaddr)
{ {
printf("Stopping hello\n");
cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t *)reclaim_stack, cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t *)reclaim_stack,
(vaddr_t)stack_vaddr); (vaddr_t)stack_vaddr);
} }
@ -264,7 +279,7 @@ static void kthread1(void *strIn)
char *str = (char *)strIn; char *str = (char *)strIn;
for (; *str != '\n'; str++) { for (; *str != '\n'; str++) {
printf("kth1: %c\n", *str); printf("kth1: %c\n", *str);
kthreadYield(); threadYield();
} }
} }
@ -273,7 +288,7 @@ static void kthread2(void *strIn)
char *str = (char *)strIn; char *str = (char *)strIn;
for (; *str != '\n'; str++) { for (; *str != '\n'; str++) {
printf("kth2: %c\n", *str); printf("kth2: %c\n", *str);
kthreadYield(); threadYield();
} }
} }
@ -286,11 +301,11 @@ void sleepThread(void *arg)
while (secSleep < 5) { while (secSleep < 5) {
// printf("Sleeping loop %d\n", secSleep); // printf("Sleeping loop %d\n", secSleep);
secSleep++; secSleep++;
kthreadMsleep(1000); threadMsleep(100);
} }
unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies); unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies);
assertmsg(ellapsedTime >= 5000 && ellapsedTime < 5100, "ellapsedTime %d\n", ellapsedTime); assertmsg(ellapsedTime >= 500 && ellapsedTime < 510, "ellapsedTime %d\n", ellapsedTime);
kthreadMsleep(0); threadMsleep(0);
printf("I should never be showed\n"); printf("I should never be showed\n");
assert(1); assert(1);
} }
@ -305,7 +320,7 @@ void mutThread(void *arg)
while (test > 0) { while (test > 0) {
mutexLock(&mutexTest); mutexLock(&mutexTest);
printf("%s sleep\n", (char *)arg); printf("%s sleep\n", (char *)arg);
kthreadMsleep(1000); threadMsleep(100);
printf("%s up\n", (char *)arg); printf("%s up\n", (char *)arg);
mutexUnlock(&mutexTest); mutexUnlock(&mutexTest);
test--; test--;
@ -317,7 +332,7 @@ void wqThread(void *arg)
(void)arg; (void)arg;
DECLARE_WAITQUEUE(test); DECLARE_WAITQUEUE(test);
waitQueueInit(&test); waitQueueInit(&test);
assert(waitTimeout(&test, 1000) == 1); assert(waitTimeout(&test, 100) == 1);
waitQueueFree(&test); waitQueueFree(&test);
haveTimeout = 1; haveTimeout = 1;
} }
@ -326,17 +341,18 @@ void testKthread()
{ {
mutexInit(&mutexTest); mutexInit(&mutexTest);
// It is not expected to have necessarily "Hello world\n" properly written // It is not expected to have necessarily "Hello world\n" properly written
kthreadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n"); threadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n");
kthreadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n"); threadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n");
kthreadMsleep(1000); threadMsleep(100);
kthreadCreate("wq timeout", wqThread, NULL); threadCreate("wq timeout", wqThread, NULL);
kthreadMsleep(2000); threadMsleep(200);
assert(haveTimeout); assert(haveTimeout);
kthreadCreate("sleep", sleepThread, NULL); threadCreate("sleep", sleepThread, NULL);
kthreadMsleep(5000); threadMsleep(500);
kthreadCreate("mtest1", mutThread, "mut1"); threadCreate("mtest1", mutThread, "mut1");
kthreadCreate("mtest2", mutThread, "mut2"); threadCreate("mtest2", mutThread, "mut2");
kthreadCreate("mtest3", mutThread, "mut3"); threadCreate("mtest3", mutThread, "mut3");
threadMsleep(2000);
} }
void testATAThread(){ void testATAThread(){
@ -354,13 +370,92 @@ void testATAThread(){
} }
} }
void testATA(){ static void testATA(){
kthreadCreate("ATA_TEST", testATAThread, NULL); threadCreate("ATA_TEST", testATAThread, NULL);
//testATAThread(); //testATAThread();
} }
static void testMMUContext()
{
printf("Testing mmu\n");
struct mmu_context *current = mmuContextGetCurrent();
assert(current != NULL);
struct mmu_context *new = mmuContextCreate();
assert(new != NULL);
mmuContextSwitch(new);
mmuContextSwitch(current);
mmuContextUnref(new);
}
static void userProgramm()
{
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"(SYSCALL_ID_WRITE), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB)
: "eax", "ebx", "ecx", "edx");
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"(SYSCALL_ID_EXIT), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB)
: "eax", "ebx", "ecx", "edx");
}
static void testProcess(){
struct process *proc= processCreate("TESTPROCESS");
struct thread *th1 = threadCreate("th1", sleepThread, NULL);
struct thread *th2 = threadCreate("th2", sleepThread, NULL);
processAddThread(proc, th1);
processAddThread(proc, th2);
processListPrint();
threadMsleep(600);
processRemoveThread(th1);
processRemoveThread(th2);
processUnref(proc);
printf("Next process list should be empty\n");
processListPrint();
printf("Running user space app\n");
struct process *proc2= processCreate("TESTPROCESS2");
threadChangeCurrentContext(processGetMMUContext(proc2));
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);
uaddr_t mem = PAGING_BASE_USER_ADDRESS;
paddr_t memPhy = allocPhyPage(1);
assert(pageMap(mem, memPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0);
unrefPhyPage(memPhy);
memcpy((void *)mem, userProgramm, PAGE_SIZE);
threadCreateUser("UserProg", proc2, mem, 0, 0, stackTop);
threadYield();
processUnref(proc2);
threadChangeCurrentContext(NULL);
printf("Running user space app DONE\n");
}
void run_test(void) void run_test(void)
{ {
uint freemem, usedmem;
uint afterFreemem, afterUsedmem;
memGetStat(&freemem, &usedmem);
testATA(); testATA();
testMemcpyPerf(); testMemcpyPerf();
{ {
@ -388,8 +483,15 @@ void run_test(void)
serialPutc('l'); serialPutc('l');
serialPutc('o'); serialPutc('o');
testAlloc(); testAlloc();
testAllocArea();
printf("Testing backtrace\n"); printf("Testing backtrace\n");
test_backtrace(); test_backtrace();
testCoroutine(); testCoroutine();
int nbThread = threadCount();
testKthread(); testKthread();
assert(nbThread + 1 == threadCount());//For sleep Thread
testMMUContext();
testProcess();
memGetStat(&afterFreemem, &afterUsedmem);
printf("free %d -> %d\n", freemem, afterFreemem);
} }