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 <assert.h>
14
15#include <sel4/sel4.h>
16#include <vka/object.h>
17#include <vka/capops.h>
18
19#include "../helpers.h"
20
21/* Arbitrarily start mapping 256mb into the virtual address range */
22#define IOPT_MAP_BASE 0x10000000
23
24/* None of these tests actually check that mappings succeed sensibly. This would
25 * require having a device that does DMA and making it perform operations.
26 * I consider this too much work, and am largely checking that none of these
27 * operations will cause the kernel to explode */
28
29#define MAX_IOPT_DEPTH 8
30
31/* The depth at which we expect the last PT to be at before 4K frame
32 * mappings. This is 'expected' depth as the actual depth can change
33 * per machine. Ideally this value should be exported from the kernel */
34#define EXPECTED_PT_DEPTH 3
35
36typedef struct iopt_cptrs {
37    int depth;
38    seL4_CPtr pts[MAX_IOPT_DEPTH];
39} iopt_cptrs_t;
40
41#define FAKE_PCI_DEVICE 0x216u
42#define DOMAIN_ID       0xf
43
44#ifdef CONFIG_IOMMU
45
46static int map_iopt_from_iospace(env_t env, seL4_CPtr iospace, iopt_cptrs_t *pts, seL4_CPtr *frame)
47{
48    int error = seL4_NoError;
49
50    pts->depth = 0;
51    /* Allocate and map page tables until we can map a frame */
52    *frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
53    test_assert(*frame);
54
55    while (seL4_X86_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE) == seL4_FailedLookup) {
56        test_assert(pts->depth < MAX_IOPT_DEPTH);
57        pts->pts[pts->depth] = vka_alloc_io_page_table_leaky(&env->vka);
58        test_assert(pts->pts[pts->depth]);
59        error = seL4_X86_IOPageTable_Map(pts->pts[pts->depth], iospace, IOPT_MAP_BASE);
60        test_eq(error, seL4_NoError);
61        pts->depth++;
62    }
63    test_eq(error, seL4_NoError);
64
65    return error;
66}
67
68static int map_iopt_set(env_t env, seL4_CPtr *iospace, iopt_cptrs_t *pts, seL4_CPtr *frame)
69{
70    int error;
71    cspacepath_t master_path, iospace_path;
72
73    /* Allocate a random device ID that hopefully doesn't exist have any
74     * RMRR regions */
75    error = vka_cspace_alloc(&env->vka, iospace);
76    test_error_eq(error, seL4_NoError);
77    vka_cspace_make_path(&env->vka, *iospace, &iospace_path);
78    vka_cspace_make_path(&env->vka, env->io_space, &master_path);
79    error = vka_cnode_mint(&iospace_path, &master_path, seL4_AllRights, (DOMAIN_ID << 16) | FAKE_PCI_DEVICE);
80    test_eq(error, seL4_NoError);
81
82    error = map_iopt_from_iospace(env, *iospace, pts, frame);
83
84    return error;
85}
86
87static void delete_iospace(env_t env, seL4_CPtr iospace)
88{
89    cspacepath_t path;
90    vka_cspace_make_path(&env->vka, iospace, &path);
91    vka_cnode_delete(&path);
92}
93
94static int test_iopt_basic_iopt(env_t env)
95{
96    int error;
97    seL4_CPtr iospace, frame;
98    iopt_cptrs_t pts;
99    error = map_iopt_set(env, &iospace, &pts, &frame);
100    test_eq(error, seL4_NoError);
101
102    delete_iospace(env, iospace);
103    return sel4test_get_result();
104}
105DEFINE_TEST(IOPT0001, "Testing basic IOPT mapping", test_iopt_basic_iopt, true)
106
107static int
108test_iopt_basic_map_unmap(env_t env)
109{
110    int error;
111    int i;
112    iopt_cptrs_t pts;
113    seL4_CPtr iospace, frame;
114    error = map_iopt_set(env, &iospace, &pts, &frame);
115    test_eq(error, seL4_NoError);
116
117    error = seL4_X86_Page_Unmap(frame);
118    test_eq(error, seL4_NoError);
119    for (i = pts.depth - 1; i >= 0; i--) {
120        error = seL4_X86_IOPageTable_Unmap(pts.pts[i]);
121        test_eq(error, seL4_NoError);
122    }
123
124    error = map_iopt_from_iospace(env, iospace, &pts, &frame);
125    test_eq(error, seL4_NoError);
126
127    for (i = 0; i < pts.depth; i++) {
128        error = seL4_X86_IOPageTable_Unmap(pts.pts[i]);
129        test_eq(error, seL4_NoError);
130    }
131
132    error = seL4_X86_Page_Unmap(frame);
133    test_eq(error, seL4_NoError);
134
135    delete_iospace(env, iospace);
136    return sel4test_get_result();
137}
138DEFINE_TEST(IOPT0002, "Test basic IOPT mapping then unmapping", test_iopt_basic_map_unmap, true)
139
140static int
141test_iopt_no_overlapping_4k(env_t env)
142{
143    int error;
144    iopt_cptrs_t pts;
145    seL4_CPtr iospace, frame;
146    error = map_iopt_set(env, &iospace, &pts, &frame);
147    test_eq(error, seL4_NoError);
148
149    frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
150    test_assert(frame);
151    error = seL4_X86_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
152    test_assert(error != seL4_NoError);
153
154    delete_iospace(env, iospace);
155    return sel4test_get_result();
156}
157DEFINE_TEST(IOPT0004, "Test IOPT cannot map overlapping 4k pages", test_iopt_no_overlapping_4k, true)
158
159static int
160test_iopt_map_remap_top_pt(env_t env)
161{
162    int error;
163    iopt_cptrs_t pts;
164    seL4_CPtr iospace, frame, pt;
165    error = map_iopt_set(env, &iospace, &pts, &frame);
166    test_eq(error, seL4_NoError);
167
168    /* unmap the top PT */
169    error = seL4_X86_IOPageTable_Unmap(pts.pts[EXPECTED_PT_DEPTH]);
170    test_eq(error, seL4_NoError);
171
172    /* now map it back in */
173    error = seL4_X86_IOPageTable_Map(pts.pts[EXPECTED_PT_DEPTH], iospace, IOPT_MAP_BASE);
174    test_eq(error, seL4_NoError);
175
176    /* it should retain its old mappings, and mapping in a new PT should fail */
177    pt = vka_alloc_io_page_table_leaky(&env->vka);
178    test_assert(pt);
179    error = seL4_X86_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
180    test_assert(error != seL4_NoError);
181
182    delete_iospace(env, iospace);
183    return sel4test_get_result();
184}
185DEFINE_TEST(IOPT0008, "Test IOPT map and remap top PT", test_iopt_map_remap_top_pt, true)
186
187static int
188test_iopt_no_overlapping_pt(env_t env)
189{
190    int error;
191    iopt_cptrs_t pts;
192    seL4_CPtr iospace, frame, pt;
193    error = map_iopt_set(env, &iospace, &pts, &frame);
194    test_eq(error, seL4_NoError);
195
196    /* Mapping in a new PT should fail */
197    pt = vka_alloc_io_page_table_leaky(&env->vka);
198    test_assert(pt);
199    error = seL4_X86_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
200    test_assert(error != seL4_NoError);
201
202    delete_iospace(env, iospace);
203    return sel4test_get_result();
204}
205DEFINE_TEST(IOPT0009, "Test iopt no overlapping PT", test_iopt_no_overlapping_pt, true)
206
207static int
208test_iopt_map_remap_pt(env_t env)
209{
210    int error;
211    iopt_cptrs_t pts;
212    seL4_CPtr iospace, frame;
213    error = map_iopt_set(env, &iospace, &pts, &frame);
214    test_eq(error, seL4_NoError);
215
216    /* unmap the pt */
217    error = seL4_X86_IOPageTable_Unmap(pts.pts[pts.depth - 1]);
218    test_eq(error, seL4_NoError);
219
220    /* now map it back in */
221    error = seL4_X86_IOPageTable_Map(pts.pts[pts.depth - 1], iospace, IOPT_MAP_BASE);
222    test_eq(error, seL4_NoError);
223
224    /* it should retain its old mappings, and mapping in a new frame should fail */
225    frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
226    test_assert(frame);
227    error = seL4_X86_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
228    test_assert(error != seL4_NoError);
229
230    delete_iospace(env, iospace);
231    return sel4test_get_result();
232}
233DEFINE_TEST(IOPT0011, "Test IOPT map and remap PT", test_iopt_map_remap_pt, true)
234
235#endif /* CONFIG_IOMMU */
236
237#ifdef CONFIG_TK1_SMMU
238/* tests for ARM SystemMMU */
239
240#define IOPT_MAP_BASE   0x10000000
241
242static int
243map_iopt_from_iospace(env_t env, seL4_CPtr iospace, seL4_CPtr *iopt, seL4_CPtr *frame)
244{
245    int error;
246    *frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
247    test_assert(*frame);
248    error = seL4_ARM_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
249
250    if (error == seL4_FailedLookup) {
251        *iopt = vka_alloc_io_page_table_leaky(&env->vka);
252        test_assert(*iopt);
253        error = seL4_ARM_IOPageTable_Map(*iopt, iospace, IOPT_MAP_BASE);
254        test_eq(error, seL4_NoError);
255        error = seL4_ARM_Page_MapIO(*frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
256        test_eq(error, seL4_NoError);
257    }
258    test_eq(error, seL4_NoError);
259
260    return error;
261
262}
263
264static int map_iopt_set(env_t env, seL4_CPtr iospace, seL4_CPtr *iopt_cptr, seL4_CPtr *frame)
265{
266    int error = map_iopt_from_iospace(env, iospace, iopt_cptr, frame);
267    return error;
268
269}
270
271static void delete_iospace(env_t env, seL4_CPtr iospace)
272{
273    cspacepath_t path;
274    vka_cspace_make_path(&env->vka, iospace, &path);
275    vka_cnode_delete(&path);
276}
277
278static int test_iopt_basic_iopt(env_t env)
279{
280    int error;
281    seL4_CPtr pt = 0;
282    seL4_CPtr frame = 0;
283    seL4_SlotRegion caps = env->io_space_caps;
284    int cap_count = caps.end - caps.start + 1;
285    seL4_CPtr cap = caps.start;
286
287    for (int i = 0; i < cap_count; i++) {
288        error = map_iopt_set(env, cap + i, &pt, &frame);
289        test_eq(error, seL4_NoError);
290        delete_iospace(env, cap + i);
291    }
292    return sel4test_get_result();
293}
294DEFINE_TEST(IOPT0001, "Testing basic ARM IOPT mapping", test_iopt_basic_iopt, true);
295
296static int test_iopt_basic_map_unmap(env_t env)
297{
298    int error;
299    int i;
300    seL4_CPtr iospace, pt, frame;
301    seL4_SlotRegion caps = env->io_space_caps;
302    int cap_count = caps.end - caps.start + 1;
303
304    for (i = 0; i < cap_count; i++) {
305        iospace = caps.start + i;
306        error = map_iopt_set(env, iospace, &pt, &frame);
307        test_eq(error, seL4_NoError);
308
309        error = seL4_ARM_Page_Unmap(frame);
310        test_eq(error, seL4_NoError);
311
312        error = seL4_ARM_IOPageTable_Unmap(pt);
313        test_eq(error, seL4_NoError);
314
315        error = map_iopt_from_iospace(env, iospace, &pt, &frame);
316        test_eq(error, seL4_NoError);
317
318        error = seL4_ARM_IOPageTable_Unmap(pt);
319        test_eq(error, seL4_NoError);
320        error = seL4_ARM_Page_Unmap(frame);
321        test_eq(error, seL4_NoError);
322        delete_iospace(env, iospace);
323    }
324    return sel4test_get_result();
325}
326DEFINE_TEST(IOPT0002, "Test basic ARM IOPT mapping then unmapping", test_iopt_basic_map_unmap, true)
327
328static int
329test_iopt_no_overlapping_4k(env_t env)
330{
331    int error;
332    int i;
333    seL4_CPtr iospace, pt, frame;
334    seL4_SlotRegion caps = env->io_space_caps;
335    int cap_count = caps.end - caps.start + 1;
336    for (i = 0; i < cap_count; i++) {
337        iospace = caps.start + i;
338        error = map_iopt_set(env, iospace, &pt, &frame);
339        test_eq(error, seL4_NoError);
340
341        frame = vka_alloc_frame_leaky(&env->vka, seL4_PageBits);
342        test_assert(frame);
343        /* mapping in a new frame should fail */
344        error = seL4_ARM_Page_MapIO(frame, iospace, seL4_AllRights, IOPT_MAP_BASE);
345        test_assert(error != seL4_NoError);
346        delete_iospace(env, iospace);
347    }
348
349    return sel4test_get_result();
350}
351DEFINE_TEST(IOPT0004, "Test ARM IOPT cannot map overlapping 4k pages", test_iopt_no_overlapping_4k, true)
352
353static int
354test_iopt_map_remap_pt(env_t env)
355{
356    int error;
357    int i;
358    seL4_CPtr iospace, pt, frame;
359    seL4_SlotRegion caps = env->io_space_caps;
360    int cap_count = caps.end - caps.start + 1;
361    for (i = 0; i < cap_count; i++) {
362        iospace = caps.start + i;
363        error = map_iopt_set(env, iospace, &pt, &frame);
364        test_eq(error, seL4_NoError);
365
366        /* unmap the PT */
367        error = seL4_ARM_IOPageTable_Unmap(pt);
368        test_eq(error, seL4_NoError);
369
370        /* now map it back in */
371        error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
372        test_eq(error, seL4_NoError);
373
374        /* it should retain its old mappings, and mapping in a new PT should fail */
375        pt = vka_alloc_io_page_table_leaky(&env->vka);
376        test_assert(pt);
377        error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
378        test_assert(error != seL4_NoError);
379        delete_iospace(env, iospace);
380    }
381
382    return sel4test_get_result();
383}
384DEFINE_TEST(IOPT0008, "Test ARM IOPT map and remap PT", test_iopt_map_remap_pt, true)
385
386static int
387test_iopt_no_overlapping_pt(env_t env)
388{
389    int error;
390    int i;
391    seL4_CPtr iospace, pt, frame;
392    seL4_SlotRegion caps = env->io_space_caps;
393    int cap_count = caps.end - caps.start + 1;
394    for (i = 0; i < cap_count; i++) {
395        iospace = caps.start + i;
396        error = map_iopt_set(env, iospace, &pt, &frame);
397        test_eq(error, seL4_NoError);
398
399        /* Mapping in a new PT should fail */
400        pt = vka_alloc_io_page_table_leaky(&env->vka);
401        test_assert(pt);
402        error = seL4_ARM_IOPageTable_Map(pt, iospace, IOPT_MAP_BASE);
403        test_assert(error != seL4_NoError);
404        delete_iospace(env, iospace);
405    }
406    return sel4test_get_result();
407}
408DEFINE_TEST(IOPT0009, "Test ARM iopt no overlapping PT", test_iopt_no_overlapping_pt, true)
409
410#endif
411