mirror of https://gitee.com/openkylin/qemu.git
Merge branch 'axp-system-7' of git://repo.or.cz/qemu/rth
* 'axp-system-7' of git://repo.or.cz/qemu/rth: target-alpha: Add high-resolution access to wall clock and an alarm. target-alpha: Implement HALT IPR. target-alpha: Implement WAIT IPR. target-alpha: Add CLIPPER emulation. target-alpha: Add custom PALcode image for CLIPPER emulation. target-alpha: Honor icount for RPCC instruction.
This commit is contained in:
commit
9a7242f786
|
@ -13,3 +13,6 @@
|
|||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = git://git.qemu.org/openbios.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = git://repo.or.cz/qemu-palcode.git
|
||||
|
|
3
Makefile
3
Makefile
|
@ -251,7 +251,8 @@ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
|||
mpc8544ds.dtb \
|
||||
multiboot.bin linuxboot.bin \
|
||||
s390-zipl.rom \
|
||||
spapr-rtas.bin slof.bin
|
||||
spapr-rtas.bin slof.bin \
|
||||
palcode-clipper
|
||||
else
|
||||
BLOBS=
|
||||
endif
|
||||
|
|
|
@ -367,6 +367,7 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o
|
|||
|
||||
obj-alpha-y = i8259.o mc146818rtc.o
|
||||
obj-alpha-y += vga.o cirrus_vga.o
|
||||
obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
|
||||
|
||||
obj-xtensa-y += xtensa_pic.o
|
||||
obj-xtensa-y += xtensa_sample.o
|
||||
|
|
|
@ -3618,7 +3618,13 @@ FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
|
|||
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
|
||||
FILES="$FILES pc-bios/spapr-rtas/Makefile"
|
||||
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
||||
for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.rom $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do
|
||||
for bios_file in \
|
||||
$source_path/pc-bios/*.bin \
|
||||
$source_path/pc-bios/*.rom \
|
||||
$source_path/pc-bios/*.dtb \
|
||||
$source_path/pc-bios/openbios-* \
|
||||
$source_path/pc-bios/palcode-*
|
||||
do
|
||||
FILES="$FILES pc-bios/`basename $bios_file`"
|
||||
done
|
||||
mkdir -p $DIRS
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
include pci.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_IDE_CORE=y
|
||||
CONFIG_IDE_QDEV=y
|
||||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_IDE_CMD646=y
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* 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 controler
|
||||
* that we need to emulate as well.
|
||||
*/
|
||||
|
||||
#include "hw.h"
|
||||
#include "elf.h"
|
||||
#include "loader.h"
|
||||
#include "boards.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "sysemu.h"
|
||||
#include "mc146818rtc.h"
|
||||
#include "ide.h"
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
static void clipper_init(ram_addr_t ram_size,
|
||||
const char *boot_device,
|
||||
const char *kernel_filename,
|
||||
const char *kernel_cmdline,
|
||||
const char *initrd_filename,
|
||||
const char *cpu_model)
|
||||
{
|
||||
CPUState *cpus[4];
|
||||
PCIBus *pci_bus;
|
||||
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) {
|
||||
cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67");
|
||||
}
|
||||
|
||||
cpus[0]->trap_arg0 = ram_size;
|
||||
cpus[0]->trap_arg1 = 0;
|
||||
cpus[0]->trap_arg2 = smp_cpus;
|
||||
|
||||
/* Init the chipset. */
|
||||
pci_bus = typhoon_init(ram_size, &rtc_irq, cpus, clipper_pci_map_irq);
|
||||
|
||||
rtc_init(1980, rtc_irq);
|
||||
pit_init(0x40, 0);
|
||||
isa_create_simple("i8042");
|
||||
|
||||
/* VGA setup. Don't bother loading the bios. */
|
||||
alpha_pci_vga_setup(pci_bus);
|
||||
|
||||
/* Serial code setup. */
|
||||
for (i = 0; i < MAX_SERIAL_PORTS; ++i) {
|
||||
if (serial_hds[i]) {
|
||||
serial_isa_init(i, serial_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Network setup. e1000 is good enough, failing Tulip support. */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
cpus[i]->pal_mode = 1;
|
||||
cpus[i]->pc = palcode_entry;
|
||||
cpus[i]->palbr = palcode_entry;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
cpus[0]->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, 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);
|
||||
|
||||
stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000UL);
|
||||
stq_phys(param_offset + 0x108, initrd_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* QEMU Alpha PCI support functions.
|
||||
*
|
||||
* Some of this isn't very Alpha specific at all.
|
||||
*
|
||||
* ??? Sparse memory access not implemented.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "qemu-log.h"
|
||||
#include "sysemu.h"
|
||||
#include "vmware_vga.h"
|
||||
|
||||
|
||||
/* PCI IO reads/writes, to byte-word addressable memory. */
|
||||
/* ??? Doesn't handle multiple PCI busses. */
|
||||
|
||||
static uint64_t bw_io_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return cpu_inb(addr);
|
||||
case 2:
|
||||
return cpu_inw(addr);
|
||||
case 4:
|
||||
return cpu_inl(addr);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void bw_io_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
cpu_outb(addr, val);
|
||||
break;
|
||||
case 2:
|
||||
cpu_outw(addr, val);
|
||||
break;
|
||||
case 4:
|
||||
cpu_outl(addr, val);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_bw_io_ops = {
|
||||
.read = bw_io_read,
|
||||
.write = bw_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
/* PCI config space reads/writes, to byte-word addressable memory. */
|
||||
static uint64_t bw_conf1_read(void *opaque, target_phys_addr_t addr,
|
||||
unsigned size)
|
||||
{
|
||||
PCIBus *b = opaque;
|
||||
return pci_data_read(b, addr, size);
|
||||
}
|
||||
|
||||
static void bw_conf1_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PCIBus *b = opaque;
|
||||
pci_data_write(b, addr, val, size);
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_conf1_ops = {
|
||||
.read = bw_conf1_read,
|
||||
.write = bw_conf1_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
/* PCI/EISA Interrupt Acknowledge Cycle. */
|
||||
|
||||
static uint64_t iack_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
return pic_read_irq(isa_pic);
|
||||
}
|
||||
|
||||
static void special_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
qemu_log("pci: special write cycle");
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_iack_ops = {
|
||||
.read = iack_read,
|
||||
.write = special_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
void alpha_pci_vga_setup(PCIBus *pci_bus)
|
||||
{
|
||||
switch (vga_interface_type) {
|
||||
#ifdef CONFIG_SPICE
|
||||
case VGA_QXL:
|
||||
pci_create_simple(pci_bus, -1, "qxl-vga");
|
||||
return;
|
||||
#endif
|
||||
case VGA_CIRRUS:
|
||||
pci_cirrus_vga_init(pci_bus);
|
||||
return;
|
||||
case VGA_VMWARE:
|
||||
if (pci_vmsvga_init(pci_bus)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* If VGA is enabled at all, and one of the above didn't work, then
|
||||
fallback to Standard VGA. */
|
||||
if (vga_interface_type != VGA_NONE) {
|
||||
pci_vga_init(pci_bus);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* Alpha cores and system support chips. */
|
||||
|
||||
#ifndef HW_ALPHA_H
|
||||
#define HW_ALPHA_H 1
|
||||
|
||||
#include "pci.h"
|
||||
#include "pci_host.h"
|
||||
#include "ide.h"
|
||||
#include "net.h"
|
||||
#include "pc.h"
|
||||
#include "usb-ohci.h"
|
||||
#include "irq.h"
|
||||
|
||||
|
||||
PCIBus *typhoon_init(ram_addr_t, qemu_irq *, CPUState *[4], pci_map_irq_fn);
|
||||
|
||||
/* alpha_pci.c. */
|
||||
extern const MemoryRegionOps alpha_pci_bw_io_ops;
|
||||
extern const MemoryRegionOps alpha_pci_conf1_ops;
|
||||
extern const MemoryRegionOps alpha_pci_iack_ops;
|
||||
|
||||
void alpha_pci_vga_setup(PCIBus *pci_bus);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,813 @@
|
|||
/*
|
||||
* DEC 21272 (TSUNAMI/TYPHOON) chipset emulation.
|
||||
*
|
||||
* Written by Richard Henderson.
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec-all.h"
|
||||
#include "hw.h"
|
||||
#include "devices.h"
|
||||
#include "sysemu.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
|
||||
typedef struct TyphoonCchip {
|
||||
MemoryRegion region;
|
||||
uint64_t misc;
|
||||
uint64_t drir;
|
||||
uint64_t dim[4];
|
||||
uint32_t iic[4];
|
||||
CPUState *cpu[4];
|
||||
} TyphoonCchip;
|
||||
|
||||
typedef struct TyphoonWindow {
|
||||
uint32_t base_addr;
|
||||
uint32_t mask;
|
||||
uint32_t translated_base_pfn;
|
||||
} TyphoonWindow;
|
||||
|
||||
typedef struct TyphoonPchip {
|
||||
MemoryRegion region;
|
||||
MemoryRegion reg_iack;
|
||||
MemoryRegion reg_mem;
|
||||
MemoryRegion reg_io;
|
||||
MemoryRegion reg_conf;
|
||||
uint64_t ctl;
|
||||
TyphoonWindow win[4];
|
||||
} TyphoonPchip;
|
||||
|
||||
typedef struct TyphoonState {
|
||||
PCIHostState host;
|
||||
TyphoonCchip cchip;
|
||||
TyphoonPchip pchip;
|
||||
MemoryRegion dchip_region;
|
||||
MemoryRegion ram_region;
|
||||
|
||||
/* QEMU emulation state. */
|
||||
uint32_t latch_tmp;
|
||||
} TyphoonState;
|
||||
|
||||
/* Called when one of DRIR or DIM changes. */
|
||||
static void cpu_irq_change(CPUState *env, uint64_t req)
|
||||
{
|
||||
/* If there are any non-masked interrupts, tell the cpu. */
|
||||
if (env) {
|
||||
if (req) {
|
||||
cpu_interrupt(env, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
CPUState *env = cpu_single_env;
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
if (addr & 4) {
|
||||
return s->latch_tmp;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* CSC: Cchip System Configuration Register. */
|
||||
/* All sorts of data here; probably the only thing relevant is
|
||||
PIP<14> Pchip 1 Present = 0. */
|
||||
break;
|
||||
|
||||
case 0x0040:
|
||||
/* MTR: Memory Timing Register. */
|
||||
/* All sorts of stuff related to real DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0080:
|
||||
/* MISC: Miscellaneous Register. */
|
||||
ret = s->cchip.misc | (env->cpu_index & 3);
|
||||
break;
|
||||
|
||||
case 0x00c0:
|
||||
/* MPD: Memory Presence Detect Register. */
|
||||
break;
|
||||
|
||||
case 0x0100: /* AAR0 */
|
||||
case 0x0140: /* AAR1 */
|
||||
case 0x0180: /* AAR2 */
|
||||
case 0x01c0: /* AAR3 */
|
||||
/* AAR: Array Address Register. */
|
||||
/* All sorts of information about DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0200:
|
||||
/* DIM0: Device Interrupt Mask Register, CPU0. */
|
||||
ret = s->cchip.dim[0];
|
||||
break;
|
||||
case 0x0240:
|
||||
/* DIM1: Device Interrupt Mask Register, CPU1. */
|
||||
ret = s->cchip.dim[1];
|
||||
break;
|
||||
case 0x0280:
|
||||
/* DIR0: Device Interrupt Request Register, CPU0. */
|
||||
ret = s->cchip.dim[0] & s->cchip.drir;
|
||||
break;
|
||||
case 0x02c0:
|
||||
/* DIR1: Device Interrupt Request Register, CPU1. */
|
||||
ret = s->cchip.dim[1] & s->cchip.drir;
|
||||
break;
|
||||
case 0x0300:
|
||||
/* DRIR: Device Raw Interrupt Request Register. */
|
||||
ret = s->cchip.drir;
|
||||
break;
|
||||
|
||||
case 0x0340:
|
||||
/* PRBEN: Probe Enable Register. */
|
||||
break;
|
||||
|
||||
case 0x0380:
|
||||
/* IIC0: Interval Ignore Count Register, CPU0. */
|
||||
ret = s->cchip.iic[0];
|
||||
break;
|
||||
case 0x03c0:
|
||||
/* IIC1: Interval Ignore Count Register, CPU1. */
|
||||
ret = s->cchip.iic[1];
|
||||
break;
|
||||
|
||||
case 0x0400: /* MPR0 */
|
||||
case 0x0440: /* MPR1 */
|
||||
case 0x0480: /* MPR2 */
|
||||
case 0x04c0: /* MPR3 */
|
||||
/* MPR: Memory Programming Register. */
|
||||
break;
|
||||
|
||||
case 0x0580:
|
||||
/* TTR: TIGbus Timing Register. */
|
||||
/* All sorts of stuff related to interrupt delivery timings. */
|
||||
break;
|
||||
case 0x05c0:
|
||||
/* TDR: TIGbug Device Timing Register. */
|
||||
break;
|
||||
|
||||
case 0x0600:
|
||||
/* DIM2: Device Interrupt Mask Register, CPU2. */
|
||||
ret = s->cchip.dim[2];
|
||||
break;
|
||||
case 0x0640:
|
||||
/* DIM3: Device Interrupt Mask Register, CPU3. */
|
||||
ret = s->cchip.dim[3];
|
||||
break;
|
||||
case 0x0680:
|
||||
/* DIR2: Device Interrupt Request Register, CPU2. */
|
||||
ret = s->cchip.dim[2] & s->cchip.drir;
|
||||
break;
|
||||
case 0x06c0:
|
||||
/* DIR3: Device Interrupt Request Register, CPU3. */
|
||||
ret = s->cchip.dim[3] & s->cchip.drir;
|
||||
break;
|
||||
|
||||
case 0x0700:
|
||||
/* IIC2: Interval Ignore Count Register, CPU2. */
|
||||
ret = s->cchip.iic[2];
|
||||
break;
|
||||
case 0x0740:
|
||||
/* IIC3: Interval Ignore Count Register, CPU3. */
|
||||
ret = s->cchip.iic[3];
|
||||
break;
|
||||
|
||||
case 0x0780:
|
||||
/* PWR: Power Management Control. */
|
||||
break;
|
||||
|
||||
case 0x0c00: /* CMONCTLA */
|
||||
case 0x0c40: /* CMONCTLB */
|
||||
case 0x0c80: /* CMONCNT01 */
|
||||
case 0x0cc0: /* CMONCNT23 */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->latch_tmp = ret >> 32;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t dchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
/* Skip this. It's all related to DRAM timing and setup. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t pchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
if (addr & 4) {
|
||||
return s->latch_tmp;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* WSBA0: Window Space Base Address Register. */
|
||||
ret = s->pchip.win[0].base_addr;
|
||||
break;
|
||||
case 0x0040:
|
||||
/* WSBA1 */
|
||||
ret = s->pchip.win[1].base_addr;
|
||||
break;
|
||||
case 0x0080:
|
||||
/* WSBA2 */
|
||||
ret = s->pchip.win[2].base_addr;
|
||||
break;
|
||||
case 0x00c0:
|
||||
/* WSBA3 */
|
||||
ret = s->pchip.win[3].base_addr;
|
||||
break;
|
||||
|
||||
case 0x0100:
|
||||
/* WSM0: Window Space Mask Register. */
|
||||
ret = s->pchip.win[0].mask;
|
||||
break;
|
||||
case 0x0140:
|
||||
/* WSM1 */
|
||||
ret = s->pchip.win[1].mask;
|
||||
break;
|
||||
case 0x0180:
|
||||
/* WSM2 */
|
||||
ret = s->pchip.win[2].mask;
|
||||
break;
|
||||
case 0x01c0:
|
||||
/* WSM3 */
|
||||
ret = s->pchip.win[3].mask;
|
||||
break;
|
||||
|
||||
case 0x0200:
|
||||
/* TBA0: Translated Base Address Register. */
|
||||
ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10;
|
||||
break;
|
||||
case 0x0240:
|
||||
/* TBA1 */
|
||||
ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10;
|
||||
break;
|
||||
case 0x0280:
|
||||
/* TBA2 */
|
||||
ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10;
|
||||
break;
|
||||
case 0x02c0:
|
||||
/* TBA3 */
|
||||
ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10;
|
||||
break;
|
||||
|
||||
case 0x0300:
|
||||
/* PCTL: Pchip Control Register. */
|
||||
ret = s->pchip.ctl;
|
||||
break;
|
||||
case 0x0340:
|
||||
/* PLAT: Pchip Master Latency Register. */
|
||||
break;
|
||||
case 0x03c0:
|
||||
/* PERROR: Pchip Error Register. */
|
||||
break;
|
||||
case 0x0400:
|
||||
/* PERRMASK: Pchip Error Mask Register. */
|
||||
break;
|
||||
case 0x0440:
|
||||
/* PERRSET: Pchip Error Set Register. */
|
||||
break;
|
||||
case 0x0480:
|
||||
/* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */
|
||||
break;
|
||||
case 0x04c0:
|
||||
/* TLBIA: Translation Buffer Invalidate All Register (WO). */
|
||||
break;
|
||||
case 0x0500: /* PMONCTL */
|
||||
case 0x0540: /* PMONCNT */
|
||||
case 0x0800: /* SPRST */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->latch_tmp = ret >> 32;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cchip_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t v32, unsigned size)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t val, oldval, newval;
|
||||
|
||||
if (addr & 4) {
|
||||
val = v32 << 32 | s->latch_tmp;
|
||||
addr ^= 4;
|
||||
} else {
|
||||
s->latch_tmp = v32;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* CSC: Cchip System Configuration Register. */
|
||||
/* All sorts of data here; nothing relevant RW. */
|
||||
break;
|
||||
|
||||
case 0x0040:
|
||||
/* MTR: Memory Timing Register. */
|
||||
/* All sorts of stuff related to real DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0080:
|
||||
/* MISC: Miscellaneous Register. */
|
||||
newval = oldval = s->cchip.misc;
|
||||
newval &= ~(val & 0x10000ff0); /* W1C fields */
|
||||
if (val & 0x100000) {
|
||||
newval &= ~0xff0000ull; /* ACL clears ABT and ABW */
|
||||
} else {
|
||||
newval |= val & 0x00f00000; /* ABT field is W1S */
|
||||
if ((newval & 0xf0000) == 0) {
|
||||
newval |= val & 0xf0000; /* ABW field is W1S iff zero */
|
||||
}
|
||||
}
|
||||
newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */
|
||||
|
||||
newval &= ~0xf0000000000ull; /* WO and RW fields */
|
||||
newval |= val & 0xf0000000000ull;
|
||||
s->cchip.misc = newval;
|
||||
|
||||
/* Pass on changes to IPI and ITI state. */
|
||||
if ((newval ^ oldval) & 0xff0) {
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
CPUState *env = s->cchip.cpu[i];
|
||||
if (env) {
|
||||
/* IPI can be either cleared or set by the write. */
|
||||
if (newval & (1 << (i + 8))) {
|
||||
cpu_interrupt(env, CPU_INTERRUPT_SMP);
|
||||
} else {
|
||||
cpu_reset_interrupt(env, CPU_INTERRUPT_SMP);
|
||||
}
|
||||
|
||||
/* ITI can only be cleared by the write. */
|
||||
if ((newval & (1 << (i + 4))) == 0) {
|
||||
cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x00c0:
|
||||
/* MPD: Memory Presence Detect Register. */
|
||||
break;
|
||||
|
||||
case 0x0100: /* AAR0 */
|
||||
case 0x0140: /* AAR1 */
|
||||
case 0x0180: /* AAR2 */
|
||||
case 0x01c0: /* AAR3 */
|
||||
/* AAR: Array Address Register. */
|
||||
/* All sorts of information about DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0200: /* DIM0 */
|
||||
/* DIM: Device Interrupt Mask Register, CPU0. */
|
||||
s->cchip.dim[0] = val;
|
||||
cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir);
|
||||
break;
|
||||
case 0x0240: /* DIM1 */
|
||||
/* DIM: Device Interrupt Mask Register, CPU1. */
|
||||
s->cchip.dim[0] = val;
|
||||
cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir);
|
||||
break;
|
||||
|
||||
case 0x0280: /* DIR0 (RO) */
|
||||
case 0x02c0: /* DIR1 (RO) */
|
||||
case 0x0300: /* DRIR (RO) */
|
||||
break;
|
||||
|
||||
case 0x0340:
|
||||
/* PRBEN: Probe Enable Register. */
|
||||
break;
|
||||
|
||||
case 0x0380: /* IIC0 */
|
||||
s->cchip.iic[0] = val & 0xffffff;
|
||||
break;
|
||||
case 0x03c0: /* IIC1 */
|
||||
s->cchip.iic[1] = val & 0xffffff;
|
||||
break;
|
||||
|
||||
case 0x0400: /* MPR0 */
|
||||
case 0x0440: /* MPR1 */
|
||||
case 0x0480: /* MPR2 */
|
||||
case 0x04c0: /* MPR3 */
|
||||
/* MPR: Memory Programming Register. */
|
||||
break;
|
||||
|
||||
case 0x0580:
|
||||
/* TTR: TIGbus Timing Register. */
|
||||
/* All sorts of stuff related to interrupt delivery timings. */
|
||||
break;
|
||||
case 0x05c0:
|
||||
/* TDR: TIGbug Device Timing Register. */
|
||||
break;
|
||||
|
||||
case 0x0600:
|
||||
/* DIM2: Device Interrupt Mask Register, CPU2. */
|
||||
s->cchip.dim[2] = val;
|
||||
cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir);
|
||||
break;
|
||||
case 0x0640:
|
||||
/* DIM3: Device Interrupt Mask Register, CPU3. */
|
||||
s->cchip.dim[3] = val;
|
||||
cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir);
|
||||
break;
|
||||
|
||||
case 0x0680: /* DIR2 (RO) */
|
||||
case 0x06c0: /* DIR3 (RO) */
|
||||
break;
|
||||
|
||||
case 0x0700: /* IIC2 */
|
||||
s->cchip.iic[2] = val & 0xffffff;
|
||||
break;
|
||||
case 0x0740: /* IIC3 */
|
||||
s->cchip.iic[3] = val & 0xffffff;
|
||||
break;
|
||||
|
||||
case 0x0780:
|
||||
/* PWR: Power Management Control. */
|
||||
break;
|
||||
|
||||
case 0x0c00: /* CMONCTLA */
|
||||
case 0x0c40: /* CMONCTLB */
|
||||
case 0x0c80: /* CMONCNT01 */
|
||||
case 0x0cc0: /* CMONCNT23 */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void dchip_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
/* Skip this. It's all related to DRAM timing and setup. */
|
||||
}
|
||||
|
||||
static void pchip_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t v32, unsigned size)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t val, oldval;
|
||||
|
||||
if (addr & 4) {
|
||||
val = v32 << 32 | s->latch_tmp;
|
||||
addr ^= 4;
|
||||
} else {
|
||||
s->latch_tmp = v32;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* WSBA0: Window Space Base Address Register. */
|
||||
s->pchip.win[0].base_addr = val;
|
||||
break;
|
||||
case 0x0040:
|
||||
/* WSBA1 */
|
||||
s->pchip.win[1].base_addr = val;
|
||||
break;
|
||||
case 0x0080:
|
||||
/* WSBA2 */
|
||||
s->pchip.win[2].base_addr = val;
|
||||
break;
|
||||
case 0x00c0:
|
||||
/* WSBA3 */
|
||||
s->pchip.win[3].base_addr = val;
|
||||
break;
|
||||
|
||||
case 0x0100:
|
||||
/* WSM0: Window Space Mask Register. */
|
||||
s->pchip.win[0].mask = val;
|
||||
break;
|
||||
case 0x0140:
|
||||
/* WSM1 */
|
||||
s->pchip.win[1].mask = val;
|
||||
break;
|
||||
case 0x0180:
|
||||
/* WSM2 */
|
||||
s->pchip.win[2].mask = val;
|
||||
break;
|
||||
case 0x01c0:
|
||||
/* WSM3 */
|
||||
s->pchip.win[3].mask = val;
|
||||
break;
|
||||
|
||||
case 0x0200:
|
||||
/* TBA0: Translated Base Address Register. */
|
||||
s->pchip.win[0].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
case 0x0240:
|
||||
/* TBA1 */
|
||||
s->pchip.win[1].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
case 0x0280:
|
||||
/* TBA2 */
|
||||
s->pchip.win[2].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
case 0x02c0:
|
||||
/* TBA3 */
|
||||
s->pchip.win[3].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
|
||||
case 0x0300:
|
||||
/* PCTL: Pchip Control Register. */
|
||||
oldval = s->pchip.ctl;
|
||||
oldval &= ~0x00001cff0fc7ffull; /* RW fields */
|
||||
oldval |= val & 0x00001cff0fc7ffull;
|
||||
|
||||
s->pchip.ctl = oldval;
|
||||
break;
|
||||
|
||||
case 0x0340:
|
||||
/* PLAT: Pchip Master Latency Register. */
|
||||
break;
|
||||
case 0x03c0:
|
||||
/* PERROR: Pchip Error Register. */
|
||||
break;
|
||||
case 0x0400:
|
||||
/* PERRMASK: Pchip Error Mask Register. */
|
||||
break;
|
||||
case 0x0440:
|
||||
/* PERRSET: Pchip Error Set Register. */
|
||||
break;
|
||||
|
||||
case 0x0480:
|
||||
/* TLBIV: Translation Buffer Invalidate Virtual Register. */
|
||||
break;
|
||||
|
||||
case 0x04c0:
|
||||
/* TLBIA: Translation Buffer Invalidate All Register (WO). */
|
||||
break;
|
||||
|
||||
case 0x0500:
|
||||
/* PMONCTL */
|
||||
case 0x0540:
|
||||
/* PMONCNT */
|
||||
case 0x0800:
|
||||
/* SPRST */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps cchip_ops = {
|
||||
.read = cchip_read,
|
||||
.write = cchip_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4, /* ??? Should be 8. */
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps dchip_ops = {
|
||||
.read = dchip_read,
|
||||
.write = dchip_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4, /* ??? Should be 8. */
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps pchip_ops = {
|
||||
.read = pchip_read,
|
||||
.write = pchip_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4, /* ??? Should be 8. */
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void typhoon_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t drir;
|
||||
int i;
|
||||
|
||||
/* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */
|
||||
drir = s->cchip.drir;
|
||||
if (level) {
|
||||
drir |= 1ull << irq;
|
||||
} else {
|
||||
drir &= ~(1ull << irq);
|
||||
}
|
||||
s->cchip.drir = drir;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir);
|
||||
}
|
||||
}
|
||||
|
||||
static void typhoon_set_isa_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
typhoon_set_irq(opaque, 55, level);
|
||||
}
|
||||
|
||||
static void typhoon_set_timer_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
int i;
|
||||
|
||||
/* Thankfully, the mc146818rtc code doesn't track the IRQ state,
|
||||
and so we don't have to worry about missing interrupts just
|
||||
because we never actually ACK the interrupt. Just ignore any
|
||||
case of the interrupt level going low. */
|
||||
if (level == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Deliver the interrupt to each CPU, considering each CPU's IIC. */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
CPUState *env = s->cchip.cpu[i];
|
||||
if (env) {
|
||||
uint32_t iic = s->cchip.iic[i];
|
||||
|
||||
/* ??? The verbage in Section 10.2.2.10 isn't 100% clear.
|
||||
Bit 24 is the OverFlow bit, RO, and set when the count
|
||||
decrements past 0. When is OF cleared? My guess is that
|
||||
OF is actually cleared when the IIC is written, and that
|
||||
the ICNT field always decrements. At least, that's an
|
||||
interpretation that makes sense, and "allows the CPU to
|
||||
determine exactly how mant interval timer ticks were
|
||||
skipped". At least within the next 4M ticks... */
|
||||
|
||||
iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000);
|
||||
s->cchip.iic[i] = iic;
|
||||
|
||||
if (iic & 0x1000000) {
|
||||
/* Set the ITI bit for this cpu. */
|
||||
s->cchip.misc |= 1 << (i + 4);
|
||||
/* And signal the interrupt. */
|
||||
cpu_interrupt(env, CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void typhoon_alarm_timer(void *opaque)
|
||||
{
|
||||
TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3);
|
||||
int cpu = (uintptr_t)opaque & 3;
|
||||
|
||||
/* Set the ITI bit for this cpu. */
|
||||
s->cchip.misc |= 1 << (cpu + 4);
|
||||
cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
|
||||
PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq,
|
||||
CPUState *cpus[4], pci_map_irq_fn sys_map_irq)
|
||||
{
|
||||
const uint64_t MB = 1024 * 1024;
|
||||
const uint64_t GB = 1024 * MB;
|
||||
MemoryRegion *addr_space = get_system_memory();
|
||||
MemoryRegion *addr_space_io = get_system_io();
|
||||
DeviceState *dev;
|
||||
PCIHostState *p;
|
||||
TyphoonState *s;
|
||||
PCIBus *b;
|
||||
int i;
|
||||
|
||||
dev = qdev_create(NULL, "typhoon-pcihost");
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev));
|
||||
s = container_of(p, TyphoonState, host);
|
||||
|
||||
/* Remember the CPUs so that we can deliver interrupts to them. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
CPUState *env = cpus[i];
|
||||
s->cchip.cpu[i] = env;
|
||||
if (env) {
|
||||
env->alarm_timer = qemu_new_timer_ns(rtc_clock,
|
||||
typhoon_alarm_timer,
|
||||
(void *)((uintptr_t)s + i));
|
||||
}
|
||||
}
|
||||
|
||||
*p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1);
|
||||
|
||||
/* Main memory region, 0x00.0000.0000. Real hardware supports 32GB,
|
||||
but the address space hole reserved at this point is 8TB. */
|
||||
memory_region_init_ram(&s->ram_region, NULL, "ram", ram_size);
|
||||
memory_region_add_subregion(addr_space, 0, &s->ram_region);
|
||||
|
||||
/* TIGbus, 0x801.0000.0000, 1GB. */
|
||||
/* ??? The TIGbus is used for delivering interrupts, and access to
|
||||
the flash ROM. I'm not sure that we need to implement it at all. */
|
||||
|
||||
/* Pchip0 CSRs, 0x801.8000.0000, 256MB. */
|
||||
memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB);
|
||||
memory_region_add_subregion(addr_space, 0x80180000000, &s->pchip.region);
|
||||
|
||||
/* Cchip CSRs, 0x801.A000.0000, 256MB. */
|
||||
memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801a0000000, &s->cchip.region);
|
||||
|
||||
/* Dchip CSRs, 0x801.B000.0000, 256MB. */
|
||||
memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801b0000000, &s->dchip_region);
|
||||
|
||||
/* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */
|
||||
memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB);
|
||||
memory_region_add_subregion(addr_space, 0x80000000000, &s->pchip.reg_mem);
|
||||
|
||||
/* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */
|
||||
/* ??? Ideally we drop the "system" i/o space on the floor and give the
|
||||
PCI subsystem the full address space reserved by the chipset.
|
||||
We can't do that until the MEM and IO paths in memory.c are unified. */
|
||||
memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL,
|
||||
"pci0-io", 32*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801fc000000, &s->pchip.reg_io);
|
||||
|
||||
b = pci_register_bus(&s->host.busdev.qdev, "pci",
|
||||
typhoon_set_irq, sys_map_irq, s,
|
||||
&s->pchip.reg_mem, addr_space_io, 0, 64);
|
||||
s->host.bus = b;
|
||||
|
||||
/* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */
|
||||
memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b,
|
||||
"pci0-iack", 64*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801f8000000, &s->pchip.reg_iack);
|
||||
|
||||
/* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */
|
||||
memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b,
|
||||
"pci0-conf", 16*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801fe000000, &s->pchip.reg_conf);
|
||||
|
||||
/* For the record, these are the mappings for the second PCI bus.
|
||||
We can get away with not implementing them because we indicate
|
||||
via the Cchip.CSC<PIP> bit that Pchip1 is not present. */
|
||||
/* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */
|
||||
/* Pchip1 CSRs, 0x802.8000.0000, 256MB. */
|
||||
/* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */
|
||||
/* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */
|
||||
/* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */
|
||||
|
||||
/* Init the ISA bus. */
|
||||
/* ??? Technically there should be a cy82c693ub pci-isa bridge. */
|
||||
{
|
||||
qemu_irq isa_pci_irq, *isa_irqs;
|
||||
|
||||
isa_bus_new(NULL, addr_space_io);
|
||||
isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1);
|
||||
isa_irqs = i8259_init(isa_pci_irq);
|
||||
isa_bus_irqs(isa_irqs);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static int typhoon_pcihost_init(SysBusDevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SysBusDeviceInfo typhoon_pcihost_info = {
|
||||
.init = typhoon_pcihost_init,
|
||||
.qdev.name = "typhoon-pcihost",
|
||||
.qdev.size = sizeof(TyphoonState),
|
||||
.qdev.no_user = 1
|
||||
};
|
||||
|
||||
static void typhoon_register(void)
|
||||
{
|
||||
sysbus_register_withprop(&typhoon_pcihost_info);
|
||||
}
|
||||
device_init(typhoon_register);
|
|
@ -32,3 +32,6 @@
|
|||
- The S390 zipl loader is an addition to the official IBM s390-tools
|
||||
package. That fork is maintained in its own git repository at:
|
||||
git://repo.or.cz/s390-tools.git
|
||||
|
||||
- The sources for the Alpha palcode image is available from:
|
||||
git://repo.or.cz/qemu-palcode.git
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
Subproject commit 7abb12f60eb3069019e9497e193733d77d8f0722
|
|
@ -265,6 +265,10 @@ struct CPUAlphaState {
|
|||
uint64_t scratch[24];
|
||||
#endif
|
||||
|
||||
/* This alarm doesn't exist in real hardware; we wish it did. */
|
||||
struct QEMUTimer *alarm_timer;
|
||||
uint64_t alarm_expire;
|
||||
|
||||
#if TARGET_LONG_BITS > HOST_LONG_BITS
|
||||
/* temporary fixed-point registers
|
||||
* used to emulate 64 bits target on 32 bits hosts
|
||||
|
|
|
@ -113,6 +113,11 @@ DEF_HELPER_2(stq_c_phys, i64, i64, i64)
|
|||
|
||||
DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void)
|
||||
DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64)
|
||||
|
||||
DEF_HELPER_1(halt, void, i64);
|
||||
|
||||
DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64)
|
||||
DEF_HELPER_FLAGS_1(set_alarm, TCG_CALL_CONST, void, i64)
|
||||
#endif
|
||||
|
||||
#include "def-helper.h"
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "host-utils.h"
|
||||
#include "softfloat.h"
|
||||
#include "helper.h"
|
||||
#include "sysemu.h"
|
||||
#include "qemu-timer.h"
|
||||
|
||||
#define FP_STATUS (env->fp_status)
|
||||
|
@ -1218,6 +1219,30 @@ void helper_tbis(uint64_t p)
|
|||
{
|
||||
tlb_flush_page(env, p);
|
||||
}
|
||||
|
||||
void helper_halt(uint64_t restart)
|
||||
{
|
||||
if (restart) {
|
||||
qemu_system_reset_request();
|
||||
} else {
|
||||
qemu_system_shutdown_request();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t helper_get_time(void)
|
||||
{
|
||||
return qemu_get_clock_ns(rtc_clock);
|
||||
}
|
||||
|
||||
void helper_set_alarm(uint64_t expire)
|
||||
{
|
||||
if (expire) {
|
||||
env->alarm_expire = expire;
|
||||
qemu_mod_timer(env->alarm_timer, expire);
|
||||
} else {
|
||||
qemu_del_timer(env->alarm_timer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -1590,18 +1590,34 @@ static int cpu_pr_data(int pr)
|
|||
return offsetof(CPUAlphaState, shadow[pr - 32]);
|
||||
case 40 ... 63:
|
||||
return offsetof(CPUAlphaState, scratch[pr - 40]);
|
||||
|
||||
case 251:
|
||||
return offsetof(CPUAlphaState, alarm_expire);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gen_mfpr(int ra, int regno)
|
||||
static ExitStatus gen_mfpr(int ra, int regno)
|
||||
{
|
||||
int data = cpu_pr_data(regno);
|
||||
|
||||
/* In our emulated PALcode, these processor registers have no
|
||||
side effects from reading. */
|
||||
if (ra == 31) {
|
||||
return;
|
||||
return NO_EXIT;
|
||||
}
|
||||
|
||||
if (regno == 250) {
|
||||
/* WALL_TIME */
|
||||
if (use_icount) {
|
||||
gen_io_start();
|
||||
gen_helper_get_time(cpu_ir[ra]);
|
||||
gen_io_end();
|
||||
return EXIT_PC_STALE;
|
||||
} else {
|
||||
gen_helper_get_time(cpu_ir[ra]);
|
||||
return NO_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
/* The basic registers are data only, and unknown registers
|
||||
|
@ -1615,11 +1631,13 @@ static void gen_mfpr(int ra, int regno)
|
|||
} else {
|
||||
tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data);
|
||||
}
|
||||
return NO_EXIT;
|
||||
}
|
||||
|
||||
static void gen_mtpr(int rb, int regno)
|
||||
static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno)
|
||||
{
|
||||
TCGv tmp;
|
||||
int data;
|
||||
|
||||
if (rb == 31) {
|
||||
tmp = tcg_const_i64(0);
|
||||
|
@ -1627,19 +1645,37 @@ static void gen_mtpr(int rb, int regno)
|
|||
tmp = cpu_ir[rb];
|
||||
}
|
||||
|
||||
/* These two register numbers perform a TLB cache flush. Thankfully we
|
||||
can only do this inside PALmode, which means that the current basic
|
||||
block cannot be affected by the change in mappings. */
|
||||
if (regno == 255) {
|
||||
switch (regno) {
|
||||
case 255:
|
||||
/* TBIA */
|
||||
gen_helper_tbia();
|
||||
} else if (regno == 254) {
|
||||
break;
|
||||
|
||||
case 254:
|
||||
/* TBIS */
|
||||
gen_helper_tbis(tmp);
|
||||
} else {
|
||||
break;
|
||||
|
||||
case 253:
|
||||
/* WAIT */
|
||||
tmp = tcg_const_i64(1);
|
||||
tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUState, halted));
|
||||
return gen_excp(ctx, EXCP_HLT, 0);
|
||||
|
||||
case 252:
|
||||
/* HALT */
|
||||
gen_helper_halt(tmp);
|
||||
return EXIT_PC_STALE;
|
||||
|
||||
case 251:
|
||||
/* ALARM */
|
||||
gen_helper_set_alarm(tmp);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* The basic registers are data only, and unknown registers
|
||||
are read-zero, write-ignore. */
|
||||
int data = cpu_pr_data(regno);
|
||||
data = cpu_pr_data(regno);
|
||||
if (data != 0) {
|
||||
if (data & PR_BYTE) {
|
||||
tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE);
|
||||
|
@ -1649,11 +1685,14 @@ static void gen_mtpr(int rb, int regno)
|
|||
tcg_gen_st_i64(tmp, cpu_env, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (rb == 31) {
|
||||
tcg_temp_free(tmp);
|
||||
}
|
||||
|
||||
return NO_EXIT;
|
||||
}
|
||||
#endif /* !USER_ONLY*/
|
||||
|
||||
|
@ -2721,8 +2760,16 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
|
|||
break;
|
||||
case 0xC000:
|
||||
/* RPCC */
|
||||
if (ra != 31)
|
||||
gen_helper_load_pcc(cpu_ir[ra]);
|
||||
if (ra != 31) {
|
||||
if (use_icount) {
|
||||
gen_io_start();
|
||||
gen_helper_load_pcc(cpu_ir[ra]);
|
||||
gen_io_end();
|
||||
ret = EXIT_PC_STALE;
|
||||
} else {
|
||||
gen_helper_load_pcc(cpu_ir[ra]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
/* RC */
|
||||
|
@ -2747,8 +2794,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
|
|||
/* HW_MFPR (PALcode) */
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (ctx->tb->flags & TB_FLAGS_PAL_MODE) {
|
||||
gen_mfpr(ra, insn & 0xffff);
|
||||
break;
|
||||
return gen_mfpr(ra, insn & 0xffff);
|
||||
}
|
||||
#endif
|
||||
goto invalid_opc;
|
||||
|
@ -3053,8 +3099,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
|
|||
/* HW_MTPR (PALcode) */
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (ctx->tb->flags & TB_FLAGS_PAL_MODE) {
|
||||
gen_mtpr(rb, insn & 0xffff);
|
||||
break;
|
||||
return gen_mtpr(ctx, rb, insn & 0xffff);
|
||||
}
|
||||
#endif
|
||||
goto invalid_opc;
|
||||
|
|
Loading…
Reference in New Issue