| /* |
| * QEMU Alpha DP264/CLIPPER hardware system emulator. |
| * |
| * Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK |
| * variants because CLIPPER doesn't have an SMC669 SuperIO controller |
| * that we need to emulate as well. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "cpu.h" |
| #include "elf.h" |
| #include "hw/loader.h" |
| #include "alpha_sys.h" |
| #include "qemu/error-report.h" |
| #include "hw/rtc/mc146818rtc.h" |
| #include "hw/ide/pci.h" |
| #include "hw/isa/superio.h" |
| #include "net/net.h" |
| #include "qemu/cutils.h" |
| #include "qemu/datadir.h" |
| |
| static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) |
| { |
| if (((addr >> 41) & 3) == 2) { |
| addr &= 0xffffffffffull; |
| } |
| return addr; |
| } |
| |
| /* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems. |
| (0) The dev_irq_n lines into the cpu, which we totally ignore, |
| (1) The DRIR lines in the typhoon chipset, |
| (2) The "vector" aka mangled interrupt number reported by SRM PALcode, |
| (3) The interrupt number assigned by the kernel. |
| The following function is concerned with (1) only. */ |
| |
| static int clipper_pci_map_irq(PCIDevice *d, int irq_num) |
| { |
| int slot = d->devfn >> 3; |
| |
| assert(irq_num >= 0 && irq_num <= 3); |
| |
| return (slot + 1) * 4 + irq_num; |
| } |
| |
| static void clipper_init(MachineState *machine) |
| { |
| ram_addr_t ram_size = machine->ram_size; |
| const char *kernel_filename = machine->kernel_filename; |
| const char *kernel_cmdline = machine->kernel_cmdline; |
| const char *initrd_filename = machine->initrd_filename; |
| MachineClass *mc = MACHINE_GET_CLASS(machine); |
| AlphaCPU *cpus[4]; |
| PCIBus *pci_bus; |
| PCIDevice *pci_dev; |
| DeviceState *i82378_dev; |
| ISABus *isa_bus; |
| qemu_irq rtc_irq; |
| qemu_irq isa_irq; |
| long size, i; |
| char *palcode_filename; |
| uint64_t palcode_entry; |
| uint64_t kernel_entry, kernel_low; |
| unsigned int smp_cpus = machine->smp.cpus; |
| |
| /* Create up to 4 cpus. */ |
| memset(cpus, 0, sizeof(cpus)); |
| for (i = 0; i < smp_cpus; ++i) { |
| cpus[i] = ALPHA_CPU(cpu_create(machine->cpu_type)); |
| } |
| |
| /* |
| * arg0 -> memory size |
| * arg1 -> kernel entry point |
| * arg2 -> config word |
| * |
| * Config word: bits 0-5 -> ncpus |
| * bit 6 -> nographics option (for HWRPB CTB) |
| * |
| * See init_hwrpb() in the PALcode. |
| */ |
| cpus[0]->env.trap_arg0 = ram_size; |
| cpus[0]->env.trap_arg1 = 0; |
| cpus[0]->env.trap_arg2 = smp_cpus | (!machine->enable_graphics << 6); |
| |
| /* |
| * Init the chipset. Because we're using CLIPPER IRQ mappings, |
| * the minimum PCI device IdSel is 1. |
| */ |
| pci_bus = typhoon_init(machine->ram, &isa_irq, &rtc_irq, cpus, |
| clipper_pci_map_irq, PCI_DEVFN(1, 0)); |
| |
| /* |
| * Init the PCI -> ISA bridge. |
| * |
| * Technically, PCI-based Alphas shipped with one of three different |
| * PCI-ISA bridges: |
| * |
| * - Intel i82378 SIO |
| * - Cypress CY82c693UB |
| * - ALI M1533 |
| * |
| * (An Intel i82375 PCI-EISA bridge was also used on some models.) |
| * |
| * For simplicity, we model an i82378 here, even though it wouldn't |
| * have been on any Tsunami/Typhoon systems; it's close enough, and |
| * we don't want to deal with modelling the CY82c693UB (which has |
| * incompatible edge/level control registers, plus other peripherals |
| * like IDE and USB) or the M1533 (which also has IDE and USB). |
| * |
| * Importantly, we need to provide a PCI device node for it, otherwise |
| * some operating systems won't notice there's an ISA bus to configure. |
| */ |
| i82378_dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(7, 0), "i82378")); |
| isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0")); |
| |
| /* Connect the ISA PIC to the Typhoon IRQ used for ISA interrupts. */ |
| qdev_connect_gpio_out(i82378_dev, 0, isa_irq); |
| |
| /* Since we have an SRM-compatible PALcode, use the SRM epoch. */ |
| mc146818_rtc_init(isa_bus, 1900, rtc_irq); |
| |
| /* VGA setup. Don't bother loading the bios. */ |
| pci_vga_init(pci_bus); |
| |
| /* Network setup. e1000 is good enough, failing Tulip support. */ |
| pci_init_nic_devices(pci_bus, mc->default_nic); |
| |
| /* Super I/O */ |
| isa_create_simple(isa_bus, TYPE_SMC37C669_SUPERIO); |
| |
| /* IDE disk setup. */ |
| pci_dev = pci_create_simple(pci_bus, -1, "cmd646-ide"); |
| pci_ide_create_devs(pci_dev); |
| |
| /* Load PALcode. Given that this is not "real" cpu palcode, |
| but one explicitly written for the emulation, we might as |
| well load it directly from and ELF image. */ |
| palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, |
| machine->firmware ?: "palcode-clipper"); |
| if (palcode_filename == NULL) { |
| error_report("no palcode provided"); |
| exit(1); |
| } |
| size = load_elf(palcode_filename, NULL, cpu_alpha_superpage_to_phys, |
| NULL, &palcode_entry, NULL, NULL, NULL, |
| 0, EM_ALPHA, 0, 0); |
| if (size < 0) { |
| error_report("could not load palcode '%s'", palcode_filename); |
| exit(1); |
| } |
| g_free(palcode_filename); |
| |
| /* Start all cpus at the PALcode RESET entry point. */ |
| for (i = 0; i < smp_cpus; ++i) { |
| cpus[i]->env.pc = palcode_entry; |
| cpus[i]->env.palbr = palcode_entry; |
| } |
| |
| /* Load a kernel. */ |
| if (kernel_filename) { |
| uint64_t param_offset; |
| |
| size = load_elf(kernel_filename, NULL, cpu_alpha_superpage_to_phys, |
| NULL, &kernel_entry, &kernel_low, NULL, NULL, |
| 0, EM_ALPHA, 0, 0); |
| if (size < 0) { |
| error_report("could not load kernel '%s'", kernel_filename); |
| exit(1); |
| } |
| |
| cpus[0]->env.trap_arg1 = kernel_entry; |
| |
| param_offset = kernel_low - 0x6000; |
| |
| if (kernel_cmdline) { |
| pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline); |
| } |
| |
| if (initrd_filename) { |
| long initrd_base; |
| int64_t initrd_size; |
| |
| initrd_size = get_image_size(initrd_filename); |
| if (initrd_size < 0) { |
| error_report("could not load initial ram disk '%s'", |
| initrd_filename); |
| exit(1); |
| } |
| |
| /* Put the initrd image as high in memory as possible. */ |
| initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK; |
| load_image_targphys(initrd_filename, initrd_base, |
| ram_size - initrd_base); |
| |
| address_space_stq(&address_space_memory, param_offset + 0x100, |
| initrd_base + 0xfffffc0000000000ULL, |
| MEMTXATTRS_UNSPECIFIED, |
| NULL); |
| address_space_stq(&address_space_memory, param_offset + 0x108, |
| initrd_size, MEMTXATTRS_UNSPECIFIED, NULL); |
| } |
| } |
| } |
| |
| static void clipper_machine_init(MachineClass *mc) |
| { |
| mc->desc = "Alpha DP264/CLIPPER"; |
| mc->init = clipper_init; |
| mc->block_default_type = IF_IDE; |
| mc->max_cpus = 4; |
| mc->is_default = true; |
| mc->default_cpu_type = ALPHA_CPU_TYPE_NAME("ev67"); |
| mc->default_ram_id = "ram"; |
| mc->default_nic = "e1000"; |
| } |
| |
| DEFINE_MACHINE("clipper", clipper_machine_init) |