mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 10:53:16 +01:00
- Fixed potential concurrency bugs due to preemption being enabled. - Introduced a new address space structure to better account for address spaces and page tables. - Currently executes fine up to forking. Will investigate.
220 lines
6.3 KiB
C
220 lines
6.3 KiB
C
/*
|
|
* System Calls
|
|
*
|
|
* Copyright (C) 2007 Bahadir Balban
|
|
*/
|
|
#include <l4/lib/mutex.h>
|
|
#include <l4/lib/printk.h>
|
|
#include <l4/generic/scheduler.h>
|
|
#include <l4/generic/tcb.h>
|
|
#include <l4/generic/pgalloc.h>
|
|
#include <l4/generic/tcb.h>
|
|
#include <l4/generic/space.h>
|
|
#include <l4/api/space.h>
|
|
#include <l4/api/ipc.h>
|
|
#include <l4/api/kip.h>
|
|
#include <l4/api/errno.h>
|
|
#include <l4/api/exregs.h>
|
|
#include INC_API(syscall.h)
|
|
#include INC_ARCH(exception.h)
|
|
|
|
void print_syscall_context(struct ktcb *t)
|
|
{
|
|
syscall_context_t *r = t->syscall_regs;
|
|
|
|
printk("Thread id: %d registers: 0x%x, 0x%x, 0x%x, 0x%x, "
|
|
"0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
|
|
t->tid, r->spsr, r->r0, r->r1, r->r2, r->r3, r->r4,
|
|
r->r5, r->r6, r->r7, r->r8, r->sp_usr, r->lr_usr);
|
|
}
|
|
|
|
/* Copy each register to task's context if its valid bit is set */
|
|
void do_exchange_registers(struct ktcb *task, struct exregs_data *exregs)
|
|
{
|
|
task_context_t *context = &task->context;
|
|
|
|
/*
|
|
* NOTE:
|
|
* We don't care if register values point at invalid addresses
|
|
* since memory protection would prevent any kernel corruption.
|
|
* We do however, make sure spsr is not modified
|
|
*/
|
|
|
|
/* Check register valid bit and copy registers */
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r0))
|
|
context->r0 = exregs->context.r0;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r1))
|
|
context->r1 = exregs->context.r1;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r2))
|
|
context->r2 = exregs->context.r2;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r3))
|
|
context->r3 = exregs->context.r3;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r4))
|
|
context->r4 = exregs->context.r4;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r5))
|
|
context->r5 = exregs->context.r5;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r6))
|
|
context->r6 = exregs->context.r6;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r7))
|
|
context->r7 = exregs->context.r7;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r8))
|
|
context->r8 = exregs->context.r8;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r9))
|
|
context->r9 = exregs->context.r9;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r10))
|
|
context->r10 = exregs->context.r10;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r11))
|
|
context->r11 = exregs->context.r11;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, r12))
|
|
context->r12 = exregs->context.r12;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, sp))
|
|
context->sp = exregs->context.sp;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, lr))
|
|
context->lr = exregs->context.lr;
|
|
if (exregs->valid_vect & FIELD_TO_BIT(exregs_context_t, pc))
|
|
context->pc = exregs->context.pc;
|
|
|
|
/* Set thread's pager if one is supplied */
|
|
if (exregs->flags & EXREGS_SET_PAGER)
|
|
task->pagerid = exregs->pagerid;
|
|
|
|
/* Set thread's utcb if supplied */
|
|
if (exregs->flags & EXREGS_SET_UTCB)
|
|
task->utcb_address = exregs->utcb_address;
|
|
}
|
|
|
|
/*
|
|
* exchange_registers()
|
|
*
|
|
* This call is used by the pagers to set (and in the future read)
|
|
* the register context of a thread. The thread's registers can be
|
|
* set only when the thread is in user mode. A newly created thread
|
|
* that is the copy of another thread (forked or cloned) will also
|
|
* be given its user mode context on the first chance to execute so
|
|
* such threads can also be modified by this call before execution.
|
|
*
|
|
* A thread executing in the kernel cannot be modified since this
|
|
* would compromise the kernel. Also the thread must be in suspended
|
|
* condition so that the scheduler does not execute it as we modify
|
|
* its context.
|
|
*/
|
|
int sys_exchange_registers(syscall_context_t *regs)
|
|
{
|
|
int err = 0;
|
|
struct ktcb *task;
|
|
struct exregs_data *exregs = (struct exregs_data *)regs->r0;
|
|
l4id_t tid = regs->r1;
|
|
|
|
/* Find tcb from its list */
|
|
if (!(task = tcb_find(tid)))
|
|
return -ESRCH;
|
|
|
|
/*
|
|
* This lock ensures task is not
|
|
* inadvertently resumed by a syscall
|
|
*/
|
|
if (!mutex_trylock(&task->thread_control_lock))
|
|
return -EAGAIN;
|
|
|
|
/* Now check that the task is suspended */
|
|
if (task->state != TASK_INACTIVE) {
|
|
err = -EACTIVE;
|
|
goto out;
|
|
}
|
|
|
|
#if 0
|
|
A suspended thread implies it cannot do any harm
|
|
even if it is in kernel mode.
|
|
/*
|
|
* The thread must be in user mode for its context
|
|
* to be modified.
|
|
*/
|
|
if (!TASK_IN_USER(task)) {
|
|
err = -EPERM;
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
/* Check UTCB is in valid range */
|
|
if (exregs->flags & EXREGS_SET_UTCB &&
|
|
!(exregs->utcb_address >= UTCB_AREA_START &&
|
|
exregs->utcb_address < UTCB_AREA_END))
|
|
return -EINVAL;
|
|
|
|
/* Copy registers */
|
|
do_exchange_registers(task, exregs);
|
|
|
|
out:
|
|
/* Unlock and return */
|
|
mutex_unlock(&task->thread_control_lock);
|
|
return err;
|
|
}
|
|
|
|
int sys_schedule(syscall_context_t *regs)
|
|
{
|
|
printk("(SVC) %s called. Tid (%d)\n", __FUNCTION__, current->tid);
|
|
return 0;
|
|
}
|
|
|
|
int sys_space_control(syscall_context_t *regs)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int sys_getid(syscall_context_t *regs)
|
|
{
|
|
struct task_ids *ids = (struct task_ids *)regs->r0;
|
|
struct ktcb *this = current;
|
|
|
|
ids->tid = this->tid;
|
|
ids->spid = this->spid;
|
|
ids->tgid = this->tgid;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Granted pages *must* be outside of the pages that are already owned and used
|
|
* by the kernel, otherwise a hostile/buggy pager can attack kernel addresses by
|
|
* fooling it to use them as freshly granted pages. Kernel owned pages are
|
|
* defined as, "any page that has been used by the kernel prior to all free
|
|
* physical memory is taken by a pager, and any other page that has been granted
|
|
* so far by any such pager."
|
|
*/
|
|
int validate_granted_pages(unsigned long pfn, int npages)
|
|
{
|
|
/* FIXME: Fill this in */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Used by a pager to grant memory to kernel for its own use. Generally
|
|
* this memory is used for thread creation and memory mapping, (e.g. new
|
|
* page tables, page middle directories, per-task kernel stack etc.)
|
|
*/
|
|
int sys_kmem_control(syscall_context_t *regs)
|
|
{
|
|
unsigned long pfn = (unsigned long)regs->r0;
|
|
int npages = (int)regs->r1;
|
|
int grant = (int)regs->r2;
|
|
|
|
/* Pager is granting us pages */
|
|
if (grant) {
|
|
/*
|
|
* Check if given set of pages are outside the pages already
|
|
* owned by the kernel.
|
|
*/
|
|
if (validate_granted_pages(pfn, npages) < 0)
|
|
return -EINVAL;
|
|
|
|
/* Add the granted pages to the allocator */
|
|
if (pgalloc_add_new_grant(pfn, npages))
|
|
BUG();
|
|
} else /* Reclaim not implemented yet */
|
|
BUG();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|