aboutsummaryrefslogtreecommitdiffstats
path: root/target/openrisc/mmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/openrisc/mmu.c')
-rw-r--r--target/openrisc/mmu.c171
1 files changed, 171 insertions, 0 deletions
diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c
new file mode 100644
index 000000000..e561ef245
--- /dev/null
+++ b/target/openrisc/mmu.c
@@ -0,0 +1,171 @@
+/*
+ * OpenRISC MMU.
+ *
+ * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
+ * Zhizhou Zhang <etouzh@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/gdbstub.h"
+#include "qemu/host-utils.h"
+#include "hw/loader.h"
+
+static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
+ target_ulong address)
+{
+ *phys_addr = address;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+}
+
+static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
+ target_ulong addr, int need, bool super)
+{
+ int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
+ uint32_t imr = cpu->env.tlb.itlb[idx].mr;
+ uint32_t itr = cpu->env.tlb.itlb[idx].tr;
+ uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
+ uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
+ int right, match, valid;
+
+ /* If the ITLB and DTLB indexes map to the same page, we want to
+ load all permissions all at once. If the destination pages do
+ not match, zap the one we don't need. */
+ if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
+ if (need & PAGE_EXEC) {
+ dmr = dtr = 0;
+ } else {
+ imr = itr = 0;
+ }
+ }
+
+ /* Check if either of the entries matches the source address. */
+ match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
+ match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
+
+ /* Check if either of the entries is valid. */
+ valid = imr & 1 ? PAGE_EXEC : 0;
+ valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
+ valid &= match;
+
+ /* Collect the permissions from the entries. */
+ right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
+ right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
+ right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
+ right &= valid;
+
+ /* Note that above we validated that itr and dtr match on page.
+ So oring them together changes nothing without having to
+ check which one we needed. We also want to store to these
+ variables even on failure, as it avoids compiler warnings. */
+ *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
+ *prot = right;
+
+ qemu_log_mask(CPU_LOG_MMU,
+ "MMU lookup: need %d match %d valid %d right %d -> %s\n",
+ need, match, valid, right, (need & right) ? "OK" : "FAIL");
+
+ /* Check the collective permissions are present. */
+ if (likely(need & right)) {
+ return 0; /* success! */
+ }
+
+ /* Determine what kind of failure we have. */
+ if (need & valid) {
+ return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
+ } else {
+ return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
+ }
+}
+
+static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
+ int exception)
+{
+ CPUState *cs = CPU(cpu);
+
+ cs->exception_index = exception;
+ cpu->env.eear = address;
+ cpu->env.lock_addr = -1;
+}
+
+bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr)
+{
+ OpenRISCCPU *cpu = OPENRISC_CPU(cs);
+ int excp = EXCP_DPF;
+ int prot;
+ hwaddr phys_addr;
+
+ if (mmu_idx == MMU_NOMMU_IDX) {
+ /* The mmu is disabled; lookups never fail. */
+ get_phys_nommu(&phys_addr, &prot, addr);
+ excp = 0;
+ } else {
+ bool super = mmu_idx == MMU_SUPERVISOR_IDX;
+ int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
+ : access_type == MMU_DATA_STORE ? PAGE_WRITE
+ : PAGE_READ);
+ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
+ }
+
+ if (likely(excp == 0)) {
+ tlb_set_page(cs, addr & TARGET_PAGE_MASK,
+ phys_addr & TARGET_PAGE_MASK, prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+ return true;
+ }
+ if (probe) {
+ return false;
+ }
+
+ raise_mmu_exception(cpu, addr, excp);
+ cpu_loop_exit_restore(cs, retaddr);
+}
+
+hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+ OpenRISCCPU *cpu = OPENRISC_CPU(cs);
+ int prot, excp, sr = cpu->env.sr;
+ hwaddr phys_addr;
+
+ switch (sr & (SR_DME | SR_IME)) {
+ case SR_DME | SR_IME:
+ /* The mmu is definitely enabled. */
+ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
+ PAGE_EXEC | PAGE_READ | PAGE_WRITE,
+ (sr & SR_SM) != 0);
+ return excp ? -1 : phys_addr;
+
+ default:
+ /* The mmu is partially enabled, and we don't really have
+ a "real" access type. Begin by trying the mmu, but if
+ that fails try again without. */
+ excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
+ PAGE_EXEC | PAGE_READ | PAGE_WRITE,
+ (sr & SR_SM) != 0);
+ if (!excp) {
+ return phys_addr;
+ }
+ /* fallthru */
+
+ case 0:
+ /* The mmu is definitely disabled; lookups never fail. */
+ get_phys_nommu(&phys_addr, &prot, addr);
+ return phys_addr;
+ }
+}