Home | History | Annotate | Line # | Download | only in sgimips
      1 /*	$NetBSD: arcemu.c,v 1.27 2026/06/11 06:30:55 rumble Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004 Steve Rumble
      5  * Copyright (c) 2004 Antti Kantee
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. The name of the author may not be used to endorse or promote products
     17  *    derived from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: arcemu.c,v 1.27 2026/06/11 06:30:55 rumble Exp $");
     33 
     34 #ifndef _LP64
     35 
     36 #include <sys/param.h>
     37 #include <sys/systm.h>
     38 
     39 #include <machine/machtype.h>
     40 #include <sys/bus.h>
     41 
     42 #include <dev/cons.h>
     43 
     44 #include <dev/arcbios/arcbios.h>
     45 #include <dev/arcbios/arcbiosvar.h>
     46 
     47 #include <dev/ic/wd33c93reg.h>
     48 
     49 #define _ARCEMU_PRIVATE
     50 #include <sgimips/sgimips/arcemu.h>
     51 #include <sgimips/sgimips/prom.h>
     52 #include <sgimips/dev/picreg.h>
     53 
     54 static struct consdev arcemu_cn = {
     55 	NULL,			/* probe */
     56 	NULL,			/* init */
     57 	NULL,			/* getc */ /* XXX: this would be nice */
     58 	arcemu_prom_putc,	/* putc */
     59 	nullcnpollc,		/* pollc */
     60 	NULL,			/* bell */
     61 	NULL,
     62 	NULL,
     63 	NODEV,
     64 	CN_NORMAL,
     65 };
     66 
     67 /*
     68  * Emulate various ARCBIOS functions on pre-ARCS sgimips
     69  * machines (<= IP17).
     70  */
     71 static struct arcbios_fv arcemu_v = {
     72 	.Load =				ARCEMU_UNIMPL,
     73 	.Invoke =			ARCEMU_UNIMPL,
     74 	.Execute = 			ARCEMU_UNIMPL,
     75 	.Halt =				ARCEMU_UNIMPL,
     76 	.PowerDown =			ARCEMU_UNIMPL,
     77 	.Restart = 			ARCEMU_UNIMPL,
     78 	.Reboot =			ARCEMU_UNIMPL,
     79 	.EnterInteractiveMode =		ARCEMU_UNIMPL,
     80 	.ReturnFromMain =		0,
     81 	.GetPeer =			ARCEMU_UNIMPL,
     82 	.GetChild =			ARCEMU_UNIMPL,
     83 	.GetParent =			ARCEMU_UNIMPL,
     84 	.GetConfigurationData =		ARCEMU_UNIMPL,
     85 	.AddChild =			ARCEMU_UNIMPL,
     86 	.DeleteComponent =		ARCEMU_UNIMPL,
     87 	.GetComponent =			ARCEMU_UNIMPL,
     88 	.SaveConfiguration =		ARCEMU_UNIMPL,
     89 	.GetSystemId =			ARCEMU_UNIMPL,
     90 	.GetMemoryDescriptor =		ARCEMU_UNIMPL,
     91 	.Signal =			0,
     92 	.GetTime =			ARCEMU_UNIMPL,
     93 	.GetRelativeTime =		ARCEMU_UNIMPL,
     94 	.GetDirectoryEntry =		ARCEMU_UNIMPL,
     95 	.Open =				ARCEMU_UNIMPL,
     96 	.Close =			ARCEMU_UNIMPL,
     97 	.Read =				ARCEMU_UNIMPL,
     98 	.GetReadStatus =		ARCEMU_UNIMPL,
     99 	.Write =			ARCEMU_UNIMPL,
    100 	.Seek =				ARCEMU_UNIMPL,
    101 	.Mount =			ARCEMU_UNIMPL,
    102 	.GetEnvironmentVariable =	ARCEMU_UNIMPL,
    103 	.SetEnvironmentVariable =	ARCEMU_UNIMPL,
    104 	.GetFileInformation =		ARCEMU_UNIMPL,
    105 	.SetFileInformation =		ARCEMU_UNIMPL,
    106 	.FlushAllCaches =		ARCEMU_UNIMPL
    107 };
    108 
    109 /*
    110  * Establish our emulated ARCBIOS vector or return ARCBIOS failure.
    111  */
    112 int
    113 arcemu_init(const char **env)
    114 {
    115 	switch ((mach_type = arcemu_identify())) {
    116 	case MACH_SGI_IP6 | MACH_SGI_IP10:
    117 	case MACH_SGI_IP12:
    118 		arcemu_ipN_init(ARCEMU_ENVOK(env) ? env : NULL);
    119 		break;
    120 
    121 	default:
    122 		return (1);
    123 	}
    124 
    125 	ARCBIOS = &arcemu_v;
    126 
    127 	return (0);
    128 }
    129 
    130 /*
    131  * Attempt to identify the SGI IP%d platform.
    132  */
    133 static int
    134 arcemu_identify(void)
    135 {
    136 	/*
    137 	 * Since all pre-ARCS machines have PROMs at the same address, we just
    138 	 * scan it for known IP-specific substrings. The smallest PROM is
    139 	 * apparently 1Mbit, so we initially search only the first 128KB as
    140 	 * badaddr isn't usable yet.
    141 	 */
    142 	static struct {
    143 		const char *pattern;
    144 		int mach_type;
    145 	} patterns[] = {
    146 		{ "IP12/GR2 PROM", MACH_SGI_IP12 },
    147 		{ "IP6 Processor", MACH_SGI_IP6 },
    148 		{ " PROM IP6 ", MACH_SGI_IP6 },
    149 		{ " PROM IP4 ", MACH_SGI_IP4 },
    150 		{ " IP 9 ", MACH_SGI_IP9 },
    151 		{ " IP 7 ", MACH_SGI_IP7 },
    152 		{ " IP 5 ", MACH_SGI_IP5 },
    153 		{ NULL, -1 },
    154 	};
    155 
    156 	int mach = -1;
    157 	for (int i = 1; i < 3; i++) {
    158 		for (int j = 0; patterns[j].pattern != NULL; j++) {
    159 			int len = i * 128 * 1024;
    160 			if (memmem((void*)MIPS_PHYS_TO_KSEG1(0x1fc00000), len,
    161 			    patterns[j].pattern,
    162 			    strlen(patterns[j].pattern)) != NULL) {
    163 				mach = patterns[j].mach_type;
    164 				break;
    165 			}
    166 		}
    167 	}
    168 
    169 	if (mach == -1) {
    170 		sgi_prom_printf("Failed to identify Pre-ARCBIOS machine. "
    171 		    "Assuming IP12.\n");
    172 		return (MACH_SGI_IP12);
    173 	}
    174 
    175 	sgi_prom_printf("Pre-ARCBIOS machine identified as IP%d\n",
    176 	    mach);
    177 	return (mach);
    178 }
    179 
    180 static boolean_t
    181 extractenv(const char **env, const char *key, char *dest, int len)
    182 {
    183 	int i;
    184 
    185 	if (env == NULL)
    186 		return (false);
    187 
    188 	for (i = 0; env[i] != NULL; i++) {
    189 		if (strncasecmp(env[i], key, strlen(key)) == 0 &&
    190 		    env[i][strlen(key)] == '=') {
    191 			strlcpy(dest, strchr(env[i], '=') + 1, len);
    192 			return (true);
    193 		}
    194 	}
    195 
    196 	return (false);
    197 }
    198 
    199 /*
    200  * The following matches IP6's and IP12's NVRAM memory layout
    201  */
    202 static struct arcemu_nvramdata {
    203 	char bootmode;
    204 	char state;
    205 	char netaddr[16];
    206 	char lbaud[5];
    207 	char rbaud[5];
    208 	char console;
    209 	char sccolor[6];
    210 	char pgcolor[6];
    211 	char lgcolor[6];
    212 	char keydb;
    213 	char pad0;
    214 	char checksum;
    215 	char diskless;
    216 	char nokdb;
    217 	char bootfile[50];
    218 	char passwd[17];
    219 	char volume[3];
    220 	uint8_t enaddr[6];
    221 } nvram;
    222 
    223 static char enaddr[18];
    224 
    225 static struct arcemu_sgienv {
    226 	char dbaud[5];
    227 	char rbaud[5];
    228 	char bootmode;
    229 	char console;
    230 	char diskless;
    231 	char volume[4];
    232 	char cpufreq[3];
    233 	char gfx[32];
    234 	char netaddr[32];
    235 	char dlserver[32];
    236 	char osloadoptions[32];
    237 } sgienv;
    238 
    239 /*
    240  * EEPROM reading routines. IP6's wiring is sufficiently ugly and the routine
    241  * sufficiently small that we just roll our own, rather than contorting the MD
    242  * driver.
    243  */
    244 static void
    245 eeprom_read(uint8_t *eeprom_buf, size_t len, int is_cs56,
    246     void (*set_pre)(int), void (*set_cs)(int), void (*set_sk)(int),
    247     int (*get_do)(void), void (*set_di)(int))
    248 {
    249 	int i, j;
    250 
    251 	for (i = 0; i < (len / 2); i++) {
    252 		uint16_t instr = 0xc000 | (i << ((is_cs56) ? 5 : 7));
    253 		uint16_t bitword = 0;
    254 
    255 		set_di(0);
    256 		set_sk(0);
    257 		set_pre(0);
    258 		set_cs(0);
    259 		set_cs(1);
    260 		set_sk(1);
    261 
    262 		for (j = 0; j < ((is_cs56) ? 11 : 9); j++) {
    263 			set_di(instr & 0x8000);
    264 			set_sk(0);
    265 			set_sk(1);
    266 			instr <<= 1;
    267 		}
    268 
    269 		set_di(0);
    270 
    271 		for (j = 0; j < 17; j++) {
    272 			bitword = (bitword << 1) | get_do();
    273 			set_sk(0);
    274 			set_sk(1);
    275 		}
    276 
    277 		eeprom_buf[i * 2 + 0] = bitword >> 8;
    278 		eeprom_buf[i * 2 + 1] = bitword & 0xff;
    279 
    280 		set_sk(0);
    281 		set_cs(0);
    282 	}
    283 }
    284 
    285 /*
    286  * Read the EEPROM. It's not clear which machines have which parts, and
    287  * there's a difference in instruction length between the two. We'll try
    288  * both and see which doesn't give us garbage.
    289  */
    290 static void
    291 arcemu_eeprom_read(void)
    292 {
    293 	int i;
    294 
    295 	/* try long instruction length first (the only one I've seen) */
    296 	for (i = 1; i >= 0; i--) {
    297 		if (mach_type == (MACH_SGI_IP6 | MACH_SGI_IP10)) {
    298 			eeprom_read((uint8_t *)&nvram, sizeof(nvram), i,
    299 			    ip6_set_pre, ip6_set_cs, ip6_set_sk,
    300 			    ip6_get_do,  ip6_set_di);
    301 		} else {
    302 			eeprom_read((uint8_t *)&nvram, sizeof(nvram), i,
    303 			    ip12_set_pre, ip12_set_cs, ip12_set_sk,
    304 			    ip12_get_do,  ip12_set_di);
    305 		}
    306 
    307 		if (nvram.enaddr[0] == 0x08 && nvram.enaddr[1] == 0x00 &&
    308 		    nvram.enaddr[2] == 0x69)
    309 			break;
    310 
    311 		if (memcmp(nvram.lbaud, "9600\x0", 5) == 0)
    312 			break;
    313 
    314 		if (memcmp(nvram.bootfile, "dksc(", 5) == 0 ||
    315 		    memcmp(nvram.bootfile, "bootp(", 6) == 0)
    316 			break;
    317 	}
    318 
    319 	/* cache enaddr string */
    320 	snprintf(enaddr, sizeof(enaddr), "%02x:%02x:%02x:%02x:%02x:%02x",
    321 	    nvram.enaddr[0],
    322 	    nvram.enaddr[1],
    323 	    nvram.enaddr[2],
    324 	    nvram.enaddr[3],
    325 	    nvram.enaddr[4],
    326 	    nvram.enaddr[5]);
    327 }
    328 
    329 static void
    330 arcemu_ipN_init(const char **env)
    331 {
    332 
    333 	arcemu_v.GetPeer =		  (intptr_t)arcemu_GetPeer;
    334 	arcemu_v.GetChild =		  (intptr_t)arcemu_GetChild;
    335 	arcemu_v.GetEnvironmentVariable = (intptr_t)arcemu_GetEnvironmentVariable;
    336 
    337 	if (mach_type == MACH_SGI_IP6 || mach_type == MACH_SGI_IP10)
    338 		arcemu_v.GetMemoryDescriptor = (intptr_t)arcemu_ip6_GetMemoryDescriptor;
    339 	else if (mach_type == MACH_SGI_IP12)
    340 		arcemu_v.GetMemoryDescriptor = (intptr_t)arcemu_ip12_GetMemoryDescriptor;
    341 
    342 	arcemu_v.Reboot =                 (intptr_t)sgi_prom_reset;
    343 	arcemu_v.PowerDown =		  (intptr_t)sgi_prom_reinit;
    344 	arcemu_v.EnterInteractiveMode =   (intptr_t)sgi_prom_reinit;
    345 
    346 	cn_tab = &arcemu_cn;
    347 
    348 	arcemu_eeprom_read();
    349 
    350 	memset(&sgienv, 0, sizeof(sgienv));
    351 	extractenv(env, "dbaud", sgienv.dbaud, sizeof(sgienv.dbaud));
    352 	extractenv(env, "rbaud", sgienv.rbaud, sizeof(sgienv.rbaud));
    353 	extractenv(env, "bootmode",&sgienv.bootmode, sizeof(sgienv.bootmode));
    354 	extractenv(env, "console", &sgienv.console, sizeof(sgienv.console));
    355 	extractenv(env, "diskless",&sgienv.diskless, sizeof(sgienv.diskless));
    356 	extractenv(env, "volume", sgienv.volume, sizeof(sgienv.volume));
    357 	extractenv(env, "cpufreq", sgienv.cpufreq, sizeof(sgienv.cpufreq));
    358 	extractenv(env, "gfx", sgienv.gfx, sizeof(sgienv.gfx));
    359 	extractenv(env, "netaddr", sgienv.netaddr, sizeof(sgienv.netaddr));
    360 	extractenv(env, "dlserver", sgienv.dlserver, sizeof(sgienv.dlserver));
    361 	extractenv(env, "osloadoptions", sgienv.osloadoptions,
    362 	    sizeof(sgienv.osloadoptions));
    363 
    364 	strcpy(arcbios_sysid_vendor, "SGI");
    365 	if (mach_type == MACH_SGI_IP6 || mach_type == MACH_SGI_IP10) {
    366 		strcpy(arcbios_system_identifier, "SGI-IP6");
    367 		strcpy(arcbios_sysid_product, "IP6");
    368 	} else if (mach_type == MACH_SGI_IP12) {
    369 		strcpy(arcbios_system_identifier, "SGI-IP12");
    370 		strcpy(arcbios_sysid_product, "IP12");
    371 	}
    372 }
    373 
    374 static void *
    375 arcemu_GetPeer(void *node)
    376 {
    377 	int i;
    378 
    379 	if (node == NULL)
    380 		return (NULL);
    381 
    382 	for (i = 0; arcemu_component_tree[i].Class != -1; i++) {
    383 		if (&arcemu_component_tree[i] == node &&
    384 		     arcemu_component_tree[i+1].Class != -1)
    385 			return (&arcemu_component_tree[i+1]);
    386 	}
    387 
    388 	return (NULL);
    389 }
    390 
    391 static void *
    392 arcemu_GetChild(void *node)
    393 {
    394 
    395 	/*
    396 	 * ARCBIOS just walks the entire tree, so we'll represent our
    397 	 * emulated tree as a single level and avoid messy hierarchies.
    398 	 */
    399 	if (node == NULL)
    400 		return (&arcemu_component_tree[0]);
    401 
    402 	return (NULL);
    403 }
    404 
    405 static const char *
    406 arcemu_GetEnvironmentVariable(const char *var)
    407 {
    408 
    409 	/* 'd'ebug (serial), 'g'raphics, 'G'raphics w/ logo */
    410 
    411 	/* XXX This does not indicate the actual current console */
    412 	if (strcasecmp("ConsoleOut", var) == 0) {
    413 		/* if no keyboard is attached, we should default to serial */
    414 		if (strstr(sgienv.gfx, "dead") != NULL)
    415 			return "serial(0)";
    416 
    417 		switch (nvram.console) {
    418 		case 'd':
    419 		case 'D':
    420 		case 's':
    421 		case 'S':
    422 			return "serial(0)";
    423 		case 'g':
    424 		case 'G':
    425 			return "video()";
    426 		default:
    427 			printf("arcemu: unknown console \"%c\", using serial\n",
    428 			    nvram.console);
    429 			return "serial(0)";
    430 		}
    431 	}
    432 
    433 	if (strcasecmp("cpufreq", var) == 0) {
    434 		if (sgienv.cpufreq[0] != '\0')
    435 			return (sgienv.cpufreq);
    436 
    437 		/* IP6 is 12, IP10 is 20 */
    438 		if (mach_type == MACH_SGI_IP6 || mach_type == MACH_SGI_IP10)
    439 			return ("16");
    440 
    441 		/* IP12 is 30, 33 or 36 */
    442 		return ("33");
    443 	}
    444 
    445 	if (strcasecmp("dbaud", var) == 0)
    446 		return (nvram.lbaud);
    447 
    448 	if (strcasecmp("eaddr", var) == 0)
    449 		return (enaddr);
    450 
    451 	if (strcasecmp("gfx", var) == 0) {
    452 		if (sgienv.gfx[0] != '\0')
    453 			return (sgienv.gfx);
    454 	}
    455 
    456 	/*
    457 	 * Ugly Kludge Alert!
    458 	 *
    459 	 * Since we don't yet have an ip12 bootloader, we can only squish
    460 	 * a kernel into the volume header. However, this makes the bootfile
    461 	 * something like 'dksc(0,1,8)', which translates into 'sd0i'. Ick.
    462 	 * Munge what we return to always map to 'sd0a'. Lord have mercy.
    463 	 *
    464 	 * makebootdev() can handle "dksc(a,b,c)/netbsd", etc already
    465 	 */
    466 	if (strcasecmp("OSLoadPartition", var) == 0) {
    467 		char *hack;
    468 
    469 		hack = strstr(nvram.bootfile, ",8)");
    470 		if (hack != NULL)
    471 			hack[1] = '0';
    472 		return (nvram.bootfile);
    473 	}
    474 
    475 	/* pull filename from e.g.: "dksc(0,1,0)netbsd" */
    476 	if (strcasecmp("OSLoadFilename", var) == 0) {
    477 		char *file;
    478 
    479 		if ((file = strrchr(nvram.bootfile, ')')) != NULL)
    480 			return (file + 1);
    481 		else
    482 			return (NULL);
    483 	}
    484 
    485 	/*
    486 	 * As far as I can tell, old systems had no analogue of OSLoadOptions.
    487 	 * So, to allow forcing of single user mode, we accommodate the
    488 	 * user setting the ARCBIOSy environment variable "OSLoadOptions" to
    489 	 * something other than "auto".
    490 	 */
    491 	if (strcasecmp("OSLoadOptions", var) == 0) {
    492 		if (sgienv.osloadoptions[0] == '\0')
    493 			return ("auto");
    494 		else
    495 			return (sgienv.osloadoptions);
    496 	}
    497 
    498 	return (NULL);
    499 }
    500 
    501 static void *
    502 arcemu_ip6_GetMemoryDescriptor(void *mem)
    503 {
    504 	static struct arcbios_mem am;
    505 	static int invoc;
    506 
    507 	unsigned int pages;
    508 	u_int8_t memcfg;
    509 
    510 	if (mem == NULL) {
    511 		/*
    512 		 * We know pages 0, 1 are occupied, emulate the reserved space.
    513 		 */
    514 		am.Type = ARCBIOS_MEM_ExceptionBlock;
    515 		am.BasePage = 0;
    516 		am.PageCount = 2;
    517 
    518 		invoc = 0;
    519 		return (&am);
    520 	}
    521 
    522 	memcfg = *(volatile uint8_t *)MIPS_PHYS_TO_KSEG1(0x1f800000) & 0x1f;
    523 	pages = (memcfg & 0x0f) + 1;
    524 
    525 	/* 4MB or 1MB units? */
    526 	if (memcfg & 0x10) {
    527 		pages *= 4096;
    528 
    529 #if 0 // may cause an exception and bring us down in flames; disable until tested
    530 		/* check for aliasing and adjust page count if necessary */
    531 		volatile uint8_t *tp1, *tp2;
    532 		uint8_t tmp;
    533 
    534 		tp1 = (volatile uint8_t *)MIPS_PHYS_TO_KSEG1((pages - 4096) << 12);
    535 		tp2 = tp1 + (4 * 1024 * 1024);
    536 
    537 		tmp = *tp1;
    538 		*tp2 = ~tmp;
    539 		if (*tp1 != tmp)
    540 			pages -= (3 * 1024);
    541 #endif
    542 	} else {
    543 		pages *= 1024;
    544 	}
    545 
    546 	/*
    547 	 * It appears that the PROM's stack is at 0x400000 in physical memory.
    548 	 * Don't destroy it, and assume (based on IP12 specs), that the prom bss
    549 	 * is below it at 0x380000. This is probably overly conservative.
    550 	 *
    551 	 * Also note that we preserve the first two pages.
    552 	 */
    553 	switch (invoc) {
    554 	case 0:
    555 		/* free: pages [2, 896) */
    556 		am.BasePage = 2;
    557 		am.PageCount = 894;
    558 		am.Type = ARCBIOS_MEM_FreeContiguous;
    559 		break;
    560 
    561 	case 1:
    562 		/* prom bss/stack: pages [896, 1023) */
    563 		am.BasePage = 896;
    564 		am.PageCount = 128;
    565 		am.Type = ARCBIOS_MEM_FirmwareTemporary;
    566 		break;
    567 
    568 	case 2:
    569 		/* free: pages [1024, ...) */
    570 		am.BasePage = 1024;
    571 		if (pages < 1024)
    572 			am.PageCount = 0;
    573 		else
    574 			am.PageCount = pages - 1024;
    575 		am.Type = ARCBIOS_MEM_FreeContiguous;
    576 		break;
    577 
    578 	default:
    579 		return (NULL);
    580 	}
    581 
    582 	invoc++;
    583 	return (&am);
    584 }
    585 
    586 static struct arcbios_mem
    587 arcemu_mem(int type, int base, int count)
    588 {
    589 	struct arcbios_mem am = {
    590 		.Type = type,
    591 		.BasePage = base,
    592 		.PageCount = count
    593 	};
    594 	return am;
    595 }
    596 
    597 /*
    598  * Real ARCBIOS reports the first 2 pages as 'Exception' and 'SystemParameter'
    599  * blocks. We just use one descriptor for both.
    600  *
    601  * Real ARCBIOS also reports where the kernel was loaded as LoadedProgram, but
    602  * we can just advertise it as free memory here (machdep code sorts out where
    603  * the kernel is anyway).
    604  */
    605 static void *
    606 arcemu_ip12_GetMemoryDescriptor(void *mem)
    607 {
    608 	static int call_num = 0;
    609 	static struct arcbios_mem am[8];
    610 	static int num_am = 0;
    611 
    612 	if (num_am == 0) {
    613 		u_int32_t memcfg;
    614 
    615 		/*
    616 		 * Pages [896,1024) -- 3.5MiB to 4MiB -- are PROM bss and stack.
    617 		 * That should always be within a single bank, but on very low
    618 		 * memory systems it might be in bank 1 instead of bank 0.
    619 		 */
    620 
    621 		for (int bank = 0; bank < 4; bank++) {
    622 			/*
    623 			 * 'volatile' prevents the compiler from emitting
    624 			 * 16-bit loads.
    625 			 */
    626 			switch (bank) {
    627 			case 0:
    628 				memcfg = *(volatile u_int32_t *)
    629 				    MIPS_PHYS_TO_KSEG1(PIC_MEMCFG0_PHYSADDR) >> 16;
    630 				break;
    631 
    632 			case 1:
    633 				memcfg = *(volatile u_int32_t *)
    634 				    MIPS_PHYS_TO_KSEG1(PIC_MEMCFG0_PHYSADDR) & 0xffff;
    635 				break;
    636 
    637 			case 2:
    638 				memcfg = *(volatile u_int32_t *)
    639 				    MIPS_PHYS_TO_KSEG1(PIC_MEMCFG1_PHYSADDR) >> 16;
    640 				break;
    641 
    642 			case 3:
    643 				memcfg = *(volatile u_int32_t *)
    644 				    MIPS_PHYS_TO_KSEG1(PIC_MEMCFG1_PHYSADDR) & 0xffff;
    645 				break;
    646 			}
    647 
    648 			const int pgsize = ARCBIOS_PAGESIZE;
    649 			int bp = PIC_MEMCFG_ADDR(memcfg) / pgsize;
    650 			int pc = PIC_MEMCFG_SIZ(memcfg) / pgsize;
    651 			int ep = bp + pc;
    652 
    653 			const int BadMemory = ARCBIOS_MEM_BadMemory;
    654 			const int FreeMemory = ARCBIOS_MEM_FreeContiguous;
    655 			const int ExceptionBlock = ARCBIOS_MEM_ExceptionBlock;
    656 			const int PromMemory = ARCBIOS_MEM_FirmwareTemporary;
    657 
    658 			if (memcfg == PIC_MEMCFG_BADADDR) {
    659 				sgi_prom_printf("Bank %d: No RAM present\n", bank);
    660 				am[num_am++] = arcemu_mem(BadMemory, bp, 0);
    661 			} else if (bp >= 1024) {
    662 				sgi_prom_printf("Bank %d base 0x%x pages %d\n",
    663 				    bank, bp, pc);
    664 				am[num_am++] = arcemu_mem(FreeMemory, bp, pc);
    665 			} else {
    666 				sgi_prom_printf("Bank %d base 0x%x pages %d",
    667 				    bank, bp, pc);
    668 
    669 				int reserved = 0;
    670 
    671 				/* Exclude exception block. */
    672 				if (bp == 0) {
    673 					am[num_am++] = arcemu_mem(
    674 					    ExceptionBlock, 0, 2);
    675 					bp += 2;
    676 					pc -= 2;
    677 					reserved += 2;
    678 				}
    679 
    680 				/* Exclude PROM bss/stack. */
    681 				if (bp < 896 && ep >= 1024) {
    682 					am[num_am++] = arcemu_mem(
    683 					    FreeMemory, bp, 896 - bp);
    684 					am[num_am++] = arcemu_mem(
    685 					    PromMemory, 896, 128);
    686 					am[num_am++] = arcemu_mem(
    687 					    FreeMemory, 1024, ep - 1024);
    688 					reserved += 128;
    689 				} else {
    690 					am[num_am++] = arcemu_mem(
    691 					    FreeMemory, bp, pc);
    692 				}
    693 
    694 				if (reserved > 0) {
    695 					sgi_prom_printf(" (%d reserved)",
    696 					    reserved);
    697 				}
    698 				sgi_prom_printf("\n");
    699 
    700 			}
    701 		}
    702 
    703 	}
    704 
    705 	return ((call_num < num_am) ? &am[call_num++] : NULL);
    706 }
    707 
    708 /*
    709  * If this breaks.. well.. then it breaks.
    710  */
    711 static void
    712 arcemu_prom_putc(dev_t dummy, int c)
    713 {
    714 	sgi_prom_printf("%c", c);
    715 }
    716 
    717 /* Unimplemented Vector */
    718 static void
    719 arcemu_unimpl(void)
    720 {
    721 
    722 	panic("arcemu vector not established on IP%d", mach_type);
    723 }
    724 #endif /* !_LP64 */
    725