]>
Commit | Line | Data |
---|---|---|
b82945bc JM |
1 | /* |
2 | * Alpha emulation - PALcode emulation for qemu. | |
5fafdf24 | 3 | * |
b82945bc JM |
4 | * Copyright (c) 2007 Jocelyn Mayer |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
8167ee88 | 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
b82945bc JM |
18 | */ |
19 | ||
20 | #include <stdint.h> | |
21 | #include <stdlib.h> | |
22 | #include <stdio.h> | |
23 | ||
b82945bc JM |
24 | #include "cpu.h" |
25 | #include "exec-all.h" | |
26 | ||
b82945bc JM |
27 | /* Shared handlers */ |
28 | static void pal_reset (CPUState *env); | |
29 | /* Console handlers */ | |
30 | static void pal_console_call (CPUState *env, uint32_t palcode); | |
31 | /* OpenVMS handlers */ | |
32 | static void pal_openvms_call (CPUState *env, uint32_t palcode); | |
33 | /* UNIX / Linux handlers */ | |
34 | static void pal_unix_call (CPUState *env, uint32_t palcode); | |
35 | ||
36 | pal_handler_t pal_handlers[] = { | |
37 | /* Console handler */ | |
38 | { | |
39 | .reset = &pal_reset, | |
40 | .call_pal = &pal_console_call, | |
41 | }, | |
42 | /* OpenVMS handler */ | |
43 | { | |
44 | .reset = &pal_reset, | |
45 | .call_pal = &pal_openvms_call, | |
46 | }, | |
47 | /* UNIX / Linux handler */ | |
48 | { | |
49 | .reset = &pal_reset, | |
50 | .call_pal = &pal_unix_call, | |
51 | }, | |
52 | }; | |
53 | ||
54 | #if 0 | |
1235fc06 | 55 | /* One must explicitly check that the TB is valid and the FOE bit is reset */ |
3f47aa8c | 56 | static void update_itb (void) |
b82945bc JM |
57 | { |
58 | /* This writes into a temp register, not the actual one */ | |
59 | mtpr(TB_TAG); | |
60 | mtpr(TB_CTL); | |
61 | /* This commits the TB update */ | |
5fafdf24 | 62 | mtpr(ITB_PTE); |
b82945bc JM |
63 | } |
64 | ||
3f47aa8c | 65 | static void update_dtb (void); |
b82945bc JM |
66 | { |
67 | mtpr(TB_CTL); | |
68 | /* This write into a temp register, not the actual one */ | |
69 | mtpr(TB_TAG); | |
70 | /* This commits the TB update */ | |
71 | mtpr(DTB_PTE); | |
72 | } | |
73 | #endif | |
74 | ||
75 | static void pal_reset (CPUState *env) | |
76 | { | |
77 | } | |
78 | ||
79 | static void do_swappal (CPUState *env, uint64_t palid) | |
80 | { | |
81 | pal_handler_t *pal_handler; | |
b82945bc | 82 | |
b82945bc JM |
83 | switch (palid) { |
84 | case 0 ... 2: | |
85 | pal_handler = &pal_handlers[palid]; | |
86 | env->pal_handler = pal_handler; | |
87 | env->ipr[IPR_PAL_BASE] = -1ULL; | |
88 | (*pal_handler->reset)(env); | |
89 | break; | |
90 | case 3 ... 255: | |
91 | /* Unknown identifier */ | |
92 | env->ir[0] = 1; | |
93 | return; | |
94 | default: | |
95 | /* We were given the entry point address */ | |
96 | env->pal_handler = NULL; | |
97 | env->ipr[IPR_PAL_BASE] = palid; | |
98 | env->pc = env->ipr[IPR_PAL_BASE]; | |
99 | cpu_loop_exit(); | |
100 | } | |
101 | } | |
102 | ||
103 | static void pal_console_call (CPUState *env, uint32_t palcode) | |
104 | { | |
105 | uint64_t palid; | |
106 | ||
107 | if (palcode < 0x00000080) { | |
108 | /* Privileged palcodes */ | |
109 | if (!(env->ps >> 3)) { | |
110 | /* TODO: generate privilege exception */ | |
111 | } | |
112 | } | |
113 | switch (palcode) { | |
114 | case 0x00000000: | |
115 | /* HALT */ | |
116 | /* REQUIRED */ | |
117 | break; | |
118 | case 0x00000001: | |
119 | /* CFLUSH */ | |
120 | break; | |
121 | case 0x00000002: | |
122 | /* DRAINA */ | |
123 | /* REQUIRED */ | |
124 | /* Implemented as no-op */ | |
125 | break; | |
126 | case 0x00000009: | |
127 | /* CSERVE */ | |
128 | /* REQUIRED */ | |
129 | break; | |
130 | case 0x0000000A: | |
131 | /* SWPPAL */ | |
132 | /* REQUIRED */ | |
133 | palid = env->ir[16]; | |
134 | do_swappal(env, palid); | |
135 | break; | |
136 | case 0x00000080: | |
137 | /* BPT */ | |
138 | /* REQUIRED */ | |
139 | break; | |
140 | case 0x00000081: | |
141 | /* BUGCHK */ | |
142 | /* REQUIRED */ | |
143 | break; | |
144 | case 0x00000086: | |
145 | /* IMB */ | |
146 | /* REQUIRED */ | |
147 | /* Implemented as no-op */ | |
148 | break; | |
149 | case 0x0000009E: | |
150 | /* RDUNIQUE */ | |
151 | /* REQUIRED */ | |
152 | break; | |
153 | case 0x0000009F: | |
154 | /* WRUNIQUE */ | |
155 | /* REQUIRED */ | |
156 | break; | |
157 | case 0x000000AA: | |
158 | /* GENTRAP */ | |
159 | /* REQUIRED */ | |
160 | break; | |
161 | default: | |
162 | break; | |
163 | } | |
164 | } | |
165 | ||
166 | static void pal_openvms_call (CPUState *env, uint32_t palcode) | |
167 | { | |
168 | uint64_t palid, val, oldval; | |
169 | ||
170 | if (palcode < 0x00000080) { | |
171 | /* Privileged palcodes */ | |
172 | if (!(env->ps >> 3)) { | |
173 | /* TODO: generate privilege exception */ | |
174 | } | |
175 | } | |
176 | switch (palcode) { | |
177 | case 0x00000000: | |
178 | /* HALT */ | |
179 | /* REQUIRED */ | |
180 | break; | |
181 | case 0x00000001: | |
182 | /* CFLUSH */ | |
183 | break; | |
184 | case 0x00000002: | |
185 | /* DRAINA */ | |
186 | /* REQUIRED */ | |
187 | /* Implemented as no-op */ | |
188 | break; | |
189 | case 0x00000003: | |
190 | /* LDQP */ | |
191 | break; | |
192 | case 0x00000004: | |
193 | /* STQP */ | |
194 | break; | |
195 | case 0x00000005: | |
196 | /* SWPCTX */ | |
197 | break; | |
198 | case 0x00000006: | |
199 | /* MFPR_ASN */ | |
200 | if (cpu_alpha_mfpr(env, IPR_ASN, &val) == 0) | |
201 | env->ir[0] = val; | |
202 | break; | |
203 | case 0x00000007: | |
204 | /* MTPR_ASTEN */ | |
205 | val = env->ir[16]; | |
206 | if (cpu_alpha_mtpr(env, IPR_ASTEN, val, &oldval) == 1) | |
207 | env->ir[0] = val; | |
208 | break; | |
209 | case 0x00000008: | |
210 | /* MTPR_ASTSR */ | |
211 | val = env->ir[16]; | |
212 | if (cpu_alpha_mtpr(env, IPR_ASTSR, val, &oldval) == 1) | |
213 | env->ir[0] = val; | |
214 | break; | |
215 | case 0x00000009: | |
216 | /* CSERVE */ | |
217 | /* REQUIRED */ | |
218 | break; | |
219 | case 0x0000000A: | |
220 | /* SWPPAL */ | |
221 | /* REQUIRED */ | |
222 | palid = env->ir[16]; | |
223 | do_swappal(env, palid); | |
224 | break; | |
225 | case 0x0000000B: | |
226 | /* MFPR_FEN */ | |
227 | if (cpu_alpha_mfpr(env, IPR_FEN, &val) == 0) | |
228 | env->ir[0] = val; | |
229 | break; | |
230 | case 0x0000000C: | |
231 | /* MTPR_FEN */ | |
232 | val = env->ir[16]; | |
233 | if (cpu_alpha_mtpr(env, IPR_FEN, val, &oldval) == 1) | |
234 | env->ir[0] = val; | |
235 | break; | |
236 | case 0x0000000D: | |
237 | /* MTPR_IPIR */ | |
238 | val = env->ir[16]; | |
239 | if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) | |
240 | env->ir[0] = val; | |
241 | break; | |
242 | case 0x0000000E: | |
243 | /* MFPR_IPL */ | |
244 | if (cpu_alpha_mfpr(env, IPR_IPL, &val) == 0) | |
245 | env->ir[0] = val; | |
246 | break; | |
247 | case 0x0000000F: | |
248 | /* MTPR_IPL */ | |
249 | val = env->ir[16]; | |
250 | if (cpu_alpha_mtpr(env, IPR_IPL, val, &oldval) == 1) | |
251 | env->ir[0] = val; | |
252 | break; | |
253 | case 0x00000010: | |
254 | /* MFPR_MCES */ | |
255 | if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) | |
256 | env->ir[0] = val; | |
257 | break; | |
258 | case 0x00000011: | |
259 | /* MTPR_MCES */ | |
260 | val = env->ir[16]; | |
261 | if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) | |
262 | env->ir[0] = val; | |
263 | break; | |
264 | case 0x00000012: | |
265 | /* MFPR_PCBB */ | |
266 | if (cpu_alpha_mfpr(env, IPR_PCBB, &val) == 0) | |
267 | env->ir[0] = val; | |
268 | break; | |
269 | case 0x00000013: | |
270 | /* MFPR_PRBR */ | |
271 | if (cpu_alpha_mfpr(env, IPR_PRBR, &val) == 0) | |
272 | env->ir[0] = val; | |
273 | break; | |
274 | case 0x00000014: | |
275 | /* MTPR_PRBR */ | |
276 | val = env->ir[16]; | |
277 | if (cpu_alpha_mtpr(env, IPR_PRBR, val, &oldval) == 1) | |
278 | env->ir[0] = val; | |
279 | break; | |
280 | case 0x00000015: | |
281 | /* MFPR_PTBR */ | |
282 | if (cpu_alpha_mfpr(env, IPR_PTBR, &val) == 0) | |
283 | env->ir[0] = val; | |
284 | break; | |
285 | case 0x00000016: | |
286 | /* MFPR_SCBB */ | |
287 | if (cpu_alpha_mfpr(env, IPR_SCBB, &val) == 0) | |
288 | env->ir[0] = val; | |
289 | break; | |
290 | case 0x00000017: | |
291 | /* MTPR_SCBB */ | |
292 | val = env->ir[16]; | |
293 | if (cpu_alpha_mtpr(env, IPR_SCBB, val, &oldval) == 1) | |
294 | env->ir[0] = val; | |
295 | break; | |
296 | case 0x00000018: | |
297 | /* MTPR_SIRR */ | |
298 | val = env->ir[16]; | |
299 | if (cpu_alpha_mtpr(env, IPR_SIRR, val, &oldval) == 1) | |
300 | env->ir[0] = val; | |
301 | break; | |
302 | case 0x00000019: | |
303 | /* MFPR_SISR */ | |
304 | if (cpu_alpha_mfpr(env, IPR_SISR, &val) == 0) | |
305 | env->ir[0] = val; | |
306 | break; | |
307 | case 0x0000001A: | |
308 | /* MFPR_TBCHK */ | |
309 | if (cpu_alpha_mfpr(env, IPR_TBCHK, &val) == 0) | |
310 | env->ir[0] = val; | |
311 | break; | |
312 | case 0x0000001B: | |
313 | /* MTPR_TBIA */ | |
314 | val = env->ir[16]; | |
315 | if (cpu_alpha_mtpr(env, IPR_TBIA, val, &oldval) == 1) | |
316 | env->ir[0] = val; | |
317 | break; | |
318 | case 0x0000001C: | |
319 | /* MTPR_TBIAP */ | |
320 | val = env->ir[16]; | |
321 | if (cpu_alpha_mtpr(env, IPR_TBIAP, val, &oldval) == 1) | |
322 | env->ir[0] = val; | |
323 | break; | |
324 | case 0x0000001D: | |
325 | /* MTPR_TBIS */ | |
326 | val = env->ir[16]; | |
327 | if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) | |
328 | env->ir[0] = val; | |
329 | break; | |
330 | case 0x0000001E: | |
331 | /* MFPR_ESP */ | |
332 | if (cpu_alpha_mfpr(env, IPR_ESP, &val) == 0) | |
333 | env->ir[0] = val; | |
334 | break; | |
335 | case 0x0000001F: | |
336 | /* MTPR_ESP */ | |
337 | val = env->ir[16]; | |
338 | if (cpu_alpha_mtpr(env, IPR_ESP, val, &oldval) == 1) | |
339 | env->ir[0] = val; | |
340 | break; | |
341 | case 0x00000020: | |
342 | /* MFPR_SSP */ | |
343 | if (cpu_alpha_mfpr(env, IPR_SSP, &val) == 0) | |
344 | env->ir[0] = val; | |
345 | break; | |
346 | case 0x00000021: | |
347 | /* MTPR_SSP */ | |
348 | val = env->ir[16]; | |
349 | if (cpu_alpha_mtpr(env, IPR_SSP, val, &oldval) == 1) | |
350 | env->ir[0] = val; | |
351 | break; | |
352 | case 0x00000022: | |
353 | /* MFPR_USP */ | |
354 | if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) | |
355 | env->ir[0] = val; | |
356 | break; | |
357 | case 0x00000023: | |
358 | /* MTPR_USP */ | |
359 | val = env->ir[16]; | |
360 | if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) | |
361 | env->ir[0] = val; | |
362 | break; | |
363 | case 0x00000024: | |
364 | /* MTPR_TBISD */ | |
365 | val = env->ir[16]; | |
366 | if (cpu_alpha_mtpr(env, IPR_TBISD, val, &oldval) == 1) | |
367 | env->ir[0] = val; | |
368 | break; | |
369 | case 0x00000025: | |
370 | /* MTPR_TBISI */ | |
371 | val = env->ir[16]; | |
372 | if (cpu_alpha_mtpr(env, IPR_TBISI, val, &oldval) == 1) | |
373 | env->ir[0] = val; | |
374 | break; | |
375 | case 0x00000026: | |
376 | /* MFPR_ASTEN */ | |
377 | if (cpu_alpha_mfpr(env, IPR_ASTEN, &val) == 0) | |
378 | env->ir[0] = val; | |
379 | break; | |
380 | case 0x00000027: | |
381 | /* MFPR_ASTSR */ | |
382 | if (cpu_alpha_mfpr(env, IPR_ASTSR, &val) == 0) | |
383 | env->ir[0] = val; | |
384 | break; | |
385 | case 0x00000029: | |
386 | /* MFPR_VPTB */ | |
387 | if (cpu_alpha_mfpr(env, IPR_VPTB, &val) == 0) | |
388 | env->ir[0] = val; | |
389 | break; | |
390 | case 0x0000002A: | |
391 | /* MTPR_VPTB */ | |
392 | val = env->ir[16]; | |
393 | if (cpu_alpha_mtpr(env, IPR_VPTB, val, &oldval) == 1) | |
394 | env->ir[0] = val; | |
395 | break; | |
396 | case 0x0000002B: | |
397 | /* MTPR_PERFMON */ | |
398 | val = env->ir[16]; | |
399 | if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) | |
400 | env->ir[0] = val; | |
401 | break; | |
402 | case 0x0000002E: | |
403 | /* MTPR_DATFX */ | |
404 | val = env->ir[16]; | |
405 | if (cpu_alpha_mtpr(env, IPR_DATFX, val, &oldval) == 1) | |
406 | env->ir[0] = val; | |
407 | break; | |
408 | case 0x0000003E: | |
409 | /* WTINT */ | |
410 | break; | |
411 | case 0x0000003F: | |
412 | /* MFPR_WHAMI */ | |
413 | if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) | |
414 | env->ir[0] = val; | |
415 | break; | |
416 | case 0x00000080: | |
417 | /* BPT */ | |
418 | /* REQUIRED */ | |
419 | break; | |
420 | case 0x00000081: | |
421 | /* BUGCHK */ | |
422 | /* REQUIRED */ | |
423 | break; | |
424 | case 0x00000082: | |
425 | /* CHME */ | |
426 | break; | |
427 | case 0x00000083: | |
428 | /* CHMK */ | |
429 | break; | |
430 | case 0x00000084: | |
431 | /* CHMS */ | |
432 | break; | |
433 | case 0x00000085: | |
434 | /* CHMU */ | |
435 | break; | |
436 | case 0x00000086: | |
437 | /* IMB */ | |
438 | /* REQUIRED */ | |
439 | /* Implemented as no-op */ | |
440 | break; | |
441 | case 0x00000087: | |
442 | /* INSQHIL */ | |
443 | break; | |
444 | case 0x00000088: | |
445 | /* INSQTIL */ | |
446 | break; | |
447 | case 0x00000089: | |
448 | /* INSQHIQ */ | |
449 | break; | |
450 | case 0x0000008A: | |
451 | /* INSQTIQ */ | |
452 | break; | |
453 | case 0x0000008B: | |
454 | /* INSQUEL */ | |
455 | break; | |
456 | case 0x0000008C: | |
457 | /* INSQUEQ */ | |
458 | break; | |
459 | case 0x0000008D: | |
460 | /* INSQUEL/D */ | |
461 | break; | |
462 | case 0x0000008E: | |
463 | /* INSQUEQ/D */ | |
464 | break; | |
465 | case 0x0000008F: | |
466 | /* PROBER */ | |
467 | break; | |
468 | case 0x00000090: | |
469 | /* PROBEW */ | |
470 | break; | |
471 | case 0x00000091: | |
472 | /* RD_PS */ | |
473 | break; | |
474 | case 0x00000092: | |
475 | /* REI */ | |
476 | break; | |
477 | case 0x00000093: | |
478 | /* REMQHIL */ | |
479 | break; | |
480 | case 0x00000094: | |
481 | /* REMQTIL */ | |
482 | break; | |
483 | case 0x00000095: | |
484 | /* REMQHIQ */ | |
485 | break; | |
486 | case 0x00000096: | |
487 | /* REMQTIQ */ | |
488 | break; | |
489 | case 0x00000097: | |
490 | /* REMQUEL */ | |
491 | break; | |
492 | case 0x00000098: | |
493 | /* REMQUEQ */ | |
494 | break; | |
495 | case 0x00000099: | |
496 | /* REMQUEL/D */ | |
497 | break; | |
498 | case 0x0000009A: | |
499 | /* REMQUEQ/D */ | |
500 | break; | |
501 | case 0x0000009B: | |
502 | /* SWASTEN */ | |
503 | break; | |
504 | case 0x0000009C: | |
505 | /* WR_PS_SW */ | |
506 | break; | |
507 | case 0x0000009D: | |
508 | /* RSCC */ | |
509 | break; | |
510 | case 0x0000009E: | |
511 | /* READ_UNQ */ | |
512 | /* REQUIRED */ | |
513 | break; | |
514 | case 0x0000009F: | |
515 | /* WRITE_UNQ */ | |
516 | /* REQUIRED */ | |
517 | break; | |
518 | case 0x000000A0: | |
519 | /* AMOVRR */ | |
520 | break; | |
521 | case 0x000000A1: | |
522 | /* AMOVRM */ | |
523 | break; | |
524 | case 0x000000A2: | |
525 | /* INSQHILR */ | |
526 | break; | |
527 | case 0x000000A3: | |
528 | /* INSQTILR */ | |
529 | break; | |
530 | case 0x000000A4: | |
531 | /* INSQHIQR */ | |
532 | break; | |
533 | case 0x000000A5: | |
534 | /* INSQTIQR */ | |
535 | break; | |
536 | case 0x000000A6: | |
537 | /* REMQHILR */ | |
538 | break; | |
539 | case 0x000000A7: | |
540 | /* REMQTILR */ | |
541 | break; | |
542 | case 0x000000A8: | |
543 | /* REMQHIQR */ | |
544 | break; | |
545 | case 0x000000A9: | |
546 | /* REMQTIQR */ | |
547 | break; | |
548 | case 0x000000AA: | |
549 | /* GENTRAP */ | |
550 | /* REQUIRED */ | |
551 | break; | |
552 | case 0x000000AE: | |
553 | /* CLRFEN */ | |
554 | break; | |
555 | default: | |
556 | break; | |
557 | } | |
558 | } | |
559 | ||
560 | static void pal_unix_call (CPUState *env, uint32_t palcode) | |
561 | { | |
562 | uint64_t palid, val, oldval; | |
563 | ||
564 | if (palcode < 0x00000080) { | |
565 | /* Privileged palcodes */ | |
566 | if (!(env->ps >> 3)) { | |
567 | /* TODO: generate privilege exception */ | |
568 | } | |
569 | } | |
570 | switch (palcode) { | |
571 | case 0x00000000: | |
572 | /* HALT */ | |
573 | /* REQUIRED */ | |
574 | break; | |
575 | case 0x00000001: | |
576 | /* CFLUSH */ | |
577 | break; | |
578 | case 0x00000002: | |
579 | /* DRAINA */ | |
580 | /* REQUIRED */ | |
581 | /* Implemented as no-op */ | |
582 | break; | |
583 | case 0x00000009: | |
584 | /* CSERVE */ | |
585 | /* REQUIRED */ | |
586 | break; | |
587 | case 0x0000000A: | |
588 | /* SWPPAL */ | |
589 | /* REQUIRED */ | |
590 | palid = env->ir[16]; | |
591 | do_swappal(env, palid); | |
592 | break; | |
593 | case 0x0000000D: | |
594 | /* WRIPIR */ | |
595 | val = env->ir[16]; | |
596 | if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) | |
597 | env->ir[0] = val; | |
598 | break; | |
599 | case 0x00000010: | |
600 | /* RDMCES */ | |
601 | if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) | |
602 | env->ir[0] = val; | |
603 | break; | |
604 | case 0x00000011: | |
605 | /* WRMCES */ | |
606 | val = env->ir[16]; | |
607 | if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) | |
608 | env->ir[0] = val; | |
609 | break; | |
610 | case 0x0000002B: | |
611 | /* WRFEN */ | |
612 | val = env->ir[16]; | |
613 | if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) | |
614 | env->ir[0] = val; | |
615 | break; | |
616 | case 0x0000002D: | |
617 | /* WRVPTPTR */ | |
618 | break; | |
619 | case 0x00000030: | |
620 | /* SWPCTX */ | |
621 | break; | |
622 | case 0x00000031: | |
623 | /* WRVAL */ | |
624 | break; | |
625 | case 0x00000032: | |
626 | /* RDVAL */ | |
627 | break; | |
628 | case 0x00000033: | |
629 | /* TBI */ | |
630 | val = env->ir[16]; | |
631 | if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) | |
632 | env->ir[0] = val; | |
633 | break; | |
634 | case 0x00000034: | |
635 | /* WRENT */ | |
636 | break; | |
637 | case 0x00000035: | |
638 | /* SWPIPL */ | |
639 | break; | |
640 | case 0x00000036: | |
641 | /* RDPS */ | |
642 | break; | |
643 | case 0x00000037: | |
644 | /* WRKGP */ | |
645 | break; | |
646 | case 0x00000038: | |
647 | /* WRUSP */ | |
648 | val = env->ir[16]; | |
649 | if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) | |
650 | env->ir[0] = val; | |
651 | break; | |
652 | case 0x00000039: | |
653 | /* WRPERFMON */ | |
654 | val = env->ir[16]; | |
655 | if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) | |
656 | env->ir[0] = val; | |
657 | break; | |
658 | case 0x0000003A: | |
659 | /* RDUSP */ | |
660 | if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) | |
661 | env->ir[0] = val; | |
662 | break; | |
663 | case 0x0000003C: | |
664 | /* WHAMI */ | |
665 | if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) | |
666 | env->ir[0] = val; | |
667 | break; | |
668 | case 0x0000003D: | |
669 | /* RETSYS */ | |
670 | break; | |
671 | case 0x0000003E: | |
672 | /* WTINT */ | |
673 | break; | |
674 | case 0x0000003F: | |
675 | /* RTI */ | |
676 | if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) | |
677 | env->ir[0] = val; | |
678 | break; | |
679 | case 0x00000080: | |
680 | /* BPT */ | |
681 | /* REQUIRED */ | |
682 | break; | |
683 | case 0x00000081: | |
684 | /* BUGCHK */ | |
685 | /* REQUIRED */ | |
686 | break; | |
687 | case 0x00000083: | |
688 | /* CALLSYS */ | |
689 | break; | |
690 | case 0x00000086: | |
691 | /* IMB */ | |
692 | /* REQUIRED */ | |
693 | /* Implemented as no-op */ | |
694 | break; | |
695 | case 0x00000092: | |
696 | /* URTI */ | |
697 | break; | |
698 | case 0x0000009E: | |
699 | /* RDUNIQUE */ | |
700 | /* REQUIRED */ | |
701 | break; | |
702 | case 0x0000009F: | |
703 | /* WRUNIQUE */ | |
704 | /* REQUIRED */ | |
705 | break; | |
706 | case 0x000000AA: | |
707 | /* GENTRAP */ | |
708 | /* REQUIRED */ | |
709 | break; | |
710 | case 0x000000AE: | |
711 | /* CLRFEN */ | |
712 | break; | |
713 | default: | |
714 | break; | |
715 | } | |
716 | } | |
717 | ||
718 | void call_pal (CPUState *env) | |
719 | { | |
720 | pal_handler_t *pal_handler = env->pal_handler; | |
721 | ||
722 | switch (env->exception_index) { | |
723 | case EXCP_RESET: | |
724 | (*pal_handler->reset)(env); | |
725 | break; | |
726 | case EXCP_MCHK: | |
727 | (*pal_handler->machine_check)(env); | |
728 | break; | |
729 | case EXCP_ARITH: | |
730 | (*pal_handler->arithmetic)(env); | |
731 | break; | |
732 | case EXCP_INTERRUPT: | |
733 | (*pal_handler->interrupt)(env); | |
734 | break; | |
735 | case EXCP_DFAULT: | |
736 | (*pal_handler->dfault)(env); | |
737 | break; | |
738 | case EXCP_DTB_MISS_PAL: | |
739 | (*pal_handler->dtb_miss_pal)(env); | |
740 | break; | |
741 | case EXCP_DTB_MISS_NATIVE: | |
742 | (*pal_handler->dtb_miss_native)(env); | |
743 | break; | |
744 | case EXCP_UNALIGN: | |
745 | (*pal_handler->unalign)(env); | |
746 | break; | |
747 | case EXCP_ITB_MISS: | |
748 | (*pal_handler->itb_miss)(env); | |
749 | break; | |
750 | case EXCP_ITB_ACV: | |
751 | (*pal_handler->itb_acv)(env); | |
752 | break; | |
753 | case EXCP_OPCDEC: | |
754 | (*pal_handler->opcdec)(env); | |
755 | break; | |
756 | case EXCP_FEN: | |
757 | (*pal_handler->fen)(env); | |
758 | break; | |
759 | default: | |
760 | if (env->exception_index >= EXCP_CALL_PAL && | |
761 | env->exception_index < EXCP_CALL_PALP) { | |
762 | /* Unprivileged PAL call */ | |
763 | (*pal_handler->call_pal) | |
764 | (env, (env->exception_index - EXCP_CALL_PAL) >> 6); | |
765 | } else if (env->exception_index >= EXCP_CALL_PALP && | |
766 | env->exception_index < EXCP_CALL_PALE) { | |
767 | /* Privileged PAL call */ | |
768 | (*pal_handler->call_pal) | |
769 | (env, ((env->exception_index - EXCP_CALL_PALP) >> 6) + 0x80); | |
770 | } else { | |
771 | /* Should never happen */ | |
772 | } | |
773 | break; | |
774 | } | |
775 | env->ipr[IPR_EXC_ADDR] &= ~1; | |
776 | } | |
777 | ||
778 | void pal_init (CPUState *env) | |
779 | { | |
780 | do_swappal(env, 0); | |
781 | } | |
782 | ||
783 | #if 0 | |
784 | static uint64_t get_ptebase (CPUState *env, uint64_t vaddr) | |
785 | { | |
786 | uint64_t virbnd, ptbr; | |
787 | ||
788 | if ((env->features & FEATURE_VIRBND)) { | |
789 | cpu_alpha_mfpr(env, IPR_VIRBND, &virbnd); | |
790 | if (vaddr >= virbnd) | |
791 | cpu_alpha_mfpr(env, IPR_SYSPTBR, &ptbr); | |
792 | else | |
793 | cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); | |
794 | } else { | |
795 | cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); | |
796 | } | |
797 | ||
798 | return ptbr; | |
799 | } | |
800 | ||
801 | static int get_page_bits (CPUState *env) | |
802 | { | |
803 | /* XXX */ | |
804 | return 13; | |
805 | } | |
806 | ||
807 | static int get_pte (uint64_t *pfnp, int *zbitsp, int *protp, | |
808 | uint64_t ptebase, int page_bits, uint64_t level, | |
6ebbf390 | 809 | int mmu_idx, int rw) |
b82945bc JM |
810 | { |
811 | uint64_t pteaddr, pte, pfn; | |
812 | uint8_t gh; | |
6ebbf390 | 813 | int ure, uwe, kre, kwe, foE, foR, foW, v, ret, ar, is_user; |
b82945bc | 814 | |
6ebbf390 JM |
815 | /* XXX: TOFIX */ |
816 | is_user = mmu_idx == MMU_USER_IDX; | |
b82945bc JM |
817 | pteaddr = (ptebase << page_bits) + (8 * level); |
818 | pte = ldq_raw(pteaddr); | |
819 | /* Decode all interresting PTE fields */ | |
820 | pfn = pte >> 32; | |
821 | uwe = (pte >> 13) & 1; | |
822 | kwe = (pte >> 12) & 1; | |
823 | ure = (pte >> 9) & 1; | |
824 | kre = (pte >> 8) & 1; | |
825 | gh = (pte >> 5) & 3; | |
826 | foE = (pte >> 3) & 1; | |
827 | foW = (pte >> 2) & 1; | |
828 | foR = (pte >> 1) & 1; | |
829 | v = pte & 1; | |
830 | ret = 0; | |
831 | if (!v) | |
832 | ret = 0x1; | |
833 | /* Check access rights */ | |
834 | ar = 0; | |
835 | if (is_user) { | |
836 | if (ure) | |
837 | ar |= PAGE_READ; | |
838 | if (uwe) | |
839 | ar |= PAGE_WRITE; | |
840 | if (rw == 1 && !uwe) | |
841 | ret |= 0x2; | |
842 | if (rw != 1 && !ure) | |
843 | ret |= 0x2; | |
844 | } else { | |
845 | if (kre) | |
846 | ar |= PAGE_READ; | |
847 | if (kwe) | |
848 | ar |= PAGE_WRITE; | |
849 | if (rw == 1 && !kwe) | |
850 | ret |= 0x2; | |
851 | if (rw != 1 && !kre) | |
852 | ret |= 0x2; | |
853 | } | |
854 | if (rw == 0 && foR) | |
855 | ret |= 0x4; | |
856 | if (rw == 2 && foE) | |
857 | ret |= 0x8; | |
858 | if (rw == 1 && foW) | |
859 | ret |= 0xC; | |
860 | *pfnp = pfn; | |
861 | if (zbitsp != NULL) | |
862 | *zbitsp = page_bits + (3 * gh); | |
863 | if (protp != NULL) | |
864 | *protp = ar; | |
865 | ||
866 | return ret; | |
867 | } | |
868 | ||
869 | static int paddr_from_pte (uint64_t *paddr, int *zbitsp, int *prot, | |
870 | uint64_t ptebase, int page_bits, | |
6ebbf390 | 871 | uint64_t vaddr, int mmu_idx, int rw) |
b82945bc JM |
872 | { |
873 | uint64_t pfn, page_mask, lvl_mask, level1, level2, level3; | |
874 | int lvl_bits, ret; | |
875 | ||
876 | page_mask = (1ULL << page_bits) - 1ULL; | |
877 | lvl_bits = page_bits - 3; | |
878 | lvl_mask = (1ULL << lvl_bits) - 1ULL; | |
879 | level3 = (vaddr >> page_bits) & lvl_mask; | |
880 | level2 = (vaddr >> (page_bits + lvl_bits)) & lvl_mask; | |
881 | level1 = (vaddr >> (page_bits + (2 * lvl_bits))) & lvl_mask; | |
882 | /* Level 1 PTE */ | |
883 | ret = get_pte(&pfn, NULL, NULL, ptebase, page_bits, level1, 0, 0); | |
884 | switch (ret) { | |
885 | case 3: | |
886 | /* Access violation */ | |
887 | return 2; | |
888 | case 2: | |
889 | /* translation not valid */ | |
890 | return 1; | |
891 | default: | |
892 | /* OK */ | |
893 | break; | |
894 | } | |
895 | /* Level 2 PTE */ | |
896 | ret = get_pte(&pfn, NULL, NULL, pfn, page_bits, level2, 0, 0); | |
897 | switch (ret) { | |
898 | case 3: | |
899 | /* Access violation */ | |
900 | return 2; | |
901 | case 2: | |
902 | /* translation not valid */ | |
903 | return 1; | |
904 | default: | |
905 | /* OK */ | |
906 | break; | |
907 | } | |
908 | /* Level 3 PTE */ | |
6ebbf390 | 909 | ret = get_pte(&pfn, zbitsp, prot, pfn, page_bits, level3, mmu_idx, rw); |
b82945bc JM |
910 | if (ret & 0x1) { |
911 | /* Translation not valid */ | |
912 | ret = 1; | |
913 | } else if (ret & 2) { | |
914 | /* Access violation */ | |
915 | ret = 2; | |
916 | } else { | |
917 | switch (ret & 0xC) { | |
918 | case 0: | |
919 | /* OK */ | |
920 | ret = 0; | |
921 | break; | |
922 | case 0x4: | |
923 | /* Fault on read */ | |
924 | ret = 3; | |
925 | break; | |
926 | case 0x8: | |
927 | /* Fault on execute */ | |
928 | ret = 4; | |
929 | break; | |
930 | case 0xC: | |
931 | /* Fault on write */ | |
932 | ret = 5; | |
933 | break; | |
934 | } | |
935 | } | |
936 | *paddr = (pfn << page_bits) | (vaddr & page_mask); | |
3b46e624 | 937 | |
b82945bc JM |
938 | return 0; |
939 | } | |
940 | ||
941 | static int virtual_to_physical (CPUState *env, uint64_t *physp, | |
942 | int *zbitsp, int *protp, | |
6ebbf390 | 943 | uint64_t virtual, int mmu_idx, int rw) |
b82945bc JM |
944 | { |
945 | uint64_t sva, ptebase; | |
946 | int seg, page_bits, ret; | |
947 | ||
948 | sva = ((int64_t)(virtual << (64 - VA_BITS))) >> (64 - VA_BITS); | |
949 | if (sva != virtual) | |
950 | seg = -1; | |
951 | else | |
952 | seg = sva >> (VA_BITS - 2); | |
953 | virtual &= ~(0xFFFFFC0000000000ULL << (VA_BITS - 43)); | |
954 | ptebase = get_ptebase(env, virtual); | |
955 | page_bits = get_page_bits(env); | |
956 | ret = 0; | |
957 | switch (seg) { | |
958 | case 0: | |
959 | /* seg1: 3 levels of PTE */ | |
960 | ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, | |
6ebbf390 | 961 | virtual, mmu_idx, rw); |
b82945bc JM |
962 | break; |
963 | case 1: | |
964 | /* seg1: 2 levels of PTE */ | |
965 | ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, | |
6ebbf390 | 966 | virtual, mmu_idx, rw); |
b82945bc JM |
967 | break; |
968 | case 2: | |
969 | /* kernel segment */ | |
6ebbf390 | 970 | if (mmu_idx != 0) { |
b82945bc JM |
971 | ret = 2; |
972 | } else { | |
973 | *physp = virtual; | |
974 | } | |
975 | break; | |
976 | case 3: | |
977 | /* seg1: TB mapped */ | |
978 | ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, | |
6ebbf390 | 979 | virtual, mmu_idx, rw); |
b82945bc JM |
980 | break; |
981 | default: | |
982 | ret = 1; | |
983 | break; | |
984 | } | |
985 | ||
986 | return ret; | |
987 | } | |
988 | ||
989 | /* XXX: code provision */ | |
990 | int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, | |
6ebbf390 | 991 | int mmu_idx, int is_softmmu) |
b82945bc JM |
992 | { |
993 | uint64_t physical, page_size, end; | |
994 | int prot, zbits, ret; | |
995 | ||
6049f4f8 RH |
996 | ret = virtual_to_physical(env, &physical, &zbits, &prot, |
997 | address, mmu_idx, rw); | |
998 | ||
b82945bc JM |
999 | switch (ret) { |
1000 | case 0: | |
1001 | /* No fault */ | |
1002 | page_size = 1ULL << zbits; | |
1003 | address &= ~(page_size - 1); | |
d4c430a8 PB |
1004 | /* FIXME: page_size should probably be passed to tlb_set_page, |
1005 | and this loop removed. */ | |
b82945bc | 1006 | for (end = physical + page_size; physical < end; physical += 0x1000) { |
d4c430a8 PB |
1007 | tlb_set_page(env, address, physical, prot, mmu_idx, |
1008 | TARGET_PAGE_SIZE); | |
b82945bc JM |
1009 | address += 0x1000; |
1010 | } | |
d4c430a8 | 1011 | ret = 0; |
b82945bc JM |
1012 | break; |
1013 | #if 0 | |
1014 | case 1: | |
1015 | env->exception_index = EXCP_DFAULT; | |
1016 | env->ipr[IPR_EXC_ADDR] = address; | |
1017 | ret = 1; | |
1018 | break; | |
1019 | case 2: | |
1020 | env->exception_index = EXCP_ACCESS_VIOLATION; | |
1021 | env->ipr[IPR_EXC_ADDR] = address; | |
1022 | ret = 1; | |
1023 | break; | |
1024 | case 3: | |
1025 | env->exception_index = EXCP_FAULT_ON_READ; | |
1026 | env->ipr[IPR_EXC_ADDR] = address; | |
1027 | ret = 1; | |
1028 | break; | |
1029 | case 4: | |
1030 | env->exception_index = EXCP_FAULT_ON_EXECUTE; | |
1031 | env->ipr[IPR_EXC_ADDR] = address; | |
1032 | ret = 1; | |
1033 | case 5: | |
1034 | env->exception_index = EXCP_FAULT_ON_WRITE; | |
1035 | env->ipr[IPR_EXC_ADDR] = address; | |
1036 | ret = 1; | |
1037 | #endif | |
1038 | default: | |
1039 | /* Should never happen */ | |
1040 | env->exception_index = EXCP_MCHK; | |
1041 | env->ipr[IPR_EXC_ADDR] = address; | |
1042 | ret = 1; | |
1043 | break; | |
1044 | } | |
1045 | ||
1046 | return ret; | |
1047 | } | |
1048 | #endif |