From d01652f8c4f78e0fa0b128d1eb06b660ffef9101 Mon Sep 17 00:00:00 2001 From: Jakub Klimek Date: Tue, 2 Dec 2025 15:04:25 +0100 Subject: [PATCH] hal/arm: per-process MPU configuration This commit introduces loader support for full MPU regions reconfiguration on context switch, allowing for more flexibile configuration of memory maps on MPU targets. Changes include * MPU configuration during app command, based on hal_syspage_prog_t structure with program configuration of MPU regions in form of ready-to-copy register values * reimplementation of mpu command, showing generated mpu configuration on per-app basis * passing all maps at once to hal/mpu module, allowing future map merging to reduce count of used MPU regions JIRA: RTOS-1149 --- cmds/app.c | 4 + cmds/mpu.c | 64 ++++---- hal/aarch64/zynqmp/hal.c | 2 +- hal/armv7a/imx6ull/hal.c | 2 +- hal/armv7a/zynq7000/hal.c | 2 +- hal/armv7m/imxrt/hal.c | 5 +- hal/armv7m/mpu.c | 227 +++++++++++++++++++------- hal/armv7m/mpu.h | 27 +-- hal/armv7m/stm32/hal.c | 5 +- hal/armv7r/mpu.c | 224 ++++++++++++++++++------- hal/armv7r/mpu.h | 27 +-- hal/armv7r/tda4vm/hal.c | 5 +- hal/armv7r/zynqmp/hal.c | 5 +- hal/armv8m/mcx/hal.c | 5 +- hal/armv8m/mpu.c | 187 ++++++++++++++++----- hal/armv8m/mpu.h | 34 +--- hal/armv8m/nrf/hal.c | 5 +- hal/armv8m/stm32/hal.c | 5 +- hal/armv8r/mps3an536/hal.c | 2 +- hal/hal.h | 4 +- hal/ia32/memory.c | 2 +- hal/riscv64/gaisler/hal.c | 2 +- hal/riscv64/generic/hal.c | 2 +- hal/sparcv8leon/gaisler/generic/hal.c | 2 +- hal/sparcv8leon/gaisler/gr712rc/hal.c | 2 +- hal/sparcv8leon/gaisler/gr716/hal.c | 2 +- hal/sparcv8leon/gaisler/gr740/hal.c | 2 +- syspage.c | 30 +++- syspage.h | 6 + 29 files changed, 607 insertions(+), 284 deletions(-) diff --git a/cmds/app.c b/cmds/app.c index a7a95ed5..b9473542 100644 --- a/cmds/app.c +++ b/cmds/app.c @@ -161,6 +161,10 @@ static int cmd_appLoad(handler_t handler, size_t size, const char *name, char *i prog->start = entry->start; prog->end = entry->end; + if ((res = hal_getProgData(prog, imaps, imapSz, dmaps, dmapSz)) < 0) { + return res; + } + return EOK; } diff --git a/cmds/mpu.c b/cmds/mpu.c index 349b5570..67f45fba 100644 --- a/cmds/mpu.c +++ b/cmds/mpu.c @@ -82,54 +82,58 @@ static void mpu_regionPrint(const char *name, u32 rbar, u32 rasr) static void cmd_mpuInfo(void) { - lib_printf("prints the use of MPU regions, usage: mpu [all]"); + lib_printf("prints the use of MPU regions, usage: mpu [prog name]"); } static int cmd_mpu(int argc, char *argv[]) { const char *name; - unsigned int i, regCnt; - const mpu_region_t *region; - const mpu_common_t *const mpu_common = mpu_getCommon(); + unsigned int i; + const syspage_prog_t *prog = syspage_progsGet(); + const syspage_prog_t *firstProg = prog; + const unsigned int regMax = mpu_getMaxRegionsCount(); - if (argc == 1) { - regCnt = mpu_common->regCnt; - } - else if (argc == 2) { - if (hal_strcmp(argv[1], "all") != 0) { - log_error("\n%s: Wrong arguments", argv[0]); - return CMD_EXIT_FAILURE; - } - - regCnt = mpu_common->regMax; - } - else { - log_error("\n%s: Wrong argument count", argv[0]); + if (prog == NULL) { + log_error("\nNo programs in syspage!"); return CMD_EXIT_FAILURE; } - if (mpu_common->regMax != sizeof(((hal_syspage_t *)0)->mpu.table) / sizeof(((hal_syspage_t *)0)->mpu.table[0])) { - log_error("\n%s: MPU hal is not initialized or unsupported type was detected", argv[0]); + if (argc > 2) { + log_error("\n%s: Wrong argument count", argv[0]); return CMD_EXIT_FAILURE; } - lib_printf(CONSOLE_BOLD "\n%-9s %-7s %-4s %-11s %-11s %-3s %-3s %-9s %-4s %-2s %-2s %-2s\n" CONSOLE_NORMAL, - "MAP NAME", "REGION", "SUB", "START", "END", "EN", "XN", "PERM P/U", "TEX", "S", "C", "B"); + do { + name = (prog->argv[0] == 'X') ? prog->argv + 1 : prog->argv; + if ((argc == 2) && (hal_strcmp(name, argv[1]) != 0)) { + prog = prog->next; + continue; + } + lib_printf("\n%-16s 0x%08x%4s 0x%08x", name, prog->start, "", prog->end); + lib_printf(CONSOLE_BOLD "\n%-9s %-7s %-4s %-11s %-11s %-3s %-3s %-9s %-4s %-2s %-2s %-2s\n" CONSOLE_NORMAL, + "MAP NAME", "REGION", "SUB", "START", "END", "EN", "XN", "PERM P/U", "TEX", "S", "C", "B"); + for (i = 0; i < prog->hal.mpu.allocCnt; i++) { + name = syspage_mapName(prog->hal.mpu.map[i]); + if (name == NULL) { + name = ""; + } + mpu_regionPrint(name, prog->hal.mpu.table[i].rbar, prog->hal.mpu.table[i].rasr); + } - for (i = 0; i < regCnt; i++) { - region = &mpu_common->region[i]; - name = syspage_mapName(mpu_common->mapId[i]); + lib_printf("Configured %d of %d MPU regions.\n", prog->hal.mpu.allocCnt, regMax); - if (name == NULL) { - name = ""; + if (argc == 2) { + return CMD_EXIT_SUCCESS; } - mpu_regionPrint(name, region->rbar, region->rasr); - } + prog = prog->next; + } while (prog != firstProg); - lib_printf("\nConfigured %d of %d MPU regions based on %d map definitions.\n", - mpu_common->regCnt, mpu_common->regMax, mpu_common->mapCnt); + if (argc == 2) { + log_error("\nProgram %s not found in syspage!", argv[1]); + return CMD_EXIT_FAILURE; + } return CMD_EXIT_SUCCESS; } diff --git a/hal/aarch64/zynqmp/hal.c b/hal/aarch64/zynqmp/hal.c index d7f44ede..7cc54273 100644 --- a/hal/aarch64/zynqmp/hal.c +++ b/hal/aarch64/zynqmp/hal.c @@ -154,7 +154,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/armv7a/imx6ull/hal.c b/hal/armv7a/imx6ull/hal.c index 5f488553..46c54c8e 100644 --- a/hal/armv7a/imx6ull/hal.c +++ b/hal/armv7a/imx6ull/hal.c @@ -171,7 +171,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/armv7a/zynq7000/hal.c b/hal/armv7a/zynq7000/hal.c index e0907e61..2a5509d0 100644 --- a/hal/armv7a/zynq7000/hal.c +++ b/hal/armv7a/zynq7000/hal.c @@ -151,7 +151,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/armv7m/imxrt/hal.c b/hal/armv7m/imxrt/hal.c index ac7d7059..c32f8109 100644 --- a/hal/armv7m/imxrt/hal.c +++ b/hal/armv7m/imxrt/hal.c @@ -124,12 +124,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv7m/mpu.c b/hal/armv7m/mpu.c index 2f6b1b39..42f53150 100644 --- a/hal/armv7m/mpu.c +++ b/hal/armv7m/mpu.c @@ -15,9 +15,15 @@ #include #include "mpu.h" +#include "lib/log.h" +#include "syspage.h" -static mpu_common_t mpu_common; +struct { + u32 type; + u32 regMax; + addr_t kernelEntryPoint; +} mpu_common; /* clang-format off */ @@ -31,18 +37,18 @@ enum { mpu_type, mpu_ctrl, mpu_rnr, mpu_rbar, mpu_rasr, mpu_rbar_a1, mpu_rasr_a1 /* Setup single MPU region entry in a local MPU context */ -static int mpu_regionSet(unsigned int *idx, u32 baseAddr, u8 srdMask, u8 sizeBit, u32 rasrAttr) +static int mpu_regionSet(hal_syspage_prog_t *progHal, unsigned int *idx, u32 baseAddr, u8 srdMask, u8 sizeBit, u32 rasrAttr) { if ((sizeBit < 5) || (*idx >= mpu_common.regMax)) { return -EPERM; } - mpu_common.region[*idx].rbar = + progHal->mpu.table[*idx].rbar = baseAddr | (1u << 4) | /* mark region as valid */ (*idx & 0xfu); - mpu_common.region[*idx].rasr = + progHal->mpu.table[*idx].rasr = rasrAttr | (((u32)srdMask) << 8) | ((((u32)sizeBit - 1) & 0x1fu) << 1); @@ -76,20 +82,20 @@ static u32 mpu_regionAttrs(u32 attr, unsigned int enable) } -static int mpu_checkOverlap(unsigned int idx, u32 start, u32 end) +static int mpu_checkOverlap(hal_syspage_prog_t *progHal, unsigned int idx, u32 start, u32 end) { unsigned int i, j; u32 srStart, srEnd; u8 sizeBit, subregions; end -= 1; for (i = 0; i < idx; i++) { - if (((mpu_common.region[i].rbar & 0x10u) == 0) || ((mpu_common.region[i].rasr & 0x1u) == 0)) { + if (((progHal->mpu.table[i].rbar & 0x10u) == 0) || ((progHal->mpu.table[i].rasr & 0x1u) == 0)) { continue; } - sizeBit = ((mpu_common.region[i].rasr >> 1) & 0x1fu) + 1; - srStart = mpu_common.region[i].rbar & ~((1u << sizeBit) - 1); - subregions = (mpu_common.region[i].rasr >> 8) & 0xffu; + sizeBit = ((progHal->mpu.table[i].rasr >> 1) & 0x1fu) + 1; + srStart = progHal->mpu.table[i].rbar & ~((1u << sizeBit) - 1); + subregions = (progHal->mpu.table[i].rasr >> 8) & 0xffu; for (j = 0; j < 8; j++) { srEnd = srStart + (1u << (sizeBit - 3)) - 1; if (((subregions & 1u) == 0) && (start <= srEnd) && (srStart <= end)) { @@ -105,7 +111,7 @@ static int mpu_checkOverlap(unsigned int idx, u32 start, u32 end) } -static int mpu_regionCalculateAndSet(unsigned int *idx, addr_t start, addr_t end, u8 sizeBit, u32 rasrAttr) +static int mpu_regionCalculateAndSet(hal_syspage_prog_t *progHal, unsigned int *idx, addr_t start, addr_t end, u8 sizeBit, u32 rasrAttr) { u8 srdMask; /* RBAR contains all MSBs that are the same */ @@ -116,12 +122,12 @@ static int mpu_regionCalculateAndSet(unsigned int *idx, addr_t start, addr_t end srEnd = (srEnd == 0) ? 8 : srEnd; /* Bit set means disable region - negate result */ srdMask = ~(((1u << srEnd) - 1) & (0xffu << srStart)); - return mpu_regionSet(idx, baseAddr, srdMask, sizeBit, rasrAttr); + return mpu_regionSet(progHal, idx, baseAddr, srdMask, sizeBit, rasrAttr); } /* Create up to 2 regions that will represent a given map */ -static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 rasrAttr) +static int mpu_regionGenerate(hal_syspage_prog_t *progHal, unsigned int *idx, addr_t start, addr_t end, u32 rasrAttr) { int res; int commonTrailingZeroes, sigBits; @@ -138,7 +144,7 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r /* Check if size is power of 2 and start is aligned - necessary for handling small regions (below 256 bytes) */ if (size == 0) { - return mpu_regionSet(idx, 0, 0, 32, rasrAttr); + return mpu_regionSet(progHal, idx, 0, 0, 32, rasrAttr); } if ((size == (1u << __builtin_ctz(size))) && ((start & (size - 1)) == 0)) { @@ -147,7 +153,7 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r return -EPERM; } - return mpu_regionSet(idx, start, 0, __builtin_ctz(size), rasrAttr); + return mpu_regionSet(progHal, idx, start, 0, __builtin_ctz(size), rasrAttr); } commonTrailingZeroes = __builtin_ctz(start | end); @@ -161,15 +167,15 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r if (sigBits <= 3) { /* Can be represented with one region + 8 subregions */ sizeBit = commonTrailingZeroes + 3; - return mpu_regionCalculateAndSet(idx, start, end, sizeBit, rasrAttr); + return mpu_regionCalculateAndSet(progHal, idx, start, end, sizeBit, rasrAttr); } else if (sigBits == 4) { /* Can be represented with 2 regions + up to 8 subregions each */ sizeBit = commonTrailingZeroes + 3; diffMask = (1u << sizeBit) - 1; reg1End = (start & (~diffMask)) + diffMask + 1; - res = mpu_regionCalculateAndSet(idx, start, reg1End, sizeBit, rasrAttr); - return (res == EOK) ? mpu_regionCalculateAndSet(idx, reg1End, end, sizeBit, rasrAttr) : res; + res = mpu_regionCalculateAndSet(progHal, idx, start, reg1End, sizeBit, rasrAttr); + return (res == EOK) ? mpu_regionCalculateAndSet(progHal, idx, reg1End, end, sizeBit, rasrAttr) : res; } else if (rasrAttr == HOLE_ATTR(rasrAttr)) { /* Cannot attempt another cutout - we are already trying to make a hole */ @@ -198,97 +204,206 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r } /* First check if our "hole" overrides any existing mappings. This would lead to unintuitive behaviors. */ - if (mpu_checkOverlap(*idx, holeStart, holeEnd) != 0) { + if (mpu_checkOverlap(progHal, *idx, holeStart, holeEnd) != 0) { return -EPERM; } - res = mpu_regionCalculateAndSet(idx, alignedStart, alignedEnd, commonMsb, rasrAttr); - return (res == EOK) ? mpu_regionGenerate(idx, holeStart, holeEnd, HOLE_ATTR(rasrAttr)) : res; + res = mpu_regionCalculateAndSet(progHal, idx, alignedStart, alignedEnd, commonMsb, rasrAttr); + return (res == EOK) ? mpu_regionGenerate(progHal, idx, holeStart, holeEnd, HOLE_ATTR(rasrAttr)) : res; } /* Invalidate range of regions */ -static void mpu_regionInvalidate(u8 first, u8 last) +static void mpu_regionInvalidate(hal_syspage_prog_t *progHal, u8 first, u8 last) { unsigned int i; for (i = first; i < last && i < mpu_common.regMax; i++) { /* set multi-map to none */ - mpu_common.mapId[i] = (u32)-1; + progHal->mpu.map[i] = (u32)-1; - /* mark i-th region as invalid */ - mpu_common.region[i].rbar = i & 0xfu; + /* empty rbar value with automatic switch of rnr register */ + progHal->mpu.table[i].rbar = (1 << 4) | (i & 0xfu); - /* set exec never and mark whole region as disabled */ - mpu_common.region[i].rasr = (1u << 28) | (0x1fu << 1); + /* mark whole region as disabled */ + progHal->mpu.table[i].rasr = 0; } } /* Assign range of regions a multi-map id */ -static void mpu_regionAssignMap(u8 first, u8 last, u32 mapId) +static void mpu_regionAssignMap(hal_syspage_prog_t *progHal, u8 first, u8 last, u32 mapId) { unsigned int i; for (i = first; i < last && i < mpu_common.regMax; i++) { - /* assign map only if region is valid */ - if (mpu_common.region[i].rbar & (1u << 4)) - mpu_common.mapId[i] = mapId; + progHal->mpu.map[i] = mapId; } } -const mpu_common_t *const mpu_getCommon(void) +static int mpu_isMapAlloced(hal_syspage_prog_t *progHal, u32 mapId) { - return &mpu_common; + unsigned int i; + + for (i = 0; i < progHal->mpu.allocCnt; i++) { + if (progHal->mpu.map[i] == mapId) { + return 1; + } + } + + return 0; } -void mpu_init(void) +static int mpu_regionAlloc(hal_syspage_prog_t *progHal, addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable) { - volatile u32 *mpu_base = (void *)0xe000ed90; + int res; + unsigned int regCur = progHal->mpu.allocCnt; + u32 rasrAttr = mpu_regionAttrs(attr, enable); - mpu_common.type = *(mpu_base + mpu_type); - mpu_common.regMax = (u8)(mpu_common.type >> 8); - mpu_common.regCnt = mpu_common.mapCnt = 0; + res = mpu_regionGenerate(progHal, ®Cur, addr, end, rasrAttr); + if (res != EOK) { + mpu_regionInvalidate(progHal, progHal->mpu.allocCnt, regCur); + return res; + } + + mpu_regionAssignMap(progHal, progHal->mpu.allocCnt, regCur, mapId); - mpu_regionInvalidate(0, sizeof(mpu_common.region) / sizeof(mpu_common.region[0])); + progHal->mpu.allocCnt = regCur; + + return EOK; } -int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable) +static int mpu_allocKernelMap(hal_syspage_prog_t *progHal) { + addr_t start, end; + u32 attr; + u8 id; int res; - unsigned int regCur = mpu_common.regCnt; - u32 rasrAttr = mpu_regionAttrs(attr, enable); + const char *kcodemap = NULL; - res = mpu_regionGenerate(®Cur, addr, end, rasrAttr); - if (res != EOK) { - mpu_regionInvalidate(mpu_common.regCnt, regCur); + start = mpu_common.kernelEntryPoint; + if (start == (addr_t)-1) { + log_error("\nMissing kernel entry point!"); + return -EINVAL; + } + if ((res = syspage_mapAddrResolve(start, &kcodemap)) < 0) { return res; } + if ((res = syspage_mapNameResolve(kcodemap, &id)) < 0) { + return res; + } + if ((res = syspage_mapRangeResolve(kcodemap, &start, &end)) < 0) { + return res; + } + if ((res = syspage_mapAttrResolve(kcodemap, &attr)) < 0) { + return res; + } + if ((res = mpu_regionAlloc(progHal, start, end, attr, id, 1)) < 0) { + log_error("\nCan't allocate MPU region for kernel code map (%s)", kcodemap); + return res; + } + return EOK; +} - mpu_regionAssignMap(mpu_common.regCnt, regCur, mapId); - mpu_common.regCnt = regCur; - mpu_common.mapCnt++; +static void mpu_initPart(hal_syspage_prog_t *progHal) +{ + hal_memset(progHal, 0, sizeof(hal_syspage_prog_t)); + progHal->mpu.allocCnt = 0; +} - return EOK; + +void mpu_init(void) +{ + volatile u32 *mpu_base = (void *)0xe000ed90; + + mpu_common.type = *(mpu_base + mpu_type); + mpu_common.regMax = (u8)(mpu_common.type >> 8); + mpu_common.kernelEntryPoint = (addr_t)-1; +} + + +void mpu_kernelEntryPoint(addr_t addr) +{ + mpu_common.kernelEntryPoint = addr; } void mpu_getHalData(hal_syspage_t *hal) { - unsigned int i; + hal->mpuType = mpu_common.type; +} + + +unsigned int mpu_getMaxRegionsCount(void) +{ + return mpu_common.regMax; +} - mpu_regionInvalidate(mpu_common.regCnt, mpu_common.regMax); - hal->mpu.type = mpu_common.type; - hal->mpu.allocCnt = mpu_common.regCnt; +static int mpu_mapsAlloc(hal_syspage_prog_t *progHal, const char *maps, size_t cnt) +{ + int i, res; + addr_t start, end; + u32 attr; + u8 id; + + for (i = 0; i < cnt; i++) { + if ((res = syspage_mapNameResolve(maps, &id)) < 0) { + return res; + } + if (mpu_isMapAlloced(progHal, id) != 0) { + maps += hal_strlen(maps) + 1; /* name + '\0' */ + continue; + } + if ((res = syspage_mapRangeResolve(maps, &start, &end)) < 0) { + return res; + } + if ((res = syspage_mapAttrResolve(maps, &attr)) < 0) { + return res; + } + if ((res = mpu_regionAlloc(progHal, start, end, attr, id, 1)) < 0) { + log_error("\nCan't allocate MPU region for %s", maps); + return res; + } + maps += hal_strlen(maps) + 1; /* name + '\0' */ + } + return EOK; +} + - for (i = 0; i < sizeof(hal->mpu.table) / sizeof(hal->mpu.table[0]); i++) { - hal->mpu.table[i].rbar = mpu_common.region[i].rbar; - hal->mpu.table[i].rasr = mpu_common.region[i].rasr; - hal->mpu.map[i] = mpu_common.mapId[i]; +extern int mpu_getHalProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) +{ + int ret; + + mpu_initPart(&prog->hal); + + + /* FIXME HACK + * allow all programs to execute (and read) kernel code map. + * Needed because of hal_jmp, syscalls handler and signals handler. + * In these functions we need to switch to the user mode when still + * executing kernel code. This will cause memory management fault + * if the application does not have access to the kernel instruction + * map. Possible fix - place return to the user code in the separate + * region and allow this region instead. */ + ret = mpu_allocKernelMap(&prog->hal); + if (ret != EOK) { + return ret; + } + ret = mpu_mapsAlloc(&prog->hal, imaps, imapSz); + if (ret != EOK) { + return ret; } + ret = mpu_mapsAlloc(&prog->hal, dmaps, dmapSz); + if (ret != EOK) { + return ret; + } + + mpu_regionInvalidate(&prog->hal, prog->hal.mpu.allocCnt, mpu_common.regMax); + + return EOK; } diff --git a/hal/armv7m/mpu.h b/hal/armv7m/mpu.h index 3d12dd18..6aede851 100644 --- a/hal/armv7m/mpu.h +++ b/hal/armv7m/mpu.h @@ -18,36 +18,23 @@ #include -typedef struct { - u32 rbar; - u32 rasr; -} mpu_region_t; - -typedef struct { - u32 type; - u32 regCnt; - u32 regMax; - u32 mapCnt; - mpu_region_t region[16] __attribute__((aligned(8))); - u32 mapId[16]; -} mpu_common_t; - - -/* Get const pointer to read only mpu_common structure */ -extern const mpu_common_t *const mpu_getCommon(void); +extern unsigned int mpu_getMaxRegionsCount(void); /* Reset structures and detect MPU type */ extern void mpu_init(void); -/* Allocate MPU sub-regions and link with a map */ -extern int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable); +extern void mpu_kernelEntryPoint(addr_t addr); -/* Get MPU regions setup into hal_syspage_t structure */ +/* Get MPU info into hal_syspage_t structure */ extern void mpu_getHalData(hal_syspage_t *hal); +/* Get MPU regions setup into syspage_prog_t structure */ +extern int mpu_getHalProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz); + + #endif diff --git a/hal/armv7m/stm32/hal.c b/hal/armv7m/stm32/hal.c index 4a0bcea8..a3e32d82 100644 --- a/hal/armv7m/stm32/hal.c +++ b/hal/armv7m/stm32/hal.c @@ -118,12 +118,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv7r/mpu.c b/hal/armv7r/mpu.c index 626e2ad1..8c43f4e6 100644 --- a/hal/armv7r/mpu.c +++ b/hal/armv7r/mpu.c @@ -15,9 +15,15 @@ #include #include "mpu.h" +#include "lib/log.h" +#include "syspage.h" -static mpu_common_t mpu_common; +struct { + u32 type; + u32 regMax; + addr_t kernelEntryPoint; +} mpu_common; /* Removes all RASR attribute bits except ENABLE */ @@ -25,18 +31,18 @@ static mpu_common_t mpu_common; /* Setup single MPU region entry in a local MPU context */ -static int mpu_regionSet(unsigned int *idx, u32 baseAddr, u8 srdMask, u8 sizeBit, u32 rasrAttr) +static int mpu_regionSet(hal_syspage_prog_t *progHal, unsigned int *idx, u32 baseAddr, u8 srdMask, u8 sizeBit, u32 rasrAttr) { if ((sizeBit < 5) || (*idx >= mpu_common.regMax)) { return -EPERM; } - mpu_common.region[*idx].rbar = + progHal->mpu.table[*idx].rbar = baseAddr | (1u << 4) | /* mark region as valid */ (*idx & 0xfu); - mpu_common.region[*idx].rasr = + progHal->mpu.table[*idx].rasr = rasrAttr | (((u32)srdMask) << 8) | ((((u32)sizeBit - 1) & 0x1fu) << 1); @@ -70,20 +76,20 @@ static u32 mpu_regionAttrs(u32 attr, unsigned int enable) } -static int mpu_checkOverlap(unsigned int idx, u32 start, u32 end) +static int mpu_checkOverlap(hal_syspage_prog_t *progHal, unsigned int idx, u32 start, u32 end) { unsigned int i, j; u32 srStart, srEnd; u8 sizeBit, subregions; end -= 1; for (i = 0; i < idx; i++) { - if (((mpu_common.region[i].rbar & 0x10u) == 0) || ((mpu_common.region[i].rasr & 0x1u) == 0)) { + if (((progHal->mpu.table[i].rbar & 0x10u) == 0) || ((progHal->mpu.table[i].rasr & 0x1u) == 0)) { continue; } - sizeBit = ((mpu_common.region[i].rasr >> 1) & 0x1fu) + 1; - srStart = mpu_common.region[i].rbar & ~((1u << sizeBit) - 1); - subregions = (mpu_common.region[i].rasr >> 8) & 0xffu; + sizeBit = ((progHal->mpu.table[i].rasr >> 1) & 0x1fu) + 1; + srStart = progHal->mpu.table[i].rbar & ~((1u << sizeBit) - 1); + subregions = (progHal->mpu.table[i].rasr >> 8) & 0xffu; for (j = 0; j < 8; j++) { srEnd = srStart + (1u << (sizeBit - 3)) - 1; if (((subregions & 1u) == 0) && (start <= srEnd) && (srStart <= end)) { @@ -99,7 +105,7 @@ static int mpu_checkOverlap(unsigned int idx, u32 start, u32 end) } -static int mpu_regionCalculateAndSet(unsigned int *idx, addr_t start, addr_t end, u8 sizeBit, u32 rasrAttr) +static int mpu_regionCalculateAndSet(hal_syspage_prog_t *progHal, unsigned int *idx, addr_t start, addr_t end, u8 sizeBit, u32 rasrAttr) { u8 srdMask; /* RBAR contains all MSBs that are the same */ @@ -110,12 +116,12 @@ static int mpu_regionCalculateAndSet(unsigned int *idx, addr_t start, addr_t end srEnd = (srEnd == 0) ? 8 : srEnd; /* Bit set means disable region - negate result */ srdMask = ~(((1u << srEnd) - 1) & (0xffu << srStart)); - return mpu_regionSet(idx, baseAddr, srdMask, sizeBit, rasrAttr); + return mpu_regionSet(progHal, idx, baseAddr, srdMask, sizeBit, rasrAttr); } /* Create up to 2 regions that will represent a given map */ -static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 rasrAttr) +static int mpu_regionGenerate(hal_syspage_prog_t *progHal, unsigned int *idx, addr_t start, addr_t end, u32 rasrAttr) { int res; int commonTrailingZeroes, sigBits; @@ -132,7 +138,7 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r /* Check if size is power of 2 and start is aligned - necessary for handling small regions (below 256 bytes) */ if (size == 0) { - return mpu_regionSet(idx, 0, 0, 32, rasrAttr); + return mpu_regionSet(progHal, idx, 0, 0, 32, rasrAttr); } if ((size == (1u << __builtin_ctz(size))) && ((start & (size - 1)) == 0)) { @@ -141,7 +147,7 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r return -EPERM; } - return mpu_regionSet(idx, start, 0, __builtin_ctz(size), rasrAttr); + return mpu_regionSet(progHal, idx, start, 0, __builtin_ctz(size), rasrAttr); } commonTrailingZeroes = __builtin_ctz(start | end); @@ -155,15 +161,15 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r if (sigBits <= 3) { /* Can be represented with one region + 8 subregions */ sizeBit = commonTrailingZeroes + 3; - return mpu_regionCalculateAndSet(idx, start, end, sizeBit, rasrAttr); + return mpu_regionCalculateAndSet(progHal, idx, start, end, sizeBit, rasrAttr); } else if (sigBits == 4) { /* Can be represented with 2 regions + up to 8 subregions each */ sizeBit = commonTrailingZeroes + 3; diffMask = (1u << sizeBit) - 1; reg1End = (start & (~diffMask)) + diffMask + 1; - res = mpu_regionCalculateAndSet(idx, start, reg1End, sizeBit, rasrAttr); - return (res == EOK) ? mpu_regionCalculateAndSet(idx, reg1End, end, sizeBit, rasrAttr) : res; + res = mpu_regionCalculateAndSet(progHal, idx, start, reg1End, sizeBit, rasrAttr); + return (res == EOK) ? mpu_regionCalculateAndSet(progHal, idx, reg1End, end, sizeBit, rasrAttr) : res; } else if (rasrAttr == HOLE_ATTR(rasrAttr)) { /* Cannot attempt another cutout - we are already trying to make a hole */ @@ -192,98 +198,206 @@ static int mpu_regionGenerate(unsigned int *idx, addr_t start, addr_t end, u32 r } /* First check if our "hole" overrides any existing mappings. This would lead to unintuitive behaviors. */ - if (mpu_checkOverlap(*idx, holeStart, holeEnd) != 0) { + if (mpu_checkOverlap(progHal, *idx, holeStart, holeEnd) != 0) { return -EPERM; } - res = mpu_regionCalculateAndSet(idx, alignedStart, alignedEnd, commonMsb, rasrAttr); - return (res == EOK) ? mpu_regionGenerate(idx, holeStart, holeEnd, HOLE_ATTR(rasrAttr)) : res; + res = mpu_regionCalculateAndSet(progHal, idx, alignedStart, alignedEnd, commonMsb, rasrAttr); + return (res == EOK) ? mpu_regionGenerate(progHal, idx, holeStart, holeEnd, HOLE_ATTR(rasrAttr)) : res; } /* Invalidate range of regions */ -static void mpu_regionInvalidate(u8 first, u8 last) +static void mpu_regionInvalidate(hal_syspage_prog_t *progHal, u8 first, u8 last) { unsigned int i; for (i = first; i < last && i < mpu_common.regMax; i++) { /* set multi-map to none */ - mpu_common.mapId[i] = (u32)-1; + progHal->mpu.map[i] = (u32)-1; /* mark i-th region as invalid */ - mpu_common.region[i].rbar = i & 0xfu; + progHal->mpu.table[i].rbar = i & 0xfu; /* set exec never and mark whole region as disabled */ - mpu_common.region[i].rasr = (1u << 28) | (0x1fu << 1); + progHal->mpu.table[i].rasr = (1u << 28) | (0x1fu << 1); } } /* Assign range of regions a multi-map id */ -static void mpu_regionAssignMap(u8 first, u8 last, u32 mapId) +static void mpu_regionAssignMap(hal_syspage_prog_t *progHal, u8 first, u8 last, u32 mapId) { unsigned int i; for (i = first; i < last && i < mpu_common.regMax; i++) { - /* assign map only if region is valid */ - if (mpu_common.region[i].rbar & (1u << 4)) - mpu_common.mapId[i] = mapId; + progHal->mpu.map[i] = mapId; } } -const mpu_common_t *const mpu_getCommon(void) +static int mpu_isMapAlloced(hal_syspage_prog_t *progHal, u32 mapId) { - return &mpu_common; + unsigned int i; + + for (i = 0; i < progHal->mpu.allocCnt; i++) { + if (progHal->mpu.map[i] == mapId) { + return 1; + } + } + + return 0; } -void mpu_init(void) +static int mpu_regionAlloc(hal_syspage_prog_t *progHal, addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable) { - u32 mpu_type; + int res; + unsigned int regCur = progHal->mpu.allocCnt; + u32 rasrAttr = mpu_regionAttrs(attr, enable); - __asm__ volatile("mrc p15, 0, %0, c0, c0, 4" : "=r"(mpu_type)); - mpu_common.type = mpu_type; - mpu_common.regMax = (u8)(mpu_common.type >> 8); - mpu_common.regCnt = mpu_common.mapCnt = 0; + res = mpu_regionGenerate(progHal, ®Cur, addr, end, rasrAttr); + if (res != EOK) { + mpu_regionInvalidate(progHal, progHal->mpu.allocCnt, regCur); + return res; + } + + mpu_regionAssignMap(progHal, progHal->mpu.allocCnt, regCur, mapId); + + progHal->mpu.allocCnt = regCur; - mpu_regionInvalidate(0, sizeof(mpu_common.region) / sizeof(mpu_common.region[0])); + return EOK; } -int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable) +static int mpu_allocKernelMap(hal_syspage_prog_t *progHal) { + addr_t start, end; + u32 attr; + u8 id; int res; - unsigned int regCur = mpu_common.regCnt; - u32 rasrAttr = mpu_regionAttrs(attr, enable); + const char *kcodemap = NULL; - res = mpu_regionGenerate(®Cur, addr, end, rasrAttr); - if (res != EOK) { - mpu_regionInvalidate(mpu_common.regCnt, regCur); + start = mpu_common.kernelEntryPoint; + if (start == (addr_t)-1) { + log_error("\nMissing kernel entry point!"); + return -EINVAL; + } + if ((res = syspage_mapAddrResolve(start, &kcodemap)) < 0) { + return res; + } + if ((res = syspage_mapNameResolve(kcodemap, &id)) < 0) { + return res; + } + if ((res = syspage_mapRangeResolve(kcodemap, &start, &end)) < 0) { return res; } + if ((res = syspage_mapAttrResolve(kcodemap, &attr)) < 0) { + return res; + } + if ((res = mpu_regionAlloc(progHal, start, end, attr, id, 1)) < 0) { + log_error("\nCan't allocate MPU region for kernel code map (%s)", kcodemap); + return res; + } + return EOK; +} - mpu_regionAssignMap(mpu_common.regCnt, regCur, mapId); - mpu_common.regCnt = regCur; - mpu_common.mapCnt++; +static void mpu_initPart(hal_syspage_prog_t *progHal) +{ + hal_memset(progHal, 0, sizeof(hal_syspage_prog_t)); + progHal->mpu.allocCnt = 0; +} - return EOK; + +void mpu_init(void) +{ + u32 mpu_type; + + __asm__ volatile("mrc p15, 0, %0, c0, c0, 4" : "=r"(mpu_type)); + mpu_common.type = mpu_type; + mpu_common.regMax = (u8)(mpu_common.type >> 8); + mpu_common.kernelEntryPoint = (addr_t)-1; +} + + +void mpu_kernelEntryPoint(addr_t addr) +{ + mpu_common.kernelEntryPoint = addr; } void mpu_getHalData(hal_syspage_t *hal) { - unsigned int i; + hal->mpuType = mpu_common.type; +} - mpu_regionInvalidate(mpu_common.regCnt, mpu_common.regMax); - hal->mpu.type = mpu_common.type; - hal->mpu.allocCnt = mpu_common.regCnt; +unsigned int mpu_getMaxRegionsCount(void) +{ + return mpu_common.regMax; +} - for (i = 0; i < sizeof(hal->mpu.table) / sizeof(hal->mpu.table[0]); i++) { - hal->mpu.table[i].rbar = mpu_common.region[i].rbar; - hal->mpu.table[i].rasr = mpu_common.region[i].rasr; - hal->mpu.map[i] = mpu_common.mapId[i]; + +static int mpu_mapsAlloc(hal_syspage_prog_t *progHal, const char *maps, size_t cnt) +{ + int i, res; + addr_t start, end; + u32 attr; + u8 id; + + for (i = 0; i < cnt; i++) { + if ((res = syspage_mapNameResolve(maps, &id)) < 0) { + return res; + } + if (mpu_isMapAlloced(progHal, id) != 0) { + maps += hal_strlen(maps) + 1; /* name + '\0' */ + continue; + } + if ((res = syspage_mapRangeResolve(maps, &start, &end)) < 0) { + return res; + } + if ((res = syspage_mapAttrResolve(maps, &attr)) < 0) { + return res; + } + if ((res = mpu_regionAlloc(progHal, start, end, attr, id, 1)) < 0) { + log_error("\nCan't allocate MPU region for %s", maps); + return res; + } + maps += hal_strlen(maps) + 1; /* name + '\0' */ } + return EOK; +} + + +extern int mpu_getHalProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) +{ + int ret; + + mpu_initPart(&prog->hal); + + /* FIXME HACK + * allow all programs to execute (and read) kernel code map. + * Needed because of hal_jmp, syscalls handler and signals handler. + * In these functions we need to switch to the user mode when still + * executing kernel code. This will cause memory management fault + * if the application does not have access to the kernel instruction + * map. Possible fix - place return to the user code in the separate + * region and allow this region instead. */ + ret = mpu_allocKernelMap(&prog->hal); + if (ret != EOK) { + return ret; + } + ret = mpu_mapsAlloc(&prog->hal, imaps, imapSz); + if (ret != EOK) { + return ret; + } + ret = mpu_mapsAlloc(&prog->hal, dmaps, dmapSz); + if (ret != EOK) { + return ret; + } + + mpu_regionInvalidate(&prog->hal, prog->hal.mpu.allocCnt, mpu_common.regMax); + + return EOK; } diff --git a/hal/armv7r/mpu.h b/hal/armv7r/mpu.h index 3d12dd18..6aede851 100644 --- a/hal/armv7r/mpu.h +++ b/hal/armv7r/mpu.h @@ -18,36 +18,23 @@ #include -typedef struct { - u32 rbar; - u32 rasr; -} mpu_region_t; - -typedef struct { - u32 type; - u32 regCnt; - u32 regMax; - u32 mapCnt; - mpu_region_t region[16] __attribute__((aligned(8))); - u32 mapId[16]; -} mpu_common_t; - - -/* Get const pointer to read only mpu_common structure */ -extern const mpu_common_t *const mpu_getCommon(void); +extern unsigned int mpu_getMaxRegionsCount(void); /* Reset structures and detect MPU type */ extern void mpu_init(void); -/* Allocate MPU sub-regions and link with a map */ -extern int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable); +extern void mpu_kernelEntryPoint(addr_t addr); -/* Get MPU regions setup into hal_syspage_t structure */ +/* Get MPU info into hal_syspage_t structure */ extern void mpu_getHalData(hal_syspage_t *hal); +/* Get MPU regions setup into syspage_prog_t structure */ +extern int mpu_getHalProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz); + + #endif diff --git a/hal/armv7r/tda4vm/hal.c b/hal/armv7r/tda4vm/hal.c index 3fb973a8..631e28a9 100644 --- a/hal/armv7r/tda4vm/hal.c +++ b/hal/armv7r/tda4vm/hal.c @@ -102,12 +102,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv7r/zynqmp/hal.c b/hal/armv7r/zynqmp/hal.c index 284e4a45..c0f8a6f8 100644 --- a/hal/armv7r/zynqmp/hal.c +++ b/hal/armv7r/zynqmp/hal.c @@ -104,12 +104,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv8m/mcx/hal.c b/hal/armv8m/mcx/hal.c index 4b64cbfc..e3169b12 100644 --- a/hal/armv8m/mcx/hal.c +++ b/hal/armv8m/mcx/hal.c @@ -98,12 +98,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv8m/mpu.c b/hal/armv8m/mpu.c index 7e040d36..96171b60 100644 --- a/hal/armv8m/mpu.c +++ b/hal/armv8m/mpu.c @@ -16,12 +16,23 @@ #include #include #include "mpu.h" +#include "lib/log.h" +#include "syspage.h" #ifndef DISABLE_MPU #define DISABLE_MPU 0 #endif -static mpu_common_t mpu_common; +#define MPU_BASE ((void *)0xe000ed90) + +#define MPU_MAX_REGIONS 16 + + +struct { + u32 type; + u32 regMax; + addr_t kernelEntryPoint; +} mpu_common; enum { @@ -77,7 +88,7 @@ static u32 mpu_regionAttrsRbar(u32 attr) /* Setup single MPU region entry in local MPU context */ -static int mpu_regionSet(unsigned int *idx, addr_t start, addr_t end, u32 rbarAttr, u32 rlarAttr, u32 mapId) +static int mpu_regionSet(hal_syspage_prog_t *progHal, unsigned int *idx, addr_t start, addr_t end, u32 rbarAttr, u32 rlarAttr, u32 mapId) { /* Allow end == 0, this means end of address range */ const size_t size = (end - start) & 0xffffffffu; @@ -101,35 +112,29 @@ static int mpu_regionSet(unsigned int *idx, addr_t start, addr_t end, u32 rbarAt return -EPERM; } - mpu_common.region[*idx].rbar = (start & ~0x1f) | rbarAttr; - mpu_common.region[*idx].rlar = (limit & ~0x1f) | rlarAttr; - mpu_common.mapId[*idx] = mapId; + progHal->mpu.table[*idx].rbar = (start & ~0x1f) | rbarAttr; + progHal->mpu.table[*idx].rlar = (limit & ~0x1f) | rlarAttr; + progHal->mpu.map[*idx] = mapId; *idx += 1; return EOK; } -const mpu_common_t *const mpu_getCommon(void) -{ - return &mpu_common; -} - - /* Invalidate range of regions */ -static void mpu_regionInvalidate(u8 first, u8 last) +static void mpu_regionInvalidate(hal_syspage_prog_t *progHal, u8 first, u8 last) { unsigned int i; for (i = first; (i < last) && (i < mpu_common.regMax); i++) { /* set multi-map to none */ - mpu_common.mapId[i] = (u32)-1; + progHal->mpu.map[i] = (u32)-1; /* mark i-th region as disabled */ - mpu_common.region[i].rlar = 0; + progHal->mpu.table[i].rlar = 0; /* set exec never */ - mpu_common.region[i].rbar = 1; + progHal->mpu.table[i].rbar = 1; } } @@ -162,17 +167,33 @@ void mpu_init(void) *(mpu_base + mpu_mair0) = 0xee04aa00; } #endif - mpu_common.regCnt = 0; - mpu_common.mapCnt = 0; + mpu_common.kernelEntryPoint = (addr_t)-1; +} + +void mpu_kernelEntryPoint(addr_t addr) +{ + mpu_common.kernelEntryPoint = addr; +} + + +static int mpu_isMapAlloced(hal_syspage_prog_t *progHal, u32 mapId) +{ + unsigned int i; + + for (i = 0; i < progHal->mpu.allocCnt; i++) { + if (progHal->mpu.map[i] == mapId) { + return 1; + } + } - mpu_regionInvalidate(0, MPU_MAX_REGIONS); + return 0; } -int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable) +static int mpu_regionAlloc(hal_syspage_prog_t *progHal, addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable) { int res; - unsigned int regCur = mpu_common.regCnt; + unsigned int regCur = progHal->mpu.allocCnt; u32 rbarAttr, rlarAttr; if (mpu_common.regMax == 0) { @@ -184,45 +205,123 @@ int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int e rbarAttr = mpu_regionAttrsRbar(attr); rlarAttr = mpu_regionAttrsRlar(attr, enable); - res = mpu_regionSet(®Cur, addr, end, rbarAttr, rlarAttr, mapId); + res = mpu_regionSet(progHal, ®Cur, addr, end, rbarAttr, rlarAttr, mapId); if (res != EOK) { - mpu_regionInvalidate(mpu_common.regCnt, regCur); + mpu_regionInvalidate(progHal, progHal->mpu.allocCnt, regCur); return res; } - mpu_common.regCnt = regCur; - mpu_common.mapCnt++; + progHal->mpu.allocCnt = regCur; + + return EOK; +} + +static int mpu_allocKernelMap(hal_syspage_prog_t *progHal) +{ + addr_t start, end; + u32 attr; + u8 id; + int res; + const char *kcodemap = NULL; + + start = mpu_common.kernelEntryPoint; + if (start == (addr_t)-1) { + log_error("\nMissing kernel entry point!"); + return -EINVAL; + } + if ((res = syspage_mapAddrResolve(start, &kcodemap)) < 0) { + return res; + } + if ((res = syspage_mapNameResolve(kcodemap, &id)) < 0) { + return res; + } + if ((res = syspage_mapRangeResolve(kcodemap, &start, &end)) < 0) { + return res; + } + if ((res = syspage_mapAttrResolve(kcodemap, &attr)) < 0) { + return res; + } + if ((res = mpu_regionAlloc(progHal, start, end, attr, id, 1)) < 0) { + log_error("\nCan't allocate MPU region for kernel code map (%s)", kcodemap); + return res; + } return EOK; } + +static void mpu_initPart(hal_syspage_prog_t *progHal) +{ + hal_memset(progHal, 0, sizeof(hal_syspage_prog_t)); + progHal->mpu.allocCnt = 0; +} + + void mpu_getHalData(hal_syspage_t *hal) { - const unsigned int syspageTableSize = sizeof(hal->mpu.table) / sizeof(hal->mpu.table[0]); - unsigned int i; + hal->mpuType = mpu_common.type; +} - mpu_regionInvalidate(mpu_common.regCnt, mpu_common.regMax); - if (mpu_common.regCnt > syspageTableSize) { - /* TODO: We need a way to handle errors here that can happen due to misconfiguration - * (size of table in syspage too small). This way to do it silently truncates the list - * which may cause undesired behaviors. */ - mpu_common.regCnt = syspageTableSize; +static int mpu_mapsAlloc(hal_syspage_prog_t *progHal, const char *maps, size_t cnt) +{ + int i, res; + addr_t start, end; + u32 attr; + u8 id; + + for (i = 0; i < cnt; i++) { + if ((res = syspage_mapNameResolve(maps, &id)) < 0) { + return res; + } + if (mpu_isMapAlloced(progHal, id) != 0) { + maps += hal_strlen(maps) + 1; /* name + '\0' */ + continue; + } + if ((res = syspage_mapRangeResolve(maps, &start, &end)) < 0) { + return res; + } + if ((res = syspage_mapAttrResolve(maps, &attr)) < 0) { + return res; + } + if ((res = mpu_regionAlloc(progHal, start, end, attr, id, 1)) < 0) { + log_error("\nCan't allocate MPU region for %s", maps); + return res; + } + maps += hal_strlen(maps) + 1; /* name + '\0' */ } + return EOK; +} - hal->mpu.type = mpu_common.type; - hal->mpu.allocCnt = mpu_common.regCnt; - for (i = 0; i < mpu_common.regCnt; i++) { - hal->mpu.table[i].rbar = mpu_common.region[i].rbar; - hal->mpu.table[i].rlar = mpu_common.region[i].rlar; - hal->mpu.map[i] = mpu_common.mapId[i]; +extern int mpu_getHalProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) +{ + int ret; + + mpu_initPart(&prog->hal); + + /* FIXME HACK + * allow all programs to execute (and read) kernel code map. + * Needed because of hal_jmp, syscalls handler and signals handler. + * In these functions we need to switch to the user mode when still + * executing kernel code. This will cause memory management fault + * if the application does not have access to the kernel instruction + * map. Possible fix - place return to the user code in the separate + * region and allow this region instead. */ + ret = mpu_allocKernelMap(&prog->hal); + if (ret != EOK) { + return ret; } - - /* Ensure all entries are initialized if mpu_common.regCnt or mpu_common.regMax is smaller than syspageTableSize */ - for (; i < syspageTableSize; i++) { - hal->mpu.map[i] = (u32)-1; /* set multi-map to none */ - hal->mpu.table[i].rlar = 0; /* mark i-th region as disabled */ - hal->mpu.table[i].rbar = 1; /* set exec never */ + ret = mpu_mapsAlloc(&prog->hal, imaps, imapSz); + if (ret != EOK) { + return ret; + } + ret = mpu_mapsAlloc(&prog->hal, dmaps, dmapSz); + if (ret != EOK) { + return ret; } + + mpu_regionInvalidate(&prog->hal, prog->hal.mpu.allocCnt, mpu_common.regMax); + + return EOK; } diff --git a/hal/armv8m/mpu.h b/hal/armv8m/mpu.h index 7b24f63d..ab7e0e1f 100644 --- a/hal/armv8m/mpu.h +++ b/hal/armv8m/mpu.h @@ -19,41 +19,19 @@ #include -#define MPU_BASE ((void *)0xe000ed90) - -#define MPU_MAX_REGIONS 16 - - -typedef struct { - u32 rbar; - u32 rlar; -} mpu_region_t; - - -typedef struct { - u32 type; - u32 regCnt; - u32 regMax; - u32 mapCnt; - mpu_region_t region[MPU_MAX_REGIONS] __attribute__((aligned(8))); - u32 mapId[MPU_MAX_REGIONS]; -} mpu_common_t; - - -/* Get const pointer to read only mpu_common structure */ -extern const mpu_common_t *const mpu_getCommon(void); - - /* Reset structures and detect MPU type */ extern void mpu_init(void); -/* Allocate MPU sub-regions and link with a map */ -extern int mpu_regionAlloc(addr_t addr, addr_t end, u32 attr, u32 mapId, unsigned int enable); +extern void mpu_kernelEntryPoint(addr_t addr); -/* Get MPU regions setup into hal_syspage_t structure */ +/* Get MPU info into hal_syspage_t structure */ extern void mpu_getHalData(hal_syspage_t *hal); +/* Get MPU regions setup into syspage_prog_t structure */ +extern int mpu_getHalProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz); + + #endif diff --git a/hal/armv8m/nrf/hal.c b/hal/armv8m/nrf/hal.c index 4b3dffe4..7570574f 100644 --- a/hal/armv8m/nrf/hal.c +++ b/hal/armv8m/nrf/hal.c @@ -94,12 +94,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv8m/stm32/hal.c b/hal/armv8m/stm32/hal.c index 35cb14b7..f387a83c 100644 --- a/hal/armv8m/stm32/hal.c +++ b/hal/armv8m/stm32/hal.c @@ -118,12 +118,13 @@ void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect) void hal_kernelEntryPoint(addr_t addr) { hal_common.entry = addr; + mpu_kernelEntryPoint(addr); } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { - return mpu_regionAlloc(start, end, attr, mapId, 1); + return mpu_getHalProgData(prog, imaps, imapSz, dmaps, dmapSz); } diff --git a/hal/armv8r/mps3an536/hal.c b/hal/armv8r/mps3an536/hal.c index 110076c7..c9a37e36 100644 --- a/hal/armv8r/mps3an536/hal.c +++ b/hal/armv8r/mps3an536/hal.c @@ -88,7 +88,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/hal.h b/hal/hal.h index 305a2164..6a6d7d8b 100644 --- a/hal/hal.h +++ b/hal/hal.h @@ -62,8 +62,8 @@ extern void hal_kernelEntryPoint(addr_t addr); extern void hal_kernelGetEntryPointOffset(addr_t *off, int *indirect); -/* Function validates and add memory map at hal region level */ -extern int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId); +/* Function validates and adds program memory maps at hal region level */ +extern int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz); /* Function returns entry located near the start of the declared memory */ diff --git a/hal/ia32/memory.c b/hal/ia32/memory.c index 5b7b5bc2..05d2db24 100644 --- a/hal/ia32/memory.c +++ b/hal/ia32/memory.c @@ -269,7 +269,7 @@ static int memory_enableA20(void) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/riscv64/gaisler/hal.c b/hal/riscv64/gaisler/hal.c index 6f9b2fd7..c29b059d 100644 --- a/hal/riscv64/gaisler/hal.c +++ b/hal/riscv64/gaisler/hal.c @@ -90,7 +90,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/riscv64/generic/hal.c b/hal/riscv64/generic/hal.c index 728081ba..67656d15 100644 --- a/hal/riscv64/generic/hal.c +++ b/hal/riscv64/generic/hal.c @@ -89,7 +89,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/sparcv8leon/gaisler/generic/hal.c b/hal/sparcv8leon/gaisler/generic/hal.c index 9097cb91..889fdeb2 100644 --- a/hal/sparcv8leon/gaisler/generic/hal.c +++ b/hal/sparcv8leon/gaisler/generic/hal.c @@ -97,7 +97,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/sparcv8leon/gaisler/gr712rc/hal.c b/hal/sparcv8leon/gaisler/gr712rc/hal.c index 19359e64..227883f6 100644 --- a/hal/sparcv8leon/gaisler/gr712rc/hal.c +++ b/hal/sparcv8leon/gaisler/gr712rc/hal.c @@ -98,7 +98,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/sparcv8leon/gaisler/gr716/hal.c b/hal/sparcv8leon/gaisler/gr716/hal.c index 5d1d3215..45c95d38 100644 --- a/hal/sparcv8leon/gaisler/gr716/hal.c +++ b/hal/sparcv8leon/gaisler/gr716/hal.c @@ -100,7 +100,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/hal/sparcv8leon/gaisler/gr740/hal.c b/hal/sparcv8leon/gaisler/gr740/hal.c index 0589eaa4..064e008e 100644 --- a/hal/sparcv8leon/gaisler/gr740/hal.c +++ b/hal/sparcv8leon/gaisler/gr740/hal.c @@ -99,7 +99,7 @@ void hal_kernelEntryPoint(addr_t addr) } -int hal_memoryAddMap(addr_t start, addr_t end, u32 attr, u32 mapId) +int hal_getProgData(syspage_prog_t *prog, const char *imaps, size_t imapSz, const char *dmaps, size_t dmapSz) { return 0; } diff --git a/syspage.c b/syspage.c index daee2b7a..7b7f379e 100644 --- a/syspage.c +++ b/syspage.c @@ -285,10 +285,6 @@ int syspage_mapAdd(const char *name, addr_t start, addr_t end, const char *attr) syspage_sortedInsert(map, entry); start = entry->end; } - res = hal_memoryAddMap(map->start, map->end, map->attr, map->id); - if (res < 0) { - return res; - } return EOK; } @@ -465,6 +461,27 @@ unsigned int syspage_mapRangeCheck(addr_t start, addr_t end, unsigned int *attrO } +int syspage_mapAddrResolve(addr_t addr, const char **name) +{ + const syspage_map_t *map = syspage_common.syspage->maps; + + if (map == NULL) { + log_error("\nsyspage: %x does not match any map", addr); + return -EINVAL; + } + + do { + if (addr < map->end && addr >= map->start) { + *name = map->name; + return EOK; + } + } while ((map = map->next) != syspage_common.syspage->maps); + + log_error("\nsyspage: %x does not match any map", addr); + return -EINVAL; +} + + const char *syspage_mapName(u8 id) { const syspage_map_t *map = syspage_common.syspage->maps; @@ -530,6 +547,11 @@ syspage_prog_t *syspage_progAdd(const char *argv, u32 flags) } +syspage_prog_t *syspage_progsGet(void) +{ + return syspage_common.syspage->progs; +} + /* Set console */ diff --git a/syspage.h b/syspage.h index 152738fe..c19f0704 100644 --- a/syspage.h +++ b/syspage.h @@ -57,6 +57,9 @@ extern int syspage_mapRangeResolve(const char *name, addr_t *start, addr_t *end) extern unsigned int syspage_mapRangeCheck(addr_t start, addr_t end, unsigned int *attrOut); +extern int syspage_mapAddrResolve(addr_t addr, const char **name); + + extern const char *syspage_mapName(u8 id); @@ -70,6 +73,9 @@ extern mapent_t *syspage_entryAdd(const char *mapName, addr_t start, size_t size extern syspage_prog_t *syspage_progAdd(const char *argv, u32 flags); +extern syspage_prog_t *syspage_progsGet(void); + + extern void syspage_progShow(void);