Implement hppa-softmmu
-----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJacdBaAAoJEGTfOOivfiFfuRkH/RVM6dlKLwdp3yUhCnQgCtHw MOPY4ioJASvqQHOa5vP4txqzouYtooKbPsOuOaMW2vvDDSxPhfyrQ4x1GspxT/qs HYZra1VX6kUMIQk0GiRdf8lBZFyGgvbr1UHM+CnbrwldrsBa37UnzsPdetKGmZDb DH+hxQzVLIsuW3jcUHo+bUDunHqMrjwpPQ/ZTEF5VQvkVKSkV7BUy5kEVdTvl3Yh ZjsPqxwPgKf3TPmmpFnStb69mMHxfU7t947HMtTd+xk4xBC1sGcSTIppKHxURiSO FUMJi4qITdbGDXlgtfVWUJJE+qcgUL0t6+xJELzdfkM7YX0WG2ze47yaLTuqvlw= =PRht -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/rth/tags/pull-hppa-20180131' into staging Implement hppa-softmmu # gpg: Signature made Wed 31 Jan 2018 14:19:06 GMT # gpg: using RSA key 0x64DF38E8AF7E215F # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * remotes/rth/tags/pull-hppa-20180131: (43 commits) target/hppa: Implement PROBE for system mode target/hppa: Fix 32-bit operand masks for 0E FCVT hw/hppa: Add MAINTAINERS entry pc-bios: Add hppa-firmware.img and git submodule hw/hppa: Implement DINO system board target/hppa: Enable MTTCG target/hppa: Implement STWA target/hppa: Implement a pause instruction target/hppa: Implement LDSID for system mode target/hppa: Fix comment target/hppa: Increase number of temp regs target/hppa: Only use EXCP_DTLB_MISS target/hppa: Implement B,GATE insn target/hppa: Add migration for the cpu target/hppa: Add system registers to gdbstub target/hppa: Optimize for flat addressing space target/hppa: Implement halt and reset instructions target/hppa: Implement SYNCDMA insn target/hppa: Implement LCI target/hppa: Implement LPA ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b05631954d
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -40,3 +40,6 @@
|
||||
[submodule "capstone"]
|
||||
path = capstone
|
||||
url = git://git.qemu.org/capstone.git
|
||||
[submodule "roms/seabios-hppa"]
|
||||
path = roms/seabios-hppa
|
||||
url = git://github.com/hdeller/seabios-hppa.git
|
||||
|
@ -133,6 +133,7 @@ HPPA (PA-RISC)
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
F: target/hppa/
|
||||
F: hw/hppa/
|
||||
F: disas/hppa.c
|
||||
|
||||
LM32
|
||||
|
3
Makefile
3
Makefile
@ -661,7 +661,8 @@ s390-ccw.img s390-netboot.img \
|
||||
spapr-rtas.bin slof.bin skiboot.lid \
|
||||
palcode-clipper \
|
||||
u-boot.e500 \
|
||||
qemu_vga.ndrv
|
||||
qemu_vga.ndrv \
|
||||
hppa-firmware.img
|
||||
else
|
||||
BLOBS=
|
||||
endif
|
||||
|
@ -156,6 +156,7 @@ trace-events-subdirs += hw/vfio
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += ui
|
||||
|
@ -53,6 +53,8 @@ int graphic_depth = 32;
|
||||
#define QEMU_ARCH QEMU_ARCH_CRIS
|
||||
#elif defined(TARGET_I386)
|
||||
#define QEMU_ARCH QEMU_ARCH_I386
|
||||
#elif defined(TARGET_HPPA)
|
||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||
#elif defined(TARGET_M68K)
|
||||
#define QEMU_ARCH QEMU_ARCH_M68K
|
||||
#elif defined(TARGET_LM32)
|
||||
|
1
configure
vendored
1
configure
vendored
@ -6555,6 +6555,7 @@ case "$target_name" in
|
||||
cris)
|
||||
;;
|
||||
hppa)
|
||||
mttcg="yes"
|
||||
;;
|
||||
lm32)
|
||||
;;
|
||||
|
14
default-configs/hppa-softmmu.mak
Normal file
14
default-configs/hppa-softmmu.mak
Normal file
@ -0,0 +1,14 @@
|
||||
include pci.mak
|
||||
include usb.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SERIAL_ISA=y
|
||||
CONFIG_ISA_BUS=y
|
||||
CONFIG_I8259=y
|
||||
CONFIG_VIRTIO_PCI=$(CONFIG_PCI)
|
||||
CONFIG_VIRTIO=y
|
||||
CONFIG_E1000_PCI=y
|
||||
CONFIG_IDE_ISA=y
|
||||
CONFIG_IDE_CMD646=y
|
||||
# CONFIG_IDE_MMIO=y
|
||||
CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_MC146818RTC=y
|
1
hw/hppa/Makefile.objs
Normal file
1
hw/hppa/Makefile.objs
Normal file
@ -0,0 +1 @@
|
||||
obj-y += machine.o pci.o dino.o
|
518
hw/hppa/dino.c
Normal file
518
hw/hppa/dino.c
Normal file
@ -0,0 +1,518 @@
|
||||
/*
|
||||
* HP-PARISC Dino PCI chipset emulation.
|
||||
*
|
||||
* (C) 2017 by Helge Deller <deller@gmx.de>
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*
|
||||
* Documentation available at:
|
||||
* https://parisc.wiki.kernel.org/images-parisc/9/91/Dino_ers.pdf
|
||||
* https://parisc.wiki.kernel.org/images-parisc/7/70/Dino_3_1_Errata.pdf
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/devices.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hppa_sys.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
|
||||
#define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost"
|
||||
|
||||
#define DINO_IAR0 0x004
|
||||
#define DINO_IODC 0x008
|
||||
#define DINO_IRR0 0x00C /* RO */
|
||||
#define DINO_IAR1 0x010
|
||||
#define DINO_IRR1 0x014 /* RO */
|
||||
#define DINO_IMR 0x018
|
||||
#define DINO_IPR 0x01C
|
||||
#define DINO_TOC_ADDR 0x020
|
||||
#define DINO_ICR 0x024
|
||||
#define DINO_ILR 0x028 /* RO */
|
||||
#define DINO_IO_COMMAND 0x030 /* WO */
|
||||
#define DINO_IO_STATUS 0x034 /* RO */
|
||||
#define DINO_IO_CONTROL 0x038
|
||||
#define DINO_IO_GSC_ERR_RESP 0x040 /* RO */
|
||||
#define DINO_IO_ERR_INFO 0x044 /* RO */
|
||||
#define DINO_IO_PCI_ERR_RESP 0x048 /* RO */
|
||||
#define DINO_IO_FBB_EN 0x05c
|
||||
#define DINO_IO_ADDR_EN 0x060
|
||||
#define DINO_PCI_CONFIG_ADDR 0x064
|
||||
#define DINO_PCI_CONFIG_DATA 0x068
|
||||
#define DINO_PCI_IO_DATA 0x06c
|
||||
#define DINO_PCI_MEM_DATA 0x070 /* Dino 3.x only */
|
||||
#define DINO_GSC2X_CONFIG 0x7b4 /* RO */
|
||||
#define DINO_GMASK 0x800
|
||||
#define DINO_PAMR 0x804
|
||||
#define DINO_PAPR 0x808
|
||||
#define DINO_DAMODE 0x80c
|
||||
#define DINO_PCICMD 0x810
|
||||
#define DINO_PCISTS 0x814 /* R/WC */
|
||||
#define DINO_MLTIM 0x81c
|
||||
#define DINO_BRDG_FEAT 0x820
|
||||
#define DINO_PCIROR 0x824
|
||||
#define DINO_PCIWOR 0x828
|
||||
#define DINO_TLTIM 0x830
|
||||
|
||||
#define DINO_IRQS 11 /* bits 0-10 are architected */
|
||||
#define DINO_IRR_MASK 0x5ff /* only 10 bits are implemented */
|
||||
#define DINO_LOCAL_IRQS (DINO_IRQS + 1)
|
||||
#define DINO_MASK_IRQ(x) (1 << (x))
|
||||
|
||||
#define PCIINTA 0x001
|
||||
#define PCIINTB 0x002
|
||||
#define PCIINTC 0x004
|
||||
#define PCIINTD 0x008
|
||||
#define PCIINTE 0x010
|
||||
#define PCIINTF 0x020
|
||||
#define GSCEXTINT 0x040
|
||||
/* #define xxx 0x080 - bit 7 is "default" */
|
||||
/* #define xxx 0x100 - bit 8 not used */
|
||||
/* #define xxx 0x200 - bit 9 not used */
|
||||
#define RS232INT 0x400
|
||||
|
||||
#define DINO_MEM_CHUNK_SIZE (8 * 1024 * 1024) /* 8MB */
|
||||
|
||||
#define DINO_PCI_HOST_BRIDGE(obj) \
|
||||
OBJECT_CHECK(DinoState, (obj), TYPE_DINO_PCI_HOST_BRIDGE)
|
||||
|
||||
typedef struct DinoState {
|
||||
PCIHostState parent_obj;
|
||||
|
||||
/* PCI_CONFIG_ADDR is parent_obj.config_reg, via pci_host_conf_be_ops,
|
||||
so that we can map PCI_CONFIG_DATA to pci_host_data_be_ops. */
|
||||
|
||||
uint32_t iar0;
|
||||
uint32_t iar1;
|
||||
uint32_t imr;
|
||||
uint32_t ipr;
|
||||
uint32_t icr;
|
||||
uint32_t ilr;
|
||||
uint32_t io_addr_en;
|
||||
uint32_t io_control;
|
||||
|
||||
MemoryRegion this_mem;
|
||||
MemoryRegion pci_mem;
|
||||
MemoryRegion pci_mem_alias[32];
|
||||
|
||||
AddressSpace bm_as;
|
||||
MemoryRegion bm;
|
||||
MemoryRegion bm_ram_alias;
|
||||
MemoryRegion bm_pci_alias;
|
||||
|
||||
MemoryRegion cpu0_eir_mem;
|
||||
} DinoState;
|
||||
|
||||
/*
|
||||
* Dino can forward memory accesses from the CPU in the range between
|
||||
* 0xf0800000 and 0xff000000 to the PCI bus.
|
||||
*/
|
||||
static void gsc_to_pci_forwarding(DinoState *s)
|
||||
{
|
||||
uint32_t io_addr_en, tmp;
|
||||
int enabled, i;
|
||||
|
||||
tmp = extract32(s->io_control, 7, 2);
|
||||
enabled = (tmp == 0x01);
|
||||
io_addr_en = s->io_addr_en;
|
||||
|
||||
memory_region_transaction_begin();
|
||||
for (i = 1; i < 31; i++) {
|
||||
MemoryRegion *mem = &s->pci_mem_alias[i];
|
||||
if (enabled && (io_addr_en & (1U << i))) {
|
||||
if (!memory_region_is_mapped(mem)) {
|
||||
uint32_t addr = 0xf0000000 + i * DINO_MEM_CHUNK_SIZE;
|
||||
memory_region_add_subregion(get_system_memory(), addr, mem);
|
||||
}
|
||||
} else if (memory_region_is_mapped(mem)) {
|
||||
memory_region_del_subregion(get_system_memory(), mem);
|
||||
}
|
||||
}
|
||||
memory_region_transaction_commit();
|
||||
}
|
||||
|
||||
static bool dino_chip_mem_valid(void *opaque, hwaddr addr,
|
||||
unsigned size, bool is_write)
|
||||
{
|
||||
switch (addr) {
|
||||
case DINO_IAR0:
|
||||
case DINO_IAR1:
|
||||
case DINO_IRR0:
|
||||
case DINO_IRR1:
|
||||
case DINO_IMR:
|
||||
case DINO_IPR:
|
||||
case DINO_ICR:
|
||||
case DINO_ILR:
|
||||
case DINO_IO_CONTROL:
|
||||
case DINO_IO_ADDR_EN:
|
||||
case DINO_PCI_IO_DATA:
|
||||
return true;
|
||||
case DINO_PCI_IO_DATA + 2:
|
||||
return size <= 2;
|
||||
case DINO_PCI_IO_DATA + 1:
|
||||
case DINO_PCI_IO_DATA + 3:
|
||||
return size == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
DinoState *s = opaque;
|
||||
MemTxResult ret = MEMTX_OK;
|
||||
AddressSpace *io;
|
||||
uint16_t ioaddr;
|
||||
uint32_t val;
|
||||
|
||||
switch (addr) {
|
||||
case DINO_PCI_IO_DATA ... DINO_PCI_IO_DATA + 3:
|
||||
/* Read from PCI IO space. */
|
||||
io = &address_space_io;
|
||||
ioaddr = s->parent_obj.config_reg;
|
||||
switch (size) {
|
||||
case 1:
|
||||
val = address_space_ldub(io, ioaddr, attrs, &ret);
|
||||
break;
|
||||
case 2:
|
||||
val = address_space_lduw_be(io, ioaddr, attrs, &ret);
|
||||
break;
|
||||
case 4:
|
||||
val = address_space_ldl_be(io, ioaddr, attrs, &ret);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
break;
|
||||
|
||||
case DINO_IO_ADDR_EN:
|
||||
val = s->io_addr_en;
|
||||
break;
|
||||
case DINO_IO_CONTROL:
|
||||
val = s->io_control;
|
||||
break;
|
||||
|
||||
case DINO_IAR0:
|
||||
val = s->iar0;
|
||||
break;
|
||||
case DINO_IAR1:
|
||||
val = s->iar1;
|
||||
break;
|
||||
case DINO_IMR:
|
||||
val = s->imr;
|
||||
break;
|
||||
case DINO_ICR:
|
||||
val = s->icr;
|
||||
break;
|
||||
case DINO_IPR:
|
||||
val = s->ipr;
|
||||
/* Any read to IPR clears the register. */
|
||||
s->ipr = 0;
|
||||
break;
|
||||
case DINO_ILR:
|
||||
val = s->ilr;
|
||||
break;
|
||||
case DINO_IRR0:
|
||||
val = s->ilr & s->imr & ~s->icr;
|
||||
break;
|
||||
case DINO_IRR1:
|
||||
val = s->ilr & s->imr & s->icr;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Controlled by dino_chip_mem_valid above. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
*data = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
DinoState *s = opaque;
|
||||
AddressSpace *io;
|
||||
MemTxResult ret;
|
||||
uint16_t ioaddr;
|
||||
|
||||
switch (addr) {
|
||||
case DINO_IO_DATA ... DINO_PCI_IO_DATA + 3:
|
||||
/* Write into PCI IO space. */
|
||||
io = &address_space_io;
|
||||
ioaddr = s->parent_obj.config_reg;
|
||||
switch (size) {
|
||||
case 1:
|
||||
address_space_stb(io, ioaddr, val, attrs, &ret);
|
||||
break;
|
||||
case 2:
|
||||
address_space_stw_be(io, ioaddr, val, attrs, &ret);
|
||||
break;
|
||||
case 4:
|
||||
address_space_stl_be(io, ioaddr, val, attrs, &ret);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return ret;
|
||||
|
||||
case DINO_IO_ADDR_EN:
|
||||
/* Never allow first (=firmware) and last (=Dino) areas. */
|
||||
s->io_addr_en = val & 0x7ffffffe;
|
||||
gsc_to_pci_forwarding(s);
|
||||
break;
|
||||
case DINO_IO_CONTROL:
|
||||
s->io_control = val;
|
||||
gsc_to_pci_forwarding(s);
|
||||
break;
|
||||
|
||||
case DINO_IAR0:
|
||||
s->iar0 = val;
|
||||
break;
|
||||
case DINO_IAR1:
|
||||
s->iar1 = val;
|
||||
break;
|
||||
case DINO_IMR:
|
||||
s->imr = val;
|
||||
break;
|
||||
case DINO_ICR:
|
||||
s->icr = val;
|
||||
break;
|
||||
case DINO_IPR:
|
||||
/* Any write to IPR clears the register. */
|
||||
s->ipr = 0;
|
||||
break;
|
||||
|
||||
case DINO_ILR:
|
||||
case DINO_IRR0:
|
||||
case DINO_IRR1:
|
||||
/* These registers are read-only. */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Controlled by dino_chip_mem_valid above. */
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps dino_chip_ops = {
|
||||
.read_with_attrs = dino_chip_read_with_attrs,
|
||||
.write_with_attrs = dino_chip_write_with_attrs,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
.accepts = dino_chip_mem_valid,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_dino = {
|
||||
.name = "Dino",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(iar0, DinoState),
|
||||
VMSTATE_UINT32(iar1, DinoState),
|
||||
VMSTATE_UINT32(imr, DinoState),
|
||||
VMSTATE_UINT32(ipr, DinoState),
|
||||
VMSTATE_UINT32(icr, DinoState),
|
||||
VMSTATE_UINT32(ilr, DinoState),
|
||||
VMSTATE_UINT32(io_addr_en, DinoState),
|
||||
VMSTATE_UINT32(io_control, DinoState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Unlike pci_config_data_le_ops, no check of high bit set in config_reg. */
|
||||
|
||||
static uint64_t dino_config_data_read(void *opaque, hwaddr addr, unsigned len)
|
||||
{
|
||||
PCIHostState *s = opaque;
|
||||
return pci_data_read(s->bus, s->config_reg | (addr & 3), len);
|
||||
}
|
||||
|
||||
static void dino_config_data_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned len)
|
||||
{
|
||||
PCIHostState *s = opaque;
|
||||
pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps dino_config_data_ops = {
|
||||
.read = dino_config_data_read,
|
||||
.write = dino_config_data_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static AddressSpace *dino_pcihost_set_iommu(PCIBus *bus, void *opaque,
|
||||
int devfn)
|
||||
{
|
||||
DinoState *s = opaque;
|
||||
|
||||
return &s->bm_as;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dino interrupts are connected as shown on Page 78, Table 23
|
||||
* (Little-endian bit numbers)
|
||||
* 0 PCI INTA
|
||||
* 1 PCI INTB
|
||||
* 2 PCI INTC
|
||||
* 3 PCI INTD
|
||||
* 4 PCI INTE
|
||||
* 5 PCI INTF
|
||||
* 6 GSC External Interrupt
|
||||
* 7 Bus Error for "less than fatal" mode
|
||||
* 8 PS2
|
||||
* 9 Unused
|
||||
* 10 RS232
|
||||
*/
|
||||
|
||||
static void dino_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
DinoState *s = opaque;
|
||||
uint32_t bit = 1u << irq;
|
||||
uint32_t old_ilr = s->ilr;
|
||||
|
||||
if (level) {
|
||||
uint32_t ena = bit & ~old_ilr;
|
||||
s->ipr |= ena;
|
||||
s->ilr = old_ilr | bit;
|
||||
if (ena & s->imr) {
|
||||
uint32_t iar = (ena & s->icr ? s->iar1 : s->iar0);
|
||||
stl_be_phys(&address_space_memory, iar & -32, iar & 31);
|
||||
}
|
||||
} else {
|
||||
s->ilr = old_ilr & ~bit;
|
||||
}
|
||||
}
|
||||
|
||||
static int dino_pci_map_irq(PCIDevice *d, int irq_num)
|
||||
{
|
||||
int slot = d->devfn >> 3;
|
||||
int local_irq;
|
||||
|
||||
assert(irq_num >= 0 && irq_num <= 3);
|
||||
|
||||
local_irq = slot & 0x03;
|
||||
|
||||
return local_irq;
|
||||
}
|
||||
|
||||
static void dino_set_timer_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
/* ??? Not connected. */
|
||||
}
|
||||
|
||||
static void dino_set_serial_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
dino_set_irq(opaque, 10, level);
|
||||
}
|
||||
|
||||
PCIBus *dino_init(MemoryRegion *addr_space,
|
||||
qemu_irq *p_rtc_irq, qemu_irq *p_ser_irq)
|
||||
{
|
||||
DeviceState *dev;
|
||||
DinoState *s;
|
||||
PCIBus *b;
|
||||
int i;
|
||||
|
||||
dev = qdev_create(NULL, TYPE_DINO_PCI_HOST_BRIDGE);
|
||||
s = DINO_PCI_HOST_BRIDGE(dev);
|
||||
|
||||
/* Dino PCI access from main memory. */
|
||||
memory_region_init_io(&s->this_mem, OBJECT(s), &dino_chip_ops,
|
||||
s, "dino", 4096);
|
||||
memory_region_add_subregion(addr_space, DINO_HPA, &s->this_mem);
|
||||
|
||||
/* Dino PCI config. */
|
||||
memory_region_init_io(&s->parent_obj.conf_mem, OBJECT(&s->parent_obj),
|
||||
&pci_host_conf_be_ops, dev, "pci-conf-idx", 4);
|
||||
memory_region_init_io(&s->parent_obj.data_mem, OBJECT(&s->parent_obj),
|
||||
&dino_config_data_ops, dev, "pci-conf-data", 4);
|
||||
memory_region_add_subregion(&s->this_mem, DINO_PCI_CONFIG_ADDR,
|
||||
&s->parent_obj.conf_mem);
|
||||
memory_region_add_subregion(&s->this_mem, DINO_CONFIG_DATA,
|
||||
&s->parent_obj.data_mem);
|
||||
|
||||
/* Dino PCI bus memory. */
|
||||
memory_region_init(&s->pci_mem, OBJECT(s), "pci-memory", 1ull << 32);
|
||||
|
||||
b = pci_register_root_bus(dev, "pci", dino_set_irq, dino_pci_map_irq, s,
|
||||
&s->pci_mem, get_system_io(),
|
||||
PCI_DEVFN(0, 0), 32, TYPE_PCI_BUS);
|
||||
s->parent_obj.bus = b;
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
/* Set up windows into PCI bus memory. */
|
||||
for (i = 1; i < 31; i++) {
|
||||
uint32_t addr = 0xf0000000 + i * DINO_MEM_CHUNK_SIZE;
|
||||
char *name = g_strdup_printf("PCI Outbound Window %d", i);
|
||||
memory_region_init_alias(&s->pci_mem_alias[i], OBJECT(s),
|
||||
name, &s->pci_mem, addr,
|
||||
DINO_MEM_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
/* Set up PCI view of memory: Bus master address space. */
|
||||
memory_region_init(&s->bm, OBJECT(s), "bm-dino", 1ull << 32);
|
||||
memory_region_init_alias(&s->bm_ram_alias, OBJECT(s),
|
||||
"bm-system", addr_space, 0,
|
||||
0xf0000000 + DINO_MEM_CHUNK_SIZE);
|
||||
memory_region_init_alias(&s->bm_pci_alias, OBJECT(s),
|
||||
"bm-pci", &s->pci_mem,
|
||||
0xf0000000 + DINO_MEM_CHUNK_SIZE,
|
||||
31 * DINO_MEM_CHUNK_SIZE);
|
||||
memory_region_add_subregion(&s->bm, 0,
|
||||
&s->bm_ram_alias);
|
||||
memory_region_add_subregion(&s->bm,
|
||||
0xf0000000 + DINO_MEM_CHUNK_SIZE,
|
||||
&s->bm_pci_alias);
|
||||
address_space_init(&s->bm_as, &s->bm, "pci-bm");
|
||||
pci_setup_iommu(b, dino_pcihost_set_iommu, s);
|
||||
|
||||
*p_rtc_irq = qemu_allocate_irq(dino_set_timer_irq, s, 0);
|
||||
*p_ser_irq = qemu_allocate_irq(dino_set_serial_irq, s, 0);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static int dino_pcihost_init(SysBusDevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dino_pcihost_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
k->init = dino_pcihost_init;
|
||||
dc->vmsd = &vmstate_dino;
|
||||
}
|
||||
|
||||
static const TypeInfo dino_pcihost_info = {
|
||||
.name = TYPE_DINO_PCI_HOST_BRIDGE,
|
||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||
.instance_size = sizeof(DinoState),
|
||||
.class_init = dino_pcihost_class_init,
|
||||
};
|
||||
|
||||
static void dino_register_types(void)
|
||||
{
|
||||
type_register_static(&dino_pcihost_info);
|
||||
}
|
||||
|
||||
type_init(dino_register_types)
|
40
hw/hppa/hppa_hardware.h
Normal file
40
hw/hppa/hppa_hardware.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* HPPA cores and system support chips. */
|
||||
|
||||
#define FIRMWARE_START 0xf0000000
|
||||
#define FIRMWARE_END 0xf0800000
|
||||
|
||||
#define DEVICE_HPA_LEN 0x00100000
|
||||
|
||||
#define GSC_HPA 0xffc00000
|
||||
#define DINO_HPA 0xfff80000
|
||||
#define DINO_UART_HPA 0xfff83000
|
||||
#define DINO_UART_BASE 0xfff83800
|
||||
#define DINO_SCSI_HPA 0xfff8c000
|
||||
#define LASI_HPA 0xffd00000
|
||||
#define LASI_UART_HPA 0xffd05000
|
||||
#define LASI_SCSI_HPA 0xffd06000
|
||||
#define LASI_LAN_HPA 0xffd07000
|
||||
#define LASI_LPT_HPA 0xffd02000
|
||||
#define LASI_AUDIO_HPA 0xffd04000
|
||||
#define LASI_PS2KBD_HPA 0xffd08000
|
||||
#define LASI_PS2MOU_HPA 0xffd08100
|
||||
#define LASI_GFX_HPA 0xf8000000
|
||||
#define CPU_HPA 0xfff10000
|
||||
#define MEMORY_HPA 0xfffbf000
|
||||
|
||||
#define PCI_HPA DINO_HPA /* PCI bus */
|
||||
#define IDE_HPA 0xf9000000 /* Boot disc controller */
|
||||
|
||||
/* offsets to DINO HPA: */
|
||||
#define DINO_PCI_ADDR 0x064
|
||||
#define DINO_CONFIG_DATA 0x068
|
||||
#define DINO_IO_DATA 0x06c
|
||||
|
||||
#define PORT_PCI_CMD (PCI_HPA + DINO_PCI_ADDR)
|
||||
#define PORT_PCI_DATA (PCI_HPA + DINO_CONFIG_DATA)
|
||||
|
||||
#define PORT_SERIAL1 (DINO_UART_HPA + 0x800)
|
||||
#define PORT_SERIAL2 (LASI_UART_HPA + 0x800)
|
||||
|
||||
#define HPPA_MAX_CPUS 32 /* max. number of SMP CPUs */
|
||||
#define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */
|
24
hw/hppa/hppa_sys.h
Normal file
24
hw/hppa/hppa_sys.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* HPPA cores and system support chips. */
|
||||
|
||||
#ifndef HW_HPPA_SYS_H
|
||||
#define HW_HPPA_SYS_H
|
||||
|
||||
#include "target/hppa/cpu-qom.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/ide.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#include "hw/hppa/hppa_hardware.h"
|
||||
|
||||
PCIBus *dino_init(MemoryRegion *, qemu_irq *, qemu_irq *);
|
||||
|
||||
#define TYPE_DINO_PCI_HOST_BRIDGE "dino-pcihost"
|
||||
|
||||
/* hppa_pci.c. */
|
||||
extern const MemoryRegionOps hppa_pci_ignore_ops;
|
||||
extern const MemoryRegionOps hppa_pci_conf1_ops;
|
||||
extern const MemoryRegionOps hppa_pci_iack_ops;
|
||||
|
||||
#endif
|
283
hw/hppa/machine.c
Normal file
283
hw/hppa/machine.c
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* QEMU HPPA hardware system emulator.
|
||||
* Copyright 2018 Helge Deller <deller@gmx.de>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "elf.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/timer/mc146818rtc.h"
|
||||
#include "hw/ide.h"
|
||||
#include "hw/timer/i8254.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/hppa/hppa_sys.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#define MAX_IDE_BUS 2
|
||||
|
||||
static ISABus *hppa_isa_bus(void)
|
||||
{
|
||||
ISABus *isa_bus;
|
||||
qemu_irq *isa_irqs;
|
||||
MemoryRegion *isa_region;
|
||||
|
||||
isa_region = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(isa_region, NULL, &hppa_pci_ignore_ops,
|
||||
NULL, "isa-io", 0x800);
|
||||
memory_region_add_subregion(get_system_memory(), IDE_HPA,
|
||||
isa_region);
|
||||
|
||||
isa_bus = isa_bus_new(NULL, get_system_memory(), isa_region,
|
||||
&error_abort);
|
||||
isa_irqs = i8259_init(isa_bus,
|
||||
/* qemu_allocate_irq(dino_set_isa_irq, s, 0)); */
|
||||
NULL);
|
||||
isa_bus_irqs(isa_bus, isa_irqs);
|
||||
|
||||
return isa_bus;
|
||||
}
|
||||
|
||||
static uint64_t cpu_hppa_to_phys(void *opaque, uint64_t addr)
|
||||
{
|
||||
addr &= (0x10000000 - 1);
|
||||
return addr;
|
||||
}
|
||||
|
||||
static HPPACPU *cpu[HPPA_MAX_CPUS];
|
||||
static uint64_t firmware_entry;
|
||||
|
||||
static void machine_hppa_init(MachineState *machine)
|
||||
{
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
PCIBus *pci_bus;
|
||||
ISABus *isa_bus;
|
||||
qemu_irq rtc_irq, serial_irq;
|
||||
char *firmware_filename;
|
||||
uint64_t firmware_low, firmware_high;
|
||||
long size;
|
||||
uint64_t kernel_entry = 0, kernel_low, kernel_high;
|
||||
MemoryRegion *addr_space = get_system_memory();
|
||||
MemoryRegion *rom_region;
|
||||
MemoryRegion *ram_region;
|
||||
MemoryRegion *cpu_region;
|
||||
long i;
|
||||
|
||||
ram_size = machine->ram_size;
|
||||
|
||||
/* Create CPUs. */
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
cpu[i] = HPPA_CPU(cpu_create(machine->cpu_type));
|
||||
|
||||
cpu_region = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(cpu_region, OBJECT(cpu[i]), &hppa_io_eir_ops,
|
||||
cpu[i], g_strdup_printf("cpu%ld-io-eir", i), 4);
|
||||
memory_region_add_subregion(addr_space, CPU_HPA + i * 0x1000,
|
||||
cpu_region);
|
||||
}
|
||||
|
||||
/* Limit main memory. */
|
||||
if (ram_size > FIRMWARE_START) {
|
||||
machine->ram_size = ram_size = FIRMWARE_START;
|
||||
}
|
||||
|
||||
/* Main memory region. */
|
||||
ram_region = g_new(MemoryRegion, 1);
|
||||
memory_region_allocate_system_memory(ram_region, OBJECT(machine),
|
||||
"ram", ram_size);
|
||||
memory_region_add_subregion(addr_space, 0, ram_region);
|
||||
|
||||
/* Init Dino (PCI host bus chip). */
|
||||
pci_bus = dino_init(addr_space, &rtc_irq, &serial_irq);
|
||||
assert(pci_bus);
|
||||
|
||||
/* Create ISA bus. */
|
||||
isa_bus = hppa_isa_bus();
|
||||
assert(isa_bus);
|
||||
|
||||
/* Realtime clock, used by firmware for PDC_TOD call. */
|
||||
mc146818_rtc_init(isa_bus, 2000, rtc_irq);
|
||||
|
||||
/* Serial code setup. */
|
||||
if (serial_hds[0]) {
|
||||
uint32_t addr = DINO_UART_HPA + 0x800;
|
||||
serial_mm_init(addr_space, addr, 0, serial_irq,
|
||||
115200, serial_hds[0], DEVICE_BIG_ENDIAN);
|
||||
fprintf(stderr, "Serial port created at 0x%x\n", addr);
|
||||
}
|
||||
|
||||
/* SCSI disk setup. */
|
||||
lsi53c895a_create(pci_bus);
|
||||
|
||||
/* Network setup. e1000 is good enough, failing Tulip support. */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
pci_nic_init_nofail(&nd_table[i], pci_bus, "e1000", NULL);
|
||||
}
|
||||
|
||||
/* Load firmware. Given that this is not "real" firmware,
|
||||
but one explicitly written for the emulation, we might as
|
||||
well load it directly from an ELF image. */
|
||||
firmware_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS,
|
||||
bios_name ? bios_name :
|
||||
"hppa-firmware.img");
|
||||
if (firmware_filename == NULL) {
|
||||
error_report("no firmware provided");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size = load_elf(firmware_filename, NULL,
|
||||
NULL, &firmware_entry, &firmware_low, &firmware_high,
|
||||
true, EM_PARISC, 0, 0);
|
||||
|
||||
/* Unfortunately, load_elf sign-extends reading elf32. */
|
||||
firmware_entry = (target_ureg)firmware_entry;
|
||||
firmware_low = (target_ureg)firmware_low;
|
||||
firmware_high = (target_ureg)firmware_high;
|
||||
|
||||
if (size < 0) {
|
||||
error_report("could not load firmware '%s'", firmware_filename);
|
||||
exit(1);
|
||||
}
|
||||
fprintf(stderr, "Firmware loaded at 0x%08" PRIx64 "-0x%08" PRIx64
|
||||
", entry at 0x%08" PRIx64 ".\n",
|
||||
firmware_low, firmware_high, firmware_entry);
|
||||
if (firmware_low < ram_size || firmware_high >= FIRMWARE_END) {
|
||||
error_report("Firmware overlaps with memory or IO space");
|
||||
exit(1);
|
||||
}
|
||||
g_free(firmware_filename);
|
||||
|
||||
rom_region = g_new(MemoryRegion, 1);
|
||||
memory_region_allocate_system_memory(rom_region, OBJECT(machine),
|
||||
"firmware",
|
||||
(FIRMWARE_END - FIRMWARE_START));
|
||||
memory_region_add_subregion(addr_space, FIRMWARE_START, rom_region);
|
||||
|
||||
/* Load kernel */
|
||||
if (kernel_filename) {
|
||||
fprintf(stderr, "LOADING kernel '%s'\n", kernel_filename);
|
||||
size = load_elf(kernel_filename, &cpu_hppa_to_phys,
|
||||
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
||||
true, EM_PARISC, 0, 0);
|
||||
|
||||
/* Unfortunately, load_elf sign-extends reading elf32. */
|
||||
kernel_entry = (target_ureg) cpu_hppa_to_phys(NULL, kernel_entry);
|
||||
kernel_low = (target_ureg)kernel_low;
|
||||
kernel_high = (target_ureg)kernel_high;
|
||||
|
||||
if (size < 0) {
|
||||
error_report("could not load kernel '%s'", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Kernel loaded at 0x%08" PRIx64 "-0x%08" PRIx64
|
||||
", entry at 0x%08" PRIx64 ", size %ld kB.\n",
|
||||
kernel_low, kernel_high, kernel_entry, size / 1024);
|
||||
|
||||
if (kernel_cmdline) {
|
||||
cpu[0]->env.gr[24] = 0x4000;
|
||||
pstrcpy_targphys("cmdline", cpu[0]->env.gr[24],
|
||||
TARGET_PAGE_SIZE, kernel_cmdline);
|
||||
}
|
||||
|
||||
if (initrd_filename) {
|
||||
ram_addr_t initrd_base;
|
||||
long 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);
|
||||
}
|
||||
|
||||
/* Load the initrd image high in memory.
|
||||
Mirror the algorithm used by palo:
|
||||
(1) Due to sign-extension problems and PDC,
|
||||
put the initrd no higher than 1G.
|
||||
(2) Reserve 64k for stack. */
|
||||
initrd_base = MIN(ram_size, 1024 * 1024 * 1024);
|
||||
initrd_base = initrd_base - 64 * 1024;
|
||||
initrd_base = (initrd_base - initrd_size) & TARGET_PAGE_MASK;
|
||||
|
||||
if (initrd_base < kernel_high) {
|
||||
error_report("kernel and initial ram disk too large!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
load_image_targphys(initrd_filename, initrd_base, initrd_size);
|
||||
cpu[0]->env.gr[23] = initrd_base;
|
||||
cpu[0]->env.gr[22] = initrd_base + initrd_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kernel_entry) {
|
||||
/* When booting via firmware, tell firmware if we want interactive
|
||||
* mode (kernel_entry=1), and to boot from CD (gr[24]='d')
|
||||
* or hard disc * (gr[24]='c').
|
||||
*/
|
||||
kernel_entry = boot_menu ? 1 : 0;
|
||||
cpu[0]->env.gr[24] = machine->boot_order[0];
|
||||
}
|
||||
|
||||
/* We jump to the firmware entry routine and pass the
|
||||
* various parameters in registers. After firmware initialization,
|
||||
* firmware will start the Linux kernel with ramdisk and cmdline.
|
||||
*/
|
||||
cpu[0]->env.gr[26] = ram_size;
|
||||
cpu[0]->env.gr[25] = kernel_entry;
|
||||
|
||||
/* tell firmware how many SMP CPUs to present in inventory table */
|
||||
cpu[0]->env.gr[21] = smp_cpus;
|
||||
}
|
||||
|
||||
static void hppa_machine_reset(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
qemu_devices_reset();
|
||||
|
||||
/* Start all CPUs at the firmware entry point.
|
||||
* Monarch CPU will initialize firmware, secondary CPUs
|
||||
* will enter a small idle look and wait for rendevouz. */
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
cpu_set_pc(CPU(cpu[i]), firmware_entry);
|
||||
cpu[i]->env.gr[5] = CPU_HPA + i * 0x1000;
|
||||
}
|
||||
|
||||
/* already initialized by machine_hppa_init()? */
|
||||
if (cpu[0]->env.gr[26] == ram_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpu[0]->env.gr[26] = ram_size;
|
||||
cpu[0]->env.gr[25] = 0; /* no firmware boot menu */
|
||||
cpu[0]->env.gr[24] = 'c';
|
||||
/* gr22/gr23 unused, no initrd while reboot. */
|
||||
cpu[0]->env.gr[21] = smp_cpus;
|
||||
}
|
||||
|
||||
|
||||
static void machine_hppa_machine_init(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "HPPA generic machine";
|
||||
mc->default_cpu_type = TYPE_HPPA_CPU;
|
||||
mc->init = machine_hppa_init;
|
||||
mc->reset = hppa_machine_reset;
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->max_cpus = HPPA_MAX_CPUS;
|
||||
mc->default_cpus = 1;
|
||||
mc->is_default = 1;
|
||||
mc->default_ram_size = 512 * M_BYTE;
|
||||
mc->default_boot_order = "cd";
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("hppa", machine_hppa_machine_init)
|
90
hw/hppa/pci.c
Normal file
90
hw/hppa/pci.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* QEMU HP-PARISC PCI support functions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hppa_sys.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
/* Fallback for unassigned PCI I/O operations. Avoids MCHK. */
|
||||
|
||||
static uint64_t ignore_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ignore_write(void *opaque, hwaddr addr, uint64_t v, unsigned size)
|
||||
{
|
||||
}
|
||||
|
||||
const MemoryRegionOps hppa_pci_ignore_ops = {
|
||||
.read = ignore_read,
|
||||
.write = ignore_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* PCI config space reads/writes, to byte-word addressable memory. */
|
||||
static uint64_t bw_conf1_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
PCIBus *b = opaque;
|
||||
return pci_data_read(b, addr, size);
|
||||
}
|
||||
|
||||
static void bw_conf1_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PCIBus *b = opaque;
|
||||
pci_data_write(b, addr, val, size);
|
||||
}
|
||||
|
||||
const MemoryRegionOps hppa_pci_conf1_ops = {
|
||||
.read = bw_conf1_read,
|
||||
.write = bw_conf1_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
/* PCI/EISA Interrupt Acknowledge Cycle. */
|
||||
|
||||
static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return pic_read_irq(isa_pic);
|
||||
}
|
||||
|
||||
static void special_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
trace_hppa_pci_iack_write();
|
||||
}
|
||||
|
||||
const MemoryRegionOps hppa_pci_iack_ops = {
|
||||
.read = iack_read,
|
||||
.write = special_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
4
hw/hppa/trace-events
Normal file
4
hw/hppa/trace-events
Normal file
@ -0,0 +1,4 @@
|
||||
# See docs/devel/tracing.txt for syntax documentation.
|
||||
|
||||
# hw/hppa/pci.c
|
||||
hppa_pci_iack_write(void) ""
|
@ -24,6 +24,7 @@ enum {
|
||||
QEMU_ARCH_MOXIE = (1 << 15),
|
||||
QEMU_ARCH_TRICORE = (1 << 16),
|
||||
QEMU_ARCH_NIOS2 = (1 << 17),
|
||||
QEMU_ARCH_HPPA = (1 << 18),
|
||||
};
|
||||
|
||||
extern const uint32_t arch_type;
|
||||
|
@ -33,7 +33,7 @@ static inline void cpu_clone_regs(CPUHPPAState *env, target_ulong newsp)
|
||||
|
||||
static inline void cpu_set_tls(CPUHPPAState *env, target_ulong newtls)
|
||||
{
|
||||
env->cr27 = newtls;
|
||||
env->cr[27] = newtls;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -3773,21 +3773,41 @@ void cpu_loop(CPUHPPAState *env)
|
||||
env->iaoq_f = env->gr[31];
|
||||
env->iaoq_b = env->gr[31] + 4;
|
||||
break;
|
||||
case EXCP_SIGSEGV:
|
||||
case EXCP_ITLB_MISS:
|
||||
case EXCP_DTLB_MISS:
|
||||
case EXCP_NA_ITLB_MISS:
|
||||
case EXCP_NA_DTLB_MISS:
|
||||
case EXCP_IMP:
|
||||
case EXCP_DMP:
|
||||
case EXCP_DMB:
|
||||
case EXCP_PAGE_REF:
|
||||
case EXCP_DMAR:
|
||||
case EXCP_DMPI:
|
||||
info.si_signo = TARGET_SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_SEGV_ACCERR;
|
||||
info._sifields._sigfault._addr = env->ior;
|
||||
info._sifields._sigfault._addr = env->cr[CR_IOR];
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
case EXCP_SIGILL:
|
||||
case EXCP_UNALIGN:
|
||||
info.si_signo = TARGET_SIGBUS;
|
||||
info.si_errno = 0;
|
||||
info.si_code = 0;
|
||||
info._sifields._sigfault._addr = env->cr[CR_IOR];
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
case EXCP_ILL:
|
||||
case EXCP_PRIV_OPR:
|
||||
case EXCP_PRIV_REG:
|
||||
info.si_signo = TARGET_SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TARGET_ILL_ILLOPN;
|
||||
info._sifields._sigfault._addr = env->iaoq_f;
|
||||
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
|
||||
break;
|
||||
case EXCP_SIGFPE:
|
||||
case EXCP_OVERFLOW:
|
||||
case EXCP_COND:
|
||||
case EXCP_ASSIST:
|
||||
info.si_signo = TARGET_SIGFPE;
|
||||
info.si_errno = 0;
|
||||
info.si_code = 0;
|
||||
|
@ -6442,7 +6442,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env)
|
||||
__put_user(env->fr[i], &sc->sc_fr[i]);
|
||||
}
|
||||
|
||||
__put_user(env->sar, &sc->sc_sar);
|
||||
__put_user(env->cr[CR_SAR], &sc->sc_sar);
|
||||
}
|
||||
|
||||
static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc)
|
||||
@ -6463,7 +6463,7 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc)
|
||||
|
||||
__get_user(env->iaoq_f, &sc->sc_iaoq[0]);
|
||||
__get_user(env->iaoq_b, &sc->sc_iaoq[1]);
|
||||
__get_user(env->sar, &sc->sc_sar);
|
||||
__get_user(env->cr[CR_SAR], &sc->sc_sar);
|
||||
}
|
||||
|
||||
/* No, this doesn't look right, but it's copied straight from the kernel. */
|
||||
|
BIN
pc-bios/hppa-firmware.img
Executable file
BIN
pc-bios/hppa-firmware.img
Executable file
Binary file not shown.
1
roms/seabios-hppa
Submodule
1
roms/seabios-hppa
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8fa4ca9935669414a824ecda24f6e70c36e8dc94
|
@ -1 +1,3 @@
|
||||
obj-y += translate.o helper.o cpu.o op_helper.o gdbstub.o
|
||||
obj-y += translate.o helper.o cpu.o op_helper.o gdbstub.o mem_helper.o
|
||||
obj-y += int_helper.o
|
||||
obj-$(CONFIG_SOFTMMU) += machine.o
|
||||
|
@ -37,9 +37,29 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
cpu->env.iaoq_f = tb->pc;
|
||||
cpu->env.iaoq_b = tb->cs_base;
|
||||
cpu->env.psw_n = tb->flags & 1;
|
||||
#else
|
||||
/* Recover the IAOQ values from the GVA + PRIV. */
|
||||
uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3;
|
||||
target_ulong cs_base = tb->cs_base;
|
||||
target_ulong iasq_f = cs_base & ~0xffffffffull;
|
||||
int32_t diff = cs_base;
|
||||
|
||||
cpu->env.iasq_f = iasq_f;
|
||||
cpu->env.iaoq_f = (tb->pc & ~iasq_f) + priv;
|
||||
if (diff) {
|
||||
cpu->env.iaoq_b = cpu->env.iaoq_f + diff;
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu->env.psw_n = (tb->flags & PSW_N) != 0;
|
||||
}
|
||||
|
||||
static bool hppa_cpu_has_work(CPUState *cs)
|
||||
{
|
||||
return cs->interrupt_request & CPU_INTERRUPT_HARD;
|
||||
}
|
||||
|
||||
static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info)
|
||||
@ -48,6 +68,23 @@ static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info)
|
||||
info->print_insn = print_insn_hppa;
|
||||
}
|
||||
|
||||
static void hppa_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||
MMUAccessType access_type,
|
||||
int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
|
||||
cs->exception_index = EXCP_UNALIGN;
|
||||
if (env->psw & PSW_Q) {
|
||||
/* ??? Needs tweaking for hppa64. */
|
||||
env->cr[CR_IOR] = addr;
|
||||
env->cr[CR_ISR] = addr >> 32;
|
||||
}
|
||||
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
}
|
||||
|
||||
static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
@ -62,6 +99,14 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
|
||||
qemu_init_vcpu(cs);
|
||||
acc->parent_realize(dev, errp);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
cpu->alarm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
hppa_cpu_alarm_timer, cpu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Sort hppabetically by type name. */
|
||||
@ -106,8 +151,10 @@ static void hppa_cpu_initfn(Object *obj)
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
|
||||
cs->env_ptr = env;
|
||||
cs->exception_index = -1;
|
||||
cpu_hppa_loaded_fr0(env);
|
||||
set_snan_bit_is_one(true, &env->fp_status);
|
||||
cpu_hppa_put_psw(env, PSW_W);
|
||||
}
|
||||
|
||||
static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model)
|
||||
@ -125,6 +172,7 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data)
|
||||
dc->realize = hppa_cpu_realizefn;
|
||||
|
||||
cc->class_by_name = hppa_cpu_class_by_name;
|
||||
cc->has_work = hppa_cpu_has_work;
|
||||
cc->do_interrupt = hppa_cpu_do_interrupt;
|
||||
cc->cpu_exec_interrupt = hppa_cpu_exec_interrupt;
|
||||
cc->dump_state = hppa_cpu_dump_state;
|
||||
@ -132,7 +180,13 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data)
|
||||
cc->synchronize_from_tb = hppa_cpu_synchronize_from_tb;
|
||||
cc->gdb_read_register = hppa_cpu_gdb_read_register;
|
||||
cc->gdb_write_register = hppa_cpu_gdb_write_register;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
cc->handle_mmu_fault = hppa_cpu_handle_mmu_fault;
|
||||
#else
|
||||
cc->get_phys_page_debug = hppa_cpu_get_phys_page_debug;
|
||||
dc->vmsd = &vmstate_hppa_cpu;
|
||||
#endif
|
||||
cc->do_unaligned_access = hppa_cpu_do_unaligned_access;
|
||||
cc->disas_set_info = hppa_cpu_disas_set_info;
|
||||
cc->tcg_initialize = hppa_translate_init;
|
||||
|
||||
|
@ -23,10 +23,30 @@
|
||||
#include "qemu-common.h"
|
||||
#include "cpu-qom.h"
|
||||
|
||||
/* We only support hppa-linux-user at present, so 32-bit only. */
|
||||
#define TARGET_LONG_BITS 32
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
#ifdef TARGET_HPPA64
|
||||
#define TARGET_LONG_BITS 64
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 64
|
||||
#define TARGET_REGISTER_BITS 64
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 64
|
||||
#elif defined(CONFIG_USER_ONLY)
|
||||
#define TARGET_LONG_BITS 32
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 32
|
||||
#define TARGET_REGISTER_BITS 32
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||
#else
|
||||
/* In order to form the GVA from space:offset,
|
||||
we need a 64-bit virtual address space. */
|
||||
#define TARGET_LONG_BITS 64
|
||||
#define TARGET_VIRT_ADDR_SPACE_BITS 64
|
||||
#define TARGET_REGISTER_BITS 32
|
||||
#define TARGET_PHYS_ADDR_SPACE_BITS 32
|
||||
#endif
|
||||
|
||||
/* PA-RISC 1.x processors have a strong memory model. */
|
||||
/* ??? While we do not yet implement PA-RISC 2.0, those processors have
|
||||
a weak memory model, but with TLB bits that force ordering on a per-page
|
||||
basis. It's probably easier to fall back to a strong memory model. */
|
||||
#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL
|
||||
|
||||
#define CPUArchState struct CPUHPPAState
|
||||
|
||||
@ -36,28 +56,145 @@
|
||||
#define TARGET_PAGE_BITS 12
|
||||
|
||||
#define ALIGNED_ONLY
|
||||
#define NB_MMU_MODES 1
|
||||
#define MMU_USER_IDX 0
|
||||
#define NB_MMU_MODES 5
|
||||
#define MMU_KERNEL_IDX 0
|
||||
#define MMU_USER_IDX 3
|
||||
#define MMU_PHYS_IDX 4
|
||||
#define TARGET_INSN_START_EXTRA_WORDS 1
|
||||
|
||||
#define EXCP_SYSCALL 1
|
||||
#define EXCP_SYSCALL_LWS 2
|
||||
#define EXCP_SIGSEGV 3
|
||||
#define EXCP_SIGILL 4
|
||||
#define EXCP_SIGFPE 5
|
||||
/* Hardware exceptions, interupts, faults, and traps. */
|
||||
#define EXCP_HPMC 1 /* high priority machine check */
|
||||
#define EXCP_POWER_FAIL 2
|
||||
#define EXCP_RC 3 /* recovery counter */
|
||||
#define EXCP_EXT_INTERRUPT 4 /* external interrupt */
|
||||
#define EXCP_LPMC 5 /* low priority machine check */
|
||||
#define EXCP_ITLB_MISS 6 /* itlb miss / instruction page fault */
|
||||
#define EXCP_IMP 7 /* instruction memory protection trap */
|
||||
#define EXCP_ILL 8 /* illegal instruction trap */
|
||||
#define EXCP_BREAK 9 /* break instruction */
|
||||
#define EXCP_PRIV_OPR 10 /* privileged operation trap */
|
||||
#define EXCP_PRIV_REG 11 /* privileged register trap */
|
||||
#define EXCP_OVERFLOW 12 /* signed overflow trap */
|
||||
#define EXCP_COND 13 /* trap-on-condition */
|
||||
#define EXCP_ASSIST 14 /* assist exception trap */
|
||||
#define EXCP_DTLB_MISS 15 /* dtlb miss / data page fault */
|
||||
#define EXCP_NA_ITLB_MISS 16 /* non-access itlb miss */
|
||||
#define EXCP_NA_DTLB_MISS 17 /* non-access dtlb miss */
|
||||
#define EXCP_DMP 18 /* data memory protection trap */
|
||||
#define EXCP_DMB 19 /* data memory break trap */
|
||||
#define EXCP_TLB_DIRTY 20 /* tlb dirty bit trap */
|
||||
#define EXCP_PAGE_REF 21 /* page reference trap */
|
||||
#define EXCP_ASSIST_EMU 22 /* assist emulation trap */
|
||||
#define EXCP_HPT 23 /* high-privilege transfer trap */
|
||||
#define EXCP_LPT 24 /* low-privilege transfer trap */
|
||||
#define EXCP_TB 25 /* taken branch trap */
|
||||
#define EXCP_DMAR 26 /* data memory access rights trap */
|
||||
#define EXCP_DMPI 27 /* data memory protection id trap */
|
||||
#define EXCP_UNALIGN 28 /* unaligned data reference trap */
|
||||
#define EXCP_PER_INTERRUPT 29 /* performance monitor interrupt */
|
||||
|
||||
/* Exceptions for linux-user emulation. */
|
||||
#define EXCP_SYSCALL 30
|
||||
#define EXCP_SYSCALL_LWS 31
|
||||
|
||||
/* Taken from Linux kernel: arch/parisc/include/asm/psw.h */
|
||||
#define PSW_I 0x00000001
|
||||
#define PSW_D 0x00000002
|
||||
#define PSW_P 0x00000004
|
||||
#define PSW_Q 0x00000008
|
||||
#define PSW_R 0x00000010
|
||||
#define PSW_F 0x00000020
|
||||
#define PSW_G 0x00000040 /* PA1.x only */
|
||||
#define PSW_O 0x00000080 /* PA2.0 only */
|
||||
#define PSW_CB 0x0000ff00
|
||||
#define PSW_M 0x00010000
|
||||
#define PSW_V 0x00020000
|
||||
#define PSW_C 0x00040000
|
||||
#define PSW_B 0x00080000
|
||||
#define PSW_X 0x00100000
|
||||
#define PSW_N 0x00200000
|
||||
#define PSW_L 0x00400000
|
||||
#define PSW_H 0x00800000
|
||||
#define PSW_T 0x01000000
|
||||
#define PSW_S 0x02000000
|
||||
#define PSW_E 0x04000000
|
||||
#ifdef TARGET_HPPA64
|
||||
#define PSW_W 0x08000000 /* PA2.0 only */
|
||||
#else
|
||||
#define PSW_W 0
|
||||
#endif
|
||||
#define PSW_Z 0x40000000 /* PA1.x only */
|
||||
#define PSW_Y 0x80000000 /* PA1.x only */
|
||||
|
||||
#define PSW_SM (PSW_W | PSW_E | PSW_O | PSW_G | PSW_F \
|
||||
| PSW_R | PSW_Q | PSW_P | PSW_D | PSW_I)
|
||||
|
||||
/* ssm/rsm instructions number PSW_W and PSW_E differently */
|
||||
#define PSW_SM_I PSW_I /* Enable External Interrupts */
|
||||
#define PSW_SM_D PSW_D
|
||||
#define PSW_SM_P PSW_P
|
||||
#define PSW_SM_Q PSW_Q /* Enable Interrupt State Collection */
|
||||
#define PSW_SM_R PSW_R /* Enable Recover Counter Trap */
|
||||
#ifdef TARGET_HPPA64
|
||||
#define PSW_SM_E 0x100
|
||||
#define PSW_SM_W 0x200 /* PA2.0 only : Enable Wide Mode */
|
||||
#else
|
||||
#define PSW_SM_E 0
|
||||
#define PSW_SM_W 0
|
||||
#endif
|
||||
|
||||
#define CR_RC 0
|
||||
#define CR_SCRCCR 10
|
||||
#define CR_SAR 11
|
||||
#define CR_IVA 14
|
||||
#define CR_EIEM 15
|
||||
#define CR_IT 16
|
||||
#define CR_IIASQ 17
|
||||
#define CR_IIAOQ 18
|
||||
#define CR_IIR 19
|
||||
#define CR_ISR 20
|
||||
#define CR_IOR 21
|
||||
#define CR_IPSW 22
|
||||
#define CR_EIRR 23
|
||||
|
||||
typedef struct CPUHPPAState CPUHPPAState;
|
||||
|
||||
#if TARGET_REGISTER_BITS == 32
|
||||
typedef uint32_t target_ureg;
|
||||
typedef int32_t target_sreg;
|
||||
#define TREG_FMT_lx "%08"PRIx32
|
||||
#define TREG_FMT_ld "%"PRId32
|
||||
#else
|
||||
typedef uint64_t target_ureg;
|
||||
typedef int64_t target_sreg;
|
||||
#define TREG_FMT_lx "%016"PRIx64
|
||||
#define TREG_FMT_ld "%"PRId64
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint64_t va_b;
|
||||
uint64_t va_e;
|
||||
target_ureg pa;
|
||||
unsigned u : 1;
|
||||
unsigned t : 1;
|
||||
unsigned d : 1;
|
||||
unsigned b : 1;
|
||||
unsigned page_size : 4;
|
||||
unsigned ar_type : 3;
|
||||
unsigned ar_pl1 : 2;
|
||||
unsigned ar_pl2 : 2;
|
||||
unsigned entry_valid : 1;
|
||||
unsigned access_id : 16;
|
||||
} hppa_tlb_entry;
|
||||
|
||||
struct CPUHPPAState {
|
||||
target_ulong gr[32];
|
||||
target_ureg gr[32];
|
||||
uint64_t fr[32];
|
||||
uint64_t sr[8]; /* stored shifted into place for gva */
|
||||
|
||||
target_ulong sar;
|
||||
target_ulong cr26;
|
||||
target_ulong cr27;
|
||||
|
||||
target_ulong psw_n; /* boolean */
|
||||
target_long psw_v; /* in most significant bit */
|
||||
target_ureg psw; /* All psw bits except the following: */
|
||||
target_ureg psw_n; /* boolean */
|
||||
target_sreg psw_v; /* in most significant bit */
|
||||
|
||||
/* Splitting the carry-borrow field into the MSB and "the rest", allows
|
||||
* for "the rest" to be deleted when it is unused, but the MSB is in use.
|
||||
@ -66,19 +203,29 @@ struct CPUHPPAState {
|
||||
* host has the appropriate add-with-carry insn to compute the msb).
|
||||
* Therefore the carry bits are stored as: cb_msb : cb & 0x11111110.
|
||||
*/
|
||||
target_ulong psw_cb; /* in least significant bit of next nibble */
|
||||
target_ulong psw_cb_msb; /* boolean */
|
||||
target_ureg psw_cb; /* in least significant bit of next nibble */
|
||||
target_ureg psw_cb_msb; /* boolean */
|
||||
|
||||
target_ulong iaoq_f; /* front */
|
||||
target_ulong iaoq_b; /* back, aka next instruction */
|
||||
|
||||
target_ulong ior; /* interrupt offset register */
|
||||
target_ureg iaoq_f; /* front */
|
||||
target_ureg iaoq_b; /* back, aka next instruction */
|
||||
uint64_t iasq_f;
|
||||
uint64_t iasq_b;
|
||||
|
||||
uint32_t fr0_shadow; /* flags, c, ca/cq, rm, d, enables */
|
||||
float_status fp_status;
|
||||
|
||||
target_ureg cr[32]; /* control registers */
|
||||
target_ureg cr_back[2]; /* back of cr17/cr18 */
|
||||
target_ureg shadow[7]; /* shadow registers */
|
||||
|
||||
/* Those resources are used only in QEMU core */
|
||||
CPU_COMMON
|
||||
|
||||
/* ??? The number of entries isn't specified by the architecture. */
|
||||
/* ??? Implement a unified itlb/dtlb for the moment. */
|
||||
/* ??? We should use a more intelligent data structure. */
|
||||
hppa_tlb_entry tlb[256];
|
||||
uint32_t tlb_last;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -93,6 +240,7 @@ struct HPPACPU {
|
||||
/*< public >*/
|
||||
|
||||
CPUHPPAState env;
|
||||
QEMUTimer *alarm_timer;
|
||||
};
|
||||
|
||||
static inline HPPACPU *hppa_env_get_cpu(CPUHPPAState *env)
|
||||
@ -107,7 +255,14 @@ static inline HPPACPU *hppa_env_get_cpu(CPUHPPAState *env)
|
||||
|
||||
static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch)
|
||||
{
|
||||
return 0;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return MMU_USER_IDX;
|
||||
#else
|
||||
if (env->psw & (ifetch ? PSW_C : PSW_D)) {
|
||||
return env->iaoq_f & 3;
|
||||
}
|
||||
return MMU_PHYS_IDX; /* mmu disabled */
|
||||
#endif
|
||||
}
|
||||
|
||||
void hppa_translate_init(void);
|
||||
@ -116,28 +271,97 @@ void hppa_translate_init(void);
|
||||
|
||||
void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
|
||||
|
||||
static inline target_ulong hppa_form_gva_psw(target_ureg psw, uint64_t spc,
|
||||
target_ureg off)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return off;
|
||||
#else
|
||||
off &= (psw & PSW_W ? 0x3fffffffffffffffull : 0xffffffffull);
|
||||
return spc | off;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc,
|
||||
target_ureg off)
|
||||
{
|
||||
return hppa_form_gva_psw(env->psw, spc, off);
|
||||
}
|
||||
|
||||
/* Since PSW_{I,CB} will never need to be in tb->flags, reuse them.
|
||||
* TB_FLAG_SR_SAME indicates that SR4 through SR7 all contain the
|
||||
* same value.
|
||||
*/
|
||||
#define TB_FLAG_SR_SAME PSW_I
|
||||
#define TB_FLAG_PRIV_SHIFT 8
|
||||
|
||||
static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc,
|
||||
target_ulong *cs_base,
|
||||
uint32_t *pflags)
|
||||
{
|
||||
uint32_t flags = env->psw_n * PSW_N;
|
||||
|
||||
/* TB lookup assumes that PC contains the complete virtual address.
|
||||
If we leave space+offset separate, we'll get ITLB misses to an
|
||||
incomplete virtual address. This also means that we must separate
|
||||
out current cpu priviledge from the low bits of IAOQ_F. */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
*pc = env->iaoq_f;
|
||||
*cs_base = env->iaoq_b;
|
||||
*pflags = env->psw_n;
|
||||
#else
|
||||
/* ??? E, T, H, L, B, P bits need to be here, when implemented. */
|
||||
flags |= env->psw & (PSW_W | PSW_C | PSW_D);
|
||||
flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT;
|
||||
|
||||
*pc = (env->psw & PSW_C
|
||||
? hppa_form_gva_psw(env->psw, env->iasq_f, env->iaoq_f & -4)
|
||||
: env->iaoq_f & -4);
|
||||
*cs_base = env->iasq_f;
|
||||
|
||||
/* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero
|
||||
low 32-bits of CS_BASE. This will succeed for all direct branches,
|
||||
which is the primary case we care about -- using goto_tb within a page.
|
||||
Failure is indicated by a zero difference. */
|
||||
if (env->iasq_f == env->iasq_b) {
|
||||
target_sreg diff = env->iaoq_b - env->iaoq_f;
|
||||
if (TARGET_REGISTER_BITS == 32 || diff == (int32_t)diff) {
|
||||
*cs_base |= (uint32_t)diff;
|
||||
}
|
||||
}
|
||||
if ((env->sr[4] == env->sr[5])
|
||||
& (env->sr[4] == env->sr[6])
|
||||
& (env->sr[4] == env->sr[7])) {
|
||||
flags |= TB_FLAG_SR_SAME;
|
||||
}
|
||||
#endif
|
||||
|
||||
*pflags = flags;
|
||||
}
|
||||
|
||||
target_ulong cpu_hppa_get_psw(CPUHPPAState *env);
|
||||
void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong);
|
||||
target_ureg cpu_hppa_get_psw(CPUHPPAState *env);
|
||||
void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg);
|
||||
void cpu_hppa_loaded_fr0(CPUHPPAState *env);
|
||||
|
||||
#define cpu_signal_handler cpu_hppa_signal_handler
|
||||
|
||||
int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc);
|
||||
int hppa_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
|
||||
int rw, int midx);
|
||||
hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr);
|
||||
int hppa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||
void hppa_cpu_do_interrupt(CPUState *cpu);
|
||||
bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req);
|
||||
void hppa_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function, int);
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int hppa_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
|
||||
int rw, int midx);
|
||||
#else
|
||||
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
|
||||
int type, hwaddr *pphys, int *pprot);
|
||||
extern const MemoryRegionOps hppa_io_eir_ops;
|
||||
extern const struct VMStateDescription vmstate_hppa_cpu;
|
||||
void hppa_cpu_alarm_timer(void *);
|
||||
int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr);
|
||||
#endif
|
||||
void QEMU_NORETURN hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra);
|
||||
|
||||
#endif /* HPPA_CPU_H */
|
||||
|
@ -26,7 +26,7 @@ int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
target_ulong val;
|
||||
target_ureg val;
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
@ -36,19 +36,97 @@ int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
val = env->gr[n];
|
||||
break;
|
||||
case 32:
|
||||
val = env->sar;
|
||||
val = env->cr[CR_SAR];
|
||||
break;
|
||||
case 33:
|
||||
val = env->iaoq_f;
|
||||
break;
|
||||
case 34:
|
||||
val = env->iasq_f >> 32;
|
||||
break;
|
||||
case 35:
|
||||
val = env->iaoq_b;
|
||||
break;
|
||||
case 36:
|
||||
val = env->iasq_b >> 32;
|
||||
break;
|
||||
case 37:
|
||||
val = env->cr[CR_EIEM];
|
||||
break;
|
||||
case 38:
|
||||
val = env->cr[CR_IIR];
|
||||
break;
|
||||
case 39:
|
||||
val = env->cr[CR_ISR];
|
||||
break;
|
||||
case 40:
|
||||
val = env->cr[CR_IOR];
|
||||
break;
|
||||
case 41:
|
||||
val = env->cr[CR_IPSW];
|
||||
break;
|
||||
case 43:
|
||||
val = env->sr[4] >> 32;
|
||||
break;
|
||||
case 44:
|
||||
val = env->sr[0] >> 32;
|
||||
break;
|
||||
case 45:
|
||||
val = env->sr[1] >> 32;
|
||||
break;
|
||||
case 46:
|
||||
val = env->sr[2] >> 32;
|
||||
break;
|
||||
case 47:
|
||||
val = env->sr[3] >> 32;
|
||||
break;
|
||||
case 48:
|
||||
val = env->sr[5] >> 32;
|
||||
break;
|
||||
case 49:
|
||||
val = env->sr[6] >> 32;
|
||||
break;
|
||||
case 50:
|
||||
val = env->sr[7] >> 32;
|
||||
break;
|
||||
case 51:
|
||||
val = env->cr[CR_RC];
|
||||
break;
|
||||
case 52:
|
||||
val = env->cr[8];
|
||||
break;
|
||||
case 53:
|
||||
val = env->cr[9];
|
||||
break;
|
||||
case 54:
|
||||
val = env->cr[CR_SCRCCR];
|
||||
break;
|
||||
case 55:
|
||||
val = env->cr[12];
|
||||
break;
|
||||
case 56:
|
||||
val = env->cr[13];
|
||||
break;
|
||||
case 57:
|
||||
val = env->cr[24];
|
||||
break;
|
||||
case 58:
|
||||
val = env->cr[25];
|
||||
break;
|
||||
case 59:
|
||||
val = env->cr26;
|
||||
val = env->cr[26];
|
||||
break;
|
||||
case 60:
|
||||
val = env->cr27;
|
||||
val = env->cr[27];
|
||||
break;
|
||||
case 61:
|
||||
val = env->cr[28];
|
||||
break;
|
||||
case 62:
|
||||
val = env->cr[29];
|
||||
break;
|
||||
case 63:
|
||||
val = env->cr[30];
|
||||
break;
|
||||
case 64 ... 127:
|
||||
val = extract64(env->fr[(n - 64) / 2], (n & 1 ? 0 : 32), 32);
|
||||
@ -61,14 +139,25 @@ int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
}
|
||||
break;
|
||||
}
|
||||
return gdb_get_regl(mem_buf, val);
|
||||
|
||||
if (TARGET_REGISTER_BITS == 64) {
|
||||
return gdb_get_reg64(mem_buf, val);
|
||||
} else {
|
||||
return gdb_get_reg32(mem_buf, val);
|
||||
}
|
||||
}
|
||||
|
||||
int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
target_ulong val = ldtul_p(mem_buf);
|
||||
target_ureg val;
|
||||
|
||||
if (TARGET_REGISTER_BITS == 64) {
|
||||
val = ldq_p(mem_buf);
|
||||
} else {
|
||||
val = ldl_p(mem_buf);
|
||||
}
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
@ -78,19 +167,97 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
env->gr[n] = val;
|
||||
break;
|
||||
case 32:
|
||||
env->sar = val;
|
||||
env->cr[CR_SAR] = val;
|
||||
break;
|
||||
case 33:
|
||||
env->iaoq_f = val;
|
||||
break;
|
||||
case 34:
|
||||
env->iasq_f = (uint64_t)val << 32;
|
||||
break;
|
||||
case 35:
|
||||
env->iaoq_b = val;
|
||||
break;
|
||||
case 36:
|
||||
env->iasq_b = (uint64_t)val << 32;
|
||||
break;
|
||||
case 37:
|
||||
env->cr[CR_EIEM] = val;
|
||||
break;
|
||||
case 38:
|
||||
env->cr[CR_IIR] = val;
|
||||
break;
|
||||
case 39:
|
||||
env->cr[CR_ISR] = val;
|
||||
break;
|
||||
case 40:
|
||||
env->cr[CR_IOR] = val;
|
||||
break;
|
||||
case 41:
|
||||
env->cr[CR_IPSW] = val;
|
||||
break;
|
||||
case 43:
|
||||
env->sr[4] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 44:
|
||||
env->sr[0] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 45:
|
||||
env->sr[1] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 46:
|
||||
env->sr[2] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 47:
|
||||
env->sr[3] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 48:
|
||||
env->sr[5] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 49:
|
||||
env->sr[6] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 50:
|
||||
env->sr[7] = (uint64_t)val << 32;
|
||||
break;
|
||||
case 51:
|
||||
env->cr[CR_RC] = val;
|
||||
break;
|
||||
case 52:
|
||||
env->cr[8] = val;
|
||||
break;
|
||||
case 53:
|
||||
env->cr[9] = val;
|
||||
break;
|
||||
case 54:
|
||||
env->cr[CR_SCRCCR] = val;
|
||||
break;
|
||||
case 55:
|
||||
env->cr[12] = val;
|
||||
break;
|
||||
case 56:
|
||||
env->cr[13] = val;
|
||||
break;
|
||||
case 57:
|
||||
env->cr[24] = val;
|
||||
break;
|
||||
case 58:
|
||||
env->cr[25] = val;
|
||||
break;
|
||||
case 59:
|
||||
env->cr26 = val;
|
||||
env->cr[26] = val;
|
||||
break;
|
||||
case 60:
|
||||
env->cr27 = val;
|
||||
env->cr[27] = val;
|
||||
break;
|
||||
case 61:
|
||||
env->cr[28] = val;
|
||||
break;
|
||||
case 62:
|
||||
env->cr[29] = val;
|
||||
break;
|
||||
case 63:
|
||||
env->cr[30] = val;
|
||||
break;
|
||||
case 64:
|
||||
env->fr[0] = deposit64(env->fr[0], 32, 32, val);
|
||||
@ -108,5 +275,5 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
|
||||
}
|
||||
break;
|
||||
}
|
||||
return sizeof(target_ulong);
|
||||
return sizeof(target_ureg);
|
||||
}
|
||||
|
@ -24,9 +24,9 @@
|
||||
#include "fpu/softfloat.h"
|
||||
#include "exec/helper-proto.h"
|
||||
|
||||
target_ulong cpu_hppa_get_psw(CPUHPPAState *env)
|
||||
target_ureg cpu_hppa_get_psw(CPUHPPAState *env)
|
||||
{
|
||||
target_ulong psw;
|
||||
target_ureg psw;
|
||||
|
||||
/* Fold carry bits down to 8 consecutive bits. */
|
||||
/* ??? Needs tweaking for hppa64. */
|
||||
@ -39,20 +39,22 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env)
|
||||
/* .........................bcdefgh */
|
||||
psw |= (psw >> 12) & 0xf;
|
||||
psw |= env->psw_cb_msb << 7;
|
||||
psw <<= 8;
|
||||
psw = (psw & 0xff) << 8;
|
||||
|
||||
psw |= env->psw_n << 21;
|
||||
psw |= (env->psw_v < 0) << 17;
|
||||
psw |= env->psw_n * PSW_N;
|
||||
psw |= (env->psw_v < 0) * PSW_V;
|
||||
psw |= env->psw;
|
||||
|
||||
return psw;
|
||||
}
|
||||
|
||||
void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
|
||||
void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw)
|
||||
{
|
||||
target_ulong cb = 0;
|
||||
target_ureg cb = 0;
|
||||
|
||||
env->psw_n = (psw >> 21) & 1;
|
||||
env->psw_v = -((psw >> 17) & 1);
|
||||
env->psw = psw & ~(PSW_N | PSW_V | PSW_CB);
|
||||
env->psw_n = (psw / PSW_N) & 1;
|
||||
env->psw_v = -((psw / PSW_V) & 1);
|
||||
env->psw_cb_msb = (psw >> 15) & 1;
|
||||
|
||||
cb |= ((psw >> 14) & 1) << 28;
|
||||
@ -65,73 +67,55 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
|
||||
env->psw_cb = cb;
|
||||
}
|
||||
|
||||
int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
|
||||
int rw, int mmu_idx)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
|
||||
cs->exception_index = EXCP_SIGSEGV;
|
||||
cpu->env.ior = address;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void hppa_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
int i = cs->exception_index;
|
||||
|
||||
if (qemu_loglevel_mask(CPU_LOG_INT)) {
|
||||
static int count;
|
||||
const char *name = "<unknown>";
|
||||
|
||||
switch (i) {
|
||||
case EXCP_SYSCALL:
|
||||
name = "syscall";
|
||||
break;
|
||||
case EXCP_SIGSEGV:
|
||||
name = "sigsegv";
|
||||
break;
|
||||
case EXCP_SIGILL:
|
||||
name = "sigill";
|
||||
break;
|
||||
case EXCP_SIGFPE:
|
||||
name = "sigfpe";
|
||||
break;
|
||||
}
|
||||
qemu_log("INT %6d: %s ia_f=" TARGET_FMT_lx "\n",
|
||||
++count, name, env->iaoq_f);
|
||||
}
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
void hppa_cpu_dump_state(CPUState *cs, FILE *f,
|
||||
fprintf_function cpu_fprintf, int flags)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
target_ureg psw = cpu_hppa_get_psw(env);
|
||||
target_ureg psw_cb;
|
||||
char psw_c[20];
|
||||
int i;
|
||||
|
||||
cpu_fprintf(f, "IA_F " TARGET_FMT_lx
|
||||
" IA_B " TARGET_FMT_lx
|
||||
" PSW " TARGET_FMT_lx
|
||||
" [N:" TARGET_FMT_ld " V:%d"
|
||||
" CB:" TARGET_FMT_lx "]\n ",
|
||||
env->iaoq_f, env->iaoq_b, cpu_hppa_get_psw(env),
|
||||
env->psw_n, env->psw_v < 0,
|
||||
((env->psw_cb >> 4) & 0x01111111) | (env->psw_cb_msb << 28));
|
||||
for (i = 1; i < 32; i++) {
|
||||
cpu_fprintf(f, "GR%02d " TARGET_FMT_lx " ", i, env->gr[i]);
|
||||
if ((i % 4) == 3) {
|
||||
cpu_fprintf(f, "\n");
|
||||
}
|
||||
cpu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx "\n",
|
||||
hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f),
|
||||
hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b));
|
||||
|
||||
psw_c[0] = (psw & PSW_W ? 'W' : '-');
|
||||
psw_c[1] = (psw & PSW_E ? 'E' : '-');
|
||||
psw_c[2] = (psw & PSW_S ? 'S' : '-');
|
||||
psw_c[3] = (psw & PSW_T ? 'T' : '-');
|
||||
psw_c[4] = (psw & PSW_H ? 'H' : '-');
|
||||
psw_c[5] = (psw & PSW_L ? 'L' : '-');
|
||||
psw_c[6] = (psw & PSW_N ? 'N' : '-');
|
||||
psw_c[7] = (psw & PSW_X ? 'X' : '-');
|
||||
psw_c[8] = (psw & PSW_B ? 'B' : '-');
|
||||
psw_c[9] = (psw & PSW_C ? 'C' : '-');
|
||||
psw_c[10] = (psw & PSW_V ? 'V' : '-');
|
||||
psw_c[11] = (psw & PSW_M ? 'M' : '-');
|
||||
psw_c[12] = (psw & PSW_F ? 'F' : '-');
|
||||
psw_c[13] = (psw & PSW_R ? 'R' : '-');
|
||||
psw_c[14] = (psw & PSW_Q ? 'Q' : '-');
|
||||
psw_c[15] = (psw & PSW_P ? 'P' : '-');
|
||||
psw_c[16] = (psw & PSW_D ? 'D' : '-');
|
||||
psw_c[17] = (psw & PSW_I ? 'I' : '-');
|
||||
psw_c[18] = '\0';
|
||||
psw_cb = ((env->psw_cb >> 4) & 0x01111111) | (env->psw_cb_msb << 28);
|
||||
|
||||
cpu_fprintf(f, "PSW " TREG_FMT_lx " CB " TREG_FMT_lx " %s\n",
|
||||
psw, psw_cb, psw_c);
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
cpu_fprintf(f, "GR%02d " TREG_FMT_lx "%c", i, env->gr[i],
|
||||
(i & 3) == 3 ? '\n' : ' ');
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
for (i = 0; i < 8; i++) {
|
||||
cpu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32),
|
||||
(i & 3) == 3 ? '\n' : ' ');
|
||||
}
|
||||
#endif
|
||||
cpu_fprintf(f, "\n");
|
||||
|
||||
/* ??? FR */
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
#if TARGET_REGISTER_BITS == 64
|
||||
# define dh_alias_tr i64
|
||||
# define dh_is_64bit_tr 1
|
||||
#else
|
||||
# define dh_alias_tr i32
|
||||
# define dh_is_64bit_tr 0
|
||||
#endif
|
||||
#define dh_ctype_tr target_ureg
|
||||
#define dh_is_signed_tr 0
|
||||
|
||||
DEF_HELPER_2(excp, noreturn, env, int)
|
||||
DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tr)
|
||||
DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tr)
|
||||
DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tr)
|
||||
DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tr)
|
||||
DEF_HELPER_FLAGS_3(stby_e_parallel, TCG_CALL_NO_WG, void, env, tl, tr)
|
||||
|
||||
DEF_HELPER_FLAGS_1(probe_r, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_1(probe_w, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_4(probe, TCG_CALL_NO_WG, tr, env, tl, i32, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env)
|
||||
|
||||
@ -66,3 +75,21 @@ DEF_HELPER_FLAGS_4(fmpyfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(fmpynfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32)
|
||||
DEF_HELPER_FLAGS_4(fmpyfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_FLAGS_4(fmpynfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tr)
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
DEF_HELPER_1(halt, noreturn, env)
|
||||
DEF_HELPER_1(reset, noreturn, env)
|
||||
DEF_HELPER_1(rfi, void, env)
|
||||
DEF_HELPER_1(rfi_r, void, env)
|
||||
DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tr)
|
||||
DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tr)
|
||||
DEF_HELPER_FLAGS_2(write_eiem, TCG_CALL_NO_RWG, void, env, tr)
|
||||
DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tr, env, tr)
|
||||
DEF_HELPER_FLAGS_3(itlba, TCG_CALL_NO_RWG, void, env, tl, tr)
|
||||
DEF_HELPER_FLAGS_3(itlbp, TCG_CALL_NO_RWG, void, env, tl, tr)
|
||||
DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl)
|
||||
DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env)
|
||||
DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl)
|
||||
#endif
|
||||
|
263
target/hppa/int_helper.c
Normal file
263
target/hppa/int_helper.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* HPPA interrupt helper routines
|
||||
*
|
||||
* Copyright (c) 2017 Richard Henderson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qom/cpu.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void eval_interrupt(HPPACPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
if (cpu->env.cr[CR_EIRR] & cpu->env.cr[CR_EIEM]) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
|
||||
/* Each CPU has a word mapped into the GSC bus. Anything on the GSC bus
|
||||
* can write to this word to raise an external interrupt on the target CPU.
|
||||
* This includes the system controler (DINO) for regular devices, or
|
||||
* another CPU for SMP interprocessor interrupts.
|
||||
*/
|
||||
static uint64_t io_eir_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
HPPACPU *cpu = opaque;
|
||||
|
||||
/* ??? What does a read of this register over the GSC bus do? */
|
||||
return cpu->env.cr[CR_EIRR];
|
||||
}
|
||||
|
||||
static void io_eir_write(void *opaque, hwaddr addr,
|
||||
uint64_t data, unsigned size)
|
||||
{
|
||||
HPPACPU *cpu = opaque;
|
||||
int le_bit = ~data & (TARGET_REGISTER_BITS - 1);
|
||||
|
||||
cpu->env.cr[CR_EIRR] |= (target_ureg)1 << le_bit;
|
||||
eval_interrupt(cpu);
|
||||
}
|
||||
|
||||
const MemoryRegionOps hppa_io_eir_ops = {
|
||||
.read = io_eir_read,
|
||||
.write = io_eir_write,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.impl.min_access_size = 4,
|
||||
.impl.max_access_size = 4,
|
||||
};
|
||||
|
||||
void hppa_cpu_alarm_timer(void *opaque)
|
||||
{
|
||||
/* Raise interrupt 0. */
|
||||
io_eir_write(opaque, 0, 0, 4);
|
||||
}
|
||||
|
||||
void HELPER(write_eirr)(CPUHPPAState *env, target_ureg val)
|
||||
{
|
||||
env->cr[CR_EIRR] &= ~val;
|
||||
qemu_mutex_lock_iothread();
|
||||
eval_interrupt(hppa_env_get_cpu(env));
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
void HELPER(write_eiem)(CPUHPPAState *env, target_ureg val)
|
||||
{
|
||||
env->cr[CR_EIEM] = val;
|
||||
qemu_mutex_lock_iothread();
|
||||
eval_interrupt(hppa_env_get_cpu(env));
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
void hppa_cpu_do_interrupt(CPUState *cs)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
int i = cs->exception_index;
|
||||
target_ureg iaoq_f = env->iaoq_f;
|
||||
target_ureg iaoq_b = env->iaoq_b;
|
||||
uint64_t iasq_f = env->iasq_f;
|
||||
uint64_t iasq_b = env->iasq_b;
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
target_ureg old_psw;
|
||||
|
||||
/* As documented in pa2.0 -- interruption handling. */
|
||||
/* step 1 */
|
||||
env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env);
|
||||
|
||||
/* step 2 -- note PSW_W == 0 for !HPPA64. */
|
||||
cpu_hppa_put_psw(env, PSW_W | (i == EXCP_HPMC ? PSW_M : 0));
|
||||
|
||||
/* step 3 */
|
||||
env->cr[CR_IIASQ] = iasq_f >> 32;
|
||||
env->cr_back[0] = iasq_b >> 32;
|
||||
env->cr[CR_IIAOQ] = iaoq_f;
|
||||
env->cr_back[1] = iaoq_b;
|
||||
|
||||
if (old_psw & PSW_Q) {
|
||||
/* step 5 */
|
||||
/* ISR and IOR will be set elsewhere. */
|
||||
switch (i) {
|
||||
case EXCP_ILL:
|
||||
case EXCP_BREAK:
|
||||
case EXCP_PRIV_REG:
|
||||
case EXCP_PRIV_OPR:
|
||||
/* IIR set via translate.c. */
|
||||
break;
|
||||
|
||||
case EXCP_OVERFLOW:
|
||||
case EXCP_COND:
|
||||
case EXCP_ASSIST:
|
||||
case EXCP_DTLB_MISS:
|
||||
case EXCP_NA_ITLB_MISS:
|
||||
case EXCP_NA_DTLB_MISS:
|
||||
case EXCP_DMAR:
|
||||
case EXCP_DMPI:
|
||||
case EXCP_UNALIGN:
|
||||
case EXCP_DMP:
|
||||
case EXCP_DMB:
|
||||
case EXCP_TLB_DIRTY:
|
||||
case EXCP_PAGE_REF:
|
||||
case EXCP_ASSIST_EMU:
|
||||
{
|
||||
/* Avoid reading directly from the virtual address, lest we
|
||||
raise another exception from some sort of TLB issue. */
|
||||
/* ??? An alternate fool-proof method would be to store the
|
||||
instruction data into the unwind info. That's probably
|
||||
a bit too much in the way of extra storage required. */
|
||||
vaddr vaddr;
|
||||
hwaddr paddr;
|
||||
|
||||
paddr = vaddr = iaoq_f & -4;
|
||||
if (old_psw & PSW_C) {
|
||||
int prot, t;
|
||||
|
||||
vaddr = hppa_form_gva_psw(old_psw, iasq_f, vaddr);
|
||||
t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX,
|
||||
0, &paddr, &prot);
|
||||
if (t >= 0) {
|
||||
/* We can't re-load the instruction. */
|
||||
env->cr[CR_IIR] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
env->cr[CR_IIR] = ldl_phys(cs->as, paddr);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Other exceptions do not set IIR. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* step 6 */
|
||||
env->shadow[0] = env->gr[1];
|
||||
env->shadow[1] = env->gr[8];
|
||||
env->shadow[2] = env->gr[9];
|
||||
env->shadow[3] = env->gr[16];
|
||||
env->shadow[4] = env->gr[17];
|
||||
env->shadow[5] = env->gr[24];
|
||||
env->shadow[6] = env->gr[25];
|
||||
}
|
||||
|
||||
/* step 7 */
|
||||
env->iaoq_f = env->cr[CR_IVA] + 32 * i;
|
||||
env->iaoq_b = env->iaoq_f + 4;
|
||||
env->iasq_f = 0;
|
||||
env->iasq_b = 0;
|
||||
#endif
|
||||
|
||||
if (qemu_loglevel_mask(CPU_LOG_INT)) {
|
||||
static const char * const names[] = {
|
||||
[EXCP_HPMC] = "high priority machine check",
|
||||
[EXCP_POWER_FAIL] = "power fail interrupt",
|
||||
[EXCP_RC] = "recovery counter trap",
|
||||
[EXCP_EXT_INTERRUPT] = "external interrupt",
|
||||
[EXCP_LPMC] = "low priority machine check",
|
||||
[EXCP_ITLB_MISS] = "instruction tlb miss fault",
|
||||
[EXCP_IMP] = "instruction memory protection trap",
|
||||
[EXCP_ILL] = "illegal instruction trap",
|
||||
[EXCP_BREAK] = "break instruction trap",
|
||||
[EXCP_PRIV_OPR] = "privileged operation trap",
|
||||
[EXCP_PRIV_REG] = "privileged register trap",
|
||||
[EXCP_OVERFLOW] = "overflow trap",
|
||||
[EXCP_COND] = "conditional trap",
|
||||
[EXCP_ASSIST] = "assist exception trap",
|
||||
[EXCP_DTLB_MISS] = "data tlb miss fault",
|
||||
[EXCP_NA_ITLB_MISS] = "non-access instruction tlb miss",
|
||||
[EXCP_NA_DTLB_MISS] = "non-access data tlb miss",
|
||||
[EXCP_DMP] = "data memory protection trap",
|
||||
[EXCP_DMB] = "data memory break trap",
|
||||
[EXCP_TLB_DIRTY] = "tlb dirty bit trap",
|
||||
[EXCP_PAGE_REF] = "page reference trap",
|
||||
[EXCP_ASSIST_EMU] = "assist emulation trap",
|
||||
[EXCP_HPT] = "high-privilege transfer trap",
|
||||
[EXCP_LPT] = "low-privilege transfer trap",
|
||||
[EXCP_TB] = "taken branch trap",
|
||||
[EXCP_DMAR] = "data memory access rights trap",
|
||||
[EXCP_DMPI] = "data memory protection id trap",
|
||||
[EXCP_UNALIGN] = "unaligned data reference trap",
|
||||
[EXCP_PER_INTERRUPT] = "performance monitor interrupt",
|
||||
[EXCP_SYSCALL] = "syscall",
|
||||
[EXCP_SYSCALL_LWS] = "syscall-lws",
|
||||
};
|
||||
static int count;
|
||||
const char *name = NULL;
|
||||
char unknown[16];
|
||||
|
||||
if (i >= 0 && i < ARRAY_SIZE(names)) {
|
||||
name = names[i];
|
||||
}
|
||||
if (!name) {
|
||||
snprintf(unknown, sizeof(unknown), "unknown %d", i);
|
||||
name = unknown;
|
||||
}
|
||||
qemu_log("INT %6d: %s @ " TARGET_FMT_lx "," TARGET_FMT_lx
|
||||
" -> " TREG_FMT_lx " " TARGET_FMT_lx "\n",
|
||||
++count, name,
|
||||
hppa_form_gva(env, iasq_f, iaoq_f),
|
||||
hppa_form_gva(env, iasq_b, iaoq_b),
|
||||
env->iaoq_f,
|
||||
hppa_form_gva(env, (uint64_t)env->cr[CR_ISR] << 32,
|
||||
env->cr[CR_IOR]));
|
||||
}
|
||||
cs->exception_index = -1;
|
||||
}
|
||||
|
||||
bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
{
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
CPUHPPAState *env = &cpu->env;
|
||||
|
||||
/* If interrupts are requested and enabled, raise them. */
|
||||
if ((env->psw & PSW_I) && (interrupt_request & CPU_INTERRUPT_HARD)) {
|
||||
cs->exception_index = EXCP_EXT_INTERRUPT;
|
||||
hppa_cpu_do_interrupt(cs);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
181
target/hppa/machine.c
Normal file
181
target/hppa/machine.c
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* HPPA interrupt helper routines
|
||||
*
|
||||
* Copyright (c) 2017 Richard Henderson
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/boards.h"
|
||||
#include "migration/cpu.h"
|
||||
|
||||
#if TARGET_REGISTER_BITS == 64
|
||||
#define qemu_put_betr qemu_put_be64
|
||||
#define qemu_get_betr qemu_get_be64
|
||||
#define VMSTATE_UINTTL_V(_f, _s, _v) \
|
||||
VMSTATE_UINT64_V(_f, _s, _v)
|
||||
#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
|
||||
VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v)
|
||||
#else
|
||||
#define qemu_put_betr qemu_put_be32
|
||||
#define qemu_get_betr qemu_get_be32
|
||||
#define VMSTATE_UINTTR_V(_f, _s, _v) \
|
||||
VMSTATE_UINT32_V(_f, _s, _v)
|
||||
#define VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, _v) \
|
||||
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v)
|
||||
#endif
|
||||
|
||||
#define VMSTATE_UINTTR(_f, _s) \
|
||||
VMSTATE_UINTTR_V(_f, _s, 0)
|
||||
#define VMSTATE_UINTTR_ARRAY(_f, _s, _n) \
|
||||
VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, 0)
|
||||
|
||||
|
||||
static int get_psw(QEMUFile *f, void *opaque, size_t size, VMStateField *field)
|
||||
{
|
||||
CPUHPPAState *env = opaque;
|
||||
cpu_hppa_put_psw(env, qemu_get_betr(f));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_psw(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field, QJSON *vmdesc)
|
||||
{
|
||||
CPUHPPAState *env = opaque;
|
||||
qemu_put_betr(f, cpu_hppa_get_psw(env));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_psw = {
|
||||
.name = "psw",
|
||||
.get = get_psw,
|
||||
.put = put_psw,
|
||||
};
|
||||
|
||||
/* FIXME: Use the PA2.0 format, which is a superset of the PA1.1 format. */
|
||||
static int get_tlb(QEMUFile *f, void *opaque, size_t size, VMStateField *field)
|
||||
{
|
||||
hppa_tlb_entry *ent = opaque;
|
||||
uint32_t val;
|
||||
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
|
||||
ent->va_b = qemu_get_be64(f);
|
||||
ent->pa = qemu_get_betr(f);
|
||||
val = qemu_get_be32(f);
|
||||
|
||||
ent->entry_valid = extract32(val, 0, 1);
|
||||
ent->access_id = extract32(val, 1, 18);
|
||||
ent->u = extract32(val, 19, 1);
|
||||
ent->ar_pl2 = extract32(val, 20, 2);
|
||||
ent->ar_pl1 = extract32(val, 22, 2);
|
||||
ent->ar_type = extract32(val, 24, 3);
|
||||
ent->b = extract32(val, 27, 1);
|
||||
ent->d = extract32(val, 28, 1);
|
||||
ent->t = extract32(val, 29, 1);
|
||||
|
||||
ent->va_e = ent->va_b + TARGET_PAGE_SIZE - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_tlb(QEMUFile *f, void *opaque, size_t size,
|
||||
VMStateField *field, QJSON *vmdesc)
|
||||
{
|
||||
hppa_tlb_entry *ent = opaque;
|
||||
uint32_t val = 0;
|
||||
|
||||
if (ent->entry_valid) {
|
||||
val = 1;
|
||||
val = deposit32(val, 1, 18, ent->access_id);
|
||||
val = deposit32(val, 19, 1, ent->u);
|
||||
val = deposit32(val, 20, 2, ent->ar_pl2);
|
||||
val = deposit32(val, 22, 2, ent->ar_pl1);
|
||||
val = deposit32(val, 24, 3, ent->ar_type);
|
||||
val = deposit32(val, 27, 1, ent->b);
|
||||
val = deposit32(val, 28, 1, ent->d);
|
||||
val = deposit32(val, 29, 1, ent->t);
|
||||
}
|
||||
|
||||
qemu_put_be64(f, ent->va_b);
|
||||
qemu_put_betr(f, ent->pa);
|
||||
qemu_put_be32(f, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateInfo vmstate_tlb = {
|
||||
.name = "tlb entry",
|
||||
.get = get_tlb,
|
||||
.put = put_tlb,
|
||||
};
|
||||
|
||||
static VMStateField vmstate_env_fields[] = {
|
||||
VMSTATE_UINTTR_ARRAY(gr, CPUHPPAState, 32),
|
||||
VMSTATE_UINT64_ARRAY(fr, CPUHPPAState, 32),
|
||||
VMSTATE_UINT64_ARRAY(sr, CPUHPPAState, 8),
|
||||
VMSTATE_UINTTR_ARRAY(cr, CPUHPPAState, 32),
|
||||
VMSTATE_UINTTR_ARRAY(cr_back, CPUHPPAState, 2),
|
||||
VMSTATE_UINTTR_ARRAY(shadow, CPUHPPAState, 7),
|
||||
|
||||
/* Save the architecture value of the psw, not the internally
|
||||
expanded version. Since this architecture value does not
|
||||
exist in memory to be stored, this requires a but of hoop
|
||||
jumping. We want OFFSET=0 so that we effectively pass ENV
|
||||
to the helper functions, and we need to fill in the name by
|
||||
hand since there's no field of that name. */
|
||||
{
|
||||
.name = "psw",
|
||||
.version_id = 0,
|
||||
.size = sizeof(uint64_t),
|
||||
.info = &vmstate_psw,
|
||||
.flags = VMS_SINGLE,
|
||||
.offset = 0
|
||||
},
|
||||
|
||||
VMSTATE_UINTTR(iaoq_f, CPUHPPAState),
|
||||
VMSTATE_UINTTR(iaoq_b, CPUHPPAState),
|
||||
VMSTATE_UINT64(iasq_f, CPUHPPAState),
|
||||
VMSTATE_UINT64(iasq_b, CPUHPPAState),
|
||||
|
||||
VMSTATE_UINT32(fr0_shadow, CPUHPPAState),
|
||||
|
||||
VMSTATE_ARRAY(tlb, CPUHPPAState, ARRAY_SIZE(((CPUHPPAState *)0)->tlb),
|
||||
0, vmstate_tlb, hppa_tlb_entry),
|
||||
VMSTATE_UINT32(tlb_last, CPUHPPAState),
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_env = {
|
||||
.name = "env",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = vmstate_env_fields,
|
||||
};
|
||||
|
||||
static VMStateField vmstate_cpu_fields[] = {
|
||||
VMSTATE_CPU(),
|
||||
VMSTATE_STRUCT(env, HPPACPU, 1, vmstate_env, CPUHPPAState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
};
|
||||
|
||||
const VMStateDescription vmstate_hppa_cpu = {
|
||||
.name = "cpu",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = vmstate_cpu_fields,
|
||||
};
|
348
target/hppa/mem_helper.c
Normal file
348
target/hppa/mem_helper.c
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* HPPA memory access helper routines
|
||||
*
|
||||
* Copyright (c) 2017 Helge Deller
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qom/cpu.h"
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
|
||||
int size, int rw, int mmu_idx)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
|
||||
/* ??? Test between data page fault and data memory protection trap,
|
||||
which would affect si_code. */
|
||||
cs->exception_index = EXCP_DMP;
|
||||
cpu->env.cr[CR_IOR] = address;
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
|
||||
hppa_tlb_entry *ent = &env->tlb[i];
|
||||
if (ent->va_b <= addr && addr <= ent->va_e) {
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent)
|
||||
{
|
||||
CPUState *cs = CPU(hppa_env_get_cpu(env));
|
||||
unsigned i, n = 1 << (2 * ent->page_size);
|
||||
uint64_t addr = ent->va_b;
|
||||
|
||||
for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) {
|
||||
/* Do not flush MMU_PHYS_IDX. */
|
||||
tlb_flush_page_by_mmuidx(cs, addr, 0xf);
|
||||
}
|
||||
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
ent->va_b = -1;
|
||||
}
|
||||
|
||||
static hppa_tlb_entry *hppa_alloc_tlb_ent(CPUHPPAState *env)
|
||||
{
|
||||
hppa_tlb_entry *ent;
|
||||
uint32_t i = env->tlb_last;
|
||||
|
||||
env->tlb_last = (i == ARRAY_SIZE(env->tlb) - 1 ? 0 : i + 1);
|
||||
ent = &env->tlb[i];
|
||||
|
||||
hppa_flush_tlb_ent(env, ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
|
||||
int type, hwaddr *pphys, int *pprot)
|
||||
{
|
||||
hwaddr phys;
|
||||
int prot, r_prot, w_prot, x_prot;
|
||||
hppa_tlb_entry *ent;
|
||||
int ret = -1;
|
||||
|
||||
/* Virtual translation disabled. Direct map virtual to physical. */
|
||||
if (mmu_idx == MMU_PHYS_IDX) {
|
||||
phys = addr;
|
||||
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
goto egress;
|
||||
}
|
||||
|
||||
/* Find a valid tlb entry that matches the virtual address. */
|
||||
ent = hppa_find_tlb(env, addr);
|
||||
if (ent == NULL || !ent->entry_valid) {
|
||||
phys = 0;
|
||||
prot = 0;
|
||||
/* ??? Unconditionally report data tlb miss,
|
||||
even if this is an instruction fetch. */
|
||||
ret = EXCP_DTLB_MISS;
|
||||
goto egress;
|
||||
}
|
||||
|
||||
/* We now know the physical address. */
|
||||
phys = ent->pa + (addr & ~TARGET_PAGE_MASK);
|
||||
|
||||
/* Map TLB access_rights field to QEMU protection. */
|
||||
r_prot = (mmu_idx <= ent->ar_pl1) * PAGE_READ;
|
||||
w_prot = (mmu_idx <= ent->ar_pl2) * PAGE_WRITE;
|
||||
x_prot = (ent->ar_pl2 <= mmu_idx && mmu_idx <= ent->ar_pl1) * PAGE_EXEC;
|
||||
switch (ent->ar_type) {
|
||||
case 0: /* read-only: data page */
|
||||
prot = r_prot;
|
||||
break;
|
||||
case 1: /* read/write: dynamic data page */
|
||||
prot = r_prot | w_prot;
|
||||
break;
|
||||
case 2: /* read/execute: normal code page */
|
||||
prot = r_prot | x_prot;
|
||||
break;
|
||||
case 3: /* read/write/execute: dynamic code page */
|
||||
prot = r_prot | w_prot | x_prot;
|
||||
break;
|
||||
default: /* execute: promote to privilege level type & 3 */
|
||||
prot = x_prot;
|
||||
break;
|
||||
}
|
||||
|
||||
/* ??? Check PSW_P and ent->access_prot. This can remove PAGE_WRITE. */
|
||||
|
||||
/* No guest access type indicates a non-architectural access from
|
||||
within QEMU. Bypass checks for access, D, B and T bits. */
|
||||
if (type == 0) {
|
||||
goto egress;
|
||||
}
|
||||
|
||||
if (unlikely(!(prot & type))) {
|
||||
/* The access isn't allowed -- Inst/Data Memory Protection Fault. */
|
||||
ret = (type & PAGE_EXEC ? EXCP_IMP : EXCP_DMP);
|
||||
goto egress;
|
||||
}
|
||||
|
||||
/* In reverse priority order, check for conditions which raise faults.
|
||||
As we go, remove PROT bits that cover the condition we want to check.
|
||||
In this way, the resulting PROT will force a re-check of the
|
||||
architectural TLB entry for the next access. */
|
||||
if (unlikely(!ent->d)) {
|
||||
if (type & PAGE_WRITE) {
|
||||
/* The D bit is not set -- TLB Dirty Bit Fault. */
|
||||
ret = EXCP_TLB_DIRTY;
|
||||
}
|
||||
prot &= PAGE_READ | PAGE_EXEC;
|
||||
}
|
||||
if (unlikely(ent->b)) {
|
||||
if (type & PAGE_WRITE) {
|
||||
/* The B bit is set -- Data Memory Break Fault. */
|
||||
ret = EXCP_DMB;
|
||||
}
|
||||
prot &= PAGE_READ | PAGE_EXEC;
|
||||
}
|
||||
if (unlikely(ent->t)) {
|
||||
if (!(type & PAGE_EXEC)) {
|
||||
/* The T bit is set -- Page Reference Fault. */
|
||||
ret = EXCP_PAGE_REF;
|
||||
}
|
||||
prot &= PAGE_EXEC;
|
||||
}
|
||||
|
||||
egress:
|
||||
*pphys = phys;
|
||||
*pprot = prot;
|
||||
return ret;
|
||||
}
|
||||
|
||||
hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
hwaddr phys;
|
||||
int prot, excp;
|
||||
|
||||
/* If the (data) mmu is disabled, bypass translation. */
|
||||
/* ??? We really ought to know if the code mmu is disabled too,
|
||||
in order to get the correct debugging dumps. */
|
||||
if (!(cpu->env.psw & PSW_D)) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
excp = hppa_get_physical_address(&cpu->env, addr, MMU_KERNEL_IDX, 0,
|
||||
&phys, &prot);
|
||||
|
||||
/* Since we're translating for debugging, the only error that is a
|
||||
hard error is no translation at all. Otherwise, while a real cpu
|
||||
access might not have permission, the debugger does. */
|
||||
return excp == EXCP_DTLB_MISS ? -1 : phys;
|
||||
}
|
||||
|
||||
void tlb_fill(CPUState *cs, target_ulong addr, int size,
|
||||
MMUAccessType type, int mmu_idx, uintptr_t retaddr)
|
||||
{
|
||||
HPPACPU *cpu = HPPA_CPU(cs);
|
||||
int prot, excp, a_prot;
|
||||
hwaddr phys;
|
||||
|
||||
switch (type) {
|
||||
case MMU_INST_FETCH:
|
||||
a_prot = PAGE_EXEC;
|
||||
break;
|
||||
case MMU_DATA_STORE:
|
||||
a_prot = PAGE_WRITE;
|
||||
break;
|
||||
default:
|
||||
a_prot = PAGE_READ;
|
||||
break;
|
||||
}
|
||||
|
||||
excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx,
|
||||
a_prot, &phys, &prot);
|
||||
if (unlikely(excp >= 0)) {
|
||||
/* Failure. Raise the indicated exception. */
|
||||
cs->exception_index = excp;
|
||||
if (cpu->env.psw & PSW_Q) {
|
||||
/* ??? Needs tweaking for hppa64. */
|
||||
cpu->env.cr[CR_IOR] = addr;
|
||||
cpu->env.cr[CR_ISR] = addr >> 32;
|
||||
}
|
||||
cpu_loop_exit_restore(cs, retaddr);
|
||||
}
|
||||
|
||||
/* Success! Store the translation into the QEMU TLB. */
|
||||
tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
|
||||
prot, mmu_idx, TARGET_PAGE_SIZE);
|
||||
}
|
||||
|
||||
/* Insert (Insn/Data) TLB Address. Note this is PA 1.1 only. */
|
||||
void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
|
||||
{
|
||||
hppa_tlb_entry *empty = NULL;
|
||||
int i;
|
||||
|
||||
/* Zap any old entries covering ADDR; notice empty entries on the way. */
|
||||
for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) {
|
||||
hppa_tlb_entry *ent = &env->tlb[i];
|
||||
if (!ent->entry_valid) {
|
||||
empty = ent;
|
||||
} else if (ent->va_b <= addr && addr <= ent->va_e) {
|
||||
hppa_flush_tlb_ent(env, ent);
|
||||
empty = ent;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't see an empty entry, evict one. */
|
||||
if (empty == NULL) {
|
||||
empty = hppa_alloc_tlb_ent(env);
|
||||
}
|
||||
|
||||
/* Note that empty->entry_valid == 0 already. */
|
||||
empty->va_b = addr & TARGET_PAGE_MASK;
|
||||
empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1;
|
||||
empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS;
|
||||
}
|
||||
|
||||
/* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */
|
||||
void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg)
|
||||
{
|
||||
hppa_tlb_entry *ent = hppa_find_tlb(env, addr);
|
||||
|
||||
if (unlikely(ent == NULL || ent->entry_valid)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ent->access_id = extract32(reg, 1, 18);
|
||||
ent->u = extract32(reg, 19, 1);
|
||||
ent->ar_pl2 = extract32(reg, 20, 2);
|
||||
ent->ar_pl1 = extract32(reg, 22, 2);
|
||||
ent->ar_type = extract32(reg, 24, 3);
|
||||
ent->b = extract32(reg, 27, 1);
|
||||
ent->d = extract32(reg, 28, 1);
|
||||
ent->t = extract32(reg, 29, 1);
|
||||
ent->entry_valid = 1;
|
||||
}
|
||||
|
||||
/* Purge (Insn/Data) TLB. This is explicitly page-based, and is
|
||||
synchronous across all processors. */
|
||||
static void ptlb_work(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
CPUHPPAState *env = cpu->env_ptr;
|
||||
target_ulong addr = (target_ulong) data.target_ptr;
|
||||
hppa_tlb_entry *ent = hppa_find_tlb(env, addr);
|
||||
|
||||
if (ent && ent->entry_valid) {
|
||||
hppa_flush_tlb_ent(env, ent);
|
||||
}
|
||||
}
|
||||
|
||||
void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr)
|
||||
{
|
||||
CPUState *src = CPU(hppa_env_get_cpu(env));
|
||||
CPUState *cpu;
|
||||
run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr);
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
if (cpu != src) {
|
||||
async_run_on_cpu(cpu, ptlb_work, data);
|
||||
}
|
||||
}
|
||||
async_safe_run_on_cpu(src, ptlb_work, data);
|
||||
}
|
||||
|
||||
/* Purge (Insn/Data) TLB entry. This affects an implementation-defined
|
||||
number of pages/entries (we choose all), and is local to the cpu. */
|
||||
void HELPER(ptlbe)(CPUHPPAState *env)
|
||||
{
|
||||
CPUState *src = CPU(hppa_env_get_cpu(env));
|
||||
|
||||
memset(env->tlb, 0, sizeof(env->tlb));
|
||||
tlb_flush_by_mmuidx(src, 0xf);
|
||||
}
|
||||
|
||||
target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
|
||||
{
|
||||
hwaddr phys;
|
||||
int prot, excp;
|
||||
|
||||
excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0,
|
||||
&phys, &prot);
|
||||
if (excp >= 0) {
|
||||
if (env->psw & PSW_Q) {
|
||||
/* ??? Needs tweaking for hppa64. */
|
||||
env->cr[CR_IOR] = addr;
|
||||
env->cr[CR_ISR] = addr >> 32;
|
||||
}
|
||||
if (excp == EXCP_DTLB_MISS) {
|
||||
excp = EXCP_NA_DTLB_MISS;
|
||||
}
|
||||
hppa_dynamic_excp(env, excp, GETPC());
|
||||
}
|
||||
return phys;
|
||||
}
|
||||
|
||||
/* Return the ar_type of the TLB at VADDR, or -1. */
|
||||
int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr)
|
||||
{
|
||||
hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr);
|
||||
return ent ? ent->ar_type : -1;
|
||||
}
|
||||
#endif /* CONFIG_USER_ONLY */
|
@ -22,6 +22,9 @@
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
|
||||
void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp)
|
||||
{
|
||||
@ -32,7 +35,7 @@ void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp)
|
||||
cpu_loop_exit(cs);
|
||||
}
|
||||
|
||||
static void QEMU_NORETURN dynexcp(CPUHPPAState *env, int excp, uintptr_t ra)
|
||||
void QEMU_NORETURN hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra)
|
||||
{
|
||||
HPPACPU *cpu = hppa_env_get_cpu(env);
|
||||
CPUState *cs = CPU(cpu);
|
||||
@ -41,26 +44,26 @@ static void QEMU_NORETURN dynexcp(CPUHPPAState *env, int excp, uintptr_t ra)
|
||||
cpu_loop_exit_restore(cs, ra);
|
||||
}
|
||||
|
||||
void HELPER(tsv)(CPUHPPAState *env, target_ulong cond)
|
||||
void HELPER(tsv)(CPUHPPAState *env, target_ureg cond)
|
||||
{
|
||||
if (unlikely((target_long)cond < 0)) {
|
||||
dynexcp(env, EXCP_SIGFPE, GETPC());
|
||||
if (unlikely((target_sreg)cond < 0)) {
|
||||
hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC());
|
||||
}
|
||||
}
|
||||
|
||||
void HELPER(tcond)(CPUHPPAState *env, target_ulong cond)
|
||||
void HELPER(tcond)(CPUHPPAState *env, target_ureg cond)
|
||||
{
|
||||
if (unlikely(cond)) {
|
||||
dynexcp(env, EXCP_SIGFPE, GETPC());
|
||||
hppa_dynamic_excp(env, EXCP_COND, GETPC());
|
||||
}
|
||||
}
|
||||
|
||||
static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val,
|
||||
uint32_t mask, uintptr_t ra)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
uint32_t old, new, cmp;
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
uint32_t *haddr = g2h(addr - 1);
|
||||
old = *haddr;
|
||||
while (1) {
|
||||
@ -72,11 +75,12 @@ static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val,
|
||||
old = cmp;
|
||||
}
|
||||
#else
|
||||
#error "Not implemented."
|
||||
/* FIXME -- we can do better. */
|
||||
cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ulong val,
|
||||
static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val,
|
||||
bool parallel)
|
||||
{
|
||||
uintptr_t ra = GETPC();
|
||||
@ -103,18 +107,18 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ulong val,
|
||||
}
|
||||
}
|
||||
|
||||
void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val)
|
||||
void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val)
|
||||
{
|
||||
do_stby_b(env, addr, val, false);
|
||||
}
|
||||
|
||||
void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr,
|
||||
target_ulong val)
|
||||
target_ureg val)
|
||||
{
|
||||
do_stby_b(env, addr, val, true);
|
||||
}
|
||||
|
||||
static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ulong val,
|
||||
static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val,
|
||||
bool parallel)
|
||||
{
|
||||
uintptr_t ra = GETPC();
|
||||
@ -145,25 +149,45 @@ static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ulong val,
|
||||
}
|
||||
}
|
||||
|
||||
void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val)
|
||||
void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val)
|
||||
{
|
||||
do_stby_e(env, addr, val, false);
|
||||
}
|
||||
|
||||
void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr,
|
||||
target_ulong val)
|
||||
target_ureg val)
|
||||
{
|
||||
do_stby_e(env, addr, val, true);
|
||||
}
|
||||
|
||||
target_ulong HELPER(probe_r)(target_ulong addr)
|
||||
target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr,
|
||||
uint32_t level, uint32_t want)
|
||||
{
|
||||
return page_check_range(addr, 1, PAGE_READ);
|
||||
}
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return page_check_range(addr, 1, want);
|
||||
#else
|
||||
int prot, excp;
|
||||
hwaddr phys;
|
||||
|
||||
target_ulong HELPER(probe_w)(target_ulong addr)
|
||||
{
|
||||
return page_check_range(addr, 1, PAGE_WRITE);
|
||||
/* Fail if the requested privilege level is higher than current. */
|
||||
if (level < (env->iaoq_f & 3)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
excp = hppa_get_physical_address(env, addr, level, 0, &phys, &prot);
|
||||
if (excp >= 0) {
|
||||
if (env->psw & PSW_Q) {
|
||||
/* ??? Needs tweaking for hppa64. */
|
||||
env->cr[CR_IOR] = addr;
|
||||
env->cr[CR_ISR] = addr >> 32;
|
||||
}
|
||||
if (excp == EXCP_DTLB_MISS) {
|
||||
excp = EXCP_NA_DTLB_MISS;
|
||||
}
|
||||
hppa_dynamic_excp(env, excp, GETPC());
|
||||
}
|
||||
return (want & prot) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HELPER(loaded_fr0)(CPUHPPAState *env)
|
||||
@ -226,7 +250,7 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
|
||||
env->fr[0] = (uint64_t)shadow << 32;
|
||||
|
||||
if (hard_exp & shadow) {
|
||||
dynexcp(env, EXCP_SIGFPE, ra);
|
||||
hppa_dynamic_excp(env, EXCP_ASSIST, ra);
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,3 +616,89 @@ float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
|
||||
update_fr0_op(env, GETPC());
|
||||
return ret;
|
||||
}
|
||||
|
||||
target_ureg HELPER(read_interval_timer)(void)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist.
|
||||
Just pass through the host cpu clock ticks. */
|
||||
return cpu_get_host_ticks();
|
||||
#else
|
||||
/* In system mode we have access to a decent high-resolution clock.
|
||||
In order to make OS-level time accounting work with the cr16,
|
||||
present it with a well-timed clock fixed at 250MHz. */
|
||||
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) >> 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void HELPER(write_interval_timer)(CPUHPPAState *env, target_ureg val)
|
||||
{
|
||||
HPPACPU *cpu = hppa_env_get_cpu(env);
|
||||
uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
uint64_t timeout;
|
||||
|
||||
/* Even in 64-bit mode, the comparator is always 32-bit. But the
|
||||
value we expose to the guest is 1/4 of the speed of the clock,
|
||||
so moosh in 34 bits. */
|
||||
timeout = deposit64(current, 0, 34, (uint64_t)val << 2);
|
||||
|
||||
/* If the mooshing puts the clock in the past, advance to next round. */
|
||||
if (timeout < current + 1000) {
|
||||
timeout += 1ULL << 34;
|
||||
}
|
||||
|
||||
cpu->env.cr[CR_IT] = timeout;
|
||||
timer_mod(cpu->alarm_timer, timeout);
|
||||
}
|
||||
|
||||
void HELPER(halt)(CPUHPPAState *env)
|
||||
{
|
||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||
helper_excp(env, EXCP_HLT);
|
||||
}
|
||||
|
||||
void HELPER(reset)(CPUHPPAState *env)
|
||||
{
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
helper_excp(env, EXCP_HLT);
|
||||
}
|
||||
|
||||
target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm)
|
||||
{
|
||||
target_ulong psw = env->psw;
|
||||
/* ??? On second reading this condition simply seems
|
||||
to be undefined rather than a diagnosed trap. */
|
||||
if (nsm & ~psw & PSW_Q) {
|
||||
hppa_dynamic_excp(env, EXCP_ILL, GETPC());
|
||||
}
|
||||
env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM);
|
||||
return psw & PSW_SM;
|
||||
}
|
||||
|
||||
void HELPER(rfi)(CPUHPPAState *env)
|
||||
{
|
||||
/* ??? On second reading this condition simply seems
|
||||
to be undefined rather than a diagnosed trap. */
|
||||
if (env->psw & (PSW_I | PSW_R | PSW_Q)) {
|
||||
helper_excp(env, EXCP_ILL);
|
||||
}
|
||||
env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32;
|
||||
env->iasq_b = (uint64_t)env->cr_back[0] << 32;
|
||||
env->iaoq_f = env->cr[CR_IIAOQ];
|
||||
env->iaoq_b = env->cr_back[1];
|
||||
cpu_hppa_put_psw(env, env->cr[CR_IPSW]);
|
||||
}
|
||||
|
||||
void HELPER(rfi_r)(CPUHPPAState *env)
|
||||
{
|
||||
env->gr[1] = env->shadow[0];
|
||||
env->gr[8] = env->shadow[1];
|
||||
env->gr[9] = env->shadow[2];
|
||||
env->gr[16] = env->shadow[3];
|
||||
env->gr[17] = env->shadow[4];
|
||||
env->gr[24] = env->shadow[5];
|
||||
env->gr[25] = env->shadow[6];
|
||||
helper_rfi(env);
|
||||
}
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user