Merge branch 'ppc-for-upstream' of git://repo.or.cz/qemu/agraf

* 'ppc-for-upstream' of git://repo.or.cz/qemu/agraf: (72 commits)
  PPC: BookE206: Bump MAS2 to 64bit
  PPC: BookE: Support 32 and 64 bit wide MAS2
  PPC: Extract SPR dump generation into its own function
  PPC: Add e5500 CPU target
  PPC: BookE: Make ivpr selectable by CPU type
  PPC: BookE: Implement EPR SPR
  PPC: Add support for MSR_CM
  PPC: Add some booke SPR defines
  uImage: increase the gzip load size
  PPC: e500: allow users to set the /compatible property via -machine
  dt: make setprop argument static
  PPC: e500: Refactor serial dt generation
  dt: Add global option to set phandle start offset
  PPC: e500: Extend address/size of / to 64bit
  PPC: e500: Define addresses as always 64bit
  PPC: e500: Use new SOC dt format
  PPC: e500: Use new MPIC dt format
  Revert "dt: temporarily disable subtree creation failure check"
  PPC: e500: enable manual loading of dtb blob
  PPC: e500: dt: use target_phys_addr_t for ramsize
  ...
This commit is contained in:
Blue Swirl 2012-06-24 10:48:56 +00:00
commit 4e469a438f
35 changed files with 9857 additions and 8462 deletions

View File

@ -260,7 +260,6 @@ pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
qemu-icon.bmp \
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
mpc8544ds.dtb \
multiboot.bin linuxboot.bin kvmvapic.bin \
s390-zipl.rom \
spapr-rtas.bin slof.bin \

View File

@ -606,7 +606,6 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVRawState *s = bs->opaque;
off_t start, data, hole;
int ret;
@ -616,11 +615,15 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
}
start = sector_num * BDRV_SECTOR_SIZE;
#ifdef CONFIG_FIEMAP
BDRVRawState *s = bs->opaque;
struct {
struct fiemap fm;
struct fiemap_extent fe;
} f;
f.fm.fm_start = start;
f.fm.fm_length = (int64_t)nb_sectors * BDRV_SECTOR_SIZE;
f.fm.fm_flags = 0;
@ -643,7 +646,11 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
data = f.fe.fe_logical;
hole = f.fe.fe_logical + f.fe.fe_length;
}
#elif defined SEEK_HOLE && defined SEEK_DATA
BDRVRawState *s = bs->opaque;
hole = lseek(s->fd, start, SEEK_HOLE);
if (hole == -1) {
/* -ENXIO indicates that sector_num was past the end of the file.

2
configure vendored
View File

@ -3679,7 +3679,7 @@ symlink "$source_path/Makefile.target" "$target_dir/Makefile"
case "$target_arch2" in
alpha | sparc* | xtensa*)
alpha | sparc* | xtensa* | ppc*)
echo "CONFIG_TCG_PASS_AREG0=y" >> $config_target_mak
;;
esac

View File

@ -291,6 +291,15 @@ extern unsigned long reserved_va;
#define stfl_kernel(p, v) stfl_raw(p, v)
#define stfq_kernel(p, vt) stfq_raw(p, v)
#ifdef CONFIG_TCG_PASS_AREG0
#define cpu_ldub_data(env, addr) ldub_raw(addr)
#define cpu_lduw_data(env, addr) lduw_raw(addr)
#define cpu_ldl_data(env, addr) ldl_raw(addr)
#define cpu_stb_data(env, addr, data) stb_raw(addr, data)
#define cpu_stw_data(env, addr, data) stw_raw(addr, data)
#define cpu_stl_data(env, addr, data) stl_raw(addr, data)
#endif
#endif /* defined(CONFIG_USER_ONLY) */
/* page related stuff */

View File

@ -22,9 +22,48 @@
#include "qemu-common.h"
#include "device_tree.h"
#include "hw/loader.h"
#include "qemu-option.h"
#include "qemu-config.h"
#include <libfdt.h>
#define FDT_MAX_SIZE 0x10000
void *create_device_tree(int *sizep)
{
void *fdt;
int ret;
*sizep = FDT_MAX_SIZE;
fdt = g_malloc0(FDT_MAX_SIZE);
ret = fdt_create(fdt, FDT_MAX_SIZE);
if (ret < 0) {
goto fail;
}
ret = fdt_begin_node(fdt, "");
if (ret < 0) {
goto fail;
}
ret = fdt_end_node(fdt);
if (ret < 0) {
goto fail;
}
ret = fdt_finish(fdt);
if (ret < 0) {
goto fail;
}
ret = fdt_open_into(fdt, fdt, *sizep);
if (ret) {
fprintf(stderr, "Unable to copy device tree in memory\n");
exit(1);
}
return fdt;
fail:
fprintf(stderr, "%s Couldn't create dt: %s\n", __func__, fdt_strerror(ret));
exit(1);
}
void *load_device_tree(const char *filename_path, int *sizep)
{
int dt_size;
@ -88,7 +127,7 @@ static int findnode_nofail(void *fdt, const char *node_path)
}
int qemu_devtree_setprop(void *fdt, const char *node_path,
const char *property, void *val_array, int size)
const char *property, const void *val_array, int size)
{
int r;
@ -117,6 +156,13 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
return r;
}
int qemu_devtree_setprop_u64(void *fdt, const char *node_path,
const char *property, uint64_t val)
{
val = cpu_to_be64(val);
return qemu_devtree_setprop(fdt, node_path, property, &val, sizeof(val));
}
int qemu_devtree_setprop_string(void *fdt, const char *node_path,
const char *property, const char *string)
{
@ -132,6 +178,59 @@ int qemu_devtree_setprop_string(void *fdt, const char *node_path,
return r;
}
uint32_t qemu_devtree_get_phandle(void *fdt, const char *path)
{
uint32_t r;
r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
if (r <= 0) {
fprintf(stderr, "%s: Couldn't get phandle for %s: %s\n", __func__,
path, fdt_strerror(r));
exit(1);
}
return r;
}
int qemu_devtree_setprop_phandle(void *fdt, const char *node_path,
const char *property,
const char *target_node_path)
{
uint32_t phandle = qemu_devtree_get_phandle(fdt, target_node_path);
return qemu_devtree_setprop_cell(fdt, node_path, property, phandle);
}
uint32_t qemu_devtree_alloc_phandle(void *fdt)
{
static int phandle = 0x0;
/*
* We need to find out if the user gave us special instruction at
* which phandle id to start allocting phandles.
*/
if (!phandle) {
QemuOpts *machine_opts;
machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
if (machine_opts) {
const char *phandle_start;
phandle_start = qemu_opt_get(machine_opts, "phandle_start");
if (phandle_start) {
phandle = strtoul(phandle_start, NULL, 0);
}
}
}
if (!phandle) {
/*
* None or invalid phandle given on the command line, so fall back to
* default starting point.
*/
phandle = 0x8000;
}
return phandle++;
}
int qemu_devtree_nop_node(void *fdt, const char *node_path)
{
int r;
@ -151,6 +250,7 @@ int qemu_devtree_add_subnode(void *fdt, const char *name)
char *dupname = g_strdup(name);
char *basename = strrchr(dupname, '/');
int retval;
int parent = 0;
if (!basename) {
g_free(dupname);
@ -160,7 +260,11 @@ int qemu_devtree_add_subnode(void *fdt, const char *name)
basename[0] = '\0';
basename++;
retval = fdt_add_subnode(fdt, findnode_nofail(fdt, dupname), basename);
if (dupname[0]) {
parent = findnode_nofail(fdt, dupname);
}
retval = fdt_add_subnode(fdt, parent, basename);
if (retval < 0) {
fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name,
fdt_strerror(retval));

View File

@ -14,15 +14,35 @@
#ifndef __DEVICE_TREE_H__
#define __DEVICE_TREE_H__
void *create_device_tree(int *sizep);
void *load_device_tree(const char *filename_path, int *sizep);
int qemu_devtree_setprop(void *fdt, const char *node_path,
const char *property, void *val_array, int size);
const char *property, const void *val_array, int size);
int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
const char *property, uint32_t val);
int qemu_devtree_setprop_u64(void *fdt, const char *node_path,
const char *property, uint64_t val);
int qemu_devtree_setprop_string(void *fdt, const char *node_path,
const char *property, const char *string);
int qemu_devtree_setprop_phandle(void *fdt, const char *node_path,
const char *property,
const char *target_node_path);
uint32_t qemu_devtree_get_phandle(void *fdt, const char *path);
uint32_t qemu_devtree_alloc_phandle(void *fdt);
int qemu_devtree_nop_node(void *fdt, const char *node_path);
int qemu_devtree_add_subnode(void *fdt, const char *name);
#define qemu_devtree_setprop_cells(fdt, node_path, property, ...) \
do { \
uint32_t qdt_tmp[] = { __VA_ARGS__ }; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(qdt_tmp); i++) { \
qdt_tmp[i] = cpu_to_be32(qdt_tmp[i]); \
} \
qemu_devtree_setprop(fdt, node_path, property, qdt_tmp, \
sizeof(qdt_tmp)); \
} while (0)
#endif /* __DEVICE_TREE_H__ */

View File

@ -0,0 +1,78 @@
When used with the "pseries" machine type, QEMU-system-ppc64 implements
a set of hypervisor calls using a subset of the server "PAPR" specification
(IBM internal at this point), which is also what IBM's proprietary hypervisor
adheres too.
The subset is selected based on the requirements of Linux as a guest.
In addition to those calls, we have added our own private hypervisor
calls which are mostly used as a private interface between the firmware
running in the guest and QEMU.
All those hypercalls start at hcall number 0xf000 which correspond
to a implementation specific range in PAPR.
- H_RTAS (0xf000)
RTAS is a set of runtime services generally provided by the firmware
inside the guest to the operating system. It predates the existence
of hypervisors (it was originally an extension to Open Firmware) and
is still used by PAPR to provide various services that aren't performance
sensitive.
We currently implement the RTAS services in QEMU itself. The actual RTAS
"firmware" blob in the guest is a small stub of a few instructions which
calls our private H_RTAS hypervisor call to pass the RTAS calls to QEMU.
Arguments:
r3 : H_RTAS (0xf000)
r4 : Guest physical address of RTAS parameter block
Returns:
H_SUCCESS : Successully called the RTAS function (RTAS result
will have been stored in the parameter block)
H_PARAMETER : Unknown token
- H_LOGICAL_MEMOP (0xf001)
When the guest runs in "real mode" (in powerpc lingua this means
with MMU disabled, ie guest effective == guest physical), it only
has access to a subset of memory and no IOs.
PAPR provides a set of hypervisor calls to perform cachable or
non-cachable accesses to any guest physical addresses that the
guest can use in order to access IO devices while in real mode.
This is typically used by the firmware running in the guest.
However, doing a hypercall for each access is extremely inefficient
(even more so when running KVM) when accessing the frame buffer. In
that case, things like scrolling become unusably slow.
This hypercall allows the guest to request a "memory op" to be applied
to memory. The supported memory ops at this point are to copy a range
of memory (supports overlap of source and destination) and XOR which
is used by our SLOF firmware to invert the screen.
Arguments:
r3: H_LOGICAL_MEMOP (0xf001)
r4: Guest physical address of destination
r5: Guest physical address of source
r6: Individual element size
0 = 1 byte
1 = 2 bytes
2 = 4 bytes
3 = 8 bytes
r7: Number of elements
r8: Operation
0 = copy
1 = xor
Returns:
H_SUCCESS : Success
H_PARAMETER : Invalid argument

View File

@ -377,9 +377,9 @@ static void zfree(void *x, void *addr)
#define DEFLATED 8
/* This is the maximum in uboot, so if a uImage overflows this, it would
/* This is the usual maximum in uboot, so if a uImage overflows this, it would
* overflow on real hardware too. */
#define UBOOT_MAX_GUNZIP_BYTES 0x800000
#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
size_t srclen)

View File

@ -15,7 +15,7 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o pci-hotplug.o
obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-y += ppc440_bamboo.o
# PowerPC E500 boards
obj-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o
obj-$(CONFIG_FDT) += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o
# PowerPC 440 Xilinx ML507 reference board.
obj-y += virtex_ml507.o
# PowerPC OpenPIC

View File

@ -31,6 +31,7 @@
#include "elf.h"
#include "sysbus.h"
#include "exec-memory.h"
#include "host-utils.h"
#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb"
#define UIMAGE_LOAD_BASE 0
@ -41,57 +42,150 @@
#define RAM_SIZES_ALIGN (64UL << 20)
#define MPC8544_CCSRBAR_BASE 0xE0000000
#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000)
#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500)
#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600)
#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000)
#define MPC8544_PCI_REGS_SIZE 0x1000
#define MPC8544_PCI_IO 0xE1000000
#define MPC8544_PCI_IOLEN 0x10000
#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000)
#define MPC8544_SPIN_BASE 0xEF000000
#define MPC8544_CCSRBAR_BASE 0xE0000000ULL
#define MPC8544_CCSRBAR_SIZE 0x00100000ULL
#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000ULL)
#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500ULL)
#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600ULL)
#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000ULL)
#define MPC8544_PCI_REGS_SIZE 0x1000ULL
#define MPC8544_PCI_IO 0xE1000000ULL
#define MPC8544_PCI_IOLEN 0x10000ULL
#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000ULL)
#define MPC8544_SPIN_BASE 0xEF000000ULL
struct boot_info
{
uint32_t dt_base;
uint32_t dt_size;
uint32_t entry;
};
static void pci_map_create(void *fdt, uint32_t *pci_map, uint32_t mpic)
{
int i;
const uint32_t tmp[] = {
/* IDSEL 0x11 J17 Slot 1 */
0x8800, 0x0, 0x0, 0x1, mpic, 0x2, 0x1, 0x0, 0x0,
0x8800, 0x0, 0x0, 0x2, mpic, 0x3, 0x1, 0x0, 0x0,
0x8800, 0x0, 0x0, 0x3, mpic, 0x4, 0x1, 0x0, 0x0,
0x8800, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0,
/* IDSEL 0x12 J16 Slot 2 */
0x9000, 0x0, 0x0, 0x1, mpic, 0x3, 0x1, 0x0, 0x0,
0x9000, 0x0, 0x0, 0x2, mpic, 0x4, 0x1, 0x0, 0x0,
0x9000, 0x0, 0x0, 0x3, mpic, 0x2, 0x1, 0x0, 0x0,
0x9000, 0x0, 0x0, 0x4, mpic, 0x1, 0x1, 0x0, 0x0,
};
for (i = 0; i < ARRAY_SIZE(tmp); i++) {
pci_map[i] = cpu_to_be32(tmp[i]);
}
}
static void dt_serial_create(void *fdt, unsigned long long offset,
const char *soc, const char *mpic,
const char *alias, int idx, bool defcon)
{
char ser[128];
snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset);
qemu_devtree_add_subnode(fdt, ser);
qemu_devtree_setprop_string(fdt, ser, "device_type", "serial");
qemu_devtree_setprop_string(fdt, ser, "compatible", "ns16550");
qemu_devtree_setprop_cells(fdt, ser, "reg", offset, 0x100);
qemu_devtree_setprop_cell(fdt, ser, "cell-index", idx);
qemu_devtree_setprop_cell(fdt, ser, "clock-frequency", 0);
qemu_devtree_setprop_cells(fdt, ser, "interrupts", 42, 2, 0, 0);
qemu_devtree_setprop_phandle(fdt, ser, "interrupt-parent", mpic);
qemu_devtree_setprop_string(fdt, "/aliases", alias, ser);
if (defcon) {
qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser);
}
}
static int mpc8544_load_device_tree(CPUPPCState *env,
target_phys_addr_t addr,
uint32_t ramsize,
target_phys_addr_t ramsize,
target_phys_addr_t initrd_base,
target_phys_addr_t initrd_size,
const char *kernel_cmdline)
{
int ret = -1;
#ifdef CONFIG_FDT
uint32_t mem_reg_property[] = {0, cpu_to_be32(ramsize)};
char *filename;
uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) };
int fdt_size;
void *fdt;
uint8_t hypercall[16];
uint32_t clock_freq = 400000000;
uint32_t tb_freq = 400000000;
int i;
const char *compatible = "MPC8544DS\0MPC85xxDS";
int compatible_len = sizeof("MPC8544DS\0MPC85xxDS");
char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus";
char model[] = "MPC8544DS";
char soc[128];
char mpic[128];
uint32_t mpic_ph;
char gutil[128];
char pci[128];
uint32_t pci_map[9 * 8];
uint32_t pci_ranges[14] =
{
0x2000000, 0x0, 0xc0000000,
0x0, 0xc0000000,
0x0, 0x20000000,
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
if (!filename) {
goto out;
0x1000000, 0x0, 0x0,
0x0, 0xe1000000,
0x0, 0x10000,
};
QemuOpts *machine_opts;
const char *dumpdtb = NULL;
const char *dtb_file = NULL;
machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
if (machine_opts) {
const char *tmp;
dumpdtb = qemu_opt_get(machine_opts, "dumpdtb");
dtb_file = qemu_opt_get(machine_opts, "dtb");
tmp = qemu_opt_get(machine_opts, "dt_compatible");
if (tmp) {
compatible = tmp;
compatible_len = strlen(compatible) + 1;
}
}
fdt = load_device_tree(filename, &fdt_size);
g_free(filename);
if (dtb_file) {
char *filename;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file);
if (!filename) {
goto out;
}
fdt = load_device_tree(filename, &fdt_size);
if (!fdt) {
goto out;
}
goto done;
}
fdt = create_device_tree(&fdt_size);
if (fdt == NULL) {
goto out;
}
/* Manipulate device tree in memory. */
ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
sizeof(mem_reg_property));
if (ret < 0)
fprintf(stderr, "couldn't set /memory/reg\n");
qemu_devtree_setprop_string(fdt, "/", "model", model);
qemu_devtree_setprop(fdt, "/", "compatible", compatible, compatible_len);
qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 2);
qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 2);
qemu_devtree_add_subnode(fdt, "/memory");
qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory");
qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
sizeof(mem_reg_property));
qemu_devtree_add_subnode(fdt, "/chosen");
if (initrd_size) {
ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
initrd_base);
@ -117,6 +211,7 @@ static int mpc8544_load_device_tree(CPUPPCState *env,
tb_freq = kvmppc_get_tbfreq();
/* indicate KVM hypercall interface */
qemu_devtree_add_subnode(fdt, "/hypervisor");
qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible",
"linux,kvm");
kvmppc_get_hypercall(env, hypercall, sizeof(hypercall));
@ -124,11 +219,16 @@ static int mpc8544_load_device_tree(CPUPPCState *env,
hypercall, sizeof(hypercall));
}
/* Create CPU nodes */
qemu_devtree_add_subnode(fdt, "/cpus");
qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1);
qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0);
/* We need to generate the cpu nodes in reverse order, so Linux can pick
the first node as boot node and be happy */
for (i = smp_cpus - 1; i >= 0; i--) {
char cpu_name[128];
uint64_t cpu_release_addr = cpu_to_be64(MPC8544_SPIN_BASE + (i * 0x20));
uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20);
for (env = first_cpu; env != NULL; env = env->next_cpu) {
if (env->cpu_index == i) {
@ -156,39 +256,133 @@ static int mpc8544_load_device_tree(CPUPPCState *env,
if (env->cpu_index) {
qemu_devtree_setprop_string(fdt, cpu_name, "status", "disabled");
qemu_devtree_setprop_string(fdt, cpu_name, "enable-method", "spin-table");
qemu_devtree_setprop(fdt, cpu_name, "cpu-release-addr",
&cpu_release_addr, sizeof(cpu_release_addr));
qemu_devtree_setprop_u64(fdt, cpu_name, "cpu-release-addr",
cpu_release_addr);
} else {
qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay");
}
}
qemu_devtree_add_subnode(fdt, "/aliases");
/* XXX These should go into their respective devices' code */
snprintf(soc, sizeof(soc), "/soc@%llx", MPC8544_CCSRBAR_BASE);
qemu_devtree_add_subnode(fdt, soc);
qemu_devtree_setprop_string(fdt, soc, "device_type", "soc");
qemu_devtree_setprop(fdt, soc, "compatible", compatible_sb,
sizeof(compatible_sb));
qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1);
qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1);
qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0,
MPC8544_CCSRBAR_BASE >> 32, MPC8544_CCSRBAR_BASE,
MPC8544_CCSRBAR_SIZE);
/* XXX should contain a reasonable value */
qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0);
snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc,
MPC8544_MPIC_REGS_BASE - MPC8544_CCSRBAR_BASE);
qemu_devtree_add_subnode(fdt, mpic);
qemu_devtree_setprop_string(fdt, mpic, "device_type", "open-pic");
qemu_devtree_setprop_string(fdt, mpic, "compatible", "fsl,mpic");
qemu_devtree_setprop_cells(fdt, mpic, "reg", MPC8544_MPIC_REGS_BASE -
MPC8544_CCSRBAR_BASE, 0x40000);
qemu_devtree_setprop_cell(fdt, mpic, "#address-cells", 0);
qemu_devtree_setprop_cell(fdt, mpic, "#interrupt-cells", 4);
mpic_ph = qemu_devtree_alloc_phandle(fdt);
qemu_devtree_setprop_cell(fdt, mpic, "phandle", mpic_ph);
qemu_devtree_setprop_cell(fdt, mpic, "linux,phandle", mpic_ph);
qemu_devtree_setprop(fdt, mpic, "interrupt-controller", NULL, 0);
qemu_devtree_setprop(fdt, mpic, "big-endian", NULL, 0);
qemu_devtree_setprop(fdt, mpic, "single-cpu-affinity", NULL, 0);
qemu_devtree_setprop_cell(fdt, mpic, "last-interrupt-source", 255);
/*
* We have to generate ser1 first, because Linux takes the first
* device it finds in the dt as serial output device. And we generate
* devices in reverse order to the dt.
*/
dt_serial_create(fdt, MPC8544_SERIAL1_REGS_BASE - MPC8544_CCSRBAR_BASE,
soc, mpic, "serial1", 1, false);
dt_serial_create(fdt, MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE,
soc, mpic, "serial0", 0, true);
snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc,
MPC8544_UTIL_BASE - MPC8544_CCSRBAR_BASE);
qemu_devtree_add_subnode(fdt, gutil);
qemu_devtree_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts");
qemu_devtree_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_BASE -
MPC8544_CCSRBAR_BASE, 0x1000);
qemu_devtree_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0);
snprintf(pci, sizeof(pci), "/pci@%llx", MPC8544_PCI_REGS_BASE);
qemu_devtree_add_subnode(fdt, pci);
qemu_devtree_setprop_cell(fdt, pci, "cell-index", 0);
qemu_devtree_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci");
qemu_devtree_setprop_string(fdt, pci, "device_type", "pci");
qemu_devtree_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0,
0x0, 0x7);
pci_map_create(fdt, pci_map, qemu_devtree_get_phandle(fdt, mpic));
qemu_devtree_setprop(fdt, pci, "interrupt-map", pci_map, sizeof(pci_map));
qemu_devtree_setprop_phandle(fdt, pci, "interrupt-parent", mpic);
qemu_devtree_setprop_cells(fdt, pci, "interrupts", 24, 2, 0, 0);
qemu_devtree_setprop_cells(fdt, pci, "bus-range", 0, 255);
for (i = 0; i < 14; i++) {
pci_ranges[i] = cpu_to_be32(pci_ranges[i]);
}
qemu_devtree_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges));
qemu_devtree_setprop_cells(fdt, pci, "reg", MPC8544_PCI_REGS_BASE >> 32,
MPC8544_PCI_REGS_BASE, 0, 0x1000);
qemu_devtree_setprop_cell(fdt, pci, "clock-frequency", 66666666);
qemu_devtree_setprop_cell(fdt, pci, "#interrupt-cells", 1);
qemu_devtree_setprop_cell(fdt, pci, "#size-cells", 2);
qemu_devtree_setprop_cell(fdt, pci, "#address-cells", 3);
qemu_devtree_setprop_string(fdt, "/aliases", "pci0", pci);
done:
if (dumpdtb) {
/* Dump the dtb to a file and quit */
FILE *f = fopen(dumpdtb, "wb");
size_t len;
len = fwrite(fdt, fdt_size, 1, f);
fclose(f);
if (len != fdt_size) {
exit(1);
}
exit(0);
}
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
if (ret < 0) {
goto out;
}
g_free(fdt);
ret = fdt_size;
out:
#endif
return ret;
}
/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
/* Create -kernel TLB entries for BookE. */
static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
{
return ffs(size >> 10) - 1;
return 63 - clz64(size >> 10);
}
static void mmubooke_create_initial_mapping(CPUPPCState *env,
target_ulong va,
target_phys_addr_t pa)
static void mmubooke_create_initial_mapping(CPUPPCState *env)
{
struct boot_info *bi = env->load_info;
ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0);
target_phys_addr_t size;
target_phys_addr_t size, dt_end;
int ps;
size = (booke206_page_size_to_tlb(256 * 1024 * 1024) << MAS1_TSIZE_SHIFT);
/* Our initial TLB entry needs to cover everything from 0 to
the device tree top */
dt_end = bi->dt_base + bi->dt_size;
ps = booke206_page_size_to_tlb(dt_end) + 1;
size = (ps << MAS1_TSIZE_SHIFT);
tlb->mas1 = MAS1_VALID | size;
tlb->mas2 = va & TARGET_PAGE_MASK;
tlb->mas7_3 = pa & TARGET_PAGE_MASK;
tlb->mas2 = 0;
tlb->mas7_3 = 0;
tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
env->tlb_dirty = true;
@ -220,7 +414,7 @@ static void mpc8544ds_cpu_reset(void *opaque)
env->gpr[1] = (16<<20) - 8;
env->gpr[3] = bi->dt_base;
env->nip = bi->entry;
mmubooke_create_initial_mapping(env, 0, 0);
mmubooke_create_initial_mapping(env);
}
static void mpc8544ds_init(ram_addr_t ram_size,
@ -275,6 +469,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT];
irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT];
env->spr[SPR_BOOKE_PIR] = env->cpu_index = i;
env->mpic_cpu_base = MPC8544_MPIC_REGS_BASE + 0x20000;
ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500);
@ -379,13 +574,12 @@ static void mpc8544ds_init(ram_addr_t ram_size,
/* If we're loading a kernel directly, we must load the device tree too. */
if (kernel_filename) {
struct boot_info *boot_info;
int dt_size;
#ifndef CONFIG_FDT
cpu_abort(env, "Compiled without FDT support - can't load kernel\n");
#endif
dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
if (mpc8544_load_device_tree(env, dt_base, ram_size,
initrd_base, initrd_size, kernel_cmdline) < 0) {
dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base,
initrd_size, kernel_cmdline);
if (dt_size < 0) {
fprintf(stderr, "couldn't load device tree\n");
exit(1);
}
@ -393,6 +587,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
boot_info = env->load_info;
boot_info->entry = entry;
boot_info->dt_base = dt_base;
boot_info->dt_size = dt_size;
}
if (kvm_enabled()) {

View File

@ -146,6 +146,40 @@ static int spapr_set_associativity(void *fdt, sPAPREnvironment *spapr)
return ret;
}
static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
size_t maxsize)
{
size_t maxcells = maxsize / sizeof(uint32_t);
int i, j, count;
uint32_t *p = prop;
for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
struct ppc_one_seg_page_size *sps = &env->sps.sps[i];
if (!sps->page_shift) {
break;
}
for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) {
if (sps->enc[count].page_shift == 0) {
break;
}
}
if ((p - prop) >= (maxcells - 3 - count * 2)) {
break;
}
*(p++) = cpu_to_be32(sps->page_shift);
*(p++) = cpu_to_be32(sps->slb_enc);
*(p++) = cpu_to_be32(count);
for (j = 0; j < count; j++) {
*(p++) = cpu_to_be32(sps->enc[j].page_shift);
*(p++) = cpu_to_be32(sps->enc[j].pte_enc);
}
}
return (p - prop) * sizeof(uint32_t);
}
static void *spapr_create_fdt_skel(const char *cpu_model,
target_phys_addr_t rma_size,
target_phys_addr_t initrd_base,
@ -163,6 +197,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
"\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk";
char qemu_hypertas_prop[] = "hcall-memop1";
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
int i;
char *modelname;
@ -298,6 +333,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
0xffffffff, 0xffffffff};
uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
uint32_t page_sizes_prop[64];
size_t page_sizes_prop_size;
if ((index % smt) != 0) {
continue;
@ -362,6 +399,13 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property_cell(fdt, "ibm,dfp", 1)));
}
page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop,
sizeof(page_sizes_prop));
if (page_sizes_prop_size) {
_FDT((fdt_property(fdt, "ibm,segment-page-sizes",
page_sizes_prop, page_sizes_prop_size)));
}
_FDT((fdt_end_node(fdt)));
}
@ -374,6 +418,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop,
sizeof(hypertas_prop))));
_FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas_prop,
sizeof(qemu_hypertas_prop))));
_FDT((fdt_property(fdt, "ibm,associativity-reference-points",
refpoints, sizeof(refpoints))));

View File

@ -264,7 +264,8 @@ typedef struct sPAPREnvironment {
*/
#define KVMPPC_HCALL_BASE 0xf000
#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
#define KVMPPC_HCALL_MAX KVMPPC_H_RTAS
#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1)
#define KVMPPC_HCALL_MAX KVMPPC_H_LOGICAL_MEMOP
extern sPAPREnvironment *spapr;

View File

@ -608,6 +608,73 @@ static target_ulong h_logical_store(CPUPPCState *env, sPAPREnvironment *spapr,
return H_PARAMETER;
}
static target_ulong h_logical_memop(CPUPPCState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong dst = args[0]; /* Destination address */
target_ulong src = args[1]; /* Source address */
target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */
target_ulong count = args[3]; /* Element count */
target_ulong op = args[4]; /* 0 = copy, 1 = invert */
uint64_t tmp;
unsigned int mask = (1 << esize) - 1;
int step = 1 << esize;
if (count > 0x80000000) {
return H_PARAMETER;
}
if ((dst & mask) || (src & mask) || (op > 1)) {
return H_PARAMETER;
}
if (dst >= src && dst < (src + (count << esize))) {
dst = dst + ((count - 1) << esize);
src = src + ((count - 1) << esize);
step = -step;
}
while (count--) {
switch (esize) {
case 0:
tmp = ldub_phys(src);
break;
case 1:
tmp = lduw_phys(src);
break;
case 2:
tmp = ldl_phys(src);
break;
case 3:
tmp = ldq_phys(src);
break;
default:
return H_PARAMETER;
}
if (op == 1) {
tmp = ~tmp;
}
switch (esize) {
case 0:
stb_phys(dst, tmp);
break;
case 1:
stw_phys(dst, tmp);
break;
case 2:
stl_phys(dst, tmp);
break;
case 3:
stq_phys(dst, tmp);
break;
}
dst = dst + step;
src = src + step;
}
return H_SUCCESS;
}
static target_ulong h_logical_icbi(CPUPPCState *env, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
@ -700,6 +767,7 @@ static void hypercall_register_types(void)
spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store);
spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi);
spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf);
spapr_register_hypercall(KVMPPC_H_LOGICAL_MEMOP, h_logical_memop);
/* qemu/KVM-PPC specific hcalls */
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);

View File

@ -800,6 +800,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
if (crq->s.IU_length > sizeof(union viosrp_iu)) {
fprintf(stderr, "VSCSI: SRP IU too long (%d bytes) !\n",
crq->s.IU_length);
vscsi_put_req(req);
return;
}
@ -807,7 +808,8 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq)
if (spapr_tce_dma_read(&s->vdev, crq->s.IU_data_ptr, &req->iu,
crq->s.IU_length)) {
fprintf(stderr, "vscsi_got_payload: DMA read failure !\n");
g_free(req);
vscsi_put_req(req);
return;
}
memcpy(&req->crq, crq, sizeof(vscsi_crq));

Binary file not shown.

View File

@ -1,119 +0,0 @@
/*
* MPC8544 DS Device Tree Source
*
* Copyright 2007, 2008 Freescale Semiconductor Inc.
*
* 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.
*/
/dts-v1/;
/ {
model = "MPC8544DS";
compatible = "MPC8544DS", "MPC85xxDS";
#address-cells = <1>;
#size-cells = <1>;
aliases {
serial0 = &serial0;
serial1 = &serial1;
pci0 = &pci0;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
};
memory {
device_type = "memory";
reg = <0x0 0x0>; // Filled by U-Boot
};
soc8544@e0000000 {
#address-cells = <1>;
#size-cells = <1>;
device_type = "soc";
compatible = "simple-bus";
ranges = <0x0 0xe0000000 0x100000>;
reg = <0xe0000000 0x1000>; // CCSRBAR 1M
bus-frequency = <0>; // Filled out by uboot.
serial0: serial@4500 {
cell-index = <0>;
device_type = "serial";
compatible = "ns16550";
reg = <0x4500 0x100>;
clock-frequency = <0>;
interrupts = <42 2>;
interrupt-parent = <&mpic>;
};
serial1: serial@4600 {
cell-index = <1>;
device_type = "serial";
compatible = "ns16550";
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <42 2>;
interrupt-parent = <&mpic>;
};
mpic: pic@40000 {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <2>;
reg = <0x40000 0x40000>;
compatible = "chrp,open-pic";
device_type = "open-pic";
};
global-utilities@e0000 { //global utilities block
compatible = "fsl,mpc8544-guts";
reg = <0xe0000 0x1000>;
fsl,has-rstcr;
};
};
pci0: pci@e0008000 {
cell-index = <0>;
compatible = "fsl,mpc8540-pci";
device_type = "pci";
interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
interrupt-map = <
/* IDSEL 0x11 J17 Slot 1 */
0x8800 0x0 0x0 0x1 &mpic 0x2 0x1
0x8800 0x0 0x0 0x2 &mpic 0x3 0x1
0x8800 0x0 0x0 0x3 &mpic 0x4 0x1
0x8800 0x0 0x0 0x4 &mpic 0x1 0x1
/* IDSEL 0x12 J16 Slot 2 */
0x9000 0x0 0x0 0x1 &mpic 0x3 0x1
0x9000 0x0 0x0 0x2 &mpic 0x4 0x1
0x9000 0x0 0x0 0x3 &mpic 0x2 0x1
0x9000 0x0 0x0 0x4 &mpic 0x1 0x1>;
interrupt-parent = <&mpic>;
interrupts = <24 2>;
bus-range = <0 255>;
ranges = <0x2000000 0x0 0xc0000000 0xc0000000 0x0 0x20000000
0x1000000 0x0 0x0 0xe1000000 0x0 0x10000>;
clock-frequency = <66666666>;
#interrupt-cells = <1>;
#size-cells = <2>;
#address-cells = <3>;
reg = <0xe0008000 0x1000>;
};
chosen {
linux,stdout-path = "/soc8544@e0000000/serial@4500";
};
hypervisor {
};
};

View File

@ -583,6 +583,18 @@ static QemuOptsList qemu_machine_opts = {
.name = "dtb",
.type = QEMU_OPT_STRING,
.help = "Linux kernel device tree file",
}, {
.name = "dumpdtb",
.type = QEMU_OPT_STRING,
.help = "Dump current dtb to a file and quit",
}, {
.name = "phandle_start",
.type = QEMU_OPT_STRING,
.help = "The first phandle ID we may generate dynamically",
}, {
.name = "dt_compatible",
.type = QEMU_OPT_STRING,
.help = "Overrides the \"compatible\" property of the dt root node",
},
{ /* End of list */ }
},

View File

@ -69,7 +69,9 @@ void GCC_FMT_ATTR(2, 3) qemu_log_mask(int mask, const char *fmt, ...);
/* cpu_dump_state() logging functions: */
static inline void log_cpu_state(CPUArchState *env1, int flags)
{
cpu_dump_state(env1, qemu_logfile, fprintf, flags);
if (qemu_log_enabled()) {
cpu_dump_state(env1, qemu_logfile, fprintf, flags);
}
}
static inline void log_cpu_state_mask(int mask, CPUArchState *env1, int flags)

View File

@ -1,6 +1,12 @@
obj-y += translate.o op_helper.o helper.o
obj-y += translate.o helper.o
obj-$(CONFIG_SOFTMMU) += machine.o
obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
obj-y += op_helper.o helper.o
$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
obj-y += helper.o
obj-y += excp_helper.o
obj-y += fpu_helper.o
obj-y += int_helper.o
obj-y += mmu_helper.o
obj-y += timebase_helper.o
obj-y += misc_helper.o
obj-y += mem_helper.o
obj-y += mpic_helper.o

View File

@ -119,6 +119,8 @@ enum powerpc_mmu_t {
POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002,
/* Architecture 2.06 variant */
POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG | 0x00000003,
/* Architecture 2.06 "degraded" (no 1T segments) */
POWERPC_MMU_2_06d = POWERPC_MMU_64 | 0x00000003,
#endif /* defined(TARGET_PPC64) */
};
@ -691,7 +693,7 @@ enum {
#define MAS1_VALID 0x80000000
#define MAS2_EPN_SHIFT 12
#define MAS2_EPN_MASK (0xfffff << MAS2_EPN_SHIFT)
#define MAS2_EPN_MASK (~0ULL << MAS2_EPN_SHIFT)
#define MAS2_ACM_SHIFT 6
#define MAS2_ACM (1 << MAS2_ACM_SHIFT)
@ -873,6 +875,29 @@ enum {
#define DBELL_LPIDTAG_MASK (0xfff << DBELL_LPIDTAG_SHIFT)
#define DBELL_PIRTAG_MASK 0x3fff
/*****************************************************************************/
/* Segment page size information, used by recent hash MMUs
* The format of this structure mirrors kvm_ppc_smmu_info
*/
#define PPC_PAGE_SIZES_MAX_SZ 8
struct ppc_one_page_size {
uint32_t page_shift; /* Page shift (or 0) */
uint32_t pte_enc; /* Encoding in the HPTE (>>12) */
};
struct ppc_one_seg_page_size {
uint32_t page_shift; /* Base page shift of segment (or 0) */
uint32_t slb_enc; /* SLB encoding for BookS */
struct ppc_one_page_size enc[PPC_PAGE_SIZES_MAX_SZ];
};
struct ppc_segment_page_sizes {
struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ];
};
/*****************************************************************************/
/* The whole PowerPC CPU context */
#define NB_MMU_MODES 3
@ -889,6 +914,9 @@ struct ppc_def_t {
powerpc_input_t bus_model;
uint32_t flags;
int bfd_mach;
#if defined(TARGET_PPC64)
const struct ppc_segment_page_sizes *sps;
#endif
void (*init_proc)(CPUPPCState *env);
int (*check_pow)(CPUPPCState *env);
};
@ -1012,6 +1040,9 @@ struct CPUPPCState {
uint32_t flags;
uint64_t insns_flags;
uint64_t insns_flags2;
#if defined(TARGET_PPC64)
struct ppc_segment_page_sizes sps;
#endif
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
target_phys_addr_t vpa;
@ -1035,6 +1066,7 @@ struct CPUPPCState {
target_ulong ivor_mask;
target_ulong ivpr_mask;
target_ulong hreset_vector;
target_phys_addr_t mpic_cpu_base;
#endif
/* Those resources are used only during code translation */
@ -1117,27 +1149,12 @@ int get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, target_ulong vaddr,
void do_interrupt (CPUPPCState *env);
void ppc_hw_interrupt (CPUPPCState *env);
void cpu_dump_rfi (target_ulong RA, target_ulong msr);
#if !defined(CONFIG_USER_ONLY)
void ppc6xx_tlb_store (CPUPPCState *env, target_ulong EPN, int way, int is_code,
target_ulong pte0, target_ulong pte1);
void ppc_store_ibatu (CPUPPCState *env, int nr, target_ulong value);
void ppc_store_ibatl (CPUPPCState *env, int nr, target_ulong value);
void ppc_store_dbatu (CPUPPCState *env, int nr, target_ulong value);
void ppc_store_dbatl (CPUPPCState *env, int nr, target_ulong value);
void ppc_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value);
void ppc_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value);
void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
#if defined(TARGET_PPC64)
void ppc_store_asr (CPUPPCState *env, target_ulong value);
target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr);
target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr);
int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
int ppc_load_slb_esid (CPUPPCState *env, target_ulong rb, target_ulong *rt);
int ppc_load_slb_vsid (CPUPPCState *env, target_ulong rb, target_ulong *rt);
#endif /* defined(TARGET_PPC64) */
void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value);
#endif /* !defined(CONFIG_USER_ONLY) */
void ppc_store_msr (CPUPPCState *env, target_ulong value);
@ -1176,19 +1193,11 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val);
void store_booke_tsr (CPUPPCState *env, target_ulong val);
void booke206_flush_tlb(CPUPPCState *env, int flags, const int check_iprot);
target_phys_addr_t booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb);
int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb,
target_phys_addr_t *raddrp, target_ulong address,
uint32_t pid, int ext, int i);
int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb,
target_phys_addr_t *raddrp, target_ulong address,
uint32_t pid);
void ppc_tlb_invalidate_all (CPUPPCState *env);
void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
#if defined(TARGET_PPC64)
void ppc_slb_invalidate_all (CPUPPCState *env);
void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0);
#endif
int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid);
#endif
#endif
@ -1387,6 +1396,7 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
#define SPR_BOOKE_TLB1PS (0x159)
#define SPR_BOOKE_TLB2PS (0x15A)
#define SPR_BOOKE_TLB3PS (0x15B)
#define SPR_BOOKE_MAS7_MAS3 (0x174)
#define SPR_BOOKE_IVOR0 (0x190)
#define SPR_BOOKE_IVOR1 (0x191)
#define SPR_BOOKE_IVOR2 (0x192)
@ -1754,6 +1764,27 @@ static inline void cpu_clone_regs(CPUPPCState *env, target_ulong newsp)
#define SPR_604_HID15 (0x3FF)
#define SPR_E500_SVR (0x3FF)
/* Disable MAS Interrupt Updates for Hypervisor */
#define EPCR_DMIUH (1 << 22)
/* Disable Guest TLB Management Instructions */
#define EPCR_DGTMI (1 << 23)
/* Guest Interrupt Computation Mode */
#define EPCR_GICM (1 << 24)
/* Interrupt Computation Mode */
#define EPCR_ICM (1 << 25)
/* Disable Embedded Hypervisor Debug */
#define EPCR_DUVD (1 << 26)
/* Instruction Storage Interrupt Directed to Guest State */
#define EPCR_ISIGS (1 << 27)
/* Data Storage Interrupt Directed to Guest State */
#define EPCR_DSIGS (1 << 28)
/* Instruction TLB Error Interrupt Directed to Guest State */
#define EPCR_ITLBGS (1 << 29)
/* Data TLB Error Interrupt Directed to Guest State */
#define EPCR_DTLBGS (1 << 30)
/* External Input Interrupt Directed to Guest State */
#define EPCR_EXTGS (1 << 31)
/*****************************************************************************/
/* PowerPC Instructions types definitions */
enum {
@ -2182,6 +2213,15 @@ static inline uint32_t booke206_tlbnps(CPUPPCState *env, const int tlbn)
#endif
static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr)
{
if (env->mmu_model == POWERPC_MMU_BOOKE206) {
return msr & (1ULL << MSR_CM);
}
return msr & (1ULL << MSR_SF);
}
extern void (*cpu_ppc_hypercall)(CPUPPCState *);
static inline bool cpu_has_work(CPUPPCState *env)

969
target-ppc/excp_helper.c Normal file
View File

@ -0,0 +1,969 @@
/*
* PowerPC exception emulation 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 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 "cpu.h"
#include "helper.h"
#include "helper_regs.h"
//#define DEBUG_OP
//#define DEBUG_EXCEPTIONS
#ifdef DEBUG_EXCEPTIONS
# define LOG_EXCP(...) qemu_log(__VA_ARGS__)
#else
# define LOG_EXCP(...) do { } while (0)
#endif
/*****************************************************************************/
/* PowerPC Hypercall emulation */
void (*cpu_ppc_hypercall)(CPUPPCState *);
/*****************************************************************************/
/* Exception processing */
#if defined(CONFIG_USER_ONLY)
void do_interrupt(CPUPPCState *env)
{
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
}
void ppc_hw_interrupt(CPUPPCState *env)
{
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
}
#else /* defined(CONFIG_USER_ONLY) */
static inline void dump_syscall(CPUPPCState *env)
{
qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64
" r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64
" nip=" TARGET_FMT_lx "\n",
ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3),
ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5),
ppc_dump_gpr(env, 6), env->nip);
}
/* Note that this function should be greatly optimized
* when called with a constant excp, from ppc_hw_interrupt
*/
static inline void powerpc_excp(CPUPPCState *env, int excp_model, int excp)
{
target_ulong msr, new_msr, vector;
int srr0, srr1, asrr0, asrr1;
int lpes0, lpes1, lev;
if (0) {
/* XXX: find a suitable condition to enable the hypervisor mode */
lpes0 = (env->spr[SPR_LPCR] >> 1) & 1;
lpes1 = (env->spr[SPR_LPCR] >> 2) & 1;
} else {
/* Those values ensure we won't enter the hypervisor mode */
lpes0 = 0;
lpes1 = 1;
}
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
" => %08x (%02x)\n", env->nip, excp, env->error_code);
/* new srr1 value excluding must-be-zero bits */
msr = env->msr & ~0x783f0000ULL;
/* new interrupt handler msr */
new_msr = env->msr & ((target_ulong)1 << MSR_ME);
/* target registers */
srr0 = SPR_SRR0;
srr1 = SPR_SRR1;
asrr0 = -1;
asrr1 = -1;
switch (excp) {
case POWERPC_EXCP_NONE:
/* Should never happen */
return;
case POWERPC_EXCP_CRITICAL: /* Critical input */
switch (excp_model) {
case POWERPC_EXCP_40x:
srr0 = SPR_40x_SRR2;
srr1 = SPR_40x_SRR3;
break;
case POWERPC_EXCP_BOOKE:
srr0 = SPR_BOOKE_CSRR0;
srr1 = SPR_BOOKE_CSRR1;
break;
case POWERPC_EXCP_G2:
break;
default:
goto excp_invalid;
}
goto store_next;
case POWERPC_EXCP_MCHECK: /* Machine check exception */
if (msr_me == 0) {
/* Machine check exception is not enabled.
* Enter checkstop state.
*/
if (qemu_log_enabled()) {
qemu_log("Machine check while not allowed. "
"Entering checkstop state\n");
} else {
fprintf(stderr, "Machine check while not allowed. "
"Entering checkstop state\n");
}
env->halted = 1;
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
}
if (0) {
/* XXX: find a suitable condition to enable the hypervisor mode */
new_msr |= (target_ulong)MSR_HVB;
}
/* machine check exceptions don't have ME set */
new_msr &= ~((target_ulong)1 << MSR_ME);
/* XXX: should also have something loaded in DAR / DSISR */
switch (excp_model) {
case POWERPC_EXCP_40x:
srr0 = SPR_40x_SRR2;
srr1 = SPR_40x_SRR3;
break;
case POWERPC_EXCP_BOOKE:
srr0 = SPR_BOOKE_MCSRR0;
srr1 = SPR_BOOKE_MCSRR1;
asrr0 = SPR_BOOKE_CSRR0;
asrr1 = SPR_BOOKE_CSRR1;
break;
default:
break;
}
goto store_next;
case POWERPC_EXCP_DSI: /* Data storage exception */
LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx
"\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_ISI: /* Instruction storage exception */
LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx
"\n", msr, env->nip);
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
msr |= env->error_code;
goto store_next;
case POWERPC_EXCP_EXTERNAL: /* External input */
if (lpes0 == 1) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_ALIGN: /* Alignment exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
/* XXX: this is false */
/* Get rS/rD and rA from faulting opcode */
env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4))
& 0x03FF0000) >> 16;
goto store_current;
case POWERPC_EXCP_PROGRAM: /* Program exception */
switch (env->error_code & ~0xF) {
case POWERPC_EXCP_FP:
if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
LOG_EXCP("Ignore floating point exception\n");
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
return;
}
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
msr |= 0x00100000;
if (msr_fe0 == msr_fe1) {
goto store_next;
}
msr |= 0x00010000;
break;
case POWERPC_EXCP_INVAL:
LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip);
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
msr |= 0x00080000;
env->spr[SPR_BOOKE_ESR] = ESR_PIL;
break;
case POWERPC_EXCP_PRIV:
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
msr |= 0x00040000;
env->spr[SPR_BOOKE_ESR] = ESR_PPR;
break;
case POWERPC_EXCP_TRAP:
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
msr |= 0x00020000;
env->spr[SPR_BOOKE_ESR] = ESR_PTR;
break;
default:
/* Should never occur */
cpu_abort(env, "Invalid program exception %d. Aborting\n",
env->error_code);
break;
}
goto store_current;
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_current;
case POWERPC_EXCP_SYSCALL: /* System call exception */
dump_syscall(env);
lev = env->error_code;
if ((lev == 1) && cpu_ppc_hypercall) {
cpu_ppc_hypercall(env);
return;
}
if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
goto store_current;
case POWERPC_EXCP_DECR: /* Decrementer exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
/* FIT on 4xx */
LOG_EXCP("FIT exception\n");
goto store_next;
case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
LOG_EXCP("WDT exception\n");
switch (excp_model) {
case POWERPC_EXCP_BOOKE:
srr0 = SPR_BOOKE_CSRR0;
srr1 = SPR_BOOKE_CSRR1;
break;
default:
break;
}
goto store_next;
case POWERPC_EXCP_DTLB: /* Data TLB error */
goto store_next;
case POWERPC_EXCP_ITLB: /* Instruction TLB error */
goto store_next;
case POWERPC_EXCP_DEBUG: /* Debug interrupt */
switch (excp_model) {
case POWERPC_EXCP_BOOKE:
srr0 = SPR_BOOKE_DSRR0;
srr1 = SPR_BOOKE_DSRR1;
asrr0 = SPR_BOOKE_CSRR0;
asrr1 = SPR_BOOKE_CSRR1;
break;
default:
break;
}
/* XXX: TODO */
cpu_abort(env, "Debug exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
goto store_current;
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
/* XXX: TODO */
cpu_abort(env, "Embedded floating point data exception "
"is not implemented yet !\n");
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
goto store_next;
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
/* XXX: TODO */
cpu_abort(env, "Embedded floating point round exception "
"is not implemented yet !\n");
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
goto store_next;
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
/* XXX: TODO */
cpu_abort(env,
"Performance counter exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
goto store_next;
case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
srr0 = SPR_BOOKE_CSRR0;
srr1 = SPR_BOOKE_CSRR1;
goto store_next;
case POWERPC_EXCP_RESET: /* System reset exception */
if (msr_pow) {
/* indicate that we resumed from power save mode */
msr |= 0x10000;
} else {
new_msr &= ~((target_ulong)1 << MSR_ME);
}
if (0) {
/* XXX: find a suitable condition to enable the hypervisor mode */
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_DSEG: /* Data segment exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_TRACE: /* Trace exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_next;
case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */
srr0 = SPR_HSRR0;
srr1 = SPR_HSRR1;
new_msr |= (target_ulong)MSR_HVB;
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
goto store_next;
case POWERPC_EXCP_VPU: /* Vector unavailable exception */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
goto store_current;
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
LOG_EXCP("PIT exception\n");
goto store_next;
case POWERPC_EXCP_IO: /* IO error exception */
/* XXX: TODO */
cpu_abort(env, "601 IO error exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_RUNM: /* Run mode exception */
/* XXX: TODO */
cpu_abort(env, "601 run mode exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_EMUL: /* Emulation trap exception */
/* XXX: TODO */
cpu_abort(env, "602 emulation trap exception "
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
if (lpes1 == 0) { /* XXX: check this */
new_msr |= (target_ulong)MSR_HVB;
}
switch (excp_model) {
case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
case POWERPC_EXCP_603E:
case POWERPC_EXCP_G2:
goto tlb_miss_tgpr;
case POWERPC_EXCP_7x5:
goto tlb_miss;
case POWERPC_EXCP_74xx:
goto tlb_miss_74xx;
default:
cpu_abort(env, "Invalid instruction TLB miss exception\n");
break;
}
break;
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
if (lpes1 == 0) { /* XXX: check this */
new_msr |= (target_ulong)MSR_HVB;
}
switch (excp_model) {
case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
case POWERPC_EXCP_603E:
case POWERPC_EXCP_G2:
goto tlb_miss_tgpr;
case POWERPC_EXCP_7x5:
goto tlb_miss;
case POWERPC_EXCP_74xx:
goto tlb_miss_74xx;
default:
cpu_abort(env, "Invalid data load TLB miss exception\n");
break;
}
break;
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
if (lpes1 == 0) { /* XXX: check this */
new_msr |= (target_ulong)MSR_HVB;
}
switch (excp_model) {
case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
case POWERPC_EXCP_603E:
case POWERPC_EXCP_G2:
tlb_miss_tgpr:
/* Swap temporary saved registers with GPRs */
if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) {
new_msr |= (target_ulong)1 << MSR_TGPR;
hreg_swap_gpr_tgpr(env);
}
goto tlb_miss;
case POWERPC_EXCP_7x5:
tlb_miss:
#if defined(DEBUG_SOFTWARE_TLB)
if (qemu_log_enabled()) {
const char *es;
target_ulong *miss, *cmp;
int en;
if (excp == POWERPC_EXCP_IFTLB) {
es = "I";
en = 'I';
miss = &env->spr[SPR_IMISS];
cmp = &env->spr[SPR_ICMP];
} else {
if (excp == POWERPC_EXCP_DLTLB) {
es = "DL";
} else {
es = "DS";
}
en = 'D';
miss = &env->spr[SPR_DMISS];
cmp = &env->spr[SPR_DCMP];
}
qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
env->spr[SPR_HASH1], env->spr[SPR_HASH2],
env->error_code);
}
#endif
msr |= env->crf[0] << 28;
msr |= env->error_code; /* key, D/I, S/L bits */
/* Set way using a LRU mechanism */
msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17;
break;
case POWERPC_EXCP_74xx:
tlb_miss_74xx:
#if defined(DEBUG_SOFTWARE_TLB)
if (qemu_log_enabled()) {
const char *es;
target_ulong *miss, *cmp;
int en;
if (excp == POWERPC_EXCP_IFTLB) {
es = "I";
en = 'I';
miss = &env->spr[SPR_TLBMISS];
cmp = &env->spr[SPR_PTEHI];
} else {
if (excp == POWERPC_EXCP_DLTLB) {
es = "DL";
} else {
es = "DS";
}
en = 'D';
miss = &env->spr[SPR_TLBMISS];
cmp = &env->spr[SPR_PTEHI];
}
qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
env->error_code);
}
#endif
msr |= env->error_code; /* key bit */
break;
default:
cpu_abort(env, "Invalid data store TLB miss exception\n");
break;
}
goto store_next;
case POWERPC_EXCP_FPA: /* Floating-point assist exception */
/* XXX: TODO */
cpu_abort(env, "Floating point assist exception "
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_DABR: /* Data address breakpoint */
/* XXX: TODO */
cpu_abort(env, "DABR exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
/* XXX: TODO */
cpu_abort(env, "IABR exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_SMI: /* System management interrupt */
/* XXX: TODO */
cpu_abort(env, "SMI exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_THERM: /* Thermal interrupt */
/* XXX: TODO */
cpu_abort(env, "Thermal management exception "
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
if (lpes1 == 0) {
new_msr |= (target_ulong)MSR_HVB;
}
/* XXX: TODO */
cpu_abort(env,
"Performance counter exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_VPUA: /* Vector assist exception */
/* XXX: TODO */
cpu_abort(env, "VPU assist exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_SOFTP: /* Soft patch exception */
/* XXX: TODO */
cpu_abort(env,
"970 soft-patch exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_MAINT: /* Maintenance exception */
/* XXX: TODO */
cpu_abort(env,
"970 maintenance exception is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */
/* XXX: TODO */
cpu_abort(env, "Maskable external exception "
"is not implemented yet !\n");
goto store_next;
case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */
/* XXX: TODO */
cpu_abort(env, "Non maskable external exception "
"is not implemented yet !\n");
goto store_next;
default:
excp_invalid:
cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp);
break;
store_current:
/* save current instruction location */
env->spr[srr0] = env->nip - 4;
break;
store_next:
/* save next instruction location */
env->spr[srr0] = env->nip;
break;
}
/* Save MSR */
env->spr[srr1] = msr;
/* If any alternate SRR register are defined, duplicate saved values */
if (asrr0 != -1) {
env->spr[asrr0] = env->spr[srr0];
}
if (asrr1 != -1) {
env->spr[asrr1] = env->spr[srr1];
}
/* If we disactivated any translation, flush TLBs */
if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) {
tlb_flush(env, 1);
}
if (msr_ile) {
new_msr |= (target_ulong)1 << MSR_LE;
}
/* Jump to handler */
vector = env->excp_vectors[excp];
if (vector == (target_ulong)-1ULL) {
cpu_abort(env, "Raised an exception without defined vector %d\n",
excp);
}
vector |= env->excp_prefix;
#if defined(TARGET_PPC64)
if (excp_model == POWERPC_EXCP_BOOKE) {
if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
/* Cat.64-bit: EPCR.ICM is copied to MSR.CM */
new_msr |= (target_ulong)1 << MSR_CM;
} else {
vector = (uint32_t)vector;
}
} else {
if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) {
vector = (uint32_t)vector;
} else {
new_msr |= (target_ulong)1 << MSR_SF;
}
}
#endif
/* XXX: we don't use hreg_store_msr here as already have treated
* any special case that could occur. Just store MSR and update hflags
*/
env->msr = new_msr & env->msr_mask;
hreg_compute_hflags(env);
env->nip = vector;
/* Reset exception state */
env->exception_index = POWERPC_EXCP_NONE;
env->error_code = 0;
if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
(env->mmu_model == POWERPC_MMU_BOOKE206)) {
/* XXX: The BookE changes address space when switching modes,
we should probably implement that as different MMU indexes,
but for the moment we do it the slow way and flush all. */
tlb_flush(env, 1);
}
}
void do_interrupt(CPUPPCState *env)
{
powerpc_excp(env, env->excp_model, env->exception_index);
}
void ppc_hw_interrupt(CPUPPCState *env)
{
int hdice;
#if 0
qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n",
__func__, env, env->pending_interrupts,
env->interrupt_request, (int)msr_me, (int)msr_ee);
#endif
/* External reset */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET);
return;
}
/* Machine check exception */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK);
return;
}
#if 0 /* TODO */
/* External debug exception */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG);
return;
}
#endif
if (0) {
/* XXX: find a suitable condition to enable the hypervisor mode */
hdice = env->spr[SPR_LPCR] & 1;
} else {
hdice = 0;
}
if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) {
/* Hypervisor decrementer exception */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR);
return;
}
}
if (msr_ce != 0) {
/* External critical interrupt */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) {
/* Taking a critical external interrupt does not clear the external
* critical interrupt status
*/
#if 0
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT);
#endif
powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL);
return;
}
}
if (msr_ee != 0) {
/* Watchdog timer on embedded PowerPC */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT);
return;
}
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI);
return;
}
/* Fixed interval timer on embedded PowerPC */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT);
return;
}
/* Programmable interval timer on embedded PowerPC */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT);
return;
}
/* Decrementer exception */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR);
return;
}
/* External interrupt */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
/* Taking an external interrupt does not clear the external
* interrupt status
*/
#if 0
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT);
#endif
powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL);
return;
}
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI);
return;
}
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM);
return;
}
/* Thermal interrupt */
if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) {
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM);
powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM);
return;
}
}
}
#endif /* !CONFIG_USER_ONLY */
#if defined(DEBUG_OP)
static void cpu_dump_rfi(target_ulong RA, target_ulong msr)
{
qemu_log("Return from exception at " TARGET_FMT_lx " with flags "
TARGET_FMT_lx "\n", RA, msr);
}
#endif
/*****************************************************************************/
/* Exceptions processing helpers */
void helper_raise_exception_err(CPUPPCState *env, uint32_t exception,
uint32_t error_code)
{
#if 0
printf("Raise exception %3x code : %d\n", exception, error_code);
#endif
env->exception_index = exception;
env->error_code = error_code;
cpu_loop_exit(env);
}
void helper_raise_exception(CPUPPCState *env, uint32_t exception)
{
helper_raise_exception_err(env, exception, 0);
}
#if !defined(CONFIG_USER_ONLY)
void helper_store_msr(CPUPPCState *env, target_ulong val)
{
val = hreg_store_msr(env, val, 0);
if (val != 0) {
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
helper_raise_exception(env, val);
}
}
static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
target_ulong msrm, int keep_msrh)
{
#if defined(TARGET_PPC64)
if (msr_is_64bit(env, msr)) {
nip = (uint64_t)nip;
msr &= (uint64_t)msrm;
} else {
nip = (uint32_t)nip;
msr = (uint32_t)(msr & msrm);
if (keep_msrh) {
msr |= env->msr & ~((uint64_t)0xFFFFFFFF);
}
}
#else
nip = (uint32_t)nip;
msr &= (uint32_t)msrm;
#endif
/* XXX: beware: this is false if VLE is supported */
env->nip = nip & ~((target_ulong)0x00000003);
hreg_store_msr(env, msr, 1);
#if defined(DEBUG_OP)
cpu_dump_rfi(env->nip, env->msr);
#endif
/* No need to raise an exception here,
* as rfi is always the last insn of a TB
*/
env->interrupt_request |= CPU_INTERRUPT_EXITTB;
}
void helper_rfi(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1],
~((target_ulong)0x783F0000), 1);
}
#if defined(TARGET_PPC64)
void helper_rfid(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1],
~((target_ulong)0x783F0000), 0);
}
void helper_hrfid(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1],
~((target_ulong)0x783F0000), 0);
}
#endif
/*****************************************************************************/
/* Embedded PowerPC specific helpers */
void helper_40x_rfci(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3],
~((target_ulong)0xFFFF0000), 0);
}
void helper_rfci(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1,
~((target_ulong)0x3FFF0000), 0);
}
void helper_rfdi(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1,
~((target_ulong)0x3FFF0000), 0);
}
void helper_rfmci(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1,
~((target_ulong)0x3FFF0000), 0);
}
#endif
void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
uint32_t flags)
{
if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) ||
((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) ||
((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) ||
((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) ||
((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) {
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_TRAP);
}
}
#if defined(TARGET_PPC64)
void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
uint32_t flags)
{
if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) ||
((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) ||
((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) ||
((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) ||
((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) {
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_TRAP);
}
}
#endif
#if !defined(CONFIG_USER_ONLY)
/*****************************************************************************/
/* PowerPC 601 specific instructions (POWER bridge) */
void helper_rfsvc(CPUPPCState *env)
{
do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0);
}
/* Embedded.Processor Control */
static int dbell2irq(target_ulong rb)
{
int msg = rb & DBELL_TYPE_MASK;
int irq = -1;
switch (msg) {
case DBELL_TYPE_DBELL:
irq = PPC_INTERRUPT_DOORBELL;
break;
case DBELL_TYPE_DBELL_CRIT:
irq = PPC_INTERRUPT_CDOORBELL;
break;
case DBELL_TYPE_G_DBELL:
case DBELL_TYPE_G_DBELL_CRIT:
case DBELL_TYPE_G_DBELL_MC:
/* XXX implement */
default:
break;
}
return irq;
}
void helper_msgclr(CPUPPCState *env, target_ulong rb)
{
int irq = dbell2irq(rb);
if (irq < 0) {
return;
}
env->pending_interrupts &= ~(1 << irq);
}
void helper_msgsnd(target_ulong rb)
{
int irq = dbell2irq(rb);
int pir = rb & DBELL_PIRTAG_MASK;
CPUPPCState *cenv;
if (irq < 0) {
return;
}
for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) {
cenv->pending_interrupts |= 1 << irq;
cpu_interrupt(cenv, CPU_INTERRUPT_HARD);
}
}
}
#endif

1740
target-ppc/fpu_helper.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,96 +1,96 @@
#include "def-helper.h"
DEF_HELPER_2(raise_exception_err, void, i32, i32)
DEF_HELPER_1(raise_exception, void, i32)
DEF_HELPER_3(tw, void, tl, tl, i32)
DEF_HELPER_3(raise_exception_err, void, env, i32, i32)
DEF_HELPER_2(raise_exception, void, env, i32)
DEF_HELPER_4(tw, void, env, tl, tl, i32)
#if defined(TARGET_PPC64)
DEF_HELPER_3(td, void, tl, tl, i32)
DEF_HELPER_4(td, void, env, tl, tl, i32)
#endif
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_1(store_msr, void, tl)
DEF_HELPER_0(rfi, void)
DEF_HELPER_0(rfsvc, void)
DEF_HELPER_0(40x_rfci, void)
DEF_HELPER_0(rfci, void)
DEF_HELPER_0(rfdi, void)
DEF_HELPER_0(rfmci, void)
DEF_HELPER_2(store_msr, void, env, tl)
DEF_HELPER_1(rfi, void, env)
DEF_HELPER_1(rfsvc, void, env)
DEF_HELPER_1(40x_rfci, void, env)
DEF_HELPER_1(rfci, void, env)
DEF_HELPER_1(rfdi, void, env)
DEF_HELPER_1(rfmci, void, env)
#if defined(TARGET_PPC64)
DEF_HELPER_0(rfid, void)
DEF_HELPER_0(hrfid, void)
DEF_HELPER_1(rfid, void, env)
DEF_HELPER_1(hrfid, void, env)
#endif
#endif
DEF_HELPER_2(lmw, void, tl, i32)
DEF_HELPER_2(stmw, void, tl, i32)
DEF_HELPER_3(lsw, void, tl, i32, i32)
DEF_HELPER_4(lswx, void, tl, i32, i32, i32)
DEF_HELPER_3(stsw, void, tl, i32, i32)
DEF_HELPER_1(dcbz, void, tl)
DEF_HELPER_1(dcbz_970, void, tl)
DEF_HELPER_1(icbi, void, tl)
DEF_HELPER_4(lscbx, tl, tl, i32, i32, i32)
DEF_HELPER_3(lmw, void, env, tl, i32)
DEF_HELPER_3(stmw, void, env, tl, i32)
DEF_HELPER_4(lsw, void, env, tl, i32, i32)
DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32)
DEF_HELPER_4(stsw, void, env, tl, i32, i32)
DEF_HELPER_2(dcbz, void, env, tl)
DEF_HELPER_2(dcbz_970, void, env, tl)
DEF_HELPER_2(icbi, void, env, tl)
DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32)
#if defined(TARGET_PPC64)
DEF_HELPER_FLAGS_2(mulhd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
DEF_HELPER_FLAGS_2(mulhdu, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
DEF_HELPER_2(mulldo, i64, i64, i64)
DEF_HELPER_3(mulldo, i64, env, i64, i64)
#endif
DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
DEF_HELPER_FLAGS_1(popcntw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
DEF_HELPER_2(sraw, tl, tl, tl)
DEF_HELPER_3(sraw, tl, env, tl, tl)
#if defined(TARGET_PPC64)
DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
DEF_HELPER_2(srad, tl, tl, tl)
DEF_HELPER_3(srad, tl, env, tl, tl)
#endif
DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
DEF_HELPER_FLAGS_2(brinc, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl)
DEF_HELPER_0(float_check_status, void)
DEF_HELPER_0(reset_fpstatus, void)
DEF_HELPER_2(compute_fprf, i32, i64, i32)
DEF_HELPER_2(store_fpscr, void, i64, i32)
DEF_HELPER_1(fpscr_clrbit, void, i32)
DEF_HELPER_1(fpscr_setbit, void, i32)
DEF_HELPER_1(float64_to_float32, i32, i64)
DEF_HELPER_1(float32_to_float64, i64, i32)
DEF_HELPER_1(float_check_status, void, env)
DEF_HELPER_1(reset_fpstatus, void, env)
DEF_HELPER_3(compute_fprf, i32, env, i64, i32)
DEF_HELPER_3(store_fpscr, void, env, i64, i32)
DEF_HELPER_2(fpscr_clrbit, void, env, i32)
DEF_HELPER_2(fpscr_setbit, void, env, i32)
DEF_HELPER_2(float64_to_float32, i32, env, i64)
DEF_HELPER_2(float32_to_float64, i64, env, i32)
DEF_HELPER_3(fcmpo, void, i64, i64, i32)
DEF_HELPER_3(fcmpu, void, i64, i64, i32)
DEF_HELPER_4(fcmpo, void, env, i64, i64, i32)
DEF_HELPER_4(fcmpu, void, env, i64, i64, i32)
DEF_HELPER_1(fctiw, i64, i64)
DEF_HELPER_1(fctiwz, i64, i64)
DEF_HELPER_2(fctiw, i64, env, i64)
DEF_HELPER_2(fctiwz, i64, env, i64)
#if defined(TARGET_PPC64)
DEF_HELPER_1(fcfid, i64, i64)
DEF_HELPER_1(fctid, i64, i64)
DEF_HELPER_1(fctidz, i64, i64)
DEF_HELPER_2(fcfid, i64, env, i64)
DEF_HELPER_2(fctid, i64, env, i64)
DEF_HELPER_2(fctidz, i64, env, i64)
#endif
DEF_HELPER_1(frsp, i64, i64)
DEF_HELPER_1(frin, i64, i64)
DEF_HELPER_1(friz, i64, i64)
DEF_HELPER_1(frip, i64, i64)
DEF_HELPER_1(frim, i64, i64)
DEF_HELPER_2(frsp, i64, env, i64)
DEF_HELPER_2(frin, i64, env, i64)
DEF_HELPER_2(friz, i64, env, i64)
DEF_HELPER_2(frip, i64, env, i64)
DEF_HELPER_2(frim, i64, env, i64)
DEF_HELPER_2(fadd, i64, i64, i64)
DEF_HELPER_2(fsub, i64, i64, i64)
DEF_HELPER_2(fmul, i64, i64, i64)
DEF_HELPER_2(fdiv, i64, i64, i64)
DEF_HELPER_3(fmadd, i64, i64, i64, i64)
DEF_HELPER_3(fmsub, i64, i64, i64, i64)
DEF_HELPER_3(fnmadd, i64, i64, i64, i64)
DEF_HELPER_3(fnmsub, i64, i64, i64, i64)
DEF_HELPER_1(fabs, i64, i64)
DEF_HELPER_1(fnabs, i64, i64)
DEF_HELPER_1(fneg, i64, i64)
DEF_HELPER_1(fsqrt, i64, i64)
DEF_HELPER_1(fre, i64, i64)
DEF_HELPER_1(fres, i64, i64)
DEF_HELPER_1(frsqrte, i64, i64)
DEF_HELPER_3(fsel, i64, i64, i64, i64)
DEF_HELPER_3(fadd, i64, env, i64, i64)
DEF_HELPER_3(fsub, i64, env, i64, i64)
DEF_HELPER_3(fmul, i64, env, i64, i64)
DEF_HELPER_3(fdiv, i64, env, i64, i64)
DEF_HELPER_4(fmadd, i64, env, i64, i64, i64)
DEF_HELPER_4(fmsub, i64, env, i64, i64, i64)
DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64)
DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64)
DEF_HELPER_2(fabs, i64, env, i64)
DEF_HELPER_2(fnabs, i64, env, i64)
DEF_HELPER_2(fneg, i64, env, i64)
DEF_HELPER_2(fsqrt, i64, env, i64)
DEF_HELPER_2(fre, i64, env, i64)
DEF_HELPER_2(fres, i64, env, i64)
DEF_HELPER_2(frsqrte, i64, env, i64)
DEF_HELPER_4(fsel, i64, env, i64, i64, i64)
#define dh_alias_avr ptr
#define dh_ctype_avr ppc_avr_t *
@ -120,32 +120,32 @@ DEF_HELPER_3(vminuw, void, avr, avr, avr)
DEF_HELPER_3(vmaxub, void, avr, avr, avr)
DEF_HELPER_3(vmaxuh, void, avr, avr, avr)
DEF_HELPER_3(vmaxuw, void, avr, avr, avr)
DEF_HELPER_3(vcmpequb, void, avr, avr, avr)
DEF_HELPER_3(vcmpequh, void, avr, avr, avr)
DEF_HELPER_3(vcmpequw, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtub, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtuh, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtuw, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtsb, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtsh, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtsw, void, avr, avr, avr)
DEF_HELPER_3(vcmpeqfp, void, avr, avr, avr)
DEF_HELPER_3(vcmpgefp, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtfp, void, avr, avr, avr)
DEF_HELPER_3(vcmpbfp, void, avr, avr, avr)
DEF_HELPER_3(vcmpequb_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpequh_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpequw_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtub_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtuh_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtuw_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtsb_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtsh_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtsw_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpeqfp_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgefp_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpgtfp_dot, void, avr, avr, avr)
DEF_HELPER_3(vcmpbfp_dot, void, avr, avr, avr)
DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr)
DEF_HELPER_4(vcmpbfp_dot, void, env, avr, avr, avr)
DEF_HELPER_3(vmrglb, void, avr, avr, avr)
DEF_HELPER_3(vmrglh, void, avr, avr, avr)
DEF_HELPER_3(vmrglw, void, avr, avr, avr)
@ -175,18 +175,18 @@ DEF_HELPER_3(vaddcuw, void, avr, avr, avr)
DEF_HELPER_3(vsubcuw, void, avr, avr, avr)
DEF_HELPER_2(lvsl, void, avr, tl);
DEF_HELPER_2(lvsr, void, avr, tl);
DEF_HELPER_3(vaddsbs, void, avr, avr, avr)
DEF_HELPER_3(vaddshs, void, avr, avr, avr)
DEF_HELPER_3(vaddsws, void, avr, avr, avr)
DEF_HELPER_3(vsubsbs, void, avr, avr, avr)
DEF_HELPER_3(vsubshs, void, avr, avr, avr)
DEF_HELPER_3(vsubsws, void, avr, avr, avr)
DEF_HELPER_3(vaddubs, void, avr, avr, avr)
DEF_HELPER_3(vadduhs, void, avr, avr, avr)
DEF_HELPER_3(vadduws, void, avr, avr, avr)
DEF_HELPER_3(vsububs, void, avr, avr, avr)
DEF_HELPER_3(vsubuhs, void, avr, avr, avr)
DEF_HELPER_3(vsubuws, void, avr, avr, avr)
DEF_HELPER_4(vaddsbs, void, env, avr, avr, avr)
DEF_HELPER_4(vaddshs, void, env, avr, avr, avr)
DEF_HELPER_4(vaddsws, void, env, avr, avr, avr)
DEF_HELPER_4(vsubsbs, void, env, avr, avr, avr)
DEF_HELPER_4(vsubshs, void, env, avr, avr, avr)
DEF_HELPER_4(vsubsws, void, env, avr, avr, avr)
DEF_HELPER_4(vaddubs, void, env, avr, avr, avr)
DEF_HELPER_4(vadduhs, void, env, avr, avr, avr)
DEF_HELPER_4(vadduws, void, env, avr, avr, avr)
DEF_HELPER_4(vsububs, void, env, avr, avr, avr)
DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr)
DEF_HELPER_4(vsubuws, void, env, avr, avr, avr)
DEF_HELPER_3(vrlb, void, avr, avr, avr)
DEF_HELPER_3(vrlh, void, avr, avr, avr)
DEF_HELPER_3(vrlw, void, avr, avr, avr)
@ -205,212 +205,213 @@ DEF_HELPER_2(vupkhsb, void, avr, avr)
DEF_HELPER_2(vupkhsh, void, avr, avr)
DEF_HELPER_2(vupklsb, void, avr, avr)
DEF_HELPER_2(vupklsh, void, avr, avr)
DEF_HELPER_4(vmsumubm, void, avr, avr, avr, avr)
DEF_HELPER_4(vmsummbm, void, avr, avr, avr, avr)
DEF_HELPER_4(vsel, void, avr, avr, avr, avr)
DEF_HELPER_4(vperm, void, avr, avr, avr, avr)
DEF_HELPER_3(vpkshss, void, avr, avr, avr)
DEF_HELPER_3(vpkshus, void, avr, avr, avr)
DEF_HELPER_3(vpkswss, void, avr, avr, avr)
DEF_HELPER_3(vpkswus, void, avr, avr, avr)
DEF_HELPER_3(vpkuhus, void, avr, avr, avr)
DEF_HELPER_3(vpkuwus, void, avr, avr, avr)
DEF_HELPER_3(vpkuhum, void, avr, avr, avr)
DEF_HELPER_3(vpkuwum, void, avr, avr, avr)
DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vperm, void, env, avr, avr, avr, avr)
DEF_HELPER_4(vpkshss, void, env, avr, avr, avr)
DEF_HELPER_4(vpkshus, void, env, avr, avr, avr)
DEF_HELPER_4(vpkswss, void, env, avr, avr, avr)
DEF_HELPER_4(vpkswus, void, env, avr, avr, avr)
DEF_HELPER_4(vpkuhus, void, env, avr, avr, avr)
DEF_HELPER_4(vpkuwus, void, env, avr, avr, avr)
DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr)
DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr)
DEF_HELPER_3(vpkpx, void, avr, avr, avr)
DEF_HELPER_4(vmhaddshs, void, avr, avr, avr, avr)
DEF_HELPER_4(vmhraddshs, void, avr, avr, avr, avr)
DEF_HELPER_4(vmsumuhm, void, avr, avr, avr, avr)
DEF_HELPER_4(vmsumuhs, void, avr, avr, avr, avr)
DEF_HELPER_4(vmsumshm, void, avr, avr, avr, avr)
DEF_HELPER_4(vmsumshs, void, avr, avr, avr, avr)
DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsumuhm, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsumuhs, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsumshm, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vmsumshs, void, env, avr, avr, avr, avr)
DEF_HELPER_4(vmladduhm, void, avr, avr, avr, avr)
DEF_HELPER_1(mtvscr, void, avr);
DEF_HELPER_2(lvebx, void, avr, tl)
DEF_HELPER_2(lvehx, void, avr, tl)
DEF_HELPER_2(lvewx, void, avr, tl)
DEF_HELPER_2(stvebx, void, avr, tl)
DEF_HELPER_2(stvehx, void, avr, tl)
DEF_HELPER_2(stvewx, void, avr, tl)
DEF_HELPER_3(vsumsws, void, avr, avr, avr)
DEF_HELPER_3(vsum2sws, void, avr, avr, avr)
DEF_HELPER_3(vsum4sbs, void, avr, avr, avr)
DEF_HELPER_3(vsum4shs, void, avr, avr, avr)
DEF_HELPER_3(vsum4ubs, void, avr, avr, avr)
DEF_HELPER_3(vaddfp, void, avr, avr, avr)
DEF_HELPER_3(vsubfp, void, avr, avr, avr)
DEF_HELPER_3(vmaxfp, void, avr, avr, avr)
DEF_HELPER_3(vminfp, void, avr, avr, avr)
DEF_HELPER_2(vrefp, void, avr, avr)
DEF_HELPER_2(vrsqrtefp, void, avr, avr)
DEF_HELPER_4(vmaddfp, void, avr, avr, avr, avr)
DEF_HELPER_4(vnmsubfp, void, avr, avr, avr, avr)
DEF_HELPER_2(vexptefp, void, avr, avr)
DEF_HELPER_2(vlogefp, void, avr, avr)
DEF_HELPER_2(vrfim, void, avr, avr)
DEF_HELPER_2(vrfin, void, avr, avr)
DEF_HELPER_2(vrfip, void, avr, avr)
DEF_HELPER_2(vrfiz, void, avr, avr)
DEF_HELPER_3(vcfux, void, avr, avr, i32)
DEF_HELPER_3(vcfsx, void, avr, avr, i32)
DEF_HELPER_3(vctuxs, void, avr, avr, i32)
DEF_HELPER_3(vctsxs, void, avr, avr, i32)
DEF_HELPER_2(mtvscr, void, env, avr);
DEF_HELPER_3(lvebx, void, env, avr, tl)
DEF_HELPER_3(lvehx, void, env, avr, tl)
DEF_HELPER_3(lvewx, void, env, avr, tl)
DEF_HELPER_3(stvebx, void, env, avr, tl)
DEF_HELPER_3(stvehx, void, env, avr, tl)
DEF_HELPER_3(stvewx, void, env, avr, tl)
DEF_HELPER_4(vsumsws, void, env, avr, avr, avr)
DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr)
DEF_HELPER_4(vsum4sbs, void, env, avr, avr, avr)
DEF_HELPER_4(vsum4shs, void, env, avr, avr, avr)
DEF_HELPER_4(vsum4ubs, void, env, avr, avr, avr)
DEF_HELPER_4(vaddfp, void, env, avr, avr, avr)
DEF_HELPER_4(vsubfp, void, env, avr, avr, avr)
DEF_HELPER_4(vmaxfp, void, env, avr, avr, avr)
DEF_HELPER_4(vminfp, void, env, avr, avr, avr)
DEF_HELPER_3(vrefp, void, env, avr, avr)
DEF_HELPER_3(vrsqrtefp, void, env, avr, avr)
DEF_HELPER_5(vmaddfp, void, env, avr, avr, avr, avr)
DEF_HELPER_5(vnmsubfp, void, env, avr, avr, avr, avr)
DEF_HELPER_3(vexptefp, void, env, avr, avr)
DEF_HELPER_3(vlogefp, void, env, avr, avr)
DEF_HELPER_3(vrfim, void, env, avr, avr)
DEF_HELPER_3(vrfin, void, env, avr, avr)
DEF_HELPER_3(vrfip, void, env, avr, avr)
DEF_HELPER_3(vrfiz, void, env, avr, avr)
DEF_HELPER_4(vcfux, void, env, avr, avr, i32)
DEF_HELPER_4(vcfsx, void, env, avr, avr, i32)
DEF_HELPER_4(vctuxs, void, env, avr, avr, i32)
DEF_HELPER_4(vctsxs, void, env, avr, avr, i32)
DEF_HELPER_1(efscfsi, i32, i32)
DEF_HELPER_1(efscfui, i32, i32)
DEF_HELPER_1(efscfuf, i32, i32)
DEF_HELPER_1(efscfsf, i32, i32)
DEF_HELPER_1(efsctsi, i32, i32)
DEF_HELPER_1(efsctui, i32, i32)
DEF_HELPER_1(efsctsiz, i32, i32)
DEF_HELPER_1(efsctuiz, i32, i32)
DEF_HELPER_1(efsctsf, i32, i32)
DEF_HELPER_1(efsctuf, i32, i32)
DEF_HELPER_1(evfscfsi, i64, i64)
DEF_HELPER_1(evfscfui, i64, i64)
DEF_HELPER_1(evfscfuf, i64, i64)
DEF_HELPER_1(evfscfsf, i64, i64)
DEF_HELPER_1(evfsctsi, i64, i64)
DEF_HELPER_1(evfsctui, i64, i64)
DEF_HELPER_1(evfsctsiz, i64, i64)
DEF_HELPER_1(evfsctuiz, i64, i64)
DEF_HELPER_1(evfsctsf, i64, i64)
DEF_HELPER_1(evfsctuf, i64, i64)
DEF_HELPER_2(efsadd, i32, i32, i32)
DEF_HELPER_2(efssub, i32, i32, i32)
DEF_HELPER_2(efsmul, i32, i32, i32)
DEF_HELPER_2(efsdiv, i32, i32, i32)
DEF_HELPER_2(evfsadd, i64, i64, i64)
DEF_HELPER_2(evfssub, i64, i64, i64)
DEF_HELPER_2(evfsmul, i64, i64, i64)
DEF_HELPER_2(evfsdiv, i64, i64, i64)
DEF_HELPER_2(efststlt, i32, i32, i32)
DEF_HELPER_2(efststgt, i32, i32, i32)
DEF_HELPER_2(efststeq, i32, i32, i32)
DEF_HELPER_2(efscmplt, i32, i32, i32)
DEF_HELPER_2(efscmpgt, i32, i32, i32)
DEF_HELPER_2(efscmpeq, i32, i32, i32)
DEF_HELPER_2(evfststlt, i32, i64, i64)
DEF_HELPER_2(evfststgt, i32, i64, i64)
DEF_HELPER_2(evfststeq, i32, i64, i64)
DEF_HELPER_2(evfscmplt, i32, i64, i64)
DEF_HELPER_2(evfscmpgt, i32, i64, i64)
DEF_HELPER_2(evfscmpeq, i32, i64, i64)
DEF_HELPER_1(efdcfsi, i64, i32)
DEF_HELPER_1(efdcfsid, i64, i64)
DEF_HELPER_1(efdcfui, i64, i32)
DEF_HELPER_1(efdcfuid, i64, i64)
DEF_HELPER_1(efdctsi, i32, i64)
DEF_HELPER_1(efdctui, i32, i64)
DEF_HELPER_1(efdctsiz, i32, i64)
DEF_HELPER_1(efdctsidz, i64, i64)
DEF_HELPER_1(efdctuiz, i32, i64)
DEF_HELPER_1(efdctuidz, i64, i64)
DEF_HELPER_1(efdcfsf, i64, i32)
DEF_HELPER_1(efdcfuf, i64, i32)
DEF_HELPER_1(efdctsf, i32, i64)
DEF_HELPER_1(efdctuf, i32, i64)
DEF_HELPER_1(efscfd, i32, i64)
DEF_HELPER_1(efdcfs, i64, i32)
DEF_HELPER_2(efdadd, i64, i64, i64)
DEF_HELPER_2(efdsub, i64, i64, i64)
DEF_HELPER_2(efdmul, i64, i64, i64)
DEF_HELPER_2(efddiv, i64, i64, i64)
DEF_HELPER_2(efdtstlt, i32, i64, i64)
DEF_HELPER_2(efdtstgt, i32, i64, i64)
DEF_HELPER_2(efdtsteq, i32, i64, i64)
DEF_HELPER_2(efdcmplt, i32, i64, i64)
DEF_HELPER_2(efdcmpgt, i32, i64, i64)
DEF_HELPER_2(efdcmpeq, i32, i64, i64)
DEF_HELPER_2(efscfsi, i32, env, i32)
DEF_HELPER_2(efscfui, i32, env, i32)
DEF_HELPER_2(efscfuf, i32, env, i32)
DEF_HELPER_2(efscfsf, i32, env, i32)
DEF_HELPER_2(efsctsi, i32, env, i32)
DEF_HELPER_2(efsctui, i32, env, i32)
DEF_HELPER_2(efsctsiz, i32, env, i32)
DEF_HELPER_2(efsctuiz, i32, env, i32)
DEF_HELPER_2(efsctsf, i32, env, i32)
DEF_HELPER_2(efsctuf, i32, env, i32)
DEF_HELPER_2(evfscfsi, i64, env, i64)
DEF_HELPER_2(evfscfui, i64, env, i64)
DEF_HELPER_2(evfscfuf, i64, env, i64)
DEF_HELPER_2(evfscfsf, i64, env, i64)
DEF_HELPER_2(evfsctsi, i64, env, i64)
DEF_HELPER_2(evfsctui, i64, env, i64)
DEF_HELPER_2(evfsctsiz, i64, env, i64)
DEF_HELPER_2(evfsctuiz, i64, env, i64)
DEF_HELPER_2(evfsctsf, i64, env, i64)
DEF_HELPER_2(evfsctuf, i64, env, i64)
DEF_HELPER_3(efsadd, i32, env, i32, i32)
DEF_HELPER_3(efssub, i32, env, i32, i32)
DEF_HELPER_3(efsmul, i32, env, i32, i32)
DEF_HELPER_3(efsdiv, i32, env, i32, i32)
DEF_HELPER_3(evfsadd, i64, env, i64, i64)
DEF_HELPER_3(evfssub, i64, env, i64, i64)
DEF_HELPER_3(evfsmul, i64, env, i64, i64)
DEF_HELPER_3(evfsdiv, i64, env, i64, i64)
DEF_HELPER_3(efststlt, i32, env, i32, i32)
DEF_HELPER_3(efststgt, i32, env, i32, i32)
DEF_HELPER_3(efststeq, i32, env, i32, i32)
DEF_HELPER_3(efscmplt, i32, env, i32, i32)
DEF_HELPER_3(efscmpgt, i32, env, i32, i32)
DEF_HELPER_3(efscmpeq, i32, env, i32, i32)
DEF_HELPER_3(evfststlt, i32, env, i64, i64)
DEF_HELPER_3(evfststgt, i32, env, i64, i64)
DEF_HELPER_3(evfststeq, i32, env, i64, i64)
DEF_HELPER_3(evfscmplt, i32, env, i64, i64)
DEF_HELPER_3(evfscmpgt, i32, env, i64, i64)
DEF_HELPER_3(evfscmpeq, i32, env, i64, i64)
DEF_HELPER_2(efdcfsi, i64, env, i32)
DEF_HELPER_2(efdcfsid, i64, env, i64)
DEF_HELPER_2(efdcfui, i64, env, i32)
DEF_HELPER_2(efdcfuid, i64, env, i64)
DEF_HELPER_2(efdctsi, i32, env, i64)
DEF_HELPER_2(efdctui, i32, env, i64)
DEF_HELPER_2(efdctsiz, i32, env, i64)
DEF_HELPER_2(efdctsidz, i64, env, i64)
DEF_HELPER_2(efdctuiz, i32, env, i64)
DEF_HELPER_2(efdctuidz, i64, env, i64)
DEF_HELPER_2(efdcfsf, i64, env, i32)
DEF_HELPER_2(efdcfuf, i64, env, i32)
DEF_HELPER_2(efdctsf, i32, env, i64)
DEF_HELPER_2(efdctuf, i32, env, i64)
DEF_HELPER_2(efscfd, i32, env, i64)
DEF_HELPER_2(efdcfs, i64, env, i32)
DEF_HELPER_3(efdadd, i64, env, i64, i64)
DEF_HELPER_3(efdsub, i64, env, i64, i64)
DEF_HELPER_3(efdmul, i64, env, i64, i64)
DEF_HELPER_3(efddiv, i64, env, i64, i64)
DEF_HELPER_3(efdtstlt, i32, env, i64, i64)
DEF_HELPER_3(efdtstgt, i32, env, i64, i64)
DEF_HELPER_3(efdtsteq, i32, env, i64, i64)
DEF_HELPER_3(efdcmplt, i32, env, i64, i64)
DEF_HELPER_3(efdcmpgt, i32, env, i64, i64)
DEF_HELPER_3(efdcmpeq, i32, env, i64, i64)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_1(4xx_tlbre_hi, tl, tl)
DEF_HELPER_1(4xx_tlbre_lo, tl, tl)
DEF_HELPER_2(4xx_tlbwe_hi, void, tl, tl)
DEF_HELPER_2(4xx_tlbwe_lo, void, tl, tl)
DEF_HELPER_1(4xx_tlbsx, tl, tl)
DEF_HELPER_2(440_tlbre, tl, i32, tl)
DEF_HELPER_3(440_tlbwe, void, i32, tl, tl)
DEF_HELPER_1(440_tlbsx, tl, tl)
DEF_HELPER_0(booke206_tlbre, void)
DEF_HELPER_0(booke206_tlbwe, void)
DEF_HELPER_1(booke206_tlbsx, void, tl)
DEF_HELPER_1(booke206_tlbivax, void, tl)
DEF_HELPER_1(booke206_tlbilx0, void, tl)
DEF_HELPER_1(booke206_tlbilx1, void, tl)
DEF_HELPER_1(booke206_tlbilx3, void, tl)
DEF_HELPER_1(booke206_tlbflush, void, i32)
DEF_HELPER_2(booke_setpid, void, i32, tl)
DEF_HELPER_1(6xx_tlbd, void, tl)
DEF_HELPER_1(6xx_tlbi, void, tl)
DEF_HELPER_1(74xx_tlbd, void, tl)
DEF_HELPER_1(74xx_tlbi, void, tl)
DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void)
DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl)
DEF_HELPER_2(4xx_tlbre_hi, tl, env, tl)
DEF_HELPER_2(4xx_tlbre_lo, tl, env, tl)
DEF_HELPER_3(4xx_tlbwe_hi, void, env, tl, tl)
DEF_HELPER_3(4xx_tlbwe_lo, void, env, tl, tl)
DEF_HELPER_2(4xx_tlbsx, tl, env, tl)
DEF_HELPER_3(440_tlbre, tl, env, i32, tl)
DEF_HELPER_4(440_tlbwe, void, env, i32, tl, tl)
DEF_HELPER_2(440_tlbsx, tl, env, tl)
DEF_HELPER_1(booke206_tlbre, void, env)
DEF_HELPER_1(booke206_tlbwe, void, env)
DEF_HELPER_2(booke206_tlbsx, void, env, tl)
DEF_HELPER_2(booke206_tlbivax, void, env, tl)
DEF_HELPER_2(booke206_tlbilx0, void, env, tl)
DEF_HELPER_2(booke206_tlbilx1, void, env, tl)
DEF_HELPER_2(booke206_tlbilx3, void, env, tl)
DEF_HELPER_2(booke206_tlbflush, void, env, i32)
DEF_HELPER_3(booke_setpid, void, env, i32, tl)
DEF_HELPER_2(6xx_tlbd, void, env, tl)
DEF_HELPER_2(6xx_tlbi, void, env, tl)
DEF_HELPER_2(74xx_tlbd, void, env, tl)
DEF_HELPER_2(74xx_tlbi, void, env, tl)
DEF_HELPER_FLAGS_1(tlbia, TCG_CALL_CONST, void, env)
DEF_HELPER_FLAGS_2(tlbie, TCG_CALL_CONST, void, env, tl)
#if defined(TARGET_PPC64)
DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl)
DEF_HELPER_1(load_slb_esid, tl, tl)
DEF_HELPER_1(load_slb_vsid, tl, tl)
DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void)
DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl)
DEF_HELPER_FLAGS_3(store_slb, TCG_CALL_CONST, void, env, tl, tl)
DEF_HELPER_2(load_slb_esid, tl, env, tl)
DEF_HELPER_2(load_slb_vsid, tl, env, tl)
DEF_HELPER_FLAGS_1(slbia, TCG_CALL_CONST, void, env)
DEF_HELPER_FLAGS_2(slbie, TCG_CALL_CONST, void, env, tl)
#endif
DEF_HELPER_FLAGS_1(load_sr, TCG_CALL_CONST, tl, tl);
DEF_HELPER_FLAGS_2(store_sr, TCG_CALL_CONST, void, tl, tl)
DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_CONST, tl, env, tl);
DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_CONST, void, env, tl, tl)
DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
DEF_HELPER_1(msgsnd, void, tl)
DEF_HELPER_1(msgclr, void, tl)
DEF_HELPER_2(msgclr, void, env, tl)
#endif
DEF_HELPER_3(dlmzb, tl, tl, tl, i32)
DEF_HELPER_FLAGS_1(clcs, TCG_CALL_CONST | TCG_CALL_PURE, tl, i32)
DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32)
DEF_HELPER_FLAGS_2(clcs, TCG_CALL_CONST | TCG_CALL_PURE, tl, env, i32)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_1(rac, tl, tl)
DEF_HELPER_2(rac, tl, env, tl)
#endif
DEF_HELPER_2(div, tl, tl, tl)
DEF_HELPER_2(divo, tl, tl, tl)
DEF_HELPER_2(divs, tl, tl, tl)
DEF_HELPER_2(divso, tl, tl, tl)
DEF_HELPER_3(div, tl, env, tl, tl)
DEF_HELPER_3(divo, tl, env, tl, tl)
DEF_HELPER_3(divs, tl, env, tl, tl)
DEF_HELPER_3(divso, tl, env, tl, tl)
DEF_HELPER_1(load_dcr, tl, tl);
DEF_HELPER_2(store_dcr, void, tl, tl)
DEF_HELPER_2(load_dcr, tl, env, tl);
DEF_HELPER_3(store_dcr, void, env, tl, tl)
DEF_HELPER_1(load_dump_spr, void, i32)
DEF_HELPER_1(store_dump_spr, void, i32)
DEF_HELPER_0(load_tbl, tl)
DEF_HELPER_0(load_tbu, tl)
DEF_HELPER_0(load_atbl, tl)
DEF_HELPER_0(load_atbu, tl)
DEF_HELPER_0(load_601_rtcl, tl)
DEF_HELPER_0(load_601_rtcu, tl)
DEF_HELPER_2(load_dump_spr, void, env, i32)
DEF_HELPER_2(store_dump_spr, void, env, i32)
DEF_HELPER_1(load_tbl, tl, env)
DEF_HELPER_1(load_tbu, tl, env)
DEF_HELPER_1(load_atbl, tl, env)
DEF_HELPER_1(load_atbu, tl, env)
DEF_HELPER_1(load_601_rtcl, tl, env)
DEF_HELPER_1(load_601_rtcu, tl, env)
#if !defined(CONFIG_USER_ONLY)
#if defined(TARGET_PPC64)
DEF_HELPER_1(store_asr, void, tl)
DEF_HELPER_0(load_purr, tl)
DEF_HELPER_2(store_asr, void, env, tl)
DEF_HELPER_1(load_purr, tl, env)
#endif
DEF_HELPER_1(store_sdr1, void, tl)
DEF_HELPER_1(store_tbl, void, tl)
DEF_HELPER_1(store_tbu, void, tl)
DEF_HELPER_1(store_atbl, void, tl)
DEF_HELPER_1(store_atbu, void, tl)
DEF_HELPER_1(store_601_rtcl, void, tl)
DEF_HELPER_1(store_601_rtcu, void, tl)
DEF_HELPER_0(load_decr, tl)
DEF_HELPER_1(store_decr, void, tl)
DEF_HELPER_1(store_hid0_601, void, tl)
DEF_HELPER_2(store_403_pbr, void, i32, tl)
DEF_HELPER_0(load_40x_pit, tl)
DEF_HELPER_1(store_40x_pit, void, tl)
DEF_HELPER_1(store_40x_dbcr0, void, tl)
DEF_HELPER_1(store_40x_sler, void, tl)
DEF_HELPER_1(store_booke_tcr, void, tl)
DEF_HELPER_1(store_booke_tsr, void, tl)
DEF_HELPER_2(store_ibatl, void, i32, tl)
DEF_HELPER_2(store_ibatu, void, i32, tl)
DEF_HELPER_2(store_dbatl, void, i32, tl)
DEF_HELPER_2(store_dbatu, void, i32, tl)
DEF_HELPER_2(store_601_batl, void, i32, tl)
DEF_HELPER_2(store_601_batu, void, i32, tl)
DEF_HELPER_2(store_sdr1, void, env, tl)
DEF_HELPER_2(store_tbl, void, env, tl)
DEF_HELPER_2(store_tbu, void, env, tl)
DEF_HELPER_2(store_atbl, void, env, tl)
DEF_HELPER_2(store_atbu, void, env, tl)
DEF_HELPER_2(store_601_rtcl, void, env, tl)
DEF_HELPER_2(store_601_rtcu, void, env, tl)
DEF_HELPER_1(load_decr, tl, env)
DEF_HELPER_2(store_decr, void, env, tl)
DEF_HELPER_2(store_hid0_601, void, env, tl)
DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
DEF_HELPER_1(load_40x_pit, tl, env)
DEF_HELPER_2(store_40x_pit, void, env, tl)
DEF_HELPER_2(store_40x_dbcr0, void, env, tl)
DEF_HELPER_2(store_40x_sler, void, env, tl)
DEF_HELPER_2(store_booke_tcr, void, env, tl)
DEF_HELPER_2(store_booke_tsr, void, env, tl)
DEF_HELPER_1(load_epr, tl, env)
DEF_HELPER_3(store_ibatl, void, env, i32, tl)
DEF_HELPER_3(store_ibatu, void, env, i32, tl)
DEF_HELPER_3(store_dbatl, void, env, i32, tl)
DEF_HELPER_3(store_dbatu, void, env, i32, tl)
DEF_HELPER_3(store_601_batl, void, env, i32, tl)
DEF_HELPER_3(store_601_batu, void, env, i32, tl)
#endif
#include "def-helper.h"

1564
target-ppc/int_helper.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/vfs.h>
#include <linux/kvm.h>
@ -167,10 +168,217 @@ static int kvm_booke206_tlb_init(CPUPPCState *env)
return 0;
}
#if defined(TARGET_PPC64)
static void kvm_get_fallback_smmu_info(CPUPPCState *env,
struct kvm_ppc_smmu_info *info)
{
memset(info, 0, sizeof(*info));
/* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so
* need to "guess" what the supported page sizes are.
*
* For that to work we make a few assumptions:
*
* - If KVM_CAP_PPC_GET_PVINFO is supported we are running "PR"
* KVM which only supports 4K and 16M pages, but supports them
* regardless of the backing store characteritics. We also don't
* support 1T segments.
*
* This is safe as if HV KVM ever supports that capability or PR
* KVM grows supports for more page/segment sizes, those versions
* will have implemented KVM_CAP_PPC_GET_SMMU_INFO and thus we
* will not hit this fallback
*
* - Else we are running HV KVM. This means we only support page
* sizes that fit in the backing store. Additionally we only
* advertize 64K pages if the processor is ARCH 2.06 and we assume
* P7 encodings for the SLB and hash table. Here too, we assume
* support for any newer processor will mean a kernel that
* implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit
* this fallback.
*/
if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO)) {
/* No flags */
info->flags = 0;
info->slb_size = 64;
/* Standard 4k base page size segment */
info->sps[0].page_shift = 12;
info->sps[0].slb_enc = 0;
info->sps[0].enc[0].page_shift = 12;
info->sps[0].enc[0].pte_enc = 0;
/* Standard 16M large page size segment */
info->sps[1].page_shift = 24;
info->sps[1].slb_enc = SLB_VSID_L;
info->sps[1].enc[0].page_shift = 24;
info->sps[1].enc[0].pte_enc = 0;
} else {
int i = 0;
/* HV KVM has backing store size restrictions */
info->flags = KVM_PPC_PAGE_SIZES_REAL;
if (env->mmu_model & POWERPC_MMU_1TSEG) {
info->flags |= KVM_PPC_1T_SEGMENTS;
}
if (env->mmu_model == POWERPC_MMU_2_06) {
info->slb_size = 32;
} else {
info->slb_size = 64;
}
/* Standard 4k base page size segment */
info->sps[i].page_shift = 12;
info->sps[i].slb_enc = 0;
info->sps[i].enc[0].page_shift = 12;
info->sps[i].enc[0].pte_enc = 0;
i++;
/* 64K on MMU 2.06 */
if (env->mmu_model == POWERPC_MMU_2_06) {
info->sps[i].page_shift = 16;
info->sps[i].slb_enc = 0x110;
info->sps[i].enc[0].page_shift = 16;
info->sps[i].enc[0].pte_enc = 1;
i++;
}
/* Standard 16M large page size segment */
info->sps[i].page_shift = 24;
info->sps[i].slb_enc = SLB_VSID_L;
info->sps[i].enc[0].page_shift = 24;
info->sps[i].enc[0].pte_enc = 0;
}
}
static void kvm_get_smmu_info(CPUPPCState *env, struct kvm_ppc_smmu_info *info)
{
int ret;
if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) {
ret = kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_SMMU_INFO, info);
if (ret == 0) {
return;
}
}
kvm_get_fallback_smmu_info(env, info);
}
static long getrampagesize(void)
{
struct statfs fs;
int ret;
if (!mem_path) {
/* guest RAM is backed by normal anonymous pages */
return getpagesize();
}
do {
ret = statfs(mem_path, &fs);
} while (ret != 0 && errno == EINTR);
if (ret != 0) {
fprintf(stderr, "Couldn't statfs() memory path: %s\n",
strerror(errno));
exit(1);
}
#define HUGETLBFS_MAGIC 0x958458f6
if (fs.f_type != HUGETLBFS_MAGIC) {
/* Explicit mempath, but it's ordinary pages */
return getpagesize();
}
/* It's hugepage, return the huge page size */
return fs.f_bsize;
}
static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
{
if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
return true;
}
return (1ul << shift) <= rampgsize;
}
static void kvm_fixup_page_sizes(CPUPPCState *env)
{
static struct kvm_ppc_smmu_info smmu_info;
static bool has_smmu_info;
long rampagesize;
int iq, ik, jq, jk;
/* We only handle page sizes for 64-bit server guests for now */
if (!(env->mmu_model & POWERPC_MMU_64)) {
return;
}
/* Collect MMU info from kernel if not already */
if (!has_smmu_info) {
kvm_get_smmu_info(env, &smmu_info);
has_smmu_info = true;
}
rampagesize = getrampagesize();
/* Convert to QEMU form */
memset(&env->sps, 0, sizeof(env->sps));
for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) {
struct ppc_one_seg_page_size *qsps = &env->sps.sps[iq];
struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik];
if (!kvm_valid_page_size(smmu_info.flags, rampagesize,
ksps->page_shift)) {
continue;
}
qsps->page_shift = ksps->page_shift;
qsps->slb_enc = ksps->slb_enc;
for (jk = jq = 0; jk < KVM_PPC_PAGE_SIZES_MAX_SZ; jk++) {
if (!kvm_valid_page_size(smmu_info.flags, rampagesize,
ksps->enc[jk].page_shift)) {
continue;
}
qsps->enc[jq].page_shift = ksps->enc[jk].page_shift;
qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc;
if (++jq >= PPC_PAGE_SIZES_MAX_SZ) {
break;
}
}
if (++iq >= PPC_PAGE_SIZES_MAX_SZ) {
break;
}
}
env->slb_nr = smmu_info.slb_size;
if (smmu_info.flags & KVM_PPC_1T_SEGMENTS) {
env->mmu_model |= POWERPC_MMU_1TSEG;
} else {
env->mmu_model &= ~POWERPC_MMU_1TSEG;
}
}
#else /* defined (TARGET_PPC64) */
static inline void kvm_fixup_page_sizes(CPUPPCState *env)
{
}
#endif /* !defined (TARGET_PPC64) */
int kvm_arch_init_vcpu(CPUPPCState *cenv)
{
int ret;
/* Gather server mmu info from KVM and update the CPU state */
kvm_fixup_page_sizes(cenv);
/* Synchronize sregs with kvm */
ret = kvm_arch_sync_sregs(cenv);
if (ret) {
return ret;

View File

@ -58,6 +58,11 @@ static inline int kvmppc_get_hypercall(CPUPPCState *env, uint8_t *buf, int buf_l
return -1;
}
static inline int kvmppc_read_segment_page_sizes(uint32_t *prop, int maxcells)
{
return -1;
}
static inline int kvmppc_set_interrupt(CPUPPCState *env, int irq, int level)
{
return -1;

295
target-ppc/mem_helper.c Normal file
View File

@ -0,0 +1,295 @@
/*
* PowerPC memory access emulation 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 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 "cpu.h"
#include "host-utils.h"
#include "helper.h"
#include "helper_regs.h"
#if !defined(CONFIG_USER_ONLY)
#include "softmmu_exec.h"
#endif /* !defined(CONFIG_USER_ONLY) */
//#define DEBUG_OP
/*****************************************************************************/
/* Memory load and stores */
static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr,
target_long arg)
{
#if defined(TARGET_PPC64)
if (!msr_is_64bit(env, env->msr)) {
return (uint32_t)(addr + arg);
} else
#endif
{
return addr + arg;
}
}
void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg)
{
for (; reg < 32; reg++) {
if (msr_le) {
env->gpr[reg] = bswap32(cpu_ldl_data(env, addr));
} else {
env->gpr[reg] = cpu_ldl_data(env, addr);
}
addr = addr_add(env, addr, 4);
}
}
void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg)
{
for (; reg < 32; reg++) {
if (msr_le) {
cpu_stl_data(env, addr, bswap32((uint32_t)env->gpr[reg]));
} else {
cpu_stl_data(env, addr, (uint32_t)env->gpr[reg]);
}
addr = addr_add(env, addr, 4);
}
}
void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg)
{
int sh;
for (; nb > 3; nb -= 4) {
env->gpr[reg] = cpu_ldl_data(env, addr);
reg = (reg + 1) % 32;
addr = addr_add(env, addr, 4);
}
if (unlikely(nb > 0)) {
env->gpr[reg] = 0;
for (sh = 24; nb > 0; nb--, sh -= 8) {
env->gpr[reg] |= cpu_ldub_data(env, addr) << sh;
addr = addr_add(env, addr, 1);
}
}
}
/* PPC32 specification says we must generate an exception if
* rA is in the range of registers to be loaded.
* In an other hand, IBM says this is valid, but rA won't be loaded.
* For now, I'll follow the spec...
*/
void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg,
uint32_t ra, uint32_t rb)
{
if (likely(xer_bc != 0)) {
if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) ||
(reg < rb && (reg + xer_bc) > rb))) {
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_INVAL |
POWERPC_EXCP_INVAL_LSWX);
} else {
helper_lsw(env, addr, xer_bc, reg);
}
}
}
void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb,
uint32_t reg)
{
int sh;
for (; nb > 3; nb -= 4) {
cpu_stl_data(env, addr, env->gpr[reg]);
reg = (reg + 1) % 32;
addr = addr_add(env, addr, 4);
}
if (unlikely(nb > 0)) {
for (sh = 24; nb > 0; nb--, sh -= 8) {
cpu_stb_data(env, addr, (env->gpr[reg] >> sh) & 0xFF);
addr = addr_add(env, addr, 1);
}
}
}
static void do_dcbz(CPUPPCState *env, target_ulong addr, int dcache_line_size)
{
int i;
addr &= ~(dcache_line_size - 1);
for (i = 0; i < dcache_line_size; i += 4) {
cpu_stl_data(env, addr + i, 0);
}
if (env->reserve_addr == addr) {
env->reserve_addr = (target_ulong)-1ULL;
}
}
void helper_dcbz(CPUPPCState *env, target_ulong addr)
{
do_dcbz(env, addr, env->dcache_line_size);
}
void helper_dcbz_970(CPUPPCState *env, target_ulong addr)
{
if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) {
do_dcbz(env, addr, 32);
} else {
do_dcbz(env, addr, env->dcache_line_size);
}
}
void helper_icbi(CPUPPCState *env, target_ulong addr)
{
addr &= ~(env->dcache_line_size - 1);
/* Invalidate one cache line :
* PowerPC specification says this is to be treated like a load
* (not a fetch) by the MMU. To be sure it will be so,
* do the load "by hand".
*/
cpu_ldl_data(env, addr);
}
/* XXX: to be tested */
target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg,
uint32_t ra, uint32_t rb)
{
int i, c, d;
d = 24;
for (i = 0; i < xer_bc; i++) {
c = cpu_ldub_data(env, addr);
addr = addr_add(env, addr, 1);
/* ra (if not 0) and rb are never modified */
if (likely(reg != rb && (ra == 0 || reg != ra))) {
env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d);
}
if (unlikely(c == xer_cmp)) {
break;
}
if (likely(d != 0)) {
d -= 8;
} else {
d = 24;
reg++;
reg = reg & 0x1F;
}
}
return i;
}
/*****************************************************************************/
/* Altivec extension helpers */
#if defined(HOST_WORDS_BIGENDIAN)
#define HI_IDX 0
#define LO_IDX 1
#else
#define HI_IDX 1
#define LO_IDX 0
#endif
#define LVE(name, access, swap, element) \
void helper_##name(CPUPPCState *env, ppc_avr_t *r, \
target_ulong addr) \
{ \
size_t n_elems = ARRAY_SIZE(r->element); \
int adjust = HI_IDX*(n_elems - 1); \
int sh = sizeof(r->element[0]) >> 1; \
int index = (addr & 0xf) >> sh; \
\
if (msr_le) { \
r->element[LO_IDX ? index : (adjust - index)] = \
swap(access(env, addr)); \
} else { \
r->element[LO_IDX ? index : (adjust - index)] = \
access(env, addr); \
} \
}
#define I(x) (x)
LVE(lvebx, cpu_ldub_data, I, u8)
LVE(lvehx, cpu_lduw_data, bswap16, u16)
LVE(lvewx, cpu_ldl_data, bswap32, u32)
#undef I
#undef LVE
#define STVE(name, access, swap, element) \
void helper_##name(CPUPPCState *env, ppc_avr_t *r, \
target_ulong addr) \
{ \
size_t n_elems = ARRAY_SIZE(r->element); \
int adjust = HI_IDX * (n_elems - 1); \
int sh = sizeof(r->element[0]) >> 1; \
int index = (addr & 0xf) >> sh; \
\
if (msr_le) { \
access(env, addr, swap(r->element[LO_IDX ? index : \
(adjust - index)])); \
} else { \
access(env, addr, r->element[LO_IDX ? index : \
(adjust - index)]); \
} \
}
#define I(x) (x)
STVE(stvebx, cpu_stb_data, I, u8)
STVE(stvehx, cpu_stw_data, bswap16, u16)
STVE(stvewx, cpu_stl_data, bswap32, u32)
#undef I
#undef LVE
#undef HI_IDX
#undef LO_IDX
/*****************************************************************************/
/* Softmmu support */
#if !defined(CONFIG_USER_ONLY)
#define MMUSUFFIX _mmu
#define SHIFT 0
#include "softmmu_template.h"
#define SHIFT 1
#include "softmmu_template.h"
#define SHIFT 2
#include "softmmu_template.h"
#define SHIFT 3
#include "softmmu_template.h"
/* try to fill the TLB and return an exception if error. If retaddr is
NULL, it means that the function was called in C code (i.e. not
from generated code or from helper.c) */
/* XXX: fix it to restore all registers */
void tlb_fill(CPUPPCState *env, target_ulong addr, int is_write, int mmu_idx,
uintptr_t retaddr)
{
TranslationBlock *tb;
int ret;
ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx);
if (unlikely(ret != 0)) {
if (likely(retaddr)) {
/* now we have a real cpu fault */
tb = tb_find_pc(retaddr);
if (likely(tb)) {
/* the PC is inside the translated code. It means that we have
a virtual CPU fault */
cpu_restore_state(tb, env, retaddr);
}
}
helper_raise_exception_err(env, env->exception_index, env->error_code);
}
}
#endif /* !CONFIG_USER_ONLY */

124
target-ppc/misc_helper.c Normal file
View File

@ -0,0 +1,124 @@
/*
* Miscellaneous PowerPC emulation 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 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 "cpu.h"
#include "helper.h"
#include "helper_regs.h"
/*****************************************************************************/
/* SPR accesses */
void helper_load_dump_spr(CPUPPCState *env, uint32_t sprn)
{
qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn,
env->spr[sprn]);
}
void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
{
qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn,
env->spr[sprn]);
}
#if !defined(CONFIG_USER_ONLY)
#if defined(TARGET_PPC64)
void helper_store_asr(CPUPPCState *env, target_ulong val)
{
ppc_store_asr(env, val);
}
#endif
void helper_store_sdr1(CPUPPCState *env, target_ulong val)
{
ppc_store_sdr1(env, val);
}
void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
{
target_ulong hid0;
hid0 = env->spr[SPR_HID0];
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__,
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)
{
if (likely(env->pb[num] != value)) {
env->pb[num] = value;
/* Should be optimized */
tlb_flush(env, 1);
}
}
void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val)
{
store_40x_dbcr0(env, val);
}
void helper_store_40x_sler(CPUPPCState *env, target_ulong val)
{
store_40x_sler(env, val);
}
#endif
/*****************************************************************************/
/* PowerPC 601 specific instructions (POWER bridge) */
target_ulong helper_clcs(CPUPPCState *env, uint32_t arg)
{
switch (arg) {
case 0x0CUL:
/* Instruction cache line size */
return env->icache_line_size;
break;
case 0x0DUL:
/* Data cache line size */
return env->dcache_line_size;
break;
case 0x0EUL:
/* Minimum cache line size */
return (env->icache_line_size < env->dcache_line_size) ?
env->icache_line_size : env->dcache_line_size;
break;
case 0x0FUL:
/* Maximum cache line size */
return (env->icache_line_size > env->dcache_line_size) ?
env->icache_line_size : env->dcache_line_size;
break;
default:
/* Undefined */
return 0;
break;
}
}
/*****************************************************************************/
/* Special registers manipulation */
/* GDBstub can read and write MSR... */
void ppc_store_msr(CPUPPCState *env, target_ulong value)
{
hreg_store_msr(env, value, 0);
}

3326
target-ppc/mmu_helper.c Normal file

File diff suppressed because it is too large Load Diff

35
target-ppc/mpic_helper.c Normal file
View File

@ -0,0 +1,35 @@
/*
* PowerPC emulation 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 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 "cpu.h"
#include "helper.h"
/*****************************************************************************/
/* SPR accesses */
#if !defined(CONFIG_USER_ONLY)
/*
* This is an ugly helper for EPR, which is basically the same as accessing
* the IACK (PIAC) register on the MPIC. Because we model the MPIC as a device
* that can only talk to the CPU through MMIO, let's access it that way!
*/
target_ulong helper_load_epr(CPUPPCState *env)
{
return ldl_phys(env->mpic_cpu_base + 0xA0);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,159 @@
/*
* PowerPC emulation 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 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 "cpu.h"
#include "helper.h"
/*****************************************************************************/
/* SPR accesses */
target_ulong helper_load_tbl(CPUPPCState *env)
{
return (target_ulong)cpu_ppc_load_tbl(env);
}
target_ulong helper_load_tbu(CPUPPCState *env)
{
return cpu_ppc_load_tbu(env);
}
target_ulong helper_load_atbl(CPUPPCState *env)
{
return (target_ulong)cpu_ppc_load_atbl(env);
}
target_ulong helper_load_atbu(CPUPPCState *env)
{
return cpu_ppc_load_atbu(env);
}
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
target_ulong helper_load_purr(CPUPPCState *env)
{
return (target_ulong)cpu_ppc_load_purr(env);
}
#endif
target_ulong helper_load_601_rtcl(CPUPPCState *env)
{
return cpu_ppc601_load_rtcl(env);
}
target_ulong helper_load_601_rtcu(CPUPPCState *env)
{
return cpu_ppc601_load_rtcu(env);
}
#if !defined(CONFIG_USER_ONLY)
void helper_store_tbl(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_tbl(env, val);
}
void helper_store_tbu(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_tbu(env, val);
}
void helper_store_atbl(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_atbl(env, val);
}
void helper_store_atbu(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_atbu(env, val);
}
void helper_store_601_rtcl(CPUPPCState *env, target_ulong val)
{
cpu_ppc601_store_rtcl(env, val);
}
void helper_store_601_rtcu(CPUPPCState *env, target_ulong val)
{
cpu_ppc601_store_rtcu(env, val);
}
target_ulong helper_load_decr(CPUPPCState *env)
{
return cpu_ppc_load_decr(env);
}
void helper_store_decr(CPUPPCState *env, target_ulong val)
{
cpu_ppc_store_decr(env, val);
}
target_ulong helper_load_40x_pit(CPUPPCState *env)
{
return load_40x_pit(env);
}
void helper_store_40x_pit(CPUPPCState *env, target_ulong val)
{
store_40x_pit(env, val);
}
void helper_store_booke_tcr(CPUPPCState *env, target_ulong val)
{
store_booke_tcr(env, val);
}
void helper_store_booke_tsr(CPUPPCState *env, target_ulong val)
{
store_booke_tsr(env, val);
}
#endif
/*****************************************************************************/
/* Embedded PowerPC specific helpers */
/* XXX: to be improved to check access rights when in user-mode */
target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn)
{
uint32_t val = 0;
if (unlikely(env->dcr_env == NULL)) {
qemu_log("No DCR environment\n");
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_INVAL |
POWERPC_EXCP_INVAL_INVAL);
} else if (unlikely(ppc_dcr_read(env->dcr_env,
(uint32_t)dcrn, &val) != 0)) {
qemu_log("DCR read error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn);
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG);
}
return val;
}
void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val)
{
if (unlikely(env->dcr_env == NULL)) {
qemu_log("No DCR environment\n");
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_INVAL |
POWERPC_EXCP_INVAL_INVAL);
} else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn,
(uint32_t)val) != 0)) {
qemu_log("DCR write error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn);
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -55,31 +55,50 @@ PPC_IRQ_INIT_FN(e500);
/* Generic callbacks:
* do nothing but store/retrieve spr value
*/
static void spr_load_dump_spr(int sprn)
{
#ifdef PPC_DUMP_SPR_ACCESSES
TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_load_dump_spr(t0);
tcg_temp_free_i32(t0);
#endif
}
static void spr_read_generic (void *opaque, int gprn, int sprn)
{
gen_load_spr(cpu_gpr[gprn], sprn);
spr_load_dump_spr(sprn);
}
static void spr_store_dump_spr(int sprn)
{
#ifdef PPC_DUMP_SPR_ACCESSES
{
TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_load_dump_spr(t0);
tcg_temp_free_i32(t0);
}
TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_store_dump_spr(t0);
tcg_temp_free_i32(t0);
#endif
}
static void spr_write_generic (void *opaque, int sprn, int gprn)
{
gen_store_spr(sprn, cpu_gpr[gprn]);
#ifdef PPC_DUMP_SPR_ACCESSES
{
TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_store_dump_spr(t0);
tcg_temp_free_i32(t0);
}
#endif
spr_store_dump_spr(sprn);
}
#if !defined(CONFIG_USER_ONLY)
static void spr_write_generic32(void *opaque, int sprn, int gprn)
{
#ifdef TARGET_PPC64
TCGv t0 = tcg_temp_new();
tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]);
gen_store_spr(sprn, t0);
tcg_temp_free(t0);
spr_store_dump_spr(sprn);
#else
spr_write_generic(opaque, sprn, gprn);
#endif
}
static void spr_write_clear (void *opaque, int sprn, int gprn)
{
TCGv t0 = tcg_temp_new();
@ -159,7 +178,7 @@ static void spr_read_decr (void *opaque, int gprn, int sprn)
if (use_icount) {
gen_io_start();
}
gen_helper_load_decr(cpu_gpr[gprn]);
gen_helper_load_decr(cpu_gpr[gprn], cpu_env);
if (use_icount) {
gen_io_end();
gen_stop_exception(opaque);
@ -171,7 +190,7 @@ static void spr_write_decr (void *opaque, int sprn, int gprn)
if (use_icount) {
gen_io_start();
}
gen_helper_store_decr(cpu_gpr[gprn]);
gen_helper_store_decr(cpu_env, cpu_gpr[gprn]);
if (use_icount) {
gen_io_end();
gen_stop_exception(opaque);
@ -186,7 +205,7 @@ static void spr_read_tbl (void *opaque, int gprn, int sprn)
if (use_icount) {
gen_io_start();
}
gen_helper_load_tbl(cpu_gpr[gprn]);
gen_helper_load_tbl(cpu_gpr[gprn], cpu_env);
if (use_icount) {
gen_io_end();
gen_stop_exception(opaque);
@ -198,7 +217,7 @@ static void spr_read_tbu (void *opaque, int gprn, int sprn)
if (use_icount) {
gen_io_start();
}
gen_helper_load_tbu(cpu_gpr[gprn]);
gen_helper_load_tbu(cpu_gpr[gprn], cpu_env);
if (use_icount) {
gen_io_end();
gen_stop_exception(opaque);
@ -208,13 +227,13 @@ static void spr_read_tbu (void *opaque, int gprn, int sprn)
__attribute__ (( unused ))
static void spr_read_atbl (void *opaque, int gprn, int sprn)
{
gen_helper_load_atbl(cpu_gpr[gprn]);
gen_helper_load_atbl(cpu_gpr[gprn], cpu_env);
}
__attribute__ (( unused ))
static void spr_read_atbu (void *opaque, int gprn, int sprn)
{
gen_helper_load_atbu(cpu_gpr[gprn]);
gen_helper_load_atbu(cpu_gpr[gprn], cpu_env);
}
#if !defined(CONFIG_USER_ONLY)
@ -223,7 +242,7 @@ static void spr_write_tbl (void *opaque, int sprn, int gprn)
if (use_icount) {
gen_io_start();
}
gen_helper_store_tbl(cpu_gpr[gprn]);
gen_helper_store_tbl(cpu_env, cpu_gpr[gprn]);
if (use_icount) {
gen_io_end();
gen_stop_exception(opaque);
@ -235,7 +254,7 @@ static void spr_write_tbu (void *opaque, int sprn, int gprn)
if (use_icount) {
gen_io_start();
}
gen_helper_store_tbu(cpu_gpr[gprn]);
gen_helper_store_tbu(cpu_env, cpu_gpr[gprn]);
if (use_icount) {
gen_io_end();
gen_stop_exception(opaque);
@ -245,20 +264,20 @@ static void spr_write_tbu (void *opaque, int sprn, int gprn)
__attribute__ (( unused ))
static void spr_write_atbl (void *opaque, int sprn, int gprn)
{
gen_helper_store_atbl(cpu_gpr[gprn]);
gen_helper_store_atbl(cpu_env, cpu_gpr[gprn]);
}
__attribute__ (( unused ))
static void spr_write_atbu (void *opaque, int sprn, int gprn)
{
gen_helper_store_atbu(cpu_gpr[gprn]);
gen_helper_store_atbu(cpu_env, cpu_gpr[gprn]);
}
#if defined(TARGET_PPC64)
__attribute__ (( unused ))
static void spr_read_purr (void *opaque, int gprn, int sprn)
{
gen_helper_load_purr(cpu_gpr[gprn]);
gen_helper_load_purr(cpu_gpr[gprn], cpu_env);
}
#endif
#endif
@ -279,28 +298,28 @@ static void spr_read_ibat_h (void *opaque, int gprn, int sprn)
static void spr_write_ibatu (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2);
gen_helper_store_ibatu(t0, cpu_gpr[gprn]);
gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_ibatu_h (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4);
gen_helper_store_ibatu(t0, cpu_gpr[gprn]);
gen_helper_store_ibatu(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_ibatl (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2);
gen_helper_store_ibatl(t0, cpu_gpr[gprn]);
gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_ibatl_h (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4);
gen_helper_store_ibatl(t0, cpu_gpr[gprn]);
gen_helper_store_ibatl(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
@ -319,35 +338,35 @@ static void spr_read_dbat_h (void *opaque, int gprn, int sprn)
static void spr_write_dbatu (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2);
gen_helper_store_dbatu(t0, cpu_gpr[gprn]);
gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_dbatu_h (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4);
gen_helper_store_dbatu(t0, cpu_gpr[gprn]);
gen_helper_store_dbatu(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_dbatl (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2);
gen_helper_store_dbatl(t0, cpu_gpr[gprn]);
gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_dbatl_h (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4);
gen_helper_store_dbatl(t0, cpu_gpr[gprn]);
gen_helper_store_dbatl(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
/* SDR1 */
static void spr_write_sdr1 (void *opaque, int sprn, int gprn)
{
gen_helper_store_sdr1(cpu_gpr[gprn]);
gen_helper_store_sdr1(cpu_env, cpu_gpr[gprn]);
}
/* 64 bits PowerPC specific SPRs */
@ -373,7 +392,7 @@ static void spr_read_asr (void *opaque, int gprn, int sprn)
static void spr_write_asr (void *opaque, int sprn, int gprn)
{
gen_helper_store_asr(cpu_gpr[gprn]);
gen_helper_store_asr(cpu_env, cpu_gpr[gprn]);
}
#endif
#endif
@ -382,30 +401,30 @@ static void spr_write_asr (void *opaque, int sprn, int gprn)
/* RTC */
static void spr_read_601_rtcl (void *opaque, int gprn, int sprn)
{
gen_helper_load_601_rtcl(cpu_gpr[gprn]);
gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env);
}
static void spr_read_601_rtcu (void *opaque, int gprn, int sprn)
{
gen_helper_load_601_rtcu(cpu_gpr[gprn]);
gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env);
}
#if !defined(CONFIG_USER_ONLY)
static void spr_write_601_rtcu (void *opaque, int sprn, int gprn)
{
gen_helper_store_601_rtcu(cpu_gpr[gprn]);
gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]);
}
static void spr_write_601_rtcl (void *opaque, int sprn, int gprn)
{
gen_helper_store_601_rtcl(cpu_gpr[gprn]);
gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]);
}
static void spr_write_hid0_601 (void *opaque, int sprn, int gprn)
{
DisasContext *ctx = opaque;
gen_helper_store_hid0_601(cpu_gpr[gprn]);
gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]);
/* Must stop the translation as endianness may have changed */
gen_stop_exception(ctx);
}
@ -421,14 +440,14 @@ static void spr_read_601_ubat (void *opaque, int gprn, int sprn)
static void spr_write_601_ubatu (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2);
gen_helper_store_601_batl(t0, cpu_gpr[gprn]);
gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
static void spr_write_601_ubatl (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2);
gen_helper_store_601_batu(t0, cpu_gpr[gprn]);
gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
#endif
@ -437,36 +456,36 @@ static void spr_write_601_ubatl (void *opaque, int sprn, int gprn)
#if !defined(CONFIG_USER_ONLY)
static void spr_read_40x_pit (void *opaque, int gprn, int sprn)
{
gen_helper_load_40x_pit(cpu_gpr[gprn]);
gen_helper_load_40x_pit(cpu_gpr[gprn], cpu_env);
}
static void spr_write_40x_pit (void *opaque, int sprn, int gprn)
{
gen_helper_store_40x_pit(cpu_gpr[gprn]);
gen_helper_store_40x_pit(cpu_env, cpu_gpr[gprn]);
}
static void spr_write_40x_dbcr0 (void *opaque, int sprn, int gprn)
{
DisasContext *ctx = opaque;
gen_helper_store_40x_dbcr0(cpu_gpr[gprn]);
gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]);
/* We must stop translation as we may have rebooted */
gen_stop_exception(ctx);
}
static void spr_write_40x_sler (void *opaque, int sprn, int gprn)
{
gen_helper_store_40x_sler(cpu_gpr[gprn]);
gen_helper_store_40x_sler(cpu_env, cpu_gpr[gprn]);
}
static void spr_write_booke_tcr (void *opaque, int sprn, int gprn)
{
gen_helper_store_booke_tcr(cpu_gpr[gprn]);
gen_helper_store_booke_tcr(cpu_env, cpu_gpr[gprn]);
}
static void spr_write_booke_tsr (void *opaque, int sprn, int gprn)
{
gen_helper_store_booke_tsr(cpu_gpr[gprn]);
gen_helper_store_booke_tsr(cpu_env, cpu_gpr[gprn]);
}
#endif
@ -481,7 +500,7 @@ static void spr_read_403_pbr (void *opaque, int gprn, int sprn)
static void spr_write_403_pbr (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1);
gen_helper_store_403_pbr(t0, cpu_gpr[gprn]);
gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
@ -1371,14 +1390,14 @@ static void spr_write_e500_l1csr0 (void *opaque, int sprn, int gprn)
static void spr_write_booke206_mmucsr0 (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_booke206_tlbflush(t0);
gen_helper_booke206_tlbflush(cpu_env, t0);
tcg_temp_free_i32(t0);
}
static void spr_write_booke_pid (void *opaque, int sprn, int gprn)
{
TCGv_i32 t0 = tcg_const_i32(sprn);
gen_helper_booke_setpid(t0, cpu_gpr[gprn]);
gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]);
tcg_temp_free_i32(t0);
}
#endif
@ -1591,10 +1610,14 @@ static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask,
/* TLB assist registers */
/* XXX : not implemented */
for (i = 0; i < 8; i++) {
void (*uea_write)(void *o, int sprn, int gprn) = &spr_write_generic32;
if (i == 2 && (mas_mask & (1 << i)) && (env->insns_flags & PPC_64B)) {
uea_write = &spr_write_generic;
}
if (mas_mask & (1 << i)) {
spr_register(env, mas_sprn[i], mas_names[i],
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
&spr_read_generic, uea_write,
0x00000000);
}
}
@ -2804,7 +2827,7 @@ static void init_excp_G2 (CPUPPCState *env)
#endif
}
static void init_excp_e200 (CPUPPCState *env)
static void init_excp_e200(CPUPPCState *env, target_ulong ivpr_mask)
{
#if !defined(CONFIG_USER_ONLY)
env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000FFC;
@ -2829,7 +2852,7 @@ static void init_excp_e200 (CPUPPCState *env)
env->excp_vectors[POWERPC_EXCP_EFPRI] = 0x00000000;
env->hreset_excp_prefix = 0x00000000UL;
env->ivor_mask = 0x0000FFF7UL;
env->ivpr_mask = 0xFFFF0000UL;
env->ivpr_mask = ivpr_mask;
/* Hardware reset vector */
env->hreset_vector = 0xFFFFFFFCUL;
#endif
@ -4307,7 +4330,7 @@ static void init_proc_e200 (CPUPPCState *env)
env->id_tlbs = 0;
env->tlb_type = TLB_EMB;
#endif
init_excp_e200(env);
init_excp_e200(env, 0xFFFF0000UL);
env->dcache_line_size = 32;
env->icache_line_size = 32;
/* XXX: TODO: allocate internal IRQ controller */
@ -4424,16 +4447,70 @@ static void init_proc_e300 (CPUPPCState *env)
#define check_pow_e500mc check_pow_none
#define init_proc_e500mc init_proc_e500mc
/* e5500 core */
#define POWERPC_INSNS_e5500 (PPC_INSNS_BASE | PPC_ISEL | \
PPC_WRTEE | PPC_RFDI | PPC_RFMCI | \
PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
PPC_FLOAT | PPC_FLOAT_FRES | \
PPC_FLOAT_FRSQRTE | PPC_FLOAT_FSEL | \
PPC_FLOAT_STFIWX | PPC_WAIT | \
PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC | \
PPC_64B | PPC_POPCNTB | PPC_POPCNTWD)
#define POWERPC_INSNS2_e5500 (PPC2_BOOKE206 | PPC2_PRCNTL)
#define POWERPC_MSRM_e5500 (0x000000009402FB36ULL)
#define POWERPC_MMU_e5500 (POWERPC_MMU_BOOKE206)
#define POWERPC_EXCP_e5500 (POWERPC_EXCP_BOOKE)
#define POWERPC_INPUT_e5500 (PPC_FLAGS_INPUT_BookE)
/* Fixme: figure out the correct flag for e5500 */
#define POWERPC_BFDM_e5500 (bfd_mach_ppc_e500)
#define POWERPC_FLAG_e5500 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \
POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
#define check_pow_e5500 check_pow_none
#define init_proc_e5500 init_proc_e5500
#if !defined(CONFIG_USER_ONLY)
static void spr_write_mas73(void *opaque, int sprn, int gprn)
{
TCGv val = tcg_temp_new();
tcg_gen_ext32u_tl(val, cpu_gpr[gprn]);
gen_store_spr(SPR_BOOKE_MAS3, val);
tcg_gen_shri_tl(val, gprn, 32);
gen_store_spr(SPR_BOOKE_MAS7, val);
tcg_temp_free(val);
}
static void spr_read_mas73(void *opaque, int gprn, int sprn)
{
TCGv mas7 = tcg_temp_new();
TCGv mas3 = tcg_temp_new();
gen_load_spr(mas7, SPR_BOOKE_MAS7);
tcg_gen_shli_tl(mas7, mas7, 32);
gen_load_spr(mas3, SPR_BOOKE_MAS3);
tcg_gen_or_tl(cpu_gpr[gprn], mas3, mas7);
tcg_temp_free(mas3);
tcg_temp_free(mas7);
}
static void spr_load_epr(void *opaque, int gprn, int sprn)
{
gen_helper_load_epr(cpu_gpr[gprn], cpu_env);
}
#endif
enum fsl_e500_version {
fsl_e500v1,
fsl_e500v2,
fsl_e500mc,
fsl_e5500,
};
static void init_proc_e500 (CPUPPCState *env, int version)
{
uint32_t tlbncfg[2];
uint64_t ivor_mask = 0x0000000F0000FFFFULL;
uint64_t ivor_mask;
uint64_t ivpr_mask = 0xFFFF0000ULL;
uint32_t l1cfg0 = 0x3800 /* 8 ways */
| 0x0020; /* 32 kb */
#if !defined(CONFIG_USER_ONLY)
@ -4447,8 +4524,16 @@ static void init_proc_e500 (CPUPPCState *env, int version)
* complain when accessing them.
* gen_spr_BookE(env, 0x0000000F0000FD7FULL);
*/
if (version == fsl_e500mc) {
ivor_mask = 0x000003FE0000FFFFULL;
switch (version) {
case fsl_e500v1:
case fsl_e500v2:
default:
ivor_mask = 0x0000000F0000FFFFULL;
break;
case fsl_e500mc:
case fsl_e5500:
ivor_mask = 0x000003FE0000FFFFULL;
break;
}
gen_spr_BookE(env, ivor_mask);
/* Processor identification */
@ -4476,6 +4561,7 @@ static void init_proc_e500 (CPUPPCState *env, int version)
tlbncfg[1] = gen_tlbncfg(16, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 16);
break;
case fsl_e500mc:
case fsl_e5500:
tlbncfg[0] = gen_tlbncfg(4, 1, 1, 0, 512);
tlbncfg[1] = gen_tlbncfg(64, 1, 12, TLBnCFG_AVAIL | TLBnCFG_IPROT, 64);
break;
@ -4491,6 +4577,7 @@ static void init_proc_e500 (CPUPPCState *env, int version)
env->icache_line_size = 32;
break;
case fsl_e500mc:
case fsl_e5500:
env->dcache_line_size = 64;
env->icache_line_size = 64;
l1cfg0 |= 0x1000000; /* 64 byte cache block size */
@ -4566,6 +4653,22 @@ static void init_proc_e500 (CPUPPCState *env, int version)
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_booke206_mmucsr0,
0x00000000);
spr_register(env, SPR_BOOKE_EPR, "EPR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_load_epr, SPR_NOACCESS,
0x00000000);
/* XXX better abstract into Emb.xxx features */
if (version == fsl_e5500) {
spr_register(env, SPR_BOOKE_EPCR, "EPCR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
spr_register(env, SPR_BOOKE_MAS7_MAS3, "MAS7_MAS3",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_mas73, &spr_write_mas73,
0x00000000);
ivpr_mask = (target_ulong)~0xFFFFULL;
}
#if !defined(CONFIG_USER_ONLY)
env->nb_tlb = 0;
@ -4575,7 +4678,7 @@ static void init_proc_e500 (CPUPPCState *env, int version)
}
#endif
init_excp_e200(env);
init_excp_e200(env, ivpr_mask);
/* Allocate hardware IRQ controller */
ppce500_irq_init(env);
}
@ -4595,6 +4698,13 @@ static void init_proc_e500mc(CPUPPCState *env)
init_proc_e500(env, fsl_e500mc);
}
#ifdef TARGET_PPC64
static void init_proc_e5500(CPUPPCState *env)
{
init_proc_e500(env, fsl_e5500);
}
#endif
/* Non-embedded PowerPC */
/* POWER : same as 601, without mfmsr, mfsr */
@ -7133,6 +7243,7 @@ enum {
CPU_POWERPC_e500v2_v22 = 0x80210022,
CPU_POWERPC_e500v2_v30 = 0x80210030,
CPU_POWERPC_e500mc = 0x80230020,
CPU_POWERPC_e5500 = 0x80240020,
/* MPC85xx microcontrollers */
#define CPU_POWERPC_MPC8533 CPU_POWERPC_MPC8533_v11
#define CPU_POWERPC_MPC8533_v10 CPU_POWERPC_e500v2_v21
@ -8527,6 +8638,9 @@ static const ppc_def_t ppc_defs[] = {
/* PowerPC e500v2 v3.0 core */
POWERPC_DEF("e500v2_v30", CPU_POWERPC_e500v2_v30, e500v2),
POWERPC_DEF("e500mc", CPU_POWERPC_e500mc, e500mc),
#ifdef TARGET_PPC64
POWERPC_DEF("e5500", CPU_POWERPC_e5500, e5500),
#endif
/* PowerPC e500 microcontrollers */
/* MPC8533 */
POWERPC_DEF_SVR("MPC8533",
@ -9928,6 +10042,27 @@ int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
env->bfd_mach = def->bfd_mach;
env->check_pow = def->check_pow;
#if defined(TARGET_PPC64)
if (def->sps)
env->sps = *def->sps;
else if (env->mmu_model & POWERPC_MMU_64) {
/* Use default sets of page sizes */
static const struct ppc_segment_page_sizes defsps = {
.sps = {
{ .page_shift = 12, /* 4K */
.slb_enc = 0,
.enc = { { .page_shift = 12, .pte_enc = 0 } }
},
{ .page_shift = 24, /* 16M */
.slb_enc = 0x100,
.enc = { { .page_shift = 24, .pte_enc = 0 } }
},
},
};
env->sps = defsps;
}
#endif /* defined(TARGET_PPC64) */
if (kvm_enabled()) {
if (kvmppc_fixup_cpu(env) != 0) {
fprintf(stderr, "Unable to virtualize selected CPU with KVM\n");