summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/arch/sparc64/openbios.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/openbios/arch/sparc64/openbios.c')
-rw-r--r--qemu/roms/openbios/arch/sparc64/openbios.c664
1 files changed, 664 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/sparc64/openbios.c b/qemu/roms/openbios/arch/sparc64/openbios.c
new file mode 100644
index 000000000..4557f7f9f
--- /dev/null
+++ b/qemu/roms/openbios/arch/sparc64/openbios.c
@@ -0,0 +1,664 @@
+/* tag: openbios forth environment, executable code
+ *
+ * Copyright (C) 2003 Patrick Mauritz, Stefan Reinauer
+ *
+ * See the file "COPYING" for further information about
+ * the copyright and warranty status of this work.
+ */
+
+#include "config.h"
+#include "libopenbios/openbios.h"
+#include "libopenbios/bindings.h"
+#include "libopenbios/console.h"
+#include "drivers/drivers.h"
+#include "dict.h"
+#include "arch/common/nvram.h"
+#include "packages/nvram.h"
+#include "libopenbios/sys_info.h"
+#include "openbios.h"
+#include "drivers/pci.h"
+#include "asm/pci.h"
+#include "boot.h"
+#include "../../drivers/timer.h" // XXX
+#define NO_QEMU_PROTOS
+#include "arch/common/fw_cfg.h"
+#include "arch/sparc64/ofmem_sparc64.h"
+#include "spitfire.h"
+
+#define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
+
+#define APB_SPECIAL_BASE 0x1fe00000000ULL
+#define APB_MEM_BASE 0x1ff00000000ULL
+
+#define MEMORY_SIZE (512*1024) /* 512K ram for hosted system */
+
+// XXX
+#define NVRAM_BASE 0x2000
+#define NVRAM_SIZE 0x2000
+#define NVRAM_IDPROM 0x1fd8
+#define NVRAM_IDPROM_SIZE 32
+#define NVRAM_OB_START (0)
+#define NVRAM_OB_SIZE ((NVRAM_IDPROM - NVRAM_OB_START) & ~15)
+
+static uint8_t idprom[NVRAM_IDPROM_SIZE];
+
+struct hwdef {
+ pci_arch_t pci;
+ uint16_t machine_id_low, machine_id_high;
+};
+
+static const struct hwdef hwdefs[] = {
+ {
+ .pci = {
+ .name = "SUNW,sabre",
+ .vendor_id = PCI_VENDOR_ID_SUN,
+ .device_id = PCI_DEVICE_ID_SUN_SABRE,
+ .cfg_addr = APB_SPECIAL_BASE + 0x1000000ULL, // PCI bus configuration space
+ .cfg_data = APB_MEM_BASE, // PCI bus memory space
+ .cfg_base = APB_SPECIAL_BASE,
+ .cfg_len = 0x2000000,
+ .host_pci_base = APB_MEM_BASE,
+ .pci_mem_base = 0x100000, /* avoid VGA at 0xa0000 */
+ .mem_len = 0x10000000,
+ .io_base = APB_SPECIAL_BASE + 0x2000000ULL, // PCI Bus I/O space
+ .io_len = 0x10000,
+ .irqs = { 0, 1, 2, 3 },
+ },
+ .machine_id_low = 0,
+ .machine_id_high = 255,
+ },
+};
+
+struct cpudef {
+ unsigned long iu_version;
+ const char *name;
+ unsigned long ecache_associativity;
+ unsigned long ecache_line_size;
+ unsigned long ecache_size;
+ unsigned long num_dtlb_entries;
+ unsigned long dcache_associativity;
+ unsigned long dcache_line_size;
+ unsigned long dcache_size;
+ unsigned long num_itlb_entries;
+ unsigned long icache_associativity;
+ unsigned long icache_line_size;
+ unsigned long icache_size;
+};
+
+/*
+ ( addr -- ? )
+*/
+
+extern volatile uint64_t client_tba;
+
+static void
+set_trap_table(void)
+{
+ unsigned long addr;
+
+ addr = POP();
+
+ /* Update client_tba to be updated on CIF exit */
+ client_tba = addr;
+}
+
+/* Reset control register is defined in 17.2.7.3 of US IIi User Manual */
+static void
+sparc64_reset_all(void)
+{
+ unsigned long addr = 0x1fe0000f020ULL;
+ unsigned long val = 1 << 29;
+
+ asm("stxa %0, [%1] 0x15\n\t"
+ : : "r" (val), "r" (addr) : "memory");
+}
+
+/* PCI Target Address Space Register (see UltraSPARC IIi User's Manual
+ section 19.3.0.4) */
+#define PBM_PCI_TARGET_AS 0x2028
+#define PBM_PCI_TARGET_AS_CD_ENABLE 0x40
+
+static void
+sparc64_set_tas_register(unsigned long val)
+{
+ unsigned long addr = APB_SPECIAL_BASE + PBM_PCI_TARGET_AS;
+
+ asm("stxa %0, [%1] 0x15\n\t"
+ : : "r" (val), "r" (addr) : "memory");
+}
+
+static void cpu_generic_init(const struct cpudef *cpu, uint32_t clock_frequency)
+{
+ unsigned long iu_version;
+
+ push_str("/");
+ fword("find-device");
+
+ fword("new-device");
+
+ push_str(cpu->name);
+ fword("device-name");
+
+ push_str("cpu");
+ fword("device-type");
+
+ asm("rdpr %%ver, %0\n"
+ : "=r"(iu_version) :);
+
+ PUSH((iu_version >> 48) & 0xff);
+ fword("encode-int");
+ push_str("manufacturer#");
+ fword("property");
+
+ PUSH((iu_version >> 32) & 0xff);
+ fword("encode-int");
+ push_str("implementation#");
+ fword("property");
+
+ PUSH((iu_version >> 24) & 0xff);
+ fword("encode-int");
+ push_str("mask#");
+ fword("property");
+
+ PUSH(9);
+ fword("encode-int");
+ push_str("sparc-version");
+ fword("property");
+
+ PUSH(0);
+ fword("encode-int");
+ push_str("cpuid");
+ fword("property");
+
+ PUSH(0);
+ fword("encode-int");
+ push_str("upa-portid");
+ fword("property");
+
+ PUSH(clock_frequency);
+ fword("encode-int");
+ push_str("clock-frequency");
+ fword("property");
+
+ PUSH(cpu->ecache_associativity);
+ fword("encode-int");
+ push_str("ecache-associativity");
+ fword("property");
+
+ PUSH(cpu->ecache_line_size);
+ fword("encode-int");
+ push_str("ecache-line-size");
+ fword("property");
+
+ PUSH(cpu->ecache_size);
+ fword("encode-int");
+ push_str("ecache-size");
+ fword("property");
+
+ PUSH(cpu->dcache_associativity);
+ fword("encode-int");
+ push_str("dcache-associativity");
+ fword("property");
+
+ PUSH(cpu->dcache_line_size);
+ fword("encode-int");
+ push_str("dcache-line-size");
+ fword("property");
+
+ PUSH(cpu->dcache_size);
+ fword("encode-int");
+ push_str("dcache-size");
+ fword("property");
+
+ PUSH(cpu->icache_associativity);
+ fword("encode-int");
+ push_str("icache-associativity");
+ fword("property");
+
+ PUSH(cpu->ecache_line_size);
+ fword("encode-int");
+ push_str("icache-line-size");
+ fword("property");
+
+ PUSH(cpu->ecache_size);
+ fword("encode-int");
+ push_str("icache-size");
+ fword("property");
+
+ PUSH(cpu->num_itlb_entries);
+ fword("encode-int");
+ push_str("#itlb-entries");
+ fword("property");
+
+ PUSH(cpu->num_dtlb_entries);
+ fword("encode-int");
+ push_str("#dtlb-entries");
+ fword("property");
+
+ fword("finish-device");
+
+ // Trap table
+ push_str("/openprom/client-services");
+ fword("find-device");
+ bind_func("SUNW,set-trap-table", set_trap_table);
+
+ // Reset
+ bind_func("sparc64-reset-all", sparc64_reset_all);
+ push_str("' sparc64-reset-all to reset-all");
+ fword("eval");
+}
+
+static const struct cpudef sparc_defs[] = {
+ {
+ .iu_version = (0x04ULL << 48) | (0x02ULL << 32),
+ .name = "FJSV,GP",
+ },
+ {
+ .iu_version = (0x04ULL << 48) | (0x03ULL << 32),
+ .name = "FJSV,GPUSK",
+ },
+ {
+ .iu_version = (0x04ULL << 48) | (0x04ULL << 32),
+ .name = "FJSV,GPUSC",
+ },
+ {
+ .iu_version = (0x04ULL << 48) | (0x05ULL << 32),
+ .name = "FJSV,GPUZC",
+ },
+ {
+ .iu_version = (0x17ULL << 48) | (0x10ULL << 32),
+ .name = "SUNW,UltraSPARC",
+ .ecache_associativity = 1, .ecache_line_size = 0x40, .ecache_size = 0x100000,
+ .dcache_associativity = 1, .dcache_line_size = 0x20, .dcache_size = 0x4000,
+ .icache_associativity = 2, .icache_line_size = 0x20, .icache_size = 0x4000,
+ .num_dtlb_entries = 0x40, .num_itlb_entries = 0x40,
+ },
+ {
+ .iu_version = (0x17ULL << 48) | (0x11ULL << 32),
+ .name = "SUNW,UltraSPARC-II",
+ .ecache_associativity = 1, .ecache_line_size = 0x40, .ecache_size = 0x100000,
+ .dcache_associativity = 1, .dcache_line_size = 0x20, .dcache_size = 0x4000,
+ .icache_associativity = 2, .icache_line_size = 0x20, .icache_size = 0x4000,
+ .num_dtlb_entries = 0x40, .num_itlb_entries = 0x40,
+ },
+ {
+ .iu_version = (0x17ULL << 48) | (0x12ULL << 32),
+ .name = "SUNW,UltraSPARC-IIi",
+ .ecache_associativity = 1, .ecache_line_size = 0x40, .ecache_size = 0x40000,
+ .dcache_associativity = 1, .dcache_line_size = 0x20, .dcache_size = 0x4000,
+ .icache_associativity = 2, .icache_line_size = 0x20, .icache_size = 0x4000,
+ .num_dtlb_entries = 0x40, .num_itlb_entries = 0x40,
+ },
+ {
+ .iu_version = (0x17ULL << 48) | (0x13ULL << 32),
+ .name = "SUNW,UltraSPARC-IIe",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x14ULL << 32),
+ .name = "SUNW,UltraSPARC-III",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x15ULL << 32),
+ .name = "SUNW,UltraSPARC-III+",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x16ULL << 32),
+ .name = "SUNW,UltraSPARC-IIIi",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x18ULL << 32),
+ .name = "SUNW,UltraSPARC-IV",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x19ULL << 32),
+ .name = "SUNW,UltraSPARC-IV+",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x22ULL << 32),
+ .name = "SUNW,UltraSPARC-IIIi+",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x23ULL << 32),
+ .name = "SUNW,UltraSPARC-T1",
+ },
+ {
+ .iu_version = (0x3eULL << 48) | (0x24ULL << 32),
+ .name = "SUNW,UltraSPARC-T2",
+ },
+ {
+ .iu_version = (0x22ULL << 48) | (0x10ULL << 32),
+ .name = "SUNW,UltraSPARC",
+ },
+};
+
+static const struct cpudef *
+id_cpu(void)
+{
+ unsigned long iu_version;
+ unsigned int i;
+
+ asm("rdpr %%ver, %0\n"
+ : "=r"(iu_version) :);
+ iu_version &= 0xffffffff00000000ULL;
+
+ for (i = 0; i < sizeof(sparc_defs)/sizeof(struct cpudef); i++) {
+ if (iu_version == sparc_defs[i].iu_version)
+ return &sparc_defs[i];
+ }
+ printk("Unknown cpu (psr %lx), freezing!\n", iu_version);
+ for (;;);
+}
+
+static void nvram_read(uint16_t offset, char *buf, unsigned int nbytes)
+{
+ unsigned int i;
+
+ for (i = 0; i < nbytes; i++) {
+ buf[i] = inb(NVRAM_BASE + offset + i);
+ }
+}
+
+static void nvram_write(uint16_t offset, const char *buf, unsigned int nbytes)
+{
+ unsigned int i;
+
+ for (i = 0; i < nbytes; i++) {
+ outb(buf[i], NVRAM_BASE + offset + i);
+ }
+}
+
+static uint8_t qemu_uuid[16];
+
+void arch_nvram_get(char *data)
+{
+ char *obio_cmdline;
+ uint32_t size = 0;
+ const struct cpudef *cpu;
+ char buf[256];
+ uint32_t temp;
+ uint64_t ram_size;
+ uint32_t clock_frequency;
+ uint16_t machine_id;
+ const char *stdin_path, *stdout_path;
+
+ fw_cfg_init();
+
+ fw_cfg_read(FW_CFG_SIGNATURE, buf, 4);
+ buf[4] = '\0';
+
+ printk("Configuration device id %s", buf);
+
+ temp = fw_cfg_read_i32(FW_CFG_ID);
+ machine_id = fw_cfg_read_i16(FW_CFG_MACHINE_ID);
+
+ printk(" version %d machine id %d\n", temp, machine_id);
+
+ if (temp != 1) {
+ printk("Incompatible configuration device version, freezing\n");
+ for(;;);
+ }
+
+ kernel_size = fw_cfg_read_i32(FW_CFG_KERNEL_SIZE);
+ if (kernel_size)
+ kernel_image = fw_cfg_read_i64(FW_CFG_KERNEL_ADDR);
+
+ size = fw_cfg_read_i32(FW_CFG_CMDLINE_SIZE);
+ if (size) {
+ obio_cmdline = (char *)malloc(size + 1);
+ fw_cfg_read(FW_CFG_CMDLINE_DATA, obio_cmdline, size);
+ obio_cmdline[size] = '\0';
+ } else {
+ obio_cmdline = strdup("");
+ }
+ qemu_cmdline = (uint64_t)obio_cmdline;
+ cmdline_size = size;
+ boot_device = fw_cfg_read_i16(FW_CFG_BOOT_DEVICE);
+
+ if (kernel_size)
+ printk("kernel addr %llx size %llx\n", kernel_image, kernel_size);
+ if (size)
+ printk("kernel cmdline %s\n", obio_cmdline);
+
+ nvram_read(NVRAM_OB_START, data, NVRAM_OB_SIZE);
+
+ temp = fw_cfg_read_i32(FW_CFG_NB_CPUS);
+
+ printk("CPUs: %x", temp);
+
+ clock_frequency = 100000000;
+
+ cpu = id_cpu();
+ //cpu->initfn();
+ cpu_generic_init(cpu, clock_frequency);
+ printk(" x %s\n", cpu->name);
+
+ // Add /uuid
+ fw_cfg_read(FW_CFG_UUID, (char *)qemu_uuid, 16);
+
+ printk("UUID: " UUID_FMT "\n", qemu_uuid[0], qemu_uuid[1], qemu_uuid[2],
+ qemu_uuid[3], qemu_uuid[4], qemu_uuid[5], qemu_uuid[6],
+ qemu_uuid[7], qemu_uuid[8], qemu_uuid[9], qemu_uuid[10],
+ qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], qemu_uuid[14],
+ qemu_uuid[15]);
+
+ push_str("/");
+ fword("find-device");
+
+ PUSH((long)&qemu_uuid);
+ PUSH(16);
+ fword("encode-bytes");
+ push_str("uuid");
+ fword("property");
+
+ // Add /idprom
+ nvram_read(NVRAM_IDPROM, (char *)idprom, NVRAM_IDPROM_SIZE);
+
+ PUSH((long)&idprom);
+ PUSH(32);
+ fword("encode-bytes");
+ push_str("idprom");
+ fword("property");
+
+ PUSH(500 * 1000 * 1000);
+ fword("encode-int");
+ push_str("clock-frequency");
+ fword("property");
+
+ ram_size = fw_cfg_read_i64(FW_CFG_RAM_SIZE);
+
+ ob_mmu_init(cpu->name, ram_size);
+
+ /* Setup nvram variables */
+ push_str("/options");
+ fword("find-device");
+
+ switch (boot_device) {
+ case 'a':
+ push_str("/obio/SUNW,fdtwo");
+ break;
+ case 'c':
+ push_str("disk:a");
+ break;
+ default:
+ case 'd':
+ push_str("cdrom:f cdrom");
+ break;
+ case 'n':
+ push_str("net");
+ break;
+ }
+
+ fword("encode-string");
+ push_str("boot-device");
+ fword("property");
+
+ push_str(obio_cmdline);
+ fword("encode-string");
+ push_str("boot-file");
+ fword("property");
+
+ /* Set up other properties */
+ push_str("/chosen");
+ fword("find-device");
+
+ if (fw_cfg_read_i16(FW_CFG_NOGRAPHIC)) {
+ stdin_path = stdout_path = "ttya";
+ } else {
+ stdin_path = "keyboard";
+ stdout_path = "screen";
+ }
+
+ push_str(stdin_path);
+ push_str("input-device");
+ fword("$setenv");
+
+ push_str(stdout_path);
+ push_str("output-device");
+ fword("$setenv");
+}
+
+void arch_nvram_put(char *data)
+{
+ nvram_write(0, data, NVRAM_OB_SIZE);
+}
+
+int arch_nvram_size(void)
+{
+ return NVRAM_OB_SIZE;
+}
+
+void setup_timers(void)
+{
+}
+
+void udelay(unsigned int usecs)
+{
+ volatile int i;
+
+ for (i = 0; i < usecs * 100; i++);
+}
+
+static void init_memory(void)
+{
+ phys_addr_t phys;
+ ucell virt;
+
+ /* Claim the memory from OFMEM (align to 512K so we only take 1 TLB slot) */
+ phys = ofmem_claim_phys(-1, MEMORY_SIZE, PAGE_SIZE_512K);
+ if (!phys)
+ printk("panic: not enough physical memory on host system.\n");
+
+ virt = ofmem_claim_virt(-1, MEMORY_SIZE, PAGE_SIZE_512K);
+ if (!virt)
+ printk("panic: not enough virtual memory on host system.\n");
+
+ /* Generate the mapping (and lock translation into the TLBs) */
+ ofmem_map(phys, virt, MEMORY_SIZE, ofmem_arch_default_translation_mode(phys) | SPITFIRE_TTE_LOCKED);
+
+ /* we push start and end of memory to the stack
+ * so that it can be used by the forth word QUIT
+ * to initialize the memory allocator
+ */
+
+ PUSH(virt);
+ PUSH(virt + MEMORY_SIZE);
+}
+
+extern volatile uint64_t *obp_ticks_pointer;
+
+static void
+arch_init( void )
+{
+ openbios_init();
+ modules_init();
+#ifdef CONFIG_DRIVER_PCI
+ ob_pci_init();
+
+ /* Set TAS register to match the virtual-dma properties
+ set during sabre configure */
+ sparc64_set_tas_register(PBM_PCI_TARGET_AS_CD_ENABLE);
+#endif
+ nvconf_init();
+ device_end();
+
+ /* Point to the Forth obp-ticks variable */
+ fword("obp-ticks");
+ obp_ticks_pointer = cell2pointer(POP());
+
+ bind_func("platform-boot", boot );
+ bind_func("(go)", go);
+}
+
+unsigned long isa_io_base;
+
+extern struct _console_ops arch_console_ops;
+
+int openbios(void)
+{
+ unsigned int i;
+ uint16_t machine_id;
+ const struct hwdef *hwdef = NULL;
+
+
+ for (i = 0; i < sizeof(hwdefs) / sizeof(struct hwdef); i++) {
+ isa_io_base = hwdefs[i].pci.io_base;
+ machine_id = fw_cfg_read_i16(FW_CFG_MACHINE_ID);
+ if (hwdefs[i].machine_id_low <= machine_id &&
+ hwdefs[i].machine_id_high >= machine_id) {
+ hwdef = &hwdefs[i];
+ arch = &hwdefs[i].pci;
+ break;
+ }
+ }
+ if (!hwdef)
+ for(;;); // Internal inconsistency, hang
+
+#ifdef CONFIG_DEBUG_CONSOLE
+ init_console(arch_console_ops);
+#ifdef CONFIG_DEBUG_CONSOLE_SERIAL
+ uart_init(CONFIG_SERIAL_PORT, CONFIG_SERIAL_SPEED);
+#endif
+ printk("OpenBIOS for Sparc64\n");
+#endif
+
+ ofmem_init();
+
+ collect_sys_info(&sys_info);
+
+ dict = (unsigned char *)sys_info.dict_start;
+ dicthead = (cell)sys_info.dict_end;
+ last = sys_info.dict_last;
+ dictlimit = sys_info.dict_limit;
+
+ forth_init();
+
+#ifdef CONFIG_DEBUG_BOOT
+ printk("forth started.\n");
+ printk("initializing memory...");
+#endif
+
+ init_memory();
+
+#ifdef CONFIG_DEBUG_BOOT
+ printk("done\n");
+#endif
+
+ PUSH_xt( bind_noname_func(arch_init) );
+ fword("PREPOST-initializer");
+
+ PC = (ucell)findword("initialize-of");
+
+ if (!PC) {
+ printk("panic: no dictionary entry point.\n");
+ return -1;
+ }
+#ifdef CONFIG_DEBUG_DICTIONARY
+ printk("done (%d bytes).\n", dicthead);
+ printk("Jumping to dictionary...\n");
+#endif
+
+ enterforth((xt_t)PC);
+ printk("falling off...\n");
+ free(dict);
+ return 0;
+}