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>
master
Peter Maydell 2018-01-31 15:50:29 +00:00
commit b05631954d
31 changed files with 4063 additions and 706 deletions

3
.gitmodules vendored
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -6555,6 +6555,7 @@ case "$target_name" in
cris)
;;
hppa)
mttcg="yes"
;;
lm32)
;;

View 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
View File

@ -0,0 +1 @@
obj-y += machine.o pci.o dino.o

518
hw/hppa/dino.c Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
# See docs/devel/tracing.txt for syntax documentation.
# hw/hppa/pci.c
hppa_pci_iack_write(void) ""

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

Binary file not shown.

1
roms/seabios-hppa Submodule

@ -0,0 +1 @@
Subproject commit 8fa4ca9935669414a824ecda24f6e70c36e8dc94

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 */
}

View File

@ -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
View 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
View 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
View 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 */

View File

@ -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