]>
Commit | Line | Data |
---|---|---|
4f23a1e6 GX |
1 | /* |
2 | * Softmmu related functions | |
3 | * | |
4 | * Copyright (C) 2010-2012 Guan Xuetao | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation, or any later version. | |
9 | * See the COPYING file in the top-level directory. | |
10 | */ | |
11 | #ifdef CONFIG_USER_ONLY | |
12 | #error This file only exist under softmmu circumstance | |
13 | #endif | |
14 | ||
5af98cc5 | 15 | #include "qemu/osdep.h" |
a9c94277 | 16 | #include "cpu.h" |
63c91552 | 17 | #include "exec/exec-all.h" |
0ac241bc | 18 | #include "qemu/error-report.h" |
4f23a1e6 | 19 | |
f3ccc323 GX |
20 | #undef DEBUG_UC32 |
21 | ||
22 | #ifdef DEBUG_UC32 | |
23 | #define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__) | |
24 | #else | |
25 | #define DPRINTF(fmt, ...) do {} while (0) | |
26 | #endif | |
27 | ||
28 | #define SUPERPAGE_SIZE (1 << 22) | |
29 | #define UC32_PAGETABLE_READ (1 << 8) | |
30 | #define UC32_PAGETABLE_WRITE (1 << 7) | |
31 | #define UC32_PAGETABLE_EXEC (1 << 6) | |
32 | #define UC32_PAGETABLE_EXIST (1 << 2) | |
33 | #define PAGETABLE_TYPE(x) ((x) & 3) | |
34 | ||
35 | ||
36 | /* Map CPU modes onto saved register banks. */ | |
447b3b60 | 37 | static inline int bank_number(CPUUniCore32State *env, int mode) |
f3ccc323 | 38 | { |
a47dddd7 AF |
39 | UniCore32CPU *cpu = uc32_env_get_cpu(env); |
40 | ||
f3ccc323 GX |
41 | switch (mode) { |
42 | case ASR_MODE_USER: | |
43 | case ASR_MODE_SUSR: | |
44 | return 0; | |
45 | case ASR_MODE_PRIV: | |
46 | return 1; | |
47 | case ASR_MODE_TRAP: | |
48 | return 2; | |
49 | case ASR_MODE_EXTN: | |
50 | return 3; | |
51 | case ASR_MODE_INTR: | |
52 | return 4; | |
53 | } | |
a47dddd7 | 54 | cpu_abort(CPU(cpu), "Bad mode %x\n", mode); |
f3ccc323 GX |
55 | return -1; |
56 | } | |
57 | ||
4f23a1e6 GX |
58 | void switch_mode(CPUUniCore32State *env, int mode) |
59 | { | |
f3ccc323 GX |
60 | int old_mode; |
61 | int i; | |
62 | ||
63 | old_mode = env->uncached_asr & ASR_M; | |
64 | if (mode == old_mode) { | |
65 | return; | |
66 | } | |
67 | ||
447b3b60 | 68 | i = bank_number(env, old_mode); |
f3ccc323 GX |
69 | env->banked_r29[i] = env->regs[29]; |
70 | env->banked_r30[i] = env->regs[30]; | |
71 | env->banked_bsr[i] = env->bsr; | |
72 | ||
447b3b60 | 73 | i = bank_number(env, mode); |
f3ccc323 GX |
74 | env->regs[29] = env->banked_r29[i]; |
75 | env->regs[30] = env->banked_r30[i]; | |
76 | env->bsr = env->banked_bsr[i]; | |
4f23a1e6 GX |
77 | } |
78 | ||
f3ccc323 | 79 | /* Handle a CPU exception. */ |
97a8ea5a | 80 | void uc32_cpu_do_interrupt(CPUState *cs) |
4f23a1e6 | 81 | { |
97a8ea5a AF |
82 | UniCore32CPU *cpu = UNICORE32_CPU(cs); |
83 | CPUUniCore32State *env = &cpu->env; | |
f3ccc323 GX |
84 | uint32_t addr; |
85 | int new_mode; | |
86 | ||
27103424 | 87 | switch (cs->exception_index) { |
f3ccc323 GX |
88 | case UC32_EXCP_PRIV: |
89 | new_mode = ASR_MODE_PRIV; | |
90 | addr = 0x08; | |
91 | break; | |
92 | case UC32_EXCP_ITRAP: | |
93 | DPRINTF("itrap happened at %x\n", env->regs[31]); | |
94 | new_mode = ASR_MODE_TRAP; | |
95 | addr = 0x0c; | |
96 | break; | |
97 | case UC32_EXCP_DTRAP: | |
98 | DPRINTF("dtrap happened at %x\n", env->regs[31]); | |
99 | new_mode = ASR_MODE_TRAP; | |
100 | addr = 0x10; | |
101 | break; | |
102 | case UC32_EXCP_INTR: | |
103 | new_mode = ASR_MODE_INTR; | |
104 | addr = 0x18; | |
105 | break; | |
106 | default: | |
a47dddd7 | 107 | cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); |
f3ccc323 GX |
108 | return; |
109 | } | |
110 | /* High vectors. */ | |
111 | if (env->cp0.c1_sys & (1 << 13)) { | |
112 | addr += 0xffff0000; | |
113 | } | |
114 | ||
115 | switch_mode(env, new_mode); | |
116 | env->bsr = cpu_asr_read(env); | |
117 | env->uncached_asr = (env->uncached_asr & ~ASR_M) | new_mode; | |
118 | env->uncached_asr |= ASR_I; | |
119 | /* The PC already points to the proper instruction. */ | |
120 | env->regs[30] = env->regs[31]; | |
121 | env->regs[31] = addr; | |
259186a7 | 122 | cs->interrupt_request |= CPU_INTERRUPT_EXITTB; |
f3ccc323 GX |
123 | } |
124 | ||
125 | static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address, | |
126 | int access_type, int is_user, uint32_t *phys_ptr, int *prot, | |
127 | target_ulong *page_size) | |
128 | { | |
a47dddd7 AF |
129 | UniCore32CPU *cpu = uc32_env_get_cpu(env); |
130 | CPUState *cs = CPU(cpu); | |
f3ccc323 GX |
131 | int code; |
132 | uint32_t table; | |
133 | uint32_t desc; | |
134 | uint32_t phys_addr; | |
135 | ||
136 | /* Pagetable walk. */ | |
137 | /* Lookup l1 descriptor. */ | |
138 | table = env->cp0.c2_base & 0xfffff000; | |
139 | table |= (address >> 20) & 0xffc; | |
fdfba1a2 | 140 | desc = ldl_phys(cs->as, table); |
f3ccc323 GX |
141 | code = 0; |
142 | switch (PAGETABLE_TYPE(desc)) { | |
143 | case 3: | |
144 | /* Superpage */ | |
145 | if (!(desc & UC32_PAGETABLE_EXIST)) { | |
146 | code = 0x0b; /* superpage miss */ | |
147 | goto do_fault; | |
148 | } | |
149 | phys_addr = (desc & 0xffc00000) | (address & 0x003fffff); | |
150 | *page_size = SUPERPAGE_SIZE; | |
151 | break; | |
152 | case 0: | |
153 | /* Lookup l2 entry. */ | |
154 | if (is_user) { | |
155 | DPRINTF("PGD address %x, desc %x\n", table, desc); | |
156 | } | |
157 | if (!(desc & UC32_PAGETABLE_EXIST)) { | |
158 | code = 0x05; /* second pagetable miss */ | |
159 | goto do_fault; | |
160 | } | |
161 | table = (desc & 0xfffff000) | ((address >> 10) & 0xffc); | |
fdfba1a2 | 162 | desc = ldl_phys(cs->as, table); |
f3ccc323 GX |
163 | /* 4k page. */ |
164 | if (is_user) { | |
165 | DPRINTF("PTE address %x, desc %x\n", table, desc); | |
166 | } | |
167 | if (!(desc & UC32_PAGETABLE_EXIST)) { | |
168 | code = 0x08; /* page miss */ | |
169 | goto do_fault; | |
170 | } | |
171 | switch (PAGETABLE_TYPE(desc)) { | |
172 | case 0: | |
173 | phys_addr = (desc & 0xfffff000) | (address & 0xfff); | |
174 | *page_size = TARGET_PAGE_SIZE; | |
175 | break; | |
176 | default: | |
a47dddd7 | 177 | cpu_abort(CPU(cpu), "wrong page type!"); |
f3ccc323 GX |
178 | } |
179 | break; | |
180 | default: | |
a47dddd7 | 181 | cpu_abort(CPU(cpu), "wrong page type!"); |
f3ccc323 GX |
182 | } |
183 | ||
184 | *phys_ptr = phys_addr; | |
185 | *prot = 0; | |
186 | /* Check access permissions. */ | |
187 | if (desc & UC32_PAGETABLE_READ) { | |
188 | *prot |= PAGE_READ; | |
189 | } else { | |
190 | if (is_user && (access_type == 0)) { | |
191 | code = 0x11; /* access unreadable area */ | |
192 | goto do_fault; | |
193 | } | |
194 | } | |
195 | ||
196 | if (desc & UC32_PAGETABLE_WRITE) { | |
197 | *prot |= PAGE_WRITE; | |
198 | } else { | |
199 | if (is_user && (access_type == 1)) { | |
200 | code = 0x12; /* access unwritable area */ | |
201 | goto do_fault; | |
202 | } | |
203 | } | |
204 | ||
205 | if (desc & UC32_PAGETABLE_EXEC) { | |
206 | *prot |= PAGE_EXEC; | |
207 | } else { | |
208 | if (is_user && (access_type == 2)) { | |
209 | code = 0x13; /* access unexecutable area */ | |
210 | goto do_fault; | |
211 | } | |
212 | } | |
213 | ||
214 | do_fault: | |
215 | return code; | |
4f23a1e6 GX |
216 | } |
217 | ||
98670d47 | 218 | int uc32_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, |
4f23a1e6 GX |
219 | int access_type, int mmu_idx) |
220 | { | |
7510454e AF |
221 | UniCore32CPU *cpu = UNICORE32_CPU(cs); |
222 | CPUUniCore32State *env = &cpu->env; | |
f3ccc323 GX |
223 | uint32_t phys_addr; |
224 | target_ulong page_size; | |
225 | int prot; | |
226 | int ret, is_user; | |
227 | ||
228 | ret = 1; | |
229 | is_user = mmu_idx == MMU_USER_IDX; | |
230 | ||
231 | if ((env->cp0.c1_sys & 1) == 0) { | |
232 | /* MMU disabled. */ | |
233 | phys_addr = address; | |
234 | prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; | |
235 | page_size = TARGET_PAGE_SIZE; | |
236 | ret = 0; | |
237 | } else { | |
238 | if ((address & (1 << 31)) || (is_user)) { | |
239 | ret = get_phys_addr_ucv2(env, address, access_type, is_user, | |
240 | &phys_addr, &prot, &page_size); | |
241 | if (is_user) { | |
7510454e | 242 | DPRINTF("user space access: ret %x, address %" VADDR_PRIx ", " |
f3ccc323 GX |
243 | "access_type %x, phys_addr %x, prot %x\n", |
244 | ret, address, access_type, phys_addr, prot); | |
245 | } | |
246 | } else { | |
247 | /*IO memory */ | |
248 | phys_addr = address | (1 << 31); | |
249 | prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; | |
250 | page_size = TARGET_PAGE_SIZE; | |
251 | ret = 0; | |
252 | } | |
253 | } | |
254 | ||
255 | if (ret == 0) { | |
256 | /* Map a single page. */ | |
257 | phys_addr &= TARGET_PAGE_MASK; | |
258 | address &= TARGET_PAGE_MASK; | |
0c591eb0 | 259 | tlb_set_page(cs, address, phys_addr, prot, mmu_idx, page_size); |
f3ccc323 GX |
260 | return 0; |
261 | } | |
262 | ||
263 | env->cp0.c3_faultstatus = ret; | |
264 | env->cp0.c4_faultaddr = address; | |
265 | if (access_type == 2) { | |
27103424 | 266 | cs->exception_index = UC32_EXCP_ITRAP; |
f3ccc323 | 267 | } else { |
27103424 | 268 | cs->exception_index = UC32_EXCP_DTRAP; |
f3ccc323 GX |
269 | } |
270 | return ret; | |
4f23a1e6 GX |
271 | } |
272 | ||
00b941e5 | 273 | hwaddr uc32_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) |
4f23a1e6 | 274 | { |
0ac241bc EO |
275 | error_report("function uc32_cpu_get_phys_page_debug not " |
276 | "implemented, aborting"); | |
277 | return -1; | |
4f23a1e6 | 278 | } |