mirror of
https://github.com/drasko/codezero.git
synced 2026-01-15 12:23:15 +01:00
New UTCB implementation almost working.
- KIP's pointer to UTCB seems to work with existing l4lib ipc functions. - Works up to clone() - In clone we mmap() the same UTCB on each new thread - excessive. - Generally during page fault handling, cloned threads may fault on the same page multiple times even though a single handling would be enough for all of them. Need to detect and handle this.
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
#include INC_GLUE(memlayout.h)
|
||||
#include INC_ARCH(bootdesc.h)
|
||||
|
||||
/* FIXME: Change the unit name */
|
||||
__attribute__ ((section(".data.kip"))) struct kip kip;
|
||||
|
||||
/* Error-checked kernel data request call */
|
||||
|
||||
@@ -79,12 +79,8 @@ void do_exchange_registers(struct ktcb *task, struct exregs_data *exregs)
|
||||
task->pagerid = exregs->pagerid;
|
||||
|
||||
/* Set thread's utcb if supplied */
|
||||
if (exregs->flags & EXREGS_SET_UTCB) {
|
||||
BUG(); /* Check that physical and virtual addresses are in range */
|
||||
task->utcb_phys = exregs->utcb_phys;
|
||||
task->utcb_virt = exregs->utcb_virt;
|
||||
}
|
||||
|
||||
if (exregs->flags & EXREGS_SET_UTCB)
|
||||
task->utcb_address = exregs->utcb_address;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -139,6 +135,12 @@ int sys_exchange_registers(syscall_context_t *regs)
|
||||
}
|
||||
#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);
|
||||
|
||||
|
||||
105
src/api/thread.c
105
src/api/thread.c
@@ -164,90 +164,9 @@ int thread_start(struct task_ids *ids)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given thread creation flags, determines whether to use a new user
|
||||
* (pager)-supplied utcb address, the utcb address of the original thread,
|
||||
* or no utcb at all. Validation of flags done at beginning of thread_create().
|
||||
*/
|
||||
int arch_new_thread_setup_utcb(struct ktcb *new, struct ktcb *orig, unsigned int flags,
|
||||
unsigned long utcb_address)
|
||||
{
|
||||
unsigned int create_flags = flags & THREAD_CREATE_MASK;
|
||||
unsigned int utcb_flags = flags & THREAD_UTCB_MASK;
|
||||
|
||||
/* In case of multiple threads in same address space */
|
||||
if (create_flags == THREAD_SAME_SPACE) {
|
||||
switch (utcb_flags) {
|
||||
case THREAD_UTCB_SAME:
|
||||
new->utcb_address = orig->utcb_address;
|
||||
break;
|
||||
case THREAD_UTCB_NEW:
|
||||
new->utcb_address = utcb_address;
|
||||
break;
|
||||
case THREAD_UTCB_NONE:
|
||||
new->utcb_address = 0;
|
||||
break;
|
||||
default:
|
||||
printk("%s: Bad thread creation flags. "
|
||||
"Incorrect flag validation?\n",__FUNCTION__);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/* In case of brand new address space and thread */
|
||||
if (create_flags == THREAD_NEW_SPACE) {
|
||||
switch (utcb_flags) {
|
||||
case THREAD_UTCB_NEW:
|
||||
new->utcb_address = utcb_address;
|
||||
break;
|
||||
/*
|
||||
* No UTCB for brand new space means the thread cannot do
|
||||
* an ipc other than exceptions. This is allowed for now.
|
||||
*/
|
||||
case THREAD_UTCB_NONE:
|
||||
new->utcb_address = 0;
|
||||
break;
|
||||
default:
|
||||
printk("%s: Bad thread creation flags. "
|
||||
"Incorrect flag validation?\n",__FUNCTION__);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This essentially corresponds to fork() and normally we would expect
|
||||
* the UTCB to be the same as original thread, since the whole address
|
||||
* space is an identical image of the original. Nevertheless it doesn't
|
||||
* do harm to have none or different utcb address and this is left to
|
||||
* the implementor to decide.
|
||||
*/
|
||||
if (create_flags == THREAD_COPY_SPACE) {
|
||||
switch (utcb_flags) {
|
||||
case THREAD_UTCB_SAME:
|
||||
new->utcb_address = orig->utcb_address;
|
||||
break;
|
||||
case THREAD_UTCB_NEW:
|
||||
new->utcb_address = utcb_address;
|
||||
break;
|
||||
case THREAD_UTCB_NONE:
|
||||
new->utcb_address = 0;
|
||||
break;
|
||||
default:
|
||||
printk("%s: Bad thread creation flags. "
|
||||
"Incorrect flag validation?\n",__FUNCTION__);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arch_setup_new_thread(struct ktcb *new, struct ktcb *orig,
|
||||
unsigned int flags, unsigned long utcb_address)
|
||||
unsigned int flags)
|
||||
{
|
||||
/* Set up the utcb address */
|
||||
arch_new_thread_setup_utcb(new, orig, flags, utcb_address);
|
||||
|
||||
/* New threads just need their mode set up */
|
||||
if ((flags & THREAD_CREATE_MASK) == THREAD_NEW_SPACE) {
|
||||
BUG_ON(orig);
|
||||
@@ -344,25 +263,10 @@ int thread_setup_new_ids(struct task_ids *ids, unsigned int flags,
|
||||
* are respectively used when creating a brand new task, creating a
|
||||
* new thread in an existing address space, or forking a task.
|
||||
*/
|
||||
int thread_create(struct task_ids *ids, unsigned int flags,
|
||||
unsigned long utcb_address)
|
||||
int thread_create(struct task_ids *ids, unsigned int flags)
|
||||
{
|
||||
struct ktcb *task = 0, *new = (struct ktcb *)zalloc_page();
|
||||
unsigned int create_flags = flags & THREAD_CREATE_MASK;
|
||||
unsigned int utcb_flags = flags & THREAD_UTCB_MASK;
|
||||
|
||||
/* Handle error cases. Should have valid flags for each */
|
||||
if (!utcb_flags || !create_flags)
|
||||
return -EINVAL;
|
||||
|
||||
/* Cannot have new space with same utcb */
|
||||
else if (create_flags == THREAD_NEW_SPACE &&
|
||||
utcb_flags == THREAD_UTCB_SAME)
|
||||
return -EINVAL;
|
||||
|
||||
/* Cannot have new utcb with invalid address */
|
||||
else if (utcb_flags == THREAD_UTCB_NEW && !utcb_address)
|
||||
return -EINVAL;
|
||||
|
||||
/* Determine space allocation */
|
||||
if (create_flags == THREAD_NEW_SPACE) {
|
||||
@@ -397,7 +301,7 @@ out:
|
||||
waitqueue_head_init(&new->wqh_recv);
|
||||
waitqueue_head_init(&new->wqh_pager);
|
||||
|
||||
arch_setup_new_thread(new, task, flags, utcb_address);
|
||||
arch_setup_new_thread(new, task, flags);
|
||||
|
||||
/* Add task to global hlist of tasks */
|
||||
add_task_global(new);
|
||||
@@ -415,11 +319,10 @@ int sys_thread_control(syscall_context_t *regs)
|
||||
int ret = 0;
|
||||
unsigned int flags = regs->r0;
|
||||
struct task_ids *ids = (struct task_ids *)regs->r1;
|
||||
unsigned long utcb_address = regs->r2;
|
||||
|
||||
switch (flags & THREAD_ACTION_MASK) {
|
||||
case THREAD_CREATE:
|
||||
ret = thread_create(ids, flags, utcb_address);
|
||||
ret = thread_create(ids, flags);
|
||||
break;
|
||||
case THREAD_RUN:
|
||||
ret = thread_start(ids);
|
||||
|
||||
@@ -572,6 +572,7 @@ void copy_pgds_by_vrange(pgd_table_t *to, pgd_table_t *from,
|
||||
irange * sizeof(pgd_t));
|
||||
}
|
||||
|
||||
|
||||
/* Scheduler uses this to switch context */
|
||||
void arch_hardware_flush(pgd_table_t *pgd)
|
||||
{
|
||||
|
||||
@@ -255,12 +255,12 @@ static inline void context_switch(struct ktcb *next)
|
||||
|
||||
// printk("(%d) to (%d)\n", cur->tid, next->tid);
|
||||
|
||||
/* Update KIP UTCB pointer for new thread to run */
|
||||
kip.utcb = next->utcb_address;
|
||||
|
||||
/* Flush caches and everything */
|
||||
arch_hardware_flush(next->pgd);
|
||||
|
||||
/* Update utcb region for next task */
|
||||
task_update_utcb(cur, next);
|
||||
|
||||
/* Switch context */
|
||||
arch_switch(cur, next);
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
#include <l4/generic/scheduler.h>
|
||||
#include <l4/generic/preempt.h>
|
||||
#include <l4/lib/idpool.h>
|
||||
#include <l4/api/kip.h>
|
||||
#include INC_ARCH(exception.h)
|
||||
#include INC_SUBARCH(mm.h)
|
||||
#include INC_GLUE(memory.h)
|
||||
|
||||
/* ID pools for threads and spaces. */
|
||||
struct id_pool *thread_id_pool;
|
||||
@@ -22,3 +25,36 @@ struct list_head global_task_list;
|
||||
unsigned int need_resched_offset = offsetof(struct ktcb, ts_need_resched);
|
||||
unsigned int syscall_regs_offset = offsetof(struct ktcb, syscall_regs);
|
||||
|
||||
/*
|
||||
* Every thread has a unique utcb region that is mapped to its address
|
||||
* space as its context is loaded. The utcb region is a function of
|
||||
* this mapping and its offset that is reached via the KIP UTCB pointer
|
||||
*/
|
||||
void task_update_utcb(struct ktcb *cur, struct ktcb *next)
|
||||
{
|
||||
/* Update the KIP pointer */
|
||||
kip.utcb = next->utcb_address;
|
||||
|
||||
/* We stick with KIP update and no private tls mapping for now */
|
||||
#if 0
|
||||
/*
|
||||
* Unless current and next are in the same address
|
||||
* space and sharing the same physical utcb page, we
|
||||
* update the mapping
|
||||
*/
|
||||
if (cur->utcb_phys != next->utcb_phys)
|
||||
add_mapping(page_align(next->utcb_phys),
|
||||
page_align(next->utcb_virt),
|
||||
page_align_up(UTCB_SIZE),
|
||||
MAP_USR_RW_FLAGS);
|
||||
/*
|
||||
* If same physical utcb but different pgd, it means two
|
||||
* address spaces share the same utcb. We treat this as a
|
||||
* bug for now.
|
||||
*/
|
||||
else
|
||||
BUG_ON(cur->pgd != next->pgd);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -84,8 +84,10 @@ void print_sections(void)
|
||||
dprintk("_end: ", (unsigned int)_end);
|
||||
}
|
||||
|
||||
/* Enable virtual memory using kernel's first level table
|
||||
* and continue execution on virtual addresses.*/
|
||||
/*
|
||||
* Enable virtual memory using kernel's pgd
|
||||
* and continue execution on virtual addresses.
|
||||
*/
|
||||
void start_vm()
|
||||
{
|
||||
/*
|
||||
@@ -125,9 +127,7 @@ void start_vm()
|
||||
/* Jump to virtual memory addresses */
|
||||
__asm__ __volatile__ (
|
||||
"add sp, sp, %0 \n" /* Update stack pointer */
|
||||
#ifndef __OPTIMIZED_FP__ /* If fp not optimised away */
|
||||
"add fp, fp, %0 \n" /* Update frame pointer */
|
||||
#endif
|
||||
/* On the next instruction below, r0 gets
|
||||
* current PC + KOFFSET + 2 instructions after itself. */
|
||||
"add r0, pc, %0 \n"
|
||||
@@ -294,6 +294,9 @@ void init_pager(char *name, struct task_ids *ids)
|
||||
|
||||
set_task_ids(task, ids);
|
||||
|
||||
/* Pager gets first UTCB area available by default */
|
||||
task->utcb_address = UTCB_AREA_START;
|
||||
|
||||
if (!task->pgd) {
|
||||
BUG(); /* Inittask won't come here */
|
||||
task->pgd = alloc_pgd();
|
||||
|
||||
Reference in New Issue
Block a user