Beispiel #1
0
static int
ipc_caller(seL4_Word ep0, seL4_Word ep1, seL4_Word word_bits, seL4_Word arg4)
{
    /* Let our parent know we are ready. */
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1);
    seL4_SetMR(0, READY_MAGIC);
    seL4_Send(ep0, tag);
    /*
     * The parent has changed our cspace on us. Check that it makes sense.
     *
     * Basically the entire cspace should be empty except for the cap at ep0.
     * We should still test that various points in the cspace resolve correctly.
     */

    /* Check that none of the typical endpoints are valid. */
    for (unsigned long i = 0; i < word_bits; i++) {
        seL4_MessageInfo_ptr_new(&tag, 0, 0, 0, 0);
        tag = seL4_Call(i, tag);
        test_assert(seL4_MessageInfo_get_label(tag) == seL4_InvalidCapability);
    }

    /* Check that changing one bit still gives an invalid cap. */
    for (unsigned long i = 0; i < word_bits; i++) {
        seL4_MessageInfo_ptr_new(&tag, 0, 0, 0, 0);
        tag = seL4_Call(ep1 ^ BIT(i), tag);
        test_assert(seL4_MessageInfo_get_label(tag) == seL4_InvalidCapability);
    }

    /* And we're done. This should be a valid cap and get us out of here! */
    seL4_MessageInfo_ptr_new(&tag, 0, 0, 0, 1);
    seL4_SetMR(0, SUCCESS_MAGIC);
    seL4_Send(ep1, tag);

    return sel4test_get_result();
}
Beispiel #2
0
pid_t my_id(void) {
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 1);
    seL4_SetMR (0, SYSCALL_PROCESS_GETPID);

    seL4_Call (PAPAYA_SYSCALL_SLOT, msg);
    return seL4_GetMR (0);
}
int call_func_ta(int ta_num,int func_id,int simp_arg,void *data,size_t size,seL4_Word *res){
	int length = size/(double)sizeof(seL4_Word);
	if((length+5) > seL4_MsgMaxLength){
		printf("Params too large. operation failed\n");
		return -1;
	}else{
		seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 5+length);
		seL4_SetTag(tag);	
		seL4_SetMR(0, CALL_FUNC_CMD);
		seL4_SetMR(1, ta_num);
		seL4_SetMR(2,func_id);
		seL4_SetMR(3,simp_arg);
		seL4_SetMR(4,length);
		seL4_Word* blockptr = (seL4_Word*)data;
		for(int i =0; i < length;++i){
			seL4_SetMR(i+5,*blockptr);
			blockptr++;
		}
		seL4_Call(TEE_EP_CPTR,tag);

		int res_val = seL4_GetMR(0);
		if(res != NULL){
			int len = seL4_GetMR(1);
			for(int i =0; i < len;++i){
				res[i] = seL4_GetMR(i+2);
			}	
		}
		return res_val;
	}		

}
Beispiel #4
0
// Block a thread forever
// we do this by making an unimplemented system call.
static void
thread_block(void){
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1);
    seL4_SetTag(tag);
    seL4_SetMR(0, 100);
    seL4_Call(SYSCALL_ENDPOINT_SLOT, tag);
}
Beispiel #5
0
int read (fildes_t file, char *buf, size_t nbyte) {
	if (nbyte >= (1 << 12)) {
		return -1;		/* FIXME: crappy limitation */
    }

    struct sos_fhandle* fh = sos_lookup_fhandle (file);
    if (!fh) {
        return -1;
    }
    
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, pawpaw_share_attach (fh->share), 3);
    //seL4_SetCap (0, fh->share->cap);

	seL4_SetMR (0, VFS_READ);
    seL4_SetMR (1, fh->share->id);
    seL4_SetMR (2, nbyte);

    seL4_Call (fh->cap, msg);
    int read = seL4_GetMR (0);

    if (read > 0) {
        memcpy (buf, fh->share->buf, read);
    }

    return read;
}
Beispiel #6
0
int process_delete(pid_t pid) {
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 2);
    seL4_SetMR (0, SYSCALL_PROCESS_DESTROY);
    seL4_SetMR (1, pid);

    seL4_Call (PAPAYA_SYSCALL_SLOT, msg);
    return seL4_GetMR (0);
}
Beispiel #7
0
pid_t process_wait(pid_t pid) {
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 2);
    seL4_SetMR (0, SYSCALL_PROCESS_WAIT);
    seL4_SetMR (1, pid);

    seL4_Call (PAPAYA_SYSCALL_SLOT, msg);
    return seL4_GetMR (0);
}
Beispiel #8
0
/**
 * Signal that a thrd has finished
 */
static void signal_thrd_finished(seL4_CPtr local_endpoint, int val)
{
    seL4_MessageInfo_t info = seL4_MessageInfo_new(0, 0, 0, 1);
    seL4_SetMR(0, val);
    seL4_Call(local_endpoint, info);
    assert(0);
    while (1);
}
Beispiel #9
0
/* sends:
 * vaddr in process address space of processes buffer (should do map-in-out)
 * maximum number to place in buffer
 */
int process_status(process_t *processes, unsigned max) {
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 3);
    seL4_SetMR (0, SYSCALL_PROCESS_SEND_STATUS);
    seL4_SetMR (1, (seL4_Word)processes);
    seL4_SetMR (2, max);

    seL4_Call (PAPAYA_SYSCALL_SLOT, msg);
    return seL4_GetMR (0);
}
Beispiel #10
0
Datei: rpc.c Projekt: seL4/refos
int
rpc_call_server()
{
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, _rpc_cp, _rpc_mr);
    int ept = rpc_get_endpoint(_rpc_label);
    _rpc_minfo = seL4_Call(ept, tag);
    rpc_reset_contents(NULL);
    return 0;
}
Beispiel #11
0
pid_t process_create_args_env (const char* path, const char* argv[], const int argc, const char* envv[], const int envc) {
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 3);
    seL4_SetMR (0, SYSCALL_PROCESS_CREATE);
    seL4_SetMR (1, (seL4_Word)path);
    seL4_SetMR (2, (seL4_Word)argv);
    char* 
    seL4_SetMR (3, argc + envc)

    seL4_Call (PAPAYA_SYSCALL_SLOT, msg);
    return seL4_GetMR (0);
}
Beispiel #12
0
pid_t process_create_args (const char* path, const char* argv[]) {
#endif
	seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 3);
    seL4_SetMR (0, SYSCALL_PROCESS_CREATE);
    seL4_SetMR (1, (seL4_Word)path);
    seL4_SetMR (2, strlen (path));
    //seL4_SetMR (2, (seL4_Word)argv);
    //seL4_SetMR (3, )

    seL4_Call (PAPAYA_SYSCALL_SLOT, msg);
    return seL4_GetMR (0);
}
Beispiel #13
0
static int
call_func(seL4_CPtr ep, seL4_Word msg, volatile seL4_Word *done, seL4_Word arg3)
{
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 1);

    /* Send the given message once. */
    seL4_SetMR(0, msg);
    tag = seL4_Call(ep, tag);
    test_check(seL4_MessageInfo_get_length(tag) == 1);
    test_check(seL4_GetMR(0) == ~msg);

    *done = 0;

    /* Send the given message again - should (eventually) fault this time. */
    seL4_SetMR(0, msg);
    tag = seL4_Call(ep, tag);
    /* The call should fail. */
    test_check(seL4_MessageInfo_get_label(tag) == seL4_InvalidCapability);

    *done = 1;

    return sel4test_get_result();
}
Beispiel #14
0
static int ep_test_func(seL4_CPtr sync_ep, seL4_CPtr test_ep, volatile seL4_Word *status, seL4_Word arg4)
{
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
    seL4_Word sender_badge;
    while (1) {
        seL4_Recv(sync_ep, &sender_badge);
        /* Hit up the test end point */
        seL4_MessageInfo_t reply = seL4_Call(test_ep, tag);
        /* See what the status was */
        *status = !!(seL4_MessageInfo_get_label(reply) != seL4_InvalidCapability);
        /* Reply */
        seL4_Reply(tag);
    }
    return sel4test_get_result();
}
Beispiel #15
0
int stat (const char *path, stat_t *buf) {
	seL4_MessageInfo_t msg;

    if (!vfs_ep) {
        vfs_ep = pawpaw_service_lookup ("svc_vfs");
    }

    struct pawpaw_share* share = pawpaw_share_new ();
    if (!share) {
        return -1;
    }

    memset (share->buf, 0, PAPAYA_IPC_PAGE_SIZE);
    if (path && path[0] != '/') {
        strcpy (share->buf, cwd);
        int cwdlen = strlen (cwd);
        if (cwdlen > 0 && cwd[cwdlen - 1] != '/') {
            strcat (share->buf, "/");
        }
        strcat (share->buf, path);
        /* XXX: check if buffer overflows */
    } else {
        strcpy (share->buf, path);  
    }

    msg = seL4_MessageInfo_new (0, 0, 1, 2);

    seL4_CPtr recv_cap = pawpaw_cspace_alloc_slot ();
    if (!recv_cap) {
        pawpaw_share_unmount (share);
        return -1;
    }

    seL4_SetCapReceivePath (PAPAYA_ROOT_CNODE_SLOT, recv_cap, PAPAYA_CSPACE_DEPTH);

    seL4_SetCap (0, share->cap);
    seL4_SetMR (0, VFS_STAT);
    seL4_SetMR (1, share->id);

    seL4_Call (vfs_ep, msg);
    int status = seL4_GetMR (0);
    if (status == 0) {
        memcpy (buf, share->buf, sizeof (stat_t));
    }

    pawpaw_share_unmount (share);
    return status;
}
Beispiel #16
0
long
sys_brk(va_list ap)
{
    //printf("sysbrk\n");
    uintptr_t newbrk = va_arg(ap, uintptr_t);
    //printf("newbrk before = %d\n",newbrk);
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(seL4_NoFault, 0, 0, 2);
    seL4_SetTag(tag);
    seL4_SetMR(0, SOS_SYSCALL_SYSBRK);
    seL4_SetMR(1, newbrk);
    seL4_MessageInfo_t message = seL4_Call(SYSCALL_ENDPOINT_SLOT, tag);
    newbrk = seL4_MessageInfo_get_label(message); 
    //printf("newbrk result = %d\n",newbrk);

    return newbrk;
}
int start_ta(char *ta_name){
	seL4_Word msg;
	int arg_len = strlen(ta_name);
	seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 2+arg_len);
	seL4_SetTag(tag);	
	seL4_SetMR(0, START_TA_CMD);
	printf("client-os: Calling TEE-CONTAINER to start TA \n");
	seL4_Word *ptr = (seL4_Word*)ta_name;
	for(int i=0;i < arg_len;++i){
		seL4_SetMR(i+2,*ptr);
		ptr++;
	}
	ptr = ta_name;
	seL4_SetMR(1,arg_len);
	seL4_Call(TEE_EP_CPTR,tag);
	msg = seL4_GetMR(0);
	return msg;
}
Beispiel #18
0
void
serial_server_disconnect(serial_client_context_t *conn)
{
    seL4_MessageInfo_t tag;

    if (conn == NULL) {
        return;
    }

    seL4_SetMR(SSMSGREG_FUNC, FUNC_DISCONNECT_REQ);
    tag = seL4_MessageInfo_new(0, 0, 0, SSMSGREG_DISCONNECT_REQ_END);

    tag = seL4_Call(conn->badged_server_ep_cspath.capPtr, tag);

    if (seL4_GetMR(SSMSGREG_FUNC) != FUNC_DISCONNECT_ACK) {
        ZF_LOGE(SERSERVC"disconnect: reply message was not a DISCONNECT_ACK "
                "as expected.");
    }
}
Beispiel #19
0
int
serial_server_kill(serial_client_context_t *conn) {
    seL4_MessageInfo_t tag;

    if (conn == NULL) {
        return seL4_InvalidArgument;
    }

    seL4_SetMR(SSMSGREG_FUNC, FUNC_KILL_REQ);
    tag = seL4_MessageInfo_new(0, 0, 0, SSMSGREG_KILL_REQ_END);

    tag = seL4_Call(conn->badged_server_ep_cspath.capPtr, tag);

    if (seL4_GetMR(SSMSGREG_FUNC) != FUNC_KILL_ACK) {
        ZF_LOGE(SERSERVC"kill: Reply message was not a KILL_ACK as expected.");
        return seL4_IllegalOperation;
    }
    return seL4_MessageInfo_get_label(tag);
}
Beispiel #20
0
int getdirent (int pos, char *path, size_t nbyte) {
	seL4_MessageInfo_t msg;

    if (!vfs_ep) {
        vfs_ep = pawpaw_service_lookup ("svc_vfs");
    }

    struct pawpaw_share* share = pawpaw_share_new ();
    if (!share) {
        return -1;
    }

    msg = seL4_MessageInfo_new (0, 0, 1, 4);

    seL4_CPtr recv_cap = pawpaw_cspace_alloc_slot ();
    if (!recv_cap) {
        pawpaw_share_unmount (share);
        return -1;
    }

    seL4_SetCapReceivePath (PAPAYA_ROOT_CNODE_SLOT, recv_cap, PAPAYA_CSPACE_DEPTH);

    seL4_SetCap (0, share->cap);
    memset (share->buf, 0, PAPAYA_IPC_PAGE_SIZE);
    strcpy (share->buf, cwd);

    seL4_SetMR (0, VFS_LISTDIR);
    seL4_SetMR (1, share->id);
    seL4_SetMR (2, pos);
    seL4_SetMR (3, nbyte);

    seL4_Call (vfs_ep, msg);
    int read = seL4_GetMR (0);
    if (read >= 0) {
        memcpy (path, share->buf, nbyte);
        pawpaw_share_unmount (share);
        return read;
    } else {
        pawpaw_share_unmount (share);
        return -1;
    }
}
Beispiel #21
0
static int
ipc_test_helper_2(ipc_test_data_t *data)
{
    /* We are a spinner thread. Our job is to do spin, and occasionally bounce
     * to thread 0 to let other code execute. */
    while (1) {
        /* Ensure nothing happens whilst we are busy. */
        int last_step = ipc_test_step;
        for (int i = 0; i < 100000; i++) {
            asm volatile ("");
        }
        test_check(last_step == ipc_test_step);

        data->spins++;

        /* Bounce. */
        seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, 0);
        seL4_Call(data->ep0, tag);
    }
    return 0;
}
Beispiel #22
0
int close(fildes_t file) {
    struct sos_fhandle* fh = sos_lookup_fhandle (file);
    if (!fh) {
        return -1;
    }

    seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, 0, 1);
    seL4_SetMR (0, VFS_CLOSE);
    seL4_Call (fh->cap, msg);
    if (seL4_GetMR (0) == 0) {
        //pawpaw_cspace_free_slot (fh->cap);
        /* should DELETE the slot, then free */
        //pawpaw_share_unmount (fh->share);

        /* FIXME: fix up list */
        //free (fh);
        return 0;
    } else {
	   return -1;
    }
}
Beispiel #23
0
/* Attempt single write, without retrying */
size_t _sos_write(void *vData, size_t count) {
    seL4_Word *realdata = (seL4_Word*) vData;
    seL4_IPCBuffer *buf = seL4_GetIPCBuffer();
    seL4_Word len = 2 + DIVROUND(count, 4);
    seL4_Word real_len = MIN(seL4_MsgMaxLength, len);
    size_t real_count  = MIN(count, (seL4_MsgMaxLength - 2) * 4);

    /* Set up syscall parameters */
    buf->msg[0] = SOS_SYSCALL_WRITE_CONSOLE;
    buf->msg[1] = real_count;

    int i;
    for (i = 2; i < len; i++) {
      buf->msg[i] = realdata[i - 2];
    }

    /* Fire off syscall and wait for reply */
    seL4_MessageInfo_t tag = seL4_MessageInfo_new(0, 0, 0, real_len);
    seL4_Call(SYSCALL_ENDPOINT_SLOT, tag);

    return seL4_GetMR(0);
}
Beispiel #24
0
/** Performs the IPC register setup for a write() call to the server.
 *
 * The Server's ABI for the write() request has changed a little: the server
 * now returns the number of bytes it wrote out to the serial in a msg-reg,
 * aside from also returning an error code in the "label" of the header.
 *
 * @param conn Initialized connection token returned by
 *             serial_server_client_connect().
 * @param len length of the data in the buffer.
 * @param server_nbytes_written The value the server reports that it actually
 *                              wrote to the serial device.
 * @return 0 on success, or integer error value on error.
 */
static ssize_t
serial_server_write_ipc_invoke(serial_client_context_t *conn, ssize_t len)
{
    seL4_MessageInfo_t tag;

    seL4_SetMR(SSMSGREG_FUNC, FUNC_WRITE_REQ);
    seL4_SetMR(SSMSGREG_WRITE_REQ_BUFF_LEN, len);
    tag = seL4_MessageInfo_new(0, 0, 0, SSMSGREG_WRITE_REQ_END);

    tag = seL4_Call(conn->badged_server_ep_cspath.capPtr, tag);

    if (seL4_GetMR(SSMSGREG_FUNC) != FUNC_WRITE_ACK) {
        ZF_LOGE(SERSERVC"printf: Reply message was not a WRITE_ACK as "
                "expected.");
        return - seL4_IllegalOperation;
    }
    if (seL4_MessageInfo_get_label(tag) != 0) {
        return - seL4_MessageInfo_get_label(tag);
    }

    return seL4_GetMR(SSMSGREG_WRITE_ACK_N_BYTES_WRITTEN);
}
Beispiel #25
0
static int
ipc_test_helper_1(ipc_test_data_t *data)
{
    seL4_Word sender_badge = 0;
    seL4_MessageInfo_t tag;
    int result = 0;

    /* TEST PART 1 */
    /* Receive a pending send. */
    CHECK_STEP(ipc_test_step, 1);
    tag = seL4_Recv(data->ep1, &sender_badge);

    /* As soon as the wait is performed, we should be preempted. */

    /* Thread 3 will give us a chance to check our message. */
    CHECK_STEP(ipc_test_step, 3);
    CHECK_TESTCASE(result, seL4_MessageInfo_get_length(tag) == 20);
    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
        CHECK_TESTCASE(result, seL4_GetMR(i) == i);
    }

    /* Now we bounce to allow thread 3 control again. */
    seL4_MessageInfo_ptr_set_length(&tag, 0);
    seL4_Call(data->ep0, tag);

    /* TEST PART 2 */
    /* Receive a send that is not yet pending. */
    CHECK_STEP(ipc_test_step, 5);
    tag = seL4_Recv(data->ep1, &sender_badge);

    CHECK_STEP(ipc_test_step, 8);
    CHECK_TESTCASE(result, seL4_MessageInfo_get_length(tag) == 19);
    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
        CHECK_TESTCASE(result, seL4_GetMR(i) == i);
    }

    return result;
}
Beispiel #26
0
int write(fildes_t file, const char *buf, size_t nbyte) {
	if (nbyte >= (1 << 12)) {
        return -1;      /* FIXME: crappy limitation */
    }

    struct sos_fhandle* fh = sos_lookup_fhandle (file);
    if (!fh) {
        return -1;
    }

    memcpy (fh->share->buf, buf, nbyte);

    seL4_MessageInfo_t msg = seL4_MessageInfo_new (0, 0, pawpaw_share_attach (fh->share), 3);
    //seL4_SetCap (0, fh->share->cap);

    seL4_SetMR (0, VFS_WRITE);
    seL4_SetMR (1, fh->share->id);
    seL4_SetMR (2, nbyte);

    seL4_Call (fh->cap, msg);
    int wrote = seL4_GetMR (0);

    return wrote;
}
Beispiel #27
0
int main(void)
{
    int error;

    /* give us a name: useful for debugging if the thread faults */
    name_thread(seL4_CapInitThreadTCB, "hello-3");

    /* get boot info */
    info = seL4_GetBootInfo();

    /* print out bootinfo */
    print_bootinfo(info);

    /* get our cspace root cnode */
    seL4_CPtr cspace_cap;
    cspace_cap = seL4_CapInitThreadCNode;

    /* get our vspace root page directory */
    seL4_CPtr pd_cap;
    pd_cap = seL4_CapInitThreadPD;

    /* TODO 1: Find free cap slots for the caps to the:
     *  - tcb
     *  - ipc frame
     *  - endpoint
     *  - badged endpoint
     *  - page table
     * hint: The bootinfo struct contains a range of free cap slot indices.
     */

    /* decide on slots to use based on what is free */
    seL4_CPtr tcb_cap = ???;
    seL4_CPtr ipc_frame_cap = ???;
    ep_cap = ???;
    badged_ep_cap = ???;
    seL4_CPtr page_table_cap = ???;

    /* get an untyped to retype into all the objects we will need */
    seL4_CPtr untyped;

    /* TODO 2: Obtain a cap to an untyped which is large enough to contain:
     *  - tcb
     *  - ipc frame
     *  - endpoint
     *  - badged endpoint
     *  - page table
     *
     * hint 1: determine the size of each object
     *         (look in libs/libsel4/arch_include/x86/sel4/arch/types.h)
     * hint 2: an array of untyped caps, and a corresponding array of untyped sizes
     *         can be found in the bootinfo struct
     * hint 3: a single untyped cap can be retyped multiple times. Each time, an appropriately sized chunk
     *         of the object is "chipped off". For simplicity, find a cap to an untyped which is large enough
     *         to contain all required objects.
     */

    /* TODO 3: Using the untyped, create the required objects, storing their caps in the roottask's root cnode.
     *
     * hint 1: int seL4_Untyped_Retype(seL4_Untyped service, int type, int size_bits, seL4_CNode root, int node_index, int node_depth, int node_offset, int num_objects)
     * hint 2: use a depth of 32
     * hint 3: use cspace_cap for the root cnode AND the cnode_index
     */

    /*
     * map the frame into the vspace at ipc_buffer_vaddr.
     * To do this we first try to map it in to the root page directory.
     * If there is already a page table mapped in the appropriate slot in the
     * page diretory where we can insert this frame, then this will succeed.
     * Otherwise we first need to create a page table, and map it in to
     * the page`directory, before we can map the frame in.
     */
    seL4_Word ipc_buffer_vaddr;
    ipc_buffer_vaddr = IPCBUF_VADDR;
    error = seL4_IA32_Page_Map(ipc_frame_cap, pd_cap, ipc_buffer_vaddr,
                               seL4_AllRights, seL4_IA32_Default_VMAttributes);
    if (error != 0) {

        /* TODO 4: Retype the untyped into page table (if this was done in TODO 3, ignore this). */

        error = seL4_IA32_PageTable_Map(page_table_cap, pd_cap,
                                        ipc_buffer_vaddr, seL4_IA32_Default_VMAttributes);
        assert(error == 0);

        /* then map the frame in */
        error = seL4_IA32_Page_Map(ipc_frame_cap, pd_cap,
                                   ipc_buffer_vaddr, seL4_AllRights, seL4_IA32_Default_VMAttributes);
        assert(error == 0);
    }

    /* set the IPC buffer's virtual address in a field of the IPC buffer */
    seL4_IPCBuffer *ipcbuf = (seL4_IPCBuffer*)ipc_buffer_vaddr;
    ipcbuf->userData = ipc_buffer_vaddr;

    /* TODO 5: Mint a copy of the endpoint cap into our cspace, using the badge EP_BADGE
     * hint: int seL4_CNode_Mint(seL4_CNode service, seL4_Word dest_index, seL4_Uint8 dest_depth, seL4_CNode src_root, seL4_Word src_index, seL4_Uint8 src_depth, seL4_CapRights rights, seL4_CapData_t badge);
     */

    /* initialise the new TCB */
    error = seL4_TCB_Configure(tcb_cap, seL4_CapNull, seL4_MaxPrio,
                               cspace_cap, seL4_NilData, pd_cap, seL4_NilData,
                               ipc_buffer_vaddr, ipc_frame_cap);
    assert(error == 0);

    /* give the new thread a name */
    name_thread(tcb_cap, "hello-3: thread_2");

    /* set start up registers for the new thread */
    size_t regs_size = sizeof(seL4_UserContext) / sizeof(seL4_Word);

    /* check that stack is aligned correctly */
    uintptr_t thread_2_stack_top = (uintptr_t)thread_2_stack + sizeof(thread_2_stack);
    assert(thread_2_stack_top % (sizeof(seL4_Word) * 2) == 0);

    /* set instruction pointer, stack pointer and gs register (used for thread local storage) */
    seL4_UserContext regs = {
        .eip = (seL4_Word)thread_2,
        .esp = (seL4_Word)thread_2_stack_top,
        .gs = IPCBUF_GDT_SELECTOR
    };

    /* actually write the TCB registers. */
    error = seL4_TCB_WriteRegisters(tcb_cap, 0, 0, regs_size, &regs);
    assert(error == 0);

    /* start the new thread running */
    error = seL4_TCB_Resume(tcb_cap);
    assert(error == 0);

    /* we are done, say hello */
    printf("main: hello world\n");

    /*
     * now send a message to the new thread, and wait for a reply
     */

    seL4_Word msg;
    seL4_MessageInfo_t tag;

    /* set the data to send. We send it in the first message register */
    tag = seL4_MessageInfo_new(0, 0, 0, 1);
    seL4_SetMR(0, MSG_DATA);

    /* send and wait for a reply */
    tag = seL4_Call(badged_ep_cap, tag);

    /* check that we got the expected repy */
    assert(seL4_MessageInfo_get_length(tag) == 1);
    msg = seL4_GetMR(0);
    assert(msg == ~MSG_DATA);

    printf("main: got a reply: %#x\n", msg);

    return 0;
}
Beispiel #28
0
int main(void)
{
    UNUSED int error;

    /* Set up logging and give us a name: useful for debugging if the thread faults */
    zf_log_set_tag_prefix("hello-3:");
    name_thread(seL4_CapInitThreadTCB, "hello-3");

    /* get boot info */
    info = seL4_GetBootInfo();

    /* init simple */
    simple_default_init_bootinfo(&simple, info);

    /* print out bootinfo and other info about simple */
    simple_print(&simple);

    /* create an allocator */
    allocman = bootstrap_use_current_simple(&simple, ALLOCATOR_STATIC_POOL_SIZE,        allocator_mem_pool);
    ZF_LOGF_IF(allocman == NULL, "Failed to initialize alloc manager.\n"
        "\tMemory pool sufficiently sized?\n"
        "\tMemory pool pointer valid?\n");

    /* create a vka (interface for interacting with the underlying allocator) */
    allocman_make_vka(&vka, allocman);

    /* get our cspace root cnode */
    seL4_CPtr cspace_cap;
    cspace_cap = simple_get_cnode(&simple);

    /* get our vspace root page directory */
    seL4_CPtr pd_cap;
    pd_cap = simple_get_pd(&simple);

    /* create a new TCB */
    vka_object_t tcb_object = {0};
    error = vka_alloc_tcb(&vka, &tcb_object);
    ZF_LOGF_IFERR(error, "Failed to allocate new TCB.\n"
        "\tVKA given sufficient bootstrap memory?");

    /*
     * create and map an ipc buffer:
     */

    /* TODO 1: get a frame cap for the ipc buffer */
    /* hint: vka_alloc_frame()
     * int vka_alloc_frame(vka_t *vka, uint32_t size_bits, vka_object_t *result)
     * @param vka Pointer to vka interface.
     * @param size_bits Frame size: 2^size_bits
     * @param result Structure for the Frame object.  This gets initialised.
     * @return 0 on success
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_1:
     */
    vka_object_t ipc_frame_object;
    error = vka_alloc_frame(&vka, IPCBUF_FRAME_SIZE_BITS, &ipc_frame_object);
    ZF_LOGF_IFERR(error, "Failed to alloc a frame for the IPC buffer.\n"
        "\tThe frame size is not the number of bytes, but an exponent.\n"
        "\tNB: This frame is not an immediately usable, virtually mapped page.\n")
    /*
     * map the frame into the vspace at ipc_buffer_vaddr.
     * To do this we first try to map it in to the root page directory.
     * If there is already a page table mapped in the appropriate slot in the
     * page diretory where we can insert this frame, then this will succeed.
     * Otherwise we first need to create a page table, and map it in to
     * the page directory, before we can map the frame in. */

    seL4_Word ipc_buffer_vaddr = IPCBUF_VADDR;

    /* TODO 2: try to map the frame the first time  */
    /* hint 1: seL4_ARCH_Page_Map()
     * The *ARCH* versions of seL4 sys calls are abstractions over the architecture provided by libsel4utils
     * this one is defined as:
     * #define seL4_ARCH_Page_Map seL4_X86_Page_Map
     * in: Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_2:
     * The signature for the underlying function is:
     * int seL4_X86_Page_Map(seL4_X86_Page service, seL4_X86_PageDirectory pd, seL4_Word vaddr, seL4_CapRights rights, seL4_X86_VMAttributes attr)
     * @param service Capability to the page to map.
     * @param pd Capability to the VSpace which will contain the mapping.
     * @param vaddr Virtual address to map the page into.
     * @param rights Rights for the mapping.
     * @param attr VM Attributes for the mapping.
     * @return 0 on success.
     *
     * Note: this function is generated during build.  It is generated from the following definition:
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_2:
     * You can find out more about it in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
     *
     * hint 2: for the rights, use seL4_AllRights
     * hint 3: for VM attributes use seL4_ARCH_Default_VMAttributes
     * Hint 4: It is normal for this function call to fail. That means there are
     *	no page tables with free slots -- proceed to the next step where you'll
     *	be led to allocate a new empty page table and map it into the VSpace,
     *	before trying again.
     */
    error = seL4_ARCH_Page_Map(ipc_frame_object.cptr, pd_cap, ipc_buffer_vaddr,
        seL4_AllRights, seL4_ARCH_Default_VMAttributes);

    if (error != 0) {
        /* TODO 3: create a page table */
        /* hint: vka_alloc_page_table()
		 * int vka_alloc_page_table(vka_t *vka, vka_object_t *result)
		 * @param vka Pointer to vka interface.
		 * @param result Structure for the PageTable object.  This gets initialised.
		 * @return 0 on success
		 * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_3:
         */
        vka_object_t pt_object;
        error =  vka_alloc_page_table(&vka, &pt_object);
        ZF_LOGF_IFERR(error, "Failed to allocate new page table.\n");

        /* TODO 4: map the page table */
        /* hint 1: seL4_ARCH_PageTable_Map()
		 * The *ARCH* versions of seL4 sys calls are abstractions over the architecture provided by libsel4utils
		 * this one is defined as:
		 * #define seL4_ARCH_PageTable_Map seL4_X86_PageTable_Map
         * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_4:
		 * The signature for the underlying function is:
		 * int seL4_X86_PageTable_Map(seL4_X86_PageTable service, seL4_X86_PageDirectory pd, seL4_Word vaddr, seL4_X86_VMAttributes attr)
		 * @param service Capability to the page table to map.
		 * @param pd Capability to the VSpace which will contain the mapping.
		 * @param vaddr Virtual address to map the page table into.
		 * @param rights Rights for the mapping.
		 * @param attr VM Attributes for the mapping.
		 * @return 0 on success.
		 *
		 * Note: this function is generated during build.  It is generated from the following definition:
         * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_4:
		 * You can find out more about it in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
		 *
         * hint 2: for VM attributes use seL4_ARCH_Default_VMAttributes
         */
    	error = seL4_ARCH_PageTable_Map(pt_object.cptr, pd_cap,
            ipc_buffer_vaddr, seL4_ARCH_Default_VMAttributes);
        ZF_LOGF_IFERR(error, "Failed to map page table into VSpace.\n"
            "\tWe are inserting a new page table into the top-level table.\n"
            "\tPass a capability to the new page table, and not for example, the IPC buffer frame vaddr.\n")

        /* TODO 5: then map the frame in */
        /* hint 1: use seL4_ARCH_Page_Map() as above
         * hint 2: for the rights, use seL4_AllRights
         * hint 3: for VM attributes use seL4_ARCH_Default_VMAttributes
         */
        error = seL4_ARCH_Page_Map(ipc_frame_object.cptr, pd_cap,
            ipc_buffer_vaddr, seL4_AllRights, seL4_ARCH_Default_VMAttributes);
        ZF_LOGF_IFERR(error, "Failed again to map the IPC buffer frame into the VSpace.\n"
			"\t(It's not supposed to fail.)\n"
            "\tPass a capability to the IPC buffer's physical frame.\n"
            "\tRevisit the first seL4_ARCH_Page_Map call above and double-check your arguments.\n");
    }

    /* set the IPC buffer's virtual address in a field of the IPC buffer */
    seL4_IPCBuffer *ipcbuf = (seL4_IPCBuffer*)ipc_buffer_vaddr;
    ipcbuf->userData = ipc_buffer_vaddr;

    /* TODO 6: create an endpoint */
    /* hint: vka_alloc_endpoint()
     * int vka_alloc_endpoint(vka_t *vka, vka_object_t *result)
     * @param vka Pointer to vka interface.
     * @param result Structure for the Endpoint object.  This gets initialised.
     * @return 0 on success
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_6:
     */
    error = vka_alloc_endpoint(&vka, &ep_object);
    ZF_LOGF_IFERR(error, "Failed to allocate new endpoint object.\n");

    /* TODO 7: make a badged copy of it in our cspace. This copy will be used to send
     * an IPC message to the original cap */
    /* hint 1: vka_mint_object()
     * int vka_mint_object(vka_t *vka, vka_object_t *object, cspacepath_t *result, seL4_CapRights rights, seL4_CapData_t badge)
     * @param[in] vka The allocator for the cspace.
     * @param[in] object Target object for cap minting.
     * @param[out] result Allocated cspacepath.
     * @param[in] rights The rights for the minted cap.
     * @param[in] badge The badge for the minted cap.
     * @return 0 on success
     *
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_7:
     *
     * hint 2: for the rights, use seL4_AllRights
     * hint 3: for the badge use seL4_CapData_Badge_new()
     * seL4_CapData_t CONST seL4_CapData_Badge_new(seL4_Uint32 Badge)
     * @param[in] Badge The badge number to use
     * @return A CapData structure containing the desired badge info
     *
     * seL4_CapData_t is generated during build.
     * The type definition and generated field access functions are defined in a generated file:
     * build/x86/pc99/libsel4/include/sel4/types_gen.h
     * It is generated from the following definition:
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_7:
     * You can find out more about it in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
     *
     * hint 4: for the badge use EP_BADGE
     */
    error = vka_mint_object(&vka, &ep_object, &ep_cap_path, seL4_AllRights,
        seL4_CapData_Badge_new(EP_BADGE));
    ZF_LOGF_IFERR(error, "Failed to mint new badged copy of IPC endpoint.\n"
        "\tseL4_Mint is the backend for vka_mint_object.\n"
        "\tseL4_Mint is simply being used here to create a badged copy of the same IPC endpoint.\n"
        "\tThink of a badge in this case as an IPC context cookie.\n");

    /* initialise the new TCB */
    error = seL4_TCB_Configure(tcb_object.cptr, seL4_CapNull, seL4_PrioProps_new(seL4_MaxPrio, seL4_MaxPrio),
        cspace_cap, seL4_NilData, pd_cap, seL4_NilData,
        ipc_buffer_vaddr, ipc_frame_object.cptr);
    ZF_LOGF_IFERR(error, "Failed to configure the new TCB object.\n"
        "\tWe're running the new thread with the root thread's CSpace.\n"
        "\tWe're running the new thread in the root thread's VSpace.\n");


    /* give the new thread a name */
    name_thread(tcb_object.cptr, "hello-3: thread_2");

    /* set start up registers for the new thread */
    seL4_UserContext regs = {0};
    size_t regs_size = sizeof(seL4_UserContext) / sizeof(seL4_Word);

    /* set instruction pointer where the thread shoud start running */
    sel4utils_set_instruction_pointer(&regs, (seL4_Word)thread_2);

    /* check that stack is aligned correctly */
    const int stack_alignment_requirement = sizeof(seL4_Word) * 2;
    uintptr_t thread_2_stack_top = (uintptr_t)thread_2_stack + sizeof(thread_2_stack);

    ZF_LOGF_IF(thread_2_stack_top % (stack_alignment_requirement) != 0,
        "Stack top isn't aligned correctly to a %dB boundary.\n"
        "\tDouble check to ensure you're not trampling.",
        stack_alignment_requirement);

    /* set stack pointer for the new thread. remember the stack grows down */
    sel4utils_set_stack_pointer(&regs, thread_2_stack_top);

    /* set the fs register for IPC buffer */
    regs.fs = IPCBUF_GDT_SELECTOR;

    /* actually write the TCB registers. */
    error = seL4_TCB_WriteRegisters(tcb_object.cptr, 0, 0, regs_size, &regs);
    ZF_LOGF_IFERR(error, "Failed to write the new thread's register set.\n"
        "\tDid you write the correct number of registers? See arg4.\n");

    /* start the new thread running */
    error = seL4_TCB_Resume(tcb_object.cptr);
    ZF_LOGF_IFERR(error, "Failed to start new thread.\n");

    /* we are done, say hello */
    printf("main: hello world\n");

    /*
     * now send a message to the new thread, and wait for a reply
     */

    seL4_Word msg;
    seL4_MessageInfo_t tag;

    /* TODO 8: set the data to send. We send it in the first message register */
    /* hint 1: seL4_MessageInfo_new()
     * seL4_MessageInfo_t CONST seL4_MessageInfo_new(seL4_Uint32 label, seL4_Uint32 capsUnwrapped, seL4_Uint32 extraCaps, seL4_Uint32 length)
     * @param label The value of the label field
     * @param capsUnwrapped The value of the capsUnwrapped field
     * @param extraCaps The value of the extraCaps field
     * @param length The number of message registers to send
     * @return The seL4_MessageInfo_t containing the given values.
     *
     * seL4_MessageInfo_new() is generated during build. It can be found in:
     * build/x86/pc99/libsel4/include/sel4/types_gen.h
     * It is generated from the following definition:
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_8:
     *
     * hint 2: use 0 for the first 3 fields.
     * hint 3: send only 1 message register of data
     *
     * hint 4: seL4_SetMR()
     * void seL4_SetMR(int i, seL4_Word mr)
     * @param i The message register to write
     * @param mr The value of the message register
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_8:
     * You can find out more about message registers in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
     *
     * hint 5: send MSG_DATA
     */
    tag = seL4_MessageInfo_new(0, 0, 0, 1);
    seL4_SetMR(0, MSG_DATA);

    /* TODO 9: send and wait for a reply. */
    /* hint: seL4_Call()
     * seL4_MessageInfo_t seL4_Call(seL4_CPtr dest, seL4_MessageInfo_t msgInfo)
     * @param dest The capability to be invoked.
     * @param msgInfo The messageinfo structure for the IPC.  This specifies information about the message to send (such as the number of message registers to send).
     * @return A seL4_MessageInfo_t structure.  This is information about the repy message.
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_9:
     * You can find out more about it in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
     *
     * hint 2: seL4_MessageInfo_t is generated during build.
     * The type definition and generated field access functions are defined in a generated file:
     * build/x86/pc99/libsel4/include/sel4/types_gen.h
     * It is generated from the following definition:
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_9:
     * You can find out more about it in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
     */
    tag = seL4_Call(ep_cap_path.capPtr, tag);

    /* TODO 10: get the reply message */
    /* hint: seL4_GetMR()
     * seL4_Word seL4_GetMR(int i)
     * @param i The message register to retreive
     * @return The message register value
     * Link to source: https://wiki.sel4.systems/seL4%20Tutorial%203#TODO_10:
     * You can find out more about message registers in the API manual: http://sel4.systems/Info/Docs/seL4-manual-3.0.0.pdf
     */
    msg = seL4_GetMR(0);

    /* check that we got the expected repy */
    ZF_LOGF_IF(seL4_MessageInfo_get_length(tag) != 1,
        "Response data from thread_2 was not the length expected.\n"
        "\tHow many registers did you set with seL4_SetMR within thread_2?\n");

    ZF_LOGF_IF(msg != ~MSG_DATA,
        "Response data from thread_2's content was not what was expected.\n");

    printf("main: got a reply: %#x\n", msg);

    return 0;
}
Beispiel #29
0
int
serial_server_client_connect(seL4_CPtr badged_server_ep_cap,
                             vka_t *client_vka, vspace_t *client_vspace,
                             serial_client_context_t *conn)
{
    seL4_Error error;
    int shmem_n_pages;
    uintptr_t shmem_tmp_vaddr;
    seL4_MessageInfo_t tag;
    cspacepath_t frame_cspath;

    if (badged_server_ep_cap == 0 || client_vka == NULL || client_vspace == NULL
            || conn == NULL) {
        return seL4_InvalidArgument;
    }

    memset(conn, 0, sizeof(serial_client_context_t));

    shmem_n_pages = BYTES_TO_4K_PAGES(SERIAL_SERVER_SHMEM_MAX_SIZE);
    if (shmem_n_pages > seL4_MsgMaxExtraCaps) {
        ZF_LOGE(SERSERVC"connect: Currently unsupported shared memory size: "
                "IPC cap transfer capability is inadequate.");
        return seL4_RangeError;
    }
    conn->shmem = vspace_new_pages(client_vspace,
                                   seL4_AllRights,
                                   shmem_n_pages,
                                   seL4_PageBits);
    if (conn->shmem == NULL) {
        ZF_LOGE(SERSERVC"connect: Failed to alloc shmem.");
        return seL4_NotEnoughMemory;
    }
    assert(IS_ALIGNED((uintptr_t)conn->shmem, seL4_PageBits));

    /* Look up the Frame cap behind each page in the shmem range, and marshal
     * all of those Frame caps to the parent. The parent will then map those
     * Frames into its VSpace and establish a shmem link.
     */
    shmem_tmp_vaddr = (uintptr_t)conn->shmem;
    for (int i = 0; i < shmem_n_pages; i++) {
        vka_cspace_make_path(client_vka,
                             vspace_get_cap(client_vspace,
                                            (void *)shmem_tmp_vaddr),
                             &frame_cspath);

        seL4_SetCap(i, frame_cspath.capPtr);
        shmem_tmp_vaddr += BIT(seL4_PageBits);
    }

    /* Call the server asking it to establish the shmem mapping with us, and
     * get us connected up.
     */
    seL4_SetMR(SSMSGREG_FUNC, FUNC_CONNECT_REQ);
    seL4_SetMR(SSMSGREG_CONNECT_REQ_SHMEM_SIZE,
               SERIAL_SERVER_SHMEM_MAX_SIZE);
    /* extraCaps doubles up as the number of shmem pages. */
    tag = seL4_MessageInfo_new(0, 0,
                               shmem_n_pages,
                               SSMSGREG_CONNECT_REQ_END);

    tag = seL4_Call(badged_server_ep_cap, tag);

    /* It makes sense to verify that the message we're getting back is an
     * ACK response to our request message.
     */
    if (seL4_GetMR(SSMSGREG_FUNC) != FUNC_CONNECT_ACK) {
        error = seL4_IllegalOperation;
        ZF_LOGE(SERSERVC"connect: Reply message was not a CONNECT_ACK as "
                "expected.");
        goto out;
    }
    /* When the parent replies, we check to see if it was successful, etc. */
    error = seL4_MessageInfo_get_label(tag);
    if (error != (int)SERIAL_SERVER_NOERROR) {
        ZF_LOGE(SERSERVC"connect ERR %d: Failed to connect to the server.",
                error);

        if (error == (int)SERIAL_SERVER_ERROR_SHMEM_TOO_LARGE) {
            ZF_LOGE(SERSERVC"connect: Your requested shmem mapping size is too "
                    "large.\n\tServer's max shmem size is %luB.",
                    (long)seL4_GetMR(SSMSGREG_CONNECT_ACK_MAX_SHMEM_SIZE));
        }
        goto out;
    }

    conn->shmem_size = SERIAL_SERVER_SHMEM_MAX_SIZE;
    vka_cspace_make_path(client_vka, badged_server_ep_cap,
                         &conn->badged_server_ep_cspath);

    return seL4_NoError;

out:
    if (conn->shmem != NULL) {
        vspace_unmap_pages(client_vspace, (void *)conn->shmem, shmem_n_pages,
                           seL4_PageBits, VSPACE_FREE);
    }
    return error;
}
Beispiel #30
0
static int
ipc_test_helper_3(ipc_test_data_t *data)
{
    seL4_MessageInfo_t tag;
    int last_spins, last_bounces, result = 0;

    /* This test starts here. */

    /* TEST PART 1 */
    /* Perform a send to a thread 1. It is not yet waiting. */
    CHECK_STEP(ipc_test_step, 0);
    seL4_MessageInfo_ptr_new(&tag, 0, 0, 0, 20);
    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
        seL4_SetMR(i, i);
    }
    last_spins = data->spins;
    last_bounces = data->bounces;
    seL4_Send(data->ep1, tag);
    /* We block, causing thread 2 to spin for a while, before it calls the
     * bouncer thread 0, which finally lets thread 1 run and reply to us. */
    CHECK_STEP(ipc_test_step, 2);
    CHECK_TESTCASE(result, data->spins - last_spins == 1);
    CHECK_TESTCASE(result, data->bounces - last_bounces == 0);

    /* Now bounce ourselves, to ensure that thread 1 can check its stuff. */
    seL4_MessageInfo_ptr_set_length(&tag, 0);
    seL4_Call(data->ep0, tag);

    /* Two bounces - us and thread 1. */
    CHECK_TESTCASE(result, data->spins - last_spins == 2);
    CHECK_TESTCASE(result, data->bounces - last_bounces == 2);
    CHECK_STEP(ipc_test_step, 4);

    /* TEST PART 2 */
    /* Perform a send to a thread 1, which is already waiting. */
    /* Bounce first to let thread prepare. */
    last_spins = data->spins;
    last_bounces = data->bounces;

    seL4_MessageInfo_ptr_set_length(&tag, 0);
    seL4_Call(data->ep0, tag);
    CHECK_STEP(ipc_test_step, 6);

    /* Do the send. */
    seL4_MessageInfo_ptr_set_length(&tag, 19);
    for (int i = 0; i < seL4_MessageInfo_get_length(tag); i++) {
        seL4_SetMR(i, i);
    }
    seL4_Send(data->ep1, tag);

    CHECK_STEP(ipc_test_step, 7);

    /* Bounce to let thread 1 check again. */
    seL4_MessageInfo_ptr_set_length(&tag, 0);
    seL4_Call(data->ep0, tag);

    CHECK_STEP(ipc_test_step, 9);

    /* Five bounces in total. */
    CHECK_TESTCASE(result, data->spins - last_spins == 2);
    CHECK_TESTCASE(result, data->bounces - last_bounces == 5);

    return result;
}