OpenCores
URL https://opencores.org/ocsvn/c0or1k/c0or1k/trunk

Subversion Repositories c0or1k

[/] [c0or1k/] [trunk/] [conts/] [test_suite0/] [src/] [api/] [ipc.c] - Rev 2

Compare with Previous | Blame | View Log

/*
 * Test ipc system call.
 *
 * Copyright (C) 2010 B Labs Ltd.
 *
 * Author: Bahadir Balban
 */
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/ipcdefs.h>
#include <tests.h>
#include <macros.h>
#include <fault.h>
#include <memory.h>
 
struct ipc_ext_data {
	void *virtual;	/* Virtual address to start ipc from */
	l4id_t partner; /* Partner to do extended ipc */
};
 
int ipc_extended_sender(void *arg)
{
	struct ipc_ext_data *data = arg;
	int err;
 
	if ((err = l4_send_extended(data->partner, 0,
				    SZ_2K, data->virtual)) < 0) {
		printf("%s: Extended send failed. err=%d\n",
		       __FUNCTION__, err);
	}
	return 0;
}
 
int ipc_extended_receiver(void *arg)
{
	struct ipc_ext_data *data = arg;
	int err;
 
	if ((err = l4_receive_extended(data->partner, SZ_2K,
				       data->virtual)) < 0) {
		printf("%s: Extended receive failed. err=%d\n",
		       __FUNCTION__, err);
	}
 
	/*
	 * Test the data received
	 */
	for (int i = 0; i < SZ_2K; i++) {
		if (((char *)data->virtual)[i] != 'A' + i)
			printf("%s: Extended receive buffer has unexpected "
			       "data: Start %p, Offset: %d, "
			       "Data=%d, expected=%d\n", __FUNCTION__,
			       data->virtual, i, ((char *)data->virtual)[i],
			       'A' + i);
		return err;
	}
 
	return 0;
}
 
int ipc_ext_handle_pfault(struct ipc_ext_data *ipc_data,
			  void **virt, void **phys)
{
	u32 mr[MR_UNUSED_TOTAL];
	struct fault_data fault;
	int err;
 
	/* Read mrs not used by syslib */
	for (int i = 0; i < MR_UNUSED_TOTAL; i++)
		mr[i] = read_mr(MR_UNUSED_START + i);
 
	fault.kdata = (fault_kdata_t *)&mr[0];
	fault.sender = l4_get_sender();
 
	/* Convert from arch-specific to generic fault data */
	set_generic_fault_params(&fault);
 
	/*
	 * Handle the fault using a basic logic - if a virtual index
	 * is faulted, map the corresponding page at same physical index.
	 */
	if (page_align(fault.address) == (unsigned long)virt[0]) {
		if ((err = l4_map(phys[0], virt[0], 1,
				  MAP_USR_RW, fault.sender)) < 0) {
			printf("%s: Error: l4_map failed. "
			       "phys=%p, virt=%p\n", __FUNCTION__,
			       phys[0], virt[0]);
			return err;
		}
	} else if (page_align(fault.address) == (unsigned long)virt[1]) {
		if ((err = l4_map(phys[1], virt[1], 1,
				  MAP_USR_RW, fault.sender)) < 0) {
			printf("%s: Error: l4_map failed. "
			       "phys=%p, virt=%p\n", __FUNCTION__,
			       phys[1], virt[1]);
			return err;
		}
	} else if (page_align(fault.address) == (unsigned long)virt[2]) {
		if ((err = l4_map(phys[2], virt[2], 1,
				  MAP_USR_RW, fault.sender)) < 0) {
			printf("%s: Error: l4_map failed. "
			       "phys=%p, virt=%p\n", __FUNCTION__,
			       phys[2], virt[2]);
			return err;
		}
	} else if (page_align(fault.address) == (unsigned long)virt[3]) {
		if ((err = l4_map(phys[3], virt[3], 1,
				  MAP_USR_RW, fault.sender)) < 0) {
			printf("%s: Error: l4_map failed. "
			       "phys=%p, virt=%p\n", __FUNCTION__,
			       phys[3], virt[3]);
			return err;
		}
	} else {
		printf("%s: Error, page fault occured on an unexpected "
		       "address. adress=0x%x\n", __FUNCTION__,
		       fault.address);
		return -1;
	}
 
	/* Reply back to fault thread and return */
	return l4_ipc_return(0);
}
 
/*
 * Create two threads who will do page-faulting ipc to each other.
 * Their parent waits and handles the page faults.
 *
 * This test allocates 4 virtual page and 4 physical page addresses.
 * It fills a total of 2KB of payload starting from the 3rd quarter
 * of the first page and until the 2nd quarter of the 2nd page to
 * be sent by the sender thread.
 *
 * The payload is copied and the pages deliberately unmapped so that
 * the sender thread will page fault during the send operation.
 *
 * The receive pages are also set up same as above, so the receiving
 * thread also faults during the receive.
 *
 * The main thread starts both ipc threads, and starts waiting on
 * page faults. It handles the faults and the test succeeds if the
 * data is transfered safely to receiving end, despite all faults.
 */
int test_ipc_extended(void)
{
	struct task_ids self_ids;
	struct ipc_ext_data ipc_data[2];
	struct l4_thread *thread[2];
	void *virt[4], *phys[4];
	int err, tag;
 
	l4_getid(&self_ids);
 
	/* Get 4 physical pages */
	for (int i = 0; i < 4; i++)
		phys[i] = physical_page_new(1);
 
	/* Get 2 pairs of virtual pages */
	virt[0] = virtual_page_new(2);
	virt[1] = virt[0] + PAGE_SIZE;
	virt[2] = virtual_page_new(2);
	virt[3] = virt[2] + PAGE_SIZE;
 
	/* Map sender pages to self */
	if ((err = l4_map(phys[0], virt[0], 1,
			  MAP_USR_RW, self_ids.tid)) < 0) {
		printf("Error: Mapping Sender pages failed. phys: 0x%p,"
		       " virt: 0x%p, tid=%d, err=%d\n", phys[0], virt[0],
		       self_ids.tid, err);
		return err;
	}
	if ((err = l4_map(phys[1], virt[1], 1,
			  MAP_USR_RW, self_ids.tid)) < 0) {
		printf("Error: Mapping Sender pages failed. phys: 0x%p,"
		       " virt: 0x%p, tid=%d, err=%d\n", phys[0], virt[0],
		       self_ids.tid, err);
		return err;
	}
 
	/*
	 * Fill them with values to be sent
	 * Filling in 3rd KB of first page to 2nd KB of second page
	 */
	for (int i = 0; i < SZ_2K; i++)
		((char *)virt[0] + SZ_1K * 3)[i] = 'A' + i;
 
	/* Unmap the pages */
	l4_unmap(virt[0], 2, self_ids.tid);
 
	/* Create ipc threads but don't start. */
	if ((err = thread_create(ipc_extended_sender,
				 &ipc_data[0],
				 TC_SHARE_SPACE | TC_NOSTART,
				 &thread[0])) < 0) {
		dbg_printf("Thread create failed. "
			   "err=%d\n", err);
		return err;
	}
 
	dbg_printf("Thread created successfully. "
		   "tid=%d\n", thread[0]->ids.tid);
 
	if ((err = thread_create(ipc_extended_receiver,
				 &ipc_data[1],
				 TC_SHARE_SPACE | TC_NOSTART,
				 &thread[1])) < 0) {
		dbg_printf("Thread create failed. "
			   "err=%d\n", err);
		return err;
	}
 
	dbg_printf("Thread created successfully. "
		   "tid=%d\n", thread[1]->ids.tid);
 
	/*
	 * Set up arguments to sender,
	 * Send offset at 3rd quarter of first page.
	 */
	ipc_data[0].virtual = virt[0] + SZ_1K * 3;
	ipc_data[0].partner = thread[1]->ids.tid;
 
	/*
	 * Set up arguments to receiver
	 * Receive offset at 3rd quarter of first page.
	 */
	ipc_data[1].virtual = virt[1] + SZ_1K * 3;
	ipc_data[1].partner = thread[0]->ids.tid;
 
	/* Start the threads */
	l4_thread_control(THREAD_RUN, &thread[0]->ids);
	l4_thread_control(THREAD_RUN, &thread[1]->ids);
 
	/* Expecting 4 faults on 4 pages */
	for (int i = 0; i < 4; i++) {
		/* Wait on page fault */
		if ((err = l4_receive(L4_ANYTHREAD)) < 0) {
			printf("Error: l4_receive() for page"
			       " fault has failed. err=%d\n",
			       err);
		}
		if ((tag = l4_get_tag()) != L4_IPC_TAG_PFAULT) {
			printf("Error: Parent thread received "
			       "non-page fault ipc tag. tag=%d\n",
			       tag);
			return -1;
		}
 
		/* Handle fault */
		if ((err = ipc_ext_handle_pfault(ipc_data, virt, phys)) < 0) {
			printf("Error: An error occured during ipc "
			       "page fault handling. err=%d\n", err);
			return err;
		}
	}
 
	/* Wait for the ipc threads */
	for (int i = 0; i < 2; i ++)
		if ((err = thread_wait(thread[i])) < 0) {
			dbg_printf("THREAD_WAIT failed. "
				   "err=%d\n", err);
			return err;
		}
 
	/* Unmap and release pages */
	for (int i = 0; i < 4; i++) {
		l4_unmap(virt[i], 1, self_ids.tid);
		virtual_page_free(virt[i], 1);
		physical_page_free(phys[i], 1);
	}
 
	return 0;
}
 
int ipc_full_thread(void *arg)
{
	l4id_t parent = *((l4id_t *)arg);
	int err;
 
	/* Do two full send/receives */
	for (int i = 0; i < 2; i++) {
		/* Full receive, return positive if error */
		if ((err = l4_receive_full(parent)) < 0) {
			dbg_printf("Full receive failed on new "
				   "thread. err=%d", err);
			return 1;
		}
 
		/* Test full utcb received values */
		for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++) {
			if (read_mr(i) != i) {
				dbg_printf("IPC full receive on new thread: "
					   "Unexpected message register "
					   "values. MR%d = %d, should be %d\n",
					   i, read_mr(i), i);
				return 1; /* Exit positive without reply */
			}
		}
 
		/*
		 * Reset all message registers
		 */
		for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++)
			write_mr(i, 0);
 
		/* Send full return reply */
		l4_send_full(parent, 0);
	}
	return 0;
}
 
int ipc_short_thread(void *arg)
{
	l4id_t parent = *((l4id_t *)arg);
	int err;
 
	/* Short receive, return positive if error */
	if ((err = l4_receive(parent)) < 0) {
		dbg_printf("Short receive failed on new "
			   "thread. err=%d", err);
		return 1;
	}
 
	/* Test received registers */
	for (int i = MR_UNUSED_START; i < MR_TOTAL; i++) {
		if (read_mr(i) != i) {
			dbg_printf("IPC Receive on new thread: "
				   "Unexpected message register "
				   "values.\n"
				   "read = %d, expected = %d\n",
				   read_mr(i), i);
			l4_print_mrs();
			return 1; /* Exit positive without reply */
		}
	}
 
	/*
	 * Reset all message registers
	 */
	for (int i = MR_UNUSED_START; i < MR_TOTAL; i++)
		write_mr(i, 0);
 
	/*
	 * Send return reply and exit
	 */
	return l4_send(parent, 0);
}
 
 
/*
 * Create a thread and do a full ipc to it
 */
int test_ipc_full(void)
{
	struct task_ids self_ids;
	struct l4_thread *thread;
	int err;
 
	l4_getid(&self_ids);
 
	/*
	 * Create a thread in the same space
	 */
	if ((err = thread_create(ipc_full_thread,
				 &self_ids.tid,
				 TC_SHARE_SPACE,
				 &thread)) < 0) {
		dbg_printf("Thread create failed. "
			   "err=%d\n", err);
		return err;
	}
 
	dbg_printf("Thread created successfully. "
		   "tid=%d\n", thread->ids.tid);
 
	/*
	 * Try one short and one full send/recv
	 * to test full send/recv occurs on both cases
	 */
 
	/*
	 * Write data to full utcb registers
	 */
	for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++)
		write_mr(i, i);
 
	/*
	 * First, do a full ipc send/recv
	 */
	if ((err = l4_sendrecv_full(thread->ids.tid,
				    thread->ids.tid,
				    0)) < 0) {
		dbg_printf("Full IPC send/recv failed. "
			   "err=%d\n", err);
		return err;
	}
 
	/*
	 * Check that payload registers are modified to 0
	 */
	dbg_printf("%s: After send/recv:\n", __FUNCTION__);
	for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++) {
		if (read_mr(i) != 0) {
			dbg_printf("Full IPC send/recv: "
				   "Received payload is not "
				   "as expected.\n "
				   "MR%d = %d, should be %d\n",
				   i, read_mr(i), 0);
			return -1;
		}
	}
 
	/*
	 * Write data to full utcb registers
	 */
	for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++)
		write_mr(i, i);
 
	/*
	 * Try a short ipc send/recv. This should still result
	 * in full ipc since the other side is doing full send/recv.
	 */
	if ((err = l4_sendrecv(thread->ids.tid,
			       thread->ids.tid,
			       0)) < 0) {
		dbg_printf("Full IPC send/recv failed. "
			   "err=%d\n", err);
		return err;
	}
 
	/*
	 * Check that payload registers are modified to 0
	 */
	// dbg_printf("%s: After send/recv:\n", __FUNCTION__);
	for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++) {
		// dbg_printf("MR%d: %d\n", i, read_mr(i));
		if (read_mr(i) != 0) {
			dbg_printf("Full IPC send/recv: "
				   "Received payload is not "
				   "as expected.\n "
				   "MR%d = %d, should be %d\n",
				   i, read_mr(i), 0);
			return -1;
		}
	}
 
	/* Wait for the ipc thread to die */
	if ((err = thread_wait(thread)) < 0) {
		dbg_printf("THREAD_WAIT failed. "
			   "err=%d\n", err);
		return err;
	}
 
	dbg_printf("Full IPC send/recv successful.\n");
	return 0;
}
 
/*
 * Create a thread and do a short ipc to it
 */
int test_ipc_short(void)
{
	struct task_ids self_ids;
	struct l4_thread *thread;
	int err;
 
	l4_getid(&self_ids);
 
	/*
	 * Create a thread in the same space
	 */
	if ((err = thread_create(ipc_short_thread,
				 &self_ids.tid,
				 TC_SHARE_SPACE,
				 &thread)) < 0) {
		dbg_printf("Thread create failed. "
			   "err=%d\n", err);
		return err;
	}
 
	dbg_printf("Thread created successfully. "
		   "tid=%d\n", thread->ids.tid);
 
	/*
	 * Write data to short ipc registers
	 */
	for (int i = MR_UNUSED_START; i < MR_TOTAL; i++)
		write_mr(i, i);
 
	/*
	 * Do short ipc send/recv and check data is reset
	 */
	if ((err = l4_sendrecv(thread->ids.tid,
			       thread->ids.tid,
			       0)) < 0) {
		dbg_printf("Short IPC send/recv failed. "
			   "err=%d\n", err);
		return err;
	}
 
	/*
	 * Check that payload registers are reset
	 */
	for (int i = MR_UNUSED_START; i < MR_TOTAL; i++) {
		if (read_mr(i) != 0) {
			dbg_printf("Short IPC send/recv: "
				   "Received payload is incorrect."
				   "read = %d, expected=%d\n",
				   read_mr(i), 0);
			return -1;
		}
	}
 
	/* Wait for the ipc thread */
	if ((err = thread_wait(thread)) < 0) {
		dbg_printf("THREAD_WAIT failed. "
			   "err=%d\n", err);
		return err;
	}
 
	dbg_printf("Short IPC send/recv successful.\n");
	return 0;
}
 
int test_api_ipc(void)
{
	int err;
 
	if ((err = test_ipc_extended()) < 0)
		goto out_err;
 
	if ((err = test_ipc_short()) < 0)
		goto out_err;
 
	if ((err = test_ipc_full()) < 0)
		goto out_err;
 
	printf("IPC:                           -- PASSED --\n");
	return 0;
 
out_err:
	printf("IPC:                           -- FAILED --\n");
	return err;
 
}
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.