mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 02:43:15 +01:00
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.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
100
src/api/ipc.c
100
src/api/ipc.c
@@ -10,6 +10,7 @@
|
||||
#include <l4/api/kip.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <l4/lib/bit.h>
|
||||
#include <l4/lib/math.h>
|
||||
#include <l4/generic/kmalloc.h>
|
||||
#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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -49,6 +49,9 @@ void main(void)
|
||||
|
||||
clonetest();
|
||||
|
||||
if (parent_of_all == getpid())
|
||||
ipc_extended_test();
|
||||
|
||||
exectest();
|
||||
|
||||
while (1)
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
/*
|
||||
* Tests for more complex ipc such as full and extended
|
||||
*
|
||||
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
|
||||
*/
|
||||
#include <l4lib/arch/syslib.h>
|
||||
#include <l4lib/ipcdefs.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <tests.h>
|
||||
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user