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 | |
15 | #include "qemu/osdep.h" |
16 | #include "cpu.h" |
17 | #include "exec/exec-all.h" |
18 | #include "qemu/error-report.h" |
19 | |
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. */ |
37 | static inline int bank_number(CPUUniCore32State *env, int mode) |
38 | { |
39 | switch (mode) { |
40 | case ASR_MODE_USER: |
41 | case ASR_MODE_SUSR: |
42 | return 0; |
43 | case ASR_MODE_PRIV: |
44 | return 1; |
45 | case ASR_MODE_TRAP: |
46 | return 2; |
47 | case ASR_MODE_EXTN: |
48 | return 3; |
49 | case ASR_MODE_INTR: |
50 | return 4; |
51 | } |
52 | cpu_abort(env_cpu(env), "Bad mode %x\n" , mode); |
53 | return -1; |
54 | } |
55 | |
56 | void switch_mode(CPUUniCore32State *env, int mode) |
57 | { |
58 | int old_mode; |
59 | int i; |
60 | |
61 | old_mode = env->uncached_asr & ASR_M; |
62 | if (mode == old_mode) { |
63 | return; |
64 | } |
65 | |
66 | i = bank_number(env, old_mode); |
67 | env->banked_r29[i] = env->regs[29]; |
68 | env->banked_r30[i] = env->regs[30]; |
69 | env->banked_bsr[i] = env->bsr; |
70 | |
71 | i = bank_number(env, mode); |
72 | env->regs[29] = env->banked_r29[i]; |
73 | env->regs[30] = env->banked_r30[i]; |
74 | env->bsr = env->banked_bsr[i]; |
75 | } |
76 | |
77 | /* Handle a CPU exception. */ |
78 | void uc32_cpu_do_interrupt(CPUState *cs) |
79 | { |
80 | UniCore32CPU *cpu = UNICORE32_CPU(cs); |
81 | CPUUniCore32State *env = &cpu->env; |
82 | uint32_t addr; |
83 | int new_mode; |
84 | |
85 | switch (cs->exception_index) { |
86 | case UC32_EXCP_PRIV: |
87 | new_mode = ASR_MODE_PRIV; |
88 | addr = 0x08; |
89 | break; |
90 | case UC32_EXCP_ITRAP: |
91 | DPRINTF("itrap happened at %x\n" , env->regs[31]); |
92 | new_mode = ASR_MODE_TRAP; |
93 | addr = 0x0c; |
94 | break; |
95 | case UC32_EXCP_DTRAP: |
96 | DPRINTF("dtrap happened at %x\n" , env->regs[31]); |
97 | new_mode = ASR_MODE_TRAP; |
98 | addr = 0x10; |
99 | break; |
100 | case UC32_EXCP_INTR: |
101 | new_mode = ASR_MODE_INTR; |
102 | addr = 0x18; |
103 | break; |
104 | default: |
105 | cpu_abort(cs, "Unhandled exception 0x%x\n" , cs->exception_index); |
106 | return; |
107 | } |
108 | /* High vectors. */ |
109 | if (env->cp0.c1_sys & (1 << 13)) { |
110 | addr += 0xffff0000; |
111 | } |
112 | |
113 | switch_mode(env, new_mode); |
114 | env->bsr = cpu_asr_read(env); |
115 | env->uncached_asr = (env->uncached_asr & ~ASR_M) | new_mode; |
116 | env->uncached_asr |= ASR_I; |
117 | /* The PC already points to the proper instruction. */ |
118 | env->regs[30] = env->regs[31]; |
119 | env->regs[31] = addr; |
120 | cs->interrupt_request |= CPU_INTERRUPT_EXITTB; |
121 | } |
122 | |
123 | static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address, |
124 | int access_type, int is_user, uint32_t *phys_ptr, int *prot, |
125 | target_ulong *page_size) |
126 | { |
127 | CPUState *cs = env_cpu(env); |
128 | int code; |
129 | uint32_t table; |
130 | uint32_t desc; |
131 | uint32_t phys_addr; |
132 | |
133 | /* Pagetable walk. */ |
134 | /* Lookup l1 descriptor. */ |
135 | table = env->cp0.c2_base & 0xfffff000; |
136 | table |= (address >> 20) & 0xffc; |
137 | desc = ldl_phys(cs->as, table); |
138 | code = 0; |
139 | switch (PAGETABLE_TYPE(desc)) { |
140 | case 3: |
141 | /* Superpage */ |
142 | if (!(desc & UC32_PAGETABLE_EXIST)) { |
143 | code = 0x0b; /* superpage miss */ |
144 | goto do_fault; |
145 | } |
146 | phys_addr = (desc & 0xffc00000) | (address & 0x003fffff); |
147 | *page_size = SUPERPAGE_SIZE; |
148 | break; |
149 | case 0: |
150 | /* Lookup l2 entry. */ |
151 | if (is_user) { |
152 | DPRINTF("PGD address %x, desc %x\n" , table, desc); |
153 | } |
154 | if (!(desc & UC32_PAGETABLE_EXIST)) { |
155 | code = 0x05; /* second pagetable miss */ |
156 | goto do_fault; |
157 | } |
158 | table = (desc & 0xfffff000) | ((address >> 10) & 0xffc); |
159 | desc = ldl_phys(cs->as, table); |
160 | /* 4k page. */ |
161 | if (is_user) { |
162 | DPRINTF("PTE address %x, desc %x\n" , table, desc); |
163 | } |
164 | if (!(desc & UC32_PAGETABLE_EXIST)) { |
165 | code = 0x08; /* page miss */ |
166 | goto do_fault; |
167 | } |
168 | switch (PAGETABLE_TYPE(desc)) { |
169 | case 0: |
170 | phys_addr = (desc & 0xfffff000) | (address & 0xfff); |
171 | *page_size = TARGET_PAGE_SIZE; |
172 | break; |
173 | default: |
174 | cpu_abort(cs, "wrong page type!" ); |
175 | } |
176 | break; |
177 | default: |
178 | cpu_abort(cs, "wrong page type!" ); |
179 | } |
180 | |
181 | *phys_ptr = phys_addr; |
182 | *prot = 0; |
183 | /* Check access permissions. */ |
184 | if (desc & UC32_PAGETABLE_READ) { |
185 | *prot |= PAGE_READ; |
186 | } else { |
187 | if (is_user && (access_type == 0)) { |
188 | code = 0x11; /* access unreadable area */ |
189 | goto do_fault; |
190 | } |
191 | } |
192 | |
193 | if (desc & UC32_PAGETABLE_WRITE) { |
194 | *prot |= PAGE_WRITE; |
195 | } else { |
196 | if (is_user && (access_type == 1)) { |
197 | code = 0x12; /* access unwritable area */ |
198 | goto do_fault; |
199 | } |
200 | } |
201 | |
202 | if (desc & UC32_PAGETABLE_EXEC) { |
203 | *prot |= PAGE_EXEC; |
204 | } else { |
205 | if (is_user && (access_type == 2)) { |
206 | code = 0x13; /* access unexecutable area */ |
207 | goto do_fault; |
208 | } |
209 | } |
210 | |
211 | do_fault: |
212 | return code; |
213 | } |
214 | |
215 | bool uc32_cpu_tlb_fill(CPUState *cs, vaddr address, int size, |
216 | MMUAccessType access_type, int mmu_idx, |
217 | bool probe, uintptr_t retaddr) |
218 | { |
219 | UniCore32CPU *cpu = UNICORE32_CPU(cs); |
220 | CPUUniCore32State *env = &cpu->env; |
221 | uint32_t phys_addr; |
222 | target_ulong page_size; |
223 | int prot; |
224 | int ret, is_user; |
225 | |
226 | ret = 1; |
227 | is_user = mmu_idx == MMU_USER_IDX; |
228 | |
229 | if ((env->cp0.c1_sys & 1) == 0) { |
230 | /* MMU disabled. */ |
231 | phys_addr = address; |
232 | prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; |
233 | page_size = TARGET_PAGE_SIZE; |
234 | ret = 0; |
235 | } else { |
236 | if ((address & (1 << 31)) || (is_user)) { |
237 | ret = get_phys_addr_ucv2(env, address, access_type, is_user, |
238 | &phys_addr, &prot, &page_size); |
239 | if (is_user) { |
240 | DPRINTF("user space access: ret %x, address %" VADDR_PRIx ", " |
241 | "access_type %x, phys_addr %x, prot %x\n" , |
242 | ret, address, access_type, phys_addr, prot); |
243 | } |
244 | } else { |
245 | /*IO memory */ |
246 | phys_addr = address | (1 << 31); |
247 | prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; |
248 | page_size = TARGET_PAGE_SIZE; |
249 | ret = 0; |
250 | } |
251 | } |
252 | |
253 | if (ret == 0) { |
254 | /* Map a single page. */ |
255 | phys_addr &= TARGET_PAGE_MASK; |
256 | address &= TARGET_PAGE_MASK; |
257 | tlb_set_page(cs, address, phys_addr, prot, mmu_idx, page_size); |
258 | return true; |
259 | } |
260 | |
261 | if (probe) { |
262 | return false; |
263 | } |
264 | |
265 | env->cp0.c3_faultstatus = ret; |
266 | env->cp0.c4_faultaddr = address; |
267 | if (access_type == 2) { |
268 | cs->exception_index = UC32_EXCP_ITRAP; |
269 | } else { |
270 | cs->exception_index = UC32_EXCP_DTRAP; |
271 | } |
272 | cpu_loop_exit_restore(cs, retaddr); |
273 | } |
274 | |
275 | hwaddr uc32_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) |
276 | { |
277 | error_report("function uc32_cpu_get_phys_page_debug not " |
278 | "implemented, aborting" ); |
279 | return -1; |
280 | } |
281 | |