1 /* $NetBSD: arcemu.c,v 1.24 2018/11/08 06:43:52 msaitoh 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.24 2018/11/08 06:43:52 msaitoh 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/dev/picreg.h> 52 53 static struct consdev arcemu_cn = { 54 NULL, /* probe */ 55 NULL, /* init */ 56 NULL, /* getc */ /* XXX: this would be nice */ 57 arcemu_prom_putc, /* putc */ 58 nullcnpollc, /* pollc */ 59 NULL, /* bell */ 60 NULL, 61 NULL, 62 NODEV, 63 CN_NORMAL, 64 }; 65 66 /* 67 * Emulate various ARCBIOS functions on pre-ARCS sgimips 68 * machines (<= IP17). 69 */ 70 static struct arcbios_fv arcemu_v = { 71 .Load = ARCEMU_UNIMPL, 72 .Invoke = ARCEMU_UNIMPL, 73 .Execute = ARCEMU_UNIMPL, 74 .Halt = ARCEMU_UNIMPL, 75 .PowerDown = ARCEMU_UNIMPL, 76 .Restart = ARCEMU_UNIMPL, 77 .Reboot = ARCEMU_UNIMPL, 78 .EnterInteractiveMode = ARCEMU_UNIMPL, 79 .ReturnFromMain = 0, 80 .GetPeer = ARCEMU_UNIMPL, 81 .GetChild = ARCEMU_UNIMPL, 82 .GetParent = ARCEMU_UNIMPL, 83 .GetConfigurationData = ARCEMU_UNIMPL, 84 .AddChild = ARCEMU_UNIMPL, 85 .DeleteComponent = ARCEMU_UNIMPL, 86 .GetComponent = ARCEMU_UNIMPL, 87 .SaveConfiguration = ARCEMU_UNIMPL, 88 .GetSystemId = ARCEMU_UNIMPL, 89 .GetMemoryDescriptor = ARCEMU_UNIMPL, 90 .Signal = 0, 91 .GetTime = ARCEMU_UNIMPL, 92 .GetRelativeTime = ARCEMU_UNIMPL, 93 .GetDirectoryEntry = ARCEMU_UNIMPL, 94 .Open = ARCEMU_UNIMPL, 95 .Close = ARCEMU_UNIMPL, 96 .Read = ARCEMU_UNIMPL, 97 .GetReadStatus = ARCEMU_UNIMPL, 98 .Write = ARCEMU_UNIMPL, 99 .Seek = ARCEMU_UNIMPL, 100 .Mount = ARCEMU_UNIMPL, 101 .GetEnvironmentVariable = ARCEMU_UNIMPL, 102 .SetEnvironmentVariable = ARCEMU_UNIMPL, 103 .GetFileInformation = ARCEMU_UNIMPL, 104 .SetFileInformation = ARCEMU_UNIMPL, 105 .FlushAllCaches = ARCEMU_UNIMPL 106 }; 107 108 /* 109 * Establish our emulated ARCBIOS vector or return ARCBIOS failure. 110 */ 111 int 112 arcemu_init(const char **env) 113 { 114 switch ((mach_type = arcemu_identify())) { 115 case MACH_SGI_IP6 | MACH_SGI_IP10: 116 case MACH_SGI_IP12: 117 arcemu_ipN_init(ARCEMU_ENVOK(env) ? env : NULL); 118 break; 119 120 default: 121 return (1); 122 } 123 124 ARCBIOS = &arcemu_v; 125 126 return (0); 127 } 128 129 /* 130 * Attempt to identify the SGI IP%d platform. This is extra ugly since 131 * we don't yet have badaddr to fall back on. 132 */ 133 static int 134 arcemu_identify(void) 135 { 136 int mach; 137 138 /* 139 * Try to write a value to one of IP12's pic(4) graphics DMA registers. 140 * This is at the same location as four byte parity strobes on IP6, 141 * which appear to always read 0. 142 */ 143 *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(0x1faa0000) = 0xdeadbeef; 144 DELAY(1000); 145 if (*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(0x1faa0000) == 0xdeadbeef) 146 mach = MACH_SGI_IP12; 147 else 148 mach = MACH_SGI_IP6; 149 *(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(0x1faa0000) = 0; 150 (void)*(volatile uint32_t *)MIPS_PHYS_TO_KSEG1(0x1faa0000); 151 152 return (mach); 153 } 154 155 static boolean_t 156 extractenv(const char **env, const char *key, char *dest, int len) 157 { 158 int i; 159 160 if (env == NULL) 161 return (false); 162 163 for (i = 0; env[i] != NULL; i++) { 164 if (strncasecmp(env[i], key, strlen(key)) == 0 && 165 env[i][strlen(key)] == '=') { 166 strlcpy(dest, strchr(env[i], '=') + 1, len); 167 return (true); 168 } 169 } 170 171 return (false); 172 } 173 174 /* Prom Vectors */ 175 static void (*sgi_prom_reset)(void) = (void *)MIPS_PHYS_TO_KSEG1(0x1fc00000); 176 static void (*sgi_prom_reinit)(void) =(void *)MIPS_PHYS_TO_KSEG1(0x1fc00018); 177 static int (*sgi_prom_printf)(const char *, ...) = 178 (void *)MIPS_PHYS_TO_KSEG1(0x1fc00080); 179 180 /* 181 * The following matches IP6's and IP12's NVRAM memory layout 182 */ 183 static struct arcemu_nvramdata { 184 char bootmode; 185 char state; 186 char netaddr[16]; 187 char lbaud[5]; 188 char rbaud[5]; 189 char console; 190 char sccolor[6]; 191 char pgcolor[6]; 192 char lgcolor[6]; 193 char keydb; 194 char pad0; 195 char checksum; 196 char diskless; 197 char nokdb; 198 char bootfile[50]; 199 char passwd[17]; 200 char volume[3]; 201 uint8_t enaddr[6]; 202 } nvram; 203 204 static char enaddr[18]; 205 206 static struct arcemu_sgienv { 207 char dbaud[5]; 208 char rbaud[5]; 209 char bootmode; 210 char console; 211 char diskless; 212 char volume[4]; 213 char cpufreq[3]; 214 char gfx[32]; 215 char netaddr[32]; 216 char dlserver[32]; 217 char osloadoptions[32]; 218 } sgienv; 219 220 /* 221 * EEPROM reading routines. IP6's wiring is sufficiently ugly and the routine 222 * sufficiently small that we just roll our own, rather than contorting the MD 223 * driver. 224 */ 225 static void 226 eeprom_read(uint8_t *eeprom_buf, size_t len, int is_cs56, 227 void (*set_pre)(int), void (*set_cs)(int), void (*set_sk)(int), 228 int (*get_do)(void), void (*set_di)(int)) 229 { 230 int i, j; 231 232 for (i = 0; i < (len / 2); i++) { 233 uint16_t instr = 0xc000 | (i << ((is_cs56) ? 5 : 7)); 234 uint16_t bitword = 0; 235 236 set_di(0); 237 set_sk(0); 238 set_pre(0); 239 set_cs(0); 240 set_cs(1); 241 set_sk(1); 242 243 for (j = 0; j < ((is_cs56) ? 11 : 9); j++) { 244 set_di(instr & 0x8000); 245 set_sk(0); 246 set_sk(1); 247 instr <<= 1; 248 } 249 250 set_di(0); 251 252 for (j = 0; j < 17; j++) { 253 bitword = (bitword << 1) | get_do(); 254 set_sk(0); 255 set_sk(1); 256 } 257 258 eeprom_buf[i * 2 + 0] = bitword >> 8; 259 eeprom_buf[i * 2 + 1] = bitword & 0xff; 260 261 set_sk(0); 262 set_cs(0); 263 } 264 } 265 266 /* 267 * Read the EEPROM. It's not clear which machines have which parts, and 268 * there's a difference in instruction length between the two. We'll try 269 * both and see which doesn't give us garbage. 270 */ 271 static void 272 arcemu_eeprom_read(void) 273 { 274 int i; 275 276 /* try long instruction length first (the only one I've seen) */ 277 for (i = 1; i >= 0; i--) { 278 if (mach_type == (MACH_SGI_IP6 | MACH_SGI_IP10)) { 279 eeprom_read((uint8_t *)&nvram, sizeof(nvram), i, 280 ip6_set_pre, ip6_set_cs, ip6_set_sk, 281 ip6_get_do, ip6_set_di); 282 } else { 283 eeprom_read((uint8_t *)&nvram, sizeof(nvram), i, 284 ip12_set_pre, ip12_set_cs, ip12_set_sk, 285 ip12_get_do, ip12_set_di); 286 } 287 288 if (nvram.enaddr[0] == 0x08 && nvram.enaddr[1] == 0x00 && 289 nvram.enaddr[2] == 0x69) 290 break; 291 292 if (memcmp(nvram.lbaud, "9600\x0", 5) == 0) 293 break; 294 295 if (memcmp(nvram.bootfile, "dksc(", 5) == 0 || 296 memcmp(nvram.bootfile, "bootp(", 6) == 0) 297 break; 298 } 299 300 /* cache enaddr string */ 301 snprintf(enaddr, sizeof(enaddr), "%02x:%02x:%02x:%02x:%02x:%02x", 302 nvram.enaddr[0], 303 nvram.enaddr[1], 304 nvram.enaddr[2], 305 nvram.enaddr[3], 306 nvram.enaddr[4], 307 nvram.enaddr[5]); 308 } 309 310 static void 311 arcemu_ipN_init(const char **env) 312 { 313 314 arcemu_v.GetPeer = (intptr_t)arcemu_GetPeer; 315 arcemu_v.GetChild = (intptr_t)arcemu_GetChild; 316 arcemu_v.GetEnvironmentVariable = (intptr_t)arcemu_GetEnvironmentVariable; 317 318 if (mach_type == MACH_SGI_IP6 || mach_type == MACH_SGI_IP10) 319 arcemu_v.GetMemoryDescriptor = (intptr_t)arcemu_ip6_GetMemoryDescriptor; 320 else if (mach_type == MACH_SGI_IP12) 321 arcemu_v.GetMemoryDescriptor = (intptr_t)arcemu_ip12_GetMemoryDescriptor; 322 323 arcemu_v.Reboot = (intptr_t)sgi_prom_reset; 324 arcemu_v.PowerDown = (intptr_t)sgi_prom_reinit; 325 arcemu_v.EnterInteractiveMode = (intptr_t)sgi_prom_reinit; 326 327 cn_tab = &arcemu_cn; 328 329 arcemu_eeprom_read(); 330 331 memset(&sgienv, 0, sizeof(sgienv)); 332 extractenv(env, "dbaud", sgienv.dbaud, sizeof(sgienv.dbaud)); 333 extractenv(env, "rbaud", sgienv.rbaud, sizeof(sgienv.rbaud)); 334 extractenv(env, "bootmode",&sgienv.bootmode, sizeof(sgienv.bootmode)); 335 extractenv(env, "console", &sgienv.console, sizeof(sgienv.console)); 336 extractenv(env, "diskless",&sgienv.diskless, sizeof(sgienv.diskless)); 337 extractenv(env, "volume", sgienv.volume, sizeof(sgienv.volume)); 338 extractenv(env, "cpufreq", sgienv.cpufreq, sizeof(sgienv.cpufreq)); 339 extractenv(env, "gfx", sgienv.gfx, sizeof(sgienv.gfx)); 340 extractenv(env, "netaddr", sgienv.netaddr, sizeof(sgienv.netaddr)); 341 extractenv(env, "dlserver", sgienv.dlserver, sizeof(sgienv.dlserver)); 342 extractenv(env, "osloadoptions", sgienv.osloadoptions, 343 sizeof(sgienv.osloadoptions)); 344 345 strcpy(arcbios_sysid_vendor, "SGI"); 346 if (mach_type == MACH_SGI_IP6 || mach_type == MACH_SGI_IP10) { 347 strcpy(arcbios_system_identifier, "SGI-IP6"); 348 strcpy(arcbios_sysid_product, "IP6"); 349 } else if (mach_type == MACH_SGI_IP12) { 350 strcpy(arcbios_system_identifier, "SGI-IP12"); 351 strcpy(arcbios_sysid_product, "IP12"); 352 } 353 } 354 355 static void * 356 arcemu_GetPeer(void *node) 357 { 358 int i; 359 360 if (node == NULL) 361 return (NULL); 362 363 for (i = 0; arcemu_component_tree[i].Class != -1; i++) { 364 if (&arcemu_component_tree[i] == node && 365 arcemu_component_tree[i+1].Class != -1) 366 return (&arcemu_component_tree[i+1]); 367 } 368 369 return (NULL); 370 } 371 372 static void * 373 arcemu_GetChild(void *node) 374 { 375 376 /* 377 * ARCBIOS just walks the entire tree, so we'll represent our 378 * emulated tree as a single level and avoid messy hierarchies. 379 */ 380 if (node == NULL) 381 return (&arcemu_component_tree[0]); 382 383 return (NULL); 384 } 385 386 static const char * 387 arcemu_GetEnvironmentVariable(const char *var) 388 { 389 390 /* 'd'ebug (serial), 'g'raphics, 'G'raphics w/ logo */ 391 392 /* XXX This does not indicate the actual current console */ 393 if (strcasecmp("ConsoleOut", var) == 0) { 394 /* if no keyboard is attached, we should default to serial */ 395 if (strstr(sgienv.gfx, "dead") != NULL) 396 return "serial(0)"; 397 398 switch (nvram.console) { 399 case 'd': 400 case 'D': 401 case 's': 402 case 'S': 403 return "serial(0)"; 404 case 'g': 405 case 'G': 406 return "video()"; 407 default: 408 printf("arcemu: unknown console \"%c\", using serial\n", 409 nvram.console); 410 return "serial(0)"; 411 } 412 } 413 414 if (strcasecmp("cpufreq", var) == 0) { 415 if (sgienv.cpufreq[0] != '\0') 416 return (sgienv.cpufreq); 417 418 /* IP6 is 12, IP10 is 20 */ 419 if (mach_type == MACH_SGI_IP6 || mach_type == MACH_SGI_IP10) 420 return ("16"); 421 422 /* IP12 is 30, 33 or 36 */ 423 return ("33"); 424 } 425 426 if (strcasecmp("dbaud", var) == 0) 427 return (nvram.lbaud); 428 429 if (strcasecmp("eaddr", var) == 0) 430 return (enaddr); 431 432 if (strcasecmp("gfx", var) == 0) { 433 if (sgienv.gfx[0] != '\0') 434 return (sgienv.gfx); 435 } 436 437 /* 438 * Ugly Kludge Alert! 439 * 440 * Since we don't yet have an ip12 bootloader, we can only squish 441 * a kernel into the volume header. However, this makes the bootfile 442 * something like 'dksc(0,1,8)', which translates into 'sd0i'. Ick. 443 * Munge what we return to always map to 'sd0a'. Lord have mercy. 444 * 445 * makebootdev() can handle "dksc(a,b,c)/netbsd", etc already 446 */ 447 if (strcasecmp("OSLoadPartition", var) == 0) { 448 char *hack; 449 450 hack = strstr(nvram.bootfile, ",8)"); 451 if (hack != NULL) 452 hack[1] = '0'; 453 return (nvram.bootfile); 454 } 455 456 /* pull filename from e.g.: "dksc(0,1,0)netbsd" */ 457 if (strcasecmp("OSLoadFilename", var) == 0) { 458 char *file; 459 460 if ((file = strrchr(nvram.bootfile, ')')) != NULL) 461 return (file + 1); 462 else 463 return (NULL); 464 } 465 466 /* 467 * As far as I can tell, old systems had no analogue of OSLoadOptions. 468 * So, to allow forcing of single user mode, we accommodate the 469 * user setting the ARCBIOSy environment variable "OSLoadOptions" to 470 * something other than "auto". 471 */ 472 if (strcasecmp("OSLoadOptions", var) == 0) { 473 if (sgienv.osloadoptions[0] == '\0') 474 return ("auto"); 475 else 476 return (sgienv.osloadoptions); 477 } 478 479 return (NULL); 480 } 481 482 static void * 483 arcemu_ip6_GetMemoryDescriptor(void *mem) 484 { 485 static struct arcbios_mem am; 486 static int invoc; 487 488 unsigned int pages; 489 u_int8_t memcfg; 490 491 if (mem == NULL) { 492 /* 493 * We know pages 0, 1 are occupied, emulate the reserved space. 494 */ 495 am.Type = ARCBIOS_MEM_ExceptionBlock; 496 am.BasePage = 0; 497 am.PageCount = 2; 498 499 invoc = 0; 500 return (&am); 501 } 502 503 memcfg = *(volatile uint8_t *)MIPS_PHYS_TO_KSEG1(0x1f800000) & 0x1f; 504 pages = (memcfg & 0x0f) + 1; 505 506 /* 4MB or 1MB units? */ 507 if (memcfg & 0x10) { 508 pages *= 4096; 509 510 #if 0 // may cause an exception and bring us down in flames; disable until tested 511 /* check for aliasing and adjust page count if necessary */ 512 volatile uint8_t *tp1, *tp2; 513 uint8_t tmp; 514 515 tp1 = (volatile uint8_t *)MIPS_PHYS_TO_KSEG1((pages - 4096) << 12); 516 tp2 = tp1 + (4 * 1024 * 1024); 517 518 tmp = *tp1; 519 *tp2 = ~tmp; 520 if (*tp1 != tmp) 521 pages -= (3 * 1024); 522 #endif 523 } else { 524 pages *= 1024; 525 } 526 527 /* 528 * It appears that the PROM's stack is at 0x400000 in physical memory. 529 * Don't destroy it, and assume (based on IP12 specs), that the prom bss 530 * is below it at 0x380000. This is probably overly conservative. 531 * 532 * Also note that we preserve the first two pages. 533 */ 534 switch (invoc) { 535 case 0: 536 /* free: pages [2, 896) */ 537 am.BasePage = 2; 538 am.PageCount = 894; 539 am.Type = ARCBIOS_MEM_FreeContiguous; 540 break; 541 542 case 1: 543 /* prom bss/stack: pages [896, 1023) */ 544 am.BasePage = 896; 545 am.PageCount = 128; 546 am.Type = ARCBIOS_MEM_FirmwareTemporary; 547 break; 548 549 case 2: 550 /* free: pages [1024, ...) */ 551 am.BasePage = 1024; 552 if (pages < 1024) 553 am.PageCount = 0; 554 else 555 am.PageCount = pages - 1024; 556 am.Type = ARCBIOS_MEM_FreeContiguous; 557 break; 558 559 default: 560 return (NULL); 561 } 562 563 invoc++; 564 return (&am); 565 } 566 567 static void * 568 arcemu_ip12_GetMemoryDescriptor(void *mem) 569 { 570 static int bank; 571 u_int32_t memcfg; 572 static struct arcbios_mem am; 573 574 if (mem == NULL) { 575 /* 576 * We know pages 0, 1 are occupied, emulate the reserved space. 577 */ 578 am.Type = ARCBIOS_MEM_ExceptionBlock; 579 am.BasePage = 0; 580 am.PageCount = 2; 581 582 bank = 0; 583 return (&am); 584 } 585 586 if (bank > 3) 587 return (NULL); 588 589 switch (bank) { 590 case 0: 591 memcfg = *(u_int32_t *) 592 MIPS_PHYS_TO_KSEG1(PIC_MEMCFG0_PHYSADDR) >> 16; 593 break; 594 595 case 1: 596 memcfg = *(u_int32_t *) 597 MIPS_PHYS_TO_KSEG1(PIC_MEMCFG0_PHYSADDR) & 0xffff; 598 break; 599 600 case 2: 601 memcfg = *(u_int32_t *) 602 MIPS_PHYS_TO_KSEG1(PIC_MEMCFG1_PHYSADDR) >> 16; 603 break; 604 605 case 3: 606 memcfg = *(u_int32_t *) 607 MIPS_PHYS_TO_KSEG1(PIC_MEMCFG1_PHYSADDR) & 0xffff; 608 break; 609 610 default: 611 memcfg = PIC_MEMCFG_BADADDR; 612 } 613 614 if (memcfg == PIC_MEMCFG_BADADDR) { 615 am.Type = ARCBIOS_MEM_BadMemory; 616 am.BasePage = 617 PIC_MEMCFG_ADDR(PIC_MEMCFG_BADADDR) / ARCBIOS_PAGESIZE; 618 am.PageCount = 0; 619 } else { 620 am.Type = ARCBIOS_MEM_FreeContiguous; 621 am.BasePage = PIC_MEMCFG_ADDR(memcfg) / ARCBIOS_PAGESIZE; 622 am.PageCount = PIC_MEMCFG_SIZ(memcfg) / ARCBIOS_PAGESIZE; 623 624 /* pages 0, 1 are occupied (if clause before switch), compensate */ 625 if (am.BasePage == 0) { 626 am.BasePage = 2; 627 am.PageCount -= 2; /* won't overflow */ 628 } 629 } 630 631 bank++; 632 return (&am); 633 } 634 635 /* 636 * If this breaks.. well.. then it breaks. 637 */ 638 static void 639 arcemu_prom_putc(dev_t dummy, int c) 640 { 641 sgi_prom_printf("%c", c); 642 } 643 644 /* Unimplemented Vector */ 645 static void 646 arcemu_unimpl(void) 647 { 648 649 panic("arcemu vector not established on IP%d", mach_type); 650 } 651 #endif /* !_LP64 */ 652