X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;ds=sidebyside;f=hw%2Fsun4u.c;h=8f9e56bb713c52d1f02a6d5ee57fc7e80265803a;hb=2ac711791b2e4aabc5e4046b7428727828c705eb;hp=36d6946f8746446f17767964639940828b941634;hpb=c7ba218da1639a054b5ca1c259530305562fa571;p=qemu.git diff --git a/hw/sun4u.c b/hw/sun4u.c index 36d6946f8..8f9e56bb7 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -31,12 +31,21 @@ #include "sysemu.h" #include "boards.h" #include "firmware_abi.h" +#include "fw_cfg.h" + +//#define DEBUG_IRQ + +#ifdef DEBUG_IRQ +#define DPRINTF(fmt, args...) \ + do { printf("CPUIRQ: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif #define KERNEL_LOAD_ADDR 0x00404000 #define CMDLINE_ADDR 0x003ff000 #define INITRD_LOAD_ADDR 0x00300000 #define PROM_SIZE_MAX (4 * 1024 * 1024) -#define PROM_ADDR 0x1fff0000000ULL #define PROM_VADDR 0x000ffd00000ULL #define APB_SPECIAL_BASE 0x1fe00000000ULL #define APB_MEM_BASE 0x1ff00000000ULL @@ -44,9 +53,18 @@ #define PROM_FILENAME "openbios-sparc64" #define NVRAM_SIZE 0x2000 #define MAX_IDE_BUS 2 +#define BIOS_CFG_IOPORT 0x510 + +#define MAX_PILS 16 + +#define TICK_INT_DIS 0x8000000000000000ULL +#define TICK_MAX 0x7fffffffffffffffULL struct hwdef { const char * const default_cpu_model; + uint16_t machine_id; + uint64_t prom_addr; + uint64_t console_serial_base; }; int DMA_get_channel_mode (int nchan) @@ -64,7 +82,6 @@ int DMA_write_memory (int nchan, void *buf, int pos, int size) void DMA_hold_DREQ (int nchan) {} void DMA_release_DREQ (int nchan) {} void DMA_schedule(int nchan) {} -void DMA_run (void) {} void DMA_init (int high_page_enable) {} void DMA_register_channel (int nchan, DMA_transfer_handler transfer_handler, @@ -72,28 +89,12 @@ void DMA_register_channel (int nchan, { } -static int nvram_boot_set(void *opaque, const char *boot_device) +static int fw_cfg_boot_set(void *opaque, const char *boot_device) { - unsigned int i; - uint8_t image[sizeof(ohwcfg_v3_t)]; - ohwcfg_v3_t *header = (ohwcfg_v3_t *)ℑ - m48t59_t *nvram = (m48t59_t *)opaque; - - for (i = 0; i < sizeof(image); i++) - image[i] = m48t59_read(nvram, i) & 0xff; - - strcpy((char *)header->boot_devices, boot_device); - header->nboot_devices = strlen(boot_device) & 0xff; - header->crc = cpu_to_be16(OHW_compute_crc(header, 0x00, 0xF8)); - - for (i = 0; i < sizeof(image); i++) - m48t59_write(nvram, i, image[i]); - + fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); return 0; } -extern int nographic; - static int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, const char *arch, ram_addr_t RAM_size, @@ -108,55 +109,17 @@ static int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, unsigned int i; uint32_t start, end; uint8_t image[0x1ff0]; - ohwcfg_v3_t *header = (ohwcfg_v3_t *)ℑ - struct sparc_arch_cfg *sparc_header; struct OpenBIOS_nvpart_v1 *part_header; memset(image, '\0', sizeof(image)); - // Try to match PPC NVRAM - strcpy((char *)header->struct_ident, "QEMU_BIOS"); - header->struct_version = cpu_to_be32(3); /* structure v3 */ - - header->nvram_size = cpu_to_be16(NVRAM_size); - header->nvram_arch_ptr = cpu_to_be16(sizeof(ohwcfg_v3_t)); - header->nvram_arch_size = cpu_to_be16(sizeof(struct sparc_arch_cfg)); - strcpy((char *)header->arch, arch); - header->nb_cpus = smp_cpus & 0xff; - header->RAM0_base = 0; - header->RAM0_size = cpu_to_be64((uint64_t)RAM_size); - strcpy((char *)header->boot_devices, boot_devices); - header->nboot_devices = strlen(boot_devices) & 0xff; - header->kernel_image = cpu_to_be64((uint64_t)kernel_image); - header->kernel_size = cpu_to_be64((uint64_t)kernel_size); - if (cmdline) { - pstrcpy_targphys(CMDLINE_ADDR, TARGET_PAGE_SIZE, cmdline); - header->cmdline = cpu_to_be64((uint64_t)CMDLINE_ADDR); - header->cmdline_size = cpu_to_be64((uint64_t)strlen(cmdline)); - } - header->initrd_image = cpu_to_be64((uint64_t)initrd_image); - header->initrd_size = cpu_to_be64((uint64_t)initrd_size); - header->NVRAM_image = cpu_to_be64((uint64_t)NVRAM_image); - - header->width = cpu_to_be16(width); - header->height = cpu_to_be16(height); - header->depth = cpu_to_be16(depth); - if (nographic) - header->graphic_flags = cpu_to_be16(OHW_GF_NOGRAPHICS); - - header->crc = cpu_to_be16(OHW_compute_crc(header, 0x00, 0xF8)); - - // Architecture specific header - start = sizeof(ohwcfg_v3_t); - sparc_header = (struct sparc_arch_cfg *)&image[start]; - sparc_header->valid = 0; - start += sizeof(struct sparc_arch_cfg); + start = 0; // OpenBIOS nvram variables // Variable partition part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; part_header->signature = OPENBIOS_PART_SYSTEM; - strcpy(part_header->name, "system"); + pstrcpy(part_header->name, sizeof(part_header->name), "system"); end = start + sizeof(struct OpenBIOS_nvpart_v1); for (i = 0; i < nb_prom_envs; i++) @@ -172,7 +135,7 @@ static int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, start = end; part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; part_header->signature = OPENBIOS_PART_FREE; - strcpy(part_header->name, "free"); + pstrcpy(part_header->name, sizeof(part_header->name), "free"); end = 0x1fd0; OpenBIOS_finish_partition(part_header, end - start); @@ -182,59 +145,134 @@ static int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, for (i = 0; i < sizeof(image); i++) m48t59_write(nvram, i, image[i]); - qemu_register_boot_set(nvram_boot_set, nvram); - return 0; } -void pic_info(void) +void pic_info(Monitor *mon) { } -void irq_info(void) +void irq_info(Monitor *mon) { } +void cpu_check_irqs(CPUState *env) +{ + uint32_t pil = env->pil_in | (env->softint & ~SOFTINT_TIMER) | + ((env->softint & SOFTINT_TIMER) << 14); + + if (pil && (env->interrupt_index == 0 || + (env->interrupt_index & ~15) == TT_EXTINT)) { + unsigned int i; + + for (i = 15; i > 0; i--) { + if (pil & (1 << i)) { + int old_interrupt = env->interrupt_index; + + env->interrupt_index = TT_EXTINT | i; + if (old_interrupt != env->interrupt_index) { + DPRINTF("Set CPU IRQ %d\n", i); + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + break; + } + } + } else if (!pil && (env->interrupt_index & ~15) == TT_EXTINT) { + DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index & 15); + env->interrupt_index = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void cpu_set_irq(void *opaque, int irq, int level) +{ + CPUState *env = opaque; + + if (level) { + DPRINTF("Raise CPU IRQ %d\n", irq); + env->halted = 0; + env->pil_in |= 1 << irq; + cpu_check_irqs(env); + } else { + DPRINTF("Lower CPU IRQ %d\n", irq); + env->pil_in &= ~(1 << irq); + cpu_check_irqs(env); + } +} + void qemu_system_powerdown(void) { } +typedef struct ResetData { + CPUState *env; + uint64_t reset_addr; +} ResetData; + static void main_cpu_reset(void *opaque) { - CPUState *env = opaque; + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; cpu_reset(env); - ptimer_set_limit(env->tick, 0x7fffffffffffffffULL, 1); - ptimer_run(env->tick, 0); - ptimer_set_limit(env->stick, 0x7fffffffffffffffULL, 1); - ptimer_run(env->stick, 0); - ptimer_set_limit(env->hstick, 0x7fffffffffffffffULL, 1); - ptimer_run(env->hstick, 0); + env->tick_cmpr = TICK_INT_DIS | 0; + ptimer_set_limit(env->tick, TICK_MAX, 1); + ptimer_run(env->tick, 1); + env->stick_cmpr = TICK_INT_DIS | 0; + ptimer_set_limit(env->stick, TICK_MAX, 1); + ptimer_run(env->stick, 1); + env->hstick_cmpr = TICK_INT_DIS | 0; + ptimer_set_limit(env->hstick, TICK_MAX, 1); + ptimer_run(env->hstick, 1); + env->gregs[1] = 0; // Memory start + env->gregs[2] = ram_size; // Memory size + env->gregs[3] = 0; // Machine description XXX + env->pc = s->reset_addr; + env->npc = env->pc + 4; } static void tick_irq(void *opaque) { CPUState *env = opaque; - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + if (!(env->tick_cmpr & TICK_INT_DIS)) { + env->softint |= SOFTINT_TIMER; + cpu_interrupt(env, CPU_INTERRUPT_TIMER); + } } static void stick_irq(void *opaque) { CPUState *env = opaque; - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + if (!(env->stick_cmpr & TICK_INT_DIS)) { + env->softint |= SOFTINT_STIMER; + cpu_interrupt(env, CPU_INTERRUPT_TIMER); + } } static void hstick_irq(void *opaque) { CPUState *env = opaque; - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + if (!(env->hstick_cmpr & TICK_INT_DIS)) { + cpu_interrupt(env, CPU_INTERRUPT_TIMER); + } +} + +void cpu_tick_set_count(void *opaque, uint64_t count) +{ + ptimer_set_count(opaque, -count); +} + +uint64_t cpu_tick_get_count(void *opaque) +{ + return -ptimer_get_count(opaque); } -static void dummy_cpu_set_irq(void *opaque, int irq, int level) +void cpu_tick_set_limit(void *opaque, uint64_t limit) { + ptimer_set_limit(opaque, -limit, 0); } static const int ide_iobase[2] = { 0x1f0, 0x170 }; @@ -249,8 +287,47 @@ static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; static fdctrl_t *floppy_controller; +static void ebus_mmio_mapfunc(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + DPRINTF("Mapping region %d registers at %08x\n", region_num, addr); + switch (region_num) { + case 0: + isa_mmio_init(addr, 0x1000000); + break; + case 1: + isa_mmio_init(addr, 0x800000); + break; + } +} + +/* EBUS (Eight bit bus) bridge */ +static void +pci_ebus_init(PCIBus *bus, int devfn) +{ + PCIDevice *s; + + s = pci_register_device(bus, "EBUS", sizeof(*s), devfn, NULL, NULL); + pci_config_set_vendor_id(s->config, PCI_VENDOR_ID_SUN); + pci_config_set_device_id(s->config, PCI_DEVICE_ID_SUN_EBUS); + s->config[0x04] = 0x06; // command = bus master, pci mem + s->config[0x05] = 0x00; + s->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error + s->config[0x07] = 0x03; // status = medium devsel + s->config[0x08] = 0x01; // revision + s->config[0x09] = 0x00; // programming i/f + pci_config_set_class(s->config, PCI_CLASS_BRIDGE_OTHER); + s->config[0x0D] = 0x0a; // latency_timer + s->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type + + pci_register_io_region(s, 0, 0x1000000, PCI_ADDRESS_SPACE_MEM, + ebus_mmio_mapfunc); + pci_register_io_region(s, 1, 0x800000, PCI_ADDRESS_SPACE_MEM, + ebus_mmio_mapfunc); +} + static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, - const char *boot_devices, DisplayState *ds, + const char *boot_devices, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model, const struct hwdef *hwdef) @@ -260,13 +337,16 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, m48t59_t *nvram; int ret, linux_boot; unsigned int i; - long prom_offset, initrd_size, kernel_size; - PCIBus *pci_bus; + ram_addr_t ram_offset, prom_offset; + long initrd_size, kernel_size; + PCIBus *pci_bus, *pci_bus2, *pci_bus3; QEMUBH *bh; qemu_irq *irq; int drive_index; BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; BlockDriverState *fd[MAX_FD]; + void *fw_cfg; + ResetData *reset_info; linux_boot = (kernel_filename != NULL); @@ -290,14 +370,22 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, bh = qemu_bh_new(hstick_irq, env); env->hstick = ptimer_init(bh); ptimer_set_period(env->hstick, 1ULL); - qemu_register_reset(main_cpu_reset, env); - main_cpu_reset(env); + + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + reset_info->reset_addr = hwdef->prom_addr + 0x40ULL; + qemu_register_reset(main_cpu_reset, reset_info); + main_cpu_reset(reset_info); + // Override warm reset address with cold start address + env->pc = hwdef->prom_addr + 0x20ULL; + env->npc = env->pc + 4; /* allocate RAM */ - cpu_register_physical_memory(0, RAM_size, 0); + ram_offset = qemu_ram_alloc(RAM_size); + cpu_register_physical_memory(0, RAM_size, ram_offset); - prom_offset = RAM_size + vga_ram_size; - cpu_register_physical_memory(PROM_ADDR, + prom_offset = qemu_ram_alloc(PROM_SIZE_MAX); + cpu_register_physical_memory(hwdef->prom_addr, (PROM_SIZE_MAX + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK, prom_offset | IO_MEM_ROM); @@ -305,11 +393,16 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, if (bios_name == NULL) bios_name = PROM_FILENAME; snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name); - ret = load_elf(buf, PROM_ADDR - PROM_VADDR, NULL, NULL, NULL); + ret = load_elf(buf, hwdef->prom_addr - PROM_VADDR, NULL, NULL, NULL); if (ret < 0) { - fprintf(stderr, "qemu: could not load prom '%s'\n", - buf); - exit(1); + ret = load_image_targphys(buf, hwdef->prom_addr, + (PROM_SIZE_MAX + TARGET_PAGE_SIZE) & + TARGET_PAGE_MASK); + if (ret < 0) { + fprintf(stderr, "qemu: could not load prom '%s'\n", + buf); + exit(1); + } } kernel_size = 0; @@ -351,12 +444,21 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, } } } - pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, NULL); + pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, NULL, &pci_bus2, + &pci_bus3); isa_mem_base = VGA_BASE; - pci_cirrus_vga_init(pci_bus, ds, phys_ram_base + RAM_size, RAM_size, - vga_ram_size); + pci_vga_init(pci_bus, vga_ram_size, 0, 0); - for(i = 0; i < MAX_SERIAL_PORTS; i++) { + // XXX Should be pci_bus3 + pci_ebus_init(pci_bus, -1); + + i = 0; + if (hwdef->console_serial_base) { + serial_mm_init(hwdef->console_serial_base, 0, NULL, 115200, + serial_hds[i], 1); + i++; + } + for(; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { serial_init(serial_io[i], NULL/*serial_irq[i]*/, 115200, serial_hds[i]); @@ -370,13 +472,10 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, } } - for(i = 0; i < nb_nics; i++) { - if (!nd_table[i].model) - nd_table[i].model = "ne2k_pci"; - pci_nic_init(pci_bus, &nd_table[i], -1); - } + for(i = 0; i < nb_nics; i++) + pci_nic_init(pci_bus, &nd_table[i], -1, "ne2k_pci"); - irq = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, 32); + irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS); if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { fprintf(stderr, "qemu: too many IDE bus\n"); exit(1); @@ -390,8 +489,8 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, hd[i] = NULL; } - // XXX pci_cmd646_ide_init(pci_bus, hd, 1); - pci_piix3_ide_init(pci_bus, hd, -1, irq); + pci_cmd646_ide_init(pci_bus, hd, 1); + /* FIXME: wire up interrupts. */ i8042_init(NULL/*1*/, NULL/*12*/, 0x60); for(i = 0; i < MAX_FD; i++) { @@ -412,49 +511,101 @@ static void sun4uv_init(ram_addr_t RAM_size, int vga_ram_size, graphic_width, graphic_height, graphic_depth, (uint8_t *)&nd_table[0].macaddr); + fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + if (kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); + pstrcpy_targphys(CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_devices[0]); + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } +enum { + sun4u_id = 0, + sun4v_id = 64, + niagara_id, +}; + static const struct hwdef hwdefs[] = { /* Sun4u generic PC-like machine */ { .default_cpu_model = "TI UltraSparc II", + .machine_id = sun4u_id, + .prom_addr = 0x1fff0000000ULL, + .console_serial_base = 0, }, /* Sun4v generic PC-like machine */ { .default_cpu_model = "Sun UltraSparc T1", + .machine_id = sun4v_id, + .prom_addr = 0x1fff0000000ULL, + .console_serial_base = 0, + }, + /* Sun4v generic Niagara machine */ + { + .default_cpu_model = "Sun UltraSparc T1", + .machine_id = niagara_id, + .prom_addr = 0xfff0000000ULL, + .console_serial_base = 0xfff0c2c000ULL, }, }; /* Sun4u hardware initialisation */ static void sun4u_init(ram_addr_t RAM_size, int vga_ram_size, - const char *boot_devices, DisplayState *ds, + const char *boot_devices, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - sun4uv_init(RAM_size, vga_ram_size, boot_devices, ds, kernel_filename, + sun4uv_init(RAM_size, vga_ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, &hwdefs[0]); } /* Sun4v hardware initialisation */ static void sun4v_init(ram_addr_t RAM_size, int vga_ram_size, - const char *boot_devices, DisplayState *ds, + const char *boot_devices, const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - sun4uv_init(RAM_size, vga_ram_size, boot_devices, ds, kernel_filename, + sun4uv_init(RAM_size, vga_ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, &hwdefs[1]); } +/* Niagara hardware initialisation */ +static void niagara_init(ram_addr_t RAM_size, int vga_ram_size, + const char *boot_devices, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + sun4uv_init(RAM_size, vga_ram_size, boot_devices, kernel_filename, + kernel_cmdline, initrd_filename, cpu_model, &hwdefs[2]); +} + QEMUMachine sun4u_machine = { - "sun4u", - "Sun4u platform", - sun4u_init, - PROM_SIZE_MAX + VGA_RAM_SIZE, + .name = "sun4u", + .desc = "Sun4u platform", + .init = sun4u_init, + .max_cpus = 1, // XXX for now }; QEMUMachine sun4v_machine = { - "sun4v", - "Sun4v platform", - sun4v_init, - PROM_SIZE_MAX + VGA_RAM_SIZE, + .name = "sun4v", + .desc = "Sun4v platform", + .init = sun4v_init, + .max_cpus = 1, // XXX for now +}; + +QEMUMachine niagara_machine = { + .name = "Niagara", + .desc = "Sun4v platform, Niagara", + .init = niagara_init, + .max_cpus = 1, // XXX for now };