Merge remote-tracking branch 'upstream' into memory/batch
* upstream: (87 commits) target-alpha: Fix compilation errors for 32 bit hosts target-alpha: Add high-resolution access to wall clock and an alarm. target-alpha: Implement HALT IPR. target-alpha: Implement WAIT IPR. target-alpha: Add CLIPPER emulation. target-alpha: Add custom PALcode image for CLIPPER emulation. target-alpha: Honor icount for RPCC instruction. tcg/s390: Remove unused tcg_out_addi() tcg/ia64: Remove unused tcg_out_addi() ARM: fix segfault ppc64: Fix linker script pseries: Implement set-time-of-day RTAS function pseries: Refactor spapr irq allocation PPC: Clean up BookE timer code PPC: booke timers KVM: PPC: Use HIOR setting for -M pseries with PR KVM KVM: Update kernel headers KVM: Update kernel headers PPC: Fix heathrow PIC to use little endian MMIO PPC: Fix via-cuda memory registration ... Conflicts: hw/milkymist-uart.c hw/ppce500_mpc8544ds.c Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
commit
df2921d326
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,3 +13,6 @@
|
||||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = git://git.qemu.org/openbios.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = git://repo.or.cz/qemu-palcode.git
|
||||
|
3
Makefile
3
Makefile
@ -251,7 +251,8 @@ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
|
||||
mpc8544ds.dtb \
|
||||
multiboot.bin linuxboot.bin \
|
||||
s390-zipl.rom \
|
||||
spapr-rtas.bin slof.bin
|
||||
spapr-rtas.bin slof.bin \
|
||||
palcode-clipper
|
||||
else
|
||||
BLOBS=
|
||||
endif
|
||||
|
@ -222,7 +222,6 @@ hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
|
||||
hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
|
||||
|
||||
# PPC devices
|
||||
hw-obj-$(CONFIG_OPENPIC) += openpic.o
|
||||
hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
|
||||
# Mac shared devices
|
||||
hw-obj-$(CONFIG_MACIO) += macio.o
|
||||
|
@ -229,7 +229,7 @@ obj-i386-$(CONFIG_KVM) += kvmclock.o
|
||||
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
|
||||
|
||||
# shared objects
|
||||
obj-ppc-y = ppc.o
|
||||
obj-ppc-y = ppc.o ppc_booke.o
|
||||
obj-ppc-y += vga.o
|
||||
# PREP target
|
||||
obj-ppc-y += i8259.o mc146818rtc.o
|
||||
@ -239,19 +239,19 @@ obj-ppc-y += ppc_oldworld.o
|
||||
# NewWorld PowerMac
|
||||
obj-ppc-y += ppc_newworld.o
|
||||
# IBM pSeries (sPAPR)
|
||||
ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
|
||||
obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
|
||||
obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
|
||||
endif
|
||||
obj-ppc-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
|
||||
obj-ppc-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
|
||||
# PowerPC 4xx boards
|
||||
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
|
||||
obj-ppc-y += ppc440.o ppc440_bamboo.o
|
||||
# PowerPC E500 boards
|
||||
obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o
|
||||
obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o
|
||||
# PowerPC 440 Xilinx ML507 reference board.
|
||||
obj-ppc-y += virtex_ml507.o
|
||||
obj-ppc-$(CONFIG_KVM) += kvm_ppc.o
|
||||
obj-ppc-$(CONFIG_FDT) += device_tree.o
|
||||
# PowerPC OpenPIC
|
||||
obj-ppc-y += openpic.o
|
||||
|
||||
# Xilinx PPC peripherals
|
||||
obj-ppc-y += xilinx_intc.o
|
||||
@ -367,6 +367,7 @@ obj-s390x-y = s390-virtio-bus.o s390-virtio.o
|
||||
|
||||
obj-alpha-y = i8259.o mc146818rtc.o
|
||||
obj-alpha-y += vga.o cirrus_vga.o
|
||||
obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
|
||||
|
||||
obj-xtensa-y += xtensa_pic.o
|
||||
obj-xtensa-y += xtensa_sample.o
|
||||
|
4
block.c
4
block.c
@ -475,6 +475,8 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
|
||||
assert(drv != NULL);
|
||||
|
||||
trace_bdrv_open_common(bs, filename, flags, drv->format_name);
|
||||
|
||||
bs->file = NULL;
|
||||
bs->total_sectors = 0;
|
||||
bs->encrypted = 0;
|
||||
@ -2997,7 +2999,7 @@ static int coroutine_fn bdrv_co_io_em(BlockDriverState *bs, int64_t sector_num,
|
||||
bdrv_co_io_em_complete, &co);
|
||||
}
|
||||
|
||||
trace_bdrv_co_io(is_write, acb);
|
||||
trace_bdrv_co_io_em(bs, sector_num, nb_sectors, is_write, acb);
|
||||
if (!acb) {
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -222,21 +222,21 @@ static void qed_read_l2_table_cb(void *opaque, int ret)
|
||||
QEDRequest *request = read_l2_table_cb->request;
|
||||
BDRVQEDState *s = read_l2_table_cb->s;
|
||||
CachedL2Table *l2_table = request->l2_table;
|
||||
uint64_t l2_offset = read_l2_table_cb->l2_offset;
|
||||
|
||||
if (ret) {
|
||||
/* can't trust loaded L2 table anymore */
|
||||
qed_unref_l2_cache_entry(l2_table);
|
||||
request->l2_table = NULL;
|
||||
} else {
|
||||
l2_table->offset = read_l2_table_cb->l2_offset;
|
||||
l2_table->offset = l2_offset;
|
||||
|
||||
qed_commit_l2_cache_entry(&s->l2_cache, l2_table);
|
||||
|
||||
/* This is guaranteed to succeed because we just committed the entry
|
||||
* to the cache.
|
||||
*/
|
||||
request->l2_table = qed_find_l2_cache_entry(&s->l2_cache,
|
||||
l2_table->offset);
|
||||
request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset);
|
||||
assert(request->l2_table != NULL);
|
||||
}
|
||||
|
||||
|
@ -911,14 +911,14 @@ static void qed_commit_l2_update(void *opaque, int ret)
|
||||
QEDAIOCB *acb = opaque;
|
||||
BDRVQEDState *s = acb_to_s(acb);
|
||||
CachedL2Table *l2_table = acb->request.l2_table;
|
||||
uint64_t l2_offset = l2_table->offset;
|
||||
|
||||
qed_commit_l2_cache_entry(&s->l2_cache, l2_table);
|
||||
|
||||
/* This is guaranteed to succeed because we just committed the entry to the
|
||||
* cache.
|
||||
*/
|
||||
acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache,
|
||||
l2_table->offset);
|
||||
acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset);
|
||||
assert(acb->request.l2_table != NULL);
|
||||
|
||||
qed_aio_next_io(opaque, ret);
|
||||
|
11
configure
vendored
11
configure
vendored
@ -3389,6 +3389,9 @@ case "$target_arch2" in
|
||||
fi
|
||||
fi
|
||||
esac
|
||||
if test "$target_arch2" = "ppc64" -a "$fdt" = "yes"; then
|
||||
echo "CONFIG_PSERIES=y" >> $config_target_mak
|
||||
fi
|
||||
if test "$target_bigendian" = "yes" ; then
|
||||
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
|
||||
fi
|
||||
@ -3615,7 +3618,13 @@ FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
|
||||
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
|
||||
FILES="$FILES pc-bios/spapr-rtas/Makefile"
|
||||
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
|
||||
for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.rom $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do
|
||||
for bios_file in \
|
||||
$source_path/pc-bios/*.bin \
|
||||
$source_path/pc-bios/*.rom \
|
||||
$source_path/pc-bios/*.dtb \
|
||||
$source_path/pc-bios/openbios-* \
|
||||
$source_path/pc-bios/palcode-*
|
||||
do
|
||||
FILES="$FILES pc-bios/`basename $bios_file`"
|
||||
done
|
||||
mkdir -p $DIRS
|
||||
|
@ -217,6 +217,7 @@ int cpu_exec(CPUState *env)
|
||||
#elif defined(TARGET_ARM)
|
||||
#elif defined(TARGET_UNICORE32)
|
||||
#elif defined(TARGET_PPC)
|
||||
env->reserve_addr = -1;
|
||||
#elif defined(TARGET_LM32)
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
#elif defined(TARGET_MIPS)
|
||||
|
@ -3,7 +3,9 @@
|
||||
include pci.mak
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_I8254=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_VGA_PCI=y
|
||||
CONFIG_IDE_CORE=y
|
||||
CONFIG_IDE_QDEV=y
|
||||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_IDE_CMD646=y
|
||||
|
@ -41,6 +41,7 @@ void *load_device_tree(const char *filename_path, int *sizep)
|
||||
}
|
||||
|
||||
/* Expand to 2x size to give enough room for manipulation. */
|
||||
dt_size += 10000;
|
||||
dt_size *= 2;
|
||||
/* First allocate space in qemu for device tree */
|
||||
fdt = g_malloc0(dt_size);
|
||||
@ -72,38 +73,99 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qemu_devtree_setprop(void *fdt, const char *node_path,
|
||||
const char *property, void *val_array, int size)
|
||||
static int findnode_nofail(void *fdt, const char *node_path)
|
||||
{
|
||||
int offset;
|
||||
|
||||
offset = fdt_path_offset(fdt, node_path);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
if (offset < 0) {
|
||||
fprintf(stderr, "%s Couldn't find node %s: %s\n", __func__, node_path,
|
||||
fdt_strerror(offset));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fdt_setprop(fdt, offset, property, val_array, size);
|
||||
return offset;
|
||||
}
|
||||
|
||||
int qemu_devtree_setprop(void *fdt, const char *node_path,
|
||||
const char *property, void *val_array, int size)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val_array, size);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: Couldn't set %s/%s: %s\n", __func__, node_path,
|
||||
property, fdt_strerror(r));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
|
||||
const char *property, uint32_t val)
|
||||
{
|
||||
int offset;
|
||||
int r;
|
||||
|
||||
offset = fdt_path_offset(fdt, node_path);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: Couldn't set %s/%s = %#08x: %s\n", __func__,
|
||||
node_path, property, val, fdt_strerror(r));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fdt_setprop_cell(fdt, offset, property, val);
|
||||
return r;
|
||||
}
|
||||
|
||||
int qemu_devtree_setprop_string(void *fdt, const char *node_path,
|
||||
const char *property, const char *string)
|
||||
{
|
||||
int offset;
|
||||
int r;
|
||||
|
||||
offset = fdt_path_offset(fdt, node_path);
|
||||
if (offset < 0)
|
||||
return offset;
|
||||
r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: Couldn't set %s/%s = %s: %s\n", __func__,
|
||||
node_path, property, string, fdt_strerror(r));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return fdt_setprop_string(fdt, offset, property, string);
|
||||
return r;
|
||||
}
|
||||
|
||||
int qemu_devtree_nop_node(void *fdt, const char *node_path)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
|
||||
if (r < 0) {
|
||||
fprintf(stderr, "%s: Couldn't nop node %s: %s\n", __func__, node_path,
|
||||
fdt_strerror(r));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int qemu_devtree_add_subnode(void *fdt, const char *name)
|
||||
{
|
||||
char *dupname = g_strdup(name);
|
||||
char *basename = strrchr(dupname, '/');
|
||||
int retval;
|
||||
|
||||
if (!basename) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
basename[0] = '\0';
|
||||
basename++;
|
||||
|
||||
retval = fdt_add_subnode(fdt, findnode_nofail(fdt, dupname), basename);
|
||||
if (retval < 0) {
|
||||
fprintf(stderr, "FDT: Failed to create subnode %s: %s\n", name,
|
||||
fdt_strerror(retval));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_free(dupname);
|
||||
return retval;
|
||||
}
|
||||
|
@ -22,5 +22,7 @@ int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
|
||||
const char *property, uint32_t val);
|
||||
int qemu_devtree_setprop_string(void *fdt, const char *node_path,
|
||||
const char *property, const char *string);
|
||||
int qemu_devtree_nop_node(void *fdt, const char *node_path);
|
||||
int qemu_devtree_add_subnode(void *fdt, const char *name);
|
||||
|
||||
#endif /* __DEVICE_TREE_H__ */
|
||||
|
@ -733,7 +733,7 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
|
||||
{
|
||||
if (gdb_has_xml)
|
||||
return 0;
|
||||
GET_REG32(0); /* fpscr */
|
||||
GET_REG32(env->fpscr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ STEXI
|
||||
changes status of a trace event
|
||||
ETEXI
|
||||
|
||||
#if defined(CONFIG_SIMPLE_TRACE)
|
||||
#if defined(CONFIG_TRACE_SIMPLE)
|
||||
{
|
||||
.name = "trace-file",
|
||||
.args_type = "op:s?,arg:F?",
|
||||
@ -1306,7 +1306,7 @@ show i8259 (PIC) state
|
||||
@item info pci
|
||||
show emulated PCI device info
|
||||
@item info tlb
|
||||
show virtual to physical memory mappings (i386, SH4 and SPARC only)
|
||||
show virtual to physical memory mappings (i386, SH4, SPARC, and PPC only)
|
||||
@item info mem
|
||||
show the active virtual memory mappings (i386 only)
|
||||
@item info jit
|
||||
|
2
hw/adb.c
2
hw/adb.c
@ -22,7 +22,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw.h"
|
||||
#include "ppc_mac.h"
|
||||
#include "adb.h"
|
||||
#include "console.h"
|
||||
|
||||
/* debug ADB */
|
||||
|
67
hw/adb.h
Normal file
67
hw/adb.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* QEMU ADB emulation shared definitions and prototypes
|
||||
*
|
||||
* Copyright (c) 2004-2007 Fabrice Bellard
|
||||
* Copyright (c) 2007 Jocelyn Mayer
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if !defined(__ADB_H__)
|
||||
#define __ADB_H__
|
||||
|
||||
#define MAX_ADB_DEVICES 16
|
||||
|
||||
#define ADB_MAX_OUT_LEN 16
|
||||
|
||||
typedef struct ADBDevice ADBDevice;
|
||||
|
||||
/* buf = NULL means polling */
|
||||
typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
|
||||
const uint8_t *buf, int len);
|
||||
typedef int ADBDeviceReset(ADBDevice *d);
|
||||
|
||||
struct ADBDevice {
|
||||
struct ADBBusState *bus;
|
||||
int devaddr;
|
||||
int handler;
|
||||
ADBDeviceRequest *devreq;
|
||||
ADBDeviceReset *devreset;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
typedef struct ADBBusState {
|
||||
ADBDevice devices[MAX_ADB_DEVICES];
|
||||
int nb_devices;
|
||||
int poll_index;
|
||||
} ADBBusState;
|
||||
|
||||
int adb_request(ADBBusState *s, uint8_t *buf_out,
|
||||
const uint8_t *buf, int len);
|
||||
int adb_poll(ADBBusState *s, uint8_t *buf_out);
|
||||
|
||||
ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
|
||||
ADBDeviceRequest *devreq,
|
||||
ADBDeviceReset *devreset,
|
||||
void *opaque);
|
||||
void adb_kbd_init(ADBBusState *bus);
|
||||
void adb_mouse_init(ADBBusState *bus);
|
||||
|
||||
extern ADBBusState adb_bus;
|
||||
#endif /* !defined(__ADB_H__) */
|
177
hw/alpha_dp264.c
Normal file
177
hw/alpha_dp264.c
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* QEMU Alpha DP264/CLIPPER hardware system emulator.
|
||||
*
|
||||
* Choose CLIPPER IRQ mappings over, say, DP264, MONET, or WEBBRICK
|
||||
* variants because CLIPPER doesn't have an SMC669 SuperIO controler
|
||||
* that we need to emulate as well.
|
||||
*/
|
||||
|
||||
#include "hw.h"
|
||||
#include "elf.h"
|
||||
#include "loader.h"
|
||||
#include "boards.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "sysemu.h"
|
||||
#include "mc146818rtc.h"
|
||||
#include "ide.h"
|
||||
|
||||
#define MAX_IDE_BUS 2
|
||||
|
||||
static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr)
|
||||
{
|
||||
if (((addr >> 41) & 3) == 2) {
|
||||
addr &= 0xffffffffffull;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Note that there are at least 3 viewpoints of IRQ numbers on Alpha systems.
|
||||
(0) The dev_irq_n lines into the cpu, which we totally ignore,
|
||||
(1) The DRIR lines in the typhoon chipset,
|
||||
(2) The "vector" aka mangled interrupt number reported by SRM PALcode,
|
||||
(3) The interrupt number assigned by the kernel.
|
||||
The following function is concerned with (1) only. */
|
||||
|
||||
static int clipper_pci_map_irq(PCIDevice *d, int irq_num)
|
||||
{
|
||||
int slot = d->devfn >> 3;
|
||||
|
||||
assert(irq_num >= 0 && irq_num <= 3);
|
||||
|
||||
return (slot + 1) * 4 + irq_num;
|
||||
}
|
||||
|
||||
static void clipper_init(ram_addr_t ram_size,
|
||||
const char *boot_device,
|
||||
const char *kernel_filename,
|
||||
const char *kernel_cmdline,
|
||||
const char *initrd_filename,
|
||||
const char *cpu_model)
|
||||
{
|
||||
CPUState *cpus[4];
|
||||
PCIBus *pci_bus;
|
||||
qemu_irq rtc_irq;
|
||||
long size, i;
|
||||
const char *palcode_filename;
|
||||
uint64_t palcode_entry, palcode_low, palcode_high;
|
||||
uint64_t kernel_entry, kernel_low, kernel_high;
|
||||
|
||||
/* Create up to 4 cpus. */
|
||||
memset(cpus, 0, sizeof(cpus));
|
||||
for (i = 0; i < smp_cpus; ++i) {
|
||||
cpus[i] = cpu_init(cpu_model ? cpu_model : "ev67");
|
||||
}
|
||||
|
||||
cpus[0]->trap_arg0 = ram_size;
|
||||
cpus[0]->trap_arg1 = 0;
|
||||
cpus[0]->trap_arg2 = smp_cpus;
|
||||
|
||||
/* Init the chipset. */
|
||||
pci_bus = typhoon_init(ram_size, &rtc_irq, cpus, clipper_pci_map_irq);
|
||||
|
||||
rtc_init(1980, rtc_irq);
|
||||
pit_init(0x40, 0);
|
||||
isa_create_simple("i8042");
|
||||
|
||||
/* VGA setup. Don't bother loading the bios. */
|
||||
alpha_pci_vga_setup(pci_bus);
|
||||
|
||||
/* Serial code setup. */
|
||||
for (i = 0; i < MAX_SERIAL_PORTS; ++i) {
|
||||
if (serial_hds[i]) {
|
||||
serial_isa_init(i, serial_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Network setup. e1000 is good enough, failing Tulip support. */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
|
||||
}
|
||||
|
||||
/* IDE disk setup. */
|
||||
{
|
||||
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
|
||||
ide_drive_get(hd, MAX_IDE_BUS);
|
||||
|
||||
pci_cmd646_ide_init(pci_bus, hd, 0);
|
||||
}
|
||||
|
||||
/* Load PALcode. Given that this is not "real" cpu palcode,
|
||||
but one explicitly written for the emulation, we might as
|
||||
well load it directly from and ELF image. */
|
||||
palcode_filename = (bios_name ? bios_name : "palcode-clipper");
|
||||
palcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, palcode_filename);
|
||||
if (palcode_filename == NULL) {
|
||||
hw_error("no palcode provided\n");
|
||||
exit(1);
|
||||
}
|
||||
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
||||
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
||||
0, EM_ALPHA, 0);
|
||||
if (size < 0) {
|
||||
hw_error("could not load palcode '%s'\n", palcode_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Start all cpus at the PALcode RESET entry point. */
|
||||
for (i = 0; i < smp_cpus; ++i) {
|
||||
cpus[i]->pal_mode = 1;
|
||||
cpus[i]->pc = palcode_entry;
|
||||
cpus[i]->palbr = palcode_entry;
|
||||
}
|
||||
|
||||
/* Load a kernel. */
|
||||
if (kernel_filename) {
|
||||
uint64_t param_offset;
|
||||
|
||||
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
|
||||
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
||||
0, EM_ALPHA, 0);
|
||||
if (size < 0) {
|
||||
hw_error("could not load kernel '%s'\n", kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cpus[0]->trap_arg1 = kernel_entry;
|
||||
|
||||
param_offset = kernel_low - 0x6000;
|
||||
|
||||
if (kernel_cmdline) {
|
||||
pstrcpy_targphys("cmdline", param_offset, 0x100, kernel_cmdline);
|
||||
}
|
||||
|
||||
if (initrd_filename) {
|
||||
long initrd_base, initrd_size;
|
||||
|
||||
initrd_size = get_image_size(initrd_filename);
|
||||
if (initrd_size < 0) {
|
||||
hw_error("could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Put the initrd image as high in memory as possible. */
|
||||
initrd_base = (ram_size - initrd_size) & TARGET_PAGE_MASK;
|
||||
load_image_targphys(initrd_filename, initrd_base,
|
||||
ram_size - initrd_base);
|
||||
|
||||
stq_phys(param_offset + 0x100, initrd_base + 0xfffffc0000000000ULL);
|
||||
stq_phys(param_offset + 0x108, initrd_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QEMUMachine clipper_machine = {
|
||||
.name = "clipper",
|
||||
.desc = "Alpha DP264/CLIPPER",
|
||||
.init = clipper_init,
|
||||
.max_cpus = 4,
|
||||
.is_default = 1,
|
||||
};
|
||||
|
||||
static void clipper_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&clipper_machine);
|
||||
}
|
||||
|
||||
machine_init(clipper_machine_init);
|
134
hw/alpha_pci.c
Normal file
134
hw/alpha_pci.c
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* QEMU Alpha PCI support functions.
|
||||
*
|
||||
* Some of this isn't very Alpha specific at all.
|
||||
*
|
||||
* ??? Sparse memory access not implemented.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "qemu-log.h"
|
||||
#include "sysemu.h"
|
||||
#include "vmware_vga.h"
|
||||
|
||||
|
||||
/* PCI IO reads/writes, to byte-word addressable memory. */
|
||||
/* ??? Doesn't handle multiple PCI busses. */
|
||||
|
||||
static uint64_t bw_io_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
return cpu_inb(addr);
|
||||
case 2:
|
||||
return cpu_inw(addr);
|
||||
case 4:
|
||||
return cpu_inl(addr);
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void bw_io_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
cpu_outb(addr, val);
|
||||
break;
|
||||
case 2:
|
||||
cpu_outw(addr, val);
|
||||
break;
|
||||
case 4:
|
||||
cpu_outl(addr, val);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_bw_io_ops = {
|
||||
.read = bw_io_read,
|
||||
.write = bw_io_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
/* PCI config space reads/writes, to byte-word addressable memory. */
|
||||
static uint64_t bw_conf1_read(void *opaque, target_phys_addr_t addr,
|
||||
unsigned size)
|
||||
{
|
||||
PCIBus *b = opaque;
|
||||
return pci_data_read(b, addr, size);
|
||||
}
|
||||
|
||||
static void bw_conf1_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
PCIBus *b = opaque;
|
||||
pci_data_write(b, addr, val, size);
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_conf1_ops = {
|
||||
.read = bw_conf1_read,
|
||||
.write = bw_conf1_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
/* PCI/EISA Interrupt Acknowledge Cycle. */
|
||||
|
||||
static uint64_t iack_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
return pic_read_irq(isa_pic);
|
||||
}
|
||||
|
||||
static void special_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
qemu_log("pci: special write cycle");
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_iack_ops = {
|
||||
.read = iack_read,
|
||||
.write = special_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
void alpha_pci_vga_setup(PCIBus *pci_bus)
|
||||
{
|
||||
switch (vga_interface_type) {
|
||||
#ifdef CONFIG_SPICE
|
||||
case VGA_QXL:
|
||||
pci_create_simple(pci_bus, -1, "qxl-vga");
|
||||
return;
|
||||
#endif
|
||||
case VGA_CIRRUS:
|
||||
pci_cirrus_vga_init(pci_bus);
|
||||
return;
|
||||
case VGA_VMWARE:
|
||||
if (pci_vmsvga_init(pci_bus)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* If VGA is enabled at all, and one of the above didn't work, then
|
||||
fallback to Standard VGA. */
|
||||
if (vga_interface_type != VGA_NONE) {
|
||||
pci_vga_init(pci_bus);
|
||||
}
|
||||
}
|
24
hw/alpha_sys.h
Normal file
24
hw/alpha_sys.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* Alpha cores and system support chips. */
|
||||
|
||||
#ifndef HW_ALPHA_H
|
||||
#define HW_ALPHA_H 1
|
||||
|
||||
#include "pci.h"
|
||||
#include "pci_host.h"
|
||||
#include "ide.h"
|
||||
#include "net.h"
|
||||
#include "pc.h"
|
||||
#include "usb-ohci.h"
|
||||
#include "irq.h"
|
||||
|
||||
|
||||
PCIBus *typhoon_init(ram_addr_t, qemu_irq *, CPUState *[4], pci_map_irq_fn);
|
||||
|
||||
/* alpha_pci.c. */
|
||||
extern const MemoryRegionOps alpha_pci_bw_io_ops;
|
||||
extern const MemoryRegionOps alpha_pci_conf1_ops;
|
||||
extern const MemoryRegionOps alpha_pci_iack_ops;
|
||||
|
||||
void alpha_pci_vga_setup(PCIBus *pci_bus);
|
||||
|
||||
#endif
|
820
hw/alpha_typhoon.c
Normal file
820
hw/alpha_typhoon.c
Normal file
@ -0,0 +1,820 @@
|
||||
/*
|
||||
* DEC 21272 (TSUNAMI/TYPHOON) chipset emulation.
|
||||
*
|
||||
* Written by Richard Henderson.
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec-all.h"
|
||||
#include "hw.h"
|
||||
#include "devices.h"
|
||||
#include "sysemu.h"
|
||||
#include "alpha_sys.h"
|
||||
#include "exec-memory.h"
|
||||
|
||||
|
||||
typedef struct TyphoonCchip {
|
||||
MemoryRegion region;
|
||||
uint64_t misc;
|
||||
uint64_t drir;
|
||||
uint64_t dim[4];
|
||||
uint32_t iic[4];
|
||||
CPUState *cpu[4];
|
||||
} TyphoonCchip;
|
||||
|
||||
typedef struct TyphoonWindow {
|
||||
uint32_t base_addr;
|
||||
uint32_t mask;
|
||||
uint32_t translated_base_pfn;
|
||||
} TyphoonWindow;
|
||||
|
||||
typedef struct TyphoonPchip {
|
||||
MemoryRegion region;
|
||||
MemoryRegion reg_iack;
|
||||
MemoryRegion reg_mem;
|
||||
MemoryRegion reg_io;
|
||||
MemoryRegion reg_conf;
|
||||
uint64_t ctl;
|
||||
TyphoonWindow win[4];
|
||||
} TyphoonPchip;
|
||||
|
||||
typedef struct TyphoonState {
|
||||
PCIHostState host;
|
||||
TyphoonCchip cchip;
|
||||
TyphoonPchip pchip;
|
||||
MemoryRegion dchip_region;
|
||||
MemoryRegion ram_region;
|
||||
|
||||
/* QEMU emulation state. */
|
||||
uint32_t latch_tmp;
|
||||
} TyphoonState;
|
||||
|
||||
/* Called when one of DRIR or DIM changes. */
|
||||
static void cpu_irq_change(CPUState *env, uint64_t req)
|
||||
{
|
||||
/* If there are any non-masked interrupts, tell the cpu. */
|
||||
if (env) {
|
||||
if (req) {
|
||||
cpu_interrupt(env, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t cchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
CPUState *env = cpu_single_env;
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
if (addr & 4) {
|
||||
return s->latch_tmp;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* CSC: Cchip System Configuration Register. */
|
||||
/* All sorts of data here; probably the only thing relevant is
|
||||
PIP<14> Pchip 1 Present = 0. */
|
||||
break;
|
||||
|
||||
case 0x0040:
|
||||
/* MTR: Memory Timing Register. */
|
||||
/* All sorts of stuff related to real DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0080:
|
||||
/* MISC: Miscellaneous Register. */
|
||||
ret = s->cchip.misc | (env->cpu_index & 3);
|
||||
break;
|
||||
|
||||
case 0x00c0:
|
||||
/* MPD: Memory Presence Detect Register. */
|
||||
break;
|
||||
|
||||
case 0x0100: /* AAR0 */
|
||||
case 0x0140: /* AAR1 */
|
||||
case 0x0180: /* AAR2 */
|
||||
case 0x01c0: /* AAR3 */
|
||||
/* AAR: Array Address Register. */
|
||||
/* All sorts of information about DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0200:
|
||||
/* DIM0: Device Interrupt Mask Register, CPU0. */
|
||||
ret = s->cchip.dim[0];
|
||||
break;
|
||||
case 0x0240:
|
||||
/* DIM1: Device Interrupt Mask Register, CPU1. */
|
||||
ret = s->cchip.dim[1];
|
||||
break;
|
||||
case 0x0280:
|
||||
/* DIR0: Device Interrupt Request Register, CPU0. */
|
||||
ret = s->cchip.dim[0] & s->cchip.drir;
|
||||
break;
|
||||
case 0x02c0:
|
||||
/* DIR1: Device Interrupt Request Register, CPU1. */
|
||||
ret = s->cchip.dim[1] & s->cchip.drir;
|
||||
break;
|
||||
case 0x0300:
|
||||
/* DRIR: Device Raw Interrupt Request Register. */
|
||||
ret = s->cchip.drir;
|
||||
break;
|
||||
|
||||
case 0x0340:
|
||||
/* PRBEN: Probe Enable Register. */
|
||||
break;
|
||||
|
||||
case 0x0380:
|
||||
/* IIC0: Interval Ignore Count Register, CPU0. */
|
||||
ret = s->cchip.iic[0];
|
||||
break;
|
||||
case 0x03c0:
|
||||
/* IIC1: Interval Ignore Count Register, CPU1. */
|
||||
ret = s->cchip.iic[1];
|
||||
break;
|
||||
|
||||
case 0x0400: /* MPR0 */
|
||||
case 0x0440: /* MPR1 */
|
||||
case 0x0480: /* MPR2 */
|
||||
case 0x04c0: /* MPR3 */
|
||||
/* MPR: Memory Programming Register. */
|
||||
break;
|
||||
|
||||
case 0x0580:
|
||||
/* TTR: TIGbus Timing Register. */
|
||||
/* All sorts of stuff related to interrupt delivery timings. */
|
||||
break;
|
||||
case 0x05c0:
|
||||
/* TDR: TIGbug Device Timing Register. */
|
||||
break;
|
||||
|
||||
case 0x0600:
|
||||
/* DIM2: Device Interrupt Mask Register, CPU2. */
|
||||
ret = s->cchip.dim[2];
|
||||
break;
|
||||
case 0x0640:
|
||||
/* DIM3: Device Interrupt Mask Register, CPU3. */
|
||||
ret = s->cchip.dim[3];
|
||||
break;
|
||||
case 0x0680:
|
||||
/* DIR2: Device Interrupt Request Register, CPU2. */
|
||||
ret = s->cchip.dim[2] & s->cchip.drir;
|
||||
break;
|
||||
case 0x06c0:
|
||||
/* DIR3: Device Interrupt Request Register, CPU3. */
|
||||
ret = s->cchip.dim[3] & s->cchip.drir;
|
||||
break;
|
||||
|
||||
case 0x0700:
|
||||
/* IIC2: Interval Ignore Count Register, CPU2. */
|
||||
ret = s->cchip.iic[2];
|
||||
break;
|
||||
case 0x0740:
|
||||
/* IIC3: Interval Ignore Count Register, CPU3. */
|
||||
ret = s->cchip.iic[3];
|
||||
break;
|
||||
|
||||
case 0x0780:
|
||||
/* PWR: Power Management Control. */
|
||||
break;
|
||||
|
||||
case 0x0c00: /* CMONCTLA */
|
||||
case 0x0c40: /* CMONCTLB */
|
||||
case 0x0c80: /* CMONCNT01 */
|
||||
case 0x0cc0: /* CMONCNT23 */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->latch_tmp = ret >> 32;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t dchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
/* Skip this. It's all related to DRAM timing and setup. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t pchip_read(void *opaque, target_phys_addr_t addr, unsigned size)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t ret = 0;
|
||||
|
||||
if (addr & 4) {
|
||||
return s->latch_tmp;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* WSBA0: Window Space Base Address Register. */
|
||||
ret = s->pchip.win[0].base_addr;
|
||||
break;
|
||||
case 0x0040:
|
||||
/* WSBA1 */
|
||||
ret = s->pchip.win[1].base_addr;
|
||||
break;
|
||||
case 0x0080:
|
||||
/* WSBA2 */
|
||||
ret = s->pchip.win[2].base_addr;
|
||||
break;
|
||||
case 0x00c0:
|
||||
/* WSBA3 */
|
||||
ret = s->pchip.win[3].base_addr;
|
||||
break;
|
||||
|
||||
case 0x0100:
|
||||
/* WSM0: Window Space Mask Register. */
|
||||
ret = s->pchip.win[0].mask;
|
||||
break;
|
||||
case 0x0140:
|
||||
/* WSM1 */
|
||||
ret = s->pchip.win[1].mask;
|
||||
break;
|
||||
case 0x0180:
|
||||
/* WSM2 */
|
||||
ret = s->pchip.win[2].mask;
|
||||
break;
|
||||
case 0x01c0:
|
||||
/* WSM3 */
|
||||
ret = s->pchip.win[3].mask;
|
||||
break;
|
||||
|
||||
case 0x0200:
|
||||
/* TBA0: Translated Base Address Register. */
|
||||
ret = (uint64_t)s->pchip.win[0].translated_base_pfn << 10;
|
||||
break;
|
||||
case 0x0240:
|
||||
/* TBA1 */
|
||||
ret = (uint64_t)s->pchip.win[1].translated_base_pfn << 10;
|
||||
break;
|
||||
case 0x0280:
|
||||
/* TBA2 */
|
||||
ret = (uint64_t)s->pchip.win[2].translated_base_pfn << 10;
|
||||
break;
|
||||
case 0x02c0:
|
||||
/* TBA3 */
|
||||
ret = (uint64_t)s->pchip.win[3].translated_base_pfn << 10;
|
||||
break;
|
||||
|
||||
case 0x0300:
|
||||
/* PCTL: Pchip Control Register. */
|
||||
ret = s->pchip.ctl;
|
||||
break;
|
||||
case 0x0340:
|
||||
/* PLAT: Pchip Master Latency Register. */
|
||||
break;
|
||||
case 0x03c0:
|
||||
/* PERROR: Pchip Error Register. */
|
||||
break;
|
||||
case 0x0400:
|
||||
/* PERRMASK: Pchip Error Mask Register. */
|
||||
break;
|
||||
case 0x0440:
|
||||
/* PERRSET: Pchip Error Set Register. */
|
||||
break;
|
||||
case 0x0480:
|
||||
/* TLBIV: Translation Buffer Invalidate Virtual Register (WO). */
|
||||
break;
|
||||
case 0x04c0:
|
||||
/* TLBIA: Translation Buffer Invalidate All Register (WO). */
|
||||
break;
|
||||
case 0x0500: /* PMONCTL */
|
||||
case 0x0540: /* PMONCNT */
|
||||
case 0x0800: /* SPRST */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->latch_tmp = ret >> 32;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cchip_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t v32, unsigned size)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t val, oldval, newval;
|
||||
|
||||
if (addr & 4) {
|
||||
val = v32 << 32 | s->latch_tmp;
|
||||
addr ^= 4;
|
||||
} else {
|
||||
s->latch_tmp = v32;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* CSC: Cchip System Configuration Register. */
|
||||
/* All sorts of data here; nothing relevant RW. */
|
||||
break;
|
||||
|
||||
case 0x0040:
|
||||
/* MTR: Memory Timing Register. */
|
||||
/* All sorts of stuff related to real DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0080:
|
||||
/* MISC: Miscellaneous Register. */
|
||||
newval = oldval = s->cchip.misc;
|
||||
newval &= ~(val & 0x10000ff0); /* W1C fields */
|
||||
if (val & 0x100000) {
|
||||
newval &= ~0xff0000ull; /* ACL clears ABT and ABW */
|
||||
} else {
|
||||
newval |= val & 0x00f00000; /* ABT field is W1S */
|
||||
if ((newval & 0xf0000) == 0) {
|
||||
newval |= val & 0xf0000; /* ABW field is W1S iff zero */
|
||||
}
|
||||
}
|
||||
newval |= (val & 0xf000) >> 4; /* IPREQ field sets IPINTR. */
|
||||
|
||||
newval &= ~0xf0000000000ull; /* WO and RW fields */
|
||||
newval |= val & 0xf0000000000ull;
|
||||
s->cchip.misc = newval;
|
||||
|
||||
/* Pass on changes to IPI and ITI state. */
|
||||
if ((newval ^ oldval) & 0xff0) {
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
CPUState *env = s->cchip.cpu[i];
|
||||
if (env) {
|
||||
/* IPI can be either cleared or set by the write. */
|
||||
if (newval & (1 << (i + 8))) {
|
||||
cpu_interrupt(env, CPU_INTERRUPT_SMP);
|
||||
} else {
|
||||
cpu_reset_interrupt(env, CPU_INTERRUPT_SMP);
|
||||
}
|
||||
|
||||
/* ITI can only be cleared by the write. */
|
||||
if ((newval & (1 << (i + 4))) == 0) {
|
||||
cpu_reset_interrupt(env, CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x00c0:
|
||||
/* MPD: Memory Presence Detect Register. */
|
||||
break;
|
||||
|
||||
case 0x0100: /* AAR0 */
|
||||
case 0x0140: /* AAR1 */
|
||||
case 0x0180: /* AAR2 */
|
||||
case 0x01c0: /* AAR3 */
|
||||
/* AAR: Array Address Register. */
|
||||
/* All sorts of information about DRAM. */
|
||||
break;
|
||||
|
||||
case 0x0200: /* DIM0 */
|
||||
/* DIM: Device Interrupt Mask Register, CPU0. */
|
||||
s->cchip.dim[0] = val;
|
||||
cpu_irq_change(s->cchip.cpu[0], val & s->cchip.drir);
|
||||
break;
|
||||
case 0x0240: /* DIM1 */
|
||||
/* DIM: Device Interrupt Mask Register, CPU1. */
|
||||
s->cchip.dim[0] = val;
|
||||
cpu_irq_change(s->cchip.cpu[1], val & s->cchip.drir);
|
||||
break;
|
||||
|
||||
case 0x0280: /* DIR0 (RO) */
|
||||
case 0x02c0: /* DIR1 (RO) */
|
||||
case 0x0300: /* DRIR (RO) */
|
||||
break;
|
||||
|
||||
case 0x0340:
|
||||
/* PRBEN: Probe Enable Register. */
|
||||
break;
|
||||
|
||||
case 0x0380: /* IIC0 */
|
||||
s->cchip.iic[0] = val & 0xffffff;
|
||||
break;
|
||||
case 0x03c0: /* IIC1 */
|
||||
s->cchip.iic[1] = val & 0xffffff;
|
||||
break;
|
||||
|
||||
case 0x0400: /* MPR0 */
|
||||
case 0x0440: /* MPR1 */
|
||||
case 0x0480: /* MPR2 */
|
||||
case 0x04c0: /* MPR3 */
|
||||
/* MPR: Memory Programming Register. */
|
||||
break;
|
||||
|
||||
case 0x0580:
|
||||
/* TTR: TIGbus Timing Register. */
|
||||
/* All sorts of stuff related to interrupt delivery timings. */
|
||||
break;
|
||||
case 0x05c0:
|
||||
/* TDR: TIGbug Device Timing Register. */
|
||||
break;
|
||||
|
||||
case 0x0600:
|
||||
/* DIM2: Device Interrupt Mask Register, CPU2. */
|
||||
s->cchip.dim[2] = val;
|
||||
cpu_irq_change(s->cchip.cpu[2], val & s->cchip.drir);
|
||||
break;
|
||||
case 0x0640:
|
||||
/* DIM3: Device Interrupt Mask Register, CPU3. */
|
||||
s->cchip.dim[3] = val;
|
||||
cpu_irq_change(s->cchip.cpu[3], val & s->cchip.drir);
|
||||
break;
|
||||
|
||||
case 0x0680: /* DIR2 (RO) */
|
||||
case 0x06c0: /* DIR3 (RO) */
|
||||
break;
|
||||
|
||||
case 0x0700: /* IIC2 */
|
||||
s->cchip.iic[2] = val & 0xffffff;
|
||||
break;
|
||||
case 0x0740: /* IIC3 */
|
||||
s->cchip.iic[3] = val & 0xffffff;
|
||||
break;
|
||||
|
||||
case 0x0780:
|
||||
/* PWR: Power Management Control. */
|
||||
break;
|
||||
|
||||
case 0x0c00: /* CMONCTLA */
|
||||
case 0x0c40: /* CMONCTLB */
|
||||
case 0x0c80: /* CMONCNT01 */
|
||||
case 0x0cc0: /* CMONCNT23 */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void dchip_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
/* Skip this. It's all related to DRAM timing and setup. */
|
||||
}
|
||||
|
||||
static void pchip_write(void *opaque, target_phys_addr_t addr,
|
||||
uint64_t v32, unsigned size)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t val, oldval;
|
||||
|
||||
if (addr & 4) {
|
||||
val = v32 << 32 | s->latch_tmp;
|
||||
addr ^= 4;
|
||||
} else {
|
||||
s->latch_tmp = v32;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case 0x0000:
|
||||
/* WSBA0: Window Space Base Address Register. */
|
||||
s->pchip.win[0].base_addr = val;
|
||||
break;
|
||||
case 0x0040:
|
||||
/* WSBA1 */
|
||||
s->pchip.win[1].base_addr = val;
|
||||
break;
|
||||
case 0x0080:
|
||||
/* WSBA2 */
|
||||
s->pchip.win[2].base_addr = val;
|
||||
break;
|
||||
case 0x00c0:
|
||||
/* WSBA3 */
|
||||
s->pchip.win[3].base_addr = val;
|
||||
break;
|
||||
|
||||
case 0x0100:
|
||||
/* WSM0: Window Space Mask Register. */
|
||||
s->pchip.win[0].mask = val;
|
||||
break;
|
||||
case 0x0140:
|
||||
/* WSM1 */
|
||||
s->pchip.win[1].mask = val;
|
||||
break;
|
||||
case 0x0180:
|
||||
/* WSM2 */
|
||||
s->pchip.win[2].mask = val;
|
||||
break;
|
||||
case 0x01c0:
|
||||
/* WSM3 */
|
||||
s->pchip.win[3].mask = val;
|
||||
break;
|
||||
|
||||
case 0x0200:
|
||||
/* TBA0: Translated Base Address Register. */
|
||||
s->pchip.win[0].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
case 0x0240:
|
||||
/* TBA1 */
|
||||
s->pchip.win[1].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
case 0x0280:
|
||||
/* TBA2 */
|
||||
s->pchip.win[2].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
case 0x02c0:
|
||||
/* TBA3 */
|
||||
s->pchip.win[3].translated_base_pfn = val >> 10;
|
||||
break;
|
||||
|
||||
case 0x0300:
|
||||
/* PCTL: Pchip Control Register. */
|
||||
oldval = s->pchip.ctl;
|
||||
oldval &= ~0x00001cff0fc7ffull; /* RW fields */
|
||||
oldval |= val & 0x00001cff0fc7ffull;
|
||||
|
||||
s->pchip.ctl = oldval;
|
||||
break;
|
||||
|
||||
case 0x0340:
|
||||
/* PLAT: Pchip Master Latency Register. */
|
||||
break;
|
||||
case 0x03c0:
|
||||
/* PERROR: Pchip Error Register. */
|
||||
break;
|
||||
case 0x0400:
|
||||
/* PERRMASK: Pchip Error Mask Register. */
|
||||
break;
|
||||
case 0x0440:
|
||||
/* PERRSET: Pchip Error Set Register. */
|
||||
break;
|
||||
|
||||
case 0x0480:
|
||||
/* TLBIV: Translation Buffer Invalidate Virtual Register. */
|
||||
break;
|
||||
|
||||
case 0x04c0:
|
||||
/* TLBIA: Translation Buffer Invalidate All Register (WO). */
|
||||
break;
|
||||
|
||||
case 0x0500:
|
||||
/* PMONCTL */
|
||||
case 0x0540:
|
||||
/* PMONCNT */
|
||||
case 0x0800:
|
||||
/* SPRST */
|
||||
break;
|
||||
|
||||
default:
|
||||
cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps cchip_ops = {
|
||||
.read = cchip_read,
|
||||
.write = cchip_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4, /* ??? Should be 8. */
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps dchip_ops = {
|
||||
.read = dchip_read,
|
||||
.write = dchip_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4, /* ??? Should be 8. */
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static const MemoryRegionOps pchip_ops = {
|
||||
.read = pchip_read,
|
||||
.write = pchip_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4, /* ??? Should be 8. */
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void typhoon_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
uint64_t drir;
|
||||
int i;
|
||||
|
||||
/* Set/Reset the bit in CCHIP.DRIR based on IRQ+LEVEL. */
|
||||
drir = s->cchip.drir;
|
||||
if (level) {
|
||||
drir |= 1ull << irq;
|
||||
} else {
|
||||
drir &= ~(1ull << irq);
|
||||
}
|
||||
s->cchip.drir = drir;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
cpu_irq_change(s->cchip.cpu[i], s->cchip.dim[i] & drir);
|
||||
}
|
||||
}
|
||||
|
||||
static void typhoon_set_isa_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
typhoon_set_irq(opaque, 55, level);
|
||||
}
|
||||
|
||||
static void typhoon_set_timer_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
TyphoonState *s = opaque;
|
||||
int i;
|
||||
|
||||
/* Thankfully, the mc146818rtc code doesn't track the IRQ state,
|
||||
and so we don't have to worry about missing interrupts just
|
||||
because we never actually ACK the interrupt. Just ignore any
|
||||
case of the interrupt level going low. */
|
||||
if (level == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Deliver the interrupt to each CPU, considering each CPU's IIC. */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
CPUState *env = s->cchip.cpu[i];
|
||||
if (env) {
|
||||
uint32_t iic = s->cchip.iic[i];
|
||||
|
||||
/* ??? The verbage in Section 10.2.2.10 isn't 100% clear.
|
||||
Bit 24 is the OverFlow bit, RO, and set when the count
|
||||
decrements past 0. When is OF cleared? My guess is that
|
||||
OF is actually cleared when the IIC is written, and that
|
||||
the ICNT field always decrements. At least, that's an
|
||||
interpretation that makes sense, and "allows the CPU to
|
||||
determine exactly how mant interval timer ticks were
|
||||
skipped". At least within the next 4M ticks... */
|
||||
|
||||
iic = ((iic - 1) & 0x1ffffff) | (iic & 0x1000000);
|
||||
s->cchip.iic[i] = iic;
|
||||
|
||||
if (iic & 0x1000000) {
|
||||
/* Set the ITI bit for this cpu. */
|
||||
s->cchip.misc |= 1 << (i + 4);
|
||||
/* And signal the interrupt. */
|
||||
cpu_interrupt(env, CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void typhoon_alarm_timer(void *opaque)
|
||||
{
|
||||
TyphoonState *s = (TyphoonState *)((uintptr_t)opaque & ~3);
|
||||
int cpu = (uintptr_t)opaque & 3;
|
||||
|
||||
/* Set the ITI bit for this cpu. */
|
||||
s->cchip.misc |= 1 << (cpu + 4);
|
||||
cpu_interrupt(s->cchip.cpu[cpu], CPU_INTERRUPT_TIMER);
|
||||
}
|
||||
|
||||
PCIBus *typhoon_init(ram_addr_t ram_size, qemu_irq *p_rtc_irq,
|
||||
CPUState *cpus[4], pci_map_irq_fn sys_map_irq)
|
||||
{
|
||||
const uint64_t MB = 1024 * 1024;
|
||||
const uint64_t GB = 1024 * MB;
|
||||
MemoryRegion *addr_space = get_system_memory();
|
||||
MemoryRegion *addr_space_io = get_system_io();
|
||||
DeviceState *dev;
|
||||
PCIHostState *p;
|
||||
TyphoonState *s;
|
||||
PCIBus *b;
|
||||
int i;
|
||||
|
||||
dev = qdev_create(NULL, "typhoon-pcihost");
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
p = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev));
|
||||
s = container_of(p, TyphoonState, host);
|
||||
|
||||
/* Remember the CPUs so that we can deliver interrupts to them. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
CPUState *env = cpus[i];
|
||||
s->cchip.cpu[i] = env;
|
||||
if (env) {
|
||||
env->alarm_timer = qemu_new_timer_ns(rtc_clock,
|
||||
typhoon_alarm_timer,
|
||||
(void *)((uintptr_t)s + i));
|
||||
}
|
||||
}
|
||||
|
||||
*p_rtc_irq = *qemu_allocate_irqs(typhoon_set_timer_irq, s, 1);
|
||||
|
||||
/* Main memory region, 0x00.0000.0000. Real hardware supports 32GB,
|
||||
but the address space hole reserved at this point is 8TB. */
|
||||
memory_region_init_ram(&s->ram_region, NULL, "ram", ram_size);
|
||||
memory_region_add_subregion(addr_space, 0, &s->ram_region);
|
||||
|
||||
/* TIGbus, 0x801.0000.0000, 1GB. */
|
||||
/* ??? The TIGbus is used for delivering interrupts, and access to
|
||||
the flash ROM. I'm not sure that we need to implement it at all. */
|
||||
|
||||
/* Pchip0 CSRs, 0x801.8000.0000, 256MB. */
|
||||
memory_region_init_io(&s->pchip.region, &pchip_ops, s, "pchip0", 256*MB);
|
||||
memory_region_add_subregion(addr_space, 0x80180000000ULL,
|
||||
&s->pchip.region);
|
||||
|
||||
/* Cchip CSRs, 0x801.A000.0000, 256MB. */
|
||||
memory_region_init_io(&s->cchip.region, &cchip_ops, s, "cchip0", 256*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801a0000000ULL,
|
||||
&s->cchip.region);
|
||||
|
||||
/* Dchip CSRs, 0x801.B000.0000, 256MB. */
|
||||
memory_region_init_io(&s->dchip_region, &dchip_ops, s, "dchip0", 256*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801b0000000ULL,
|
||||
&s->dchip_region);
|
||||
|
||||
/* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */
|
||||
memory_region_init(&s->pchip.reg_mem, "pci0-mem", 4*GB);
|
||||
memory_region_add_subregion(addr_space, 0x80000000000ULL,
|
||||
&s->pchip.reg_mem);
|
||||
|
||||
/* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */
|
||||
/* ??? Ideally we drop the "system" i/o space on the floor and give the
|
||||
PCI subsystem the full address space reserved by the chipset.
|
||||
We can't do that until the MEM and IO paths in memory.c are unified. */
|
||||
memory_region_init_io(&s->pchip.reg_io, &alpha_pci_bw_io_ops, NULL,
|
||||
"pci0-io", 32*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801fc000000ULL,
|
||||
&s->pchip.reg_io);
|
||||
|
||||
b = pci_register_bus(&s->host.busdev.qdev, "pci",
|
||||
typhoon_set_irq, sys_map_irq, s,
|
||||
&s->pchip.reg_mem, addr_space_io, 0, 64);
|
||||
s->host.bus = b;
|
||||
|
||||
/* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */
|
||||
memory_region_init_io(&s->pchip.reg_iack, &alpha_pci_iack_ops, b,
|
||||
"pci0-iack", 64*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801f8000000ULL,
|
||||
&s->pchip.reg_iack);
|
||||
|
||||
/* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */
|
||||
memory_region_init_io(&s->pchip.reg_conf, &alpha_pci_conf1_ops, b,
|
||||
"pci0-conf", 16*MB);
|
||||
memory_region_add_subregion(addr_space, 0x801fe000000ULL,
|
||||
&s->pchip.reg_conf);
|
||||
|
||||
/* For the record, these are the mappings for the second PCI bus.
|
||||
We can get away with not implementing them because we indicate
|
||||
via the Cchip.CSC<PIP> bit that Pchip1 is not present. */
|
||||
/* Pchip1 PCI memory, 0x802.0000.0000, 4GB. */
|
||||
/* Pchip1 CSRs, 0x802.8000.0000, 256MB. */
|
||||
/* Pchip1 PCI special/interrupt acknowledge, 0x802.F800.0000, 64MB. */
|
||||
/* Pchip1 PCI I/O, 0x802.FC00.0000, 32MB. */
|
||||
/* Pchip1 PCI configuration, 0x802.FE00.0000, 16MB. */
|
||||
|
||||
/* Init the ISA bus. */
|
||||
/* ??? Technically there should be a cy82c693ub pci-isa bridge. */
|
||||
{
|
||||
qemu_irq isa_pci_irq, *isa_irqs;
|
||||
|
||||
isa_bus_new(NULL, addr_space_io);
|
||||
isa_pci_irq = *qemu_allocate_irqs(typhoon_set_isa_irq, s, 1);
|
||||
isa_irqs = i8259_init(isa_pci_irq);
|
||||
isa_bus_irqs(isa_irqs);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static int typhoon_pcihost_init(SysBusDevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SysBusDeviceInfo typhoon_pcihost_info = {
|
||||
.init = typhoon_pcihost_init,
|
||||
.qdev.name = "typhoon-pcihost",
|
||||
.qdev.size = sizeof(TyphoonState),
|
||||
.qdev.no_user = 1
|
||||
};
|
||||
|
||||
static void typhoon_register(void)
|
||||
{
|
||||
sysbus_register_withprop(&typhoon_pcihost_info);
|
||||
}
|
||||
device_init(typhoon_register);
|
29
hw/cuda.c
29
hw/cuda.c
@ -24,6 +24,7 @@
|
||||
*/
|
||||
#include "hw.h"
|
||||
#include "ppc_mac.h"
|
||||
#include "adb.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "sysemu.h"
|
||||
|
||||
@ -633,16 +634,20 @@ static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CPUWriteMemoryFunc * const cuda_write[] = {
|
||||
&cuda_writeb,
|
||||
&cuda_writew,
|
||||
&cuda_writel,
|
||||
};
|
||||
|
||||
static CPUReadMemoryFunc * const cuda_read[] = {
|
||||
&cuda_readb,
|
||||
&cuda_readw,
|
||||
&cuda_readl,
|
||||
static MemoryRegionOps cuda_ops = {
|
||||
.old_mmio = {
|
||||
.write = {
|
||||
cuda_writeb,
|
||||
cuda_writew,
|
||||
cuda_writel,
|
||||
},
|
||||
.read = {
|
||||
cuda_readb,
|
||||
cuda_readw,
|
||||
cuda_readl,
|
||||
},
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static bool cuda_timer_exist(void *opaque, int version_id)
|
||||
@ -739,8 +744,8 @@ void cuda_init (MemoryRegion **cuda_mem, qemu_irq irq)
|
||||
s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
|
||||
|
||||
s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s);
|
||||
cpu_register_io_memory(cuda_read, cuda_write, s,
|
||||
DEVICE_NATIVE_ENDIAN);
|
||||
memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000);
|
||||
|
||||
*cuda_mem = &s->mem;
|
||||
vmstate_register(NULL, -1, &vmstate_cuda, s);
|
||||
qemu_register_reset(cuda_reset, s);
|
||||
|
@ -604,12 +604,10 @@ dma_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||
static void
|
||||
dma_update_state(struct fs_dma_ctrl *ctrl, int c)
|
||||
{
|
||||
if ((ctrl->channels[c].regs[RW_CFG] & 1) != 3) {
|
||||
if (ctrl->channels[c].regs[RW_CFG] & 2)
|
||||
ctrl->channels[c].state = STOPPED;
|
||||
if (!(ctrl->channels[c].regs[RW_CFG] & 1))
|
||||
ctrl->channels[c].state = RST;
|
||||
}
|
||||
if (ctrl->channels[c].regs[RW_CFG] & 2)
|
||||
ctrl->channels[c].state = STOPPED;
|
||||
if (!(ctrl->channels[c].regs[RW_CFG] & 1))
|
||||
ctrl->channels[c].state = RST;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -126,7 +126,7 @@ static uint64_t pic_read(void *opaque, target_phys_addr_t addr,
|
||||
static const MemoryRegionOps heathrow_pic_ops = {
|
||||
.read = pic_read,
|
||||
.write = pic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void heathrow_pic_set_irq(void *opaque, int num, int level)
|
||||
|
@ -5,15 +5,14 @@
|
||||
#include "qdev-addr.h"
|
||||
|
||||
static inline DeviceState *milkymist_uart_create(target_phys_addr_t base,
|
||||
qemu_irq rx_irq, qemu_irq tx_irq)
|
||||
qemu_irq irq)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(NULL, "milkymist-uart");
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
|
||||
sysbus_connect_irq(sysbus_from_qdev(dev), 0, rx_irq);
|
||||
sysbus_connect_irq(sysbus_from_qdev(dev), 1, tx_irq);
|
||||
sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -30,21 +30,54 @@
|
||||
enum {
|
||||
R_RXTX = 0,
|
||||
R_DIV,
|
||||
R_STAT,
|
||||
R_CTRL,
|
||||
R_DBG,
|
||||
R_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
STAT_THRE = (1<<0),
|
||||
STAT_RX_EVT = (1<<1),
|
||||
STAT_TX_EVT = (1<<2),
|
||||
};
|
||||
|
||||
enum {
|
||||
CTRL_RX_IRQ_EN = (1<<0),
|
||||
CTRL_TX_IRQ_EN = (1<<1),
|
||||
CTRL_THRU_EN = (1<<2),
|
||||
};
|
||||
|
||||
enum {
|
||||
DBG_BREAK_EN = (1<<0),
|
||||
};
|
||||
|
||||
struct MilkymistUartState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion regs_region;
|
||||
CharDriverState *chr;
|
||||
|
||||
qemu_irq rx_irq;
|
||||
qemu_irq tx_irq;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t regs[R_MAX];
|
||||
};
|
||||
typedef struct MilkymistUartState MilkymistUartState;
|
||||
|
||||
static void uart_update_irq(MilkymistUartState *s)
|
||||
{
|
||||
int rx_event = s->regs[R_STAT] & STAT_RX_EVT;
|
||||
int tx_event = s->regs[R_STAT] & STAT_TX_EVT;
|
||||
int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN;
|
||||
int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN;
|
||||
|
||||
if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) {
|
||||
trace_milkymist_uart_raise_irq();
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
trace_milkymist_uart_lower_irq();
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
|
||||
unsigned size)
|
||||
{
|
||||
@ -54,7 +87,12 @@ static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
|
||||
addr >>= 2;
|
||||
switch (addr) {
|
||||
case R_RXTX:
|
||||
r = s->regs[addr];
|
||||
break;
|
||||
case R_DIV:
|
||||
case R_STAT:
|
||||
case R_CTRL:
|
||||
case R_DBG:
|
||||
r = s->regs[addr];
|
||||
break;
|
||||
|
||||
@ -83,18 +121,26 @@ static void uart_write(void *opaque, target_phys_addr_t addr, uint64_t value,
|
||||
if (s->chr) {
|
||||
qemu_chr_fe_write(s->chr, &ch, 1);
|
||||
}
|
||||
trace_milkymist_uart_pulse_irq_tx();
|
||||
qemu_irq_pulse(s->tx_irq);
|
||||
s->regs[R_STAT] |= STAT_TX_EVT;
|
||||
break;
|
||||
case R_DIV:
|
||||
case R_CTRL:
|
||||
case R_DBG:
|
||||
s->regs[addr] = value;
|
||||
break;
|
||||
|
||||
case R_STAT:
|
||||
/* write one to clear bits */
|
||||
s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT));
|
||||
break;
|
||||
|
||||
default:
|
||||
error_report("milkymist_uart: write access to unknown register 0x"
|
||||
TARGET_FMT_plx, addr << 2);
|
||||
break;
|
||||
}
|
||||
|
||||
uart_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps uart_mmio_ops = {
|
||||
@ -111,14 +157,19 @@ static void uart_rx(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
MilkymistUartState *s = opaque;
|
||||
|
||||
assert(!(s->regs[R_STAT] & STAT_RX_EVT));
|
||||
|
||||
s->regs[R_STAT] |= STAT_RX_EVT;
|
||||
s->regs[R_RXTX] = *buf;
|
||||
trace_milkymist_uart_pulse_irq_rx();
|
||||
qemu_irq_pulse(s->rx_irq);
|
||||
|
||||
uart_update_irq(s);
|
||||
}
|
||||
|
||||
static int uart_can_rx(void *opaque)
|
||||
{
|
||||
return 1;
|
||||
MilkymistUartState *s = opaque;
|
||||
|
||||
return !(s->regs[R_STAT] & STAT_RX_EVT);
|
||||
}
|
||||
|
||||
static void uart_event(void *opaque, int event)
|
||||
@ -133,14 +184,16 @@ static void milkymist_uart_reset(DeviceState *d)
|
||||
for (i = 0; i < R_MAX; i++) {
|
||||
s->regs[i] = 0;
|
||||
}
|
||||
|
||||
/* THRE is always set */
|
||||
s->regs[R_STAT] = STAT_THRE;
|
||||
}
|
||||
|
||||
static int milkymist_uart_init(SysBusDevice *dev)
|
||||
{
|
||||
MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev);
|
||||
|
||||
sysbus_init_irq(dev, &s->rx_irq);
|
||||
sysbus_init_irq(dev, &s->tx_irq);
|
||||
sysbus_init_irq(dev, &s->irq);
|
||||
|
||||
memory_region_init_io(&s->regs_region, &uart_mmio_ops, s,
|
||||
"milkymist-uart", R_MAX * 4);
|
||||
|
@ -146,17 +146,17 @@ milkymist_init(ram_addr_t ram_size_not_used,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
milkymist_uart_create(0x60000000, irq[0], irq[1]);
|
||||
milkymist_sysctl_create(0x60001000, irq[2], irq[3], irq[4],
|
||||
milkymist_uart_create(0x60000000, irq[0]);
|
||||
milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3],
|
||||
80000000, 0x10014d31, 0x0000041f, 0x00000001);
|
||||
milkymist_hpdmc_create(0x60002000);
|
||||
milkymist_vgafb_create(0x60003000, 0x40000000, 0x0fffffff);
|
||||
milkymist_memcard_create(0x60004000);
|
||||
milkymist_ac97_create(0x60005000, irq[5], irq[6], irq[7], irq[8]);
|
||||
milkymist_pfpu_create(0x60006000, irq[9]);
|
||||
milkymist_tmu2_create(0x60007000, irq[10]);
|
||||
milkymist_minimac2_create(0x60008000, 0x30000000, irq[11], irq[12]);
|
||||
milkymist_softusb_create(0x6000f000, irq[17],
|
||||
milkymist_ac97_create(0x60005000, irq[4], irq[5], irq[6], irq[7]);
|
||||
milkymist_pfpu_create(0x60006000, irq[8]);
|
||||
milkymist_tmu2_create(0x60007000, irq[9]);
|
||||
milkymist_minimac2_create(0x60008000, 0x30000000, irq[10], irq[11]);
|
||||
milkymist_softusb_create(0x6000f000, irq[15],
|
||||
0x20000000, 0x1000, 0x20020000, 0x2000);
|
||||
|
||||
/* make sure juart isn't the first chardev */
|
||||
|
291
hw/openpic.c
291
hw/openpic.c
@ -2,6 +2,7 @@
|
||||
* OpenPIC emulation
|
||||
*
|
||||
* Copyright (c) 2004 Jocelyn Mayer
|
||||
* 2011 Alexander Graf
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -56,13 +57,13 @@
|
||||
#define MAX_MBX 4
|
||||
#define MAX_TMR 4
|
||||
#define VECTOR_BITS 8
|
||||
#define MAX_IPI 0
|
||||
#define MAX_IPI 4
|
||||
|
||||
#define VID (0x00000000)
|
||||
|
||||
#elif defined(USE_MPCxxx)
|
||||
|
||||
#define MAX_CPU 2
|
||||
#define MAX_CPU 15
|
||||
#define MAX_IRQ 128
|
||||
#define MAX_DBL 0
|
||||
#define MAX_MBX 0
|
||||
@ -127,14 +128,14 @@ enum {
|
||||
#define MPIC_MSI_REG_START 0x11C00
|
||||
#define MPIC_MSI_REG_SIZE 0x100
|
||||
#define MPIC_CPU_REG_START 0x20000
|
||||
#define MPIC_CPU_REG_SIZE 0x100
|
||||
#define MPIC_CPU_REG_SIZE 0x100 + ((MAX_CPU - 1) * 0x1000)
|
||||
|
||||
enum mpic_ide_bits {
|
||||
IDR_EP = 0,
|
||||
IDR_CI0 = 1,
|
||||
IDR_CI1 = 2,
|
||||
IDR_P1 = 30,
|
||||
IDR_P0 = 31,
|
||||
IDR_EP = 31,
|
||||
IDR_CI0 = 30,
|
||||
IDR_CI1 = 29,
|
||||
IDR_P1 = 1,
|
||||
IDR_P0 = 0,
|
||||
};
|
||||
|
||||
#else
|
||||
@ -161,6 +162,16 @@ static inline int test_bit (uint32_t *field, int bit)
|
||||
return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
|
||||
}
|
||||
|
||||
static int get_current_cpu(void)
|
||||
{
|
||||
return cpu_single_env->cpu_index;
|
||||
}
|
||||
|
||||
static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr,
|
||||
int idx);
|
||||
static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val, int idx);
|
||||
|
||||
enum {
|
||||
IRQ_EXTERNAL = 0x01,
|
||||
IRQ_INTERNAL = 0x02,
|
||||
@ -465,46 +476,35 @@ static void openpic_reset (void *opaque)
|
||||
opp->glbc = 0x00000000;
|
||||
}
|
||||
|
||||
static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg)
|
||||
static inline uint32_t read_IRQreg_ide(openpic_t *opp, int n_IRQ)
|
||||
{
|
||||
uint32_t retval;
|
||||
|
||||
switch (reg) {
|
||||
case IRQ_IPVP:
|
||||
retval = opp->src[n_IRQ].ipvp;
|
||||
break;
|
||||
case IRQ_IDE:
|
||||
retval = opp->src[n_IRQ].ide;
|
||||
break;
|
||||
}
|
||||
|
||||
return retval;
|
||||
return opp->src[n_IRQ].ide;
|
||||
}
|
||||
|
||||
static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
|
||||
uint32_t reg, uint32_t val)
|
||||
static inline uint32_t read_IRQreg_ipvp(openpic_t *opp, int n_IRQ)
|
||||
{
|
||||
return opp->src[n_IRQ].ipvp;
|
||||
}
|
||||
|
||||
static inline void write_IRQreg_ide(openpic_t *opp, int n_IRQ, uint32_t val)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
switch (reg) {
|
||||
case IRQ_IPVP:
|
||||
/* NOTE: not fully accurate for special IRQs, but simple and
|
||||
sufficient */
|
||||
/* ACTIVITY bit is read-only */
|
||||
opp->src[n_IRQ].ipvp =
|
||||
(opp->src[n_IRQ].ipvp & 0x40000000) |
|
||||
(val & 0x800F00FF);
|
||||
openpic_update_irq(opp, n_IRQ);
|
||||
DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n",
|
||||
n_IRQ, val, opp->src[n_IRQ].ipvp);
|
||||
break;
|
||||
case IRQ_IDE:
|
||||
tmp = val & 0xC0000000;
|
||||
tmp |= val & ((1 << MAX_CPU) - 1);
|
||||
opp->src[n_IRQ].ide = tmp;
|
||||
DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
|
||||
break;
|
||||
}
|
||||
tmp = val & 0xC0000000;
|
||||
tmp |= val & ((1ULL << MAX_CPU) - 1);
|
||||
opp->src[n_IRQ].ide = tmp;
|
||||
DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
|
||||
}
|
||||
|
||||
static inline void write_IRQreg_ipvp(openpic_t *opp, int n_IRQ, uint32_t val)
|
||||
{
|
||||
/* NOTE: not fully accurate for special IRQs, but simple and sufficient */
|
||||
/* ACTIVITY bit is read-only */
|
||||
opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000)
|
||||
| (val & 0x800F00FF);
|
||||
openpic_update_irq(opp, n_IRQ);
|
||||
DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val,
|
||||
opp->src[n_IRQ].ipvp);
|
||||
}
|
||||
|
||||
#if 0 // Code provision for Intel model
|
||||
@ -516,10 +516,10 @@ static uint32_t read_doorbell_register (openpic_t *opp,
|
||||
|
||||
switch (offset) {
|
||||
case DBL_IPVP_OFFSET:
|
||||
retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl);
|
||||
break;
|
||||
case DBL_IDE_OFFSET:
|
||||
retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(opp, IRQ_DBL0 + n_dbl);
|
||||
break;
|
||||
case DBL_DMR_OFFSET:
|
||||
retval = opp->doorbells[n_dbl].dmr;
|
||||
@ -534,10 +534,10 @@ static void write_doorbell_register (penpic_t *opp, int n_dbl,
|
||||
{
|
||||
switch (offset) {
|
||||
case DBL_IVPR_OFFSET:
|
||||
write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value);
|
||||
write_IRQreg_ipvp(opp, IRQ_DBL0 + n_dbl, value);
|
||||
break;
|
||||
case DBL_IDE_OFFSET:
|
||||
write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value);
|
||||
write_IRQreg_ide(opp, IRQ_DBL0 + n_dbl, value);
|
||||
break;
|
||||
case DBL_DMR_OFFSET:
|
||||
opp->doorbells[n_dbl].dmr = value;
|
||||
@ -557,10 +557,10 @@ static uint32_t read_mailbox_register (openpic_t *opp,
|
||||
retval = opp->mailboxes[n_mbx].mbr;
|
||||
break;
|
||||
case MBX_IVPR_OFFSET:
|
||||
retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx);
|
||||
break;
|
||||
case MBX_DMR_OFFSET:
|
||||
retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(opp, IRQ_MBX0 + n_mbx);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -575,10 +575,10 @@ static void write_mailbox_register (openpic_t *opp, int n_mbx,
|
||||
opp->mailboxes[n_mbx].mbr = value;
|
||||
break;
|
||||
case MBX_IVPR_OFFSET:
|
||||
write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value);
|
||||
write_IRQreg_ipvp(opp, IRQ_MBX0 + n_mbx, value);
|
||||
break;
|
||||
case MBX_DMR_OFFSET:
|
||||
write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value);
|
||||
write_IRQreg_ide(opp, IRQ_MBX0 + n_mbx, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -594,18 +594,27 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v
|
||||
DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
|
||||
if (addr & 0xF)
|
||||
return;
|
||||
addr &= 0xFF;
|
||||
switch (addr) {
|
||||
case 0x00: /* FREP */
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xA0:
|
||||
case 0xB0:
|
||||
openpic_cpu_write_internal(opp, addr, val, get_current_cpu());
|
||||
break;
|
||||
case 0x20: /* GLBC */
|
||||
case 0x1000: /* FREP */
|
||||
break;
|
||||
case 0x1020: /* GLBC */
|
||||
if (val & 0x80000000 && opp->reset)
|
||||
opp->reset(opp);
|
||||
opp->glbc = val & ~0x80000000;
|
||||
break;
|
||||
case 0x80: /* VENI */
|
||||
case 0x1080: /* VENI */
|
||||
break;
|
||||
case 0x90: /* PINT */
|
||||
case 0x1090: /* PINT */
|
||||
for (idx = 0; idx < opp->nb_cpus; idx++) {
|
||||
if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) {
|
||||
DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx);
|
||||
@ -619,22 +628,20 @@ static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t v
|
||||
}
|
||||
opp->pint = val;
|
||||
break;
|
||||
#if MAX_IPI > 0
|
||||
case 0xA0: /* IPI_IPVP */
|
||||
case 0xB0:
|
||||
case 0xC0:
|
||||
case 0xD0:
|
||||
case 0x10A0: /* IPI_IPVP */
|
||||
case 0x10B0:
|
||||
case 0x10C0:
|
||||
case 0x10D0:
|
||||
{
|
||||
int idx;
|
||||
idx = (addr - 0xA0) >> 4;
|
||||
write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP, val);
|
||||
idx = (addr - 0x10A0) >> 4;
|
||||
write_IRQreg_ipvp(opp, opp->irq_ipi0 + idx, val);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 0xE0: /* SPVE */
|
||||
case 0x10E0: /* SPVE */
|
||||
opp->spve = val & 0x000000FF;
|
||||
break;
|
||||
case 0xF0: /* TIFR */
|
||||
case 0x10F0: /* TIFR */
|
||||
opp->tifr = val;
|
||||
break;
|
||||
default:
|
||||
@ -651,36 +658,43 @@ static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr)
|
||||
retval = 0xFFFFFFFF;
|
||||
if (addr & 0xF)
|
||||
return retval;
|
||||
addr &= 0xFF;
|
||||
switch (addr) {
|
||||
case 0x00: /* FREP */
|
||||
case 0x1000: /* FREP */
|
||||
retval = opp->frep;
|
||||
break;
|
||||
case 0x20: /* GLBC */
|
||||
case 0x1020: /* GLBC */
|
||||
retval = opp->glbc;
|
||||
break;
|
||||
case 0x80: /* VENI */
|
||||
case 0x1080: /* VENI */
|
||||
retval = opp->veni;
|
||||
break;
|
||||
case 0x90: /* PINT */
|
||||
case 0x1090: /* PINT */
|
||||
retval = 0x00000000;
|
||||
break;
|
||||
#if MAX_IPI > 0
|
||||
case 0xA0: /* IPI_IPVP */
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xA0:
|
||||
case 0xB0:
|
||||
case 0xC0:
|
||||
case 0xD0:
|
||||
retval = openpic_cpu_read_internal(opp, addr, get_current_cpu());
|
||||
break;
|
||||
case 0x10A0: /* IPI_IPVP */
|
||||
case 0x10B0:
|
||||
case 0x10C0:
|
||||
case 0x10D0:
|
||||
{
|
||||
int idx;
|
||||
idx = (addr - 0xA0) >> 4;
|
||||
retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP);
|
||||
idx = (addr - 0x10A0) >> 4;
|
||||
retval = read_IRQreg_ipvp(opp, opp->irq_ipi0 + idx);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 0xE0: /* SPVE */
|
||||
case 0x10E0: /* SPVE */
|
||||
retval = opp->spve;
|
||||
break;
|
||||
case 0xF0: /* TIFR */
|
||||
case 0x10F0: /* TIFR */
|
||||
retval = opp->tifr;
|
||||
break;
|
||||
default:
|
||||
@ -714,10 +728,10 @@ static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
|
||||
opp->timers[idx].tibc = val;
|
||||
break;
|
||||
case 0x20: /* TIVP */
|
||||
write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(opp, opp->irq_tim0 + idx, val);
|
||||
break;
|
||||
case 0x30: /* TIDE */
|
||||
write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(opp, opp->irq_tim0 + idx, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -744,10 +758,10 @@ static uint32_t openpic_timer_read (void *opaque, uint32_t addr)
|
||||
retval = opp->timers[idx].tibc;
|
||||
break;
|
||||
case 0x20: /* TIPV */
|
||||
retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(opp, opp->irq_tim0 + idx);
|
||||
break;
|
||||
case 0x30: /* TIDE */
|
||||
retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(opp, opp->irq_tim0 + idx);
|
||||
break;
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
@ -767,10 +781,10 @@ static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val)
|
||||
idx = addr >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
write_IRQreg(opp, idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(opp, idx, val);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
write_IRQreg(opp, idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(opp, idx, val);
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,38 +802,40 @@ static uint32_t openpic_src_read (void *opaque, uint32_t addr)
|
||||
idx = addr >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
retval = read_IRQreg(opp, idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(opp, idx);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
retval = read_IRQreg(opp, idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(opp, idx);
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
static void openpic_cpu_write_internal(void *opaque, target_phys_addr_t addr,
|
||||
uint32_t val, int idx)
|
||||
{
|
||||
openpic_t *opp = opaque;
|
||||
IRQ_src_t *src;
|
||||
IRQ_dst_t *dst;
|
||||
int idx, s_IRQ, n_IRQ;
|
||||
int s_IRQ, n_IRQ;
|
||||
|
||||
DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
|
||||
DPRINTF("%s: cpu %d addr " TARGET_FMT_plx " <= %08x\n", __func__, idx,
|
||||
addr, val);
|
||||
if (addr & 0xF)
|
||||
return;
|
||||
addr &= 0x1FFF0;
|
||||
idx = addr / 0x1000;
|
||||
dst = &opp->dst[idx];
|
||||
addr &= 0xFF0;
|
||||
switch (addr) {
|
||||
#if MAX_IPI > 0
|
||||
case 0x40: /* PIPD */
|
||||
case 0x40: /* IPIDR */
|
||||
case 0x50:
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
idx = (addr - 0x40) >> 4;
|
||||
write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, val);
|
||||
/* we use IDE as mask which CPUs to deliver the IPI to still. */
|
||||
write_IRQreg_ide(opp, opp->irq_ipi0 + idx,
|
||||
opp->src[opp->irq_ipi0 + idx].ide | val);
|
||||
openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
|
||||
openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
|
||||
break;
|
||||
@ -856,20 +872,24 @@ static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t v
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
|
||||
static void openpic_cpu_write(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
openpic_cpu_write_internal(opaque, addr, val, (addr & 0x1f000) >> 12);
|
||||
}
|
||||
|
||||
static uint32_t openpic_cpu_read_internal(void *opaque, target_phys_addr_t addr,
|
||||
int idx)
|
||||
{
|
||||
openpic_t *opp = opaque;
|
||||
IRQ_src_t *src;
|
||||
IRQ_dst_t *dst;
|
||||
uint32_t retval;
|
||||
int idx, n_IRQ;
|
||||
int n_IRQ;
|
||||
|
||||
DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
|
||||
DPRINTF("%s: cpu %d addr " TARGET_FMT_plx "\n", __func__, idx, addr);
|
||||
retval = 0xFFFFFFFF;
|
||||
if (addr & 0xF)
|
||||
return retval;
|
||||
addr &= 0x1FFF0;
|
||||
idx = addr / 0x1000;
|
||||
dst = &opp->dst[idx];
|
||||
addr &= 0xFF0;
|
||||
switch (addr) {
|
||||
@ -909,18 +929,22 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
|
||||
reset_bit(&src->ipvp, IPVP_ACTIVITY);
|
||||
src->pending = 0;
|
||||
}
|
||||
|
||||
if ((n_IRQ >= opp->irq_ipi0) && (n_IRQ < (opp->irq_ipi0 + MAX_IPI))) {
|
||||
src->ide &= ~(1 << idx);
|
||||
if (src->ide && !test_bit(&src->ipvp, IPVP_SENSE)) {
|
||||
/* trigger on CPUs that didn't know about it yet */
|
||||
openpic_set_irq(opp, n_IRQ, 1);
|
||||
openpic_set_irq(opp, n_IRQ, 0);
|
||||
/* if all CPUs knew about it, set active bit again */
|
||||
set_bit(&src->ipvp, IPVP_ACTIVITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xB0: /* PEOI */
|
||||
retval = 0;
|
||||
break;
|
||||
#if MAX_IPI > 0
|
||||
case 0x40: /* IDE */
|
||||
case 0x50:
|
||||
idx = (addr - 0x40) >> 4;
|
||||
retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -929,6 +953,11 @@ static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static uint32_t openpic_cpu_read(void *opaque, target_phys_addr_t addr)
|
||||
{
|
||||
return openpic_cpu_read_internal(opaque, addr, (addr & 0x1f000) >> 12);
|
||||
}
|
||||
|
||||
static void openpic_buggy_write (void *opaque,
|
||||
target_phys_addr_t addr, uint32_t val)
|
||||
{
|
||||
@ -1247,7 +1276,7 @@ static void mpic_reset (void *opaque)
|
||||
|
||||
mpp->glbc = 0x80000000;
|
||||
/* Initialise controller registers */
|
||||
mpp->frep = 0x004f0002;
|
||||
mpp->frep = 0x004f0002 | ((mpp->nb_cpus - 1) << 8);
|
||||
mpp->veni = VENI;
|
||||
mpp->pint = 0x00000000;
|
||||
mpp->spve = 0x0000FFFF;
|
||||
@ -1256,6 +1285,10 @@ static void mpic_reset (void *opaque)
|
||||
mpp->src[i].ipvp = 0x80800000;
|
||||
mpp->src[i].ide = 0x00000001;
|
||||
}
|
||||
/* Set IDE for IPIs to 0 so we don't get spurious interrupts */
|
||||
for (i = mpp->irq_ipi0; i < (mpp->irq_ipi0 + MAX_IPI); i++) {
|
||||
mpp->src[i].ide = 0;
|
||||
}
|
||||
/* Initialise IRQ destinations */
|
||||
for (i = 0; i < MAX_CPU; i++) {
|
||||
mpp->dst[i].pctp = 0x0000000F;
|
||||
@ -1296,13 +1329,13 @@ static void mpic_timer_write (void *opaque, target_phys_addr_t addr, uint32_t va
|
||||
mpp->timers[idx].tibc = val;
|
||||
break;
|
||||
case 0x20: /* GTIVPR */
|
||||
write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx, val);
|
||||
break;
|
||||
case 0x30: /* GTIDR & TFRR */
|
||||
if ((addr & 0xF0) == 0xF0)
|
||||
mpp->dst[cpu].tfrr = val;
|
||||
else
|
||||
write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1328,13 +1361,13 @@ static uint32_t mpic_timer_read (void *opaque, target_phys_addr_t addr)
|
||||
retval = mpp->timers[idx].tibc;
|
||||
break;
|
||||
case 0x20: /* TIPV */
|
||||
retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(mpp, MPIC_TMR_IRQ + idx);
|
||||
break;
|
||||
case 0x30: /* TIDR */
|
||||
if ((addr &0xF0) == 0XF0)
|
||||
retval = mpp->dst[cpu].tfrr;
|
||||
else
|
||||
retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(mpp, MPIC_TMR_IRQ + idx);
|
||||
break;
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
@ -1357,10 +1390,10 @@ static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr,
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
write_IRQreg(mpp, idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(mpp, idx, val);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
write_IRQreg(mpp, idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(mpp, idx, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1381,10 +1414,10 @@ static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr)
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(mpp, idx);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(mpp, idx);
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
}
|
||||
@ -1407,10 +1440,10 @@ static void mpic_src_int_write (void *opaque, target_phys_addr_t addr,
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
write_IRQreg(mpp, idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(mpp, idx, val);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
write_IRQreg(mpp, idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(mpp, idx, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1431,10 +1464,10 @@ static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr)
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(mpp, idx);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(mpp, idx);
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
}
|
||||
@ -1457,10 +1490,10 @@ static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr,
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
write_IRQreg(mpp, idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(mpp, idx, val);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
write_IRQreg(mpp, idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(mpp, idx, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1481,10 +1514,10 @@ static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr)
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(mpp, idx);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(mpp, idx);
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
}
|
||||
@ -1507,10 +1540,10 @@ static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr,
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
write_IRQreg(mpp, idx, IRQ_IDE, val);
|
||||
write_IRQreg_ide(mpp, idx, val);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
write_IRQreg(mpp, idx, IRQ_IPVP, val);
|
||||
write_IRQreg_ipvp(mpp, idx, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1530,10 +1563,10 @@ static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr)
|
||||
idx += (addr & 0xFFF0) >> 5;
|
||||
if (addr & 0x10) {
|
||||
/* EXDE / IFEDE / IEEDE */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IDE);
|
||||
retval = read_IRQreg_ide(mpp, idx);
|
||||
} else {
|
||||
/* EXVP / IFEVP / IEEVP */
|
||||
retval = read_IRQreg(mpp, idx, IRQ_IPVP);
|
||||
retval = read_IRQreg_ipvp(mpp, idx);
|
||||
}
|
||||
DPRINTF("%s: => %08x\n", __func__, retval);
|
||||
}
|
||||
@ -1659,10 +1692,6 @@ qemu_irq *mpic_init (MemoryRegion *address_space, target_phys_addr_t base,
|
||||
{"cpu", &mpic_cpu_ops, MPIC_CPU_REG_START, MPIC_CPU_REG_SIZE},
|
||||
};
|
||||
|
||||
/* XXX: for now, only one CPU is supported */
|
||||
if (nb_cpus != 1)
|
||||
return NULL;
|
||||
|
||||
mpp = g_malloc0(sizeof(openpic_t));
|
||||
|
||||
memory_region_init(&mpp->mem, "mpic", 0x40000);
|
||||
|
138
hw/ppc.c
138
hw/ppc.c
@ -50,7 +50,7 @@
|
||||
static void cpu_ppc_tb_stop (CPUState *env);
|
||||
static void cpu_ppc_tb_start (CPUState *env);
|
||||
|
||||
static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
|
||||
void ppc_set_irq(CPUState *env, int n_IRQ, int level)
|
||||
{
|
||||
unsigned int old_pending = env->pending_interrupts;
|
||||
|
||||
@ -423,25 +423,8 @@ void ppce500_irq_init (CPUState *env)
|
||||
}
|
||||
/*****************************************************************************/
|
||||
/* PowerPC time base and decrementer emulation */
|
||||
struct ppc_tb_t {
|
||||
/* Time base management */
|
||||
int64_t tb_offset; /* Compensation */
|
||||
int64_t atb_offset; /* Compensation */
|
||||
uint32_t tb_freq; /* TB frequency */
|
||||
/* Decrementer management */
|
||||
uint64_t decr_next; /* Tick for next decr interrupt */
|
||||
uint32_t decr_freq; /* decrementer frequency */
|
||||
struct QEMUTimer *decr_timer;
|
||||
/* Hypervisor decrementer management */
|
||||
uint64_t hdecr_next; /* Tick for next hdecr interrupt */
|
||||
struct QEMUTimer *hdecr_timer;
|
||||
uint64_t purr_load;
|
||||
uint64_t purr_start;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk,
|
||||
int64_t tb_offset)
|
||||
uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
|
||||
{
|
||||
/* TB time in tb periods */
|
||||
return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
|
||||
@ -611,10 +594,13 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
|
||||
int64_t diff;
|
||||
|
||||
diff = next - qemu_get_clock_ns(vm_clock);
|
||||
if (diff >= 0)
|
||||
if (diff >= 0) {
|
||||
decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
|
||||
else
|
||||
} else if (tb_env->flags & PPC_TIMER_BOOKE) {
|
||||
decr = 0;
|
||||
} else {
|
||||
decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
|
||||
}
|
||||
LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
|
||||
|
||||
return decr;
|
||||
@ -678,18 +664,24 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
|
||||
decr, value);
|
||||
now = qemu_get_clock_ns(vm_clock);
|
||||
next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
|
||||
if (is_excp)
|
||||
if (is_excp) {
|
||||
next += *nextp - now;
|
||||
if (next == now)
|
||||
}
|
||||
if (next == now) {
|
||||
next++;
|
||||
}
|
||||
*nextp = next;
|
||||
/* Adjust timer */
|
||||
qemu_mod_timer(timer, next);
|
||||
/* If we set a negative value and the decrementer was positive,
|
||||
* raise an exception.
|
||||
|
||||
/* If we set a negative value and the decrementer was positive, raise an
|
||||
* exception.
|
||||
*/
|
||||
if ((value & 0x80000000) && !(decr & 0x80000000))
|
||||
if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED)
|
||||
&& (value & 0x80000000)
|
||||
&& !(decr & 0x80000000)) {
|
||||
(*raise_excp)(env);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
|
||||
@ -763,6 +755,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
|
||||
|
||||
tb_env = g_malloc0(sizeof(ppc_tb_t));
|
||||
env->tb_env = tb_env;
|
||||
tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
|
||||
/* Create new timer */
|
||||
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env);
|
||||
if (0) {
|
||||
@ -806,11 +799,11 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env)
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Embedded PowerPC timers */
|
||||
/* PowerPC 40x timers */
|
||||
|
||||
/* PIT, FIT & WDT */
|
||||
typedef struct ppcemb_timer_t ppcemb_timer_t;
|
||||
struct ppcemb_timer_t {
|
||||
typedef struct ppc40x_timer_t ppc40x_timer_t;
|
||||
struct ppc40x_timer_t {
|
||||
uint64_t pit_reload; /* PIT auto-reload value */
|
||||
uint64_t fit_next; /* Tick for next FIT interrupt */
|
||||
struct QEMUTimer *fit_timer;
|
||||
@ -826,12 +819,12 @@ static void cpu_4xx_fit_cb (void *opaque)
|
||||
{
|
||||
CPUState *env;
|
||||
ppc_tb_t *tb_env;
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
uint64_t now, next;
|
||||
|
||||
env = opaque;
|
||||
tb_env = env->tb_env;
|
||||
ppcemb_timer = tb_env->opaque;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
now = qemu_get_clock_ns(vm_clock);
|
||||
switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
|
||||
case 0:
|
||||
@ -853,7 +846,7 @@ static void cpu_4xx_fit_cb (void *opaque)
|
||||
next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
|
||||
if (next == now)
|
||||
next++;
|
||||
qemu_mod_timer(ppcemb_timer->fit_timer, next);
|
||||
qemu_mod_timer(ppc40x_timer->fit_timer, next);
|
||||
env->spr[SPR_40x_TSR] |= 1 << 26;
|
||||
if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
|
||||
ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
|
||||
@ -865,11 +858,11 @@ static void cpu_4xx_fit_cb (void *opaque)
|
||||
/* Programmable interval timer */
|
||||
static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
|
||||
{
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
uint64_t now, next;
|
||||
|
||||
ppcemb_timer = tb_env->opaque;
|
||||
if (ppcemb_timer->pit_reload <= 1 ||
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
if (ppc40x_timer->pit_reload <= 1 ||
|
||||
!((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
|
||||
(is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
|
||||
/* Stop PIT */
|
||||
@ -877,9 +870,9 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
|
||||
qemu_del_timer(tb_env->decr_timer);
|
||||
} else {
|
||||
LOG_TB("%s: start PIT %016" PRIx64 "\n",
|
||||
__func__, ppcemb_timer->pit_reload);
|
||||
__func__, ppc40x_timer->pit_reload);
|
||||
now = qemu_get_clock_ns(vm_clock);
|
||||
next = now + muldiv64(ppcemb_timer->pit_reload,
|
||||
next = now + muldiv64(ppc40x_timer->pit_reload,
|
||||
get_ticks_per_sec(), tb_env->decr_freq);
|
||||
if (is_excp)
|
||||
next += tb_env->decr_next - now;
|
||||
@ -894,21 +887,21 @@ static void cpu_4xx_pit_cb (void *opaque)
|
||||
{
|
||||
CPUState *env;
|
||||
ppc_tb_t *tb_env;
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
|
||||
env = opaque;
|
||||
tb_env = env->tb_env;
|
||||
ppcemb_timer = tb_env->opaque;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
env->spr[SPR_40x_TSR] |= 1 << 27;
|
||||
if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
|
||||
ppc_set_irq(env, ppcemb_timer->decr_excp, 1);
|
||||
ppc_set_irq(env, ppc40x_timer->decr_excp, 1);
|
||||
start_stop_pit(env, tb_env, 1);
|
||||
LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
|
||||
"%016" PRIx64 "\n", __func__,
|
||||
(int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
|
||||
(int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
|
||||
env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
|
||||
ppcemb_timer->pit_reload);
|
||||
ppc40x_timer->pit_reload);
|
||||
}
|
||||
|
||||
/* Watchdog timer */
|
||||
@ -916,12 +909,12 @@ static void cpu_4xx_wdt_cb (void *opaque)
|
||||
{
|
||||
CPUState *env;
|
||||
ppc_tb_t *tb_env;
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
uint64_t now, next;
|
||||
|
||||
env = opaque;
|
||||
tb_env = env->tb_env;
|
||||
ppcemb_timer = tb_env->opaque;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
now = qemu_get_clock_ns(vm_clock);
|
||||
switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
|
||||
case 0:
|
||||
@ -948,13 +941,13 @@ static void cpu_4xx_wdt_cb (void *opaque)
|
||||
switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
qemu_mod_timer(ppcemb_timer->wdt_timer, next);
|
||||
ppcemb_timer->wdt_next = next;
|
||||
qemu_mod_timer(ppc40x_timer->wdt_timer, next);
|
||||
ppc40x_timer->wdt_next = next;
|
||||
env->spr[SPR_40x_TSR] |= 1 << 31;
|
||||
break;
|
||||
case 0x2:
|
||||
qemu_mod_timer(ppcemb_timer->wdt_timer, next);
|
||||
ppcemb_timer->wdt_next = next;
|
||||
qemu_mod_timer(ppc40x_timer->wdt_timer, next);
|
||||
ppc40x_timer->wdt_next = next;
|
||||
env->spr[SPR_40x_TSR] |= 1 << 30;
|
||||
if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
|
||||
ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
|
||||
@ -982,12 +975,12 @@ static void cpu_4xx_wdt_cb (void *opaque)
|
||||
void store_40x_pit (CPUState *env, target_ulong val)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
ppcemb_timer = tb_env->opaque;
|
||||
ppc40x_timer = tb_env->opaque;
|
||||
LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
|
||||
ppcemb_timer->pit_reload = val;
|
||||
ppc40x_timer->pit_reload = val;
|
||||
start_stop_pit(env, tb_env, 0);
|
||||
}
|
||||
|
||||
@ -996,31 +989,7 @@ target_ulong load_40x_pit (CPUState *env)
|
||||
return cpu_ppc_load_decr(env);
|
||||
}
|
||||
|
||||
void store_booke_tsr (CPUState *env, target_ulong val)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
|
||||
ppcemb_timer = tb_env->opaque;
|
||||
|
||||
LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
|
||||
env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
|
||||
if (val & 0x80000000)
|
||||
ppc_set_irq(env, ppcemb_timer->decr_excp, 0);
|
||||
}
|
||||
|
||||
void store_booke_tcr (CPUState *env, target_ulong val)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
|
||||
env->spr[SPR_40x_TCR] = val & 0xFFC00000;
|
||||
start_stop_pit(env, tb_env, 1);
|
||||
cpu_4xx_wdt_cb(env);
|
||||
}
|
||||
|
||||
static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
|
||||
static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
|
||||
{
|
||||
CPUState *env = opaque;
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
@ -1032,30 +1001,31 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
|
||||
/* XXX: we should also update all timers */
|
||||
}
|
||||
|
||||
clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
|
||||
clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
|
||||
unsigned int decr_excp)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
ppcemb_timer_t *ppcemb_timer;
|
||||
ppc40x_timer_t *ppc40x_timer;
|
||||
|
||||
tb_env = g_malloc0(sizeof(ppc_tb_t));
|
||||
env->tb_env = tb_env;
|
||||
ppcemb_timer = g_malloc0(sizeof(ppcemb_timer_t));
|
||||
tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
|
||||
ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
|
||||
tb_env->tb_freq = freq;
|
||||
tb_env->decr_freq = freq;
|
||||
tb_env->opaque = ppcemb_timer;
|
||||
tb_env->opaque = ppc40x_timer;
|
||||
LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
|
||||
if (ppcemb_timer != NULL) {
|
||||
if (ppc40x_timer != NULL) {
|
||||
/* We use decr timer for PIT */
|
||||
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env);
|
||||
ppcemb_timer->fit_timer =
|
||||
ppc40x_timer->fit_timer =
|
||||
qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env);
|
||||
ppcemb_timer->wdt_timer =
|
||||
ppc40x_timer->wdt_timer =
|
||||
qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env);
|
||||
ppcemb_timer->decr_excp = decr_excp;
|
||||
ppc40x_timer->decr_excp = decr_excp;
|
||||
}
|
||||
|
||||
return &ppc_emb_set_tb_clk;
|
||||
return &ppc_40x_set_tb_clk;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
37
hw/ppc.h
37
hw/ppc.h
@ -1,3 +1,5 @@
|
||||
void ppc_set_irq (CPUState *env, int n_IRQ, int level);
|
||||
|
||||
/* PowerPC hardware exceptions management helpers */
|
||||
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
|
||||
typedef struct clk_setup_t clk_setup_t;
|
||||
@ -11,6 +13,36 @@ static inline void clk_setup (clk_setup_t *clk, uint32_t freq)
|
||||
(*clk->cb)(clk->opaque, freq);
|
||||
}
|
||||
|
||||
struct ppc_tb_t {
|
||||
/* Time base management */
|
||||
int64_t tb_offset; /* Compensation */
|
||||
int64_t atb_offset; /* Compensation */
|
||||
uint32_t tb_freq; /* TB frequency */
|
||||
/* Decrementer management */
|
||||
uint64_t decr_next; /* Tick for next decr interrupt */
|
||||
uint32_t decr_freq; /* decrementer frequency */
|
||||
struct QEMUTimer *decr_timer;
|
||||
/* Hypervisor decrementer management */
|
||||
uint64_t hdecr_next; /* Tick for next hdecr interrupt */
|
||||
struct QEMUTimer *hdecr_timer;
|
||||
uint64_t purr_load;
|
||||
uint64_t purr_start;
|
||||
void *opaque;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* PPC Timers flags */
|
||||
#define PPC_TIMER_BOOKE (1 << 0) /* Enable Booke support */
|
||||
#define PPC_TIMER_E500 (1 << 1) /* Enable e500 support */
|
||||
#define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when
|
||||
* the most significant bit
|
||||
* changes from 0 to 1.
|
||||
*/
|
||||
#define PPC_DECR_ZERO_TRIGGERED (1 << 3) /* Decr interrupt triggered when
|
||||
* the decrementer reaches zero.
|
||||
*/
|
||||
|
||||
uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset);
|
||||
clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq);
|
||||
/* Embedded PowerPC DCR management */
|
||||
typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn);
|
||||
@ -19,7 +51,7 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn),
|
||||
int (*dcr_write_error)(int dcrn));
|
||||
int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
|
||||
dcr_read_cb drc_read, dcr_write_cb dcr_write);
|
||||
clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
|
||||
clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
|
||||
unsigned int decr_excp);
|
||||
|
||||
/* Embedded PowerPC reset */
|
||||
@ -55,3 +87,6 @@ enum {
|
||||
#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07)
|
||||
|
||||
#define PPC_SERIAL_MM_BAUDBASE 399193
|
||||
|
||||
/* ppc_booke.c */
|
||||
void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags);
|
||||
|
@ -213,7 +213,8 @@ static void ref405ep_init (ram_addr_t ram_size,
|
||||
sram_size = 512 * 1024;
|
||||
sram_offset = qemu_ram_alloc(NULL, "ef405ep.sram", sram_size);
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset);
|
||||
printf("%s: register SRAM at offset " RAM_ADDR_FMT "\n",
|
||||
__func__, sram_offset);
|
||||
#endif
|
||||
cpu_register_physical_memory(0xFFF00000, sram_size,
|
||||
sram_offset | IO_MEM_RAM);
|
||||
@ -357,7 +358,7 @@ static void ref405ep_init (ram_addr_t ram_size,
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: Done\n", __func__);
|
||||
#endif
|
||||
printf("bdloc %016lx\n", (unsigned long)bdloc);
|
||||
printf("bdloc " RAM_ADDR_FMT "\n", bdloc);
|
||||
}
|
||||
|
||||
static QEMUMachine ref405ep_machine = {
|
||||
|
@ -44,6 +44,8 @@ static int bamboo_load_device_tree(target_phys_addr_t addr,
|
||||
char *filename;
|
||||
int fdt_size;
|
||||
void *fdt;
|
||||
uint32_t tb_freq = 400000000;
|
||||
uint32_t clock_freq = 400000000;
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
|
||||
if (!filename) {
|
||||
@ -77,8 +79,18 @@ static int bamboo_load_device_tree(target_phys_addr_t addr,
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
|
||||
if (kvm_enabled())
|
||||
kvmppc_fdt_update(fdt);
|
||||
/* Copy data from the host device tree into the guest. Since the guest can
|
||||
* directly access the timebase without host involvement, we must expose
|
||||
* the correct frequencies. */
|
||||
if (kvm_enabled()) {
|
||||
tb_freq = kvmppc_get_tbfreq();
|
||||
clock_freq = kvmppc_get_clockfreq();
|
||||
}
|
||||
|
||||
qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
|
||||
clock_freq);
|
||||
qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
|
||||
tb_freq);
|
||||
|
||||
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
|
||||
g_free(fdt);
|
||||
|
@ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model,
|
||||
cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
|
||||
cpu_clk->opaque = env;
|
||||
/* Set time-base frequency to sysclk */
|
||||
tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
|
||||
tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
|
||||
tb_clk->opaque = env;
|
||||
ppc_dcr_init(env, NULL, NULL);
|
||||
/* Register qemu callbacks */
|
||||
|
254
hw/ppc_booke.c
Normal file
254
hw/ppc_booke.c
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* QEMU PowerPC Booke hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2011 AdaCore
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw.h"
|
||||
#include "ppc.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "sysemu.h"
|
||||
#include "nvram.h"
|
||||
#include "qemu-log.h"
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
/* Timer Control Register */
|
||||
|
||||
#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */
|
||||
#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT)
|
||||
#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */
|
||||
#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT)
|
||||
#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */
|
||||
#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */
|
||||
#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */
|
||||
#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT)
|
||||
#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */
|
||||
#define TCR_ARE (1 << 22) /* Auto-Reload Enable */
|
||||
|
||||
/* Timer Control Register (e500 specific fields) */
|
||||
|
||||
#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */
|
||||
#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT)
|
||||
#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */
|
||||
#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT)
|
||||
|
||||
/* Timer Status Register */
|
||||
|
||||
#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */
|
||||
#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */
|
||||
#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */
|
||||
#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT)
|
||||
#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */
|
||||
#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */
|
||||
|
||||
typedef struct booke_timer_t booke_timer_t;
|
||||
struct booke_timer_t {
|
||||
|
||||
uint64_t fit_next;
|
||||
struct QEMUTimer *fit_timer;
|
||||
|
||||
uint64_t wdt_next;
|
||||
struct QEMUTimer *wdt_timer;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static void booke_update_irq(CPUState *env)
|
||||
{
|
||||
ppc_set_irq(env, PPC_INTERRUPT_DECR,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_DIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_DIE));
|
||||
|
||||
ppc_set_irq(env, PPC_INTERRUPT_WDT,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_WIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_WIE));
|
||||
|
||||
ppc_set_irq(env, PPC_INTERRUPT_FIT,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_FIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_FIE));
|
||||
}
|
||||
|
||||
/* Return the location of the bit of time base at which the FIT will raise an
|
||||
interrupt */
|
||||
static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env)
|
||||
{
|
||||
uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT;
|
||||
|
||||
if (tb_env->flags & PPC_TIMER_E500) {
|
||||
/* e500 Fixed-interval timer period extension */
|
||||
uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK)
|
||||
>> TCR_E500_FPEXT_SHIFT;
|
||||
fp = 63 - (fp | fpext << 2);
|
||||
} else {
|
||||
fp = env->fit_period[fp];
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* Return the location of the bit of time base at which the WDT will raise an
|
||||
interrupt */
|
||||
static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env)
|
||||
{
|
||||
uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT;
|
||||
|
||||
if (tb_env->flags & PPC_TIMER_E500) {
|
||||
/* e500 Watchdog timer period extension */
|
||||
uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK)
|
||||
>> TCR_E500_WPEXT_SHIFT;
|
||||
wp = 63 - (wp | wpext << 2);
|
||||
} else {
|
||||
wp = env->wdt_period[wp];
|
||||
}
|
||||
|
||||
return wp;
|
||||
}
|
||||
|
||||
static void booke_update_fixed_timer(CPUState *env,
|
||||
uint8_t target_bit,
|
||||
uint64_t *next,
|
||||
struct QEMUTimer *timer)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t lapse;
|
||||
uint64_t tb;
|
||||
uint64_t period = 1 << (target_bit + 1);
|
||||
uint64_t now;
|
||||
|
||||
now = qemu_get_clock_ns(vm_clock);
|
||||
tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset);
|
||||
|
||||
lapse = period - ((tb - (1 << target_bit)) & (period - 1));
|
||||
|
||||
*next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq);
|
||||
|
||||
/* XXX: If expire time is now. We can't run the callback because we don't
|
||||
* have access to it. So we just set the timer one nanosecond later.
|
||||
*/
|
||||
|
||||
if (*next == now) {
|
||||
(*next)++;
|
||||
}
|
||||
|
||||
qemu_mod_timer(timer, *next);
|
||||
}
|
||||
|
||||
static void booke_decr_cb(void *opaque)
|
||||
{
|
||||
CPUState *env = opaque;
|
||||
|
||||
env->spr[SPR_BOOKE_TSR] |= TSR_DIS;
|
||||
booke_update_irq(env);
|
||||
|
||||
if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
|
||||
/* Auto Reload */
|
||||
cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
|
||||
}
|
||||
}
|
||||
|
||||
static void booke_fit_cb(void *opaque)
|
||||
{
|
||||
CPUState *env;
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
env = opaque;
|
||||
tb_env = env->tb_env;
|
||||
booke_timer = tb_env->opaque;
|
||||
env->spr[SPR_BOOKE_TSR] |= TSR_FIS;
|
||||
|
||||
booke_update_irq(env);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer);
|
||||
}
|
||||
|
||||
static void booke_wdt_cb(void *opaque)
|
||||
{
|
||||
CPUState *env;
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
env = opaque;
|
||||
tb_env = env->tb_env;
|
||||
booke_timer = tb_env->opaque;
|
||||
|
||||
/* TODO: There's lots of complicated stuff to do here */
|
||||
|
||||
booke_update_irq(env);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer);
|
||||
}
|
||||
|
||||
void store_booke_tsr(CPUState *env, target_ulong val)
|
||||
{
|
||||
env->spr[SPR_BOOKE_TSR] &= ~val;
|
||||
booke_update_irq(env);
|
||||
}
|
||||
|
||||
void store_booke_tcr(CPUState *env, target_ulong val)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
booke_timer_t *booke_timer = tb_env->opaque;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
env->spr[SPR_BOOKE_TCR] = val;
|
||||
|
||||
booke_update_irq(env);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer);
|
||||
|
||||
}
|
||||
|
||||
void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = g_malloc0(sizeof(ppc_tb_t));
|
||||
booke_timer = g_malloc0(sizeof(booke_timer_t));
|
||||
|
||||
env->tb_env = tb_env;
|
||||
tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED;
|
||||
|
||||
tb_env->tb_freq = freq;
|
||||
tb_env->decr_freq = freq;
|
||||
tb_env->opaque = booke_timer;
|
||||
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env);
|
||||
|
||||
booke_timer->fit_timer =
|
||||
qemu_new_timer_ns(vm_clock, &booke_fit_cb, env);
|
||||
booke_timer->wdt_timer =
|
||||
qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env);
|
||||
}
|
42
hw/ppc_mac.h
42
hw/ppc_mac.h
@ -77,46 +77,4 @@ void macio_nvram_setup_bar(MacIONVRAMState *s, MemoryRegion *bar,
|
||||
void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len);
|
||||
uint32_t macio_nvram_read (void *opaque, uint32_t addr);
|
||||
void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val);
|
||||
|
||||
/* adb.c */
|
||||
|
||||
#define MAX_ADB_DEVICES 16
|
||||
|
||||
#define ADB_MAX_OUT_LEN 16
|
||||
|
||||
typedef struct ADBDevice ADBDevice;
|
||||
|
||||
/* buf = NULL means polling */
|
||||
typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
|
||||
const uint8_t *buf, int len);
|
||||
typedef int ADBDeviceReset(ADBDevice *d);
|
||||
|
||||
struct ADBDevice {
|
||||
struct ADBBusState *bus;
|
||||
int devaddr;
|
||||
int handler;
|
||||
ADBDeviceRequest *devreq;
|
||||
ADBDeviceReset *devreset;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
typedef struct ADBBusState {
|
||||
ADBDevice devices[MAX_ADB_DEVICES];
|
||||
int nb_devices;
|
||||
int poll_index;
|
||||
} ADBBusState;
|
||||
|
||||
int adb_request(ADBBusState *s, uint8_t *buf_out,
|
||||
const uint8_t *buf, int len);
|
||||
int adb_poll(ADBBusState *s, uint8_t *buf_out);
|
||||
|
||||
ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
|
||||
ADBDeviceRequest *devreq,
|
||||
ADBDeviceReset *devreset,
|
||||
void *opaque);
|
||||
void adb_kbd_init(ADBBusState *bus);
|
||||
void adb_mouse_init(ADBBusState *bus);
|
||||
|
||||
extern ADBBusState adb_bus;
|
||||
|
||||
#endif /* !defined(__PPC_MAC_H__) */
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "hw.h"
|
||||
#include "ppc.h"
|
||||
#include "ppc_mac.h"
|
||||
#include "adb.h"
|
||||
#include "mac_dbdma.h"
|
||||
#include "nvram.h"
|
||||
#include "pc.h"
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "hw.h"
|
||||
#include "ppc.h"
|
||||
#include "ppc_mac.h"
|
||||
#include "adb.h"
|
||||
#include "mac_dbdma.h"
|
||||
#include "nvram.h"
|
||||
#include "pc.h"
|
||||
|
@ -14,8 +14,6 @@
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "qemu-common.h"
|
||||
#include "net.h"
|
||||
@ -52,6 +50,7 @@
|
||||
#define MPC8544_PCI_IO 0xE1000000
|
||||
#define MPC8544_PCI_IOLEN 0x10000
|
||||
#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000)
|
||||
#define MPC8544_SPIN_BASE 0xEF000000
|
||||
|
||||
struct boot_info
|
||||
{
|
||||
@ -59,30 +58,6 @@ struct boot_info
|
||||
uint32_t entry;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FDT
|
||||
static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop)
|
||||
{
|
||||
uint32_t cell;
|
||||
int ret;
|
||||
|
||||
ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't read host %s/%s\n", node, prop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
|
||||
prop, cell);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't set guest /cpus/PowerPC,8544@0/%s\n", prop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mpc8544_load_device_tree(CPUState *env,
|
||||
target_phys_addr_t addr,
|
||||
uint32_t ramsize,
|
||||
@ -97,6 +72,9 @@ static int mpc8544_load_device_tree(CPUState *env,
|
||||
int fdt_size;
|
||||
void *fdt;
|
||||
uint8_t hypercall[16];
|
||||
uint32_t clock_freq = 400000000;
|
||||
uint32_t tb_freq = 400000000;
|
||||
int i;
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
|
||||
if (!filename) {
|
||||
@ -134,32 +112,9 @@ static int mpc8544_load_device_tree(CPUState *env,
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
|
||||
if (kvm_enabled()) {
|
||||
struct dirent *dirp;
|
||||
DIR *dp;
|
||||
char buf[128];
|
||||
|
||||
if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) {
|
||||
printf("Can't open directory /proc/device-tree/cpus/\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf[0] = '\0';
|
||||
while ((dirp = readdir(dp)) != NULL) {
|
||||
if (strncmp(dirp->d_name, "PowerPC", 7) == 0) {
|
||||
snprintf(buf, 128, "/cpus/%s", dirp->d_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
if (buf[0] == '\0') {
|
||||
printf("Unknow host!\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mpc8544_copy_soc_cell(fdt, buf, "clock-frequency");
|
||||
mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency");
|
||||
/* Read out host's frequencies */
|
||||
clock_freq = kvmppc_get_clockfreq();
|
||||
tb_freq = kvmppc_get_tbfreq();
|
||||
|
||||
/* indicate KVM hypercall interface */
|
||||
qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible",
|
||||
@ -167,13 +122,45 @@ static int mpc8544_load_device_tree(CPUState *env,
|
||||
kvmppc_get_hypercall(env, hypercall, sizeof(hypercall));
|
||||
qemu_devtree_setprop(fdt, "/hypervisor", "hcall-instructions",
|
||||
hypercall, sizeof(hypercall));
|
||||
} else {
|
||||
const uint32_t freq = 400000000;
|
||||
}
|
||||
|
||||
qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
|
||||
"clock-frequency", freq);
|
||||
qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
|
||||
"timebase-frequency", freq);
|
||||
/* 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));
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (env->cpu_index == i) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!env) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", env->cpu_index);
|
||||
qemu_devtree_add_subnode(fdt, cpu_name);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq);
|
||||
qemu_devtree_setprop_string(fdt, cpu_name, "device_type", "cpu");
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "reg", env->cpu_index);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-line-size",
|
||||
env->dcache_line_size);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-line-size",
|
||||
env->icache_line_size);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "d-cache-size", 0x8000);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "i-cache-size", 0x8000);
|
||||
qemu_devtree_setprop_cell(fdt, cpu_name, "bus-frequency", 0);
|
||||
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));
|
||||
} else {
|
||||
qemu_devtree_setprop_string(fdt, cpu_name, "status", "okay");
|
||||
}
|
||||
}
|
||||
|
||||
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
|
||||
@ -188,7 +175,7 @@ out:
|
||||
/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
|
||||
static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
|
||||
{
|
||||
return (ffs(size >> 10) - 1) >> 1;
|
||||
return ffs(size >> 10) - 1;
|
||||
}
|
||||
|
||||
static void mmubooke_create_initial_mapping(CPUState *env,
|
||||
@ -203,6 +190,20 @@ static void mmubooke_create_initial_mapping(CPUState *env,
|
||||
tlb->mas2 = va & TARGET_PAGE_MASK;
|
||||
tlb->mas7_3 = pa & TARGET_PAGE_MASK;
|
||||
tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
|
||||
|
||||
env->tlb_dirty = true;
|
||||
}
|
||||
|
||||
static void mpc8544ds_cpu_reset_sec(void *opaque)
|
||||
{
|
||||
CPUState *env = opaque;
|
||||
|
||||
cpu_reset(env);
|
||||
|
||||
/* Secondary CPU starts in halted state for now. Needs to change when
|
||||
implementing non-kernel boot. */
|
||||
env->halted = 1;
|
||||
env->exception_index = EXCP_HLT;
|
||||
}
|
||||
|
||||
static void mpc8544ds_cpu_reset(void *opaque)
|
||||
@ -213,6 +214,7 @@ static void mpc8544ds_cpu_reset(void *opaque)
|
||||
cpu_reset(env);
|
||||
|
||||
/* Set initial guest state. */
|
||||
env->halted = 0;
|
||||
env->gpr[1] = (16<<20) - 8;
|
||||
env->gpr[3] = bi->dt_base;
|
||||
env->nip = bi->entry;
|
||||
@ -228,7 +230,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
|
||||
{
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
PCIBus *pci_bus;
|
||||
CPUState *env;
|
||||
CPUState *env = NULL;
|
||||
uint64_t elf_entry;
|
||||
uint64_t elf_lowaddr;
|
||||
target_phys_addr_t entry=0;
|
||||
@ -239,27 +241,51 @@ static void mpc8544ds_init(ram_addr_t ram_size,
|
||||
target_long initrd_size=0;
|
||||
int i=0;
|
||||
unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
|
||||
qemu_irq *irqs, *mpic;
|
||||
qemu_irq **irqs, *mpic;
|
||||
DeviceState *dev;
|
||||
struct boot_info *boot_info;
|
||||
CPUState *firstenv = NULL;
|
||||
|
||||
/* Setup CPU */
|
||||
/* Setup CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
cpu_model = "e500v2_v30";
|
||||
}
|
||||
|
||||
env = cpu_ppc_init(cpu_model);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Unable to initialize CPU!\n");
|
||||
exit(1);
|
||||
irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *));
|
||||
irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
qemu_irq *input;
|
||||
env = cpu_ppc_init(cpu_model);
|
||||
if (!env) {
|
||||
fprintf(stderr, "Unable to initialize CPU!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!firstenv) {
|
||||
firstenv = env;
|
||||
}
|
||||
|
||||
irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB);
|
||||
input = (qemu_irq *)env->irq_inputs;
|
||||
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;
|
||||
|
||||
ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500);
|
||||
|
||||
/* Register reset handler */
|
||||
if (!i) {
|
||||
/* Primary CPU */
|
||||
struct boot_info *boot_info;
|
||||
boot_info = g_malloc0(sizeof(struct boot_info));
|
||||
qemu_register_reset(mpc8544ds_cpu_reset, env);
|
||||
env->load_info = boot_info;
|
||||
} else {
|
||||
/* Secondary CPUs */
|
||||
qemu_register_reset(mpc8544ds_cpu_reset_sec, env);
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX register timer? */
|
||||
ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR);
|
||||
ppc_dcr_init(env, NULL, NULL);
|
||||
|
||||
/* Register reset handler */
|
||||
qemu_register_reset(mpc8544ds_cpu_reset, env);
|
||||
env = firstenv;
|
||||
|
||||
/* Fixup Memory size on a alignment boundary */
|
||||
ram_size &= ~(RAM_SIZES_ALIGN - 1);
|
||||
@ -269,10 +295,12 @@ static void mpc8544ds_init(ram_addr_t ram_size,
|
||||
"mpc8544ds.ram", ram_size));
|
||||
|
||||
/* MPIC */
|
||||
irqs = g_malloc0(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
|
||||
irqs[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_INT];
|
||||
irqs[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_CINT];
|
||||
mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE, 1, &irqs, NULL);
|
||||
mpic = mpic_init(address_space_mem, MPC8544_MPIC_REGS_BASE,
|
||||
smp_cpus, irqs, NULL);
|
||||
|
||||
if (!mpic) {
|
||||
cpu_abort(env, "MPIC failed to initialize\n");
|
||||
}
|
||||
|
||||
/* Serial */
|
||||
if (serial_hds[0]) {
|
||||
@ -308,6 +336,9 @@ static void mpc8544ds_init(ram_addr_t ram_size,
|
||||
}
|
||||
}
|
||||
|
||||
/* Register spinning region */
|
||||
sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL);
|
||||
|
||||
/* Load kernel. */
|
||||
if (kernel_filename) {
|
||||
kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
|
||||
@ -338,10 +369,10 @@ static void mpc8544ds_init(ram_addr_t ram_size,
|
||||
}
|
||||
}
|
||||
|
||||
boot_info = g_malloc0(sizeof(struct boot_info));
|
||||
|
||||
/* If we're loading a kernel directly, we must load the device tree too. */
|
||||
if (kernel_filename) {
|
||||
struct boot_info *boot_info;
|
||||
|
||||
#ifndef CONFIG_FDT
|
||||
cpu_abort(env, "Compiled without FDT support - can't load kernel\n");
|
||||
#endif
|
||||
@ -352,10 +383,10 @@ static void mpc8544ds_init(ram_addr_t ram_size,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
boot_info = env->load_info;
|
||||
boot_info->entry = entry;
|
||||
boot_info->dt_base = dt_base;
|
||||
}
|
||||
env->load_info = boot_info;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvmppc_init();
|
||||
@ -366,6 +397,7 @@ static QEMUMachine mpc8544ds_machine = {
|
||||
.name = "mpc8544ds",
|
||||
.desc = "mpc8544ds",
|
||||
.init = mpc8544ds_init,
|
||||
.max_cpus = 15,
|
||||
};
|
||||
|
||||
static void mpc8544ds_machine_init(void)
|
||||
|
215
hw/ppce500_spin.c
Normal file
215
hw/ppce500_spin.c
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* QEMU PowerPC e500v2 ePAPR spinning code
|
||||
*
|
||||
* Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* Author: Alexander Graf, <agraf@suse.de>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* This code is not really a device, but models an interface that usually
|
||||
* firmware takes care of. It's used when QEMU plays the role of firmware.
|
||||
*
|
||||
* Specification:
|
||||
*
|
||||
* https://www.power.org/resources/downloads/Power_ePAPR_APPROVED_v1.1.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw.h"
|
||||
#include "sysemu.h"
|
||||
#include "sysbus.h"
|
||||
#include "kvm.h"
|
||||
|
||||
#define MAX_CPUS 32
|
||||
|
||||
typedef struct spin_info {
|
||||
uint64_t addr;
|
||||
uint64_t r3;
|
||||
uint32_t resv;
|
||||
uint32_t pir;
|
||||
uint64_t reserved;
|
||||
} __attribute__ ((packed)) SpinInfo;
|
||||
|
||||
typedef struct spin_state {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
SpinInfo spin[MAX_CPUS];
|
||||
} SpinState;
|
||||
|
||||
typedef struct spin_kick {
|
||||
CPUState *env;
|
||||
SpinInfo *spin;
|
||||
} SpinKick;
|
||||
|
||||
static void spin_reset(void *opaque)
|
||||
{
|
||||
SpinState *s = opaque;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CPUS; i++) {
|
||||
SpinInfo *info = &s->spin[i];
|
||||
|
||||
info->pir = i;
|
||||
info->r3 = i;
|
||||
info->addr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create -kernel TLB entries for BookE, linearly spanning 256MB. */
|
||||
static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
|
||||
{
|
||||
return (ffs(size >> 10) - 1) >> 1;
|
||||
}
|
||||
|
||||
static void mmubooke_create_initial_mapping(CPUState *env,
|
||||
target_ulong va,
|
||||
target_phys_addr_t pa,
|
||||
target_phys_addr_t len)
|
||||
{
|
||||
ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1);
|
||||
target_phys_addr_t size;
|
||||
|
||||
size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT);
|
||||
tlb->mas1 = MAS1_VALID | size;
|
||||
tlb->mas2 = (va & TARGET_PAGE_MASK) | MAS2_M;
|
||||
tlb->mas7_3 = pa & TARGET_PAGE_MASK;
|
||||
tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
|
||||
}
|
||||
|
||||
static void spin_kick(void *data)
|
||||
{
|
||||
SpinKick *kick = data;
|
||||
CPUState *env = kick->env;
|
||||
SpinInfo *curspin = kick->spin;
|
||||
target_phys_addr_t map_size = 64 * 1024 * 1024;
|
||||
target_phys_addr_t map_start;
|
||||
|
||||
cpu_synchronize_state(env);
|
||||
stl_p(&curspin->pir, env->spr[SPR_PIR]);
|
||||
env->nip = ldq_p(&curspin->addr) & (map_size - 1);
|
||||
env->gpr[3] = ldq_p(&curspin->r3);
|
||||
env->gpr[4] = 0;
|
||||
env->gpr[5] = 0;
|
||||
env->gpr[6] = 0;
|
||||
env->gpr[7] = map_size;
|
||||
env->gpr[8] = 0;
|
||||
env->gpr[9] = 0;
|
||||
|
||||
map_start = ldq_p(&curspin->addr) & ~(map_size - 1);
|
||||
mmubooke_create_initial_mapping(env, 0, map_start, map_size);
|
||||
|
||||
env->halted = 0;
|
||||
env->exception_index = -1;
|
||||
qemu_cpu_kick(env);
|
||||
}
|
||||
|
||||
static void spin_write(void *opaque, target_phys_addr_t addr, uint64_t value,
|
||||
unsigned len)
|
||||
{
|
||||
SpinState *s = opaque;
|
||||
int env_idx = addr / sizeof(SpinInfo);
|
||||
CPUState *env;
|
||||
SpinInfo *curspin = &s->spin[env_idx];
|
||||
uint8_t *curspin_p = (uint8_t*)curspin;
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
if (env->cpu_index == env_idx) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!env) {
|
||||
/* Unknown CPU */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!env->cpu_index) {
|
||||
/* primary CPU doesn't spin */
|
||||
return;
|
||||
}
|
||||
|
||||
curspin_p = &curspin_p[addr % sizeof(SpinInfo)];
|
||||
switch (len) {
|
||||
case 1:
|
||||
stb_p(curspin_p, value);
|
||||
break;
|
||||
case 2:
|
||||
stw_p(curspin_p, value);
|
||||
break;
|
||||
case 4:
|
||||
stl_p(curspin_p, value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(ldq_p(&curspin->addr) & 1)) {
|
||||
/* run CPU */
|
||||
SpinKick kick = {
|
||||
.env = env,
|
||||
.spin = curspin,
|
||||
};
|
||||
|
||||
run_on_cpu(env, spin_kick, &kick);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t spin_read(void *opaque, target_phys_addr_t addr, unsigned len)
|
||||
{
|
||||
SpinState *s = opaque;
|
||||
uint8_t *spin_p = &((uint8_t*)s->spin)[addr];
|
||||
|
||||
switch (len) {
|
||||
case 1:
|
||||
return ldub_p(spin_p);
|
||||
case 2:
|
||||
return lduw_p(spin_p);
|
||||
case 4:
|
||||
return ldl_p(spin_p);
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
const MemoryRegionOps spin_rw_ops = {
|
||||
.read = spin_read,
|
||||
.write = spin_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
};
|
||||
|
||||
static int ppce500_spin_initfn(SysBusDevice *dev)
|
||||
{
|
||||
SpinState *s;
|
||||
|
||||
s = FROM_SYSBUS(SpinState, sysbus_from_qdev(dev));
|
||||
|
||||
memory_region_init_io(&s->iomem, &spin_rw_ops, s, "e500 spin pv device",
|
||||
sizeof(SpinInfo) * MAX_CPUS);
|
||||
sysbus_init_mmio_region(dev, &s->iomem);
|
||||
|
||||
qemu_register_reset(spin_reset, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SysBusDeviceInfo ppce500_spin_info = {
|
||||
.init = ppce500_spin_initfn,
|
||||
.qdev.name = "e500-spin",
|
||||
.qdev.size = sizeof(SpinState),
|
||||
};
|
||||
|
||||
static void ppce500_spin_register(void)
|
||||
{
|
||||
sysbus_register_withprop(&ppce500_spin_info);
|
||||
}
|
||||
device_init(ppce500_spin_register);
|
77
hw/spapr.c
77
hw/spapr.c
@ -38,6 +38,9 @@
|
||||
#include "hw/spapr_vio.h"
|
||||
#include "hw/xics.h"
|
||||
|
||||
#include "kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x00000000
|
||||
@ -54,8 +57,34 @@
|
||||
#define MAX_CPUS 256
|
||||
#define XICS_IRQS 1024
|
||||
|
||||
#define PHANDLE_XICP 0x00001111
|
||||
|
||||
sPAPREnvironment *spapr;
|
||||
|
||||
qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num)
|
||||
{
|
||||
uint32_t irq;
|
||||
qemu_irq qirq;
|
||||
|
||||
if (hint) {
|
||||
irq = hint;
|
||||
/* FIXME: we should probably check for collisions somehow */
|
||||
} else {
|
||||
irq = spapr->next_irq++;
|
||||
}
|
||||
|
||||
qirq = xics_find_qirq(spapr->icp, irq);
|
||||
if (!qirq) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (irq_num) {
|
||||
*irq_num = irq;
|
||||
}
|
||||
|
||||
return qirq;
|
||||
}
|
||||
|
||||
static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||
target_phys_addr_t initrd_base,
|
||||
target_phys_addr_t initrd_size,
|
||||
@ -70,7 +99,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
|
||||
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-tce\0hcall-vio\0hcall-splpar\0hcall-bulk";
|
||||
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
|
||||
int i;
|
||||
char *modelname;
|
||||
@ -137,6 +166,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||
char *nodename;
|
||||
uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
|
||||
0xffffffff, 0xffffffff};
|
||||
uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
|
||||
uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
|
||||
|
||||
if (asprintf(&nodename, "%s@%x", modelname, index) < 0) {
|
||||
fprintf(stderr, "Allocation failure\n");
|
||||
@ -155,10 +186,8 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||
env->dcache_line_size)));
|
||||
_FDT((fdt_property_cell(fdt, "icache-block-size",
|
||||
env->icache_line_size)));
|
||||
_FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ)));
|
||||
/* Hardcode CPU frequency for now. It's kind of arbitrary on
|
||||
* full emu, for kvm we should copy it from the host */
|
||||
_FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000)));
|
||||
_FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq)));
|
||||
_FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq)));
|
||||
_FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
|
||||
_FDT((fdt_property(fdt, "ibm,pft-size",
|
||||
pft_size_prop, sizeof(pft_size_prop))));
|
||||
@ -189,16 +218,18 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* interrupt controller */
|
||||
_FDT((fdt_begin_node(fdt, "interrupt-controller@0")));
|
||||
_FDT((fdt_begin_node(fdt, "interrupt-controller")));
|
||||
|
||||
_FDT((fdt_property_string(fdt, "device_type",
|
||||
"PowerPC-External-Interrupt-Presentation")));
|
||||
_FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")));
|
||||
_FDT((fdt_property_cell(fdt, "reg", 0)));
|
||||
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
|
||||
_FDT((fdt_property(fdt, "ibm,interrupt-server-ranges",
|
||||
interrupt_server_ranges_prop,
|
||||
sizeof(interrupt_server_ranges_prop))));
|
||||
_FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
|
||||
_FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP)));
|
||||
_FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP)));
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
@ -298,7 +329,6 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
long kernel_size, initrd_size, fw_size;
|
||||
long pteg_shift = 17;
|
||||
char *filename;
|
||||
int irq = 16;
|
||||
|
||||
spapr = g_malloc(sizeof(*spapr));
|
||||
cpu_ppc_hypercall = emulate_spapr_hypercall;
|
||||
@ -330,19 +360,29 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
}
|
||||
|
||||
/* allocate RAM */
|
||||
ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
|
||||
spapr->ram_limit = ram_size;
|
||||
ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", spapr->ram_limit);
|
||||
cpu_register_physical_memory(0, ram_size, ram_offset);
|
||||
|
||||
/* allocate hash page table. For now we always make this 16mb,
|
||||
* later we should probably make it scale to the size of guest
|
||||
* RAM */
|
||||
spapr->htab_size = 1ULL << (pteg_shift + 7);
|
||||
spapr->htab = g_malloc(spapr->htab_size);
|
||||
spapr->htab = qemu_memalign(spapr->htab_size, spapr->htab_size);
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
env->external_htab = spapr->htab;
|
||||
env->htab_base = -1;
|
||||
env->htab_mask = spapr->htab_size - 1;
|
||||
|
||||
/* Tell KVM that we're in PAPR mode */
|
||||
env->spr[SPR_SDR1] = (unsigned long)spapr->htab |
|
||||
((pteg_shift + 7) - 18);
|
||||
env->spr[SPR_HIOR] = 0;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvmppc_set_papr(env);
|
||||
}
|
||||
}
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
|
||||
@ -356,19 +396,19 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
|
||||
/* Set up Interrupt Controller */
|
||||
spapr->icp = xics_system_init(XICS_IRQS);
|
||||
spapr->next_irq = 16;
|
||||
|
||||
/* Set up VIO bus */
|
||||
spapr->vio_bus = spapr_vio_bus_init();
|
||||
|
||||
for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) {
|
||||
for (i = 0; i < MAX_SERIAL_PORTS; i++) {
|
||||
if (serial_hds[i]) {
|
||||
spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i,
|
||||
serial_hds[i], xics_find_qirq(spapr->icp, irq),
|
||||
irq);
|
||||
serial_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_nics; i++, irq++) {
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
|
||||
if (!nd->model) {
|
||||
@ -376,8 +416,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
}
|
||||
|
||||
if (strcmp(nd->model, "ibmveth") == 0) {
|
||||
spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd,
|
||||
xics_find_qirq(spapr->icp, irq), irq);
|
||||
spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd);
|
||||
} else {
|
||||
fprintf(stderr, "pSeries (sPAPR) platform does not support "
|
||||
"NIC model '%s' (only ibmveth is supported)\n",
|
||||
@ -387,9 +426,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
}
|
||||
|
||||
for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) {
|
||||
spapr_vscsi_create(spapr->vio_bus, 0x2000 + i,
|
||||
xics_find_qirq(spapr->icp, irq), irq);
|
||||
irq++;
|
||||
spapr_vscsi_create(spapr->vio_bus, 0x2000 + i);
|
||||
}
|
||||
|
||||
if (kernel_filename) {
|
||||
@ -430,7 +467,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
|
||||
"%ldM guest RAM\n", MIN_RAM_SLOF);
|
||||
exit(1);
|
||||
}
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME);
|
||||
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
|
||||
if (fw_size < 0) {
|
||||
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
|
||||
|
@ -1,6 +1,8 @@
|
||||
#if !defined(__HW_SPAPR_H__)
|
||||
#define __HW_SPAPR_H__
|
||||
|
||||
#include "hw/xics.h"
|
||||
|
||||
struct VIOsPAPRBus;
|
||||
struct icp_state;
|
||||
|
||||
@ -8,12 +10,15 @@ typedef struct sPAPREnvironment {
|
||||
struct VIOsPAPRBus *vio_bus;
|
||||
struct icp_state *icp;
|
||||
|
||||
target_phys_addr_t ram_limit;
|
||||
void *htab;
|
||||
long htab_size;
|
||||
target_phys_addr_t fdt_addr, rtas_addr;
|
||||
long rtas_size;
|
||||
void *fdt_skel;
|
||||
target_ulong entry_point;
|
||||
int next_irq;
|
||||
int rtc_offset;
|
||||
} sPAPREnvironment;
|
||||
|
||||
#define H_SUCCESS 0
|
||||
@ -278,6 +283,8 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn);
|
||||
target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
|
||||
target_ulong *args);
|
||||
|
||||
qemu_irq spapr_allocate_irq(uint32_t hint, uint32_t *irq_num);
|
||||
|
||||
static inline uint32_t rtas_ld(target_ulong phys, int n)
|
||||
{
|
||||
return ldl_be_phys(phys + 4*n);
|
||||
|
220
hw/spapr_hcall.c
220
hw/spapr_hcall.c
@ -99,6 +99,8 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong pte_index = args[1];
|
||||
target_ulong pteh = args[2];
|
||||
target_ulong ptel = args[3];
|
||||
target_ulong page_shift = 12;
|
||||
target_ulong raddr;
|
||||
target_ulong i;
|
||||
uint8_t *hpte;
|
||||
|
||||
@ -111,6 +113,7 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
|
||||
#endif
|
||||
if ((ptel & 0xff000) == 0) {
|
||||
/* 16M page */
|
||||
page_shift = 24;
|
||||
/* lowest AVA bit must be 0 for 16M pages */
|
||||
if (pteh & 0x80) {
|
||||
return H_PARAMETER;
|
||||
@ -120,12 +123,23 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: bounds check the pa? */
|
||||
raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1);
|
||||
|
||||
/* Check WIMG */
|
||||
if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
|
||||
return H_PARAMETER;
|
||||
if (raddr < spapr->ram_limit) {
|
||||
/* Regular RAM - should have WIMG=0010 */
|
||||
if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
/* Looks like an IO address */
|
||||
/* FIXME: What WIMG combinations could be sensible for IO?
|
||||
* For now we allow WIMG=010x, but are there others? */
|
||||
/* FIXME: Should we check against registered IO addresses? */
|
||||
if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
pteh &= ~0x60ULL;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
@ -160,20 +174,26 @@ static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
enum {
|
||||
REMOVE_SUCCESS = 0,
|
||||
REMOVE_NOT_FOUND = 1,
|
||||
REMOVE_PARM = 2,
|
||||
REMOVE_HW = 3,
|
||||
};
|
||||
|
||||
static target_ulong remove_hpte(CPUState *env, target_ulong ptex,
|
||||
target_ulong avpn,
|
||||
target_ulong flags,
|
||||
target_ulong *vp, target_ulong *rp)
|
||||
{
|
||||
target_ulong flags = args[0];
|
||||
target_ulong pte_index = args[1];
|
||||
target_ulong avpn = args[2];
|
||||
uint8_t *hpte;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
return H_PARAMETER;
|
||||
if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
return REMOVE_PARM;
|
||||
}
|
||||
|
||||
hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
|
||||
hpte = env->external_htab + (ptex * HASH_PTE_SIZE_64);
|
||||
while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
|
||||
/* We have no real concurrency in qemu soft-emulation, so we
|
||||
* will never actually have a contested lock */
|
||||
@ -188,14 +208,106 @@ static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
|
||||
((flags & H_ANDCOND) && (v & avpn) != 0)) {
|
||||
stq_p(hpte, v & ~HPTE_V_HVLOCK);
|
||||
assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
|
||||
return H_NOT_FOUND;
|
||||
return REMOVE_NOT_FOUND;
|
||||
}
|
||||
args[0] = v & ~HPTE_V_HVLOCK;
|
||||
args[1] = r;
|
||||
*vp = v & ~HPTE_V_HVLOCK;
|
||||
*rp = r;
|
||||
stq_p(hpte, 0);
|
||||
rb = compute_tlbie_rb(v, r, pte_index);
|
||||
rb = compute_tlbie_rb(v, r, ptex);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
|
||||
return REMOVE_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong flags = args[0];
|
||||
target_ulong pte_index = args[1];
|
||||
target_ulong avpn = args[2];
|
||||
int ret;
|
||||
|
||||
ret = remove_hpte(env, pte_index, avpn, flags,
|
||||
&args[0], &args[1]);
|
||||
|
||||
switch (ret) {
|
||||
case REMOVE_SUCCESS:
|
||||
return H_SUCCESS;
|
||||
|
||||
case REMOVE_NOT_FOUND:
|
||||
return H_NOT_FOUND;
|
||||
|
||||
case REMOVE_PARM:
|
||||
return H_PARAMETER;
|
||||
|
||||
case REMOVE_HW:
|
||||
return H_HARDWARE;
|
||||
}
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL
|
||||
#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL
|
||||
#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL
|
||||
#define H_BULK_REMOVE_END 0xc000000000000000ULL
|
||||
#define H_BULK_REMOVE_CODE 0x3000000000000000ULL
|
||||
#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL
|
||||
#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL
|
||||
#define H_BULK_REMOVE_PARM 0x2000000000000000ULL
|
||||
#define H_BULK_REMOVE_HW 0x3000000000000000ULL
|
||||
#define H_BULK_REMOVE_RC 0x0c00000000000000ULL
|
||||
#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL
|
||||
#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL
|
||||
#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL
|
||||
#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL
|
||||
#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL
|
||||
|
||||
#define H_BULK_REMOVE_MAX_BATCH 4
|
||||
|
||||
static target_ulong h_bulk_remove(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
|
||||
target_ulong *tsh = &args[i*2];
|
||||
target_ulong tsl = args[i*2 + 1];
|
||||
target_ulong v, r, ret;
|
||||
|
||||
if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) {
|
||||
break;
|
||||
} else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
*tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS;
|
||||
*tsh |= H_BULK_REMOVE_RESPONSE;
|
||||
|
||||
if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) {
|
||||
*tsh |= H_BULK_REMOVE_PARM;
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl,
|
||||
(*tsh & H_BULK_REMOVE_FLAGS) >> 26,
|
||||
&v, &r);
|
||||
|
||||
*tsh |= ret << 60;
|
||||
|
||||
switch (ret) {
|
||||
case REMOVE_SUCCESS:
|
||||
*tsh |= (r & (HPTE_R_C | HPTE_R_R)) << 43;
|
||||
break;
|
||||
|
||||
case REMOVE_PARM:
|
||||
return H_PARAMETER;
|
||||
|
||||
case REMOVE_HW:
|
||||
return H_HARDWARE;
|
||||
}
|
||||
}
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
@ -449,6 +561,67 @@ static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
|
||||
nret, rtas_r3 + 12 + 4*nargs);
|
||||
}
|
||||
|
||||
static target_ulong h_logical_load(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong size = args[0];
|
||||
target_ulong addr = args[1];
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
args[0] = ldub_phys(addr);
|
||||
return H_SUCCESS;
|
||||
case 2:
|
||||
args[0] = lduw_phys(addr);
|
||||
return H_SUCCESS;
|
||||
case 4:
|
||||
args[0] = ldl_phys(addr);
|
||||
return H_SUCCESS;
|
||||
case 8:
|
||||
args[0] = ldq_phys(addr);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
static target_ulong h_logical_store(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong size = args[0];
|
||||
target_ulong addr = args[1];
|
||||
target_ulong val = args[2];
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
stb_phys(addr, val);
|
||||
return H_SUCCESS;
|
||||
case 2:
|
||||
stw_phys(addr, val);
|
||||
return H_SUCCESS;
|
||||
case 4:
|
||||
stl_phys(addr, val);
|
||||
return H_SUCCESS;
|
||||
case 8:
|
||||
stq_phys(addr, val);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
static target_ulong h_logical_icbi(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
/* Nothing to do on emulation, KVM will trap this in the kernel */
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_logical_dcbf(CPUState *env, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
/* Nothing to do on emulation, KVM will trap this in the kernel */
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
|
||||
static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1];
|
||||
|
||||
@ -506,6 +679,9 @@ static void hypercall_init(void)
|
||||
spapr_register_hypercall(H_REMOVE, h_remove);
|
||||
spapr_register_hypercall(H_PROTECT, h_protect);
|
||||
|
||||
/* hcall-bulk */
|
||||
spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove);
|
||||
|
||||
/* hcall-dabr */
|
||||
spapr_register_hypercall(H_SET_DABR, h_set_dabr);
|
||||
|
||||
@ -513,6 +689,18 @@ static void hypercall_init(void)
|
||||
spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
|
||||
spapr_register_hypercall(H_CEDE, h_cede);
|
||||
|
||||
/* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate
|
||||
* here between the "CI" and the "CACHE" variants, they will use whatever
|
||||
* mapping attributes qemu is using. When using KVM, the kernel will
|
||||
* enforce the attributes more strongly
|
||||
*/
|
||||
spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load);
|
||||
spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store);
|
||||
spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load);
|
||||
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);
|
||||
|
||||
/* qemu/KVM-PPC specific hcalls */
|
||||
spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
|
||||
}
|
||||
|
@ -195,11 +195,9 @@ static int spapr_vlan_init(VIOsPAPRDevice *sdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
|
||||
qemu_irq qirq, uint32_t vio_irq_num)
|
||||
void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd)
|
||||
{
|
||||
DeviceState *dev;
|
||||
VIOsPAPRDevice *sdev;
|
||||
|
||||
dev = qdev_create(&bus->bus, "spapr-vlan");
|
||||
qdev_prop_set_uint32(dev, "reg", reg);
|
||||
@ -207,9 +205,6 @@ void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
|
||||
qdev_set_nic_properties(dev, nd);
|
||||
|
||||
qdev_init_nofail(dev);
|
||||
sdev = (VIOsPAPRDevice *)dev;
|
||||
sdev->qirq = qirq;
|
||||
sdev->vio_irq_num = vio_irq_num;
|
||||
}
|
||||
|
||||
static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
|
||||
@ -500,9 +495,7 @@ static VIOsPAPRDeviceInfo spapr_vlan = {
|
||||
.qdev.name = "spapr-vlan",
|
||||
.qdev.size = sizeof(VIOsPAPRVLANDevice),
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x1000),
|
||||
DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice, rtce_window_size,
|
||||
0x10000000),
|
||||
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev, 0x1000, 0x10000000),
|
||||
DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
|
@ -67,7 +67,7 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr,
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_get_timedate(&tm, 0);
|
||||
qemu_get_timedate(&tm, spapr->rtc_offset);
|
||||
|
||||
rtas_st(rets, 0, 0); /* Success */
|
||||
rtas_st(rets, 1, tm.tm_year + 1900);
|
||||
@ -79,6 +79,27 @@ static void rtas_get_time_of_day(sPAPREnvironment *spapr,
|
||||
rtas_st(rets, 7, 0); /* we don't do nanoseconds */
|
||||
}
|
||||
|
||||
static void rtas_set_time_of_day(sPAPREnvironment *spapr,
|
||||
uint32_t token, uint32_t nargs,
|
||||
target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
tm.tm_year = rtas_ld(args, 0) - 1900;
|
||||
tm.tm_mon = rtas_ld(args, 1) - 1;
|
||||
tm.tm_mday = rtas_ld(args, 2);
|
||||
tm.tm_hour = rtas_ld(args, 3);
|
||||
tm.tm_min = rtas_ld(args, 4);
|
||||
tm.tm_sec = rtas_ld(args, 5);
|
||||
|
||||
/* Just generate a monitor event for the change */
|
||||
rtc_change_mon_event(&tm);
|
||||
spapr->rtc_offset = qemu_timedate_diff(&tm);
|
||||
|
||||
rtas_st(rets, 0, 0); /* Success */
|
||||
}
|
||||
|
||||
static void rtas_power_off(sPAPREnvironment *spapr,
|
||||
uint32_t token, uint32_t nargs, target_ulong args,
|
||||
uint32_t nret, target_ulong rets)
|
||||
@ -271,6 +292,7 @@ static void register_core_rtas(void)
|
||||
{
|
||||
spapr_rtas_register("display-character", rtas_display_character);
|
||||
spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
|
||||
spapr_rtas_register("set-time-of-day", rtas_set_time_of_day);
|
||||
spapr_rtas_register("power-off", rtas_power_off);
|
||||
spapr_rtas_register("query-cpu-stopped-state",
|
||||
rtas_query_cpu_stopped_state);
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include "hw/spapr.h"
|
||||
#include "hw/spapr_vio.h"
|
||||
#include "hw/xics.h"
|
||||
|
||||
#ifdef CONFIG_FDT
|
||||
#include <libfdt.h>
|
||||
@ -51,6 +52,10 @@
|
||||
static struct BusInfo spapr_vio_bus_info = {
|
||||
.name = "spapr-vio",
|
||||
.size = sizeof(VIOsPAPRBus),
|
||||
.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, vio_irq_num, 0), \
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
|
||||
@ -603,6 +608,11 @@ static int spapr_vio_busdev_init(DeviceState *qdev, DeviceInfo *qinfo)
|
||||
|
||||
dev->qdev.id = id;
|
||||
|
||||
dev->qirq = spapr_allocate_irq(dev->vio_irq_num, &dev->vio_irq_num);
|
||||
if (!dev->qirq) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtce_init(dev);
|
||||
|
||||
return info->init(dev);
|
||||
|
@ -60,6 +60,11 @@ typedef struct VIOsPAPRDevice {
|
||||
VIOsPAPR_CRQ crq;
|
||||
} VIOsPAPRDevice;
|
||||
|
||||
#define DEFINE_SPAPR_PROPERTIES(type, field, default_reg, default_dma_window) \
|
||||
DEFINE_PROP_UINT32("reg", type, field.reg, default_reg), \
|
||||
DEFINE_PROP_UINT32("dma-window", type, field.rtce_window_size, \
|
||||
default_dma_window)
|
||||
|
||||
typedef struct VIOsPAPRBus {
|
||||
BusState bus;
|
||||
} VIOsPAPRBus;
|
||||
@ -98,15 +103,9 @@ uint64_t ldq_tce(VIOsPAPRDevice *dev, uint64_t taddr);
|
||||
int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq);
|
||||
|
||||
void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len);
|
||||
void spapr_vty_create(VIOsPAPRBus *bus,
|
||||
uint32_t reg, CharDriverState *chardev,
|
||||
qemu_irq qirq, uint32_t vio_irq_num);
|
||||
|
||||
void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd,
|
||||
qemu_irq qirq, uint32_t vio_irq_num);
|
||||
|
||||
void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
|
||||
qemu_irq qirq, uint32_t vio_irq_num);
|
||||
void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev);
|
||||
void spapr_vlan_create(VIOsPAPRBus *bus, uint32_t reg, NICInfo *nd);
|
||||
void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg);
|
||||
|
||||
int spapr_tce_set_bypass(uint32_t unit, uint32_t enable);
|
||||
void spapr_vio_quiesce(void);
|
||||
|
@ -483,7 +483,6 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
|
||||
if (status == CHECK_CONDITION) {
|
||||
req->senselen = scsi_req_get_sense(req->sreq, req->sense,
|
||||
sizeof(req->sense));
|
||||
status = 0;
|
||||
dprintf("VSCSI: Sense data, %d bytes:\n", len);
|
||||
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
req->sense[0], req->sense[1], req->sense[2], req->sense[3],
|
||||
@ -893,20 +892,14 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg,
|
||||
qemu_irq qirq, uint32_t vio_irq_num)
|
||||
void spapr_vscsi_create(VIOsPAPRBus *bus, uint32_t reg)
|
||||
{
|
||||
DeviceState *dev;
|
||||
VIOsPAPRDevice *sdev;
|
||||
|
||||
dev = qdev_create(&bus->bus, "spapr-vscsi");
|
||||
qdev_prop_set_uint32(dev, "reg", reg);
|
||||
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
sdev = (VIOsPAPRDevice *)dev;
|
||||
sdev->qirq = qirq;
|
||||
sdev->vio_irq_num = vio_irq_num;
|
||||
}
|
||||
|
||||
static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
|
||||
@ -936,9 +929,7 @@ static VIOsPAPRDeviceInfo spapr_vscsi = {
|
||||
.qdev.name = "spapr-vscsi",
|
||||
.qdev.size = sizeof(VSCSIState),
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0x2000),
|
||||
DEFINE_PROP_UINT32("dma-window", VIOsPAPRDevice,
|
||||
rtce_window_size, 0x10000000),
|
||||
DEFINE_SPAPR_PROPERTIES(VSCSIState, vdev, 0x2000, 0x10000000),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
@ -115,20 +115,14 @@ static target_ulong h_get_term_char(CPUState *env, sPAPREnvironment *spapr,
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
void spapr_vty_create(VIOsPAPRBus *bus,
|
||||
uint32_t reg, CharDriverState *chardev,
|
||||
qemu_irq qirq, uint32_t vio_irq_num)
|
||||
void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev)
|
||||
{
|
||||
DeviceState *dev;
|
||||
VIOsPAPRDevice *sdev;
|
||||
|
||||
dev = qdev_create(&bus->bus, "spapr-vty");
|
||||
qdev_prop_set_uint32(dev, "reg", reg);
|
||||
qdev_prop_set_chr(dev, "chardev", chardev);
|
||||
qdev_init_nofail(dev);
|
||||
sdev = (VIOsPAPRDevice *)dev;
|
||||
sdev->qirq = qirq;
|
||||
sdev->vio_irq_num = vio_irq_num;
|
||||
}
|
||||
|
||||
static void vty_hcalls(VIOsPAPRBus *bus)
|
||||
@ -146,7 +140,7 @@ static VIOsPAPRDeviceInfo spapr_vty = {
|
||||
.qdev.name = "spapr-vty",
|
||||
.qdev.size = sizeof(VIOsPAPRVTYDevice),
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_UINT32("reg", VIOsPAPRDevice, reg, 0),
|
||||
DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, 0, 0),
|
||||
DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
|
@ -82,7 +82,6 @@ static void mmubooke_create_initial_mapping(CPUState *env,
|
||||
static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
|
||||
int do_init,
|
||||
const char *cpu_model,
|
||||
clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
|
||||
uint32_t sysclk)
|
||||
{
|
||||
CPUState *env;
|
||||
@ -94,11 +93,7 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
|
||||
cpu_clk->opaque = env;
|
||||
/* Set time-base frequency to sysclk */
|
||||
tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR);
|
||||
tb_clk->opaque = env;
|
||||
ppc_booke_timers_init(env, sysclk, 0/* no flags */);
|
||||
|
||||
ppc_dcr_init(env, NULL, NULL);
|
||||
|
||||
@ -199,7 +194,6 @@ static void virtex_init(ram_addr_t ram_size,
|
||||
DriveInfo *dinfo;
|
||||
ram_addr_t phys_ram;
|
||||
qemu_irq irq[32], *cpu_irq;
|
||||
clk_setup_t clk_setup[7];
|
||||
int kernel_size;
|
||||
int i;
|
||||
|
||||
@ -209,8 +203,7 @@ static void virtex_init(ram_addr_t ram_size,
|
||||
}
|
||||
|
||||
memset(clk_setup, 0, sizeof(clk_setup));
|
||||
env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0],
|
||||
&clk_setup[1], 400000000);
|
||||
env = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000);
|
||||
qemu_register_reset(main_cpu_reset, env);
|
||||
|
||||
phys_ram = qemu_ram_alloc(NULL, "ram", ram_size);
|
||||
|
17
hw/xics.c
17
hw/xics.c
@ -185,17 +185,17 @@ static int ics_valid_irq(struct ics_state *ics, uint32_t nr)
|
||||
&& (nr < (ics->offset + ics->nr_irqs));
|
||||
}
|
||||
|
||||
static void ics_set_irq_msi(void *opaque, int nr, int val)
|
||||
static void ics_set_irq_msi(void *opaque, int srcno, int val)
|
||||
{
|
||||
struct ics_state *ics = (struct ics_state *)opaque;
|
||||
struct ics_irq_state *irq = ics->irqs + nr;
|
||||
struct ics_irq_state *irq = ics->irqs + srcno;
|
||||
|
||||
if (val) {
|
||||
if (irq->priority == 0xff) {
|
||||
irq->masked_pending = 1;
|
||||
/* masked pending */ ;
|
||||
} else {
|
||||
icp_irq(ics->icp, irq->server, nr + ics->offset, irq->priority);
|
||||
icp_irq(ics->icp, irq->server, srcno + ics->offset, irq->priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,7 +227,7 @@ static void ics_resend_msi(struct ics_state *ics)
|
||||
static void ics_write_xive_msi(struct ics_state *ics, int nr, int server,
|
||||
uint8_t priority)
|
||||
{
|
||||
struct ics_irq_state *irq = ics->irqs + nr;
|
||||
struct ics_irq_state *irq = ics->irqs + nr - ics->offset;
|
||||
|
||||
irq->server = server;
|
||||
irq->priority = priority;
|
||||
@ -237,7 +237,7 @@ static void ics_write_xive_msi(struct ics_state *ics, int nr, int server,
|
||||
}
|
||||
|
||||
irq->masked_pending = 0;
|
||||
icp_irq(ics->icp, server, nr + ics->offset, priority);
|
||||
icp_irq(ics->icp, server, nr, priority);
|
||||
}
|
||||
|
||||
static void ics_reject(struct ics_state *ics, int nr)
|
||||
@ -332,7 +332,7 @@ static void rtas_set_xive(sPAPREnvironment *spapr, uint32_t token,
|
||||
return;
|
||||
}
|
||||
|
||||
ics_write_xive_msi(ics, nr - ics->offset, server, priority);
|
||||
ics_write_xive_msi(ics, nr, server, priority);
|
||||
|
||||
rtas_st(rets, 0, 0); /* Success */
|
||||
}
|
||||
@ -386,7 +386,7 @@ static void rtas_int_off(sPAPREnvironment *spapr, uint32_t token,
|
||||
struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
|
||||
|
||||
irq->saved_priority = irq->priority;
|
||||
ics_write_xive_msi(xics, nr - xics->offset, irq->server, 0xff);
|
||||
ics_write_xive_msi(xics, nr, irq->server, 0xff);
|
||||
#endif
|
||||
|
||||
rtas_st(rets, 0, 0); /* Success */
|
||||
@ -416,8 +416,7 @@ static void rtas_int_on(sPAPREnvironment *spapr, uint32_t token,
|
||||
#if 0
|
||||
struct ics_irq_state *irq = xics->irqs + (nr - xics->offset);
|
||||
|
||||
ics_write_xive_msi(xics, nr - xics->offset,
|
||||
irq->server, irq->saved_priority);
|
||||
ics_write_xive_msi(xics, nr, irq->server, irq->saved_priority);
|
||||
#endif
|
||||
|
||||
rtas_st(rets, 0, 0); /* Success */
|
||||
|
@ -22,6 +22,10 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Select powerpc specific features in <linux/kvm.h> */
|
||||
#define __KVM_HAVE_SPAPR_TCE
|
||||
#define __KVM_HAVE_PPC_SMT
|
||||
|
||||
struct kvm_regs {
|
||||
__u64 pc;
|
||||
__u64 cr;
|
||||
@ -272,4 +276,57 @@ struct kvm_guest_debug_arch {
|
||||
#define KVM_INTERRUPT_UNSET -2U
|
||||
#define KVM_INTERRUPT_SET_LEVEL -3U
|
||||
|
||||
#define KVM_CPU_440 1
|
||||
#define KVM_CPU_E500V2 2
|
||||
#define KVM_CPU_3S_32 3
|
||||
#define KVM_CPU_3S_64 4
|
||||
|
||||
/* for KVM_CAP_SPAPR_TCE */
|
||||
struct kvm_create_spapr_tce {
|
||||
__u64 liobn;
|
||||
__u32 window_size;
|
||||
};
|
||||
|
||||
/* for KVM_ALLOCATE_RMA */
|
||||
struct kvm_allocate_rma {
|
||||
__u64 rma_size;
|
||||
};
|
||||
|
||||
struct kvm_book3e_206_tlb_entry {
|
||||
__u32 mas8;
|
||||
__u32 mas1;
|
||||
__u64 mas2;
|
||||
__u64 mas7_3;
|
||||
};
|
||||
|
||||
struct kvm_book3e_206_tlb_params {
|
||||
/*
|
||||
* For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV:
|
||||
*
|
||||
* - The number of ways of TLB0 must be a power of two between 2 and
|
||||
* 16.
|
||||
* - TLB1 must be fully associative.
|
||||
* - The size of TLB0 must be a multiple of the number of ways, and
|
||||
* the number of sets must be a power of two.
|
||||
* - The size of TLB1 may not exceed 64 entries.
|
||||
* - TLB0 supports 4 KiB pages.
|
||||
* - The page sizes supported by TLB1 are as indicated by
|
||||
* TLB1CFG (if MMUCFG[MAVN] = 0) or TLB1PS (if MMUCFG[MAVN] = 1)
|
||||
* as returned by KVM_GET_SREGS.
|
||||
* - TLB2 and TLB3 are reserved, and their entries in tlb_sizes[]
|
||||
* and tlb_ways[] must be zero.
|
||||
*
|
||||
* tlb_ways[n] = tlb_sizes[n] means the array is fully associative.
|
||||
*
|
||||
* KVM will adjust TLBnCFG based on the sizes configured here,
|
||||
* though arrays greater than 2048 entries will have TLBnCFG[NENTRY]
|
||||
* set to zero.
|
||||
*/
|
||||
__u32 tlb_sizes[4];
|
||||
__u32 tlb_ways[4];
|
||||
__u32 reserved[8];
|
||||
};
|
||||
|
||||
#define KVM_ONE_REG_PPC_HIOR KVM_ONE_REG_PPC | 0x100
|
||||
|
||||
#endif /* __LINUX_KVM_POWERPC_H */
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
#define KVM_FEATURE_CLOCKSOURCE2 3
|
||||
#define KVM_FEATURE_ASYNC_PF 4
|
||||
#define KVM_FEATURE_STEAL_TIME 5
|
||||
|
||||
/* The last 8 bits are used to indicate how to interpret the flags field
|
||||
* in pvclock structure. If no bits are set, all flags are ignored.
|
||||
@ -30,10 +31,23 @@
|
||||
#define MSR_KVM_WALL_CLOCK 0x11
|
||||
#define MSR_KVM_SYSTEM_TIME 0x12
|
||||
|
||||
#define KVM_MSR_ENABLED 1
|
||||
/* Custom MSRs falls in the range 0x4b564d00-0x4b564dff */
|
||||
#define MSR_KVM_WALL_CLOCK_NEW 0x4b564d00
|
||||
#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01
|
||||
#define MSR_KVM_ASYNC_PF_EN 0x4b564d02
|
||||
#define MSR_KVM_STEAL_TIME 0x4b564d03
|
||||
|
||||
struct kvm_steal_time {
|
||||
__u64 steal;
|
||||
__u32 version;
|
||||
__u32 flags;
|
||||
__u32 pad[12];
|
||||
};
|
||||
|
||||
#define KVM_STEAL_ALIGNMENT_BITS 5
|
||||
#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1)))
|
||||
#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1)
|
||||
|
||||
#define KVM_MAX_MMU_OP_BATCH 32
|
||||
|
||||
|
@ -161,6 +161,7 @@ struct kvm_pit_config {
|
||||
#define KVM_EXIT_NMI 16
|
||||
#define KVM_EXIT_INTERNAL_ERROR 17
|
||||
#define KVM_EXIT_OSI 18
|
||||
#define KVM_EXIT_PAPR_HCALL 19
|
||||
|
||||
/* For KVM_EXIT_INTERNAL_ERROR */
|
||||
#define KVM_INTERNAL_ERROR_EMULATION 1
|
||||
@ -264,6 +265,11 @@ struct kvm_run {
|
||||
struct {
|
||||
__u64 gprs[32];
|
||||
} osi;
|
||||
struct {
|
||||
__u64 nr;
|
||||
__u64 ret;
|
||||
__u64 args[9];
|
||||
} papr_hcall;
|
||||
/* Fix the size of the union. */
|
||||
char padding[256];
|
||||
};
|
||||
@ -457,7 +463,7 @@ struct kvm_ppc_pvinfo {
|
||||
#define KVM_CAP_VAPIC 6
|
||||
#define KVM_CAP_EXT_CPUID 7
|
||||
#define KVM_CAP_CLOCKSOURCE 8
|
||||
#define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */
|
||||
#define KVM_CAP_NR_VCPUS 9 /* returns recommended max vcpus per vm */
|
||||
#define KVM_CAP_NR_MEMSLOTS 10 /* returns max memory slots per vm */
|
||||
#define KVM_CAP_PIT 11
|
||||
#define KVM_CAP_NOP_IO_DELAY 12
|
||||
@ -544,6 +550,14 @@ struct kvm_ppc_pvinfo {
|
||||
#define KVM_CAP_TSC_CONTROL 60
|
||||
#define KVM_CAP_GET_TSC_KHZ 61
|
||||
#define KVM_CAP_PPC_BOOKE_SREGS 62
|
||||
#define KVM_CAP_SPAPR_TCE 63
|
||||
#define KVM_CAP_PPC_SMT 64
|
||||
#define KVM_CAP_PPC_RMA 65
|
||||
#define KVM_CAP_MAX_VCPUS 66 /* returns max vcpus per vm */
|
||||
#define KVM_CAP_PPC_HIOR 67
|
||||
#define KVM_CAP_PPC_PAPR 68
|
||||
#define KVM_CAP_SW_TLB 69
|
||||
#define KVM_CAP_ONE_REG 70
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
@ -623,6 +637,49 @@ struct kvm_clock_data {
|
||||
__u32 pad[9];
|
||||
};
|
||||
|
||||
#define KVM_MMU_FSL_BOOKE_NOHV 0
|
||||
#define KVM_MMU_FSL_BOOKE_HV 1
|
||||
|
||||
struct kvm_config_tlb {
|
||||
__u64 params;
|
||||
__u64 array;
|
||||
__u32 mmu_type;
|
||||
__u32 array_len;
|
||||
};
|
||||
|
||||
struct kvm_dirty_tlb {
|
||||
__u64 bitmap;
|
||||
__u32 num_dirty;
|
||||
};
|
||||
|
||||
/* Available with KVM_CAP_ONE_REG */
|
||||
|
||||
#define KVM_ONE_REG_GENERIC 0x0000000000000000ULL
|
||||
|
||||
/*
|
||||
* Architecture specific registers are to be defined in arch headers and
|
||||
* ORed with the arch identifier.
|
||||
*/
|
||||
#define KVM_ONE_REG_PPC 0x1000000000000000ULL
|
||||
#define KVM_ONE_REG_X86 0x2000000000000000ULL
|
||||
#define KVM_ONE_REG_IA64 0x3000000000000000ULL
|
||||
#define KVM_ONE_REG_ARM 0x4000000000000000ULL
|
||||
#define KVM_ONE_REG_S390 0x5000000000000000ULL
|
||||
|
||||
struct kvm_one_reg {
|
||||
__u64 id;
|
||||
union {
|
||||
__u8 reg8;
|
||||
__u16 reg16;
|
||||
__u32 reg32;
|
||||
__u64 reg64;
|
||||
__u8 reg128[16];
|
||||
__u8 reg256[32];
|
||||
__u8 reg512[64];
|
||||
__u8 reg1024[128];
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
* ioctls for VM fds
|
||||
*/
|
||||
@ -746,6 +803,14 @@ struct kvm_clock_data {
|
||||
/* Available with KVM_CAP_XCRS */
|
||||
#define KVM_GET_XCRS _IOR(KVMIO, 0xa6, struct kvm_xcrs)
|
||||
#define KVM_SET_XCRS _IOW(KVMIO, 0xa7, struct kvm_xcrs)
|
||||
#define KVM_CREATE_SPAPR_TCE _IOW(KVMIO, 0xa8, struct kvm_create_spapr_tce)
|
||||
/* Available with KVM_CAP_RMA */
|
||||
#define KVM_ALLOCATE_RMA _IOR(KVMIO, 0xa9, struct kvm_allocate_rma)
|
||||
/* Available with KVM_CAP_SW_TLB */
|
||||
#define KVM_DIRTY_TLB _IOW(KVMIO, 0xaa, struct kvm_dirty_tlb)
|
||||
/* Available with KVM_CAP_ONE_REG */
|
||||
#define KVM_GET_ONE_REG _IOWR(KVMIO, 0xab, struct kvm_one_reg)
|
||||
#define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg)
|
||||
|
||||
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
|
||||
|
||||
@ -773,20 +838,14 @@ struct kvm_assigned_pci_dev {
|
||||
|
||||
struct kvm_assigned_irq {
|
||||
__u32 assigned_dev_id;
|
||||
__u32 host_irq;
|
||||
__u32 host_irq; /* ignored (legacy field) */
|
||||
__u32 guest_irq;
|
||||
__u32 flags;
|
||||
union {
|
||||
struct {
|
||||
__u32 addr_lo;
|
||||
__u32 addr_hi;
|
||||
__u32 data;
|
||||
} guest_msi;
|
||||
__u32 reserved[12];
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
struct kvm_assigned_msix_nr {
|
||||
__u32 assigned_dev_id;
|
||||
__u16 entry_nr;
|
||||
|
@ -26,3 +26,4 @@
|
||||
#include <asm/kvm_para.h>
|
||||
|
||||
#endif /* __LINUX_KVM_PARA_H */
|
||||
|
||||
|
121
memory.c
121
memory.c
@ -1271,3 +1271,124 @@ void set_system_io_map(MemoryRegion *mr)
|
||||
address_space_io.root = mr;
|
||||
memory_region_update_topology();
|
||||
}
|
||||
|
||||
typedef struct MemoryRegionList MemoryRegionList;
|
||||
|
||||
struct MemoryRegionList {
|
||||
const MemoryRegion *mr;
|
||||
bool printed;
|
||||
QTAILQ_ENTRY(MemoryRegionList) queue;
|
||||
};
|
||||
|
||||
typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead;
|
||||
|
||||
static void mtree_print_mr(fprintf_function mon_printf, void *f,
|
||||
const MemoryRegion *mr, unsigned int level,
|
||||
target_phys_addr_t base,
|
||||
MemoryRegionListHead *alias_print_queue)
|
||||
{
|
||||
MemoryRegionList *new_ml, *ml, *next_ml;
|
||||
MemoryRegionListHead submr_print_queue;
|
||||
const MemoryRegion *submr;
|
||||
unsigned int i;
|
||||
|
||||
if (!mr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < level; i++) {
|
||||
mon_printf(f, " ");
|
||||
}
|
||||
|
||||
if (mr->alias) {
|
||||
MemoryRegionList *ml;
|
||||
bool found = false;
|
||||
|
||||
/* check if the alias is already in the queue */
|
||||
QTAILQ_FOREACH(ml, alias_print_queue, queue) {
|
||||
if (ml->mr == mr->alias && !ml->printed) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
ml = g_new(MemoryRegionList, 1);
|
||||
ml->mr = mr->alias;
|
||||
ml->printed = false;
|
||||
QTAILQ_INSERT_TAIL(alias_print_queue, ml, queue);
|
||||
}
|
||||
mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): alias %s @%s "
|
||||
TARGET_FMT_plx "-" TARGET_FMT_plx "\n",
|
||||
base + mr->addr,
|
||||
base + mr->addr + (target_phys_addr_t)mr->size - 1,
|
||||
mr->priority,
|
||||
mr->name,
|
||||
mr->alias->name,
|
||||
mr->alias_offset,
|
||||
mr->alias_offset + (target_phys_addr_t)mr->size - 1);
|
||||
} else {
|
||||
mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d): %s\n",
|
||||
base + mr->addr,
|
||||
base + mr->addr + (target_phys_addr_t)mr->size - 1,
|
||||
mr->priority,
|
||||
mr->name);
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&submr_print_queue);
|
||||
|
||||
QTAILQ_FOREACH(submr, &mr->subregions, subregions_link) {
|
||||
new_ml = g_new(MemoryRegionList, 1);
|
||||
new_ml->mr = submr;
|
||||
QTAILQ_FOREACH(ml, &submr_print_queue, queue) {
|
||||
if (new_ml->mr->addr < ml->mr->addr ||
|
||||
(new_ml->mr->addr == ml->mr->addr &&
|
||||
new_ml->mr->priority > ml->mr->priority)) {
|
||||
QTAILQ_INSERT_BEFORE(ml, new_ml, queue);
|
||||
new_ml = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (new_ml) {
|
||||
QTAILQ_INSERT_TAIL(&submr_print_queue, new_ml, queue);
|
||||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(ml, &submr_print_queue, queue) {
|
||||
mtree_print_mr(mon_printf, f, ml->mr, level + 1, base + mr->addr,
|
||||
alias_print_queue);
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH_SAFE(next_ml, &submr_print_queue, queue, ml) {
|
||||
g_free(ml);
|
||||
}
|
||||
}
|
||||
|
||||
void mtree_info(fprintf_function mon_printf, void *f)
|
||||
{
|
||||
MemoryRegionListHead ml_head;
|
||||
MemoryRegionList *ml, *ml2;
|
||||
|
||||
QTAILQ_INIT(&ml_head);
|
||||
|
||||
mon_printf(f, "memory\n");
|
||||
mtree_print_mr(mon_printf, f, address_space_memory.root, 0, 0, &ml_head);
|
||||
|
||||
/* print aliased regions */
|
||||
QTAILQ_FOREACH(ml, &ml_head, queue) {
|
||||
if (!ml->printed) {
|
||||
mon_printf(f, "%s\n", ml->mr->name);
|
||||
mtree_print_mr(mon_printf, f, ml->mr, 0, 0, &ml_head);
|
||||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH_SAFE(ml, &ml_head, queue, ml2) {
|
||||
g_free(ml2);
|
||||
}
|
||||
|
||||
if (address_space_io.root &&
|
||||
!QTAILQ_EMPTY(&address_space_io.root->subregions)) {
|
||||
QTAILQ_INIT(&ml_head);
|
||||
mon_printf(f, "I/O\n");
|
||||
mtree_print_mr(mon_printf, f, address_space_io.root, 0, 0, &ml_head);
|
||||
}
|
||||
}
|
||||
|
2
memory.h
2
memory.h
@ -501,6 +501,8 @@ void memory_region_transaction_begin(void);
|
||||
*/
|
||||
void memory_region_transaction_commit(void);
|
||||
|
||||
void mtree_info(fprintf_function mon_printf, void *f);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
25
monitor.c
25
monitor.c
@ -57,12 +57,13 @@
|
||||
#include "json-parser.h"
|
||||
#include "osdep.h"
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "trace/control.h"
|
||||
#ifdef CONFIG_TRACE_SIMPLE
|
||||
#include "trace/simple.h"
|
||||
#endif
|
||||
#include "trace/control.h"
|
||||
#include "ui/qemu-spice.h"
|
||||
#include "memory.h"
|
||||
|
||||
//#define DEBUG
|
||||
//#define DEBUG_COMPLETION
|
||||
@ -369,6 +370,8 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data)
|
||||
{
|
||||
QDict *qmp;
|
||||
|
||||
trace_monitor_protocol_emitter(mon);
|
||||
|
||||
qmp = qdict_new();
|
||||
|
||||
if (!monitor_has_error(mon)) {
|
||||
@ -605,7 +608,7 @@ static void do_trace_event_set_state(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SIMPLE_TRACE
|
||||
#ifdef CONFIG_TRACE_SIMPLE
|
||||
static void do_trace_file(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
const char *op = qdict_get_try_str(qdict, "op");
|
||||
@ -2461,7 +2464,7 @@ static void tlb_info(Monitor *mon)
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_SPARC)
|
||||
#if defined(TARGET_SPARC) || defined(TARGET_PPC)
|
||||
static void tlb_info(Monitor *mon)
|
||||
{
|
||||
CPUState *env1 = mon_get_cpu();
|
||||
@ -2470,6 +2473,11 @@ static void tlb_info(Monitor *mon)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void do_info_mtree(Monitor *mon)
|
||||
{
|
||||
mtree_info((fprintf_function)monitor_printf, mon);
|
||||
}
|
||||
|
||||
static void do_info_kvm_print(Monitor *mon, const QObject *data)
|
||||
{
|
||||
QDict *qdict;
|
||||
@ -2959,7 +2967,8 @@ static const mon_cmd_t info_cmds[] = {
|
||||
.user_print = do_pci_info_print,
|
||||
.mhandler.info_new = do_pci_info,
|
||||
},
|
||||
#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC)
|
||||
#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \
|
||||
defined(TARGET_PPC)
|
||||
{
|
||||
.name = "tlb",
|
||||
.args_type = "",
|
||||
@ -2977,6 +2986,13 @@ static const mon_cmd_t info_cmds[] = {
|
||||
.mhandler.info = mem_info,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.name = "mtree",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show memory tree",
|
||||
.mhandler.info = do_info_mtree,
|
||||
},
|
||||
{
|
||||
.name = "jit",
|
||||
.args_type = "",
|
||||
@ -5089,6 +5105,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
|
||||
qobject_incref(mon->mc->id);
|
||||
|
||||
cmd_name = qdict_get_str(input, "execute");
|
||||
trace_handle_qmp_command(mon, cmd_name);
|
||||
if (invalid_qmp_mode(mon, cmd_name)) {
|
||||
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
|
||||
goto err_out;
|
||||
|
@ -32,3 +32,6 @@
|
||||
- The S390 zipl loader is an addition to the official IBM s390-tools
|
||||
package. That fork is maintained in its own git repository at:
|
||||
git://repo.or.cz/s390-tools.git
|
||||
|
||||
- The sources for the Alpha palcode image is available from:
|
||||
git://repo.or.cz/qemu-palcode.git
|
||||
|
Binary file not shown.
@ -25,18 +25,6 @@
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
PowerPC,8544@0 {
|
||||
device_type = "cpu";
|
||||
reg = <0x0>;
|
||||
d-cache-line-size = <32>; // 32 bytes
|
||||
i-cache-line-size = <32>; // 32 bytes
|
||||
d-cache-size = <0x8000>; // L1, 32K
|
||||
i-cache-size = <0x8000>; // L1, 32K
|
||||
timebase-frequency = <0>;
|
||||
bus-frequency = <0>;
|
||||
clock-frequency = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
memory {
|
||||
|
BIN
pc-bios/palcode-clipper
Executable file
BIN
pc-bios/palcode-clipper
Executable file
Binary file not shown.
4
ppc64.ld
4
ppc64.ld
@ -81,8 +81,8 @@ SECTIONS
|
||||
.sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
|
||||
.sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) }
|
||||
*(.gcc_except_table.*) } /* Adjust the address for the data segment. We want to
|
||||
adjust up to + the same address within the page on the next page up. */
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN (0x10000) - ((0x10000 - .) & (0x10000 - 1)); . = DATA_SEGMENT_ALIGN
|
||||
(0x10000, 0x1000); /* Exception handling */
|
||||
.eh_frame : { KEEP (*(.eh_frame)) }
|
||||
|
1
roms/qemu-palcode
Submodule
1
roms/qemu-palcode
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 7abb12f60eb3069019e9497e193733d77d8f0722
|
@ -265,6 +265,10 @@ struct CPUAlphaState {
|
||||
uint64_t scratch[24];
|
||||
#endif
|
||||
|
||||
/* This alarm doesn't exist in real hardware; we wish it did. */
|
||||
struct QEMUTimer *alarm_timer;
|
||||
uint64_t alarm_expire;
|
||||
|
||||
#if TARGET_LONG_BITS > HOST_LONG_BITS
|
||||
/* temporary fixed-point registers
|
||||
* used to emulate 64 bits target on 32 bits hosts
|
||||
|
@ -113,6 +113,11 @@ DEF_HELPER_2(stq_c_phys, i64, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_0(tbia, TCG_CALL_CONST, void)
|
||||
DEF_HELPER_FLAGS_1(tbis, TCG_CALL_CONST, void, i64)
|
||||
|
||||
DEF_HELPER_1(halt, void, i64);
|
||||
|
||||
DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64)
|
||||
DEF_HELPER_FLAGS_1(set_alarm, TCG_CALL_CONST, void, i64)
|
||||
#endif
|
||||
|
||||
#include "def-helper.h"
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "host-utils.h"
|
||||
#include "softfloat.h"
|
||||
#include "helper.h"
|
||||
#include "sysemu.h"
|
||||
#include "qemu-timer.h"
|
||||
|
||||
#define FP_STATUS (env->fp_status)
|
||||
@ -1218,6 +1219,30 @@ void helper_tbis(uint64_t p)
|
||||
{
|
||||
tlb_flush_page(env, p);
|
||||
}
|
||||
|
||||
void helper_halt(uint64_t restart)
|
||||
{
|
||||
if (restart) {
|
||||
qemu_system_reset_request();
|
||||
} else {
|
||||
qemu_system_shutdown_request();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t helper_get_time(void)
|
||||
{
|
||||
return qemu_get_clock_ns(rtc_clock);
|
||||
}
|
||||
|
||||
void helper_set_alarm(uint64_t expire)
|
||||
{
|
||||
if (expire) {
|
||||
env->alarm_expire = expire;
|
||||
qemu_mod_timer(env->alarm_timer, expire);
|
||||
} else {
|
||||
qemu_del_timer(env->alarm_timer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -1590,18 +1590,34 @@ static int cpu_pr_data(int pr)
|
||||
return offsetof(CPUAlphaState, shadow[pr - 32]);
|
||||
case 40 ... 63:
|
||||
return offsetof(CPUAlphaState, scratch[pr - 40]);
|
||||
|
||||
case 251:
|
||||
return offsetof(CPUAlphaState, alarm_expire);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gen_mfpr(int ra, int regno)
|
||||
static ExitStatus gen_mfpr(int ra, int regno)
|
||||
{
|
||||
int data = cpu_pr_data(regno);
|
||||
|
||||
/* In our emulated PALcode, these processor registers have no
|
||||
side effects from reading. */
|
||||
if (ra == 31) {
|
||||
return;
|
||||
return NO_EXIT;
|
||||
}
|
||||
|
||||
if (regno == 250) {
|
||||
/* WALL_TIME */
|
||||
if (use_icount) {
|
||||
gen_io_start();
|
||||
gen_helper_get_time(cpu_ir[ra]);
|
||||
gen_io_end();
|
||||
return EXIT_PC_STALE;
|
||||
} else {
|
||||
gen_helper_get_time(cpu_ir[ra]);
|
||||
return NO_EXIT;
|
||||
}
|
||||
}
|
||||
|
||||
/* The basic registers are data only, and unknown registers
|
||||
@ -1615,11 +1631,13 @@ static void gen_mfpr(int ra, int regno)
|
||||
} else {
|
||||
tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data);
|
||||
}
|
||||
return NO_EXIT;
|
||||
}
|
||||
|
||||
static void gen_mtpr(int rb, int regno)
|
||||
static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno)
|
||||
{
|
||||
TCGv tmp;
|
||||
int data;
|
||||
|
||||
if (rb == 31) {
|
||||
tmp = tcg_const_i64(0);
|
||||
@ -1627,19 +1645,37 @@ static void gen_mtpr(int rb, int regno)
|
||||
tmp = cpu_ir[rb];
|
||||
}
|
||||
|
||||
/* These two register numbers perform a TLB cache flush. Thankfully we
|
||||
can only do this inside PALmode, which means that the current basic
|
||||
block cannot be affected by the change in mappings. */
|
||||
if (regno == 255) {
|
||||
switch (regno) {
|
||||
case 255:
|
||||
/* TBIA */
|
||||
gen_helper_tbia();
|
||||
} else if (regno == 254) {
|
||||
break;
|
||||
|
||||
case 254:
|
||||
/* TBIS */
|
||||
gen_helper_tbis(tmp);
|
||||
} else {
|
||||
break;
|
||||
|
||||
case 253:
|
||||
/* WAIT */
|
||||
tmp = tcg_const_i64(1);
|
||||
tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUState, halted));
|
||||
return gen_excp(ctx, EXCP_HLT, 0);
|
||||
|
||||
case 252:
|
||||
/* HALT */
|
||||
gen_helper_halt(tmp);
|
||||
return EXIT_PC_STALE;
|
||||
|
||||
case 251:
|
||||
/* ALARM */
|
||||
gen_helper_set_alarm(tmp);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* The basic registers are data only, and unknown registers
|
||||
are read-zero, write-ignore. */
|
||||
int data = cpu_pr_data(regno);
|
||||
data = cpu_pr_data(regno);
|
||||
if (data != 0) {
|
||||
if (data & PR_BYTE) {
|
||||
tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE);
|
||||
@ -1649,11 +1685,14 @@ static void gen_mtpr(int rb, int regno)
|
||||
tcg_gen_st_i64(tmp, cpu_env, data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (rb == 31) {
|
||||
tcg_temp_free(tmp);
|
||||
}
|
||||
|
||||
return NO_EXIT;
|
||||
}
|
||||
#endif /* !USER_ONLY*/
|
||||
|
||||
@ -2721,8 +2760,16 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
|
||||
break;
|
||||
case 0xC000:
|
||||
/* RPCC */
|
||||
if (ra != 31)
|
||||
gen_helper_load_pcc(cpu_ir[ra]);
|
||||
if (ra != 31) {
|
||||
if (use_icount) {
|
||||
gen_io_start();
|
||||
gen_helper_load_pcc(cpu_ir[ra]);
|
||||
gen_io_end();
|
||||
ret = EXIT_PC_STALE;
|
||||
} else {
|
||||
gen_helper_load_pcc(cpu_ir[ra]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xE000:
|
||||
/* RC */
|
||||
@ -2747,8 +2794,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
|
||||
/* HW_MFPR (PALcode) */
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (ctx->tb->flags & TB_FLAGS_PAL_MODE) {
|
||||
gen_mfpr(ra, insn & 0xffff);
|
||||
break;
|
||||
return gen_mfpr(ra, insn & 0xffff);
|
||||
}
|
||||
#endif
|
||||
goto invalid_opc;
|
||||
@ -3053,8 +3099,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
|
||||
/* HW_MTPR (PALcode) */
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (ctx->tb->flags & TB_FLAGS_PAL_MODE) {
|
||||
gen_mtpr(rb, insn & 0xffff);
|
||||
break;
|
||||
return gen_mtpr(ctx, rb, insn & 0xffff);
|
||||
}
|
||||
#endif
|
||||
goto invalid_opc;
|
||||
|
@ -84,6 +84,7 @@ void tlb_fill(CPUState *env1, target_ulong addr, int is_write, int mmu_idx,
|
||||
int ret;
|
||||
|
||||
saved_env = env;
|
||||
env = env1;
|
||||
ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx);
|
||||
if (unlikely(ret)) {
|
||||
if (retaddr) {
|
||||
|
@ -218,6 +218,7 @@ CPUState *cpu_lm32_init(const char *cpu_model)
|
||||
|
||||
cpu_exec_init(env);
|
||||
cpu_reset(env);
|
||||
qemu_init_vcpu(env);
|
||||
|
||||
if (!tcg_initialized) {
|
||||
tcg_initialized = 1;
|
||||
|
@ -555,6 +555,8 @@ enum {
|
||||
/* Decrementer clock: RTC clock (POWER, 601) or bus clock */
|
||||
POWERPC_FLAG_RTC_CLK = 0x00010000,
|
||||
POWERPC_FLAG_BUS_CLK = 0x00020000,
|
||||
/* Has CFAR */
|
||||
POWERPC_FLAG_CFAR = 0x00040000,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -667,8 +669,8 @@ enum {
|
||||
#define MAS0_ATSEL_TLB 0
|
||||
#define MAS0_ATSEL_LRAT MAS0_ATSEL
|
||||
|
||||
#define MAS1_TSIZE_SHIFT 8
|
||||
#define MAS1_TSIZE_MASK (0xf << MAS1_TSIZE_SHIFT)
|
||||
#define MAS1_TSIZE_SHIFT 7
|
||||
#define MAS1_TSIZE_MASK (0x1f << MAS1_TSIZE_SHIFT)
|
||||
|
||||
#define MAS1_TS_SHIFT 12
|
||||
#define MAS1_TS (1 << MAS1_TS_SHIFT)
|
||||
@ -872,6 +874,10 @@ struct CPUPPCState {
|
||||
target_ulong ctr;
|
||||
/* condition register */
|
||||
uint32_t crf[8];
|
||||
#if defined(TARGET_PPC64)
|
||||
/* CFAR */
|
||||
target_ulong cfar;
|
||||
#endif
|
||||
/* XER */
|
||||
target_ulong xer;
|
||||
/* Reservation address */
|
||||
@ -934,6 +940,8 @@ struct CPUPPCState {
|
||||
ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */
|
||||
/* 403 dedicated access protection registers */
|
||||
target_ulong pb[4];
|
||||
bool tlb_dirty; /* Set to non-zero when modifying TLB */
|
||||
bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */
|
||||
#endif
|
||||
|
||||
/* Other registers */
|
||||
@ -1010,8 +1018,35 @@ struct CPUPPCState {
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void *load_info; /* Holds boot loading state. */
|
||||
#endif
|
||||
|
||||
/* booke timers */
|
||||
|
||||
/* Specifies bit locations of the Time Base used to signal a fixed timer
|
||||
* exception on a transition from 0 to 1. (watchdog or fixed-interval timer)
|
||||
*
|
||||
* 0 selects the least significant bit.
|
||||
* 63 selects the most significant bit.
|
||||
*/
|
||||
uint8_t fit_period[4];
|
||||
uint8_t wdt_period[4];
|
||||
};
|
||||
|
||||
#define SET_FIT_PERIOD(a_, b_, c_, d_) \
|
||||
do { \
|
||||
env->fit_period[0] = (a_); \
|
||||
env->fit_period[1] = (b_); \
|
||||
env->fit_period[2] = (c_); \
|
||||
env->fit_period[3] = (d_); \
|
||||
} while (0)
|
||||
|
||||
#define SET_WDT_PERIOD(a_, b_, c_, d_) \
|
||||
do { \
|
||||
env->wdt_period[0] = (a_); \
|
||||
env->wdt_period[1] = (b_); \
|
||||
env->wdt_period[2] = (c_); \
|
||||
env->wdt_period[3] = (d_); \
|
||||
} while (0)
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* Context used internally during MMU translations */
|
||||
typedef struct mmu_ctx_t mmu_ctx_t;
|
||||
@ -1202,6 +1237,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
|
||||
#define SPR_601_UDECR (0x006)
|
||||
#define SPR_LR (0x008)
|
||||
#define SPR_CTR (0x009)
|
||||
#define SPR_DSCR (0x011)
|
||||
#define SPR_DSISR (0x012)
|
||||
#define SPR_DAR (0x013) /* DAE for PowerPC 601 */
|
||||
#define SPR_601_RTCU (0x014)
|
||||
@ -1210,6 +1246,7 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
|
||||
#define SPR_SDR1 (0x019)
|
||||
#define SPR_SRR0 (0x01A)
|
||||
#define SPR_SRR1 (0x01B)
|
||||
#define SPR_CFAR (0x01C)
|
||||
#define SPR_AMR (0x01D)
|
||||
#define SPR_BOOKE_PID (0x030)
|
||||
#define SPR_BOOKE_DECAR (0x036)
|
||||
@ -2043,4 +2080,6 @@ static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
|
||||
env->nip = tb->pc;
|
||||
}
|
||||
|
||||
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env);
|
||||
|
||||
#endif /* !defined (__CPU_PPC_H__) */
|
||||
|
@ -1293,7 +1293,7 @@ target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb)
|
||||
{
|
||||
uint32_t tlbncfg;
|
||||
int tlbn = booke206_tlbm_to_tlbn(env, tlb);
|
||||
target_phys_addr_t tlbm_size;
|
||||
int tlbm_size;
|
||||
|
||||
tlbncfg = env->spr[SPR_BOOKE_TLB0CFG + tlbn];
|
||||
|
||||
@ -1301,9 +1301,10 @@ target_phys_addr_t booke206_tlb_to_page_size(CPUState *env, ppcmas_tlb_t *tlb)
|
||||
tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT;
|
||||
} else {
|
||||
tlbm_size = (tlbncfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT;
|
||||
tlbm_size <<= 1;
|
||||
}
|
||||
|
||||
return (1 << (tlbm_size << 1)) << 10;
|
||||
return 1024ULL << tlbm_size;
|
||||
}
|
||||
|
||||
/* TLB check function for MAS based SoftTLBs */
|
||||
@ -1465,6 +1466,94 @@ found_tlb:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *book3e_tsize_to_str[32] = {
|
||||
"1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K",
|
||||
"1M", "2M", "4M", "8M", "16M", "32M", "64M", "128M", "256M", "512M",
|
||||
"1G", "2G", "4G", "8G", "16G", "32G", "64G", "128G", "256G", "512G",
|
||||
"1T", "2T"
|
||||
};
|
||||
|
||||
static void mmubooke206_dump_one_tlb(FILE *f, fprintf_function cpu_fprintf,
|
||||
CPUState *env, int tlbn, int offset,
|
||||
int tlbsize)
|
||||
{
|
||||
ppcmas_tlb_t *entry;
|
||||
int i;
|
||||
|
||||
cpu_fprintf(f, "\nTLB%d:\n", tlbn);
|
||||
cpu_fprintf(f, "Effective Physical Size TID TS SRWX URWX WIMGE U0123\n");
|
||||
|
||||
entry = &env->tlb.tlbm[offset];
|
||||
for (i = 0; i < tlbsize; i++, entry++) {
|
||||
target_phys_addr_t ea, pa, size;
|
||||
int tsize;
|
||||
|
||||
if (!(entry->mas1 & MAS1_VALID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tsize = (entry->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT;
|
||||
size = 1024ULL << tsize;
|
||||
ea = entry->mas2 & ~(size - 1);
|
||||
pa = entry->mas7_3 & ~(size - 1);
|
||||
|
||||
cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %4s %-5u %1u S%c%c%c U%c%c%c %c%c%c%c%c U%c%c%c%c\n",
|
||||
(uint64_t)ea, (uint64_t)pa,
|
||||
book3e_tsize_to_str[tsize],
|
||||
(entry->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT,
|
||||
(entry->mas1 & MAS1_TS) >> MAS1_TS_SHIFT,
|
||||
entry->mas7_3 & MAS3_SR ? 'R' : '-',
|
||||
entry->mas7_3 & MAS3_SW ? 'W' : '-',
|
||||
entry->mas7_3 & MAS3_SX ? 'X' : '-',
|
||||
entry->mas7_3 & MAS3_UR ? 'R' : '-',
|
||||
entry->mas7_3 & MAS3_UW ? 'W' : '-',
|
||||
entry->mas7_3 & MAS3_UX ? 'X' : '-',
|
||||
entry->mas2 & MAS2_W ? 'W' : '-',
|
||||
entry->mas2 & MAS2_I ? 'I' : '-',
|
||||
entry->mas2 & MAS2_M ? 'M' : '-',
|
||||
entry->mas2 & MAS2_G ? 'G' : '-',
|
||||
entry->mas2 & MAS2_E ? 'E' : '-',
|
||||
entry->mas7_3 & MAS3_U0 ? '0' : '-',
|
||||
entry->mas7_3 & MAS3_U1 ? '1' : '-',
|
||||
entry->mas7_3 & MAS3_U2 ? '2' : '-',
|
||||
entry->mas7_3 & MAS3_U3 ? '3' : '-');
|
||||
}
|
||||
}
|
||||
|
||||
static void mmubooke206_dump_mmu(FILE *f, fprintf_function cpu_fprintf,
|
||||
CPUState *env)
|
||||
{
|
||||
int offset = 0;
|
||||
int i;
|
||||
|
||||
if (kvm_enabled() && !env->kvm_sw_tlb) {
|
||||
cpu_fprintf(f, "Cannot access KVM TLB\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
|
||||
int size = booke206_tlb_size(env, i);
|
||||
|
||||
if (size == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mmubooke206_dump_one_tlb(f, cpu_fprintf, env, i, offset, size);
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
|
||||
{
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_BOOKE206:
|
||||
mmubooke206_dump_mmu(f, cpu_fprintf, env);
|
||||
break;
|
||||
default:
|
||||
cpu_fprintf(f, "%s: unimplemented\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int check_physical(CPUState *env, mmu_ctx_t *ctx,
|
||||
target_ulong eaddr, int rw)
|
||||
{
|
||||
|
211
target-ppc/kvm.c
211
target-ppc/kvm.c
@ -14,6 +14,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
@ -28,6 +29,10 @@
|
||||
#include "cpu.h"
|
||||
#include "device_tree.h"
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/spapr.h"
|
||||
#include "hw/spapr_vio.h"
|
||||
|
||||
//#define DEBUG_KVM
|
||||
|
||||
#ifdef DEBUG_KVM
|
||||
@ -38,6 +43,8 @@
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
#define PROC_DEVTREE_CPU "/proc/device-tree/cpus/"
|
||||
|
||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
@ -105,6 +112,52 @@ static int kvm_arch_sync_sregs(CPUState *cenv)
|
||||
return kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs);
|
||||
}
|
||||
|
||||
/* Set up a shared TLB array with KVM */
|
||||
static int kvm_booke206_tlb_init(CPUState *env)
|
||||
{
|
||||
struct kvm_book3e_206_tlb_params params = {};
|
||||
struct kvm_config_tlb cfg = {};
|
||||
struct kvm_enable_cap encap = {};
|
||||
unsigned int entries = 0;
|
||||
int ret, i;
|
||||
|
||||
if (!kvm_enabled() ||
|
||||
!kvm_check_extension(env->kvm_state, KVM_CAP_SW_TLB)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(ARRAY_SIZE(params.tlb_sizes) == BOOKE206_MAX_TLBN);
|
||||
|
||||
for (i = 0; i < BOOKE206_MAX_TLBN; i++) {
|
||||
params.tlb_sizes[i] = booke206_tlb_size(env, i);
|
||||
params.tlb_ways[i] = booke206_tlb_ways(env, i);
|
||||
entries += params.tlb_sizes[i];
|
||||
}
|
||||
|
||||
assert(entries == env->nb_tlb);
|
||||
assert(sizeof(struct kvm_book3e_206_tlb_entry) == sizeof(ppcmas_tlb_t));
|
||||
|
||||
env->tlb_dirty = true;
|
||||
|
||||
cfg.array = (uintptr_t)env->tlb.tlbm;
|
||||
cfg.array_len = sizeof(ppcmas_tlb_t) * entries;
|
||||
cfg.params = (uintptr_t)¶ms;
|
||||
cfg.mmu_type = KVM_MMU_FSL_BOOKE_NOHV;
|
||||
|
||||
encap.cap = KVM_CAP_SW_TLB;
|
||||
encap.args[0] = (uintptr_t)&cfg;
|
||||
|
||||
ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &encap);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "%s: couldn't enable KVM_CAP_SW_TLB: %s\n",
|
||||
__func__, strerror(-ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
env->kvm_sw_tlb = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_init_vcpu(CPUState *cenv)
|
||||
{
|
||||
int ret;
|
||||
@ -116,6 +169,15 @@ int kvm_arch_init_vcpu(CPUState *cenv)
|
||||
|
||||
idle_timer = qemu_new_timer_ns(vm_clock, kvm_kick_env, cenv);
|
||||
|
||||
/* Some targets support access to KVM's guest TLB. */
|
||||
switch (cenv->mmu_model) {
|
||||
case POWERPC_MMU_BOOKE206:
|
||||
ret = kvm_booke206_tlb_init(cenv);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -123,6 +185,31 @@ void kvm_arch_reset_vcpu(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
static void kvm_sw_tlb_put(CPUState *env)
|
||||
{
|
||||
struct kvm_dirty_tlb dirty_tlb;
|
||||
unsigned char *bitmap;
|
||||
int ret;
|
||||
|
||||
if (!env->kvm_sw_tlb) {
|
||||
return;
|
||||
}
|
||||
|
||||
bitmap = g_malloc((env->nb_tlb + 7) / 8);
|
||||
memset(bitmap, 0xFF, (env->nb_tlb + 7) / 8);
|
||||
|
||||
dirty_tlb.bitmap = (uintptr_t)bitmap;
|
||||
dirty_tlb.num_dirty = env->nb_tlb;
|
||||
|
||||
ret = kvm_vcpu_ioctl(env, KVM_DIRTY_TLB, &dirty_tlb);
|
||||
if (ret) {
|
||||
fprintf(stderr, "%s: KVM_DIRTY_TLB: %s\n",
|
||||
__func__, strerror(-ret));
|
||||
}
|
||||
|
||||
g_free(bitmap);
|
||||
}
|
||||
|
||||
int kvm_arch_put_registers(CPUState *env, int level)
|
||||
{
|
||||
struct kvm_regs regs;
|
||||
@ -160,6 +247,11 @@ int kvm_arch_put_registers(CPUState *env, int level)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (env->tlb_dirty) {
|
||||
kvm_sw_tlb_put(env);
|
||||
env->tlb_dirty = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -452,6 +544,14 @@ int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
|
||||
dprintf("handle halt\n");
|
||||
ret = kvmppc_handle_halt(env);
|
||||
break;
|
||||
#ifdef CONFIG_PSERIES
|
||||
case KVM_EXIT_PAPR_HCALL:
|
||||
dprintf("handle PAPR hypercall\n");
|
||||
run->papr_hcall.ret = spapr_hypercall(env, run->papr_hcall.nr,
|
||||
run->papr_hcall.args);
|
||||
ret = 1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
|
||||
ret = -1;
|
||||
@ -509,6 +609,70 @@ uint32_t kvmppc_get_tbfreq(void)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Try to find a device tree node for a CPU with clock-frequency property */
|
||||
static int kvmppc_find_cpu_dt(char *buf, int buf_len)
|
||||
{
|
||||
struct dirent *dirp;
|
||||
DIR *dp;
|
||||
|
||||
if ((dp = opendir(PROC_DEVTREE_CPU)) == NULL) {
|
||||
printf("Can't open directory " PROC_DEVTREE_CPU "\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[0] = '\0';
|
||||
while ((dirp = readdir(dp)) != NULL) {
|
||||
FILE *f;
|
||||
snprintf(buf, buf_len, "%s%s/clock-frequency", PROC_DEVTREE_CPU,
|
||||
dirp->d_name);
|
||||
f = fopen(buf, "r");
|
||||
if (f) {
|
||||
snprintf(buf, buf_len, "%s%s", PROC_DEVTREE_CPU, dirp->d_name);
|
||||
fclose(f);
|
||||
break;
|
||||
}
|
||||
buf[0] = '\0';
|
||||
}
|
||||
closedir(dp);
|
||||
if (buf[0] == '\0') {
|
||||
printf("Unknown host!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t kvmppc_get_clockfreq(void)
|
||||
{
|
||||
char buf[512];
|
||||
uint32_t tb[2];
|
||||
FILE *f;
|
||||
int len;
|
||||
|
||||
if (kvmppc_find_cpu_dt(buf, sizeof(buf))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strncat(buf, "/clock-frequency", sizeof(buf) - strlen(buf));
|
||||
|
||||
f = fopen(buf, "rb");
|
||||
if (!f) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = fread(tb, sizeof(tb[0]), 2, f);
|
||||
fclose(f);
|
||||
switch (len) {
|
||||
case 1:
|
||||
/* freq is only a single cell */
|
||||
return tb[0];
|
||||
case 2:
|
||||
return *(uint64_t*)tb;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
|
||||
{
|
||||
uint32_t *hc = (uint32_t*)buf;
|
||||
@ -539,6 +703,53 @@ int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvmppc_set_papr(CPUState *env)
|
||||
{
|
||||
struct kvm_enable_cap cap = {};
|
||||
struct kvm_one_reg reg = {};
|
||||
struct kvm_sregs sregs = {};
|
||||
int ret;
|
||||
|
||||
cap.cap = KVM_CAP_PPC_PAPR;
|
||||
ret = kvm_vcpu_ioctl(env, KVM_ENABLE_CAP, &cap);
|
||||
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX We set HIOR here. It really should be a qdev property of
|
||||
* the CPU node, but we don't have CPUs converted to qdev yet.
|
||||
*
|
||||
* Once we have qdev CPUs, move HIOR to a qdev property and
|
||||
* remove this chunk.
|
||||
*/
|
||||
reg.id = KVM_ONE_REG_PPC_HIOR;
|
||||
reg.u.reg64 = env->spr[SPR_HIOR];
|
||||
ret = kvm_vcpu_ioctl(env, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set SDR1 so kernel space finds the HTAB */
|
||||
ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sregs.u.s.sdr1 = env->spr[SPR_SDR1];
|
||||
|
||||
ret = kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
cpu_abort(env, "This KVM version does not support PAPR\n");
|
||||
}
|
||||
|
||||
bool kvm_arch_stop_on_emulation_error(CPUState *env)
|
||||
{
|
||||
return true;
|
||||
|
@ -21,71 +21,6 @@
|
||||
static QEMUTimer *kvmppc_timer;
|
||||
static unsigned int kvmppc_timer_rate;
|
||||
|
||||
#ifdef CONFIG_FDT
|
||||
int kvmppc_read_host_property(const char *node_path, const char *prop,
|
||||
void *val, size_t len)
|
||||
{
|
||||
char *path;
|
||||
FILE *f;
|
||||
int ret = 0;
|
||||
int pathlen;
|
||||
|
||||
pathlen = snprintf(NULL, 0, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop)
|
||||
+ 1;
|
||||
path = g_malloc(pathlen);
|
||||
|
||||
snprintf(path, pathlen, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop);
|
||||
|
||||
f = fopen(path, "rb");
|
||||
if (f == NULL) {
|
||||
ret = errno;
|
||||
goto free;
|
||||
}
|
||||
|
||||
len = fread(val, len, 1, f);
|
||||
if (len != 1) {
|
||||
ret = ferror(f);
|
||||
goto close;
|
||||
}
|
||||
|
||||
close:
|
||||
fclose(f);
|
||||
free:
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvmppc_copy_host_cell(void *fdt, const char *node, const char *prop)
|
||||
{
|
||||
uint32_t cell;
|
||||
int ret;
|
||||
|
||||
ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't read host %s/%s\n", node, prop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = qemu_devtree_setprop_cell(fdt, node, prop, cell);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't set guest %s/%s\n", node, prop);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kvmppc_fdt_update(void *fdt)
|
||||
{
|
||||
/* Copy data from the host device tree into the guest. Since the guest can
|
||||
* directly access the timebase without host involvement, we must expose
|
||||
* the correct frequencies. */
|
||||
kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency");
|
||||
kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void kvmppc_timer_hack(void *opaque)
|
||||
{
|
||||
qemu_notify_event();
|
||||
|
@ -10,22 +10,42 @@
|
||||
#define __KVM_PPC_H__
|
||||
|
||||
void kvmppc_init(void);
|
||||
void kvmppc_fdt_update(void *fdt);
|
||||
#ifndef CONFIG_KVM
|
||||
static inline int kvmppc_read_host_property(const char *node_path, const char *prop,
|
||||
void *val, size_t len)
|
||||
{
|
||||
assert(0);
|
||||
return -ENOSYS;
|
||||
}
|
||||
#else
|
||||
int kvmppc_read_host_property(const char *node_path, const char *prop,
|
||||
void *val, size_t len);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
|
||||
uint32_t kvmppc_get_tbfreq(void);
|
||||
uint64_t kvmppc_get_clockfreq(void);
|
||||
int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);
|
||||
int kvmppc_set_interrupt(CPUState *env, int irq, int level);
|
||||
void kvmppc_set_papr(CPUState *env);
|
||||
|
||||
#else
|
||||
|
||||
static inline uint32_t kvmppc_get_tbfreq(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint64_t kvmppc_get_clockfreq(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int kvmppc_set_interrupt(CPUState *env, int irq, int level)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void kvmppc_set_papr(CPUState *env)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_KVM
|
||||
#define kvmppc_eieio() do { } while (0)
|
||||
|
@ -69,6 +69,9 @@ static TCGv cpu_nip;
|
||||
static TCGv cpu_msr;
|
||||
static TCGv cpu_ctr;
|
||||
static TCGv cpu_lr;
|
||||
#if defined(TARGET_PPC64)
|
||||
static TCGv cpu_cfar;
|
||||
#endif
|
||||
static TCGv cpu_xer;
|
||||
static TCGv cpu_reserve;
|
||||
static TCGv_i32 cpu_fpscr;
|
||||
@ -154,6 +157,11 @@ void ppc_translate_init(void)
|
||||
cpu_lr = tcg_global_mem_new(TCG_AREG0,
|
||||
offsetof(CPUState, lr), "lr");
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
cpu_cfar = tcg_global_mem_new(TCG_AREG0,
|
||||
offsetof(CPUState, cfar), "cfar");
|
||||
#endif
|
||||
|
||||
cpu_xer = tcg_global_mem_new(TCG_AREG0,
|
||||
offsetof(CPUState, xer), "xer");
|
||||
|
||||
@ -187,6 +195,7 @@ typedef struct DisasContext {
|
||||
int le_mode;
|
||||
#if defined(TARGET_PPC64)
|
||||
int sf_mode;
|
||||
int has_cfar;
|
||||
#endif
|
||||
int fpu_enabled;
|
||||
int altivec_enabled;
|
||||
@ -3345,6 +3354,14 @@ static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
|
||||
/* stfiwx */
|
||||
GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX);
|
||||
|
||||
static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip)
|
||||
{
|
||||
#if defined(TARGET_PPC64)
|
||||
if (ctx->has_cfar)
|
||||
tcg_gen_movi_tl(cpu_cfar, nip);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*** Branch ***/
|
||||
static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
|
||||
{
|
||||
@ -3407,6 +3424,7 @@ static void gen_b(DisasContext *ctx)
|
||||
target = li;
|
||||
if (LK(ctx->opcode))
|
||||
gen_setlr(ctx, ctx->nip);
|
||||
gen_update_cfar(ctx, ctx->nip);
|
||||
gen_goto_tb(ctx, 0, target);
|
||||
}
|
||||
|
||||
@ -3469,6 +3487,7 @@ static inline void gen_bcond(DisasContext *ctx, int type)
|
||||
}
|
||||
tcg_temp_free_i32(temp);
|
||||
}
|
||||
gen_update_cfar(ctx, ctx->nip);
|
||||
if (type == BCOND_IM) {
|
||||
target_ulong li = (target_long)((int16_t)(BD(ctx->opcode)));
|
||||
if (likely(AA(ctx->opcode) == 0)) {
|
||||
@ -3580,6 +3599,7 @@ static void gen_rfi(DisasContext *ctx)
|
||||
gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
|
||||
return;
|
||||
}
|
||||
gen_update_cfar(ctx, ctx->nip);
|
||||
gen_helper_rfi();
|
||||
gen_sync_exception(ctx);
|
||||
#endif
|
||||
@ -3596,6 +3616,7 @@ static void gen_rfid(DisasContext *ctx)
|
||||
gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
|
||||
return;
|
||||
}
|
||||
gen_update_cfar(ctx, ctx->nip);
|
||||
gen_helper_rfid();
|
||||
gen_sync_exception(ctx);
|
||||
#endif
|
||||
@ -9263,6 +9284,12 @@ void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
|
||||
*/
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
if (env->flags & POWERPC_FLAG_CFAR) {
|
||||
cpu_fprintf(f, " CFAR " TARGET_FMT_lx"\n", env->cfar);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (env->mmu_model) {
|
||||
case POWERPC_MMU_32B:
|
||||
case POWERPC_MMU_601:
|
||||
@ -9371,6 +9398,7 @@ static inline void gen_intermediate_code_internal(CPUState *env,
|
||||
ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0;
|
||||
#if defined(TARGET_PPC64)
|
||||
ctx.sf_mode = msr_sf;
|
||||
ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
|
||||
#endif
|
||||
ctx.fpu_enabled = msr_fp;
|
||||
if ((env->flags & POWERPC_FLAG_SPE) && msr_spe)
|
||||
|
@ -129,6 +129,19 @@ static void spr_write_lr (void *opaque, int sprn, int gprn)
|
||||
tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]);
|
||||
}
|
||||
|
||||
/* CFAR */
|
||||
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
|
||||
static void spr_read_cfar (void *opaque, int gprn, int sprn)
|
||||
{
|
||||
tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar);
|
||||
}
|
||||
|
||||
static void spr_write_cfar (void *opaque, int sprn, int gprn)
|
||||
{
|
||||
tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]);
|
||||
}
|
||||
#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
|
||||
|
||||
/* CTR */
|
||||
static void spr_read_ctr (void *opaque, int gprn, int sprn)
|
||||
{
|
||||
@ -3253,6 +3266,9 @@ static void init_proc_401 (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* PowerPC 401x2 */
|
||||
@ -3291,6 +3307,9 @@ static void init_proc_401x2 (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* PowerPC 401x3 */
|
||||
@ -3324,6 +3343,9 @@ static void init_proc_401x3 (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* IOP480 */
|
||||
@ -3362,6 +3384,9 @@ static void init_proc_IOP480 (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* PowerPC 403 */
|
||||
@ -3392,6 +3417,9 @@ static void init_proc_403 (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* PowerPC 403 GCX */
|
||||
@ -3442,6 +3470,9 @@ static void init_proc_403GCX (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* PowerPC 405 */
|
||||
@ -3491,6 +3522,9 @@ static void init_proc_405 (CPUPPCState *env)
|
||||
env->icache_line_size = 32;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(8, 12, 16, 20);
|
||||
SET_WDT_PERIOD(16, 20, 24, 28);
|
||||
}
|
||||
|
||||
/* PowerPC 440 EP */
|
||||
@ -3573,6 +3607,9 @@ static void init_proc_440EP (CPUPPCState *env)
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* XXX: TODO: allocate internal IRQ controller */
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(20, 24, 28, 32);
|
||||
}
|
||||
|
||||
/* PowerPC 440 GP */
|
||||
@ -3637,6 +3674,9 @@ static void init_proc_440GP (CPUPPCState *env)
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* XXX: TODO: allocate internal IRQ controller */
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(20, 24, 28, 32);
|
||||
}
|
||||
|
||||
/* PowerPC 440x4 */
|
||||
@ -3701,6 +3741,9 @@ static void init_proc_440x4 (CPUPPCState *env)
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* XXX: TODO: allocate internal IRQ controller */
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(20, 24, 28, 32);
|
||||
}
|
||||
|
||||
/* PowerPC 440x5 */
|
||||
@ -3782,6 +3825,9 @@ static void init_proc_440x5 (CPUPPCState *env)
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
ppc40x_irq_init(env);
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(20, 24, 28, 32);
|
||||
}
|
||||
|
||||
/* PowerPC 460 (guessed) */
|
||||
@ -3870,6 +3916,9 @@ static void init_proc_460 (CPUPPCState *env)
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* XXX: TODO: allocate internal IRQ controller */
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(20, 24, 28, 32);
|
||||
}
|
||||
|
||||
/* PowerPC 460F (guessed) */
|
||||
@ -3961,6 +4010,9 @@ static void init_proc_460F (CPUPPCState *env)
|
||||
env->dcache_line_size = 32;
|
||||
env->icache_line_size = 32;
|
||||
/* XXX: TODO: allocate internal IRQ controller */
|
||||
|
||||
SET_FIT_PERIOD(12, 16, 20, 24);
|
||||
SET_WDT_PERIOD(20, 24, 28, 32);
|
||||
}
|
||||
|
||||
/* Freescale 5xx cores (aka RCPU) */
|
||||
@ -6489,7 +6541,7 @@ static void init_proc_970MP (CPUPPCState *env)
|
||||
#define POWERPC_BFDM_POWER7 (bfd_mach_ppc64)
|
||||
#define POWERPC_FLAG_POWER7 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
|
||||
POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
|
||||
POWERPC_FLAG_BUS_CLK)
|
||||
POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR)
|
||||
#define check_pow_POWER7 check_pow_nocheck
|
||||
|
||||
static void init_proc_POWER7 (CPUPPCState *env)
|
||||
@ -6508,6 +6560,14 @@ static void init_proc_POWER7 (CPUPPCState *env)
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
&spr_read_purr, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_CFAR, "SPR_CFAR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_cfar, &spr_write_cfar,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_DSCR, "SPR_DSCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
/* Memory management */
|
||||
/* XXX : not implemented */
|
||||
@ -9679,8 +9739,7 @@ static int gdb_get_float_reg(CPUState *env, uint8_t *mem_buf, int n)
|
||||
return 8;
|
||||
}
|
||||
if (n == 32) {
|
||||
/* FPSCR not implemented */
|
||||
memset(mem_buf, 0, 4);
|
||||
stl_p(mem_buf, env->fpscr);
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
|
@ -847,25 +847,6 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type,
|
||||
tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg));
|
||||
}
|
||||
|
||||
static inline void tcg_out_addi(TCGContext *s, TCGArg reg, tcg_target_long val)
|
||||
{
|
||||
if (val == ((int32_t)val << 10) >> 10) {
|
||||
tcg_out_bundle(s, MmI,
|
||||
tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5,
|
||||
TCG_REG_R2, val, TCG_REG_R0),
|
||||
tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
|
||||
tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg,
|
||||
reg, TCG_REG_R2));
|
||||
} else {
|
||||
tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, val);
|
||||
tcg_out_bundle(s, mmI,
|
||||
tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
|
||||
tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
|
||||
tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg,
|
||||
reg, TCG_REG_R2));
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_out_br(TCGContext *s, int label_index)
|
||||
{
|
||||
TCGLabel *l = &s->labels[label_index];
|
||||
|
@ -2322,8 +2322,3 @@ static void tcg_target_qemu_prologue(TCGContext *s)
|
||||
/* br %r14 (return) */
|
||||
tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14);
|
||||
}
|
||||
|
||||
static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
|
||||
{
|
||||
tcg_abort();
|
||||
}
|
||||
|
11
trace-events
11
trace-events
@ -56,6 +56,7 @@ virtio_console_chr_read(unsigned int port, int size) "port %u, size %d"
|
||||
virtio_console_chr_event(unsigned int port, int event) "port %u, event %d"
|
||||
|
||||
# block.c
|
||||
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags %#x format_name \"%s\""
|
||||
multiwrite_cb(void *mcb, int ret) "mcb %p ret %d"
|
||||
bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d"
|
||||
bdrv_aio_multiwrite_earlyfail(void *mcb) "mcb %p"
|
||||
@ -66,7 +67,7 @@ bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs
|
||||
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
|
||||
bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
|
||||
bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
|
||||
bdrv_co_io(int is_write, void *acb) "is_write %d acb %p"
|
||||
bdrv_co_io_em(void *bs, int64_t sector_num, int nb_sectors, int is_write, void *acb) "bs %p sector_num %"PRId64" nb_sectors %d is_write %d acb %p"
|
||||
|
||||
# hw/virtio-blk.c
|
||||
virtio_blk_req_complete(void *req, int status) "req %p status %d"
|
||||
@ -444,8 +445,8 @@ milkymist_tmu2_pulse_irq(void) "Pulse IRQ"
|
||||
# hw/milkymist-uart.c
|
||||
milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
|
||||
milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr %08x value %08x"
|
||||
milkymist_uart_pulse_irq_rx(void) "Pulse IRQ RX"
|
||||
milkymist_uart_pulse_irq_tx(void) "Pulse IRQ TX"
|
||||
milkymist_uart_raise_irq(void) "Raise IRQ"
|
||||
milkymist_uart_lower_irq(void) "Lower IRQ"
|
||||
|
||||
# hw/milkymist-vgafb.c
|
||||
milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr %08x value %08x"
|
||||
@ -533,3 +534,7 @@ esp_mem_writeb_cmd_sel(uint32_t val) "Select without ATN (%2.2x)"
|
||||
esp_mem_writeb_cmd_selatn(uint32_t val) "Select with ATN (%2.2x)"
|
||||
esp_mem_writeb_cmd_selatns(uint32_t val) "Select with ATN & stop (%2.2x)"
|
||||
esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (%2.2x)"
|
||||
|
||||
# monitor.c
|
||||
handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\""
|
||||
monitor_protocol_emitter(void *mon) "mon %p"
|
||||
|
Loading…
Reference in New Issue
Block a user