xref: /openbmc/qemu/target/openrisc/mmu.c (revision 7e6055c9)
1 /*
2  * OpenRISC MMU.
3  *
4  * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
5  *                         Zhizhou Zhang <etouzh@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "exec/exec-all.h"
24 #include "exec/gdbstub.h"
25 #include "qemu/host-utils.h"
26 #include "hw/loader.h"
27 
28 static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
29                                   target_ulong address)
30 {
31     *phys_addr = address;
32     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
33 }
34 
35 static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
36                         target_ulong addr, int need, bool super)
37 {
38     int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
39     uint32_t imr = cpu->env.tlb.itlb[idx].mr;
40     uint32_t itr = cpu->env.tlb.itlb[idx].tr;
41     uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
42     uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
43     int right, match, valid;
44 
45     /* If the ITLB and DTLB indexes map to the same page, we want to
46        load all permissions all at once.  If the destination pages do
47        not match, zap the one we don't need.  */
48     if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
49         if (need & PAGE_EXEC) {
50             dmr = dtr = 0;
51         } else {
52             imr = itr = 0;
53         }
54     }
55 
56     /* Check if either of the entries matches the source address.  */
57     match  = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
58     match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
59 
60     /* Check if either of the entries is valid.  */
61     valid  = imr & 1 ? PAGE_EXEC : 0;
62     valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
63     valid &= match;
64 
65     /* Collect the permissions from the entries.  */
66     right  = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
67     right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
68     right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
69     right &= valid;
70 
71     /* Note that above we validated that itr and dtr match on page.
72        So oring them together changes nothing without having to
73        check which one we needed.  We also want to store to these
74        variables even on failure, as it avoids compiler warnings.  */
75     *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
76     *prot = right;
77 
78     qemu_log_mask(CPU_LOG_MMU,
79                   "MMU lookup: need %d match %d valid %d right %d -> %s\n",
80                   need, match, valid, right, (need & right) ? "OK" : "FAIL");
81 
82     /* Check the collective permissions are present.  */
83     if (likely(need & right)) {
84         return 0;  /* success! */
85     }
86 
87     /* Determine what kind of failure we have.  */
88     if (need & valid) {
89         return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
90     } else {
91         return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
92     }
93 }
94 
95 static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
96                                 int exception)
97 {
98     CPUState *cs = CPU(cpu);
99 
100     cs->exception_index = exception;
101     cpu->env.eear = address;
102     cpu->env.lock_addr = -1;
103 }
104 
105 bool openrisc_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
106                            MMUAccessType access_type, int mmu_idx,
107                            bool probe, uintptr_t retaddr)
108 {
109     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
110     int excp = EXCP_DPF;
111     int prot;
112     hwaddr phys_addr;
113 
114     if (mmu_idx == MMU_NOMMU_IDX) {
115         /* The mmu is disabled; lookups never fail.  */
116         get_phys_nommu(&phys_addr, &prot, addr);
117         excp = 0;
118     } else {
119         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
120         int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
121                     : access_type == MMU_DATA_STORE ? PAGE_WRITE
122                     : PAGE_READ);
123         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
124     }
125 
126     if (likely(excp == 0)) {
127         tlb_set_page(cs, addr & TARGET_PAGE_MASK,
128                      phys_addr & TARGET_PAGE_MASK, prot,
129                      mmu_idx, TARGET_PAGE_SIZE);
130         return true;
131     }
132     if (probe) {
133         return false;
134     }
135 
136     raise_mmu_exception(cpu, addr, excp);
137     cpu_loop_exit_restore(cs, retaddr);
138 }
139 
140 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
141 {
142     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
143     int prot, excp, sr = cpu->env.sr;
144     hwaddr phys_addr;
145 
146     switch (sr & (SR_DME | SR_IME)) {
147     case SR_DME | SR_IME:
148         /* The mmu is definitely enabled.  */
149         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
150                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
151                             (sr & SR_SM) != 0);
152         return excp ? -1 : phys_addr;
153 
154     default:
155         /* The mmu is partially enabled, and we don't really have
156            a "real" access type.  Begin by trying the mmu, but if
157            that fails try again without.  */
158         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
159                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
160                             (sr & SR_SM) != 0);
161         if (!excp) {
162             return phys_addr;
163         }
164         /* fallthru */
165 
166     case 0:
167         /* The mmu is definitely disabled; lookups never fail.  */
168         get_phys_nommu(&phys_addr, &prot, addr);
169         return phys_addr;
170     }
171 }
172