[coreboot-gerrit] Change in coreboot[master]: riscv: add physical memory protection(PMP) support

Xiang Wang (Code Review) gerrit at coreboot.org
Tue Sep 11 09:56:33 CEST 2018


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 at 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);
+}
+

-- 
To view, visit https://review.coreboot.org/28569
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I2e9e0c94e6518f63ade7680a3ce68bacfae219d4
Gerrit-Change-Number: 28569
Gerrit-PatchSet: 1
Gerrit-Owner: Xiang Wang <wxjstz at 126.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20180911/00aab6b7/attachment-0001.html>


More information about the coreboot-gerrit mailing list