2011-08-25 23:38:59 +02:00
|
|
|
/*
|
|
|
|
* QEMU Alpha DP264/CLIPPER hardware system emulator.
|
|
|
|
*
|
|
|
|
* Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK
|
2011-11-29 09:52:39 +01:00
|
|
|
* variants because CLIPPER doesn't have an SMC669 SuperIO controller
|
2011-08-25 23:38:59 +02:00
|
|
|
* that we need to emulate as well.
|
|
|
|
*/
|
|
|
|
|
2013-02-04 15:40:22 +01:00
|
|
|
#include "hw/hw.h"
|
2011-08-25 23:38:59 +02:00
|
|
|
#include "elf.h"
|
2013-02-04 15:40:22 +01:00
|
|
|
#include "hw/loader.h"
|
|
|
|
#include "hw/boards.h"
|
2013-03-18 17:36:02 +01:00
|
|
|
#include "alpha_sys.h"
|
2012-12-17 18:20:04 +01:00
|
|
|
#include "sysemu/sysemu.h"
|
2013-02-05 17:06:20 +01:00
|
|
|
#include "hw/timer/mc146818rtc.h"
|
2013-02-04 15:40:22 +01:00
|
|
|
#include "hw/ide.h"
|
2013-02-05 17:06:20 +01:00
|
|
|
#include "hw/timer/i8254.h"
|
|
|
|
#include "hw/char/serial.h"
|
2011-08-25 23:38:59 +02:00
|
|
|
|
|
|
|
#define MAX_IDE_BUS 2
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-10-15 22:22:02 +02:00
|
|
|
static void clipper_init(QEMUMachineInitArgs *args)
|
2011-08-25 23:38:59 +02:00
|
|
|
{
|
2012-10-15 22:22:02 +02:00
|
|
|
ram_addr_t ram_size = args->ram_size;
|
|
|
|
const char *cpu_model = args->cpu_model;
|
|
|
|
const char *kernel_filename = args->kernel_filename;
|
|
|
|
const char *kernel_cmdline = args->kernel_cmdline;
|
|
|
|
const char *initrd_filename = args->initrd_filename;
|
2012-10-16 02:45:53 +02:00
|
|
|
AlphaCPU *cpus[4];
|
2011-08-25 23:38:59 +02:00
|
|
|
PCIBus *pci_bus;
|
2011-12-15 22:09:51 +01:00
|
|
|
ISABus *isa_bus;
|
2011-08-25 23:38:59 +02:00
|
|
|
qemu_irq rtc_irq;
|
|
|
|
long size, i;
|
|
|
|
const char *palcode_filename;
|
|
|
|
uint64_t palcode_entry, palcode_low, palcode_high;
|
|
|
|
uint64_t kernel_entry, kernel_low, kernel_high;
|
|
|
|
|
|
|
|
/* Create up to 4 cpus. */
|
|
|
|
memset(cpus, 0, sizeof(cpus));
|
|
|
|
for (i = 0; i < smp_cpus; ++i) {
|
2012-10-16 02:45:53 +02:00
|
|
|
cpus[i] = cpu_alpha_init(cpu_model ? cpu_model : "ev67");
|
2011-08-25 23:38:59 +02:00
|
|
|
}
|
|
|
|
|
2012-10-16 02:45:53 +02:00
|
|
|
cpus[0]->env.trap_arg0 = ram_size;
|
|
|
|
cpus[0]->env.trap_arg1 = 0;
|
|
|
|
cpus[0]->env.trap_arg2 = smp_cpus;
|
2011-08-25 23:38:59 +02:00
|
|
|
|
|
|
|
/* Init the chipset. */
|
2011-12-15 22:09:55 +01:00
|
|
|
pci_bus = typhoon_init(ram_size, &isa_bus, &rtc_irq, cpus,
|
|
|
|
clipper_pci_map_irq);
|
2011-08-25 23:38:59 +02:00
|
|
|
|
2013-07-14 02:23:37 +02:00
|
|
|
/* Since we have an SRM-compatible PALcode, use the SRM epoch. */
|
|
|
|
rtc_init(isa_bus, 1900, rtc_irq);
|
|
|
|
|
2012-02-01 20:31:40 +01:00
|
|
|
pit_init(isa_bus, 0x40, 0, NULL);
|
2011-12-15 22:09:51 +01:00
|
|
|
isa_create_simple(isa_bus, "i8042");
|
2011-08-25 23:38:59 +02:00
|
|
|
|
|
|
|
/* VGA setup. Don't bother loading the bios. */
|
2012-09-08 12:16:28 +02:00
|
|
|
pci_vga_init(pci_bus);
|
2011-08-25 23:38:59 +02:00
|
|
|
|
|
|
|
/* Serial code setup. */
|
|
|
|
for (i = 0; i < MAX_SERIAL_PORTS; ++i) {
|
|
|
|
if (serial_hds[i]) {
|
2011-12-15 22:09:51 +01:00
|
|
|
serial_isa_init(isa_bus, i, serial_hds[i]);
|
2011-08-25 23:38:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Network setup. e1000 is good enough, failing Tulip support. */
|
|
|
|
for (i = 0; i < nb_nics; i++) {
|
2013-06-06 10:48:51 +02:00
|
|
|
pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL);
|
2011-08-25 23:38:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* IDE disk setup. */
|
|
|
|
{
|
|
|
|
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
|
|
|
|
ide_drive_get(hd, MAX_IDE_BUS);
|
|
|
|
|
|
|
|
pci_cmd646_ide_init(pci_bus, hd, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 = (bios_name ? bios_name : "palcode-clipper");
|
|
|
|
palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename);
|
|
|
|
if (palcode_filename == NULL) {
|
|
|
|
hw_error("no palcode provided\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
|
|
|
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
|
|
|
0, EM_ALPHA, 0);
|
|
|
|
if (size < 0) {
|
|
|
|
hw_error("could not load palcode '%s'\n", palcode_filename);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Start all cpus at the PALcode RESET entry point. */
|
|
|
|
for (i = 0; i < smp_cpus; ++i) {
|
2012-10-16 02:45:53 +02:00
|
|
|
cpus[i]->env.pal_mode = 1;
|
|
|
|
cpus[i]->env.pc = palcode_entry;
|
|
|
|
cpus[i]->env.palbr = palcode_entry;
|
2011-08-25 23:38:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Load a kernel. */
|
|
|
|
if (kernel_filename) {
|
|
|
|
uint64_t param_offset;
|
|
|
|
|
|
|
|
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
|
|
|
|
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
|
|
|
0, EM_ALPHA, 0);
|
|
|
|
if (size < 0) {
|
|
|
|
hw_error("could not load kernel '%s'\n", kernel_filename);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2012-10-16 02:45:53 +02:00
|
|
|
cpus[0]->env.trap_arg1 = kernel_entry;
|
2011-08-25 23:38:59 +02:00
|
|
|
|
|
|
|
param_offset = kernel_low - 0x6000;
|
|
|
|
|
|
|
|
if (kernel_cmdline) {
|
|
|
|
pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (initrd_filename) {
|
|
|
|
long initrd_base, initrd_size;
|
|
|
|
|
|
|
|
initrd_size = get_image_size(initrd_filename);
|
|
|
|
if (initrd_size < 0) {
|
|
|
|
hw_error("could not load initial ram disk '%s'\n",
|
|
|
|
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);
|
|
|
|
|
2013-11-28 00:11:44 +01:00
|
|
|
stq_phys(&address_space_memory,
|
|
|
|
param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL);
|
|
|
|
stq_phys(&address_space_memory, param_offset + 0x108, initrd_size);
|
2011-08-25 23:38:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static QEMUMachine clipper_machine = {
|
|
|
|
.name = "clipper",
|
|
|
|
.desc = "Alpha DP264/CLIPPER",
|
|
|
|
.init = clipper_init,
|
|
|
|
.max_cpus = 4,
|
|
|
|
.is_default = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void clipper_machine_init(void)
|
|
|
|
{
|
|
|
|
qemu_register_machine(&clipper_machine);
|
|
|
|
}
|
|
|
|
|
|
|
|
machine_init(clipper_machine_init);
|