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:
commit
4e469a438f
1
Makefile
1
Makefile
@ -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 \
|
||||
|
@ -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
2
configure
vendored
@ -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
|
||||
|
@ -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 */
|
||||
|
108
device_tree.c
108
device_tree.c
@ -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));
|
||||
|
@ -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__ */
|
||||
|
78
docs/specs/ppc-spapr-hcalls.txt
Normal file
78
docs/specs/ppc-spapr-hcalls.txt
Normal 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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
46
hw/spapr.c
46
hw/spapr.c
@ -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))));
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
@ -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 {
|
||||
};
|
||||
};
|
@ -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 */ }
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
969
target-ppc/excp_helper.c
Normal 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
1740
target-ppc/fpu_helper.c
Normal file
File diff suppressed because it is too large
Load Diff
3168
target-ppc/helper.c
3168
target-ppc/helper.c
File diff suppressed because it is too large
Load Diff
@ -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
1564
target-ppc/int_helper.c
Normal file
File diff suppressed because it is too large
Load Diff
208
target-ppc/kvm.c
208
target-ppc/kvm.c
@ -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;
|
||||
|
@ -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
295
target-ppc/mem_helper.c
Normal 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
124
target-ppc/misc_helper.c
Normal 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
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
35
target-ppc/mpic_helper.c
Normal 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
159
target-ppc/timebase_helper.c
Normal file
159
target-ppc/timebase_helper.c
Normal 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
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user