1 /*
2 * Copyright (C) 2005-2018 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * Alpha PALcode-related functionality.
29 *
30 * (See http://www.alphalinux.org/docs/alphaahb.html for good descriptions
31 * of many PALcode functions.)
32 */
33
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #include "console.h"
41 #include "cpu.h"
42 #include "machine.h"
43 #include "memory.h"
44 #include "misc.h"
45 #include "symbol.h"
46
47 #include "thirdparty/alpha_prom.h"
48
49
50 /*
51 * alpha_palcode_name():
52 *
53 * Return the name of a PALcode number, as a string.
54 */
alpha_palcode_name(uint32_t palcode,char * buf,size_t buflen)55 void alpha_palcode_name(uint32_t palcode, char *buf, size_t buflen)
56 {
57 switch (palcode) {
58 case 0x00: snprintf(buf, buflen, "PAL_halt"); break;
59 case 0x01: snprintf(buf, buflen, "PAL_cflush"); break;
60 case 0x02: snprintf(buf, buflen, "PAL_draina"); break;
61 case 0x09: snprintf(buf, buflen, "PAL_cserve"); break;
62 case 0x0a: snprintf(buf, buflen, "PAL_swppal"); break;
63 case 0x0d: snprintf(buf, buflen, "PAL_ipir"); break;
64 case 0x10: snprintf(buf, buflen, "PAL_OSF1_rdmces"); break;
65 case 0x11: snprintf(buf, buflen, "PAL_OSF1_wrmces"); break;
66 case 0x2b: snprintf(buf, buflen, "PAL_OSF1_wrfen"); break;
67 case 0x2d: snprintf(buf, buflen, "PAL_OSF1_wrvptptr"); break;
68 case 0x30: snprintf(buf, buflen, "PAL_OSF1_swpctx"); break;
69 case 0x31: snprintf(buf, buflen, "PAL_OSF1_wrval"); break;
70 case 0x32: snprintf(buf, buflen, "PAL_OSF1_rdval"); break;
71 case 0x33: snprintf(buf, buflen, "PAL_OSF1_tbi"); break;
72 case 0x34: snprintf(buf, buflen, "PAL_OSF1_wrent"); break;
73 case 0x35: snprintf(buf, buflen, "PAL_OSF1_swpipl"); break;
74 case 0x36: snprintf(buf, buflen, "PAL_rdps"); break;
75 case 0x37: snprintf(buf, buflen, "PAL_OSF1_wrkgp"); break;
76 case 0x38: snprintf(buf, buflen, "PAL_OSF1_wrusp"); break;
77 case 0x39: snprintf(buf, buflen, "PAL_OSF1_wrperfmon"); break;
78 case 0x3a: snprintf(buf, buflen, "PAL_OSF1_rdusp"); break;
79 case 0x3c: snprintf(buf, buflen, "PAL_whami"); break;
80 case 0x3d: snprintf(buf, buflen, "PAL_OSF1_retsys"); break;
81 case 0x3e: snprintf(buf, buflen, "PAL_wtint"); break;
82 case 0x3f: snprintf(buf, buflen, "PAL_OSF1_rti"); break;
83 case 0x80: snprintf(buf, buflen, "PAL_bpt"); break;
84 case 0x81: snprintf(buf, buflen, "PAL_bugchk"); break;
85 case 0x83: snprintf(buf, buflen, "PAL_OSF1_callsys"); break;
86 case 0x86: snprintf(buf, buflen, "PAL_imb"); break;
87 case 0x92: snprintf(buf, buflen, "PAL_OSF1_urti"); break;
88 case 0x9e: snprintf(buf, buflen, "PAL_rdunique"); break;
89 case 0x9f: snprintf(buf, buflen, "PAL_wrunique"); break;
90 case 0xaa: snprintf(buf, buflen, "PAL_gentrap"); break;
91 case 0xae: snprintf(buf, buflen, "PAL_clrfen"); break;
92 case 0x3fffffe: snprintf(buf, buflen, "GXemul_PROM"); break;
93 default:snprintf(buf, buflen, "UNKNOWN 0x%" PRIx32, palcode);
94 }
95 }
96
97
98 /*
99 * alpha_prom_call():
100 */
alpha_prom_call(struct cpu * cpu)101 void alpha_prom_call(struct cpu *cpu)
102 {
103 uint64_t addr, a1 = cpu->cd.alpha.r[ALPHA_A1];
104 uint64_t a2 = cpu->cd.alpha.r[ALPHA_A2], a3 = cpu->cd.alpha.r[ALPHA_A3];
105 uint64_t len;
106 const char *s = "";
107
108 switch (cpu->cd.alpha.r[ALPHA_A0]) {
109
110 case PROM_R_PUTS:
111 /* a1 = channel, a2 = ptr to buf, a3 = len */
112 for (addr = a2; addr < a2 + a3; addr ++) {
113 unsigned char ch;
114 cpu->memory_rw(cpu, cpu->mem, addr, &ch, sizeof(ch),
115 MEM_READ, CACHE_DATA | NO_EXCEPTIONS);
116 console_putchar(cpu->machine->main_console_handle, ch);
117 }
118 cpu->cd.alpha.r[ALPHA_V0] = a3;
119 break;
120
121 case PROM_R_GETENV:
122 /* a1 = variable id, a2 = char *buf, a3 = bufsize */
123 switch (a1) {
124 case PROM_E_BOOTED_DEV:
125 s = ""; /* TODO */
126 break;
127 case PROM_E_BOOTED_FILE:
128 s = cpu->machine->boot_kernel_filename;
129 break;
130 case PROM_E_BOOTED_OSFLAGS:
131 s = cpu->machine->boot_string_argument;
132 break;
133 case PROM_E_TTY_DEV:
134 s = ""; /* TODO */
135 break;
136 default:fatal("[ Alpha PALcode: GXemul PROM getenv %i: TODO "
137 "]\n", cpu->cd.alpha.r[ALPHA_A1]);
138 cpu->running = 0;
139 }
140 /* Copy at most a3 bytes. */
141 len = a3;
142 if (strlen(s) < len)
143 len = strlen(s) + 1;
144 store_buf(cpu, a2, s, len);
145 break;
146
147 default:fatal("[ Alpha PALcode: GXemul PROM call, a0=0x%" PRIx64" ]\n",
148 (uint64_t) cpu->cd.alpha.r[ALPHA_A0]);
149 cpu->running = 0;
150 }
151
152 /* Return from the PROM call. */
153 cpu->pc = cpu->cd.alpha.r[ALPHA_RA];
154 }
155
156
157 /*
158 * alpha_palcode():
159 *
160 * Execute an Alpha PALcode instruction. (Most of these correspond to
161 * OSF1 palcodes, used by for example NetBSD/alpha.)
162 */
alpha_palcode(struct cpu * cpu,uint32_t palcode)163 void alpha_palcode(struct cpu *cpu, uint32_t palcode)
164 {
165 uint64_t a0 = cpu->cd.alpha.r[ALPHA_A0], a1 = cpu->cd.alpha.r[ALPHA_A1];
166 bool userMode = cpu->cd.alpha.ps & ALPHA_PSL_USERMODE;
167
168 /*
169 * Only these are unprivileged, i.e. can be invoked in user mode,
170 * according to the manual:
171 *
172 * bpt kernel and user
173 * bugchk kernel and user
174 * callsys user
175 * clrfen user
176 * gentrap kernel and user
177 * imb kernel and user
178 * rdunique kernel and user
179 * urti user
180 * wrunique kernel and user
181 */
182
183 if (userMode && !(
184 palcode == 0x80 || palcode == 0x81 || palcode == 0x83 ||
185 palcode == 0xae || palcode == 0xaa || palcode == 0x86 ||
186 palcode == 0x9e || palcode == 0x92 || palcode == 0x9f)) {
187 // opDec fault.
188 fatal("[ Privileged Alpha PALcode called from user mode: TODO ]");
189 cpu->running = 0;
190 return;
191 }
192
193 switch (palcode) {
194 #if 0
195 TODO: Uncomment more again, as I progress with the emulation...
196 Make sure they are correct, as documented in the Manual.
197
198 case 0x02: /* PAL_draina */
199 /* TODO? */
200 break;
201 #endif
202 case 0x10: /* PAL_OSF1_rdmces */
203 /* Return Machine Check status in v0. */
204 cpu->cd.alpha.r[ALPHA_V0] = cpu->cd.alpha.mces;
205 break;
206 case 0x11: /* PAL_OSF1_wrmces */
207 /* Clear bits 0, 1, and 2 of the Machine Check and Error status. */
208 cpu->cd.alpha.mces = ~(cpu->cd.alpha.r[ALPHA_A0] & 7);
209
210 /* Set bits 3 and 4 of the Machine Check and Error status. */
211 cpu->cd.alpha.mces &= ~0x18;
212 cpu->cd.alpha.mces |= (cpu->cd.alpha.r[ALPHA_A0] & 0x18);
213 break;
214 case 0x2b: /* PAL_OSF1_wrfen */
215 /* Floating point enable: a0 = 1 or 0. */
216 /* TODO. Since clrfen is documented as:
217 FEN ← 0
218 (PCBB+40)<0> ← 0
219 then most likely wrfen should do the reverse.
220 */
221 cpu->cd.alpha.pcb.apcb_flags = cpu->cd.alpha.r[ALPHA_A0];
222 store_64bit_word(cpu, cpu->cd.alpha.ctx + 40,
223 cpu->cd.alpha.pcb.apcb_flags);
224 break;
225 case 0x2d: /* PAL_OSF1_wrvptptr */
226 /* Write Virtual Page Table Pointer. a0 = value */
227 cpu->cd.alpha.vptptr = a0;
228 break;
229 case 0x30: /* PAL_OSF1_swpctx */
230 /* Save old context: */
231 store_64bit_word(cpu, cpu->cd.alpha.ctx + 0,
232 cpu->cd.alpha.pcb.apcb_ksp);
233 store_64bit_word(cpu, cpu->cd.alpha.ctx + 8,
234 cpu->cd.alpha.pcb.apcb_usp);
235 store_64bit_word(cpu, cpu->cd.alpha.ctx + 16,
236 cpu->cd.alpha.pcb.apcb_ptbr);
237 store_32bit_word(cpu, cpu->cd.alpha.ctx + 24,
238 cpu->cd.alpha.pcb.apcb_cpc);
239 store_32bit_word(cpu, cpu->cd.alpha.ctx + 28,
240 cpu->cd.alpha.pcb.apcb_asn);
241 store_64bit_word(cpu, cpu->cd.alpha.ctx + 32,
242 cpu->cd.alpha.pcb.apcb_unique);
243 store_64bit_word(cpu, cpu->cd.alpha.ctx + 40,
244 cpu->cd.alpha.pcb.apcb_flags);
245 store_64bit_word(cpu, cpu->cd.alpha.ctx + 48,
246 cpu->cd.alpha.pcb.apcb_decrsv0);
247 store_64bit_word(cpu, cpu->cd.alpha.ctx + 56,
248 cpu->cd.alpha.pcb.apcb_decrsv1);
249
250 /* Return old context in v0. */
251 cpu->cd.alpha.r[ALPHA_A0] = cpu->cd.alpha.ctx;
252
253 /* Load new context: */
254 {
255 cpu->cd.alpha.ctx = a0;
256 uint64_t new_ksp =
257 load_64bit_word(cpu, cpu->cd.alpha.ctx + 0);
258 uint64_t new_usp =
259 load_64bit_word(cpu, cpu->cd.alpha.ctx + 8);
260 uint64_t new_ptbr =
261 load_64bit_word(cpu, cpu->cd.alpha.ctx + 16);
262 uint64_t new_cpc =
263 load_32bit_word(cpu, cpu->cd.alpha.ctx + 24);
264 uint64_t new_asn =
265 load_32bit_word(cpu, cpu->cd.alpha.ctx + 28);
266 uint64_t new_unique =
267 load_64bit_word(cpu, cpu->cd.alpha.ctx + 32);
268 uint64_t new_flags =
269 load_64bit_word(cpu, cpu->cd.alpha.ctx + 40);
270 uint64_t new_decrsv0 =
271 load_64bit_word(cpu, cpu->cd.alpha.ctx + 48);
272 uint64_t new_decrsv1 =
273 load_64bit_word(cpu, cpu->cd.alpha.ctx + 56);
274
275 // Update all variables in the pcb simultaneously:
276 cpu->cd.alpha.pcb.apcb_ksp = new_ksp;
277 cpu->cd.alpha.pcb.apcb_usp = new_usp;
278 cpu->cd.alpha.pcb.apcb_ptbr = new_ptbr;
279 cpu->cd.alpha.pcb.apcb_cpc = new_cpc;
280 cpu->cd.alpha.pcb.apcb_asn = new_asn;
281 cpu->cd.alpha.pcb.apcb_unique = new_unique;
282 cpu->cd.alpha.pcb.apcb_ptbr = new_flags;
283 cpu->cd.alpha.pcb.apcb_decrsv0 = new_decrsv0;
284 cpu->cd.alpha.pcb.apcb_decrsv1 = new_decrsv1;
285
286 // TODO: Don't invalidate EVERYTHING!
287 cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL);
288 }
289 break;
290 #if 0
291 case 0x31: /* PAL_OSF1_wrval */
292 /* a0 = value */
293 cpu->cd.alpha.sysvalue = a0;
294 break;
295 case 0x32: /* PAL_OSF1_rdval */
296 /* return: v0 = value */
297 cpu->cd.alpha.r[ALPHA_V0] = cpu->cd.alpha.sysvalue;
298 break;
299 #endif
300 case 0x33: /* PAL_OSF1_tbi */
301 /*
302 * a0 = op, a1 = vaddr
303 * a0 is ignored for now. TODO.
304 * a0 = 1: ITB invalidate
305 * a0 = 2: DTB invalidate
306 * a0 = 3: both ITB and DTB
307 * a0 = -1: invalidate everything with ASM=0.
308 * a0 = -2: invalidate everything
309 */
310 // debug("[ Alpha PALcode: PAL_OSF1_tbi: a0=%" PRIi64" a1=0x%"
311 // PRIx64" ]\n", (int64_t)a0, (uint64_t)a1);
312 if (a0 >= 1)
313 cpu->invalidate_translation_caches(cpu, a1, INVALIDATE_VADDR);
314 else
315 cpu->invalidate_translation_caches(cpu, 0, INVALIDATE_ALL);
316 break;
317 case 0x34: /* PAL_OSF1_wrent (Write System Entry Address) */
318 /* a0 = new vector, a1 = vector selector */
319 if (a1 < N_ALPHA_KENTRY)
320 cpu->cd.alpha.kentry[a1] = a0;
321 else {
322 fatal("[ Alpha PALcode: PAL_OSF1_wrent: attempt to "
323 "write to non-implemented selector %i ]\n",
324 (int)a1);
325 cpu->running = 0;
326 }
327 break;
328 case 0x35: /* PAL_OSF1_swpipl */
329 /* a0 = new ipl, v0 = return old ipl */
330 cpu->cd.alpha.r[ALPHA_V0] = cpu->cd.alpha.ps & ALPHA_PSL_IPL_MASK;
331 cpu->cd.alpha.ps &= ~ALPHA_PSL_IPL_MASK;
332 cpu->cd.alpha.ps |= (a0 & ALPHA_PSL_IPL_MASK);
333 break;
334 case 0x36: /* PAL_rdps */
335 cpu->cd.alpha.r[ALPHA_V0] = cpu->cd.alpha.ps;
336 break;
337 case 0x37: /* PAL_OSF1_wrkgp */
338 cpu->cd.alpha.kgp = a0;
339 break;
340 #if 0
341 case 0x38: /* PAL_OSF1_wrusp */
342 /* a0 = value */
343 cpu->cd.alpha.pcb.apcb_usp = a0;
344 break;
345 case 0x3a: /* PAL_OSF1_rdusp */
346 /* return: v0 = value */
347 cpu->cd.alpha.r[ALPHA_V0] = cpu->cd.alpha.pcb.apcb_usp;
348 break;
349 #endif
350 case 0x3c: /* PAL_whami */
351 /* Returns CPU id in v0: */
352 cpu->cd.alpha.r[ALPHA_V0] = cpu->cpu_id;
353 break;
354 #if 0
355 case 0x81: /* PAL_bugchk */
356 cpu->running = 0;
357 break;
358 case 0x83: /* PAL_OSF1_syscall */
359 fatal("[ Alpha PALcode: syscall, but no syscall handler ]\n");
360 cpu->running = 0;
361 break;
362 #endif
363 case 0x86: /* PAL_imb */
364 /* TODO */
365 break;
366 #if 0
367 case 0x3fffffc:
368 fatal("[ Alpha: KENTRY not set! Halting. ]");
369 cpu->running = 0;
370 break;
371 case 0x3fffffd:
372 fatal("[ Alpha PALcode: Fixup: TODO ]\n");
373 /* Return from the fixup call. */
374 cpu->cd.alpha.r[ALPHA_V0] = 0; /* Success? */
375 cpu->pc = cpu->cd.alpha.r[ALPHA_RA];
376 break;
377 #endif
378 case 0x3fffffe:
379 alpha_prom_call(cpu);
380 break;
381 default:fatal("[ Alpha PALcode 0x%x unimplemented! ]\n", palcode);
382 cpu->running = 0;
383 }
384
385 /*
386 * Many PALcode instructions in the Alpha manual (and in NetBSD/Alpha
387 * source code) are described with "clobbers t0, t8-t11", some also
388 * with a0, and some also with a1 included.
389 *
390 * However, it's easier to just leave the registers as they are.
391 */
392 }
393
394