blob: 887fe758c31e311186a747559082c1a1c5666bb0 [file] [log] [blame]
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <sel4vm/guest_vm.h>
#include <sel4vm/guest_vcpu_fault.h>
#include <sel4vm/guest_memory_helpers.h>
#include <sel4vmmplatsupport/guest_memory_util.h>
#include <sel4vmmplatsupport/device.h>
#include <sel4vmmplatsupport/device_utils.h>
int vm_install_ram_only_device(vm_t *vm, const struct device *device)
{
struct device d;
uintptr_t paddr;
int err;
d = *device;
vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d.pstart, d.size,
default_error_fault_callback, NULL);
if (!reservation) {
return -1;
}
err = map_frame_alloc_reservation(vm, reservation);
assert(!err);
return err;
}
static memory_fault_result_t passthrough_device_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr,
size_t fault_length, void *cookie)
{
ZF_LOGE("Fault occured on passthrough device");
return FAULT_ERROR;
}
int vm_install_passthrough_device(vm_t *vm, const struct device *device)
{
struct device d;
uintptr_t paddr;
int err;
d = *device;
for (paddr = d.pstart; paddr - d.pstart < d.size; paddr += 0x1000) {
void *addr;
vm_memory_reservation_t *reservation;
reservation = vm_reserve_memory_at(vm, paddr, 0x1000, passthrough_device_fault, NULL);
if (!reservation) {
return -1;
}
err = map_ut_alloc_reservation(vm, reservation);
#ifdef PLAT_EXYNOS5
if (err && paddr == MCT_ADDR) {
printf("*****************************************\n");
printf("*** Linux will try to use the MCT but ***\n");
printf("*** the kernel is not exporting it! ***\n");
printf("*****************************************\n");
/* VMCT is not fully functional yet */
// err = vm_install_vmct(vm);
return -1;
}
#endif
if (err) {
return -1;
}
}
return err;
}
static memory_fault_result_t handle_listening_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr,
size_t fault_length, void *cookie)
{
volatile uint32_t *reg;
int offset;
void **map;
struct device *d = (struct device *)cookie;
assert(d->priv);
map = (void **)d->priv;
offset = fault_addr - d->pstart;
reg = (volatile uint32_t *)(map[offset >> 12] + (offset & MASK(12)));
printf("[Listener/%s] ", d->name);
if (is_vcpu_read_fault(vcpu)) {
printf("read ");
set_vcpu_fault_data(vcpu, *reg);
} else {
printf("write");
*reg = emulate_vcpu_fault(vcpu, *reg);
}
printf(" ");
seL4_Word data = get_vcpu_fault_data(vcpu);
seL4_Word data_mask = get_vcpu_fault_data_mask(vcpu);
printf("0x%x", data & data_mask);
printf(" address %p @ pc %p\n", (void *) fault_addr,
(void *) get_vcpu_fault_ip(vcpu));
advance_vcpu_fault(vcpu);
return FAULT_HANDLED;
}
int vm_install_listening_device(vm_t *vm, const struct device *dev_listening)
{
struct device *d;
int pages;
int i;
void **map;
int err;
pages = dev_listening->size >> 12;
d = (struct device *)calloc(1, sizeof(struct device));
if (!d) {
return -1;
}
memcpy(d, dev_listening, sizeof(struct device));
/* Build device memory map */
map = (void **)calloc(1, sizeof(void *) * pages);
if (map == NULL) {
return -1;
}
d->priv = map;
for (i = 0; i < pages; i++) {
map[i] = ps_io_map(&vm->io_ops->io_mapper, d->pstart + (i << 12), PAGE_SIZE_4K, 0, PS_MEM_NORMAL);
}
vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d->pstart, d->size,
handle_listening_fault, (void *)d);
if (!reservation) {
free(d);
free(map);
return -1;
}
return 0;
}
static memory_fault_result_t handle_listening_ram_fault(vm_t *vm, vm_vcpu_t *vcpu, uintptr_t fault_addr,
size_t fault_length, void *cookie)
{
volatile uint32_t *reg;
int offset;
struct device *d = (struct device *)cookie;
assert(d->priv);
offset = fault_addr - d->pstart;
reg = (volatile uint32_t *)(d->priv + offset);
if (is_vcpu_read_fault(vcpu)) {
set_vcpu_fault_data(vcpu, *reg);
} else {
*reg = emulate_vcpu_fault(vcpu, *reg);
}
printf("Listener pc%p| %s%p:%p\n", (void *) get_vcpu_fault_ip(vcpu),
is_vcpu_read_fault(vcpu) ? "r" : "w",
(void *) fault_addr,
(void *) get_vcpu_fault_data(vcpu));
advance_vcpu_fault(vcpu);
return FAULT_HANDLED;
}
const struct device dev_listening_ram = {
.name = "<listing_ram>",
.pstart = 0x0,
.size = 0x1000,
.priv = NULL
};
int vm_install_listening_ram(vm_t *vm, uintptr_t addr, size_t size)
{
struct device *d;
int err;
d = (struct device *)calloc(1, sizeof(struct device));
if (!d) {
return -1;
}
memcpy(d, &dev_listening_ram, sizeof(struct device));
d->pstart = addr;
d->size = size;
d->priv = calloc(1, 0x1000);
assert(d->priv);
if (!d->priv) {
ZF_LOGE("calloc failed\n");
return -1;
}
vm_memory_reservation_t *reservation = vm_reserve_memory_at(vm, d->pstart, d->size,
handle_listening_ram_fault, (void *)d);
if (!reservation) {
return -1;
}
return 0;
}