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:
mathieu 2021-11-04 16:17:35 +01:00
commit f81a515514
45 changed files with 2259 additions and 580 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

12
core/mmuContext.h Normal file
View File

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

168
core/process.c Normal file
View File

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

17
core/process.h Normal file
View File

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

View File

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

View File

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

27
core/syscall.c Normal file
View File

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

9
core/syscall.h Normal file
View File

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

391
core/thread.c Normal file
View File

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

90
core/thread.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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