ppc patch queue 2021-05-04
Here's the first ppc pull request for qemu-6.1. It has a wide variety of stuff accumulated during the 6.0 freeze. Highlights are: * Multi-phase reset cleanups for PAPR * Preliminary cleanups towards allowing !CONFIG_TCG for the ppc target * Cleanup of AIL logic and extension to POWER10 * Further improvements to handling of hot unplug failures on PAPR * Allow much larger numbers of CPU on pseries * Support for the H_SCM_HEALTH hypercall * Add support for the Pegasos II board * Substantial cleanup to hflag handling * Assorted minor fixes and cleanups -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEdfRlhq5hpmzETofcbDjKyiDZs5IFAmCQ4ScACgkQbDjKyiDZ s5KmNhAAsICdDqeu/jm1uhRCr0DDT/Wa6KE1xlglQ53ybWb5Hm2ae0Uwzti5ZWkt T9yryObX++wiugbU5Dlx9eXTiJIPgTbDoBV1wfOa3a1BAxSEES1t70jwuwAXXBpX mgU++SurQB70IB7vVvyXDi2Z592qGvMiKXqT0sdkfoexPHzAL0+KkQPyJZLeFchM Ap/zRHAodXf9SuWAl+LwLXeb350jivXYXBWNcFRrBbOGpbVT0AJMYrk/TEa2ZIpi SvbzAWuW+9mX0EOmk7JK5JfkT41cGNdcBcwd0bt4xyvUpmkXLaTMFDLVHj3HWSUn PFA4RB3uKXyTfISVtWdxJBbFOzMpchI6lEiRJHCS+KuY7UsACqV1T/y54ATOUauC ycLc9APgRaStdNPxfDl+xeFfoVb/f0mQsNwcmY1tv7z+3qE/trY9bMyrbgaebBFn /TAkmPvXfwtAREnx8xF/57poarWUkvupGTQkANNosdFokpExmrLj8T0sKv90hh5Y vkGf5zP4pYGN1Rs8qhOdHu+IjhVJvUl/L3LZYWcoMI6E61D8rGRc0Dkacx7gcja+ sluFi5Yh2fQn55y6LTi3049cB1wMd6wly0214F11RKoBswguiGuaqJmL4sNDO/s4 IcMCy5mg6C0jNZA5kHcdWmqsVzD2+XwP5J29n/LedlmgXoHYF+M= =N0qr -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.1-20210504' into staging ppc patch queue 2021-05-04 Here's the first ppc pull request for qemu-6.1. It has a wide variety of stuff accumulated during the 6.0 freeze. Highlights are: * Multi-phase reset cleanups for PAPR * Preliminary cleanups towards allowing !CONFIG_TCG for the ppc target * Cleanup of AIL logic and extension to POWER10 * Further improvements to handling of hot unplug failures on PAPR * Allow much larger numbers of CPU on pseries * Support for the H_SCM_HEALTH hypercall * Add support for the Pegasos II board * Substantial cleanup to hflag handling * Assorted minor fixes and cleanups # gpg: Signature made Tue 04 May 2021 06:52:39 BST # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dg-gitlab/tags/ppc-for-6.1-20210504: (46 commits) hw/ppc/pnv_psi: Use device_cold_reset() instead of device_legacy_reset() hw/ppc/spapr_vio: Reset TCE table object with device_cold_reset() hw/intc/spapr_xive: Use device_cold_reset() instead of device_legacy_reset() target/ppc: removed VSCR from SPR registration target/ppc: Reduce the size of ppc_spr_t target/ppc: Clean up _spr_register et al target/ppc: Add POWER10 exception model target/ppc: rework AIL logic in interrupt delivery target/ppc: move opcode table logic to translate.c target/ppc: code motion from translate_init.c.inc to gdbstub.c spapr_drc.c: handle hotunplug errors in drc_unisolate_logical() spapr.h: increase FDT_MAX_SIZE spapr.c: do not use MachineClass::max_cpus to limit CPUs ppc: Rename current DAWR macros and variables target/ppc: POWER10 supports scv target/ppc: Fix POWER9 radix guest HV interrupt AIL behaviour docs/system: ppc: Add documentation for ppce500 machine roms/u-boot: Bump ppce500 u-boot to v2021.04 to fix broken pci support roms/Makefile: Update ppce500 u-boot build directory name ppc/spapr: Add support for implement support for H_SCM_HEALTH ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
d90f154867
10
MAINTAINERS
10
MAINTAINERS
@ -1366,6 +1366,16 @@ F: pc-bios/canyonlands.dt[sb]
|
||||
F: pc-bios/u-boot-sam460ex-20100605.bin
|
||||
F: roms/u-boot-sam460ex
|
||||
|
||||
pegasos2
|
||||
M: BALATON Zoltan <balaton@eik.bme.hu>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ppc/pegasos2.c
|
||||
F: hw/pci-host/mv64361.c
|
||||
F: hw/pci-host/mv643xx.h
|
||||
F: include/hw/pci-host/mv64361.h
|
||||
|
||||
RISC-V Machines
|
||||
---------------
|
||||
OpenTitan
|
||||
|
@ -14,5 +14,7 @@ CONFIG_SAM460EX=y
|
||||
CONFIG_MAC_OLDWORLD=y
|
||||
CONFIG_MAC_NEWWORLD=y
|
||||
|
||||
CONFIG_PEGASOS2=n
|
||||
|
||||
# For PReP
|
||||
CONFIG_PREP=y
|
||||
|
156
docs/system/ppc/ppce500.rst
Normal file
156
docs/system/ppc/ppce500.rst
Normal file
@ -0,0 +1,156 @@
|
||||
ppce500 generic platform (``ppce500``)
|
||||
======================================
|
||||
|
||||
QEMU for PPC supports a special ``ppce500`` machine designed for emulation and
|
||||
virtualization purposes.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
|
||||
The ``ppce500`` machine supports the following devices:
|
||||
|
||||
* PowerPC e500 series core (e500v2/e500mc/e5500/e6500)
|
||||
* Configuration, Control, and Status Register (CCSR)
|
||||
* Multicore Programmable Interrupt Controller (MPIC) with MSI support
|
||||
* 1 16550A UART device
|
||||
* 1 Freescale MPC8xxx I2C controller
|
||||
* 1 Pericom pt7c4338 RTC via I2C
|
||||
* 1 Freescale MPC8xxx GPIO controller
|
||||
* Power-off functionality via one GPIO pin
|
||||
* 1 Freescale MPC8xxx PCI host controller
|
||||
* VirtIO devices via PCI bus
|
||||
|
||||
Hardware configuration information
|
||||
----------------------------------
|
||||
|
||||
The ``ppce500`` machine automatically generates a device tree blob ("dtb")
|
||||
which it passes to the guest, if there is no ``-dtb`` option. This provides
|
||||
information about the addresses, interrupt lines and other configuration of
|
||||
the various devices in the system.
|
||||
|
||||
If users want to provide their own DTB, they can use the ``-dtb`` option.
|
||||
These DTBs should have the following requirements:
|
||||
|
||||
* The number of subnodes under /cpus node should match QEMU's ``-smp`` option
|
||||
* The /memory reg size should match QEMU’s selected ram_size via ``-m``
|
||||
|
||||
Both ``qemu-system-ppc`` and ``qemu-system-ppc64`` provide emulation for the
|
||||
following 32-bit PowerPC CPUs:
|
||||
|
||||
* e500v2
|
||||
* e500mc
|
||||
|
||||
Additionally ``qemu-system-ppc64`` provides support for the following 64-bit
|
||||
PowerPC CPUs:
|
||||
|
||||
* e5500
|
||||
* e6500
|
||||
|
||||
The CPU type can be specified via the ``-cpu`` command line. If not specified,
|
||||
it creates a machine with e500v2 core. The following example shows an e6500
|
||||
based machine creation:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc64 -nographic -M ppce500 -cpu e6500
|
||||
|
||||
Boot options
|
||||
------------
|
||||
|
||||
The ``ppce500`` machine can start using the standard -kernel functionality
|
||||
for loading a payload like an OS kernel (e.g.: Linux), or U-Boot firmware.
|
||||
|
||||
When -bios is omitted, the default pc-bios/u-boot.e500 firmware image is used
|
||||
as the BIOS. QEMU follows below truth table to select which payload to execute:
|
||||
|
||||
===== ========== =======
|
||||
-bios -kernel payload
|
||||
===== ========== =======
|
||||
N N u-boot
|
||||
N Y kernel
|
||||
Y don't care u-boot
|
||||
===== ========== =======
|
||||
|
||||
When both -bios and -kernel are present, QEMU loads U-Boot and U-Boot in turns
|
||||
automatically loads the kernel image specified by the -kernel parameter via
|
||||
U-Boot's built-in "bootm" command, hence a legacy uImage format is required in
|
||||
such senario.
|
||||
|
||||
Running Linux kernel
|
||||
--------------------
|
||||
|
||||
Linux mainline v5.11 release is tested at the time of writing. To build a
|
||||
Linux mainline kernel that can be booted by the ``ppce500`` machine in
|
||||
64-bit mode, simply configure the kernel using the defconfig configuration:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export ARCH=powerpc
|
||||
$ export CROSS_COMPILE=powerpc-linux-
|
||||
$ make corenet64_smp_defconfig
|
||||
$ make menuconfig
|
||||
|
||||
then manually select the following configuration:
|
||||
|
||||
Platform support > Freescale Book-E Machine Type > QEMU generic e500 platform
|
||||
|
||||
To boot the newly built Linux kernel in QEMU with the ``ppce500`` machine:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc64 -M ppce500 -cpu e5500 -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel vmlinux \
|
||||
-initrd /path/to/rootfs.cpio \
|
||||
-append "root=/dev/ram"
|
||||
|
||||
To build a Linux mainline kernel that can be booted by the ``ppce500`` machine
|
||||
in 32-bit mode, use the same 64-bit configuration steps except the defconfig
|
||||
file should use corenet32_smp_defconfig.
|
||||
|
||||
To boot the 32-bit Linux kernel:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc{64|32} -M ppce500 -cpu e500mc -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-kernel vmlinux \
|
||||
-initrd /path/to/rootfs.cpio \
|
||||
-append "root=/dev/ram"
|
||||
|
||||
Running U-Boot
|
||||
--------------
|
||||
|
||||
U-Boot mainline v2021.04 release is tested at the time of writing. To build a
|
||||
U-Boot mainline bootloader that can be booted by the ``ppce500`` machine, use
|
||||
the qemu-ppce500_defconfig with similar commands as described above for Linux:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ export CROSS_COMPILE=powerpc-linux-
|
||||
$ make qemu-ppce500_defconfig
|
||||
|
||||
You will get u-boot file in the build tree.
|
||||
|
||||
When U-Boot boots, you will notice the following if using with ``-cpu e6500``:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
CPU: Unknown, Version: 0.0, (0x00000000)
|
||||
Core: e6500, Version: 2.0, (0x80400020)
|
||||
|
||||
This is because we only specified a core name to QEMU and it does not have a
|
||||
meaningful SVR value which represents an actual SoC that integrates such core.
|
||||
You can specify a real world SoC device that QEMU has built-in support but all
|
||||
these SoCs are e500v2 based MPC85xx series, hence you cannot test anything
|
||||
built for P4080 (e500mc), P5020 (e5500) and T2080 (e6500).
|
||||
|
||||
By default a VirtIO standard PCI networking device is connected as an ethernet
|
||||
interface at PCI address 0.1.0, but we can switch that to an e1000 NIC by:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ qemu-system-ppc -M ppce500 -smp 4 -m 2G \
|
||||
-display none -serial stdio \
|
||||
-bios u-boot \
|
||||
-nic tap,ifname=tap0,script=no,downscript=no,model=e1000
|
@ -20,5 +20,6 @@ help``.
|
||||
ppc/embedded
|
||||
ppc/powermac
|
||||
ppc/powernv
|
||||
ppc/ppce500
|
||||
ppc/prep
|
||||
ppc/pseries
|
||||
|
@ -1798,7 +1798,7 @@ static target_ulong h_int_reset(PowerPCCPU *cpu,
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
device_legacy_reset(DEVICE(xive));
|
||||
device_cold_reset(DEVICE(xive));
|
||||
|
||||
if (spapr_xive_in_kernel(xive)) {
|
||||
Error *local_err = NULL;
|
||||
|
@ -8,6 +8,9 @@
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*
|
||||
* VT8231 south bridge support and general clean up to allow it
|
||||
* Copyright (c) 2018-2020 BALATON Zoltan
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
@ -264,15 +267,80 @@ static const TypeInfo vt8231_pm_info = {
|
||||
};
|
||||
|
||||
|
||||
typedef struct SuperIOConfig {
|
||||
uint8_t regs[0x100];
|
||||
MemoryRegion io;
|
||||
} SuperIOConfig;
|
||||
#define TYPE_VIA_SUPERIO "via-superio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ViaSuperIOState, VIA_SUPERIO)
|
||||
|
||||
static void superio_cfg_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
unsigned size)
|
||||
struct ViaSuperIOState {
|
||||
ISASuperIODevice superio;
|
||||
uint8_t regs[0x100];
|
||||
const MemoryRegionOps *io_ops;
|
||||
MemoryRegion io;
|
||||
};
|
||||
|
||||
static inline void via_superio_io_enable(ViaSuperIOState *s, bool enable)
|
||||
{
|
||||
SuperIOConfig *sc = opaque;
|
||||
memory_region_set_enabled(&s->io, enable);
|
||||
}
|
||||
|
||||
static void via_superio_realize(DeviceState *d, Error **errp)
|
||||
{
|
||||
ViaSuperIOState *s = VIA_SUPERIO(d);
|
||||
ISASuperIOClass *ic = ISA_SUPERIO_GET_CLASS(s);
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(s->io_ops);
|
||||
ic->parent_realize(d, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
memory_region_init_io(&s->io, OBJECT(d), s->io_ops, s, "via-superio", 2);
|
||||
memory_region_set_enabled(&s->io, false);
|
||||
/* The floppy also uses 0x3f0 and 0x3f1 but this seems to work anyway */
|
||||
memory_region_add_subregion(isa_address_space_io(ISA_DEVICE(s)), 0x3f0,
|
||||
&s->io);
|
||||
}
|
||||
|
||||
static uint64_t via_superio_cfg_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ViaSuperIOState *sc = opaque;
|
||||
uint8_t idx = sc->regs[0];
|
||||
uint8_t val = sc->regs[idx];
|
||||
|
||||
if (addr == 0) {
|
||||
return idx;
|
||||
}
|
||||
if (addr == 1 && idx == 0) {
|
||||
val = 0; /* reading reg 0 where we store index value */
|
||||
}
|
||||
trace_via_superio_read(idx, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void via_superio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
||||
|
||||
sc->parent_realize = dc->realize;
|
||||
dc->realize = via_superio_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo via_superio_info = {
|
||||
.name = TYPE_VIA_SUPERIO,
|
||||
.parent = TYPE_ISA_SUPERIO,
|
||||
.instance_size = sizeof(ViaSuperIOState),
|
||||
.class_size = sizeof(ISASuperIOClass),
|
||||
.class_init = via_superio_class_init,
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
|
||||
|
||||
static void vt82c686b_superio_cfg_write(void *opaque, hwaddr addr,
|
||||
uint64_t data, unsigned size)
|
||||
{
|
||||
ViaSuperIOState *sc = opaque;
|
||||
uint8_t idx = sc->regs[0];
|
||||
|
||||
if (addr == 0) { /* config index register */
|
||||
@ -303,25 +371,9 @@ static void superio_cfg_write(void *opaque, hwaddr addr, uint64_t data,
|
||||
sc->regs[idx] = data;
|
||||
}
|
||||
|
||||
static uint64_t superio_cfg_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
SuperIOConfig *sc = opaque;
|
||||
uint8_t idx = sc->regs[0];
|
||||
uint8_t val = sc->regs[idx];
|
||||
|
||||
if (addr == 0) {
|
||||
return idx;
|
||||
}
|
||||
if (addr == 1 && idx == 0) {
|
||||
val = 0; /* reading reg 0 where we store index value */
|
||||
}
|
||||
trace_via_superio_read(idx, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps superio_cfg_ops = {
|
||||
.read = superio_cfg_read,
|
||||
.write = superio_cfg_write,
|
||||
static const MemoryRegionOps vt82c686b_superio_cfg_ops = {
|
||||
.read = via_superio_cfg_read,
|
||||
.write = vt82c686b_superio_cfg_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
@ -329,47 +381,215 @@ static const MemoryRegionOps superio_cfg_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static void vt82c686b_superio_reset(DeviceState *dev)
|
||||
{
|
||||
ViaSuperIOState *s = VIA_SUPERIO(dev);
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VT82C686BISAState, VT82C686B_ISA)
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
/* Device ID */
|
||||
vt82c686b_superio_cfg_write(s, 0, 0xe0, 1);
|
||||
vt82c686b_superio_cfg_write(s, 1, 0x3c, 1);
|
||||
/* Function select - all disabled */
|
||||
vt82c686b_superio_cfg_write(s, 0, 0xe2, 1);
|
||||
vt82c686b_superio_cfg_write(s, 1, 0x03, 1);
|
||||
/* Floppy ctrl base addr 0x3f0-7 */
|
||||
vt82c686b_superio_cfg_write(s, 0, 0xe3, 1);
|
||||
vt82c686b_superio_cfg_write(s, 1, 0xfc, 1);
|
||||
/* Parallel port base addr 0x378-f */
|
||||
vt82c686b_superio_cfg_write(s, 0, 0xe6, 1);
|
||||
vt82c686b_superio_cfg_write(s, 1, 0xde, 1);
|
||||
/* Serial port 1 base addr 0x3f8-f */
|
||||
vt82c686b_superio_cfg_write(s, 0, 0xe7, 1);
|
||||
vt82c686b_superio_cfg_write(s, 1, 0xfe, 1);
|
||||
/* Serial port 2 base addr 0x2f8-f */
|
||||
vt82c686b_superio_cfg_write(s, 0, 0xe8, 1);
|
||||
vt82c686b_superio_cfg_write(s, 1, 0xbe, 1);
|
||||
|
||||
struct VT82C686BISAState {
|
||||
vt82c686b_superio_cfg_write(s, 0, 0, 1);
|
||||
}
|
||||
|
||||
static void vt82c686b_superio_init(Object *obj)
|
||||
{
|
||||
VIA_SUPERIO(obj)->io_ops = &vt82c686b_superio_cfg_ops;
|
||||
}
|
||||
|
||||
static void vt82c686b_superio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
||||
|
||||
dc->reset = vt82c686b_superio_reset;
|
||||
sc->serial.count = 2;
|
||||
sc->parallel.count = 1;
|
||||
sc->ide.count = 0; /* emulated by via-ide */
|
||||
sc->floppy.count = 1;
|
||||
}
|
||||
|
||||
static const TypeInfo vt82c686b_superio_info = {
|
||||
.name = TYPE_VT82C686B_SUPERIO,
|
||||
.parent = TYPE_VIA_SUPERIO,
|
||||
.instance_size = sizeof(ViaSuperIOState),
|
||||
.instance_init = vt82c686b_superio_init,
|
||||
.class_size = sizeof(ISASuperIOClass),
|
||||
.class_init = vt82c686b_superio_class_init,
|
||||
};
|
||||
|
||||
|
||||
#define TYPE_VT8231_SUPERIO "vt8231-superio"
|
||||
|
||||
static void vt8231_superio_cfg_write(void *opaque, hwaddr addr,
|
||||
uint64_t data, unsigned size)
|
||||
{
|
||||
ViaSuperIOState *sc = opaque;
|
||||
uint8_t idx = sc->regs[0];
|
||||
|
||||
if (addr == 0) { /* config index register */
|
||||
sc->regs[0] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
/* config data register */
|
||||
trace_via_superio_write(idx, data);
|
||||
switch (idx) {
|
||||
case 0x00 ... 0xdf:
|
||||
case 0xe7 ... 0xef:
|
||||
case 0xf0 ... 0xf1:
|
||||
case 0xf5:
|
||||
case 0xf8:
|
||||
case 0xfd:
|
||||
/* ignore write to read only registers */
|
||||
return;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"via_superio_cfg: unimplemented register 0x%x\n", idx);
|
||||
break;
|
||||
}
|
||||
sc->regs[idx] = data;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps vt8231_superio_cfg_ops = {
|
||||
.read = via_superio_cfg_read,
|
||||
.write = vt8231_superio_cfg_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void vt8231_superio_reset(DeviceState *dev)
|
||||
{
|
||||
ViaSuperIOState *s = VIA_SUPERIO(dev);
|
||||
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
/* Device ID */
|
||||
s->regs[0xf0] = 0x3c;
|
||||
/* Device revision */
|
||||
s->regs[0xf1] = 0x01;
|
||||
/* Function select - all disabled */
|
||||
vt8231_superio_cfg_write(s, 0, 0xf2, 1);
|
||||
vt8231_superio_cfg_write(s, 1, 0x03, 1);
|
||||
/* Serial port base addr */
|
||||
vt8231_superio_cfg_write(s, 0, 0xf4, 1);
|
||||
vt8231_superio_cfg_write(s, 1, 0xfe, 1);
|
||||
/* Parallel port base addr */
|
||||
vt8231_superio_cfg_write(s, 0, 0xf6, 1);
|
||||
vt8231_superio_cfg_write(s, 1, 0xde, 1);
|
||||
/* Floppy ctrl base addr */
|
||||
vt8231_superio_cfg_write(s, 0, 0xf7, 1);
|
||||
vt8231_superio_cfg_write(s, 1, 0xfc, 1);
|
||||
|
||||
vt8231_superio_cfg_write(s, 0, 0, 1);
|
||||
}
|
||||
|
||||
static void vt8231_superio_init(Object *obj)
|
||||
{
|
||||
VIA_SUPERIO(obj)->io_ops = &vt8231_superio_cfg_ops;
|
||||
}
|
||||
|
||||
static uint16_t vt8231_superio_serial_iobase(ISASuperIODevice *sio,
|
||||
uint8_t index)
|
||||
{
|
||||
return 0x2f8; /* FIXME: This should be settable via registers f2-f4 */
|
||||
}
|
||||
|
||||
static void vt8231_superio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
||||
|
||||
dc->reset = vt8231_superio_reset;
|
||||
sc->serial.count = 1;
|
||||
sc->serial.get_iobase = vt8231_superio_serial_iobase;
|
||||
sc->parallel.count = 1;
|
||||
sc->ide.count = 0; /* emulated by via-ide */
|
||||
sc->floppy.count = 1;
|
||||
}
|
||||
|
||||
static const TypeInfo vt8231_superio_info = {
|
||||
.name = TYPE_VT8231_SUPERIO,
|
||||
.parent = TYPE_VIA_SUPERIO,
|
||||
.instance_size = sizeof(ViaSuperIOState),
|
||||
.instance_init = vt8231_superio_init,
|
||||
.class_size = sizeof(ISASuperIOClass),
|
||||
.class_init = vt8231_superio_class_init,
|
||||
};
|
||||
|
||||
|
||||
#define TYPE_VIA_ISA "via-isa"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA)
|
||||
|
||||
struct ViaISAState {
|
||||
PCIDevice dev;
|
||||
qemu_irq cpu_intr;
|
||||
SuperIOConfig superio_cfg;
|
||||
ViaSuperIOState *via_sio;
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_via = {
|
||||
.name = "via-isa",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(dev, ViaISAState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const TypeInfo via_isa_info = {
|
||||
.name = TYPE_VIA_ISA,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(ViaISAState),
|
||||
.abstract = true,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void via_isa_request_i8259_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
VT82C686BISAState *s = opaque;
|
||||
ViaISAState *s = opaque;
|
||||
qemu_set_irq(s->cpu_intr, level);
|
||||
}
|
||||
|
||||
/* TYPE_VT82C686B_ISA */
|
||||
|
||||
static void vt82c686b_write_config(PCIDevice *d, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
VT82C686BISAState *s = VT82C686B_ISA(d);
|
||||
ViaISAState *s = VIA_ISA(d);
|
||||
|
||||
trace_via_isa_write(addr, val, len);
|
||||
pci_default_write_config(d, addr, val, len);
|
||||
if (addr == 0x85) {
|
||||
/* BIT(1): enable or disable superio config io ports */
|
||||
memory_region_set_enabled(&s->superio_cfg.io, val & BIT(1));
|
||||
via_superio_io_enable(s->via_sio, val & BIT(1));
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_via = {
|
||||
.name = "vt82c686b",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(dev, VT82C686BISAState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void vt82c686b_isa_reset(DeviceState *dev)
|
||||
{
|
||||
VT82C686BISAState *s = VT82C686B_ISA(dev);
|
||||
ViaISAState *s = VIA_ISA(dev);
|
||||
uint8_t *pci_conf = s->dev.config;
|
||||
|
||||
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
|
||||
@ -385,18 +605,11 @@ static void vt82c686b_isa_reset(DeviceState *dev)
|
||||
pci_conf[0x5a] = 0x04; /* KBC/RTC Control*/
|
||||
pci_conf[0x5f] = 0x04;
|
||||
pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */
|
||||
|
||||
s->superio_cfg.regs[0xe0] = 0x3c; /* Device ID */
|
||||
s->superio_cfg.regs[0xe2] = 0x03; /* Function select */
|
||||
s->superio_cfg.regs[0xe3] = 0xfc; /* Floppy ctrl base addr */
|
||||
s->superio_cfg.regs[0xe6] = 0xde; /* Parallel port base addr */
|
||||
s->superio_cfg.regs[0xe7] = 0xfe; /* Serial port 1 base addr */
|
||||
s->superio_cfg.regs[0xe8] = 0xbe; /* Serial port 2 base addr */
|
||||
}
|
||||
|
||||
static void vt82c686b_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
VT82C686BISAState *s = VT82C686B_ISA(d);
|
||||
ViaISAState *s = VIA_ISA(d);
|
||||
DeviceState *dev = DEVICE(d);
|
||||
ISABus *isa_bus;
|
||||
qemu_irq *isa_irq;
|
||||
@ -409,7 +622,8 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
|
||||
isa_bus_irqs(isa_bus, i8259_init(isa_bus, *isa_irq));
|
||||
i8254_pit_init(isa_bus, 0x40, 0, NULL);
|
||||
i8257_dma_init(isa_bus, 0);
|
||||
isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO);
|
||||
s->via_sio = VIA_SUPERIO(isa_create_simple(isa_bus,
|
||||
TYPE_VT82C686B_SUPERIO));
|
||||
mc146818_rtc_init(isa_bus, 2000, NULL);
|
||||
|
||||
for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) {
|
||||
@ -417,19 +631,9 @@ static void vt82c686b_realize(PCIDevice *d, Error **errp)
|
||||
d->wmask[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->superio_cfg.io, OBJECT(d), &superio_cfg_ops,
|
||||
&s->superio_cfg, "superio_cfg", 2);
|
||||
memory_region_set_enabled(&s->superio_cfg.io, false);
|
||||
/*
|
||||
* The floppy also uses 0x3f0 and 0x3f1.
|
||||
* But we do not emulate a floppy, so just set it here.
|
||||
*/
|
||||
memory_region_add_subregion(isa_bus->address_space_io, 0x3f0,
|
||||
&s->superio_cfg.io);
|
||||
}
|
||||
|
||||
static void via_class_init(ObjectClass *klass, void *data)
|
||||
static void vt82c686b_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
@ -437,47 +641,101 @@ static void via_class_init(ObjectClass *klass, void *data)
|
||||
k->realize = vt82c686b_realize;
|
||||
k->config_write = vt82c686b_write_config;
|
||||
k->vendor_id = PCI_VENDOR_ID_VIA;
|
||||
k->device_id = PCI_DEVICE_ID_VIA_ISA_BRIDGE;
|
||||
k->device_id = PCI_DEVICE_ID_VIA_82C686B_ISA;
|
||||
k->class_id = PCI_CLASS_BRIDGE_ISA;
|
||||
k->revision = 0x40;
|
||||
dc->reset = vt82c686b_isa_reset;
|
||||
dc->desc = "ISA bridge";
|
||||
dc->vmsd = &vmstate_via;
|
||||
/*
|
||||
* Reason: part of VIA VT82C686 southbridge, needs to be wired up,
|
||||
* e.g. by mips_fuloong2e_init()
|
||||
*/
|
||||
/* Reason: part of VIA VT82C686 southbridge, needs to be wired up */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo via_info = {
|
||||
static const TypeInfo vt82c686b_isa_info = {
|
||||
.name = TYPE_VT82C686B_ISA,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(VT82C686BISAState),
|
||||
.class_init = via_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
.parent = TYPE_VIA_ISA,
|
||||
.instance_size = sizeof(ViaISAState),
|
||||
.class_init = vt82c686b_class_init,
|
||||
};
|
||||
|
||||
/* TYPE_VT8231_ISA */
|
||||
|
||||
static void vt82c686b_superio_class_init(ObjectClass *klass, void *data)
|
||||
static void vt8231_write_config(PCIDevice *d, uint32_t addr,
|
||||
uint32_t val, int len)
|
||||
{
|
||||
ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass);
|
||||
ViaISAState *s = VIA_ISA(d);
|
||||
|
||||
sc->serial.count = 2;
|
||||
sc->parallel.count = 1;
|
||||
sc->ide.count = 0;
|
||||
sc->floppy.count = 1;
|
||||
trace_via_isa_write(addr, val, len);
|
||||
pci_default_write_config(d, addr, val, len);
|
||||
if (addr == 0x50) {
|
||||
/* BIT(2): enable or disable superio config io ports */
|
||||
via_superio_io_enable(s->via_sio, val & BIT(2));
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo via_superio_info = {
|
||||
.name = TYPE_VT82C686B_SUPERIO,
|
||||
.parent = TYPE_ISA_SUPERIO,
|
||||
.instance_size = sizeof(ISASuperIODevice),
|
||||
.class_size = sizeof(ISASuperIOClass),
|
||||
.class_init = vt82c686b_superio_class_init,
|
||||
static void vt8231_isa_reset(DeviceState *dev)
|
||||
{
|
||||
ViaISAState *s = VIA_ISA(dev);
|
||||
uint8_t *pci_conf = s->dev.config;
|
||||
|
||||
pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
|
||||
pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
||||
PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL);
|
||||
pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
|
||||
|
||||
pci_conf[0x58] = 0x40; /* Miscellaneous Control 0 */
|
||||
pci_conf[0x67] = 0x08; /* Fast IR Config */
|
||||
pci_conf[0x6b] = 0x01; /* Fast IR I/O Base */
|
||||
}
|
||||
|
||||
static void vt8231_realize(PCIDevice *d, Error **errp)
|
||||
{
|
||||
ViaISAState *s = VIA_ISA(d);
|
||||
DeviceState *dev = DEVICE(d);
|
||||
ISABus *isa_bus;
|
||||
qemu_irq *isa_irq;
|
||||
int i;
|
||||
|
||||
qdev_init_gpio_out(dev, &s->cpu_intr, 1);
|
||||
isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1);
|
||||
isa_bus = isa_bus_new(dev, get_system_memory(), pci_address_space_io(d),
|
||||
&error_fatal);
|
||||
isa_bus_irqs(isa_bus, i8259_init(isa_bus, *isa_irq));
|
||||
i8254_pit_init(isa_bus, 0x40, 0, NULL);
|
||||
i8257_dma_init(isa_bus, 0);
|
||||
s->via_sio = VIA_SUPERIO(isa_create_simple(isa_bus, TYPE_VT8231_SUPERIO));
|
||||
mc146818_rtc_init(isa_bus, 2000, NULL);
|
||||
|
||||
for (i = 0; i < PCI_CONFIG_HEADER_SIZE; i++) {
|
||||
if (i < PCI_COMMAND || i >= PCI_REVISION_ID) {
|
||||
d->wmask[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vt8231_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = vt8231_realize;
|
||||
k->config_write = vt8231_write_config;
|
||||
k->vendor_id = PCI_VENDOR_ID_VIA;
|
||||
k->device_id = PCI_DEVICE_ID_VIA_8231_ISA;
|
||||
k->class_id = PCI_CLASS_BRIDGE_ISA;
|
||||
k->revision = 0x10;
|
||||
dc->reset = vt8231_isa_reset;
|
||||
dc->desc = "ISA bridge";
|
||||
dc->vmsd = &vmstate_via;
|
||||
/* Reason: part of VIA VT8231 southbridge, needs to be wired up */
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo vt8231_isa_info = {
|
||||
.name = TYPE_VT8231_ISA,
|
||||
.parent = TYPE_VIA_ISA,
|
||||
.instance_size = sizeof(ViaISAState),
|
||||
.class_init = vt8231_class_init,
|
||||
};
|
||||
|
||||
|
||||
@ -486,8 +744,12 @@ static void vt82c686b_register_types(void)
|
||||
type_register_static(&via_pm_info);
|
||||
type_register_static(&vt82c686b_pm_info);
|
||||
type_register_static(&vt8231_pm_info);
|
||||
type_register_static(&via_info);
|
||||
type_register_static(&via_superio_info);
|
||||
type_register_static(&vt82c686b_superio_info);
|
||||
type_register_static(&vt8231_superio_info);
|
||||
type_register_static(&via_isa_info);
|
||||
type_register_static(&vt82c686b_isa_info);
|
||||
type_register_static(&vt8231_isa_info);
|
||||
}
|
||||
|
||||
type_init(vt82c686b_register_types)
|
||||
|
@ -72,3 +72,7 @@ config REMOTE_PCIHOST
|
||||
config SH_PCI
|
||||
bool
|
||||
select PCI
|
||||
|
||||
config MV64361
|
||||
bool
|
||||
select PCI
|
||||
|
@ -19,6 +19,8 @@ pci_ss.add(when: 'CONFIG_GRACKLE_PCI', if_true: files('grackle.c'))
|
||||
pci_ss.add(when: 'CONFIG_UNIN_PCI', if_true: files('uninorth.c'))
|
||||
# PowerPC E500 boards
|
||||
pci_ss.add(when: 'CONFIG_PPCE500_PCI', if_true: files('ppce500.c'))
|
||||
# Pegasos2
|
||||
pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c'))
|
||||
|
||||
# ARM devices
|
||||
pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c'))
|
||||
|
951
hw/pci-host/mv64361.c
Normal file
951
hw/pci-host/mv64361.c
Normal file
@ -0,0 +1,951 @@
|
||||
/*
|
||||
* Marvell Discovery II MV64361 System Controller for
|
||||
* QEMU PowerPC CHRP (Genesi/bPlan Pegasos II) hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2018-2020 BALATON Zoltan
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/intc/i8259.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "trace.h"
|
||||
#include "hw/pci-host/mv64361.h"
|
||||
#include "mv643xx.h"
|
||||
|
||||
#define TYPE_MV64361_PCI_BRIDGE "mv64361-pcibridge"
|
||||
|
||||
static void mv64361_pcibridge_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_MARVELL;
|
||||
k->device_id = PCI_DEVICE_ID_MARVELL_MV6436X;
|
||||
k->class_id = PCI_CLASS_BRIDGE_HOST;
|
||||
/*
|
||||
* PCI-facing part of the host bridge,
|
||||
* not usable without the host-facing part
|
||||
*/
|
||||
dc->user_creatable = false;
|
||||
}
|
||||
|
||||
static const TypeInfo mv64361_pcibridge_info = {
|
||||
.name = TYPE_MV64361_PCI_BRIDGE,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(PCIDevice),
|
||||
.class_init = mv64361_pcibridge_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
#define TYPE_MV64361_PCI "mv64361-pcihost"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(MV64361PCIState, MV64361_PCI)
|
||||
|
||||
struct MV64361PCIState {
|
||||
PCIHostState parent_obj;
|
||||
|
||||
uint8_t index;
|
||||
MemoryRegion io;
|
||||
MemoryRegion mem;
|
||||
qemu_irq irq[PCI_NUM_PINS];
|
||||
|
||||
uint32_t io_base;
|
||||
uint32_t io_size;
|
||||
uint32_t mem_base[4];
|
||||
uint32_t mem_size[4];
|
||||
uint64_t remap[5];
|
||||
};
|
||||
|
||||
static int mv64361_pcihost_map_irq(PCIDevice *pci_dev, int n)
|
||||
{
|
||||
return (n + PCI_SLOT(pci_dev->devfn)) % PCI_NUM_PINS;
|
||||
}
|
||||
|
||||
static void mv64361_pcihost_set_irq(void *opaque, int n, int level)
|
||||
{
|
||||
MV64361PCIState *s = opaque;
|
||||
qemu_set_irq(s->irq[n], level);
|
||||
}
|
||||
|
||||
static void mv64361_pcihost_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MV64361PCIState *s = MV64361_PCI(dev);
|
||||
PCIHostState *h = PCI_HOST_BRIDGE(dev);
|
||||
char *name;
|
||||
|
||||
name = g_strdup_printf("pci%d-io", s->index);
|
||||
memory_region_init(&s->io, OBJECT(dev), name, 0x10000);
|
||||
g_free(name);
|
||||
name = g_strdup_printf("pci%d-mem", s->index);
|
||||
memory_region_init(&s->mem, OBJECT(dev), name, 1ULL << 32);
|
||||
g_free(name);
|
||||
name = g_strdup_printf("pci.%d", s->index);
|
||||
h->bus = pci_register_root_bus(dev, name, mv64361_pcihost_set_irq,
|
||||
mv64361_pcihost_map_irq, dev,
|
||||
&s->mem, &s->io, 0, 4, TYPE_PCI_BUS);
|
||||
g_free(name);
|
||||
pci_create_simple(h->bus, 0, TYPE_MV64361_PCI_BRIDGE);
|
||||
}
|
||||
|
||||
static Property mv64361_pcihost_props[] = {
|
||||
DEFINE_PROP_UINT8("index", MV64361PCIState, index, 0),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
static void mv64361_pcihost_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = mv64361_pcihost_realize;
|
||||
device_class_set_props(dc, mv64361_pcihost_props);
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo mv64361_pcihost_info = {
|
||||
.name = TYPE_MV64361_PCI,
|
||||
.parent = TYPE_PCI_HOST_BRIDGE,
|
||||
.instance_size = sizeof(MV64361PCIState),
|
||||
.class_init = mv64361_pcihost_class_init,
|
||||
};
|
||||
|
||||
static void mv64361_pci_register_types(void)
|
||||
{
|
||||
type_register_static(&mv64361_pcihost_info);
|
||||
type_register_static(&mv64361_pcibridge_info);
|
||||
}
|
||||
|
||||
type_init(mv64361_pci_register_types)
|
||||
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(MV64361State, MV64361)
|
||||
|
||||
struct MV64361State {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion regs;
|
||||
MV64361PCIState pci[2];
|
||||
MemoryRegion cpu_win[19];
|
||||
qemu_irq cpu_irq;
|
||||
|
||||
/* registers state */
|
||||
uint32_t cpu_conf;
|
||||
uint32_t regs_base;
|
||||
uint32_t base_addr_enable;
|
||||
uint64_t main_int_cr;
|
||||
uint64_t cpu0_int_mask;
|
||||
uint32_t gpp_io;
|
||||
uint32_t gpp_level;
|
||||
uint32_t gpp_value;
|
||||
uint32_t gpp_int_cr;
|
||||
uint32_t gpp_int_mask;
|
||||
bool gpp_int_level;
|
||||
};
|
||||
|
||||
enum mv64361_irq_cause {
|
||||
MV64361_IRQ_DEVERR = 1,
|
||||
MV64361_IRQ_DMAERR = 2,
|
||||
MV64361_IRQ_CPUERR = 3,
|
||||
MV64361_IRQ_IDMA0 = 4,
|
||||
MV64361_IRQ_IDMA1 = 5,
|
||||
MV64361_IRQ_IDMA2 = 6,
|
||||
MV64361_IRQ_IDMA3 = 7,
|
||||
MV64361_IRQ_TIMER0 = 8,
|
||||
MV64361_IRQ_TIMER1 = 9,
|
||||
MV64361_IRQ_TIMER2 = 10,
|
||||
MV64361_IRQ_TIMER3 = 11,
|
||||
MV64361_IRQ_PCI0 = 12,
|
||||
MV64361_IRQ_SRAMERR = 13,
|
||||
MV64361_IRQ_GBEERR = 14,
|
||||
MV64361_IRQ_CERR = 15,
|
||||
MV64361_IRQ_PCI1 = 16,
|
||||
MV64361_IRQ_DRAMERR = 17,
|
||||
MV64361_IRQ_WDNMI = 18,
|
||||
MV64361_IRQ_WDE = 19,
|
||||
MV64361_IRQ_PCI0IN = 20,
|
||||
MV64361_IRQ_PCI0OUT = 21,
|
||||
MV64361_IRQ_PCI1IN = 22,
|
||||
MV64361_IRQ_PCI1OUT = 23,
|
||||
MV64361_IRQ_P1_GPP0_7 = 24,
|
||||
MV64361_IRQ_P1_GPP8_15 = 25,
|
||||
MV64361_IRQ_P1_GPP16_23 = 26,
|
||||
MV64361_IRQ_P1_GPP24_31 = 27,
|
||||
MV64361_IRQ_P1_CPU_DB = 28,
|
||||
/* 29-31: reserved */
|
||||
MV64361_IRQ_GBE0 = 32,
|
||||
MV64361_IRQ_GBE1 = 33,
|
||||
MV64361_IRQ_GBE2 = 34,
|
||||
/* 35: reserved */
|
||||
MV64361_IRQ_SDMA0 = 36,
|
||||
MV64361_IRQ_TWSI = 37,
|
||||
MV64361_IRQ_SDMA1 = 38,
|
||||
MV64361_IRQ_BRG = 39,
|
||||
MV64361_IRQ_MPSC0 = 40,
|
||||
MV64361_IRQ_MPSC1 = 41,
|
||||
MV64361_IRQ_G0RX = 42,
|
||||
MV64361_IRQ_G0TX = 43,
|
||||
MV64361_IRQ_G0MISC = 44,
|
||||
MV64361_IRQ_G1RX = 45,
|
||||
MV64361_IRQ_G1TX = 46,
|
||||
MV64361_IRQ_G1MISC = 47,
|
||||
MV64361_IRQ_G2RX = 48,
|
||||
MV64361_IRQ_G2TX = 49,
|
||||
MV64361_IRQ_G2MISC = 50,
|
||||
/* 51-55: reserved */
|
||||
MV64361_IRQ_P0_GPP0_7 = 56,
|
||||
MV64361_IRQ_P0_GPP8_15 = 57,
|
||||
MV64361_IRQ_P0_GPP16_23 = 58,
|
||||
MV64361_IRQ_P0_GPP24_31 = 59,
|
||||
MV64361_IRQ_P0_CPU_DB = 60,
|
||||
/* 61-63: reserved */
|
||||
};
|
||||
|
||||
PCIBus *mv64361_get_pci_bus(DeviceState *dev, int n)
|
||||
{
|
||||
MV64361State *mv = MV64361(dev);
|
||||
return PCI_HOST_BRIDGE(&mv->pci[n])->bus;
|
||||
}
|
||||
|
||||
static void unmap_region(MemoryRegion *mr)
|
||||
{
|
||||
if (memory_region_is_mapped(mr)) {
|
||||
memory_region_del_subregion(get_system_memory(), mr);
|
||||
object_unparent(OBJECT(mr));
|
||||
}
|
||||
}
|
||||
|
||||
static void map_pci_region(MemoryRegion *mr, MemoryRegion *parent,
|
||||
struct Object *owner, const char *name,
|
||||
hwaddr poffs, uint64_t size, hwaddr moffs)
|
||||
{
|
||||
memory_region_init_alias(mr, owner, name, parent, poffs, size);
|
||||
memory_region_add_subregion(get_system_memory(), moffs, mr);
|
||||
trace_mv64361_region_map(name, poffs, size, moffs);
|
||||
}
|
||||
|
||||
static void set_mem_windows(MV64361State *s, uint32_t val)
|
||||
{
|
||||
MV64361PCIState *p;
|
||||
MemoryRegion *mr;
|
||||
uint32_t mask;
|
||||
int i;
|
||||
|
||||
val &= 0x1fffff;
|
||||
for (mask = 1, i = 0; i < 21; i++, mask <<= 1) {
|
||||
if ((val & mask) != (s->base_addr_enable & mask)) {
|
||||
trace_mv64361_region_enable(!(val & mask) ? "enable" : "disable", i);
|
||||
/*
|
||||
* 0-3 are SDRAM chip selects but we map all RAM directly
|
||||
* 4-7 are device chip selects (not sure what those are)
|
||||
* 8 is Boot device (ROM) chip select but we map that directly too
|
||||
*/
|
||||
if (i == 9) {
|
||||
p = &s->pci[0];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->io, OBJECT(s), "pci0-io-win",
|
||||
p->remap[4], (p->io_size + 1) << 16,
|
||||
(p->io_base & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 10) {
|
||||
p = &s->pci[0];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci0-mem0-win",
|
||||
p->remap[0], (p->mem_size[0] + 1) << 16,
|
||||
(p->mem_base[0] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 11) {
|
||||
p = &s->pci[0];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci0-mem1-win",
|
||||
p->remap[1], (p->mem_size[1] + 1) << 16,
|
||||
(p->mem_base[1] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 12) {
|
||||
p = &s->pci[0];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci0-mem2-win",
|
||||
p->remap[2], (p->mem_size[2] + 1) << 16,
|
||||
(p->mem_base[2] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 13) {
|
||||
p = &s->pci[0];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci0-mem3-win",
|
||||
p->remap[3], (p->mem_size[3] + 1) << 16,
|
||||
(p->mem_base[3] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 14) {
|
||||
p = &s->pci[1];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->io, OBJECT(s), "pci1-io-win",
|
||||
p->remap[4], (p->io_size + 1) << 16,
|
||||
(p->io_base & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 15) {
|
||||
p = &s->pci[1];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci1-mem0-win",
|
||||
p->remap[0], (p->mem_size[0] + 1) << 16,
|
||||
(p->mem_base[0] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 16) {
|
||||
p = &s->pci[1];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci1-mem1-win",
|
||||
p->remap[1], (p->mem_size[1] + 1) << 16,
|
||||
(p->mem_base[1] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 17) {
|
||||
p = &s->pci[1];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci1-mem2-win",
|
||||
p->remap[2], (p->mem_size[2] + 1) << 16,
|
||||
(p->mem_base[2] & 0xfffff) << 16);
|
||||
}
|
||||
} else if (i == 18) {
|
||||
p = &s->pci[1];
|
||||
mr = &s->cpu_win[i];
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
map_pci_region(mr, &p->mem, OBJECT(s), "pci1-mem3-win",
|
||||
p->remap[3], (p->mem_size[3] + 1) << 16,
|
||||
(p->mem_base[3] & 0xfffff) << 16);
|
||||
}
|
||||
/* 19 is integrated SRAM */
|
||||
} else if (i == 20) {
|
||||
mr = &s->regs;
|
||||
unmap_region(mr);
|
||||
if (!(val & mask)) {
|
||||
memory_region_add_subregion(get_system_memory(),
|
||||
(s->regs_base & 0xfffff) << 16, mr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s->base_addr_enable = val;
|
||||
}
|
||||
|
||||
static void mv64361_update_irq(void *opaque, int n, int level)
|
||||
{
|
||||
MV64361State *s = opaque;
|
||||
uint64_t val = s->main_int_cr;
|
||||
|
||||
if (level) {
|
||||
val |= BIT_ULL(n);
|
||||
} else {
|
||||
val &= ~BIT_ULL(n);
|
||||
}
|
||||
if ((s->main_int_cr & s->cpu0_int_mask) != (val & s->cpu0_int_mask)) {
|
||||
qemu_set_irq(s->cpu_irq, level);
|
||||
}
|
||||
s->main_int_cr = val;
|
||||
}
|
||||
|
||||
static uint64_t mv64361_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
MV64361State *s = MV64361(opaque);
|
||||
uint32_t ret = 0;
|
||||
|
||||
switch (addr) {
|
||||
case MV64340_CPU_CONFIG:
|
||||
ret = s->cpu_conf;
|
||||
break;
|
||||
case MV64340_PCI_0_IO_BASE_ADDR:
|
||||
ret = s->pci[0].io_base;
|
||||
break;
|
||||
case MV64340_PCI_0_IO_SIZE:
|
||||
ret = s->pci[0].io_size;
|
||||
break;
|
||||
case MV64340_PCI_0_IO_ADDR_REMAP:
|
||||
ret = s->pci[0].remap[4] >> 16;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_BASE_ADDR:
|
||||
ret = s->pci[0].mem_base[0];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_SIZE:
|
||||
ret = s->pci[0].mem_size[0];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[0].remap[0] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[0].remap[0] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_BASE_ADDR:
|
||||
ret = s->pci[0].mem_base[1];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_SIZE:
|
||||
ret = s->pci[0].mem_size[1];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[0].remap[1] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[0].remap[1] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_BASE_ADDR:
|
||||
ret = s->pci[0].mem_base[2];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_SIZE:
|
||||
ret = s->pci[0].mem_size[2];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[0].remap[2] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[0].remap[2] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_BASE_ADDR:
|
||||
ret = s->pci[0].mem_base[3];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_SIZE:
|
||||
ret = s->pci[0].mem_size[3];
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[0].remap[3] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[0].remap[3] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_1_IO_BASE_ADDR:
|
||||
ret = s->pci[1].io_base;
|
||||
break;
|
||||
case MV64340_PCI_1_IO_SIZE:
|
||||
ret = s->pci[1].io_size;
|
||||
break;
|
||||
case MV64340_PCI_1_IO_ADDR_REMAP:
|
||||
ret = s->pci[1].remap[4] >> 16;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_BASE_ADDR:
|
||||
ret = s->pci[1].mem_base[0];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_SIZE:
|
||||
ret = s->pci[1].mem_size[0];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[1].remap[0] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[1].remap[0] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_BASE_ADDR:
|
||||
ret = s->pci[1].mem_base[1];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_SIZE:
|
||||
ret = s->pci[1].mem_size[1];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[1].remap[1] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[1].remap[1] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_BASE_ADDR:
|
||||
ret = s->pci[1].mem_base[2];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_SIZE:
|
||||
ret = s->pci[1].mem_size[2];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[1].remap[2] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[1].remap[2] >> 32;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_BASE_ADDR:
|
||||
ret = s->pci[1].mem_base[3];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_SIZE:
|
||||
ret = s->pci[1].mem_size[3];
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_LOW_ADDR_REMAP:
|
||||
ret = (s->pci[1].remap[3] & 0xffff0000) >> 16;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_HIGH_ADDR_REMAP:
|
||||
ret = s->pci[1].remap[3] >> 32;
|
||||
break;
|
||||
case MV64340_INTERNAL_SPACE_BASE_ADDR:
|
||||
ret = s->regs_base;
|
||||
break;
|
||||
case MV64340_BASE_ADDR_ENABLE:
|
||||
ret = s->base_addr_enable;
|
||||
break;
|
||||
case MV64340_PCI_0_CONFIG_ADDR:
|
||||
ret = pci_host_conf_le_ops.read(PCI_HOST_BRIDGE(&s->pci[0]), 0, size);
|
||||
break;
|
||||
case MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG ...
|
||||
MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG + 3:
|
||||
ret = pci_host_data_le_ops.read(PCI_HOST_BRIDGE(&s->pci[0]),
|
||||
addr - MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG, size);
|
||||
break;
|
||||
case MV64340_PCI_1_CONFIG_ADDR:
|
||||
ret = pci_host_conf_le_ops.read(PCI_HOST_BRIDGE(&s->pci[1]), 0, size);
|
||||
break;
|
||||
case MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG ...
|
||||
MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG + 3:
|
||||
ret = pci_host_data_le_ops.read(PCI_HOST_BRIDGE(&s->pci[1]),
|
||||
addr - MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG, size);
|
||||
break;
|
||||
case MV64340_PCI_1_INTERRUPT_ACKNOWLEDGE_VIRTUAL_REG:
|
||||
/* FIXME: Should this be sent via the PCI bus somehow? */
|
||||
if (s->gpp_int_level && (s->gpp_value & BIT(31))) {
|
||||
ret = pic_read_irq(isa_pic);
|
||||
}
|
||||
break;
|
||||
case MV64340_MAIN_INTERRUPT_CAUSE_LOW:
|
||||
ret = s->main_int_cr;
|
||||
break;
|
||||
case MV64340_MAIN_INTERRUPT_CAUSE_HIGH:
|
||||
ret = s->main_int_cr >> 32;
|
||||
break;
|
||||
case MV64340_CPU_INTERRUPT0_MASK_LOW:
|
||||
ret = s->cpu0_int_mask;
|
||||
break;
|
||||
case MV64340_CPU_INTERRUPT0_MASK_HIGH:
|
||||
ret = s->cpu0_int_mask >> 32;
|
||||
break;
|
||||
case MV64340_CPU_INTERRUPT0_SELECT_CAUSE:
|
||||
ret = s->main_int_cr;
|
||||
if (s->main_int_cr & s->cpu0_int_mask) {
|
||||
if (!(s->main_int_cr & s->cpu0_int_mask & 0xffffffff)) {
|
||||
ret = s->main_int_cr >> 32 | BIT(30);
|
||||
} else if ((s->main_int_cr & s->cpu0_int_mask) >> 32) {
|
||||
ret |= BIT(31);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MV64340_CUNIT_ARBITER_CONTROL_REG:
|
||||
ret = 0x11ff0000 | (s->gpp_int_level << 10);
|
||||
break;
|
||||
case MV64340_GPP_IO_CONTROL:
|
||||
ret = s->gpp_io;
|
||||
break;
|
||||
case MV64340_GPP_LEVEL_CONTROL:
|
||||
ret = s->gpp_level;
|
||||
break;
|
||||
case MV64340_GPP_VALUE:
|
||||
ret = s->gpp_value;
|
||||
break;
|
||||
case MV64340_GPP_VALUE_SET:
|
||||
case MV64340_GPP_VALUE_CLEAR:
|
||||
ret = 0;
|
||||
break;
|
||||
case MV64340_GPP_INTERRUPT_CAUSE:
|
||||
ret = s->gpp_int_cr;
|
||||
break;
|
||||
case MV64340_GPP_INTERRUPT_MASK0:
|
||||
case MV64340_GPP_INTERRUPT_MASK1:
|
||||
ret = s->gpp_int_mask;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register read 0x%"
|
||||
HWADDR_PRIx "\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
if (addr != MV64340_PCI_1_INTERRUPT_ACKNOWLEDGE_VIRTUAL_REG) {
|
||||
trace_mv64361_reg_read(addr, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void warn_swap_bit(uint64_t val)
|
||||
{
|
||||
if ((val & 0x3000000ULL) >> 24 != 1) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Data swap not implemented", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void mv64361_set_pci_mem_remap(MV64361State *s, int bus, int idx,
|
||||
uint64_t val, bool high)
|
||||
{
|
||||
if (high) {
|
||||
s->pci[bus].remap[idx] = val;
|
||||
} else {
|
||||
s->pci[bus].remap[idx] &= 0xffffffff00000000ULL;
|
||||
s->pci[bus].remap[idx] |= (val & 0xffffULL) << 16;
|
||||
}
|
||||
}
|
||||
|
||||
static void mv64361_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned int size)
|
||||
{
|
||||
MV64361State *s = MV64361(opaque);
|
||||
|
||||
trace_mv64361_reg_write(addr, val);
|
||||
switch (addr) {
|
||||
case MV64340_CPU_CONFIG:
|
||||
s->cpu_conf = val & 0xe4e3bffULL;
|
||||
s->cpu_conf |= BIT(23);
|
||||
break;
|
||||
case MV64340_PCI_0_IO_BASE_ADDR:
|
||||
s->pci[0].io_base = val & 0x30fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
s->pci[0].remap[4] = (val & 0xffffULL) << 16;
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_0_IO_SIZE:
|
||||
s->pci[0].io_size = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_0_IO_ADDR_REMAP:
|
||||
s->pci[0].remap[4] = (val & 0xffffULL) << 16;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_BASE_ADDR:
|
||||
s->pci[0].mem_base[0] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 0, 0, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_SIZE:
|
||||
s->pci[0].mem_size[0] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY0_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_0_MEMORY0_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 0, 0, val,
|
||||
(addr == MV64340_PCI_0_MEMORY0_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_BASE_ADDR:
|
||||
s->pci[0].mem_base[1] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 0, 1, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_SIZE:
|
||||
s->pci[0].mem_size[1] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY1_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_0_MEMORY1_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 0, 1, val,
|
||||
(addr == MV64340_PCI_0_MEMORY1_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_BASE_ADDR:
|
||||
s->pci[0].mem_base[2] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 0, 2, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_SIZE:
|
||||
s->pci[0].mem_size[2] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY2_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_0_MEMORY2_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 0, 2, val,
|
||||
(addr == MV64340_PCI_0_MEMORY2_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_BASE_ADDR:
|
||||
s->pci[0].mem_base[3] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 0, 3, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_SIZE:
|
||||
s->pci[0].mem_size[3] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_0_MEMORY3_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_0_MEMORY3_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 0, 3, val,
|
||||
(addr == MV64340_PCI_0_MEMORY3_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_1_IO_BASE_ADDR:
|
||||
s->pci[1].io_base = val & 0x30fffffULL;
|
||||
warn_swap_bit(val);
|
||||
break;
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
s->pci[1].remap[4] = (val & 0xffffULL) << 16;
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_1_IO_SIZE:
|
||||
s->pci[1].io_size = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_BASE_ADDR:
|
||||
s->pci[1].mem_base[0] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 1, 0, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_SIZE:
|
||||
s->pci[1].mem_size[0] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY0_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_1_MEMORY0_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 1, 0, val,
|
||||
(addr == MV64340_PCI_1_MEMORY0_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_BASE_ADDR:
|
||||
s->pci[1].mem_base[1] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 1, 1, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_SIZE:
|
||||
s->pci[1].mem_size[1] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY1_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_1_MEMORY1_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 1, 1, val,
|
||||
(addr == MV64340_PCI_1_MEMORY1_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_BASE_ADDR:
|
||||
s->pci[1].mem_base[2] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 1, 2, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_SIZE:
|
||||
s->pci[1].mem_size[2] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY2_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_1_MEMORY2_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 1, 2, val,
|
||||
(addr == MV64340_PCI_1_MEMORY2_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_BASE_ADDR:
|
||||
s->pci[1].mem_base[3] = val & 0x70fffffULL;
|
||||
warn_swap_bit(val);
|
||||
if (!(s->cpu_conf & BIT(27))) {
|
||||
mv64361_set_pci_mem_remap(s, 1, 3, val, false);
|
||||
}
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_SIZE:
|
||||
s->pci[1].mem_size[3] = val & 0xffffULL;
|
||||
break;
|
||||
case MV64340_PCI_1_MEMORY3_LOW_ADDR_REMAP:
|
||||
case MV64340_PCI_1_MEMORY3_HIGH_ADDR_REMAP:
|
||||
mv64361_set_pci_mem_remap(s, 1, 3, val,
|
||||
(addr == MV64340_PCI_1_MEMORY3_HIGH_ADDR_REMAP));
|
||||
break;
|
||||
case MV64340_INTERNAL_SPACE_BASE_ADDR:
|
||||
s->regs_base = val & 0xfffffULL;
|
||||
break;
|
||||
case MV64340_BASE_ADDR_ENABLE:
|
||||
set_mem_windows(s, val);
|
||||
break;
|
||||
case MV64340_PCI_0_CONFIG_ADDR:
|
||||
pci_host_conf_le_ops.write(PCI_HOST_BRIDGE(&s->pci[0]), 0, val, size);
|
||||
break;
|
||||
case MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG ...
|
||||
MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG + 3:
|
||||
pci_host_data_le_ops.write(PCI_HOST_BRIDGE(&s->pci[0]),
|
||||
addr - MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG, val, size);
|
||||
break;
|
||||
case MV64340_PCI_1_CONFIG_ADDR:
|
||||
pci_host_conf_le_ops.write(PCI_HOST_BRIDGE(&s->pci[1]), 0, val, size);
|
||||
break;
|
||||
case MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG ...
|
||||
MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG + 3:
|
||||
pci_host_data_le_ops.write(PCI_HOST_BRIDGE(&s->pci[1]),
|
||||
addr - MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG, val, size);
|
||||
break;
|
||||
case MV64340_CPU_INTERRUPT0_MASK_LOW:
|
||||
s->cpu0_int_mask &= 0xffffffff00000000ULL;
|
||||
s->cpu0_int_mask |= val & 0xffffffffULL;
|
||||
break;
|
||||
case MV64340_CPU_INTERRUPT0_MASK_HIGH:
|
||||
s->cpu0_int_mask &= 0xffffffffULL;
|
||||
s->cpu0_int_mask |= val << 32;
|
||||
break;
|
||||
case MV64340_CUNIT_ARBITER_CONTROL_REG:
|
||||
s->gpp_int_level = !!(val & BIT(10));
|
||||
break;
|
||||
case MV64340_GPP_IO_CONTROL:
|
||||
s->gpp_io = val;
|
||||
break;
|
||||
case MV64340_GPP_LEVEL_CONTROL:
|
||||
s->gpp_level = val;
|
||||
break;
|
||||
case MV64340_GPP_VALUE:
|
||||
s->gpp_value &= ~s->gpp_io;
|
||||
s->gpp_value |= val & s->gpp_io;
|
||||
break;
|
||||
case MV64340_GPP_VALUE_SET:
|
||||
s->gpp_value |= val & s->gpp_io;
|
||||
break;
|
||||
case MV64340_GPP_VALUE_CLEAR:
|
||||
s->gpp_value &= ~(val & s->gpp_io);
|
||||
break;
|
||||
case MV64340_GPP_INTERRUPT_CAUSE:
|
||||
if (!s->gpp_int_level && val != s->gpp_int_cr) {
|
||||
int i;
|
||||
uint32_t ch = s->gpp_int_cr ^ val;
|
||||
s->gpp_int_cr = val;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((ch & 0xff << i) && !(val & 0xff << i)) {
|
||||
mv64361_update_irq(opaque, MV64361_IRQ_P0_GPP0_7 + i, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->gpp_int_cr = val;
|
||||
}
|
||||
break;
|
||||
case MV64340_GPP_INTERRUPT_MASK0:
|
||||
case MV64340_GPP_INTERRUPT_MASK1:
|
||||
s->gpp_int_mask = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register write 0x%"
|
||||
HWADDR_PRIx " = %"PRIx64"\n", __func__, addr, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mv64361_ops = {
|
||||
.read = mv64361_read,
|
||||
.write = mv64361_write,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void mv64361_gpp_irq(void *opaque, int n, int level)
|
||||
{
|
||||
MV64361State *s = opaque;
|
||||
uint32_t mask = BIT(n);
|
||||
uint32_t val = s->gpp_value & ~mask;
|
||||
|
||||
if (s->gpp_level & mask) {
|
||||
level = !level;
|
||||
}
|
||||
val |= level << n;
|
||||
if (val > s->gpp_value) {
|
||||
s->gpp_value = val;
|
||||
s->gpp_int_cr |= mask;
|
||||
if (s->gpp_int_mask & mask) {
|
||||
mv64361_update_irq(opaque, MV64361_IRQ_P0_GPP0_7 + n / 8, 1);
|
||||
}
|
||||
} else if (val < s->gpp_value) {
|
||||
int b = n / 8;
|
||||
s->gpp_value = val;
|
||||
if (s->gpp_int_level && !(val & 0xff << b)) {
|
||||
mv64361_update_irq(opaque, MV64361_IRQ_P0_GPP0_7 + b, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mv64361_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MV64361State *s = MV64361(dev);
|
||||
int i;
|
||||
|
||||
s->base_addr_enable = 0x1fffff;
|
||||
memory_region_init_io(&s->regs, OBJECT(s), &mv64361_ops, s,
|
||||
TYPE_MV64361, 0x10000);
|
||||
for (i = 0; i < 2; i++) {
|
||||
g_autofree char *name = g_strdup_printf("pcihost%d", i);
|
||||
object_initialize_child(OBJECT(dev), name, &s->pci[i],
|
||||
TYPE_MV64361_PCI);
|
||||
DeviceState *pci = DEVICE(&s->pci[i]);
|
||||
qdev_prop_set_uint8(pci, "index", i);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(pci), &error_fatal);
|
||||
}
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cpu_irq);
|
||||
qdev_init_gpio_in_named(dev, mv64361_gpp_irq, "gpp", 32);
|
||||
/* FIXME: PCI IRQ connections may be board specific */
|
||||
for (i = 0; i < PCI_NUM_PINS; i++) {
|
||||
s->pci[1].irq[i] = qdev_get_gpio_in_named(dev, "gpp", 12 + i);
|
||||
}
|
||||
}
|
||||
|
||||
static void mv64361_reset(DeviceState *dev)
|
||||
{
|
||||
MV64361State *s = MV64361(dev);
|
||||
int i, j;
|
||||
|
||||
/*
|
||||
* These values may be board specific
|
||||
* Real chip supports init from an eprom but that's not modelled
|
||||
*/
|
||||
set_mem_windows(s, 0x1fffff);
|
||||
s->cpu_conf = 0x28000ff;
|
||||
s->regs_base = 0x100f100;
|
||||
s->pci[0].io_base = 0x100f800;
|
||||
s->pci[0].io_size = 0xff;
|
||||
s->pci[0].mem_base[0] = 0x100c000;
|
||||
s->pci[0].mem_size[0] = 0x1fff;
|
||||
s->pci[0].mem_base[1] = 0x100f900;
|
||||
s->pci[0].mem_size[1] = 0xff;
|
||||
s->pci[0].mem_base[2] = 0x100f400;
|
||||
s->pci[0].mem_size[2] = 0x1ff;
|
||||
s->pci[0].mem_base[3] = 0x100f600;
|
||||
s->pci[0].mem_size[3] = 0x1ff;
|
||||
s->pci[1].io_base = 0x100fe00;
|
||||
s->pci[1].io_size = 0xff;
|
||||
s->pci[1].mem_base[0] = 0x1008000;
|
||||
s->pci[1].mem_size[0] = 0x3fff;
|
||||
s->pci[1].mem_base[1] = 0x100fd00;
|
||||
s->pci[1].mem_size[1] = 0xff;
|
||||
s->pci[1].mem_base[2] = 0x1002600;
|
||||
s->pci[1].mem_size[2] = 0x1ff;
|
||||
s->pci[1].mem_base[3] = 0x100ff80;
|
||||
s->pci[1].mem_size[3] = 0x7f;
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
s->pci[i].remap[j] = s->pci[i].mem_base[j] << 16;
|
||||
}
|
||||
}
|
||||
s->pci[0].remap[1] = 0;
|
||||
s->pci[1].remap[1] = 0;
|
||||
set_mem_windows(s, 0xfbfff);
|
||||
}
|
||||
|
||||
static void mv64361_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = mv64361_realize;
|
||||
dc->reset = mv64361_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo mv64361_type_info = {
|
||||
.name = TYPE_MV64361,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(MV64361State),
|
||||
.class_init = mv64361_class_init,
|
||||
};
|
||||
|
||||
static void mv64361_register_types(void)
|
||||
{
|
||||
type_register_static(&mv64361_type_info);
|
||||
}
|
||||
|
||||
type_init(mv64361_register_types)
|
918
hw/pci-host/mv643xx.h
Normal file
918
hw/pci-host/mv643xx.h
Normal file
@ -0,0 +1,918 @@
|
||||
/*
|
||||
* mv643xx.h - MV-643XX Internal registers definition file.
|
||||
*
|
||||
* Copyright 2002 Momentum Computer, Inc.
|
||||
* Author: Matthew Dharm <mdharm@momenco.com>
|
||||
* Copyright 2002 GALILEO TECHNOLOGY, LTD.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*/
|
||||
#ifndef ASM_MV643XX_H
|
||||
#define ASM_MV643XX_H
|
||||
|
||||
/****************************************/
|
||||
/* Processor Address Space */
|
||||
/****************************************/
|
||||
|
||||
/* DDR SDRAM BAR and size registers */
|
||||
|
||||
#define MV64340_CS_0_BASE_ADDR 0x008
|
||||
#define MV64340_CS_0_SIZE 0x010
|
||||
#define MV64340_CS_1_BASE_ADDR 0x208
|
||||
#define MV64340_CS_1_SIZE 0x210
|
||||
#define MV64340_CS_2_BASE_ADDR 0x018
|
||||
#define MV64340_CS_2_SIZE 0x020
|
||||
#define MV64340_CS_3_BASE_ADDR 0x218
|
||||
#define MV64340_CS_3_SIZE 0x220
|
||||
|
||||
/* Devices BAR and size registers */
|
||||
|
||||
#define MV64340_DEV_CS0_BASE_ADDR 0x028
|
||||
#define MV64340_DEV_CS0_SIZE 0x030
|
||||
#define MV64340_DEV_CS1_BASE_ADDR 0x228
|
||||
#define MV64340_DEV_CS1_SIZE 0x230
|
||||
#define MV64340_DEV_CS2_BASE_ADDR 0x248
|
||||
#define MV64340_DEV_CS2_SIZE 0x250
|
||||
#define MV64340_DEV_CS3_BASE_ADDR 0x038
|
||||
#define MV64340_DEV_CS3_SIZE 0x040
|
||||
#define MV64340_BOOTCS_BASE_ADDR 0x238
|
||||
#define MV64340_BOOTCS_SIZE 0x240
|
||||
|
||||
/* PCI 0 BAR and size registers */
|
||||
|
||||
#define MV64340_PCI_0_IO_BASE_ADDR 0x048
|
||||
#define MV64340_PCI_0_IO_SIZE 0x050
|
||||
#define MV64340_PCI_0_MEMORY0_BASE_ADDR 0x058
|
||||
#define MV64340_PCI_0_MEMORY0_SIZE 0x060
|
||||
#define MV64340_PCI_0_MEMORY1_BASE_ADDR 0x080
|
||||
#define MV64340_PCI_0_MEMORY1_SIZE 0x088
|
||||
#define MV64340_PCI_0_MEMORY2_BASE_ADDR 0x258
|
||||
#define MV64340_PCI_0_MEMORY2_SIZE 0x260
|
||||
#define MV64340_PCI_0_MEMORY3_BASE_ADDR 0x280
|
||||
#define MV64340_PCI_0_MEMORY3_SIZE 0x288
|
||||
|
||||
/* PCI 1 BAR and size registers */
|
||||
#define MV64340_PCI_1_IO_BASE_ADDR 0x090
|
||||
#define MV64340_PCI_1_IO_SIZE 0x098
|
||||
#define MV64340_PCI_1_MEMORY0_BASE_ADDR 0x0a0
|
||||
#define MV64340_PCI_1_MEMORY0_SIZE 0x0a8
|
||||
#define MV64340_PCI_1_MEMORY1_BASE_ADDR 0x0b0
|
||||
#define MV64340_PCI_1_MEMORY1_SIZE 0x0b8
|
||||
#define MV64340_PCI_1_MEMORY2_BASE_ADDR 0x2a0
|
||||
#define MV64340_PCI_1_MEMORY2_SIZE 0x2a8
|
||||
#define MV64340_PCI_1_MEMORY3_BASE_ADDR 0x2b0
|
||||
#define MV64340_PCI_1_MEMORY3_SIZE 0x2b8
|
||||
|
||||
/* SRAM base address */
|
||||
#define MV64340_INTEGRATED_SRAM_BASE_ADDR 0x268
|
||||
|
||||
/* internal registers space base address */
|
||||
#define MV64340_INTERNAL_SPACE_BASE_ADDR 0x068
|
||||
|
||||
/* Enables the CS , DEV_CS , PCI 0 and PCI 1 windows above */
|
||||
#define MV64340_BASE_ADDR_ENABLE 0x278
|
||||
|
||||
/****************************************/
|
||||
/* PCI remap registers */
|
||||
/****************************************/
|
||||
|
||||
/* PCI 0 */
|
||||
#define MV64340_PCI_0_IO_ADDR_REMAP 0x0f0
|
||||
#define MV64340_PCI_0_MEMORY0_LOW_ADDR_REMAP 0x0f8
|
||||
#define MV64340_PCI_0_MEMORY0_HIGH_ADDR_REMAP 0x320
|
||||
#define MV64340_PCI_0_MEMORY1_LOW_ADDR_REMAP 0x100
|
||||
#define MV64340_PCI_0_MEMORY1_HIGH_ADDR_REMAP 0x328
|
||||
#define MV64340_PCI_0_MEMORY2_LOW_ADDR_REMAP 0x2f8
|
||||
#define MV64340_PCI_0_MEMORY2_HIGH_ADDR_REMAP 0x330
|
||||
#define MV64340_PCI_0_MEMORY3_LOW_ADDR_REMAP 0x300
|
||||
#define MV64340_PCI_0_MEMORY3_HIGH_ADDR_REMAP 0x338
|
||||
/* PCI 1 */
|
||||
#define MV64340_PCI_1_IO_ADDR_REMAP 0x108
|
||||
#define MV64340_PCI_1_MEMORY0_LOW_ADDR_REMAP 0x110
|
||||
#define MV64340_PCI_1_MEMORY0_HIGH_ADDR_REMAP 0x340
|
||||
#define MV64340_PCI_1_MEMORY1_LOW_ADDR_REMAP 0x118
|
||||
#define MV64340_PCI_1_MEMORY1_HIGH_ADDR_REMAP 0x348
|
||||
#define MV64340_PCI_1_MEMORY2_LOW_ADDR_REMAP 0x310
|
||||
#define MV64340_PCI_1_MEMORY2_HIGH_ADDR_REMAP 0x350
|
||||
#define MV64340_PCI_1_MEMORY3_LOW_ADDR_REMAP 0x318
|
||||
#define MV64340_PCI_1_MEMORY3_HIGH_ADDR_REMAP 0x358
|
||||
|
||||
#define MV64340_CPU_PCI_0_HEADERS_RETARGET_CONTROL 0x3b0
|
||||
#define MV64340_CPU_PCI_0_HEADERS_RETARGET_BASE 0x3b8
|
||||
#define MV64340_CPU_PCI_1_HEADERS_RETARGET_CONTROL 0x3c0
|
||||
#define MV64340_CPU_PCI_1_HEADERS_RETARGET_BASE 0x3c8
|
||||
#define MV64340_CPU_GE_HEADERS_RETARGET_CONTROL 0x3d0
|
||||
#define MV64340_CPU_GE_HEADERS_RETARGET_BASE 0x3d8
|
||||
#define MV64340_CPU_IDMA_HEADERS_RETARGET_CONTROL 0x3e0
|
||||
#define MV64340_CPU_IDMA_HEADERS_RETARGET_BASE 0x3e8
|
||||
|
||||
/****************************************/
|
||||
/* CPU Control Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_CPU_CONFIG 0x000
|
||||
#define MV64340_CPU_MODE 0x120
|
||||
#define MV64340_CPU_MASTER_CONTROL 0x160
|
||||
#define MV64340_CPU_CROSS_BAR_CONTROL_LOW 0x150
|
||||
#define MV64340_CPU_CROSS_BAR_CONTROL_HIGH 0x158
|
||||
#define MV64340_CPU_CROSS_BAR_TIMEOUT 0x168
|
||||
|
||||
/****************************************/
|
||||
/* SMP RegisterS */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_SMP_WHO_AM_I 0x200
|
||||
#define MV64340_SMP_CPU0_DOORBELL 0x214
|
||||
#define MV64340_SMP_CPU0_DOORBELL_CLEAR 0x21C
|
||||
#define MV64340_SMP_CPU1_DOORBELL 0x224
|
||||
#define MV64340_SMP_CPU1_DOORBELL_CLEAR 0x22C
|
||||
#define MV64340_SMP_CPU0_DOORBELL_MASK 0x234
|
||||
#define MV64340_SMP_CPU1_DOORBELL_MASK 0x23C
|
||||
#define MV64340_SMP_SEMAPHOR0 0x244
|
||||
#define MV64340_SMP_SEMAPHOR1 0x24c
|
||||
#define MV64340_SMP_SEMAPHOR2 0x254
|
||||
#define MV64340_SMP_SEMAPHOR3 0x25c
|
||||
#define MV64340_SMP_SEMAPHOR4 0x264
|
||||
#define MV64340_SMP_SEMAPHOR5 0x26c
|
||||
#define MV64340_SMP_SEMAPHOR6 0x274
|
||||
#define MV64340_SMP_SEMAPHOR7 0x27c
|
||||
|
||||
/****************************************/
|
||||
/* CPU Sync Barrier Register */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_CPU_0_SYNC_BARRIER_TRIGGER 0x0c0
|
||||
#define MV64340_CPU_0_SYNC_BARRIER_VIRTUAL 0x0c8
|
||||
#define MV64340_CPU_1_SYNC_BARRIER_TRIGGER 0x0d0
|
||||
#define MV64340_CPU_1_SYNC_BARRIER_VIRTUAL 0x0d8
|
||||
|
||||
/****************************************/
|
||||
/* CPU Access Protect */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_CPU_PROTECT_WINDOW_0_BASE_ADDR 0x180
|
||||
#define MV64340_CPU_PROTECT_WINDOW_0_SIZE 0x188
|
||||
#define MV64340_CPU_PROTECT_WINDOW_1_BASE_ADDR 0x190
|
||||
#define MV64340_CPU_PROTECT_WINDOW_1_SIZE 0x198
|
||||
#define MV64340_CPU_PROTECT_WINDOW_2_BASE_ADDR 0x1a0
|
||||
#define MV64340_CPU_PROTECT_WINDOW_2_SIZE 0x1a8
|
||||
#define MV64340_CPU_PROTECT_WINDOW_3_BASE_ADDR 0x1b0
|
||||
#define MV64340_CPU_PROTECT_WINDOW_3_SIZE 0x1b8
|
||||
|
||||
|
||||
/****************************************/
|
||||
/* CPU Error Report */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_CPU_ERROR_ADDR_LOW 0x070
|
||||
#define MV64340_CPU_ERROR_ADDR_HIGH 0x078
|
||||
#define MV64340_CPU_ERROR_DATA_LOW 0x128
|
||||
#define MV64340_CPU_ERROR_DATA_HIGH 0x130
|
||||
#define MV64340_CPU_ERROR_PARITY 0x138
|
||||
#define MV64340_CPU_ERROR_CAUSE 0x140
|
||||
#define MV64340_CPU_ERROR_MASK 0x148
|
||||
|
||||
/****************************************/
|
||||
/* CPU Interface Debug Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_PUNIT_SLAVE_DEBUG_LOW 0x360
|
||||
#define MV64340_PUNIT_SLAVE_DEBUG_HIGH 0x368
|
||||
#define MV64340_PUNIT_MASTER_DEBUG_LOW 0x370
|
||||
#define MV64340_PUNIT_MASTER_DEBUG_HIGH 0x378
|
||||
#define MV64340_PUNIT_MMASK 0x3e4
|
||||
|
||||
/****************************************/
|
||||
/* Integrated SRAM Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_SRAM_CONFIG 0x380
|
||||
#define MV64340_SRAM_TEST_MODE 0X3F4
|
||||
#define MV64340_SRAM_ERROR_CAUSE 0x388
|
||||
#define MV64340_SRAM_ERROR_ADDR 0x390
|
||||
#define MV64340_SRAM_ERROR_ADDR_HIGH 0X3F8
|
||||
#define MV64340_SRAM_ERROR_DATA_LOW 0x398
|
||||
#define MV64340_SRAM_ERROR_DATA_HIGH 0x3a0
|
||||
#define MV64340_SRAM_ERROR_DATA_PARITY 0x3a8
|
||||
|
||||
/****************************************/
|
||||
/* SDRAM Configuration */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_SDRAM_CONFIG 0x1400
|
||||
#define MV64340_D_UNIT_CONTROL_LOW 0x1404
|
||||
#define MV64340_D_UNIT_CONTROL_HIGH 0x1424
|
||||
#define MV64340_SDRAM_TIMING_CONTROL_LOW 0x1408
|
||||
#define MV64340_SDRAM_TIMING_CONTROL_HIGH 0x140c
|
||||
#define MV64340_SDRAM_ADDR_CONTROL 0x1410
|
||||
#define MV64340_SDRAM_OPEN_PAGES_CONTROL 0x1414
|
||||
#define MV64340_SDRAM_OPERATION 0x1418
|
||||
#define MV64340_SDRAM_MODE 0x141c
|
||||
#define MV64340_EXTENDED_DRAM_MODE 0x1420
|
||||
#define MV64340_SDRAM_CROSS_BAR_CONTROL_LOW 0x1430
|
||||
#define MV64340_SDRAM_CROSS_BAR_CONTROL_HIGH 0x1434
|
||||
#define MV64340_SDRAM_CROSS_BAR_TIMEOUT 0x1438
|
||||
#define MV64340_SDRAM_ADDR_CTRL_PADS_CALIBRATION 0x14c0
|
||||
#define MV64340_SDRAM_DATA_PADS_CALIBRATION 0x14c4
|
||||
|
||||
/****************************************/
|
||||
/* SDRAM Error Report */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_SDRAM_ERROR_DATA_LOW 0x1444
|
||||
#define MV64340_SDRAM_ERROR_DATA_HIGH 0x1440
|
||||
#define MV64340_SDRAM_ERROR_ADDR 0x1450
|
||||
#define MV64340_SDRAM_RECEIVED_ECC 0x1448
|
||||
#define MV64340_SDRAM_CALCULATED_ECC 0x144c
|
||||
#define MV64340_SDRAM_ECC_CONTROL 0x1454
|
||||
#define MV64340_SDRAM_ECC_ERROR_COUNTER 0x1458
|
||||
|
||||
/******************************************/
|
||||
/* Controlled Delay Line (CDL) Registers */
|
||||
/******************************************/
|
||||
|
||||
#define MV64340_DFCDL_CONFIG0 0x1480
|
||||
#define MV64340_DFCDL_CONFIG1 0x1484
|
||||
#define MV64340_DLL_WRITE 0x1488
|
||||
#define MV64340_DLL_READ 0x148c
|
||||
#define MV64340_SRAM_ADDR 0x1490
|
||||
#define MV64340_SRAM_DATA0 0x1494
|
||||
#define MV64340_SRAM_DATA1 0x1498
|
||||
#define MV64340_SRAM_DATA2 0x149c
|
||||
#define MV64340_DFCL_PROBE 0x14a0
|
||||
|
||||
/******************************************/
|
||||
/* Debug Registers */
|
||||
/******************************************/
|
||||
|
||||
#define MV64340_DUNIT_DEBUG_LOW 0x1460
|
||||
#define MV64340_DUNIT_DEBUG_HIGH 0x1464
|
||||
#define MV64340_DUNIT_MMASK 0X1b40
|
||||
|
||||
/****************************************/
|
||||
/* Device Parameters */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_DEVICE_BANK0_PARAMETERS 0x45c
|
||||
#define MV64340_DEVICE_BANK1_PARAMETERS 0x460
|
||||
#define MV64340_DEVICE_BANK2_PARAMETERS 0x464
|
||||
#define MV64340_DEVICE_BANK3_PARAMETERS 0x468
|
||||
#define MV64340_DEVICE_BOOT_BANK_PARAMETERS 0x46c
|
||||
#define MV64340_DEVICE_INTERFACE_CONTROL 0x4c0
|
||||
#define MV64340_DEVICE_INTERFACE_CROSS_BAR_CONTROL_LOW 0x4c8
|
||||
#define MV64340_DEVICE_INTERFACE_CROSS_BAR_CONTROL_HIGH 0x4cc
|
||||
#define MV64340_DEVICE_INTERFACE_CROSS_BAR_TIMEOUT 0x4c4
|
||||
|
||||
/****************************************/
|
||||
/* Device interrupt registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_DEVICE_INTERRUPT_CAUSE 0x4d0
|
||||
#define MV64340_DEVICE_INTERRUPT_MASK 0x4d4
|
||||
#define MV64340_DEVICE_ERROR_ADDR 0x4d8
|
||||
#define MV64340_DEVICE_ERROR_DATA 0x4dc
|
||||
#define MV64340_DEVICE_ERROR_PARITY 0x4e0
|
||||
|
||||
/****************************************/
|
||||
/* Device debug registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_DEVICE_DEBUG_LOW 0x4e4
|
||||
#define MV64340_DEVICE_DEBUG_HIGH 0x4e8
|
||||
#define MV64340_RUNIT_MMASK 0x4f0
|
||||
|
||||
/****************************************/
|
||||
/* PCI Slave Address Decoding registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_PCI_0_CS_0_BANK_SIZE 0xc08
|
||||
#define MV64340_PCI_1_CS_0_BANK_SIZE 0xc88
|
||||
#define MV64340_PCI_0_CS_1_BANK_SIZE 0xd08
|
||||
#define MV64340_PCI_1_CS_1_BANK_SIZE 0xd88
|
||||
#define MV64340_PCI_0_CS_2_BANK_SIZE 0xc0c
|
||||
#define MV64340_PCI_1_CS_2_BANK_SIZE 0xc8c
|
||||
#define MV64340_PCI_0_CS_3_BANK_SIZE 0xd0c
|
||||
#define MV64340_PCI_1_CS_3_BANK_SIZE 0xd8c
|
||||
#define MV64340_PCI_0_DEVCS_0_BANK_SIZE 0xc10
|
||||
#define MV64340_PCI_1_DEVCS_0_BANK_SIZE 0xc90
|
||||
#define MV64340_PCI_0_DEVCS_1_BANK_SIZE 0xd10
|
||||
#define MV64340_PCI_1_DEVCS_1_BANK_SIZE 0xd90
|
||||
#define MV64340_PCI_0_DEVCS_2_BANK_SIZE 0xd18
|
||||
#define MV64340_PCI_1_DEVCS_2_BANK_SIZE 0xd98
|
||||
#define MV64340_PCI_0_DEVCS_3_BANK_SIZE 0xc14
|
||||
#define MV64340_PCI_1_DEVCS_3_BANK_SIZE 0xc94
|
||||
#define MV64340_PCI_0_DEVCS_BOOT_BANK_SIZE 0xd14
|
||||
#define MV64340_PCI_1_DEVCS_BOOT_BANK_SIZE 0xd94
|
||||
#define MV64340_PCI_0_P2P_MEM0_BAR_SIZE 0xd1c
|
||||
#define MV64340_PCI_1_P2P_MEM0_BAR_SIZE 0xd9c
|
||||
#define MV64340_PCI_0_P2P_MEM1_BAR_SIZE 0xd20
|
||||
#define MV64340_PCI_1_P2P_MEM1_BAR_SIZE 0xda0
|
||||
#define MV64340_PCI_0_P2P_I_O_BAR_SIZE 0xd24
|
||||
#define MV64340_PCI_1_P2P_I_O_BAR_SIZE 0xda4
|
||||
#define MV64340_PCI_0_CPU_BAR_SIZE 0xd28
|
||||
#define MV64340_PCI_1_CPU_BAR_SIZE 0xda8
|
||||
#define MV64340_PCI_0_INTERNAL_SRAM_BAR_SIZE 0xe00
|
||||
#define MV64340_PCI_1_INTERNAL_SRAM_BAR_SIZE 0xe80
|
||||
#define MV64340_PCI_0_EXPANSION_ROM_BAR_SIZE 0xd2c
|
||||
#define MV64340_PCI_1_EXPANSION_ROM_BAR_SIZE 0xd9c
|
||||
#define MV64340_PCI_0_BASE_ADDR_REG_ENABLE 0xc3c
|
||||
#define MV64340_PCI_1_BASE_ADDR_REG_ENABLE 0xcbc
|
||||
#define MV64340_PCI_0_CS_0_BASE_ADDR_REMAP 0xc48
|
||||
#define MV64340_PCI_1_CS_0_BASE_ADDR_REMAP 0xcc8
|
||||
#define MV64340_PCI_0_CS_1_BASE_ADDR_REMAP 0xd48
|
||||
#define MV64340_PCI_1_CS_1_BASE_ADDR_REMAP 0xdc8
|
||||
#define MV64340_PCI_0_CS_2_BASE_ADDR_REMAP 0xc4c
|
||||
#define MV64340_PCI_1_CS_2_BASE_ADDR_REMAP 0xccc
|
||||
#define MV64340_PCI_0_CS_3_BASE_ADDR_REMAP 0xd4c
|
||||
#define MV64340_PCI_1_CS_3_BASE_ADDR_REMAP 0xdcc
|
||||
#define MV64340_PCI_0_CS_0_BASE_HIGH_ADDR_REMAP 0xF04
|
||||
#define MV64340_PCI_1_CS_0_BASE_HIGH_ADDR_REMAP 0xF84
|
||||
#define MV64340_PCI_0_CS_1_BASE_HIGH_ADDR_REMAP 0xF08
|
||||
#define MV64340_PCI_1_CS_1_BASE_HIGH_ADDR_REMAP 0xF88
|
||||
#define MV64340_PCI_0_CS_2_BASE_HIGH_ADDR_REMAP 0xF0C
|
||||
#define MV64340_PCI_1_CS_2_BASE_HIGH_ADDR_REMAP 0xF8C
|
||||
#define MV64340_PCI_0_CS_3_BASE_HIGH_ADDR_REMAP 0xF10
|
||||
#define MV64340_PCI_1_CS_3_BASE_HIGH_ADDR_REMAP 0xF90
|
||||
#define MV64340_PCI_0_DEVCS_0_BASE_ADDR_REMAP 0xc50
|
||||
#define MV64340_PCI_1_DEVCS_0_BASE_ADDR_REMAP 0xcd0
|
||||
#define MV64340_PCI_0_DEVCS_1_BASE_ADDR_REMAP 0xd50
|
||||
#define MV64340_PCI_1_DEVCS_1_BASE_ADDR_REMAP 0xdd0
|
||||
#define MV64340_PCI_0_DEVCS_2_BASE_ADDR_REMAP 0xd58
|
||||
#define MV64340_PCI_1_DEVCS_2_BASE_ADDR_REMAP 0xdd8
|
||||
#define MV64340_PCI_0_DEVCS_3_BASE_ADDR_REMAP 0xc54
|
||||
#define MV64340_PCI_1_DEVCS_3_BASE_ADDR_REMAP 0xcd4
|
||||
#define MV64340_PCI_0_DEVCS_BOOTCS_BASE_ADDR_REMAP 0xd54
|
||||
#define MV64340_PCI_1_DEVCS_BOOTCS_BASE_ADDR_REMAP 0xdd4
|
||||
#define MV64340_PCI_0_P2P_MEM0_BASE_ADDR_REMAP_LOW 0xd5c
|
||||
#define MV64340_PCI_1_P2P_MEM0_BASE_ADDR_REMAP_LOW 0xddc
|
||||
#define MV64340_PCI_0_P2P_MEM0_BASE_ADDR_REMAP_HIGH 0xd60
|
||||
#define MV64340_PCI_1_P2P_MEM0_BASE_ADDR_REMAP_HIGH 0xde0
|
||||
#define MV64340_PCI_0_P2P_MEM1_BASE_ADDR_REMAP_LOW 0xd64
|
||||
#define MV64340_PCI_1_P2P_MEM1_BASE_ADDR_REMAP_LOW 0xde4
|
||||
#define MV64340_PCI_0_P2P_MEM1_BASE_ADDR_REMAP_HIGH 0xd68
|
||||
#define MV64340_PCI_1_P2P_MEM1_BASE_ADDR_REMAP_HIGH 0xde8
|
||||
#define MV64340_PCI_0_P2P_I_O_BASE_ADDR_REMAP 0xd6c
|
||||
#define MV64340_PCI_1_P2P_I_O_BASE_ADDR_REMAP 0xdec
|
||||
#define MV64340_PCI_0_CPU_BASE_ADDR_REMAP_LOW 0xd70
|
||||
#define MV64340_PCI_1_CPU_BASE_ADDR_REMAP_LOW 0xdf0
|
||||
#define MV64340_PCI_0_CPU_BASE_ADDR_REMAP_HIGH 0xd74
|
||||
#define MV64340_PCI_1_CPU_BASE_ADDR_REMAP_HIGH 0xdf4
|
||||
#define MV64340_PCI_0_INTEGRATED_SRAM_BASE_ADDR_REMAP 0xf00
|
||||
#define MV64340_PCI_1_INTEGRATED_SRAM_BASE_ADDR_REMAP 0xf80
|
||||
#define MV64340_PCI_0_EXPANSION_ROM_BASE_ADDR_REMAP 0xf38
|
||||
#define MV64340_PCI_1_EXPANSION_ROM_BASE_ADDR_REMAP 0xfb8
|
||||
#define MV64340_PCI_0_ADDR_DECODE_CONTROL 0xd3c
|
||||
#define MV64340_PCI_1_ADDR_DECODE_CONTROL 0xdbc
|
||||
#define MV64340_PCI_0_HEADERS_RETARGET_CONTROL 0xF40
|
||||
#define MV64340_PCI_1_HEADERS_RETARGET_CONTROL 0xFc0
|
||||
#define MV64340_PCI_0_HEADERS_RETARGET_BASE 0xF44
|
||||
#define MV64340_PCI_1_HEADERS_RETARGET_BASE 0xFc4
|
||||
#define MV64340_PCI_0_HEADERS_RETARGET_HIGH 0xF48
|
||||
#define MV64340_PCI_1_HEADERS_RETARGET_HIGH 0xFc8
|
||||
|
||||
/***********************************/
|
||||
/* PCI Control Register Map */
|
||||
/***********************************/
|
||||
|
||||
#define MV64340_PCI_0_DLL_STATUS_AND_COMMAND 0x1d20
|
||||
#define MV64340_PCI_1_DLL_STATUS_AND_COMMAND 0x1da0
|
||||
#define MV64340_PCI_0_MPP_PADS_DRIVE_CONTROL 0x1d1C
|
||||
#define MV64340_PCI_1_MPP_PADS_DRIVE_CONTROL 0x1d9C
|
||||
#define MV64340_PCI_0_COMMAND 0xc00
|
||||
#define MV64340_PCI_1_COMMAND 0xc80
|
||||
#define MV64340_PCI_0_MODE 0xd00
|
||||
#define MV64340_PCI_1_MODE 0xd80
|
||||
#define MV64340_PCI_0_RETRY 0xc04
|
||||
#define MV64340_PCI_1_RETRY 0xc84
|
||||
#define MV64340_PCI_0_READ_BUFFER_DISCARD_TIMER 0xd04
|
||||
#define MV64340_PCI_1_READ_BUFFER_DISCARD_TIMER 0xd84
|
||||
#define MV64340_PCI_0_MSI_TRIGGER_TIMER 0xc38
|
||||
#define MV64340_PCI_1_MSI_TRIGGER_TIMER 0xcb8
|
||||
#define MV64340_PCI_0_ARBITER_CONTROL 0x1d00
|
||||
#define MV64340_PCI_1_ARBITER_CONTROL 0x1d80
|
||||
#define MV64340_PCI_0_CROSS_BAR_CONTROL_LOW 0x1d08
|
||||
#define MV64340_PCI_1_CROSS_BAR_CONTROL_LOW 0x1d88
|
||||
#define MV64340_PCI_0_CROSS_BAR_CONTROL_HIGH 0x1d0c
|
||||
#define MV64340_PCI_1_CROSS_BAR_CONTROL_HIGH 0x1d8c
|
||||
#define MV64340_PCI_0_CROSS_BAR_TIMEOUT 0x1d04
|
||||
#define MV64340_PCI_1_CROSS_BAR_TIMEOUT 0x1d84
|
||||
#define MV64340_PCI_0_SYNC_BARRIER_TRIGGER_REG 0x1D18
|
||||
#define MV64340_PCI_1_SYNC_BARRIER_TRIGGER_REG 0x1D98
|
||||
#define MV64340_PCI_0_SYNC_BARRIER_VIRTUAL_REG 0x1d10
|
||||
#define MV64340_PCI_1_SYNC_BARRIER_VIRTUAL_REG 0x1d90
|
||||
#define MV64340_PCI_0_P2P_CONFIG 0x1d14
|
||||
#define MV64340_PCI_1_P2P_CONFIG 0x1d94
|
||||
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_0_LOW 0x1e00
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_0_HIGH 0x1e04
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_0 0x1e08
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_1_LOW 0x1e10
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_1_HIGH 0x1e14
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_1 0x1e18
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_2_LOW 0x1e20
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_2_HIGH 0x1e24
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_2 0x1e28
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_3_LOW 0x1e30
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_3_HIGH 0x1e34
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_3 0x1e38
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_4_LOW 0x1e40
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_4_HIGH 0x1e44
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_4 0x1e48
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_5_LOW 0x1e50
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_BASE_5_HIGH 0x1e54
|
||||
#define MV64340_PCI_0_ACCESS_CONTROL_SIZE_5 0x1e58
|
||||
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_0_LOW 0x1e80
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_0_HIGH 0x1e84
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_0 0x1e88
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_1_LOW 0x1e90
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_1_HIGH 0x1e94
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_1 0x1e98
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_2_LOW 0x1ea0
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_2_HIGH 0x1ea4
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_2 0x1ea8
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_3_LOW 0x1eb0
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_3_HIGH 0x1eb4
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_3 0x1eb8
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_4_LOW 0x1ec0
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_4_HIGH 0x1ec4
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_4 0x1ec8
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_5_LOW 0x1ed0
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_BASE_5_HIGH 0x1ed4
|
||||
#define MV64340_PCI_1_ACCESS_CONTROL_SIZE_5 0x1ed8
|
||||
|
||||
/****************************************/
|
||||
/* PCI Configuration Access Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_PCI_0_CONFIG_ADDR 0xcf8
|
||||
#define MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG 0xcfc
|
||||
#define MV64340_PCI_1_CONFIG_ADDR 0xc78
|
||||
#define MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG 0xc7c
|
||||
#define MV64340_PCI_0_INTERRUPT_ACKNOWLEDGE_VIRTUAL_REG 0xc34
|
||||
#define MV64340_PCI_1_INTERRUPT_ACKNOWLEDGE_VIRTUAL_REG 0xcb4
|
||||
|
||||
/****************************************/
|
||||
/* PCI Error Report Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_PCI_0_SERR_MASK 0xc28
|
||||
#define MV64340_PCI_1_SERR_MASK 0xca8
|
||||
#define MV64340_PCI_0_ERROR_ADDR_LOW 0x1d40
|
||||
#define MV64340_PCI_1_ERROR_ADDR_LOW 0x1dc0
|
||||
#define MV64340_PCI_0_ERROR_ADDR_HIGH 0x1d44
|
||||
#define MV64340_PCI_1_ERROR_ADDR_HIGH 0x1dc4
|
||||
#define MV64340_PCI_0_ERROR_ATTRIBUTE 0x1d48
|
||||
#define MV64340_PCI_1_ERROR_ATTRIBUTE 0x1dc8
|
||||
#define MV64340_PCI_0_ERROR_COMMAND 0x1d50
|
||||
#define MV64340_PCI_1_ERROR_COMMAND 0x1dd0
|
||||
#define MV64340_PCI_0_ERROR_CAUSE 0x1d58
|
||||
#define MV64340_PCI_1_ERROR_CAUSE 0x1dd8
|
||||
#define MV64340_PCI_0_ERROR_MASK 0x1d5c
|
||||
#define MV64340_PCI_1_ERROR_MASK 0x1ddc
|
||||
|
||||
/****************************************/
|
||||
/* PCI Debug Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_PCI_0_MMASK 0X1D24
|
||||
#define MV64340_PCI_1_MMASK 0X1DA4
|
||||
|
||||
/*********************************************/
|
||||
/* PCI Configuration, Function 0, Registers */
|
||||
/*********************************************/
|
||||
|
||||
#define MV64340_PCI_DEVICE_AND_VENDOR_ID 0x000
|
||||
#define MV64340_PCI_STATUS_AND_COMMAND 0x004
|
||||
#define MV64340_PCI_CLASS_CODE_AND_REVISION_ID 0x008
|
||||
#define MV64340_PCI_BIST_HEADER_TYPE_LATENCY_TIMER_CACHE_LINE 0x00C
|
||||
|
||||
#define MV64340_PCI_SCS_0_BASE_ADDR_LOW 0x010
|
||||
#define MV64340_PCI_SCS_0_BASE_ADDR_HIGH 0x014
|
||||
#define MV64340_PCI_SCS_1_BASE_ADDR_LOW 0x018
|
||||
#define MV64340_PCI_SCS_1_BASE_ADDR_HIGH 0x01C
|
||||
#define MV64340_PCI_INTERNAL_REG_MEM_MAPPED_BASE_ADDR_LOW 0x020
|
||||
#define MV64340_PCI_INTERNAL_REG_MEM_MAPPED_BASE_ADDR_HIGH 0x024
|
||||
#define MV64340_PCI_SUBSYSTEM_ID_AND_SUBSYSTEM_VENDOR_ID 0x02c
|
||||
#define MV64340_PCI_EXPANSION_ROM_BASE_ADDR_REG 0x030
|
||||
#define MV64340_PCI_CAPABILTY_LIST_POINTER 0x034
|
||||
#define MV64340_PCI_INTERRUPT_PIN_AND_LINE 0x03C
|
||||
/* capability list */
|
||||
#define MV64340_PCI_POWER_MANAGEMENT_CAPABILITY 0x040
|
||||
#define MV64340_PCI_POWER_MANAGEMENT_STATUS_AND_CONTROL 0x044
|
||||
#define MV64340_PCI_VPD_ADDR 0x048
|
||||
#define MV64340_PCI_VPD_DATA 0x04c
|
||||
#define MV64340_PCI_MSI_MESSAGE_CONTROL 0x050
|
||||
#define MV64340_PCI_MSI_MESSAGE_ADDR 0x054
|
||||
#define MV64340_PCI_MSI_MESSAGE_UPPER_ADDR 0x058
|
||||
#define MV64340_PCI_MSI_MESSAGE_DATA 0x05c
|
||||
#define MV64340_PCI_X_COMMAND 0x060
|
||||
#define MV64340_PCI_X_STATUS 0x064
|
||||
#define MV64340_PCI_COMPACT_PCI_HOT_SWAP 0x068
|
||||
|
||||
/***********************************************/
|
||||
/* PCI Configuration, Function 1, Registers */
|
||||
/***********************************************/
|
||||
|
||||
#define MV64340_PCI_SCS_2_BASE_ADDR_LOW 0x110
|
||||
#define MV64340_PCI_SCS_2_BASE_ADDR_HIGH 0x114
|
||||
#define MV64340_PCI_SCS_3_BASE_ADDR_LOW 0x118
|
||||
#define MV64340_PCI_SCS_3_BASE_ADDR_HIGH 0x11c
|
||||
#define MV64340_PCI_INTERNAL_SRAM_BASE_ADDR_LOW 0x120
|
||||
#define MV64340_PCI_INTERNAL_SRAM_BASE_ADDR_HIGH 0x124
|
||||
|
||||
/***********************************************/
|
||||
/* PCI Configuration, Function 2, Registers */
|
||||
/***********************************************/
|
||||
|
||||
#define MV64340_PCI_DEVCS_0_BASE_ADDR_LOW 0x210
|
||||
#define MV64340_PCI_DEVCS_0_BASE_ADDR_HIGH 0x214
|
||||
#define MV64340_PCI_DEVCS_1_BASE_ADDR_LOW 0x218
|
||||
#define MV64340_PCI_DEVCS_1_BASE_ADDR_HIGH 0x21c
|
||||
#define MV64340_PCI_DEVCS_2_BASE_ADDR_LOW 0x220
|
||||
#define MV64340_PCI_DEVCS_2_BASE_ADDR_HIGH 0x224
|
||||
|
||||
/***********************************************/
|
||||
/* PCI Configuration, Function 3, Registers */
|
||||
/***********************************************/
|
||||
|
||||
#define MV64340_PCI_DEVCS_3_BASE_ADDR_LOW 0x310
|
||||
#define MV64340_PCI_DEVCS_3_BASE_ADDR_HIGH 0x314
|
||||
#define MV64340_PCI_BOOT_CS_BASE_ADDR_LOW 0x318
|
||||
#define MV64340_PCI_BOOT_CS_BASE_ADDR_HIGH 0x31c
|
||||
#define MV64340_PCI_CPU_BASE_ADDR_LOW 0x220
|
||||
#define MV64340_PCI_CPU_BASE_ADDR_HIGH 0x224
|
||||
|
||||
/***********************************************/
|
||||
/* PCI Configuration, Function 4, Registers */
|
||||
/***********************************************/
|
||||
|
||||
#define MV64340_PCI_P2P_MEM0_BASE_ADDR_LOW 0x410
|
||||
#define MV64340_PCI_P2P_MEM0_BASE_ADDR_HIGH 0x414
|
||||
#define MV64340_PCI_P2P_MEM1_BASE_ADDR_LOW 0x418
|
||||
#define MV64340_PCI_P2P_MEM1_BASE_ADDR_HIGH 0x41c
|
||||
#define MV64340_PCI_P2P_I_O_BASE_ADDR 0x420
|
||||
#define MV64340_PCI_INTERNAL_REGS_I_O_MAPPED_BASE_ADDR 0x424
|
||||
|
||||
/****************************************/
|
||||
/* Messaging Unit Registers (I20) */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG0_PCI_0_SIDE 0x010
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG1_PCI_0_SIDE 0x014
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_PCI_0_SIDE 0x018
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_PCI_0_SIDE 0x01C
|
||||
#define MV64340_I2O_INBOUND_DOORBELL_REG_PCI_0_SIDE 0x020
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_PCI_0_SIDE 0x024
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_PCI_0_SIDE 0x028
|
||||
#define MV64340_I2O_OUTBOUND_DOORBELL_REG_PCI_0_SIDE 0x02C
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_PCI_0_SIDE 0x030
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_PCI_0_SIDE 0x034
|
||||
#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_0_SIDE 0x040
|
||||
#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_0_SIDE 0x044
|
||||
#define MV64340_I2O_QUEUE_CONTROL_REG_PCI_0_SIDE 0x050
|
||||
#define MV64340_I2O_QUEUE_BASE_ADDR_REG_PCI_0_SIDE 0x054
|
||||
#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_PCI_0_SIDE 0x060
|
||||
#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_PCI_0_SIDE 0x064
|
||||
#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_PCI_0_SIDE 0x068
|
||||
#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_PCI_0_SIDE 0x06C
|
||||
#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_PCI_0_SIDE 0x070
|
||||
#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_PCI_0_SIDE 0x074
|
||||
#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_PCI_0_SIDE 0x0F8
|
||||
#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_PCI_0_SIDE 0x0FC
|
||||
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG0_PCI_1_SIDE 0x090
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG1_PCI_1_SIDE 0x094
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_PCI_1_SIDE 0x098
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_PCI_1_SIDE 0x09C
|
||||
#define MV64340_I2O_INBOUND_DOORBELL_REG_PCI_1_SIDE 0x0A0
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_PCI_1_SIDE 0x0A4
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_PCI_1_SIDE 0x0A8
|
||||
#define MV64340_I2O_OUTBOUND_DOORBELL_REG_PCI_1_SIDE 0x0AC
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_PCI_1_SIDE 0x0B0
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_PCI_1_SIDE 0x0B4
|
||||
#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_1_SIDE 0x0C0
|
||||
#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_PCI_1_SIDE 0x0C4
|
||||
#define MV64340_I2O_QUEUE_CONTROL_REG_PCI_1_SIDE 0x0D0
|
||||
#define MV64340_I2O_QUEUE_BASE_ADDR_REG_PCI_1_SIDE 0x0D4
|
||||
#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_PCI_1_SIDE 0x0E0
|
||||
#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_PCI_1_SIDE 0x0E4
|
||||
#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_PCI_1_SIDE 0x0E8
|
||||
#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_PCI_1_SIDE 0x0EC
|
||||
#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_PCI_1_SIDE 0x0F0
|
||||
#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_PCI_1_SIDE 0x0F4
|
||||
#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_PCI_1_SIDE 0x078
|
||||
#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_PCI_1_SIDE 0x07C
|
||||
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG0_CPU0_SIDE 0x1C10
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG1_CPU0_SIDE 0x1C14
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_CPU0_SIDE 0x1C18
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_CPU0_SIDE 0x1C1C
|
||||
#define MV64340_I2O_INBOUND_DOORBELL_REG_CPU0_SIDE 0x1C20
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_CPU0_SIDE 0x1C24
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_CPU0_SIDE 0x1C28
|
||||
#define MV64340_I2O_OUTBOUND_DOORBELL_REG_CPU0_SIDE 0x1C2C
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_CPU0_SIDE 0x1C30
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_CPU0_SIDE 0x1C34
|
||||
#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_CPU0_SIDE 0x1C40
|
||||
#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_CPU0_SIDE 0x1C44
|
||||
#define MV64340_I2O_QUEUE_CONTROL_REG_CPU0_SIDE 0x1C50
|
||||
#define MV64340_I2O_QUEUE_BASE_ADDR_REG_CPU0_SIDE 0x1C54
|
||||
#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_CPU0_SIDE 0x1C60
|
||||
#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_CPU0_SIDE 0x1C64
|
||||
#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_CPU0_SIDE 0x1C68
|
||||
#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_CPU0_SIDE 0x1C6C
|
||||
#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_CPU0_SIDE 0x1C70
|
||||
#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_CPU0_SIDE 0x1C74
|
||||
#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_CPU0_SIDE 0x1CF8
|
||||
#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_CPU0_SIDE 0x1CFC
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG0_CPU1_SIDE 0x1C90
|
||||
#define MV64340_I2O_INBOUND_MESSAGE_REG1_CPU1_SIDE 0x1C94
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG0_CPU1_SIDE 0x1C98
|
||||
#define MV64340_I2O_OUTBOUND_MESSAGE_REG1_CPU1_SIDE 0x1C9C
|
||||
#define MV64340_I2O_INBOUND_DOORBELL_REG_CPU1_SIDE 0x1CA0
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_CAUSE_REG_CPU1_SIDE 0x1CA4
|
||||
#define MV64340_I2O_INBOUND_INTERRUPT_MASK_REG_CPU1_SIDE 0x1CA8
|
||||
#define MV64340_I2O_OUTBOUND_DOORBELL_REG_CPU1_SIDE 0x1CAC
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_CAUSE_REG_CPU1_SIDE 0x1CB0
|
||||
#define MV64340_I2O_OUTBOUND_INTERRUPT_MASK_REG_CPU1_SIDE 0x1CB4
|
||||
#define MV64340_I2O_INBOUND_QUEUE_PORT_VIRTUAL_REG_CPU1_SIDE 0x1CC0
|
||||
#define MV64340_I2O_OUTBOUND_QUEUE_PORT_VIRTUAL_REG_CPU1_SIDE 0x1CC4
|
||||
#define MV64340_I2O_QUEUE_CONTROL_REG_CPU1_SIDE 0x1CD0
|
||||
#define MV64340_I2O_QUEUE_BASE_ADDR_REG_CPU1_SIDE 0x1CD4
|
||||
#define MV64340_I2O_INBOUND_FREE_HEAD_POINTER_REG_CPU1_SIDE 0x1CE0
|
||||
#define MV64340_I2O_INBOUND_FREE_TAIL_POINTER_REG_CPU1_SIDE 0x1CE4
|
||||
#define MV64340_I2O_INBOUND_POST_HEAD_POINTER_REG_CPU1_SIDE 0x1CE8
|
||||
#define MV64340_I2O_INBOUND_POST_TAIL_POINTER_REG_CPU1_SIDE 0x1CEC
|
||||
#define MV64340_I2O_OUTBOUND_FREE_HEAD_POINTER_REG_CPU1_SIDE 0x1CF0
|
||||
#define MV64340_I2O_OUTBOUND_FREE_TAIL_POINTER_REG_CPU1_SIDE 0x1CF4
|
||||
#define MV64340_I2O_OUTBOUND_POST_HEAD_POINTER_REG_CPU1_SIDE 0x1C78
|
||||
#define MV64340_I2O_OUTBOUND_POST_TAIL_POINTER_REG_CPU1_SIDE 0x1C7C
|
||||
|
||||
/****************************************/
|
||||
/* Ethernet Unit Registers */
|
||||
/****************************************/
|
||||
|
||||
/*******************************************/
|
||||
/* CUNIT Registers */
|
||||
/*******************************************/
|
||||
|
||||
/* Address Decoding Register Map */
|
||||
|
||||
#define MV64340_CUNIT_BASE_ADDR_REG0 0xf200
|
||||
#define MV64340_CUNIT_BASE_ADDR_REG1 0xf208
|
||||
#define MV64340_CUNIT_BASE_ADDR_REG2 0xf210
|
||||
#define MV64340_CUNIT_BASE_ADDR_REG3 0xf218
|
||||
#define MV64340_CUNIT_SIZE0 0xf204
|
||||
#define MV64340_CUNIT_SIZE1 0xf20c
|
||||
#define MV64340_CUNIT_SIZE2 0xf214
|
||||
#define MV64340_CUNIT_SIZE3 0xf21c
|
||||
#define MV64340_CUNIT_HIGH_ADDR_REMAP_REG0 0xf240
|
||||
#define MV64340_CUNIT_HIGH_ADDR_REMAP_REG1 0xf244
|
||||
#define MV64340_CUNIT_BASE_ADDR_ENABLE_REG 0xf250
|
||||
#define MV64340_MPSC0_ACCESS_PROTECTION_REG 0xf254
|
||||
#define MV64340_MPSC1_ACCESS_PROTECTION_REG 0xf258
|
||||
#define MV64340_CUNIT_INTERNAL_SPACE_BASE_ADDR_REG 0xf25C
|
||||
|
||||
/* Error Report Registers */
|
||||
|
||||
#define MV64340_CUNIT_INTERRUPT_CAUSE_REG 0xf310
|
||||
#define MV64340_CUNIT_INTERRUPT_MASK_REG 0xf314
|
||||
#define MV64340_CUNIT_ERROR_ADDR 0xf318
|
||||
|
||||
/* Cunit Control Registers */
|
||||
|
||||
#define MV64340_CUNIT_ARBITER_CONTROL_REG 0xf300
|
||||
#define MV64340_CUNIT_CONFIG_REG 0xb40c
|
||||
#define MV64340_CUNIT_CRROSBAR_TIMEOUT_REG 0xf304
|
||||
|
||||
/* Cunit Debug Registers */
|
||||
|
||||
#define MV64340_CUNIT_DEBUG_LOW 0xf340
|
||||
#define MV64340_CUNIT_DEBUG_HIGH 0xf344
|
||||
#define MV64340_CUNIT_MMASK 0xf380
|
||||
|
||||
/* MPSCs Clocks Routing Registers */
|
||||
|
||||
#define MV64340_MPSC_ROUTING_REG 0xb400
|
||||
#define MV64340_MPSC_RX_CLOCK_ROUTING_REG 0xb404
|
||||
#define MV64340_MPSC_TX_CLOCK_ROUTING_REG 0xb408
|
||||
|
||||
/* MPSCs Interrupts Registers */
|
||||
|
||||
#define MV64340_MPSC_CAUSE_REG(port) (0xb804 + (port << 3))
|
||||
#define MV64340_MPSC_MASK_REG(port) (0xb884 + (port << 3))
|
||||
|
||||
#define MV64340_MPSC_MAIN_CONFIG_LOW(port) (0x8000 + (port << 12))
|
||||
#define MV64340_MPSC_MAIN_CONFIG_HIGH(port) (0x8004 + (port << 12))
|
||||
#define MV64340_MPSC_PROTOCOL_CONFIG(port) (0x8008 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG1(port) (0x800c + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG2(port) (0x8010 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG3(port) (0x8014 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG4(port) (0x8018 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG5(port) (0x801c + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG6(port) (0x8020 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG7(port) (0x8024 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG8(port) (0x8028 + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG9(port) (0x802c + (port << 12))
|
||||
#define MV64340_MPSC_CHANNEL_REG10(port) (0x8030 + (port << 12))
|
||||
|
||||
/* MPSC0 Registers */
|
||||
|
||||
|
||||
/***************************************/
|
||||
/* SDMA Registers */
|
||||
/***************************************/
|
||||
|
||||
#define MV64340_SDMA_CONFIG_REG(channel) (0x4000 + (channel << 13))
|
||||
#define MV64340_SDMA_COMMAND_REG(channel) (0x4008 + (channel << 13))
|
||||
#define MV64340_SDMA_CURRENT_RX_DESCRIPTOR_POINTER(channel) (0x4810 + (channel << 13))
|
||||
#define MV64340_SDMA_CURRENT_TX_DESCRIPTOR_POINTER(channel) (0x4c10 + (channel << 13))
|
||||
#define MV64340_SDMA_FIRST_TX_DESCRIPTOR_POINTER(channel) (0x4c14 + (channel << 13))
|
||||
|
||||
#define MV64340_SDMA_CAUSE_REG 0xb800
|
||||
#define MV64340_SDMA_MASK_REG 0xb880
|
||||
|
||||
/* BRG Interrupts */
|
||||
|
||||
#define MV64340_BRG_CONFIG_REG(brg) (0xb200 + (brg << 3))
|
||||
#define MV64340_BRG_BAUDE_TUNING_REG(brg) (0xb208 + (brg << 3))
|
||||
#define MV64340_BRG_CAUSE_REG 0xb834
|
||||
#define MV64340_BRG_MASK_REG 0xb8b4
|
||||
|
||||
/****************************************/
|
||||
/* DMA Channel Control */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_DMA_CHANNEL0_CONTROL 0x840
|
||||
#define MV64340_DMA_CHANNEL0_CONTROL_HIGH 0x880
|
||||
#define MV64340_DMA_CHANNEL1_CONTROL 0x844
|
||||
#define MV64340_DMA_CHANNEL1_CONTROL_HIGH 0x884
|
||||
#define MV64340_DMA_CHANNEL2_CONTROL 0x848
|
||||
#define MV64340_DMA_CHANNEL2_CONTROL_HIGH 0x888
|
||||
#define MV64340_DMA_CHANNEL3_CONTROL 0x84C
|
||||
#define MV64340_DMA_CHANNEL3_CONTROL_HIGH 0x88C
|
||||
|
||||
|
||||
/****************************************/
|
||||
/* IDMA Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_DMA_CHANNEL0_BYTE_COUNT 0x800
|
||||
#define MV64340_DMA_CHANNEL1_BYTE_COUNT 0x804
|
||||
#define MV64340_DMA_CHANNEL2_BYTE_COUNT 0x808
|
||||
#define MV64340_DMA_CHANNEL3_BYTE_COUNT 0x80C
|
||||
#define MV64340_DMA_CHANNEL0_SOURCE_ADDR 0x810
|
||||
#define MV64340_DMA_CHANNEL1_SOURCE_ADDR 0x814
|
||||
#define MV64340_DMA_CHANNEL2_SOURCE_ADDR 0x818
|
||||
#define MV64340_DMA_CHANNEL3_SOURCE_ADDR 0x81c
|
||||
#define MV64340_DMA_CHANNEL0_DESTINATION_ADDR 0x820
|
||||
#define MV64340_DMA_CHANNEL1_DESTINATION_ADDR 0x824
|
||||
#define MV64340_DMA_CHANNEL2_DESTINATION_ADDR 0x828
|
||||
#define MV64340_DMA_CHANNEL3_DESTINATION_ADDR 0x82C
|
||||
#define MV64340_DMA_CHANNEL0_NEXT_DESCRIPTOR_POINTER 0x830
|
||||
#define MV64340_DMA_CHANNEL1_NEXT_DESCRIPTOR_POINTER 0x834
|
||||
#define MV64340_DMA_CHANNEL2_NEXT_DESCRIPTOR_POINTER 0x838
|
||||
#define MV64340_DMA_CHANNEL3_NEXT_DESCRIPTOR_POINTER 0x83C
|
||||
#define MV64340_DMA_CHANNEL0_CURRENT_DESCRIPTOR_POINTER 0x870
|
||||
#define MV64340_DMA_CHANNEL1_CURRENT_DESCRIPTOR_POINTER 0x874
|
||||
#define MV64340_DMA_CHANNEL2_CURRENT_DESCRIPTOR_POINTER 0x878
|
||||
#define MV64340_DMA_CHANNEL3_CURRENT_DESCRIPTOR_POINTER 0x87C
|
||||
|
||||
/* IDMA Address Decoding Base Address Registers */
|
||||
|
||||
#define MV64340_DMA_BASE_ADDR_REG0 0xa00
|
||||
#define MV64340_DMA_BASE_ADDR_REG1 0xa08
|
||||
#define MV64340_DMA_BASE_ADDR_REG2 0xa10
|
||||
#define MV64340_DMA_BASE_ADDR_REG3 0xa18
|
||||
#define MV64340_DMA_BASE_ADDR_REG4 0xa20
|
||||
#define MV64340_DMA_BASE_ADDR_REG5 0xa28
|
||||
#define MV64340_DMA_BASE_ADDR_REG6 0xa30
|
||||
#define MV64340_DMA_BASE_ADDR_REG7 0xa38
|
||||
|
||||
/* IDMA Address Decoding Size Address Register */
|
||||
|
||||
#define MV64340_DMA_SIZE_REG0 0xa04
|
||||
#define MV64340_DMA_SIZE_REG1 0xa0c
|
||||
#define MV64340_DMA_SIZE_REG2 0xa14
|
||||
#define MV64340_DMA_SIZE_REG3 0xa1c
|
||||
#define MV64340_DMA_SIZE_REG4 0xa24
|
||||
#define MV64340_DMA_SIZE_REG5 0xa2c
|
||||
#define MV64340_DMA_SIZE_REG6 0xa34
|
||||
#define MV64340_DMA_SIZE_REG7 0xa3C
|
||||
|
||||
/* IDMA Address Decoding High Address Remap and Access Protection Registers */
|
||||
|
||||
#define MV64340_DMA_HIGH_ADDR_REMAP_REG0 0xa60
|
||||
#define MV64340_DMA_HIGH_ADDR_REMAP_REG1 0xa64
|
||||
#define MV64340_DMA_HIGH_ADDR_REMAP_REG2 0xa68
|
||||
#define MV64340_DMA_HIGH_ADDR_REMAP_REG3 0xa6C
|
||||
#define MV64340_DMA_BASE_ADDR_ENABLE_REG 0xa80
|
||||
#define MV64340_DMA_CHANNEL0_ACCESS_PROTECTION_REG 0xa70
|
||||
#define MV64340_DMA_CHANNEL1_ACCESS_PROTECTION_REG 0xa74
|
||||
#define MV64340_DMA_CHANNEL2_ACCESS_PROTECTION_REG 0xa78
|
||||
#define MV64340_DMA_CHANNEL3_ACCESS_PROTECTION_REG 0xa7c
|
||||
#define MV64340_DMA_ARBITER_CONTROL 0x860
|
||||
#define MV64340_DMA_CROSS_BAR_TIMEOUT 0x8d0
|
||||
|
||||
/* IDMA Headers Retarget Registers */
|
||||
|
||||
#define MV64340_DMA_HEADERS_RETARGET_CONTROL 0xa84
|
||||
#define MV64340_DMA_HEADERS_RETARGET_BASE 0xa88
|
||||
|
||||
/* IDMA Interrupt Register */
|
||||
|
||||
#define MV64340_DMA_INTERRUPT_CAUSE_REG 0x8c0
|
||||
#define MV64340_DMA_INTERRUPT_CAUSE_MASK 0x8c4
|
||||
#define MV64340_DMA_ERROR_ADDR 0x8c8
|
||||
#define MV64340_DMA_ERROR_SELECT 0x8cc
|
||||
|
||||
/* IDMA Debug Register ( for internal use ) */
|
||||
|
||||
#define MV64340_DMA_DEBUG_LOW 0x8e0
|
||||
#define MV64340_DMA_DEBUG_HIGH 0x8e4
|
||||
#define MV64340_DMA_SPARE 0xA8C
|
||||
|
||||
/****************************************/
|
||||
/* Timer_Counter */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_TIMER_COUNTER0 0x850
|
||||
#define MV64340_TIMER_COUNTER1 0x854
|
||||
#define MV64340_TIMER_COUNTER2 0x858
|
||||
#define MV64340_TIMER_COUNTER3 0x85C
|
||||
#define MV64340_TIMER_COUNTER_0_3_CONTROL 0x864
|
||||
#define MV64340_TIMER_COUNTER_0_3_INTERRUPT_CAUSE 0x868
|
||||
#define MV64340_TIMER_COUNTER_0_3_INTERRUPT_MASK 0x86c
|
||||
|
||||
/****************************************/
|
||||
/* Watchdog registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_WATCHDOG_CONFIG_REG 0xb410
|
||||
#define MV64340_WATCHDOG_VALUE_REG 0xb414
|
||||
|
||||
/****************************************/
|
||||
/* I2C Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64XXX_I2C_OFFSET 0xc000
|
||||
#define MV64XXX_I2C_REG_BLOCK_SIZE 0x0020
|
||||
|
||||
/****************************************/
|
||||
/* GPP Interface Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_GPP_IO_CONTROL 0xf100
|
||||
#define MV64340_GPP_LEVEL_CONTROL 0xf110
|
||||
#define MV64340_GPP_VALUE 0xf104
|
||||
#define MV64340_GPP_INTERRUPT_CAUSE 0xf108
|
||||
#define MV64340_GPP_INTERRUPT_MASK0 0xf10c
|
||||
#define MV64340_GPP_INTERRUPT_MASK1 0xf114
|
||||
#define MV64340_GPP_VALUE_SET 0xf118
|
||||
#define MV64340_GPP_VALUE_CLEAR 0xf11c
|
||||
|
||||
/****************************************/
|
||||
/* Interrupt Controller Registers */
|
||||
/****************************************/
|
||||
|
||||
/****************************************/
|
||||
/* Interrupts */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_MAIN_INTERRUPT_CAUSE_LOW 0x004
|
||||
#define MV64340_MAIN_INTERRUPT_CAUSE_HIGH 0x00c
|
||||
#define MV64340_CPU_INTERRUPT0_MASK_LOW 0x014
|
||||
#define MV64340_CPU_INTERRUPT0_MASK_HIGH 0x01c
|
||||
#define MV64340_CPU_INTERRUPT0_SELECT_CAUSE 0x024
|
||||
#define MV64340_CPU_INTERRUPT1_MASK_LOW 0x034
|
||||
#define MV64340_CPU_INTERRUPT1_MASK_HIGH 0x03c
|
||||
#define MV64340_CPU_INTERRUPT1_SELECT_CAUSE 0x044
|
||||
#define MV64340_INTERRUPT0_MASK_0_LOW 0x054
|
||||
#define MV64340_INTERRUPT0_MASK_0_HIGH 0x05c
|
||||
#define MV64340_INTERRUPT0_SELECT_CAUSE 0x064
|
||||
#define MV64340_INTERRUPT1_MASK_0_LOW 0x074
|
||||
#define MV64340_INTERRUPT1_MASK_0_HIGH 0x07c
|
||||
#define MV64340_INTERRUPT1_SELECT_CAUSE 0x084
|
||||
|
||||
/****************************************/
|
||||
/* MPP Interface Registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_MPP_CONTROL0 0xf000
|
||||
#define MV64340_MPP_CONTROL1 0xf004
|
||||
#define MV64340_MPP_CONTROL2 0xf008
|
||||
#define MV64340_MPP_CONTROL3 0xf00c
|
||||
|
||||
/****************************************/
|
||||
/* Serial Initialization registers */
|
||||
/****************************************/
|
||||
|
||||
#define MV64340_SERIAL_INIT_LAST_DATA 0xf324
|
||||
#define MV64340_SERIAL_INIT_CONTROL 0xf328
|
||||
#define MV64340_SERIAL_INIT_STATUS 0xf32c
|
||||
|
||||
#endif /* ASM_MV643XX_H */
|
@ -3,6 +3,12 @@
|
||||
# grackle.c
|
||||
grackle_set_irq(int irq_num, int level) "set_irq num %d level %d"
|
||||
|
||||
# mv64361.c
|
||||
mv64361_region_map(const char *name, uint64_t poffs, uint64_t size, uint64_t moffs) "Mapping %s 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64
|
||||
mv64361_region_enable(const char *op, int num) "Should %s region %d"
|
||||
mv64361_reg_read(uint64_t addr, uint32_t val) "0x%"PRIx64" -> 0x%x"
|
||||
mv64361_reg_write(uint64_t addr, uint64_t val) "0x%"PRIx64" <- 0x%"PRIx64
|
||||
|
||||
# sabre.c
|
||||
sabre_set_request(int irq_num) "request irq %d"
|
||||
sabre_clear_request(int irq_num) "clear request irq %d"
|
||||
|
@ -68,6 +68,15 @@ config SAM460EX
|
||||
select USB_OHCI
|
||||
select FDT_PPC
|
||||
|
||||
config PEGASOS2
|
||||
bool
|
||||
select MV64361
|
||||
select VT82C686
|
||||
select IDE_VIA
|
||||
select SMBUS_EEPROM
|
||||
# This should come with VT82C686
|
||||
select ACPI_X86
|
||||
|
||||
config PREP
|
||||
bool
|
||||
imply PCI_DEVICES
|
||||
|
@ -155,6 +155,10 @@ static void ppc_core99_init(MachineState *machine)
|
||||
}
|
||||
|
||||
/* allocate RAM */
|
||||
if (machine->ram_size > 2 * GiB) {
|
||||
error_report("RAM size more than 2 GiB is not supported");
|
||||
exit(1);
|
||||
}
|
||||
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
|
||||
|
||||
/* allocate and load firmware ROM */
|
||||
|
@ -78,5 +78,7 @@ ppc_ss.add(when: 'CONFIG_E500', if_true: files(
|
||||
))
|
||||
# PowerPC 440 Xilinx ML507 reference board.
|
||||
ppc_ss.add(when: 'CONFIG_VIRTEX', if_true: files('virtex_ml507.c'))
|
||||
# Pegasos2
|
||||
ppc_ss.add(when: 'CONFIG_PEGASOS2', if_true: files('pegasos2.c'))
|
||||
|
||||
hw_arch += {'ppc': ppc_ss}
|
||||
|
144
hw/ppc/pegasos2.c
Normal file
144
hw/ppc/pegasos2.c
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* QEMU PowerPC CHRP (Genesi/bPlan Pegasos II) hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2018-2020 BALATON Zoltan
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ppc/ppc.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/pci-host/mv64361.h"
|
||||
#include "hw/isa/vt82c686.h"
|
||||
#include "hw/ide/pci.h"
|
||||
#include "hw/i2c/smbus_eeprom.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/fw-path-provider.h"
|
||||
#include "elf.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/datadir.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
|
||||
#define PROM_FILENAME "pegasos2.rom"
|
||||
#define PROM_ADDR 0xfff00000
|
||||
#define PROM_SIZE 0x80000
|
||||
|
||||
#define BUS_FREQ_HZ 133333333
|
||||
|
||||
static void pegasos2_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
cpu->env.spr[SPR_HID1] = 7ULL << 28;
|
||||
}
|
||||
|
||||
static void pegasos2_init(MachineState *machine)
|
||||
{
|
||||
PowerPCCPU *cpu = NULL;
|
||||
MemoryRegion *rom = g_new(MemoryRegion, 1);
|
||||
DeviceState *mv;
|
||||
PCIBus *pci_bus;
|
||||
PCIDevice *dev;
|
||||
I2CBus *i2c_bus;
|
||||
const char *fwname = machine->firmware ?: PROM_FILENAME;
|
||||
char *filename;
|
||||
int sz;
|
||||
uint8_t *spd_data;
|
||||
|
||||
/* init CPU */
|
||||
cpu = POWERPC_CPU(cpu_create(machine->cpu_type));
|
||||
if (PPC_INPUT(&cpu->env) != PPC_FLAGS_INPUT_6xx) {
|
||||
error_report("Incompatible CPU, only 6xx bus supported");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set time-base frequency */
|
||||
cpu_ppc_tb_init(&cpu->env, BUS_FREQ_HZ / 4);
|
||||
qemu_register_reset(pegasos2_cpu_reset, cpu);
|
||||
|
||||
/* RAM */
|
||||
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
|
||||
|
||||
/* allocate and load firmware */
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, fwname);
|
||||
if (!filename) {
|
||||
error_report("Could not find firmware '%s'", fwname);
|
||||
exit(1);
|
||||
}
|
||||
memory_region_init_rom(rom, NULL, "pegasos2.rom", PROM_SIZE, &error_fatal);
|
||||
memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom);
|
||||
sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1,
|
||||
PPC_ELF_MACHINE, 0, 0);
|
||||
if (sz <= 0) {
|
||||
sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE);
|
||||
}
|
||||
if (sz <= 0 || sz > PROM_SIZE) {
|
||||
error_report("Could not load firmware '%s'", filename);
|
||||
exit(1);
|
||||
}
|
||||
g_free(filename);
|
||||
|
||||
/* Marvell Discovery II system controller */
|
||||
mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1,
|
||||
((qemu_irq *)cpu->env.irq_inputs)[PPC6xx_INPUT_INT]));
|
||||
pci_bus = mv64361_get_pci_bus(mv, 1);
|
||||
|
||||
/* VIA VT8231 South Bridge (multifunction PCI device) */
|
||||
/* VT8231 function 0: PCI-to-ISA Bridge */
|
||||
dev = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(12, 0), true,
|
||||
TYPE_VT8231_ISA);
|
||||
qdev_connect_gpio_out(DEVICE(dev), 0,
|
||||
qdev_get_gpio_in_named(mv, "gpp", 31));
|
||||
|
||||
/* VT8231 function 1: IDE Controller */
|
||||
dev = pci_create_simple(pci_bus, PCI_DEVFN(12, 1), "via-ide");
|
||||
pci_ide_create_devs(dev);
|
||||
|
||||
/* VT8231 function 2-3: USB Ports */
|
||||
pci_create_simple(pci_bus, PCI_DEVFN(12, 2), "vt82c686b-usb-uhci");
|
||||
pci_create_simple(pci_bus, PCI_DEVFN(12, 3), "vt82c686b-usb-uhci");
|
||||
|
||||
/* VT8231 function 4: Power Management Controller */
|
||||
dev = pci_create_simple(pci_bus, PCI_DEVFN(12, 4), TYPE_VT8231_PM);
|
||||
i2c_bus = I2C_BUS(qdev_get_child_bus(DEVICE(dev), "i2c"));
|
||||
spd_data = spd_data_generate(DDR, machine->ram_size);
|
||||
smbus_eeprom_init_one(i2c_bus, 0x57, spd_data);
|
||||
|
||||
/* VT8231 function 5-6: AC97 Audio & Modem */
|
||||
pci_create_simple(pci_bus, PCI_DEVFN(12, 5), TYPE_VIA_AC97);
|
||||
pci_create_simple(pci_bus, PCI_DEVFN(12, 6), TYPE_VIA_MC97);
|
||||
|
||||
/* other PC hardware */
|
||||
pci_vga_init(pci_bus);
|
||||
}
|
||||
|
||||
static void pegasos2_machine(MachineClass *mc)
|
||||
{
|
||||
mc->desc = "Genesi/bPlan Pegasos II";
|
||||
mc->init = pegasos2_init;
|
||||
mc->block_default_type = IF_IDE;
|
||||
mc->default_boot_order = "cd";
|
||||
mc->default_display = "std";
|
||||
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7400_v2.9");
|
||||
mc->default_ram_id = "pegasos2.ram";
|
||||
mc->default_ram_size = 512 * MiB;
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("pegasos2", pegasos2_machine)
|
@ -29,6 +29,7 @@
|
||||
#include "hw/ppc/pnv_xscom.h"
|
||||
#include "hw/ppc/xics.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "helper_regs.h"
|
||||
|
||||
static const char *pnv_core_cpu_typename(PnvCore *pc)
|
||||
{
|
||||
@ -55,8 +56,8 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu)
|
||||
env->gpr[3] = PNV_FDT_ADDR;
|
||||
env->nip = 0x10;
|
||||
env->msr |= MSR_HVB; /* Hypervisor mode */
|
||||
|
||||
env->spr[SPR_HRMOR] = pc->hrmor;
|
||||
hreg_compute_hflags(env);
|
||||
|
||||
pcc->intc_reset(pc->chip, cpu);
|
||||
}
|
||||
|
@ -465,7 +465,7 @@ static void pnv_psi_reset(DeviceState *dev)
|
||||
|
||||
static void pnv_psi_reset_handler(void *dev)
|
||||
{
|
||||
device_legacy_reset(DEVICE(dev));
|
||||
device_cold_reset(DEVICE(dev));
|
||||
}
|
||||
|
||||
static void pnv_psi_realize(DeviceState *dev, Error **errp)
|
||||
@ -709,7 +709,7 @@ static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr,
|
||||
break;
|
||||
case PSIHB9_INTERRUPT_CONTROL:
|
||||
if (val & PSIHB9_IRQ_RESET) {
|
||||
device_legacy_reset(DEVICE(&psi9->source));
|
||||
device_cold_reset(DEVICE(&psi9->source));
|
||||
}
|
||||
psi->regs[reg] = val;
|
||||
break;
|
||||
|
@ -98,7 +98,7 @@
|
||||
*
|
||||
* We load our kernel at 4M, leaving space for SLOF initial image
|
||||
*/
|
||||
#define RTAS_MAX_ADDR 0x80000000 /* RTAS must stay below that */
|
||||
#define FDT_MAX_ADDR 0x80000000 /* FDT must stay below that */
|
||||
#define FW_MAX_SIZE 0x400000
|
||||
#define FW_FILE_NAME "slof.bin"
|
||||
#define FW_OVERHEAD 0x2800000
|
||||
@ -1615,11 +1615,11 @@ static void spapr_machine_reset(MachineState *machine)
|
||||
spapr_clear_pending_events(spapr);
|
||||
|
||||
/*
|
||||
* We place the device tree and RTAS just below either the top of the RMA,
|
||||
* We place the device tree just below either the top of the RMA,
|
||||
* or just below 2GB, whichever is lower, so that it can be
|
||||
* processed with 32-bit real mode code if necessary
|
||||
*/
|
||||
fdt_addr = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FDT_MAX_SIZE;
|
||||
fdt_addr = MIN(spapr->rma_size, FDT_MAX_ADDR) - FDT_MAX_SIZE;
|
||||
|
||||
fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE);
|
||||
|
||||
@ -2692,7 +2692,7 @@ static void spapr_machine_init(MachineState *machine)
|
||||
spapr->rma_size = spapr_rma_size(spapr, &error_fatal);
|
||||
|
||||
/* Setup a load limit for the ramdisk leaving room for SLOF and FDT */
|
||||
load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
|
||||
load_limit = MIN(spapr->rma_size, FDT_MAX_ADDR) - FW_OVERHEAD;
|
||||
|
||||
/*
|
||||
* VSMT must be set in order to be able to compute VCPU ids, ie to
|
||||
@ -4485,7 +4485,16 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->init = spapr_machine_init;
|
||||
mc->reset = spapr_machine_reset;
|
||||
mc->block_default_type = IF_SCSI;
|
||||
mc->max_cpus = 1024;
|
||||
|
||||
/*
|
||||
* Setting max_cpus to INT32_MAX. Both KVM and TCG max_cpus values
|
||||
* should be limited by the host capability instead of hardcoded.
|
||||
* max_cpus for KVM guests will be checked in kvm_init(), and TCG
|
||||
* guests are welcome to have as many CPUs as the host are capable
|
||||
* of emulate.
|
||||
*/
|
||||
mc->max_cpus = INT32_MAX;
|
||||
|
||||
mc->no_parallel = 1;
|
||||
mc->default_boot_order = "";
|
||||
mc->default_ram_size = 512 * MiB;
|
||||
|
@ -150,9 +150,32 @@ static uint32_t drc_isolate_logical(SpaprDrc *drc)
|
||||
|
||||
static uint32_t drc_unisolate_logical(SpaprDrc *drc)
|
||||
{
|
||||
SpaprMachineState *spapr = NULL;
|
||||
|
||||
switch (drc->state) {
|
||||
case SPAPR_DRC_STATE_LOGICAL_UNISOLATE:
|
||||
case SPAPR_DRC_STATE_LOGICAL_CONFIGURED:
|
||||
/*
|
||||
* Unisolating a logical DRC that was marked for unplug
|
||||
* means that the kernel is refusing the removal.
|
||||
*/
|
||||
if (drc->unplug_requested && drc->dev) {
|
||||
if (spapr_drc_type(drc) == SPAPR_DR_CONNECTOR_TYPE_LMB) {
|
||||
spapr = SPAPR_MACHINE(qdev_get_machine());
|
||||
|
||||
spapr_memory_unplug_rollback(spapr, drc->dev);
|
||||
}
|
||||
|
||||
drc->unplug_requested = false;
|
||||
error_report("Device hotunplug rejected by the guest "
|
||||
"for device %s", drc->dev->id);
|
||||
|
||||
/*
|
||||
* TODO: send a QAPI DEVICE_UNPLUG_ERROR event when
|
||||
* it is implemented.
|
||||
*/
|
||||
}
|
||||
|
||||
return RTAS_OUT_SUCCESS; /* Nothing to do */
|
||||
case SPAPR_DRC_STATE_LOGICAL_AVAILABLE:
|
||||
break; /* see below */
|
||||
|
@ -1394,7 +1394,13 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu,
|
||||
return H_P4;
|
||||
}
|
||||
|
||||
if (mflags == AIL_RESERVED) {
|
||||
if (mflags == 1) {
|
||||
/* AIL=1 is reserved in POWER8/POWER9/POWER10 */
|
||||
return H_UNSUPPORTED_FLAG;
|
||||
}
|
||||
|
||||
if (mflags == 2 && (pcc->insns_flags2 & PPC2_ISA310)) {
|
||||
/* AIL=2 is reserved in POWER10 (ISA v3.1) */
|
||||
return H_UNSUPPORTED_FLAG;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,10 @@
|
||||
#include "qemu/range.h"
|
||||
#include "hw/ppc/spapr_numa.h"
|
||||
|
||||
/* DIMM health bitmap bitmap indicators. Taken from kernel's papr_scm.c */
|
||||
/* SCM device is unable to persist memory contents */
|
||||
#define PAPR_PMEM_UNARMED PPC_BIT(0)
|
||||
|
||||
bool spapr_nvdimm_validate(HotplugHandler *hotplug_dev, NVDIMMDevice *nvdimm,
|
||||
uint64_t size, Error **errp)
|
||||
{
|
||||
@ -467,6 +471,37 @@ static target_ulong h_scm_unbind_all(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_scm_health(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
|
||||
NVDIMMDevice *nvdimm;
|
||||
uint64_t hbitmap = 0;
|
||||
uint32_t drc_index = args[0];
|
||||
SpaprDrc *drc = spapr_drc_by_index(drc_index);
|
||||
const uint64_t hbitmap_mask = PAPR_PMEM_UNARMED;
|
||||
|
||||
|
||||
/* Ensure that the drc is valid & is valid PMEM dimm and is plugged in */
|
||||
if (!drc || !drc->dev ||
|
||||
spapr_drc_type(drc) != SPAPR_DR_CONNECTOR_TYPE_PMEM) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
nvdimm = NVDIMM(drc->dev);
|
||||
|
||||
/* Update if the nvdimm is unarmed and send its status via health bitmaps */
|
||||
if (object_property_get_bool(OBJECT(nvdimm), NVDIMM_UNARMED_PROP, NULL)) {
|
||||
hbitmap |= PAPR_PMEM_UNARMED;
|
||||
}
|
||||
|
||||
/* Update the out args with health bitmap/mask */
|
||||
args[0] = hbitmap;
|
||||
args[1] = hbitmap_mask;
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static void spapr_scm_register_types(void)
|
||||
{
|
||||
/* qemu/scm specific hcalls */
|
||||
@ -475,6 +510,7 @@ static void spapr_scm_register_types(void)
|
||||
spapr_register_hypercall(H_SCM_BIND_MEM, h_scm_bind_mem);
|
||||
spapr_register_hypercall(H_SCM_UNBIND_MEM, h_scm_unbind_mem);
|
||||
spapr_register_hypercall(H_SCM_UNBIND_ALL, h_scm_unbind_all);
|
||||
spapr_register_hypercall(H_SCM_HEALTH, h_scm_health);
|
||||
}
|
||||
|
||||
type_init(spapr_scm_register_types)
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "target/ppc/mmu-hash64.h"
|
||||
#include "target/ppc/mmu-book3s-v3.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "helper_regs.h"
|
||||
|
||||
static void rtas_display_character(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
@ -161,6 +162,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr,
|
||||
cpu_synchronize_state(CPU(newcpu));
|
||||
|
||||
env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
|
||||
hreg_compute_hflags(env);
|
||||
|
||||
/* Enable Power-saving mode Exit Cause exceptions for the new CPU */
|
||||
lpcr = env->spr[SPR_LPCR];
|
||||
|
@ -310,7 +310,7 @@ int spapr_vio_send_crq(SpaprVioDevice *dev, uint8_t *crq)
|
||||
static void spapr_vio_quiesce_one(SpaprVioDevice *dev)
|
||||
{
|
||||
if (dev->tcet) {
|
||||
device_legacy_reset(DEVICE(dev->tcet));
|
||||
device_cold_reset(DEVICE(dev->tcet));
|
||||
}
|
||||
free_crq(dev);
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
#define HW_VT82C686_H
|
||||
|
||||
#define TYPE_VT82C686B_ISA "vt82c686b-isa"
|
||||
#define TYPE_VT82C686B_SUPERIO "vt82c686b-superio"
|
||||
#define TYPE_VT82C686B_PM "vt82c686b-pm"
|
||||
#define TYPE_VT8231_ISA "vt8231-isa"
|
||||
#define TYPE_VT8231_PM "vt8231-pm"
|
||||
#define TYPE_VIA_AC97 "via-ac97"
|
||||
#define TYPE_VIA_MC97 "via-mc97"
|
||||
|
8
include/hw/pci-host/mv64361.h
Normal file
8
include/hw/pci-host/mv64361.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef MV64361_H
|
||||
#define MV64361_H
|
||||
|
||||
#define TYPE_MV64361 "mv64361"
|
||||
|
||||
PCIBus *mv64361_get_pci_bus(DeviceState *dev, int n);
|
||||
|
||||
#endif
|
@ -204,15 +204,17 @@
|
||||
#define PCI_VENDOR_ID_XILINX 0x10ee
|
||||
|
||||
#define PCI_VENDOR_ID_VIA 0x1106
|
||||
#define PCI_DEVICE_ID_VIA_ISA_BRIDGE 0x0686
|
||||
#define PCI_DEVICE_ID_VIA_82C686B_ISA 0x0686
|
||||
#define PCI_DEVICE_ID_VIA_IDE 0x0571
|
||||
#define PCI_DEVICE_ID_VIA_UHCI 0x3038
|
||||
#define PCI_DEVICE_ID_VIA_82C686B_PM 0x3057
|
||||
#define PCI_DEVICE_ID_VIA_AC97 0x3058
|
||||
#define PCI_DEVICE_ID_VIA_MC97 0x3068
|
||||
#define PCI_DEVICE_ID_VIA_8231_ISA 0x8231
|
||||
#define PCI_DEVICE_ID_VIA_8231_PM 0x8235
|
||||
|
||||
#define PCI_VENDOR_ID_MARVELL 0x11ab
|
||||
#define PCI_DEVICE_ID_MARVELL_MV6436X 0x6460
|
||||
|
||||
#define PCI_VENDOR_ID_SILICON_MOTION 0x126f
|
||||
#define PCI_DEVICE_ID_SM501 0x0501
|
||||
|
@ -95,7 +95,7 @@ typedef enum {
|
||||
#define SPAPR_CAP_FIXED_CCD 0x03
|
||||
#define SPAPR_CAP_FIXED_NA 0x10 /* Lets leave a bit of a gap... */
|
||||
|
||||
#define FDT_MAX_SIZE 0x100000
|
||||
#define FDT_MAX_SIZE 0x200000
|
||||
|
||||
/*
|
||||
* NUMA related macros. MAX_DISTANCE_REF_POINTS was taken
|
||||
@ -363,7 +363,7 @@ struct SpaprMachineState {
|
||||
|
||||
/* Values for 2nd argument to H_SET_MODE */
|
||||
#define H_SET_MODE_RESOURCE_SET_CIABR 1
|
||||
#define H_SET_MODE_RESOURCE_SET_DAWR 2
|
||||
#define H_SET_MODE_RESOURCE_SET_DAWR0 2
|
||||
#define H_SET_MODE_RESOURCE_ADDR_TRANS_MODE 3
|
||||
#define H_SET_MODE_RESOURCE_LE 4
|
||||
|
||||
@ -538,8 +538,9 @@ struct SpaprMachineState {
|
||||
#define H_SCM_BIND_MEM 0x3EC
|
||||
#define H_SCM_UNBIND_MEM 0x3F0
|
||||
#define H_SCM_UNBIND_ALL 0x3FC
|
||||
#define H_SCM_HEALTH 0x400
|
||||
|
||||
#define MAX_HCALL_OPCODE H_SCM_UNBIND_ALL
|
||||
#define MAX_HCALL_OPCODE H_SCM_HEALTH
|
||||
|
||||
/* The hcalls above are standardized in PAPR and implemented by pHyp
|
||||
* as well.
|
||||
|
@ -492,11 +492,12 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
|
||||
#if defined(TARGET_PPC64)
|
||||
int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF;
|
||||
#if defined(TARGET_ABI32)
|
||||
env->msr &= ~((target_ulong)1 << flag);
|
||||
ppc_store_msr(env, env->msr & ~((target_ulong)1 << flag));
|
||||
#else
|
||||
env->msr |= (target_ulong)1 << flag;
|
||||
ppc_store_msr(env, env->msr | (target_ulong)1 << flag);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
env->nip = regs->nip;
|
||||
for(i = 0; i < 32; i++) {
|
||||
env->gpr[i] = regs->gpr[i];
|
||||
|
@ -261,9 +261,6 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame)
|
||||
__put_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]);
|
||||
__put_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]);
|
||||
}
|
||||
/* Set MSR_VR in the saved MSR value to indicate that
|
||||
frame->mc_vregs contains valid data. */
|
||||
msr |= MSR_VR;
|
||||
#if defined(TARGET_PPC64)
|
||||
vrsave = (uint32_t *)&frame->mc_vregs.altivec[33];
|
||||
/* 64-bit needs to put a pointer to the vectors in the frame */
|
||||
@ -300,9 +297,6 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame)
|
||||
for (i = 0; i < ARRAY_SIZE(env->gprh); i++) {
|
||||
__put_user(env->gprh[i], &frame->mc_vregs.spe[i]);
|
||||
}
|
||||
/* Set MSR_SPE in the saved MSR value to indicate that
|
||||
frame->mc_vregs contains valid data. */
|
||||
msr |= MSR_SPE;
|
||||
__put_user(env->spe_fscr, &frame->mc_vregs.spe[32]);
|
||||
}
|
||||
#endif
|
||||
@ -354,8 +348,10 @@ static void restore_user_regs(CPUPPCState *env,
|
||||
__get_user(msr, &frame->mc_gregs[TARGET_PT_MSR]);
|
||||
|
||||
/* If doing signal return, restore the previous little-endian mode. */
|
||||
if (sig)
|
||||
env->msr = (env->msr & ~(1ull << MSR_LE)) | (msr & (1ull << MSR_LE));
|
||||
if (sig) {
|
||||
ppc_store_msr(env, ((env->msr & ~(1ull << MSR_LE)) |
|
||||
(msr & (1ull << MSR_LE))));
|
||||
}
|
||||
|
||||
/* Restore Altivec registers if necessary. */
|
||||
if (env->insns_flags & PPC_ALTIVEC) {
|
||||
@ -376,8 +372,6 @@ static void restore_user_regs(CPUPPCState *env,
|
||||
__get_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]);
|
||||
__get_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]);
|
||||
}
|
||||
/* Set MSR_VEC in the saved MSR value to indicate that
|
||||
frame->mc_vregs contains valid data. */
|
||||
#if defined(TARGET_PPC64)
|
||||
vrsave = (uint32_t *)&v_regs[33];
|
||||
#else
|
||||
@ -468,7 +462,7 @@ void setup_frame(int sig, struct target_sigaction *ka,
|
||||
env->nip = (target_ulong) ka->_sa_handler;
|
||||
|
||||
/* Signal handlers are entered in big-endian mode. */
|
||||
env->msr &= ~(1ull << MSR_LE);
|
||||
ppc_store_msr(env, env->msr & ~(1ull << MSR_LE));
|
||||
|
||||
unlock_user_struct(frame, frame_addr, 1);
|
||||
return;
|
||||
@ -563,8 +557,13 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
|
||||
env->nip = (target_ulong) ka->_sa_handler;
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
/* Signal handlers are entered in big-endian mode. */
|
||||
env->msr &= ~(1ull << MSR_LE);
|
||||
ppc_store_msr(env, env->msr & ~(1ull << MSR_LE));
|
||||
#else
|
||||
/* Signal handlers are entered in little-endian mode. */
|
||||
ppc_store_msr(env, env->msr | (1ull << MSR_LE));
|
||||
#endif
|
||||
|
||||
unlock_user_struct(rt_sf, rt_sf_addr, 1);
|
||||
return;
|
||||
|
Binary file not shown.
@ -154,10 +154,10 @@ slof:
|
||||
cp SLOF/boot_rom.bin ../pc-bios/slof.bin
|
||||
|
||||
u-boot.e500:
|
||||
$(MAKE) -C u-boot O=build.e500 qemu-ppce500_config
|
||||
$(MAKE) -C u-boot O=build-e500 qemu-ppce500_config
|
||||
$(MAKE) -C u-boot CROSS_COMPILE=$(powerpc_cross_prefix) \
|
||||
O=build.e500
|
||||
$(powerpc_cross_prefix)strip u-boot/build.e500/u-boot -o \
|
||||
O=build-e500
|
||||
$(powerpc_cross_prefix)strip u-boot/build-e500/u-boot -o \
|
||||
../pc-bios/u-boot.e500
|
||||
|
||||
u-boot.sam460:
|
||||
@ -205,7 +205,7 @@ clean:
|
||||
$(MAKE) -C ipxe/src veryclean
|
||||
$(MAKE) -C edk2/BaseTools clean
|
||||
$(MAKE) -C SLOF clean
|
||||
rm -rf u-boot/build.e500
|
||||
rm -rf u-boot/build-e500
|
||||
$(MAKE) -C u-boot-sam460ex distclean
|
||||
$(MAKE) -C skiboot clean
|
||||
$(MAKE) -f Makefile.edk2 clean
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d3689267f92c5956e09cc7d1baa4700141662bff
|
||||
Subproject commit b46dd116ce03e235f2a7d4843c6278e1da44b5e1
|
@ -116,6 +116,8 @@ enum powerpc_excp_t {
|
||||
POWERPC_EXCP_POWER8,
|
||||
/* POWER9 exception model */
|
||||
POWERPC_EXCP_POWER9,
|
||||
/* POWER10 exception model */
|
||||
POWERPC_EXCP_POWER10,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -192,17 +192,21 @@ typedef struct ppc_hash_pte64 ppc_hash_pte64_t;
|
||||
|
||||
/* SPR access micro-ops generations callbacks */
|
||||
struct ppc_spr_t {
|
||||
const char *name;
|
||||
target_ulong default_value;
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
unsigned int gdb_id;
|
||||
#endif
|
||||
#ifdef CONFIG_TCG
|
||||
void (*uea_read)(DisasContext *ctx, int gpr_num, int spr_num);
|
||||
void (*uea_write)(DisasContext *ctx, int spr_num, int gpr_num);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
# ifndef CONFIG_USER_ONLY
|
||||
void (*oea_read)(DisasContext *ctx, int gpr_num, int spr_num);
|
||||
void (*oea_write)(DisasContext *ctx, int spr_num, int gpr_num);
|
||||
void (*hea_read)(DisasContext *ctx, int gpr_num, int spr_num);
|
||||
void (*hea_write)(DisasContext *ctx, int spr_num, int gpr_num);
|
||||
unsigned int gdb_id;
|
||||
# endif
|
||||
#endif
|
||||
const char *name;
|
||||
target_ulong default_value;
|
||||
#ifdef CONFIG_KVM
|
||||
/*
|
||||
* We (ab)use the fact that all the SPRs will have ids for the
|
||||
@ -322,13 +326,13 @@ typedef struct ppc_v3_pate_t {
|
||||
#define MSR_PR 14 /* Problem state hflags */
|
||||
#define MSR_FP 13 /* Floating point available hflags */
|
||||
#define MSR_ME 12 /* Machine check interrupt enable */
|
||||
#define MSR_FE0 11 /* Floating point exception mode 0 hflags */
|
||||
#define MSR_FE0 11 /* Floating point exception mode 0 */
|
||||
#define MSR_SE 10 /* Single-step trace enable x hflags */
|
||||
#define MSR_DWE 10 /* Debug wait enable on 405 x */
|
||||
#define MSR_UBLE 10 /* User BTB lock enable on e500 x */
|
||||
#define MSR_BE 9 /* Branch trace enable x hflags */
|
||||
#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC x */
|
||||
#define MSR_FE1 8 /* Floating point exception mode 1 hflags */
|
||||
#define MSR_FE1 8 /* Floating point exception mode 1 */
|
||||
#define MSR_AL 7 /* AL bit on POWER */
|
||||
#define MSR_EP 6 /* Exception prefix on 601 */
|
||||
#define MSR_IR 5 /* Instruction relocate */
|
||||
@ -354,10 +358,11 @@ typedef struct ppc_v3_pate_t {
|
||||
#define LPCR_PECE_U_SHIFT (63 - 19)
|
||||
#define LPCR_PECE_U_MASK (0x7ull << LPCR_PECE_U_SHIFT)
|
||||
#define LPCR_HVEE PPC_BIT(17) /* Hypervisor Virt Exit Enable */
|
||||
#define LPCR_RMLS_SHIFT (63 - 37)
|
||||
#define LPCR_RMLS_SHIFT (63 - 37) /* RMLS (removed in ISA v3.0) */
|
||||
#define LPCR_RMLS (0xfull << LPCR_RMLS_SHIFT)
|
||||
#define LPCR_HAIL PPC_BIT(37) /* ISA v3.1 HV AIL=3 equivalent */
|
||||
#define LPCR_ILE PPC_BIT(38)
|
||||
#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */
|
||||
#define LPCR_AIL_SHIFT (63 - 40) /* Alternate interrupt location */
|
||||
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
|
||||
#define LPCR_UPRT PPC_BIT(41) /* Use Process Table */
|
||||
#define LPCR_EVIRT PPC_BIT(42) /* Enhanced Virtualisation */
|
||||
@ -581,6 +586,34 @@ enum {
|
||||
POWERPC_FLAG_TM = 0x00100000,
|
||||
/* Has SCV (ISA 3.00) */
|
||||
POWERPC_FLAG_SCV = 0x00200000,
|
||||
/* Has HID0 for LE bit (601) */
|
||||
POWERPC_FLAG_HID0_LE = 0x00400000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Bits for env->hflags.
|
||||
*
|
||||
* Most of these bits overlap with corresponding bits in MSR,
|
||||
* but some come from other sources. Those that do come from
|
||||
* the MSR are validated in hreg_compute_hflags.
|
||||
*/
|
||||
enum {
|
||||
HFLAGS_LE = 0, /* MSR_LE -- comes from elsewhere on 601 */
|
||||
HFLAGS_HV = 1, /* computed from MSR_HV and other state */
|
||||
HFLAGS_64 = 2, /* computed from MSR_CE and MSR_SF */
|
||||
HFLAGS_GTSE = 3, /* computed from SPR_LPCR[GTSE] */
|
||||
HFLAGS_DR = 4, /* MSR_DR */
|
||||
HFLAGS_SPE = 6, /* from MSR_SPE if cpu has SPE; avoid overlap w/ MSR_VR */
|
||||
HFLAGS_TM = 8, /* computed from MSR_TM */
|
||||
HFLAGS_BE = 9, /* MSR_BE -- from elsewhere on embedded ppc */
|
||||
HFLAGS_SE = 10, /* MSR_SE -- from elsewhere on embedded ppc */
|
||||
HFLAGS_FP = 13, /* MSR_FP */
|
||||
HFLAGS_PR = 14, /* MSR_PR */
|
||||
HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */
|
||||
HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */
|
||||
|
||||
HFLAGS_IMMU_IDX = 26, /* 26..28 -- the composite immu_idx */
|
||||
HFLAGS_DMMU_IDX = 29, /* 29..31 -- the composite dmmu_idx */
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -1102,11 +1135,9 @@ struct CPUPPCState {
|
||||
bool resume_as_sreset;
|
||||
#endif
|
||||
|
||||
/* These resources are used only in QEMU core */
|
||||
target_ulong hflags; /* hflags is MSR & HFLAGS_MASK */
|
||||
target_ulong hflags_nmsr; /* specific hflags, not coming from MSR */
|
||||
int immu_idx; /* precomputed MMU index to speed up insn accesses */
|
||||
int dmmu_idx; /* precomputed MMU index to speed up data accesses */
|
||||
/* These resources are used only in TCG */
|
||||
uint32_t hflags;
|
||||
target_ulong hflags_compat_nmsr; /* for migration compatibility */
|
||||
|
||||
/* Power management */
|
||||
int (*check_pow)(CPUPPCState *env);
|
||||
@ -1342,7 +1373,11 @@ int ppc_dcr_write(ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
|
||||
#define MMU_USER_IDX 0
|
||||
static inline int cpu_mmu_index(CPUPPCState *env, bool ifetch)
|
||||
{
|
||||
return ifetch ? env->immu_idx : env->dmmu_idx;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return MMU_USER_IDX;
|
||||
#else
|
||||
return (env->hflags >> (ifetch ? HFLAGS_IMMU_IDX : HFLAGS_DMMU_IDX)) & 7;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compatibility modes */
|
||||
@ -1459,10 +1494,10 @@ typedef PowerPCCPU ArchCPU;
|
||||
#define SPR_MPC_BAR (0x09F)
|
||||
#define SPR_PSPB (0x09F)
|
||||
#define SPR_DPDES (0x0B0)
|
||||
#define SPR_DAWR (0x0B4)
|
||||
#define SPR_DAWR0 (0x0B4)
|
||||
#define SPR_RPR (0x0BA)
|
||||
#define SPR_CIABR (0x0BB)
|
||||
#define SPR_DAWRX (0x0BC)
|
||||
#define SPR_DAWRX0 (0x0BC)
|
||||
#define SPR_HFSCR (0x0BE)
|
||||
#define SPR_VRSAVE (0x100)
|
||||
#define SPR_USPRG0 (0x100)
|
||||
@ -2375,14 +2410,6 @@ enum {
|
||||
HMER_XSCOM_STATUS_MASK = PPC_BITMASK(21, 23),
|
||||
};
|
||||
|
||||
/* Alternate Interrupt Location (AIL) */
|
||||
enum {
|
||||
AIL_NONE = 0,
|
||||
AIL_RESERVED = 1,
|
||||
AIL_0001_8000 = 2,
|
||||
AIL_C000_0000_0000_4000 = 3,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define is_isa300(ctx) (!!(ctx->insns_flags2 & PPC2_ISA300))
|
||||
@ -2395,6 +2422,10 @@ void cpu_write_xer(CPUPPCState *env, target_ulong xer);
|
||||
*/
|
||||
#define is_book3s_arch2x(ctx) (!!((ctx)->insns_flags & PPC_SEGMENT_64B))
|
||||
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *flags);
|
||||
#else
|
||||
static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *flags)
|
||||
{
|
||||
@ -2402,6 +2433,7 @@ static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
|
||||
*cs_base = 0;
|
||||
*flags = env->hflags;
|
||||
}
|
||||
#endif
|
||||
|
||||
void QEMU_NORETURN raise_exception(CPUPPCState *env, uint32_t exception);
|
||||
void QEMU_NORETURN raise_exception_ra(CPUPPCState *env, uint32_t exception,
|
||||
|
@ -136,25 +136,157 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
|
||||
return POWERPC_EXCP_RESET;
|
||||
}
|
||||
|
||||
static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail)
|
||||
/*
|
||||
* AIL - Alternate Interrupt Location, a mode that allows interrupts to be
|
||||
* taken with the MMU on, and which uses an alternate location (e.g., so the
|
||||
* kernel/hv can map the vectors there with an effective address).
|
||||
*
|
||||
* An interrupt is considered to be taken "with AIL" or "AIL applies" if they
|
||||
* are delivered in this way. AIL requires the LPCR to be set to enable this
|
||||
* mode, and then a number of conditions have to be true for AIL to apply.
|
||||
*
|
||||
* First of all, SRESET, MCE, and HMI are always delivered without AIL, because
|
||||
* they specifically want to be in real mode (e.g., the MCE might be signaling
|
||||
* a SLB multi-hit which requires SLB flush before the MMU can be enabled).
|
||||
*
|
||||
* After that, behaviour depends on the current MSR[IR], MSR[DR], MSR[HV],
|
||||
* whether or not the interrupt changes MSR[HV] from 0 to 1, and the current
|
||||
* radix mode (LPCR[HR]).
|
||||
*
|
||||
* POWER8, POWER9 with LPCR[HR]=0
|
||||
* | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
|
||||
* +-----------+-------------+---------+-------------+-----+
|
||||
* | a | 00/01/10 | x | x | 0 |
|
||||
* | a | 11 | 0 | 1 | 0 |
|
||||
* | a | 11 | 1 | 1 | a |
|
||||
* | a | 11 | 0 | 0 | a |
|
||||
* +-------------------------------------------------------+
|
||||
*
|
||||
* POWER9 with LPCR[HR]=1
|
||||
* | LPCR[AIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
|
||||
* +-----------+-------------+---------+-------------+-----+
|
||||
* | a | 00/01/10 | x | x | 0 |
|
||||
* | a | 11 | x | x | a |
|
||||
* +-------------------------------------------------------+
|
||||
*
|
||||
* The difference with POWER9 being that MSR[HV] 0->1 interrupts can be sent to
|
||||
* the hypervisor in AIL mode if the guest is radix. This is good for
|
||||
* performance but allows the guest to influence the AIL of hypervisor
|
||||
* interrupts using its MSR, and also the hypervisor must disallow guest
|
||||
* interrupts (MSR[HV] 0->0) from using AIL if the hypervisor does not want to
|
||||
* use AIL for its MSR[HV] 0->1 interrupts.
|
||||
*
|
||||
* POWER10 addresses those issues with a new LPCR[HAIL] bit that is applied to
|
||||
* interrupts that begin execution with MSR[HV]=1 (so both MSR[HV] 0->1 and
|
||||
* MSR[HV] 1->1).
|
||||
*
|
||||
* HAIL=1 is equivalent to AIL=3, for interrupts delivered with MSR[HV]=1.
|
||||
*
|
||||
* POWER10 behaviour is
|
||||
* | LPCR[AIL] | LPCR[HAIL] | MSR[IR||DR] | MSR[HV] | new MSR[HV] | AIL |
|
||||
* +-----------+------------+-------------+---------+-------------+-----+
|
||||
* | a | h | 00/01/10 | 0 | 0 | 0 |
|
||||
* | a | h | 11 | 0 | 0 | a |
|
||||
* | a | h | x | 0 | 1 | h |
|
||||
* | a | h | 00/01/10 | 1 | 1 | 0 |
|
||||
* | a | h | 11 | 1 | 1 | h |
|
||||
* +--------------------------------------------------------------------+
|
||||
*/
|
||||
static inline void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp,
|
||||
target_ulong msr,
|
||||
target_ulong *new_msr,
|
||||
target_ulong *vector)
|
||||
{
|
||||
uint64_t offset = 0;
|
||||
#if defined(TARGET_PPC64)
|
||||
CPUPPCState *env = &cpu->env;
|
||||
bool mmu_all_on = ((msr >> MSR_IR) & 1) && ((msr >> MSR_DR) & 1);
|
||||
bool hv_escalation = !(msr & MSR_HVB) && (*new_msr & MSR_HVB);
|
||||
int ail = 0;
|
||||
|
||||
switch (ail) {
|
||||
case AIL_NONE:
|
||||
break;
|
||||
case AIL_0001_8000:
|
||||
offset = 0x18000;
|
||||
break;
|
||||
case AIL_C000_0000_0000_4000:
|
||||
offset = 0xc000000000004000ull;
|
||||
break;
|
||||
default:
|
||||
cpu_abort(cs, "Invalid AIL combination %d\n", ail);
|
||||
break;
|
||||
if (excp == POWERPC_EXCP_MCHECK ||
|
||||
excp == POWERPC_EXCP_RESET ||
|
||||
excp == POWERPC_EXCP_HV_MAINT) {
|
||||
/* SRESET, MCE, HMI never apply AIL */
|
||||
return;
|
||||
}
|
||||
|
||||
return offset;
|
||||
if (excp_model == POWERPC_EXCP_POWER8 ||
|
||||
excp_model == POWERPC_EXCP_POWER9) {
|
||||
if (!mmu_all_on) {
|
||||
/* AIL only works if MSR[IR] and MSR[DR] are both enabled. */
|
||||
return;
|
||||
}
|
||||
if (hv_escalation && !(env->spr[SPR_LPCR] & LPCR_HR)) {
|
||||
/*
|
||||
* AIL does not work if there is a MSR[HV] 0->1 transition and the
|
||||
* partition is in HPT mode. For radix guests, such interrupts are
|
||||
* allowed to be delivered to the hypervisor in ail mode.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
|
||||
if (ail == 0) {
|
||||
return;
|
||||
}
|
||||
if (ail == 1) {
|
||||
/* AIL=1 is reserved, treat it like AIL=0 */
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (excp_model == POWERPC_EXCP_POWER10) {
|
||||
if (!mmu_all_on && !hv_escalation) {
|
||||
/*
|
||||
* AIL works for HV interrupts even with guest MSR[IR/DR] disabled.
|
||||
* Guest->guest and HV->HV interrupts do require MMU on.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (*new_msr & MSR_HVB) {
|
||||
if (!(env->spr[SPR_LPCR] & LPCR_HAIL)) {
|
||||
/* HV interrupts depend on LPCR[HAIL] */
|
||||
return;
|
||||
}
|
||||
ail = 3; /* HAIL=1 gives AIL=3 behaviour for HV interrupts */
|
||||
} else {
|
||||
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
|
||||
}
|
||||
if (ail == 0) {
|
||||
return;
|
||||
}
|
||||
if (ail == 1 || ail == 2) {
|
||||
/* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Other processors do not support AIL */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* AIL applies, so the new MSR gets IR and DR set, and an offset applied
|
||||
* to the new IP.
|
||||
*/
|
||||
*new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
|
||||
|
||||
if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
|
||||
if (ail == 2) {
|
||||
*vector |= 0x0000000000018000ull;
|
||||
} else if (ail == 3) {
|
||||
*vector |= 0xc000000000004000ull;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* scv AIL is a little different. AIL=2 does not change the address,
|
||||
* only the MSR. AIL=3 replaces the 0x17000 base with 0xc...3000.
|
||||
*/
|
||||
if (ail == 3) {
|
||||
*vector &= ~0x0000000000017000ull; /* Un-apply the base offset */
|
||||
*vector |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void powerpc_set_excp_state(PowerPCCPU *cpu,
|
||||
@ -197,7 +329,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
target_ulong msr, new_msr, vector;
|
||||
int srr0, srr1, asrr0, asrr1, lev = -1, ail;
|
||||
int srr0, srr1, asrr0, asrr1, lev = -1;
|
||||
bool lpes0;
|
||||
|
||||
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
|
||||
@ -238,25 +370,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
*
|
||||
* On anything else, we behave as if LPES0 is 1
|
||||
* (externals don't alter MSR:HV)
|
||||
*
|
||||
* AIL is initialized here but can be cleared by
|
||||
* selected exceptions
|
||||
*/
|
||||
#if defined(TARGET_PPC64)
|
||||
if (excp_model == POWERPC_EXCP_POWER7 ||
|
||||
excp_model == POWERPC_EXCP_POWER8 ||
|
||||
excp_model == POWERPC_EXCP_POWER9) {
|
||||
excp_model == POWERPC_EXCP_POWER9 ||
|
||||
excp_model == POWERPC_EXCP_POWER10) {
|
||||
lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
|
||||
if (excp_model != POWERPC_EXCP_POWER7) {
|
||||
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
|
||||
} else {
|
||||
ail = 0;
|
||||
}
|
||||
} else
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
{
|
||||
lpes0 = true;
|
||||
ail = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -315,7 +439,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
*/
|
||||
new_msr |= (target_ulong)MSR_HVB;
|
||||
}
|
||||
ail = 0;
|
||||
|
||||
/* machine check exceptions don't have ME set */
|
||||
new_msr &= ~((target_ulong)1 << MSR_ME);
|
||||
@ -519,7 +642,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
"exception %d with no HV support\n", excp);
|
||||
}
|
||||
}
|
||||
ail = 0;
|
||||
break;
|
||||
case POWERPC_EXCP_DSEG: /* Data segment exception */
|
||||
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
|
||||
@ -773,7 +895,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
}
|
||||
} else if (excp_model == POWERPC_EXCP_POWER9) {
|
||||
} else if (excp_model == POWERPC_EXCP_POWER9 ||
|
||||
excp_model == POWERPC_EXCP_POWER10) {
|
||||
if (new_msr & MSR_HVB) {
|
||||
if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
|
||||
new_msr |= (target_ulong)1 << MSR_LE;
|
||||
@ -790,15 +913,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* AIL only works if there is no HV transition and we are running
|
||||
* with translations enabled
|
||||
*/
|
||||
if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) ||
|
||||
((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
|
||||
ail = 0;
|
||||
}
|
||||
|
||||
vector = env->excp_vectors[excp];
|
||||
if (vector == (target_ulong)-1ULL) {
|
||||
cpu_abort(cs, "Raised an exception without defined vector %d\n",
|
||||
@ -839,23 +953,8 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
/* Save MSR */
|
||||
env->spr[srr1] = msr;
|
||||
|
||||
/* Handle AIL */
|
||||
if (ail) {
|
||||
new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
|
||||
vector |= ppc_excp_vector_offset(cs, ail);
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
} else {
|
||||
/* scv AIL is a little different */
|
||||
if (ail) {
|
||||
new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
|
||||
}
|
||||
if (ail == AIL_C000_0000_0000_4000) {
|
||||
vector |= 0xc000000000003000ull;
|
||||
} else {
|
||||
vector |= 0x0000000000017000ull;
|
||||
}
|
||||
vector += lev * 0x20;
|
||||
|
||||
env->lr = env->nip;
|
||||
@ -863,6 +962,9 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This can update new_msr and vector if AIL applies */
|
||||
ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector);
|
||||
|
||||
powerpc_set_excp_state(cpu, vector, new_msr);
|
||||
}
|
||||
|
||||
@ -1130,6 +1232,15 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
void helper_scv(CPUPPCState *env, uint32_t lev)
|
||||
{
|
||||
if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) {
|
||||
raise_exception_err(env, POWERPC_EXCP_SYSCALL_VECTORED, lev);
|
||||
} else {
|
||||
raise_exception_err(env, POWERPC_EXCP_FU, FSCR_IC_SCV);
|
||||
}
|
||||
}
|
||||
|
||||
void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "internal.h"
|
||||
|
||||
static int ppc_gdb_register_len_apple(int n)
|
||||
{
|
||||
@ -387,3 +389,259 @@ const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name)
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool avr_need_swap(CPUPPCState *env)
|
||||
{
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
return msr_le;
|
||||
#else
|
||||
return !msr_le;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static int gdb_find_spr_idx(CPUPPCState *env, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
|
||||
ppc_spr_t *spr = &env->spr_cb[i];
|
||||
|
||||
if (spr->name && spr->gdb_id == n) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
int reg;
|
||||
int len;
|
||||
|
||||
reg = gdb_find_spr_idx(env, n);
|
||||
if (reg < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = TARGET_LONG_SIZE;
|
||||
gdb_get_regl(buf, env->spr[reg]);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
int reg;
|
||||
int len;
|
||||
|
||||
reg = gdb_find_spr_idx(env, n);
|
||||
if (reg < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = TARGET_LONG_SIZE;
|
||||
ppc_maybe_bswap_register(env, mem_buf, len);
|
||||
env->spr[reg] = ldn_p(mem_buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
uint8_t *mem_buf;
|
||||
if (n < 32) {
|
||||
gdb_get_reg64(buf, *cpu_fpr_ptr(env, n));
|
||||
mem_buf = gdb_get_reg_ptr(buf, 8);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
return 8;
|
||||
}
|
||||
if (n == 32) {
|
||||
gdb_get_reg32(buf, env->fpscr);
|
||||
mem_buf = gdb_get_reg_ptr(buf, 4);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
*cpu_fpr_ptr(env, n) = ldq_p(mem_buf);
|
||||
return 8;
|
||||
}
|
||||
if (n == 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
store_fpscr(env, ldl_p(mem_buf), 0xffffffff);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
uint8_t *mem_buf;
|
||||
|
||||
if (n < 32) {
|
||||
ppc_avr_t *avr = cpu_avr_ptr(env, n);
|
||||
if (!avr_need_swap(env)) {
|
||||
gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]);
|
||||
} else {
|
||||
gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]);
|
||||
}
|
||||
mem_buf = gdb_get_reg_ptr(buf, 16);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
ppc_maybe_bswap_register(env, mem_buf + 8, 8);
|
||||
return 16;
|
||||
}
|
||||
if (n == 32) {
|
||||
gdb_get_reg32(buf, helper_mfvscr(env));
|
||||
mem_buf = gdb_get_reg_ptr(buf, 4);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
return 4;
|
||||
}
|
||||
if (n == 33) {
|
||||
gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]);
|
||||
mem_buf = gdb_get_reg_ptr(buf, 4);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
ppc_avr_t *avr = cpu_avr_ptr(env, n);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
ppc_maybe_bswap_register(env, mem_buf + 8, 8);
|
||||
if (!avr_need_swap(env)) {
|
||||
avr->u64[0] = ldq_p(mem_buf);
|
||||
avr->u64[1] = ldq_p(mem_buf + 8);
|
||||
} else {
|
||||
avr->u64[1] = ldq_p(mem_buf);
|
||||
avr->u64[0] = ldq_p(mem_buf + 8);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
if (n == 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
helper_mtvscr(env, ldl_p(mem_buf));
|
||||
return 4;
|
||||
}
|
||||
if (n == 33) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
#if defined(TARGET_PPC64)
|
||||
gdb_get_reg32(buf, env->gpr[n] >> 32);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
|
||||
#else
|
||||
gdb_get_reg32(buf, env->gprh[n]);
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
if (n == 32) {
|
||||
gdb_get_reg64(buf, env->spe_acc);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
|
||||
return 8;
|
||||
}
|
||||
if (n == 33) {
|
||||
gdb_get_reg32(buf, env->spe_fscr);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
#if defined(TARGET_PPC64)
|
||||
target_ulong lo = (uint32_t)env->gpr[n];
|
||||
target_ulong hi;
|
||||
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
|
||||
hi = (target_ulong)ldl_p(mem_buf) << 32;
|
||||
env->gpr[n] = lo | hi;
|
||||
#else
|
||||
env->gprh[n] = ldl_p(mem_buf);
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
if (n == 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
env->spe_acc = ldq_p(mem_buf);
|
||||
return 8;
|
||||
}
|
||||
if (n == 33) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
env->spe_fscr = ldl_p(mem_buf);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n));
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
*cpu_vsrl_ptr(env, n) = ldq_p(mem_buf);
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
gchar *ppc_gdb_arch_name(CPUState *cs)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
return g_strdup("powerpc:common64");
|
||||
#else
|
||||
return g_strdup("powerpc:common");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *pcc)
|
||||
{
|
||||
if (pcc->insns_flags & PPC_FLOAT) {
|
||||
gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg,
|
||||
33, "power-fpu.xml", 0);
|
||||
}
|
||||
if (pcc->insns_flags & PPC_ALTIVEC) {
|
||||
gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg,
|
||||
34, "power-altivec.xml", 0);
|
||||
}
|
||||
if (pcc->insns_flags & PPC_SPE) {
|
||||
gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg,
|
||||
34, "power-spe.xml", 0);
|
||||
}
|
||||
if (pcc->insns_flags2 & PPC2_VSX) {
|
||||
gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg,
|
||||
32, "power-vsx.xml", 0);
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg,
|
||||
pcc->gdb_num_sprs, "power-spr.xml", 0);
|
||||
#endif
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ DEF_HELPER_1(rfci, void, env)
|
||||
DEF_HELPER_1(rfdi, void, env)
|
||||
DEF_HELPER_1(rfmci, void, env)
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_2(scv, noreturn, env, i32)
|
||||
DEF_HELPER_2(pminsn, void, env, i32)
|
||||
DEF_HELPER_1(rfid, void, env)
|
||||
DEF_HELPER_1(rfscv, void, env)
|
||||
|
280
target/ppc/helper_regs.c
Normal file
280
target/ppc/helper_regs.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* PowerPC emulation special registers manipulation helpers for qemu.
|
||||
*
|
||||
* Copyright (c) 2003-2007 Jocelyn Mayer
|
||||
*
|
||||
* 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.1 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 "qemu/main-loop.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "helper_regs.h"
|
||||
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
void hreg_swap_gpr_tgpr(CPUPPCState *env)
|
||||
{
|
||||
target_ulong tmp;
|
||||
|
||||
tmp = env->gpr[0];
|
||||
env->gpr[0] = env->tgpr[0];
|
||||
env->tgpr[0] = tmp;
|
||||
tmp = env->gpr[1];
|
||||
env->gpr[1] = env->tgpr[1];
|
||||
env->tgpr[1] = tmp;
|
||||
tmp = env->gpr[2];
|
||||
env->gpr[2] = env->tgpr[2];
|
||||
env->tgpr[2] = tmp;
|
||||
tmp = env->gpr[3];
|
||||
env->gpr[3] = env->tgpr[3];
|
||||
env->tgpr[3] = tmp;
|
||||
}
|
||||
|
||||
static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
|
||||
{
|
||||
target_ulong msr = env->msr;
|
||||
uint32_t ppc_flags = env->flags;
|
||||
uint32_t hflags = 0;
|
||||
uint32_t msr_mask;
|
||||
|
||||
/* Some bits come straight across from MSR. */
|
||||
QEMU_BUILD_BUG_ON(MSR_LE != HFLAGS_LE);
|
||||
QEMU_BUILD_BUG_ON(MSR_PR != HFLAGS_PR);
|
||||
QEMU_BUILD_BUG_ON(MSR_DR != HFLAGS_DR);
|
||||
QEMU_BUILD_BUG_ON(MSR_FP != HFLAGS_FP);
|
||||
msr_mask = ((1 << MSR_LE) | (1 << MSR_PR) |
|
||||
(1 << MSR_DR) | (1 << MSR_FP));
|
||||
|
||||
if (ppc_flags & POWERPC_FLAG_HID0_LE) {
|
||||
/*
|
||||
* Note that MSR_LE is not set in env->msr_mask for this cpu,
|
||||
* and so will never be set in msr.
|
||||
*/
|
||||
uint32_t le = extract32(env->spr[SPR_HID0], 3, 1);
|
||||
hflags |= le << MSR_LE;
|
||||
}
|
||||
|
||||
if (ppc_flags & POWERPC_FLAG_DE) {
|
||||
target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0];
|
||||
if (dbcr0 & DBCR0_ICMP) {
|
||||
hflags |= 1 << HFLAGS_SE;
|
||||
}
|
||||
if (dbcr0 & DBCR0_BRT) {
|
||||
hflags |= 1 << HFLAGS_BE;
|
||||
}
|
||||
} else {
|
||||
if (ppc_flags & POWERPC_FLAG_BE) {
|
||||
QEMU_BUILD_BUG_ON(MSR_BE != HFLAGS_BE);
|
||||
msr_mask |= 1 << MSR_BE;
|
||||
}
|
||||
if (ppc_flags & POWERPC_FLAG_SE) {
|
||||
QEMU_BUILD_BUG_ON(MSR_SE != HFLAGS_SE);
|
||||
msr_mask |= 1 << MSR_SE;
|
||||
}
|
||||
}
|
||||
|
||||
if (msr_is_64bit(env, msr)) {
|
||||
hflags |= 1 << HFLAGS_64;
|
||||
}
|
||||
if ((ppc_flags & POWERPC_FLAG_SPE) && (msr & (1 << MSR_SPE))) {
|
||||
hflags |= 1 << HFLAGS_SPE;
|
||||
}
|
||||
if (ppc_flags & POWERPC_FLAG_VRE) {
|
||||
QEMU_BUILD_BUG_ON(MSR_VR != HFLAGS_VR);
|
||||
msr_mask |= 1 << MSR_VR;
|
||||
}
|
||||
if (ppc_flags & POWERPC_FLAG_VSX) {
|
||||
QEMU_BUILD_BUG_ON(MSR_VSX != HFLAGS_VSX);
|
||||
msr_mask |= 1 << MSR_VSX;
|
||||
}
|
||||
if ((ppc_flags & POWERPC_FLAG_TM) && (msr & (1ull << MSR_TM))) {
|
||||
hflags |= 1 << HFLAGS_TM;
|
||||
}
|
||||
if (env->spr[SPR_LPCR] & LPCR_GTSE) {
|
||||
hflags |= 1 << HFLAGS_GTSE;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) {
|
||||
hflags |= 1 << HFLAGS_HV;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is our encoding for server processors. The architecture
|
||||
* specifies that there is no such thing as userspace with
|
||||
* translation off, however it appears that MacOS does it and some
|
||||
* 32-bit CPUs support it. Weird...
|
||||
*
|
||||
* 0 = Guest User space virtual mode
|
||||
* 1 = Guest Kernel space virtual mode
|
||||
* 2 = Guest User space real mode
|
||||
* 3 = Guest Kernel space real mode
|
||||
* 4 = HV User space virtual mode
|
||||
* 5 = HV Kernel space virtual mode
|
||||
* 6 = HV User space real mode
|
||||
* 7 = HV Kernel space real mode
|
||||
*
|
||||
* For BookE, we need 8 MMU modes as follow:
|
||||
*
|
||||
* 0 = AS 0 HV User space
|
||||
* 1 = AS 0 HV Kernel space
|
||||
* 2 = AS 1 HV User space
|
||||
* 3 = AS 1 HV Kernel space
|
||||
* 4 = AS 0 Guest User space
|
||||
* 5 = AS 0 Guest Kernel space
|
||||
* 6 = AS 1 Guest User space
|
||||
* 7 = AS 1 Guest Kernel space
|
||||
*/
|
||||
unsigned immu_idx, dmmu_idx;
|
||||
dmmu_idx = msr & (1 << MSR_PR) ? 0 : 1;
|
||||
if (env->mmu_model & POWERPC_MMU_BOOKE) {
|
||||
dmmu_idx |= msr & (1 << MSR_GS) ? 4 : 0;
|
||||
immu_idx = dmmu_idx;
|
||||
immu_idx |= msr & (1 << MSR_IS) ? 2 : 0;
|
||||
dmmu_idx |= msr & (1 << MSR_DS) ? 2 : 0;
|
||||
} else {
|
||||
dmmu_idx |= msr & (1ull << MSR_HV) ? 4 : 0;
|
||||
immu_idx = dmmu_idx;
|
||||
immu_idx |= msr & (1 << MSR_IR) ? 0 : 2;
|
||||
dmmu_idx |= msr & (1 << MSR_DR) ? 0 : 2;
|
||||
}
|
||||
hflags |= immu_idx << HFLAGS_IMMU_IDX;
|
||||
hflags |= dmmu_idx << HFLAGS_DMMU_IDX;
|
||||
#endif
|
||||
|
||||
return hflags | (msr & msr_mask);
|
||||
}
|
||||
|
||||
void hreg_compute_hflags(CPUPPCState *env)
|
||||
{
|
||||
env->hflags = hreg_compute_hflags_value(env);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *flags)
|
||||
{
|
||||
uint32_t hflags_current = env->hflags;
|
||||
uint32_t hflags_rebuilt;
|
||||
|
||||
*pc = env->nip;
|
||||
*cs_base = 0;
|
||||
*flags = hflags_current;
|
||||
|
||||
hflags_rebuilt = hreg_compute_hflags_value(env);
|
||||
if (unlikely(hflags_current != hflags_rebuilt)) {
|
||||
cpu_abort(env_cpu(env),
|
||||
"TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n",
|
||||
hflags_current, hflags_rebuilt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void cpu_interrupt_exittb(CPUState *cs)
|
||||
{
|
||||
if (!kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
qemu_mutex_unlock_iothread();
|
||||
} else {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
}
|
||||
}
|
||||
|
||||
int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv)
|
||||
{
|
||||
int excp;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
CPUState *cs = env_cpu(env);
|
||||
#endif
|
||||
|
||||
excp = 0;
|
||||
value &= env->msr_mask;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Neither mtmsr nor guest state can alter HV */
|
||||
if (!alter_hv || !(env->msr & MSR_HVB)) {
|
||||
value &= ~MSR_HVB;
|
||||
value |= env->msr & MSR_HVB;
|
||||
}
|
||||
if (((value >> MSR_IR) & 1) != msr_ir ||
|
||||
((value >> MSR_DR) & 1) != msr_dr) {
|
||||
cpu_interrupt_exittb(cs);
|
||||
}
|
||||
if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
|
||||
((value >> MSR_GS) & 1) != msr_gs) {
|
||||
cpu_interrupt_exittb(cs);
|
||||
}
|
||||
if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
|
||||
((value ^ env->msr) & (1 << MSR_TGPR)))) {
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
hreg_swap_gpr_tgpr(env);
|
||||
}
|
||||
if (unlikely((value >> MSR_EP) & 1) != msr_ep) {
|
||||
/* Change the exception prefix on PowerPC 601 */
|
||||
env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
|
||||
}
|
||||
/*
|
||||
* If PR=1 then EE, IR and DR must be 1
|
||||
*
|
||||
* Note: We only enforce this on 64-bit server processors.
|
||||
* It appears that:
|
||||
* - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS
|
||||
* exploits it.
|
||||
* - 64-bit embedded implementations do not need any operation to be
|
||||
* performed when PR is set.
|
||||
*/
|
||||
if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) {
|
||||
value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR);
|
||||
}
|
||||
#endif
|
||||
env->msr = value;
|
||||
hreg_compute_hflags(env);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (unlikely(msr_pow == 1)) {
|
||||
if (!env->pending_interrupts && (*env->check_pow)(env)) {
|
||||
cs->halted = 1;
|
||||
excp = EXCP_HALTED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return excp;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
void check_tlb_flush(CPUPPCState *env, bool global)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
/* Handle global flushes first */
|
||||
if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
|
||||
env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
|
||||
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
|
||||
tlb_flush_all_cpus_synced(cs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Then handle local ones */
|
||||
if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
|
||||
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
|
||||
tlb_flush(cs);
|
||||
}
|
||||
}
|
||||
#endif
|
@ -20,184 +20,15 @@
|
||||
#ifndef HELPER_REGS_H
|
||||
#define HELPER_REGS_H
|
||||
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/kvm.h"
|
||||
void hreg_swap_gpr_tgpr(CPUPPCState *env);
|
||||
void hreg_compute_hflags(CPUPPCState *env);
|
||||
void cpu_interrupt_exittb(CPUState *cs);
|
||||
int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv);
|
||||
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
static inline void hreg_swap_gpr_tgpr(CPUPPCState *env)
|
||||
{
|
||||
target_ulong tmp;
|
||||
|
||||
tmp = env->gpr[0];
|
||||
env->gpr[0] = env->tgpr[0];
|
||||
env->tgpr[0] = tmp;
|
||||
tmp = env->gpr[1];
|
||||
env->gpr[1] = env->tgpr[1];
|
||||
env->tgpr[1] = tmp;
|
||||
tmp = env->gpr[2];
|
||||
env->gpr[2] = env->tgpr[2];
|
||||
env->tgpr[2] = tmp;
|
||||
tmp = env->gpr[3];
|
||||
env->gpr[3] = env->tgpr[3];
|
||||
env->tgpr[3] = tmp;
|
||||
}
|
||||
|
||||
static inline void hreg_compute_mem_idx(CPUPPCState *env)
|
||||
{
|
||||
/*
|
||||
* This is our encoding for server processors. The architecture
|
||||
* specifies that there is no such thing as userspace with
|
||||
* translation off, however it appears that MacOS does it and some
|
||||
* 32-bit CPUs support it. Weird...
|
||||
*
|
||||
* 0 = Guest User space virtual mode
|
||||
* 1 = Guest Kernel space virtual mode
|
||||
* 2 = Guest User space real mode
|
||||
* 3 = Guest Kernel space real mode
|
||||
* 4 = HV User space virtual mode
|
||||
* 5 = HV Kernel space virtual mode
|
||||
* 6 = HV User space real mode
|
||||
* 7 = HV Kernel space real mode
|
||||
*
|
||||
* For BookE, we need 8 MMU modes as follow:
|
||||
*
|
||||
* 0 = AS 0 HV User space
|
||||
* 1 = AS 0 HV Kernel space
|
||||
* 2 = AS 1 HV User space
|
||||
* 3 = AS 1 HV Kernel space
|
||||
* 4 = AS 0 Guest User space
|
||||
* 5 = AS 0 Guest Kernel space
|
||||
* 6 = AS 1 Guest User space
|
||||
* 7 = AS 1 Guest Kernel space
|
||||
*/
|
||||
if (env->mmu_model & POWERPC_MMU_BOOKE) {
|
||||
env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1;
|
||||
env->immu_idx += msr_is ? 2 : 0;
|
||||
env->dmmu_idx += msr_ds ? 2 : 0;
|
||||
env->immu_idx += msr_gs ? 4 : 0;
|
||||
env->dmmu_idx += msr_gs ? 4 : 0;
|
||||
} else {
|
||||
env->immu_idx = env->dmmu_idx = msr_pr ? 0 : 1;
|
||||
env->immu_idx += msr_ir ? 0 : 2;
|
||||
env->dmmu_idx += msr_dr ? 0 : 2;
|
||||
env->immu_idx += msr_hv ? 4 : 0;
|
||||
env->dmmu_idx += msr_hv ? 4 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hreg_compute_hflags(CPUPPCState *env)
|
||||
{
|
||||
target_ulong hflags_mask;
|
||||
|
||||
/* We 'forget' FE0 & FE1: we'll never generate imprecise exceptions */
|
||||
hflags_mask = (1 << MSR_VR) | (1 << MSR_AP) | (1 << MSR_SA) |
|
||||
(1 << MSR_PR) | (1 << MSR_FP) | (1 << MSR_SE) | (1 << MSR_BE) |
|
||||
(1 << MSR_LE) | (1 << MSR_VSX) | (1 << MSR_IR) | (1 << MSR_DR);
|
||||
hflags_mask |= (1ULL << MSR_CM) | (1ULL << MSR_SF) | MSR_HVB;
|
||||
hreg_compute_mem_idx(env);
|
||||
env->hflags = env->msr & hflags_mask;
|
||||
/* Merge with hflags coming from other registers */
|
||||
env->hflags |= env->hflags_nmsr;
|
||||
}
|
||||
|
||||
static inline void cpu_interrupt_exittb(CPUState *cs)
|
||||
{
|
||||
if (!kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
qemu_mutex_unlock_iothread();
|
||||
} else {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
|
||||
int alter_hv)
|
||||
{
|
||||
int excp;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
CPUState *cs = env_cpu(env);
|
||||
#endif
|
||||
|
||||
excp = 0;
|
||||
value &= env->msr_mask;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Neither mtmsr nor guest state can alter HV */
|
||||
if (!alter_hv || !(env->msr & MSR_HVB)) {
|
||||
value &= ~MSR_HVB;
|
||||
value |= env->msr & MSR_HVB;
|
||||
}
|
||||
if (((value >> MSR_IR) & 1) != msr_ir ||
|
||||
((value >> MSR_DR) & 1) != msr_dr) {
|
||||
cpu_interrupt_exittb(cs);
|
||||
}
|
||||
if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
|
||||
((value >> MSR_GS) & 1) != msr_gs) {
|
||||
cpu_interrupt_exittb(cs);
|
||||
}
|
||||
if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
|
||||
((value ^ env->msr) & (1 << MSR_TGPR)))) {
|
||||
/* Swap temporary saved registers with GPRs */
|
||||
hreg_swap_gpr_tgpr(env);
|
||||
}
|
||||
if (unlikely((value >> MSR_EP) & 1) != msr_ep) {
|
||||
/* Change the exception prefix on PowerPC 601 */
|
||||
env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
|
||||
}
|
||||
/*
|
||||
* If PR=1 then EE, IR and DR must be 1
|
||||
*
|
||||
* Note: We only enforce this on 64-bit server processors.
|
||||
* It appears that:
|
||||
* - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS
|
||||
* exploits it.
|
||||
* - 64-bit embedded implementations do not need any operation to be
|
||||
* performed when PR is set.
|
||||
*/
|
||||
if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) {
|
||||
value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR);
|
||||
}
|
||||
#endif
|
||||
env->msr = value;
|
||||
hreg_compute_hflags(env);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (unlikely(msr_pow == 1)) {
|
||||
if (!env->pending_interrupts && (*env->check_pow)(env)) {
|
||||
cs->halted = 1;
|
||||
excp = EXCP_HALTED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return excp;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static inline void check_tlb_flush(CPUPPCState *env, bool global)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
/* Handle global flushes first */
|
||||
if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
|
||||
env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
|
||||
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
|
||||
tlb_flush_all_cpus_synced(cs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Then handle local ones */
|
||||
if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
|
||||
env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
|
||||
tlb_flush(cs);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline void check_tlb_flush(CPUPPCState *env, bool global) { }
|
||||
#else
|
||||
void check_tlb_flush(CPUPPCState *env, bool global);
|
||||
#endif
|
||||
|
||||
#endif /* HELPER_REGS_H */
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "internal.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "crypto/aes.h"
|
||||
#include "fpu/softfloat.h"
|
||||
|
@ -215,4 +215,17 @@ void helper_compute_fprf_float128(CPUPPCState *env, float128 arg);
|
||||
void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
|
||||
MMUAccessType access_type,
|
||||
int mmu_idx, uintptr_t retaddr);
|
||||
|
||||
/* translate.c */
|
||||
|
||||
/* #define PPC_DUMP_CPU */
|
||||
|
||||
int ppc_fixup_cpu(PowerPCCPU *cpu);
|
||||
void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp);
|
||||
void destroy_ppc_opcodes(PowerPCCPU *cpu);
|
||||
|
||||
/* gdbstub.c */
|
||||
void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *ppc);
|
||||
gchar *ppc_gdb_arch_name(CPUState *cs);
|
||||
|
||||
#endif /* PPC_INTERNAL_H */
|
||||
|
@ -10,6 +10,18 @@
|
||||
#include "kvm_ppc.h"
|
||||
#include "exec/helper-proto.h"
|
||||
|
||||
static void post_load_update_msr(CPUPPCState *env)
|
||||
{
|
||||
target_ulong msr = env->msr;
|
||||
|
||||
/*
|
||||
* Invalidate all supported msr bits except MSR_TGPR/MSR_HVB
|
||||
* before restoring. Note that this recomputes hflags.
|
||||
*/
|
||||
env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB);
|
||||
ppc_store_msr(env, msr);
|
||||
}
|
||||
|
||||
static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
@ -111,13 +123,12 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
|
||||
qemu_get_betls(f, &env->ivpr_mask);
|
||||
qemu_get_betls(f, &env->hreset_vector);
|
||||
qemu_get_betls(f, &env->nip);
|
||||
qemu_get_betls(f, &env->hflags);
|
||||
qemu_get_betls(f, &env->hflags_nmsr);
|
||||
qemu_get_sbetl(f); /* Discard unused hflags */
|
||||
qemu_get_sbetl(f); /* Discard unused hflags_nmsr */
|
||||
qemu_get_sbe32(f); /* Discard unused mmu_idx */
|
||||
qemu_get_sbe32(f); /* Discard unused power_mode */
|
||||
|
||||
/* Recompute mmu indices */
|
||||
hreg_compute_mem_idx(env);
|
||||
post_load_update_msr(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -304,6 +315,10 @@ static int cpu_pre_save(void *opaque)
|
||||
}
|
||||
}
|
||||
|
||||
/* Retain migration compatibility for pre 6.0 for 601 machines. */
|
||||
env->hflags_compat_nmsr = (env->flags & POWERPC_FLAG_HID0_LE
|
||||
? env->hflags & MSR_LE : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -333,7 +348,6 @@ static int cpu_post_load(void *opaque, int version_id)
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
int i;
|
||||
target_ulong msr;
|
||||
|
||||
/*
|
||||
* If we're operating in compat mode, we should be ok as long as
|
||||
@ -407,15 +421,7 @@ static int cpu_post_load(void *opaque, int version_id)
|
||||
ppc_store_sdr1(env, env->spr[SPR_SDR1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate all supported msr bits except MSR_TGPR/MSR_HVB
|
||||
* before restoring
|
||||
*/
|
||||
msr = env->msr;
|
||||
env->msr ^= env->msr_mask & ~((1ULL << MSR_TGPR) | MSR_HVB);
|
||||
ppc_store_msr(env, msr);
|
||||
|
||||
hreg_compute_mem_idx(env);
|
||||
post_load_update_msr(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -825,9 +831,8 @@ const VMStateDescription vmstate_ppc_cpu = {
|
||||
/* Supervisor mode architected state */
|
||||
VMSTATE_UINTTL(env.msr, PowerPCCPU),
|
||||
|
||||
/* Internal state */
|
||||
VMSTATE_UINTTL(env.hflags_nmsr, PowerPCCPU),
|
||||
/* FIXME: access_type? */
|
||||
/* Backward compatible internal state */
|
||||
VMSTATE_UINTTL(env.hflags_compat_nmsr, PowerPCCPU),
|
||||
|
||||
/* Sanity checking */
|
||||
VMSTATE_UINTTL_TEST(mig_msr_mask, PowerPCCPU, cpu_pre_2_8_migration),
|
||||
|
@ -278,7 +278,7 @@ static void dcbz_common(CPUPPCState *env, target_ulong addr,
|
||||
target_ulong mask, dcbz_size = env->dcache_line_size;
|
||||
uint32_t i;
|
||||
void *haddr;
|
||||
int mmu_idx = epid ? PPC_TLB_EPID_STORE : env->dmmu_idx;
|
||||
int mmu_idx = epid ? PPC_TLB_EPID_STORE : cpu_mmu_index(env, false);
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
/* Check for dcbz vs dcbzl on 970 */
|
||||
|
@ -6,6 +6,7 @@ ppc_ss.add(files(
|
||||
'excp_helper.c',
|
||||
'fpu_helper.c',
|
||||
'gdbstub.c',
|
||||
'helper_regs.c',
|
||||
'int_helper.c',
|
||||
'mem_helper.c',
|
||||
'misc_helper.c',
|
||||
|
@ -194,16 +194,14 @@ void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
|
||||
target_ulong hid0;
|
||||
|
||||
hid0 = env->spr[SPR_HID0];
|
||||
env->spr[SPR_HID0] = (uint32_t)val;
|
||||
|
||||
if ((val ^ hid0) & 0x00000008) {
|
||||
/* Change current endianness */
|
||||
env->hflags &= ~(1 << MSR_LE);
|
||||
env->hflags_nmsr &= ~(1 << MSR_LE);
|
||||
env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE);
|
||||
env->hflags |= env->hflags_nmsr;
|
||||
qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__,
|
||||
hreg_compute_hflags(env);
|
||||
qemu_log("%s: set endianness to %c => %08x\n", __func__,
|
||||
val & 0x8 ? 'l' : 'b', env->hflags);
|
||||
}
|
||||
env->spr[SPR_HID0] = (uint32_t)val;
|
||||
}
|
||||
|
||||
void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value)
|
||||
@ -217,6 +215,9 @@ void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value)
|
||||
|
||||
void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
/* Bits 26 & 27 affect single-stepping. */
|
||||
hreg_compute_hflags(env);
|
||||
/* Bits 28 & 29 affect reset or shutdown. */
|
||||
store_40x_dbcr0(env, val);
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "exec/log.h"
|
||||
#include "hw/hw.h"
|
||||
#include "mmu-book3s-v3.h"
|
||||
#include "helper_regs.h"
|
||||
|
||||
/* #define DEBUG_SLB */
|
||||
|
||||
@ -1125,6 +1126,8 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val)
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
env->spr[SPR_LPCR] = val & pcc->lpcr_mask;
|
||||
/* The gtse bit affects hflags */
|
||||
hreg_compute_hflags(env);
|
||||
}
|
||||
|
||||
void helper_store_lpcr(CPUPPCState *env, target_ulong val)
|
||||
|
@ -173,7 +173,6 @@ struct DisasContext {
|
||||
bool vsx_enabled;
|
||||
bool spe_enabled;
|
||||
bool tm_enabled;
|
||||
bool scv_enabled;
|
||||
bool gtse;
|
||||
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
|
||||
int singlestep_enabled;
|
||||
@ -4081,15 +4080,16 @@ static void gen_sc(DisasContext *ctx)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static void gen_scv(DisasContext *ctx)
|
||||
{
|
||||
uint32_t lev;
|
||||
uint32_t lev = (ctx->opcode >> 5) & 0x7F;
|
||||
|
||||
if (unlikely(!ctx->scv_enabled)) {
|
||||
gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_SCV);
|
||||
return;
|
||||
/* Set the PC back to the faulting instruction. */
|
||||
if (ctx->exception == POWERPC_EXCP_NONE) {
|
||||
gen_update_nip(ctx, ctx->base.pc_next - 4);
|
||||
}
|
||||
gen_helper_scv(cpu_env, tcg_constant_i32(lev));
|
||||
|
||||
lev = (ctx->opcode >> 5) & 0x7F;
|
||||
gen_exception_err(ctx, POWERPC_SYSCALL_VECTORED, lev);
|
||||
/* This need not be exact, just not POWERPC_EXCP_NONE */
|
||||
ctx->exception = POWERPC_SYSCALL_VECTORED;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@ -7657,9 +7657,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
env->nip, env->lr, env->ctr, cpu_read_xer(env),
|
||||
cs->cpu_index);
|
||||
qemu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF "
|
||||
TARGET_FMT_lx " iidx %d didx %d\n",
|
||||
env->msr, env->spr[SPR_HID0],
|
||||
env->hflags, env->immu_idx, env->dmmu_idx);
|
||||
"%08x iidx %d didx %d\n",
|
||||
env->msr, env->spr[SPR_HID0], env->hflags,
|
||||
cpu_mmu_index(env, true), cpu_mmu_index(env, false));
|
||||
#if !defined(NO_TIMER_DUMP)
|
||||
qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@ -7731,7 +7731,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
#if defined(TARGET_PPC64)
|
||||
if (env->excp_model == POWERPC_EXCP_POWER7 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER8 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER9) {
|
||||
env->excp_model == POWERPC_EXCP_POWER9 ||
|
||||
env->excp_model == POWERPC_EXCP_POWER10) {
|
||||
qemu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n",
|
||||
env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
|
||||
}
|
||||
@ -7825,6 +7826,400 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
#undef RFPL
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Opcode types */
|
||||
enum {
|
||||
PPC_DIRECT = 0, /* Opcode routine */
|
||||
PPC_INDIRECT = 1, /* Indirect opcode table */
|
||||
};
|
||||
|
||||
#define PPC_OPCODE_MASK 0x3
|
||||
|
||||
static inline int is_indirect_opcode(void *handler)
|
||||
{
|
||||
return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT;
|
||||
}
|
||||
|
||||
static inline opc_handler_t **ind_table(void *handler)
|
||||
{
|
||||
return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK);
|
||||
}
|
||||
|
||||
/* Instruction table creation */
|
||||
/* Opcodes tables creation */
|
||||
static void fill_new_table(opc_handler_t **table, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
table[i] = &invalid_handler;
|
||||
}
|
||||
}
|
||||
|
||||
static int create_new_table(opc_handler_t **table, unsigned char idx)
|
||||
{
|
||||
opc_handler_t **tmp;
|
||||
|
||||
tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||
fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||
table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insert_in_table(opc_handler_t **table, unsigned char idx,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
if (table[idx] != &invalid_handler) {
|
||||
return -1;
|
||||
}
|
||||
table[idx] = handler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_direct_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx, opc_handler_t *handler)
|
||||
{
|
||||
if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
|
||||
printf("*** ERROR: opcode %02x already assigned in main "
|
||||
"opcode table\n", idx);
|
||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||
ppc_opcodes[idx]->oname, handler->oname);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_ind_in_table(opc_handler_t **table,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
if (table[idx1] == &invalid_handler) {
|
||||
if (create_new_table(table, idx1) < 0) {
|
||||
printf("*** ERROR: unable to create indirect table "
|
||||
"idx=%02x\n", idx1);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (!is_indirect_opcode(table[idx1])) {
|
||||
printf("*** ERROR: idx %02x already assigned to a direct "
|
||||
"opcode\n", idx1);
|
||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (handler != NULL &&
|
||||
insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
|
||||
printf("*** ERROR: opcode %02x already assigned in "
|
||||
"opcode table %02x\n", idx2, idx1);
|
||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_ind_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
return register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
|
||||
}
|
||||
|
||||
static int register_dblind_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
unsigned char idx3, opc_handler_t *handler)
|
||||
{
|
||||
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
||||
printf("*** ERROR: unable to join indirect table idx "
|
||||
"[%02x-%02x]\n", idx1, idx2);
|
||||
return -1;
|
||||
}
|
||||
if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
|
||||
handler) < 0) {
|
||||
printf("*** ERROR: unable to insert opcode "
|
||||
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_trplind_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
unsigned char idx3, unsigned char idx4,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
opc_handler_t **table;
|
||||
|
||||
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
||||
printf("*** ERROR: unable to join indirect table idx "
|
||||
"[%02x-%02x]\n", idx1, idx2);
|
||||
return -1;
|
||||
}
|
||||
table = ind_table(ppc_opcodes[idx1]);
|
||||
if (register_ind_in_table(table, idx2, idx3, NULL) < 0) {
|
||||
printf("*** ERROR: unable to join 2nd-level indirect table idx "
|
||||
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
||||
return -1;
|
||||
}
|
||||
table = ind_table(table[idx2]);
|
||||
if (register_ind_in_table(table, idx3, idx4, handler) < 0) {
|
||||
printf("*** ERROR: unable to insert opcode "
|
||||
"[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn)
|
||||
{
|
||||
if (insn->opc2 != 0xFF) {
|
||||
if (insn->opc3 != 0xFF) {
|
||||
if (insn->opc4 != 0xFF) {
|
||||
if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
||||
insn->opc3, insn->opc4,
|
||||
&insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
||||
insn->opc3, &insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (register_ind_insn(ppc_opcodes, insn->opc1,
|
||||
insn->opc2, &insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_opcode_table(opc_handler_t **table, int len)
|
||||
{
|
||||
int i, count, tmp;
|
||||
|
||||
for (i = 0, count = 0; i < len; i++) {
|
||||
/* Consistency fixup */
|
||||
if (table[i] == NULL) {
|
||||
table[i] = &invalid_handler;
|
||||
}
|
||||
if (table[i] != &invalid_handler) {
|
||||
if (is_indirect_opcode(table[i])) {
|
||||
tmp = test_opcode_table(ind_table(table[i]),
|
||||
PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||
if (tmp == 0) {
|
||||
free(table[i]);
|
||||
table[i] = &invalid_handler;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void fix_opcode_tables(opc_handler_t **ppc_opcodes)
|
||||
{
|
||||
if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) {
|
||||
printf("*** WARNING: no opcode defined !\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
|
||||
{
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||
opcode_t *opc;
|
||||
|
||||
fill_new_table(cpu->opcodes, PPC_CPU_OPCODES_LEN);
|
||||
for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
|
||||
if (((opc->handler.type & pcc->insns_flags) != 0) ||
|
||||
((opc->handler.type2 & pcc->insns_flags2) != 0)) {
|
||||
if (register_insn(cpu->opcodes, opc) < 0) {
|
||||
error_setg(errp, "ERROR initializing PowerPC instruction "
|
||||
"0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2,
|
||||
opc->opc3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fix_opcode_tables(cpu->opcodes);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
void destroy_ppc_opcodes(PowerPCCPU *cpu)
|
||||
{
|
||||
opc_handler_t **table, **table_2;
|
||||
int i, j, k;
|
||||
|
||||
for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) {
|
||||
if (cpu->opcodes[i] == &invalid_handler) {
|
||||
continue;
|
||||
}
|
||||
if (is_indirect_opcode(cpu->opcodes[i])) {
|
||||
table = ind_table(cpu->opcodes[i]);
|
||||
for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) {
|
||||
if (table[j] == &invalid_handler) {
|
||||
continue;
|
||||
}
|
||||
if (is_indirect_opcode(table[j])) {
|
||||
table_2 = ind_table(table[j]);
|
||||
for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) {
|
||||
if (table_2[k] != &invalid_handler &&
|
||||
is_indirect_opcode(table_2[k])) {
|
||||
g_free((opc_handler_t *)((uintptr_t)table_2[k] &
|
||||
~PPC_INDIRECT));
|
||||
}
|
||||
}
|
||||
g_free((opc_handler_t *)((uintptr_t)table[j] &
|
||||
~PPC_INDIRECT));
|
||||
}
|
||||
}
|
||||
g_free((opc_handler_t *)((uintptr_t)cpu->opcodes[i] &
|
||||
~PPC_INDIRECT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PPC_DUMP_CPU)
|
||||
static void dump_ppc_insns(CPUPPCState *env)
|
||||
{
|
||||
opc_handler_t **table, *handler;
|
||||
const char *p, *q;
|
||||
uint8_t opc1, opc2, opc3, opc4;
|
||||
|
||||
printf("Instructions set:\n");
|
||||
/* opc1 is 6 bits long */
|
||||
for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) {
|
||||
table = env->opcodes;
|
||||
handler = table[opc1];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
/* opc2 is 5 bits long */
|
||||
for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) {
|
||||
table = env->opcodes;
|
||||
handler = env->opcodes[opc1];
|
||||
table = ind_table(handler);
|
||||
handler = table[opc2];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
/* opc3 is 5 bits long */
|
||||
for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
||||
opc3++) {
|
||||
handler = table[opc3];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
/* opc4 is 5 bits long */
|
||||
for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
||||
opc4++) {
|
||||
handler = table[opc4];
|
||||
if (handler->handler != &gen_invalid) {
|
||||
printf("INSN: %02x %02x %02x %02x -- "
|
||||
"(%02d %04d %02d) : %s\n",
|
||||
opc1, opc2, opc3, opc4,
|
||||
opc1, (opc3 << 5) | opc2, opc4,
|
||||
handler->oname);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (handler->handler != &gen_invalid) {
|
||||
/* Special hack to properly dump SPE insns */
|
||||
p = strchr(handler->oname, '_');
|
||||
if (p == NULL) {
|
||||
printf("INSN: %02x %02x %02x (%02d %04d) : "
|
||||
"%s\n",
|
||||
opc1, opc2, opc3, opc1,
|
||||
(opc3 << 5) | opc2,
|
||||
handler->oname);
|
||||
} else {
|
||||
q = "speundef";
|
||||
if ((p - handler->oname) != strlen(q)
|
||||
|| (memcmp(handler->oname, q, strlen(q))
|
||||
!= 0)) {
|
||||
/* First instruction */
|
||||
printf("INSN: %02x %02x %02x"
|
||||
"(%02d %04d) : %.*s\n",
|
||||
opc1, opc2 << 1, opc3, opc1,
|
||||
(opc3 << 6) | (opc2 << 1),
|
||||
(int)(p - handler->oname),
|
||||
handler->oname);
|
||||
}
|
||||
if (strcmp(p + 1, q) != 0) {
|
||||
/* Second instruction */
|
||||
printf("INSN: %02x %02x %02x "
|
||||
"(%02d %04d) : %s\n", opc1,
|
||||
(opc2 << 1) | 1, opc3, opc1,
|
||||
(opc3 << 6) | (opc2 << 1) | 1,
|
||||
p + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (handler->handler != &gen_invalid) {
|
||||
printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
|
||||
opc1, opc2, opc1, opc2, handler->oname);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (handler->handler != &gen_invalid) {
|
||||
printf("INSN: %02x -- -- (%02d ----) : %s\n",
|
||||
opc1, opc1, handler->oname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int ppc_fixup_cpu(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
/*
|
||||
* TCG doesn't (yet) emulate some groups of instructions that are
|
||||
* implemented on some otherwise supported CPUs (e.g. VSX and
|
||||
* decimal floating point instructions on POWER7). We remove
|
||||
* unsupported instruction groups from the cpu state's instruction
|
||||
* masks and hope the guest can cope. For at least the pseries
|
||||
* machine, the unavailability of these instructions can be
|
||||
* advertised to the guest via the device tree.
|
||||
*/
|
||||
if ((env->insns_flags & ~PPC_TCG_INSNS)
|
||||
|| (env->insns_flags2 & ~PPC_TCG_INSNS2)) {
|
||||
warn_report("Disabling some instructions which are not "
|
||||
"emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")",
|
||||
env->insns_flags & ~PPC_TCG_INSNS,
|
||||
env->insns_flags2 & ~PPC_TCG_INSNS2);
|
||||
}
|
||||
env->insns_flags &= PPC_TCG_INSNS;
|
||||
env->insns_flags2 &= PPC_TCG_INSNS2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ppc_cpu_dump_statistics(CPUState *cs, int flags)
|
||||
{
|
||||
#if defined(DO_PPC_STATISTICS)
|
||||
@ -7879,87 +8274,47 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
CPUPPCState *env = cs->env_ptr;
|
||||
uint32_t hflags = ctx->base.tb->flags;
|
||||
int bound;
|
||||
|
||||
ctx->exception = POWERPC_EXCP_NONE;
|
||||
ctx->spr_cb = env->spr_cb;
|
||||
ctx->pr = msr_pr;
|
||||
ctx->mem_idx = env->dmmu_idx;
|
||||
ctx->dr = msr_dr;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
ctx->hv = msr_hv || !env->has_hv_mode;
|
||||
#endif
|
||||
ctx->pr = (hflags >> HFLAGS_PR) & 1;
|
||||
ctx->mem_idx = (hflags >> HFLAGS_DMMU_IDX) & 7;
|
||||
ctx->dr = (hflags >> HFLAGS_DR) & 1;
|
||||
ctx->hv = (hflags >> HFLAGS_HV) & 1;
|
||||
ctx->insns_flags = env->insns_flags;
|
||||
ctx->insns_flags2 = env->insns_flags2;
|
||||
ctx->access_type = -1;
|
||||
ctx->need_access_type = !mmu_is_64bit(env->mmu_model);
|
||||
ctx->le_mode = !!(env->hflags & (1 << MSR_LE));
|
||||
ctx->le_mode = (hflags >> HFLAGS_LE) & 1;
|
||||
ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE;
|
||||
ctx->flags = env->flags;
|
||||
#if defined(TARGET_PPC64)
|
||||
ctx->sf_mode = msr_is_64bit(env, env->msr);
|
||||
ctx->sf_mode = (hflags >> HFLAGS_64) & 1;
|
||||
ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
|
||||
#endif
|
||||
ctx->lazy_tlb_flush = env->mmu_model == POWERPC_MMU_32B
|
||||
|| env->mmu_model == POWERPC_MMU_601
|
||||
|| env->mmu_model & POWERPC_MMU_64;
|
||||
|
||||
ctx->fpu_enabled = !!msr_fp;
|
||||
if ((env->flags & POWERPC_FLAG_SPE) && msr_spe) {
|
||||
ctx->spe_enabled = !!msr_spe;
|
||||
} else {
|
||||
ctx->spe_enabled = false;
|
||||
}
|
||||
if ((env->flags & POWERPC_FLAG_VRE) && msr_vr) {
|
||||
ctx->altivec_enabled = !!msr_vr;
|
||||
} else {
|
||||
ctx->altivec_enabled = false;
|
||||
}
|
||||
if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
|
||||
ctx->vsx_enabled = !!msr_vsx;
|
||||
} else {
|
||||
ctx->vsx_enabled = false;
|
||||
}
|
||||
if ((env->flags & POWERPC_FLAG_SCV)
|
||||
&& (env->spr[SPR_FSCR] & (1ull << FSCR_SCV))) {
|
||||
ctx->scv_enabled = true;
|
||||
} else {
|
||||
ctx->scv_enabled = false;
|
||||
}
|
||||
#if defined(TARGET_PPC64)
|
||||
if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
|
||||
ctx->tm_enabled = !!msr_tm;
|
||||
} else {
|
||||
ctx->tm_enabled = false;
|
||||
}
|
||||
#endif
|
||||
ctx->gtse = !!(env->spr[SPR_LPCR] & LPCR_GTSE);
|
||||
if ((env->flags & POWERPC_FLAG_SE) && msr_se) {
|
||||
ctx->singlestep_enabled = CPU_SINGLE_STEP;
|
||||
} else {
|
||||
ctx->singlestep_enabled = 0;
|
||||
}
|
||||
if ((env->flags & POWERPC_FLAG_BE) && msr_be) {
|
||||
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
||||
}
|
||||
if ((env->flags & POWERPC_FLAG_DE) && msr_de) {
|
||||
ctx->singlestep_enabled = 0;
|
||||
target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0];
|
||||
if (dbcr0 & DBCR0_ICMP) {
|
||||
ctx->singlestep_enabled |= CPU_SINGLE_STEP;
|
||||
}
|
||||
if (dbcr0 & DBCR0_BRT) {
|
||||
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
||||
}
|
||||
ctx->fpu_enabled = (hflags >> HFLAGS_FP) & 1;
|
||||
ctx->spe_enabled = (hflags >> HFLAGS_SPE) & 1;
|
||||
ctx->altivec_enabled = (hflags >> HFLAGS_VR) & 1;
|
||||
ctx->vsx_enabled = (hflags >> HFLAGS_VSX) & 1;
|
||||
ctx->tm_enabled = (hflags >> HFLAGS_TM) & 1;
|
||||
ctx->gtse = (hflags >> HFLAGS_GTSE) & 1;
|
||||
|
||||
ctx->singlestep_enabled = 0;
|
||||
if ((hflags >> HFLAGS_SE) & 1) {
|
||||
ctx->singlestep_enabled |= CPU_SINGLE_STEP;
|
||||
}
|
||||
if ((hflags >> HFLAGS_BE) & 1) {
|
||||
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
||||
}
|
||||
if (unlikely(ctx->base.singlestep_enabled)) {
|
||||
ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP;
|
||||
}
|
||||
#if defined(DO_SINGLE_STEP) && 0
|
||||
/* Single step trace mode */
|
||||
msr_se = 1;
|
||||
#endif
|
||||
|
||||
bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
|
||||
ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include "fpu/softfloat.h"
|
||||
#include "qapi/qapi-commands-machine-target.h"
|
||||
|
||||
/* #define PPC_DUMP_CPU */
|
||||
/* #define PPC_DEBUG_SPR */
|
||||
/* #define PPC_DUMP_SPR_ACCESSES */
|
||||
/* #define USE_APPLE_GDB */
|
||||
@ -721,104 +720,98 @@ static inline void vscr_init(CPUPPCState *env, uint32_t val)
|
||||
helper_mtvscr(env, val);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define spr_register_kvm(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, uea_read, uea_write, initial_value)
|
||||
#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, uea_read, uea_write, initial_value)
|
||||
/**
|
||||
* _spr_register
|
||||
*
|
||||
* Register an SPR with all the callbacks required for tcg,
|
||||
* and the ID number for KVM.
|
||||
*
|
||||
* The reason for the conditional compilation is that the tcg functions
|
||||
* may be compiled out, and the system kvm header may not be available
|
||||
* for supplying the ID numbers. This is ugly, but the best we can do.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
# define USR_ARG(X) X,
|
||||
# ifdef CONFIG_USER_ONLY
|
||||
# define SYS_ARG(X)
|
||||
# else
|
||||
# define SYS_ARG(X) X,
|
||||
# endif
|
||||
#else
|
||||
#if !defined(CONFIG_KVM)
|
||||
#define spr_register_kvm(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, oea_read, oea_write, initial_value)
|
||||
#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, initial_value)
|
||||
# define USR_ARG(X)
|
||||
# define SYS_ARG(X)
|
||||
#endif
|
||||
#ifdef CONFIG_KVM
|
||||
# define KVM_ARG(X) X,
|
||||
#else
|
||||
#define spr_register_kvm(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, oea_read, oea_write, \
|
||||
one_reg_id, initial_value)
|
||||
#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
one_reg_id, initial_value)
|
||||
#endif
|
||||
# define KVM_ARG(X)
|
||||
#endif
|
||||
|
||||
#define spr_register(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, initial_value) \
|
||||
spr_register_kvm(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, 0, initial_value)
|
||||
typedef void spr_callback(DisasContext *, int, int);
|
||||
|
||||
#define spr_register_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
initial_value) \
|
||||
spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
0, initial_value)
|
||||
|
||||
static inline void _spr_register(CPUPPCState *env, int num,
|
||||
const char *name,
|
||||
void (*uea_read)(DisasContext *ctx,
|
||||
int gprn, int sprn),
|
||||
void (*uea_write)(DisasContext *ctx,
|
||||
int sprn, int gprn),
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
void (*oea_read)(DisasContext *ctx,
|
||||
int gprn, int sprn),
|
||||
void (*oea_write)(DisasContext *ctx,
|
||||
int sprn, int gprn),
|
||||
void (*hea_read)(DisasContext *opaque,
|
||||
int gprn, int sprn),
|
||||
void (*hea_write)(DisasContext *opaque,
|
||||
int sprn, int gprn),
|
||||
#endif
|
||||
#if defined(CONFIG_KVM)
|
||||
uint64_t one_reg_id,
|
||||
#endif
|
||||
target_ulong initial_value)
|
||||
static void _spr_register(CPUPPCState *env, int num, const char *name,
|
||||
USR_ARG(spr_callback *uea_read)
|
||||
USR_ARG(spr_callback *uea_write)
|
||||
SYS_ARG(spr_callback *oea_read)
|
||||
SYS_ARG(spr_callback *oea_write)
|
||||
SYS_ARG(spr_callback *hea_read)
|
||||
SYS_ARG(spr_callback *hea_write)
|
||||
KVM_ARG(uint64_t one_reg_id)
|
||||
target_ulong initial_value)
|
||||
{
|
||||
ppc_spr_t *spr;
|
||||
ppc_spr_t *spr = &env->spr_cb[num];
|
||||
|
||||
/* No SPR should be registered twice. */
|
||||
assert(spr->name == NULL);
|
||||
assert(name != NULL);
|
||||
|
||||
spr = &env->spr_cb[num];
|
||||
if (spr->name != NULL || env->spr[num] != 0x00000000 ||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
spr->oea_read != NULL || spr->oea_write != NULL ||
|
||||
#endif
|
||||
spr->uea_read != NULL || spr->uea_write != NULL) {
|
||||
printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num);
|
||||
exit(1);
|
||||
}
|
||||
#if defined(PPC_DEBUG_SPR)
|
||||
printf("*** register spr %d (%03x) %s val " TARGET_FMT_lx "\n", num, num,
|
||||
name, initial_value);
|
||||
#endif
|
||||
spr->name = name;
|
||||
spr->default_value = initial_value;
|
||||
env->spr[num] = initial_value;
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
spr->uea_read = uea_read;
|
||||
spr->uea_write = uea_write;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
# ifndef CONFIG_USER_ONLY
|
||||
spr->oea_read = oea_read;
|
||||
spr->oea_write = oea_write;
|
||||
spr->hea_read = hea_read;
|
||||
spr->hea_write = hea_write;
|
||||
# endif
|
||||
#endif
|
||||
#if defined(CONFIG_KVM)
|
||||
spr->one_reg_id = one_reg_id,
|
||||
#ifdef CONFIG_KVM
|
||||
spr->one_reg_id = one_reg_id;
|
||||
#endif
|
||||
env->spr[num] = spr->default_value = initial_value;
|
||||
}
|
||||
|
||||
/* spr_register_kvm_hv passes all required arguments. */
|
||||
#define spr_register_kvm_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, \
|
||||
one_reg_id, initial_value) \
|
||||
_spr_register(env, num, name, \
|
||||
USR_ARG(uea_read) USR_ARG(uea_write) \
|
||||
SYS_ARG(oea_read) SYS_ARG(oea_write) \
|
||||
SYS_ARG(hea_read) SYS_ARG(hea_write) \
|
||||
KVM_ARG(one_reg_id) initial_value)
|
||||
|
||||
/* spr_register_kvm duplicates the oea callbacks to the hea callbacks. */
|
||||
#define spr_register_kvm(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, one_reg_id, ival) \
|
||||
spr_register_kvm_hv(env, num, name, uea_read, uea_write, oea_read, \
|
||||
oea_write, oea_read, oea_write, one_reg_id, ival)
|
||||
|
||||
/* spr_register_hv and spr_register are similar, except there is no kvm id. */
|
||||
#define spr_register_hv(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, hea_read, hea_write, ival) \
|
||||
spr_register_kvm_hv(env, num, name, uea_read, uea_write, oea_read, \
|
||||
oea_write, hea_read, hea_write, 0, ival)
|
||||
|
||||
#define spr_register(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, ival) \
|
||||
spr_register_kvm(env, num, name, uea_read, uea_write, \
|
||||
oea_read, oea_write, 0, ival)
|
||||
|
||||
/* Generic PowerPC SPRs */
|
||||
static void gen_spr_generic(CPUPPCState *env)
|
||||
{
|
||||
@ -1700,8 +1693,6 @@ static void gen_spr_74xx(CPUPPCState *env)
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, spr_access_nop,
|
||||
0x00000000);
|
||||
/* Not strictly an SPR */
|
||||
vscr_init(env, 0x00010000);
|
||||
}
|
||||
|
||||
static void gen_l3_ctrl(CPUPPCState *env)
|
||||
@ -3457,7 +3448,7 @@ static void init_excp_POWER9(CPUPPCState *env)
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->excp_vectors[POWERPC_EXCP_HVIRT] = 0x00000EA0;
|
||||
env->excp_vectors[POWERPC_EXCP_SYSCALL_VECTORED] = 0x00000000;
|
||||
env->excp_vectors[POWERPC_EXCP_SYSCALL_VECTORED] = 0x00017000;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -5441,7 +5432,7 @@ POWERPC_FAMILY(601)(ObjectClass *oc, void *data)
|
||||
pcc->excp_model = POWERPC_EXCP_601;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_601;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE;
|
||||
}
|
||||
|
||||
#define POWERPC_MSRR_601v (0x0000000000001040ULL)
|
||||
@ -5485,7 +5476,7 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data)
|
||||
#endif
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_6xx;
|
||||
pcc->bfd_mach = bfd_mach_ppc_601;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK;
|
||||
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE;
|
||||
}
|
||||
|
||||
static void init_proc_602(CPUPPCState *env)
|
||||
@ -6625,6 +6616,7 @@ static void init_proc_7400(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_UBAMR, "UBAMR",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
@ -6704,6 +6696,7 @@ static void init_proc_7410(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_UBAMR, "UBAMR",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
@ -6789,6 +6782,7 @@ static void init_proc_7440(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_UBAMR, "UBAMR",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
@ -6897,6 +6891,7 @@ static void init_proc_7450(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* Level 3 cache control */
|
||||
gen_l3_ctrl(env);
|
||||
/* L3ITCR1 */
|
||||
@ -7031,6 +7026,7 @@ static void init_proc_7445(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* LDSTCR */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_LDSTCR, "LDSTCR",
|
||||
@ -7168,6 +7164,7 @@ static void init_proc_7455(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* Level 3 cache control */
|
||||
gen_l3_ctrl(env);
|
||||
/* LDSTCR */
|
||||
@ -7307,6 +7304,7 @@ static void init_proc_7457(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* Level 3 cache control */
|
||||
gen_l3_ctrl(env);
|
||||
/* L3ITCR1 */
|
||||
@ -7470,6 +7468,7 @@ static void init_proc_e600(CPUPPCState *env)
|
||||
gen_tbl(env);
|
||||
/* 74xx specific SPR */
|
||||
gen_spr_74xx(env);
|
||||
vscr_init(env, 0x00010000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_UBAMR, "UBAMR",
|
||||
&spr_read_ureg, SPR_NOACCESS,
|
||||
@ -7720,11 +7719,6 @@ static void gen_spr_book3s_altivec(CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_VRSAVE, 0x00000000);
|
||||
|
||||
/*
|
||||
* Can't find information on what this should be on reset. This
|
||||
* value is the one used by 74xx processors.
|
||||
*/
|
||||
vscr_init(env, 0x00010000);
|
||||
}
|
||||
|
||||
static void gen_spr_book3s_dbg(CPUPPCState *env)
|
||||
@ -7748,12 +7742,12 @@ static void gen_spr_book3s_dbg(CPUPPCState *env)
|
||||
|
||||
static void gen_spr_book3s_207_dbg(CPUPPCState *env)
|
||||
{
|
||||
spr_register_kvm_hv(env, SPR_DAWR, "DAWR",
|
||||
spr_register_kvm_hv(env, SPR_DAWR0, "DAWR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_DAWR, 0x00000000);
|
||||
spr_register_kvm_hv(env, SPR_DAWRX, "DAWRX",
|
||||
spr_register_kvm_hv(env, SPR_DAWRX0, "DAWRX0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
@ -8422,6 +8416,11 @@ static void init_proc_book3s_common(CPUPPCState *env)
|
||||
gen_spr_book3s_pmu_sup(env);
|
||||
gen_spr_book3s_pmu_user(env);
|
||||
gen_spr_book3s_ctrl(env);
|
||||
/*
|
||||
* Can't find information on what this should be on reset. This
|
||||
* value is the one used by 74xx processors.
|
||||
*/
|
||||
vscr_init(env, 0x00010000);
|
||||
}
|
||||
|
||||
static void init_proc_970(CPUPPCState *env)
|
||||
@ -9317,13 +9316,13 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data)
|
||||
pcc->radix_page_info = &POWER10_radix_page_info;
|
||||
pcc->lrg_decr_bits = 56;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_POWER9;
|
||||
pcc->excp_model = POWERPC_EXCP_POWER10;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_POWER9;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
|
||||
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
|
||||
POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
|
||||
POWERPC_FLAG_VSX | POWERPC_FLAG_TM;
|
||||
POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV;
|
||||
pcc->l1_dcache_size = 0x8000;
|
||||
pcc->l1_icache_size = 0x8000;
|
||||
pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
|
||||
@ -9560,590 +9559,6 @@ static void dump_ppc_sprs(CPUPPCState *env)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Opcode types */
|
||||
enum {
|
||||
PPC_DIRECT = 0, /* Opcode routine */
|
||||
PPC_INDIRECT = 1, /* Indirect opcode table */
|
||||
};
|
||||
|
||||
#define PPC_OPCODE_MASK 0x3
|
||||
|
||||
static inline int is_indirect_opcode(void *handler)
|
||||
{
|
||||
return ((uintptr_t)handler & PPC_OPCODE_MASK) == PPC_INDIRECT;
|
||||
}
|
||||
|
||||
static inline opc_handler_t **ind_table(void *handler)
|
||||
{
|
||||
return (opc_handler_t **)((uintptr_t)handler & ~PPC_OPCODE_MASK);
|
||||
}
|
||||
|
||||
/* Instruction table creation */
|
||||
/* Opcodes tables creation */
|
||||
static void fill_new_table(opc_handler_t **table, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
table[i] = &invalid_handler;
|
||||
}
|
||||
}
|
||||
|
||||
static int create_new_table(opc_handler_t **table, unsigned char idx)
|
||||
{
|
||||
opc_handler_t **tmp;
|
||||
|
||||
tmp = g_new(opc_handler_t *, PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||
fill_new_table(tmp, PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||
table[idx] = (opc_handler_t *)((uintptr_t)tmp | PPC_INDIRECT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insert_in_table(opc_handler_t **table, unsigned char idx,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
if (table[idx] != &invalid_handler) {
|
||||
return -1;
|
||||
}
|
||||
table[idx] = handler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_direct_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx, opc_handler_t *handler)
|
||||
{
|
||||
if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
|
||||
printf("*** ERROR: opcode %02x already assigned in main "
|
||||
"opcode table\n", idx);
|
||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||
ppc_opcodes[idx]->oname, handler->oname);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_ind_in_table(opc_handler_t **table,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
if (table[idx1] == &invalid_handler) {
|
||||
if (create_new_table(table, idx1) < 0) {
|
||||
printf("*** ERROR: unable to create indirect table "
|
||||
"idx=%02x\n", idx1);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (!is_indirect_opcode(table[idx1])) {
|
||||
printf("*** ERROR: idx %02x already assigned to a direct "
|
||||
"opcode\n", idx1);
|
||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (handler != NULL &&
|
||||
insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
|
||||
printf("*** ERROR: opcode %02x already assigned in "
|
||||
"opcode table %02x\n", idx2, idx1);
|
||||
#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
|
||||
printf(" Registered handler '%s' - new handler '%s'\n",
|
||||
ind_table(table[idx1])[idx2]->oname, handler->oname);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_ind_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
return register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
|
||||
}
|
||||
|
||||
static int register_dblind_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
unsigned char idx3, opc_handler_t *handler)
|
||||
{
|
||||
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
||||
printf("*** ERROR: unable to join indirect table idx "
|
||||
"[%02x-%02x]\n", idx1, idx2);
|
||||
return -1;
|
||||
}
|
||||
if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
|
||||
handler) < 0) {
|
||||
printf("*** ERROR: unable to insert opcode "
|
||||
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int register_trplind_insn(opc_handler_t **ppc_opcodes,
|
||||
unsigned char idx1, unsigned char idx2,
|
||||
unsigned char idx3, unsigned char idx4,
|
||||
opc_handler_t *handler)
|
||||
{
|
||||
opc_handler_t **table;
|
||||
|
||||
if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
|
||||
printf("*** ERROR: unable to join indirect table idx "
|
||||
"[%02x-%02x]\n", idx1, idx2);
|
||||
return -1;
|
||||
}
|
||||
table = ind_table(ppc_opcodes[idx1]);
|
||||
if (register_ind_in_table(table, idx2, idx3, NULL) < 0) {
|
||||
printf("*** ERROR: unable to join 2nd-level indirect table idx "
|
||||
"[%02x-%02x-%02x]\n", idx1, idx2, idx3);
|
||||
return -1;
|
||||
}
|
||||
table = ind_table(table[idx2]);
|
||||
if (register_ind_in_table(table, idx3, idx4, handler) < 0) {
|
||||
printf("*** ERROR: unable to insert opcode "
|
||||
"[%02x-%02x-%02x-%02x]\n", idx1, idx2, idx3, idx4);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static int register_insn(opc_handler_t **ppc_opcodes, opcode_t *insn)
|
||||
{
|
||||
if (insn->opc2 != 0xFF) {
|
||||
if (insn->opc3 != 0xFF) {
|
||||
if (insn->opc4 != 0xFF) {
|
||||
if (register_trplind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
||||
insn->opc3, insn->opc4,
|
||||
&insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
|
||||
insn->opc3, &insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (register_ind_insn(ppc_opcodes, insn->opc1,
|
||||
insn->opc2, &insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_opcode_table(opc_handler_t **table, int len)
|
||||
{
|
||||
int i, count, tmp;
|
||||
|
||||
for (i = 0, count = 0; i < len; i++) {
|
||||
/* Consistency fixup */
|
||||
if (table[i] == NULL) {
|
||||
table[i] = &invalid_handler;
|
||||
}
|
||||
if (table[i] != &invalid_handler) {
|
||||
if (is_indirect_opcode(table[i])) {
|
||||
tmp = test_opcode_table(ind_table(table[i]),
|
||||
PPC_CPU_INDIRECT_OPCODES_LEN);
|
||||
if (tmp == 0) {
|
||||
free(table[i]);
|
||||
table[i] = &invalid_handler;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void fix_opcode_tables(opc_handler_t **ppc_opcodes)
|
||||
{
|
||||
if (test_opcode_table(ppc_opcodes, PPC_CPU_OPCODES_LEN) == 0) {
|
||||
printf("*** WARNING: no opcode defined !\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static void create_ppc_opcodes(PowerPCCPU *cpu, Error **errp)
|
||||
{
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||
opcode_t *opc;
|
||||
|
||||
fill_new_table(cpu->opcodes, PPC_CPU_OPCODES_LEN);
|
||||
for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
|
||||
if (((opc->handler.type & pcc->insns_flags) != 0) ||
|
||||
((opc->handler.type2 & pcc->insns_flags2) != 0)) {
|
||||
if (register_insn(cpu->opcodes, opc) < 0) {
|
||||
error_setg(errp, "ERROR initializing PowerPC instruction "
|
||||
"0x%02x 0x%02x 0x%02x", opc->opc1, opc->opc2,
|
||||
opc->opc3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fix_opcode_tables(cpu->opcodes);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
#if defined(PPC_DUMP_CPU)
|
||||
static void dump_ppc_insns(CPUPPCState *env)
|
||||
{
|
||||
opc_handler_t **table, *handler;
|
||||
const char *p, *q;
|
||||
uint8_t opc1, opc2, opc3, opc4;
|
||||
|
||||
printf("Instructions set:\n");
|
||||
/* opc1 is 6 bits long */
|
||||
for (opc1 = 0x00; opc1 < PPC_CPU_OPCODES_LEN; opc1++) {
|
||||
table = env->opcodes;
|
||||
handler = table[opc1];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
/* opc2 is 5 bits long */
|
||||
for (opc2 = 0; opc2 < PPC_CPU_INDIRECT_OPCODES_LEN; opc2++) {
|
||||
table = env->opcodes;
|
||||
handler = env->opcodes[opc1];
|
||||
table = ind_table(handler);
|
||||
handler = table[opc2];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
/* opc3 is 5 bits long */
|
||||
for (opc3 = 0; opc3 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
||||
opc3++) {
|
||||
handler = table[opc3];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
/* opc4 is 5 bits long */
|
||||
for (opc4 = 0; opc4 < PPC_CPU_INDIRECT_OPCODES_LEN;
|
||||
opc4++) {
|
||||
handler = table[opc4];
|
||||
if (handler->handler != &gen_invalid) {
|
||||
printf("INSN: %02x %02x %02x %02x -- "
|
||||
"(%02d %04d %02d) : %s\n",
|
||||
opc1, opc2, opc3, opc4,
|
||||
opc1, (opc3 << 5) | opc2, opc4,
|
||||
handler->oname);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (handler->handler != &gen_invalid) {
|
||||
/* Special hack to properly dump SPE insns */
|
||||
p = strchr(handler->oname, '_');
|
||||
if (p == NULL) {
|
||||
printf("INSN: %02x %02x %02x (%02d %04d) : "
|
||||
"%s\n",
|
||||
opc1, opc2, opc3, opc1,
|
||||
(opc3 << 5) | opc2,
|
||||
handler->oname);
|
||||
} else {
|
||||
q = "speundef";
|
||||
if ((p - handler->oname) != strlen(q)
|
||||
|| (memcmp(handler->oname, q, strlen(q))
|
||||
!= 0)) {
|
||||
/* First instruction */
|
||||
printf("INSN: %02x %02x %02x"
|
||||
"(%02d %04d) : %.*s\n",
|
||||
opc1, opc2 << 1, opc3, opc1,
|
||||
(opc3 << 6) | (opc2 << 1),
|
||||
(int)(p - handler->oname),
|
||||
handler->oname);
|
||||
}
|
||||
if (strcmp(p + 1, q) != 0) {
|
||||
/* Second instruction */
|
||||
printf("INSN: %02x %02x %02x "
|
||||
"(%02d %04d) : %s\n", opc1,
|
||||
(opc2 << 1) | 1, opc3, opc1,
|
||||
(opc3 << 6) | (opc2 << 1) | 1,
|
||||
p + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (handler->handler != &gen_invalid) {
|
||||
printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
|
||||
opc1, opc2, opc1, opc2, handler->oname);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (handler->handler != &gen_invalid) {
|
||||
printf("INSN: %02x -- -- (%02d ----) : %s\n",
|
||||
opc1, opc1, handler->oname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool avr_need_swap(CPUPPCState *env)
|
||||
{
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
return msr_le;
|
||||
#else
|
||||
return !msr_le;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static int gdb_find_spr_idx(CPUPPCState *env, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
|
||||
ppc_spr_t *spr = &env->spr_cb[i];
|
||||
|
||||
if (spr->name && spr->gdb_id == n) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int gdb_get_spr_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
int reg;
|
||||
int len;
|
||||
|
||||
reg = gdb_find_spr_idx(env, n);
|
||||
if (reg < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = TARGET_LONG_SIZE;
|
||||
gdb_get_regl(buf, env->spr[reg]);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, len), len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
int reg;
|
||||
int len;
|
||||
|
||||
reg = gdb_find_spr_idx(env, n);
|
||||
if (reg < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = TARGET_LONG_SIZE;
|
||||
ppc_maybe_bswap_register(env, mem_buf, len);
|
||||
env->spr[reg] = ldn_p(mem_buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int gdb_get_float_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
uint8_t *mem_buf;
|
||||
if (n < 32) {
|
||||
gdb_get_reg64(buf, *cpu_fpr_ptr(env, n));
|
||||
mem_buf = gdb_get_reg_ptr(buf, 8);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
return 8;
|
||||
}
|
||||
if (n == 32) {
|
||||
gdb_get_reg32(buf, env->fpscr);
|
||||
mem_buf = gdb_get_reg_ptr(buf, 4);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_float_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
*cpu_fpr_ptr(env, n) = ldq_p(mem_buf);
|
||||
return 8;
|
||||
}
|
||||
if (n == 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
helper_store_fpscr(env, ldl_p(mem_buf), 0xffffffff);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_get_avr_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
uint8_t *mem_buf;
|
||||
|
||||
if (n < 32) {
|
||||
ppc_avr_t *avr = cpu_avr_ptr(env, n);
|
||||
if (!avr_need_swap(env)) {
|
||||
gdb_get_reg128(buf, avr->u64[0] , avr->u64[1]);
|
||||
} else {
|
||||
gdb_get_reg128(buf, avr->u64[1] , avr->u64[0]);
|
||||
}
|
||||
mem_buf = gdb_get_reg_ptr(buf, 16);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
ppc_maybe_bswap_register(env, mem_buf + 8, 8);
|
||||
return 16;
|
||||
}
|
||||
if (n == 32) {
|
||||
gdb_get_reg32(buf, helper_mfvscr(env));
|
||||
mem_buf = gdb_get_reg_ptr(buf, 4);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
return 4;
|
||||
}
|
||||
if (n == 33) {
|
||||
gdb_get_reg32(buf, (uint32_t)env->spr[SPR_VRSAVE]);
|
||||
mem_buf = gdb_get_reg_ptr(buf, 4);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_avr_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
ppc_avr_t *avr = cpu_avr_ptr(env, n);
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
ppc_maybe_bswap_register(env, mem_buf + 8, 8);
|
||||
if (!avr_need_swap(env)) {
|
||||
avr->u64[0] = ldq_p(mem_buf);
|
||||
avr->u64[1] = ldq_p(mem_buf + 8);
|
||||
} else {
|
||||
avr->u64[1] = ldq_p(mem_buf);
|
||||
avr->u64[0] = ldq_p(mem_buf + 8);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
if (n == 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
helper_mtvscr(env, ldl_p(mem_buf));
|
||||
return 4;
|
||||
}
|
||||
if (n == 33) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_get_spe_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
#if defined(TARGET_PPC64)
|
||||
gdb_get_reg32(buf, env->gpr[n] >> 32);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
|
||||
#else
|
||||
gdb_get_reg32(buf, env->gprh[n]);
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
if (n == 32) {
|
||||
gdb_get_reg64(buf, env->spe_acc);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
|
||||
return 8;
|
||||
}
|
||||
if (n == 33) {
|
||||
gdb_get_reg32(buf, env->spe_fscr);
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 4), 4);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
#if defined(TARGET_PPC64)
|
||||
target_ulong lo = (uint32_t)env->gpr[n];
|
||||
target_ulong hi;
|
||||
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
|
||||
hi = (target_ulong)ldl_p(mem_buf) << 32;
|
||||
env->gpr[n] = lo | hi;
|
||||
#else
|
||||
env->gprh[n] = ldl_p(mem_buf);
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
if (n == 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
env->spe_acc = ldq_p(mem_buf);
|
||||
return 8;
|
||||
}
|
||||
if (n == 33) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 4);
|
||||
env->spe_fscr = ldl_p(mem_buf);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_get_vsx_reg(CPUPPCState *env, GByteArray *buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
gdb_get_reg64(buf, *cpu_vsrl_ptr(env, n));
|
||||
ppc_maybe_bswap_register(env, gdb_get_reg_ptr(buf, 8), 8);
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdb_set_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (n < 32) {
|
||||
ppc_maybe_bswap_register(env, mem_buf, 8);
|
||||
*cpu_vsrl_ptr(env, n) = ldq_p(mem_buf);
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppc_fixup_cpu(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
/*
|
||||
* TCG doesn't (yet) emulate some groups of instructions that are
|
||||
* implemented on some otherwise supported CPUs (e.g. VSX and
|
||||
* decimal floating point instructions on POWER7). We remove
|
||||
* unsupported instruction groups from the cpu state's instruction
|
||||
* masks and hope the guest can cope. For at least the pseries
|
||||
* machine, the unavailability of these instructions can be
|
||||
* advertised to the guest via the device tree.
|
||||
*/
|
||||
if ((env->insns_flags & ~PPC_TCG_INSNS)
|
||||
|| (env->insns_flags2 & ~PPC_TCG_INSNS2)) {
|
||||
warn_report("Disabling some instructions which are not "
|
||||
"emulated by TCG (0x%" PRIx64 ", 0x%" PRIx64 ")",
|
||||
env->insns_flags & ~PPC_TCG_INSNS,
|
||||
env->insns_flags2 & ~PPC_TCG_INSNS2);
|
||||
}
|
||||
env->insns_flags &= PPC_TCG_INSNS;
|
||||
env->insns_flags2 &= PPC_TCG_INSNS2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ppc_cpu_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
@ -10174,26 +9589,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
init_ppc_proc(cpu);
|
||||
|
||||
if (pcc->insns_flags & PPC_FLOAT) {
|
||||
gdb_register_coprocessor(cs, gdb_get_float_reg, gdb_set_float_reg,
|
||||
33, "power-fpu.xml", 0);
|
||||
}
|
||||
if (pcc->insns_flags & PPC_ALTIVEC) {
|
||||
gdb_register_coprocessor(cs, gdb_get_avr_reg, gdb_set_avr_reg,
|
||||
34, "power-altivec.xml", 0);
|
||||
}
|
||||
if (pcc->insns_flags & PPC_SPE) {
|
||||
gdb_register_coprocessor(cs, gdb_get_spe_reg, gdb_set_spe_reg,
|
||||
34, "power-spe.xml", 0);
|
||||
}
|
||||
if (pcc->insns_flags2 & PPC2_VSX) {
|
||||
gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg,
|
||||
32, "power-vsx.xml", 0);
|
||||
}
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg,
|
||||
pcc->gdb_num_sprs, "power-spr.xml", 0);
|
||||
#endif
|
||||
ppc_gdb_init(cs, pcc);
|
||||
qemu_init_vcpu(cs);
|
||||
|
||||
pcc->parent_realize(dev, errp);
|
||||
@ -10374,40 +9770,12 @@ static void ppc_cpu_unrealize(DeviceState *dev)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(dev);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
|
||||
opc_handler_t **table, **table_2;
|
||||
int i, j, k;
|
||||
|
||||
pcc->parent_unrealize(dev);
|
||||
|
||||
cpu_remove_sync(CPU(cpu));
|
||||
|
||||
for (i = 0; i < PPC_CPU_OPCODES_LEN; i++) {
|
||||
if (cpu->opcodes[i] == &invalid_handler) {
|
||||
continue;
|
||||
}
|
||||
if (is_indirect_opcode(cpu->opcodes[i])) {
|
||||
table = ind_table(cpu->opcodes[i]);
|
||||
for (j = 0; j < PPC_CPU_INDIRECT_OPCODES_LEN; j++) {
|
||||
if (table[j] == &invalid_handler) {
|
||||
continue;
|
||||
}
|
||||
if (is_indirect_opcode(table[j])) {
|
||||
table_2 = ind_table(table[j]);
|
||||
for (k = 0; k < PPC_CPU_INDIRECT_OPCODES_LEN; k++) {
|
||||
if (table_2[k] != &invalid_handler &&
|
||||
is_indirect_opcode(table_2[k])) {
|
||||
g_free((opc_handler_t *)((uintptr_t)table_2[k] &
|
||||
~PPC_INDIRECT));
|
||||
}
|
||||
}
|
||||
g_free((opc_handler_t *)((uintptr_t)table[j] &
|
||||
~PPC_INDIRECT));
|
||||
}
|
||||
}
|
||||
g_free((opc_handler_t *)((uintptr_t)cpu->opcodes[i] &
|
||||
~PPC_INDIRECT));
|
||||
}
|
||||
}
|
||||
destroy_ppc_opcodes(cpu);
|
||||
}
|
||||
|
||||
static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b)
|
||||
@ -10835,15 +10203,6 @@ static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr)
|
||||
return pcc->pvr == pvr;
|
||||
}
|
||||
|
||||
static gchar *ppc_gdb_arch_name(CPUState *cs)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
return g_strdup("powerpc:common64");
|
||||
#else
|
||||
return g_strdup("powerpc:common");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ppc_disas_set_info(CPUState *cs, disassemble_info *info)
|
||||
{
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
|
Loading…
Reference in New Issue
Block a user