diff options
Diffstat (limited to 'qemu/roms/openbios/arch/sparc64/openbios.c')
-rw-r--r-- | qemu/roms/openbios/arch/sparc64/openbios.c | 664 |
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; +} |