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