user_space #4
19
Makefile
19
Makefile
@ -12,16 +12,19 @@ QEMU_OPT += -hda disk.img
|
||||
ARCH?=x86
|
||||
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)
|
||||
gasmsrc=$(wildcard arch/$(ARCH)/*.S arch/$(ARCH)/boot/*.S)
|
||||
gasmobj=$(gasmsrc:%.S=%.o)
|
||||
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
|
||||
deps = $(csrc:%.c=%.d)
|
||||
cobj=$(csrc:%.c=%.o)
|
||||
deps=$(csrc:%.c=%.d) $(gasmsrc:%.S=%.d)
|
||||
|
||||
kernel kernel.sym &: $(asmobj) $(cobj) linker.ld
|
||||
$(CC) -m32 -ffreestanding -nostdlib $(cobj) $(asmobj) -o kernel -T linker.ld -lgcc
|
||||
kernel kernel.sym &: $(asmobj) $(gasmobj) $(cobj) linker.ld
|
||||
$(CC) -m32 -ffreestanding -nostdlib $(cobj) $(gasmobj) $(asmobj) -o kernel -T linker.ld -lgcc
|
||||
objcopy --only-keep-debug kernel kernel.sym
|
||||
objcopy --strip-debug kernel
|
||||
|
||||
@ -38,7 +41,7 @@ disk.img:
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
%.o: %.S
|
||||
$(CC) "-I$(PWD)" -c "$<" $(CFLAGS) -o "$@"
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c "$<" -o "$@"
|
||||
|
||||
|
||||
test: CFLAGS += -DRUN_TEST
|
||||
@ -60,7 +63,7 @@ debug_test: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST
|
||||
debug_test: debug
|
||||
|
||||
clean:
|
||||
$(RM) kernel $(asmobj) $(cobj) $(deps) fd.iso kernel.sym
|
||||
$(RM) kernel $(asmobj) $(gasmobj) $(cobj) $(deps) fd.iso kernel.sym
|
||||
$(RM) -r isodir
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
|
@ -31,7 +31,7 @@ stack is properly aligned and failure to align the stack will result in
|
||||
undefined behavior.
|
||||
*/
|
||||
.section .bss
|
||||
.align 16
|
||||
.align 4096
|
||||
stack_bottom:
|
||||
.skip 16384 # 16 KiB
|
||||
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.
|
||||
*/
|
||||
.size _start, . - _start
|
||||
.global _stack_bottom
|
||||
_stack_bottom: stack_bottom
|
||||
.global _stack_stop
|
||||
_stack_stop: stack_top
|
@ -28,7 +28,7 @@ align 4
|
||||
; stack is properly aligned and failure to align the stack will result in
|
||||
; undefined behavior.
|
||||
section .bss
|
||||
align 16
|
||||
align 4096
|
||||
stack_bottom:
|
||||
resb 16384 ; 16 KiB
|
||||
stack_top:
|
||||
|
@ -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
|
||||
|
||||
Initially taken from SOS
|
||||
*/
|
||||
|
||||
#include "cpu_context.h"
|
||||
#include "assert.h"
|
||||
#include "gdt.h"
|
||||
#include "klibc.h"
|
||||
#include "segment.h"
|
||||
|
||||
#include "cpu_context.h"
|
||||
|
||||
/**
|
||||
* Here is the definition of a CPU context for IA32 processors. This
|
||||
* is a Matos/SOS convention, not a specification given by the IA32
|
||||
@ -37,12 +38,11 @@ struct cpu_state {
|
||||
uint16_t alignment_padding; /* unused */
|
||||
uint32_t edi;
|
||||
uint32_t esi;
|
||||
uint32_t esp;
|
||||
uint32_t ebp;
|
||||
uint32_t ebx;
|
||||
uint32_t edx;
|
||||
uint32_t ecx;
|
||||
uint32_t eax;
|
||||
uint32_t ebp;
|
||||
|
||||
/* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
|
||||
uint32_t error_code;
|
||||
@ -76,6 +76,22 @@ struct cpu_kstate {
|
||||
struct cpu_state regs;
|
||||
} __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
|
||||
* 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,
|
||||
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
||||
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
|
||||
*/
|
||||
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)
|
||||
{
|
||||
@ -233,6 +427,27 @@ vaddr_t cpu_context_get_SP(const struct cpu_state *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)
|
||||
{
|
||||
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)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);
|
||||
}
|
||||
|
||||
|
@ -2,18 +2,35 @@
|
||||
|
||||
.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
|
||||
.type cpu_context_switch, @function
|
||||
cpu_context_switch:
|
||||
// arg2= to_context -- esp+68
|
||||
// arg1= from_context -- esp+64
|
||||
// caller ip -- esp+60
|
||||
pushf // (eflags) esp+56
|
||||
pushl %cs // (cs) esp+52
|
||||
pushl $resume_pc // (ip) esp+48
|
||||
pushl $0 // (error code) esp+12+8x4
|
||||
pushal // (general reg) esp+12
|
||||
// arg2= to_context -- esp+64
|
||||
// arg1= from_context -- esp+60
|
||||
// caller ip -- esp+56
|
||||
pushf // (eflags) esp+52
|
||||
pushl %cs // (cs) esp+48
|
||||
pushl $resume_pc // (ip) esp+44
|
||||
pushl $0 // (error code) esp+12+7x4
|
||||
pushl %ebp
|
||||
pushl %eax
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
subl $2, %esp // (alignment) esp+10
|
||||
pushw %ss // esp+8
|
||||
pushw %ds // esp+6
|
||||
@ -26,11 +43,19 @@ cpu_context_switch:
|
||||
*/
|
||||
|
||||
/* Store the address of the saved context */
|
||||
movl 64(%esp), %ebx
|
||||
movl 60(%esp), %ebx
|
||||
movl %esp, (%ebx)
|
||||
|
||||
/* 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 */
|
||||
popw %gs
|
||||
@ -39,7 +64,13 @@ cpu_context_switch:
|
||||
popw %ds
|
||||
popw %ss
|
||||
addl $2,%esp
|
||||
popal
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %eax
|
||||
popl %ebp
|
||||
addl $4, %esp /* Ignore "error code" */
|
||||
|
||||
/* This restores the eflags, the cs and the eip registers */
|
||||
@ -75,6 +106,14 @@ cpu_context_exit_to:
|
||||
call *8(%eax)
|
||||
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 */
|
||||
popw %gs
|
||||
popw %fs
|
||||
@ -82,7 +121,13 @@ cpu_context_exit_to:
|
||||
popw %ds
|
||||
popw %ss
|
||||
addl $2,%esp
|
||||
popal
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %eax
|
||||
popl %ebp
|
||||
addl $4, %esp /* Ignore "error code" */
|
||||
|
||||
/* This restores the eflags, the cs and the eip registers */
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "interrupt.h"
|
||||
#include "irq.h"
|
||||
#include "klibc.h"
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
#include "vga.h"
|
||||
|
||||
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)
|
||||
{
|
||||
// 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();
|
||||
printf("page fault while in thread %s at 0x%x 0x%x\n", current->name, faulting_address,
|
||||
cpu_context_get_PC(frame));
|
||||
struct thread *current = getCurrentThread();
|
||||
printf("page fault while in thread %s code at 0x%x when trying to access 0x%x err_code 0x%x\n", current->name,
|
||||
cpu_context_get_PC(frame), cpu_context_get_EX_faulting_vaddr(frame), cpu_context_get_EX_err(frame));
|
||||
VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr);
|
||||
(void)intr;
|
||||
for (;;)
|
||||
|
@ -1,10 +1,19 @@
|
||||
#define ASM_SOURCE 1
|
||||
#include "segment.h"
|
||||
.file "irq_wrappers.S"
|
||||
.text
|
||||
|
||||
.extern exception_handler_wrap
|
||||
.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
|
||||
|
||||
|
||||
@ -13,9 +22,9 @@
|
||||
.type exception_wrapper_\id,@function
|
||||
/* INTERRUPT FRAME START */
|
||||
/* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */
|
||||
/* uint32_t ip */
|
||||
/* uint32_t cs; */
|
||||
/* uint32_t flags */
|
||||
/* uint32_t cs; */
|
||||
/* uint32_t ip */
|
||||
/* Pushes the other reg to save same and look like a struct cpu_state*/
|
||||
/* Fake error code */
|
||||
pushl $0
|
||||
@ -24,12 +33,12 @@
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %edx
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
pushl %eax
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
subl $2,%esp
|
||||
pushw %ss
|
||||
pushw %ds
|
||||
@ -37,11 +46,31 @@
|
||||
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
|
||||
@ -49,25 +78,100 @@
|
||||
popw %ds
|
||||
popw %ss
|
||||
addl $2,%esp
|
||||
popl %eax
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %edx
|
||||
popl %esi
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %eax
|
||||
popl %ebp
|
||||
|
||||
/* Remove fake error code */
|
||||
addl $4, %esp
|
||||
|
||||
iret
|
||||
.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
|
||||
.rept 0x20
|
||||
exception_mac %i
|
||||
.set i, i+1
|
||||
/* Backup the actual 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
|
||||
|
||||
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
|
||||
|
||||
.macro ref_exception_wrapper id
|
||||
|
@ -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.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -17,7 +18,6 @@
|
||||
USA.
|
||||
*/
|
||||
#include "segment.h"
|
||||
|
||||
#include "gdt.h"
|
||||
|
||||
/**
|
||||
@ -111,6 +111,10 @@ static struct x86_segment_descriptor gdt[] = {
|
||||
},
|
||||
[SEG_KCODE] = BUILD_GDTE(0, 1),
|
||||
[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)
|
||||
@ -144,3 +148,29 @@ int gdtSetup(void)
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -17,7 +18,8 @@
|
||||
USA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "stdarg.h"
|
||||
/**
|
||||
* @file gdt.h
|
||||
*
|
||||
@ -34,3 +36,4 @@
|
||||
* address space (ie "flat" virtual space).
|
||||
*/
|
||||
int gdtSetup(void);
|
||||
int gdtRegisterTSS(vaddr_t tss_vaddr);
|
||||
|
@ -1,3 +1,5 @@
|
||||
#define ASM_SOURCE 1
|
||||
#include "segment.h"
|
||||
.file "irq_pit.S"
|
||||
|
||||
.text
|
||||
@ -7,8 +9,16 @@
|
||||
.globl pit_handler
|
||||
.type pit_handler, @function
|
||||
pit_handler: // already got eflags, cs and eip on stack thanks to CPU
|
||||
pushl $0 // err_code esp+12+8*4=44
|
||||
pushal // (general reg) esp+12
|
||||
pushl $0 // err_code esp+12+7*4=40
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
pushl %eax
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
subl $2, %esp // (alignment) esp+10
|
||||
pushw %ss // esp+8
|
||||
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 %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 */
|
||||
movb $0x20, %al
|
||||
outb %al, $0x20
|
||||
|
||||
pushl %esp
|
||||
call pitIrqHandler
|
||||
addl $4, %esp
|
||||
movl %eax,%esp
|
||||
|
||||
/* Restore the CPU context */
|
||||
@ -31,7 +49,13 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU
|
||||
popw %ds
|
||||
popw %ss
|
||||
addl $2,%esp
|
||||
popal
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %eax
|
||||
popl %ebp
|
||||
addl $4, %esp /* Ignore "error code" */
|
||||
|
||||
/* This restores the eflags, the cs and the eip registers */
|
||||
|
@ -1,9 +1,17 @@
|
||||
#define ASM_SOURCE 1
|
||||
#include "segment.h"
|
||||
.file "irq_wrappers.S"
|
||||
.text
|
||||
|
||||
.extern interrupt_handler_pic
|
||||
.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
|
||||
|
||||
@ -24,12 +32,12 @@
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
|
||||
pushl %edi
|
||||
pushl %esi
|
||||
pushl %edx
|
||||
pushl %ecx
|
||||
pushl %ebx
|
||||
pushl %eax
|
||||
pushl %ecx
|
||||
pushl %edx
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
subl $2,%esp
|
||||
pushw %ss
|
||||
pushw %ds
|
||||
@ -37,11 +45,31 @@
|
||||
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 $\irq
|
||||
call interrupt_handler_pic
|
||||
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 */
|
||||
popw %gs
|
||||
popw %fs
|
||||
@ -49,12 +77,12 @@
|
||||
popw %ds
|
||||
popw %ss
|
||||
addl $2,%esp
|
||||
popl %eax
|
||||
popl %ebx
|
||||
popl %ecx
|
||||
popl %edx
|
||||
popl %esi
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %edx
|
||||
popl %ecx
|
||||
popl %eax
|
||||
popl %ebp
|
||||
|
||||
/* Remove fake error code */
|
||||
|
170
arch/x86/mmuContext.c
Normal file
170
arch/x86/mmuContext.c
Normal 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;
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
#include "allocArea.h"
|
||||
#include "paging.h"
|
||||
#include "errno.h"
|
||||
#include "kernel.h"
|
||||
#include "klibc.h"
|
||||
#include "mem.h"
|
||||
#include "mmuContext.h"
|
||||
#include "stdarg.h"
|
||||
|
||||
// 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
|
||||
pd[PD_MIRROR_PAGE_IDX].present = 1;
|
||||
pd[PD_MIRROR_PAGE_IDX].write = 1;
|
||||
pd[PD_MIRROR_PAGE_IDX].pt_addr = ((paddr_t)pd >> PT_SHIFT);
|
||||
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].present = 1;
|
||||
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].write = 1;
|
||||
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:
|
||||
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 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
|
||||
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) {
|
||||
paddr_t ptPhy = allocPhyPage(1);
|
||||
if (ptPhy == (vaddr_t)NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pd[pdEntry].user = (flags & PAGING_MEM_USER) ? 1 : 0;
|
||||
pd[pdEntry].present = 1;
|
||||
pd[pdEntry].write = 1;
|
||||
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);
|
||||
memset((void *)pt, 0, PAGE_SIZE);
|
||||
} else {
|
||||
}
|
||||
{
|
||||
|
||||
// Already mapped ? Remove old mapping
|
||||
if (pt[ptEntry].present) {
|
||||
@ -175,11 +190,14 @@ int pageUnmap(vaddr_t vaddr)
|
||||
uint pdEntry = vaddr >> (PD_SHIFT);
|
||||
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
|
||||
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)
|
||||
return -EINVAL;
|
||||
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 ?
|
||||
if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 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);
|
||||
@ -198,7 +219,133 @@ int pageUnmap(vaddr_t vaddr)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,20 @@
|
||||
#pragma once
|
||||
#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_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 pageUnmap(vaddr_t vaddr);
|
||||
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
18
arch/x86/swintr.c
Normal 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
6
arch/x86/swintr.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define SYSCALL_INTR_NB 0x42
|
||||
|
||||
|
||||
int syscallSetup();
|
91
arch/x86/syscall_wrappers.S
Normal file
91
arch/x86/syscall_wrappers.S
Normal 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
|
90
core/alloc.c
90
core/alloc.c
@ -17,7 +17,7 @@ static struct slabDesc *slub;
|
||||
static int allocInitialized = FALSE;
|
||||
|
||||
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,
|
||||
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 slabSize;
|
||||
unsigned char isSelf;
|
||||
} initSlab[] = {{4, PAGE_SIZE, 0},
|
||||
{8, PAGE_SIZE, 0},
|
||||
{16, PAGE_SIZE, 0},
|
||||
{32, PAGE_SIZE, 0},
|
||||
{64, PAGE_SIZE, 0},
|
||||
{128, PAGE_SIZE, 0},
|
||||
{256, 2 * PAGE_SIZE, 0},
|
||||
{1024, 2 * PAGE_SIZE, 0},
|
||||
{2048, 3 * PAGE_SIZE, 0},
|
||||
{4096, 4 * PAGE_SIZE, 0},
|
||||
{0, 0, 0}};
|
||||
unsigned char neverEmpty;
|
||||
} initSlab[] = {{4, PAGE_SIZE, 0, 0},
|
||||
{8, PAGE_SIZE, 0, 0},
|
||||
{16, PAGE_SIZE, 0, 0},
|
||||
{32, PAGE_SIZE, 0, 0},
|
||||
{64, PAGE_SIZE, 0, 0},
|
||||
{128, PAGE_SIZE, 0, 0},
|
||||
{256, 2 * PAGE_SIZE, 0, 0},
|
||||
{1024, 2 * PAGE_SIZE, 0, 0},
|
||||
{2048, 3 * PAGE_SIZE, 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)
|
||||
{
|
||||
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));
|
||||
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));
|
||||
assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE) == 0);
|
||||
assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE, TRUE) == 0);
|
||||
*areaAddr = (vaddr_t)allocGetSlab(sizeOfArea);
|
||||
allocInitialized = TRUE;
|
||||
|
||||
@ -61,7 +62,7 @@ int allocPopulate()
|
||||
int ret;
|
||||
|
||||
if ((ret = allocBookSlab(initSlab[i].elementSize, initSlab[i].slabSize,
|
||||
initSlab[i].isSelf))) {
|
||||
initSlab[i].isSelf, initSlab[i].neverEmpty))) {
|
||||
if (ret == -EEXIST)
|
||||
continue;
|
||||
pr_err("Fail to allocBookSlab %d for %d \n", ret, (1U << i));
|
||||
@ -73,7 +74,7 @@ int allocPopulate()
|
||||
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 *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");
|
||||
restore_IRQs(flags);
|
||||
return ret;
|
||||
@ -112,7 +113,8 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained)
|
||||
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;
|
||||
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);
|
||||
if (allocInitialized) {
|
||||
alloc = areaAlloc(nbPage);
|
||||
alloc = areaAlloc(nbPage, AREA_PHY_MAP);
|
||||
if (alloc == (paddr_t)NULL)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
@ -156,6 +158,7 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s
|
||||
slab->page = (vaddr_t)alloc;
|
||||
slab->full = 0;
|
||||
slab->size = sizeSlab;
|
||||
(*desc)->neverEmpty = neverEmpty;
|
||||
(*desc)->size = size;
|
||||
|
||||
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);
|
||||
vaddr_t alloc = areaAlloc(nbPage);
|
||||
vaddr_t alloc = areaAlloc(nbPage, AREA_PHY_MAP);
|
||||
if (alloc == (paddr_t)NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -202,8 +205,6 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab,
|
||||
(*desc)->size = sizeSlab;
|
||||
|
||||
return formatPage((*desc), size, sizeSlab, selfContained);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (size >= PAGE_SIZE){
|
||||
vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE));
|
||||
if (size >= PAGE_SIZE) {
|
||||
vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE), AREA_PHY_MAP);
|
||||
|
||||
return (void *)area;
|
||||
}
|
||||
@ -271,12 +272,36 @@ void *malloc(size_t size)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct slabEntry *slabList = &slab->slab;
|
||||
list_foreach(&slab->slab, slabEntry, slabIdx)
|
||||
{
|
||||
if (!slabEntry->full) {
|
||||
// pr_devel("found place in slub %d at idx %d for size %d\n", slubIdx,
|
||||
// slabIdx, size);
|
||||
// pr_devel("found place in slab idx %d for size %d\n", slabIdx, slab->size);
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
@ -284,7 +309,6 @@ void *malloc(size_t size)
|
||||
|
||||
// No room found
|
||||
struct slabEntry *newSlabEntry;
|
||||
struct slabEntry *slabList = &slab->slab;
|
||||
size_t slabSize = MAX(PAGE_SIZE, size);
|
||||
int retSlab;
|
||||
|
||||
@ -304,7 +328,8 @@ void *malloc(size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *zalloc(size_t size){
|
||||
void *zalloc(size_t size)
|
||||
{
|
||||
void *alloc = malloc(size);
|
||||
|
||||
if (alloc != NULL)
|
||||
@ -333,7 +358,8 @@ static int freeFromSlab(void *ptr, struct slabEntry *slab)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int freeSlabAllocated(void *ptr){
|
||||
int freeSlabAllocated(void *ptr)
|
||||
{
|
||||
struct slabDesc *slab;
|
||||
int slabIdx;
|
||||
int flags;
|
||||
@ -361,8 +387,8 @@ void free(void *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
if(!freeSlabAllocated(ptr))
|
||||
if (!freeSlabAllocated(ptr))
|
||||
return;
|
||||
if(areaFree((vaddr_t)ptr))
|
||||
if (areaFree((vaddr_t)ptr))
|
||||
pr_err("free: cannot found origin\n");
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ int allocPopulate();
|
||||
* Allow malloc to allocate elements of this precise size.
|
||||
* 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 *zalloc(size_t size);
|
||||
@ -22,9 +22,9 @@ void free(void *ptr);
|
||||
*/
|
||||
struct slabEntry {
|
||||
vaddr_t page;
|
||||
size_t size;
|
||||
void *freeEl;
|
||||
char full;
|
||||
size_t size;
|
||||
bool_t full;//TODO replace by freeEl == NULL
|
||||
struct slabEntry *next;
|
||||
struct slabEntry *prev;
|
||||
};
|
||||
@ -32,6 +32,7 @@ struct slabEntry {
|
||||
struct slabDesc {
|
||||
struct slabEntry slab;
|
||||
size_t size;
|
||||
bool_t neverEmpty;
|
||||
struct slabDesc *next;
|
||||
struct slabDesc *prev;
|
||||
};
|
||||
|
131
core/allocArea.c
131
core/allocArea.c
@ -1,33 +1,43 @@
|
||||
#include "allocArea.h"
|
||||
#include "alloc.h"
|
||||
#include "assert.h"
|
||||
#include "irq.h"
|
||||
#include "kernel.h"
|
||||
#include "list.h"
|
||||
#include "mem.h"
|
||||
#include "stdarg.h"
|
||||
|
||||
|
||||
static struct memArea *freeArea;
|
||||
static struct memArea *usedArea;
|
||||
|
||||
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(usedArea);
|
||||
|
||||
vaddr_t areaAddr, descAddr, entryAddr;
|
||||
allocSetup(sizeof(struct memArea), &areaAddr, &descAddr, &entryAddr);
|
||||
areaAdd(descAddr, PAGE_SIZE, FALSE);
|
||||
areaAdd(descAddr, descAddr + PAGE_SIZE, FALSE);
|
||||
if (entryAddr != descAddr)
|
||||
areaAdd(entryAddr, PAGE_SIZE, FALSE);
|
||||
areaAdd(entryAddr, entryAddr + PAGE_SIZE, FALSE);
|
||||
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);
|
||||
areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), nbPages, FALSE);
|
||||
areaAdd(ALIGN_DOWN(areaAddr + PAGE_SIZE, PAGE_SIZE), 300 , TRUE);
|
||||
// kernel bootstrap part
|
||||
areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), stackBottom, FALSE);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
@ -62,23 +72,81 @@ static void insertSorted(struct memArea **list, struct memArea *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);
|
||||
if (!area)
|
||||
if (!area) {
|
||||
printf("NULL<\n");
|
||||
restore_IRQs(irqFlags);
|
||||
|
||||
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) {
|
||||
list_delete(freeArea, area);
|
||||
insertSorted(&usedArea, area);
|
||||
allocated = area;
|
||||
}
|
||||
|
||||
} else {
|
||||
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
|
||||
if (!newArea){
|
||||
if (!newArea) {
|
||||
pr_devel("Failed to allocated area of %d pages\n", nbPages);
|
||||
restore_IRQs(irqFlags);
|
||||
return (vaddr_t)NULL;
|
||||
}
|
||||
|
||||
@ -93,30 +161,48 @@ vaddr_t areaAlloc(unsigned int nbPages)
|
||||
insertSorted(&usedArea, newArea);
|
||||
|
||||
allocated = newArea;
|
||||
}
|
||||
|
||||
if (flags & AREA_PHY_MAP) {
|
||||
for (uint i = 0; i < nbPages; i++) {
|
||||
paddr_t page = allocPhyPage(1);
|
||||
if (page) {
|
||||
pageMap(newArea->startAddr + i * PAGE_SIZE, page, PAGING_MEM_WRITE);
|
||||
if (pageMap(allocated->startAddr + i * PAGE_SIZE, page, PAGING_MEM_WRITE)) {
|
||||
page = (paddr_t)NULL;
|
||||
} else {
|
||||
// TODO
|
||||
assert(1);
|
||||
unrefPhyPage(page);
|
||||
}
|
||||
}
|
||||
if (page == (paddr_t)NULL) {
|
||||
areaFree(allocated->startAddr);
|
||||
restore_IRQs(irqFlags);
|
||||
|
||||
return (vaddr_t)NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restore_IRQs(irqFlags);
|
||||
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;
|
||||
|
||||
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));
|
||||
if (!newArea)
|
||||
return (vaddr_t)NULL;
|
||||
|
||||
newArea->nbPages = nbPages;
|
||||
newArea->startAddr = addr;
|
||||
newArea->startAddr = start;
|
||||
|
||||
if (isFree) {
|
||||
area = &freeArea;
|
||||
@ -158,7 +244,7 @@ static struct memArea *areaFindMemArea(struct memArea *list, vaddr_t addr)
|
||||
int 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;
|
||||
}
|
||||
|
||||
@ -174,6 +260,11 @@ int areaFree(vaddr_t addr)
|
||||
pr_info("Cannot find memArea associated to %p\n", addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < area->nbPages; i++) {
|
||||
pageUnmap(area->startAddr + i * PAGE_SIZE);
|
||||
}
|
||||
|
||||
list_delete(usedArea, area);
|
||||
insertSorted(&freeArea, area);
|
||||
|
||||
|
@ -2,6 +2,12 @@
|
||||
#include "paging.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 {
|
||||
vaddr_t startAddr;
|
||||
uint nbPages;
|
||||
@ -10,7 +16,11 @@ struct memArea {
|
||||
struct memArea *prev;
|
||||
};
|
||||
|
||||
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed);
|
||||
vaddr_t areaAlloc(unsigned int nbPages);
|
||||
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stack_bottom, vaddr_t stack_top);
|
||||
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 areaAdd(vaddr_t addr, uint nbPages, int isFree);
|
||||
int areaAdd(vaddr_t begin, vaddr_t end, int isFree);
|
||||
|
@ -6,7 +6,7 @@
|
||||
do { \
|
||||
if (!(p)) { \
|
||||
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
|
||||
printStackTrace(5); \
|
||||
printStackTrace(3); \
|
||||
while (1) { \
|
||||
} \
|
||||
} \
|
||||
@ -17,7 +17,7 @@
|
||||
if (!(p)) { \
|
||||
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
|
||||
printf(__VA_ARGS__); \
|
||||
printStackTrace(5); \
|
||||
printStackTrace(3); \
|
||||
while (1) { \
|
||||
} \
|
||||
} \
|
||||
|
@ -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
|
||||
|
||||
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,
|
||||
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
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -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
|
||||
* 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
|
||||
@ -181,7 +195,23 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
||||
vaddr_t kernel_stack_bottom,
|
||||
size_t kernel_stack_size);
|
||||
#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 */})
|
||||
#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);
|
||||
|
260
core/kthread.c
260
core/kthread.c
@ -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(¤t->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(¤t->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(¤t->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;
|
||||
}
|
@ -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);
|
22
core/main.c
22
core/main.c
@ -9,14 +9,17 @@
|
||||
#include "irq.h"
|
||||
#include "keyboard.h"
|
||||
#include "klibc.h"
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
#include "mem.h"
|
||||
#include "mmuContext.h"
|
||||
#include "multiboot.h"
|
||||
#include "paging.h"
|
||||
#include "pit.h"
|
||||
#include "process.h"
|
||||
#include "serial.h"
|
||||
#include "stack.h"
|
||||
#include "stdarg.h"
|
||||
#include "swintr.h"
|
||||
#ifdef RUN_TEST
|
||||
#include "test.h"
|
||||
#endif
|
||||
@ -31,7 +34,7 @@ void idleThread(void *arg)
|
||||
(void)arg;
|
||||
while (1) {
|
||||
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 !
|
||||
// https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/
|
||||
if (mmap[i].addr < 0x100000) {
|
||||
printf(" -> skipping\n");
|
||||
continue;
|
||||
}
|
||||
memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem),
|
||||
@ -160,12 +164,16 @@ void kmain(unsigned long magic, unsigned long addr)
|
||||
serialSetup(115200);
|
||||
|
||||
printf("[Setup] allocation system\n");
|
||||
areaInit(firstUsedByMem, lastUsedByMem);
|
||||
//allocSetup();
|
||||
areaInit(firstUsedByMem, lastUsedByMem, _stack_bottom, _stack_top);
|
||||
|
||||
mmuContextSetup();
|
||||
cpu_context_subsystem_setup();
|
||||
|
||||
printf("[Setup] thread system\n");
|
||||
kthreadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1));
|
||||
kthreadCreate("idle ", idleThread, NULL);
|
||||
threadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1));
|
||||
threadCreate("idle ", idleThread, NULL);
|
||||
processSetup();
|
||||
syscallSetup();
|
||||
|
||||
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
|
||||
// So finish this by ourself
|
||||
kthreadExit();
|
||||
threadExit();
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#include "mem.h"
|
||||
#include "assert.h"
|
||||
#include "errno.h"
|
||||
#include "kernel.h"
|
||||
#include "klibc.h"
|
||||
#include "list.h"
|
||||
#include "mem.h"
|
||||
#include "types.h"
|
||||
|
||||
static struct phyMemDesc *pageDesc = (struct phyMemDesc *)&__ld_kernel_end;
|
||||
@ -132,6 +133,7 @@ paddr_t allocPhyPage(uint nbPage)
|
||||
next = mem->next;
|
||||
}
|
||||
allocatedPage += nbPage;
|
||||
|
||||
return head->phy_addr;
|
||||
}
|
||||
|
||||
@ -139,9 +141,10 @@ int unrefPhyPage(paddr_t addr)
|
||||
{
|
||||
struct phyMemDesc *mem = addr2memDesc(addr);
|
||||
if (!mem) {
|
||||
return -1;
|
||||
return -EINVAL;
|
||||
}
|
||||
assert(mem->ref > 0);
|
||||
if(mem->ref <= 0)
|
||||
return -EINVAL;
|
||||
mem->ref--;
|
||||
if (mem->ref == 0) {
|
||||
allocatedPage--;
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#define PAGE_SHIFT 12U
|
||||
#define PAGE_SIZE (1U << PAGE_SHIFT)
|
||||
#define PAGE_MASK (PAGE_SIZE - 1)
|
||||
|
||||
|
||||
// Defined in linker.ld script
|
||||
extern uint32_t __ld_kernel_begin;
|
||||
|
12
core/mmuContext.h
Normal file
12
core/mmuContext.h
Normal 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
168
core/process.c
Normal 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
17
core/process.h
Normal 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);
|
@ -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.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -29,8 +30,6 @@
|
||||
* @see Intel x86 doc, vol 3 chapter 3.
|
||||
*/
|
||||
|
||||
#include "stdarg.h"
|
||||
|
||||
/*
|
||||
* Global segment selectors (GDT) for SOS/x86.
|
||||
*
|
||||
@ -39,13 +38,23 @@
|
||||
#define SEG_NULL 0 /* NULL segment, unused by the procesor */
|
||||
#define SEG_KCODE 1 /* Kernel code 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
|
||||
*/
|
||||
#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) \
|
||||
((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | ((seg_index) << 3))
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Local segment selectors (LDT) for SOS/x86
|
||||
*/
|
||||
|
@ -51,7 +51,7 @@ int mutexFree(struct mutex *m)
|
||||
int mutexLock(struct mutex *m)
|
||||
{
|
||||
uint32_t flags;
|
||||
struct kthread *current;
|
||||
struct thread *current;
|
||||
|
||||
disable_IRQs(flags);
|
||||
current = getCurrentThread();
|
||||
|
27
core/syscall.c
Normal file
27
core/syscall.c
Normal 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
9
core/syscall.h
Normal 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
391
core/thread.c
Normal 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(¤t->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(¤t->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(¤t->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
90
core/thread.h
Normal 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();
|
@ -33,3 +33,6 @@ typedef unsigned long vaddr_t;
|
||||
|
||||
// Physical address
|
||||
typedef unsigned long paddr_t;
|
||||
|
||||
// Userspace vaddr
|
||||
typedef unsigned long uaddr_t;
|
||||
|
14
core/wait.c
14
core/wait.c
@ -1,5 +1,5 @@
|
||||
#include "irq.h"
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
#include "list.h"
|
||||
#include "wait.h"
|
||||
|
||||
@ -25,13 +25,13 @@ int waitQueueFree(struct wait_queue *wq)
|
||||
|
||||
int wakeUp(struct wait_queue *wq)
|
||||
{
|
||||
struct kthread *th;
|
||||
struct thread *th;
|
||||
uint32_t flags;
|
||||
|
||||
disable_IRQs(flags);
|
||||
list_collapse(wq->thread, th)
|
||||
{
|
||||
kthreadAddThread(th);
|
||||
threadAddThread(th);
|
||||
}
|
||||
|
||||
restore_IRQs(flags);
|
||||
@ -46,7 +46,7 @@ int wait(struct wait_queue *wq)
|
||||
|
||||
int waitTimeout(struct wait_queue *wq, unsigned long msec)
|
||||
{
|
||||
struct kthread *current, *next;
|
||||
struct thread *current, *next;
|
||||
uint32_t flags;
|
||||
int ret;
|
||||
|
||||
@ -54,11 +54,11 @@ int waitTimeout(struct wait_queue *wq, unsigned long msec)
|
||||
|
||||
current = getCurrentThread();
|
||||
current->state = WAITING;
|
||||
next = kthreadSelectNext();
|
||||
kthreadUnsched(current);
|
||||
next = threadSelectNext();
|
||||
threadUnsched(current);
|
||||
|
||||
list_add_tail(wq->thread, current);
|
||||
ret = kthreadWait(current, next, msec);
|
||||
ret = threadWait(current, next, msec);
|
||||
|
||||
restore_IRQs(flags);
|
||||
return ret;
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
|
||||
struct wait_queue {
|
||||
struct kthread *thread;
|
||||
struct thread *thread;
|
||||
struct wait_queue *next;
|
||||
struct wait_queue *prev;
|
||||
};
|
||||
@ -35,6 +35,6 @@ struct semaphore {
|
||||
};
|
||||
|
||||
struct mutex {
|
||||
struct kthread *owner;
|
||||
struct thread *owner;
|
||||
struct wait_queue *wait;
|
||||
};
|
||||
|
@ -39,32 +39,32 @@ class CustomPrettyPrinterLocator(PrettyPrinter):
|
||||
if typename is None:
|
||||
typename = val.type.name
|
||||
|
||||
if typename == "kthread":
|
||||
if typename == "thread":
|
||||
return KthreadPrettyPrinter(val)
|
||||
|
||||
|
||||
class KthreadListDumpCmd(gdb.Command):
|
||||
"""Prints the kthread list"""
|
||||
class threadListDumpCmd(gdb.Command):
|
||||
"""Prints the thread list"""
|
||||
|
||||
def __init__(self):
|
||||
super(KthreadListDumpCmd, self).__init__(
|
||||
"kthread_list_dump", gdb.COMMAND_USER
|
||||
super(threadListDumpCmd, self).__init__(
|
||||
"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.
|
||||
|
||||
We will simply follow the 'next' pointers until we encounter the HEAD again
|
||||
"""
|
||||
idx = 0
|
||||
head = val
|
||||
kthread_ptr = val
|
||||
thread_ptr = val
|
||||
result = ""
|
||||
while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head):
|
||||
result += "\n%d: %s" % (idx, KthreadPrettyPrinter(kthread_ptr).to_string())
|
||||
kthread_ptr = kthread_ptr["next"]
|
||||
while thread_ptr != 0 and (idx == 0 or thread_ptr != head):
|
||||
result += "\n%d: %s" % (idx, KthreadPrettyPrinter(thread_ptr).to_string())
|
||||
thread_ptr = thread_ptr["next"]
|
||||
idx += 1
|
||||
result = ("Found a Linked List with %d kthread:" % idx) + result
|
||||
result = ("Found a Linked List with %d thread:" % idx) + result
|
||||
return result
|
||||
|
||||
def complete(self, text, word):
|
||||
@ -77,14 +77,14 @@ class KthreadListDumpCmd(gdb.Command):
|
||||
# to do argument parsing
|
||||
print("Args Passed: %s" % args)
|
||||
if args:
|
||||
kthread_ptr_val = gdb.parse_and_eval(args)
|
||||
thread_ptr_val = gdb.parse_and_eval(args)
|
||||
else:
|
||||
kthread_ptr_val = gdb.parse_and_eval("currentThread")
|
||||
if str(kthread_ptr_val.type) != "struct kthread *":
|
||||
print("Expected pointer argument of type (struct kthread *)")
|
||||
thread_ptr_val = gdb.parse_and_eval("currentThread")
|
||||
if str(thread_ptr_val.type) != "struct thread *":
|
||||
print("Expected pointer argument of type (struct thread *)")
|
||||
return
|
||||
|
||||
print(self._kthread_list_to_str(kthread_ptr_val))
|
||||
print(self._thread_list_to_str(thread_ptr_val))
|
||||
|
||||
|
||||
class PhyMemDescListDumpCmd(gdb.Command):
|
||||
@ -175,6 +175,7 @@ class PrintStructC99Cmd(gdb.Command):
|
||||
rr_rval = rr_s[1].strip()
|
||||
print(' ' * hs + '.' + rr_s[0] + '= ' + rr_rval)
|
||||
|
||||
|
||||
class ListDumpCmd(gdb.Command):
|
||||
"""Prints a linked list"""
|
||||
|
||||
@ -190,13 +191,13 @@ class ListDumpCmd(gdb.Command):
|
||||
"""
|
||||
idx = 0
|
||||
head = val
|
||||
kthread_ptr = val
|
||||
thread_ptr = val
|
||||
result = ""
|
||||
while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head):
|
||||
result += gdb.execute('p *({}){}'.format(str(kthread_ptr.type),kthread_ptr), to_string=True)
|
||||
kthread_ptr = kthread_ptr["next"]
|
||||
while thread_ptr != 0 and (idx == 0 or thread_ptr != head):
|
||||
result += gdb.execute('p *({}){}'.format(str(thread_ptr.type),thread_ptr), to_string=True)
|
||||
thread_ptr = thread_ptr["next"]
|
||||
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
|
||||
|
||||
def complete(self, text, word):
|
||||
@ -220,8 +221,9 @@ class ListDumpCmd(gdb.Command):
|
||||
|
||||
print(self._print_list(ptr_val))
|
||||
|
||||
|
||||
register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True)
|
||||
KthreadListDumpCmd()
|
||||
threadListDumpCmd()
|
||||
PhyMemDescListDumpCmd()
|
||||
PrintStructC99Cmd()
|
||||
ListDumpCmd()
|
||||
|
@ -1,3 +1,5 @@
|
||||
add-symbol-file kernel.sym
|
||||
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
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "io.h"
|
||||
#include "kernel.h"
|
||||
#include "klibc.h"
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
|
||||
// from
|
||||
// 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)
|
||||
{
|
||||
struct ata_controller *ctl = dev->ctl;
|
||||
int ret = 0;
|
||||
|
||||
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");
|
||||
return -1;
|
||||
}
|
||||
while (1) {
|
||||
int retry = 100;
|
||||
while (retry) {
|
||||
int status = inb(ctl->base + ATA_PIO_STATUS);
|
||||
if (status & ATA_PIO_STATUS_REG_ERR) {
|
||||
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) &&
|
||||
!(status & ATA_PIO_STATUS_DRIVE_BUSY))
|
||||
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++) {
|
||||
outw(ctl->base + ATA_PIO_DATA, *ptr++);
|
||||
}
|
||||
retry = 1000;
|
||||
// Wait for the device to receive the data
|
||||
while (1) {
|
||||
while (retry) {
|
||||
int status = inb(ctl->base + ATA_PIO_STATUS);
|
||||
if (status & ATA_PIO_STATUS_REG_ERR) {
|
||||
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) &&
|
||||
!(status & ATA_PIO_STATUS_DRIVE_BUSY))
|
||||
break;
|
||||
retry --;
|
||||
}
|
||||
if(retry == 0){
|
||||
printf("ATA write data timeout error\n");
|
||||
ret=-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutexUnlock(&ctl->mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ata_device *ATAGetDevice(int ctlId, int devId)
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "io.h"
|
||||
#include "irq.h"
|
||||
#include "klibc.h"
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
#include "time.h"
|
||||
|
||||
int pitSetup(unsigned int freq)
|
||||
@ -20,8 +20,8 @@ int pitSetup(unsigned int freq)
|
||||
struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu)
|
||||
{
|
||||
__atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED);
|
||||
kthreadOnJieffiesTick();
|
||||
threadOnJieffiesTick();
|
||||
// Uncomment for non-preemptible kernel
|
||||
// return prevCpu;
|
||||
return kthreadSwitch(prevCpu);
|
||||
return threadSwitch(prevCpu);
|
||||
}
|
||||
|
160
tests/test.c
160
tests/test.c
@ -1,15 +1,21 @@
|
||||
#include "alloc.h"
|
||||
#include "allocArea.h"
|
||||
#include "ata.h"
|
||||
#include "assert.h"
|
||||
#include "cpu_context.h"
|
||||
#include "kernel.h"
|
||||
#include "klibc.h"
|
||||
#include "kthread.h"
|
||||
#include "thread.h"
|
||||
#include "list.h"
|
||||
#include "mem.h"
|
||||
#include "mmuContext.h"
|
||||
#include "paging.h"
|
||||
#include "process.h"
|
||||
#include "serial.h"
|
||||
#include "stack.h"
|
||||
#include "synchro.h"
|
||||
#include "syscall.h"
|
||||
#include "swintr.h"
|
||||
#include "time.h"
|
||||
|
||||
void testMemcpyPerf()
|
||||
@ -84,14 +90,22 @@ static void *testAllocNSet(size_t size)
|
||||
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);
|
||||
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++) {
|
||||
malloc(sizeof(struct slabDesc));
|
||||
assert(malloc(sizeof(struct slabDesc)) != NULL);
|
||||
}
|
||||
assert(malloc(1));
|
||||
assert(malloc(2));
|
||||
@ -103,6 +117,8 @@ static void testAlloc(void)
|
||||
free(malloc2);
|
||||
void *malloc3 = malloc(sizeof(void *));
|
||||
assertmsg((char *)malloc2 == (char *)malloc3, " %d %d\n", malloc2, malloc3);
|
||||
free(malloc1);
|
||||
free(malloc3);
|
||||
void *alloc1 = testAllocNSet(1024);
|
||||
void *alloc2 = testAllocNSet(1024);
|
||||
void *alloc3 = testAllocNSet(1024);
|
||||
@ -133,7 +149,7 @@ static void testAlloc(void)
|
||||
free(alloc16);
|
||||
}
|
||||
|
||||
static void testPaging(void)
|
||||
void testPaging(void)
|
||||
{
|
||||
printf("Testing paging\n");
|
||||
struct phyMemDesc *allocated_page_list;
|
||||
@ -143,9 +159,7 @@ static void testPaging(void)
|
||||
int allocCount = 0;
|
||||
int freeCount = 0;
|
||||
|
||||
while ((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL) {
|
||||
assertmsg(pageMap((vaddr_t)page, (paddr_t)page, PAGING_MEM_WRITE) == 0,
|
||||
"Fail to map page %d\n", allocCount);
|
||||
while ((page = (struct phyMemDesc *)areaAlloc(1, AREA_PHY_MAP)) != NULL) {
|
||||
memset(page, allocCount, PAGE_SIZE);
|
||||
allocCount++;
|
||||
list_add_tail(allocated_page_list, page);
|
||||
@ -156,12 +170,12 @@ static void testPaging(void)
|
||||
(page = list_pop_head(allocated_page_list)) != NULL) {
|
||||
assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %d\n",
|
||||
freeCount, page->phy_addr);
|
||||
assertmsg(unrefPhyPage((ulong)page) >= 0, "Failed to free page %d\n", (ulong)page);
|
||||
pageUnmap((vaddr_t)page);
|
||||
areaFree((vaddr_t)page);
|
||||
freeCount++;
|
||||
}
|
||||
printf("%d pages freed\n", freeCount);
|
||||
|
||||
assert(freeCount == allocCount);
|
||||
assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL,
|
||||
"Cannot allocate memory\n");
|
||||
unrefPhyPage((ulong)page);
|
||||
@ -202,6 +216,7 @@ static void reclaim_stack(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,
|
||||
(vaddr_t)stack_vaddr);
|
||||
}
|
||||
@ -264,7 +279,7 @@ static void kthread1(void *strIn)
|
||||
char *str = (char *)strIn;
|
||||
for (; *str != '\n'; str++) {
|
||||
printf("kth1: %c\n", *str);
|
||||
kthreadYield();
|
||||
threadYield();
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +288,7 @@ static void kthread2(void *strIn)
|
||||
char *str = (char *)strIn;
|
||||
for (; *str != '\n'; str++) {
|
||||
printf("kth2: %c\n", *str);
|
||||
kthreadYield();
|
||||
threadYield();
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,11 +301,11 @@ void sleepThread(void *arg)
|
||||
while (secSleep < 5) {
|
||||
// printf("Sleeping loop %d\n", secSleep);
|
||||
secSleep++;
|
||||
kthreadMsleep(1000);
|
||||
threadMsleep(100);
|
||||
}
|
||||
unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies);
|
||||
assertmsg(ellapsedTime >= 5000 && ellapsedTime < 5100, "ellapsedTime %d\n", ellapsedTime);
|
||||
kthreadMsleep(0);
|
||||
assertmsg(ellapsedTime >= 500 && ellapsedTime < 510, "ellapsedTime %d\n", ellapsedTime);
|
||||
threadMsleep(0);
|
||||
printf("I should never be showed\n");
|
||||
assert(1);
|
||||
}
|
||||
@ -305,7 +320,7 @@ void mutThread(void *arg)
|
||||
while (test > 0) {
|
||||
mutexLock(&mutexTest);
|
||||
printf("%s sleep\n", (char *)arg);
|
||||
kthreadMsleep(1000);
|
||||
threadMsleep(100);
|
||||
printf("%s up\n", (char *)arg);
|
||||
mutexUnlock(&mutexTest);
|
||||
test--;
|
||||
@ -317,7 +332,7 @@ void wqThread(void *arg)
|
||||
(void)arg;
|
||||
DECLARE_WAITQUEUE(test);
|
||||
waitQueueInit(&test);
|
||||
assert(waitTimeout(&test, 1000) == 1);
|
||||
assert(waitTimeout(&test, 100) == 1);
|
||||
waitQueueFree(&test);
|
||||
haveTimeout = 1;
|
||||
}
|
||||
@ -326,17 +341,18 @@ void testKthread()
|
||||
{
|
||||
mutexInit(&mutexTest);
|
||||
// It is not expected to have necessarily "Hello world\n" properly written
|
||||
kthreadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n");
|
||||
kthreadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n");
|
||||
kthreadMsleep(1000);
|
||||
kthreadCreate("wq timeout", wqThread, NULL);
|
||||
kthreadMsleep(2000);
|
||||
threadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n");
|
||||
threadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n");
|
||||
threadMsleep(100);
|
||||
threadCreate("wq timeout", wqThread, NULL);
|
||||
threadMsleep(200);
|
||||
assert(haveTimeout);
|
||||
kthreadCreate("sleep", sleepThread, NULL);
|
||||
kthreadMsleep(5000);
|
||||
kthreadCreate("mtest1", mutThread, "mut1");
|
||||
kthreadCreate("mtest2", mutThread, "mut2");
|
||||
kthreadCreate("mtest3", mutThread, "mut3");
|
||||
threadCreate("sleep", sleepThread, NULL);
|
||||
threadMsleep(500);
|
||||
threadCreate("mtest1", mutThread, "mut1");
|
||||
threadCreate("mtest2", mutThread, "mut2");
|
||||
threadCreate("mtest3", mutThread, "mut3");
|
||||
threadMsleep(2000);
|
||||
}
|
||||
|
||||
void testATAThread(){
|
||||
@ -354,13 +370,92 @@ void testATAThread(){
|
||||
}
|
||||
}
|
||||
|
||||
void testATA(){
|
||||
kthreadCreate("ATA_TEST", testATAThread, NULL);
|
||||
static void testATA(){
|
||||
threadCreate("ATA_TEST", testATAThread, NULL);
|
||||
//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)
|
||||
{
|
||||
|
||||
uint freemem, usedmem;
|
||||
uint afterFreemem, afterUsedmem;
|
||||
memGetStat(&freemem, &usedmem);
|
||||
testATA();
|
||||
testMemcpyPerf();
|
||||
{
|
||||
@ -388,8 +483,15 @@ void run_test(void)
|
||||
serialPutc('l');
|
||||
serialPutc('o');
|
||||
testAlloc();
|
||||
testAllocArea();
|
||||
printf("Testing backtrace\n");
|
||||
test_backtrace();
|
||||
testCoroutine();
|
||||
int nbThread = threadCount();
|
||||
testKthread();
|
||||
assert(nbThread + 1 == threadCount());//For sleep Thread
|
||||
testMMUContext();
|
||||
testProcess();
|
||||
memGetStat(&afterFreemem, &afterUsedmem);
|
||||
printf("free %d -> %d\n", freemem, afterFreemem);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user