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:
Avi Kivity 2011-10-09 13:11:50 +02:00
commit df2921d326
78 changed files with 3467 additions and 661 deletions

3
.gitmodules vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -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;
return fdt_setprop_string(fdt, offset, property, string);
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 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -604,13 +604,11 @@ 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;
}
}
static void
dma_write(void *opaque, target_phys_addr_t addr,

View File

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

View File

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

View File

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

View File

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

View File

@ -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 opp->src[n_IRQ].ide;
}
return retval;
static inline uint32_t read_IRQreg_ipvp(openpic_t *opp, int n_IRQ)
{
return opp->src[n_IRQ].ipvp;
}
static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
uint32_t reg, uint32_t val)
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);
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);
break;
}
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
View File

@ -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,19 +664,25 @@ 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,
uint32_t value, int is_excp)
@ -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;
}
/*****************************************************************************/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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";
}
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);
}
/* XXX register timer? */
ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR);
ppc_dcr_init(env, NULL, NULL);
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);
}
}
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
View 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);

View File

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

View File

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

View File

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

View File

@ -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(),
},

View File

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

View File

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

View File

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

View File

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

View File

@ -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(),
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,3 +26,4 @@
#include <asm/kvm_para.h>
#endif /* __LINUX_KVM_PARA_H */

121
memory.c
View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

@ -0,0 +1 @@
Subproject commit 7abb12f60eb3069019e9497e193733d77d8f0722

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)&params;
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, &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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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