From: Nikhil Devshatwar <[email protected]> Implement regmap as a unit, Use reg_map_data as book keeping data structure per cell.
Register a MMIO handler for each regmap region and handle the mmio access based on the regmap described in the config. Implement the regmap_modify_root to map and unmap the regmap access from the root cell while creating inmate cells. Signed-off-by: Nikhil Devshatwar <[email protected]> --- hypervisor/Makefile | 2 +- hypervisor/include/jailhouse/cell.h | 2 + hypervisor/include/jailhouse/regmap.h | 47 +++++ hypervisor/regmap.c | 258 ++++++++++++++++++++++++++ 4 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 hypervisor/include/jailhouse/regmap.h create mode 100644 hypervisor/regmap.c diff --git a/hypervisor/Makefile b/hypervisor/Makefile index 893ead42..62c86a4b 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -36,7 +36,7 @@ ifneq ($(wildcard $(INC_CONFIG_H)),) KBUILD_CFLAGS += -include $(INC_CONFIG_H) endif -CORE_OBJECTS = setup.o printk.o paging.o control.o lib.o mmio.o pci.o ivshmem.o +CORE_OBJECTS = setup.o printk.o paging.o control.o lib.o mmio.o pci.o ivshmem.o regmap.o CORE_OBJECTS += uart.o uart-8250.o ifdef CONFIG_JAILHOUSE_GCOV diff --git a/hypervisor/include/jailhouse/cell.h b/hypervisor/include/jailhouse/cell.h index c804a5df..90575bb9 100644 --- a/hypervisor/include/jailhouse/cell.h +++ b/hypervisor/include/jailhouse/cell.h @@ -69,6 +69,8 @@ struct cell { unsigned int num_mmio_regions; /** Maximum number of MMIO regions. */ unsigned int max_mmio_regions; + /** List of register maps assigned to this cell. */ + struct reg_map_data *regmap; }; extern struct cell root_cell; diff --git a/hypervisor/include/jailhouse/regmap.h b/hypervisor/include/jailhouse/regmap.h new file mode 100644 index 00000000..98faf2c8 --- /dev/null +++ b/hypervisor/include/jailhouse/regmap.h @@ -0,0 +1,47 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) 2019 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: + * Nikhil Devshatwar <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef _JAILHOUSE_REGMAP_H +#define _JAILHOUSE_REGMAP_H + +#include <jailhouse/types.h> +#include <asm/mmio.h> +#include <jailhouse/cell-config.h> + +struct cell; + +/** + * @defgroup REGMAP Regmap subsystem + * + * This subsystem provides interpretation and handling of intercepted + * register accesses performed by cells. + * + * @{ + */ + +#define JAILHOUSE_REGMAP_WORDS 8 +#define JAILHOUSE_REGMAP_BITS (JAILHOUSE_REGMAP_WORDS * 32) + +/** Register map description */ +struct reg_map_data { + /** Reference to regmap defined in config */ + const struct jailhouse_regmap *info; + /** Owning cell */ + struct cell *cell; + /** virt address where this regmap is mapped */ + void *map_base; + /** Ownership details for each register */ + u32 reg_bitmap[JAILHOUSE_REGMAP_WORDS]; +}; + +/** @} REGMAP */ +#endif /* !_JAILHOUSE_REGMAP_H */ diff --git a/hypervisor/regmap.c b/hypervisor/regmap.c new file mode 100644 index 00000000..9f3d32dc --- /dev/null +++ b/hypervisor/regmap.c @@ -0,0 +1,258 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) 2019 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: + * Nikhil Devshatwar <[email protected]> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include <jailhouse/cell.h> +#include <jailhouse/control.h> +#include <jailhouse/paging.h> +#include <jailhouse/printk.h> +#include <jailhouse/unit.h> +#include <jailhouse/percpu.h> +#include <jailhouse/regmap.h> + +static inline bool regmap_is_enabled(struct reg_map_data *regmap, int reg) +{ + u32 idx, mask; + + idx = reg / 32; + mask = 1 << (reg % 32); + + return regmap->reg_bitmap[idx] & mask ? 1 : 0; +} + +static inline void regmap_enable(struct reg_map_data *regmap, int reg) +{ + u32 idx, mask; + + idx = reg / 32; + mask = 1 << (reg % 32); + + regmap->reg_bitmap[idx] |= mask; +} + +static inline void regmap_disable(struct reg_map_data *regmap, int reg) +{ + u32 idx, mask; + + idx = reg / 32; + mask = 1 << (reg % 32); + + regmap->reg_bitmap[idx] &= ~mask; +} + +/** + * Find the regmap which degines the ownership bitmap for + * the register address provided. + * + * @param cell Cell in which to search. + * @param addr Register address to match + * @param idx Pointer to index, populated with index of register in + * the matching regmap + * + * @return Valid reg_map_data or NULL when not found. + */ +static struct reg_map_data *cell_get_regmap(struct cell *cell, + unsigned long addr, unsigned int *idx) +{ + const struct jailhouse_regmap *info; + struct reg_map_data *regmap; + unsigned long start, end; + u32 i; + + for (i = 0; i < cell->config->num_regmaps; i++) { + regmap = &cell->regmap[i]; + info = regmap->info; + start = (unsigned long)info->reg_base; + end = (unsigned long)start + info->reg_size * info->reg_count; + + if (addr < start || addr >= end) + continue; + + *idx = (addr - info->reg_base) / info->reg_size; + return regmap; + } + return NULL; +} + +/** + * Handle emulation of regmap access as per permission bitmap + * Check regmap access permissions and ownership + * Based on that, allow or forbid the MMIOs access to register + * + * @param arg Private argument, reg_map_data. + * @param mmio describes the mmio access which caused the fault + * + * @return MMIO_HANDLED if the access is as per regmap description, + * MMIO_ERROR if it violates some of the permissions, + */ +static enum mmio_result regmap_handler(void *arg, struct mmio_access *mmio) +{ + struct reg_map_data *regmap = (struct reg_map_data *)arg; + const struct jailhouse_regmap *info; + unsigned int idx; + + info = regmap->info; + idx = mmio->address / info->reg_size; + + if (mmio->is_write) { + if ((info->flags & JAILHOUSE_MEM_WRITE) == 0) + return MMIO_ERROR; + } else { + if ((info->flags & JAILHOUSE_MEM_READ) == 0) + return MMIO_ERROR; + } + + if (regmap_is_enabled(regmap, idx)) { + mmio_perform_access(regmap->map_base, mmio); + return MMIO_HANDLED; + } else { + printk("MMIO access disabled\n"); + return MMIO_ERROR; + } +} + +/** + * Modify root_cell's bitmap to (un)mask the registers defined in inmate cell. + * Ignore if the root cell does not describe the regmap used by inmate + * Handles the case where root cell describes the registers using + * different address range + * + * @param cell inmate cell handle. + * @param regmap register (un)map to be removed from root_cell. + * @param map true to map the regmap, false to unmap. + * + * @return 0 on successfully (un)mapping the regmap. + */ +static int regmap_modify_root(struct cell *cell, struct reg_map_data *regmap, + bool map) +{ + const struct jailhouse_regmap *info = regmap->info; + struct reg_map_data *root_regmap = NULL; + unsigned long long addr; + u32 reg, idx; + + if (cell == &root_cell) + return 0; + if (info->flags & JAILHOUSE_MEM_ROOTSHARED) + return 0; + + for (reg = 0; reg < info->reg_count; reg++) { + + addr = info->reg_base + reg * info->reg_size; + if (!root_regmap) { + root_regmap = cell_get_regmap(&root_cell, addr, &idx); + if (!root_regmap) + continue; + } + + if (regmap_is_enabled(regmap, reg)) { + if (map) { + regmap_enable(root_regmap, idx); + + /* For unmapping, ensure that its mapped in root cell regmap */ + } else if (regmap_is_enabled(root_regmap, idx)) { + + regmap_disable(root_regmap, idx); + } else { + printk("ERROR: Root cell does not own bitmap for reg %llx\n", + addr); + return -EINVAL; + } + } + + /* reuse the same root_regmap for next register if idx is within limit */ + idx++; + if (idx >= root_regmap->info->reg_count) + root_regmap = NULL; + } + return 0; +} + +static int regmap_cell_init(struct cell *cell) +{ + const struct jailhouse_regmap *info; + struct reg_map_data *regmap; + u32 i, num_pages, size; + int ret; + + if (cell->config->num_regmaps == 0) + return 0; + + num_pages = PAGES(cell->config->num_regmaps * sizeof(struct reg_map_data)); + cell->regmap = page_alloc(&mem_pool, num_pages); + if (!cell->regmap) + return -ENOMEM; + + info = jailhouse_cell_regmaps(cell->config); + for (i = 0; i < cell->config->num_regmaps; i++, info++) { + + regmap = &cell->regmap[i]; + regmap->info = info; + regmap->cell = cell; + size = info->reg_size * info->reg_count; + + if (info->reg_count > JAILHOUSE_REGMAP_BITS || + (info->flags & (JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE)) == 0) + goto invalid; + + regmap->map_base = paging_map_device(info->reg_base, size); + if (!regmap->map_base) + return -ENOMEM; + + memcpy(regmap->reg_bitmap, info->reg_bitmap, + sizeof(regmap->reg_bitmap)); + + mmio_region_register(cell, info->reg_base, size, + regmap_handler, regmap); + + /* Unmap the memory so that handler can be triggered */ + ret = paging_destroy(&cell->arch.mm, info->reg_base, size, + PAGING_COHERENT); + if (ret) + goto invalid; + + ret = regmap_modify_root(cell, regmap, false); + if (ret) + goto invalid; + } + + return 0; +invalid: + page_free(&mem_pool, cell->regmap, 1); + return -EINVAL; +} + +static void regmap_cell_exit(struct cell *cell) +{ + struct reg_map_data *regmap; + u32 i, num_pages; + + for (i = 0; i < cell->config->num_regmaps; i++) { + regmap = &cell->regmap[i]; + regmap_modify_root(cell, regmap, true); + } + + num_pages = PAGES(cell->config->num_regmaps); + page_free(&mem_pool, cell->regmap, num_pages); +} + +static int regmap_init(void) +{ + return regmap_cell_init(&root_cell); +} + +static unsigned int regmap_mmio_count_regions(struct cell *cell) +{ + return cell->config->num_regmaps; +} + +DEFINE_UNIT_SHUTDOWN_STUB(regmap); +DEFINE_UNIT(regmap, "regmap"); -- 2.17.1 -- You received this message because you are subscribed to the Google Groups "Jailhouse" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/jailhouse-dev/20200127135611.21302-4-nikhil.nd%40ti.com.
