|
|
76daa3 |
From 5eb7572177f7cd58bbd8556707fe6546ec46c1d2 Mon Sep 17 00:00:00 2001
|
|
|
76daa3 |
From: Laurent Vivier <lvivier@redhat.com>
|
|
|
76daa3 |
Date: Thu, 13 Jul 2017 16:36:27 +0200
|
|
|
76daa3 |
Subject: [PATCH 3/6] target/ppc: Implement ISA V3.00 radix page fault handler
|
|
|
76daa3 |
|
|
|
76daa3 |
RH-Author: Laurent Vivier <lvivier@redhat.com>
|
|
|
76daa3 |
Message-id: <20170713163630.24848-2-lvivier@redhat.com>
|
|
|
76daa3 |
Patchwork-id: 75768
|
|
|
76daa3 |
O-Subject: [RHEL-ALT-7.4 qemu-kvm PATCH 1/4] target/ppc: Implement ISA V3.00 radix page fault handler
|
|
|
76daa3 |
Bugzilla: 1470558
|
|
|
76daa3 |
RH-Acked-by: David Gibson <dgibson@redhat.com>
|
|
|
76daa3 |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
76daa3 |
RH-Acked-by: Suraj Jitindar Singh <sursingh@redhat.com>
|
|
|
76daa3 |
|
|
|
76daa3 |
From: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
|
|
|
76daa3 |
|
|
|
76daa3 |
ISA V3.00 introduced a new radix mmu model. Implement the page fault
|
|
|
76daa3 |
handler for this so we can run a tcg guest in radix mode and perform
|
|
|
76daa3 |
address translation correctly.
|
|
|
76daa3 |
|
|
|
76daa3 |
In real mode (mmu turned off) addresses are masked to remove the top
|
|
|
76daa3 |
4 bits and then are subject to partition scoped translation, since we only
|
|
|
76daa3 |
support pseries at this stage it is only necessary to perform the masking
|
|
|
76daa3 |
and then we're done.
|
|
|
76daa3 |
|
|
|
76daa3 |
In virtual mode (mmu turned on) address translation if performed as
|
|
|
76daa3 |
follows:
|
|
|
76daa3 |
|
|
|
76daa3 |
1. Use the quadrant to determine the fully qualified address.
|
|
|
76daa3 |
|
|
|
76daa3 |
The fully qualified address is defined as the combination of the effective
|
|
|
76daa3 |
address, the effective logical partition id (LPID) and the effective
|
|
|
76daa3 |
process id (PID). Based on the quadrant (EA63:62) we set the pid and lpid
|
|
|
76daa3 |
like so:
|
|
|
76daa3 |
|
|
|
76daa3 |
quadrant 0: lpid = LPIDR, pid = PIDR
|
|
|
76daa3 |
quadrant 1: HV only (not allowed in pseries)
|
|
|
76daa3 |
quadrant 2: HV only (not allowed in pseries)
|
|
|
76daa3 |
quadrant 3: lpid = LPIDR, pid = 0
|
|
|
76daa3 |
|
|
|
76daa3 |
If we can't get the fully qualified address we raise a segment interrupt.
|
|
|
76daa3 |
|
|
|
76daa3 |
2. Find the guest radix tree
|
|
|
76daa3 |
|
|
|
76daa3 |
We ask the virtual hypervisor for the partition table which was registered
|
|
|
76daa3 |
with H_REGISTER_PROC_TBL which points us to the process table in guest
|
|
|
76daa3 |
memory. We then index this table by pid to get the process table entry
|
|
|
76daa3 |
which points us to the appropriate radix tree to translate the address.
|
|
|
76daa3 |
|
|
|
76daa3 |
If the process table isn't big enough to contain an entry for the current
|
|
|
76daa3 |
pid then we raise a storage interrupt.
|
|
|
76daa3 |
|
|
|
76daa3 |
3. Walk the radix tree
|
|
|
76daa3 |
|
|
|
76daa3 |
Next we walk the radix tree where each level is a table of page directory
|
|
|
76daa3 |
entries indexed by some number of bits from the effective address, where
|
|
|
76daa3 |
the number of bits is determined by the table size. We continue to walk
|
|
|
76daa3 |
the tree (while entries are valid and the table is of minimum size) until
|
|
|
76daa3 |
we reach a table of page table entries, indicated by having the leaf bit
|
|
|
76daa3 |
set. The appropriate pte is then checked for sufficient access permissions,
|
|
|
76daa3 |
the reference and change bits are updated and the real address is
|
|
|
76daa3 |
calculated from the real page number bits of the pte and the low bits of
|
|
|
76daa3 |
the effective address.
|
|
|
76daa3 |
|
|
|
76daa3 |
If we can't find an entry or can't access the entry bacause of permissions
|
|
|
76daa3 |
then we raise a storage interrupt.
|
|
|
76daa3 |
|
|
|
76daa3 |
Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
|
|
|
76daa3 |
[dwg: Add missing parentheses to macro]
|
|
|
76daa3 |
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
|
|
|
76daa3 |
|
|
|
76daa3 |
(cherry picked from commit d5fee0bbe68d5e61e2d2beb5ff6de0b9c1cfd182)
|
|
|
76daa3 |
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
|
|
|
76daa3 |
|
|
|
76daa3 |
BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1470558
|
|
|
76daa3 |
BREW: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=13654102
|
|
|
76daa3 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
76daa3 |
---
|
|
|
76daa3 |
target/ppc/Makefile.objs | 1 +
|
|
|
76daa3 |
target/ppc/cpu.h | 2 +
|
|
|
76daa3 |
target/ppc/mmu-book3s-v3.c | 6 +-
|
|
|
76daa3 |
target/ppc/mmu-book3s-v3.h | 5 +
|
|
|
76daa3 |
target/ppc/mmu-radix64.c | 259 +++++++++++++++++++++++++++++++++++++++++++++
|
|
|
76daa3 |
target/ppc/mmu-radix64.h | 72 +++++++++++++
|
|
|
76daa3 |
6 files changed, 341 insertions(+), 4 deletions(-)
|
|
|
76daa3 |
create mode 100644 target/ppc/mmu-radix64.c
|
|
|
76daa3 |
create mode 100644 target/ppc/mmu-radix64.h
|
|
|
76daa3 |
|
|
|
76daa3 |
diff --git a/target/ppc/Makefile.objs b/target/ppc/Makefile.objs
|
|
|
76daa3 |
index f963777..f92ba67 100644
|
|
|
76daa3 |
--- a/target/ppc/Makefile.objs
|
|
|
76daa3 |
+++ b/target/ppc/Makefile.objs
|
|
|
76daa3 |
@@ -4,6 +4,7 @@ obj-y += translate.o
|
|
|
76daa3 |
ifeq ($(CONFIG_SOFTMMU),y)
|
|
|
76daa3 |
obj-y += machine.o mmu_helper.o mmu-hash32.o monitor.o arch_dump.o
|
|
|
76daa3 |
obj-$(TARGET_PPC64) += mmu-hash64.o mmu-book3s-v3.o compat.o
|
|
|
76daa3 |
+obj-$(TARGET_PPC64) += mmu-radix64.o
|
|
|
76daa3 |
endif
|
|
|
76daa3 |
obj-$(CONFIG_KVM) += kvm.o
|
|
|
76daa3 |
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
|
|
76daa3 |
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
|
|
|
76daa3 |
index cacdd0a..43bd257 100644
|
|
|
76daa3 |
--- a/target/ppc/cpu.h
|
|
|
76daa3 |
+++ b/target/ppc/cpu.h
|
|
|
76daa3 |
@@ -480,6 +480,8 @@ struct ppc_slb_t {
|
|
|
76daa3 |
#define DSISR_ISSTORE 0x02000000
|
|
|
76daa3 |
/* Not permitted by virtual page class key protection */
|
|
|
76daa3 |
#define DSISR_AMR 0x00200000
|
|
|
76daa3 |
+/* Unsupported Radix Tree Configuration */
|
|
|
76daa3 |
+#define DSISR_R_BADCONFIG 0x00080000
|
|
|
76daa3 |
|
|
|
76daa3 |
/* SRR1 error code fields */
|
|
|
76daa3 |
|
|
|
76daa3 |
diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c
|
|
|
76daa3 |
index 005c963..e7798b3 100644
|
|
|
76daa3 |
--- a/target/ppc/mmu-book3s-v3.c
|
|
|
76daa3 |
+++ b/target/ppc/mmu-book3s-v3.c
|
|
|
76daa3 |
@@ -22,15 +22,13 @@
|
|
|
76daa3 |
#include "cpu.h"
|
|
|
76daa3 |
#include "mmu-hash64.h"
|
|
|
76daa3 |
#include "mmu-book3s-v3.h"
|
|
|
76daa3 |
-#include "qemu/error-report.h"
|
|
|
76daa3 |
+#include "mmu-radix64.h"
|
|
|
76daa3 |
|
|
|
76daa3 |
int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
|
|
76daa3 |
int mmu_idx)
|
|
|
76daa3 |
{
|
|
|
76daa3 |
if (ppc64_radix_guest(cpu)) { /* Guest uses radix */
|
|
|
76daa3 |
- /* TODO - Unsupported */
|
|
|
76daa3 |
- error_report("Guest Radix Support Unimplemented");
|
|
|
76daa3 |
- exit(1);
|
|
|
76daa3 |
+ return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
|
|
|
76daa3 |
} else { /* Guest uses hash */
|
|
|
76daa3 |
return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx);
|
|
|
76daa3 |
}
|
|
|
76daa3 |
diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h
|
|
|
76daa3 |
index 636f6ab..56095da 100644
|
|
|
76daa3 |
--- a/target/ppc/mmu-book3s-v3.h
|
|
|
76daa3 |
+++ b/target/ppc/mmu-book3s-v3.h
|
|
|
76daa3 |
@@ -25,6 +25,11 @@
|
|
|
76daa3 |
/* Partition Table Entry Fields */
|
|
|
76daa3 |
#define PATBE1_GR 0x8000000000000000
|
|
|
76daa3 |
|
|
|
76daa3 |
+/* Process Table Entry */
|
|
|
76daa3 |
+struct prtb_entry {
|
|
|
76daa3 |
+ uint64_t prtbe0, prtbe1;
|
|
|
76daa3 |
+};
|
|
|
76daa3 |
+
|
|
|
76daa3 |
#ifdef TARGET_PPC64
|
|
|
76daa3 |
|
|
|
76daa3 |
static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu)
|
|
|
76daa3 |
diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c
|
|
|
76daa3 |
new file mode 100644
|
|
|
76daa3 |
index 0000000..de18c0b
|
|
|
76daa3 |
--- /dev/null
|
|
|
76daa3 |
+++ b/target/ppc/mmu-radix64.c
|
|
|
76daa3 |
@@ -0,0 +1,259 @@
|
|
|
76daa3 |
+/*
|
|
|
76daa3 |
+ * PowerPC Radix MMU mulation helpers for QEMU.
|
|
|
76daa3 |
+ *
|
|
|
76daa3 |
+ * Copyright (c) 2016 Suraj Jitindar Singh, IBM Corporation
|
|
|
76daa3 |
+ *
|
|
|
76daa3 |
+ * This library is free software; you can redistribute it and/or
|
|
|
76daa3 |
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
76daa3 |
+ * License as published by the Free Software Foundation; either
|
|
|
76daa3 |
+ * version 2 of the License, or (at your option) any later version.
|
|
|
76daa3 |
+ *
|
|
|
76daa3 |
+ * This library is distributed in the hope that it will be useful,
|
|
|
76daa3 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
76daa3 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
76daa3 |
+ * Lesser General Public License for more details.
|
|
|
76daa3 |
+ *
|
|
|
76daa3 |
+ * You should have received a copy of the GNU Lesser General Public
|
|
|
76daa3 |
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
76daa3 |
+ */
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#include "qemu/osdep.h"
|
|
|
76daa3 |
+#include "qapi/error.h"
|
|
|
76daa3 |
+#include "cpu.h"
|
|
|
76daa3 |
+#include "exec/exec-all.h"
|
|
|
76daa3 |
+#include "exec/helper-proto.h"
|
|
|
76daa3 |
+#include "qemu/error-report.h"
|
|
|
76daa3 |
+#include "sysemu/kvm.h"
|
|
|
76daa3 |
+#include "kvm_ppc.h"
|
|
|
76daa3 |
+#include "exec/log.h"
|
|
|
76daa3 |
+#include "mmu-radix64.h"
|
|
|
76daa3 |
+#include "mmu-book3s-v3.h"
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr,
|
|
|
76daa3 |
+ uint64_t *lpid, uint64_t *pid)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ /* We don't have HV support yet and shouldn't get here with it set anyway */
|
|
|
76daa3 |
+ assert(!msr_hv);
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (!msr_hv) { /* !MSR[HV] -> Guest */
|
|
|
76daa3 |
+ switch (eaddr & R_EADDR_QUADRANT) {
|
|
|
76daa3 |
+ case R_EADDR_QUADRANT0: /* Guest application */
|
|
|
76daa3 |
+ *lpid = env->spr[SPR_LPIDR];
|
|
|
76daa3 |
+ *pid = env->spr[SPR_BOOKS_PID];
|
|
|
76daa3 |
+ break;
|
|
|
76daa3 |
+ case R_EADDR_QUADRANT1: /* Illegal */
|
|
|
76daa3 |
+ case R_EADDR_QUADRANT2:
|
|
|
76daa3 |
+ return false;
|
|
|
76daa3 |
+ case R_EADDR_QUADRANT3: /* Guest OS */
|
|
|
76daa3 |
+ *lpid = env->spr[SPR_LPIDR];
|
|
|
76daa3 |
+ *pid = 0; /* pid set to 0 -> addresses guest operating system */
|
|
|
76daa3 |
+ break;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ return true;
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static void ppc_radix64_raise_segi(PowerPCCPU *cpu, int rwx, vaddr eaddr)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUState *cs = CPU(cpu);
|
|
|
76daa3 |
+ CPUPPCState *env = &cpu->env;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (rwx == 2) { /* Instruction Segment Interrupt */
|
|
|
76daa3 |
+ cs->exception_index = POWERPC_EXCP_ISEG;
|
|
|
76daa3 |
+ } else { /* Data Segment Interrupt */
|
|
|
76daa3 |
+ cs->exception_index = POWERPC_EXCP_DSEG;
|
|
|
76daa3 |
+ env->spr[SPR_DAR] = eaddr;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+ env->error_code = 0;
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static void ppc_radix64_raise_si(PowerPCCPU *cpu, int rwx, vaddr eaddr,
|
|
|
76daa3 |
+ uint32_t cause)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUState *cs = CPU(cpu);
|
|
|
76daa3 |
+ CPUPPCState *env = &cpu->env;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (rwx == 2) { /* Instruction Storage Interrupt */
|
|
|
76daa3 |
+ cs->exception_index = POWERPC_EXCP_ISI;
|
|
|
76daa3 |
+ env->error_code = cause;
|
|
|
76daa3 |
+ } else { /* Data Storage Interrupt */
|
|
|
76daa3 |
+ cs->exception_index = POWERPC_EXCP_DSI;
|
|
|
76daa3 |
+ if (rwx == 1) { /* Write -> Store */
|
|
|
76daa3 |
+ cause |= DSISR_ISSTORE;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+ env->spr[SPR_DSISR] = cause;
|
|
|
76daa3 |
+ env->spr[SPR_DAR] = eaddr;
|
|
|
76daa3 |
+ env->error_code = 0;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static bool ppc_radix64_check_prot(PowerPCCPU *cpu, int rwx, uint64_t pte,
|
|
|
76daa3 |
+ int *fault_cause, int *prot)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUPPCState *env = &cpu->env;
|
|
|
76daa3 |
+ const int need_prot[] = { PAGE_READ, PAGE_WRITE, PAGE_EXEC };
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Check Page Attributes (pte58:59) */
|
|
|
76daa3 |
+ if (((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO) && (rwx == 2)) {
|
|
|
76daa3 |
+ /*
|
|
|
76daa3 |
+ * Radix PTE entries with the non-idempotent I/O attribute are treated
|
|
|
76daa3 |
+ * as guarded storage
|
|
|
76daa3 |
+ */
|
|
|
76daa3 |
+ *fault_cause |= SRR1_NOEXEC_GUARD;
|
|
|
76daa3 |
+ return true;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Determine permissions allowed by Encoded Access Authority */
|
|
|
76daa3 |
+ if ((pte & R_PTE_EAA_PRIV) && msr_pr) { /* Insufficient Privilege */
|
|
|
76daa3 |
+ *prot = 0;
|
|
|
76daa3 |
+ } else if (msr_pr || (pte & R_PTE_EAA_PRIV)) {
|
|
|
76daa3 |
+ *prot = ppc_radix64_get_prot_eaa(pte);
|
|
|
76daa3 |
+ } else { /* !msr_pr && !(pte & R_PTE_EAA_PRIV) */
|
|
|
76daa3 |
+ *prot = ppc_radix64_get_prot_eaa(pte);
|
|
|
76daa3 |
+ *prot &= ppc_radix64_get_prot_amr(cpu); /* Least combined permissions */
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Check if requested access type is allowed */
|
|
|
76daa3 |
+ if (need_prot[rwx] & ~(*prot)) { /* Page Protected for that Access */
|
|
|
76daa3 |
+ *fault_cause |= DSISR_PROTFAULT;
|
|
|
76daa3 |
+ return true;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ return false;
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static void ppc_radix64_set_rc(PowerPCCPU *cpu, int rwx, uint64_t pte,
|
|
|
76daa3 |
+ hwaddr pte_addr, int *prot)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUState *cs = CPU(cpu);
|
|
|
76daa3 |
+ uint64_t npte;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ npte = pte | R_PTE_R; /* Always set reference bit */
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (rwx == 1) { /* Store/Write */
|
|
|
76daa3 |
+ npte |= R_PTE_C; /* Set change bit */
|
|
|
76daa3 |
+ } else {
|
|
|
76daa3 |
+ /*
|
|
|
76daa3 |
+ * Treat the page as read-only for now, so that a later write
|
|
|
76daa3 |
+ * will pass through this function again to set the C bit.
|
|
|
76daa3 |
+ */
|
|
|
76daa3 |
+ *prot &= ~PAGE_WRITE;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (pte ^ npte) { /* If pte has changed then write it back */
|
|
|
76daa3 |
+ stq_phys(cs->as, pte_addr, npte);
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, int rwx, vaddr eaddr,
|
|
|
76daa3 |
+ uint64_t base_addr, uint64_t nls,
|
|
|
76daa3 |
+ hwaddr *raddr, int *psize,
|
|
|
76daa3 |
+ int *fault_cause, int *prot,
|
|
|
76daa3 |
+ hwaddr *pte_addr)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUState *cs = CPU(cpu);
|
|
|
76daa3 |
+ uint64_t index, pde;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (nls < 5) { /* Directory maps less than 2**5 entries */
|
|
|
76daa3 |
+ *fault_cause |= DSISR_R_BADCONFIG;
|
|
|
76daa3 |
+ return 0;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Read page <directory/table> entry from guest address space */
|
|
|
76daa3 |
+ index = eaddr >> (*psize - nls); /* Shift */
|
|
|
76daa3 |
+ index &= ((1UL << nls) - 1); /* Mask */
|
|
|
76daa3 |
+ pde = ldq_phys(cs->as, base_addr + (index * sizeof(pde)));
|
|
|
76daa3 |
+ if (!(pde & R_PTE_VALID)) { /* Invalid Entry */
|
|
|
76daa3 |
+ *fault_cause |= DSISR_NOPTE;
|
|
|
76daa3 |
+ return 0;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ *psize -= nls;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Check if Leaf Entry -> Page Table Entry -> Stop the Search */
|
|
|
76daa3 |
+ if (pde & R_PTE_LEAF) {
|
|
|
76daa3 |
+ uint64_t rpn = pde & R_PTE_RPN;
|
|
|
76daa3 |
+ uint64_t mask = (1UL << *psize) - 1;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ if (ppc_radix64_check_prot(cpu, rwx, pde, fault_cause, prot)) {
|
|
|
76daa3 |
+ return 0; /* Protection Denied Access */
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Or high bits of rpn and low bits to ea to form whole real addr */
|
|
|
76daa3 |
+ *raddr = (rpn & ~mask) | (eaddr & mask);
|
|
|
76daa3 |
+ *pte_addr = base_addr + (index * sizeof(pde));
|
|
|
76daa3 |
+ return pde;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Next Level of Radix Tree */
|
|
|
76daa3 |
+ return ppc_radix64_walk_tree(cpu, rwx, eaddr, pde & R_PDE_NLB,
|
|
|
76daa3 |
+ pde & R_PDE_NLS, raddr, psize,
|
|
|
76daa3 |
+ fault_cause, prot, pte_addr);
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
|
|
76daa3 |
+ int mmu_idx)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUState *cs = CPU(cpu);
|
|
|
76daa3 |
+ CPUPPCState *env = &cpu->env;
|
|
|
76daa3 |
+ PPCVirtualHypervisorClass *vhc =
|
|
|
76daa3 |
+ PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
|
|
|
76daa3 |
+ hwaddr raddr, pte_addr;
|
|
|
76daa3 |
+ uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte;
|
|
|
76daa3 |
+ int page_size, prot, fault_cause = 0;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ assert((rwx == 0) || (rwx == 1) || (rwx == 2));
|
|
|
76daa3 |
+ assert(!msr_hv); /* For now there is no Radix PowerNV Support */
|
|
|
76daa3 |
+ assert(cpu->vhyp);
|
|
|
76daa3 |
+ assert(ppc64_use_proc_tbl(cpu));
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Real Mode Access */
|
|
|
76daa3 |
+ if (((rwx == 2) && (msr_ir == 0)) || ((rwx != 2) && (msr_dr == 0))) {
|
|
|
76daa3 |
+ /* In real mode top 4 effective addr bits (mostly) ignored */
|
|
|
76daa3 |
+ raddr = eaddr & 0x0FFFFFFFFFFFFFFFULL;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
|
|
76daa3 |
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC, mmu_idx,
|
|
|
76daa3 |
+ TARGET_PAGE_SIZE);
|
|
|
76daa3 |
+ return 0;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Virtual Mode Access - get the fully qualified address */
|
|
|
76daa3 |
+ if (!ppc_radix64_get_fully_qualified_addr(env, eaddr, &lpid, &pid)) {
|
|
|
76daa3 |
+ ppc_radix64_raise_segi(cpu, rwx, eaddr);
|
|
|
76daa3 |
+ return 1;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Get Process Table */
|
|
|
76daa3 |
+ patbe = vhc->get_patbe(cpu->vhyp);
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Index Process Table by PID to Find Corresponding Process Table Entry */
|
|
|
76daa3 |
+ offset = pid * sizeof(struct prtb_entry);
|
|
|
76daa3 |
+ size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12);
|
|
|
76daa3 |
+ if (offset >= size) {
|
|
|
76daa3 |
+ /* offset exceeds size of the process table */
|
|
|
76daa3 |
+ ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE);
|
|
|
76daa3 |
+ return 1;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+ prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset);
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Walk Radix Tree from Process Table Entry to Convert EA to RA */
|
|
|
76daa3 |
+ page_size = PRTBE_R_GET_RTS(prtbe0);
|
|
|
76daa3 |
+ pte = ppc_radix64_walk_tree(cpu, rwx, eaddr & R_EADDR_MASK,
|
|
|
76daa3 |
+ prtbe0 & PRTBE_R_RPDB, prtbe0 & PRTBE_R_RPDS,
|
|
|
76daa3 |
+ &raddr, &page_size, &fault_cause, &prot,
|
|
|
76daa3 |
+ &pte_addr);
|
|
|
76daa3 |
+ if (!pte) {
|
|
|
76daa3 |
+ ppc_radix64_raise_si(cpu, rwx, eaddr, fault_cause);
|
|
|
76daa3 |
+ return 1;
|
|
|
76daa3 |
+ }
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ /* Update Reference and Change Bits */
|
|
|
76daa3 |
+ ppc_radix64_set_rc(cpu, rwx, pte, pte_addr, &prot;;
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ tlb_set_page(cs, eaddr & TARGET_PAGE_MASK, raddr & TARGET_PAGE_MASK,
|
|
|
76daa3 |
+ prot, mmu_idx, 1UL << page_size);
|
|
|
76daa3 |
+ return 1;
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h
|
|
|
76daa3 |
new file mode 100644
|
|
|
76daa3 |
index 0000000..1d5c7cf
|
|
|
76daa3 |
--- /dev/null
|
|
|
76daa3 |
+++ b/target/ppc/mmu-radix64.h
|
|
|
76daa3 |
@@ -0,0 +1,72 @@
|
|
|
76daa3 |
+#ifndef MMU_RADIX64_H
|
|
|
76daa3 |
+#define MMU_RADIX64_H
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#ifndef CONFIG_USER_ONLY
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+/* Radix Quadrants */
|
|
|
76daa3 |
+#define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF
|
|
|
76daa3 |
+#define R_EADDR_QUADRANT 0xC000000000000000
|
|
|
76daa3 |
+#define R_EADDR_QUADRANT0 0x0000000000000000
|
|
|
76daa3 |
+#define R_EADDR_QUADRANT1 0x4000000000000000
|
|
|
76daa3 |
+#define R_EADDR_QUADRANT2 0x8000000000000000
|
|
|
76daa3 |
+#define R_EADDR_QUADRANT3 0xC000000000000000
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+/* Radix Partition Table Entry Fields */
|
|
|
76daa3 |
+#define PATBE1_R_PRTB 0x0FFFFFFFFFFFF000
|
|
|
76daa3 |
+#define PATBE1_R_PRTS 0x000000000000001F
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+/* Radix Process Table Entry Fields */
|
|
|
76daa3 |
+#define PRTBE_R_GET_RTS(rts) \
|
|
|
76daa3 |
+ ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31)
|
|
|
76daa3 |
+#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00
|
|
|
76daa3 |
+#define PRTBE_R_RPDS 0x000000000000001F
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+/* Radix Page Directory/Table Entry Fields */
|
|
|
76daa3 |
+#define R_PTE_VALID 0x8000000000000000
|
|
|
76daa3 |
+#define R_PTE_LEAF 0x4000000000000000
|
|
|
76daa3 |
+#define R_PTE_SW0 0x2000000000000000
|
|
|
76daa3 |
+#define R_PTE_RPN 0x01FFFFFFFFFFF000
|
|
|
76daa3 |
+#define R_PTE_SW1 0x0000000000000E00
|
|
|
76daa3 |
+#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7))
|
|
|
76daa3 |
+#define R_PTE_R 0x0000000000000100
|
|
|
76daa3 |
+#define R_PTE_C 0x0000000000000080
|
|
|
76daa3 |
+#define R_PTE_ATT 0x0000000000000030
|
|
|
76daa3 |
+#define R_PTE_ATT_NORMAL 0x0000000000000000
|
|
|
76daa3 |
+#define R_PTE_ATT_SAO 0x0000000000000010
|
|
|
76daa3 |
+#define R_PTE_ATT_NI_IO 0x0000000000000020
|
|
|
76daa3 |
+#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030
|
|
|
76daa3 |
+#define R_PTE_EAA_PRIV 0x0000000000000008
|
|
|
76daa3 |
+#define R_PTE_EAA_R 0x0000000000000004
|
|
|
76daa3 |
+#define R_PTE_EAA_RW 0x0000000000000002
|
|
|
76daa3 |
+#define R_PTE_EAA_X 0x0000000000000001
|
|
|
76daa3 |
+#define R_PDE_NLB PRTBE_R_RPDB
|
|
|
76daa3 |
+#define R_PDE_NLS PRTBE_R_RPDS
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#ifdef TARGET_PPC64
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx,
|
|
|
76daa3 |
+ int mmu_idx);
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static inline int ppc_radix64_get_prot_eaa(uint64_t pte)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ return (pte & R_PTE_EAA_R ? PAGE_READ : 0) |
|
|
|
76daa3 |
+ (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) |
|
|
|
76daa3 |
+ (pte & R_PTE_EAA_X ? PAGE_EXEC : 0);
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+static inline int ppc_radix64_get_prot_amr(PowerPCCPU *cpu)
|
|
|
76daa3 |
+{
|
|
|
76daa3 |
+ CPUPPCState *env = &cpu->env;
|
|
|
76daa3 |
+ int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */
|
|
|
76daa3 |
+ int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+ return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */
|
|
|
76daa3 |
+ (amr & 0x1 ? 0 : PAGE_READ) |
|
|
|
76daa3 |
+ (iamr & 0x1 ? 0 : PAGE_EXEC);
|
|
|
76daa3 |
+}
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#endif /* TARGET_PPC64 */
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#endif /* CONFIG_USER_ONLY */
|
|
|
76daa3 |
+
|
|
|
76daa3 |
+#endif /* MMU_RADIX64_H */
|
|
|
76daa3 |
--
|
|
|
76daa3 |
1.8.3.1
|
|
|
76daa3 |
|