Merge pull request 'user_space' (#4) from user_space into master
Reviewed-on: http://git.mathux.org/mathieu/matos/pulls/4
This commit is contained in:
commit
f81a515514
19
Makefile
19
Makefile
@ -12,16 +12,19 @@ QEMU_OPT += -hda disk.img
|
|||||||
ARCH?=x86
|
ARCH?=x86
|
||||||
SUBDIRS := core drivers tests arch/$(ARCH)
|
SUBDIRS := core drivers tests arch/$(ARCH)
|
||||||
|
|
||||||
CPPFLAGS += $(foreach dir, $(SUBDIRS), -I$(dir))
|
INCDIRS += $(foreach dir, $(SUBDIRS), -I$(dir))
|
||||||
|
CPPFLAGS += $(INCDIRS)
|
||||||
|
|
||||||
asmsrc=$(wildcard arch/$(ARCH)/boot/*.asm)
|
asmsrc=
|
||||||
asmobj=$(asmsrc:%.asm=%.o)
|
asmobj=$(asmsrc:%.asm=%.o)
|
||||||
|
gasmsrc=$(wildcard arch/$(ARCH)/*.S arch/$(ARCH)/boot/*.S)
|
||||||
|
gasmobj=$(gasmsrc:%.S=%.o)
|
||||||
csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c)
|
csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c)
|
||||||
cobj=$(csrc:%.c=%.o) arch/$(ARCH)/cpu_context_switch.o arch/$(ARCH)/irq_pit.o arch/$(ARCH)/irq_wrappers.o arch/$(ARCH)/exception_wrappers.o
|
cobj=$(csrc:%.c=%.o)
|
||||||
deps = $(csrc:%.c=%.d)
|
deps=$(csrc:%.c=%.d) $(gasmsrc:%.S=%.d)
|
||||||
|
|
||||||
kernel kernel.sym &: $(asmobj) $(cobj) linker.ld
|
kernel kernel.sym &: $(asmobj) $(gasmobj) $(cobj) linker.ld
|
||||||
$(CC) -m32 -ffreestanding -nostdlib $(cobj) $(asmobj) -o kernel -T linker.ld -lgcc
|
$(CC) -m32 -ffreestanding -nostdlib $(cobj) $(gasmobj) $(asmobj) -o kernel -T linker.ld -lgcc
|
||||||
objcopy --only-keep-debug kernel kernel.sym
|
objcopy --only-keep-debug kernel kernel.sym
|
||||||
objcopy --strip-debug kernel
|
objcopy --strip-debug kernel
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ disk.img:
|
|||||||
$(AS) $(ASFLAGS) -o $@ $<
|
$(AS) $(ASFLAGS) -o $@ $<
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CC) "-I$(PWD)" -c "$<" $(CFLAGS) -o "$@"
|
$(CC) $(CFLAGS) $(CPPFLAGS) -c "$<" -o "$@"
|
||||||
|
|
||||||
|
|
||||||
test: CFLAGS += -DRUN_TEST
|
test: CFLAGS += -DRUN_TEST
|
||||||
@ -60,7 +63,7 @@ debug_test: CFLAGS += $(DEBUG_FLAGS) -DRUN_TEST
|
|||||||
debug_test: debug
|
debug_test: debug
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) kernel $(asmobj) $(cobj) $(deps) fd.iso kernel.sym
|
$(RM) kernel $(asmobj) $(gasmobj) $(cobj) $(deps) fd.iso kernel.sym
|
||||||
$(RM) -r isodir
|
$(RM) -r isodir
|
||||||
|
|
||||||
ifneq ($(MAKECMDGOALS),clean)
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
|
@ -31,7 +31,7 @@ stack is properly aligned and failure to align the stack will result in
|
|||||||
undefined behavior.
|
undefined behavior.
|
||||||
*/
|
*/
|
||||||
.section .bss
|
.section .bss
|
||||||
.align 16
|
.align 4096
|
||||||
stack_bottom:
|
stack_bottom:
|
||||||
.skip 16384 # 16 KiB
|
.skip 16384 # 16 KiB
|
||||||
stack_top:
|
stack_top:
|
||||||
@ -117,3 +117,7 @@ Set the size of the _start symbol to the current location '.' minus its start.
|
|||||||
This is useful when debugging or when you implement call tracing.
|
This is useful when debugging or when you implement call tracing.
|
||||||
*/
|
*/
|
||||||
.size _start, . - _start
|
.size _start, . - _start
|
||||||
|
.global _stack_bottom
|
||||||
|
_stack_bottom: stack_bottom
|
||||||
|
.global _stack_stop
|
||||||
|
_stack_stop: stack_top
|
@ -28,7 +28,7 @@ align 4
|
|||||||
; stack is properly aligned and failure to align the stack will result in
|
; stack is properly aligned and failure to align the stack will result in
|
||||||
; undefined behavior.
|
; undefined behavior.
|
||||||
section .bss
|
section .bss
|
||||||
align 16
|
align 4096
|
||||||
stack_bottom:
|
stack_bottom:
|
||||||
resb 16384 ; 16 KiB
|
resb 16384 ; 16 KiB
|
||||||
stack_top:
|
stack_top:
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
/* Copyright (C) 2005 David Decotigny
|
/* Copyright (C) 2021 Mathieu Maret
|
||||||
|
Copyright (C) 2005 David Decotigny
|
||||||
Copyright (C) 2000-2004, The KOS team
|
Copyright (C) 2000-2004, The KOS team
|
||||||
|
|
||||||
Initially taken from SOS
|
Initially taken from SOS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "cpu_context.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
#include "gdt.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "segment.h"
|
#include "segment.h"
|
||||||
|
|
||||||
#include "cpu_context.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Here is the definition of a CPU context for IA32 processors. This
|
* Here is the definition of a CPU context for IA32 processors. This
|
||||||
* is a Matos/SOS convention, not a specification given by the IA32
|
* is a Matos/SOS convention, not a specification given by the IA32
|
||||||
@ -37,12 +38,11 @@ struct cpu_state {
|
|||||||
uint16_t alignment_padding; /* unused */
|
uint16_t alignment_padding; /* unused */
|
||||||
uint32_t edi;
|
uint32_t edi;
|
||||||
uint32_t esi;
|
uint32_t esi;
|
||||||
uint32_t esp;
|
|
||||||
uint32_t ebp;
|
|
||||||
uint32_t ebx;
|
uint32_t ebx;
|
||||||
uint32_t edx;
|
uint32_t edx;
|
||||||
uint32_t ecx;
|
uint32_t ecx;
|
||||||
uint32_t eax;
|
uint32_t eax;
|
||||||
|
uint32_t ebp;
|
||||||
|
|
||||||
/* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
|
/* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
|
||||||
uint32_t error_code;
|
uint32_t error_code;
|
||||||
@ -76,6 +76,22 @@ struct cpu_kstate {
|
|||||||
struct cpu_state regs;
|
struct cpu_state regs;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of an interrupted User thread's context. This is almost
|
||||||
|
* the same as a kernel context, except that 2 additional values are
|
||||||
|
* pushed on the stack before the eflags/cs/eip of the interrupted
|
||||||
|
* context: the stack configuration of the interrupted user context.
|
||||||
|
*
|
||||||
|
* @see Section 6.4.1 of Intel x86 vol 1
|
||||||
|
*/
|
||||||
|
struct cpu_ustate {
|
||||||
|
struct cpu_state regs;
|
||||||
|
struct {
|
||||||
|
uint32_t cpl3_esp;
|
||||||
|
uint16_t cpl3_ss;
|
||||||
|
};
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* THE main operation of a kernel thread. This routine calls the
|
* THE main operation of a kernel thread. This routine calls the
|
||||||
* kernel thread function start_func and calls exit_func when
|
* kernel thread function start_func and calls exit_func when
|
||||||
@ -96,6 +112,123 @@ static void core_routine(cpu_kstate_function_arg1_t *start_func, void *start_arg
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure of a Task State Segment on the x86 Architecture.
|
||||||
|
*
|
||||||
|
* @see Intel x86 spec vol 3, figure 6-2
|
||||||
|
*
|
||||||
|
* @note Such a data structure should not cross any page boundary (see
|
||||||
|
* end of section 6.2.1 of Intel spec vol 3). This is the reason why
|
||||||
|
* we tell gcc to align it on a 128B boundary (its size is 104B, which
|
||||||
|
* is <= 128).
|
||||||
|
*/
|
||||||
|
struct x86_tss {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intel provides a way for a task to switch to another in an
|
||||||
|
* automatic way (call gates). In this case, the back_link field
|
||||||
|
* stores the source TSS of the context switch. This allows to
|
||||||
|
* easily implement coroutines, task backtracking, ... In Matos/SOS we
|
||||||
|
* don't use TSS for the context switch purpouse, so we always
|
||||||
|
* ignore this field.
|
||||||
|
* (+0)
|
||||||
|
*/
|
||||||
|
uint16_t back_link;
|
||||||
|
|
||||||
|
uint16_t reserved1;
|
||||||
|
|
||||||
|
/* CPL0 saved context. (+4) */
|
||||||
|
vaddr_t esp0;
|
||||||
|
uint16_t ss0;
|
||||||
|
|
||||||
|
uint16_t reserved2;
|
||||||
|
|
||||||
|
/* CPL1 saved context. (+12) */
|
||||||
|
vaddr_t esp1;
|
||||||
|
uint16_t ss1;
|
||||||
|
|
||||||
|
uint16_t reserved3;
|
||||||
|
|
||||||
|
/* CPL2 saved context. (+20) */
|
||||||
|
vaddr_t esp2;
|
||||||
|
uint16_t ss2;
|
||||||
|
|
||||||
|
uint16_t reserved4;
|
||||||
|
|
||||||
|
/* Interrupted context's saved registers. (+28) */
|
||||||
|
vaddr_t cr3;
|
||||||
|
vaddr_t eip;
|
||||||
|
uint32_t eflags;
|
||||||
|
uint32_t eax;
|
||||||
|
uint32_t ecx;
|
||||||
|
uint32_t edx;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t esp;
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t edi;
|
||||||
|
|
||||||
|
/* +72 */
|
||||||
|
uint16_t es;
|
||||||
|
uint16_t reserved5;
|
||||||
|
|
||||||
|
/* +76 */
|
||||||
|
uint16_t cs;
|
||||||
|
uint16_t reserved6;
|
||||||
|
|
||||||
|
/* +80 */
|
||||||
|
uint16_t ss;
|
||||||
|
uint16_t reserved7;
|
||||||
|
|
||||||
|
/* +84 */
|
||||||
|
uint16_t ds;
|
||||||
|
uint16_t reserved8;
|
||||||
|
|
||||||
|
/* +88 */
|
||||||
|
uint16_t fs;
|
||||||
|
uint16_t reserved9;
|
||||||
|
|
||||||
|
/* +92 */
|
||||||
|
uint16_t gs;
|
||||||
|
uint16_t reserved10;
|
||||||
|
|
||||||
|
/* +96 */
|
||||||
|
uint16_t ldtr;
|
||||||
|
uint16_t reserved11;
|
||||||
|
|
||||||
|
/* +100 */
|
||||||
|
uint16_t debug_trap_flag : 1;
|
||||||
|
uint16_t reserved12 : 15;
|
||||||
|
uint16_t iomap_base_addr;
|
||||||
|
|
||||||
|
/* 104 */
|
||||||
|
} __attribute__((packed, aligned(128)));
|
||||||
|
|
||||||
|
static struct x86_tss kernel_tss;
|
||||||
|
|
||||||
|
int cpu_context_subsystem_setup()
|
||||||
|
{
|
||||||
|
/* Reset the kernel TSS */
|
||||||
|
memset(&kernel_tss, 0x0, sizeof(kernel_tss));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Now setup the kernel TSS.
|
||||||
|
*
|
||||||
|
* Considering the privilege change method we choose (cpl3 -> cpl0
|
||||||
|
* through a software interrupt), we don't need to initialize a
|
||||||
|
* full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually,
|
||||||
|
* only a correct value for the kernel esp and ss are required (aka
|
||||||
|
* "ss0" and "esp0" fields). Since the esp0 will have to be updated
|
||||||
|
* at privilege change time, we don't have to set it up now.
|
||||||
|
*/
|
||||||
|
kernel_tss.ss0 = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA);
|
||||||
|
|
||||||
|
/* Register this TSS into the gdt */
|
||||||
|
gdtRegisterTSS((vaddr_t)&kernel_tss);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func,
|
int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_func,
|
||||||
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
||||||
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg)
|
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg)
|
||||||
@ -177,6 +310,43 @@ int cpu_kstate_init(struct cpu_state **ctxt, cpu_kstate_function_arg1_t *start_f
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2,
|
||||||
|
uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize)
|
||||||
|
{
|
||||||
|
|
||||||
|
// The user context is stacked above the usual cpu state by the CPU on context switch.
|
||||||
|
// So store it when the cpu expect it (See cpu_kstate_init for more details)
|
||||||
|
struct cpu_ustate *uctx =
|
||||||
|
(struct cpu_ustate *)(kernelStackBottom + kernelStackSize - sizeof(struct cpu_ustate));
|
||||||
|
|
||||||
|
/* If needed, poison the stack */
|
||||||
|
#ifdef CPU_STATE_DETECT_UNINIT_KERNEL_VARS
|
||||||
|
memset((void *)kernelStackBottom, CPU_STATE_STACK_POISON, kernelStackSize);
|
||||||
|
#elif defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
|
||||||
|
cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(uctx, 0, sizeof(struct cpu_ustate));
|
||||||
|
|
||||||
|
uctx->regs.eip = startPC;
|
||||||
|
uctx->regs.eax = arg1;
|
||||||
|
uctx->regs.ebx = arg2;
|
||||||
|
|
||||||
|
uctx->regs.cs = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE); // Code
|
||||||
|
uctx->regs.ds = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // Data
|
||||||
|
uctx->regs.es = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // Data
|
||||||
|
uctx->regs.cpl0_ss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KDATA); // Kernel Stack
|
||||||
|
uctx->cpl3_ss = BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UDATA); // User Stack
|
||||||
|
|
||||||
|
uctx->cpl3_esp = startSP;
|
||||||
|
|
||||||
|
/* The newly created context is initially interruptible */
|
||||||
|
uctx->regs.eflags = (1 << 9); /* set IF bit */
|
||||||
|
|
||||||
|
*ctx = (struct cpu_state *)uctx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
|
#if defined(CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
|
||||||
void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
void cpu_state_prepare_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
||||||
vaddr_t stack_bottom, size_t stack_size)
|
vaddr_t stack_bottom, size_t stack_size)
|
||||||
@ -213,6 +383,30 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt, vaddr_
|
|||||||
/* =======================================================================
|
/* =======================================================================
|
||||||
* Public Accessor functions
|
* Public Accessor functions
|
||||||
*/
|
*/
|
||||||
|
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt)
|
||||||
|
{
|
||||||
|
/* An interrupted user thread has its CS register set to that of the
|
||||||
|
User code segment */
|
||||||
|
switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs)) {
|
||||||
|
case BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE):
|
||||||
|
return TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE):
|
||||||
|
return FALSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_err("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !",
|
||||||
|
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs),
|
||||||
|
BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_KCODE),
|
||||||
|
BUILD_SEGMENT_REG_VALUE(3, FALSE, SEG_UCODE));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should never get here */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt)
|
vaddr_t cpu_context_get_PC(const struct cpu_state *ctxt)
|
||||||
{
|
{
|
||||||
@ -233,6 +427,27 @@ vaddr_t cpu_context_get_SP(const struct cpu_state *ctxt)
|
|||||||
return (vaddr_t)ctxt;
|
return (vaddr_t)ctxt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt)
|
||||||
|
{
|
||||||
|
assert(NULL != ctxt);
|
||||||
|
|
||||||
|
/* This is the Err_code of the interrupted context (ie kernel or user
|
||||||
|
context). */
|
||||||
|
return ctxt->error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
vaddr_t cpu_context_get_EX_faulting_vaddr(const struct cpu_state *ctxt)
|
||||||
|
{
|
||||||
|
assert(NULL != ctxt);
|
||||||
|
|
||||||
|
// A page fault has occurred.
|
||||||
|
// The faulting address is stored in the CR2 register.
|
||||||
|
vaddr_t faulting_address;
|
||||||
|
asm volatile("mov %%cr2, %0" : "=r"(faulting_address));
|
||||||
|
|
||||||
|
return faulting_address;
|
||||||
|
}
|
||||||
|
|
||||||
void cpu_context_dump(const struct cpu_state *ctxt)
|
void cpu_context_dump(const struct cpu_state *ctxt)
|
||||||
{
|
{
|
||||||
printf("CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", (unsigned)ctxt->eip,
|
printf("CPU: eip=%x esp=%x eflags=%x cs=%x ds=%x ss=%x err=%x", (unsigned)ctxt->eip,
|
||||||
@ -240,3 +455,68 @@ void cpu_context_dump(const struct cpu_state *ctxt)
|
|||||||
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds,
|
(unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds,
|
||||||
(unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code);
|
(unsigned)ctxt->cpl0_ss, (unsigned)ctxt->error_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* *************************************************************
|
||||||
|
* Function to manage the TSS. This function is not really "public":
|
||||||
|
* it is reserved to the assembler routines defined in
|
||||||
|
* cpu_context_switch.S
|
||||||
|
*
|
||||||
|
* Update the kernel stack address so that the IRQ, syscalls and
|
||||||
|
* exception return in a correct stack location when coming back into
|
||||||
|
* kernel mode.
|
||||||
|
*/
|
||||||
|
void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt)
|
||||||
|
{
|
||||||
|
/* next_ctxt corresponds to an interrupted user thread ? */
|
||||||
|
if (cpu_context_is_in_user_mode(next_ctxt)) {
|
||||||
|
/*
|
||||||
|
* Yes: "next_ctxt" is an interrupted user thread => we are
|
||||||
|
* going to switch to user mode ! Setup the stack address so
|
||||||
|
* that the user thread "next_ctxt" can come back to the correct
|
||||||
|
* stack location when returning in kernel mode.
|
||||||
|
*
|
||||||
|
* This stack location corresponds to the SP of the next user
|
||||||
|
* thread once its context has been transferred on the CPU, ie
|
||||||
|
* once the CPU has executed all the pop/iret instruction of the
|
||||||
|
* context switch with privilege change.
|
||||||
|
*/
|
||||||
|
kernel_tss.esp0 = ((vaddr_t)next_ctxt) + sizeof(struct cpu_ustate);
|
||||||
|
/* Note: no need to protect this agains IRQ because IRQs are not
|
||||||
|
allowed to update it by themselves, and they are not allowed
|
||||||
|
to block */
|
||||||
|
} else {
|
||||||
|
/* No: No need to update kernel TSS when we stay in kernel
|
||||||
|
mode */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline
|
||||||
|
int syscallGet3args(const struct cpu_state *user_ctxt,
|
||||||
|
/* out */unsigned int *arg1,
|
||||||
|
/* out */unsigned int *arg2,
|
||||||
|
/* out */unsigned int *arg3)
|
||||||
|
{
|
||||||
|
*arg1 = user_ctxt->ebx;
|
||||||
|
*arg2 = user_ctxt->ecx;
|
||||||
|
*arg3 = user_ctxt->edx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int syscallGet1arg(const struct cpu_state *user_ctxt,
|
||||||
|
/* out */unsigned int *arg1)
|
||||||
|
{
|
||||||
|
unsigned int unused;
|
||||||
|
return syscallGet3args(user_ctxt, arg1, & unused, & unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int _syscallGet2args(const struct cpu_state *user_ctxt,
|
||||||
|
/* out */unsigned int *arg1,
|
||||||
|
/* out */unsigned int *arg2)
|
||||||
|
{
|
||||||
|
unsigned int unused;
|
||||||
|
return syscallGet3args(user_ctxt, arg1, arg2, & unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -2,18 +2,35 @@
|
|||||||
|
|
||||||
.text
|
.text
|
||||||
|
|
||||||
|
/**
|
||||||
|
* C Function called by the routines below in order to tell the CPU
|
||||||
|
* where will be the kernel stack (needed by the interrupt handlers)
|
||||||
|
* when next_ctxt will come back into kernel mode.
|
||||||
|
*
|
||||||
|
* void cpu_context_update_kernel_tss(struct cpu_state *next_ctxt)
|
||||||
|
*
|
||||||
|
* @see end of cpu_context.c
|
||||||
|
*/
|
||||||
|
.extern cpu_context_update_kernel_tss
|
||||||
|
|
||||||
|
|
||||||
.globl cpu_context_switch
|
.globl cpu_context_switch
|
||||||
.type cpu_context_switch, @function
|
.type cpu_context_switch, @function
|
||||||
cpu_context_switch:
|
cpu_context_switch:
|
||||||
// arg2= to_context -- esp+68
|
// arg2= to_context -- esp+64
|
||||||
// arg1= from_context -- esp+64
|
// arg1= from_context -- esp+60
|
||||||
// caller ip -- esp+60
|
// caller ip -- esp+56
|
||||||
pushf // (eflags) esp+56
|
pushf // (eflags) esp+52
|
||||||
pushl %cs // (cs) esp+52
|
pushl %cs // (cs) esp+48
|
||||||
pushl $resume_pc // (ip) esp+48
|
pushl $resume_pc // (ip) esp+44
|
||||||
pushl $0 // (error code) esp+12+8x4
|
pushl $0 // (error code) esp+12+7x4
|
||||||
pushal // (general reg) esp+12
|
pushl %ebp
|
||||||
|
pushl %eax
|
||||||
|
pushl %ecx
|
||||||
|
pushl %edx
|
||||||
|
pushl %ebx
|
||||||
|
pushl %esi
|
||||||
|
pushl %edi
|
||||||
subl $2, %esp // (alignment) esp+10
|
subl $2, %esp // (alignment) esp+10
|
||||||
pushw %ss // esp+8
|
pushw %ss // esp+8
|
||||||
pushw %ds // esp+6
|
pushw %ds // esp+6
|
||||||
@ -26,11 +43,19 @@ cpu_context_switch:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Store the address of the saved context */
|
/* Store the address of the saved context */
|
||||||
movl 64(%esp), %ebx
|
movl 60(%esp), %ebx
|
||||||
movl %esp, (%ebx)
|
movl %esp, (%ebx)
|
||||||
|
|
||||||
/* This is the proper context switch ! We change the stack here */
|
/* This is the proper context switch ! We change the stack here */
|
||||||
movl 68(%esp), %esp
|
movl 64(%esp), %esp
|
||||||
|
|
||||||
|
/* Prepare kernel TSS in case we are switching to a user thread: we
|
||||||
|
make sure that we will come back into the kernel at a correct
|
||||||
|
stack location */
|
||||||
|
pushl %esp /* Pass the location of the context we are
|
||||||
|
restoring to the function */
|
||||||
|
call cpu_context_update_kernel_tss
|
||||||
|
addl $4, %esp
|
||||||
|
|
||||||
/* Restore the CPU context */
|
/* Restore the CPU context */
|
||||||
popw %gs
|
popw %gs
|
||||||
@ -39,7 +64,13 @@ cpu_context_switch:
|
|||||||
popw %ds
|
popw %ds
|
||||||
popw %ss
|
popw %ss
|
||||||
addl $2,%esp
|
addl $2,%esp
|
||||||
popal
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %ebx
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %eax
|
||||||
|
popl %ebp
|
||||||
addl $4, %esp /* Ignore "error code" */
|
addl $4, %esp /* Ignore "error code" */
|
||||||
|
|
||||||
/* This restores the eflags, the cs and the eip registers */
|
/* This restores the eflags, the cs and the eip registers */
|
||||||
@ -75,6 +106,14 @@ cpu_context_exit_to:
|
|||||||
call *8(%eax)
|
call *8(%eax)
|
||||||
addl $4, %esp
|
addl $4, %esp
|
||||||
|
|
||||||
|
/* Prepare kernel TSS in case we are switching to a user thread: we
|
||||||
|
make sure that we will come back into the kernel at a correct
|
||||||
|
stack location */
|
||||||
|
pushl %esp /* Pass the location of the context we are
|
||||||
|
restoring to the function */
|
||||||
|
call cpu_context_update_kernel_tss
|
||||||
|
addl $4, %esp
|
||||||
|
|
||||||
/* Restore the CPU context */
|
/* Restore the CPU context */
|
||||||
popw %gs
|
popw %gs
|
||||||
popw %fs
|
popw %fs
|
||||||
@ -82,7 +121,13 @@ cpu_context_exit_to:
|
|||||||
popw %ds
|
popw %ds
|
||||||
popw %ss
|
popw %ss
|
||||||
addl $2,%esp
|
addl $2,%esp
|
||||||
popal
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %ebx
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %eax
|
||||||
|
popl %ebp
|
||||||
addl $4, %esp /* Ignore "error code" */
|
addl $4, %esp /* Ignore "error code" */
|
||||||
|
|
||||||
/* This restores the eflags, the cs and the eip registers */
|
/* This restores the eflags, the cs and the eip registers */
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "interrupt.h"
|
#include "interrupt.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
#include "vga.h"
|
#include "vga.h"
|
||||||
|
|
||||||
exception_handler exception_handler_array[EXCEPTION_NUM] = {
|
exception_handler exception_handler_array[EXCEPTION_NUM] = {
|
||||||
@ -51,14 +51,10 @@ void print_handler(struct cpu_state *frame, ulong intr)
|
|||||||
|
|
||||||
void pagefault_handler(struct cpu_state *frame, ulong intr)
|
void pagefault_handler(struct cpu_state *frame, ulong intr)
|
||||||
{
|
{
|
||||||
// A page fault has occurred.
|
|
||||||
// The faulting address is stored in the CR2 register.
|
|
||||||
uint32_t faulting_address;
|
|
||||||
asm volatile("mov %%cr2, %0" : "=r"(faulting_address));
|
|
||||||
|
|
||||||
struct kthread *current = getCurrentThread();
|
struct thread *current = getCurrentThread();
|
||||||
printf("page fault while in thread %s at 0x%x 0x%x\n", current->name, faulting_address,
|
printf("page fault while in thread %s code at 0x%x when trying to access 0x%x err_code 0x%x\n", current->name,
|
||||||
cpu_context_get_PC(frame));
|
cpu_context_get_PC(frame), cpu_context_get_EX_faulting_vaddr(frame), cpu_context_get_EX_err(frame));
|
||||||
VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr);
|
VGAPrintf(RED, BLACK, 0, VGA_HEIGHT - 1, "PAGE FAULT %d", intr);
|
||||||
(void)intr;
|
(void)intr;
|
||||||
for (;;)
|
for (;;)
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
#define ASM_SOURCE 1
|
#define ASM_SOURCE 1
|
||||||
|
#include "segment.h"
|
||||||
.file "irq_wrappers.S"
|
.file "irq_wrappers.S"
|
||||||
.text
|
.text
|
||||||
|
|
||||||
.extern exception_handler_wrap
|
.extern exception_handler_wrap
|
||||||
.globl exception_handler_wrapper_array
|
.globl exception_handler_wrapper_array
|
||||||
|
|
||||||
|
/** Update the kernel TSS in case we are switching to a thread in user
|
||||||
|
mode in order to come back into the correct kernel stack */
|
||||||
|
.extern cpu_context_update_kernel_tss
|
||||||
|
|
||||||
|
/* The address of the function to call to set back the user thread's
|
||||||
|
MMU configuration upon return to user context */
|
||||||
|
.extern threadPrepareExceptionSwitchBack
|
||||||
|
|
||||||
.altmacro
|
.altmacro
|
||||||
|
|
||||||
|
|
||||||
@ -13,9 +22,9 @@
|
|||||||
.type exception_wrapper_\id,@function
|
.type exception_wrapper_\id,@function
|
||||||
/* INTERRUPT FRAME START */
|
/* INTERRUPT FRAME START */
|
||||||
/* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */
|
/* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */
|
||||||
/* uint32_t ip */
|
|
||||||
/* uint32_t cs; */
|
|
||||||
/* uint32_t flags */
|
/* uint32_t flags */
|
||||||
|
/* uint32_t cs; */
|
||||||
|
/* uint32_t ip */
|
||||||
/* Pushes the other reg to save same and look like a struct cpu_state*/
|
/* Pushes the other reg to save same and look like a struct cpu_state*/
|
||||||
/* Fake error code */
|
/* Fake error code */
|
||||||
pushl $0
|
pushl $0
|
||||||
@ -24,12 +33,12 @@
|
|||||||
pushl %ebp
|
pushl %ebp
|
||||||
movl %esp, %ebp
|
movl %esp, %ebp
|
||||||
|
|
||||||
pushl %edi
|
|
||||||
pushl %esi
|
|
||||||
pushl %edx
|
|
||||||
pushl %ecx
|
|
||||||
pushl %ebx
|
|
||||||
pushl %eax
|
pushl %eax
|
||||||
|
pushl %ecx
|
||||||
|
pushl %edx
|
||||||
|
pushl %ebx
|
||||||
|
pushl %esi
|
||||||
|
pushl %edi
|
||||||
subl $2,%esp
|
subl $2,%esp
|
||||||
pushw %ss
|
pushw %ss
|
||||||
pushw %ds
|
pushw %ds
|
||||||
@ -37,11 +46,31 @@
|
|||||||
pushw %fs
|
pushw %fs
|
||||||
pushw %gs
|
pushw %gs
|
||||||
|
|
||||||
|
/* Set correct kernel segment descriptors' value */
|
||||||
|
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
|
||||||
|
pushw %di ; popw %ds
|
||||||
|
pushw %di ; popw %es
|
||||||
|
pushw %di ; popw %fs
|
||||||
|
pushw %di ; popw %gs
|
||||||
|
|
||||||
push %esp
|
push %esp
|
||||||
pushl $\id
|
pushl $\id
|
||||||
call exception_handler_wrap
|
call exception_handler_wrap
|
||||||
addl $8, %esp
|
addl $8, %esp
|
||||||
|
|
||||||
|
/* 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 */
|
/* Restore the context */
|
||||||
popw %gs
|
popw %gs
|
||||||
popw %fs
|
popw %fs
|
||||||
@ -49,25 +78,100 @@
|
|||||||
popw %ds
|
popw %ds
|
||||||
popw %ss
|
popw %ss
|
||||||
addl $2,%esp
|
addl $2,%esp
|
||||||
popl %eax
|
|
||||||
popl %ebx
|
|
||||||
popl %ecx
|
|
||||||
popl %edx
|
|
||||||
popl %esi
|
|
||||||
popl %edi
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %ebx
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %eax
|
||||||
popl %ebp
|
popl %ebp
|
||||||
|
|
||||||
/* Remove fake error code */
|
/* Remove fake error code */
|
||||||
addl $4, %esp
|
addl $4, %esp
|
||||||
|
|
||||||
iret
|
iret
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
.macro exception_mac_with_errcode id
|
||||||
|
exception_wrapper_\id:
|
||||||
|
.type exception_wrapper_\id,@function
|
||||||
|
/* INTERRUPT FRAME START */
|
||||||
|
/* ALREADY PUSHED TO US BY THE PROCESSOR UPON ENTRY TO THIS INTERRUPT */
|
||||||
|
/* uint32_t flags */
|
||||||
|
/* uint32_t cs; */
|
||||||
|
/* uint32_t ip */
|
||||||
|
/* uint32_t errcode */
|
||||||
|
/* Pushes the other reg to save same and look like a struct cpu_state*/
|
||||||
|
|
||||||
.set i, 0
|
/* Backup the actual context */
|
||||||
.rept 0x20
|
pushl %ebp
|
||||||
exception_mac %i
|
movl %esp, %ebp
|
||||||
.set i, i+1
|
|
||||||
|
pushl %eax
|
||||||
|
pushl %ecx
|
||||||
|
pushl %edx
|
||||||
|
pushl %ebx
|
||||||
|
pushl %esi
|
||||||
|
pushl %edi
|
||||||
|
subl $2,%esp
|
||||||
|
pushw %ss
|
||||||
|
pushw %ds
|
||||||
|
pushw %es
|
||||||
|
pushw %fs
|
||||||
|
pushw %gs
|
||||||
|
|
||||||
|
/* Set correct kernel segment descriptors' value */
|
||||||
|
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
|
||||||
|
pushw %di ; popw %ds
|
||||||
|
pushw %di ; popw %es
|
||||||
|
pushw %di ; popw %fs
|
||||||
|
pushw %di ; popw %gs
|
||||||
|
|
||||||
|
push %esp
|
||||||
|
pushl $\id
|
||||||
|
call exception_handler_wrap
|
||||||
|
addl $8, %esp
|
||||||
|
|
||||||
|
/* Reconfigure the MMU if needed */
|
||||||
|
pushl %esp /* cpu_ctxt */
|
||||||
|
call threadPrepareExceptionSwitchBack
|
||||||
|
addl $4, %esp /* Unallocate the stack */
|
||||||
|
|
||||||
|
/* Prepare kernel TSS in case we are switching to a
|
||||||
|
user thread: we make sure that we will come back
|
||||||
|
into the kernel at a correct stack location */
|
||||||
|
pushl %esp /* Pass the location of the context we are
|
||||||
|
restoring to the function */
|
||||||
|
call cpu_context_update_kernel_tss
|
||||||
|
addl $4, %esp
|
||||||
|
|
||||||
|
/* Restore the context */
|
||||||
|
popw %gs
|
||||||
|
popw %fs
|
||||||
|
popw %es
|
||||||
|
popw %ds
|
||||||
|
popw %ss
|
||||||
|
addl $2,%esp
|
||||||
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %ebx
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %eax
|
||||||
|
popl %ebp
|
||||||
|
|
||||||
|
/* Error code isn't compatible with iretd */
|
||||||
|
addl $4, %esp
|
||||||
|
|
||||||
|
iret
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/*List of exception w or w/o err codehttps://wiki.osdev.org/Exceptions*/
|
||||||
|
.irp exception_id, 8, 10, 11, 12, 13, 14, 17, 30
|
||||||
|
exception_mac_with_errcode exception_id
|
||||||
|
.endr
|
||||||
|
|
||||||
|
.irp exception_id, 0, 1, 2, 3, 4, 5, 6, 7, 9, 15, 16, 18, 19, 20 21, 22, 23, 24, 25, 26, 27, 28, 29, 31
|
||||||
|
exception_mac exception_id
|
||||||
.endr
|
.endr
|
||||||
|
|
||||||
.macro ref_exception_wrapper id
|
.macro ref_exception_wrapper id
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* Copyright (C) 2004 David Decotigny
|
/* Copyright (C) 2021 Mathieu Maret
|
||||||
|
Copyright (C) 2004 David Decotigny
|
||||||
Copyright (C) 1999 Free Software Foundation, Inc.
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
@ -17,7 +18,6 @@
|
|||||||
USA.
|
USA.
|
||||||
*/
|
*/
|
||||||
#include "segment.h"
|
#include "segment.h"
|
||||||
|
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,6 +111,10 @@ static struct x86_segment_descriptor gdt[] = {
|
|||||||
},
|
},
|
||||||
[SEG_KCODE] = BUILD_GDTE(0, 1),
|
[SEG_KCODE] = BUILD_GDTE(0, 1),
|
||||||
[SEG_KDATA] = BUILD_GDTE(0, 0),
|
[SEG_KDATA] = BUILD_GDTE(0, 0),
|
||||||
|
[SEG_UCODE] = BUILD_GDTE(3, 1),
|
||||||
|
[SEG_UDATA] = BUILD_GDTE(3, 0),
|
||||||
|
[SEG_K_TSS] = {0,}, // Used by syscall, IRQ while in user space
|
||||||
|
// initialized by gdtRegisterTSS
|
||||||
};
|
};
|
||||||
|
|
||||||
int gdtSetup(void)
|
int gdtSetup(void)
|
||||||
@ -144,3 +148,29 @@ int gdtSetup(void)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int gdtRegisterTSS(vaddr_t tss_vaddr)
|
||||||
|
{
|
||||||
|
uint16_t regval_tss;
|
||||||
|
|
||||||
|
/* Initialize the GDT entry */
|
||||||
|
gdt[SEG_K_TSS] = (struct x86_segment_descriptor){
|
||||||
|
.limit_15_0 = 0x67, /* See Intel x86 vol 3 section 6.2.2 */
|
||||||
|
.base_paged_addr_15_0 = (tss_vaddr)&0xffff,
|
||||||
|
.base_paged_addr_23_16 = (tss_vaddr >> 16) & 0xff,
|
||||||
|
.segment_type = 0x9, /* See Intel x86 vol 3 figure 6-3 */
|
||||||
|
.descriptor_type = 0, /* (idem) */
|
||||||
|
.dpl = 3, /* Allowed for CPL3 tasks */
|
||||||
|
.present = 1,
|
||||||
|
.limit_19_16 = 0, /* Size of a TSS is < 2^16 ! */
|
||||||
|
.custom = 0, /* Unused */
|
||||||
|
.op_size = 0, /* See Intel x86 vol 3 figure 6-3 */
|
||||||
|
.granularity = 1, /* limit is in Bytes */
|
||||||
|
.base_paged_addr_31_24 = (tss_vaddr >> 24) & 0xff};
|
||||||
|
|
||||||
|
/* Load the TSS register into the processor */
|
||||||
|
regval_tss = BUILD_SEGMENT_REG_VALUE(0, FALSE, SEG_K_TSS);
|
||||||
|
asm("ltr %0" : : "r"(regval_tss));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* Copyright (C) 2004 David Decotigny
|
/* Copyright (C) 2021 Mathieu Maret
|
||||||
|
Copyright (C) 2004 David Decotigny
|
||||||
Copyright (C) 1999 Free Software Foundation, Inc.
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
@ -17,7 +18,8 @@
|
|||||||
USA.
|
USA.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include "stdarg.h"
|
||||||
/**
|
/**
|
||||||
* @file gdt.h
|
* @file gdt.h
|
||||||
*
|
*
|
||||||
@ -34,3 +36,4 @@
|
|||||||
* address space (ie "flat" virtual space).
|
* address space (ie "flat" virtual space).
|
||||||
*/
|
*/
|
||||||
int gdtSetup(void);
|
int gdtSetup(void);
|
||||||
|
int gdtRegisterTSS(vaddr_t tss_vaddr);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#define ASM_SOURCE 1
|
||||||
|
#include "segment.h"
|
||||||
.file "irq_pit.S"
|
.file "irq_pit.S"
|
||||||
|
|
||||||
.text
|
.text
|
||||||
@ -7,8 +9,16 @@
|
|||||||
.globl pit_handler
|
.globl pit_handler
|
||||||
.type pit_handler, @function
|
.type pit_handler, @function
|
||||||
pit_handler: // already got eflags, cs and eip on stack thanks to CPU
|
pit_handler: // already got eflags, cs and eip on stack thanks to CPU
|
||||||
pushl $0 // err_code esp+12+8*4=44
|
pushl $0 // err_code esp+12+7*4=40
|
||||||
pushal // (general reg) esp+12
|
pushl %ebp
|
||||||
|
movl %esp, %ebp
|
||||||
|
|
||||||
|
pushl %eax
|
||||||
|
pushl %ecx
|
||||||
|
pushl %edx
|
||||||
|
pushl %ebx
|
||||||
|
pushl %esi
|
||||||
|
pushl %edi
|
||||||
subl $2, %esp // (alignment) esp+10
|
subl $2, %esp // (alignment) esp+10
|
||||||
pushw %ss // esp+8
|
pushw %ss // esp+8
|
||||||
pushw %ds // esp+6
|
pushw %ds // esp+6
|
||||||
@ -16,12 +26,20 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU
|
|||||||
pushw %fs // esp+2
|
pushw %fs // esp+2
|
||||||
pushw %gs // esp
|
pushw %gs // esp
|
||||||
|
|
||||||
|
/* Set correct kernel segment descriptors' value */
|
||||||
|
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
|
||||||
|
pushw %di ; popw %ds
|
||||||
|
pushw %di ; popw %es
|
||||||
|
pushw %di ; popw %fs
|
||||||
|
pushw %di ; popw %gs
|
||||||
|
|
||||||
/* Send EOI to PIC */
|
/* Send EOI to PIC */
|
||||||
movb $0x20, %al
|
movb $0x20, %al
|
||||||
outb %al, $0x20
|
outb %al, $0x20
|
||||||
|
|
||||||
pushl %esp
|
pushl %esp
|
||||||
call pitIrqHandler
|
call pitIrqHandler
|
||||||
|
addl $4, %esp
|
||||||
movl %eax,%esp
|
movl %eax,%esp
|
||||||
|
|
||||||
/* Restore the CPU context */
|
/* Restore the CPU context */
|
||||||
@ -31,7 +49,13 @@ pit_handler: // already got eflags, cs and eip on stack thanks to CPU
|
|||||||
popw %ds
|
popw %ds
|
||||||
popw %ss
|
popw %ss
|
||||||
addl $2,%esp
|
addl $2,%esp
|
||||||
popal
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %ebx
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %eax
|
||||||
|
popl %ebp
|
||||||
addl $4, %esp /* Ignore "error code" */
|
addl $4, %esp /* Ignore "error code" */
|
||||||
|
|
||||||
/* This restores the eflags, the cs and the eip registers */
|
/* This restores the eflags, the cs and the eip registers */
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
#define ASM_SOURCE 1
|
#define ASM_SOURCE 1
|
||||||
|
#include "segment.h"
|
||||||
.file "irq_wrappers.S"
|
.file "irq_wrappers.S"
|
||||||
.text
|
.text
|
||||||
|
|
||||||
.extern interrupt_handler_pic
|
.extern interrupt_handler_pic
|
||||||
.globl irq_handler_wrapper_array
|
.globl irq_handler_wrapper_array
|
||||||
|
/** Update the kernel TSS in case we are switching to a thread in user
|
||||||
|
mode in order to come back into the correct kernel stack */
|
||||||
|
.extern cpu_context_update_kernel_tss
|
||||||
|
|
||||||
|
/* The address of the function to call to set back the user thread's
|
||||||
|
MMU configuration upon return to user context */
|
||||||
|
.extern threadPrepareIrqSwitchBack
|
||||||
|
|
||||||
.altmacro
|
.altmacro
|
||||||
|
|
||||||
@ -24,12 +32,12 @@
|
|||||||
pushl %ebp
|
pushl %ebp
|
||||||
movl %esp, %ebp
|
movl %esp, %ebp
|
||||||
|
|
||||||
pushl %edi
|
|
||||||
pushl %esi
|
|
||||||
pushl %edx
|
|
||||||
pushl %ecx
|
|
||||||
pushl %ebx
|
|
||||||
pushl %eax
|
pushl %eax
|
||||||
|
pushl %ecx
|
||||||
|
pushl %edx
|
||||||
|
pushl %ebx
|
||||||
|
pushl %esi
|
||||||
|
pushl %edi
|
||||||
subl $2,%esp
|
subl $2,%esp
|
||||||
pushw %ss
|
pushw %ss
|
||||||
pushw %ds
|
pushw %ds
|
||||||
@ -37,11 +45,31 @@
|
|||||||
pushw %fs
|
pushw %fs
|
||||||
pushw %gs
|
pushw %gs
|
||||||
|
|
||||||
|
/* Set correct kernel segment descriptors' value */
|
||||||
|
movw $BUILD_SEGMENT_REG_VALUE(0, 0, SEG_KDATA), %di
|
||||||
|
pushw %di ; popw %ds
|
||||||
|
pushw %di ; popw %es
|
||||||
|
pushw %di ; popw %fs
|
||||||
|
pushw %di ; popw %gs
|
||||||
|
|
||||||
push %esp
|
push %esp
|
||||||
pushl $\irq
|
pushl $\irq
|
||||||
call interrupt_handler_pic
|
call interrupt_handler_pic
|
||||||
addl $8, %esp
|
addl $8, %esp
|
||||||
|
|
||||||
|
/* Reconfigure the MMU if needed */
|
||||||
|
pushl %esp /* cpu_ctxt */
|
||||||
|
call threadPrepareIrqSwitchBack
|
||||||
|
addl $4, %esp /* Unallocate the stack */
|
||||||
|
|
||||||
|
/* Prepare kernel TSS in case we are switching to a
|
||||||
|
user thread: we make sure that we will come back
|
||||||
|
into the kernel at a correct stack location */
|
||||||
|
pushl %esp /* Pass the location of the context we are
|
||||||
|
restoring to the function */
|
||||||
|
call cpu_context_update_kernel_tss
|
||||||
|
addl $4, %esp
|
||||||
|
|
||||||
/* Restore the context */
|
/* Restore the context */
|
||||||
popw %gs
|
popw %gs
|
||||||
popw %fs
|
popw %fs
|
||||||
@ -49,12 +77,12 @@
|
|||||||
popw %ds
|
popw %ds
|
||||||
popw %ss
|
popw %ss
|
||||||
addl $2,%esp
|
addl $2,%esp
|
||||||
popl %eax
|
|
||||||
popl %ebx
|
|
||||||
popl %ecx
|
|
||||||
popl %edx
|
|
||||||
popl %esi
|
|
||||||
popl %edi
|
popl %edi
|
||||||
|
popl %esi
|
||||||
|
popl %ebx
|
||||||
|
popl %edx
|
||||||
|
popl %ecx
|
||||||
|
popl %eax
|
||||||
popl %ebp
|
popl %ebp
|
||||||
|
|
||||||
/* Remove fake error code */
|
/* 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 "paging.h"
|
||||||
#include "errno.h"
|
#include "errno.h"
|
||||||
|
#include "kernel.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "mmuContext.h"
|
||||||
#include "stdarg.h"
|
#include "stdarg.h"
|
||||||
|
|
||||||
// In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry
|
// In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry
|
||||||
@ -107,9 +110,10 @@ int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup mirroring
|
// Setup mirroring
|
||||||
pd[PD_MIRROR_PAGE_IDX].present = 1;
|
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].present = 1;
|
||||||
pd[PD_MIRROR_PAGE_IDX].write = 1;
|
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].write = 1;
|
||||||
pd[PD_MIRROR_PAGE_IDX].pt_addr = ((paddr_t)pd >> PT_SHIFT);
|
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].pt_addr = ((paddr_t)pd >> PT_SHIFT);
|
||||||
|
pd[PAGING_MIRROR_VADDR >> PD_SHIFT].user = 0;
|
||||||
|
|
||||||
// Loading of the PDBR in the MMU:
|
// Loading of the PDBR in the MMU:
|
||||||
asm volatile("movl %0,%%cr3\n\t"
|
asm volatile("movl %0,%%cr3\n\t"
|
||||||
@ -130,25 +134,36 @@ int pageMap(vaddr_t vaddr, paddr_t paddr, int flags)
|
|||||||
uint pdEntry = vaddr >> (PD_SHIFT);
|
uint pdEntry = vaddr >> (PD_SHIFT);
|
||||||
uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK;
|
uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK;
|
||||||
|
|
||||||
|
if ((vaddr >= PAGING_MIRROR_VADDR) && (vaddr < PAGING_MIRROR_VADDR + PAGING_MIRROR_SIZE))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
// Thank to mirroring, we can access the PD
|
// Thank to mirroring, we can access the PD
|
||||||
struct pde *pd =
|
struct pde *pd =
|
||||||
(struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT));
|
(struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT));
|
||||||
|
|
||||||
struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT));
|
struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE));
|
||||||
|
|
||||||
if (!pd[pdEntry].present) {
|
if (!pd[pdEntry].present) {
|
||||||
paddr_t ptPhy = allocPhyPage(1);
|
paddr_t ptPhy = allocPhyPage(1);
|
||||||
if (ptPhy == (vaddr_t)NULL)
|
if (ptPhy == (vaddr_t)NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pd[pdEntry].user = (flags & PAGING_MEM_USER) ? 1 : 0;
|
|
||||||
pd[pdEntry].present = 1;
|
pd[pdEntry].present = 1;
|
||||||
pd[pdEntry].write = 1;
|
pd[pdEntry].write = 1;
|
||||||
pd[pdEntry].pt_addr = (ptPhy >> PT_SHIFT);
|
pd[pdEntry].pt_addr = (ptPhy >> PT_SHIFT);
|
||||||
|
|
||||||
|
if (vaddr < PAGING_BASE_USER_ADDRESS) {
|
||||||
|
pd[pdEntry].user = 0;
|
||||||
|
mmuContextSyncKernelPDE(pdEntry, &pd[pdEntry], sizeof(struct pde));
|
||||||
|
} else {
|
||||||
|
assert(flags & PAGING_MEM_USER);
|
||||||
|
pd[pdEntry].user = 1;
|
||||||
|
}
|
||||||
|
|
||||||
__native_flush_tlb_single((vaddr_t)pt);
|
__native_flush_tlb_single((vaddr_t)pt);
|
||||||
memset((void *)pt, 0, PAGE_SIZE);
|
memset((void *)pt, 0, PAGE_SIZE);
|
||||||
} else {
|
}
|
||||||
|
{
|
||||||
|
|
||||||
// Already mapped ? Remove old mapping
|
// Already mapped ? Remove old mapping
|
||||||
if (pt[ptEntry].present) {
|
if (pt[ptEntry].present) {
|
||||||
@ -175,11 +190,14 @@ int pageUnmap(vaddr_t vaddr)
|
|||||||
uint pdEntry = vaddr >> (PD_SHIFT);
|
uint pdEntry = vaddr >> (PD_SHIFT);
|
||||||
uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK;
|
uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK;
|
||||||
|
|
||||||
|
if ((vaddr >= PAGING_MIRROR_VADDR) && (vaddr < PAGING_MIRROR_VADDR + PAGING_MIRROR_SIZE))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
// Thank to mirroring, we can access the PD
|
// Thank to mirroring, we can access the PD
|
||||||
struct pde *pd =
|
struct pde *pd =
|
||||||
(struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT));
|
(struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT));
|
||||||
|
|
||||||
struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT));
|
struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE));
|
||||||
if (!pd[pdEntry].present)
|
if (!pd[pdEntry].present)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!pt[ptEntry].present)
|
if (!pt[ptEntry].present)
|
||||||
@ -191,6 +209,9 @@ int pageUnmap(vaddr_t vaddr)
|
|||||||
// PTE not used. Decrease refcount on it. Is PT not used anymore ?
|
// PTE not used. Decrease refcount on it. Is PT not used anymore ?
|
||||||
if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 0) {
|
if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 0) {
|
||||||
pd[pdEntry].present = 0;
|
pd[pdEntry].present = 0;
|
||||||
|
if (vaddr < PAGING_BASE_USER_ADDRESS) {
|
||||||
|
mmuContextSyncKernelPDE(pdEntry, &pd[pdEntry], sizeof(struct pde));
|
||||||
|
}
|
||||||
__native_flush_tlb_single((vaddr_t)pt);
|
__native_flush_tlb_single((vaddr_t)pt);
|
||||||
}
|
}
|
||||||
__native_flush_tlb_single(vaddr);
|
__native_flush_tlb_single(vaddr);
|
||||||
@ -198,7 +219,133 @@ int pageUnmap(vaddr_t vaddr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paddr_t pagingGetPaddr(vaddr_t vaddr)
|
||||||
|
{
|
||||||
|
/* Get the page directory entry and table entry index for this
|
||||||
|
address */
|
||||||
|
unsigned pdEntry = vaddr >> PD_SHIFT;
|
||||||
|
unsigned ptEntry = vaddr >> PT_SHIFT & PTE_MASK;
|
||||||
|
unsigned pageOffset = vaddr & PAGE_MASK;
|
||||||
|
|
||||||
|
// Thank to mirroring, we can access the PD
|
||||||
|
struct pde *pd =
|
||||||
|
(struct pde *)(PAGING_MIRROR_VADDR + PAGE_SIZE * (PAGING_MIRROR_VADDR >> PD_SHIFT));
|
||||||
|
|
||||||
|
struct pte *pt = (struct pte *)((PAGING_MIRROR_VADDR) + (pdEntry * PAGE_SIZE));
|
||||||
|
|
||||||
|
/* No page mapped at this address ? */
|
||||||
|
if (!pd[pdEntry].present)
|
||||||
|
return (paddr_t)NULL;
|
||||||
|
if (!pt[ptEntry].present)
|
||||||
|
return (paddr_t)NULL;
|
||||||
|
|
||||||
|
return (pt[ptEntry].paddr << PT_SHIFT) + pageOffset;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned long getNbMappedPage(void)
|
unsigned long getNbMappedPage(void)
|
||||||
{
|
{
|
||||||
return mappedPage;
|
return mappedPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paddr_t pagingGetCurrentPDPaddr()
|
||||||
|
{
|
||||||
|
struct pdbr pdbr;
|
||||||
|
asm volatile("movl %%cr3, %0\n" : "=r"(pdbr));
|
||||||
|
return (pdbr.pd_paddr << 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pagingSetCurrentPDPaddr(paddr_t paddrPD)
|
||||||
|
{
|
||||||
|
struct pdbr pdbr;
|
||||||
|
|
||||||
|
assert(paddrPD != 0);
|
||||||
|
assert(IS_ALIGNED(paddrPD, PAGE_SIZE));
|
||||||
|
|
||||||
|
/* Setup the value of the PDBR */
|
||||||
|
memset(&pdbr, 0x0, sizeof(struct pdbr)); /* Reset the PDBR */
|
||||||
|
pdbr.pd_paddr = (paddrPD >> 12);
|
||||||
|
|
||||||
|
/* Configure the MMU according to the PDBR */
|
||||||
|
asm volatile("movl %0,%%cr3\n" ::"r"(pdbr));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmap page inside this MMU context
|
||||||
|
int pagingClearUserContext(vaddr_t vaddr_PD)
|
||||||
|
{
|
||||||
|
struct pde *pd = (struct pde *)vaddr_PD;
|
||||||
|
//Tmp pt to unref page they reference
|
||||||
|
struct pte *pt = (struct pte *)areaAlloc(1, 0);
|
||||||
|
|
||||||
|
if(pt == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (int pdIdx = PAGING_BASE_USER_ADDRESS >> PD_SHIFT; pdIdx < 1024; pdIdx++) {
|
||||||
|
|
||||||
|
if(!pd[pdIdx].present){
|
||||||
|
memset(&pd[pdIdx], 0, sizeof(struct pde));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
paddr_t ptAddr = pd[pdIdx].pt_addr << PT_SHIFT;
|
||||||
|
assert(!pageMap(ptAddr, (vaddr_t)pt, PAGING_MEM_USER | PAGING_MEM_WRITE));
|
||||||
|
for(int ptIdx = 0; ptIdx < 1024; ptIdx ++){
|
||||||
|
if(!pt[ptIdx].present){
|
||||||
|
memset(&pt[ptIdx], 0, sizeof(struct pte));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unrefPhyPage(pt[ptIdx].paddr);
|
||||||
|
memset(&pt[ptIdx], 0, sizeof(struct pte));
|
||||||
|
}
|
||||||
|
assert(!pageUnmap((vaddr_t)pt));
|
||||||
|
memset(&pd[pdIdx], 0, sizeof(struct pde));
|
||||||
|
|
||||||
|
unrefPhyPage(ptAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
areaFree((vaddr_t)pt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pagingCopyKernelSpace(vaddr_t destVaddrPD, paddr_t destPaddrPD, vaddr_t srcVaddrPD)
|
||||||
|
{
|
||||||
|
struct pde *src_pd = (struct pde *)srcVaddrPD;
|
||||||
|
struct pde *dest_pd = (struct pde *)destVaddrPD;
|
||||||
|
struct pde mirror_pde;
|
||||||
|
uint index_in_pd;
|
||||||
|
|
||||||
|
/* Fill destination PD with zeros */
|
||||||
|
memset((void *)destVaddrPD, 0x0, PAGE_SIZE);
|
||||||
|
|
||||||
|
/* Synchronize it with the master Kernel MMU context. Stop just
|
||||||
|
before the mirroring ! */
|
||||||
|
for (index_in_pd = 0; index_in_pd < (PAGING_MIRROR_VADDR >> 22); /* 1 PDE = 1 PT
|
||||||
|
= 1024 Pages
|
||||||
|
= 4MB */
|
||||||
|
index_in_pd++) {
|
||||||
|
/* Copy the master's configuration */
|
||||||
|
dest_pd[index_in_pd] = src_pd[index_in_pd];
|
||||||
|
|
||||||
|
/* We DON'T mark the underlying PT and pages as referenced
|
||||||
|
because all the PD are equivalent in the kernel space: as
|
||||||
|
soon as a page is mapped in the kernel, it is mapped by X
|
||||||
|
address spaces, and as soon as it is unmapped by 1 address
|
||||||
|
space, it is unmapped in all the others. So that for X
|
||||||
|
address spaces, the reference counter will be either 0 or X,
|
||||||
|
and not something else: using the reference counter correctly
|
||||||
|
won't be of any use and would consume some time in updating it. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the mirroring for the new address space */
|
||||||
|
mirror_pde.present = TRUE;
|
||||||
|
mirror_pde.write = 1;
|
||||||
|
mirror_pde.user = 0; /* This is a KERNEL PDE */
|
||||||
|
mirror_pde.pt_addr = (destPaddrPD >> 12);
|
||||||
|
dest_pd[PAGING_MIRROR_VADDR >> 22] = mirror_pde;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define PAGING_MEM_USER 1
|
/** Frontier between kernel and user space virtual addresses */
|
||||||
|
#define PAGING_BASE_USER_ADDRESS (0x40000000) /* 1GB (must be 4MB-aligned) */
|
||||||
|
#define PAGING_TOP_USER_ADDRESS (0xFFFFFFFF) /* 4GB - 1B */
|
||||||
|
#define PAGING_USER_SPACE_SIZE (0xc0000000) /* 3GB */
|
||||||
|
|
||||||
|
/** Length of the space reserved for the mirroring in the kernel
|
||||||
|
virtual space */
|
||||||
|
#define PAGING_MIRROR_SIZE (PAGE_SIZE << 10) /* 1 PD = 1024 Page Tables = 4MB */
|
||||||
|
|
||||||
|
/** Virtual address where the mirroring takes place */
|
||||||
|
#define PAGING_MIRROR_VADDR \
|
||||||
|
(PAGING_BASE_USER_ADDRESS - PAGING_MIRROR_SIZE)
|
||||||
|
|
||||||
|
#define PAGING_MEM_USER (1U << 0)
|
||||||
#define PAGING_MEM_READ (1U << 1)
|
#define PAGING_MEM_READ (1U << 1)
|
||||||
#define PAGING_MEM_WRITE (1U << 2)
|
#define PAGING_MEM_WRITE (1U << 2)
|
||||||
|
|
||||||
@ -10,3 +23,9 @@ int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr);
|
|||||||
int pageMap(vaddr_t vaddr, paddr_t paddr, int flags);
|
int pageMap(vaddr_t vaddr, paddr_t paddr, int flags);
|
||||||
int pageUnmap(vaddr_t vaddr);
|
int pageUnmap(vaddr_t vaddr);
|
||||||
unsigned long getNbMappedPage(void);
|
unsigned long getNbMappedPage(void);
|
||||||
|
|
||||||
|
int pagingSetCurrentPDPaddr(paddr_t paddrPD);
|
||||||
|
paddr_t pagingGetPaddr(vaddr_t vaddr);
|
||||||
|
paddr_t pagingGetCurrentPDPaddr();
|
||||||
|
int pagingCopyKernelSpace(vaddr_t destVaddrPD, paddr_t destPaddrPD, vaddr_t srcVaddrPD);
|
||||||
|
int pagingClearUserContext(vaddr_t vaddr_PD);
|
||||||
|
18
arch/x86/swintr.c
Normal file
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 allocInitialized = FALSE;
|
||||||
|
|
||||||
static int allocSlab(struct slabDesc **desc, size_t sizeEl, size_t sizeSlab,
|
static int allocSlab(struct slabDesc **desc, size_t sizeEl, size_t sizeSlab,
|
||||||
int self_containing);
|
int self_containing, int neverEmpty);
|
||||||
static int allocSlabEntry(struct slabEntry **desc, size_t sizeEl, size_t sizeSlab,
|
static int allocSlabEntry(struct slabEntry **desc, size_t sizeEl, size_t sizeSlab,
|
||||||
int selfContained);
|
int selfContained);
|
||||||
static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained);
|
static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained);
|
||||||
@ -28,27 +28,28 @@ static struct {
|
|||||||
size_t elementSize;
|
size_t elementSize;
|
||||||
size_t slabSize;
|
size_t slabSize;
|
||||||
unsigned char isSelf;
|
unsigned char isSelf;
|
||||||
} initSlab[] = {{4, PAGE_SIZE, 0},
|
unsigned char neverEmpty;
|
||||||
{8, PAGE_SIZE, 0},
|
} initSlab[] = {{4, PAGE_SIZE, 0, 0},
|
||||||
{16, PAGE_SIZE, 0},
|
{8, PAGE_SIZE, 0, 0},
|
||||||
{32, PAGE_SIZE, 0},
|
{16, PAGE_SIZE, 0, 0},
|
||||||
{64, PAGE_SIZE, 0},
|
{32, PAGE_SIZE, 0, 0},
|
||||||
{128, PAGE_SIZE, 0},
|
{64, PAGE_SIZE, 0, 0},
|
||||||
{256, 2 * PAGE_SIZE, 0},
|
{128, PAGE_SIZE, 0, 0},
|
||||||
{1024, 2 * PAGE_SIZE, 0},
|
{256, 2 * PAGE_SIZE, 0, 0},
|
||||||
{2048, 3 * PAGE_SIZE, 0},
|
{1024, 2 * PAGE_SIZE, 0, 0},
|
||||||
{4096, 4 * PAGE_SIZE, 0},
|
{2048, 3 * PAGE_SIZE, 0, 0},
|
||||||
{0, 0, 0}};
|
{4096, 4 * PAGE_SIZE, 0, 0},
|
||||||
|
{0, 0, 0, 0}};
|
||||||
|
|
||||||
int allocSetup(size_t sizeOfArea, vaddr_t *areaAddr, vaddr_t *descAddr, vaddr_t *entryAddr)
|
int allocSetup(size_t sizeOfArea, vaddr_t *areaAddr, vaddr_t *descAddr, vaddr_t *entryAddr)
|
||||||
{
|
{
|
||||||
list_init(slub);
|
list_init(slub);
|
||||||
|
|
||||||
assert(allocBookSlab(sizeof(struct slabDesc), PAGE_SIZE, TRUE) == 0);
|
assert(allocBookSlab(sizeof(struct slabDesc), PAGE_SIZE, TRUE, FALSE) == 0);
|
||||||
*descAddr = (vaddr_t)allocGetSlab(sizeof(struct slabDesc));
|
*descAddr = (vaddr_t)allocGetSlab(sizeof(struct slabDesc));
|
||||||
assert(allocBookSlab(sizeof(struct slabEntry), PAGE_SIZE, TRUE) == 0);
|
assert(allocBookSlab(sizeof(struct slabEntry), PAGE_SIZE, TRUE, FALSE) == 0);
|
||||||
*entryAddr = (vaddr_t)allocGetSlab(sizeof(struct slabEntry));
|
*entryAddr = (vaddr_t)allocGetSlab(sizeof(struct slabEntry));
|
||||||
assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE) == 0);
|
assert(allocBookSlab(sizeOfArea, PAGE_SIZE, TRUE, TRUE) == 0);
|
||||||
*areaAddr = (vaddr_t)allocGetSlab(sizeOfArea);
|
*areaAddr = (vaddr_t)allocGetSlab(sizeOfArea);
|
||||||
allocInitialized = TRUE;
|
allocInitialized = TRUE;
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ int allocPopulate()
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((ret = allocBookSlab(initSlab[i].elementSize, initSlab[i].slabSize,
|
if ((ret = allocBookSlab(initSlab[i].elementSize, initSlab[i].slabSize,
|
||||||
initSlab[i].isSelf))) {
|
initSlab[i].isSelf, initSlab[i].neverEmpty))) {
|
||||||
if (ret == -EEXIST)
|
if (ret == -EEXIST)
|
||||||
continue;
|
continue;
|
||||||
pr_err("Fail to allocBookSlab %d for %d \n", ret, (1U << i));
|
pr_err("Fail to allocBookSlab %d for %d \n", ret, (1U << i));
|
||||||
@ -73,7 +74,7 @@ int allocPopulate()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained)
|
int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained, int neverEmpty)
|
||||||
{
|
{
|
||||||
struct slabDesc *slab = NULL;
|
struct slabDesc *slab = NULL;
|
||||||
struct slabDesc *newSlab = NULL;
|
struct slabDesc *newSlab = NULL;
|
||||||
@ -95,7 +96,7 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ret = allocSlab(&newSlab, sizeEl, sizeSlab, selfContained))) {
|
if ((ret = allocSlab(&newSlab, sizeEl, sizeSlab, selfContained, neverEmpty))) {
|
||||||
pr_devel("Failed to alloc Slab\n");
|
pr_devel("Failed to alloc Slab\n");
|
||||||
restore_IRQs(flags);
|
restore_IRQs(flags);
|
||||||
return ret;
|
return ret;
|
||||||
@ -112,7 +113,8 @@ int allocBookSlab(size_t sizeEl, size_t sizeSlab, int selfContained)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int selfContained)
|
static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int selfContained,
|
||||||
|
int neverEmpty)
|
||||||
{
|
{
|
||||||
uint nbPage, i;
|
uint nbPage, i;
|
||||||
vaddr_t alloc;
|
vaddr_t alloc;
|
||||||
@ -127,7 +129,7 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s
|
|||||||
|
|
||||||
nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE);
|
nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE);
|
||||||
if (allocInitialized) {
|
if (allocInitialized) {
|
||||||
alloc = areaAlloc(nbPage);
|
alloc = areaAlloc(nbPage, AREA_PHY_MAP);
|
||||||
if (alloc == (paddr_t)NULL)
|
if (alloc == (paddr_t)NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
@ -156,6 +158,7 @@ static int allocSlab(struct slabDesc **desc, size_t size, size_t sizeSlab, int s
|
|||||||
slab->page = (vaddr_t)alloc;
|
slab->page = (vaddr_t)alloc;
|
||||||
slab->full = 0;
|
slab->full = 0;
|
||||||
slab->size = sizeSlab;
|
slab->size = sizeSlab;
|
||||||
|
(*desc)->neverEmpty = neverEmpty;
|
||||||
(*desc)->size = size;
|
(*desc)->size = size;
|
||||||
|
|
||||||
return formatPage(&(*desc)->slab, size, sizeSlab, selfContained);
|
return formatPage(&(*desc)->slab, size, sizeSlab, selfContained);
|
||||||
@ -182,7 +185,7 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE);
|
nbPage = DIV_ROUND_UP(sizeSlab, PAGE_SIZE);
|
||||||
vaddr_t alloc = areaAlloc(nbPage);
|
vaddr_t alloc = areaAlloc(nbPage, AREA_PHY_MAP);
|
||||||
if (alloc == (paddr_t)NULL)
|
if (alloc == (paddr_t)NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -202,8 +205,6 @@ static int allocSlabEntry(struct slabEntry **desc, size_t size, size_t sizeSlab,
|
|||||||
(*desc)->size = sizeSlab;
|
(*desc)->size = sizeSlab;
|
||||||
|
|
||||||
return formatPage((*desc), size, sizeSlab, selfContained);
|
return formatPage((*desc), size, sizeSlab, selfContained);
|
||||||
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained)
|
static int formatPage(struct slabEntry *desc, size_t size, size_t sizeSlab, int selfContained)
|
||||||
@ -261,8 +262,8 @@ void *malloc(size_t size)
|
|||||||
|
|
||||||
disable_IRQs(flags);
|
disable_IRQs(flags);
|
||||||
|
|
||||||
if (size >= PAGE_SIZE){
|
if (size >= PAGE_SIZE) {
|
||||||
vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE));
|
vaddr_t area = areaAlloc(DIV_ROUND_UP(size, PAGE_SIZE), AREA_PHY_MAP);
|
||||||
|
|
||||||
return (void *)area;
|
return (void *)area;
|
||||||
}
|
}
|
||||||
@ -271,12 +272,36 @@ void *malloc(size_t size)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct slabEntry *slabList = &slab->slab;
|
||||||
list_foreach(&slab->slab, slabEntry, slabIdx)
|
list_foreach(&slab->slab, slabEntry, slabIdx)
|
||||||
{
|
{
|
||||||
if (!slabEntry->full) {
|
if (!slabEntry->full) {
|
||||||
// pr_devel("found place in slub %d at idx %d for size %d\n", slubIdx,
|
// pr_devel("found place in slab idx %d for size %d\n", slabIdx, slab->size);
|
||||||
// slabIdx, size);
|
|
||||||
ret = allocFromSlab(slabEntry);
|
ret = allocFromSlab(slabEntry);
|
||||||
|
if (slabEntry->full && slab->neverEmpty) {
|
||||||
|
|
||||||
|
pr_devel("Prealloc place for slab for object size %d\n", slab->size);
|
||||||
|
assert(IS_SELF_CONTAINED(slabEntry));
|
||||||
|
size_t slabSize = slabEntry->size;
|
||||||
|
int nbPages = DIV_ROUND_UP(slabSize, PAGE_SIZE);
|
||||||
|
struct slabEntry *entry = (struct slabEntry *)areaBook(nbPages, AREA_PHY_MAP);
|
||||||
|
if (entry == NULL) {
|
||||||
|
restore_IRQs(flags);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
(entry)->freeEl = (char *)(entry) + sizeof(struct slabEntry);
|
||||||
|
list_singleton(entry, entry);
|
||||||
|
entry->page = (vaddr_t)entry;
|
||||||
|
entry->full = 0;
|
||||||
|
entry->size = slabSize;
|
||||||
|
|
||||||
|
formatPage(entry, slab->size, slabSize, 1);
|
||||||
|
list_add_tail(slabList, entry);
|
||||||
|
areaAdd((vaddr_t)entry, (vaddr_t)entry + nbPages * PAGE_SIZE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
restore_IRQs(flags);
|
restore_IRQs(flags);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -284,7 +309,6 @@ void *malloc(size_t size)
|
|||||||
|
|
||||||
// No room found
|
// No room found
|
||||||
struct slabEntry *newSlabEntry;
|
struct slabEntry *newSlabEntry;
|
||||||
struct slabEntry *slabList = &slab->slab;
|
|
||||||
size_t slabSize = MAX(PAGE_SIZE, size);
|
size_t slabSize = MAX(PAGE_SIZE, size);
|
||||||
int retSlab;
|
int retSlab;
|
||||||
|
|
||||||
@ -304,7 +328,8 @@ void *malloc(size_t size)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *zalloc(size_t size){
|
void *zalloc(size_t size)
|
||||||
|
{
|
||||||
void *alloc = malloc(size);
|
void *alloc = malloc(size);
|
||||||
|
|
||||||
if (alloc != NULL)
|
if (alloc != NULL)
|
||||||
@ -333,7 +358,8 @@ static int freeFromSlab(void *ptr, struct slabEntry *slab)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int freeSlabAllocated(void *ptr){
|
int freeSlabAllocated(void *ptr)
|
||||||
|
{
|
||||||
struct slabDesc *slab;
|
struct slabDesc *slab;
|
||||||
int slabIdx;
|
int slabIdx;
|
||||||
int flags;
|
int flags;
|
||||||
@ -361,8 +387,8 @@ void free(void *ptr)
|
|||||||
{
|
{
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return;
|
return;
|
||||||
if(!freeSlabAllocated(ptr))
|
if (!freeSlabAllocated(ptr))
|
||||||
return;
|
return;
|
||||||
if(areaFree((vaddr_t)ptr))
|
if (areaFree((vaddr_t)ptr))
|
||||||
pr_err("free: cannot found origin\n");
|
pr_err("free: cannot found origin\n");
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ int allocPopulate();
|
|||||||
* Allow malloc to allocate elements of this precise size.
|
* Allow malloc to allocate elements of this precise size.
|
||||||
* Otherwise the allocation will be in the closest biggest pool.
|
* Otherwise the allocation will be in the closest biggest pool.
|
||||||
* */
|
* */
|
||||||
int allocBookSlab(size_t size, size_t sizeSlab, int selfContained);
|
int allocBookSlab(size_t size, size_t sizeSlab, int selfContained, int neverEmpty);
|
||||||
|
|
||||||
void *malloc(size_t size);
|
void *malloc(size_t size);
|
||||||
void *zalloc(size_t size);
|
void *zalloc(size_t size);
|
||||||
@ -22,9 +22,9 @@ void free(void *ptr);
|
|||||||
*/
|
*/
|
||||||
struct slabEntry {
|
struct slabEntry {
|
||||||
vaddr_t page;
|
vaddr_t page;
|
||||||
size_t size;
|
|
||||||
void *freeEl;
|
void *freeEl;
|
||||||
char full;
|
size_t size;
|
||||||
|
bool_t full;//TODO replace by freeEl == NULL
|
||||||
struct slabEntry *next;
|
struct slabEntry *next;
|
||||||
struct slabEntry *prev;
|
struct slabEntry *prev;
|
||||||
};
|
};
|
||||||
@ -32,6 +32,7 @@ struct slabEntry {
|
|||||||
struct slabDesc {
|
struct slabDesc {
|
||||||
struct slabEntry slab;
|
struct slabEntry slab;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
bool_t neverEmpty;
|
||||||
struct slabDesc *next;
|
struct slabDesc *next;
|
||||||
struct slabDesc *prev;
|
struct slabDesc *prev;
|
||||||
};
|
};
|
||||||
|
131
core/allocArea.c
131
core/allocArea.c
@ -1,33 +1,43 @@
|
|||||||
#include "allocArea.h"
|
#include "allocArea.h"
|
||||||
#include "alloc.h"
|
#include "alloc.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
#include "irq.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "stdarg.h"
|
#include "stdarg.h"
|
||||||
|
|
||||||
|
|
||||||
static struct memArea *freeArea;
|
static struct memArea *freeArea;
|
||||||
static struct memArea *usedArea;
|
static struct memArea *usedArea;
|
||||||
|
|
||||||
static int areaMergeFreeArea(struct memArea *prev, struct memArea *next);
|
static int areaMergeFreeArea(struct memArea *prev, struct memArea *next);
|
||||||
|
|
||||||
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed)
|
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stackBottom, vaddr_t stackTop)
|
||||||
{
|
{
|
||||||
list_init(freeArea);
|
list_init(freeArea);
|
||||||
list_init(usedArea);
|
list_init(usedArea);
|
||||||
|
|
||||||
vaddr_t areaAddr, descAddr, entryAddr;
|
vaddr_t areaAddr, descAddr, entryAddr;
|
||||||
allocSetup(sizeof(struct memArea), &areaAddr, &descAddr, &entryAddr);
|
allocSetup(sizeof(struct memArea), &areaAddr, &descAddr, &entryAddr);
|
||||||
areaAdd(descAddr, PAGE_SIZE, FALSE);
|
areaAdd(descAddr, descAddr + PAGE_SIZE, FALSE);
|
||||||
if (entryAddr != descAddr)
|
if (entryAddr != descAddr)
|
||||||
areaAdd(entryAddr, PAGE_SIZE, FALSE);
|
areaAdd(entryAddr, entryAddr + PAGE_SIZE, FALSE);
|
||||||
if (areaAddr != descAddr && areaAddr != entryAddr)
|
if (areaAddr != descAddr && areaAddr != entryAddr)
|
||||||
areaAdd(areaAddr, PAGE_SIZE, FALSE);
|
areaAdd(areaAddr, areaAddr + PAGE_SIZE, FALSE);
|
||||||
|
|
||||||
int nbPages = DIV_ROUND_UP((lastUsed - firstMemUsed), PAGE_SIZE);
|
// kernel bootstrap part
|
||||||
areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), nbPages, FALSE);
|
areaAdd(ALIGN_DOWN(firstMemUsed, PAGE_SIZE), stackBottom, FALSE);
|
||||||
areaAdd(ALIGN_DOWN(areaAddr + PAGE_SIZE, PAGE_SIZE), 300 , TRUE);
|
|
||||||
|
// Initial kernel stack
|
||||||
|
areaAdd(stackBottom, stackTop, FALSE);
|
||||||
|
|
||||||
|
// Rest of kernel code
|
||||||
|
areaAdd(stackTop, lastUsed, FALSE);
|
||||||
|
|
||||||
|
// Rest of virtual mem is free
|
||||||
|
areaAdd(areaAddr + PAGE_SIZE, AREA_MEM_TOP, TRUE);
|
||||||
|
|
||||||
|
// Create allocBank for the rest of the system
|
||||||
allocPopulate();
|
allocPopulate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,23 +72,81 @@ static void insertSorted(struct memArea **list, struct memArea *item)
|
|||||||
list_add_tail(*list, item);
|
list_add_tail(*list, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
vaddr_t areaAlloc(unsigned int nbPages)
|
vaddr_t areaBook(unsigned int nbPages, uint32_t flags)
|
||||||
{
|
{
|
||||||
struct memArea *area, *allocated;
|
|
||||||
|
struct memArea *area;
|
||||||
|
vaddr_t allocated;
|
||||||
|
uint32_t irqFlags;
|
||||||
|
|
||||||
|
disable_IRQs(irqFlags);
|
||||||
|
|
||||||
area = areaFindFit(nbPages);
|
area = areaFindFit(nbPages);
|
||||||
if (!area)
|
if (!area) {
|
||||||
|
printf("NULL<\n");
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
|
|
||||||
return (vaddr_t)NULL;
|
return (vaddr_t)NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (area->nbPages == nbPages) {
|
||||||
|
list_delete(freeArea, area);
|
||||||
|
allocated = area->startAddr;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
allocated = area->startAddr;
|
||||||
|
|
||||||
|
area->nbPages -= nbPages;
|
||||||
|
area->startAddr += nbPages * PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & AREA_PHY_MAP) {
|
||||||
|
for (uint i = 0; i < nbPages; i++) {
|
||||||
|
paddr_t page = allocPhyPage(1);
|
||||||
|
if (page) {
|
||||||
|
if (pageMap(allocated + i * PAGE_SIZE, page, PAGING_MEM_WRITE)) {
|
||||||
|
unrefPhyPage(page);
|
||||||
|
page = (paddr_t)NULL;
|
||||||
|
} else {
|
||||||
|
unrefPhyPage(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (page == (paddr_t)NULL) {
|
||||||
|
areaFree(allocated);
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
|
|
||||||
|
return (vaddr_t)NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
|
return allocated;
|
||||||
|
}
|
||||||
|
|
||||||
|
vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags)
|
||||||
|
{
|
||||||
|
struct memArea *area, *allocated;
|
||||||
|
uint32_t irqFlags;
|
||||||
|
|
||||||
|
disable_IRQs(irqFlags);
|
||||||
|
|
||||||
|
area = areaFindFit(nbPages);
|
||||||
|
if (!area) {
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
|
|
||||||
|
return (vaddr_t)NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (area->nbPages == nbPages) {
|
if (area->nbPages == nbPages) {
|
||||||
list_delete(freeArea, area);
|
list_delete(freeArea, area);
|
||||||
insertSorted(&usedArea, area);
|
insertSorted(&usedArea, area);
|
||||||
allocated = area;
|
allocated = area;
|
||||||
}
|
} else {
|
||||||
|
|
||||||
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
|
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
|
||||||
if (!newArea){
|
if (!newArea) {
|
||||||
pr_devel("Failed to allocated area of %d pages\n", nbPages);
|
pr_devel("Failed to allocated area of %d pages\n", nbPages);
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
return (vaddr_t)NULL;
|
return (vaddr_t)NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,30 +161,48 @@ vaddr_t areaAlloc(unsigned int nbPages)
|
|||||||
insertSorted(&usedArea, newArea);
|
insertSorted(&usedArea, newArea);
|
||||||
|
|
||||||
allocated = newArea;
|
allocated = newArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & AREA_PHY_MAP) {
|
||||||
for (uint i = 0; i < nbPages; i++) {
|
for (uint i = 0; i < nbPages; i++) {
|
||||||
paddr_t page = allocPhyPage(1);
|
paddr_t page = allocPhyPage(1);
|
||||||
if (page) {
|
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 {
|
} else {
|
||||||
// TODO
|
unrefPhyPage(page);
|
||||||
assert(1);
|
}
|
||||||
|
}
|
||||||
|
if (page == (paddr_t)NULL) {
|
||||||
|
areaFree(allocated->startAddr);
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
|
|
||||||
|
return (vaddr_t)NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restore_IRQs(irqFlags);
|
||||||
return allocated->startAddr;
|
return allocated->startAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int areaAdd(vaddr_t addr, uint nbPages, int isFree)
|
int areaAdd(vaddr_t start, vaddr_t end, int isFree)
|
||||||
{
|
{
|
||||||
struct memArea **area;
|
struct memArea **area;
|
||||||
|
|
||||||
|
int nbPages = (end - start) / PAGE_SIZE;
|
||||||
|
pr_devel("Add %s area 0x%x->0x%x (%d)\n", isFree ? "free" : "used", start, end, nbPages);
|
||||||
|
|
||||||
|
assert(nbPages > 0);
|
||||||
|
assert(IS_ALIGNED(start, PAGE_SIZE));
|
||||||
|
assert(IS_ALIGNED(end, PAGE_SIZE));
|
||||||
|
|
||||||
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
|
struct memArea *newArea = (struct memArea *)malloc(sizeof(struct memArea));
|
||||||
if (!newArea)
|
if (!newArea)
|
||||||
return (vaddr_t)NULL;
|
return (vaddr_t)NULL;
|
||||||
|
|
||||||
newArea->nbPages = nbPages;
|
newArea->nbPages = nbPages;
|
||||||
newArea->startAddr = addr;
|
newArea->startAddr = start;
|
||||||
|
|
||||||
if (isFree) {
|
if (isFree) {
|
||||||
area = &freeArea;
|
area = &freeArea;
|
||||||
@ -158,7 +244,7 @@ static struct memArea *areaFindMemArea(struct memArea *list, vaddr_t addr)
|
|||||||
int count;
|
int count;
|
||||||
list_foreach(list, area, count)
|
list_foreach(list, area, count)
|
||||||
{
|
{
|
||||||
if (area->startAddr <= addr && addr <= area->startAddr + area->nbPages * PAGE_SIZE)
|
if (area->startAddr <= addr && addr < area->startAddr + area->nbPages * PAGE_SIZE)
|
||||||
return area;
|
return area;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,6 +260,11 @@ int areaFree(vaddr_t addr)
|
|||||||
pr_info("Cannot find memArea associated to %p\n", addr);
|
pr_info("Cannot find memArea associated to %p\n", addr);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < area->nbPages; i++) {
|
||||||
|
pageUnmap(area->startAddr + i * PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
list_delete(usedArea, area);
|
list_delete(usedArea, area);
|
||||||
insertSorted(&freeArea, area);
|
insertSorted(&freeArea, area);
|
||||||
|
|
||||||
|
@ -2,6 +2,12 @@
|
|||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
#include "stdarg.h"
|
#include "stdarg.h"
|
||||||
|
|
||||||
|
/* Pure Virtual Memory Allocation */
|
||||||
|
|
||||||
|
// areaAlloc map vmem to phy pages
|
||||||
|
#define AREA_PHY_MAP (1<<0)
|
||||||
|
#define AREA_MEM_TOP PAGING_MIRROR_VADDR
|
||||||
|
|
||||||
struct memArea {
|
struct memArea {
|
||||||
vaddr_t startAddr;
|
vaddr_t startAddr;
|
||||||
uint nbPages;
|
uint nbPages;
|
||||||
@ -10,7 +16,11 @@ struct memArea {
|
|||||||
struct memArea *prev;
|
struct memArea *prev;
|
||||||
};
|
};
|
||||||
|
|
||||||
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed);
|
void areaInit(vaddr_t firstMemUsed, vaddr_t lastUsed, vaddr_t stack_bottom, vaddr_t stack_top);
|
||||||
vaddr_t areaAlloc(unsigned int nbPages);
|
vaddr_t areaAlloc(unsigned int nbPages, uint32_t flags);
|
||||||
|
// Remove an area from the free ones but do not add it into used ones.
|
||||||
|
// This area should be latter added woth areaAdd.
|
||||||
|
// Used by malloc to avoid recursivity issue
|
||||||
|
vaddr_t areaBook(unsigned int nbPages, uint32_t flags);
|
||||||
int areaFree(vaddr_t addr);
|
int areaFree(vaddr_t addr);
|
||||||
int areaAdd(vaddr_t addr, uint nbPages, int isFree);
|
int areaAdd(vaddr_t begin, vaddr_t end, int isFree);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
do { \
|
do { \
|
||||||
if (!(p)) { \
|
if (!(p)) { \
|
||||||
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
|
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
|
||||||
printStackTrace(5); \
|
printStackTrace(3); \
|
||||||
while (1) { \
|
while (1) { \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
@ -17,7 +17,7 @@
|
|||||||
if (!(p)) { \
|
if (!(p)) { \
|
||||||
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
|
printf("BUG at %s:%d assert(%s)\n", __FILE__, __LINE__, #p); \
|
||||||
printf(__VA_ARGS__); \
|
printf(__VA_ARGS__); \
|
||||||
printStackTrace(5); \
|
printStackTrace(3); \
|
||||||
while (1) { \
|
while (1) { \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/* Copyright (C) 2005 David Decotigny
|
/* Copyright (C) 2021 Mathieu Maret
|
||||||
|
Copyright (C) 2005 David Decotigny
|
||||||
Copyright (C) 2000-2004, The KOS team
|
Copyright (C) 2000-2004, The KOS team
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
@ -84,6 +85,11 @@ int cpu_kstate_init(struct cpu_state **kctxt, cpu_kstate_function_arg1_t *start_
|
|||||||
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
vaddr_t start_arg, vaddr_t stack_bottom, size_t stack_size,
|
||||||
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg);
|
cpu_kstate_function_arg1_t *exit_func, vaddr_t exit_arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the system to deal with multiple CPU execution contexts
|
||||||
|
*/
|
||||||
|
int cpu_context_subsystem_setup();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that performs an immediate context-switch from one
|
* Function that performs an immediate context-switch from one
|
||||||
* kernel/user thread to another one. It stores the current executing
|
* kernel/user thread to another one. It stores the current executing
|
||||||
@ -119,6 +125,14 @@ void cpu_context_exit_to(struct cpu_state *switch_to_ctxt,
|
|||||||
* Public Accessor functions
|
* Public Accessor functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether the saved context was in kernel or user context
|
||||||
|
*
|
||||||
|
* @return TRUE when context was interrupted when in user mode, FALSE
|
||||||
|
* when in kernel mode, < 0 on error.
|
||||||
|
*/
|
||||||
|
int cpu_context_is_in_user_mode(const struct cpu_state *ctxt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return Program Counter stored in the saved kernel/user context
|
* Return Program Counter stored in the saved kernel/user context
|
||||||
*/
|
*/
|
||||||
@ -142,7 +156,7 @@ void cpu_context_dump(const struct cpu_state *ctxt);
|
|||||||
* Return the argument passed by the CPU upon exception, as stored in the
|
* Return the argument passed by the CPU upon exception, as stored in the
|
||||||
* saved context
|
* saved context
|
||||||
*/
|
*/
|
||||||
uint32_t cpu_context_get_EX_info(const struct cpu_state *ctxt);
|
uint32_t cpu_context_get_EX_err(const struct cpu_state *ctxt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the faulting address of the exception
|
* Return the faulting address of the exception
|
||||||
@ -181,7 +195,23 @@ void cpu_state_detect_kernel_stack_overflow(const struct cpu_state *ctxt,
|
|||||||
vaddr_t kernel_stack_bottom,
|
vaddr_t kernel_stack_bottom,
|
||||||
size_t kernel_stack_size);
|
size_t kernel_stack_size);
|
||||||
#else
|
#else
|
||||||
#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop \
|
#define cpu_state_prepare_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) \
|
||||||
|
({/* nop \
|
||||||
*/})
|
*/})
|
||||||
#define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */})
|
#define cpu_state_detect_kernel_stack_overflow(ctxt, stkbottom, stksize) ({/* nop */})
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int cpu_ustate_init(struct cpu_state **ctx, uaddr_t startPC, uint32_t arg1, uint32_t arg2,
|
||||||
|
uaddr_t startSP, vaddr_t kernelStackBottom, size_t kernelStackSize);
|
||||||
|
|
||||||
|
int syscallGet3args(const struct cpu_state *user_ctxt,
|
||||||
|
/* out */ unsigned int *arg1,
|
||||||
|
/* out */ unsigned int *arg2,
|
||||||
|
/* out */ unsigned int *arg3);
|
||||||
|
|
||||||
|
int syscallGet1arg(const struct cpu_state *user_ctxt,
|
||||||
|
/* out */ unsigned int *arg1);
|
||||||
|
|
||||||
|
int syscallGet2args(const struct cpu_state *user_ctxt,
|
||||||
|
/* out */ unsigned int *arg1,
|
||||||
|
/* out */ unsigned int *arg2);
|
||||||
|
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 "irq.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "mmuContext.h"
|
||||||
#include "multiboot.h"
|
#include "multiboot.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
#include "pit.h"
|
#include "pit.h"
|
||||||
|
#include "process.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
#include "stdarg.h"
|
#include "stdarg.h"
|
||||||
|
#include "swintr.h"
|
||||||
#ifdef RUN_TEST
|
#ifdef RUN_TEST
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
#endif
|
#endif
|
||||||
@ -31,7 +34,7 @@ void idleThread(void *arg)
|
|||||||
(void)arg;
|
(void)arg;
|
||||||
while (1) {
|
while (1) {
|
||||||
VGAPrintf(GREEN, BLACK, 0, VGA_HEIGHT - 1, "%d", (jiffies / HZ));
|
VGAPrintf(GREEN, BLACK, 0, VGA_HEIGHT - 1, "%d", (jiffies / HZ));
|
||||||
kthreadYield();
|
threadYield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +132,7 @@ void kmain(unsigned long magic, unsigned long addr)
|
|||||||
// Turns out linux and windows do the same !
|
// Turns out linux and windows do the same !
|
||||||
// https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/
|
// https://lore.kernel.org/lkml/MWHPR21MB159330952629D36EEDE706B3D7379@MWHPR21MB1593.namprd21.prod.outlook.com/
|
||||||
if (mmap[i].addr < 0x100000) {
|
if (mmap[i].addr < 0x100000) {
|
||||||
|
printf(" -> skipping\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem),
|
memAddBank(max(mmap[i].addr, (multiboot_uint64_t)lastUsedByMem),
|
||||||
@ -160,12 +164,16 @@ void kmain(unsigned long magic, unsigned long addr)
|
|||||||
serialSetup(115200);
|
serialSetup(115200);
|
||||||
|
|
||||||
printf("[Setup] allocation system\n");
|
printf("[Setup] allocation system\n");
|
||||||
areaInit(firstUsedByMem, lastUsedByMem);
|
areaInit(firstUsedByMem, lastUsedByMem, _stack_bottom, _stack_top);
|
||||||
//allocSetup();
|
|
||||||
|
mmuContextSetup();
|
||||||
|
cpu_context_subsystem_setup();
|
||||||
|
|
||||||
printf("[Setup] thread system\n");
|
printf("[Setup] thread system\n");
|
||||||
kthreadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1));
|
threadSetup(_stack_bottom, (_stack_top - _stack_bottom + 1));
|
||||||
kthreadCreate("idle ", idleThread, NULL);
|
threadCreate("idle ", idleThread, NULL);
|
||||||
|
processSetup();
|
||||||
|
syscallSetup();
|
||||||
|
|
||||||
irqSetRoutine(IRQ_TIMER, pit_handler);
|
irqSetRoutine(IRQ_TIMER, pit_handler);
|
||||||
|
|
||||||
@ -183,5 +191,5 @@ void kmain(unsigned long magic, unsigned long addr)
|
|||||||
|
|
||||||
// There is no real caller behind this point
|
// There is no real caller behind this point
|
||||||
// So finish this by ourself
|
// So finish this by ourself
|
||||||
kthreadExit();
|
threadExit();
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#include "mem.h"
|
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
#include "errno.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
#include "mem.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
static struct phyMemDesc *pageDesc = (struct phyMemDesc *)&__ld_kernel_end;
|
static struct phyMemDesc *pageDesc = (struct phyMemDesc *)&__ld_kernel_end;
|
||||||
@ -132,6 +133,7 @@ paddr_t allocPhyPage(uint nbPage)
|
|||||||
next = mem->next;
|
next = mem->next;
|
||||||
}
|
}
|
||||||
allocatedPage += nbPage;
|
allocatedPage += nbPage;
|
||||||
|
|
||||||
return head->phy_addr;
|
return head->phy_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,9 +141,10 @@ int unrefPhyPage(paddr_t addr)
|
|||||||
{
|
{
|
||||||
struct phyMemDesc *mem = addr2memDesc(addr);
|
struct phyMemDesc *mem = addr2memDesc(addr);
|
||||||
if (!mem) {
|
if (!mem) {
|
||||||
return -1;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
assert(mem->ref > 0);
|
if(mem->ref <= 0)
|
||||||
|
return -EINVAL;
|
||||||
mem->ref--;
|
mem->ref--;
|
||||||
if (mem->ref == 0) {
|
if (mem->ref == 0) {
|
||||||
allocatedPage--;
|
allocatedPage--;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#define PAGE_SHIFT 12U
|
#define PAGE_SHIFT 12U
|
||||||
#define PAGE_SIZE (1U << PAGE_SHIFT)
|
#define PAGE_SIZE (1U << PAGE_SHIFT)
|
||||||
|
#define PAGE_MASK (PAGE_SIZE - 1)
|
||||||
|
|
||||||
|
|
||||||
// Defined in linker.ld script
|
// Defined in linker.ld script
|
||||||
extern uint32_t __ld_kernel_begin;
|
extern uint32_t __ld_kernel_begin;
|
||||||
|
12
core/mmuContext.h
Normal file
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.
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
This program is free software; you can redistribute it and/or
|
||||||
@ -29,8 +30,6 @@
|
|||||||
* @see Intel x86 doc, vol 3 chapter 3.
|
* @see Intel x86 doc, vol 3 chapter 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "stdarg.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global segment selectors (GDT) for SOS/x86.
|
* Global segment selectors (GDT) for SOS/x86.
|
||||||
*
|
*
|
||||||
@ -39,13 +38,23 @@
|
|||||||
#define SEG_NULL 0 /* NULL segment, unused by the procesor */
|
#define SEG_NULL 0 /* NULL segment, unused by the procesor */
|
||||||
#define SEG_KCODE 1 /* Kernel code segment */
|
#define SEG_KCODE 1 /* Kernel code segment */
|
||||||
#define SEG_KDATA 2 /* Kernel data segment */
|
#define SEG_KDATA 2 /* Kernel data segment */
|
||||||
|
#define SEG_UCODE 3 /* User code segment */
|
||||||
|
#define SEG_UDATA 4 /* User data segment */
|
||||||
|
#define SEG_K_TSS 5 /* Kernel TSS for priviledge change (user to kernel) */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper macro that builds a segment register's value
|
* Helper macro that builds a segment register's value
|
||||||
*/
|
*/
|
||||||
|
#ifdef ASM_SOURCE
|
||||||
|
#define BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \
|
||||||
|
( (((desc_privilege) & 0x3) << 0) \
|
||||||
|
| ((in_ldt & 1) << 2) \
|
||||||
|
| ((seg_index) << 3) )
|
||||||
|
|
||||||
|
#else
|
||||||
#define BUILD_SEGMENT_REG_VALUE(desc_privilege, in_ldt, seg_index) \
|
#define BUILD_SEGMENT_REG_VALUE(desc_privilege, in_ldt, seg_index) \
|
||||||
((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | ((seg_index) << 3))
|
((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | ((seg_index) << 3))
|
||||||
|
#endif
|
||||||
/*
|
/*
|
||||||
* Local segment selectors (LDT) for SOS/x86
|
* Local segment selectors (LDT) for SOS/x86
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +51,7 @@ int mutexFree(struct mutex *m)
|
|||||||
int mutexLock(struct mutex *m)
|
int mutexLock(struct mutex *m)
|
||||||
{
|
{
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
struct kthread *current;
|
struct thread *current;
|
||||||
|
|
||||||
disable_IRQs(flags);
|
disable_IRQs(flags);
|
||||||
current = getCurrentThread();
|
current = getCurrentThread();
|
||||||
|
27
core/syscall.c
Normal file
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
|
// Physical address
|
||||||
typedef unsigned long paddr_t;
|
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 "irq.h"
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "wait.h"
|
#include "wait.h"
|
||||||
|
|
||||||
@ -25,13 +25,13 @@ int waitQueueFree(struct wait_queue *wq)
|
|||||||
|
|
||||||
int wakeUp(struct wait_queue *wq)
|
int wakeUp(struct wait_queue *wq)
|
||||||
{
|
{
|
||||||
struct kthread *th;
|
struct thread *th;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
|
||||||
disable_IRQs(flags);
|
disable_IRQs(flags);
|
||||||
list_collapse(wq->thread, th)
|
list_collapse(wq->thread, th)
|
||||||
{
|
{
|
||||||
kthreadAddThread(th);
|
threadAddThread(th);
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_IRQs(flags);
|
restore_IRQs(flags);
|
||||||
@ -46,7 +46,7 @@ int wait(struct wait_queue *wq)
|
|||||||
|
|
||||||
int waitTimeout(struct wait_queue *wq, unsigned long msec)
|
int waitTimeout(struct wait_queue *wq, unsigned long msec)
|
||||||
{
|
{
|
||||||
struct kthread *current, *next;
|
struct thread *current, *next;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -54,11 +54,11 @@ int waitTimeout(struct wait_queue *wq, unsigned long msec)
|
|||||||
|
|
||||||
current = getCurrentThread();
|
current = getCurrentThread();
|
||||||
current->state = WAITING;
|
current->state = WAITING;
|
||||||
next = kthreadSelectNext();
|
next = threadSelectNext();
|
||||||
kthreadUnsched(current);
|
threadUnsched(current);
|
||||||
|
|
||||||
list_add_tail(wq->thread, current);
|
list_add_tail(wq->thread, current);
|
||||||
ret = kthreadWait(current, next, msec);
|
ret = threadWait(current, next, msec);
|
||||||
|
|
||||||
restore_IRQs(flags);
|
restore_IRQs(flags);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
struct wait_queue {
|
struct wait_queue {
|
||||||
struct kthread *thread;
|
struct thread *thread;
|
||||||
struct wait_queue *next;
|
struct wait_queue *next;
|
||||||
struct wait_queue *prev;
|
struct wait_queue *prev;
|
||||||
};
|
};
|
||||||
@ -35,6 +35,6 @@ struct semaphore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct mutex {
|
struct mutex {
|
||||||
struct kthread *owner;
|
struct thread *owner;
|
||||||
struct wait_queue *wait;
|
struct wait_queue *wait;
|
||||||
};
|
};
|
||||||
|
@ -39,32 +39,32 @@ class CustomPrettyPrinterLocator(PrettyPrinter):
|
|||||||
if typename is None:
|
if typename is None:
|
||||||
typename = val.type.name
|
typename = val.type.name
|
||||||
|
|
||||||
if typename == "kthread":
|
if typename == "thread":
|
||||||
return KthreadPrettyPrinter(val)
|
return KthreadPrettyPrinter(val)
|
||||||
|
|
||||||
|
|
||||||
class KthreadListDumpCmd(gdb.Command):
|
class threadListDumpCmd(gdb.Command):
|
||||||
"""Prints the kthread list"""
|
"""Prints the thread list"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(KthreadListDumpCmd, self).__init__(
|
super(threadListDumpCmd, self).__init__(
|
||||||
"kthread_list_dump", gdb.COMMAND_USER
|
"thread_list_dump", gdb.COMMAND_USER
|
||||||
)
|
)
|
||||||
|
|
||||||
def _kthread_list_to_str(self, val):
|
def _thread_list_to_str(self, val):
|
||||||
"""Walk through the Kthread list.
|
"""Walk through the Kthread list.
|
||||||
|
|
||||||
We will simply follow the 'next' pointers until we encounter the HEAD again
|
We will simply follow the 'next' pointers until we encounter the HEAD again
|
||||||
"""
|
"""
|
||||||
idx = 0
|
idx = 0
|
||||||
head = val
|
head = val
|
||||||
kthread_ptr = val
|
thread_ptr = val
|
||||||
result = ""
|
result = ""
|
||||||
while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head):
|
while thread_ptr != 0 and (idx == 0 or thread_ptr != head):
|
||||||
result += "\n%d: %s" % (idx, KthreadPrettyPrinter(kthread_ptr).to_string())
|
result += "\n%d: %s" % (idx, KthreadPrettyPrinter(thread_ptr).to_string())
|
||||||
kthread_ptr = kthread_ptr["next"]
|
thread_ptr = thread_ptr["next"]
|
||||||
idx += 1
|
idx += 1
|
||||||
result = ("Found a Linked List with %d kthread:" % idx) + result
|
result = ("Found a Linked List with %d thread:" % idx) + result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def complete(self, text, word):
|
def complete(self, text, word):
|
||||||
@ -77,14 +77,14 @@ class KthreadListDumpCmd(gdb.Command):
|
|||||||
# to do argument parsing
|
# to do argument parsing
|
||||||
print("Args Passed: %s" % args)
|
print("Args Passed: %s" % args)
|
||||||
if args:
|
if args:
|
||||||
kthread_ptr_val = gdb.parse_and_eval(args)
|
thread_ptr_val = gdb.parse_and_eval(args)
|
||||||
else:
|
else:
|
||||||
kthread_ptr_val = gdb.parse_and_eval("currentThread")
|
thread_ptr_val = gdb.parse_and_eval("currentThread")
|
||||||
if str(kthread_ptr_val.type) != "struct kthread *":
|
if str(thread_ptr_val.type) != "struct thread *":
|
||||||
print("Expected pointer argument of type (struct kthread *)")
|
print("Expected pointer argument of type (struct thread *)")
|
||||||
return
|
return
|
||||||
|
|
||||||
print(self._kthread_list_to_str(kthread_ptr_val))
|
print(self._thread_list_to_str(thread_ptr_val))
|
||||||
|
|
||||||
|
|
||||||
class PhyMemDescListDumpCmd(gdb.Command):
|
class PhyMemDescListDumpCmd(gdb.Command):
|
||||||
@ -175,6 +175,7 @@ class PrintStructC99Cmd(gdb.Command):
|
|||||||
rr_rval = rr_s[1].strip()
|
rr_rval = rr_s[1].strip()
|
||||||
print(' ' * hs + '.' + rr_s[0] + '= ' + rr_rval)
|
print(' ' * hs + '.' + rr_s[0] + '= ' + rr_rval)
|
||||||
|
|
||||||
|
|
||||||
class ListDumpCmd(gdb.Command):
|
class ListDumpCmd(gdb.Command):
|
||||||
"""Prints a linked list"""
|
"""Prints a linked list"""
|
||||||
|
|
||||||
@ -190,13 +191,13 @@ class ListDumpCmd(gdb.Command):
|
|||||||
"""
|
"""
|
||||||
idx = 0
|
idx = 0
|
||||||
head = val
|
head = val
|
||||||
kthread_ptr = val
|
thread_ptr = val
|
||||||
result = ""
|
result = ""
|
||||||
while kthread_ptr != 0 and (idx == 0 or kthread_ptr != head):
|
while thread_ptr != 0 and (idx == 0 or thread_ptr != head):
|
||||||
result += gdb.execute('p *({}){}'.format(str(kthread_ptr.type),kthread_ptr), to_string=True)
|
result += gdb.execute('p *({}){}'.format(str(thread_ptr.type),thread_ptr), to_string=True)
|
||||||
kthread_ptr = kthread_ptr["next"]
|
thread_ptr = thread_ptr["next"]
|
||||||
idx += 1
|
idx += 1
|
||||||
result = ("Found a Linked List with %d items:" % idx) + "\n"+ result
|
result = ("Found a Linked List with %d items:" % idx) + "\n" + result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def complete(self, text, word):
|
def complete(self, text, word):
|
||||||
@ -220,8 +221,9 @@ class ListDumpCmd(gdb.Command):
|
|||||||
|
|
||||||
print(self._print_list(ptr_val))
|
print(self._print_list(ptr_val))
|
||||||
|
|
||||||
|
|
||||||
register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True)
|
register_pretty_printer(None, CustomPrettyPrinterLocator(), replace=True)
|
||||||
KthreadListDumpCmd()
|
threadListDumpCmd()
|
||||||
PhyMemDescListDumpCmd()
|
PhyMemDescListDumpCmd()
|
||||||
PrintStructC99Cmd()
|
PrintStructC99Cmd()
|
||||||
ListDumpCmd()
|
ListDumpCmd()
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
add-symbol-file kernel.sym
|
add-symbol-file kernel.sym
|
||||||
source custom_gdb_extension.py
|
source custom_gdb_extension.py
|
||||||
target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M
|
#For ASM sources
|
||||||
|
directory arch/x86/:core
|
||||||
|
target remote | qemu-system-i386 -S -gdb stdio -kernel kernel -m 16M -serial file:serialOut
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
// from
|
// from
|
||||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data
|
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data
|
||||||
@ -536,6 +536,7 @@ int ATAReadSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
|
|||||||
int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
|
int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
|
||||||
{
|
{
|
||||||
struct ata_controller *ctl = dev->ctl;
|
struct ata_controller *ctl = dev->ctl;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
mutexLock(&ctl->mutex);
|
mutexLock(&ctl->mutex);
|
||||||
|
|
||||||
@ -561,7 +562,8 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
|
|||||||
printf("ATA write error\n");
|
printf("ATA write error\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
while (1) {
|
int retry = 100;
|
||||||
|
while (retry) {
|
||||||
int status = inb(ctl->base + ATA_PIO_STATUS);
|
int status = inb(ctl->base + ATA_PIO_STATUS);
|
||||||
if (status & ATA_PIO_STATUS_REG_ERR) {
|
if (status & ATA_PIO_STATUS_REG_ERR) {
|
||||||
mutexUnlock(&ctl->mutex);
|
mutexUnlock(&ctl->mutex);
|
||||||
@ -571,12 +573,19 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
|
|||||||
if ((status & ATA_PIO_STATUS_DRQ) &&
|
if ((status & ATA_PIO_STATUS_DRQ) &&
|
||||||
!(status & ATA_PIO_STATUS_DRIVE_BUSY))
|
!(status & ATA_PIO_STATUS_DRIVE_BUSY))
|
||||||
break;
|
break;
|
||||||
|
retry--;
|
||||||
|
}
|
||||||
|
if(retry == 0){
|
||||||
|
printf("ATA write timeout error\n");
|
||||||
|
ret=-1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
for (uint i = 0; i < (DISK_SECTOR_SIZE / sizeof(uint16_t)); i++) {
|
for (uint i = 0; i < (DISK_SECTOR_SIZE / sizeof(uint16_t)); i++) {
|
||||||
outw(ctl->base + ATA_PIO_DATA, *ptr++);
|
outw(ctl->base + ATA_PIO_DATA, *ptr++);
|
||||||
}
|
}
|
||||||
|
retry = 1000;
|
||||||
// Wait for the device to receive the data
|
// Wait for the device to receive the data
|
||||||
while (1) {
|
while (retry) {
|
||||||
int status = inb(ctl->base + ATA_PIO_STATUS);
|
int status = inb(ctl->base + ATA_PIO_STATUS);
|
||||||
if (status & ATA_PIO_STATUS_REG_ERR) {
|
if (status & ATA_PIO_STATUS_REG_ERR) {
|
||||||
mutexUnlock(&ctl->mutex);
|
mutexUnlock(&ctl->mutex);
|
||||||
@ -586,12 +595,18 @@ int ATAWriteSector(struct ata_device *dev, int lba, uint nb_sector, void *buf)
|
|||||||
if (!(status & ATA_PIO_STATUS_DRQ) &&
|
if (!(status & ATA_PIO_STATUS_DRQ) &&
|
||||||
!(status & ATA_PIO_STATUS_DRIVE_BUSY))
|
!(status & ATA_PIO_STATUS_DRIVE_BUSY))
|
||||||
break;
|
break;
|
||||||
|
retry --;
|
||||||
|
}
|
||||||
|
if(retry == 0){
|
||||||
|
printf("ATA write data timeout error\n");
|
||||||
|
ret=-1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutexUnlock(&ctl->mutex);
|
mutexUnlock(&ctl->mutex);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ata_device *ATAGetDevice(int ctlId, int devId)
|
struct ata_device *ATAGetDevice(int ctlId, int devId)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "io.h"
|
#include "io.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
#include "time.h"
|
#include "time.h"
|
||||||
|
|
||||||
int pitSetup(unsigned int freq)
|
int pitSetup(unsigned int freq)
|
||||||
@ -20,8 +20,8 @@ int pitSetup(unsigned int freq)
|
|||||||
struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu)
|
struct cpu_state *pitIrqHandler(struct cpu_state *prevCpu)
|
||||||
{
|
{
|
||||||
__atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED);
|
__atomic_add_fetch(&jiffies, 1, __ATOMIC_RELAXED);
|
||||||
kthreadOnJieffiesTick();
|
threadOnJieffiesTick();
|
||||||
// Uncomment for non-preemptible kernel
|
// Uncomment for non-preemptible kernel
|
||||||
// return prevCpu;
|
// return prevCpu;
|
||||||
return kthreadSwitch(prevCpu);
|
return threadSwitch(prevCpu);
|
||||||
}
|
}
|
||||||
|
160
tests/test.c
160
tests/test.c
@ -1,15 +1,21 @@
|
|||||||
#include "alloc.h"
|
#include "alloc.h"
|
||||||
|
#include "allocArea.h"
|
||||||
#include "ata.h"
|
#include "ata.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "cpu_context.h"
|
#include "cpu_context.h"
|
||||||
|
#include "kernel.h"
|
||||||
#include "klibc.h"
|
#include "klibc.h"
|
||||||
#include "kthread.h"
|
#include "thread.h"
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "mmuContext.h"
|
||||||
#include "paging.h"
|
#include "paging.h"
|
||||||
|
#include "process.h"
|
||||||
#include "serial.h"
|
#include "serial.h"
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
#include "synchro.h"
|
#include "synchro.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
#include "swintr.h"
|
||||||
#include "time.h"
|
#include "time.h"
|
||||||
|
|
||||||
void testMemcpyPerf()
|
void testMemcpyPerf()
|
||||||
@ -84,14 +90,22 @@ static void *testAllocNSet(size_t size)
|
|||||||
return allocated;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testAlloc(void)
|
void testAllocArea(){
|
||||||
|
vaddr_t area = areaAlloc(1, 0);
|
||||||
|
vaddr_t area2 = areaAlloc(1, AREA_PHY_MAP);
|
||||||
|
assert(area != area2);
|
||||||
|
areaFree(area);
|
||||||
|
areaFree(area2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testAlloc(void)
|
||||||
{
|
{
|
||||||
assert(malloc(1410065407) == NULL);
|
assert(malloc(1410065407) == NULL);
|
||||||
for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabEntry)); i++) {
|
for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabEntry)); i++) {
|
||||||
malloc(sizeof(struct slabEntry));
|
assert(malloc(sizeof(struct slabEntry)) != NULL);
|
||||||
}
|
}
|
||||||
for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabDesc)); i++) {
|
for (uint i = 0; i < PAGE_SIZE / (sizeof(struct slabDesc)); i++) {
|
||||||
malloc(sizeof(struct slabDesc));
|
assert(malloc(sizeof(struct slabDesc)) != NULL);
|
||||||
}
|
}
|
||||||
assert(malloc(1));
|
assert(malloc(1));
|
||||||
assert(malloc(2));
|
assert(malloc(2));
|
||||||
@ -103,6 +117,8 @@ static void testAlloc(void)
|
|||||||
free(malloc2);
|
free(malloc2);
|
||||||
void *malloc3 = malloc(sizeof(void *));
|
void *malloc3 = malloc(sizeof(void *));
|
||||||
assertmsg((char *)malloc2 == (char *)malloc3, " %d %d\n", malloc2, malloc3);
|
assertmsg((char *)malloc2 == (char *)malloc3, " %d %d\n", malloc2, malloc3);
|
||||||
|
free(malloc1);
|
||||||
|
free(malloc3);
|
||||||
void *alloc1 = testAllocNSet(1024);
|
void *alloc1 = testAllocNSet(1024);
|
||||||
void *alloc2 = testAllocNSet(1024);
|
void *alloc2 = testAllocNSet(1024);
|
||||||
void *alloc3 = testAllocNSet(1024);
|
void *alloc3 = testAllocNSet(1024);
|
||||||
@ -133,7 +149,7 @@ static void testAlloc(void)
|
|||||||
free(alloc16);
|
free(alloc16);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void testPaging(void)
|
void testPaging(void)
|
||||||
{
|
{
|
||||||
printf("Testing paging\n");
|
printf("Testing paging\n");
|
||||||
struct phyMemDesc *allocated_page_list;
|
struct phyMemDesc *allocated_page_list;
|
||||||
@ -143,9 +159,7 @@ static void testPaging(void)
|
|||||||
int allocCount = 0;
|
int allocCount = 0;
|
||||||
int freeCount = 0;
|
int freeCount = 0;
|
||||||
|
|
||||||
while ((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL) {
|
while ((page = (struct phyMemDesc *)areaAlloc(1, AREA_PHY_MAP)) != NULL) {
|
||||||
assertmsg(pageMap((vaddr_t)page, (paddr_t)page, PAGING_MEM_WRITE) == 0,
|
|
||||||
"Fail to map page %d\n", allocCount);
|
|
||||||
memset(page, allocCount, PAGE_SIZE);
|
memset(page, allocCount, PAGE_SIZE);
|
||||||
allocCount++;
|
allocCount++;
|
||||||
list_add_tail(allocated_page_list, page);
|
list_add_tail(allocated_page_list, page);
|
||||||
@ -156,12 +170,12 @@ static void testPaging(void)
|
|||||||
(page = list_pop_head(allocated_page_list)) != NULL) {
|
(page = list_pop_head(allocated_page_list)) != NULL) {
|
||||||
assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %d\n",
|
assertmsg((char)page->phy_addr == (char)freeCount, "page modified %d but is %d\n",
|
||||||
freeCount, page->phy_addr);
|
freeCount, page->phy_addr);
|
||||||
assertmsg(unrefPhyPage((ulong)page) >= 0, "Failed to free page %d\n", (ulong)page);
|
areaFree((vaddr_t)page);
|
||||||
pageUnmap((vaddr_t)page);
|
|
||||||
freeCount++;
|
freeCount++;
|
||||||
}
|
}
|
||||||
printf("%d pages freed\n", freeCount);
|
printf("%d pages freed\n", freeCount);
|
||||||
|
|
||||||
|
assert(freeCount == allocCount);
|
||||||
assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL,
|
assertmsg((page = (struct phyMemDesc *)allocPhyPage(1)) != NULL,
|
||||||
"Cannot allocate memory\n");
|
"Cannot allocate memory\n");
|
||||||
unrefPhyPage((ulong)page);
|
unrefPhyPage((ulong)page);
|
||||||
@ -202,6 +216,7 @@ static void reclaim_stack(void *stack_vaddr)
|
|||||||
|
|
||||||
static void exit_hello12(void *stack_vaddr)
|
static void exit_hello12(void *stack_vaddr)
|
||||||
{
|
{
|
||||||
|
printf("Stopping hello\n");
|
||||||
cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t *)reclaim_stack,
|
cpu_context_exit_to(ctxt_main, (cpu_kstate_function_arg1_t *)reclaim_stack,
|
||||||
(vaddr_t)stack_vaddr);
|
(vaddr_t)stack_vaddr);
|
||||||
}
|
}
|
||||||
@ -264,7 +279,7 @@ static void kthread1(void *strIn)
|
|||||||
char *str = (char *)strIn;
|
char *str = (char *)strIn;
|
||||||
for (; *str != '\n'; str++) {
|
for (; *str != '\n'; str++) {
|
||||||
printf("kth1: %c\n", *str);
|
printf("kth1: %c\n", *str);
|
||||||
kthreadYield();
|
threadYield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +288,7 @@ static void kthread2(void *strIn)
|
|||||||
char *str = (char *)strIn;
|
char *str = (char *)strIn;
|
||||||
for (; *str != '\n'; str++) {
|
for (; *str != '\n'; str++) {
|
||||||
printf("kth2: %c\n", *str);
|
printf("kth2: %c\n", *str);
|
||||||
kthreadYield();
|
threadYield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,11 +301,11 @@ void sleepThread(void *arg)
|
|||||||
while (secSleep < 5) {
|
while (secSleep < 5) {
|
||||||
// printf("Sleeping loop %d\n", secSleep);
|
// printf("Sleeping loop %d\n", secSleep);
|
||||||
secSleep++;
|
secSleep++;
|
||||||
kthreadMsleep(1000);
|
threadMsleep(100);
|
||||||
}
|
}
|
||||||
unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies);
|
unsigned long ellapsedTime = jiffies_to_msecs(jiffies - initialJiffies);
|
||||||
assertmsg(ellapsedTime >= 5000 && ellapsedTime < 5100, "ellapsedTime %d\n", ellapsedTime);
|
assertmsg(ellapsedTime >= 500 && ellapsedTime < 510, "ellapsedTime %d\n", ellapsedTime);
|
||||||
kthreadMsleep(0);
|
threadMsleep(0);
|
||||||
printf("I should never be showed\n");
|
printf("I should never be showed\n");
|
||||||
assert(1);
|
assert(1);
|
||||||
}
|
}
|
||||||
@ -305,7 +320,7 @@ void mutThread(void *arg)
|
|||||||
while (test > 0) {
|
while (test > 0) {
|
||||||
mutexLock(&mutexTest);
|
mutexLock(&mutexTest);
|
||||||
printf("%s sleep\n", (char *)arg);
|
printf("%s sleep\n", (char *)arg);
|
||||||
kthreadMsleep(1000);
|
threadMsleep(100);
|
||||||
printf("%s up\n", (char *)arg);
|
printf("%s up\n", (char *)arg);
|
||||||
mutexUnlock(&mutexTest);
|
mutexUnlock(&mutexTest);
|
||||||
test--;
|
test--;
|
||||||
@ -317,7 +332,7 @@ void wqThread(void *arg)
|
|||||||
(void)arg;
|
(void)arg;
|
||||||
DECLARE_WAITQUEUE(test);
|
DECLARE_WAITQUEUE(test);
|
||||||
waitQueueInit(&test);
|
waitQueueInit(&test);
|
||||||
assert(waitTimeout(&test, 1000) == 1);
|
assert(waitTimeout(&test, 100) == 1);
|
||||||
waitQueueFree(&test);
|
waitQueueFree(&test);
|
||||||
haveTimeout = 1;
|
haveTimeout = 1;
|
||||||
}
|
}
|
||||||
@ -326,17 +341,18 @@ void testKthread()
|
|||||||
{
|
{
|
||||||
mutexInit(&mutexTest);
|
mutexInit(&mutexTest);
|
||||||
// It is not expected to have necessarily "Hello world\n" properly written
|
// It is not expected to have necessarily "Hello world\n" properly written
|
||||||
kthreadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n");
|
threadCreate("Test2", (cpu_kstate_function_arg1_t *)kthread2, (void *)"el ol\n");
|
||||||
kthreadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n");
|
threadCreate("Test1", (cpu_kstate_function_arg1_t *)kthread1, (void *)"Hlowrd\n");
|
||||||
kthreadMsleep(1000);
|
threadMsleep(100);
|
||||||
kthreadCreate("wq timeout", wqThread, NULL);
|
threadCreate("wq timeout", wqThread, NULL);
|
||||||
kthreadMsleep(2000);
|
threadMsleep(200);
|
||||||
assert(haveTimeout);
|
assert(haveTimeout);
|
||||||
kthreadCreate("sleep", sleepThread, NULL);
|
threadCreate("sleep", sleepThread, NULL);
|
||||||
kthreadMsleep(5000);
|
threadMsleep(500);
|
||||||
kthreadCreate("mtest1", mutThread, "mut1");
|
threadCreate("mtest1", mutThread, "mut1");
|
||||||
kthreadCreate("mtest2", mutThread, "mut2");
|
threadCreate("mtest2", mutThread, "mut2");
|
||||||
kthreadCreate("mtest3", mutThread, "mut3");
|
threadCreate("mtest3", mutThread, "mut3");
|
||||||
|
threadMsleep(2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testATAThread(){
|
void testATAThread(){
|
||||||
@ -354,13 +370,92 @@ void testATAThread(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testATA(){
|
static void testATA(){
|
||||||
kthreadCreate("ATA_TEST", testATAThread, NULL);
|
threadCreate("ATA_TEST", testATAThread, NULL);
|
||||||
//testATAThread();
|
//testATAThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testMMUContext()
|
||||||
|
{
|
||||||
|
printf("Testing mmu\n");
|
||||||
|
struct mmu_context *current = mmuContextGetCurrent();
|
||||||
|
assert(current != NULL);
|
||||||
|
struct mmu_context *new = mmuContextCreate();
|
||||||
|
assert(new != NULL);
|
||||||
|
mmuContextSwitch(new);
|
||||||
|
mmuContextSwitch(current);
|
||||||
|
mmuContextUnref(new);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void userProgramm()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
asm volatile("movl %1,%%eax \n"
|
||||||
|
"movl %2,%%ebx \n"
|
||||||
|
"movl %3,%%ecx \n"
|
||||||
|
"movl %4,%%edx \n"
|
||||||
|
"int %5\n"
|
||||||
|
"movl %%eax, %0"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(SYSCALL_ID_WRITE), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB)
|
||||||
|
: "eax", "ebx", "ecx", "edx");
|
||||||
|
asm volatile("movl %1,%%eax \n"
|
||||||
|
"movl %2,%%ebx \n"
|
||||||
|
"movl %3,%%ecx \n"
|
||||||
|
"movl %4,%%edx \n"
|
||||||
|
"int %5\n"
|
||||||
|
"movl %%eax, %0"
|
||||||
|
: "=g"(ret)
|
||||||
|
: "g"(SYSCALL_ID_EXIT), "g"(0), "g"(0), "g"(0), "i"(SYSCALL_INTR_NB)
|
||||||
|
: "eax", "ebx", "ecx", "edx");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testProcess(){
|
||||||
|
struct process *proc= processCreate("TESTPROCESS");
|
||||||
|
struct thread *th1 = threadCreate("th1", sleepThread, NULL);
|
||||||
|
struct thread *th2 = threadCreate("th2", sleepThread, NULL);
|
||||||
|
processAddThread(proc, th1);
|
||||||
|
processAddThread(proc, th2);
|
||||||
|
processListPrint();
|
||||||
|
threadMsleep(600);
|
||||||
|
|
||||||
|
processRemoveThread(th1);
|
||||||
|
processRemoveThread(th2);
|
||||||
|
processUnref(proc);
|
||||||
|
printf("Next process list should be empty\n");
|
||||||
|
processListPrint();
|
||||||
|
printf("Running user space app\n");
|
||||||
|
struct process *proc2= processCreate("TESTPROCESS2");
|
||||||
|
|
||||||
|
threadChangeCurrentContext(processGetMMUContext(proc2));
|
||||||
|
uaddr_t stackTop = 0xfffffffc;
|
||||||
|
uaddr_t stackBottom = ALIGN_DOWN(stackTop, PAGE_SIZE);
|
||||||
|
paddr_t stackPhy = allocPhyPage(1);
|
||||||
|
assert(pageMap(stackBottom, stackPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0);
|
||||||
|
unrefPhyPage(stackPhy);
|
||||||
|
|
||||||
|
uaddr_t mem = PAGING_BASE_USER_ADDRESS;
|
||||||
|
paddr_t memPhy = allocPhyPage(1);
|
||||||
|
assert(pageMap(mem, memPhy, PAGING_MEM_USER | PAGING_MEM_WRITE | PAGING_MEM_READ) == 0);
|
||||||
|
unrefPhyPage(memPhy);
|
||||||
|
|
||||||
|
memcpy((void *)mem, userProgramm, PAGE_SIZE);
|
||||||
|
|
||||||
|
threadCreateUser("UserProg", proc2, mem, 0, 0, stackTop);
|
||||||
|
threadYield();
|
||||||
|
processUnref(proc2);
|
||||||
|
threadChangeCurrentContext(NULL);
|
||||||
|
|
||||||
|
printf("Running user space app DONE\n");
|
||||||
|
}
|
||||||
|
|
||||||
void run_test(void)
|
void run_test(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
uint freemem, usedmem;
|
||||||
|
uint afterFreemem, afterUsedmem;
|
||||||
|
memGetStat(&freemem, &usedmem);
|
||||||
testATA();
|
testATA();
|
||||||
testMemcpyPerf();
|
testMemcpyPerf();
|
||||||
{
|
{
|
||||||
@ -388,8 +483,15 @@ void run_test(void)
|
|||||||
serialPutc('l');
|
serialPutc('l');
|
||||||
serialPutc('o');
|
serialPutc('o');
|
||||||
testAlloc();
|
testAlloc();
|
||||||
|
testAllocArea();
|
||||||
printf("Testing backtrace\n");
|
printf("Testing backtrace\n");
|
||||||
test_backtrace();
|
test_backtrace();
|
||||||
testCoroutine();
|
testCoroutine();
|
||||||
|
int nbThread = threadCount();
|
||||||
testKthread();
|
testKthread();
|
||||||
|
assert(nbThread + 1 == threadCount());//For sleep Thread
|
||||||
|
testMMUContext();
|
||||||
|
testProcess();
|
||||||
|
memGetStat(&afterFreemem, &afterUsedmem);
|
||||||
|
printf("free %d -> %d\n", freemem, afterFreemem);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user