add first steps to support user-level tasks

This commit is contained in:
Stefan Lankes 2013-12-06 09:19:42 +01:00
parent aed7b1ce8d
commit b83bc1d261
15 changed files with 415 additions and 28 deletions

View file

@ -58,7 +58,7 @@ inline static void irq_disable(void) {
*
* @return The set of flags which have been set until now
*/
inline static uint32_t irq_nested_disable(void) {
inline static uint8_t irq_nested_disable(void) {
size_t flags;
asm volatile("pushf; cli; pop %0": "=r"(flags) : : "memory");
if (flags & (1 << 9))

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2011, Stefan Lankes, RWTH Aachen University
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Stefan Lankes
* @file arch/x86/include/asm/syscall.h
* @brief Systemcall related code
*
* This file defines the syscall function and convenience
* based macro definitions for calling it.
*/
#ifndef __ARCH_SYSCALL_H__
#define __ARCH_SYSCALL_H__
#include <eduos/stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define _STR(token) #token
#define _SYSCALLSTR(x) "int $" _STR(x) " "
/** @brief the syscall function which issues an interrupt to the kernel
*
* It's supposed to be used by the macros defined in this file as the could would read
* cleaner then.
*
* @param nr System call number
* @param arg0 Argument 0
* @param arg1 Argument 1
* @param arg2 Argument 2
* @param arg3 Argument 3
* @param arg4 Argument 4
* @return The return value of the system call
*/
inline static long
syscall(int nr, unsigned long arg0, unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4)
{
long res;
asm volatile (_SYSCALLSTR(INT_SYSCALL)
: "=a" (res)
: "0" (nr), "b" (arg0), "c" (arg1), "d" (arg2), "S" (arg3), "D" (arg4)
: "memory", "cc");
return res;
}
/// System call macro with one single argument; the syscall number
#define SYSCALL0(NR) \
syscall(NR, 0, 0, 0, 0, 0)
/// System call macro with system call number and one argument
#define SYSCALL1(NR, ARG1) \
syscall(NR, (unsigned long)ARG1, 0, 0, 0, 0)
/// System call macro with system call number and 2 arguments
#define SYSCALL2(NR, ARG1, ARG2) \
syscall(NR, (unsigned long)ARG1, (unsigned long)ARG2, 0, 0, 0)
/// System call macro with system call number and 3 arguments
#define SYSCALL3(NR, ARG1, ARG2, ARG3) \
syscall(NR, (unsigned long)ARG1, (unsigned long)ARG2, (unsigned long)ARG3, 0, 0)
/// System call macro with system call number and 4 arguments
#define SYSCALL4(NR, ARG1, ARG2, ARG3, ARG4) \
syscall(NR, (unsigned long)ARG1, (unsigned long)ARG2, (unsigned long)ARG3, (unsigned long) ARG4, 0)
/// System call macro with system call number and 5 arguments
#define SYSCALL5(NR, ARG1, ARG2, ARG3, ARG4) \
syscall(NR, (unsigned long)ARG1, (unsigned long)ARG2, (unsigned long)ARG3, (unsigned long) ARG4, (unsigned long) ARG5)
#ifdef __cplusplus
}
#endif
#endif

View file

@ -59,7 +59,21 @@ void switch_context(size_t** stack);
* - 0 on success
* - -EINVAL (-22) on failure
*/
int create_default_frame(task_t* task, entry_point_t ep, void* arg);
int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user);
/** @brief Register a task's TSS at GDT
*
* @return
* - 0 on success
*/
static inline int register_task(void)
{
uint16_t sel = 5 << 3;
asm volatile ("ltr %%ax" : : "a"(sel));
return 0;
}
#ifdef __cplusplus
}

View file

@ -196,6 +196,47 @@ isrstub_pseudo_error 9
%assign i i+1
%endrep
extern syscall_handler
global isrsyscall
; used to realize system calls
; by entering the handler, the interrupt flag is not cleared
isrsyscall:
cli
push es
push ds
push ebp
push edi
push esi
push edx
push ecx
push ebx
push eax
; set kernel data segmenets
mov ax, 0x10
mov ds, ax
mov es, ax
mov eax, [esp]
sti
call syscall_handler
cli
add esp, 4 ; eax contains the return value
; => we did not restore eax
pop ebx
pop ecx
pop edx
pop esi
pop edi
pop ebp
pop ds
pop es
sti
iret
extern irq_handler
extern get_current_stack
extern finish_task_switch

View file

@ -36,9 +36,9 @@
#include <asm/tss.h>
gdt_ptr_t gp;
static tss_t task_state_segment __attribute__ ((aligned (PAGE_SIZE)));
static tss_t task_state_segment __attribute__ ((aligned (PAGE_SIZE)));
// currently, our kernel has full access to the ioports
static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}};
static gdt_entry_t gdt[GDT_ENTRIES] = {[0 ... GDT_ENTRIES-1] = {0, 0, 0, 0, 0, 0}};
/*
* This is defined in entry.asm. We use this to properly reload

View file

@ -74,6 +74,8 @@ void idt_set_gate(unsigned char num, size_t base, unsigned short sel,
configure_idt_entry(&idt[num], base, sel, flags);
}
extern void isrsyscall(void);
/* Installs the IDT */
void idt_install(void)
{
@ -87,6 +89,8 @@ void idt_install(void)
idtp.base = (size_t)&idt;
/* Add any new ISRs to the IDT here using idt_set_gate */
idt_set_gate(INT_SYSCALL, (size_t)isrsyscall, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING3|IDT_FLAG_32BIT|IDT_FLAG_TRAPGATE);
}
/* Points the processor's internal register to the new IDT */

View file

@ -32,6 +32,8 @@
#include <eduos/errno.h>
#include <eduos/processor.h>
static unsigned char ustacks[MAX_TASKS][KERNEL_STACK_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
size_t* get_current_stack(void)
{
task_t* curr_task = current_task;
@ -39,9 +41,11 @@ size_t* get_current_stack(void)
return curr_task->last_stack_pointer;
}
int create_default_frame(task_t* task, entry_point_t ep, void* arg)
int create_default_frame(task_t* task, entry_point_t ep, void* arg, uint8_t user)
{
size_t *stack;
uint16_t cs = user ? 0x1B : 0x08;
uint16_t ds = user ? 0x23 : 0x10;
size_t *stack, *ustack;
struct state *stptr;
size_t state_size;
@ -52,12 +56,16 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg)
return -EINVAL;
memset(task->stack, 0xCD, KERNEL_STACK_SIZE);
memset(ustacks[task->id] , 0xCD, KERNEL_STACK_SIZE);
/* The difference between setting up a task for SW-task-switching
* and not for HW-task-switching is setting up a stack and not a TSS.
* This is the stack which will be activated and popped off for iret later.
*/
stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned
if (user)
stack = (size_t*) (ustacks[task->id] + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned
else
stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16); // => stack is 16byte aligned
/* Only marker for debugging purposes, ... */
*stack-- = 0xDEADBEEF;
@ -66,14 +74,21 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg)
/* and the "caller" we shall return to.
* This procedure cleans the task after exit. */
*stack = (size_t) leave_kernel_task;
if (user)
*stack = (size_t) leave_user_task;
else
*stack = (size_t) leave_kernel_task;
/* Next bunch on the stack is the initial register state.
* The stack must look like the stack of a task which was
* scheduled away previously. */
/* In legacy modes, this push is conditional and based on a change in current privilege level (CPL).*/
state_size = sizeof(struct state) - 2*sizeof(size_t);
if (user) {
ustack = stack;
stack = (size_t*) (task->stack + KERNEL_STACK_SIZE - 16);
state_size = sizeof(struct state);
} else state_size = sizeof(struct state) - 2*sizeof(size_t);
stack = (size_t*) ((size_t) stack - state_size);
stptr = (struct state *) stack;
@ -86,11 +101,16 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg)
/* The instruction pointer shall be set on the first function to be called
after IRETing */
stptr->eip = (size_t)ep;
stptr->cs = 0x08;
stptr->ds = stptr->es = 0x10;
stptr->cs = cs;
stptr->ds = stptr->es = ds;
stptr->eflags = 0x1202;
// the creation of a kernel tasks didn't change the IOPL level
// => useresp & ss is not required
if (user) {
// the creation of a user-level tasks change the IOPL level
// => useresp & ss is required
stptr->ss = ds;
stptr->useresp = (size_t)ustack;
}
/* Set the task's stack pointer entry to the stack we have crafted right now. */
task->last_stack_pointer = (size_t*)stack;

View file

@ -60,9 +60,9 @@ static void timer_handler(struct state *s)
* Every TIMER_FREQ clocks (approximately 1 second), we will
* display a message on the screen
*/
/*if (timer_ticks % TIMER_FREQ == 0) {
if (timer_ticks % TIMER_FREQ == 0) {
vga_puts("One second has passed\n");
}*/
}
}
#define LATCH(f) ((CLOCK_TICK_RATE + f/2) / f)

View file

@ -40,6 +40,7 @@ extern "C" {
#define CACHE_LINE 64
#define KERNEL_STACK_SIZE (8*1024)
#define PAGE_SHIFT 12
#define INT_SYSCALL 0x80
#define BYTE_ORDER LITTLE_ENDIAN

84
include/eduos/syscall.h Normal file
View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2011, Stefan Lankes, RWTH Aachen University
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author Stefan Lankes
* @file include/eduos/syscall.h
* @brief System call number definitions
*
* This file contains define constants for every syscall's number.
*/
#ifndef __SYSCALL_H__
#define __SYSCALL_H__
#include <eduos/stddef.h>
#include <asm/syscall.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __NR_exit 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_read 4
#define __NR_lseek 6
#define __NR_unlink 7
#define __NR_getpid 8
#define __NR_kill 9
#define __NR_fstat 10
#define __NR_sbrk 11
#define __NR_fork 12
#define __NR_wait 13
#define __NR_execve 14
#define __NR_times 15
#define __NR_accept 16
#define __NR_bind 17
#define __NR_closesocket 18
#define __NR_connect 19
#define __NR_listen 20
#define __NR_recv 21
#define __NR_send 22
#define __NR_socket 23
#define __NR_getsockopt 24
#define __NR_setsockopt 25
#define __NR_gethostbyname 26
#define __NR_sendto 27
#define __NR_recvfrom 28
#define __NR_select 29
#define __NR_stat 30
#define __NR_dup 31
#define __NR_dup2 32
#define __NR_clone 33
#ifdef __cplusplus
}
#endif
#endif

View file

@ -44,6 +44,9 @@
extern "C" {
#endif
/** @brief System call to terminate a user level process */
void NORETURN sys_exit(int);
/** @brief Task switcher
*
* Timer-interrupted use of this function for task switching
@ -65,7 +68,7 @@ size_t** scheduler(void);
*/
int multitasking_init(void);
/** @brief create a kernel task.
/** @brief create a kernel-level task.
*
* @param id The value behind this pointer will be set to the new task's id
* @param ep Pointer to the entry function for the new task
@ -78,6 +81,19 @@ int multitasking_init(void);
*/
int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio);
/** @brief create a user-level task.
*
* @param id The value behind this pointer will be set to the new task's id
* @param ep Pointer to the entry function for the new task
* @param args Arguments the task shall start with
* @param prio Desired priority of the new kernel task
*
* @return
* - 0 on success
* - -EINVAL (-22) on failure
*/
int create_user_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio);
/** @brief determine the highest priority of all tasks, which are ready
*
* @return
@ -115,9 +131,12 @@ int block_current_task(void);
/** @brief Abort current task */
void NORETURN abort(void);
/** @brief This function shall be called by leaving kernel level tasks */
/** @brief This function shall be called by leaving kernel-level tasks */
void NORETURN leave_kernel_task(void);
/** @brief This function shall be called by leaving user-level tasks */
void NORETURN leave_user_task(void);
#ifdef __cplusplus
}
#endif

View file

@ -1,4 +1,4 @@
C_source := main.c tasks.c
C_source := main.c tasks.c syscall.c
MODULE := kernel
include $(TOPDIR)/Makefile.inc

View file

@ -32,7 +32,7 @@
#include <eduos/tasks.h>
#include <eduos/processor.h>
#include <eduos/tasks.h>
#include <eduos/semaphore.h>
#include <eduos/syscall.h>
#include <asm/irq.h>
#include <asm/irqflags.h>
@ -47,7 +47,14 @@ extern const void bss_end;
extern char __BUILD_DATE;
extern char __BUILD_TIME;
static sem_t sem;
/*static*/ int userfoo(void* arg)
{
//SYSCALL1(__NR_write, "hello from userfoo\n");
//kprintf("hello from %s\n", (char*) arg);
while(1) { }
return 0;
}
static int foo(void* arg)
{
@ -57,6 +64,11 @@ static int foo(void* arg)
kprintf("hello from %s\n", (char*) arg);
}
// demo of an exception
/*i = 0;
i = 32 / i;
kprintf("i = %d\n", i);*/
return 0;
}
@ -88,9 +100,8 @@ int main(void)
kprintf("Processor frequency: %u MHz\n", get_cpu_frequency());
sem_init(&sem, 1);
create_kernel_task(&id1, foo, "foo1", NORMAL_PRIO);
create_kernel_task(&id2, foo, "foo2", NORMAL_PRIO);
create_user_task(&id2, userfoo, "userfoo", NORMAL_PRIO);
while(1) {
HALT;

67
kernel/syscall.c Normal file
View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2010, Stefan Lankes, RWTH Aachen University
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <eduos/stddef.h>
#include <eduos/stdio.h>
#include <eduos/tasks.h>
#include <eduos/errno.h>
#include <eduos/syscall.h>
static int sys_write(const char* buff)
{
kputs("bbba\n");
kputs(buff);
return 0;
}
int syscall_handler(uint32_t sys_nr, ...)
{
int ret = -EINVAL;
va_list vl;
va_start(vl, sys_nr);
switch(sys_nr)
{
case __NR_exit:
sys_exit(va_arg(vl, uint32_t));
ret = 0;
break;
case __NR_write:
ret = sys_write(va_arg(vl, const char*));
break;
default:
kputs("invalid system call\n");
ret = -ENOSYS;
break;
};
va_end(vl);
return ret;
}

View file

@ -28,10 +28,12 @@
#include <eduos/stddef.h>
#include <eduos/stdlib.h>
#include <eduos/stdio.h>
#include <eduos/string.h>
#include <eduos/tasks.h>
#include <eduos/tasks_types.h>
#include <eduos/spinlock.h>
#include <eduos/errno.h>
#include <eduos/syscall.h>
/** @brief Array of task structures (aka PCB)
*
@ -71,6 +73,9 @@ int multitasking_init(void)
task_table[0].prio = IDLE_PRIO;
task_table[0].stack = (void*) &boot_stack;
// register idle task
register_task();
return 0;
}
@ -127,6 +132,15 @@ static void NORETURN do_exit(int arg)
}
}
/** @brief A procedure to be called by user-level tasks */
void NORETURN leave_user_task(void)
{
SYSCALL1(__NR_exit, 0);
// we should never reach this point
while(1) { HALT; }
}
/** @brief A procedure to be called by kernel tasks */
void NORETURN leave_kernel_task(void) {
int result;
@ -135,6 +149,11 @@ void NORETURN leave_kernel_task(void) {
do_exit(result);
}
/** @brief To be called by the systemcall to exit tasks */
void NORETURN sys_exit(int arg) {
do_exit(arg);
}
/** @brief Aborting a task is like exiting it with result -1 */
void NORETURN abort(void) {
do_exit(-1);
@ -152,7 +171,7 @@ void NORETURN abort(void) {
* - 0 on success
* - -ENOMEM (-12) or -EINVAL (-22) on failure
*/
static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio)
static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio, uint8_t user)
{
int ret = -ENOMEM;
uint32_t i;
@ -177,7 +196,7 @@ static int create_task(tid_t* id, entry_point_t ep, void* arg, uint8_t prio)
if (id)
*id = i;
ret = create_default_frame(task_table+i, ep, arg);
ret = create_default_frame(task_table+i, ep, arg, user);
// add task in the readyqueues
spinlock_irqsave_lock(&readyqueues.lock);
@ -208,7 +227,15 @@ int create_kernel_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio)
if (prio > MAX_PRIO)
prio = NORMAL_PRIO;
return create_task(id, ep, args, prio);
return create_task(id, ep, args, prio, 0);
}
int create_user_task(tid_t* id, entry_point_t ep, void* args, uint8_t prio)
{
if (prio > MAX_PRIO)
prio = NORMAL_PRIO;
return create_task(id, ep, args, prio, 1);
}
/** @brief Wakeup a blocked task
@ -247,7 +274,7 @@ int wakeup_task(tid_t id)
task->next = NULL;
readyqueues.queue[prio-1].last->next = task;
readyqueues.queue[prio-1].last = task;
}
}
spinlock_irqsave_unlock(&readyqueues.lock);
}
@ -358,7 +385,7 @@ get_task_out:
spinlock_irqsave_unlock(&readyqueues.lock);
if (current_task != orig_task) {
//kprintf("schedule from %u to %u with prio %u\n", orig_task->id, current_task->id, (uint32_t)current_task->prio);
kprintf("schedule from %u to %u with prio %u\n", orig_task->id, current_task->id, (uint32_t)current_task->prio);
return (size_t**) &(orig_task->last_stack_pointer);
}