1/*
2 * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <sel4vm/guest_vm.h>
8#include <sel4vm/guest_vcpu_fault.h>
9#include <sel4vm/guest_memory_helpers.h>
10
11#include <sel4vmmplatsupport/guest_memory_util.h>
12#include <sel4vmmplatsupport/device.h>
13#include <sel4vmmplatsupport/device_utils.h>
14
15int vm_install_ram_only_device(vm_t *vm, const struct device *device)
16{
17    struct device d;
18    uintptr_t paddr;
19    int err;
20    d = *device;
21
22    vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d.pstart, d.size,
23                                                                default_error_fault_callback, NULL);
24    if (!reservation) {
25        return -1;
26    }
27
28    err = map_frame_alloc_reservation(vm, reservation);
29    assert(!err);
30    return err;
31}
32
33static memory_fault_result_t passthrough_device_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr,
34                                                      size_t fault_length, void *cookie)
35{
36    ZF_LOGE("Fault occured on passthrough device");
37    return FAULT_ERROR;
38}
39
40int vm_install_passthrough_device(vm_t *vm, const struct device *device)
41{
42    struct device d;
43    uintptr_t paddr;
44    int err;
45    d = *device;
46    for (paddr = d.pstart; paddr - d.pstart < d.size; paddr += 0x1000) {
47        void *addr;
48        vm_memory_reservation_t *reservation;
49        reservation = vm_reserve_memory_at(vm, paddr, 0x1000, passthrough_device_fault, NULL);
50        if (!reservation) {
51            return -1;
52        }
53        err = map_ut_alloc_reservation(vm, reservation);
54#ifdef PLAT_EXYNOS5
55        if (err && paddr == MCT_ADDR) {
56            printf("*****************************************\n");
57            printf("*** Linux will try to use the MCT but ***\n");
58            printf("*** the kernel is not exporting it!   ***\n");
59            printf("*****************************************\n");
60            /* VMCT is not fully functional yet */
61//            err = vm_install_vmct(vm);
62            return -1;
63        }
64#endif
65        if (err) {
66            return -1;
67        }
68    }
69    return err;
70}
71
72static memory_fault_result_t handle_listening_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr,
73                                                    size_t fault_length, void *cookie)
74{
75    volatile uint32_t *reg;
76    int offset;
77    void **map;
78    struct device *d = (struct device *)cookie;
79
80    assert(d->priv);
81    map = (void **)d->priv;
82    offset = fault_addr - d->pstart;
83
84    reg = (volatile uint32_t *)(map[offset >> 12] + (offset & MASK(12)));
85
86    printf("[Listener/%s] ", d->name);
87    if (is_vcpu_read_fault(vcpu)) {
88        printf("read ");
89        set_vcpu_fault_data(vcpu, *reg);
90    } else {
91        printf("write");
92        *reg = emulate_vcpu_fault(vcpu, *reg);
93    }
94    printf(" ");
95    seL4_Word data = get_vcpu_fault_data(vcpu);
96    seL4_Word data_mask = get_vcpu_fault_data_mask(vcpu);
97    printf("0x%x", data & data_mask);
98    printf(" address %p @ pc %p\n", (void *) fault_addr,
99           (void *) get_vcpu_fault_ip(vcpu));
100    advance_vcpu_fault(vcpu);
101    return FAULT_HANDLED;
102}
103
104
105int vm_install_listening_device(vm_t *vm, const struct device *dev_listening)
106{
107    struct device *d;
108    int pages;
109    int i;
110    void **map;
111    int err;
112    pages = dev_listening->size >> 12;
113    d = (struct device *)calloc(1, sizeof(struct device));
114    if (!d) {
115        return -1;
116    }
117    memcpy(d, dev_listening, sizeof(struct device));
118    /* Build device memory map */
119    map = (void **)calloc(1, sizeof(void *) * pages);
120    if (map == NULL) {
121        return -1;
122    }
123    d->priv = map;
124    for (i = 0; i < pages; i++) {
125        map[i] = ps_io_map(&vm->io_ops->io_mapper, d->pstart + (i << 12), PAGE_SIZE_4K, 0, PS_MEM_NORMAL);
126    }
127    vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d->pstart, d->size,
128                                                                handle_listening_fault, (void *)d);
129    if (!reservation) {
130        free(d);
131        free(map);
132        return -1;
133    }
134    return 0;
135}
136
137
138static memory_fault_result_t handle_listening_ram_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr,
139                                                        size_t fault_length, void *cookie)
140{
141    volatile uint32_t *reg;
142    int offset;
143
144    struct device *d = (struct device *)cookie;
145    assert(d->priv);
146    offset = fault_addr - d->pstart;
147
148    reg = (volatile uint32_t *)(d->priv + offset);
149
150    if (is_vcpu_read_fault(vcpu)) {
151        set_vcpu_fault_data(vcpu, *reg);
152    } else {
153        *reg = emulate_vcpu_fault(vcpu, *reg);
154    }
155    printf("Listener pc%p| %s%p:%p\n", (void *) get_vcpu_fault_ip(vcpu),
156           is_vcpu_read_fault(vcpu) ? "r" : "w",
157           (void *) fault_addr,
158           (void *) get_vcpu_fault_data(vcpu));
159    advance_vcpu_fault(vcpu);
160    return FAULT_HANDLED;
161}
162
163
164const struct device dev_listening_ram = {
165    .name = "<listing_ram>",
166    .pstart = 0x0,
167    .size = 0x1000,
168    .priv = NULL
169};
170
171
172int vm_install_listening_ram(vm_t *vm, uintptr_t addr, size_t size)
173{
174    struct device *d;
175    int err;
176
177    d = (struct device *)calloc(1, sizeof(struct device));
178    if (!d) {
179        return -1;
180    }
181    memcpy(d, &dev_listening_ram, sizeof(struct device));
182
183    d->pstart = addr;
184    d->size = size;
185    d->priv = calloc(1, 0x1000);
186    assert(d->priv);
187    if (!d->priv) {
188        ZF_LOGE("calloc failed\n");
189        return -1;
190    }
191
192    vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d->pstart, d->size,
193                                                                handle_listening_ram_fault, (void *)d);
194    if (!reservation) {
195        return -1;
196    }
197    return 0;
198}
199