1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <assert.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <sel4/sel4.h>
18#include <sel4utils/arch/util.h>
19
20#include <vka/object.h>
21
22#include "../test.h"
23#include "../helpers.h"
24
25#ifdef CONFIG_ARCH_X86
26
27#define START_PORT 0
28#define END_PORT BIT(16)
29#define PORT_STRIDE 256
30#define EXPECTED_FAULTS ((END_PORT - START_PORT) / PORT_STRIDE)
31
32static volatile int total_faults = 0;
33
34static void increment_pc(seL4_CPtr tcb, seL4_Word inc)
35{
36    seL4_UserContext ctx;
37    seL4_Error error = seL4_TCB_ReadRegisters(tcb,
38                                              false,
39                                              0,
40                                              sizeof(ctx) / sizeof(seL4_Word),
41                                              &ctx);
42    test_eq(error, seL4_NoError);
43#ifdef CONFIG_ARCH_X86_64
44    ctx.rax = 1;
45    ctx.rip += inc;
46#else
47    ctx.eax = 1;
48    ctx.eip += inc;
49#endif
50    error = seL4_TCB_WriteRegisters(tcb,
51                                    true,
52                                    0,
53                                    sizeof(ctx) / sizeof(seL4_Word),
54                                    &ctx);
55    test_eq(error, seL4_NoError);
56}
57
58static int handle_fault(seL4_CPtr fault_ep, seL4_CPtr tcb, seL4_Word expected_fault, seL4_CPtr reply)
59{
60    seL4_MessageInfo_t tag;
61    seL4_Word sender_badge = 0;
62
63    while (1) {
64        tag = api_recv(fault_ep, &sender_badge, reply);
65
66        test_check(seL4_MessageInfo_get_label(tag) == seL4_Fault_UserException);
67
68        total_faults++;
69        increment_pc(tcb, 1);
70    }
71
72    return 0;
73}
74
75static int do_ioports(int arg1, int arg2, int arg3, int arg4)
76{
77    unsigned int i;
78    for (i = START_PORT; i < END_PORT; i += PORT_STRIDE) {
79        volatile unsigned char dummy = 0;
80        asm volatile("inb %1,%0"
81                     : "=a"(dummy)
82                     : "dN"((uint16_t)i)
83                    );
84        test_check(dummy == 1);
85    }
86    return 0;
87}
88
89static int test_native_ioports(env_t env)
90{
91    helper_thread_t handler_thread;
92    helper_thread_t faulter_thread;
93    int error;
94    seL4_Word handler_arg0, handler_arg1;
95    /* The endpoint on which faults are received. */
96    seL4_CPtr fault_ep = vka_alloc_endpoint_leaky(&env->vka);
97    seL4_CPtr faulter_vspace, faulter_cspace;
98
99    create_helper_thread(env, &faulter_thread);
100    create_helper_thread(env, &handler_thread);
101    faulter_cspace = env->cspace_root;
102    faulter_vspace = env->page_directory;
103    handler_arg0 = fault_ep;
104    handler_arg1 = get_helper_tcb(&faulter_thread);
105    set_helper_priority(env, &handler_thread, 100);
106
107    error = api_tcb_set_space(get_helper_tcb(&faulter_thread),
108                              fault_ep,
109                              faulter_cspace,
110                              api_make_guard_skip_word(seL4_WordBits - env->cspace_size_bits),
111                              faulter_vspace, seL4_NilData);
112    set_helper_priority(env, &faulter_thread, 100);
113
114    test_error_eq(error, seL4_NoError);
115
116    /* clear the faults */
117    total_faults = 0;
118
119    start_helper(env, &handler_thread, (helper_fn_t) handle_fault,
120                 handler_arg0, handler_arg1, 0, get_helper_reply(&handler_thread));
121    start_helper(env, &faulter_thread, (helper_fn_t) do_ioports,
122                 0, 0, 0, 0);
123
124    wait_for_helper(&faulter_thread);
125
126    test_check(total_faults == EXPECTED_FAULTS);
127
128    cleanup_helper(env, &handler_thread);
129    cleanup_helper(env, &faulter_thread);
130
131    return (total_faults == EXPECTED_FAULTS) ? SUCCESS : FAILURE;
132}
133DEFINE_TEST(IOPORTS1000, "Test fault if directly using I/O ports", test_native_ioports, true)
134
135#endif
136