xref: /openbmc/qemu/target/openrisc/mmu.c (revision f6a51c84)
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 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 int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
33                                 hwaddr *physical,
34                                 int *prot, target_ulong address, int rw)
35 {
36     *physical = address;
37     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
38     return TLBRET_MATCH;
39 }
40 
41 int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
42                                hwaddr *physical,
43                                int *prot, target_ulong address, int rw)
44 {
45     int vpn = address >> TARGET_PAGE_BITS;
46     int idx = vpn & ITLB_MASK;
47     int right = 0;
48 
49     if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
50         return TLBRET_NOMATCH;
51     }
52     if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) {
53         return TLBRET_INVALID;
54     }
55 
56     if (cpu->env.sr & SR_SM) { /* supervisor mode */
57         if (cpu->env.tlb->itlb[0][idx].tr & SXE) {
58             right |= PAGE_EXEC;
59         }
60     } else {
61         if (cpu->env.tlb->itlb[0][idx].tr & UXE) {
62             right |= PAGE_EXEC;
63         }
64     }
65 
66     if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
67         return TLBRET_BADADDR;
68     }
69 
70     *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) |
71                 (address & (TARGET_PAGE_SIZE-1));
72     *prot = right;
73     return TLBRET_MATCH;
74 }
75 
76 int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
77                                hwaddr *physical,
78                                int *prot, target_ulong address, int rw)
79 {
80     int vpn = address >> TARGET_PAGE_BITS;
81     int idx = vpn & DTLB_MASK;
82     int right = 0;
83 
84     if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
85         return TLBRET_NOMATCH;
86     }
87     if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) {
88         return TLBRET_INVALID;
89     }
90 
91     if (cpu->env.sr & SR_SM) { /* supervisor mode */
92         if (cpu->env.tlb->dtlb[0][idx].tr & SRE) {
93             right |= PAGE_READ;
94         }
95         if (cpu->env.tlb->dtlb[0][idx].tr & SWE) {
96             right |= PAGE_WRITE;
97         }
98     } else {
99         if (cpu->env.tlb->dtlb[0][idx].tr & URE) {
100             right |= PAGE_READ;
101         }
102         if (cpu->env.tlb->dtlb[0][idx].tr & UWE) {
103             right |= PAGE_WRITE;
104         }
105     }
106 
107     if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
108         return TLBRET_BADADDR;
109     }
110     if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
111         return TLBRET_BADADDR;
112     }
113 
114     *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) |
115                 (address & (TARGET_PAGE_SIZE-1));
116     *prot = right;
117     return TLBRET_MATCH;
118 }
119 
120 static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu,
121                                       hwaddr *physical,
122                                       int *prot, target_ulong address,
123                                       int rw)
124 {
125     int ret = TLBRET_MATCH;
126 
127     if (rw == 2) {    /* ITLB */
128        *physical = 0;
129         ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical,
130                                                           prot, address, rw);
131     } else {          /* DTLB */
132         ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical,
133                                                           prot, address, rw);
134     }
135 
136     return ret;
137 }
138 #endif
139 
140 static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu,
141                                              target_ulong address,
142                                              int rw, int tlb_error)
143 {
144     CPUState *cs = CPU(cpu);
145     int exception = 0;
146 
147     switch (tlb_error) {
148     default:
149         if (rw == 2) {
150             exception = EXCP_IPF;
151         } else {
152             exception = EXCP_DPF;
153         }
154         break;
155 #ifndef CONFIG_USER_ONLY
156     case TLBRET_BADADDR:
157         if (rw == 2) {
158             exception = EXCP_IPF;
159         } else {
160             exception = EXCP_DPF;
161         }
162         break;
163     case TLBRET_INVALID:
164     case TLBRET_NOMATCH:
165         /* No TLB match for a mapped address */
166         if (rw == 2) {
167             exception = EXCP_ITLBMISS;
168         } else {
169             exception = EXCP_DTLBMISS;
170         }
171         break;
172 #endif
173     }
174 
175     cs->exception_index = exception;
176     cpu->env.eear = address;
177 }
178 
179 #ifndef CONFIG_USER_ONLY
180 int openrisc_cpu_handle_mmu_fault(CPUState *cs,
181                                   vaddr address, int rw, int mmu_idx)
182 {
183     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
184     int ret = 0;
185     hwaddr physical = 0;
186     int prot = 0;
187 
188     ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot,
189                                      address, rw);
190 
191     if (ret == TLBRET_MATCH) {
192         tlb_set_page(cs, address & TARGET_PAGE_MASK,
193                      physical & TARGET_PAGE_MASK, prot,
194                      mmu_idx, TARGET_PAGE_SIZE);
195         ret = 0;
196     } else if (ret < 0) {
197         cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
198         ret = 1;
199     }
200 
201     return ret;
202 }
203 #else
204 int openrisc_cpu_handle_mmu_fault(CPUState *cs,
205                                   vaddr address, int rw, int mmu_idx)
206 {
207     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
208     int ret = 0;
209 
210     cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
211     ret = 1;
212 
213     return ret;
214 }
215 #endif
216 
217 #ifndef CONFIG_USER_ONLY
218 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
219 {
220     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
221     hwaddr phys_addr;
222     int prot;
223 
224     if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) {
225         return -1;
226     }
227 
228     return phys_addr;
229 }
230 
231 void cpu_openrisc_mmu_init(OpenRISCCPU *cpu)
232 {
233     cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
234 
235     cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
236     cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
237 }
238 #endif
239