Xiang Wang has uploaded this change for review. ( https://review.coreboot.org/28569
Change subject: riscv: add physical memory protection(PMP) support ......................................................................
riscv: add physical memory protection(PMP) support
Change-Id: I2e9e0c94e6518f63ade7680a3ce68bacfae219d4 Signed-off-by: Xiang Wang wxjstz@126.com --- A src/arch/riscv/include/arch/pmp.h A src/arch/riscv/pmp.c 2 files changed, 342 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/69/28569/1
diff --git a/src/arch/riscv/include/arch/pmp.h b/src/arch/riscv/include/arch/pmp.h new file mode 100644 index 0000000..bac8e9d --- /dev/null +++ b/src/arch/riscv/include/arch/pmp.h @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2018 HardenedLinux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __RISCV_PMP_H__ +#define __RISCV_PMP_H__ + +/* this function needs to be implemented by a specific SoC. + * return number of pmp entries for current hart */ +extern int pmp_entries_num(void); + +/* reset PMP setting */ +void reset_pmp(void); + +/* this function used to setup pmp record */ +void setup_pmp(uintptr_t base, uintptr_t size, uintptr_t flags); + +#endif /* __RISCV_PMP_H__ */ + diff --git a/src/arch/riscv/pmp.c b/src/arch/riscv/pmp.c new file mode 100644 index 0000000..c4ee8d3 --- /dev/null +++ b/src/arch/riscv/pmp.c @@ -0,0 +1,312 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2018 HardenedLinux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/encoding.h> +#include <stdint.h> +#include <arch/pmp.h> +#include <console/console.h> + +#define GRANULE (1 << PMP_SHIFT) +#define IS2POW(n) ((n != 0) && ((n & (n - 1)) == 0)) + +/* This structure is used to temporarily record PMP + * configuration information. */ +typedef struct { + /* used to record the value of pmpcfg[i] */ + uintptr_t cfg; + /* When generating a TOR type configuration, + * the previous entry needs to record the starting address. + * used to record the value of pmpaddr[i - 1] */ + uintptr_t before_address; + /* used to record the value of pmpaddr[i] */ + uintptr_t address; +} pmpcfg_t; + +/* This variable is used to record which entries have been used. */ +static uintptr_t pmp_entry_used_mask; + +/* helper function used to read pmpcfg[idx] */ +static uintptr_t read_pmpcfg(int idx) +{ +#if __riscv_xlen == 32 + int shift = 8 * (idx & 3); + switch (idx >> 2) { + case 0: + return (read_csr(pmpcfg0) >> shift) & 0xff; + case 1: + return (read_csr(pmpcfg1) >> shift) & 0xff; + case 2: + return (read_csr(pmpcfg2) >> shift) & 0xff; + case 3: + return (read_csr(pmpcfg3) >> shift) & 0xff; + } +#elif __riscv_xlen == 64 + int shift = 8 * (idx & 7); + switch (idx >> 3) { + case 0: + return (read_csr(pmpcfg0) >> shift) & 0xff; + case 1: + return (read_csr(pmpcfg2) >> shift) & 0xff; + } +#endif + return -1; +} + + +/* helper function used to write pmpcfg[idx] */ +static void write_pmpcfg(int idx, uintptr_t cfg) +{ + uintptr_t old; + uintptr_t new; +#if __riscv_xlen == 32 + int shift = 8 * (idx & 3); + switch (idx >> 2) { + case 0: + old = read_csr(pmpcfg0); + new = (old & ~(0xff << shift)) | ((cfg & 0xff) << shift); + write_csr(pmpcfg0, new); + break; + case 1: + old = read_csr(pmpcfg1); + new = (old & ~(0xff << shift)) | ((cfg & 0xff) << shift); + write_csr(pmpcfg1, new); + break; + case 2: + old = read_csr(pmpcfg2); + new = (old & ~(0xff << shift)) | ((cfg & 0xff) << shift); + write_csr(pmpcfg2, new); + break; + case 3: + old = read_csr(pmpcfg3); + new = (old & ~(0xff << shift)) | ((cfg & 0xff) << shift); + write_csr(pmpcfg3, new); + break; + } +#elif __riscv_xlen == 64 + int shift = 8 * (idx & 7); + switch (idx >> 3) { + case 0: + old = read_csr(pmpcfg0); + new = (old & ~(0xff << shift)) | ((cfg & 0xff) << shift); + write_csr(pmpcfg0, new); + break; + case 1: + old = read_csr(pmpcfg2); + new = (old & ~(0xff << shift)) | ((cfg & 0xff) << shift); + write_csr(pmpcfg2, new); + break; + } +#endif +} + +/* helper function used to read pmpaddr[idx] */ +/* +static uintptr_t read_pmpaddr(int idx) +{ + switch(idx) { + case 0: + return read_csr(pmpaddr0); + case 1: + return read_csr(pmpaddr1); + case 2: + return read_csr(pmpaddr2); + case 3: + return read_csr(pmpaddr3); + case 4: + return read_csr(pmpaddr4); + case 5: + return read_csr(pmpaddr5); + case 6: + return read_csr(pmpaddr6); + case 7: + return read_csr(pmpaddr7); + case 8: + return read_csr(pmpaddr8); + case 9: + return read_csr(pmpaddr9); + case 10: + return read_csr(pmpaddr10); + case 11: + return read_csr(pmpaddr11); + case 12: + return read_csr(pmpaddr12); + case 13: + return read_csr(pmpaddr13); + case 14: + return read_csr(pmpaddr14); + case 15: + return read_csr(pmpaddr15); + } +} +*/ + +/* helper function used to write pmpaddr[idx] */ +static void write_pmpaddr(int idx, uintptr_t val) +{ + switch (idx) { + case 0: + write_csr(pmpaddr0, val); + break; + case 1: + write_csr(pmpaddr1, val); + break; + case 2: + write_csr(pmpaddr2, val); + break; + case 3: + write_csr(pmpaddr3, val); + break; + case 4: + write_csr(pmpaddr4, val); + break; + case 5: + write_csr(pmpaddr5, val); + break; + case 6: + write_csr(pmpaddr6, val); + break; + case 7: + write_csr(pmpaddr7, val); + break; + case 8: + write_csr(pmpaddr8, val); + break; + case 9: + write_csr(pmpaddr9, val); + break; + case 10: + write_csr(pmpaddr10, val); + break; + case 11: + write_csr(pmpaddr11, val); + break; + case 12: + write_csr(pmpaddr12, val); + break; + case 13: + write_csr(pmpaddr13, val); + break; + case 14: + write_csr(pmpaddr14, val); + break; + case 15: + write_csr(pmpaddr15, val); + break; + } +} + + +/* Generate a pmp configuration of type NA4/NAPOT */ +static pmpcfg_t generate_pmp_napot( + uintptr_t base, uintptr_t size, uintptr_t flags) +{ + pmpcfg_t p; + flags = flags & (PMP_R | PMP_W | PMP_X | PMP_L); + p.cfg = flags | (size > GRANULE ? PMP_NAPOT : PMP_NA4); + p.before_address = 0; + p.address = (base + (size / 2 - 1)) >> PMP_SHIFT; + return p; +} + + +/* Generate a pmp configuration of type TOR */ +static pmpcfg_t generate_pmp_range( + uintptr_t base, uintptr_t size, uintptr_t flags) +{ + pmpcfg_t p; + flags = flags & (PMP_R | PMP_W | PMP_X | PMP_L); + p.cfg = flags | PMP_TOR; + p.before_address = base >> PMP_SHIFT; + p.address = (base + size) >> PMP_SHIFT; + return p; +} + + +/* Generate a pmp configuration */ +static pmpcfg_t generate_pmp(uintptr_t base, uintptr_t size, uintptr_t flags) +{ + if (IS2POW(size) && (size >= 4) && ((base & (size - 1)) == 0)) + return generate_pmp_napot(base, size, flags); + else + return generate_pmp_range(base, size, flags); +} + + + +/* find empty pmp entry by type + * TOR type configuration requires two consecutive PMP entries, + * others requires one. */ +static int find_empty_pmp_entry(int is_range) +{ + int status = 0; + for (int i = 0; i < pmp_entries_num(); i++) { + if (pmp_entry_used_mask & (1 << i)) + status = 0; + else + status++; + if (is_range && (status == 2)) + return i; + if (!is_range && (status == 1)) + return i; + } + die("Too many pmp configurations, no free entries can be used!"); + return -1; +} + +/* mark pmp entry has be used + * this function need be used with find_entry_pmp_entry + * + * n = find_empty_pmp_entry(is_range) + * ... // pmp set operate + * mask_pmp_entry_used(n, is_range); + */ +static void mask_pmp_entry_used(int idx, int is_range) +{ + pmp_entry_used_mask |= 1 << idx; + if (is_range) + pmp_entry_used_mask |= 1 << (idx - 1); +} + +/* reset PMP setting */ +void reset_pmp(void) +{ + for (int i = 0; i < pmp_entries_num(); i++) { + if (read_pmpcfg(i) & PMP_L) + die("Some PMP configurations are locked " + "and cannot be reset!"); + write_pmpcfg(i, 0); + write_pmpaddr(i, 0); + } +} + +/* this function used to setup pmp record */ +void setup_pmp(uintptr_t base, uintptr_t size, uintptr_t flags) +{ + pmpcfg_t p; + int is_range, n; + + p = generate_pmp(base, size, flags); + is_range = ((p.cfg & PMP_A) == PMP_TOR); + + n = find_empty_pmp_entry(is_range); + + write_pmpaddr(n, p.address); + if (is_range) + write_pmpaddr(n - 1, p.before_address); + write_pmpcfg(n, p.cfg); + + mask_pmp_entry_used(n, is_range); +} +