1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <autoconf.h>
8#include <sel4vm/gen_config.h>
9
10#include <sel4utils/vspace.h>
11#include <sel4utils/vspace_internal.h>
12#include <vka/capops.h>
13
14#include <sel4vm/guest_iospace.h>
15
16#include "guest_vspace.h"
17#include "guest_vspace_arch.h"
18
19typedef struct guest_iospace {
20    seL4_CPtr iospace;
21    struct sel4utils_alloc_data iospace_vspace_data;
22    vspace_t iospace_vspace;
23} guest_iospace_t;
24
25typedef struct guest_vspace {
26    /* We abuse struct ordering and this member MUST be the first
27     * thing in the struct */
28    struct sel4utils_alloc_data vspace_data;
29    /* debug flag for checking if we add io spaces late */
30    int done_mapping;
31    int num_iospaces;
32    guest_iospace_t **iospaces;
33} guest_vspace_t;
34
35static int guest_vspace_map(vspace_t *vspace, seL4_CPtr cap, void *vaddr, seL4_CapRights_t rights,
36                            int cacheable, size_t size_bits)
37{
38    int error;
39    /* perform the guest mapping */
40    error = guest_vspace_map_page_arch(vspace, cap, vaddr, rights, cacheable, size_bits);
41    if (error) {
42        return error;
43    }
44
45#if defined(CONFIG_TK1_SMMU) || defined(CONFIG_IOMMU)
46    struct sel4utils_alloc_data *data = get_alloc_data(vspace);
47    /* this type cast works because the alloc data was at the start of the struct
48     * so it has the same address.
49     * This conversion is guaranteed to work by the C standard */
50    guest_vspace_t *guest_vspace = (guest_vspace_t *) data;
51    /* set the mapping bit */
52    guest_vspace->done_mapping = 1;
53    cspacepath_t orig_path;
54    /* duplicate the cap so we can do a mapping */
55    vka_cspace_make_path(guest_vspace->vspace_data.vka, cap, &orig_path);
56    /* map into all the io spaces */
57    for (int i = 0; i < guest_vspace->num_iospaces; i++) {
58        cspacepath_t new_path;
59        error = vka_cspace_alloc_path(guest_vspace->vspace_data.vka, &new_path);
60        if (error) {
61            ZF_LOGE("Failed to allocate cslot to duplicate frame cap");
62            return error;
63        }
64        error = vka_cnode_copy(&new_path, &orig_path, seL4_AllRights);
65
66        guest_iospace_t *guest_iospace = guest_vspace->iospaces[i];
67
68        assert(error == seL4_NoError);
69        error = sel4utils_map_iospace_page(guest_vspace->vspace_data.vka, guest_iospace->iospace,
70                                           new_path.capPtr, (uintptr_t)vaddr, rights, 1,
71                                           size_bits, NULL, NULL);
72        if (error) {
73            ZF_LOGE("Failed to map page into iospace");
74            return error;
75        }
76
77        /* Store the slot of the frame cap copy in a vspace so they can be looked up and
78         * freed when this address gets unmapped. */
79        error = update_entries(&guest_iospace->iospace_vspace, (uintptr_t)vaddr, new_path.capPtr, size_bits, 0 /* cookie */);
80        if (error) {
81            ZF_LOGE("Failed to add iospace mapping information");
82            return error;
83        }
84    }
85#endif
86    return 0;
87}
88
89void guest_vspace_unmap(vspace_t *vspace, void *vaddr, size_t num_pages, size_t size_bits, vka_t *vka)
90{
91    struct sel4utils_alloc_data *data = get_alloc_data(vspace);
92    guest_vspace_t *guest_vspace = (guest_vspace_t *) data;
93
94    int error;
95
96    /* Unmap pages from PT.
97     * vaddr is a guest physical address.
98     * This can be done in a single call as mappings are contiguous in this vspace. */
99    sel4utils_unmap_pages(vspace, vaddr, num_pages, size_bits, vka);
100
101#if defined(CONFIG_ARM_SMMU) || defined(CONFIG_IOMMU)
102    /* Each page must be unmapped individually from the vmm vspace, as mappings are not
103     * necessarily host-virtually contiguous. */
104    size_t page_size = BIT(size_bits);
105    for (int i = 0; i < num_pages; i++) {
106        void *page_vaddr = (void *)(vaddr + i * page_size);
107        /* Unmap the vaddr from each iospace, freeing the cslots used to store the
108         * copy of the frame cap. */
109        for (int i = 0; i < guest_vspace->num_iospaces; i++) {
110            guest_iospace_t *guest_iospace = guest_vspace->iospaces[i];
111            seL4_CPtr iospace_frame_cap_copy = vspace_get_cap(&guest_iospace->iospace_vspace, page_vaddr);
112
113            error = seL4_ARCH_Page_Unmap(iospace_frame_cap_copy);
114            if (error) {
115                ZF_LOGE("Failed to unmap page from iospace");
116                return;
117            }
118
119            cspacepath_t path;
120            vka_cspace_make_path(guest_vspace->vspace_data.vka, iospace_frame_cap_copy, &path);
121            error = vka_cnode_delete(&path);
122            if (error) {
123                ZF_LOGE("Failed to delete frame cap copy");
124                return;
125            }
126
127            vka_cspace_free(guest_vspace->vspace_data.vka, iospace_frame_cap_copy);
128
129            error = clear_entries(&guest_iospace->iospace_vspace, (uintptr_t)page_vaddr, size_bits);
130            if (error) {
131                ZF_LOGE("Failed to clear iospace mapping information");
132                return;
133            }
134        }
135    }
136#endif
137}
138
139int vm_guest_add_iospace(vm_t *vm, vspace_t *loader, seL4_CPtr iospace)
140{
141    struct sel4utils_alloc_data *data = get_alloc_data(&vm->mem.vm_vspace);
142    guest_vspace_t *guest_vspace = (guest_vspace_t *) data;
143
144    assert(!guest_vspace->done_mapping);
145
146    guest_vspace->iospaces = realloc(guest_vspace->iospaces, sizeof(guest_iospace_t) * (guest_vspace->num_iospaces + 1));
147    assert(guest_vspace->iospaces);
148    guest_vspace->iospaces[guest_vspace->num_iospaces] = calloc(1, sizeof(guest_iospace_t));
149    assert(guest_vspace->iospaces[guest_vspace->num_iospaces]);
150
151    guest_iospace_t *guest_iospace = guest_vspace->iospaces[guest_vspace->num_iospaces];
152    guest_iospace->iospace = iospace;
153    int error = sel4utils_get_empty_vspace(loader, &guest_iospace->iospace_vspace, &guest_iospace->iospace_vspace_data,
154                                           guest_vspace->vspace_data.vka, seL4_CapNull, NULL, NULL);
155    if (error) {
156        ZF_LOGE("Failed to allocate vspace for new iospace");
157        return error;
158    }
159
160    guest_vspace->num_iospaces++;
161    return 0;
162}
163
164int vm_init_guest_vspace(vspace_t *loader, vspace_t *vmm, vspace_t *new_vspace, vka_t *vka, seL4_CPtr page_directory)
165{
166    int error;
167    guest_vspace_t *vspace = calloc(1, sizeof(*vspace));
168    if (!vspace) {
169        ZF_LOGE("Malloc failed");
170        return -1;
171    }
172    vspace->done_mapping = 0;
173    vspace->num_iospaces = 0;
174    vspace->iospaces = malloc(0);
175    assert(vspace->iospaces);
176    error = sel4utils_get_empty_vspace_with_map(loader, new_vspace, &vspace->vspace_data, vka, page_directory, NULL, NULL,
177                                                guest_vspace_map);
178    if (error) {
179        ZF_LOGE("Failed to create guest vspace");
180        return error;
181    }
182
183    new_vspace->unmap_pages = guest_vspace_unmap;
184
185    return 0;
186}
187