Skip to content

Commit

Permalink
libsel4vm, vgic: read gic registers from fdt
Browse files Browse the repository at this point in the history
This commit adds an "fdt_ori" and "gic_node" parameter to the vm struct,
which allows the library to find the gic register addresses required for
emulating the vgic.

Signed-off-by: Chris Guikema <[email protected]>
  • Loading branch information
chrisguikema committed Oct 27, 2022
1 parent e4a4cc4 commit 46b5800
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 41 deletions.
3 changes: 3 additions & 0 deletions libsel4vm/include/sel4vm/guest_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ struct vm {
char *vm_name;
unsigned int vm_id;
bool vm_initialised;

char *gic_path;
void *fdt_ori;
};

/***
Expand Down
11 changes: 11 additions & 0 deletions libsel4vm/src/arch/arm/guest_irq_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,16 @@ int vm_create_default_irq_controller(vm_t *vm)
ZF_LOGE("Failed to initialise default irq controller: Invalid vm");
return -1;
}

if (!vm->gic_path) {
ZF_LOGE("Failed to find GIC path in VM structure");
return -1;
}

if (!vm->fdt_ori) {
ZF_LOGE("Failed to find reference FDT");
return -1;
}

return vm_install_vgic(vm);
}
35 changes: 0 additions & 35 deletions libsel4vm/src/arch/arm/vgic/gicv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,6 @@
#include <assert.h>
#include <stdint.h>


/* FIXME these should be defined in a way that is friendlier to extension. */
#if defined(CONFIG_PLAT_EXYNOS5)
#define GIC_PADDR 0x10480000
#elif defined(CONFIG_PLAT_TK1) || defined(CONFIG_PLAT_TX1)
#define GIC_PADDR 0x50040000
#elif defined(CONFIG_PLAT_TX2)
#define GIC_PADDR 0x03880000
#elif defined(CONFIG_PLAT_QEMU_ARM_VIRT)
#define GIC_PADDR 0x8000000
#elif defined(CONFIG_PLAT_ODROIDC2)
#define GIC_PADDR 0xc4300000
#elif defined(CONFIG_PLAT_ZYNQMP)
#define GIC_PADDR 0xf9000000
#else
#error "Unsupported platform for GIC"
#endif

#ifdef CONFIG_PLAT_QEMU_ARM_VIRT
#define GIC_DIST_PADDR (GIC_PADDR)
#define GIC_CPU_PADDR (GIC_PADDR + 0x00010000)
#define GIC_VCPU_CNTR_PADDR (GIC_PADDR + 0x00030000)
#define GIC_VCPU_PADDR (GIC_PADDR + 0x00040000)
#elif defined(CONFIG_PLAT_ZYNQMP)
#define GIC_DIST_PADDR (GIC_PADDR + 0x10000)
#define GIC_CPU_PADDR (GIC_PADDR + 0x20000)
#define GIC_VCPU_CNTR_PADDR (GIC_PADDR + 0x40000)
#define GIC_VCPU_PADDR (GIC_PADDR + 0x60000)
#else
#define GIC_DIST_PADDR (GIC_PADDR + 0x1000)
#define GIC_CPU_PADDR (GIC_PADDR + 0x2000)
#define GIC_VCPU_CNTR_PADDR (GIC_PADDR + 0x4000)
#define GIC_VCPU_PADDR (GIC_PADDR + 0x6000)
#endif

/* Memory map for GIC distributor */
struct gic_dist_map {
uint32_t enable; /* 0x000 */
Expand Down
78 changes: 72 additions & 6 deletions libsel4vm/src/arch/arm/vgic/vgic_v2.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,70 @@
#include "gicv2.h"
#include "vdist.h"

#include <libfdt.h>

static uintptr_t vcpu_addr;
static uintptr_t cpu_addr;
static uintptr_t dist_addr;
static struct vgic_dist_device *vgic_dist;

static int gic_get_registers_from_fdt(vm_t *vm, uintptr_t *dist, uintptr_t *cpu, uintptr_t *vcpu)
{
const struct fdt_property *c;
int len;

int gic_offset = fdt_path_offset(vm->fdt_ori, vm->gic_path);
if (0 > gic_offset) {
ZF_LOGE("Failed to find gic offset from path: %s", vm->gic_path);
return -1;
}

int parent_offset = fdt_parent_offset(vm->fdt_ori, gic_offset);
if (0 > parent_offset) {
ZF_LOGE("Failed to find gic parent offset");
return -1;
}

int address_cells = fdt_address_cells(vm->fdt_ori, parent_offset);
int size_cells = fdt_address_cells(vm->fdt_ori, parent_offset);

if ((address_cells == 0) || (size_cells == 0)) {
ZF_LOGE("#address-cells or #size-cells are 0");
return -1;
}

c = fdt_get_property(vm->fdt_ori, gic_offset, "reg", &len);
if (!c) {
ZF_LOGE("Failed to find reg in gic node");
return -1;
}

/*
* 1st reg entry: Distributor
* 2nd reg entry: CPU Interface
* 3rd reg entry: GIC Virtual Interface Control Register
* 4th reg entry: GIC Virtual CPU Interface Register
*/
uint64_t gic_regs[NUM_GIC_REGS];
int calculated_size = size_cells * address_cells * NUM_GIC_REGS * sizeof(uint32_t);
int reg_offset = calculated_size / NUM_GIC_REGS;

if (len != calculated_size) {
ZF_LOGE("Register size is unexpected. %d != %d", len, calculated_size);
return -1;
}

for (int i = 0; i < NUM_GIC_REGS; i++) {
memcpy(&gic_regs[i], (void *)(c->data + (i * reg_offset)), sizeof(uint32_t) * address_cells);
}

*dist = fdt64_to_cpu(gic_regs[GIC_DIST_ENTRY]);
*cpu = fdt64_to_cpu(gic_regs[GIC_CPU_ENTRY]);
*vcpu = fdt64_to_cpu(gic_regs[GIC_VCPU_ENTRY]);

return 0;
}

static inline struct gic_dist_map *vgic_priv_get_dist(struct vgic_dist_device *d)
{
assert(d);
Expand Down Expand Up @@ -215,17 +276,17 @@ static vm_frame_t vgic_vcpu_iterator(uintptr_t addr, void *cookie)
return frame_result;
}
seL4_Word vka_cookie;
err = vka_utspace_alloc_at(vm->vka, &frame, kobject_get_type(KOBJECT_FRAME, 12), 12, GIC_VCPU_PADDR, &vka_cookie);
err = vka_utspace_alloc_at(vm->vka, &frame, kobject_get_type(KOBJECT_FRAME, 12), 12, vcpu_addr, &vka_cookie);
if (err) {
err = simple_get_frame_cap(vm->simple, (void *)GIC_VCPU_PADDR, 12, &frame);
err = simple_get_frame_cap(vm->simple, (void *)vcpu_addr, 12, &frame);
if (err) {
ZF_LOGE("Failed to find device cap for vgic vcpu");
return frame_result;
}
}
frame_result.cptr = frame.capPtr;
frame_result.rights = seL4_AllRights;
frame_result.vaddr = GIC_CPU_PADDR;
frame_result.vaddr = cpu_addr;
frame_result.size_bits = seL4_PageBits;
return frame_result;
}
Expand All @@ -236,6 +297,9 @@ static vm_frame_t vgic_vcpu_iterator(uintptr_t addr, void *cookie)
*/
int vm_install_vgic(vm_t *vm)
{
/* Read device tree to find memory regions */
gic_get_registers_from_fdt(vm, &dist_addr, &cpu_addr, &vcpu_addr);

struct vgic *vgic = calloc(1, sizeof(*vgic));
if (!vgic) {
assert(!"Unable to calloc memory for VGIC");
Expand All @@ -251,19 +315,21 @@ int vm_install_vgic(vm_t *vm)
return -1;
}
memcpy(vgic_dist, &dev_vgic_dist, sizeof(struct vgic_dist_device));
vgic_dist->pstart = dist_addr;

vgic->dist = calloc(1, sizeof(struct gic_dist_map));
assert(vgic->dist);
if (vgic->dist == NULL) {
return -1;
}
vm_memory_reservation_t *vgic_dist_res = vm_reserve_memory_at(vm, GIC_DIST_PADDR, PAGE_SIZE_4K,

vm_memory_reservation_t *vgic_dist_res = vm_reserve_memory_at(vm, dist_addr, PAGE_SIZE_4K,
handle_vgic_dist_fault, (void *)vgic_dist);
vgic_dist->vgic = vgic;
vgic_dist_reset(vgic_dist);

/* Remap VCPU to CPU */
vm_memory_reservation_t *vgic_vcpu_reservation = vm_reserve_memory_at(vm, GIC_CPU_PADDR, PAGE_SIZE_4K,
vm_memory_reservation_t *vgic_vcpu_reservation = vm_reserve_memory_at(vm, cpu_addr, PAGE_SIZE_4K,
handle_vgic_vcpu_fault, NULL);
int err = vm_map_reservation(vm, vgic_vcpu_reservation, vgic_vcpu_iterator, (void *)vm);
if (err) {
Expand Down Expand Up @@ -292,7 +358,7 @@ int vm_vgic_maintenance_handler(vm_vcpu_t *vcpu)
}

const struct vgic_dist_device dev_vgic_dist = {
.pstart = GIC_DIST_PADDR,
.pstart = 0,
.size = 0x1000,
.vgic = NULL,
};
10 changes: 10 additions & 0 deletions libsel4vm/src/arch/arm/vgic/vgicv2_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,13 @@
#define GIC_DIST_SGI_CPU_TARGET_LIST_MASK 0xFF << GIC_DIST_SGI_CPU_TARGET_LIST_SHIFT

#define GIC_DIST_SGI_INTID_MASK 0xF

/*
* https://elixir.bootlin.com/linux/v3.8/source/Documentation/devicetree/bindings/arm/gic.txt
*
* Contants for getting registers from FDT */
#define NUM_GIC_REGS 4
#define GIC_DIST_ENTRY 0
#define GIC_CPU_ENTRY 1
#define GIC_VCPU_CNTR_ENTRY 2
#define GIC_VCPU_ENTRY 3

0 comments on commit 46b5800

Please sign in to comment.