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