From 3ff519439bf15a6df04fafc47094b94a0105ca5e Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Wed, 27 May 2009 14:07:17 +0300 Subject: [PATCH] Added first part of extended ipc support. - Extended ipc tests - Need to copy from ktcb-to-ktcb instead of ktcb-to-user - Need to check flags of both ipc parties before ipc copy type. --- include/l4/generic/tcb.h | 29 +++++++- src/api/ipc.c | 100 ++++++++++++++++--------- src/generic/space.c | 11 ++- src/generic/tcb.c | 8 ++ tasks/libl4/include/l4lib/ipcdefs.h | 1 + tasks/test0/include/tests.h | 1 + tasks/test0/main.c | 3 + tasks/test0/src/ipctest.c | 110 +++++++++++++++++++++++++++- 8 files changed, 222 insertions(+), 41 deletions(-) diff --git a/include/l4/generic/tcb.h b/include/l4/generic/tcb.h index ff76b37..e1cc8da 100644 --- a/include/l4/generic/tcb.h +++ b/include/l4/generic/tcb.h @@ -1,7 +1,7 @@ /* * Thread Control Block, kernel portion. * - * Copyright (C) 2007 Bahadir Balban + * Copyright (C) 2007-2009 Bahadir Bilgehan Balban */ #ifndef __TCB_H__ #define __TCB_H__ @@ -17,6 +17,10 @@ #include INC_GLUE(context.h) #include INC_SUBARCH(mm.h) +/* + * Bit mappings for the ktcb flags field + */ + /* * These are a mixture of flags that indicate the task is * in a transitional state that could include one or more @@ -29,6 +33,10 @@ /* IPC resulted in a fault error (For ipcs that cannot page fault) */ #define IPC_EFAULT (1 << 3) +/* IPC type is encoded in task flags in bits [7:4] */ +#define TASK_FLAGS_IPC_TYPE_MASK 0xF0 +#define TASK_FLAGS_IPC_TYPE_SHIFT 4 + /* Task states */ enum task_state { TASK_INACTIVE = 0, @@ -106,7 +114,8 @@ struct ktcb { struct waitqueue *wq; /* Extended ipc buffer, points to the space after ktcb */ - char extended_ipc_buffer[]; + char *extended_ipc_buffer; + unsigned long extended_ipc_size; }; /* Per thread kernel stack unified on a single page. */ @@ -129,6 +138,21 @@ static inline void set_task_ids(struct ktcb *task, struct task_ids *ids) task->tgid = ids->tgid; } +static inline void tcb_set_ipc_flags(struct ktcb *task, + unsigned int flags) +{ + task->flags |= ((flags & L4_IPC_FLAGS_TYPE_MASK) + << TASK_FLAGS_IPC_TYPE_SHIFT) & + TASK_FLAGS_IPC_TYPE_MASK; +} + +static inline unsigned int tcb_get_ipc_flags(struct ktcb *task) +{ + return ((task->flags & TASK_FLAGS_IPC_TYPE_MASK) + >> TASK_FLAGS_IPC_TYPE_SHIFT) + & L4_IPC_FLAGS_TYPE_MASK; +} + #define THREAD_IDS_MAX 1024 #define SPACE_IDS_MAX 1024 #define TGROUP_IDS_MAX 1024 @@ -138,7 +162,6 @@ extern struct id_pool *thread_id_pool; extern struct id_pool *space_id_pool; struct ktcb *tcb_find(l4id_t tid); -struct ktcb *tcb_find_by_space(l4id_t tid); void tcb_add(struct ktcb *tcb); void tcb_remove(struct ktcb *tcb); diff --git a/src/api/ipc.c b/src/api/ipc.c index fd525d7..f175021 100644 --- a/src/api/ipc.c +++ b/src/api/ipc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include INC_API(syscall.h) #include INC_GLUE(message.h) @@ -25,7 +26,7 @@ enum IPC_TYPE { IPC_SENDRECV = 3, }; -int ipc_short_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) +int ipc_short_copy(struct ktcb *to, struct ktcb *from) { unsigned int *mr0_src = KTCB_REF_MR0(from); unsigned int *mr0_dst = KTCB_REF_MR0(to); @@ -40,14 +41,14 @@ int ipc_short_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) /* Copy full utcb region from one task to another. */ -int ipc_full_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) +int ipc_full_copy(struct ktcb *to, struct ktcb *from) { struct utcb *from_utcb = (struct utcb *)from->utcb_address; struct utcb *to_utcb = (struct utcb *)to->utcb_address; int ret; /* First do the short copy of primary mrs */ - if ((ret = ipc_short_copy(to, from, flags)) < 0) + if ((ret = ipc_short_copy(to, from)) < 0) return ret; /* Check that utcb memory accesses won't fault us */ @@ -65,48 +66,31 @@ int ipc_full_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) static inline int extended_ipc_msg_index(unsigned int flags) { - return (flags >> L4_IPC_FLAGS_MSG_INDEX_SHIFT) & L4_IPC_FLAGS_MSG_INDEX_MASK; + return (flags & L4_IPC_FLAGS_MSG_INDEX_MASK) + >> L4_IPC_FLAGS_MSG_INDEX_SHIFT; } static inline int extended_ipc_msg_size(unsigned int flags) { - return (flags >> L4_IPC_FLAGS_SIZE_SHIFT) & L4_IPC_FLAGS_SIZE_MASK; + return (flags & L4_IPC_FLAGS_SIZE_MASK) + >> L4_IPC_FLAGS_SIZE_SHIFT; } /* * Extended copy is asymmetric in that the copying always occurs from * the sender's kernel stack to receivers userspace buffers. */ -int ipc_extended_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) +int ipc_extended_copy(struct ktcb *to, struct ktcb *from) { - unsigned long msg_index; - unsigned long ipc_address; - unsigned int size; - unsigned int *mr0_receiver; - - /* - * Obtain primary message register index - * containing extended ipc buffer address - */ - msg_index = extended_ipc_msg_index(flags); - - /* Get the pointer to primary message registers */ - mr0_receiver = KTCB_REF_MR0(to); - - /* Obtain extended ipc address */ - ipc_address = (unsigned long)mr0_receiver[msg_index]; - - /* Obtain extended ipc size */ - size = extended_ipc_msg_size(flags); - - /* This ought to be checked before coming here */ - BUG_ON(size > L4_IPC_EXTENDED_MAX_SIZE); + unsigned long size = min(from->extended_ipc_size, + to->extended_ipc_size); /* * Copy from sender's kernel stack buffer * to receiver's paged-in userspace buffer */ - memcpy((void *)ipc_address, from->extended_ipc_buffer, size); + memcpy(to->extended_ipc_buffer, + from->extended_ipc_buffer, size); return 0; } @@ -120,21 +104,41 @@ int ipc_extended_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) * L4_ANYTHREAD. This is done for security since the receiver cannot trust * the sender info provided by the sender task. */ -int ipc_msg_copy(struct ktcb *to, struct ktcb *from, unsigned int flags) +int ipc_msg_copy(struct ktcb *to, struct ktcb *from, + unsigned int current_flags) { int ret = 0; unsigned int *mr0_dst; +#if 0 + unsigned int recv_ipc_type = tcb_get_ipc_flags(to); + unsigned int send_ipc_type = tcb_get_ipc_flags(from); + + if (recv_ipc_type == L4_IPC_FLAGS_FULL || + send_ipc_type == L4_IPC_FLAGS_FULL) { + ret = ipc_full_copy(to, from); + } + if (recv_ipc_type == L4_IPC_FLAGS_SHORT) + /* + * Check ipc type flags of both parties and use the following rules: + * + * SHORT SHORT -> SHORT IPC + * FULL X -> FULL IPC + * EXTENDED EXTENDED-> EXTENDED IPC + * EXTENDED X -> X IPC + */ +#endif + /* Check type of utcb copying and do it */ - switch (flags & L4_IPC_FLAGS_TYPE_MASK) { + switch (current_flags & L4_IPC_FLAGS_TYPE_MASK) { case L4_IPC_FLAGS_SHORT: - ret = ipc_short_copy(to, from, flags); + ret = ipc_short_copy(to, from); break; case L4_IPC_FLAGS_FULL: - ret = ipc_full_copy(to, from, flags); + ret = ipc_full_copy(to, from); break; case L4_IPC_FLAGS_EXTENDED: - ret = ipc_extended_copy(to, from, flags); + ret = ipc_extended_copy(to, from); break; } @@ -385,12 +389,23 @@ int ipc_recv_extended(l4id_t sendertid, unsigned int flags) /* Obtain extended ipc address */ ipc_address = (unsigned long)mr0_current[msg_index]; + /* Set extended ipc buffer as the user buffer address */ + current->extended_ipc_buffer = (char *)ipc_address; + /* Obtain extended ipc size */ size = extended_ipc_msg_size(flags); /* Check size is good */ if (size > L4_IPC_EXTENDED_MAX_SIZE) return -EINVAL; + current->extended_ipc_size = size; + + /* + * TODO: We may need to save primary mrs before engaging + * in page fault ipc. Currently after extended ipc address + * is obtained, primaries are not used during the ipc. + * In the future if needed, we should save them. + */ /* Page fault those pages on the current task if needed */ if ((err = check_access(ipc_address, size, @@ -436,12 +451,24 @@ int ipc_send_extended(l4id_t recv_tid, unsigned int flags) /* Check size is good */ if (size > L4_IPC_EXTENDED_MAX_SIZE) return -EINVAL; + current->extended_ipc_size = size; + + /* + * TODO: We may need to save primary mrs before engaging + * in page fault ipc. Currently after extended ipc address + * is obtained, primaries are not used during the ipc. + * In the future if needed, we should save them. + */ /* Page fault those pages on the current task if needed */ if ((err = check_access(ipc_address, size, MAP_USR_RW_FLAGS, 1)) < 0) return err; + /* Set extended ipc buffer as the end of ktcb */ + current->extended_ipc_buffer = + (void *)current + sizeof(struct ktcb); + /* * It is now safe to access user pages. * Copy message from user buffer into kernel stack @@ -516,7 +543,7 @@ int sys_ipc(syscall_context_t *regs) int ret = 0; /* - if (flags) + if ((flags & L4_IPC_FLAGS_TYPE_MASK) == L4_IPC_FLAGS_EXTENDED) __asm__ __volatile__ ( "1:\n" "b 1b\n"); @@ -549,6 +576,9 @@ int sys_ipc(syscall_context_t *regs) goto error; } + /* Encode ipc type in task flags */ + tcb_set_ipc_flags(current, flags); + if ((ret = __sys_ipc(to, from, ipc_type, flags)) < 0) goto error; return ret; diff --git a/src/generic/space.c b/src/generic/space.c index f3146a5..155ff12 100644 --- a/src/generic/space.c +++ b/src/generic/space.c @@ -162,17 +162,24 @@ struct address_space *address_space_create(struct address_space *orig) int check_access(unsigned long vaddr, unsigned long size, unsigned int flags, int page_in) { int err; + unsigned long start, end, mapsize; /* Do not allow ridiculously big sizes */ if (size >= USER_AREA_SIZE) return -EINVAL; + /* Get lower and upper page boundaries */ + start = page_align(vaddr); + end = page_align_up(vaddr + size); + mapsize = end - start; + /* Check if the address is mapped with given flags */ - if (!check_mapping(vaddr, size, flags)) { + if (!check_mapping(start, mapsize, flags)) { /* Is a page in requested? */ if (page_in) { /* Ask pager if paging in is possible */ - if((err = pager_pagein_request(vaddr, size, flags)) < 0) + if((err = pager_pagein_request(start, mapsize, + flags)) < 0) return err; } else return -EFAULT; diff --git a/src/generic/tcb.c b/src/generic/tcb.c index dbee4b7..3e8f9aa 100644 --- a/src/generic/tcb.c +++ b/src/generic/tcb.c @@ -182,6 +182,14 @@ int tcb_check_and_lazy_map_utcb(struct ktcb *task) BUG_ON(!task->utcb_address); + /* + * FIXME: + * + * A task may have the utcb mapping of a destroyed thread + * at the given virtual address. This would silently be accepted + * as *mapped*. We need to ensure utcbs of destroyed tasks + * are cleared from all other task's page tables. + */ if ((ret = check_access(task->utcb_address, UTCB_SIZE, MAP_SVC_RW_FLAGS, 0)) < 0) { /* Current task simply hasn't mapped its utcb */ diff --git a/tasks/libl4/include/l4lib/ipcdefs.h b/tasks/libl4/include/l4lib/ipcdefs.h index 3866c11..ef0f650 100644 --- a/tasks/libl4/include/l4lib/ipcdefs.h +++ b/tasks/libl4/include/l4lib/ipcdefs.h @@ -17,6 +17,7 @@ /*** IPC Tags used between server tasks ***/ /* For ping ponging */ +#define L4_IPC_TAG_SYNC_EXTENDED 1 #define L4_IPC_TAG_SYNC_FULL 2 #define L4_IPC_TAG_SYNC 3 diff --git a/tasks/test0/include/tests.h b/tasks/test0/include/tests.h index c2ade14..44cc897 100644 --- a/tasks/test0/include/tests.h +++ b/tasks/test0/include/tests.h @@ -14,6 +14,7 @@ extern pid_t parent_of_all; void ipc_full_test(void); +void ipc_extended_test(void); int shmtest(void); int forktest(void); diff --git a/tasks/test0/main.c b/tasks/test0/main.c index b850f1c..0cdc894 100644 --- a/tasks/test0/main.c +++ b/tasks/test0/main.c @@ -49,6 +49,9 @@ void main(void) clonetest(); + if (parent_of_all == getpid()) + ipc_extended_test(); + exectest(); while (1) diff --git a/tasks/test0/src/ipctest.c b/tasks/test0/src/ipctest.c index 4cd2532..fb2d61e 100644 --- a/tasks/test0/src/ipctest.c +++ b/tasks/test0/src/ipctest.c @@ -1,5 +1,15 @@ +/* + * Tests for more complex ipc such as full and extended + * + * Copyright (C) 2007-2009 Bahadir Bilgehan Balban + */ #include #include +#include +#include +#include +#include +#include /* * Full ipc test. Sends/receives full utcb, done with the pager. @@ -15,7 +25,8 @@ void ipc_full_test(void) } /* Call the pager */ - if ((ret = l4_sendrecv_full(PAGER_TID, PAGER_TID, L4_IPC_TAG_SYNC_FULL)) < 0) { + if ((ret = l4_sendrecv_full(PAGER_TID, PAGER_TID, + L4_IPC_TAG_SYNC_FULL)) < 0) { printf("%s: Failed with %d\n", __FUNCTION__, ret); BUG(); } @@ -34,3 +45,100 @@ void ipc_full_test(void) printf("FULL IPC TEST: -- FAILED --\n"); } +/* + * This is an extended ipc test that is done between 2 tasks that fork. + */ +void ipc_extended_test(void) +{ + pid_t child, parent; + void *base; + char *ipcbuf; + int err; + + /* Get parent pid */ + parent = getpid(); + + /* Fork the current task */ + if ((child = fork()) < 0) { + printf("%s: Fork failed with %d\n", __FUNCTION__, errno); + goto out_err; + } + if (child) + printf("%d: Created child with pid %d\n", getpid(), child); + else + printf("Child %d running.\n", getpid()); + + /* This test makes this assumption */ + BUG_ON(L4_IPC_EXTENDED_MAX_SIZE > PAGE_SIZE); + + /* + * Both child and parent gets 2 pages + * of privately mapped anonymous memory + */ + if ((int)(base = mmap(0, PAGE_SIZE*2, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) < 0) { + printf("%s: mmap for extended ipc buffer failed: %d\n", + __FUNCTION__, (int)base); + goto out_err; + } else + printf("mmap: Anonymous private buffer at %p\n", base); + + /* + * Both tasks read/write both pages + * across the page boundary for messages + */ + ipcbuf = base + PAGE_SIZE - L4_IPC_EXTENDED_MAX_SIZE / 2; + + if (!child) { + /* Child creates a string of maximum extended ipc size */ + for (int i = 0; i < L4_IPC_EXTENDED_MAX_SIZE; i++) { + ipcbuf[i] = 'A' + i % 20; + } + /* Finish string with newline and null pointer */ + ipcbuf[L4_IPC_EXTENDED_MAX_SIZE - 2] = '\n'; + ipcbuf[L4_IPC_EXTENDED_MAX_SIZE - 1] = '\0'; + + /* Send message to parent */ + printf("Child sending message: %s\n", ipcbuf); + if ((err = l4_send_extended(parent, L4_IPC_TAG_SYNC_EXTENDED, + L4_IPC_EXTENDED_MAX_SIZE, + ipcbuf)) < 0) { + printf("Extended ipc error: %d\n", err); + goto out_err; + } + + } else { + /* + * Parent receives on the buffer - we are sure that the + * buffer is not paged in because we did not touch it. + * This should prove that page faults are handled during + * the ipc. + */ + printf("Parent: extended receiving from child.\n"); + if ((err = l4_receive_extended(child, L4_IPC_EXTENDED_MAX_SIZE, + ipcbuf)) < 0) { + printf("Extended ipc error: %d\n", err); + goto out_err; + } + + /* We now print the message created by child. */ + printf("(%d): Message received from child: %s\n", + getpid(), ipcbuf); + + /* Check that child string is there */ + for (int i = 0; i < L4_IPC_EXTENDED_MAX_SIZE; i++) { + if (ipcbuf[i] != ('A' + i % 20)) + goto out_err; + } + printf("EXTENDED IPC TEST: -- PASSED --\n"); + } + return; + +out_err: + printf("EXTENDED IPC TEST: -- FAILED --\n"); +} + + + + +