[PATCH 08/11] ARM64: mmu: implement ARMv8 mmuinfo command
Ahmad Fatoum
a.fatoum at pengutronix.de
Sun May 21 22:28:32 PDT 2023
To aid with debugging of MMU code, let's implement mmuinfo for ARMv8,
like we already support for ARMv7.
Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
arch/arm/configs/multi_v8_defconfig | 1 +
arch/arm/cpu/Makefile | 2 +-
arch/arm/cpu/mmuinfo.c | 2 +
arch/arm/cpu/mmuinfo_64.c | 215 ++++++++++++++++++++++++++++
arch/arm/include/asm/mmuinfo.h | 1 +
arch/arm/include/asm/sysreg.h | 76 ++++++++++
commands/Kconfig | 6 +-
7 files changed, 299 insertions(+), 4 deletions(-)
create mode 100644 arch/arm/cpu/mmuinfo_64.c
create mode 100644 arch/arm/include/asm/sysreg.h
diff --git a/arch/arm/configs/multi_v8_defconfig b/arch/arm/configs/multi_v8_defconfig
index 62afe3829350..d30158d7f880 100644
--- a/arch/arm/configs/multi_v8_defconfig
+++ b/arch/arm/configs/multi_v8_defconfig
@@ -57,6 +57,7 @@ CONFIG_LONGHELP=y
CONFIG_CMD_IOMEM=y
CONFIG_CMD_IMD=y
CONFIG_CMD_MEMINFO=y
+CONFIG_CMD_ARM_MMUINFO=y
CONFIG_CMD_REGULATOR=y
CONFIG_CMD_MMC_EXTCSD=y
CONFIG_CMD_POLLER=y
diff --git a/arch/arm/cpu/Makefile b/arch/arm/cpu/Makefile
index a271de2c1f38..5baff2fad087 100644
--- a/arch/arm/cpu/Makefile
+++ b/arch/arm/cpu/Makefile
@@ -27,7 +27,7 @@ obj-$(CONFIG_ARM_PSCI_CLIENT) += psci-client.o
# Any variants can be called as start-armxyz.S
#
obj-$(CONFIG_CMD_ARM_CPUINFO) += cpuinfo.o
-obj-$(CONFIG_MMUINFO) += mmuinfo.o mmuinfo_32.o
+obj-$(CONFIG_MMUINFO) += mmuinfo.o mmuinfo_$(S64_32).o
obj-$(CONFIG_OFDEVICE) += dtb.o
ifeq ($(CONFIG_MMU),)
diff --git a/arch/arm/cpu/mmuinfo.c b/arch/arm/cpu/mmuinfo.c
index 49e393149b69..413f2f337e95 100644
--- a/arch/arm/cpu/mmuinfo.c
+++ b/arch/arm/cpu/mmuinfo.c
@@ -12,6 +12,8 @@
int mmuinfo(void *addr)
{
+ if (IS_ENABLED(CONFIG_CPU_V8))
+ return mmuinfo_v8(addr);
if (IS_ENABLED(CONFIG_CPU_V7) && cpu_architecture() == CPU_ARCH_ARMv7)
return mmuinfo_v7(addr);
diff --git a/arch/arm/cpu/mmuinfo_64.c b/arch/arm/cpu/mmuinfo_64.c
new file mode 100644
index 000000000000..de4945f43e8e
--- /dev/null
+++ b/arch/arm/cpu/mmuinfo_64.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2023 Ahmad Fatoum <a.fatoum at pengutronix.de>, Pengutronix
+/*
+ * mmuinfo_64.c - Show MMU/cache information via AT instruction
+ */
+
+#include <common.h>
+#include <asm/mmuinfo.h>
+#include <asm/system.h>
+#include <asm/sysreg.h>
+#include <linux/bitfield.h>
+
+#define at_par(reg, addr) ({ \
+ asm volatile("at " reg ", %0\n" :: "r" (addr)); \
+ isb(); \
+ read_sysreg_par(); \
+})
+
+#define BITS(from, to, val) FIELD_GET(GENMASK(from, to), val)
+
+static const char *decode_devmem_attr(u8 attr)
+{
+ switch (attr & ~0x1) {
+ case 0b00000000:
+ return "0b0000 Device-nGnRnE memory";
+ case 0b00000100:
+ return "0b0100 Device-nGnRE memory";
+ case 0b00001000:
+ return "0b1000 Device-nGRE memory";
+ case 0b00001100:
+ return "0b1100 Device-GRE memory";
+ default:
+ return "unknown";
+ };
+}
+
+static char *cache_attr[] = {
+ "0b0000 Wrongly decoded",
+ "0b0001 Write-Through Transient, Write-Allocate, no Read-Allocate",
+ "0b0010 Write-Through Transient, no Write-Allocate",
+ "0b0011 Write-Through Transient, Write-Allocate",
+ "0b0100 Non-Cacheable",
+ "0b0101 Write-Back Transient, Write-Allocate, no Read-Allocate",
+ "0b0110 Write-Back Transient, no Write-Allocate",
+ "0b0111 Write-Back Transient, Write-Allocate",
+ "0b1000 Write-Through Non-transient, no Write-Allocate no Read-Allocate",
+ "0b1001 Write-Through Non-transient, Write-Allocate no Read-Allocate",
+ "0b1010 Write-Through Non-transient, no Write-Allocate",
+ "0b1011 Write-Through Non-transient, Write-Allocate",
+ "0b1100 Write-Back Non-transient, no Write-Allocate no Read-Allocate",
+ "0b1101 Write-Back Non-transient, Write-Allocate no Read-Allocate",
+ "0b1110 Write-Back Non-transient, no Write-Allocate",
+ "0b1111 Write-Back Non-transient, Write-Allocate",
+};
+
+static char *share_attr[] = {
+ "0b00 Non-Shareable",
+ "0b01 Reserved",
+ "0b10 Outer Shareable",
+ "0b11 Inner Shareable",
+};
+
+static char *stage_fault[] = {
+ "stage 1 translation",
+ "stage 2 translation",
+};
+
+static char *fault_status_leveled[] = {
+ "Address size fault", /* of translation or translation table base register */
+ "Translation fault",
+ "Access flag fault",
+ "Permission fault",
+ "Synchronous External abort", /* level -1 */
+ "Synchronous External abort", /* on translation table walk or hardware update of translation table */
+ "Synchronous parity or ECC error", /* level -1 */
+ "Synchronous parity or ECC error", /* on memory access on translation table walk or hardware update of translation table */
+};
+
+static const char *decode_fault_status_level(u8 fst)
+{
+ if (!(fst & BIT(5)))
+ return "";
+
+ switch (BITS(5, 0, fst)) {
+ case 0b010011:
+ case 0b011011:
+ return ", level -1";
+ }
+
+ switch (BITS(1, 0, fst)) {
+ case 0b00:
+ return ", level 0";
+ case 0b01:
+ return ", level 1";
+ case 0b10:
+ return ", level 2";
+ case 0b11:
+ return ", level 3";
+ }
+
+ BUG();
+}
+
+static const char *decode_fault_status(u8 fst)
+{
+
+ switch (BITS(5, 0, fst)) {
+ case 0b101001: /* When FEAT_LPA2 is implemented */
+ return "Address size fault, level -1";
+ case 0b101011: /* When FEAT_LPA2 is implemented */
+ return "Translation fault, level -1";
+ case 0b110000:
+ return "TLB conflict abort";
+ case 0b110001: /* When FEAT_HAFDBS is implemented */
+ return "Unsupported atomic hardware update fault";
+ case 0b111101: /* When EL1 is capable of using AArch32 */
+ return "Section Domain fault, from an AArch32 stage 1 EL1&0 "
+ "translation regime using Short-descriptor translation "
+ "table format";
+ case 0b111110: /* When EL1 is capable of using AArch32 */
+ return "Page Domain fault, from an AArch32 stage 1 EL1&0 "
+ "translation regime using Short-descriptor translation "
+ "table format";
+ default:
+ if (fst & BIT(5))
+ return fault_status_leveled[BITS(4, 2, fst)];
+
+ return "Reserved";
+ }
+};
+
+static void decode_par(unsigned long par)
+{
+ u8 devmem_attr = BITS(63, 56, par);
+
+ if (par & 1) {
+ printf(" Translation aborted [9:8]: because of a fault in the %s%s\n",
+ stage_fault[BITS(9, 9, par)],
+ BITS(8, 8, par) ? " during a stage 1 translation table walk" : "");
+ printf(" Fault Status Code [6:1]: 0x%02lx (%s%s)\n", BITS(6, 1, par),
+ decode_fault_status(BITS(6, 1, par)),
+ decode_fault_status_level(BITS(6, 1, par)));
+ printf(" Failure [0]: 0x1\n");
+ } else {
+ if ((devmem_attr & 0xf0) && (devmem_attr & 0x0f)) {
+ printf(" Outer mem. attr. [63:60]: 0x%02lx (%s)\n", BITS(63, 60, par),
+ cache_attr[BITS(63, 60, par)]);
+ printf(" Inner mem. attr. [59:56]: 0x%02lx (%s)\n", BITS(59, 56, par),
+ cache_attr[BITS(59, 56, par)]);
+ } else if ((devmem_attr & 0b11110010) == 0) {
+ printf(" Memory attr. [63:56]: 0x%02x (%s)\n",
+ devmem_attr, decode_devmem_attr(devmem_attr));
+ if (devmem_attr & 1)
+ printf(" (XS == 0 if FEAT_XS implemented)\n");
+ } else if (devmem_attr == 0b01000000) {
+ printf(" Outer mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par),
+ "Non-Cacheable");
+ printf(" Inner mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par),
+ "Non-Cacheable");
+ printf(" (XS == 0 if FEAT_XS implemented)\n");
+ } else if (devmem_attr == 0b10100000) {
+ printf(" Outer mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par),
+ "Write-Through, No Write-Allocate");
+ printf(" Inner mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par),
+ "Write-Through");
+ printf(" (XS == 0 if FEAT_XS implemented)\n");
+ } else if (devmem_attr == 0b11110000) {
+ printf(" Outer mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par),
+ "Write-Back");
+ printf(" Inner mem. attr. [63:56]: 0x%02lx (%s)\n", BITS(63, 56, par),
+ "Write-Back, Write-Allocate, Non-transient");
+ printf(" (if FEAT_MTE2 implemented)\n");
+ }
+ printf(" Physical Address [51:12]: 0x%08lx\n", par & GENMASK(51, 12));
+ printf(" Non-Secure [9]: 0x%lx\n", BITS(9, 9, par));
+ printf(" Shareability attr. [8:7]: 0x%02lx (%s)\n", BITS(8, 7, par),
+ share_attr[BITS(8, 7, par)]);
+ printf(" Failure [0]: 0x0\n");
+ }
+}
+
+int mmuinfo_v8(void *_addr)
+{
+ unsigned long addr = (unsigned long)_addr;
+ unsigned long priv_read, priv_write;
+
+ switch (current_el()) {
+ case 3:
+ priv_read = at_par("s1e3r", addr);
+ priv_write = at_par("s1e3w", addr);
+ break;
+ case 2:
+ priv_read = at_par("s1e2r", addr);
+ priv_write = at_par("s1e2w", addr);
+ break;
+ case 1:
+ priv_read = at_par("s1e1r", addr);
+ priv_write = at_par("s1e1w", addr);
+ break;
+ case 0:
+ priv_read = at_par("s1e0r", addr);
+ priv_write = at_par("s1e0w", addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ printf("PAR result for 0x%08lx: \n", addr);
+ printf(" privileged read: 0x%08lx\n", priv_read);
+ decode_par(priv_read);
+ printf(" privileged write: 0x%08lx\n", priv_write);
+ decode_par(priv_write);
+
+ return 0;
+}
diff --git a/arch/arm/include/asm/mmuinfo.h b/arch/arm/include/asm/mmuinfo.h
index bc17bf8982ab..3005c388b967 100644
--- a/arch/arm/include/asm/mmuinfo.h
+++ b/arch/arm/include/asm/mmuinfo.h
@@ -4,5 +4,6 @@
#define __ARM_ASM_MMUINFO_H__
int mmuinfo_v7(void *addr);
+int mmuinfo_v8(void *addr);
#endif
diff --git a/arch/arm/include/asm/sysreg.h b/arch/arm/include/asm/sysreg.h
new file mode 100644
index 000000000000..7d567e08d8b7
--- /dev/null
+++ b/arch/arm/include/asm/sysreg.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Macros for accessing system registers with older binutils.
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas at arm.com>
+ */
+
+#ifndef __ASM_SYSREG_H
+#define __ASM_SYSREG_H
+
+#include <asm/system.h>
+#include <linux/stringify.h>
+
+/*
+ * Unlike read_cpuid, calls to read_sysreg are never expected to be
+ * optimized away or replaced with synthetic values.
+ */
+#define read_sysreg(r) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \
+ __val; \
+})
+
+/*
+ * The "Z" constraint normally means a zero immediate, but when combined with
+ * the "%x0" template means XZR.
+ */
+#define write_sysreg(v, r) do { \
+ u64 __val = (u64)(v); \
+ asm volatile("msr " __stringify(r) ", %x0" \
+ : : "rZ" (__val)); \
+} while (0)
+
+/*
+ * For registers without architectural names, or simply unsupported by
+ * GAS.
+ */
+#define read_sysreg_s(r) ({ \
+ u64 __val; \
+ asm volatile(__mrs_s("%0", r) : "=r" (__val)); \
+ __val; \
+})
+
+#define write_sysreg_s(v, r) do { \
+ u64 __val = (u64)(v); \
+ asm volatile(__msr_s(r, "%x0") : : "rZ" (__val)); \
+} while (0)
+
+/*
+ * Modify bits in a sysreg. Bits in the clear mask are zeroed, then bits in the
+ * set mask are set. Other bits are left as-is.
+ */
+#define sysreg_clear_set(sysreg, clear, set) do { \
+ u64 __scs_val = read_sysreg(sysreg); \
+ u64 __scs_new = (__scs_val & ~(u64)(clear)) | (set); \
+ if (__scs_new != __scs_val) \
+ write_sysreg(__scs_new, sysreg); \
+} while (0)
+
+#define sysreg_clear_set_s(sysreg, clear, set) do { \
+ u64 __scs_val = read_sysreg_s(sysreg); \
+ u64 __scs_new = (__scs_val & ~(u64)(clear)) | (set); \
+ if (__scs_new != __scs_val) \
+ write_sysreg_s(__scs_new, sysreg); \
+} while (0)
+
+#define read_sysreg_par() ({ \
+ u64 par; \
+ asm("dmb sy"); \
+ par = read_sysreg(par_el1); \
+ asm("dmb sy"); \
+ par; \
+})
+
+#endif /* __ASM_SYSREG_H */
diff --git a/commands/Kconfig b/commands/Kconfig
index bc697d52b730..3a43682b2b2c 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -201,13 +201,13 @@ config CMD_MEMINFO
config CMD_ARM_MMUINFO
bool "mmuinfo command"
- depends on CPU_V7
+ depends on CPU_V7 || CPU_V8
select MMUINFO
help
Say yes here to get a mmuinfo command to show some
- MMU and cache information using the cp15 registers.
+ MMU and cache information using the cp15/model-specific registers.
- Example:
+ Example for ARMv7:
PAR result for 0x00110000:
privileged read: 0x00110090
--
2.39.2
More information about the barebox
mailing list