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