xref: /openbmc/qemu/target/openrisc/mmu.c (revision 5ee5c14c)
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 "qemu-common.h"
25 #include "exec/gdbstub.h"
26 #include "qemu/host-utils.h"
27 #ifndef CONFIG_USER_ONLY
28 #include "hw/loader.h"
29 #endif
30 
31 #ifndef CONFIG_USER_ONLY
32 static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
33                                   target_ulong address)
34 {
35     *phys_addr = address;
36     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
37 }
38 
39 static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
40                         target_ulong addr, int need, bool super)
41 {
42     int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
43     uint32_t imr = cpu->env.tlb.itlb[idx].mr;
44     uint32_t itr = cpu->env.tlb.itlb[idx].tr;
45     uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
46     uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
47     int right, match, valid;
48 
49     /* If the ITLB and DTLB indexes map to the same page, we want to
50        load all permissions all at once.  If the destination pages do
51        not match, zap the one we don't need.  */
52     if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
53         if (need & PAGE_EXEC) {
54             dmr = dtr = 0;
55         } else {
56             imr = itr = 0;
57         }
58     }
59 
60     /* Check if either of the entries matches the source address.  */
61     match  = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
62     match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
63 
64     /* Check if either of the entries is valid.  */
65     valid  = imr & 1 ? PAGE_EXEC : 0;
66     valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
67     valid &= match;
68 
69     /* Collect the permissions from the entries.  */
70     right  = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
71     right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
72     right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
73     right &= valid;
74 
75     /* Note that above we validated that itr and dtr match on page.
76        So oring them together changes nothing without having to
77        check which one we needed.  We also want to store to these
78        variables even on failure, as it avoids compiler warnings.  */
79     *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
80     *prot = right;
81 
82     qemu_log_mask(CPU_LOG_MMU,
83                   "MMU lookup: need %d match %d valid %d right %d -> %s\n",
84                   need, match, valid, right, (need & right) ? "OK" : "FAIL");
85 
86     /* Check the collective permissions are present.  */
87     if (likely(need & right)) {
88         return 0;  /* success! */
89     }
90 
91     /* Determine what kind of failure we have.  */
92     if (need & valid) {
93         return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
94     } else {
95         return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
96     }
97 }
98 #endif
99 
100 static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
101                                 int exception)
102 {
103     CPUState *cs = CPU(cpu);
104 
105     cs->exception_index = exception;
106     cpu->env.eear = address;
107     cpu->env.lock_addr = -1;
108 }
109 
110 int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
111                                   int rw, int mmu_idx)
112 {
113 #ifdef CONFIG_USER_ONLY
114     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
115     raise_mmu_exception(cpu, address, EXCP_DPF);
116     return 1;
117 #else
118     g_assert_not_reached();
119 #endif
120 }
121 
122 #ifndef CONFIG_USER_ONLY
123 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
124 {
125     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
126     int prot, excp, sr = cpu->env.sr;
127     hwaddr phys_addr;
128 
129     switch (sr & (SR_DME | SR_IME)) {
130     case SR_DME | SR_IME:
131         /* The mmu is definitely enabled.  */
132         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
133                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
134                             (sr & SR_SM) != 0);
135         return excp ? -1 : phys_addr;
136 
137     default:
138         /* The mmu is partially enabled, and we don't really have
139            a "real" access type.  Begin by trying the mmu, but if
140            that fails try again without.  */
141         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
142                             PAGE_EXEC | PAGE_READ | PAGE_WRITE,
143                             (sr & SR_SM) != 0);
144         if (!excp) {
145             return phys_addr;
146         }
147         /* fallthru */
148 
149     case 0:
150         /* The mmu is definitely disabled; lookups never fail.  */
151         get_phys_nommu(&phys_addr, &prot, addr);
152         return phys_addr;
153     }
154 }
155 
156 void tlb_fill(CPUState *cs, target_ulong addr, int size,
157               MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
158 {
159     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
160     int prot, excp;
161     hwaddr phys_addr;
162 
163     if (mmu_idx == MMU_NOMMU_IDX) {
164         /* The mmu is disabled; lookups never fail.  */
165         get_phys_nommu(&phys_addr, &prot, addr);
166         excp = 0;
167     } else {
168         bool super = mmu_idx == MMU_SUPERVISOR_IDX;
169         int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
170                     : access_type == MMU_DATA_STORE ? PAGE_WRITE
171                     : PAGE_READ);
172         excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
173     }
174 
175     if (unlikely(excp)) {
176         raise_mmu_exception(cpu, addr, excp);
177         cpu_loop_exit_restore(cs, retaddr);
178     }
179 
180     tlb_set_page(cs, addr & TARGET_PAGE_MASK,
181                  phys_addr & TARGET_PAGE_MASK, prot,
182                  mmu_idx, TARGET_PAGE_SIZE);
183 }
184 #endif
185