From 4f346cea539f45bcd7fae672007ef898a30e1f5b Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Fri, 29 Feb 2008 21:56:05 +0000 Subject: [PATCH] Added the first means to pass information between 2 tasks using the utcb as a shared page instead of the message registers. Implemented the code that passes task information from mm0 to fs0 using the fs0 utcb. The code seems to work OK but: There's an issue with anon pages that they end up on the same swapfile and with same file offsets (e.g. utcb and stack at offset 0). Need to fix this issue but otherwise this implementation seems to work. TODO: - Separate anon regions into separate vmfiles. - Possibly map the stacks from virtual files so that they can be read from userspace in the future for debugging. - Possibly utcb could be created as a shared memory object using shmget/shmat during startup. --- src/arch/arm/exception.c | 6 ++ src/arch/arm/v5/mm.c | 2 +- tasks/fs0/include/task.h | 1 + tasks/fs0/src/task.c | 62 ++++++++++++++--- tasks/libl4/include/l4lib/arch-arm/utcb.h | 2 +- tasks/libposix/open.c | 14 +++- tasks/mm0/src/fault.c | 11 +++ tasks/mm0/src/file.c | 4 ++ tasks/mm0/src/task.c | 83 +++++++++++++++++++++-- 9 files changed, 166 insertions(+), 19 deletions(-) diff --git a/src/arch/arm/exception.c b/src/arch/arm/exception.c index ce9aba1..d3a4c4e 100644 --- a/src/arch/arm/exception.c +++ b/src/arch/arm/exception.c @@ -61,6 +61,12 @@ void fault_ipc_to_pager(u32 faulty_pc, u32 fsr, u32 far) */ } +/* + * When a task calls the kernel and the supplied user buffer is not mapped, the kernel + * generates a page fault to the task's pager so that the pager can make the decision + * on mapping the buffer. Remember that if a task maps its own user buffer to itself + * this way, the kernel can access it, since it shares that task's page table. + */ int pager_pagein_request(unsigned long addr, unsigned long size, unsigned int flags) { u32 abort; diff --git a/src/arch/arm/v5/mm.c b/src/arch/arm/v5/mm.c index 92ce586..231b76b 100644 --- a/src/arch/arm/v5/mm.c +++ b/src/arch/arm/v5/mm.c @@ -252,7 +252,7 @@ int check_mapping_pgd(unsigned long vaddr, unsigned long size, /* Convert generic map flags to pagetable-specific */ BUG_ON(!(flags = space_flags_to_ptflags(flags))); - + for (int i = 0; i < npages; i++) { pte = virt_to_pte(vaddr + i * PAGE_SIZE); diff --git a/tasks/fs0/include/task.h b/tasks/fs0/include/task.h index 0c76ade..1d30e52 100644 --- a/tasks/fs0/include/task.h +++ b/tasks/fs0/include/task.h @@ -15,6 +15,7 @@ /* Thread control block, fs0 portion */ struct tcb { l4id_t tid; + unsigned long utcb_address; struct list_head list; int fd[TASK_OFILES_MAX]; struct id_pool *fdpool; diff --git a/tasks/fs0/src/task.c b/tasks/fs0/src/task.c index a9d992d..fc46857 100644 --- a/tasks/fs0/src/task.c +++ b/tasks/fs0/src/task.c @@ -35,7 +35,7 @@ struct tcb *find_task(int tid) * registers are sufficient. First argument tells how many there are, the rest * tells the tids. */ -int receive_pager_taskdata(l4id_t *tdata) +int receive_pager_taskdata_orig(l4id_t *tdata) { int err; @@ -69,15 +69,51 @@ int receive_pager_taskdata(l4id_t *tdata) return 0; } +struct task_data { + unsigned long tid; + unsigned long utcb_address; +}; + +struct task_data_head { + unsigned long total; + struct task_data tdata[]; +}; + +/* Read task information into the utcb buffer, since it wont fit into mrs. */ +struct task_data_head *receive_pager_taskdata(void) +{ + int err; + + /* Ask pager to write the data at this address */ + write_mr(L4SYS_ARG0, (unsigned long)utcb->buf); + + /* Make the actual ipc call */ + if ((err = l4_sendrecv(PAGER_TID, PAGER_TID, + L4_IPC_TAG_TASKDATA)) < 0) { + printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err); + return PTR_ERR(err); + } + + /* Check if call itself was successful */ + if ((err = l4_get_retval()) < 0) { + printf("%s: Error: %d.\n", __FUNCTION__, err); + return PTR_ERR(err); + } + + printf("%s: %d Total tasks.\n", __FUNCTION__, + ((struct task_data_head *)utcb->buf)->total); + + return (struct task_data_head *)utcb->buf; +} + /* Allocate a task struct and initialise it */ -struct tcb *create_tcb(l4id_t tid) +struct tcb *create_tcb(void) { struct tcb *t; if (!(t = kmalloc(sizeof(*t)))) return PTR_ERR(-ENOMEM); - t->tid = tid; INIT_LIST_HEAD(&t->list); list_add_tail(&t->list, &tcb_head.list); tcb_head.total++; @@ -85,31 +121,37 @@ struct tcb *create_tcb(l4id_t tid) return t; } -int init_task_structs(l4id_t *tdata) +int init_task_structs(struct task_data_head *tdata_head) { struct tcb *t; - int total = tdata[0]; - for (int i = 0; i < total; i++) { - if (IS_ERR(t = create_tcb(tdata[1 + i]))) + for (int i = 0; i < tdata_head->total; i++) { + if (IS_ERR(t = create_tcb())) return (int)t; + + /* Initialise fields sent by pager */ + t->tid = tdata_head->tdata[i].tid; + t->utcb_address = tdata_head->tdata[i].utcb_address; + + /* Initialise vfs specific fields. */ t->rootdir = vfs_root.pivot; t->curdir = vfs_root.pivot; } + return 0; } int init_task_data(void) { - l4id_t tdata[MR_UNUSED_TOTAL]; + struct task_data_head *tdata_head; INIT_LIST_HEAD(&tcb_head.list); /* Read how many tasks and tids of each */ - BUG_ON(receive_pager_taskdata(tdata) < 0); + BUG_ON((tdata_head = receive_pager_taskdata()) < 0); /* Initialise local task structs using this info */ - BUG_ON(init_task_structs(tdata) < 0); + BUG_ON(init_task_structs(tdata_head) < 0); return 0; } diff --git a/tasks/libl4/include/l4lib/arch-arm/utcb.h b/tasks/libl4/include/l4lib/arch-arm/utcb.h index 71383e5..f9e949a 100644 --- a/tasks/libl4/include/l4lib/arch-arm/utcb.h +++ b/tasks/libl4/include/l4lib/arch-arm/utcb.h @@ -24,7 +24,7 @@ struct utcb { * For passing ipc data larger than mrs, * that is, if the callee is allowed to map it */ - char userbuf[]; + char buf[]; }; extern struct utcb *utcb; diff --git a/tasks/libposix/open.c b/tasks/libposix/open.c index cb57247..82a540d 100644 --- a/tasks/libposix/open.c +++ b/tasks/libposix/open.c @@ -14,11 +14,23 @@ #include #include +/* + * Arguments that are too large to fit in message registers are + * copied onto another area that is still on the utcb, and the servers + * map-in the task utcb and read those arguments from there. + */ +void *copy_to_utcb(void *arg, int size) +{ + BUG_ON(size > PAGE_SIZE); + memcpy(utcb->buf, arg, size); +} + static inline int l4_open(const char *pathname, int flags, mode_t mode) { int fd; - write_mr(L4SYS_ARG0, (unsigned long)pathname); + // write_mr(L4SYS_ARG0, (unsigned long)pathname); + copy_to_utcb(pathname, strlen(pathname)); write_mr(L4SYS_ARG1, flags); write_mr(L4SYS_ARG2, (u32)mode); diff --git a/tasks/mm0/src/fault.c b/tasks/mm0/src/fault.c index 6816a11..ab4b9e7 100644 --- a/tasks/mm0/src/fault.c +++ b/tasks/mm0/src/fault.c @@ -324,6 +324,17 @@ int do_anon_page(struct fault_data *fault) l4_map(paddr, (void *)page_align(fault->address), 1, MAP_USR_RW_FLAGS, fault->task->tid); + /*** DEBUG CODE FOR FS0 UTCB ***/ + if(page_align(fault->address) == 0xf8001000) { + printf("For FS0 utcb @ 0xf8001000, mapping page @ 0x%x, foffset: 0x%x, owned by vma @ 0x%x, vmfile @ 0x%x\n", + (unsigned long)page, page->f_offset, fault->vma, fault->vma->owner); + } + if(page_align(fault->address) == 0xf8002000) { + printf("For FS0 utcb @ 0xf8002000, mapping page @ 0x%x, foffset: 0x%x, owned by vma @ 0x%x, vmfile @ 0x%x\n", + (unsigned long)page, page->f_offset, fault->vma, fault->vma->owner); + } + /*** DEBUG CODE FOR FS0 UTCB ***/ + spin_lock(&page->lock); /* vma's swap file owns this page */ page->owner = fault->vma->owner; diff --git a/tasks/mm0/src/file.c b/tasks/mm0/src/file.c index f45ffcb..1e81b4b 100644 --- a/tasks/mm0/src/file.c +++ b/tasks/mm0/src/file.c @@ -168,6 +168,8 @@ int insert_page_olist(struct page *this, struct vm_file *f) /* Add to beginning if smaller */ else if (this->f_offset < before->f_offset) list_add(&this->list, &before->list); + else + BUG(); return 0; } @@ -177,6 +179,8 @@ int insert_page_olist(struct page *this, struct vm_file *f) list_add_tail(&this->list, &before->list); return 0; } + BUG_ON(this->f_offset == before->f_offset); + BUG_ON(this->f_offset == after->f_offset); } BUG(); } diff --git a/tasks/mm0/src/task.c b/tasks/mm0/src/task.c index 1487224..714746f 100644 --- a/tasks/mm0/src/task.c +++ b/tasks/mm0/src/task.c @@ -9,9 +9,11 @@ #include #include #include +#include #include INC_GLUE(memory.h) #include #include +#include #include #include #include @@ -221,6 +223,61 @@ void init_pm(struct initdata *initdata) start_boot_tasks(initdata, &tcb_head); } +/* + * Makes the virtual to page translation for a given user task. + */ +struct page *task_virt_to_page(struct tcb *t, unsigned long virtual) +{ + unsigned long vaddr_vma_offset; + unsigned long vaddr_file_offset; + struct vm_area *vma; + struct vm_file *vmfile; + struct page *page; + + /* First find the vma that maps that virtual address */ + if (!(vma = find_vma(virtual, &t->vm_area_list))) { + printf("%s: No VMA found for 0x%x on task: %d\n", + __FUNCTION__, virtual, t->tid); + return PTR_ERR(-EINVAL); + } + + /* Find the pfn offset of virtual address in this vma */ + BUG_ON(__pfn(virtual) < vma->pfn_start || + __pfn(virtual) > vma->pfn_end); + vaddr_vma_offset = __pfn(virtual) - vma->pfn_start; + + /* Find the file offset of virtual address in this file */ + vmfile = vma->owner; + vaddr_file_offset = vma->f_offset + vaddr_vma_offset; + + /* + * Find the page with the same file offset with that of the + * virtual address, that is, if the page is resident in memory. + */ + list_for_each_entry(page, &vmfile->page_cache_list, list) + if (vaddr_file_offset == page->f_offset) { + printf("%s: %s: Found page @ 0x%x, f_offset: 0x%x, with vma @ 0x%x, vmfile @ 0x%x\n", __TASKNAME__, + __FUNCTION__, (unsigned long)page, page->f_offset, vma, vma->owner); + return page; + } + + /* + * The page is not found, meaning that it is not mapped in + * yet, e.g. via a page fault. + */ + return 0; +} + +struct task_data { + unsigned long tid; + unsigned long utcb_address; +}; + +struct task_data_head { + unsigned long total; + struct task_data tdata[]; +}; + /* * During its initialisation FS0 wants to learn how many boot tasks * are running, and their tids, which includes itself. This function @@ -228,8 +285,10 @@ void init_pm(struct initdata *initdata) */ void send_task_data(l4id_t requester) { - struct tcb *t; int li, err; + struct tcb *t, *vfs; + struct utcb *vfs_utcb; + struct task_data_head *tdata_head; if (requester != VFS_TID) { printf("%s: Task data requested by %d, which is not " @@ -238,14 +297,26 @@ void send_task_data(l4id_t requester) return; } - /* First word is total number of tcbs */ - write_mr(L4SYS_ARG0, tcb_head.total); + BUG_ON(!(vfs = find_task(requester))); - /* Write each tcb's tid */ + /* Map in vfs's utcb. FIXME: Whatif it is already mapped? */ + l4_map((void *)page_to_phys(task_virt_to_page(vfs, vfs->utcb_address)), + (void *)vfs->utcb_address, 1, MAP_USR_RW_FLAGS, self_tid()); + + /* Get a handle on vfs utcb */ + vfs_utcb = (struct utcb *)vfs->utcb_address; + + /* Write all requested task information to utcb's user buffer area */ + tdata_head = (struct task_data_head *)vfs_utcb->buf; + + /* First word is total number of tcbs */ + tdata_head->total = tcb_head.total; + + /* Write per-task data for all tasks */ li = 0; list_for_each_entry(t, &tcb_head.list, list) { - BUG_ON(li >= MR_USABLE_TOTAL); - write_mr(L4SYS_ARG1 + li, t->tid); + tdata_head->tdata[li].tid = t->tid; + tdata_head->tdata[li].utcb_address = t->utcb_address; li++; }