mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
Patch queue for ppc - 2014-03-05
This pull request includes: - VSX emulation support - book3s pr/hv selection - some bug fixes - qdev stable numbering - eTSEC emulation -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQIcBAABAgAGBQJTFoh7AAoJECszeR4D/txgZkQQAIBNHeYN96PR0JWziEig9a3i ByOC+ug2x6KH5RPy/plMFG00W8AnDt0fYO+zQebvu/stJbQtSGRA5fdOiKbRGer/ 7p0XUpeGWHTPULvgNBWrZKYbC36GOPtFvYyYShzS7oLQOhufNdbzP6Br41Lr816m 4UdGjcItmn/WOron0TpL+yBJA1UIK6n6bmJqtDk+N3kyDSQ9XauBJoZ9yRBSRgp7 ToabDa5lLcA7u9QLIT1b0AuTKEW+Opq8bSRwCQQDpfd9UDxLoX60pvsO8oupvu/6 yLwiaYIdc336nI0R4jjXwgBCUacSCBubIAI0bm5xw91JH7Jo7G2i2g01ok5VfelG /ljzwcjcanDFYr04NfOEK1pmQhULSDyOBKDs8ZUNfiwjoGMM0+ZVmqPY8VPyTVRB TxViS2Fm0cnymcfBarAaP2gVJBmqZF29QMQl7KgwbkNYrn5+WM3XcOEham/hTN2+ Q5dhiVBuWQBILKPV09SrijpEV1iNVVvJ3UNyMoQ9Ngb0MypHN3siw9X8b0rx6Dsi O2SxiUKowVyK7BfTai53Pep+ep8Zo6lAzXFGh1CHtEMhyme+p07FXZrh9mgPsdZU 5C1jKzTMZDEcibmFGj/Jb52RmXVGHWpalh4OBaqRv3kooU8eEL0e0f2wRvLAGLrD J4firKDdB/CIPvatLD0a =a6Q/ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/agraf/tags/signed-ppc-for-upstream' into staging Patch queue for ppc - 2014-03-05 This pull request includes: - VSX emulation support - book3s pr/hv selection - some bug fixes - qdev stable numbering - eTSEC emulation # gpg: Signature made Wed 05 Mar 2014 02:14:19 GMT using RSA key ID 03FEDC60 # gpg: Can't check signature: public key not found * remotes/agraf/tags/signed-ppc-for-upstream: (130 commits) target-ppc: spapr: e500: fix to use cpu_dt_id target-ppc: add PowerPCCPU::cpu_dt_id target-ppc: Introduce hypervisor call H_GET_TCE target-ppc: Update ppc_hash64_store_hpte to support updating in-kernel htab target-ppc: Change the hpte store API target-ppc: Fix page table lookup with kvm enabled target-ppc: Fix htab_mask calculation target-ppc: Use Additional Temporary in stqcx Case target-ppc: Fix Compiler Warnings Due to 64-Bit Constants Declared as UL PPC: sPAPR: Only use getpagesize() when we run with kvm target-ppc/translate.c: Use ULL suffix for 64 bit constants spapr-vlan: flush queue whenever can_receive can go from false to true target-ppc: Altivec 2.07: Vector Permute and Exclusive OR target-ppc: Altivec 2.07: Vector SHA Sigma Instructions target-ppc: Altivec 2.07: AES Instructions target-ppc: Altivec 2.07: Binary Coded Decimal Instructions target-ppc: Altivec 2.07: Vector Polynomial Multiply Sum target-ppc: Altivec 2.07: Vector Gather Bits by Bytes target-ppc: Altivec 2.07: Doubleword Compares target-ppc: Altivec 2.07: vbpermq Instruction ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
bb2b045034
@ -47,4 +47,5 @@ CONFIG_E500=y
|
||||
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
||||
# For PReP
|
||||
CONFIG_MC146818RTC=y
|
||||
CONFIG_ETSEC=y
|
||||
CONFIG_ISA_TESTDEV=y
|
||||
|
@ -284,12 +284,30 @@ static void *load_at(int fd, int offset, int size)
|
||||
#define SZ 64
|
||||
#include "hw/elf_ops.h"
|
||||
|
||||
const char *load_elf_strerror(int error)
|
||||
{
|
||||
switch (error) {
|
||||
case 0:
|
||||
return "No error";
|
||||
case ELF_LOAD_FAILED:
|
||||
return "Failed to load ELF";
|
||||
case ELF_LOAD_NOT_ELF:
|
||||
return "The image is not ELF";
|
||||
case ELF_LOAD_WRONG_ARCH:
|
||||
return "The image is from incompatible architecture";
|
||||
case ELF_LOAD_WRONG_ENDIAN:
|
||||
return "The image has incorrect endianness";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
/* return < 0 if error, otherwise the number of bytes loaded in memory */
|
||||
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
|
||||
uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
|
||||
{
|
||||
int fd, data_order, target_data_order, must_swab, ret;
|
||||
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
|
||||
uint8_t e_ident[EI_NIDENT];
|
||||
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
@ -302,8 +320,10 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||
if (e_ident[0] != ELFMAG0 ||
|
||||
e_ident[1] != ELFMAG1 ||
|
||||
e_ident[2] != ELFMAG2 ||
|
||||
e_ident[3] != ELFMAG3)
|
||||
e_ident[3] != ELFMAG3) {
|
||||
ret = ELF_LOAD_NOT_ELF;
|
||||
goto fail;
|
||||
}
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
data_order = ELFDATA2MSB;
|
||||
#else
|
||||
@ -317,6 +337,7 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||
}
|
||||
|
||||
if (target_data_order != e_ident[EI_DATA]) {
|
||||
ret = ELF_LOAD_WRONG_ENDIAN;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -329,12 +350,9 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
close(fd);
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bswap_uboot_header(uboot_image_header_t *hdr)
|
||||
|
@ -440,27 +440,33 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
||||
static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
|
||||
{
|
||||
const char *typename = object_get_typename(OBJECT(bus));
|
||||
BusClass *bc;
|
||||
char *buf;
|
||||
int i,len;
|
||||
int i, len, bus_id;
|
||||
|
||||
bus->parent = parent;
|
||||
|
||||
if (name) {
|
||||
bus->name = g_strdup(name);
|
||||
} else if (bus->parent && bus->parent->id) {
|
||||
/* parent device has id -> use it for bus name */
|
||||
/* parent device has id -> use it plus parent-bus-id for bus name */
|
||||
bus_id = bus->parent->num_child_bus;
|
||||
|
||||
len = strlen(bus->parent->id) + 16;
|
||||
buf = g_malloc(len);
|
||||
snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus);
|
||||
snprintf(buf, len, "%s.%d", bus->parent->id, bus_id);
|
||||
bus->name = buf;
|
||||
} else {
|
||||
/* no id -> use lowercase bus type for bus name */
|
||||
/* no id -> use lowercase bus type plus global bus-id for bus name */
|
||||
bc = BUS_GET_CLASS(bus);
|
||||
bus_id = bc->automatic_ids++;
|
||||
|
||||
len = strlen(typename) + 16;
|
||||
buf = g_malloc(len);
|
||||
len = snprintf(buf, len, "%s.%d", typename,
|
||||
bus->parent ? bus->parent->num_child_bus : 0);
|
||||
for (i = 0; i < len; i++)
|
||||
len = snprintf(buf, len, "%s.%d", typename, bus_id);
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = qemu_tolower(buf[i]);
|
||||
}
|
||||
bus->name = buf;
|
||||
}
|
||||
|
||||
|
@ -221,10 +221,16 @@ static void pc_init1(QEMUMachineInitArgs *args,
|
||||
} else {
|
||||
for(i = 0; i < MAX_IDE_BUS; i++) {
|
||||
ISADevice *dev;
|
||||
char busname[] = "ide.0";
|
||||
dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i],
|
||||
ide_irq[i],
|
||||
hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
|
||||
idebus[i] = qdev_get_child_bus(DEVICE(dev), "ide.0");
|
||||
/*
|
||||
* The ide bus name is ide.0 for the first bus and ide.1 for the
|
||||
* second one.
|
||||
*/
|
||||
busname[4] = '0' + i;
|
||||
idebus[i] = qdev_get_child_bus(DEVICE(dev), busname);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +228,7 @@ int kvm_openpic_connect_vcpu(DeviceState *d, CPUState *cs)
|
||||
|
||||
encap.cap = KVM_CAP_IRQ_MPIC;
|
||||
encap.args[0] = opp->fd;
|
||||
encap.args[1] = cs->cpu_index;
|
||||
encap.args[1] = kvm_arch_vcpu_id(cs);
|
||||
|
||||
return kvm_vcpu_ioctl(cs, KVM_ENABLE_CAP, &encap);
|
||||
}
|
||||
|
@ -33,6 +33,17 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/visitor.h"
|
||||
|
||||
static int get_cpu_index_by_dt_id(int cpu_dt_id)
|
||||
{
|
||||
PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id);
|
||||
|
||||
if (cpu) {
|
||||
return cpu->parent_obj.cpu_index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
@ -659,7 +670,7 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
static target_ulong h_ipi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong server = args[0];
|
||||
target_ulong server = get_cpu_index_by_dt_id(args[0]);
|
||||
target_ulong mfrr = args[1];
|
||||
|
||||
if (server >= spapr->icp->nr_servers) {
|
||||
@ -728,7 +739,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
}
|
||||
|
||||
nr = rtas_ld(args, 0);
|
||||
server = rtas_ld(args, 1);
|
||||
server = get_cpu_index_by_dt_id(rtas_ld(args, 1));
|
||||
priority = rtas_ld(args, 2);
|
||||
|
||||
if (!ics_valid_irq(ics, nr) || (server >= ics->icp->nr_servers)
|
||||
|
@ -65,7 +65,7 @@ static void icp_get_kvm_state(ICPState *ss)
|
||||
ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to retrieve KVM interrupt controller state"
|
||||
" for CPU %d: %s", ss->cs->cpu_index, strerror(errno));
|
||||
" for CPU %ld: %s", kvm_arch_vcpu_id(ss->cs), strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ static int icp_set_kvm_state(ICPState *ss, int version_id)
|
||||
ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to restore KVM interrupt controller state (0x%"
|
||||
PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index,
|
||||
PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(ss->cs),
|
||||
strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
@ -325,15 +325,15 @@ static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
|
||||
struct kvm_enable_cap xics_enable_cap = {
|
||||
.cap = KVM_CAP_IRQ_XICS,
|
||||
.flags = 0,
|
||||
.args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0},
|
||||
.args = {icpkvm->kernel_xics_fd, kvm_arch_vcpu_id(cs), 0, 0},
|
||||
};
|
||||
|
||||
ss->cs = cs;
|
||||
|
||||
ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap);
|
||||
if (ret < 0) {
|
||||
error_report("Unable to connect CPU%d to kernel XICS: %s",
|
||||
cs->cpu_index, strerror(errno));
|
||||
error_report("Unable to connect CPU%ld to kernel XICS: %s",
|
||||
kvm_arch_vcpu_id(cs), strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params)
|
||||
&entry, &kernel_low, &kernel_high, 1,
|
||||
ELF_MACHINE, 0);
|
||||
|
||||
if (!kernel_size) {
|
||||
if (kernel_size <= 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
loader_params->kernel_filename);
|
||||
exit(1);
|
||||
|
@ -32,3 +32,6 @@ obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o
|
||||
|
||||
obj-$(CONFIG_VIRTIO) += virtio-net.o
|
||||
obj-y += vhost_net.o
|
||||
|
||||
obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \
|
||||
fsl_etsec/rings.o fsl_etsec/miim.o
|
||||
|
465
hw/net/fsl_etsec/etsec.c
Normal file
465
hw/net/fsl_etsec/etsec.c
Normal file
@ -0,0 +1,465 @@
|
||||
/*
|
||||
* QEMU Freescale eTSEC Emulator
|
||||
*
|
||||
* Copyright (c) 2011-2013 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This implementation doesn't include ring priority, TCP/IP Off-Load, QoS.
|
||||
*/
|
||||
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "trace.h"
|
||||
#include "hw/ptimer.h"
|
||||
#include "etsec.h"
|
||||
#include "registers.h"
|
||||
|
||||
/* #define HEX_DUMP */
|
||||
/* #define DEBUG_REGISTER */
|
||||
|
||||
#ifdef DEBUG_REGISTER
|
||||
static const int debug_etsec = 1;
|
||||
#else
|
||||
static const int debug_etsec;
|
||||
#endif
|
||||
|
||||
#define DPRINTF(fmt, ...) do { \
|
||||
if (debug_etsec) { \
|
||||
qemu_log(fmt , ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
eTSEC *etsec = opaque;
|
||||
uint32_t reg_index = addr / 4;
|
||||
eTSEC_Register *reg = NULL;
|
||||
uint32_t ret = 0x0;
|
||||
|
||||
assert(reg_index < ETSEC_REG_NUMBER);
|
||||
|
||||
reg = &etsec->regs[reg_index];
|
||||
|
||||
|
||||
switch (reg->access) {
|
||||
case ACC_WO:
|
||||
ret = 0x00000000;
|
||||
break;
|
||||
|
||||
case ACC_RW:
|
||||
case ACC_W1C:
|
||||
case ACC_RO:
|
||||
default:
|
||||
ret = reg->value;
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("Read 0x%08x @ 0x" TARGET_FMT_plx
|
||||
" : %s (%s)\n",
|
||||
ret, addr, reg->name, reg->desc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void write_tstat(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* Check THLTi flag in TSTAT */
|
||||
if (value & (1 << (31 - i))) {
|
||||
etsec_walk_tx_ring(etsec, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write 1 to clear */
|
||||
reg->value &= ~value;
|
||||
}
|
||||
|
||||
static void write_rstat(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* Check QHLTi flag in RSTAT */
|
||||
if (value & (1 << (23 - i)) && !(reg->value & (1 << (23 - i)))) {
|
||||
etsec_walk_rx_ring(etsec, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write 1 to clear */
|
||||
reg->value &= ~value;
|
||||
}
|
||||
|
||||
static void write_tbasex(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
reg->value = value & ~0x7;
|
||||
|
||||
/* Copy this value in the ring's TxBD pointer */
|
||||
etsec->regs[TBPTR0 + (reg_index - TBASE0)].value = value & ~0x7;
|
||||
}
|
||||
|
||||
static void write_rbasex(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
reg->value = value & ~0x7;
|
||||
|
||||
/* Copy this value in the ring's RxBD pointer */
|
||||
etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7;
|
||||
}
|
||||
|
||||
static void write_ievent(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
/* Write 1 to clear */
|
||||
reg->value &= ~value;
|
||||
|
||||
if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) {
|
||||
qemu_irq_lower(etsec->tx_irq);
|
||||
}
|
||||
if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) {
|
||||
qemu_irq_lower(etsec->rx_irq);
|
||||
}
|
||||
|
||||
if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC |
|
||||
IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC |
|
||||
IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ |
|
||||
IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE |
|
||||
IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD |
|
||||
IEVENT_MMRW))) {
|
||||
qemu_irq_lower(etsec->err_irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_dmactrl(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
reg->value = value;
|
||||
|
||||
if (value & DMACTRL_GRS) {
|
||||
|
||||
if (etsec->rx_buffer_len != 0) {
|
||||
/* Graceful receive stop delayed until end of frame */
|
||||
} else {
|
||||
/* Graceful receive stop now */
|
||||
etsec->regs[IEVENT].value |= IEVENT_GRSC;
|
||||
if (etsec->regs[IMASK].value & IMASK_GRSCEN) {
|
||||
qemu_irq_raise(etsec->err_irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value & DMACTRL_GTS) {
|
||||
|
||||
if (etsec->tx_buffer_len != 0) {
|
||||
/* Graceful transmit stop delayed until end of frame */
|
||||
} else {
|
||||
/* Graceful transmit stop now */
|
||||
etsec->regs[IEVENT].value |= IEVENT_GTSC;
|
||||
if (etsec->regs[IMASK].value & IMASK_GTSCEN) {
|
||||
qemu_irq_raise(etsec->err_irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(value & DMACTRL_WOP)) {
|
||||
/* Start polling */
|
||||
ptimer_stop(etsec->ptimer);
|
||||
ptimer_set_count(etsec->ptimer, 1);
|
||||
ptimer_run(etsec->ptimer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void etsec_write(void *opaque,
|
||||
hwaddr addr,
|
||||
uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
eTSEC *etsec = opaque;
|
||||
uint32_t reg_index = addr / 4;
|
||||
eTSEC_Register *reg = NULL;
|
||||
uint32_t before = 0x0;
|
||||
|
||||
assert(reg_index < ETSEC_REG_NUMBER);
|
||||
|
||||
reg = &etsec->regs[reg_index];
|
||||
before = reg->value;
|
||||
|
||||
switch (reg_index) {
|
||||
case IEVENT:
|
||||
write_ievent(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
case DMACTRL:
|
||||
write_dmactrl(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
case TSTAT:
|
||||
write_tstat(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
case RSTAT:
|
||||
write_rstat(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
case TBASE0 ... TBASE7:
|
||||
write_tbasex(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
case RBASE0 ... RBASE7:
|
||||
write_rbasex(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
case MIIMCFG ... MIIMIND:
|
||||
etsec_write_miim(etsec, reg, reg_index, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Default handling */
|
||||
switch (reg->access) {
|
||||
|
||||
case ACC_RW:
|
||||
case ACC_WO:
|
||||
reg->value = value;
|
||||
break;
|
||||
|
||||
case ACC_W1C:
|
||||
reg->value &= ~value;
|
||||
break;
|
||||
|
||||
case ACC_RO:
|
||||
default:
|
||||
/* Read Only or Unknown register */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DPRINTF("Write 0x%08x @ 0x" TARGET_FMT_plx
|
||||
" val:0x%08x->0x%08x : %s (%s)\n",
|
||||
(unsigned int)value, addr, before, reg->value,
|
||||
reg->name, reg->desc);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps etsec_ops = {
|
||||
.read = etsec_read,
|
||||
.write = etsec_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void etsec_timer_hit(void *opaque)
|
||||
{
|
||||
eTSEC *etsec = opaque;
|
||||
|
||||
ptimer_stop(etsec->ptimer);
|
||||
|
||||
if (!(etsec->regs[DMACTRL].value & DMACTRL_WOP)) {
|
||||
|
||||
if (!(etsec->regs[DMACTRL].value & DMACTRL_GTS)) {
|
||||
etsec_walk_tx_ring(etsec, 0);
|
||||
}
|
||||
ptimer_set_count(etsec->ptimer, 1);
|
||||
ptimer_run(etsec->ptimer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void etsec_reset(DeviceState *d)
|
||||
{
|
||||
eTSEC *etsec = ETSEC_COMMON(d);
|
||||
int i = 0;
|
||||
int reg_index = 0;
|
||||
|
||||
/* Default value for all registers */
|
||||
for (i = 0; i < ETSEC_REG_NUMBER; i++) {
|
||||
etsec->regs[i].name = "Reserved";
|
||||
etsec->regs[i].desc = "";
|
||||
etsec->regs[i].access = ACC_UNKNOWN;
|
||||
etsec->regs[i].value = 0x00000000;
|
||||
}
|
||||
|
||||
/* Set-up known registers */
|
||||
for (i = 0; eTSEC_registers_def[i].name != NULL; i++) {
|
||||
|
||||
reg_index = eTSEC_registers_def[i].offset / 4;
|
||||
|
||||
etsec->regs[reg_index].name = eTSEC_registers_def[i].name;
|
||||
etsec->regs[reg_index].desc = eTSEC_registers_def[i].desc;
|
||||
etsec->regs[reg_index].access = eTSEC_registers_def[i].access;
|
||||
etsec->regs[reg_index].value = eTSEC_registers_def[i].reset;
|
||||
}
|
||||
|
||||
etsec->tx_buffer = NULL;
|
||||
etsec->tx_buffer_len = 0;
|
||||
etsec->rx_buffer = NULL;
|
||||
etsec->rx_buffer_len = 0;
|
||||
|
||||
etsec->phy_status =
|
||||
MII_SR_EXTENDED_CAPS | MII_SR_LINK_STATUS | MII_SR_AUTONEG_CAPS |
|
||||
MII_SR_AUTONEG_COMPLETE | MII_SR_PREAMBLE_SUPPRESS |
|
||||
MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS |
|
||||
MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS |
|
||||
MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS;
|
||||
}
|
||||
|
||||
static void etsec_cleanup(NetClientState *nc)
|
||||
{
|
||||
/* qemu_log("eTSEC cleanup\n"); */
|
||||
}
|
||||
|
||||
static int etsec_can_receive(NetClientState *nc)
|
||||
{
|
||||
eTSEC *etsec = qemu_get_nic_opaque(nc);
|
||||
|
||||
return etsec->rx_buffer_len == 0;
|
||||
}
|
||||
|
||||
static ssize_t etsec_receive(NetClientState *nc,
|
||||
const uint8_t *buf,
|
||||
size_t size)
|
||||
{
|
||||
eTSEC *etsec = qemu_get_nic_opaque(nc);
|
||||
|
||||
#if defined(HEX_DUMP)
|
||||
fprintf(stderr, "%s receive size:%d\n", etsec->nic->nc.name, size);
|
||||
qemu_hexdump(buf, stderr, "", size);
|
||||
#endif
|
||||
etsec_rx_ring_write(etsec, buf, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static void etsec_set_link_status(NetClientState *nc)
|
||||
{
|
||||
eTSEC *etsec = qemu_get_nic_opaque(nc);
|
||||
|
||||
etsec_miim_link_status(etsec, nc);
|
||||
}
|
||||
|
||||
static NetClientInfo net_etsec_info = {
|
||||
.type = NET_CLIENT_OPTIONS_KIND_NIC,
|
||||
.size = sizeof(NICState),
|
||||
.can_receive = etsec_can_receive,
|
||||
.receive = etsec_receive,
|
||||
.cleanup = etsec_cleanup,
|
||||
.link_status_changed = etsec_set_link_status,
|
||||
};
|
||||
|
||||
static void etsec_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
eTSEC *etsec = ETSEC_COMMON(dev);
|
||||
|
||||
etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, etsec);
|
||||
qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a);
|
||||
|
||||
|
||||
etsec->bh = qemu_bh_new(etsec_timer_hit, etsec);
|
||||
etsec->ptimer = ptimer_init(etsec->bh);
|
||||
ptimer_set_freq(etsec->ptimer, 100);
|
||||
}
|
||||
|
||||
static void etsec_instance_init(Object *obj)
|
||||
{
|
||||
eTSEC *etsec = ETSEC_COMMON(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&etsec->io_area, OBJECT(etsec), &etsec_ops, etsec,
|
||||
"eTSEC", 0x1000);
|
||||
sysbus_init_mmio(sbd, &etsec->io_area);
|
||||
|
||||
sysbus_init_irq(sbd, &etsec->tx_irq);
|
||||
sysbus_init_irq(sbd, &etsec->rx_irq);
|
||||
sysbus_init_irq(sbd, &etsec->err_irq);
|
||||
}
|
||||
|
||||
static Property etsec_properties[] = {
|
||||
DEFINE_NIC_PROPERTIES(eTSEC, conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void etsec_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = etsec_realize;
|
||||
dc->reset = etsec_reset;
|
||||
dc->props = etsec_properties;
|
||||
}
|
||||
|
||||
static TypeInfo etsec_info = {
|
||||
.name = "eTSEC",
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(eTSEC),
|
||||
.class_init = etsec_class_init,
|
||||
.instance_init = etsec_instance_init,
|
||||
};
|
||||
|
||||
static void etsec_register_types(void)
|
||||
{
|
||||
type_register_static(&etsec_info);
|
||||
}
|
||||
|
||||
type_init(etsec_register_types)
|
||||
|
||||
DeviceState *etsec_create(hwaddr base,
|
||||
MemoryRegion * mr,
|
||||
NICInfo * nd,
|
||||
qemu_irq tx_irq,
|
||||
qemu_irq rx_irq,
|
||||
qemu_irq err_irq)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(NULL, "eTSEC");
|
||||
qdev_set_nic_properties(dev, nd);
|
||||
|
||||
if (qdev_init(dev)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq);
|
||||
|
||||
memory_region_add_subregion(mr, base,
|
||||
SYS_BUS_DEVICE(dev)->mmio[0].memory);
|
||||
|
||||
return dev;
|
||||
}
|
174
hw/net/fsl_etsec/etsec.h
Normal file
174
hw/net/fsl_etsec/etsec.h
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* QEMU Freescale eTSEC Emulator
|
||||
*
|
||||
* Copyright (c) 2011-2013 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.
|
||||
*/
|
||||
#ifndef _ETSEC_H_
|
||||
#define _ETSEC_H_
|
||||
|
||||
#include "hw/qdev.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "net/net.h"
|
||||
#include "hw/ptimer.h"
|
||||
|
||||
/* Buffer Descriptors */
|
||||
|
||||
typedef struct eTSEC_rxtx_bd {
|
||||
uint16_t flags;
|
||||
uint16_t length;
|
||||
uint32_t bufptr;
|
||||
} eTSEC_rxtx_bd;
|
||||
|
||||
#define BD_WRAP (1 << 13)
|
||||
#define BD_INTERRUPT (1 << 12)
|
||||
#define BD_LAST (1 << 11)
|
||||
|
||||
#define BD_TX_READY (1 << 15)
|
||||
#define BD_TX_PADCRC (1 << 14)
|
||||
#define BD_TX_TC (1 << 10)
|
||||
#define BD_TX_PREDEF (1 << 9)
|
||||
#define BD_TX_HFELC (1 << 7)
|
||||
#define BD_TX_CFRL (1 << 6)
|
||||
#define BD_TX_RC_MASK 0xF
|
||||
#define BD_TX_RC_OFFSET 0x2
|
||||
#define BD_TX_TOEUN (1 << 1)
|
||||
#define BD_TX_TR (1 << 0)
|
||||
|
||||
#define BD_RX_EMPTY (1 << 15)
|
||||
#define BD_RX_RO1 (1 << 14)
|
||||
#define BD_RX_FIRST (1 << 10)
|
||||
#define BD_RX_MISS (1 << 8)
|
||||
#define BD_RX_BROADCAST (1 << 7)
|
||||
#define BD_RX_MULTICAST (1 << 6)
|
||||
#define BD_RX_LG (1 << 5)
|
||||
#define BD_RX_NO (1 << 4)
|
||||
#define BD_RX_SH (1 << 3)
|
||||
#define BD_RX_CR (1 << 2)
|
||||
#define BD_RX_OV (1 << 1)
|
||||
#define BD_RX_TR (1 << 0)
|
||||
|
||||
/* Tx FCB flags */
|
||||
#define FCB_TX_VLN (1 << 7)
|
||||
#define FCB_TX_IP (1 << 6)
|
||||
#define FCB_TX_IP6 (1 << 5)
|
||||
#define FCB_TX_TUP (1 << 4)
|
||||
#define FCB_TX_UDP (1 << 3)
|
||||
#define FCB_TX_CIP (1 << 2)
|
||||
#define FCB_TX_CTU (1 << 1)
|
||||
#define FCB_TX_NPH (1 << 0)
|
||||
|
||||
/* PHY Status Register */
|
||||
#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */
|
||||
#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */
|
||||
#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
|
||||
#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */
|
||||
#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */
|
||||
#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
|
||||
#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
|
||||
#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */
|
||||
#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */
|
||||
#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */
|
||||
#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */
|
||||
#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */
|
||||
#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */
|
||||
#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
|
||||
#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
|
||||
|
||||
/* eTSEC */
|
||||
|
||||
/* Number of register in the device */
|
||||
#define ETSEC_REG_NUMBER 1024
|
||||
|
||||
typedef struct eTSEC_Register {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
uint32_t access;
|
||||
uint32_t value;
|
||||
} eTSEC_Register;
|
||||
|
||||
typedef struct eTSEC {
|
||||
SysBusDevice busdev;
|
||||
|
||||
MemoryRegion io_area;
|
||||
|
||||
eTSEC_Register regs[ETSEC_REG_NUMBER];
|
||||
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
|
||||
/* Tx */
|
||||
|
||||
uint8_t *tx_buffer;
|
||||
uint32_t tx_buffer_len;
|
||||
eTSEC_rxtx_bd first_bd;
|
||||
|
||||
/* Rx */
|
||||
|
||||
uint8_t *rx_buffer;
|
||||
uint32_t rx_buffer_len;
|
||||
uint32_t rx_remaining_data;
|
||||
uint8_t rx_first_in_frame;
|
||||
uint8_t rx_fcb_size;
|
||||
eTSEC_rxtx_bd rx_first_bd;
|
||||
uint8_t rx_fcb[10];
|
||||
uint32_t rx_padding;
|
||||
|
||||
/* IRQs */
|
||||
qemu_irq tx_irq;
|
||||
qemu_irq rx_irq;
|
||||
qemu_irq err_irq;
|
||||
|
||||
|
||||
uint16_t phy_status;
|
||||
uint16_t phy_control;
|
||||
|
||||
/* Polling */
|
||||
QEMUBH *bh;
|
||||
struct ptimer_state *ptimer;
|
||||
|
||||
} eTSEC;
|
||||
|
||||
#define TYPE_ETSEC_COMMON "eTSEC"
|
||||
#define ETSEC_COMMON(obj) \
|
||||
OBJECT_CHECK(eTSEC, (obj), TYPE_ETSEC_COMMON)
|
||||
|
||||
#define eTSEC_TRANSMIT 1
|
||||
#define eTSEC_RECEIVE 2
|
||||
|
||||
DeviceState *etsec_create(hwaddr base,
|
||||
MemoryRegion *mr,
|
||||
NICInfo *nd,
|
||||
qemu_irq tx_irq,
|
||||
qemu_irq rx_irq,
|
||||
qemu_irq err_irq);
|
||||
|
||||
void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr);
|
||||
void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr);
|
||||
void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size);
|
||||
|
||||
void etsec_write_miim(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value);
|
||||
|
||||
void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc);
|
||||
|
||||
#endif /* ! _ETSEC_H_ */
|
146
hw/net/fsl_etsec/miim.c
Normal file
146
hw/net/fsl_etsec/miim.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* QEMU Freescale eTSEC Emulator
|
||||
*
|
||||
* Copyright (c) 2011-2013 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 "etsec.h"
|
||||
#include "registers.h"
|
||||
|
||||
/* #define DEBUG_MIIM */
|
||||
|
||||
#define MIIM_CONTROL 0
|
||||
#define MIIM_STATUS 1
|
||||
#define MIIM_PHY_ID_1 2
|
||||
#define MIIM_PHY_ID_2 3
|
||||
#define MIIM_T2_STATUS 10
|
||||
#define MIIM_EXT_STATUS 15
|
||||
|
||||
static void miim_read_cycle(eTSEC *etsec)
|
||||
{
|
||||
uint8_t phy;
|
||||
uint8_t addr;
|
||||
uint16_t value;
|
||||
|
||||
phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F;
|
||||
(void)phy; /* Unreferenced */
|
||||
addr = etsec->regs[MIIMADD].value & 0x1F;
|
||||
|
||||
switch (addr) {
|
||||
case MIIM_CONTROL:
|
||||
value = etsec->phy_control;
|
||||
break;
|
||||
case MIIM_STATUS:
|
||||
value = etsec->phy_status;
|
||||
break;
|
||||
case MIIM_T2_STATUS:
|
||||
value = 0x1800; /* Local and remote receivers OK */
|
||||
break;
|
||||
default:
|
||||
value = 0x0;
|
||||
break;
|
||||
};
|
||||
|
||||
#ifdef DEBUG_MIIM
|
||||
qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value);
|
||||
#endif
|
||||
|
||||
etsec->regs[MIIMSTAT].value = value;
|
||||
}
|
||||
|
||||
static void miim_write_cycle(eTSEC *etsec)
|
||||
{
|
||||
uint8_t phy;
|
||||
uint8_t addr;
|
||||
uint16_t value;
|
||||
|
||||
phy = (etsec->regs[MIIMADD].value >> 8) & 0x1F;
|
||||
(void)phy; /* Unreferenced */
|
||||
addr = etsec->regs[MIIMADD].value & 0x1F;
|
||||
value = etsec->regs[MIIMCON].value & 0xffff;
|
||||
|
||||
#ifdef DEBUG_MIIM
|
||||
qemu_log("%s phy:%d addr:0x%x value:0x%x\n", __func__, phy, addr, value);
|
||||
#endif
|
||||
|
||||
switch (addr) {
|
||||
case MIIM_CONTROL:
|
||||
etsec->phy_control = value & ~(0x8100);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void etsec_write_miim(eTSEC *etsec,
|
||||
eTSEC_Register *reg,
|
||||
uint32_t reg_index,
|
||||
uint32_t value)
|
||||
{
|
||||
|
||||
switch (reg_index) {
|
||||
|
||||
case MIIMCOM:
|
||||
/* Read and scan cycle */
|
||||
|
||||
if ((!(reg->value & MIIMCOM_READ)) && (value & MIIMCOM_READ)) {
|
||||
/* Read */
|
||||
miim_read_cycle(etsec);
|
||||
}
|
||||
reg->value = value;
|
||||
break;
|
||||
|
||||
case MIIMCON:
|
||||
reg->value = value & 0xffff;
|
||||
miim_write_cycle(etsec);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Default handling */
|
||||
switch (reg->access) {
|
||||
|
||||
case ACC_RW:
|
||||
case ACC_WO:
|
||||
reg->value = value;
|
||||
break;
|
||||
|
||||
case ACC_W1C:
|
||||
reg->value &= ~value;
|
||||
break;
|
||||
|
||||
case ACC_RO:
|
||||
default:
|
||||
/* Read Only or Unknown register */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void etsec_miim_link_status(eTSEC *etsec, NetClientState *nc)
|
||||
{
|
||||
/* Set link status */
|
||||
if (nc->link_down) {
|
||||
etsec->phy_status &= ~MII_SR_LINK_STATUS;
|
||||
} else {
|
||||
etsec->phy_status |= MII_SR_LINK_STATUS;
|
||||
}
|
||||
}
|
295
hw/net/fsl_etsec/registers.c
Normal file
295
hw/net/fsl_etsec/registers.c
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* QEMU Freescale eTSEC Emulator
|
||||
*
|
||||
* Copyright (c) 2011-2013 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 "registers.h"
|
||||
|
||||
const eTSEC_Register_Definition eTSEC_registers_def[] = {
|
||||
{0x000, "TSEC_ID", "Controller ID register", ACC_RO, 0x01240000},
|
||||
{0x004, "TSEC_ID2", "Controller ID register 2", ACC_RO, 0x003000F0},
|
||||
{0x010, "IEVENT", "Interrupt event register", ACC_W1C, 0x00000000},
|
||||
{0x014, "IMASK", "Interrupt mask register", ACC_RW, 0x00000000},
|
||||
{0x018, "EDIS", "Error disabled register", ACC_RW, 0x00000000},
|
||||
{0x020, "ECNTRL", "Ethernet control register", ACC_RW, 0x00000040},
|
||||
{0x028, "PTV", "Pause time value register", ACC_RW, 0x00000000},
|
||||
{0x02C, "DMACTRL", "DMA control register", ACC_RW, 0x00000000},
|
||||
{0x030, "TBIPA", "TBI PHY address register", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC FIFO Control and Status Registers */
|
||||
|
||||
{0x058, "FIFO_RX_ALARM", "FIFO receive alarm start threshold register", ACC_RW, 0x00000040},
|
||||
{0x05C, "FIFO_RX_ALARM_SHUTOFF", "FIFO receive alarm shut-off threshold register", ACC_RW, 0x00000080},
|
||||
{0x08C, "FIFO_TX_THR", "FIFO transmit threshold register", ACC_RW, 0x00000080},
|
||||
{0x098, "FIFO_TX_STARVE", "FIFO transmit starve register", ACC_RW, 0x00000040},
|
||||
{0x09C, "FIFO_TX_STARVE_SHUTOFF", "FIFO transmit starve shut-off register", ACC_RW, 0x00000080},
|
||||
|
||||
/* eTSEC Transmit Control and Status Registers */
|
||||
|
||||
{0x100, "TCTRL", "Transmit control register", ACC_RW, 0x00000000},
|
||||
{0x104, "TSTAT", "Transmit status register", ACC_W1C, 0x00000000},
|
||||
{0x108, "DFVLAN", "Default VLAN control word", ACC_RW, 0x81000000},
|
||||
{0x110, "TXIC", "Transmit interrupt coalescing register", ACC_RW, 0x00000000},
|
||||
{0x114, "TQUEUE", "Transmit queue control register", ACC_RW, 0x00008000},
|
||||
{0x140, "TR03WT", "TxBD Rings 0-3 round-robin weightings", ACC_RW, 0x00000000},
|
||||
{0x144, "TR47WT", "TxBD Rings 4-7 round-robin weightings", ACC_RW, 0x00000000},
|
||||
{0x180, "TBDBPH", "Tx data buffer pointer high bits", ACC_RW, 0x00000000},
|
||||
{0x184, "TBPTR0", "TxBD pointer for ring 0", ACC_RW, 0x00000000},
|
||||
{0x18C, "TBPTR1", "TxBD pointer for ring 1", ACC_RW, 0x00000000},
|
||||
{0x194, "TBPTR2", "TxBD pointer for ring 2", ACC_RW, 0x00000000},
|
||||
{0x19C, "TBPTR3", "TxBD pointer for ring 3", ACC_RW, 0x00000000},
|
||||
{0x1A4, "TBPTR4", "TxBD pointer for ring 4", ACC_RW, 0x00000000},
|
||||
{0x1AC, "TBPTR5", "TxBD pointer for ring 5", ACC_RW, 0x00000000},
|
||||
{0x1B4, "TBPTR6", "TxBD pointer for ring 6", ACC_RW, 0x00000000},
|
||||
{0x1BC, "TBPTR7", "TxBD pointer for ring 7", ACC_RW, 0x00000000},
|
||||
{0x200, "TBASEH", "TxBD base address high bits", ACC_RW, 0x00000000},
|
||||
{0x204, "TBASE0", "TxBD base address of ring 0", ACC_RW, 0x00000000},
|
||||
{0x20C, "TBASE1", "TxBD base address of ring 1", ACC_RW, 0x00000000},
|
||||
{0x214, "TBASE2", "TxBD base address of ring 2", ACC_RW, 0x00000000},
|
||||
{0x21C, "TBASE3", "TxBD base address of ring 3", ACC_RW, 0x00000000},
|
||||
{0x224, "TBASE4", "TxBD base address of ring 4", ACC_RW, 0x00000000},
|
||||
{0x22C, "TBASE5", "TxBD base address of ring 5", ACC_RW, 0x00000000},
|
||||
{0x234, "TBASE6", "TxBD base address of ring 6", ACC_RW, 0x00000000},
|
||||
{0x23C, "TBASE7", "TxBD base address of ring 7", ACC_RW, 0x00000000},
|
||||
{0x280, "TMR_TXTS1_ID", "Tx time stamp identification tag (set 1)", ACC_RO, 0x00000000},
|
||||
{0x284, "TMR_TXTS2_ID", "Tx time stamp identification tag (set 2)", ACC_RO, 0x00000000},
|
||||
{0x2C0, "TMR_TXTS1_H", "Tx time stamp high (set 1)", ACC_RO, 0x00000000},
|
||||
{0x2C4, "TMR_TXTS1_L", "Tx time stamp high (set 1)", ACC_RO, 0x00000000},
|
||||
{0x2C8, "TMR_TXTS2_H", "Tx time stamp high (set 2)", ACC_RO, 0x00000000},
|
||||
{0x2CC, "TMR_TXTS2_L", "Tx time stamp high (set 2)", ACC_RO, 0x00000000},
|
||||
|
||||
/* eTSEC Receive Control and Status Registers */
|
||||
|
||||
{0x300, "RCTRL", "Receive control register", ACC_RW, 0x00000000},
|
||||
{0x304, "RSTAT", "Receive status register", ACC_W1C, 0x00000000},
|
||||
{0x310, "RXIC", "Receive interrupt coalescing register", ACC_RW, 0x00000000},
|
||||
{0x314, "RQUEUE", "Receive queue control register.", ACC_RW, 0x00800080},
|
||||
{0x330, "RBIFX", "Receive bit field extract control register", ACC_RW, 0x00000000},
|
||||
{0x334, "RQFAR", "Receive queue filing table address register", ACC_RW, 0x00000000},
|
||||
{0x338, "RQFCR", "Receive queue filing table control register", ACC_RW, 0x00000000},
|
||||
{0x33C, "RQFPR", "Receive queue filing table property register", ACC_RW, 0x00000000},
|
||||
{0x340, "MRBLR", "Maximum receive buffer length register", ACC_RW, 0x00000000},
|
||||
{0x380, "RBDBPH", "Rx data buffer pointer high bits", ACC_RW, 0x00000000},
|
||||
{0x384, "RBPTR0", "RxBD pointer for ring 0", ACC_RW, 0x00000000},
|
||||
{0x38C, "RBPTR1", "RxBD pointer for ring 1", ACC_RW, 0x00000000},
|
||||
{0x394, "RBPTR2", "RxBD pointer for ring 2", ACC_RW, 0x00000000},
|
||||
{0x39C, "RBPTR3", "RxBD pointer for ring 3", ACC_RW, 0x00000000},
|
||||
{0x3A4, "RBPTR4", "RxBD pointer for ring 4", ACC_RW, 0x00000000},
|
||||
{0x3AC, "RBPTR5", "RxBD pointer for ring 5", ACC_RW, 0x00000000},
|
||||
{0x3B4, "RBPTR6", "RxBD pointer for ring 6", ACC_RW, 0x00000000},
|
||||
{0x3BC, "RBPTR7", "RxBD pointer for ring 7", ACC_RW, 0x00000000},
|
||||
{0x400, "RBASEH", "RxBD base address high bits", ACC_RW, 0x00000000},
|
||||
{0x404, "RBASE0", "RxBD base address of ring 0", ACC_RW, 0x00000000},
|
||||
{0x40C, "RBASE1", "RxBD base address of ring 1", ACC_RW, 0x00000000},
|
||||
{0x414, "RBASE2", "RxBD base address of ring 2", ACC_RW, 0x00000000},
|
||||
{0x41C, "RBASE3", "RxBD base address of ring 3", ACC_RW, 0x00000000},
|
||||
{0x424, "RBASE4", "RxBD base address of ring 4", ACC_RW, 0x00000000},
|
||||
{0x42C, "RBASE5", "RxBD base address of ring 5", ACC_RW, 0x00000000},
|
||||
{0x434, "RBASE6", "RxBD base address of ring 6", ACC_RW, 0x00000000},
|
||||
{0x43C, "RBASE7", "RxBD base address of ring 7", ACC_RW, 0x00000000},
|
||||
{0x4C0, "TMR_RXTS_H", "Rx timer time stamp register high", ACC_RW, 0x00000000},
|
||||
{0x4C4, "TMR_RXTS_L", "Rx timer time stamp register low", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC MAC Registers */
|
||||
|
||||
{0x500, "MACCFG1", "MAC configuration register 1", ACC_RW, 0x00000000},
|
||||
{0x504, "MACCFG2", "MAC configuration register 2", ACC_RW, 0x00007000},
|
||||
{0x508, "IPGIFG", "Inter-packet/inter-frame gap register", ACC_RW, 0x40605060},
|
||||
{0x50C, "HAFDUP", "Half-duplex control", ACC_RW, 0x00A1F037},
|
||||
{0x510, "MAXFRM", "Maximum frame length", ACC_RW, 0x00000600},
|
||||
{0x520, "MIIMCFG", "MII management configuration", ACC_RW, 0x00000007},
|
||||
{0x524, "MIIMCOM", "MII management command", ACC_RW, 0x00000000},
|
||||
{0x528, "MIIMADD", "MII management address", ACC_RW, 0x00000000},
|
||||
{0x52C, "MIIMCON", "MII management control", ACC_WO, 0x00000000},
|
||||
{0x530, "MIIMSTAT", "MII management status", ACC_RO, 0x00000000},
|
||||
{0x534, "MIIMIND", "MII management indicator", ACC_RO, 0x00000000},
|
||||
{0x53C, "IFSTAT", "Interface status", ACC_RO, 0x00000000},
|
||||
{0x540, "MACSTNADDR1", "MAC station address register 1", ACC_RW, 0x00000000},
|
||||
{0x544, "MACSTNADDR2", "MAC station address register 2", ACC_RW, 0x00000000},
|
||||
{0x548, "MAC01ADDR1", "MAC exact match address 1, part 1", ACC_RW, 0x00000000},
|
||||
{0x54C, "MAC01ADDR2", "MAC exact match address 1, part 2", ACC_RW, 0x00000000},
|
||||
{0x550, "MAC02ADDR1", "MAC exact match address 2, part 1", ACC_RW, 0x00000000},
|
||||
{0x554, "MAC02ADDR2", "MAC exact match address 2, part 2", ACC_RW, 0x00000000},
|
||||
{0x558, "MAC03ADDR1", "MAC exact match address 3, part 1", ACC_RW, 0x00000000},
|
||||
{0x55C, "MAC03ADDR2", "MAC exact match address 3, part 2", ACC_RW, 0x00000000},
|
||||
{0x560, "MAC04ADDR1", "MAC exact match address 4, part 1", ACC_RW, 0x00000000},
|
||||
{0x564, "MAC04ADDR2", "MAC exact match address 4, part 2", ACC_RW, 0x00000000},
|
||||
{0x568, "MAC05ADDR1", "MAC exact match address 5, part 1", ACC_RW, 0x00000000},
|
||||
{0x56C, "MAC05ADDR2", "MAC exact match address 5, part 2", ACC_RW, 0x00000000},
|
||||
{0x570, "MAC06ADDR1", "MAC exact match address 6, part 1", ACC_RW, 0x00000000},
|
||||
{0x574, "MAC06ADDR2", "MAC exact match address 6, part 2", ACC_RW, 0x00000000},
|
||||
{0x578, "MAC07ADDR1", "MAC exact match address 7, part 1", ACC_RW, 0x00000000},
|
||||
{0x57C, "MAC07ADDR2", "MAC exact match address 7, part 2", ACC_RW, 0x00000000},
|
||||
{0x580, "MAC08ADDR1", "MAC exact match address 8, part 1", ACC_RW, 0x00000000},
|
||||
{0x584, "MAC08ADDR2", "MAC exact match address 8, part 2", ACC_RW, 0x00000000},
|
||||
{0x588, "MAC09ADDR1", "MAC exact match address 9, part 1", ACC_RW, 0x00000000},
|
||||
{0x58C, "MAC09ADDR2", "MAC exact match address 9, part 2", ACC_RW, 0x00000000},
|
||||
{0x590, "MAC10ADDR1", "MAC exact match address 10, part 1", ACC_RW, 0x00000000},
|
||||
{0x594, "MAC10ADDR2", "MAC exact match address 10, part 2", ACC_RW, 0x00000000},
|
||||
{0x598, "MAC11ADDR1", "MAC exact match address 11, part 1", ACC_RW, 0x00000000},
|
||||
{0x59C, "MAC11ADDR2", "MAC exact match address 11, part 2", ACC_RW, 0x00000000},
|
||||
{0x5A0, "MAC12ADDR1", "MAC exact match address 12, part 1", ACC_RW, 0x00000000},
|
||||
{0x5A4, "MAC12ADDR2", "MAC exact match address 12, part 2", ACC_RW, 0x00000000},
|
||||
{0x5A8, "MAC13ADDR1", "MAC exact match address 13, part 1", ACC_RW, 0x00000000},
|
||||
{0x5AC, "MAC13ADDR2", "MAC exact match address 13, part 2", ACC_RW, 0x00000000},
|
||||
{0x5B0, "MAC14ADDR1", "MAC exact match address 14, part 1", ACC_RW, 0x00000000},
|
||||
{0x5B4, "MAC14ADDR2", "MAC exact match address 14, part 2", ACC_RW, 0x00000000},
|
||||
{0x5B8, "MAC15ADDR1", "MAC exact match address 15, part 1", ACC_RW, 0x00000000},
|
||||
{0x5BC, "MAC15ADDR2", "MAC exact match address 15, part 2", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC, "Transmit", "and", Receive, Counters */
|
||||
|
||||
{0x680, "TR64", "Transmit and receive 64-byte frame counter ", ACC_RW, 0x00000000},
|
||||
{0x684, "TR127", "Transmit and receive 65- to 127-byte frame counter", ACC_RW, 0x00000000},
|
||||
{0x688, "TR255", "Transmit and receive 128- to 255-byte frame counter", ACC_RW, 0x00000000},
|
||||
{0x68C, "TR511", "Transmit and receive 256- to 511-byte frame counter", ACC_RW, 0x00000000},
|
||||
{0x690, "TR1K", "Transmit and receive 512- to 1023-byte frame counter", ACC_RW, 0x00000000},
|
||||
{0x694, "TRMAX", "Transmit and receive 1024- to 1518-byte frame counter", ACC_RW, 0x00000000},
|
||||
{0x698, "TRMGV", "Transmit and receive 1519- to 1522-byte good VLAN frame count", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC Receive Counters */
|
||||
|
||||
{0x69C, "RBYT", "Receive byte counter", ACC_RW, 0x00000000},
|
||||
{0x6A0, "RPKT", "Receive packet counter", ACC_RW, 0x00000000},
|
||||
{0x6A4, "RFCS", "Receive FCS error counter", ACC_RW, 0x00000000},
|
||||
{0x6A8, "RMCA", "Receive multicast packet counter", ACC_RW, 0x00000000},
|
||||
{0x6AC, "RBCA", "Receive broadcast packet counter", ACC_RW, 0x00000000},
|
||||
{0x6B0, "RXCF", "Receive control frame packet counter ", ACC_RW, 0x00000000},
|
||||
{0x6B4, "RXPF", "Receive PAUSE frame packet counter", ACC_RW, 0x00000000},
|
||||
{0x6B8, "RXUO", "Receive unknown OP code counter ", ACC_RW, 0x00000000},
|
||||
{0x6BC, "RALN", "Receive alignment error counter ", ACC_RW, 0x00000000},
|
||||
{0x6C0, "RFLR", "Receive frame length error counter ", ACC_RW, 0x00000000},
|
||||
{0x6C4, "RCDE", "Receive code error counter ", ACC_RW, 0x00000000},
|
||||
{0x6C8, "RCSE", "Receive carrier sense error counter", ACC_RW, 0x00000000},
|
||||
{0x6CC, "RUND", "Receive undersize packet counter", ACC_RW, 0x00000000},
|
||||
{0x6D0, "ROVR", "Receive oversize packet counter ", ACC_RW, 0x00000000},
|
||||
{0x6D4, "RFRG", "Receive fragments counter", ACC_RW, 0x00000000},
|
||||
{0x6D8, "RJBR", "Receive jabber counter ", ACC_RW, 0x00000000},
|
||||
{0x6DC, "RDRP", "Receive drop counter", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC Transmit Counters */
|
||||
|
||||
{0x6E0, "TBYT", "Transmit byte counter", ACC_RW, 0x00000000},
|
||||
{0x6E4, "TPKT", "Transmit packet counter", ACC_RW, 0x00000000},
|
||||
{0x6E8, "TMCA", "Transmit multicast packet counter ", ACC_RW, 0x00000000},
|
||||
{0x6EC, "TBCA", "Transmit broadcast packet counter ", ACC_RW, 0x00000000},
|
||||
{0x6F0, "TXPF", "Transmit PAUSE control frame counter ", ACC_RW, 0x00000000},
|
||||
{0x6F4, "TDFR", "Transmit deferral packet counter ", ACC_RW, 0x00000000},
|
||||
{0x6F8, "TEDF", "Transmit excessive deferral packet counter ", ACC_RW, 0x00000000},
|
||||
{0x6FC, "TSCL", "Transmit single collision packet counter", ACC_RW, 0x00000000},
|
||||
{0x700, "TMCL", "Transmit multiple collision packet counter", ACC_RW, 0x00000000},
|
||||
{0x704, "TLCL", "Transmit late collision packet counter", ACC_RW, 0x00000000},
|
||||
{0x708, "TXCL", "Transmit excessive collision packet counter", ACC_RW, 0x00000000},
|
||||
{0x70C, "TNCL", "Transmit total collision counter ", ACC_RW, 0x00000000},
|
||||
{0x714, "TDRP", "Transmit drop frame counter", ACC_RW, 0x00000000},
|
||||
{0x718, "TJBR", "Transmit jabber frame counter ", ACC_RW, 0x00000000},
|
||||
{0x71C, "TFCS", "Transmit FCS error counter", ACC_RW, 0x00000000},
|
||||
{0x720, "TXCF", "Transmit control frame counter ", ACC_RW, 0x00000000},
|
||||
{0x724, "TOVR", "Transmit oversize frame counter", ACC_RW, 0x00000000},
|
||||
{0x728, "TUND", "Transmit undersize frame counter ", ACC_RW, 0x00000000},
|
||||
{0x72C, "TFRG", "Transmit fragments frame counter ", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC Counter Control and TOE Statistics Registers */
|
||||
|
||||
{0x730, "CAR1", "Carry register one register", ACC_W1C, 0x00000000},
|
||||
{0x734, "CAR2", "Carry register two register ", ACC_W1C, 0x00000000},
|
||||
{0x738, "CAM1", "Carry register one mask register ", ACC_RW, 0xFE03FFFF},
|
||||
{0x73C, "CAM2", "Carry register two mask register ", ACC_RW, 0x000FFFFD},
|
||||
{0x740, "RREJ", "Receive filer rejected packet counter", ACC_RW, 0x00000000},
|
||||
|
||||
/* Hash Function Registers */
|
||||
|
||||
{0x800, "IGADDR0", "Individual/group address register 0", ACC_RW, 0x00000000},
|
||||
{0x804, "IGADDR1", "Individual/group address register 1", ACC_RW, 0x00000000},
|
||||
{0x808, "IGADDR2", "Individual/group address register 2", ACC_RW, 0x00000000},
|
||||
{0x80C, "IGADDR3", "Individual/group address register 3", ACC_RW, 0x00000000},
|
||||
{0x810, "IGADDR4", "Individual/group address register 4", ACC_RW, 0x00000000},
|
||||
{0x814, "IGADDR5", "Individual/group address register 5", ACC_RW, 0x00000000},
|
||||
{0x818, "IGADDR6", "Individual/group address register 6", ACC_RW, 0x00000000},
|
||||
{0x81C, "IGADDR7", "Individual/group address register 7", ACC_RW, 0x00000000},
|
||||
{0x880, "GADDR0", "Group address register 0", ACC_RW, 0x00000000},
|
||||
{0x884, "GADDR1", "Group address register 1", ACC_RW, 0x00000000},
|
||||
{0x888, "GADDR2", "Group address register 2", ACC_RW, 0x00000000},
|
||||
{0x88C, "GADDR3", "Group address register 3", ACC_RW, 0x00000000},
|
||||
{0x890, "GADDR4", "Group address register 4", ACC_RW, 0x00000000},
|
||||
{0x894, "GADDR5", "Group address register 5", ACC_RW, 0x00000000},
|
||||
{0x898, "GADDR6", "Group address register 6", ACC_RW, 0x00000000},
|
||||
{0x89C, "GADDR7", "Group address register 7", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC DMA Attribute Registers */
|
||||
|
||||
{0xBF8, "ATTR", "Attribute register", ACC_RW, 0x00000000},
|
||||
{0xBFC, "ATTRELI", "Attribute extract length and extract index register", ACC_RW, 0x00000000},
|
||||
|
||||
|
||||
/* eTSEC Lossless Flow Control Registers */
|
||||
|
||||
{0xC00, "RQPRM0", "Receive Queue Parameters register 0 ", ACC_RW, 0x00000000},
|
||||
{0xC04, "RQPRM1", "Receive Queue Parameters register 1 ", ACC_RW, 0x00000000},
|
||||
{0xC08, "RQPRM2", "Receive Queue Parameters register 2 ", ACC_RW, 0x00000000},
|
||||
{0xC0C, "RQPRM3", "Receive Queue Parameters register 3 ", ACC_RW, 0x00000000},
|
||||
{0xC10, "RQPRM4", "Receive Queue Parameters register 4 ", ACC_RW, 0x00000000},
|
||||
{0xC14, "RQPRM5", "Receive Queue Parameters register 5 ", ACC_RW, 0x00000000},
|
||||
{0xC18, "RQPRM6", "Receive Queue Parameters register 6 ", ACC_RW, 0x00000000},
|
||||
{0xC1C, "RQPRM7", "Receive Queue Parameters register 7 ", ACC_RW, 0x00000000},
|
||||
{0xC44, "RFBPTR0", "Last Free RxBD pointer for ring 0", ACC_RW, 0x00000000},
|
||||
{0xC4C, "RFBPTR1", "Last Free RxBD pointer for ring 1", ACC_RW, 0x00000000},
|
||||
{0xC54, "RFBPTR2", "Last Free RxBD pointer for ring 2", ACC_RW, 0x00000000},
|
||||
{0xC5C, "RFBPTR3", "Last Free RxBD pointer for ring 3", ACC_RW, 0x00000000},
|
||||
{0xC64, "RFBPTR4", "Last Free RxBD pointer for ring 4", ACC_RW, 0x00000000},
|
||||
{0xC6C, "RFBPTR5", "Last Free RxBD pointer for ring 5", ACC_RW, 0x00000000},
|
||||
{0xC74, "RFBPTR6", "Last Free RxBD pointer for ring 6", ACC_RW, 0x00000000},
|
||||
{0xC7C, "RFBPTR7", "Last Free RxBD pointer for ring 7", ACC_RW, 0x00000000},
|
||||
|
||||
/* eTSEC Future Expansion Space */
|
||||
|
||||
/* Reserved*/
|
||||
|
||||
/* eTSEC IEEE 1588 Registers */
|
||||
|
||||
{0xE00, "TMR_CTRL", "Timer control register", ACC_RW, 0x00010001},
|
||||
{0xE04, "TMR_TEVENT", "time stamp event register", ACC_W1C, 0x00000000},
|
||||
{0xE08, "TMR_TEMASK", "Timer event mask register", ACC_RW, 0x00000000},
|
||||
{0xE0C, "TMR_PEVENT", "time stamp event register", ACC_RW, 0x00000000},
|
||||
{0xE10, "TMR_PEMASK", "Timer event mask register", ACC_RW, 0x00000000},
|
||||
{0xE14, "TMR_STAT", "time stamp status register", ACC_RW, 0x00000000},
|
||||
{0xE18, "TMR_CNT_H", "timer counter high register", ACC_RW, 0x00000000},
|
||||
{0xE1C, "TMR_CNT_L", "timer counter low register", ACC_RW, 0x00000000},
|
||||
{0xE20, "TMR_ADD", "Timer drift compensation addend register", ACC_RW, 0x00000000},
|
||||
{0xE24, "TMR_ACC", "Timer accumulator register", ACC_RW, 0x00000000},
|
||||
{0xE28, "TMR_PRSC", "Timer prescale", ACC_RW, 0x00000002},
|
||||
{0xE30, "TMROFF_H", "Timer offset high", ACC_RW, 0x00000000},
|
||||
{0xE34, "TMROFF_L", "Timer offset low", ACC_RW, 0x00000000},
|
||||
{0xE40, "TMR_ALARM1_H", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF},
|
||||
{0xE44, "TMR_ALARM1_L", "Timer alarm 1 high register", ACC_RW, 0xFFFFFFFF},
|
||||
{0xE48, "TMR_ALARM2_H", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF},
|
||||
{0xE4C, "TMR_ALARM2_L", "Timer alarm 2 high register", ACC_RW, 0xFFFFFFFF},
|
||||
{0xE80, "TMR_FIPER1", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF},
|
||||
{0xE84, "TMR_FIPER2", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF},
|
||||
{0xE88, "TMR_FIPER3", "Timer fixed period interval", ACC_RW, 0xFFFFFFFF},
|
||||
{0xEA0, "TMR_ETTS1_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000},
|
||||
{0xEA4, "TMR_ETTS1_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000},
|
||||
{0xEA8, "TMR_ETTS2_H", "Time stamp of general purpose external trigger ", ACC_RW, 0x00000000},
|
||||
{0xEAC, "TMR_ETTS2_L", "Time stamp of general purpose external trigger", ACC_RW, 0x00000000},
|
||||
|
||||
/* End Of Table */
|
||||
{0x0, 0x0, 0x0, 0x0, 0x0}
|
||||
};
|
320
hw/net/fsl_etsec/registers.h
Normal file
320
hw/net/fsl_etsec/registers.h
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* QEMU Freescale eTSEC Emulator
|
||||
*
|
||||
* Copyright (c) 2011-2013 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.
|
||||
*/
|
||||
#ifndef _ETSEC_REGISTERS_H_
|
||||
#define _ETSEC_REGISTERS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum eTSEC_Register_Access_Type {
|
||||
ACC_RW = 1, /* Read/Write */
|
||||
ACC_RO = 2, /* Read Only */
|
||||
ACC_WO = 3, /* Write Only */
|
||||
ACC_W1C = 4, /* Write 1 to clear */
|
||||
ACC_UNKNOWN = 5 /* Unknown register*/
|
||||
};
|
||||
|
||||
typedef struct eTSEC_Register_Definition {
|
||||
uint32_t offset;
|
||||
const char *name;
|
||||
const char *desc;
|
||||
enum eTSEC_Register_Access_Type access;
|
||||
uint32_t reset;
|
||||
} eTSEC_Register_Definition;
|
||||
|
||||
extern const eTSEC_Register_Definition eTSEC_registers_def[];
|
||||
|
||||
#define DMACTRL_LE (1 << 15)
|
||||
#define DMACTRL_GRS (1 << 4)
|
||||
#define DMACTRL_GTS (1 << 3)
|
||||
#define DMACTRL_WOP (1 << 0)
|
||||
|
||||
#define IEVENT_PERR (1 << 0)
|
||||
#define IEVENT_DPE (1 << 1)
|
||||
#define IEVENT_FIQ (1 << 2)
|
||||
#define IEVENT_FIR (1 << 3)
|
||||
#define IEVENT_FGPI (1 << 4)
|
||||
#define IEVENT_RXF (1 << 7)
|
||||
#define IEVENT_GRSC (1 << 8)
|
||||
#define IEVENT_MMRW (1 << 9)
|
||||
#define IEVENT_MMRD (1 << 10)
|
||||
#define IEVENT_MAG (1 << 11)
|
||||
#define IEVENT_RXB (1 << 15)
|
||||
#define IEVENT_XFUN (1 << 16)
|
||||
#define IEVENT_CRL (1 << 17)
|
||||
#define IEVENT_LC (1 << 18)
|
||||
#define IEVENT_TXF (1 << 20)
|
||||
#define IEVENT_TXB (1 << 21)
|
||||
#define IEVENT_TXE (1 << 22)
|
||||
#define IEVENT_TXC (1 << 23)
|
||||
#define IEVENT_BABT (1 << 24)
|
||||
#define IEVENT_GTSC (1 << 25)
|
||||
#define IEVENT_MSRO (1 << 26)
|
||||
#define IEVENT_EBERR (1 << 28)
|
||||
#define IEVENT_BSY (1 << 29)
|
||||
#define IEVENT_RXC (1 << 30)
|
||||
#define IEVENT_BABR (1 << 31)
|
||||
|
||||
#define IMASK_RXFEN (1 << 7)
|
||||
#define IMASK_GRSCEN (1 << 8)
|
||||
#define IMASK_RXBEN (1 << 15)
|
||||
#define IMASK_TXFEN (1 << 20)
|
||||
#define IMASK_TXBEN (1 << 21)
|
||||
#define IMASK_GTSCEN (1 << 25)
|
||||
|
||||
#define MACCFG1_TX_EN (1 << 0)
|
||||
#define MACCFG1_RX_EN (1 << 2)
|
||||
|
||||
#define MACCFG2_CRC_EN (1 << 1)
|
||||
#define MACCFG2_PADCRC (1 << 2)
|
||||
|
||||
#define MIIMCOM_READ (1 << 0)
|
||||
#define MIIMCOM_SCAN (1 << 1)
|
||||
|
||||
#define RCTRL_PRSDEP_MASK (0x3)
|
||||
#define RCTRL_PRSDEP_OFFSET (6)
|
||||
#define RCTRL_RSF (1 << 2)
|
||||
|
||||
/* Index of each register */
|
||||
|
||||
#define TSEC_ID (0x000 / 4)
|
||||
#define TSEC_ID2 (0x004 / 4)
|
||||
#define IEVENT (0x010 / 4)
|
||||
#define IMASK (0x014 / 4)
|
||||
#define EDIS (0x018 / 4)
|
||||
#define ECNTRL (0x020 / 4)
|
||||
#define PTV (0x028 / 4)
|
||||
#define DMACTRL (0x02C / 4)
|
||||
#define TBIPA (0x030 / 4)
|
||||
#define TCTRL (0x100 / 4)
|
||||
#define TSTAT (0x104 / 4)
|
||||
#define DFVLAN (0x108 / 4)
|
||||
#define TXIC (0x110 / 4)
|
||||
#define TQUEUE (0x114 / 4)
|
||||
#define TR03WT (0x140 / 4)
|
||||
#define TR47WT (0x144 / 4)
|
||||
#define TBDBPH (0x180 / 4)
|
||||
#define TBPTR0 (0x184 / 4)
|
||||
#define TBPTR1 (0x18C / 4)
|
||||
#define TBPTR2 (0x194 / 4)
|
||||
#define TBPTR3 (0x19C / 4)
|
||||
#define TBPTR4 (0x1A4 / 4)
|
||||
#define TBPTR5 (0x1AC / 4)
|
||||
#define TBPTR6 (0x1B4 / 4)
|
||||
#define TBPTR7 (0x1BC / 4)
|
||||
#define TBASEH (0x200 / 4)
|
||||
#define TBASE0 (0x204 / 4)
|
||||
#define TBASE1 (0x20C / 4)
|
||||
#define TBASE2 (0x214 / 4)
|
||||
#define TBASE3 (0x21C / 4)
|
||||
#define TBASE4 (0x224 / 4)
|
||||
#define TBASE5 (0x22C / 4)
|
||||
#define TBASE6 (0x234 / 4)
|
||||
#define TBASE7 (0x23C / 4)
|
||||
#define TMR_TXTS1_ID (0x280 / 4)
|
||||
#define TMR_TXTS2_ID (0x284 / 4)
|
||||
#define TMR_TXTS1_H (0x2C0 / 4)
|
||||
#define TMR_TXTS1_L (0x2C4 / 4)
|
||||
#define TMR_TXTS2_H (0x2C8 / 4)
|
||||
#define TMR_TXTS2_L (0x2CC / 4)
|
||||
#define RCTRL (0x300 / 4)
|
||||
#define RSTAT (0x304 / 4)
|
||||
#define RXIC (0x310 / 4)
|
||||
#define RQUEUE (0x314 / 4)
|
||||
#define RBIFX (0x330 / 4)
|
||||
#define RQFAR (0x334 / 4)
|
||||
#define RQFCR (0x338 / 4)
|
||||
#define RQFPR (0x33C / 4)
|
||||
#define MRBLR (0x340 / 4)
|
||||
#define RBDBPH (0x380 / 4)
|
||||
#define RBPTR0 (0x384 / 4)
|
||||
#define RBPTR1 (0x38C / 4)
|
||||
#define RBPTR2 (0x394 / 4)
|
||||
#define RBPTR3 (0x39C / 4)
|
||||
#define RBPTR4 (0x3A4 / 4)
|
||||
#define RBPTR5 (0x3AC / 4)
|
||||
#define RBPTR6 (0x3B4 / 4)
|
||||
#define RBPTR7 (0x3BC / 4)
|
||||
#define RBASEH (0x400 / 4)
|
||||
#define RBASE0 (0x404 / 4)
|
||||
#define RBASE1 (0x40C / 4)
|
||||
#define RBASE2 (0x414 / 4)
|
||||
#define RBASE3 (0x41C / 4)
|
||||
#define RBASE4 (0x424 / 4)
|
||||
#define RBASE5 (0x42C / 4)
|
||||
#define RBASE6 (0x434 / 4)
|
||||
#define RBASE7 (0x43C / 4)
|
||||
#define TMR_RXTS_H (0x4C0 / 4)
|
||||
#define TMR_RXTS_L (0x4C4 / 4)
|
||||
#define MACCFG1 (0x500 / 4)
|
||||
#define MACCFG2 (0x504 / 4)
|
||||
#define IPGIFG (0x508 / 4)
|
||||
#define HAFDUP (0x50C / 4)
|
||||
#define MAXFRM (0x510 / 4)
|
||||
#define MIIMCFG (0x520 / 4)
|
||||
#define MIIMCOM (0x524 / 4)
|
||||
#define MIIMADD (0x528 / 4)
|
||||
#define MIIMCON (0x52C / 4)
|
||||
#define MIIMSTAT (0x530 / 4)
|
||||
#define MIIMIND (0x534 / 4)
|
||||
#define IFSTAT (0x53C / 4)
|
||||
#define MACSTNADDR1 (0x540 / 4)
|
||||
#define MACSTNADDR2 (0x544 / 4)
|
||||
#define MAC01ADDR1 (0x548 / 4)
|
||||
#define MAC01ADDR2 (0x54C / 4)
|
||||
#define MAC02ADDR1 (0x550 / 4)
|
||||
#define MAC02ADDR2 (0x554 / 4)
|
||||
#define MAC03ADDR1 (0x558 / 4)
|
||||
#define MAC03ADDR2 (0x55C / 4)
|
||||
#define MAC04ADDR1 (0x560 / 4)
|
||||
#define MAC04ADDR2 (0x564 / 4)
|
||||
#define MAC05ADDR1 (0x568 / 4)
|
||||
#define MAC05ADDR2 (0x56C / 4)
|
||||
#define MAC06ADDR1 (0x570 / 4)
|
||||
#define MAC06ADDR2 (0x574 / 4)
|
||||
#define MAC07ADDR1 (0x578 / 4)
|
||||
#define MAC07ADDR2 (0x57C / 4)
|
||||
#define MAC08ADDR1 (0x580 / 4)
|
||||
#define MAC08ADDR2 (0x584 / 4)
|
||||
#define MAC09ADDR1 (0x588 / 4)
|
||||
#define MAC09ADDR2 (0x58C / 4)
|
||||
#define MAC10ADDR1 (0x590 / 4)
|
||||
#define MAC10ADDR2 (0x594 / 4)
|
||||
#define MAC11ADDR1 (0x598 / 4)
|
||||
#define MAC11ADDR2 (0x59C / 4)
|
||||
#define MAC12ADDR1 (0x5A0 / 4)
|
||||
#define MAC12ADDR2 (0x5A4 / 4)
|
||||
#define MAC13ADDR1 (0x5A8 / 4)
|
||||
#define MAC13ADDR2 (0x5AC / 4)
|
||||
#define MAC14ADDR1 (0x5B0 / 4)
|
||||
#define MAC14ADDR2 (0x5B4 / 4)
|
||||
#define MAC15ADDR1 (0x5B8 / 4)
|
||||
#define MAC15ADDR2 (0x5BC / 4)
|
||||
#define TR64 (0x680 / 4)
|
||||
#define TR127 (0x684 / 4)
|
||||
#define TR255 (0x688 / 4)
|
||||
#define TR511 (0x68C / 4)
|
||||
#define TR1K (0x690 / 4)
|
||||
#define TRMAX (0x694 / 4)
|
||||
#define TRMGV (0x698 / 4)
|
||||
#define RBYT (0x69C / 4)
|
||||
#define RPKT (0x6A0 / 4)
|
||||
#define RFCS (0x6A4 / 4)
|
||||
#define RMCA (0x6A8 / 4)
|
||||
#define RBCA (0x6AC / 4)
|
||||
#define RXCF (0x6B0 / 4)
|
||||
#define RXPF (0x6B4 / 4)
|
||||
#define RXUO (0x6B8 / 4)
|
||||
#define RALN (0x6BC / 4)
|
||||
#define RFLR (0x6C0 / 4)
|
||||
#define RCDE (0x6C4 / 4)
|
||||
#define RCSE (0x6C8 / 4)
|
||||
#define RUND (0x6CC / 4)
|
||||
#define ROVR (0x6D0 / 4)
|
||||
#define RFRG (0x6D4 / 4)
|
||||
#define RJBR (0x6D8 / 4)
|
||||
#define RDRP (0x6DC / 4)
|
||||
#define TBYT (0x6E0 / 4)
|
||||
#define TPKT (0x6E4 / 4)
|
||||
#define TMCA (0x6E8 / 4)
|
||||
#define TBCA (0x6EC / 4)
|
||||
#define TXPF (0x6F0 / 4)
|
||||
#define TDFR (0x6F4 / 4)
|
||||
#define TEDF (0x6F8 / 4)
|
||||
#define TSCL (0x6FC / 4)
|
||||
#define TMCL (0x700 / 4)
|
||||
#define TLCL (0x704 / 4)
|
||||
#define TXCL (0x708 / 4)
|
||||
#define TNCL (0x70C / 4)
|
||||
#define TDRP (0x714 / 4)
|
||||
#define TJBR (0x718 / 4)
|
||||
#define TFCS (0x71C / 4)
|
||||
#define TXCF (0x720 / 4)
|
||||
#define TOVR (0x724 / 4)
|
||||
#define TUND (0x728 / 4)
|
||||
#define TFRG (0x72C / 4)
|
||||
#define CAR1 (0x730 / 4)
|
||||
#define CAR2 (0x734 / 4)
|
||||
#define CAM1 (0x738 / 4)
|
||||
#define CAM2 (0x73C / 4)
|
||||
#define RREJ (0x740 / 4)
|
||||
#define IGADDR0 (0x800 / 4)
|
||||
#define IGADDR1 (0x804 / 4)
|
||||
#define IGADDR2 (0x808 / 4)
|
||||
#define IGADDR3 (0x80C / 4)
|
||||
#define IGADDR4 (0x810 / 4)
|
||||
#define IGADDR5 (0x814 / 4)
|
||||
#define IGADDR6 (0x818 / 4)
|
||||
#define IGADDR7 (0x81C / 4)
|
||||
#define GADDR0 (0x880 / 4)
|
||||
#define GADDR1 (0x884 / 4)
|
||||
#define GADDR2 (0x888 / 4)
|
||||
#define GADDR3 (0x88C / 4)
|
||||
#define GADDR4 (0x890 / 4)
|
||||
#define GADDR5 (0x894 / 4)
|
||||
#define GADDR6 (0x898 / 4)
|
||||
#define GADDR7 (0x89C / 4)
|
||||
#define ATTR (0xBF8 / 4)
|
||||
#define ATTRELI (0xBFC / 4)
|
||||
#define RQPRM0 (0xC00 / 4)
|
||||
#define RQPRM1 (0xC04 / 4)
|
||||
#define RQPRM2 (0xC08 / 4)
|
||||
#define RQPRM3 (0xC0C / 4)
|
||||
#define RQPRM4 (0xC10 / 4)
|
||||
#define RQPRM5 (0xC14 / 4)
|
||||
#define RQPRM6 (0xC18 / 4)
|
||||
#define RQPRM7 (0xC1C / 4)
|
||||
#define RFBPTR0 (0xC44 / 4)
|
||||
#define RFBPTR1 (0xC4C / 4)
|
||||
#define RFBPTR2 (0xC54 / 4)
|
||||
#define RFBPTR3 (0xC5C / 4)
|
||||
#define RFBPTR4 (0xC64 / 4)
|
||||
#define RFBPTR5 (0xC6C / 4)
|
||||
#define RFBPTR6 (0xC74 / 4)
|
||||
#define RFBPTR7 (0xC7C / 4)
|
||||
#define TMR_CTRL (0xE00 / 4)
|
||||
#define TMR_TEVENT (0xE04 / 4)
|
||||
#define TMR_TEMASK (0xE08 / 4)
|
||||
#define TMR_PEVENT (0xE0C / 4)
|
||||
#define TMR_PEMASK (0xE10 / 4)
|
||||
#define TMR_STAT (0xE14 / 4)
|
||||
#define TMR_CNT_H (0xE18 / 4)
|
||||
#define TMR_CNT_L (0xE1C / 4)
|
||||
#define TMR_ADD (0xE20 / 4)
|
||||
#define TMR_ACC (0xE24 / 4)
|
||||
#define TMR_PRSC (0xE28 / 4)
|
||||
#define TMROFF_H (0xE30 / 4)
|
||||
#define TMROFF_L (0xE34 / 4)
|
||||
#define TMR_ALARM1_H (0xE40 / 4)
|
||||
#define TMR_ALARM1_L (0xE44 / 4)
|
||||
#define TMR_ALARM2_H (0xE48 / 4)
|
||||
#define TMR_ALARM2_L (0xE4C / 4)
|
||||
#define TMR_FIPER1 (0xE80 / 4)
|
||||
#define TMR_FIPER2 (0xE84 / 4)
|
||||
#define TMR_FIPER3 (0xE88 / 4)
|
||||
#define TMR_ETTS1_H (0xEA0 / 4)
|
||||
#define TMR_ETTS1_L (0xEA4 / 4)
|
||||
#define TMR_ETTS2_H (0xEA8 / 4)
|
||||
#define TMR_ETTS2_L (0xEAC / 4)
|
||||
|
||||
#endif /* ! _ETSEC_REGISTERS_H_ */
|
650
hw/net/fsl_etsec/rings.c
Normal file
650
hw/net/fsl_etsec/rings.c
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
* QEMU Freescale eTSEC Emulator
|
||||
*
|
||||
* Copyright (c) 2011-2013 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 "net/checksum.h"
|
||||
|
||||
#include "etsec.h"
|
||||
#include "registers.h"
|
||||
|
||||
/* #define ETSEC_RING_DEBUG */
|
||||
/* #define HEX_DUMP */
|
||||
/* #define DEBUG_BD */
|
||||
|
||||
#ifdef ETSEC_RING_DEBUG
|
||||
static const int debug_etsec = 1;
|
||||
#else
|
||||
static const int debug_etsec;
|
||||
#endif
|
||||
|
||||
#define RING_DEBUG(fmt, ...) do { \
|
||||
if (debug_etsec) { \
|
||||
qemu_log(fmt , ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG_BD
|
||||
|
||||
static void print_tx_bd_flags(uint16_t flags)
|
||||
{
|
||||
qemu_log(" Ready: %d\n", !!(flags & BD_TX_READY));
|
||||
qemu_log(" PAD/CRC: %d\n", !!(flags & BD_TX_PADCRC));
|
||||
qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP));
|
||||
qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT));
|
||||
qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST));
|
||||
qemu_log(" Tx CRC: %d\n", !!(flags & BD_TX_TC));
|
||||
qemu_log(" User-defined preamble / defer: %d\n",
|
||||
!!(flags & BD_TX_PREDEF));
|
||||
qemu_log(" Huge frame enable / Late collision: %d\n",
|
||||
!!(flags & BD_TX_HFELC));
|
||||
qemu_log(" Control frame / Retransmission Limit: %d\n",
|
||||
!!(flags & BD_TX_CFRL));
|
||||
qemu_log(" Retry count: %d\n",
|
||||
(flags >> BD_TX_RC_OFFSET) & BD_TX_RC_MASK);
|
||||
qemu_log(" Underrun / TCP/IP off-load enable: %d\n",
|
||||
!!(flags & BD_TX_TOEUN));
|
||||
qemu_log(" Truncation: %d\n", !!(flags & BD_TX_TR));
|
||||
}
|
||||
|
||||
static void print_rx_bd_flags(uint16_t flags)
|
||||
{
|
||||
qemu_log(" Empty: %d\n", !!(flags & BD_RX_EMPTY));
|
||||
qemu_log(" Receive software ownership: %d\n", !!(flags & BD_RX_RO1));
|
||||
qemu_log(" Wrap: %d\n", !!(flags & BD_WRAP));
|
||||
qemu_log(" Interrupt: %d\n", !!(flags & BD_INTERRUPT));
|
||||
qemu_log(" Last in frame: %d\n", !!(flags & BD_LAST));
|
||||
qemu_log(" First in frame: %d\n", !!(flags & BD_RX_FIRST));
|
||||
qemu_log(" Miss: %d\n", !!(flags & BD_RX_MISS));
|
||||
qemu_log(" Broadcast: %d\n", !!(flags & BD_RX_BROADCAST));
|
||||
qemu_log(" Multicast: %d\n", !!(flags & BD_RX_MULTICAST));
|
||||
qemu_log(" Rx frame length violation: %d\n", !!(flags & BD_RX_LG));
|
||||
qemu_log(" Rx non-octet aligned frame: %d\n", !!(flags & BD_RX_NO));
|
||||
qemu_log(" Short frame: %d\n", !!(flags & BD_RX_SH));
|
||||
qemu_log(" Rx CRC Error: %d\n", !!(flags & BD_RX_CR));
|
||||
qemu_log(" Overrun: %d\n", !!(flags & BD_RX_OV));
|
||||
qemu_log(" Truncation: %d\n", !!(flags & BD_RX_TR));
|
||||
}
|
||||
|
||||
|
||||
static void print_bd(eTSEC_rxtx_bd bd, int mode, uint32_t index)
|
||||
{
|
||||
qemu_log("eTSEC %s Data Buffer Descriptor (%u)\n",
|
||||
mode == eTSEC_TRANSMIT ? "Transmit" : "Receive",
|
||||
index);
|
||||
qemu_log(" Flags : 0x%04x\n", bd.flags);
|
||||
if (mode == eTSEC_TRANSMIT) {
|
||||
print_tx_bd_flags(bd.flags);
|
||||
} else {
|
||||
print_rx_bd_flags(bd.flags);
|
||||
}
|
||||
qemu_log(" Length : 0x%04x\n", bd.length);
|
||||
qemu_log(" Pointer : 0x%08x\n", bd.bufptr);
|
||||
}
|
||||
|
||||
#endif /* DEBUG_BD */
|
||||
|
||||
static void read_buffer_descriptor(eTSEC *etsec,
|
||||
hwaddr addr,
|
||||
eTSEC_rxtx_bd *bd)
|
||||
{
|
||||
assert(bd != NULL);
|
||||
|
||||
RING_DEBUG("READ Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr);
|
||||
cpu_physical_memory_read(addr,
|
||||
bd,
|
||||
sizeof(eTSEC_rxtx_bd));
|
||||
|
||||
if (etsec->regs[DMACTRL].value & DMACTRL_LE) {
|
||||
bd->flags = lduw_le_p(&bd->flags);
|
||||
bd->length = lduw_le_p(&bd->length);
|
||||
bd->bufptr = ldl_le_p(&bd->bufptr);
|
||||
} else {
|
||||
bd->flags = lduw_be_p(&bd->flags);
|
||||
bd->length = lduw_be_p(&bd->length);
|
||||
bd->bufptr = ldl_be_p(&bd->bufptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_buffer_descriptor(eTSEC *etsec,
|
||||
hwaddr addr,
|
||||
eTSEC_rxtx_bd *bd)
|
||||
{
|
||||
assert(bd != NULL);
|
||||
|
||||
if (etsec->regs[DMACTRL].value & DMACTRL_LE) {
|
||||
stw_le_p(&bd->flags, bd->flags);
|
||||
stw_le_p(&bd->length, bd->length);
|
||||
stl_le_p(&bd->bufptr, bd->bufptr);
|
||||
} else {
|
||||
stw_be_p(&bd->flags, bd->flags);
|
||||
stw_be_p(&bd->length, bd->length);
|
||||
stl_be_p(&bd->bufptr, bd->bufptr);
|
||||
}
|
||||
|
||||
RING_DEBUG("Write Buffer Descriptor @ 0x" TARGET_FMT_plx"\n", addr);
|
||||
cpu_physical_memory_write(addr,
|
||||
bd,
|
||||
sizeof(eTSEC_rxtx_bd));
|
||||
}
|
||||
|
||||
static void ievent_set(eTSEC *etsec,
|
||||
uint32_t flags)
|
||||
{
|
||||
etsec->regs[IEVENT].value |= flags;
|
||||
|
||||
if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN)
|
||||
|| (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) {
|
||||
qemu_irq_raise(etsec->tx_irq);
|
||||
RING_DEBUG("%s Raise Tx IRQ\n", __func__);
|
||||
}
|
||||
|
||||
if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN)
|
||||
|| (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) {
|
||||
qemu_irq_pulse(etsec->rx_irq);
|
||||
RING_DEBUG("%s Raise Rx IRQ\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len)
|
||||
{
|
||||
int add = min_frame_len - etsec->tx_buffer_len;
|
||||
|
||||
/* Padding */
|
||||
if (add > 0) {
|
||||
RING_DEBUG("pad:%u\n", add);
|
||||
etsec->tx_buffer = g_realloc(etsec->tx_buffer,
|
||||
etsec->tx_buffer_len + add);
|
||||
|
||||
memset(etsec->tx_buffer + etsec->tx_buffer_len, 0x0, add);
|
||||
etsec->tx_buffer_len += add;
|
||||
}
|
||||
|
||||
/* Never add CRC in QEMU */
|
||||
}
|
||||
|
||||
static void process_tx_fcb(eTSEC *etsec)
|
||||
{
|
||||
uint8_t flags = (uint8_t)(*etsec->tx_buffer);
|
||||
/* L3 header offset from start of frame */
|
||||
uint8_t l3_header_offset = (uint8_t)*(etsec->tx_buffer + 3);
|
||||
/* L4 header offset from start of L3 header */
|
||||
uint8_t l4_header_offset = (uint8_t)*(etsec->tx_buffer + 2);
|
||||
/* L3 header */
|
||||
uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset;
|
||||
/* L4 header */
|
||||
uint8_t *l4_header = l3_header + l4_header_offset;
|
||||
|
||||
/* if packet is IP4 and IP checksum is requested */
|
||||
if (flags & FCB_TX_IP && flags & FCB_TX_CIP) {
|
||||
/* do IP4 checksum (TODO This funtion does TCP/UDP checksum but not sure
|
||||
* if it also does IP4 checksum. */
|
||||
net_checksum_calculate(etsec->tx_buffer + 8,
|
||||
etsec->tx_buffer_len - 8);
|
||||
}
|
||||
/* TODO Check the correct usage of the PHCS field of the FCB in case the NPH
|
||||
* flag is on */
|
||||
|
||||
/* if packet is IP4 and TCP or UDP */
|
||||
if (flags & FCB_TX_IP && flags & FCB_TX_TUP) {
|
||||
/* if UDP */
|
||||
if (flags & FCB_TX_UDP) {
|
||||
/* if checksum is requested */
|
||||
if (flags & FCB_TX_CTU) {
|
||||
/* do UDP checksum */
|
||||
|
||||
net_checksum_calculate(etsec->tx_buffer + 8,
|
||||
etsec->tx_buffer_len - 8);
|
||||
} else {
|
||||
/* set checksum field to 0 */
|
||||
l4_header[6] = 0;
|
||||
l4_header[7] = 0;
|
||||
}
|
||||
} else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */
|
||||
/* do TCP checksum */
|
||||
net_checksum_calculate(etsec->tx_buffer + 8,
|
||||
etsec->tx_buffer_len - 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_tx_bd(eTSEC *etsec,
|
||||
eTSEC_rxtx_bd *bd)
|
||||
{
|
||||
uint8_t *tmp_buff = NULL;
|
||||
hwaddr tbdbth = (hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32;
|
||||
|
||||
if (bd->length == 0) {
|
||||
/* ERROR */
|
||||
return;
|
||||
}
|
||||
|
||||
if (etsec->tx_buffer_len == 0) {
|
||||
/* It's the first BD */
|
||||
etsec->first_bd = *bd;
|
||||
}
|
||||
|
||||
/* TODO: if TxBD[TOE/UN] skip the Tx Frame Control Block*/
|
||||
|
||||
/* Load this Data Buffer */
|
||||
etsec->tx_buffer = g_realloc(etsec->tx_buffer,
|
||||
etsec->tx_buffer_len + bd->length);
|
||||
tmp_buff = etsec->tx_buffer + etsec->tx_buffer_len;
|
||||
cpu_physical_memory_read(bd->bufptr + tbdbth, tmp_buff, bd->length);
|
||||
|
||||
/* Update buffer length */
|
||||
etsec->tx_buffer_len += bd->length;
|
||||
|
||||
|
||||
if (etsec->tx_buffer_len != 0 && (bd->flags & BD_LAST)) {
|
||||
if (etsec->regs[MACCFG1].value & MACCFG1_TX_EN) {
|
||||
/* MAC Transmit enabled */
|
||||
|
||||
/* Process offload Tx FCB */
|
||||
if (etsec->first_bd.flags & BD_TX_TOEUN) {
|
||||
process_tx_fcb(etsec);
|
||||
}
|
||||
|
||||
if (etsec->first_bd.flags & BD_TX_PADCRC
|
||||
|| etsec->regs[MACCFG2].value & MACCFG2_PADCRC) {
|
||||
|
||||
/* Padding and CRC (Padding implies CRC) */
|
||||
tx_padding_and_crc(etsec, 64);
|
||||
|
||||
} else if (etsec->first_bd.flags & BD_TX_TC
|
||||
|| etsec->regs[MACCFG2].value & MACCFG2_CRC_EN) {
|
||||
|
||||
/* Only CRC */
|
||||
/* Never add CRC in QEMU */
|
||||
}
|
||||
|
||||
#if defined(HEX_DUMP)
|
||||
qemu_log("eTSEC Send packet size:%d\n", etsec->tx_buffer_len);
|
||||
qemu_hexdump(etsec->tx_buffer, stderr, "", etsec->tx_buffer_len);
|
||||
#endif /* ETSEC_RING_DEBUG */
|
||||
|
||||
if (etsec->first_bd.flags & BD_TX_TOEUN) {
|
||||
qemu_send_packet(qemu_get_queue(etsec->nic),
|
||||
etsec->tx_buffer + 8,
|
||||
etsec->tx_buffer_len - 8);
|
||||
} else {
|
||||
qemu_send_packet(qemu_get_queue(etsec->nic),
|
||||
etsec->tx_buffer,
|
||||
etsec->tx_buffer_len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
etsec->tx_buffer_len = 0;
|
||||
|
||||
if (bd->flags & BD_INTERRUPT) {
|
||||
ievent_set(etsec, IEVENT_TXF);
|
||||
}
|
||||
} else {
|
||||
if (bd->flags & BD_INTERRUPT) {
|
||||
ievent_set(etsec, IEVENT_TXB);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update DB flags */
|
||||
|
||||
/* Clear Ready */
|
||||
bd->flags &= ~BD_TX_READY;
|
||||
|
||||
/* Clear Defer */
|
||||
bd->flags &= ~BD_TX_PREDEF;
|
||||
|
||||
/* Clear Late Collision */
|
||||
bd->flags &= ~BD_TX_HFELC;
|
||||
|
||||
/* Clear Retransmission Limit */
|
||||
bd->flags &= ~BD_TX_CFRL;
|
||||
|
||||
/* Clear Retry Count */
|
||||
bd->flags &= ~(BD_TX_RC_MASK << BD_TX_RC_OFFSET);
|
||||
|
||||
/* Clear Underrun */
|
||||
bd->flags &= ~BD_TX_TOEUN;
|
||||
|
||||
/* Clear Truncation */
|
||||
bd->flags &= ~BD_TX_TR;
|
||||
}
|
||||
|
||||
void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr)
|
||||
{
|
||||
hwaddr ring_base = 0;
|
||||
hwaddr bd_addr = 0;
|
||||
eTSEC_rxtx_bd bd;
|
||||
uint16_t bd_flags;
|
||||
|
||||
if (!(etsec->regs[MACCFG1].value & MACCFG1_TX_EN)) {
|
||||
RING_DEBUG("%s: MAC Transmit not enabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ring_base = (hwaddr)(etsec->regs[TBASEH].value & 0xF) << 32;
|
||||
ring_base += etsec->regs[TBASE0 + ring_nbr].value & ~0x7;
|
||||
bd_addr = etsec->regs[TBPTR0 + ring_nbr].value & ~0x7;
|
||||
|
||||
do {
|
||||
read_buffer_descriptor(etsec, bd_addr, &bd);
|
||||
|
||||
#ifdef DEBUG_BD
|
||||
print_bd(bd,
|
||||
eTSEC_TRANSMIT,
|
||||
(bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd));
|
||||
|
||||
#endif /* DEBUG_BD */
|
||||
|
||||
/* Save flags before BD update */
|
||||
bd_flags = bd.flags;
|
||||
|
||||
if (bd_flags & BD_TX_READY) {
|
||||
process_tx_bd(etsec, &bd);
|
||||
|
||||
/* Write back BD after update */
|
||||
write_buffer_descriptor(etsec, bd_addr, &bd);
|
||||
}
|
||||
|
||||
/* Wrap or next BD */
|
||||
if (bd_flags & BD_WRAP) {
|
||||
bd_addr = ring_base;
|
||||
} else {
|
||||
bd_addr += sizeof(eTSEC_rxtx_bd);
|
||||
}
|
||||
|
||||
} while (bd_addr != ring_base);
|
||||
|
||||
bd_addr = ring_base;
|
||||
|
||||
/* Save the Buffer Descriptor Pointers to current bd */
|
||||
etsec->regs[TBPTR0 + ring_nbr].value = bd_addr;
|
||||
|
||||
/* Set transmit halt THLTx */
|
||||
etsec->regs[TSTAT].value |= 1 << (31 - ring_nbr);
|
||||
}
|
||||
|
||||
static void fill_rx_bd(eTSEC *etsec,
|
||||
eTSEC_rxtx_bd *bd,
|
||||
const uint8_t **buf,
|
||||
size_t *size)
|
||||
{
|
||||
uint16_t to_write;
|
||||
hwaddr bufptr = bd->bufptr +
|
||||
((hwaddr)(etsec->regs[TBDBPH].value & 0xF) << 32);
|
||||
uint8_t padd[etsec->rx_padding];
|
||||
uint8_t rem;
|
||||
|
||||
RING_DEBUG("eTSEC fill Rx buffer @ 0x%016" HWADDR_PRIx
|
||||
" size:%zu(padding + crc:%u) + fcb:%u\n",
|
||||
bufptr, *size, etsec->rx_padding, etsec->rx_fcb_size);
|
||||
|
||||
bd->length = 0;
|
||||
|
||||
/* This operation will only write FCB */
|
||||
if (etsec->rx_fcb_size != 0) {
|
||||
|
||||
cpu_physical_memory_write(bufptr, etsec->rx_fcb, etsec->rx_fcb_size);
|
||||
|
||||
bufptr += etsec->rx_fcb_size;
|
||||
bd->length += etsec->rx_fcb_size;
|
||||
etsec->rx_fcb_size = 0;
|
||||
|
||||
}
|
||||
|
||||
/* We remove padding from the computation of to_write because it is not
|
||||
* allocated in the buffer.
|
||||
*/
|
||||
to_write = MIN(*size - etsec->rx_padding,
|
||||
etsec->regs[MRBLR].value - etsec->rx_fcb_size);
|
||||
|
||||
/* This operation can only write packet data and no padding */
|
||||
if (to_write > 0) {
|
||||
cpu_physical_memory_write(bufptr, *buf, to_write);
|
||||
|
||||
*buf += to_write;
|
||||
bufptr += to_write;
|
||||
*size -= to_write;
|
||||
|
||||
bd->flags &= ~BD_RX_EMPTY;
|
||||
bd->length += to_write;
|
||||
}
|
||||
|
||||
if (*size == etsec->rx_padding) {
|
||||
/* The remaining bytes are only for padding which is not actually
|
||||
* allocated in the data buffer.
|
||||
*/
|
||||
|
||||
rem = MIN(etsec->regs[MRBLR].value - bd->length, etsec->rx_padding);
|
||||
|
||||
if (rem > 0) {
|
||||
memset(padd, 0x0, sizeof(padd));
|
||||
etsec->rx_padding -= rem;
|
||||
*size -= rem;
|
||||
bd->length += rem;
|
||||
cpu_physical_memory_write(bufptr, padd, rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_init_frame(eTSEC *etsec, const uint8_t *buf, size_t size)
|
||||
{
|
||||
uint32_t fcb_size = 0;
|
||||
uint8_t prsdep = (etsec->regs[RCTRL].value >> RCTRL_PRSDEP_OFFSET)
|
||||
& RCTRL_PRSDEP_MASK;
|
||||
|
||||
if (prsdep != 0) {
|
||||
/* Prepend FCB (FCB size + RCTRL[PAL]) */
|
||||
fcb_size = 8 + ((etsec->regs[RCTRL].value >> 16) & 0x1F);
|
||||
|
||||
etsec->rx_fcb_size = fcb_size;
|
||||
|
||||
/* TODO: fill_FCB(etsec); */
|
||||
memset(etsec->rx_fcb, 0x0, sizeof(etsec->rx_fcb));
|
||||
|
||||
} else {
|
||||
etsec->rx_fcb_size = 0;
|
||||
}
|
||||
|
||||
if (etsec->rx_buffer != NULL) {
|
||||
g_free(etsec->rx_buffer);
|
||||
}
|
||||
|
||||
/* Do not copy the frame for now */
|
||||
etsec->rx_buffer = (uint8_t *)buf;
|
||||
etsec->rx_buffer_len = size;
|
||||
|
||||
/* CRC padding (We don't have to compute the CRC) */
|
||||
etsec->rx_padding = 4;
|
||||
|
||||
etsec->rx_first_in_frame = 1;
|
||||
etsec->rx_remaining_data = etsec->rx_buffer_len;
|
||||
RING_DEBUG("%s: rx_buffer_len:%u rx_padding+crc:%u\n", __func__,
|
||||
etsec->rx_buffer_len, etsec->rx_padding);
|
||||
}
|
||||
|
||||
void etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size)
|
||||
{
|
||||
int ring_nbr = 0; /* Always use ring0 (no filer) */
|
||||
|
||||
if (etsec->rx_buffer_len != 0) {
|
||||
RING_DEBUG("%s: We can't receive now,"
|
||||
" a buffer is already in the pipe\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (etsec->regs[RSTAT].value & 1 << (23 - ring_nbr)) {
|
||||
RING_DEBUG("%s: The ring is halted\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (etsec->regs[DMACTRL].value & DMACTRL_GRS) {
|
||||
RING_DEBUG("%s: Graceful receive stop\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(etsec->regs[MACCFG1].value & MACCFG1_RX_EN)) {
|
||||
RING_DEBUG("%s: MAC Receive not enabled\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((etsec->regs[RCTRL].value & RCTRL_RSF) && (size < 60)) {
|
||||
/* CRC is not in the packet yet, so short frame is below 60 bytes */
|
||||
RING_DEBUG("%s: Drop short frame\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
rx_init_frame(etsec, buf, size);
|
||||
|
||||
etsec_walk_rx_ring(etsec, ring_nbr);
|
||||
}
|
||||
|
||||
void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr)
|
||||
{
|
||||
hwaddr ring_base = 0;
|
||||
hwaddr bd_addr = 0;
|
||||
hwaddr start_bd_addr = 0;
|
||||
eTSEC_rxtx_bd bd;
|
||||
uint16_t bd_flags;
|
||||
size_t remaining_data;
|
||||
const uint8_t *buf;
|
||||
uint8_t *tmp_buf;
|
||||
size_t size;
|
||||
|
||||
if (etsec->rx_buffer_len == 0) {
|
||||
/* No frame to send */
|
||||
RING_DEBUG("No frame to send\n");
|
||||
return;
|
||||
}
|
||||
|
||||
remaining_data = etsec->rx_remaining_data + etsec->rx_padding;
|
||||
buf = etsec->rx_buffer
|
||||
+ (etsec->rx_buffer_len - etsec->rx_remaining_data);
|
||||
size = etsec->rx_buffer_len + etsec->rx_padding;
|
||||
|
||||
ring_base = (hwaddr)(etsec->regs[RBASEH].value & 0xF) << 32;
|
||||
ring_base += etsec->regs[RBASE0 + ring_nbr].value & ~0x7;
|
||||
start_bd_addr = bd_addr = etsec->regs[RBPTR0 + ring_nbr].value & ~0x7;
|
||||
|
||||
do {
|
||||
read_buffer_descriptor(etsec, bd_addr, &bd);
|
||||
|
||||
#ifdef DEBUG_BD
|
||||
print_bd(bd,
|
||||
eTSEC_RECEIVE,
|
||||
(bd_addr - ring_base) / sizeof(eTSEC_rxtx_bd));
|
||||
|
||||
#endif /* DEBUG_BD */
|
||||
|
||||
/* Save flags before BD update */
|
||||
bd_flags = bd.flags;
|
||||
|
||||
if (bd_flags & BD_RX_EMPTY) {
|
||||
fill_rx_bd(etsec, &bd, &buf, &remaining_data);
|
||||
|
||||
if (etsec->rx_first_in_frame) {
|
||||
bd.flags |= BD_RX_FIRST;
|
||||
etsec->rx_first_in_frame = 0;
|
||||
etsec->rx_first_bd = bd;
|
||||
}
|
||||
|
||||
/* Last in frame */
|
||||
if (remaining_data == 0) {
|
||||
|
||||
/* Clear flags */
|
||||
|
||||
bd.flags &= ~0x7ff;
|
||||
|
||||
bd.flags |= BD_LAST;
|
||||
|
||||
/* NOTE: non-octet aligned frame is impossible in qemu */
|
||||
|
||||
if (size >= etsec->regs[MAXFRM].value) {
|
||||
/* frame length violation */
|
||||
qemu_log("%s frame length violation: size:%zu MAXFRM:%d\n",
|
||||
__func__, size, etsec->regs[MAXFRM].value);
|
||||
|
||||
bd.flags |= BD_RX_LG;
|
||||
}
|
||||
|
||||
if (size < 64) {
|
||||
/* Short frame */
|
||||
bd.flags |= BD_RX_SH;
|
||||
}
|
||||
|
||||
/* TODO: Broadcast and Multicast */
|
||||
|
||||
if (bd.flags | BD_INTERRUPT) {
|
||||
/* Set RXFx */
|
||||
etsec->regs[RSTAT].value |= 1 << (7 - ring_nbr);
|
||||
|
||||
/* Set IEVENT */
|
||||
ievent_set(etsec, IEVENT_RXF);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (bd.flags | BD_INTERRUPT) {
|
||||
/* Set IEVENT */
|
||||
ievent_set(etsec, IEVENT_RXB);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write back BD after update */
|
||||
write_buffer_descriptor(etsec, bd_addr, &bd);
|
||||
}
|
||||
|
||||
/* Wrap or next BD */
|
||||
if (bd_flags & BD_WRAP) {
|
||||
bd_addr = ring_base;
|
||||
} else {
|
||||
bd_addr += sizeof(eTSEC_rxtx_bd);
|
||||
}
|
||||
} while (remaining_data != 0
|
||||
&& (bd_flags & BD_RX_EMPTY)
|
||||
&& bd_addr != start_bd_addr);
|
||||
|
||||
/* Reset ring ptr */
|
||||
etsec->regs[RBPTR0 + ring_nbr].value = bd_addr;
|
||||
|
||||
/* The frame is too large to fit in the Rx ring */
|
||||
if (remaining_data > 0) {
|
||||
|
||||
/* Set RSTAT[QHLTx] */
|
||||
etsec->regs[RSTAT].value |= 1 << (23 - ring_nbr);
|
||||
|
||||
/* Save remaining data to send the end of the frame when the ring will
|
||||
* be restarted
|
||||
*/
|
||||
etsec->rx_remaining_data = remaining_data;
|
||||
|
||||
/* Copy the frame */
|
||||
tmp_buf = g_malloc(size);
|
||||
memcpy(tmp_buf, etsec->rx_buffer, size);
|
||||
etsec->rx_buffer = tmp_buf;
|
||||
|
||||
RING_DEBUG("no empty RxBD available any more\n");
|
||||
} else {
|
||||
etsec->rx_buffer_len = 0;
|
||||
etsec->rx_buffer = NULL;
|
||||
}
|
||||
|
||||
RING_DEBUG("eTSEC End of ring_write: remaining_data:%zu\n", remaining_data);
|
||||
}
|
@ -405,6 +405,8 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu,
|
||||
|
||||
dev->rx_bufs++;
|
||||
|
||||
qemu_flush_queued_packets(qemu_get_queue(dev->nic));
|
||||
|
||||
DPRINTF("h_add_logical_lan_buffer(): Added buf ptr=%d rx_bufs=%d"
|
||||
" bd=0x%016llx\n", dev->add_buf_ptr, dev->rx_bufs,
|
||||
(unsigned long long)buf);
|
||||
|
@ -238,6 +238,7 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
|
||||
the first node as boot node and be happy */
|
||||
for (i = smp_cpus - 1; i >= 0; i--) {
|
||||
CPUState *cpu;
|
||||
PowerPCCPU *pcpu;
|
||||
char cpu_name[128];
|
||||
uint64_t cpu_release_addr = MPC8544_SPIN_BASE + (i * 0x20);
|
||||
|
||||
@ -246,14 +247,16 @@ static int ppce500_load_device_tree(QEMUMachineInitArgs *args,
|
||||
continue;
|
||||
}
|
||||
env = cpu->env_ptr;
|
||||
pcpu = POWERPC_CPU(cpu);
|
||||
|
||||
snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x",
|
||||
cpu->cpu_index);
|
||||
ppc_get_vcpu_dt_id(pcpu));
|
||||
qemu_fdt_add_subnode(fdt, cpu_name);
|
||||
qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq);
|
||||
qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq);
|
||||
qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
|
||||
qemu_fdt_setprop_cell(fdt, cpu_name, "reg", cpu->cpu_index);
|
||||
qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
|
||||
ppc_get_vcpu_dt_id(pcpu));
|
||||
qemu_fdt_setprop_cell(fdt, cpu_name, "d-cache-line-size",
|
||||
env->dcache_line_size);
|
||||
qemu_fdt_setprop_cell(fdt, cpu_name, "i-cache-line-size",
|
||||
|
22
hw/ppc/ppc.c
22
hw/ppc/ppc.c
@ -26,6 +26,7 @@
|
||||
#include "hw/ppc/ppc_e500.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "hw/timer/m48t59.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/loader.h"
|
||||
@ -1362,3 +1363,24 @@ int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CPU device-tree ID helpers */
|
||||
int ppc_get_vcpu_dt_id(PowerPCCPU *cpu)
|
||||
{
|
||||
return cpu->cpu_dt_id;
|
||||
}
|
||||
|
||||
PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
||||
CPU_FOREACH(cs) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
|
||||
if (cpu->cpu_dt_id == cpu_dt_id) {
|
||||
return cpu;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/usb.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
@ -206,19 +207,20 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(cpu);
|
||||
int index = ppc_get_vcpu_dt_id(POWERPC_CPU(cpu));
|
||||
uint32_t associativity[] = {cpu_to_be32(0x5),
|
||||
cpu_to_be32(0x0),
|
||||
cpu_to_be32(0x0),
|
||||
cpu_to_be32(0x0),
|
||||
cpu_to_be32(cpu->numa_node),
|
||||
cpu_to_be32(cpu->cpu_index)};
|
||||
cpu_to_be32(index)};
|
||||
|
||||
if ((cpu->cpu_index % smt) != 0) {
|
||||
if ((index % smt) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name,
|
||||
cpu->cpu_index);
|
||||
index);
|
||||
|
||||
offset = fdt_path_offset(fdt, cpu_model);
|
||||
if (offset < 0) {
|
||||
@ -367,7 +369,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base,
|
||||
CPUPPCState *env = &cpu->env;
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(cs);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
|
||||
int index = cs->cpu_index;
|
||||
int index = ppc_get_vcpu_dt_id(cpu);
|
||||
uint32_t servers_prop[smp_threads];
|
||||
uint32_t gservers_prop[smp_threads * 2];
|
||||
char *nodename;
|
||||
@ -685,6 +687,7 @@ static void spapr_reset_htab(sPAPREnvironment *spapr)
|
||||
if (shift > 0) {
|
||||
/* Kernel handles htab, we don't need to allocate one */
|
||||
spapr->htab_shift = shift;
|
||||
kvmppc_kern_htab = true;
|
||||
} else {
|
||||
if (!spapr->htab) {
|
||||
/* Allocate an htab if we don't yet have one */
|
||||
@ -740,8 +743,21 @@ static void spapr_cpu_reset(void *opaque)
|
||||
env->spr[SPR_HIOR] = 0;
|
||||
|
||||
env->external_htab = (uint8_t *)spapr->htab;
|
||||
if (kvm_enabled() && !env->external_htab) {
|
||||
/*
|
||||
* HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte*
|
||||
* functions do the right thing.
|
||||
*/
|
||||
env->external_htab = (void *)1;
|
||||
}
|
||||
env->htab_base = -1;
|
||||
env->htab_mask = HTAB_SIZE(spapr) - 1;
|
||||
/*
|
||||
* htab_mask is the mask used to normalize hash value to PTEG index.
|
||||
* htab_shift is log2 of hash table size.
|
||||
* We have 8 hpte per group, and each hpte is 16 bytes.
|
||||
* ie have 128 bytes per hpte entry.
|
||||
*/
|
||||
env->htab_mask = (1ULL << ((spapr)->htab_shift - 7)) - 1;
|
||||
env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab |
|
||||
(spapr->htab_shift - 18);
|
||||
}
|
||||
@ -1305,20 +1321,15 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
|
||||
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
|
||||
NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
|
||||
if (kernel_size < 0) {
|
||||
if (kernel_size == ELF_LOAD_WRONG_ENDIAN) {
|
||||
kernel_size = load_elf(kernel_filename,
|
||||
translate_kernel_address, NULL,
|
||||
NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0);
|
||||
kernel_le = kernel_size > 0;
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
kernel_size = load_image_targphys(kernel_filename,
|
||||
KERNEL_LOAD_ADDR,
|
||||
load_limit - KERNEL_LOAD_ADDR);
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
fprintf(stderr, "qemu: error loading %s: %s\n",
|
||||
kernel_filename, load_elf_strerror(kernel_size));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -1366,6 +1377,24 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
assert(spapr->fdt_skel != NULL);
|
||||
}
|
||||
|
||||
static int spapr_kvm_type(const char *vm_type)
|
||||
{
|
||||
if (!vm_type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(vm_type, "HV")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(vm_type, "PR")) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
error_report("Unknown kvm-type specified '%s'", vm_type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static QEMUMachine spapr_machine = {
|
||||
.name = "pseries",
|
||||
.desc = "pSeries Logical Partition (PAPR compliant)",
|
||||
@ -1376,6 +1405,7 @@ static QEMUMachine spapr_machine = {
|
||||
.max_cpus = MAX_CPUS,
|
||||
.no_parallel = 1,
|
||||
.default_boot_order = NULL,
|
||||
.kvm_type = spapr_kvm_type,
|
||||
};
|
||||
|
||||
static void spapr_machine_init(void)
|
||||
|
@ -40,6 +40,17 @@ static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
|
||||
return rb;
|
||||
}
|
||||
|
||||
static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index)
|
||||
{
|
||||
/*
|
||||
* hash value/pteg group index is normalized by htab_mask
|
||||
*/
|
||||
if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
@ -50,8 +61,8 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong ptel = args[3];
|
||||
target_ulong page_shift = 12;
|
||||
target_ulong raddr;
|
||||
target_ulong i;
|
||||
hwaddr hpte;
|
||||
target_ulong index;
|
||||
uint64_t token;
|
||||
|
||||
/* only handle 4k and 16M pages for now */
|
||||
if (pteh & HPTE64_V_LARGE) {
|
||||
@ -91,33 +102,37 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
|
||||
pteh &= ~0x60ULL;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
if (!valid_pte_index(env, pte_index)) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
if (likely((flags & H_EXACT) == 0)) {
|
||||
pte_index &= ~7ULL;
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
for (i = 0; ; ++i) {
|
||||
if (i == 8) {
|
||||
token = ppc_hash64_start_access(cpu, pte_index);
|
||||
do {
|
||||
if (index == 8) {
|
||||
ppc_hash64_stop_access(token);
|
||||
return H_PTEG_FULL;
|
||||
}
|
||||
if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) {
|
||||
if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) {
|
||||
break;
|
||||
}
|
||||
hpte += HASH_PTE_SIZE_64;
|
||||
}
|
||||
} while (index++);
|
||||
ppc_hash64_stop_access(token);
|
||||
} else {
|
||||
i = 0;
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) {
|
||||
token = ppc_hash64_start_access(cpu, pte_index);
|
||||
if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) {
|
||||
ppc_hash64_stop_access(token);
|
||||
return H_PTEG_FULL;
|
||||
}
|
||||
ppc_hash64_stop_access(token);
|
||||
}
|
||||
ppc_hash64_store_hpte1(env, hpte, ptel);
|
||||
/* eieio(); FIXME: need some sort of barrier for smp? */
|
||||
ppc_hash64_store_hpte0(env, hpte, pteh | HPTE64_V_HPTE_DIRTY);
|
||||
|
||||
args[0] = pte_index + i;
|
||||
ppc_hash64_store_hpte(env, pte_index + index,
|
||||
pteh | HPTE64_V_HPTE_DIRTY, ptel);
|
||||
|
||||
args[0] = pte_index + index;
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
@ -133,17 +148,17 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
|
||||
target_ulong flags,
|
||||
target_ulong *vp, target_ulong *rp)
|
||||
{
|
||||
hwaddr hpte;
|
||||
uint64_t token;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
if (!valid_pte_index(env, ptex)) {
|
||||
return REMOVE_PARM;
|
||||
}
|
||||
|
||||
hpte = ptex * HASH_PTE_SIZE_64;
|
||||
|
||||
v = ppc_hash64_load_hpte0(env, hpte);
|
||||
r = ppc_hash64_load_hpte1(env, hpte);
|
||||
token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex);
|
||||
v = ppc_hash64_load_hpte0(env, token, 0);
|
||||
r = ppc_hash64_load_hpte1(env, token, 0);
|
||||
ppc_hash64_stop_access(token);
|
||||
|
||||
if ((v & HPTE64_V_VALID) == 0 ||
|
||||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
|
||||
@ -152,7 +167,7 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
|
||||
}
|
||||
*vp = v;
|
||||
*rp = r;
|
||||
ppc_hash64_store_hpte0(env, hpte, HPTE64_V_HPTE_DIRTY);
|
||||
ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0);
|
||||
rb = compute_tlbie_rb(v, r, ptex);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
return REMOVE_SUCCESS;
|
||||
@ -259,17 +274,17 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong flags = args[0];
|
||||
target_ulong pte_index = args[1];
|
||||
target_ulong avpn = args[2];
|
||||
hwaddr hpte;
|
||||
uint64_t token;
|
||||
target_ulong v, r, rb;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
if (!valid_pte_index(env, pte_index)) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
hpte = pte_index * HASH_PTE_SIZE_64;
|
||||
|
||||
v = ppc_hash64_load_hpte0(env, hpte);
|
||||
r = ppc_hash64_load_hpte1(env, hpte);
|
||||
token = ppc_hash64_start_access(cpu, pte_index);
|
||||
v = ppc_hash64_load_hpte0(env, token, 0);
|
||||
r = ppc_hash64_load_hpte1(env, token, 0);
|
||||
ppc_hash64_stop_access(token);
|
||||
|
||||
if ((v & HPTE64_V_VALID) == 0 ||
|
||||
((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
|
||||
@ -282,11 +297,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
r |= (flags << 48) & HPTE64_R_KEY_HI;
|
||||
r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
|
||||
rb = compute_tlbie_rb(v, r, pte_index);
|
||||
ppc_hash64_store_hpte0(env, hpte, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY);
|
||||
ppc_hash64_store_hpte(env, pte_index,
|
||||
(v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0);
|
||||
ppc_tlb_invalidate_one(env, rb);
|
||||
ppc_hash64_store_hpte1(env, hpte, r);
|
||||
/* Don't need a memory barrier, due to qemu's global lock */
|
||||
ppc_hash64_store_hpte0(env, hpte, v | HPTE64_V_HPTE_DIRTY);
|
||||
ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r);
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
@ -299,7 +314,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
uint8_t *hpte;
|
||||
int i, ridx, n_entries = 1;
|
||||
|
||||
if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
|
||||
if (!valid_pte_index(env, pte_index)) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
@ -467,13 +482,13 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong vpa = args[2];
|
||||
target_ulong ret = H_PARAMETER;
|
||||
CPUPPCState *tenv;
|
||||
CPUState *tcpu;
|
||||
PowerPCCPU *tcpu;
|
||||
|
||||
tcpu = qemu_get_cpu(procno);
|
||||
tcpu = ppc_get_vcpu_by_dt_id(procno);
|
||||
if (!tcpu) {
|
||||
return H_PARAMETER;
|
||||
}
|
||||
tenv = tcpu->env_ptr;
|
||||
tenv = &tcpu->env;
|
||||
|
||||
switch (flags) {
|
||||
case FLAGS_REGISTER_VPA:
|
||||
|
@ -243,6 +243,42 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba,
|
||||
target_ulong *tce)
|
||||
{
|
||||
if (ioba >= tcet->window_size) {
|
||||
hcall_dprintf("spapr_iommu_get_tce on out-of-bounds IOBA 0x"
|
||||
TARGET_FMT_lx "\n", ioba);
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
*tce = tcet->table[ioba >> SPAPR_TCE_PAGE_SHIFT];
|
||||
|
||||
return H_SUCCESS;
|
||||
}
|
||||
|
||||
static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||
target_ulong opcode, target_ulong *args)
|
||||
{
|
||||
target_ulong liobn = args[0];
|
||||
target_ulong ioba = args[1];
|
||||
target_ulong tce = 0;
|
||||
target_ulong ret = H_PARAMETER;
|
||||
sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn);
|
||||
|
||||
ioba &= ~(SPAPR_TCE_PAGE_SIZE - 1);
|
||||
|
||||
if (tcet) {
|
||||
ret = get_tce_emu(tcet, ioba, &tce);
|
||||
if (!ret) {
|
||||
args[0] = tce;
|
||||
}
|
||||
}
|
||||
trace_spapr_iommu_get(liobn, ioba, ret, tce);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spapr_dma_dt(void *fdt, int node_off, const char *propname,
|
||||
uint32_t liobn, uint64_t window, uint32_t size)
|
||||
{
|
||||
@ -295,6 +331,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data)
|
||||
|
||||
/* hcall-tce */
|
||||
spapr_register_hypercall(H_PUT_TCE, h_put_tce);
|
||||
spapr_register_hypercall(H_GET_TCE, h_get_tce);
|
||||
}
|
||||
|
||||
static TypeInfo spapr_tce_table_info = {
|
||||
|
@ -469,6 +469,8 @@ static const MemoryRegionOps spapr_msi_ops = {
|
||||
|
||||
void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr)
|
||||
{
|
||||
uint64_t window_size = 4096;
|
||||
|
||||
/*
|
||||
* As MSI/MSIX interrupts trigger by writing at MSI/MSIX vectors,
|
||||
* we need to allocate some memory to catch those writes coming
|
||||
@ -476,10 +478,19 @@ void spapr_pci_msi_init(sPAPREnvironment *spapr, hwaddr addr)
|
||||
* As MSIMessage:addr is going to be the same and MSIMessage:data
|
||||
* is going to be a VIRQ number, 4 bytes of the MSI MR will only
|
||||
* be used.
|
||||
*
|
||||
* For KVM we want to ensure that this memory is a full page so that
|
||||
* our memory slot is of page size granularity.
|
||||
*/
|
||||
#ifdef CONFIG_KVM
|
||||
if (kvm_enabled()) {
|
||||
window_size = getpagesize();
|
||||
}
|
||||
#endif
|
||||
|
||||
spapr->msi_win_addr = addr;
|
||||
memory_region_init_io(&spapr->msiwindow, NULL, &spapr_msi_ops, spapr,
|
||||
"msi", getpagesize());
|
||||
"msi", window_size);
|
||||
memory_region_add_subregion(get_system_memory(), spapr->msi_win_addr,
|
||||
&spapr->msiwindow);
|
||||
}
|
||||
@ -728,6 +739,8 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data)
|
||||
dc->props = spapr_phb_properties;
|
||||
dc->reset = spapr_phb_reset;
|
||||
dc->vmsd = &vmstate_spapr_pci;
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->cannot_instantiate_with_device_add_yet = false;
|
||||
}
|
||||
|
||||
static const TypeInfo spapr_phb_info = {
|
||||
|
@ -131,7 +131,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
target_ulong id;
|
||||
CPUState *cpu;
|
||||
PowerPCCPU *cpu;
|
||||
|
||||
if (nargs != 1 || nret != 2) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
@ -139,9 +139,9 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_,
|
||||
}
|
||||
|
||||
id = rtas_ld(args, 0);
|
||||
cpu = qemu_get_cpu(id);
|
||||
cpu = ppc_get_vcpu_by_dt_id(id);
|
||||
if (cpu != NULL) {
|
||||
if (cpu->halted) {
|
||||
if (CPU(cpu)->halted) {
|
||||
rtas_st(rets, 1, 0);
|
||||
} else {
|
||||
rtas_st(rets, 1, 2);
|
||||
@ -161,7 +161,7 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
|
||||
uint32_t nret, target_ulong rets)
|
||||
{
|
||||
target_ulong id, start, r3;
|
||||
CPUState *cs;
|
||||
PowerPCCPU *cpu;
|
||||
|
||||
if (nargs != 3 || nret != 1) {
|
||||
rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR);
|
||||
@ -172,9 +172,9 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPREnvironment *spapr,
|
||||
start = rtas_ld(args, 1);
|
||||
r3 = rtas_ld(args, 2);
|
||||
|
||||
cs = qemu_get_cpu(id);
|
||||
if (cs != NULL) {
|
||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||
cpu = ppc_get_vcpu_by_dt_id(id);
|
||||
if (cpu != NULL) {
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (!cs->halted) {
|
||||
|
@ -174,6 +174,19 @@ static int xilinx_load_device_tree(hwaddr addr,
|
||||
if (!fdt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
||||
initrd_base);
|
||||
if (r < 0) {
|
||||
error_report("couldn't set /chosen/linux,initrd-start");
|
||||
}
|
||||
|
||||
r = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
||||
(initrd_base + initrd_size));
|
||||
if (r < 0) {
|
||||
error_report("couldn't set /chosen/linux,initrd-end");
|
||||
}
|
||||
|
||||
r = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
|
||||
if (r < 0)
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
@ -187,6 +200,8 @@ static void virtex_init(QEMUMachineInitArgs *args)
|
||||
const char *cpu_model = args->cpu_model;
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
hwaddr initrd_base = 0;
|
||||
int initrd_size = 0;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
DeviceState *dev;
|
||||
PowerPCCPU *cpu;
|
||||
@ -259,10 +274,27 @@ static void virtex_init(QEMUMachineInitArgs *args)
|
||||
|
||||
boot_info.ima_size = kernel_size;
|
||||
|
||||
/* Load initrd. */
|
||||
if (args->initrd_filename) {
|
||||
initrd_base = high = ROUND_UP(high, 4);
|
||||
initrd_size = load_image_targphys(args->initrd_filename,
|
||||
high, ram_size - high);
|
||||
|
||||
if (initrd_size < 0) {
|
||||
error_report("couldn't load ram disk '%s'",
|
||||
args->initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
high = ROUND_UP(high + initrd_size, 4);
|
||||
}
|
||||
|
||||
/* Provide a device-tree. */
|
||||
boot_info.fdt = high + (8192 * 2);
|
||||
boot_info.fdt &= ~8191;
|
||||
xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline);
|
||||
|
||||
xilinx_load_device_tree(boot_info.fdt, ram_size,
|
||||
initrd_base, initrd_size,
|
||||
kernel_cmdline);
|
||||
}
|
||||
env->load_info = &boot_info;
|
||||
}
|
||||
|
@ -98,10 +98,10 @@ static int s390_ipl_init(SysBusDevice *dev)
|
||||
uint64_t pentry = KERN_IMAGE_START;
|
||||
kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
|
||||
NULL, 1, ELF_MACHINE, 0);
|
||||
if (kernel_size == -1) {
|
||||
if (kernel_size < 0) {
|
||||
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
|
||||
}
|
||||
if (kernel_size == -1) {
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
|
||||
return -1;
|
||||
}
|
||||
|
@ -62,6 +62,8 @@
|
||||
|
||||
#define SRP_RSP_SENSE_DATA_LEN 18
|
||||
|
||||
#define SRP_REPORT_LUNS_WLUN 0xc10100000000000ULL
|
||||
|
||||
typedef union vscsi_crq {
|
||||
struct viosrp_crq s;
|
||||
uint8_t raw[16];
|
||||
@ -719,12 +721,70 @@ static void vscsi_inquiry_no_target(VSCSIState *s, vscsi_req *req)
|
||||
}
|
||||
}
|
||||
|
||||
static void vscsi_report_luns(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
BusChild *kid;
|
||||
int i, len, n, rc;
|
||||
uint8_t *resp_data;
|
||||
bool found_lun0;
|
||||
|
||||
n = 0;
|
||||
found_lun0 = false;
|
||||
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
|
||||
SCSIDevice *dev = SCSI_DEVICE(kid->child);
|
||||
|
||||
n += 8;
|
||||
if (dev->channel == 0 && dev->id == 0 && dev->lun == 0) {
|
||||
found_lun0 = true;
|
||||
}
|
||||
}
|
||||
if (!found_lun0) {
|
||||
n += 8;
|
||||
}
|
||||
len = n+8;
|
||||
|
||||
resp_data = g_malloc0(len);
|
||||
memset(resp_data, 0, len);
|
||||
stl_be_p(resp_data, n);
|
||||
i = found_lun0 ? 8 : 16;
|
||||
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
|
||||
DeviceState *qdev = kid->child;
|
||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||
|
||||
if (dev->id == 0 && dev->channel == 0) {
|
||||
resp_data[i] = 0; /* Use simple LUN for 0 (SAM5 4.7.7.1) */
|
||||
} else {
|
||||
resp_data[i] = (2 << 6); /* Otherwise LUN addressing (4.7.7.4) */
|
||||
}
|
||||
resp_data[i] |= dev->id;
|
||||
resp_data[i+1] = (dev->channel << 5);
|
||||
resp_data[i+1] |= dev->lun;
|
||||
i += 8;
|
||||
}
|
||||
|
||||
vscsi_preprocess_desc(req);
|
||||
rc = vscsi_srp_transfer_data(s, req, 0, resp_data, len);
|
||||
g_free(resp_data);
|
||||
if (rc < 0) {
|
||||
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
} else {
|
||||
vscsi_send_rsp(s, req, 0, len - rc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
union srp_iu *srp = &req->iu.srp;
|
||||
SCSIDevice *sdev;
|
||||
int n, lun;
|
||||
|
||||
if ((srp->cmd.lun == 0 || be64_to_cpu(srp->cmd.lun) == SRP_REPORT_LUNS_WLUN)
|
||||
&& srp->cmd.cdb[0] == REPORT_LUNS) {
|
||||
vscsi_report_luns(s, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
|
||||
if (!sdev) {
|
||||
DPRINTF("VSCSI: Command for lun %08" PRIx64 " with no drive\n",
|
||||
|
@ -4,10 +4,9 @@
|
||||
#define HW_BOARDS_H
|
||||
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/qemumachine.h"
|
||||
#include "hw/qdev.h"
|
||||
|
||||
typedef struct QEMUMachine QEMUMachine;
|
||||
|
||||
typedef struct QEMUMachineInitArgs {
|
||||
const QEMUMachine *machine;
|
||||
ram_addr_t ram_size;
|
||||
@ -24,6 +23,8 @@ typedef void QEMUMachineResetFunc(void);
|
||||
|
||||
typedef void QEMUMachineHotAddCPUFunc(const int64_t id, Error **errp);
|
||||
|
||||
typedef int QEMUMachineGetKvmtypeFunc(const char *arg);
|
||||
|
||||
struct QEMUMachine {
|
||||
const char *name;
|
||||
const char *alias;
|
||||
@ -31,6 +32,7 @@ struct QEMUMachine {
|
||||
QEMUMachineInitFunc *init;
|
||||
QEMUMachineResetFunc *reset;
|
||||
QEMUMachineHotAddCPUFunc *hot_add_cpu;
|
||||
QEMUMachineGetKvmtypeFunc *kvm_type;
|
||||
BlockInterfaceType block_default_type;
|
||||
int max_cpus;
|
||||
unsigned int no_serial:1,
|
||||
|
@ -201,6 +201,7 @@ static int glue(load_elf, SZ)(const char *name, int fd,
|
||||
uint64_t addr, low = (uint64_t)-1, high = 0;
|
||||
uint8_t *data = NULL;
|
||||
char label[128];
|
||||
int ret = ELF_LOAD_FAILED;
|
||||
|
||||
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
|
||||
goto fail;
|
||||
@ -211,22 +212,30 @@ static int glue(load_elf, SZ)(const char *name, int fd,
|
||||
switch (elf_machine) {
|
||||
case EM_PPC64:
|
||||
if (EM_PPC64 != ehdr.e_machine)
|
||||
if (EM_PPC != ehdr.e_machine)
|
||||
if (EM_PPC != ehdr.e_machine) {
|
||||
ret = ELF_LOAD_WRONG_ARCH;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case EM_X86_64:
|
||||
if (EM_X86_64 != ehdr.e_machine)
|
||||
if (EM_386 != ehdr.e_machine)
|
||||
if (EM_386 != ehdr.e_machine) {
|
||||
ret = ELF_LOAD_WRONG_ARCH;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
case EM_MICROBLAZE:
|
||||
if (EM_MICROBLAZE != ehdr.e_machine)
|
||||
if (EM_MICROBLAZE_OLD != ehdr.e_machine)
|
||||
if (EM_MICROBLAZE_OLD != ehdr.e_machine) {
|
||||
ret = ELF_LOAD_WRONG_ARCH;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (elf_machine != ehdr.e_machine)
|
||||
if (elf_machine != ehdr.e_machine) {
|
||||
ret = ELF_LOAD_WRONG_ARCH;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (pentry)
|
||||
@ -305,5 +314,5 @@ static int glue(load_elf, SZ)(const char *name, int fd,
|
||||
fail:
|
||||
g_free(data);
|
||||
g_free(phdr);
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
@ -15,6 +15,12 @@ int get_image_size(const char *filename);
|
||||
int load_image(const char *filename, uint8_t *addr); /* deprecated */
|
||||
int load_image_targphys(const char *filename, hwaddr,
|
||||
uint64_t max_sz);
|
||||
|
||||
#define ELF_LOAD_FAILED -1
|
||||
#define ELF_LOAD_NOT_ELF -2
|
||||
#define ELF_LOAD_WRONG_ARCH -3
|
||||
#define ELF_LOAD_WRONG_ENDIAN -4
|
||||
const char *load_elf_strerror(int error);
|
||||
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
|
||||
uint64_t *highaddr, int big_endian, int elf_machine,
|
||||
|
@ -176,6 +176,8 @@ struct BusClass {
|
||||
void (*reset)(BusState *bus);
|
||||
/* maximum devices allowed on the bus, 0: no limit. */
|
||||
int max_dev;
|
||||
/* number of automatically allocated bus ids (e.g. ide.0) */
|
||||
int automatic_ids;
|
||||
};
|
||||
|
||||
typedef struct BusChild {
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "hw/irq.h"
|
||||
#include "qemu-common.h"
|
||||
#include "sysemu/qemumachine.h"
|
||||
|
||||
/* xen-machine.c */
|
||||
enum xen_mode {
|
||||
@ -36,7 +37,7 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level);
|
||||
|
||||
qemu_irq *xen_interrupt_controller_init(void);
|
||||
|
||||
int xen_init(void);
|
||||
int xen_init(QEMUMachine *machine);
|
||||
int xen_hvm_init(MemoryRegion **ram_memory);
|
||||
void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
|
||||
|
||||
|
@ -44,9 +44,37 @@ static inline void muls64(uint64_t *plow, uint64_t *phigh,
|
||||
*plow = r;
|
||||
*phigh = r >> 64;
|
||||
}
|
||||
|
||||
static inline int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor)
|
||||
{
|
||||
if (divisor == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
__uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow;
|
||||
__uint128_t result = dividend / divisor;
|
||||
*plow = result;
|
||||
*phigh = dividend % divisor;
|
||||
return result > UINT64_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int divs128(int64_t *plow, int64_t *phigh, int64_t divisor)
|
||||
{
|
||||
if (divisor == 0) {
|
||||
return 1;
|
||||
} else {
|
||||
__int128_t dividend = ((__int128_t)*phigh << 64) | *plow;
|
||||
__int128_t result = dividend / divisor;
|
||||
*plow = result;
|
||||
*phigh = dividend % divisor;
|
||||
return result != *plow;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b);
|
||||
void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b);
|
||||
int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor);
|
||||
int divs128(int64_t *plow, int64_t *phigh, int64_t divisor);
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "config-host.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "sysemu/qemumachine.h"
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
#include <linux/kvm.h>
|
||||
@ -152,7 +153,7 @@ extern KVMState *kvm_state;
|
||||
|
||||
/* external API */
|
||||
|
||||
int kvm_init(void);
|
||||
int kvm_init(QEMUMachine *machine);
|
||||
|
||||
int kvm_has_sync_mmu(void);
|
||||
int kvm_has_vcpu_events(void);
|
||||
|
16
include/sysemu/qemumachine.h
Normal file
16
include/sysemu/qemumachine.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* QEMU Machine typedef
|
||||
*
|
||||
* Copyright Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMUMACHINE_H
|
||||
#define QEMUMACHINE_H
|
||||
|
||||
typedef struct QEMUMachine QEMUMachine;
|
||||
|
||||
#endif /* !QEMUMACHINE_H */
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/qemumachine.h"
|
||||
|
||||
extern bool qtest_allowed;
|
||||
|
||||
@ -26,7 +27,7 @@ static inline bool qtest_enabled(void)
|
||||
|
||||
bool qtest_driver(void);
|
||||
|
||||
int qtest_init_accel(void);
|
||||
int qtest_init_accel(QEMUMachine *machine);
|
||||
void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
|
||||
|
||||
static inline int qtest_available(void)
|
||||
|
17
kvm-all.c
17
kvm-all.c
@ -36,6 +36,8 @@
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
/* This check must be after config-host.h is included */
|
||||
#ifdef CONFIG_EVENTFD
|
||||
#include <sys/eventfd.h>
|
||||
@ -1339,7 +1341,7 @@ static int kvm_max_vcpus(KVMState *s)
|
||||
return (ret) ? ret : kvm_recommended_vcpus(s);
|
||||
}
|
||||
|
||||
int kvm_init(void)
|
||||
int kvm_init(QEMUMachine *machine)
|
||||
{
|
||||
static const char upgrade_note[] =
|
||||
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
|
||||
@ -1356,7 +1358,8 @@ int kvm_init(void)
|
||||
KVMState *s;
|
||||
const KVMCapabilityInfo *missing_cap;
|
||||
int ret;
|
||||
int i;
|
||||
int i, type = 0;
|
||||
const char *kvm_type;
|
||||
|
||||
s = g_malloc0(sizeof(KVMState));
|
||||
|
||||
@ -1430,8 +1433,16 @@ int kvm_init(void)
|
||||
nc++;
|
||||
}
|
||||
|
||||
kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type");
|
||||
if (machine->kvm_type) {
|
||||
type = machine->kvm_type(kvm_type);
|
||||
} else if (kvm_type) {
|
||||
fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type);
|
||||
goto err;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = kvm_ioctl(s, KVM_CREATE_VM, 0);
|
||||
ret = kvm_ioctl(s, KVM_CREATE_VM, type);
|
||||
} while (ret == -EINTR);
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "hw/hw.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/qemumachine.h"
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
#include "hw/pci/msi.h"
|
||||
@ -34,7 +35,7 @@ int kvm_init_vcpu(CPUState *cpu)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int kvm_init(void)
|
||||
int kvm_init(QEMUMachine *machine)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
@ -1492,7 +1492,7 @@ static int do_store_exclusive(CPUPPCState *env)
|
||||
{
|
||||
target_ulong addr;
|
||||
target_ulong page_addr;
|
||||
target_ulong val;
|
||||
target_ulong val, val2 __attribute__((unused));
|
||||
int flags;
|
||||
int segv = 0;
|
||||
|
||||
@ -1515,6 +1515,13 @@ static int do_store_exclusive(CPUPPCState *env)
|
||||
case 4: segv = get_user_u32(val, addr); break;
|
||||
#if defined(TARGET_PPC64)
|
||||
case 8: segv = get_user_u64(val, addr); break;
|
||||
case 16: {
|
||||
segv = get_user_u64(val, addr);
|
||||
if (!segv) {
|
||||
segv = get_user_u64(val2, addr + 8);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: abort();
|
||||
}
|
||||
@ -1526,6 +1533,15 @@ static int do_store_exclusive(CPUPPCState *env)
|
||||
case 4: segv = put_user_u32(val, addr); break;
|
||||
#if defined(TARGET_PPC64)
|
||||
case 8: segv = put_user_u64(val, addr); break;
|
||||
case 16: {
|
||||
if (val2 == env->reserve_val2) {
|
||||
segv = put_user_u64(val, addr);
|
||||
if (!segv) {
|
||||
segv = put_user_u64(val2, addr + 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default: abort();
|
||||
}
|
||||
|
2
qtest.c
2
qtest.c
@ -500,7 +500,7 @@ static void qtest_event(void *opaque, int event)
|
||||
}
|
||||
}
|
||||
|
||||
int qtest_init_accel(void)
|
||||
int qtest_init_accel(QEMUMachine *machine)
|
||||
{
|
||||
configure_icount("0");
|
||||
|
||||
|
@ -377,15 +377,6 @@ MMU OK
|
||||
EXCP KO partially implemented
|
||||
Remarks: Should be able to boot but there is no hw platform currently emulated.
|
||||
|
||||
PowerPC 970GX:
|
||||
INSN KO Altivec missing and more
|
||||
SPR KO
|
||||
MSR ?
|
||||
IRQ OK
|
||||
MMU OK
|
||||
EXCP KO partially implemented
|
||||
Remarks: Should be able to boot but there is no hw platform currently emulated.
|
||||
|
||||
PowerPC Cell:
|
||||
INSN KO Altivec missing and more
|
||||
SPR KO
|
||||
|
@ -1156,8 +1156,6 @@
|
||||
"PowerPC 970FX v3.0 (G5)")
|
||||
POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970FX,
|
||||
"PowerPC 970FX v3.1 (G5)")
|
||||
POWERPC_DEF("970gx", CPU_POWERPC_970GX, 970GX,
|
||||
"PowerPC 970GX (G5)")
|
||||
POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970MP,
|
||||
"PowerPC 970MP v1.0")
|
||||
POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970MP,
|
||||
|
@ -570,7 +570,6 @@ enum {
|
||||
CPU_POWERPC_970FX_v21 = 0x003C0201,
|
||||
CPU_POWERPC_970FX_v30 = 0x003C0300,
|
||||
CPU_POWERPC_970FX_v31 = 0x003C0301,
|
||||
CPU_POWERPC_970GX = 0x00450000,
|
||||
CPU_POWERPC_970MP_v10 = 0x00440100,
|
||||
CPU_POWERPC_970MP_v11 = 0x00440101,
|
||||
#define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32
|
||||
|
@ -79,6 +79,7 @@ typedef struct PowerPCCPUClass {
|
||||
/**
|
||||
* PowerPCCPU:
|
||||
* @env: #CPUPPCState
|
||||
* @cpu_dt_id: CPU index used in the device tree. KVM uses this index too
|
||||
*
|
||||
* A PowerPC CPU.
|
||||
*/
|
||||
@ -88,6 +89,7 @@ typedef struct PowerPCCPU {
|
||||
/*< public >*/
|
||||
|
||||
CPUPPCState env;
|
||||
int cpu_dt_id;
|
||||
} PowerPCCPU;
|
||||
|
||||
static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env)
|
||||
|
@ -352,6 +352,10 @@ union ppc_avr_t {
|
||||
int16_t s16[8];
|
||||
int32_t s32[4];
|
||||
uint64_t u64[2];
|
||||
int64_t s64[2];
|
||||
#ifdef CONFIG_INT128
|
||||
__uint128_t u128;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
@ -926,6 +930,7 @@ struct CPUPPCState {
|
||||
target_ulong reserve_addr;
|
||||
/* Reservation value */
|
||||
target_ulong reserve_val;
|
||||
target_ulong reserve_val2;
|
||||
/* Reservation store address */
|
||||
target_ulong reserve_ea;
|
||||
/* Reserved store source register and size */
|
||||
@ -961,6 +966,7 @@ struct CPUPPCState {
|
||||
#endif
|
||||
/* segment registers */
|
||||
hwaddr htab_base;
|
||||
/* mask used to normalize hash value to PTEG index */
|
||||
hwaddr htab_mask;
|
||||
target_ulong sr[32];
|
||||
/* externally stored hash table */
|
||||
@ -1250,7 +1256,7 @@ static inline int cpu_mmu_index (CPUPPCState *env)
|
||||
#define SPR_MPC_EIE (0x050)
|
||||
#define SPR_MPC_EID (0x051)
|
||||
#define SPR_MPC_NRI (0x052)
|
||||
#define SPR_CTRL (0x088)
|
||||
#define SPR_UCTRL (0x088)
|
||||
#define SPR_MPC_CMPA (0x090)
|
||||
#define SPR_MPC_CMPB (0x091)
|
||||
#define SPR_MPC_CMPC (0x092)
|
||||
@ -1259,7 +1265,7 @@ static inline int cpu_mmu_index (CPUPPCState *env)
|
||||
#define SPR_MPC_DER (0x095)
|
||||
#define SPR_MPC_COUNTA (0x096)
|
||||
#define SPR_MPC_COUNTB (0x097)
|
||||
#define SPR_UCTRL (0x098)
|
||||
#define SPR_CTRL (0x098)
|
||||
#define SPR_MPC_CMPE (0x098)
|
||||
#define SPR_MPC_CMPF (0x099)
|
||||
#define SPR_MPC_CMPG (0x09A)
|
||||
@ -1322,12 +1328,12 @@ static inline int cpu_mmu_index (CPUPPCState *env)
|
||||
#define SPR_BOOKE_IAC3 (0x13A)
|
||||
#define SPR_HSRR1 (0x13B)
|
||||
#define SPR_BOOKE_IAC4 (0x13B)
|
||||
#define SPR_LPCR (0x13C)
|
||||
#define SPR_BOOKE_DAC1 (0x13C)
|
||||
#define SPR_LPIDR (0x13D)
|
||||
#define SPR_DABR2 (0x13D)
|
||||
#define SPR_BOOKE_DAC2 (0x13D)
|
||||
#define SPR_BOOKE_DVC1 (0x13E)
|
||||
#define SPR_LPCR (0x13E)
|
||||
#define SPR_BOOKE_DVC2 (0x13F)
|
||||
#define SPR_BOOKE_TSR (0x150)
|
||||
#define SPR_BOOKE_TCR (0x154)
|
||||
@ -1508,6 +1514,7 @@ static inline int cpu_mmu_index (CPUPPCState *env)
|
||||
#define SPR_RCPU_L2U_RA2 (0x32A)
|
||||
#define SPR_MPC_MD_DBRAM1 (0x32A)
|
||||
#define SPR_RCPU_L2U_RA3 (0x32B)
|
||||
#define SPR_TAR (0x32F)
|
||||
#define SPR_440_INV0 (0x370)
|
||||
#define SPR_440_INV1 (0x371)
|
||||
#define SPR_440_INV2 (0x372)
|
||||
@ -1875,9 +1882,31 @@ enum {
|
||||
PPC2_DBRX = 0x0000000000000010ULL,
|
||||
/* Book I 2.05 PowerPC specification */
|
||||
PPC2_ISA205 = 0x0000000000000020ULL,
|
||||
/* VSX additions in ISA 2.07 */
|
||||
PPC2_VSX207 = 0x0000000000000040ULL,
|
||||
/* ISA 2.06B bpermd */
|
||||
PPC2_PERM_ISA206 = 0x0000000000000080ULL,
|
||||
/* ISA 2.06B divide extended variants */
|
||||
PPC2_DIVE_ISA206 = 0x0000000000000100ULL,
|
||||
/* ISA 2.06B larx/stcx. instructions */
|
||||
PPC2_ATOMIC_ISA206 = 0x0000000000000200ULL,
|
||||
/* ISA 2.06B floating point integer conversion */
|
||||
PPC2_FP_CVT_ISA206 = 0x0000000000000400ULL,
|
||||
/* ISA 2.06B floating point test instructions */
|
||||
PPC2_FP_TST_ISA206 = 0x0000000000000800ULL,
|
||||
/* ISA 2.07 bctar instruction */
|
||||
PPC2_BCTAR_ISA207 = 0x0000000000001000ULL,
|
||||
/* ISA 2.07 load/store quadword */
|
||||
PPC2_LSQ_ISA207 = 0x0000000000002000ULL,
|
||||
/* ISA 2.07 Altivec */
|
||||
PPC2_ALTIVEC_207 = 0x0000000000004000ULL,
|
||||
|
||||
#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
|
||||
PPC2_ISA205)
|
||||
PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \
|
||||
PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | \
|
||||
PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \
|
||||
PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \
|
||||
PPC2_ALTIVEC_207)
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -2154,4 +2183,22 @@ static inline bool cpu_has_work(CPUState *cpu)
|
||||
|
||||
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env);
|
||||
|
||||
/**
|
||||
* ppc_get_vcpu_dt_id:
|
||||
* @cs: a PowerPCCPU struct.
|
||||
*
|
||||
* Returns a device-tree ID for a CPU.
|
||||
*/
|
||||
int ppc_get_vcpu_dt_id(PowerPCCPU *cpu);
|
||||
|
||||
/**
|
||||
* ppc_get_vcpu_by_dt_id:
|
||||
* @cpu_dt_id: a device tree id
|
||||
*
|
||||
* Searches for a CPU by @cpu_dt_id.
|
||||
*
|
||||
* Returns: a PowerPCCPU struct
|
||||
*/
|
||||
PowerPCCPU *ppc_get_vcpu_by_dt_id(int cpu_dt_id);
|
||||
|
||||
#endif /* !defined (__CPU_PPC_H__) */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,11 @@ DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32)
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_3(mulldo, i64, env, i64, i64)
|
||||
DEF_HELPER_4(divdeu, i64, env, i64, i64, i32)
|
||||
DEF_HELPER_4(divde, i64, env, i64, i64, i32)
|
||||
#endif
|
||||
DEF_HELPER_4(divweu, tl, env, tl, tl, i32)
|
||||
DEF_HELPER_4(divwe, tl, env, tl, tl, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
@ -41,6 +45,7 @@ DEF_HELPER_3(sraw, tl, env, tl, tl)
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_1(popcntd, TCG_CALL_NO_RWG_SE, tl, tl)
|
||||
DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64)
|
||||
DEF_HELPER_3(srad, tl, env, tl, tl)
|
||||
#endif
|
||||
|
||||
@ -61,11 +66,18 @@ DEF_HELPER_4(fcmpo, void, env, i64, i64, i32)
|
||||
DEF_HELPER_4(fcmpu, void, env, i64, i64, i32)
|
||||
|
||||
DEF_HELPER_2(fctiw, i64, env, i64)
|
||||
DEF_HELPER_2(fctiwu, i64, env, i64)
|
||||
DEF_HELPER_2(fctiwz, i64, env, i64)
|
||||
DEF_HELPER_2(fctiwuz, i64, env, i64)
|
||||
#if defined(TARGET_PPC64)
|
||||
DEF_HELPER_2(fcfid, i64, env, i64)
|
||||
DEF_HELPER_2(fcfidu, i64, env, i64)
|
||||
DEF_HELPER_2(fcfids, i64, env, i64)
|
||||
DEF_HELPER_2(fcfidus, i64, env, i64)
|
||||
DEF_HELPER_2(fctid, i64, env, i64)
|
||||
DEF_HELPER_2(fctidu, i64, env, i64)
|
||||
DEF_HELPER_2(fctidz, i64, env, i64)
|
||||
DEF_HELPER_2(fctiduz, i64, env, i64)
|
||||
#endif
|
||||
DEF_HELPER_2(frsp, i64, env, i64)
|
||||
DEF_HELPER_2(frin, i64, env, i64)
|
||||
@ -87,6 +99,9 @@ DEF_HELPER_2(fres, i64, env, i64)
|
||||
DEF_HELPER_2(frsqrte, i64, env, i64)
|
||||
DEF_HELPER_4(fsel, i64, env, i64, i64, i64)
|
||||
|
||||
DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64)
|
||||
DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64)
|
||||
|
||||
#define dh_alias_avr ptr
|
||||
#define dh_ctype_avr ppc_avr_t *
|
||||
#define dh_is_signed_avr dh_is_signed_ptr
|
||||
@ -94,9 +109,11 @@ DEF_HELPER_4(fsel, i64, env, i64, i64, i64)
|
||||
DEF_HELPER_3(vaddubm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vadduhm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vadduwm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vaddudm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsububm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsubuhm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsubuwm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsubudm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vavgub, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vavguh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vavguw, void, avr, avr, avr)
|
||||
@ -106,24 +123,31 @@ DEF_HELPER_3(vavgsw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminsb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminsh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminsw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminsd, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxsb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxsh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxsw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxsd, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminub, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminuh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminuw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vminud, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxub, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxuh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxuw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmaxud, void, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequd, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtub, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtuh, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtuw, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtud, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsb, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsh, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsw, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsd, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpeqfp, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgefp, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtfp, void, env, avr, avr, avr)
|
||||
@ -131,12 +155,15 @@ DEF_HELPER_4(vcmpbfp, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequb_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequh_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequw_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpequd_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtub_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtuh_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtuw_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtud_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsb_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsh_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsw_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtsd_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpeqfp_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgefp_dot, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vcmpgtfp_dot, void, env, avr, avr, avr)
|
||||
@ -149,21 +176,29 @@ DEF_HELPER_3(vmrghh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmrghw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulesb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulesh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulesw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmuleub, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmuleuh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmuleuw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulosb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulosh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulosw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmuloub, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulouh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmulouw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vmuluwm, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrab, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrah, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsraw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrad, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsrd, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vslb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vslh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vslw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsld, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vslo, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsro, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vaddcuw, void, avr, avr, avr)
|
||||
@ -182,9 +217,18 @@ DEF_HELPER_4(vadduws, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vsububs, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vsubuws, void, env, avr, avr, avr)
|
||||
DEF_HELPER_3(vadduqm, void, avr, avr, avr)
|
||||
DEF_HELPER_4(vaddecuq, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_4(vaddeuqm, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_3(vaddcuq, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsubuqm, void, avr, avr, avr)
|
||||
DEF_HELPER_4(vsubecuq, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_4(vsubeuqm, void, avr, avr, avr, avr)
|
||||
DEF_HELPER_3(vsubcuq, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vrlb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vrlh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vrlw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vrld, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsl, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vsr, void, avr, avr, avr)
|
||||
DEF_HELPER_4(vsldoi, void, avr, avr, avr, i32)
|
||||
@ -198,8 +242,10 @@ DEF_HELPER_2(vupkhpx, void, avr, avr)
|
||||
DEF_HELPER_2(vupklpx, void, avr, avr)
|
||||
DEF_HELPER_2(vupkhsb, void, avr, avr)
|
||||
DEF_HELPER_2(vupkhsh, void, avr, avr)
|
||||
DEF_HELPER_2(vupkhsw, void, avr, avr)
|
||||
DEF_HELPER_2(vupklsb, void, avr, avr)
|
||||
DEF_HELPER_2(vupklsh, void, avr, avr)
|
||||
DEF_HELPER_2(vupklsw, void, avr, avr)
|
||||
DEF_HELPER_5(vmsumubm, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(vmsummbm, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(vsel, void, env, avr, avr, avr, avr)
|
||||
@ -208,10 +254,14 @@ DEF_HELPER_4(vpkshss, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkshus, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkswss, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkswus, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpksdss, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpksdus, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkuhus, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkuwus, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkudus, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkuhum, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkuwum, void, env, avr, avr, avr)
|
||||
DEF_HELPER_4(vpkudum, void, env, avr, avr, avr)
|
||||
DEF_HELPER_3(vpkpx, void, avr, avr, avr)
|
||||
DEF_HELPER_5(vmhaddshs, void, env, avr, avr, avr, avr)
|
||||
DEF_HELPER_5(vmhraddshs, void, env, avr, avr, avr, avr)
|
||||
@ -251,6 +301,163 @@ DEF_HELPER_4(vcfsx, void, env, avr, avr, i32)
|
||||
DEF_HELPER_4(vctuxs, void, env, avr, avr, i32)
|
||||
DEF_HELPER_4(vctsxs, void, env, avr, avr, i32)
|
||||
|
||||
DEF_HELPER_2(vclzb, void, avr, avr)
|
||||
DEF_HELPER_2(vclzh, void, avr, avr)
|
||||
DEF_HELPER_2(vclzw, void, avr, avr)
|
||||
DEF_HELPER_2(vclzd, void, avr, avr)
|
||||
DEF_HELPER_2(vpopcntb, void, avr, avr)
|
||||
DEF_HELPER_2(vpopcnth, void, avr, avr)
|
||||
DEF_HELPER_2(vpopcntw, void, avr, avr)
|
||||
DEF_HELPER_2(vpopcntd, void, avr, avr)
|
||||
DEF_HELPER_3(vbpermq, void, avr, avr, avr)
|
||||
DEF_HELPER_2(vgbbd, void, avr, avr)
|
||||
DEF_HELPER_3(vpmsumb, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vpmsumh, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vpmsumw, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vpmsumd, void, avr, avr, avr)
|
||||
|
||||
DEF_HELPER_2(vsbox, void, avr, avr)
|
||||
DEF_HELPER_3(vcipher, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vcipherlast, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vncipher, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vncipherlast, void, avr, avr, avr)
|
||||
DEF_HELPER_3(vshasigmaw, void, avr, avr, i32)
|
||||
DEF_HELPER_3(vshasigmad, void, avr, avr, i32)
|
||||
DEF_HELPER_4(vpermxor, void, avr, avr, avr, avr)
|
||||
|
||||
DEF_HELPER_4(bcdadd, i32, avr, avr, avr, i32)
|
||||
DEF_HELPER_4(bcdsub, i32, avr, avr, avr, i32)
|
||||
|
||||
DEF_HELPER_2(xsadddp, void, env, i32)
|
||||
DEF_HELPER_2(xssubdp, void, env, i32)
|
||||
DEF_HELPER_2(xsmuldp, void, env, i32)
|
||||
DEF_HELPER_2(xsdivdp, void, env, i32)
|
||||
DEF_HELPER_2(xsredp, void, env, i32)
|
||||
DEF_HELPER_2(xssqrtdp, void, env, i32)
|
||||
DEF_HELPER_2(xsrsqrtedp, void, env, i32)
|
||||
DEF_HELPER_2(xstdivdp, void, env, i32)
|
||||
DEF_HELPER_2(xstsqrtdp, void, env, i32)
|
||||
DEF_HELPER_2(xsmaddadp, void, env, i32)
|
||||
DEF_HELPER_2(xsmaddmdp, void, env, i32)
|
||||
DEF_HELPER_2(xsmsubadp, void, env, i32)
|
||||
DEF_HELPER_2(xsmsubmdp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmaddadp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmaddmdp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmsubadp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmsubmdp, void, env, i32)
|
||||
DEF_HELPER_2(xscmpodp, void, env, i32)
|
||||
DEF_HELPER_2(xscmpudp, void, env, i32)
|
||||
DEF_HELPER_2(xsmaxdp, void, env, i32)
|
||||
DEF_HELPER_2(xsmindp, void, env, i32)
|
||||
DEF_HELPER_2(xscvdpsp, void, env, i32)
|
||||
DEF_HELPER_2(xscvdpspn, i64, env, i64)
|
||||
DEF_HELPER_2(xscvspdp, void, env, i32)
|
||||
DEF_HELPER_2(xscvspdpn, i64, env, i64)
|
||||
DEF_HELPER_2(xscvdpsxds, void, env, i32)
|
||||
DEF_HELPER_2(xscvdpsxws, void, env, i32)
|
||||
DEF_HELPER_2(xscvdpuxds, void, env, i32)
|
||||
DEF_HELPER_2(xscvdpuxws, void, env, i32)
|
||||
DEF_HELPER_2(xscvsxddp, void, env, i32)
|
||||
DEF_HELPER_2(xscvuxdsp, void, env, i32)
|
||||
DEF_HELPER_2(xscvsxdsp, void, env, i32)
|
||||
DEF_HELPER_2(xscvuxddp, void, env, i32)
|
||||
DEF_HELPER_2(xsrdpi, void, env, i32)
|
||||
DEF_HELPER_2(xsrdpic, void, env, i32)
|
||||
DEF_HELPER_2(xsrdpim, void, env, i32)
|
||||
DEF_HELPER_2(xsrdpip, void, env, i32)
|
||||
DEF_HELPER_2(xsrdpiz, void, env, i32)
|
||||
|
||||
DEF_HELPER_2(xsaddsp, void, env, i32)
|
||||
DEF_HELPER_2(xssubsp, void, env, i32)
|
||||
DEF_HELPER_2(xsmulsp, void, env, i32)
|
||||
DEF_HELPER_2(xsdivsp, void, env, i32)
|
||||
DEF_HELPER_2(xsresp, void, env, i32)
|
||||
DEF_HELPER_2(xsrsp, i64, env, i64)
|
||||
DEF_HELPER_2(xssqrtsp, void, env, i32)
|
||||
DEF_HELPER_2(xsrsqrtesp, void, env, i32)
|
||||
DEF_HELPER_2(xsmaddasp, void, env, i32)
|
||||
DEF_HELPER_2(xsmaddmsp, void, env, i32)
|
||||
DEF_HELPER_2(xsmsubasp, void, env, i32)
|
||||
DEF_HELPER_2(xsmsubmsp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmaddasp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmaddmsp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmsubasp, void, env, i32)
|
||||
DEF_HELPER_2(xsnmsubmsp, void, env, i32)
|
||||
|
||||
DEF_HELPER_2(xvadddp, void, env, i32)
|
||||
DEF_HELPER_2(xvsubdp, void, env, i32)
|
||||
DEF_HELPER_2(xvmuldp, void, env, i32)
|
||||
DEF_HELPER_2(xvdivdp, void, env, i32)
|
||||
DEF_HELPER_2(xvredp, void, env, i32)
|
||||
DEF_HELPER_2(xvsqrtdp, void, env, i32)
|
||||
DEF_HELPER_2(xvrsqrtedp, void, env, i32)
|
||||
DEF_HELPER_2(xvtdivdp, void, env, i32)
|
||||
DEF_HELPER_2(xvtsqrtdp, void, env, i32)
|
||||
DEF_HELPER_2(xvmaddadp, void, env, i32)
|
||||
DEF_HELPER_2(xvmaddmdp, void, env, i32)
|
||||
DEF_HELPER_2(xvmsubadp, void, env, i32)
|
||||
DEF_HELPER_2(xvmsubmdp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmaddadp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmaddmdp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmsubadp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmsubmdp, void, env, i32)
|
||||
DEF_HELPER_2(xvmaxdp, void, env, i32)
|
||||
DEF_HELPER_2(xvmindp, void, env, i32)
|
||||
DEF_HELPER_2(xvcmpeqdp, void, env, i32)
|
||||
DEF_HELPER_2(xvcmpgedp, void, env, i32)
|
||||
DEF_HELPER_2(xvcmpgtdp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvdpsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvdpsxds, void, env, i32)
|
||||
DEF_HELPER_2(xvcvdpsxws, void, env, i32)
|
||||
DEF_HELPER_2(xvcvdpuxds, void, env, i32)
|
||||
DEF_HELPER_2(xvcvdpuxws, void, env, i32)
|
||||
DEF_HELPER_2(xvcvsxddp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvuxddp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvsxwdp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvuxwdp, void, env, i32)
|
||||
DEF_HELPER_2(xvrdpi, void, env, i32)
|
||||
DEF_HELPER_2(xvrdpic, void, env, i32)
|
||||
DEF_HELPER_2(xvrdpim, void, env, i32)
|
||||
DEF_HELPER_2(xvrdpip, void, env, i32)
|
||||
DEF_HELPER_2(xvrdpiz, void, env, i32)
|
||||
|
||||
DEF_HELPER_2(xvaddsp, void, env, i32)
|
||||
DEF_HELPER_2(xvsubsp, void, env, i32)
|
||||
DEF_HELPER_2(xvmulsp, void, env, i32)
|
||||
DEF_HELPER_2(xvdivsp, void, env, i32)
|
||||
DEF_HELPER_2(xvresp, void, env, i32)
|
||||
DEF_HELPER_2(xvsqrtsp, void, env, i32)
|
||||
DEF_HELPER_2(xvrsqrtesp, void, env, i32)
|
||||
DEF_HELPER_2(xvtdivsp, void, env, i32)
|
||||
DEF_HELPER_2(xvtsqrtsp, void, env, i32)
|
||||
DEF_HELPER_2(xvmaddasp, void, env, i32)
|
||||
DEF_HELPER_2(xvmaddmsp, void, env, i32)
|
||||
DEF_HELPER_2(xvmsubasp, void, env, i32)
|
||||
DEF_HELPER_2(xvmsubmsp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmaddasp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmaddmsp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmsubasp, void, env, i32)
|
||||
DEF_HELPER_2(xvnmsubmsp, void, env, i32)
|
||||
DEF_HELPER_2(xvmaxsp, void, env, i32)
|
||||
DEF_HELPER_2(xvminsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcmpeqsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcmpgesp, void, env, i32)
|
||||
DEF_HELPER_2(xvcmpgtsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvspdp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvspsxds, void, env, i32)
|
||||
DEF_HELPER_2(xvcvspsxws, void, env, i32)
|
||||
DEF_HELPER_2(xvcvspuxds, void, env, i32)
|
||||
DEF_HELPER_2(xvcvspuxws, void, env, i32)
|
||||
DEF_HELPER_2(xvcvsxdsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvuxdsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvsxwsp, void, env, i32)
|
||||
DEF_HELPER_2(xvcvuxwsp, void, env, i32)
|
||||
DEF_HELPER_2(xvrspi, void, env, i32)
|
||||
DEF_HELPER_2(xvrspic, void, env, i32)
|
||||
DEF_HELPER_2(xvrspim, void, env, i32)
|
||||
DEF_HELPER_2(xvrspip, void, env, i32)
|
||||
DEF_HELPER_2(xvrspiz, void, env, i32)
|
||||
|
||||
DEF_HELPER_2(efscfsi, i32, env, i32)
|
||||
DEF_HELPER_2(efscfui, i32, env, i32)
|
||||
DEF_HELPER_2(efscfuf, i32, env, i32)
|
||||
|
File diff suppressed because it is too large
Load Diff
119
target-ppc/kvm.c
119
target-ppc/kvm.c
@ -36,6 +36,7 @@
|
||||
#include "hw/ppc/spapr.h"
|
||||
#include "hw/ppc/spapr_vio.h"
|
||||
#include "sysemu/watchdog.h"
|
||||
#include "trace.h"
|
||||
|
||||
//#define DEBUG_KVM
|
||||
|
||||
@ -401,7 +402,7 @@ static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu)
|
||||
|
||||
unsigned long kvm_arch_vcpu_id(CPUState *cpu)
|
||||
{
|
||||
return cpu->cpu_index;
|
||||
return ppc_get_vcpu_dt_id(POWERPC_CPU(cpu));
|
||||
}
|
||||
|
||||
int kvm_arch_init_vcpu(CPUState *cs)
|
||||
@ -480,8 +481,7 @@ static void kvm_get_one_spr(CPUState *cs, uint64_t id, int spr)
|
||||
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Warning: Unable to retrieve SPR %d from KVM: %s\n",
|
||||
spr, strerror(errno));
|
||||
trace_kvm_failed_spr_get(spr, strerror(errno));
|
||||
} else {
|
||||
switch (id & KVM_REG_SIZE_MASK) {
|
||||
case KVM_REG_SIZE_U32:
|
||||
@ -529,8 +529,7 @@ static void kvm_put_one_spr(CPUState *cs, uint64_t id, int spr)
|
||||
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Warning: Unable to set SPR %d to KVM: %s\n",
|
||||
spr, strerror(errno));
|
||||
trace_kvm_failed_spr_set(spr, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
@ -820,6 +819,9 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||
#ifdef TARGET_PPC64
|
||||
for (i = 0; i < ARRAY_SIZE(env->slb); i++) {
|
||||
sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid;
|
||||
if (env->slb[i].esid & SLB_ESID_V) {
|
||||
sregs.u.s.ppc64.slb[i].slbe |= i;
|
||||
}
|
||||
sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid;
|
||||
}
|
||||
#endif
|
||||
@ -1029,7 +1031,9 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ppc_store_sdr1(env, sregs.u.s.sdr1);
|
||||
if (!env->external_htab) {
|
||||
ppc_store_sdr1(env, sregs.u.s.sdr1);
|
||||
}
|
||||
|
||||
/* Sync SLB */
|
||||
#ifdef TARGET_PPC64
|
||||
@ -1766,24 +1770,16 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
int kvmppc_fixup_cpu(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
int smt;
|
||||
|
||||
/* Adjust cpu index for SMT */
|
||||
smt = kvmppc_smt_threads();
|
||||
cs->cpu_index = (cs->cpu_index / smp_threads) * smt
|
||||
+ (cs->cpu_index % smp_threads);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool kvmppc_has_cap_epr(void)
|
||||
{
|
||||
return cap_epr;
|
||||
}
|
||||
|
||||
bool kvmppc_has_cap_htab_fd(void)
|
||||
{
|
||||
return cap_htab_fd;
|
||||
}
|
||||
|
||||
static int kvm_ppc_register_host_cpu_type(void)
|
||||
{
|
||||
TypeInfo type_info = {
|
||||
@ -1934,3 +1930,88 @@ void kvm_arch_remove_all_hw_breakpoints(void)
|
||||
void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg)
|
||||
{
|
||||
}
|
||||
|
||||
struct kvm_get_htab_buf {
|
||||
struct kvm_get_htab_header header;
|
||||
/*
|
||||
* We require one extra byte for read
|
||||
*/
|
||||
target_ulong hpte[(HPTES_PER_GROUP * 2) + 1];
|
||||
};
|
||||
|
||||
uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index)
|
||||
{
|
||||
int htab_fd;
|
||||
struct kvm_get_htab_fd ghf;
|
||||
struct kvm_get_htab_buf *hpte_buf;
|
||||
|
||||
ghf.flags = 0;
|
||||
ghf.start_index = pte_index;
|
||||
htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf);
|
||||
if (htab_fd < 0) {
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
hpte_buf = g_malloc0(sizeof(*hpte_buf));
|
||||
/*
|
||||
* Read the hpte group
|
||||
*/
|
||||
if (read(htab_fd, hpte_buf, sizeof(*hpte_buf)) < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
close(htab_fd);
|
||||
return (uint64_t)(uintptr_t) hpte_buf->hpte;
|
||||
|
||||
out_close:
|
||||
g_free(hpte_buf);
|
||||
close(htab_fd);
|
||||
error_out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvmppc_hash64_free_pteg(uint64_t token)
|
||||
{
|
||||
struct kvm_get_htab_buf *htab_buf;
|
||||
|
||||
htab_buf = container_of((void *)(uintptr_t) token, struct kvm_get_htab_buf,
|
||||
hpte);
|
||||
g_free(htab_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index,
|
||||
target_ulong pte0, target_ulong pte1)
|
||||
{
|
||||
int htab_fd;
|
||||
struct kvm_get_htab_fd ghf;
|
||||
struct kvm_get_htab_buf hpte_buf;
|
||||
|
||||
ghf.flags = 0;
|
||||
ghf.start_index = 0; /* Ignored */
|
||||
htab_fd = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_HTAB_FD, &ghf);
|
||||
if (htab_fd < 0) {
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
hpte_buf.header.n_valid = 1;
|
||||
hpte_buf.header.n_invalid = 0;
|
||||
hpte_buf.header.index = pte_index;
|
||||
hpte_buf.hpte[0] = pte0;
|
||||
hpte_buf.hpte[1] = pte1;
|
||||
/*
|
||||
* Write the hpte entry.
|
||||
* CAUTION: write() has the warn_unused_result attribute. Hence we
|
||||
* need to check the return value, even though we do nothing.
|
||||
*/
|
||||
if (write(htab_fd, &hpte_buf, sizeof(hpte_buf)) < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
out_close:
|
||||
close(htab_fd);
|
||||
return;
|
||||
|
||||
error_out:
|
||||
return;
|
||||
}
|
||||
|
@ -36,13 +36,18 @@ int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size);
|
||||
int kvmppc_reset_htab(int shift_hint);
|
||||
uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
int kvmppc_fixup_cpu(PowerPCCPU *cpu);
|
||||
bool kvmppc_has_cap_epr(void);
|
||||
int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function);
|
||||
bool kvmppc_has_cap_htab_fd(void);
|
||||
int kvmppc_get_htab_fd(bool write);
|
||||
int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns);
|
||||
int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index,
|
||||
uint16_t n_valid, uint16_t n_invalid);
|
||||
uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu, target_ulong pte_index);
|
||||
void kvmppc_hash64_free_pteg(uint64_t token);
|
||||
|
||||
void kvmppc_hash64_write_pte(CPUPPCState *env, target_ulong pte_index,
|
||||
target_ulong pte0, target_ulong pte1);
|
||||
|
||||
#else
|
||||
|
||||
@ -155,11 +160,6 @@ static inline int kvmppc_update_sdr1(CPUPPCState *env)
|
||||
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
||||
static inline int kvmppc_fixup_cpu(PowerPCCPU *cpu)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool kvmppc_has_cap_epr(void)
|
||||
{
|
||||
return false;
|
||||
@ -171,6 +171,11 @@ static inline int kvmppc_define_rtas_kernel_token(uint32_t token,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool kvmppc_has_cap_htab_fd(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int kvmppc_get_htab_fd(bool write)
|
||||
{
|
||||
return -1;
|
||||
@ -188,6 +193,24 @@ static inline int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index,
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline uint64_t kvmppc_hash64_read_pteg(PowerPCCPU *cpu,
|
||||
target_ulong pte_index)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline void kvmppc_hash64_free_pteg(uint64_t token)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
static inline void kvmppc_hash64_write_pte(CPUPPCState *env,
|
||||
target_ulong pte_index,
|
||||
target_ulong pte0, target_ulong pte1)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_KVM
|
||||
|
@ -70,7 +70,9 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
|
||||
qemu_get_betls(f, &env->pb[i]);
|
||||
for (i = 0; i < 1024; i++)
|
||||
qemu_get_betls(f, &env->spr[i]);
|
||||
ppc_store_sdr1(env, sdr1);
|
||||
if (!env->external_htab) {
|
||||
ppc_store_sdr1(env, sdr1);
|
||||
}
|
||||
qemu_get_be32s(f, &env->vscr);
|
||||
qemu_get_be64s(f, &env->spe_acc);
|
||||
qemu_get_be32s(f, &env->spe_fscr);
|
||||
@ -179,9 +181,10 @@ static int cpu_post_load(void *opaque, int version_id)
|
||||
env->IBAT[1][i+4] = env->spr[SPR_IBAT4U + 2*i + 1];
|
||||
}
|
||||
|
||||
/* Restore htab_base and htab_mask variables */
|
||||
ppc_store_sdr1(env, env->spr[SPR_SDR1]);
|
||||
|
||||
if (!env->external_htab) {
|
||||
/* Restore htab_base and htab_mask variables */
|
||||
ppc_store_sdr1(env, env->spr[SPR_SDR1]);
|
||||
}
|
||||
hreg_compute_hflags(env);
|
||||
hreg_compute_mem_idx(env);
|
||||
|
||||
|
@ -38,7 +38,9 @@ void helper_store_dump_spr(CPUPPCState *env, uint32_t sprn)
|
||||
|
||||
void helper_store_sdr1(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
ppc_store_sdr1(env, val);
|
||||
if (!env->external_htab) {
|
||||
ppc_store_sdr1(env, val);
|
||||
}
|
||||
}
|
||||
|
||||
void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
|
||||
|
@ -40,6 +40,11 @@
|
||||
# define LOG_SLB(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Used to indicate whether we have allocated htab in the
|
||||
* host kernel
|
||||
*/
|
||||
bool kvmppc_kern_htab;
|
||||
/*
|
||||
* SLB handling
|
||||
*/
|
||||
@ -278,12 +283,12 @@ static int ppc_hash64_pte_prot(CPUPPCState *env,
|
||||
static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte)
|
||||
{
|
||||
int key, amrbits;
|
||||
int prot = PAGE_EXEC;
|
||||
int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
|
||||
|
||||
/* Only recent MMUs implement Virtual Page Class Key Protection */
|
||||
if (!(env->mmu_model & POWERPC_MMU_AMR)) {
|
||||
return PAGE_READ | PAGE_WRITE | PAGE_EXEC;
|
||||
return prot;
|
||||
}
|
||||
|
||||
key = HPTE64_R_KEY(pte.pte1);
|
||||
@ -292,39 +297,94 @@ static int ppc_hash64_amr_prot(CPUPPCState *env, ppc_hash_pte64_t pte)
|
||||
/* fprintf(stderr, "AMR protection: key=%d AMR=0x%" PRIx64 "\n", key, */
|
||||
/* env->spr[SPR_AMR]); */
|
||||
|
||||
/*
|
||||
* A store is permitted if the AMR bit is 0. Remove write
|
||||
* protection if it is set.
|
||||
*/
|
||||
if (amrbits & 0x2) {
|
||||
prot |= PAGE_WRITE;
|
||||
prot &= ~PAGE_WRITE;
|
||||
}
|
||||
/*
|
||||
* A load is permitted if the AMR bit is 0. Remove read
|
||||
* protection if it is set.
|
||||
*/
|
||||
if (amrbits & 0x1) {
|
||||
prot |= PAGE_READ;
|
||||
prot &= ~PAGE_READ;
|
||||
}
|
||||
|
||||
return prot;
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr pteg_off,
|
||||
uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index)
|
||||
{
|
||||
uint64_t token = 0;
|
||||
hwaddr pte_offset;
|
||||
|
||||
pte_offset = pte_index * HASH_PTE_SIZE_64;
|
||||
if (kvmppc_kern_htab) {
|
||||
/*
|
||||
* HTAB is controlled by KVM. Fetch the PTEG into a new buffer.
|
||||
*/
|
||||
token = kvmppc_hash64_read_pteg(cpu, pte_index);
|
||||
if (token) {
|
||||
return token;
|
||||
}
|
||||
/*
|
||||
* pteg read failed, even though we have allocated htab via
|
||||
* kvmppc_reset_htab.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* HTAB is controlled by QEMU. Just point to the internally
|
||||
* accessible PTEG.
|
||||
*/
|
||||
if (cpu->env.external_htab) {
|
||||
token = (uint64_t)(uintptr_t) cpu->env.external_htab + pte_offset;
|
||||
} else if (cpu->env.htab_base) {
|
||||
token = cpu->env.htab_base + pte_offset;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
void ppc_hash64_stop_access(uint64_t token)
|
||||
{
|
||||
if (kvmppc_kern_htab) {
|
||||
return kvmppc_hash64_free_pteg(token);
|
||||
}
|
||||
}
|
||||
|
||||
static hwaddr ppc_hash64_pteg_search(CPUPPCState *env, hwaddr hash,
|
||||
bool secondary, target_ulong ptem,
|
||||
ppc_hash_pte64_t *pte)
|
||||
{
|
||||
hwaddr pte_offset = pteg_off;
|
||||
target_ulong pte0, pte1;
|
||||
int i;
|
||||
uint64_t token;
|
||||
target_ulong pte0, pte1;
|
||||
target_ulong pte_index;
|
||||
|
||||
pte_index = (hash & env->htab_mask) * HPTES_PER_GROUP;
|
||||
token = ppc_hash64_start_access(ppc_env_get_cpu(env), pte_index);
|
||||
if (!token) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < HPTES_PER_GROUP; i++) {
|
||||
pte0 = ppc_hash64_load_hpte0(env, pte_offset);
|
||||
pte1 = ppc_hash64_load_hpte1(env, pte_offset);
|
||||
pte0 = ppc_hash64_load_hpte0(env, token, i);
|
||||
pte1 = ppc_hash64_load_hpte1(env, token, i);
|
||||
|
||||
if ((pte0 & HPTE64_V_VALID)
|
||||
&& (secondary == !!(pte0 & HPTE64_V_SECONDARY))
|
||||
&& HPTE64_V_COMPARE(pte0, ptem)) {
|
||||
pte->pte0 = pte0;
|
||||
pte->pte1 = pte1;
|
||||
return pte_offset;
|
||||
ppc_hash64_stop_access(token);
|
||||
return (pte_index + i) * HASH_PTE_SIZE_64;
|
||||
}
|
||||
|
||||
pte_offset += HASH_PTE_SIZE_64;
|
||||
}
|
||||
|
||||
ppc_hash64_stop_access(token);
|
||||
/*
|
||||
* We didn't find a valid entry.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -332,7 +392,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env,
|
||||
ppc_slb_t *slb, target_ulong eaddr,
|
||||
ppc_hash_pte64_t *pte)
|
||||
{
|
||||
hwaddr pteg_off, pte_offset;
|
||||
hwaddr pte_offset;
|
||||
hwaddr hash;
|
||||
uint64_t vsid, epnshift, epnmask, epn, ptem;
|
||||
|
||||
@ -367,8 +427,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env,
|
||||
" vsid=" TARGET_FMT_lx " ptem=" TARGET_FMT_lx
|
||||
" hash=" TARGET_FMT_plx "\n",
|
||||
env->htab_base, env->htab_mask, vsid, ptem, hash);
|
||||
pteg_off = (hash * HASH_PTEG_SIZE_64) & env->htab_mask;
|
||||
pte_offset = ppc_hash64_pteg_search(env, pteg_off, 0, ptem, pte);
|
||||
pte_offset = ppc_hash64_pteg_search(env, hash, 0, ptem, pte);
|
||||
|
||||
if (pte_offset == -1) {
|
||||
/* Secondary PTEG lookup */
|
||||
@ -377,8 +436,7 @@ static hwaddr ppc_hash64_htab_lookup(CPUPPCState *env,
|
||||
" hash=" TARGET_FMT_plx "\n", env->htab_base,
|
||||
env->htab_mask, vsid, ptem, ~hash);
|
||||
|
||||
pteg_off = (~hash * HASH_PTEG_SIZE_64) & env->htab_mask;
|
||||
pte_offset = ppc_hash64_pteg_search(env, pteg_off, 1, ptem, pte);
|
||||
pte_offset = ppc_hash64_pteg_search(env, ~hash, 1, ptem, pte);
|
||||
}
|
||||
|
||||
return pte_offset;
|
||||
@ -508,7 +566,8 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong eaddr,
|
||||
}
|
||||
|
||||
if (new_pte1 != pte.pte1) {
|
||||
ppc_hash64_store_hpte1(env, pte_offset, new_pte1);
|
||||
ppc_hash64_store_hpte(env, pte_offset / HASH_PTE_SIZE_64,
|
||||
pte.pte0, new_pte1);
|
||||
}
|
||||
|
||||
/* 7. Determine the real address from the PTE */
|
||||
@ -544,3 +603,23 @@ hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr)
|
||||
|
||||
return ppc_hash64_pte_raddr(slb, pte, addr) & TARGET_PAGE_MASK;
|
||||
}
|
||||
|
||||
void ppc_hash64_store_hpte(CPUPPCState *env,
|
||||
target_ulong pte_index,
|
||||
target_ulong pte0, target_ulong pte1)
|
||||
{
|
||||
CPUState *cs = ENV_GET_CPU(env);
|
||||
|
||||
if (kvmppc_kern_htab) {
|
||||
return kvmppc_hash64_write_pte(env, pte_index, pte0, pte1);
|
||||
}
|
||||
|
||||
pte_index *= HASH_PTE_SIZE_64;
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_index, pte0);
|
||||
stq_p(env->external_htab + pte_index + HASH_PTE_SIZE_64/2, pte1);
|
||||
} else {
|
||||
stq_phys(cs->as, env->htab_base + pte_index, pte0);
|
||||
stq_phys(cs->as, env->htab_base + pte_index + HASH_PTE_SIZE_64/2, pte1);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ int ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
|
||||
hwaddr ppc_hash64_get_phys_page_debug(CPUPPCState *env, target_ulong addr);
|
||||
int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
int mmu_idx);
|
||||
void ppc_hash64_store_hpte(CPUPPCState *env, target_ulong index,
|
||||
target_ulong pte0, target_ulong pte1);
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -75,49 +77,34 @@ int ppc_hash64_handle_mmu_fault(CPUPPCState *env, target_ulong address, int rw,
|
||||
#define HPTE64_V_1TB_SEG 0x4000000000000000ULL
|
||||
#define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL
|
||||
|
||||
|
||||
extern bool kvmppc_kern_htab;
|
||||
uint64_t ppc_hash64_start_access(PowerPCCPU *cpu, target_ulong pte_index);
|
||||
void ppc_hash64_stop_access(uint64_t token);
|
||||
|
||||
static inline target_ulong ppc_hash64_load_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
uint64_t token, int index)
|
||||
{
|
||||
CPUState *cs = ENV_GET_CPU(env);
|
||||
uint64_t addr;
|
||||
addr = token + (index * HASH_PTE_SIZE_64);
|
||||
if (env->external_htab) {
|
||||
return ldq_p(env->external_htab + pte_offset);
|
||||
return ldq_p((const void *)(uintptr_t)addr);
|
||||
} else {
|
||||
return ldq_phys(cs->as, env->htab_base + pte_offset);
|
||||
return ldq_phys(cs->as, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline target_ulong ppc_hash64_load_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset)
|
||||
uint64_t token, int index)
|
||||
{
|
||||
CPUState *cs = ENV_GET_CPU(env);
|
||||
uint64_t addr;
|
||||
addr = token + (index * HASH_PTE_SIZE_64) + HASH_PTE_SIZE_64/2;
|
||||
if (env->external_htab) {
|
||||
return ldq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2);
|
||||
return ldq_p((const void *)(uintptr_t)addr);
|
||||
} else {
|
||||
return ldq_phys(cs->as,
|
||||
env->htab_base + pte_offset + HASH_PTE_SIZE_64/2);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ppc_hash64_store_hpte0(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte0)
|
||||
{
|
||||
CPUState *cs = ENV_GET_CPU(env);
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_offset, pte0);
|
||||
} else {
|
||||
stq_phys(cs->as, env->htab_base + pte_offset, pte0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ppc_hash64_store_hpte1(CPUPPCState *env,
|
||||
hwaddr pte_offset, target_ulong pte1)
|
||||
{
|
||||
CPUState *cs = ENV_GET_CPU(env);
|
||||
if (env->external_htab) {
|
||||
stq_p(env->external_htab + pte_offset + HASH_PTE_SIZE_64/2, pte1);
|
||||
} else {
|
||||
stq_phys(cs->as,
|
||||
env->htab_base + pte_offset + HASH_PTE_SIZE_64/2, pte1);
|
||||
return ldq_phys(cs->as, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2014,6 +2014,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr)
|
||||
void ppc_store_sdr1(CPUPPCState *env, target_ulong value)
|
||||
{
|
||||
LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value);
|
||||
assert(!env->external_htab);
|
||||
if (env->spr[SPR_SDR1] != value) {
|
||||
env->spr[SPR_SDR1] = value;
|
||||
#if defined(TARGET_PPC64)
|
||||
@ -2025,7 +2026,7 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value)
|
||||
" stored in SDR1\n", htabsize);
|
||||
htabsize = 28;
|
||||
}
|
||||
env->htab_mask = (1ULL << (htabsize + 18)) - 1;
|
||||
env->htab_mask = (1ULL << (htabsize + 18 - 7)) - 1;
|
||||
env->htab_base = value & SDR_64_HTABORG;
|
||||
} else
|
||||
#endif /* defined(TARGET_PPC64) */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -627,6 +627,9 @@ static inline void _spr_register(CPUPPCState *env, int num,
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
spr->oea_read = oea_read;
|
||||
spr->oea_write = oea_write;
|
||||
#endif
|
||||
#if defined(CONFIG_KVM)
|
||||
spr->one_reg_id = one_reg_id,
|
||||
#endif
|
||||
env->spr[num] = initial_value;
|
||||
}
|
||||
@ -1064,7 +1067,7 @@ static void gen_spr_amr (CPUPPCState *env)
|
||||
spr_register_kvm(env, SPR_AMR, "AMR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_AMR, 0xffffffffffffffffULL);
|
||||
KVM_REG_PPC_AMR, 0);
|
||||
spr_register_kvm(env, SPR_UAMOR, "UAMOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
@ -2578,7 +2581,6 @@ static void gen_spr_8xx (CPUPPCState *env)
|
||||
* HRMOR => SPR 313 (Power 2.04 hypv)
|
||||
* HSRR0 => SPR 314 (Power 2.04 hypv)
|
||||
* HSRR1 => SPR 315 (Power 2.04 hypv)
|
||||
* LPCR => SPR 316 (970)
|
||||
* LPIDR => SPR 317 (970)
|
||||
* EPR => SPR 702 (Power 2.04 emb)
|
||||
* perf => 768-783 (Power 2.04)
|
||||
@ -4720,7 +4722,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data)
|
||||
PPC_FLOAT_STFIWX | PPC_WAIT |
|
||||
PPC_MEM_TLBSYNC | PPC_TLBIVAX | PPC_MEM_SYNC |
|
||||
PPC_64B | PPC_POPCNTB | PPC_POPCNTWD;
|
||||
pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL;
|
||||
pcc->insns_flags2 = PPC2_BOOKE206 | PPC2_PRCNTL | PPC2_PERM_ISA206;
|
||||
pcc->msr_mask = 0x000000009402FB36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_BOOKE206;
|
||||
pcc->excp_model = POWERPC_EXCP_BOOKE;
|
||||
@ -6644,33 +6646,13 @@ static void init_proc_970 (CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_750FX_HID2, "HID2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_970_HID5, "HID5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
POWERPC970_HID5_INIT);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_L2CR, "L2CR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, spr_access_nop,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
/* XXX: not correct */
|
||||
gen_low_BATs(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCFG, "MMUCFG",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCSR0, "MMUCSR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000); /* TOFIX */
|
||||
spr_register(env, SPR_HIOR, "SPR_HIOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_hior, &spr_write_hior,
|
||||
@ -6744,44 +6726,24 @@ static void init_proc_970FX (CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_750FX_HID2, "HID2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_970_HID5, "HID5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
POWERPC970_HID5_INIT);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_L2CR, "L2CR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, spr_access_nop,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
/* XXX: not correct */
|
||||
gen_low_BATs(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCFG, "MMUCFG",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCSR0, "MMUCSR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000); /* TOFIX */
|
||||
spr_register(env, SPR_HIOR, "SPR_HIOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_hior, &spr_write_hior,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_CTRL, "SPR_CTRL",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_UCTRL, "SPR_UCTRL",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_VRSAVE, "SPR_VRSAVE",
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
@ -6830,106 +6792,6 @@ POWERPC_FAMILY(970FX)(ObjectClass *oc, void *data)
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static int check_pow_970GX (CPUPPCState *env)
|
||||
{
|
||||
if (env->spr[SPR_HID0] & 0x00600000)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_proc_970GX (CPUPPCState *env)
|
||||
{
|
||||
gen_spr_ne_601(env);
|
||||
gen_spr_7xx(env);
|
||||
/* Time base */
|
||||
gen_tbl(env);
|
||||
/* Hardware implementation registers */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_HID0, "HID0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_clear,
|
||||
0x60000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_HID1, "HID1",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_750FX_HID2, "HID2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_970_HID5, "HID5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
POWERPC970_HID5_INIT);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_L2CR, "L2CR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, spr_access_nop,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
/* XXX: not correct */
|
||||
gen_low_BATs(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCFG, "MMUCFG",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCSR0, "MMUCSR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000); /* TOFIX */
|
||||
spr_register(env, SPR_HIOR, "SPR_HIOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_hior, &spr_write_hior,
|
||||
0x00000000);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->slb_nr = 32;
|
||||
#endif
|
||||
init_excp_970(env);
|
||||
env->dcache_line_size = 128;
|
||||
env->icache_line_size = 128;
|
||||
/* Allocate hardware IRQ controller */
|
||||
ppc970_irq_init(env);
|
||||
/* Can't find information on what this should be on reset. This
|
||||
* value is the one used by 74xx processors. */
|
||||
vscr_init(env, 0x00010000);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(970GX)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||
|
||||
dc->desc = "PowerPC 970 GX";
|
||||
pcc->init_proc = init_proc_970GX;
|
||||
pcc->check_pow = check_pow_970GX;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_64B | PPC_ALTIVEC |
|
||||
PPC_SEGMENT_64B | PPC_SLBI;
|
||||
pcc->msr_mask = 0x800000000204FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_64B;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault;
|
||||
#endif
|
||||
pcc->excp_model = POWERPC_EXCP_970;
|
||||
pcc->bus_model = PPC_FLAGS_INPUT_970;
|
||||
pcc->bfd_mach = bfd_mach_ppc64;
|
||||
pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
|
||||
POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
|
||||
POWERPC_FLAG_BUS_CLK;
|
||||
}
|
||||
|
||||
static int check_pow_970MP (CPUPPCState *env)
|
||||
{
|
||||
if (env->spr[SPR_HID0] & 0x01C00000)
|
||||
@ -6956,37 +6818,23 @@ static void init_proc_970MP (CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_750FX_HID2, "HID2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_970_HID5, "HID5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
POWERPC970_HID5_INIT);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_L2CR, "L2CR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, spr_access_nop,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
/* XXX: not correct */
|
||||
gen_low_BATs(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCFG, "MMUCFG",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCSR0, "MMUCSR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000); /* TOFIX */
|
||||
spr_register(env, SPR_HIOR, "SPR_HIOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_hior, &spr_write_hior,
|
||||
0x00000000);
|
||||
/* Logical partitionning */
|
||||
spr_register_kvm(env, SPR_LPCR, "LPCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_LPCR, 0x00000000);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->slb_nr = 32;
|
||||
#endif
|
||||
@ -7048,49 +6896,34 @@ static void init_proc_power5plus(CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_750FX_HID2, "HID2",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_970_HID5, "HID5",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
POWERPC970_HID5_INIT);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_L2CR, "L2CR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, spr_access_nop,
|
||||
0x00000000);
|
||||
/* Memory management */
|
||||
/* XXX: not correct */
|
||||
gen_low_BATs(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCFG, "MMUCFG",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCSR0, "MMUCSR0",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000); /* TOFIX */
|
||||
spr_register(env, SPR_HIOR, "SPR_HIOR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_hior, &spr_write_hior,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_CTRL, "SPR_CTRL",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_UCTRL, "SPR_UCTRL",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000);
|
||||
spr_register(env, SPR_VRSAVE, "SPR_VRSAVE",
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* Logical partitionning */
|
||||
spr_register_kvm(env, SPR_LPCR, "LPCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_LPCR, 0x00000000);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->slb_nr = 64;
|
||||
#endif
|
||||
@ -7177,21 +7010,15 @@ static void init_proc_POWER7 (CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_PMC6, 0x00000000);
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
/* Memory management */
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_MMUCFG, "MMUCFG",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x00000000); /* TOFIX */
|
||||
gen_spr_amr(env);
|
||||
/* XXX : not implemented */
|
||||
spr_register(env, SPR_CTRL, "SPR_CTRLT",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
SPR_NOACCESS, &spr_write_generic,
|
||||
0x80800000);
|
||||
spr_register(env, SPR_UCTRL, "SPR_CTRLF",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, SPR_NOACCESS,
|
||||
0x80800000);
|
||||
spr_register(env, SPR_VRSAVE, "SPR_VRSAVE",
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
@ -7201,6 +7028,11 @@ static void init_proc_POWER7 (CPUPPCState *env)
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
/* Logical partitionning */
|
||||
spr_register_kvm(env, SPR_LPCR, "LPCR",
|
||||
SPR_NOACCESS, SPR_NOACCESS,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
KVM_REG_PPC_LPCR, 0x00000000);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
env->slb_nr = 32;
|
||||
#endif
|
||||
@ -7229,14 +7061,19 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_FRSQRTES |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_FLOAT_EXT |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_64B | PPC_ALTIVEC |
|
||||
PPC_SEGMENT_64B | PPC_SLBI |
|
||||
PPC_POPCNTB | PPC_POPCNTWD;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
|
||||
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
|
||||
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
|
||||
PPC2_FP_TST_ISA206;
|
||||
pcc->msr_mask = 0x800000000284FF37ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_2_06;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
@ -7267,14 +7104,19 @@ POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data)
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_FRSQRTES |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_FLOAT_EXT |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_64B | PPC_ALTIVEC |
|
||||
PPC_SEGMENT_64B | PPC_SLBI |
|
||||
PPC_POPCNTB | PPC_POPCNTWD;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
|
||||
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
|
||||
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
|
||||
PPC2_FP_TST_ISA206;
|
||||
pcc->msr_mask = 0x800000000204FF37ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_2_06;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
@ -7291,6 +7133,18 @@ POWERPC_FAMILY(POWER7P)(ObjectClass *oc, void *data)
|
||||
pcc->l1_icache_size = 0x8000;
|
||||
}
|
||||
|
||||
static void init_proc_POWER8(CPUPPCState *env)
|
||||
{
|
||||
/* inherit P7 */
|
||||
init_proc_POWER7(env);
|
||||
|
||||
/* P8 supports the TAR */
|
||||
spr_register(env, SPR_TAR, "TAR",
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
&spr_read_generic, &spr_write_generic,
|
||||
0x00000000);
|
||||
}
|
||||
|
||||
POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
@ -7300,19 +7154,25 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
|
||||
dc->desc = "POWER8";
|
||||
pcc->pvr = CPU_POWERPC_POWER8_BASE;
|
||||
pcc->pvr_mask = CPU_POWERPC_POWER8_MASK;
|
||||
pcc->init_proc = init_proc_POWER7;
|
||||
pcc->init_proc = init_proc_POWER8;
|
||||
pcc->check_pow = check_pow_nocheck;
|
||||
pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
|
||||
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
|
||||
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
|
||||
PPC_FLOAT_FRSQRTES |
|
||||
PPC_FLOAT_STFIWX |
|
||||
PPC_FLOAT_EXT |
|
||||
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
|
||||
PPC_MEM_SYNC | PPC_MEM_EIEIO |
|
||||
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
|
||||
PPC_64B | PPC_ALTIVEC |
|
||||
PPC_64B | PPC_64BX | PPC_ALTIVEC |
|
||||
PPC_SEGMENT_64B | PPC_SLBI |
|
||||
PPC_POPCNTB | PPC_POPCNTWD;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX;
|
||||
pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX |
|
||||
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
|
||||
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
|
||||
PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 |
|
||||
PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207;
|
||||
pcc->msr_mask = 0x800000000284FF36ULL;
|
||||
pcc->mmu_model = POWERPC_MMU_2_06;
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
@ -7987,14 +7847,12 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
max_smt, kvm_enabled() ? "KVM" : "TCG");
|
||||
return;
|
||||
}
|
||||
|
||||
cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt
|
||||
+ (cs->cpu_index % smp_threads);
|
||||
#endif
|
||||
|
||||
if (kvm_enabled()) {
|
||||
if (kvmppc_fixup_cpu(cpu) != 0) {
|
||||
error_setg(errp, "Unable to virtualize selected CPU with KVM");
|
||||
return;
|
||||
}
|
||||
} else if (tcg_enabled()) {
|
||||
if (tcg_enabled()) {
|
||||
if (ppc_fixup_cpu(cpu) != 0) {
|
||||
error_setg(errp, "Unable to emulate selected CPU with TCG");
|
||||
return;
|
||||
@ -8149,9 +8007,10 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
}
|
||||
printf("PowerPC %-12s : PVR %08x MSR %016" PRIx64 "\n"
|
||||
" MMU model : %s\n",
|
||||
pcc->name, pcc->pvr, pcc->msr_mask, mmu_model);
|
||||
object_class_get_name(OBJECT_CLASS(pcc)),
|
||||
pcc->pvr, pcc->msr_mask, mmu_model);
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (env->tlb != NULL) {
|
||||
if (env->tlb.tlb6) {
|
||||
printf(" %d %s TLB in %d ways\n",
|
||||
env->nb_tlb, env->id_tlbs ? "splitted" : "merged",
|
||||
env->nb_ways);
|
||||
@ -8598,6 +8457,7 @@ static void ppc_cpu_initfn(Object *obj)
|
||||
|
||||
cs->env_ptr = env;
|
||||
cpu_exec_init(env);
|
||||
cpu->cpu_dt_id = cs->cpu_index;
|
||||
|
||||
env->msr_mask = pcc->msr_mask;
|
||||
env->mmu_model = pcc->mmu_model;
|
||||
|
@ -1136,6 +1136,7 @@ xics_ics_eoi(int nr) "ics_eoi: irq %#x"
|
||||
|
||||
# hw/ppc/spapr_iommu.c
|
||||
spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64
|
||||
spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64
|
||||
spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, unsigned pgsize) "liobn=%"PRIx64" 0x%"PRIx64" -> 0x%"PRIx64" perm=%u mask=%x"
|
||||
spapr_iommu_new_table(uint64_t liobn, void *tcet, void *table, int fd) "liobn=%"PRIx64" tcet=%p table=%p fd=%d"
|
||||
|
||||
@ -1177,6 +1178,8 @@ kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p"
|
||||
kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p"
|
||||
kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
|
||||
kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p"
|
||||
kvm_failed_spr_set(int str, const char *msg) "Warning: Unable to set SPR %d to KVM: %s"
|
||||
kvm_failed_spr_get(int str, const char *msg) "Warning: Unable to retrieve SPR %d from KVM: %s"
|
||||
|
||||
# memory.c
|
||||
memory_region_ops_read(void *mr, uint64_t addr, uint64_t value, unsigned size) "mr %p addr %#"PRIx64" value %#"PRIx64" size %u"
|
||||
|
@ -86,4 +86,79 @@ void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
|
||||
}
|
||||
*phigh = rh;
|
||||
}
|
||||
|
||||
/* Unsigned 128x64 division. Returns 1 if overflow (divide by zero or */
|
||||
/* quotient exceeds 64 bits). Otherwise returns quotient via plow and */
|
||||
/* remainder via phigh. */
|
||||
int divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor)
|
||||
{
|
||||
uint64_t dhi = *phigh;
|
||||
uint64_t dlo = *plow;
|
||||
unsigned i;
|
||||
uint64_t carry = 0;
|
||||
|
||||
if (divisor == 0) {
|
||||
return 1;
|
||||
} else if (dhi == 0) {
|
||||
*plow = dlo / divisor;
|
||||
*phigh = dlo % divisor;
|
||||
return 0;
|
||||
} else if (dhi > divisor) {
|
||||
return 1;
|
||||
} else {
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
carry = dhi >> 63;
|
||||
dhi = (dhi << 1) | (dlo >> 63);
|
||||
if (carry || (dhi >= divisor)) {
|
||||
dhi -= divisor;
|
||||
carry = 1;
|
||||
} else {
|
||||
carry = 0;
|
||||
}
|
||||
dlo = (dlo << 1) | carry;
|
||||
}
|
||||
|
||||
*plow = dlo;
|
||||
*phigh = dhi;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int divs128(int64_t *plow, int64_t *phigh, int64_t divisor)
|
||||
{
|
||||
int sgn_dvdnd = *phigh < 0;
|
||||
int sgn_divsr = divisor < 0;
|
||||
int overflow = 0;
|
||||
|
||||
if (sgn_dvdnd) {
|
||||
*plow = ~(*plow);
|
||||
*phigh = ~(*phigh);
|
||||
if (*plow == (int64_t)-1) {
|
||||
*plow = 0;
|
||||
(*phigh)++;
|
||||
} else {
|
||||
(*plow)++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sgn_divsr) {
|
||||
divisor = 0 - divisor;
|
||||
}
|
||||
|
||||
overflow = divu128((uint64_t *)plow, (uint64_t *)phigh, (uint64_t)divisor);
|
||||
|
||||
if (sgn_dvdnd ^ sgn_divsr) {
|
||||
*plow = 0 - *plow;
|
||||
}
|
||||
|
||||
if (!overflow) {
|
||||
if ((*plow < 0) ^ (sgn_dvdnd ^ sgn_divsr)) {
|
||||
overflow = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return overflow;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_INT128 */
|
||||
|
14
vl.c
14
vl.c
@ -374,6 +374,10 @@ static QemuOptsList qemu_machine_opts = {
|
||||
.name = "firmware",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "firmware image",
|
||||
},{
|
||||
.name = "kvm-type",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Specifies the KVM virtualization mode (HV, PR)",
|
||||
},
|
||||
{ /* End of list */ }
|
||||
},
|
||||
@ -2578,7 +2582,7 @@ static QEMUMachine *machine_parse(const char *name)
|
||||
exit(!name || !is_help_option(name));
|
||||
}
|
||||
|
||||
static int tcg_init(void)
|
||||
static int tcg_init(QEMUMachine *machine)
|
||||
{
|
||||
tcg_exec_init(tcg_tb_size * 1024 * 1024);
|
||||
return 0;
|
||||
@ -2588,7 +2592,7 @@ static struct {
|
||||
const char *opt_name;
|
||||
const char *name;
|
||||
int (*available)(void);
|
||||
int (*init)(void);
|
||||
int (*init)(QEMUMachine *);
|
||||
bool *allowed;
|
||||
} accel_list[] = {
|
||||
{ "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },
|
||||
@ -2597,7 +2601,7 @@ static struct {
|
||||
{ "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed },
|
||||
};
|
||||
|
||||
static int configure_accelerator(void)
|
||||
static int configure_accelerator(QEMUMachine *machine)
|
||||
{
|
||||
const char *p;
|
||||
char buf[10];
|
||||
@ -2624,7 +2628,7 @@ static int configure_accelerator(void)
|
||||
continue;
|
||||
}
|
||||
*(accel_list[i].allowed) = true;
|
||||
ret = accel_list[i].init();
|
||||
ret = accel_list[i].init(machine);
|
||||
if (ret < 0) {
|
||||
init_failed = true;
|
||||
fprintf(stderr, "failed to initialize %s: %s\n",
|
||||
@ -4053,7 +4057,7 @@ int main(int argc, char **argv, char **envp)
|
||||
exit(0);
|
||||
}
|
||||
|
||||
configure_accelerator();
|
||||
configure_accelerator(machine);
|
||||
|
||||
if (qtest_chrdev) {
|
||||
Error *local_err = NULL;
|
||||
|
@ -1001,7 +1001,7 @@ static void xen_exit_notifier(Notifier *n, void *data)
|
||||
xs_daemon_close(state->xenstore);
|
||||
}
|
||||
|
||||
int xen_init(void)
|
||||
int xen_init(QEMUMachine *machine)
|
||||
{
|
||||
xen_xc = xen_xc_interface_open(0, 0, 0);
|
||||
if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
|
||||
|
@ -47,7 +47,7 @@ qemu_irq *xen_interrupt_controller_init(void)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int xen_init(void)
|
||||
int xen_init(QEMUMachine *machine)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user