mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 09:13:55 -06:00
Misc HW patches
- Remove TCG dependency on ARM_GICV3 (Philippe) - Add MMIO interface to PVPanic device (Alexander) - Add vmapple machine (Alexander & Phil) - Restrict part of sPAPR PAGE_INIT hypercall to TCG (Philippe) - Make ghes_record_cper_errors() scope static (Gavin) - Do not expose the ARM virt machines on Xen-only binary (Philippe) - Xen header cleanups (Philippe) - Set Freescale eTSEC network device description & category (Zoltan) - Improve RX FIFO depth for various UARTs (Philippe) - Prevent TX FIFO memory leak in SiFive UART (Alistair) - Cleanups in MacIO and AT24C EEPROM (Zoltan) - Add UFS temperature event notification support & test (Keoseong) - Remove printf() calls in hw/arm/ (Peter) -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmfHpagACgkQ4+MsLN6t wN4dTg//Z2neoENdnZglRp6q66LLxSTHJnHUMNHe2Bp+KKfhce+//j+CFYgOnJjV P5eExzvHvtYFL6lNXOR7MTM/i/4pixcLYh8anyrnUWkEiufGXI4/30QePbwAAdkZ kQ1CpLI43ZdnMLTh18JswLo/JMkl/jJGvCGNjhtQVnobqam+ATgAbjmcnwA8esJS cCQVFzno9TNvsCnUtMe3IdOfIs9K3atCARbXpA3i3wd52P+NHvGO1jU0QPFBm2Lr 4kcFLI1Sg2vYWD3FujDPeCcfI7kcRDIjHS6vVplK1VlCjjdaQmxx6XKO72PnZZOG 7sra8xJpW8mZS2jfN2AxKMVlNndZMYzAJoS2UBMp1YX8KBEzlcmI2uSjGas6bcmQ XIaP7/mggx7yBU3nspUl8rqY/3Z3wVjwepf1l8KOn9Z13wo+APv4aj6W+zkK96A5 NGq6EpoKquINGLBBMkZAdfBOKk97NhwHh1Op04VgJNnPESpLnb5hENtZW1ksg92r tijWdDWnC/JtWMU2dAnmT/9Vaxk4fukGqqncXqQJzGBw0OylzWh5b2Zer02WFyxz oplISEwU3uFcg0OLa9aUHgs7lVWBUbEW3pSk1CtKuqgPtwxm/Gr2KbAVgAbFiyzr 6YPQsfFP5z6CjreOWMELPWmsYV+jtTj/KoIek2QlYt7Ex6uiA6c= =TcY9 -----END PGP SIGNATURE----- Merge tag 'hw-misc-20250305' of https://github.com/philmd/qemu into staging Misc HW patches - Remove TCG dependency on ARM_GICV3 (Philippe) - Add MMIO interface to PVPanic device (Alexander) - Add vmapple machine (Alexander & Phil) - Restrict part of sPAPR PAGE_INIT hypercall to TCG (Philippe) - Make ghes_record_cper_errors() scope static (Gavin) - Do not expose the ARM virt machines on Xen-only binary (Philippe) - Xen header cleanups (Philippe) - Set Freescale eTSEC network device description & category (Zoltan) - Improve RX FIFO depth for various UARTs (Philippe) - Prevent TX FIFO memory leak in SiFive UART (Alistair) - Cleanups in MacIO and AT24C EEPROM (Zoltan) - Add UFS temperature event notification support & test (Keoseong) - Remove printf() calls in hw/arm/ (Peter) # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmfHpagACgkQ4+MsLN6t # wN4dTg//Z2neoENdnZglRp6q66LLxSTHJnHUMNHe2Bp+KKfhce+//j+CFYgOnJjV # P5eExzvHvtYFL6lNXOR7MTM/i/4pixcLYh8anyrnUWkEiufGXI4/30QePbwAAdkZ # kQ1CpLI43ZdnMLTh18JswLo/JMkl/jJGvCGNjhtQVnobqam+ATgAbjmcnwA8esJS # cCQVFzno9TNvsCnUtMe3IdOfIs9K3atCARbXpA3i3wd52P+NHvGO1jU0QPFBm2Lr # 4kcFLI1Sg2vYWD3FujDPeCcfI7kcRDIjHS6vVplK1VlCjjdaQmxx6XKO72PnZZOG # 7sra8xJpW8mZS2jfN2AxKMVlNndZMYzAJoS2UBMp1YX8KBEzlcmI2uSjGas6bcmQ # XIaP7/mggx7yBU3nspUl8rqY/3Z3wVjwepf1l8KOn9Z13wo+APv4aj6W+zkK96A5 # NGq6EpoKquINGLBBMkZAdfBOKk97NhwHh1Op04VgJNnPESpLnb5hENtZW1ksg92r # tijWdDWnC/JtWMU2dAnmT/9Vaxk4fukGqqncXqQJzGBw0OylzWh5b2Zer02WFyxz # oplISEwU3uFcg0OLa9aUHgs7lVWBUbEW3pSk1CtKuqgPtwxm/Gr2KbAVgAbFiyzr # 6YPQsfFP5z6CjreOWMELPWmsYV+jtTj/KoIek2QlYt7Ex6uiA6c= # =TcY9 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 05 Mar 2025 09:15:20 HKT # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * tag 'hw-misc-20250305' of https://github.com/philmd/qemu: (41 commits) hw/nvram/eeprom_at24c: Reorganise init to avoid overwriting values hw/nvram/eeprom_at24c: Remove memset after g_malloc0 hw/nvram/eeprom_at24c: Remove ERR macro that calls fprintf to stderr hw/nvram/eeprom_at24c: Use OBJECT_DECLARE_SIMPLE_TYPE hw/arm/versatilepb: Convert printfs to LOG_GUEST_ERROR hw/arm/omap_sx1: Remove ifdeffed out debug printf hw/arm/omap1: Convert information printfs to tracepoints hw/arm/omap1: Drop ALMDEBUG ifdeffed out code hw/arm/omap1: Convert raw printfs to qemu_log_mask() tests/qtest/ufs-test: Add test code for the temperature feature hw/ufs: Add temperature event notification support hw/misc/macio/gpio: Add constants for register bits hw/misc/macio: Improve trace logs hw/char/sifive_uart: Free fifo on unrealize hw/char/sh_serial: Return correct number of empty RX FIFO elements hw/char/mcf_uart: Really use RX FIFO depth hw/char/mcf_uart: Use FIFO_DEPTH definition instead of magic values hw/char/imx_serial: Really use RX FIFO depth hw/char/bcm2835_aux: Really use RX FIFO depth hw/char/pl011: Really use RX FIFO depth ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
f5e6e13124
68 changed files with 2558 additions and 204 deletions
|
@ -2826,6 +2826,14 @@ S: Maintained
|
|||
F: hw/uefi/
|
||||
F: include/hw/uefi/
|
||||
|
||||
VMapple
|
||||
M: Alexander Graf <agraf@csgraf.de>
|
||||
M: Phil Dennis-Jordan <phil@philjordan.eu>
|
||||
S: Maintained
|
||||
F: hw/vmapple/*
|
||||
F: include/hw/vmapple/*
|
||||
F: docs/system/arm/vmapple.rst
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Overall Audio backends
|
||||
|
|
|
@ -16,4 +16,5 @@ config KVM
|
|||
config XEN
|
||||
bool
|
||||
select FSDEV_9P if VIRTFS
|
||||
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||
select XEN_BUS
|
||||
|
|
12
contrib/vmapple/uuid.sh
Executable file
12
contrib/vmapple/uuid.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Used for converting a guest provisioned using Virtualization.framework
|
||||
# for use with the QEMU 'vmapple' aarch64 machine type.
|
||||
#
|
||||
# Extracts the Machine UUID from Virtualization.framework VM JSON file.
|
||||
# (as produced by 'macosvm', passed as command line argument)
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
plutil -extract machineId raw "$1" | base64 -d | plutil -extract ECID raw -
|
||||
|
65
docs/system/arm/vmapple.rst
Normal file
65
docs/system/arm/vmapple.rst
Normal file
|
@ -0,0 +1,65 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
VMApple machine emulation
|
||||
========================================================================================
|
||||
|
||||
VMApple is the device model that the macOS built-in hypervisor called "Virtualization.framework"
|
||||
exposes to Apple Silicon macOS guests. The "vmapple" machine model in QEMU implements the same
|
||||
device model, but does not use any code from Virtualization.Framework.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
To run the vmapple machine model, you need to
|
||||
|
||||
* Run on Apple Silicon
|
||||
* Run on macOS 12.0 or above
|
||||
* Have an already installed copy of a Virtualization.Framework macOS 12 virtual
|
||||
machine. Note that newer versions than 12.x are currently NOT supported on
|
||||
the guest side. I will assume that you installed it using the
|
||||
`macosvm <https://github.com/s-u/macosvm>`__ CLI.
|
||||
|
||||
First, we need to extract the UUID from the virtual machine that you installed. You can do this
|
||||
by running the shell script in contrib/vmapple/uuid.sh on the macosvm.json file.
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: uuid.sh script to extract the UUID from a macosvm.json file
|
||||
|
||||
$ contrib/vmapple/uuid.sh "path/to/macosvm.json"
|
||||
|
||||
Now we also need to trim the aux partition. It contains metadata that we can just discard:
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Command to trim the aux file
|
||||
|
||||
$ dd if="aux.img" of="aux.img.trimmed" bs=$(( 0x4000 )) skip=1
|
||||
|
||||
How to run
|
||||
----------
|
||||
|
||||
Then, we can launch QEMU with the Virtualization.Framework pre-boot environment and the readily
|
||||
installed target disk images. I recommend to port forward the VM's ssh and vnc ports to the host
|
||||
to get better interactive access into the target system:
|
||||
|
||||
.. code-block:: bash
|
||||
:caption: Example execution command line
|
||||
|
||||
$ UUID="$(contrib/vmapple/uuid.sh 'macosvm.json')"
|
||||
$ AVPBOOTER="/System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin"
|
||||
$ AUX="aux.img.trimmed"
|
||||
$ DISK="disk.img"
|
||||
$ qemu-system-aarch64 \
|
||||
-serial mon:stdio \
|
||||
-m 4G \
|
||||
-accel hvf \
|
||||
-M vmapple,uuid="$UUID" \
|
||||
-bios "$AVPBOOTER" \
|
||||
-drive file="$AUX",if=pflash,format=raw \
|
||||
-drive file="$DISK",if=pflash,format=raw \
|
||||
-drive file="$AUX",if=none,id=aux,format=raw \
|
||||
-drive file="$DISK",if=none,id=root,format=raw \
|
||||
-device vmapple-virtio-blk-pci,variant=aux,drive=aux \
|
||||
-device vmapple-virtio-blk-pci,variant=root,drive=root \
|
||||
-netdev user,id=net0,ipv6=off,hostfwd=tcp::2222-:22,hostfwd=tcp::5901-:5900 \
|
||||
-device virtio-net-pci,netdev=net0
|
||||
|
|
@ -103,6 +103,7 @@ Board-specific documentation
|
|||
arm/stellaris
|
||||
arm/stm32
|
||||
arm/virt
|
||||
arm/vmapple
|
||||
arm/xenpvh
|
||||
arm/xlnx-versal-virt
|
||||
arm/xlnx-zynq
|
||||
|
|
|
@ -42,6 +42,7 @@ source ufs/Kconfig
|
|||
source usb/Kconfig
|
||||
source virtio/Kconfig
|
||||
source vfio/Kconfig
|
||||
source vmapple/Kconfig
|
||||
source xen/Kconfig
|
||||
source watchdog/Kconfig
|
||||
|
||||
|
|
|
@ -390,7 +390,7 @@ static void get_hw_error_offsets(uint64_t ghes_addr,
|
|||
*read_ack_register_addr = ghes_addr + sizeof(uint64_t);
|
||||
}
|
||||
|
||||
void ghes_record_cper_errors(const void *cper, size_t len,
|
||||
static void ghes_record_cper_errors(const void *cper, size_t len,
|
||||
uint16_t source_id, Error **errp)
|
||||
{
|
||||
uint64_t cper_addr = 0, read_ack_register_addr = 0, read_ack_register;
|
||||
|
@ -440,8 +440,6 @@ void ghes_record_cper_errors(const void *cper, size_t len,
|
|||
|
||||
/* Write the generic error data entry into guest memory */
|
||||
cpu_physical_memory_write(cper_addr, cper, len);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int acpi_ghes_memory_errors(uint16_t source_id, uint64_t physical_address)
|
||||
|
|
|
@ -2,6 +2,7 @@ config ARM_VIRT
|
|||
bool
|
||||
default y
|
||||
depends on ARM
|
||||
depends on TCG || KVM || HVF
|
||||
imply PCI_DEVICES
|
||||
imply TEST_DEVICES
|
||||
imply VFIO_AMD_XGBE
|
||||
|
|
125
hw/arm/omap1.c
125
hw/arm/omap1.c
|
@ -42,6 +42,7 @@
|
|||
#include "qemu/cutils.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "target/arm/cpu-qom.h"
|
||||
#include "trace.h"
|
||||
|
||||
static inline void omap_log_badwidth(const char *funcname, hwaddr addr, int sz)
|
||||
{
|
||||
|
@ -1731,7 +1732,7 @@ static void omap_clkm_write(void *opaque, hwaddr addr,
|
|||
case 0x18: /* ARM_SYSST */
|
||||
if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) {
|
||||
s->clkm.clocking_scheme = (value >> 11) & 7;
|
||||
printf("%s: clocking scheme set to %s\n", __func__,
|
||||
trace_omap1_pwl_clocking_scheme(
|
||||
clkschemename[s->clkm.clocking_scheme]);
|
||||
}
|
||||
s->clkm.cold_start &= value & 0x3f;
|
||||
|
@ -2335,7 +2336,7 @@ static void omap_pwl_update(struct omap_pwl_s *s)
|
|||
|
||||
if (output != s->output) {
|
||||
s->output = output;
|
||||
printf("%s: Backlight now at %i/256\n", __func__, output);
|
||||
trace_omap1_pwl_backlight(output);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2470,8 +2471,8 @@ static void omap_pwt_write(void *opaque, hwaddr addr,
|
|||
break;
|
||||
case 0x04: /* VRC */
|
||||
if ((value ^ s->vrc) & 1) {
|
||||
if (value & 1)
|
||||
printf("%s: %iHz buzz on\n", __func__, (int)
|
||||
if (value & 1) {
|
||||
trace_omap1_pwt_buzz(
|
||||
/* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */
|
||||
((omap_clk_getrate(s->clk) >> 3) /
|
||||
/* Pre-multiplexer divider */
|
||||
|
@ -2487,8 +2488,9 @@ static void omap_pwt_write(void *opaque, hwaddr addr,
|
|||
/* 80/127 divider */
|
||||
((value & (1 << 5)) ? 80 : 127) /
|
||||
(107 * 55 * 63 * 127)));
|
||||
else
|
||||
printf("%s: silence!\n", __func__);
|
||||
} else {
|
||||
trace_omap1_pwt_silence();
|
||||
}
|
||||
}
|
||||
s->vrc = value & 0x7f;
|
||||
break;
|
||||
|
@ -2559,8 +2561,9 @@ static void omap_rtc_interrupts_update(struct omap_rtc_s *s)
|
|||
static void omap_rtc_alarm_update(struct omap_rtc_s *s)
|
||||
{
|
||||
s->alarm_ti = mktimegm(&s->alarm_tm);
|
||||
if (s->alarm_ti == -1)
|
||||
printf("%s: conversion failed\n", __func__);
|
||||
if (s->alarm_ti == -1) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: conversion failed\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t omap_rtc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
|
@ -2659,25 +2662,16 @@ static void omap_rtc_write(void *opaque, hwaddr addr,
|
|||
|
||||
switch (offset) {
|
||||
case 0x00: /* SECONDS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC SEC_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->ti -= s->current_tm.tm_sec;
|
||||
s->ti += from_bcd(value);
|
||||
return;
|
||||
|
||||
case 0x04: /* MINUTES_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC MIN_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->ti -= s->current_tm.tm_min * 60;
|
||||
s->ti += from_bcd(value) * 60;
|
||||
return;
|
||||
|
||||
case 0x08: /* HOURS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC HRS_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->ti -= s->current_tm.tm_hour * 3600;
|
||||
if (s->pm_am) {
|
||||
s->ti += (from_bcd(value & 0x3f) & 12) * 3600;
|
||||
|
@ -2687,17 +2681,11 @@ static void omap_rtc_write(void *opaque, hwaddr addr,
|
|||
return;
|
||||
|
||||
case 0x0c: /* DAYS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC DAY_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->ti -= s->current_tm.tm_mday * 86400;
|
||||
s->ti += from_bcd(value) * 86400;
|
||||
return;
|
||||
|
||||
case 0x10: /* MONTHS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC MTH_REG <-- %02x\n", value);
|
||||
#endif
|
||||
memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
|
||||
new_tm.tm_mon = from_bcd(value);
|
||||
ti[0] = mktimegm(&s->current_tm);
|
||||
|
@ -2714,9 +2702,6 @@ static void omap_rtc_write(void *opaque, hwaddr addr,
|
|||
return;
|
||||
|
||||
case 0x14: /* YEARS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC YRS_REG <-- %02x\n", value);
|
||||
#endif
|
||||
memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
|
||||
new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100);
|
||||
ti[0] = mktimegm(&s->current_tm);
|
||||
|
@ -2736,25 +2721,16 @@ static void omap_rtc_write(void *opaque, hwaddr addr,
|
|||
return; /* Ignored */
|
||||
|
||||
case 0x20: /* ALARM_SECONDS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("ALM SEC_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->alarm_tm.tm_sec = from_bcd(value);
|
||||
omap_rtc_alarm_update(s);
|
||||
return;
|
||||
|
||||
case 0x24: /* ALARM_MINUTES_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("ALM MIN_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->alarm_tm.tm_min = from_bcd(value);
|
||||
omap_rtc_alarm_update(s);
|
||||
return;
|
||||
|
||||
case 0x28: /* ALARM_HOURS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("ALM HRS_REG <-- %02x\n", value);
|
||||
#endif
|
||||
if (s->pm_am)
|
||||
s->alarm_tm.tm_hour =
|
||||
((from_bcd(value & 0x3f)) % 12) +
|
||||
|
@ -2765,33 +2741,21 @@ static void omap_rtc_write(void *opaque, hwaddr addr,
|
|||
return;
|
||||
|
||||
case 0x2c: /* ALARM_DAYS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("ALM DAY_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->alarm_tm.tm_mday = from_bcd(value);
|
||||
omap_rtc_alarm_update(s);
|
||||
return;
|
||||
|
||||
case 0x30: /* ALARM_MONTHS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("ALM MON_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->alarm_tm.tm_mon = from_bcd(value);
|
||||
omap_rtc_alarm_update(s);
|
||||
return;
|
||||
|
||||
case 0x34: /* ALARM_YEARS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("ALM YRS_REG <-- %02x\n", value);
|
||||
#endif
|
||||
s->alarm_tm.tm_year = from_bcd(value);
|
||||
omap_rtc_alarm_update(s);
|
||||
return;
|
||||
|
||||
case 0x40: /* RTC_CTRL_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC CONTROL <-- %02x\n", value);
|
||||
#endif
|
||||
s->pm_am = (value >> 3) & 1;
|
||||
s->auto_comp = (value >> 2) & 1;
|
||||
s->round = (value >> 1) & 1;
|
||||
|
@ -2801,32 +2765,20 @@ static void omap_rtc_write(void *opaque, hwaddr addr,
|
|||
return;
|
||||
|
||||
case 0x44: /* RTC_STATUS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC STATUSL <-- %02x\n", value);
|
||||
#endif
|
||||
s->status &= ~((value & 0xc0) ^ 0x80);
|
||||
omap_rtc_interrupts_update(s);
|
||||
return;
|
||||
|
||||
case 0x48: /* RTC_INTERRUPTS_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC INTRS <-- %02x\n", value);
|
||||
#endif
|
||||
s->interrupts = value;
|
||||
return;
|
||||
|
||||
case 0x4c: /* RTC_COMP_LSB_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC COMPLSB <-- %02x\n", value);
|
||||
#endif
|
||||
s->comp_reg &= 0xff00;
|
||||
s->comp_reg |= 0x00ff & value;
|
||||
return;
|
||||
|
||||
case 0x50: /* RTC_COMP_MSB_REG */
|
||||
#ifdef ALMDEBUG
|
||||
printf("RTC COMPMSB <-- %02x\n", value);
|
||||
#endif
|
||||
s->comp_reg &= 0x00ff;
|
||||
s->comp_reg |= 0xff00 & (value << 8);
|
||||
return;
|
||||
|
@ -3024,8 +2976,9 @@ static void omap_mcbsp_source_tick(void *opaque)
|
|||
|
||||
if (!s->rx_rate)
|
||||
return;
|
||||
if (s->rx_req)
|
||||
printf("%s: Rx FIFO overrun\n", __func__);
|
||||
if (s->rx_req) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO overrun\n", __func__);
|
||||
}
|
||||
|
||||
s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7];
|
||||
|
||||
|
@ -3070,8 +3023,9 @@ static void omap_mcbsp_sink_tick(void *opaque)
|
|||
|
||||
if (!s->tx_rate)
|
||||
return;
|
||||
if (s->tx_req)
|
||||
printf("%s: Tx FIFO underrun\n", __func__);
|
||||
if (s->tx_req) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO underrun\n", __func__);
|
||||
}
|
||||
|
||||
s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7];
|
||||
|
||||
|
@ -3173,7 +3127,7 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr,
|
|||
/* Fall through. */
|
||||
case 0x02: /* DRR1 */
|
||||
if (s->rx_req < 2) {
|
||||
printf("%s: Rx FIFO underrun\n", __func__);
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO underrun\n", __func__);
|
||||
omap_mcbsp_rx_done(s);
|
||||
} else {
|
||||
s->tx_req -= 2;
|
||||
|
@ -3278,8 +3232,9 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr,
|
|||
}
|
||||
if (s->tx_req < 2)
|
||||
omap_mcbsp_tx_done(s);
|
||||
} else
|
||||
printf("%s: Tx FIFO overrun\n", __func__);
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO overrun\n", __func__);
|
||||
}
|
||||
return;
|
||||
|
||||
case 0x08: /* SPCR2 */
|
||||
|
@ -3293,8 +3248,11 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr,
|
|||
case 0x0a: /* SPCR1 */
|
||||
s->spcr[0] &= 0x0006;
|
||||
s->spcr[0] |= 0xf8f9 & value;
|
||||
if (value & (1 << 15)) /* DLB */
|
||||
printf("%s: Digital Loopback mode enable attempt\n", __func__);
|
||||
if (value & (1 << 15)) { /* DLB */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Digital Loopback mode enable attempt\n",
|
||||
__func__);
|
||||
}
|
||||
if (~value & 1) { /* RRST */
|
||||
s->spcr[0] &= ~6;
|
||||
s->rx_req = 0;
|
||||
|
@ -3325,13 +3283,19 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr,
|
|||
return;
|
||||
case 0x18: /* MCR2 */
|
||||
s->mcr[1] = value & 0x03e3;
|
||||
if (value & 3) /* XMCM */
|
||||
printf("%s: Tx channel selection mode enable attempt\n", __func__);
|
||||
if (value & 3) { /* XMCM */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Tx channel selection mode enable attempt\n",
|
||||
__func__);
|
||||
}
|
||||
return;
|
||||
case 0x1a: /* MCR1 */
|
||||
s->mcr[0] = value & 0x03e1;
|
||||
if (value & 1) /* RMCM */
|
||||
printf("%s: Rx channel selection mode enable attempt\n", __func__);
|
||||
if (value & 1) { /* RMCM */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Rx channel selection mode enable attempt\n",
|
||||
__func__);
|
||||
}
|
||||
return;
|
||||
case 0x1c: /* RCERA */
|
||||
s->rcer[0] = value & 0xffff;
|
||||
|
@ -3412,8 +3376,9 @@ static void omap_mcbsp_writew(void *opaque, hwaddr addr,
|
|||
}
|
||||
if (s->tx_req < 4)
|
||||
omap_mcbsp_tx_done(s);
|
||||
} else
|
||||
printf("%s: Tx FIFO overrun\n", __func__);
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Tx FIFO overrun\n", __func__);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3531,7 +3496,7 @@ static void omap_lpg_tick(void *opaque)
|
|||
timer_mod(s->tm, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + s->on);
|
||||
|
||||
s->cycle = !s->cycle;
|
||||
printf("%s: LED is %s\n", __func__, s->cycle ? "on" : "off");
|
||||
trace_omap1_lpg_led(s->cycle ? "on" : "off");
|
||||
}
|
||||
|
||||
static void omap_lpg_update(struct omap_lpg_s *s)
|
||||
|
@ -3551,11 +3516,11 @@ static void omap_lpg_update(struct omap_lpg_s *s)
|
|||
}
|
||||
|
||||
timer_del(s->tm);
|
||||
if (on == period && s->on < s->period)
|
||||
printf("%s: LED is on\n", __func__);
|
||||
else if (on == 0 && s->on)
|
||||
printf("%s: LED is off\n", __func__);
|
||||
else if (on && (on != s->on || period != s->period)) {
|
||||
if (on == period && s->on < s->period) {
|
||||
trace_omap1_lpg_led("on");
|
||||
} else if (on == 0 && s->on) {
|
||||
trace_omap1_lpg_led("off");
|
||||
} else if (on && (on != s->on || period != s->period)) {
|
||||
s->cycle = 0;
|
||||
s->on = on;
|
||||
s->period = period;
|
||||
|
|
|
@ -76,10 +76,6 @@ static uint64_t static_read(void *opaque, hwaddr offset,
|
|||
static void static_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
#ifdef SPY
|
||||
printf("%s: value %" PRIx64 " %u bytes written at 0x%x\n",
|
||||
__func__, value, size, (int)offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const MemoryRegionOps static_ops = {
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
|
||||
# omap1.c
|
||||
omap1_pwl_clocking_scheme(const char *scheme) "omap1 CLKM: clocking scheme set to %s"
|
||||
omap1_pwl_backlight(int output) "omap1 PWL: backlight now at %d/256"
|
||||
omap1_pwt_buzz(int freq) "omap1 PWT: %dHz buzz on"
|
||||
omap1_pwt_silence(void) "omap1 PWT: buzzer silenced"
|
||||
omap1_lpg_led(const char *onoff) "omap1 LPG: LED is %s"
|
||||
|
||||
# virt-acpi-build.c
|
||||
virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out."
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "qom/object.h"
|
||||
#include "audio/audio.h"
|
||||
#include "target/arm/cpu-qom.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
#define VERSATILE_FLASH_ADDR 0x34000000
|
||||
#define VERSATILE_FLASH_SIZE (64 * 1024 * 1024)
|
||||
|
@ -110,7 +111,8 @@ static uint64_t vpb_sic_read(void *opaque, hwaddr offset,
|
|||
case 8: /* PICENABLE */
|
||||
return s->pic_enable;
|
||||
default:
|
||||
printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"vpb_sic_read: Bad register offset 0x%x\n", (int)offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +146,8 @@ static void vpb_sic_write(void *opaque, hwaddr offset,
|
|||
vpb_sic_update_pic(s);
|
||||
break;
|
||||
default:
|
||||
printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset);
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"vpb_sic_write: Bad register offset 0x%x\n", (int)offset);
|
||||
return;
|
||||
}
|
||||
vpb_sic_update(s);
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/qapi-commands-migration.h"
|
||||
#include "hw/boards.h"
|
||||
#include "system/system.h"
|
||||
#include "system/xen.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/xen/xen-hvm-common.h"
|
||||
#include "hw/xen/arch_hvm.h"
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq,
|
|||
req->mr_next = NULL;
|
||||
}
|
||||
|
||||
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
|
||||
void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
|
||||
{
|
||||
VirtIOBlock *s = req->dev;
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||
|
@ -961,9 +961,19 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
|
|||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
/*
|
||||
* Give subclasses a chance to handle unknown requests. This way the
|
||||
* class lookup is not in the hot path.
|
||||
*/
|
||||
VirtIOBlkClass *vbk = VIRTIO_BLK_GET_CLASS(s);
|
||||
if (!vbk->handle_unknown_request ||
|
||||
!vbk->handle_unknown_request(req, mrb, type)) {
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
|
||||
g_free(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2029,6 +2039,7 @@ static const TypeInfo virtio_blk_info = {
|
|||
.instance_size = sizeof(VirtIOBlock),
|
||||
.instance_init = virtio_blk_instance_init,
|
||||
.class_init = virtio_blk_class_init,
|
||||
.class_size = sizeof(VirtIOBlkClass),
|
||||
};
|
||||
|
||||
static void virtio_register_types(void)
|
||||
|
|
|
@ -221,7 +221,7 @@ static int bcm2835_aux_can_receive(void *opaque)
|
|||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
|
||||
return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
|
||||
return BCM2835_AUX_RX_FIFO_LEN - s->read_count;
|
||||
}
|
||||
|
||||
static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
|
||||
|
@ -243,7 +243,9 @@ static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
|
|||
|
||||
static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
bcm2835_aux_put_fifo(opaque, *buf);
|
||||
for (int i = 0; i < size; i++) {
|
||||
bcm2835_aux_put_fifo(opaque, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_aux_ops = {
|
||||
|
|
|
@ -386,7 +386,8 @@ static void imx_serial_write(void *opaque, hwaddr offset,
|
|||
static int imx_can_receive(void *opaque)
|
||||
{
|
||||
IMXSerialState *s = (IMXSerialState *)opaque;
|
||||
return s->ucr2 & UCR2_RXEN && fifo32_num_used(&s->rx_fifo) < FIFO_SIZE;
|
||||
|
||||
return s->ucr2 & UCR2_RXEN ? fifo32_num_free(&s->rx_fifo) : 0;
|
||||
}
|
||||
|
||||
static void imx_put_data(void *opaque, uint32_t value)
|
||||
|
@ -417,7 +418,10 @@ static void imx_receive(void *opaque, const uint8_t *buf, int size)
|
|||
IMXSerialState *s = (IMXSerialState *)opaque;
|
||||
|
||||
s->usr2 |= USR2_WAKE;
|
||||
imx_put_data(opaque, *buf);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
imx_put_data(opaque, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_event(void *opaque, QEMUChrEvent event)
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "chardev/char-fe.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define FIFO_DEPTH 4
|
||||
|
||||
struct mcf_uart_state {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
|
@ -27,7 +29,7 @@ struct mcf_uart_state {
|
|||
uint8_t imr;
|
||||
uint8_t bg1;
|
||||
uint8_t bg2;
|
||||
uint8_t fifo[4];
|
||||
uint8_t fifo[FIFO_DEPTH];
|
||||
uint8_t tb;
|
||||
int current_mr;
|
||||
int fifo_len;
|
||||
|
@ -247,14 +249,16 @@ static void mcf_uart_reset(DeviceState *dev)
|
|||
static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data)
|
||||
{
|
||||
/* Break events overwrite the last byte if the fifo is full. */
|
||||
if (s->fifo_len == 4)
|
||||
if (s->fifo_len == FIFO_DEPTH) {
|
||||
s->fifo_len--;
|
||||
}
|
||||
|
||||
s->fifo[s->fifo_len] = data;
|
||||
s->fifo_len++;
|
||||
s->sr |= MCF_UART_RxRDY;
|
||||
if (s->fifo_len == 4)
|
||||
if (s->fifo_len == FIFO_DEPTH) {
|
||||
s->sr |= MCF_UART_FFULL;
|
||||
}
|
||||
|
||||
mcf_uart_update(s);
|
||||
}
|
||||
|
@ -277,14 +281,16 @@ static int mcf_uart_can_receive(void *opaque)
|
|||
{
|
||||
mcf_uart_state *s = (mcf_uart_state *)opaque;
|
||||
|
||||
return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0;
|
||||
return s->rx_enabled ? FIFO_DEPTH - s->fifo_len : 0;
|
||||
}
|
||||
|
||||
static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
mcf_uart_state *s = (mcf_uart_state *)opaque;
|
||||
|
||||
mcf_uart_push_byte(s, buf[0]);
|
||||
for (int i = 0; i < size; i++) {
|
||||
mcf_uart_push_byte(s, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mcf_uart_ops = {
|
||||
|
|
|
@ -85,6 +85,7 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
|
|||
#define CR_OUT1 (1 << 12)
|
||||
#define CR_RTS (1 << 11)
|
||||
#define CR_DTR (1 << 10)
|
||||
#define CR_RXE (1 << 9)
|
||||
#define CR_TXE (1 << 8)
|
||||
#define CR_LBE (1 << 7)
|
||||
#define CR_UARTEN (1 << 0)
|
||||
|
@ -184,7 +185,7 @@ static void pl011_fifo_rx_put(void *opaque, uint32_t value)
|
|||
s->read_fifo[slot] = value;
|
||||
s->read_count++;
|
||||
s->flags &= ~PL011_FLAG_RXFE;
|
||||
trace_pl011_fifo_rx_put(value, s->read_count);
|
||||
trace_pl011_fifo_rx_put(value, s->read_count, pipe_depth);
|
||||
if (s->read_count == pipe_depth) {
|
||||
trace_pl011_fifo_rx_full();
|
||||
s->flags |= PL011_FLAG_RXFF;
|
||||
|
@ -247,12 +248,13 @@ static void pl011_write_txdata(PL011State *s, uint8_t data)
|
|||
static uint32_t pl011_read_rxdata(PL011State *s)
|
||||
{
|
||||
uint32_t c;
|
||||
unsigned fifo_depth = pl011_get_fifo_depth(s);
|
||||
|
||||
s->flags &= ~PL011_FLAG_RXFF;
|
||||
c = s->read_fifo[s->read_pos];
|
||||
if (s->read_count > 0) {
|
||||
s->read_count--;
|
||||
s->read_pos = (s->read_pos + 1) & (pl011_get_fifo_depth(s) - 1);
|
||||
s->read_pos = (s->read_pos + 1) & (fifo_depth - 1);
|
||||
}
|
||||
if (s->read_count == 0) {
|
||||
s->flags |= PL011_FLAG_RXFE;
|
||||
|
@ -260,7 +262,7 @@ static uint32_t pl011_read_rxdata(PL011State *s)
|
|||
if (s->read_count == s->read_trigger - 1) {
|
||||
s->int_level &= ~INT_RX;
|
||||
}
|
||||
trace_pl011_read_fifo(s->read_count);
|
||||
trace_pl011_read_fifo(s->read_count, fifo_depth);
|
||||
s->rsr = c >> 8;
|
||||
pl011_update(s);
|
||||
qemu_chr_fe_accept_input(&s->chr);
|
||||
|
@ -485,15 +487,25 @@ static void pl011_write(void *opaque, hwaddr offset,
|
|||
static int pl011_can_receive(void *opaque)
|
||||
{
|
||||
PL011State *s = (PL011State *)opaque;
|
||||
int r;
|
||||
unsigned fifo_depth = pl011_get_fifo_depth(s);
|
||||
unsigned fifo_available = fifo_depth - s->read_count;
|
||||
|
||||
r = s->read_count < pl011_get_fifo_depth(s);
|
||||
trace_pl011_can_receive(s->lcr, s->read_count, r);
|
||||
return r;
|
||||
if (!(s->cr & CR_UARTEN)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"PL011 receiving data on disabled UART\n");
|
||||
}
|
||||
if (!(s->cr & CR_RXE)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"PL011 receiving data on disabled RX UART\n");
|
||||
}
|
||||
trace_pl011_can_receive(s->lcr, s->read_count, fifo_depth, fifo_available);
|
||||
|
||||
return fifo_available;
|
||||
}
|
||||
|
||||
static void pl011_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
trace_pl011_receive(size);
|
||||
/*
|
||||
* In loopback mode, the RX input signal is internally disconnected
|
||||
* from the entire receiving logics; thus, all inputs are ignored,
|
||||
|
@ -503,7 +515,9 @@ static void pl011_receive(void *opaque, const uint8_t *buf, int size)
|
|||
return;
|
||||
}
|
||||
|
||||
pl011_fifo_rx_put(opaque, *buf);
|
||||
for (int i = 0; i < size; i++) {
|
||||
pl011_fifo_rx_put(opaque, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void pl011_event(void *opaque, QEMUChrEvent event)
|
||||
|
|
|
@ -320,7 +320,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs,
|
|||
|
||||
static int sh_serial_can_receive(SHSerialState *s)
|
||||
{
|
||||
return s->scr & (1 << 4);
|
||||
return s->scr & (1 << 4) ? SH_RX_FIFO_LENGTH - s->rx_head : 0;
|
||||
}
|
||||
|
||||
static void sh_serial_receive_break(SHSerialState *s)
|
||||
|
@ -353,7 +353,6 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
|
|||
if (s->feat & SH_SERIAL_FEAT_SCIF) {
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
|
||||
s->rx_fifo[s->rx_head++] = buf[i];
|
||||
if (s->rx_head == SH_RX_FIFO_LENGTH) {
|
||||
s->rx_head = 0;
|
||||
|
@ -370,7 +369,6 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
|
|||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->rx_fifo[0] = buf[0];
|
||||
}
|
||||
|
|
|
@ -251,6 +251,23 @@ static int sifive_uart_be_change(void *opaque)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_uart_reset_enter(Object *obj, ResetType type)
|
||||
{
|
||||
SiFiveUARTState *s = SIFIVE_UART(obj);
|
||||
|
||||
s->txfifo = 0;
|
||||
s->ie = 0;
|
||||
s->ip = 0;
|
||||
s->txctrl = 0;
|
||||
s->rxctrl = 0;
|
||||
s->div = 0;
|
||||
|
||||
s->rx_fifo_len = 0;
|
||||
|
||||
memset(s->rx_fifo, 0, SIFIVE_UART_RX_FIFO_SIZE);
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
}
|
||||
|
||||
static const Property sifive_uart_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", SiFiveUARTState, chr),
|
||||
};
|
||||
|
@ -270,30 +287,24 @@ static void sifive_uart_realize(DeviceState *dev, Error **errp)
|
|||
{
|
||||
SiFiveUARTState *s = SIFIVE_UART(dev);
|
||||
|
||||
fifo8_create(&s->tx_fifo, SIFIVE_UART_TX_FIFO_SIZE);
|
||||
|
||||
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
fifo_trigger_update, s);
|
||||
|
||||
if (qemu_chr_fe_backend_connected(&s->chr)) {
|
||||
qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx,
|
||||
sifive_uart_event, sifive_uart_be_change, s,
|
||||
NULL, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void sifive_uart_reset_enter(Object *obj, ResetType type)
|
||||
static void sifive_uart_unrealize(DeviceState *dev)
|
||||
{
|
||||
SiFiveUARTState *s = SIFIVE_UART(obj);
|
||||
SiFiveUARTState *s = SIFIVE_UART(dev);
|
||||
|
||||
s->txfifo = 0;
|
||||
s->ie = 0;
|
||||
s->ip = 0;
|
||||
s->txctrl = 0;
|
||||
s->rxctrl = 0;
|
||||
s->div = 0;
|
||||
|
||||
s->rx_fifo_len = 0;
|
||||
|
||||
memset(s->rx_fifo, 0, SIFIVE_UART_RX_FIFO_SIZE);
|
||||
fifo8_create(&s->tx_fifo, SIFIVE_UART_TX_FIFO_SIZE);
|
||||
fifo8_destroy(&s->tx_fifo);
|
||||
}
|
||||
|
||||
static void sifive_uart_reset_hold(Object *obj, ResetType type)
|
||||
|
@ -329,6 +340,7 @@ static void sifive_uart_class_init(ObjectClass *oc, void *data)
|
|||
ResettableClass *rc = RESETTABLE_CLASS(oc);
|
||||
|
||||
dc->realize = sifive_uart_realize;
|
||||
dc->unrealize = sifive_uart_unrealize;
|
||||
dc->vmsd = &vmstate_sifive_uart;
|
||||
rc->phases.enter = sifive_uart_reset_enter;
|
||||
rc->phases.hold = sifive_uart_reset_hold;
|
||||
|
|
|
@ -60,12 +60,13 @@ imx_serial_put_data(const char *chrname, uint32_t value) "%s: 0x%" PRIx32
|
|||
# pl011.c
|
||||
pl011_irq_state(int level) "irq state %d"
|
||||
pl011_read(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s"
|
||||
pl011_read_fifo(int read_count) "FIFO read, read_count now %d"
|
||||
pl011_read_fifo(unsigned rx_fifo_used, size_t rx_fifo_depth) "RX FIFO read, used %u/%zu"
|
||||
pl011_write(uint32_t addr, uint32_t value, const char *regname) "addr 0x%03x value 0x%08x reg %s"
|
||||
pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d returning %d"
|
||||
pl011_fifo_rx_put(uint32_t c, int read_count) "new char 0x%02x read_count now %d"
|
||||
pl011_can_receive(uint32_t lcr, unsigned rx_fifo_used, size_t rx_fifo_depth, unsigned rx_fifo_available) "LCR 0x%02x, RX FIFO used %u/%zu, can_receive %u chars"
|
||||
pl011_fifo_rx_put(uint32_t c, unsigned read_count, size_t rx_fifo_depth) "RX FIFO push char [0x%02x] %d/%zu depth used"
|
||||
pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set"
|
||||
pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")"
|
||||
pl011_receive(int size) "recv %d chars"
|
||||
|
||||
# cmsdk-apb-uart.c
|
||||
cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
|
||||
|
|
|
@ -1294,3 +1294,12 @@ const PropertyInfo qdev_prop_endian_mode = {
|
|||
.set = qdev_propinfo_set_enum,
|
||||
.set_default_value = qdev_propinfo_set_default_value_enum,
|
||||
};
|
||||
|
||||
const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = {
|
||||
.name = "VMAppleVirtioBlkVariant",
|
||||
.description = "unspecified/root/aux",
|
||||
.enum_table = &VMAppleVirtioBlkVariant_lookup,
|
||||
.get = qdev_propinfo_get_enum,
|
||||
.set = qdev_propinfo_set_enum,
|
||||
.set_default_value = qdev_propinfo_set_default_value_enum,
|
||||
};
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-migration.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/i386/apic-msidef.h"
|
||||
|
@ -24,6 +26,10 @@
|
|||
#include "hw/xen/arch_hvm.h"
|
||||
#include <xen/hvm/e820.h>
|
||||
#include "exec/target_page.h"
|
||||
#include "target/i386/cpu.h"
|
||||
#include "system/runstate.h"
|
||||
#include "system/xen-mapcache.h"
|
||||
#include "system/xen.h"
|
||||
|
||||
static MemoryRegion ram_640k, ram_lo, ram_hi;
|
||||
static MemoryRegion *framebuffer;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "hw/xen/arch_hvm.h"
|
||||
#include <xen/hvm/hvm_info_table.h>
|
||||
#include "hw/xen/xen-pvh-common.h"
|
||||
#include "target/i386/cpu.h"
|
||||
|
||||
#define TYPE_XEN_PVH_X86 MACHINE_TYPE_NAME("xenpvh")
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(XenPVHx86State, XEN_PVH_X86)
|
||||
|
|
|
@ -23,13 +23,13 @@ config APIC
|
|||
|
||||
config ARM_GIC
|
||||
bool
|
||||
select ARM_GICV3_TCG if TCG
|
||||
select ARM_GICV3 if TCG
|
||||
select ARM_GIC_KVM if KVM
|
||||
select MSI_NONBROKEN
|
||||
|
||||
config ARM_GICV3_TCG
|
||||
config ARM_GICV3
|
||||
bool
|
||||
depends on ARM_GIC && TCG
|
||||
depends on ARM_GIC
|
||||
|
||||
config ARM_GIC_KVM
|
||||
bool
|
||||
|
|
|
@ -6,7 +6,7 @@ system_ss.add(when: 'CONFIG_ARM_GIC', if_true: files(
|
|||
'arm_gicv3_common.c',
|
||||
'arm_gicv3_its_common.c',
|
||||
))
|
||||
system_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files(
|
||||
system_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files(
|
||||
'arm_gicv3.c',
|
||||
'arm_gicv3_dist.c',
|
||||
'arm_gicv3_its.c',
|
||||
|
@ -39,7 +39,7 @@ endif
|
|||
|
||||
specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c'))
|
||||
specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c'))
|
||||
specific_ss.add(when: 'CONFIG_ARM_GICV3_TCG', if_true: files('arm_gicv3_cpuif.c'))
|
||||
specific_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c'))
|
||||
specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c'))
|
||||
specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c'))
|
||||
specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c'))
|
||||
|
|
|
@ -40,6 +40,7 @@ subdir('ufs')
|
|||
subdir('usb')
|
||||
subdir('vfio')
|
||||
subdir('virtio')
|
||||
subdir('vmapple')
|
||||
subdir('watchdog')
|
||||
subdir('xen')
|
||||
subdir('xenpv')
|
||||
|
|
|
@ -148,6 +148,10 @@ config PVPANIC_ISA
|
|||
depends on ISA_BUS
|
||||
select PVPANIC_COMMON
|
||||
|
||||
config PVPANIC_MMIO
|
||||
bool
|
||||
select PVPANIC_COMMON
|
||||
|
||||
config AUX
|
||||
bool
|
||||
select I2C
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
#include "qemu/module.h"
|
||||
#include "trace.h"
|
||||
|
||||
enum MacioGPIORegisterBits {
|
||||
OUT_DATA = 1,
|
||||
IN_DATA = 2,
|
||||
OUT_ENABLE = 4,
|
||||
};
|
||||
|
||||
void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state)
|
||||
{
|
||||
|
@ -41,14 +46,14 @@ void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state)
|
|||
|
||||
trace_macio_set_gpio(gpio, state);
|
||||
|
||||
if (s->gpio_regs[gpio] & 4) {
|
||||
if (s->gpio_regs[gpio] & OUT_ENABLE) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPIO: Setting GPIO %d while it's an output\n", gpio);
|
||||
}
|
||||
|
||||
new_reg = s->gpio_regs[gpio] & ~2;
|
||||
new_reg = s->gpio_regs[gpio] & ~IN_DATA;
|
||||
if (state) {
|
||||
new_reg |= 2;
|
||||
new_reg |= IN_DATA;
|
||||
}
|
||||
|
||||
if (new_reg == s->gpio_regs[gpio]) {
|
||||
|
@ -107,12 +112,12 @@ static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value,
|
|||
|
||||
addr -= 8;
|
||||
if (addr < 36) {
|
||||
value &= ~2;
|
||||
value &= ~IN_DATA;
|
||||
|
||||
if (value & 4) {
|
||||
ibit = (value & 1) << 1;
|
||||
if (value & OUT_ENABLE) {
|
||||
ibit = (value & OUT_DATA) << 1;
|
||||
} else {
|
||||
ibit = s->gpio_regs[addr] & 2;
|
||||
ibit = s->gpio_regs[addr] & IN_DATA;
|
||||
}
|
||||
|
||||
s->gpio_regs[addr] = value | ibit;
|
||||
|
@ -135,7 +140,7 @@ static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
|||
}
|
||||
}
|
||||
|
||||
trace_macio_gpio_write(addr, val);
|
||||
trace_macio_gpio_read(addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx6
|
|||
macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d"
|
||||
macio_gpio_irq_assert(int gpio) "asserting GPIO %d"
|
||||
macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d"
|
||||
macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64
|
||||
macio_gpio_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64
|
||||
macio_gpio_read(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64
|
||||
|
||||
# pmu.c
|
||||
pmu_adb_poll(int olen) "ADB autopoll, olen=%d"
|
||||
|
|
|
@ -126,6 +126,7 @@ system_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c'))
|
|||
|
||||
system_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c'))
|
||||
system_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c'))
|
||||
system_ss.add(when: 'CONFIG_PVPANIC_MMIO', if_true: files('pvpanic-mmio.c'))
|
||||
system_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c'))
|
||||
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
|
||||
'aspeed_hace.c',
|
||||
|
|
60
hw/misc/pvpanic-mmio.c
Normal file
60
hw/misc/pvpanic-mmio.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* QEMU simulated pvpanic device (MMIO frontend)
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/misc/pvpanic.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "standard-headers/misc/pvpanic.h"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PVPanicMMIOState, PVPANIC_MMIO_DEVICE)
|
||||
|
||||
#define PVPANIC_MMIO_SIZE 0x2
|
||||
|
||||
struct PVPanicMMIOState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
PVPanicState pvpanic;
|
||||
};
|
||||
|
||||
static void pvpanic_mmio_initfn(Object *obj)
|
||||
{
|
||||
PVPanicMMIOState *s = PVPANIC_MMIO_DEVICE(obj);
|
||||
|
||||
pvpanic_setup_io(&s->pvpanic, DEVICE(s), PVPANIC_MMIO_SIZE);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pvpanic.mr);
|
||||
}
|
||||
|
||||
static const Property pvpanic_mmio_properties[] = {
|
||||
DEFINE_PROP_UINT8("events", PVPanicMMIOState, pvpanic.events,
|
||||
PVPANIC_PANICKED | PVPANIC_CRASH_LOADED),
|
||||
};
|
||||
|
||||
static void pvpanic_mmio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, pvpanic_mmio_properties);
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo pvpanic_mmio_info = {
|
||||
.name = TYPE_PVPANIC_MMIO_DEVICE,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PVPanicMMIOState),
|
||||
.instance_init = pvpanic_mmio_initfn,
|
||||
.class_init = pvpanic_mmio_class_init,
|
||||
};
|
||||
|
||||
static void pvpanic_register_types(void)
|
||||
{
|
||||
type_register_static(&pvpanic_mmio_info);
|
||||
}
|
||||
|
||||
type_init(pvpanic_register_types)
|
|
@ -423,8 +423,10 @@ static void etsec_class_init(ObjectClass *klass, void *data)
|
|||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = etsec_realize;
|
||||
dc->desc = "Freescale Enhanced Three-Speed Ethernet Controller";
|
||||
device_class_set_legacy_reset(dc, etsec_reset);
|
||||
device_class_set_props(dc, etsec_properties);
|
||||
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo etsec_types[] = {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/nvram/eeprom_at24c.h"
|
||||
|
@ -26,13 +27,8 @@
|
|||
#define DPRINTK(FMT, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define ERR(FMT, ...) fprintf(stderr, TYPE_AT24C_EE " : " FMT, \
|
||||
## __VA_ARGS__)
|
||||
|
||||
#define TYPE_AT24C_EE "at24c-eeprom"
|
||||
typedef struct EEPROMState EEPROMState;
|
||||
DECLARE_INSTANCE_CHECKER(EEPROMState, AT24C_EE,
|
||||
TYPE_AT24C_EE)
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(EEPROMState, AT24C_EE)
|
||||
|
||||
struct EEPROMState {
|
||||
I2CSlave parent_obj;
|
||||
|
@ -77,8 +73,7 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
|
|||
if (ee->blk && ee->changed) {
|
||||
int ret = blk_pwrite(ee->blk, 0, ee->rsize, ee->mem, 0);
|
||||
if (ret < 0) {
|
||||
ERR(TYPE_AT24C_EE
|
||||
" : failed to write backing file\n");
|
||||
error_report("%s: failed to write backing file", __func__);
|
||||
}
|
||||
DPRINTK("Wrote to backing file\n");
|
||||
}
|
||||
|
@ -195,20 +190,18 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
|
||||
ee->mem = g_malloc0(ee->rsize);
|
||||
memset(ee->mem, 0, ee->rsize);
|
||||
|
||||
if (ee->init_rom) {
|
||||
memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize));
|
||||
}
|
||||
|
||||
if (ee->blk) {
|
||||
int ret = blk_pread(ee->blk, 0, ee->rsize, ee->mem, 0);
|
||||
|
||||
if (ret < 0) {
|
||||
ERR(TYPE_AT24C_EE
|
||||
" : Failed initial sync with backing file\n");
|
||||
error_setg(errp, "%s: Failed initial sync with backing file",
|
||||
TYPE_AT24C_EE);
|
||||
return;
|
||||
}
|
||||
DPRINTK("Reset read backing file\n");
|
||||
} else if (ee->init_rom) {
|
||||
memcpy(ee->mem, ee->init_rom, MIN(ee->init_rom_size, ee->rsize));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -299,8 +299,10 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr,
|
|||
if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) {
|
||||
if (kvm_enabled()) {
|
||||
kvmppc_icbi_range(cpu, pdst, len);
|
||||
} else {
|
||||
} else if (tcg_enabled()) {
|
||||
tb_flush(CPU(cpu));
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
78
hw/ufs/ufs.c
78
hw/ufs/ufs.c
|
@ -34,6 +34,11 @@
|
|||
#define UFS_MAX_NUTMRS 8
|
||||
#define UFS_MCQ_QCFGPTR 2
|
||||
|
||||
/* Each value represents the temperature in celsius as (value - 80) */
|
||||
#define UFS_TEMPERATURE 120
|
||||
#define UFS_TOO_HIGH_TEMP_BOUNDARY 160
|
||||
#define UFS_TOO_LOW_TEMP_BOUNDARY 60
|
||||
|
||||
static void ufs_exec_req(UfsRequest *req);
|
||||
static void ufs_clear_req(UfsRequest *req);
|
||||
|
||||
|
@ -838,6 +843,42 @@ static const MemoryRegionOps ufs_mmio_ops = {
|
|||
},
|
||||
};
|
||||
|
||||
static void ufs_update_ee_status(UfsHc *u)
|
||||
{
|
||||
uint16_t ee_status = be16_to_cpu(u->attributes.exception_event_status);
|
||||
uint8_t high_temp_thresh = u->attributes.device_too_high_temp_boundary;
|
||||
uint8_t low_temp_thresh = u->attributes.device_too_low_temp_boundary;
|
||||
|
||||
if (u->temperature >= high_temp_thresh) {
|
||||
ee_status |= MASK_EE_TOO_HIGH_TEMP;
|
||||
} else {
|
||||
ee_status &= ~MASK_EE_TOO_HIGH_TEMP;
|
||||
}
|
||||
|
||||
if (u->temperature <= low_temp_thresh) {
|
||||
ee_status |= MASK_EE_TOO_LOW_TEMP;
|
||||
} else {
|
||||
ee_status &= ~MASK_EE_TOO_LOW_TEMP;
|
||||
}
|
||||
|
||||
u->attributes.exception_event_status = cpu_to_be16(ee_status);
|
||||
}
|
||||
|
||||
static bool ufs_check_exception_event_alert(UfsHc *u, uint8_t trans_type)
|
||||
{
|
||||
uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control);
|
||||
uint16_t ee_status;
|
||||
|
||||
if (trans_type != UFS_UPIU_TRANSACTION_RESPONSE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ufs_update_ee_status(u);
|
||||
|
||||
ee_status = be16_to_cpu(u->attributes.exception_event_status);
|
||||
|
||||
return ee_control & ee_status;
|
||||
}
|
||||
|
||||
void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
|
||||
uint8_t response, uint8_t scsi_status,
|
||||
|
@ -848,6 +889,8 @@ void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
|
|||
req->rsp_upiu.header.flags = flags;
|
||||
req->rsp_upiu.header.response = response;
|
||||
req->rsp_upiu.header.scsi_status = scsi_status;
|
||||
req->rsp_upiu.header.device_inf =
|
||||
ufs_check_exception_event_alert(req->hc, trans_type);
|
||||
req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
|
||||
}
|
||||
|
||||
|
@ -1042,6 +1085,25 @@ static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op)
|
|||
return UFS_QUERY_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static inline uint8_t ufs_read_device_temp(UfsHc *u)
|
||||
{
|
||||
uint8_t feat_sup = u->device_desc.ufs_features_support;
|
||||
bool high_temp_sup, low_temp_sup, high_temp_en, low_temp_en;
|
||||
uint16_t ee_control = be16_to_cpu(u->attributes.exception_event_control);
|
||||
|
||||
high_temp_sup = feat_sup & UFS_DEV_HIGH_TEMP_NOTIF;
|
||||
low_temp_sup = feat_sup & UFS_DEV_LOW_TEMP_NOTIF;
|
||||
high_temp_en = ee_control & MASK_EE_TOO_HIGH_TEMP;
|
||||
low_temp_en = ee_control & MASK_EE_TOO_LOW_TEMP;
|
||||
|
||||
if ((high_temp_sup && high_temp_en) ||
|
||||
(low_temp_sup && low_temp_en)) {
|
||||
return u->temperature;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
|
||||
{
|
||||
switch (idn) {
|
||||
|
@ -1072,6 +1134,7 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
|
|||
case UFS_QUERY_ATTR_IDN_EE_CONTROL:
|
||||
return be16_to_cpu(u->attributes.exception_event_control);
|
||||
case UFS_QUERY_ATTR_IDN_EE_STATUS:
|
||||
ufs_update_ee_status(u);
|
||||
return be16_to_cpu(u->attributes.exception_event_status);
|
||||
case UFS_QUERY_ATTR_IDN_SECONDS_PASSED:
|
||||
return be32_to_cpu(u->attributes.seconds_passed);
|
||||
|
@ -1086,7 +1149,8 @@ static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
|
|||
case UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME:
|
||||
return u->attributes.ref_clk_gating_wait_time;
|
||||
case UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP:
|
||||
return u->attributes.device_case_rough_temperaure;
|
||||
u->attributes.device_case_rough_temperature = ufs_read_device_temp(u);
|
||||
return u->attributes.device_case_rough_temperature;
|
||||
case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND:
|
||||
return u->attributes.device_too_high_temp_boundary;
|
||||
case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND:
|
||||
|
@ -1677,8 +1741,12 @@ static void ufs_init_hc(UfsHc *u)
|
|||
u->device_desc.ud_0_base_offset = 0x16;
|
||||
u->device_desc.ud_config_p_length = 0x1A;
|
||||
u->device_desc.device_rtt_cap = 0x02;
|
||||
u->device_desc.ufs_features_support = UFS_DEV_HIGH_TEMP_NOTIF |
|
||||
UFS_DEV_LOW_TEMP_NOTIF;
|
||||
u->device_desc.queue_depth = u->params.nutrs;
|
||||
u->device_desc.product_revision_level = 0x04;
|
||||
u->device_desc.extended_ufs_features_support =
|
||||
cpu_to_be32(UFS_DEV_HIGH_TEMP_NOTIF | UFS_DEV_LOW_TEMP_NOTIF);
|
||||
|
||||
memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
|
||||
u->geometry_desc.length = sizeof(GeometryDescriptor);
|
||||
|
@ -1702,9 +1770,17 @@ static void ufs_init_hc(UfsHc *u)
|
|||
/* configure descriptor is not supported */
|
||||
u->attributes.config_descr_lock = 0x01;
|
||||
u->attributes.max_num_of_rtt = 0x02;
|
||||
u->attributes.device_too_high_temp_boundary = UFS_TOO_HIGH_TEMP_BOUNDARY;
|
||||
u->attributes.device_too_low_temp_boundary = UFS_TOO_LOW_TEMP_BOUNDARY;
|
||||
|
||||
memset(&u->flags, 0, sizeof(u->flags));
|
||||
u->flags.permanently_disable_fw_update = 1;
|
||||
|
||||
/*
|
||||
* The temperature value is fixed to UFS_TEMPERATURE and does not change
|
||||
* dynamically
|
||||
*/
|
||||
u->temperature = UFS_TEMPERATURE;
|
||||
}
|
||||
|
||||
static void ufs_realize(PCIDevice *pci_dev, Error **errp)
|
||||
|
|
|
@ -146,6 +146,8 @@ typedef struct UfsHc {
|
|||
/* MCQ properties */
|
||||
UfsSq *sq[UFS_MAX_MCQ_QNUM];
|
||||
UfsCq *cq[UFS_MAX_MCQ_QNUM];
|
||||
|
||||
uint8_t temperature;
|
||||
} UfsHc;
|
||||
|
||||
static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
|
||||
|
|
|
@ -82,6 +82,21 @@ static bool xhci_pci_intr_raise(XHCIState *xhci, int n, bool level)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool xhci_pci_intr_mapping_conditional(XHCIState *xhci)
|
||||
{
|
||||
XHCIPciState *s = container_of(xhci, XHCIPciState, xhci);
|
||||
PCIDevice *pci_dev = PCI_DEVICE(s);
|
||||
|
||||
/*
|
||||
* Implementation of the "conditional-intr-mapping" property, which only
|
||||
* enables interrupter mapping if MSI or MSI-X is available and active.
|
||||
* Forces all events onto interrupter/event ring 0 in pin-based IRQ mode.
|
||||
* Provides compatibility with macOS guests on machine types where MSI(-X)
|
||||
* is not available.
|
||||
*/
|
||||
return msix_enabled(pci_dev) || msi_enabled(pci_dev);
|
||||
}
|
||||
|
||||
static void xhci_pci_reset(DeviceState *dev)
|
||||
{
|
||||
XHCIPciState *s = XHCI_PCI(dev);
|
||||
|
@ -119,6 +134,9 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp)
|
|||
object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL);
|
||||
s->xhci.intr_update = xhci_pci_intr_update;
|
||||
s->xhci.intr_raise = xhci_pci_intr_raise;
|
||||
if (s->conditional_intr_mapping) {
|
||||
s->xhci.intr_mapping_supported = xhci_pci_intr_mapping_conditional;
|
||||
}
|
||||
if (!qdev_realize(DEVICE(&s->xhci), NULL, errp)) {
|
||||
return;
|
||||
}
|
||||
|
@ -201,6 +219,8 @@ static void xhci_instance_init(Object *obj)
|
|||
static const Property xhci_pci_properties[] = {
|
||||
DEFINE_PROP_ON_OFF_AUTO("msi", XHCIPciState, msi, ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_BOOL("conditional-intr-mapping", XHCIPciState,
|
||||
conditional_intr_mapping, false),
|
||||
};
|
||||
|
||||
static void xhci_class_init(ObjectClass *klass, void *data)
|
||||
|
@ -215,6 +235,10 @@ static void xhci_class_init(ObjectClass *klass, void *data)
|
|||
k->exit = usb_xhci_pci_exit;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
device_class_set_props(dc, xhci_pci_properties);
|
||||
object_class_property_set_description(klass, "conditional-intr-mapping",
|
||||
"When true, disables interrupter mapping for pin-based IRQ mode. "
|
||||
"Intended to be used with guest drivers with questionable behaviour, "
|
||||
"such as macOS's.");
|
||||
}
|
||||
|
||||
static const TypeInfo xhci_pci_info = {
|
||||
|
|
|
@ -40,6 +40,7 @@ typedef struct XHCIPciState {
|
|||
XHCIState xhci;
|
||||
OnOffAuto msi;
|
||||
OnOffAuto msix;
|
||||
bool conditional_intr_mapping;
|
||||
} XHCIPciState;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -644,7 +644,8 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
|||
dma_addr_t erdp;
|
||||
unsigned int dp_idx;
|
||||
|
||||
if (xhci->numintrs == 1) {
|
||||
if (xhci->numintrs == 1 ||
|
||||
(xhci->intr_mapping_supported && !xhci->intr_mapping_supported(xhci))) {
|
||||
v = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -193,6 +193,11 @@ typedef struct XHCIState {
|
|||
uint32_t max_pstreams_mask;
|
||||
void (*intr_update)(XHCIState *s, int n, bool enable);
|
||||
bool (*intr_raise)(XHCIState *s, int n, bool level);
|
||||
/*
|
||||
* Callback for special-casing interrupter mapping support. NULL for most
|
||||
* implementations, for defaulting to enabled mapping unless numintrs == 1.
|
||||
*/
|
||||
bool (*intr_mapping_supported)(XHCIState *s);
|
||||
DeviceState *hostOpaque;
|
||||
|
||||
/* Operational Registers */
|
||||
|
|
34
hw/vmapple/Kconfig
Normal file
34
hw/vmapple/Kconfig
Normal file
|
@ -0,0 +1,34 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
config VMAPPLE_AES
|
||||
bool
|
||||
|
||||
config VMAPPLE_BDIF
|
||||
bool
|
||||
|
||||
config VMAPPLE_CFG
|
||||
bool
|
||||
|
||||
config VMAPPLE_VIRTIO_BLK
|
||||
bool
|
||||
|
||||
config VMAPPLE
|
||||
bool
|
||||
depends on ARM
|
||||
depends on HVF
|
||||
default y if ARM
|
||||
imply PCI_DEVICES
|
||||
select ARM_GICV3
|
||||
select PLATFORM_BUS
|
||||
select PCI_EXPRESS
|
||||
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||
select PL011 # UART
|
||||
select PL031 # RTC
|
||||
select PL061 # GPIO
|
||||
select GPIO_PWR
|
||||
select PVPANIC_MMIO
|
||||
select VMAPPLE_AES
|
||||
select VMAPPLE_BDIF
|
||||
select VMAPPLE_CFG
|
||||
select MAC_PVG_MMIO
|
||||
select VMAPPLE_VIRTIO_BLK
|
581
hw/vmapple/aes.c
Normal file
581
hw/vmapple/aes.c
Normal file
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* QEMU Apple AES device emulation
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "trace.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "crypto/aes.h"
|
||||
#include "crypto/cipher.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/vmapple/vmapple.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "system/dma.h"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(AESState, APPLE_AES)
|
||||
|
||||
#define MAX_FIFO_SIZE 9
|
||||
|
||||
#define CMD_KEY 0x1
|
||||
#define CMD_KEY_CONTEXT_SHIFT 27
|
||||
#define CMD_KEY_CONTEXT_MASK (0x1 << CMD_KEY_CONTEXT_SHIFT)
|
||||
#define CMD_KEY_SELECT_MAX_IDX 0x7
|
||||
#define CMD_KEY_SELECT_SHIFT 24
|
||||
#define CMD_KEY_SELECT_MASK (CMD_KEY_SELECT_MAX_IDX << CMD_KEY_SELECT_SHIFT)
|
||||
#define CMD_KEY_KEY_LEN_NUM 4u
|
||||
#define CMD_KEY_KEY_LEN_SHIFT 22
|
||||
#define CMD_KEY_KEY_LEN_MASK ((CMD_KEY_KEY_LEN_NUM - 1u) << CMD_KEY_KEY_LEN_SHIFT)
|
||||
#define CMD_KEY_ENCRYPT_SHIFT 20
|
||||
#define CMD_KEY_ENCRYPT_MASK (0x1 << CMD_KEY_ENCRYPT_SHIFT)
|
||||
#define CMD_KEY_BLOCK_MODE_SHIFT 16
|
||||
#define CMD_KEY_BLOCK_MODE_MASK (0x3 << CMD_KEY_BLOCK_MODE_SHIFT)
|
||||
#define CMD_IV 0x2
|
||||
#define CMD_IV_CONTEXT_SHIFT 26
|
||||
#define CMD_IV_CONTEXT_MASK (0x3 << CMD_KEY_CONTEXT_SHIFT)
|
||||
#define CMD_DSB 0x3
|
||||
#define CMD_SKG 0x4
|
||||
#define CMD_DATA 0x5
|
||||
#define CMD_DATA_KEY_CTX_SHIFT 27
|
||||
#define CMD_DATA_KEY_CTX_MASK (0x1 << CMD_DATA_KEY_CTX_SHIFT)
|
||||
#define CMD_DATA_IV_CTX_SHIFT 25
|
||||
#define CMD_DATA_IV_CTX_MASK (0x3 << CMD_DATA_IV_CTX_SHIFT)
|
||||
#define CMD_DATA_LEN_MASK 0xffffff
|
||||
#define CMD_STORE_IV 0x6
|
||||
#define CMD_STORE_IV_ADDR_MASK 0xffffff
|
||||
#define CMD_WRITE_REG 0x7
|
||||
#define CMD_FLAG 0x8
|
||||
#define CMD_FLAG_STOP_MASK BIT(26)
|
||||
#define CMD_FLAG_RAISE_IRQ_MASK BIT(27)
|
||||
#define CMD_FLAG_INFO_MASK 0xff
|
||||
#define CMD_MAX 0x10
|
||||
|
||||
#define CMD_SHIFT 28
|
||||
|
||||
#define REG_STATUS 0xc
|
||||
#define REG_STATUS_DMA_READ_RUNNING BIT(0)
|
||||
#define REG_STATUS_DMA_READ_PENDING BIT(1)
|
||||
#define REG_STATUS_DMA_WRITE_RUNNING BIT(2)
|
||||
#define REG_STATUS_DMA_WRITE_PENDING BIT(3)
|
||||
#define REG_STATUS_BUSY BIT(4)
|
||||
#define REG_STATUS_EXECUTING BIT(5)
|
||||
#define REG_STATUS_READY BIT(6)
|
||||
#define REG_STATUS_TEXT_DPA_SEEDED BIT(7)
|
||||
#define REG_STATUS_UNWRAP_DPA_SEEDED BIT(8)
|
||||
|
||||
#define REG_IRQ_STATUS 0x18
|
||||
#define REG_IRQ_STATUS_INVALID_CMD BIT(2)
|
||||
#define REG_IRQ_STATUS_FLAG BIT(5)
|
||||
#define REG_IRQ_ENABLE 0x1c
|
||||
#define REG_WATERMARK 0x20
|
||||
#define REG_Q_STATUS 0x24
|
||||
#define REG_FLAG_INFO 0x30
|
||||
#define REG_FIFO 0x200
|
||||
|
||||
static const uint32_t key_lens[CMD_KEY_KEY_LEN_NUM] = {
|
||||
[0] = 16,
|
||||
[1] = 24,
|
||||
[2] = 32,
|
||||
[3] = 64,
|
||||
};
|
||||
|
||||
typedef struct Key {
|
||||
uint32_t key_len;
|
||||
uint8_t key[32];
|
||||
} Key;
|
||||
|
||||
typedef struct IV {
|
||||
uint32_t iv[4];
|
||||
} IV;
|
||||
|
||||
static Key builtin_keys[CMD_KEY_SELECT_MAX_IDX + 1] = {
|
||||
[1] = {
|
||||
.key_len = 32,
|
||||
.key = { 0x1 },
|
||||
},
|
||||
[2] = {
|
||||
.key_len = 32,
|
||||
.key = { 0x2 },
|
||||
},
|
||||
[3] = {
|
||||
.key_len = 32,
|
||||
.key = { 0x3 },
|
||||
}
|
||||
};
|
||||
|
||||
struct AESState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
qemu_irq irq;
|
||||
MemoryRegion iomem1;
|
||||
MemoryRegion iomem2;
|
||||
AddressSpace *as;
|
||||
|
||||
uint32_t status;
|
||||
uint32_t q_status;
|
||||
uint32_t irq_status;
|
||||
uint32_t irq_enable;
|
||||
uint32_t watermark;
|
||||
uint32_t flag_info;
|
||||
uint32_t fifo[MAX_FIFO_SIZE];
|
||||
uint32_t fifo_idx;
|
||||
Key key[2];
|
||||
IV iv[4];
|
||||
bool is_encrypt;
|
||||
QCryptoCipherMode block_mode;
|
||||
};
|
||||
|
||||
static void aes_update_irq(AESState *s)
|
||||
{
|
||||
qemu_set_irq(s->irq, !!(s->irq_status & s->irq_enable));
|
||||
}
|
||||
|
||||
static uint64_t aes1_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
AESState *s = opaque;
|
||||
uint64_t res = 0;
|
||||
|
||||
switch (offset) {
|
||||
case REG_STATUS:
|
||||
res = s->status;
|
||||
break;
|
||||
case REG_IRQ_STATUS:
|
||||
res = s->irq_status;
|
||||
break;
|
||||
case REG_IRQ_ENABLE:
|
||||
res = s->irq_enable;
|
||||
break;
|
||||
case REG_WATERMARK:
|
||||
res = s->watermark;
|
||||
break;
|
||||
case REG_Q_STATUS:
|
||||
res = s->q_status;
|
||||
break;
|
||||
case REG_FLAG_INFO:
|
||||
res = s->flag_info;
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unknown AES MMIO offset %" PRIx64 "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_aes_read(offset, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void fifo_append(AESState *s, uint64_t val)
|
||||
{
|
||||
if (s->fifo_idx == MAX_FIFO_SIZE) {
|
||||
/* Exceeded the FIFO. Bail out */
|
||||
return;
|
||||
}
|
||||
|
||||
s->fifo[s->fifo_idx++] = val;
|
||||
}
|
||||
|
||||
static bool has_payload(AESState *s, uint32_t elems)
|
||||
{
|
||||
return s->fifo_idx >= elems + 1;
|
||||
}
|
||||
|
||||
static bool cmd_key(AESState *s)
|
||||
{
|
||||
uint32_t cmd = s->fifo[0];
|
||||
uint32_t key_select = (cmd & CMD_KEY_SELECT_MASK) >> CMD_KEY_SELECT_SHIFT;
|
||||
uint32_t ctxt = (cmd & CMD_KEY_CONTEXT_MASK) >> CMD_KEY_CONTEXT_SHIFT;
|
||||
uint32_t key_len;
|
||||
|
||||
switch ((cmd & CMD_KEY_BLOCK_MODE_MASK) >> CMD_KEY_BLOCK_MODE_SHIFT) {
|
||||
case 0:
|
||||
s->block_mode = QCRYPTO_CIPHER_MODE_ECB;
|
||||
break;
|
||||
case 1:
|
||||
s->block_mode = QCRYPTO_CIPHER_MODE_CBC;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
s->is_encrypt = cmd & CMD_KEY_ENCRYPT_MASK;
|
||||
key_len = key_lens[(cmd & CMD_KEY_KEY_LEN_MASK) >> CMD_KEY_KEY_LEN_SHIFT];
|
||||
|
||||
if (key_select) {
|
||||
trace_aes_cmd_key_select_builtin(ctxt, key_select,
|
||||
s->is_encrypt ? "en" : "de",
|
||||
QCryptoCipherMode_str(s->block_mode));
|
||||
s->key[ctxt] = builtin_keys[key_select];
|
||||
} else {
|
||||
trace_aes_cmd_key_select_new(ctxt, key_len,
|
||||
s->is_encrypt ? "en" : "de",
|
||||
QCryptoCipherMode_str(s->block_mode));
|
||||
if (key_len > sizeof(s->key[ctxt].key)) {
|
||||
return false;
|
||||
}
|
||||
if (!has_payload(s, key_len / sizeof(uint32_t))) {
|
||||
/* wait for payload */
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__);
|
||||
return false;
|
||||
}
|
||||
memcpy(&s->key[ctxt].key, &s->fifo[1], key_len);
|
||||
s->key[ctxt].key_len = key_len;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cmd_iv(AESState *s)
|
||||
{
|
||||
uint32_t cmd = s->fifo[0];
|
||||
uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT;
|
||||
|
||||
if (!has_payload(s, 4)) {
|
||||
/* wait for payload */
|
||||
return false;
|
||||
}
|
||||
memcpy(&s->iv[ctxt].iv, &s->fifo[1], sizeof(s->iv[ctxt].iv));
|
||||
trace_aes_cmd_iv(ctxt, s->fifo[1], s->fifo[2], s->fifo[3], s->fifo[4]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dump_data(const char *desc, const void *p, size_t len)
|
||||
{
|
||||
static const size_t MAX_LEN = 0x1000;
|
||||
char hex[MAX_LEN * 2 + 1] = "";
|
||||
|
||||
if (len > MAX_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_hexdump_to_buffer(hex, sizeof(hex), p, len);
|
||||
trace_aes_dump_data(desc, hex);
|
||||
}
|
||||
|
||||
static bool cmd_data(AESState *s)
|
||||
{
|
||||
uint32_t cmd = s->fifo[0];
|
||||
uint32_t ctxt_iv = 0;
|
||||
uint32_t ctxt_key = (cmd & CMD_DATA_KEY_CTX_MASK) >> CMD_DATA_KEY_CTX_SHIFT;
|
||||
uint32_t len = cmd & CMD_DATA_LEN_MASK;
|
||||
uint64_t src_addr = s->fifo[2];
|
||||
uint64_t dst_addr = s->fifo[3];
|
||||
QCryptoCipherAlgo alg;
|
||||
g_autoptr(QCryptoCipher) cipher = NULL;
|
||||
g_autoptr(GByteArray) src = NULL;
|
||||
g_autoptr(GByteArray) dst = NULL;
|
||||
MemTxResult r;
|
||||
|
||||
src_addr |= ((uint64_t)s->fifo[1] << 16) & 0xffff00000000ULL;
|
||||
dst_addr |= ((uint64_t)s->fifo[1] << 32) & 0xffff00000000ULL;
|
||||
|
||||
trace_aes_cmd_data(ctxt_key, ctxt_iv, src_addr, dst_addr, len);
|
||||
|
||||
if (!has_payload(s, 3)) {
|
||||
/* wait for payload */
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxt_key >= ARRAY_SIZE(s->key) ||
|
||||
ctxt_iv >= ARRAY_SIZE(s->iv)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key or iv\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
src = g_byte_array_sized_new(len);
|
||||
g_byte_array_set_size(src, len);
|
||||
dst = g_byte_array_sized_new(len);
|
||||
g_byte_array_set_size(dst, len);
|
||||
|
||||
r = dma_memory_read(s->as, src_addr, src->data, len, MEMTXATTRS_UNSPECIFIED);
|
||||
if (r != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA read of %"PRIu32" bytes "
|
||||
"from 0x%"PRIx64" failed. (r=%d)\n",
|
||||
__func__, len, src_addr, r);
|
||||
return false;
|
||||
}
|
||||
|
||||
dump_data("cmd_data(): src_data=", src->data, len);
|
||||
|
||||
switch (s->key[ctxt_key].key_len) {
|
||||
case 128 / 8:
|
||||
alg = QCRYPTO_CIPHER_ALGO_AES_128;
|
||||
break;
|
||||
case 192 / 8:
|
||||
alg = QCRYPTO_CIPHER_ALGO_AES_192;
|
||||
break;
|
||||
case 256 / 8:
|
||||
alg = QCRYPTO_CIPHER_ALGO_AES_256;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid key length\n", __func__);
|
||||
return false;
|
||||
}
|
||||
cipher = qcrypto_cipher_new(alg, s->block_mode,
|
||||
s->key[ctxt_key].key,
|
||||
s->key[ctxt_key].key_len, NULL);
|
||||
if (!cipher) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to create cipher object\n",
|
||||
__func__);
|
||||
return false;
|
||||
}
|
||||
if (s->block_mode != QCRYPTO_CIPHER_MODE_ECB) {
|
||||
if (qcrypto_cipher_setiv(cipher, (void *)s->iv[ctxt_iv].iv,
|
||||
sizeof(s->iv[ctxt_iv].iv), NULL) != 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to set IV\n", __func__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (s->is_encrypt) {
|
||||
if (qcrypto_cipher_encrypt(cipher, src->data, dst->data, len, NULL) != 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Encryption failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (qcrypto_cipher_decrypt(cipher, src->data, dst->data, len, NULL) != 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Decryption failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
dump_data("cmd_data(): dst_data=", dst->data, len);
|
||||
r = dma_memory_write(s->as, dst_addr, dst->data, len, MEMTXATTRS_UNSPECIFIED);
|
||||
if (r != MEMTX_OK) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA write of %"PRIu32" bytes "
|
||||
"to 0x%"PRIx64" failed. (r=%d)\n",
|
||||
__func__, len, src_addr, r);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cmd_store_iv(AESState *s)
|
||||
{
|
||||
uint32_t cmd = s->fifo[0];
|
||||
uint32_t ctxt = (cmd & CMD_IV_CONTEXT_MASK) >> CMD_IV_CONTEXT_SHIFT;
|
||||
uint64_t addr = s->fifo[1];
|
||||
MemTxResult dma_result;
|
||||
|
||||
if (!has_payload(s, 1)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: No payload\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxt >= ARRAY_SIZE(s->iv)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Invalid context. ctxt = %u, allowed: 0..%zu\n",
|
||||
__func__, ctxt, ARRAY_SIZE(s->iv) - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
addr |= ((uint64_t)cmd << 32) & 0xff00000000ULL;
|
||||
dma_result = dma_memory_write(&address_space_memory, addr,
|
||||
&s->iv[ctxt].iv, sizeof(s->iv[ctxt].iv),
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
|
||||
trace_aes_cmd_store_iv(ctxt, addr, s->iv[ctxt].iv[0], s->iv[ctxt].iv[1],
|
||||
s->iv[ctxt].iv[2], s->iv[ctxt].iv[3]);
|
||||
|
||||
return dma_result == MEMTX_OK;
|
||||
}
|
||||
|
||||
static bool cmd_flag(AESState *s)
|
||||
{
|
||||
uint32_t cmd = s->fifo[0];
|
||||
uint32_t raise_irq = cmd & CMD_FLAG_RAISE_IRQ_MASK;
|
||||
|
||||
/* We always process data when it's coming in, so fire an IRQ immediately */
|
||||
if (raise_irq) {
|
||||
s->irq_status |= REG_IRQ_STATUS_FLAG;
|
||||
}
|
||||
|
||||
s->flag_info = cmd & CMD_FLAG_INFO_MASK;
|
||||
|
||||
trace_aes_cmd_flag(!!raise_irq, s->flag_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void fifo_process(AESState *s)
|
||||
{
|
||||
uint32_t cmd = s->fifo[0] >> CMD_SHIFT;
|
||||
bool success = false;
|
||||
|
||||
if (!s->fifo_idx) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_KEY:
|
||||
success = cmd_key(s);
|
||||
break;
|
||||
case CMD_IV:
|
||||
success = cmd_iv(s);
|
||||
break;
|
||||
case CMD_DATA:
|
||||
success = cmd_data(s);
|
||||
break;
|
||||
case CMD_STORE_IV:
|
||||
success = cmd_store_iv(s);
|
||||
break;
|
||||
case CMD_FLAG:
|
||||
success = cmd_flag(s);
|
||||
break;
|
||||
default:
|
||||
s->irq_status |= REG_IRQ_STATUS_INVALID_CMD;
|
||||
break;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
s->fifo_idx = 0;
|
||||
}
|
||||
|
||||
trace_aes_fifo_process(cmd, success);
|
||||
}
|
||||
|
||||
static void aes1_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
{
|
||||
AESState *s = opaque;
|
||||
|
||||
trace_aes_write(offset, val);
|
||||
|
||||
switch (offset) {
|
||||
case REG_IRQ_STATUS:
|
||||
s->irq_status &= ~val;
|
||||
break;
|
||||
case REG_IRQ_ENABLE:
|
||||
s->irq_enable = val;
|
||||
break;
|
||||
case REG_FIFO:
|
||||
fifo_append(s, val);
|
||||
fifo_process(s);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Unknown AES MMIO offset %"PRIx64", data %"PRIx64"\n",
|
||||
__func__, offset, val);
|
||||
return;
|
||||
}
|
||||
|
||||
aes_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aes1_ops = {
|
||||
.read = aes1_read,
|
||||
.write = aes1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static uint64_t aes2_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint64_t res = 0;
|
||||
|
||||
switch (offset) {
|
||||
case 0:
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Unknown AES MMIO 2 offset %"PRIx64"\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
trace_aes_2_read(offset, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void aes2_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
{
|
||||
trace_aes_2_write(offset, val);
|
||||
|
||||
switch (offset) {
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Unknown AES MMIO 2 offset %"PRIx64", data %"PRIx64"\n",
|
||||
__func__, offset, val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aes2_ops = {
|
||||
.read = aes2_read,
|
||||
.write = aes2_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static void aes_reset(Object *obj, ResetType type)
|
||||
{
|
||||
AESState *s = APPLE_AES(obj);
|
||||
|
||||
s->status = 0x3f80;
|
||||
s->q_status = 2;
|
||||
s->irq_status = 0;
|
||||
s->irq_enable = 0;
|
||||
s->watermark = 0;
|
||||
}
|
||||
|
||||
static void aes_init(Object *obj)
|
||||
{
|
||||
AESState *s = APPLE_AES(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem1, obj, &aes1_ops, s, TYPE_APPLE_AES, 0x4000);
|
||||
memory_region_init_io(&s->iomem2, obj, &aes2_ops, s, TYPE_APPLE_AES, 0x4000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem1);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem2);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
|
||||
s->as = &address_space_memory;
|
||||
}
|
||||
|
||||
static void aes_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
|
||||
rc->phases.hold = aes_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo aes_info = {
|
||||
.name = TYPE_APPLE_AES,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(AESState),
|
||||
.class_init = aes_class_init,
|
||||
.instance_init = aes_init,
|
||||
};
|
||||
|
||||
static void aes_register_types(void)
|
||||
{
|
||||
type_register_static(&aes_info);
|
||||
}
|
||||
|
||||
type_init(aes_register_types)
|
274
hw/vmapple/bdif.c
Normal file
274
hw/vmapple/bdif.c
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* VMApple Backdoor Interface
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "trace.h"
|
||||
#include "hw/vmapple/vmapple.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/block/block.h"
|
||||
#include "qapi/error.h"
|
||||
#include "system/block-backend.h"
|
||||
#include "system/dma.h"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF)
|
||||
|
||||
struct VMAppleBdifState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
BlockBackend *aux;
|
||||
BlockBackend *root;
|
||||
MemoryRegion mmio;
|
||||
};
|
||||
|
||||
#define VMAPPLE_BDIF_SIZE 0x00200000
|
||||
|
||||
#define REG_DEVID_MASK 0xffff0000
|
||||
#define DEVID_ROOT 0x00000000
|
||||
#define DEVID_AUX 0x00010000
|
||||
#define DEVID_USB 0x00100000
|
||||
|
||||
#define REG_STATUS 0x0
|
||||
#define REG_STATUS_ACTIVE BIT(0)
|
||||
#define REG_CFG 0x4
|
||||
#define REG_CFG_ACTIVE BIT(1)
|
||||
#define REG_UNK1 0x8
|
||||
#define REG_BUSY 0x10
|
||||
#define REG_BUSY_READY BIT(0)
|
||||
#define REG_UNK2 0x400
|
||||
#define REG_CMD 0x408
|
||||
#define REG_NEXT_DEVICE 0x420
|
||||
#define REG_UNK3 0x434
|
||||
|
||||
typedef struct VblkSector {
|
||||
uint32_t pad;
|
||||
uint32_t pad2;
|
||||
uint32_t sector;
|
||||
uint32_t pad3;
|
||||
} VblkSector;
|
||||
|
||||
typedef struct VblkReqCmd {
|
||||
uint64_t addr;
|
||||
uint32_t len;
|
||||
uint32_t flags;
|
||||
} VblkReqCmd;
|
||||
|
||||
typedef struct VblkReq {
|
||||
VblkReqCmd sector;
|
||||
VblkReqCmd data;
|
||||
VblkReqCmd retval;
|
||||
} VblkReq;
|
||||
|
||||
#define VBLK_DATA_FLAGS_READ 0x00030001
|
||||
#define VBLK_DATA_FLAGS_WRITE 0x00010001
|
||||
|
||||
#define VBLK_RET_SUCCESS 0
|
||||
#define VBLK_RET_FAILED 1
|
||||
|
||||
static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint64_t ret = -1;
|
||||
uint64_t devid = offset & REG_DEVID_MASK;
|
||||
|
||||
switch (offset & ~REG_DEVID_MASK) {
|
||||
case REG_STATUS:
|
||||
ret = REG_STATUS_ACTIVE;
|
||||
break;
|
||||
case REG_CFG:
|
||||
ret = REG_CFG_ACTIVE;
|
||||
break;
|
||||
case REG_UNK1:
|
||||
ret = 0x420;
|
||||
break;
|
||||
case REG_BUSY:
|
||||
ret = REG_BUSY_READY;
|
||||
break;
|
||||
case REG_UNK2:
|
||||
ret = 0x1;
|
||||
break;
|
||||
case REG_UNK3:
|
||||
ret = 0x0;
|
||||
break;
|
||||
case REG_NEXT_DEVICE:
|
||||
switch (devid) {
|
||||
case DEVID_ROOT:
|
||||
ret = 0x8000000;
|
||||
break;
|
||||
case DEVID_AUX:
|
||||
ret = 0x10000;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
trace_bdif_read(offset, size, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void le2cpu_sector(VblkSector *sector)
|
||||
{
|
||||
sector->sector = le32_to_cpu(sector->sector);
|
||||
}
|
||||
|
||||
static void le2cpu_reqcmd(VblkReqCmd *cmd)
|
||||
{
|
||||
cmd->addr = le64_to_cpu(cmd->addr);
|
||||
cmd->len = le32_to_cpu(cmd->len);
|
||||
cmd->flags = le32_to_cpu(cmd->flags);
|
||||
}
|
||||
|
||||
static void le2cpu_req(VblkReq *req)
|
||||
{
|
||||
le2cpu_reqcmd(&req->sector);
|
||||
le2cpu_reqcmd(&req->data);
|
||||
le2cpu_reqcmd(&req->retval);
|
||||
}
|
||||
|
||||
static void vblk_cmd(uint64_t devid, BlockBackend *blk, uint64_t gp_addr,
|
||||
uint64_t static_off)
|
||||
{
|
||||
VblkReq req;
|
||||
VblkSector sector;
|
||||
uint64_t off = 0;
|
||||
g_autofree char *buf = NULL;
|
||||
uint8_t ret = VBLK_RET_FAILED;
|
||||
int r;
|
||||
MemTxResult dma_result;
|
||||
|
||||
dma_result = dma_memory_read(&address_space_memory, gp_addr,
|
||||
&req, sizeof(req), MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_result != MEMTX_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
le2cpu_req(&req);
|
||||
|
||||
if (req.sector.len != sizeof(sector)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Read the vblk command */
|
||||
dma_result = dma_memory_read(&address_space_memory, req.sector.addr,
|
||||
§or, sizeof(sector),
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_result != MEMTX_OK) {
|
||||
goto out;
|
||||
}
|
||||
le2cpu_sector(§or);
|
||||
|
||||
off = sector.sector * 512ULL + static_off;
|
||||
|
||||
/* Sanity check that we're not allocating bogus sizes */
|
||||
if (req.data.len > 128 * MiB) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = g_malloc0(req.data.len);
|
||||
switch (req.data.flags) {
|
||||
case VBLK_DATA_FLAGS_READ:
|
||||
r = blk_pread(blk, off, req.data.len, buf, 0);
|
||||
trace_bdif_vblk_read(devid == DEVID_AUX ? "aux" : "root",
|
||||
req.data.addr, off, req.data.len, r);
|
||||
if (r < 0) {
|
||||
goto out;
|
||||
}
|
||||
dma_result = dma_memory_write(&address_space_memory, req.data.addr, buf,
|
||||
req.data.len, MEMTXATTRS_UNSPECIFIED);
|
||||
if (dma_result == MEMTX_OK) {
|
||||
ret = VBLK_RET_SUCCESS;
|
||||
}
|
||||
break;
|
||||
case VBLK_DATA_FLAGS_WRITE:
|
||||
/* Not needed, iBoot only reads */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
dma_memory_write(&address_space_memory, req.retval.addr, &ret, 1,
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
}
|
||||
|
||||
static void bdif_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
VMAppleBdifState *s = opaque;
|
||||
uint64_t devid = (offset & REG_DEVID_MASK);
|
||||
|
||||
trace_bdif_write(offset, size, value);
|
||||
|
||||
switch (offset & ~REG_DEVID_MASK) {
|
||||
case REG_CMD:
|
||||
switch (devid) {
|
||||
case DEVID_ROOT:
|
||||
vblk_cmd(devid, s->root, value, 0x0);
|
||||
break;
|
||||
case DEVID_AUX:
|
||||
vblk_cmd(devid, s->aux, value, 0x0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bdif_ops = {
|
||||
.read = bdif_read,
|
||||
.write = bdif_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static void bdif_init(Object *obj)
|
||||
{
|
||||
VMAppleBdifState *s = VMAPPLE_BDIF(obj);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &bdif_ops, obj,
|
||||
"VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static const Property bdif_properties[] = {
|
||||
DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux),
|
||||
DEFINE_PROP_DRIVE("root", VMAppleBdifState, root),
|
||||
};
|
||||
|
||||
static void bdif_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->desc = "VMApple Backdoor Interface";
|
||||
device_class_set_props(dc, bdif_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo bdif_info = {
|
||||
.name = TYPE_VMAPPLE_BDIF,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(VMAppleBdifState),
|
||||
.instance_init = bdif_init,
|
||||
.class_init = bdif_class_init,
|
||||
};
|
||||
|
||||
static void bdif_register_types(void)
|
||||
{
|
||||
type_register_static(&bdif_info);
|
||||
}
|
||||
|
||||
type_init(bdif_register_types)
|
195
hw/vmapple/cfg.c
Normal file
195
hw/vmapple/cfg.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* VMApple Configuration Region
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/vmapple/vmapple.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "net/net.h"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VMAppleCfgState, VMAPPLE_CFG)
|
||||
|
||||
#define VMAPPLE_CFG_SIZE 0x00010000
|
||||
|
||||
typedef struct VMAppleCfg {
|
||||
uint32_t version; /* 0x000 */
|
||||
uint32_t nr_cpus; /* 0x004 */
|
||||
uint32_t unk1; /* 0x008 */
|
||||
uint32_t unk2; /* 0x00c */
|
||||
uint32_t unk3; /* 0x010 */
|
||||
uint32_t unk4; /* 0x014 */
|
||||
uint64_t ecid; /* 0x018 */
|
||||
uint64_t ram_size; /* 0x020 */
|
||||
uint32_t run_installer1; /* 0x028 */
|
||||
uint32_t unk5; /* 0x02c */
|
||||
uint32_t unk6; /* 0x030 */
|
||||
uint32_t run_installer2; /* 0x034 */
|
||||
uint32_t rnd; /* 0x038 */
|
||||
uint32_t unk7; /* 0x03c */
|
||||
MACAddr mac_en0; /* 0x040 */
|
||||
uint8_t pad1[2];
|
||||
MACAddr mac_en1; /* 0x048 */
|
||||
uint8_t pad2[2];
|
||||
MACAddr mac_wifi0; /* 0x050 */
|
||||
uint8_t pad3[2];
|
||||
MACAddr mac_bt0; /* 0x058 */
|
||||
uint8_t pad4[2];
|
||||
uint8_t reserved[0xa0]; /* 0x060 */
|
||||
uint32_t cpu_ids[0x80]; /* 0x100 */
|
||||
uint8_t scratch[0x200]; /* 0x180 */
|
||||
char serial[32]; /* 0x380 */
|
||||
char unk8[32]; /* 0x3a0 */
|
||||
char model[32]; /* 0x3c0 */
|
||||
uint8_t unk9[32]; /* 0x3e0 */
|
||||
uint32_t unk10; /* 0x400 */
|
||||
char soc_name[32]; /* 0x404 */
|
||||
} VMAppleCfg;
|
||||
|
||||
struct VMAppleCfgState {
|
||||
SysBusDevice parent_obj;
|
||||
VMAppleCfg cfg;
|
||||
|
||||
MemoryRegion mem;
|
||||
char *serial;
|
||||
char *model;
|
||||
char *soc_name;
|
||||
};
|
||||
|
||||
static void vmapple_cfg_reset(Object *obj, ResetType type)
|
||||
{
|
||||
VMAppleCfgState *s = VMAPPLE_CFG(obj);
|
||||
VMAppleCfg *cfg;
|
||||
|
||||
cfg = memory_region_get_ram_ptr(&s->mem);
|
||||
memset(cfg, 0, VMAPPLE_CFG_SIZE);
|
||||
*cfg = s->cfg;
|
||||
}
|
||||
|
||||
static bool set_fixlen_property_or_error(char *restrict dst,
|
||||
const char *restrict src,
|
||||
size_t dst_size, Error **errp,
|
||||
const char *property_name)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
size_t len;
|
||||
|
||||
len = g_strlcpy(dst, src, dst_size);
|
||||
if (len < dst_size) { /* len does not count nul terminator */
|
||||
return true;
|
||||
}
|
||||
|
||||
error_setg(errp, "Provided value too long for property '%s'", property_name);
|
||||
error_append_hint(errp, "length (%zu) exceeds maximum of %zu\n",
|
||||
len, dst_size - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define set_fixlen_property_or_return(dst_array, src, errp, property_name) \
|
||||
do { \
|
||||
if (!set_fixlen_property_or_error((dst_array), (src), \
|
||||
ARRAY_SIZE(dst_array), \
|
||||
(errp), (property_name))) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void vmapple_cfg_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VMAppleCfgState *s = VMAPPLE_CFG(dev);
|
||||
uint32_t i;
|
||||
|
||||
if (!s->serial) {
|
||||
s->serial = g_strdup("1234");
|
||||
}
|
||||
if (!s->model) {
|
||||
s->model = g_strdup("VM0001");
|
||||
}
|
||||
if (!s->soc_name) {
|
||||
s->soc_name = g_strdup("Apple M1 (Virtual)");
|
||||
}
|
||||
|
||||
set_fixlen_property_or_return(s->cfg.serial, s->serial, errp, "serial");
|
||||
set_fixlen_property_or_return(s->cfg.model, s->model, errp, "model");
|
||||
set_fixlen_property_or_return(s->cfg.soc_name, s->soc_name, errp, "soc_name");
|
||||
set_fixlen_property_or_return(s->cfg.unk8, "D/A", errp, "unk8");
|
||||
s->cfg.version = 2;
|
||||
s->cfg.unk1 = 1;
|
||||
s->cfg.unk2 = 1;
|
||||
s->cfg.unk3 = 0x20;
|
||||
s->cfg.unk4 = 0;
|
||||
s->cfg.unk5 = 1;
|
||||
s->cfg.unk6 = 1;
|
||||
s->cfg.unk7 = 0;
|
||||
s->cfg.unk10 = 1;
|
||||
|
||||
if (s->cfg.nr_cpus > ARRAY_SIZE(s->cfg.cpu_ids)) {
|
||||
error_setg(errp,
|
||||
"Failed to create %u CPUs, vmapple machine supports %zu max",
|
||||
s->cfg.nr_cpus, ARRAY_SIZE(s->cfg.cpu_ids));
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < s->cfg.nr_cpus; i++) {
|
||||
s->cfg.cpu_ids[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
static void vmapple_cfg_init(Object *obj)
|
||||
{
|
||||
VMAppleCfgState *s = VMAPPLE_CFG(obj);
|
||||
|
||||
memory_region_init_ram(&s->mem, obj, "VMApple Config", VMAPPLE_CFG_SIZE,
|
||||
&error_fatal);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mem);
|
||||
}
|
||||
|
||||
static const Property vmapple_cfg_properties[] = {
|
||||
DEFINE_PROP_UINT32("nr-cpus", VMAppleCfgState, cfg.nr_cpus, 1),
|
||||
DEFINE_PROP_UINT64("ecid", VMAppleCfgState, cfg.ecid, 0),
|
||||
DEFINE_PROP_UINT64("ram-size", VMAppleCfgState, cfg.ram_size, 0),
|
||||
DEFINE_PROP_UINT32("run_installer1", VMAppleCfgState, cfg.run_installer1, 0),
|
||||
DEFINE_PROP_UINT32("run_installer2", VMAppleCfgState, cfg.run_installer2, 0),
|
||||
DEFINE_PROP_UINT32("rnd", VMAppleCfgState, cfg.rnd, 0),
|
||||
DEFINE_PROP_MACADDR("mac-en0", VMAppleCfgState, cfg.mac_en0),
|
||||
DEFINE_PROP_MACADDR("mac-en1", VMAppleCfgState, cfg.mac_en1),
|
||||
DEFINE_PROP_MACADDR("mac-wifi0", VMAppleCfgState, cfg.mac_wifi0),
|
||||
DEFINE_PROP_MACADDR("mac-bt0", VMAppleCfgState, cfg.mac_bt0),
|
||||
DEFINE_PROP_STRING("serial", VMAppleCfgState, serial),
|
||||
DEFINE_PROP_STRING("model", VMAppleCfgState, model),
|
||||
DEFINE_PROP_STRING("soc_name", VMAppleCfgState, soc_name),
|
||||
};
|
||||
|
||||
static void vmapple_cfg_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
|
||||
dc->realize = vmapple_cfg_realize;
|
||||
dc->desc = "VMApple Configuration Region";
|
||||
device_class_set_props(dc, vmapple_cfg_properties);
|
||||
rc->phases.hold = vmapple_cfg_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo vmapple_cfg_info = {
|
||||
.name = TYPE_VMAPPLE_CFG,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(VMAppleCfgState),
|
||||
.instance_init = vmapple_cfg_init,
|
||||
.class_init = vmapple_cfg_class_init,
|
||||
};
|
||||
|
||||
static void vmapple_cfg_register_types(void)
|
||||
{
|
||||
type_register_static(&vmapple_cfg_info);
|
||||
}
|
||||
|
||||
type_init(vmapple_cfg_register_types)
|
7
hw/vmapple/meson.build
Normal file
7
hw/vmapple/meson.build
Normal file
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
system_ss.add(when: 'CONFIG_VMAPPLE_AES', if_true: files('aes.c'))
|
||||
system_ss.add(when: 'CONFIG_VMAPPLE_BDIF', if_true: files('bdif.c'))
|
||||
system_ss.add(when: 'CONFIG_VMAPPLE_CFG', if_true: files('cfg.c'))
|
||||
system_ss.add(when: 'CONFIG_VMAPPLE_VIRTIO_BLK', if_true: files('virtio-blk.c'))
|
||||
specific_ss.add(when: 'CONFIG_VMAPPLE', if_true: files('vmapple.c'))
|
21
hw/vmapple/trace-events
Normal file
21
hw/vmapple/trace-events
Normal file
|
@ -0,0 +1,21 @@
|
|||
# See docs/devel/tracing.rst for syntax documentation.
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# aes.c
|
||||
aes_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
|
||||
aes_cmd_key_select_builtin(uint32_t ctx, uint32_t key_id, const char *direction, const char *cipher) "[%d] Selecting builtin key %d to %scrypt with %s"
|
||||
aes_cmd_key_select_new(uint32_t ctx, uint32_t key_len, const char *direction, const char *cipher) "[%d] Selecting new key size=%d to %scrypt with %s"
|
||||
aes_cmd_iv(uint32_t ctx, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] 0x%08x 0x%08x 0x%08x 0x%08x"
|
||||
aes_cmd_data(uint32_t key, uint32_t iv, uint64_t src, uint64_t dst, uint32_t len) "[key=%d iv=%d] src=0x%"PRIx64" dst=0x%"PRIx64" len=0x%x"
|
||||
aes_cmd_store_iv(uint32_t ctx, uint64_t addr, uint32_t iv0, uint32_t iv1, uint32_t iv2, uint32_t iv3) "[%d] addr=0x%"PRIx64"x -> 0x%08x 0x%08x 0x%08x 0x%08x"
|
||||
aes_cmd_flag(uint32_t raise, uint32_t flag_info) "raise=%d flag_info=0x%x"
|
||||
aes_fifo_process(uint32_t cmd, bool success) "cmd=%d success=%d"
|
||||
aes_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
|
||||
aes_2_read(uint64_t offset, uint64_t res) "offset=0x%"PRIx64" res=0x%"PRIx64
|
||||
aes_2_write(uint64_t offset, uint64_t val) "offset=0x%"PRIx64" val=0x%"PRIx64
|
||||
aes_dump_data(const char *desc, const char *hex) "%s%s"
|
||||
|
||||
# bdif.c
|
||||
bdif_read(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64
|
||||
bdif_write(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64
|
||||
bdif_vblk_read(const char *dev, uint64_t addr, uint64_t offset, uint32_t len, int r) "dev=%s addr=0x%"PRIx64" off=0x%"PRIx64" size=0x%x r=%d"
|
2
hw/vmapple/trace.h
Normal file
2
hw/vmapple/trace.h
Normal file
|
@ -0,0 +1,2 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#include "trace/trace-hw_vmapple.h"
|
204
hw/vmapple/virtio-blk.c
Normal file
204
hw/vmapple/virtio-blk.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* VMApple specific VirtIO Block implementation
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* VMApple uses almost standard VirtIO Block, but with a few key differences:
|
||||
*
|
||||
* - Different PCI device/vendor ID
|
||||
* - An additional "type" identifier to differentiate AUX and Root volumes
|
||||
* - An additional BARRIER command
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/vmapple/vmapple.h"
|
||||
#include "hw/virtio/virtio-blk.h"
|
||||
#include "hw/virtio/virtio-pci.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk"
|
||||
OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK)
|
||||
|
||||
typedef struct VMAppleVirtIOBlkClass {
|
||||
VirtIOBlkClass parent;
|
||||
|
||||
void (*get_config)(VirtIODevice *vdev, uint8_t *config);
|
||||
} VMAppleVirtIOBlkClass;
|
||||
|
||||
typedef struct VMAppleVirtIOBlk {
|
||||
VirtIOBlock parent_obj;
|
||||
|
||||
uint32_t apple_type;
|
||||
} VMAppleVirtIOBlk;
|
||||
|
||||
/*
|
||||
* vmapple-virtio-blk-pci: This extends VirtioPCIProxy.
|
||||
*/
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI)
|
||||
|
||||
#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000
|
||||
|
||||
static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req,
|
||||
MultiReqBuffer *mrb,
|
||||
uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case VIRTIO_BLK_T_APPLE_BARRIER:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Barrier requests are currently no-ops\n",
|
||||
__func__);
|
||||
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
|
||||
g_free(req);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* VMApple virtio-blk uses the same config format as normal virtio, with one
|
||||
* exception: It adds an "apple type" specififer at the same location that
|
||||
* the spec reserves for max_secure_erase_sectors. Let's hook into the
|
||||
* get_config code path here, run it as usual and then patch in the apple type.
|
||||
*/
|
||||
static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
{
|
||||
VMAppleVirtIOBlk *dev = VMAPPLE_VIRTIO_BLK(vdev);
|
||||
VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_GET_CLASS(dev);
|
||||
struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config;
|
||||
|
||||
vvbk->get_config(vdev, config);
|
||||
|
||||
g_assert(dev->parent_obj.config_size >= endof(struct virtio_blk_config, zoned));
|
||||
|
||||
/* Apple abuses the field for max_secure_erase_sectors as type id */
|
||||
stl_he_p(&blkcfg->max_secure_erase_sectors, dev->apple_type);
|
||||
}
|
||||
|
||||
static void vmapple_virtio_blk_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass);
|
||||
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||
VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_CLASS(klass);
|
||||
|
||||
vbk->handle_unknown_request = vmapple_virtio_blk_handle_unknown_request;
|
||||
vvbk->get_config = vdc->get_config;
|
||||
vdc->get_config = vmapple_virtio_blk_get_config;
|
||||
}
|
||||
|
||||
static const TypeInfo vmapple_virtio_blk_info = {
|
||||
.name = TYPE_VMAPPLE_VIRTIO_BLK,
|
||||
.parent = TYPE_VIRTIO_BLK,
|
||||
.instance_size = sizeof(VMAppleVirtIOBlk),
|
||||
.class_size = sizeof(VMAppleVirtIOBlkClass),
|
||||
.class_init = vmapple_virtio_blk_class_init,
|
||||
};
|
||||
|
||||
/* PCI Devices */
|
||||
|
||||
struct VMAppleVirtIOBlkPCI {
|
||||
VirtIOPCIProxy parent_obj;
|
||||
|
||||
VMAppleVirtIOBlk vdev;
|
||||
VMAppleVirtioBlkVariant variant;
|
||||
};
|
||||
|
||||
static const Property vmapple_virtio_blk_pci_properties[] = {
|
||||
DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0),
|
||||
DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
|
||||
VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
|
||||
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
|
||||
DEV_NVECTORS_UNSPECIFIED),
|
||||
DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT("variant", VMAppleVirtIOBlkPCI, variant,
|
||||
VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED),
|
||||
};
|
||||
|
||||
static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev);
|
||||
DeviceState *vdev = DEVICE(&dev->vdev);
|
||||
VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf;
|
||||
|
||||
if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) {
|
||||
error_setg(errp, "vmapple virtio block device variant unspecified");
|
||||
error_append_hint(errp,
|
||||
"Variant property must be set to 'aux' or 'root'.\n"
|
||||
"Use a regular virtio-blk-pci device instead when "
|
||||
"neither is applicaple.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) {
|
||||
conf->num_queues = virtio_pci_optimal_num_queues(0);
|
||||
}
|
||||
|
||||
if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
|
||||
vpci_dev->nvectors = conf->num_queues + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't support zones, but we need the additional config space size.
|
||||
* Let's just expose the feature so the rest of the virtio-blk logic
|
||||
* allocates enough space for us. The guest will ignore zones anyway.
|
||||
*/
|
||||
virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED);
|
||||
/* Propagate the apple type down to the virtio-blk device */
|
||||
dev->vdev.apple_type = dev->variant;
|
||||
/* and spawn the virtio-blk device */
|
||||
qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
|
||||
|
||||
/*
|
||||
* The virtio-pci machinery adjusts its vendor/device ID based on whether
|
||||
* we support modern or legacy virtio. Let's patch it back to the Apple
|
||||
* identifiers here.
|
||||
*/
|
||||
pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE);
|
||||
pci_config_set_device_id(vpci_dev->pci_dev.config,
|
||||
PCI_DEVICE_ID_APPLE_VIRTIO_BLK);
|
||||
}
|
||||
|
||||
static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
|
||||
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
device_class_set_props(dc, vmapple_virtio_blk_pci_properties);
|
||||
k->realize = vmapple_virtio_blk_pci_realize;
|
||||
pcidev_k->vendor_id = PCI_VENDOR_ID_APPLE;
|
||||
pcidev_k->device_id = PCI_DEVICE_ID_APPLE_VIRTIO_BLK;
|
||||
pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
|
||||
pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
|
||||
}
|
||||
|
||||
static void vmapple_virtio_blk_pci_instance_init(Object *obj)
|
||||
{
|
||||
VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj);
|
||||
|
||||
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
||||
TYPE_VMAPPLE_VIRTIO_BLK);
|
||||
}
|
||||
|
||||
static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = {
|
||||
.generic_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI,
|
||||
.instance_size = sizeof(VMAppleVirtIOBlkPCI),
|
||||
.instance_init = vmapple_virtio_blk_pci_instance_init,
|
||||
.class_init = vmapple_virtio_blk_pci_class_init,
|
||||
};
|
||||
|
||||
static void vmapple_virtio_blk_register_types(void)
|
||||
{
|
||||
type_register_static(&vmapple_virtio_blk_info);
|
||||
virtio_pci_types_register(&vmapple_virtio_blk_pci_info);
|
||||
}
|
||||
|
||||
type_init(vmapple_virtio_blk_register_types)
|
618
hw/vmapple/vmapple.c
Normal file
618
hw/vmapple/vmapple.c
Normal file
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* VMApple machine emulation
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* VMApple is the device model that the macOS built-in hypervisor called
|
||||
* "Virtualization.framework" exposes to Apple Silicon macOS guests. The
|
||||
* machine model in this file implements the same device model in QEMU, but
|
||||
* does not use any code from Virtualization.Framework.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/datadir.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/guest-random.h"
|
||||
#include "qemu/help-texts.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/units.h"
|
||||
#include "monitor/qdev.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/arm/primecell.h"
|
||||
#include "hw/char/pl011.h"
|
||||
#include "hw/intc/arm_gic.h"
|
||||
#include "hw/intc/arm_gicv3_common.h"
|
||||
#include "hw/misc/pvpanic.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
#include "hw/usb/hcd-xhci-pci.h"
|
||||
#include "hw/virtio/virtio-pci.h"
|
||||
#include "hw/vmapple/vmapple.h"
|
||||
#include "net/net.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/qapi-visit-common.h"
|
||||
#include "qobject/qlist.h"
|
||||
#include "standard-headers/linux/input.h"
|
||||
#include "system/hvf.h"
|
||||
#include "system/reset.h"
|
||||
#include "system/runstate.h"
|
||||
#include "system/system.h"
|
||||
|
||||
struct VMAppleMachineState {
|
||||
MachineState parent;
|
||||
|
||||
Notifier machine_done;
|
||||
struct arm_boot_info bootinfo;
|
||||
const MemMapEntry *memmap;
|
||||
const int *irqmap;
|
||||
DeviceState *gic;
|
||||
DeviceState *cfg;
|
||||
DeviceState *pvpanic;
|
||||
Notifier powerdown_notifier;
|
||||
PCIBus *bus;
|
||||
MemoryRegion fw_mr;
|
||||
MemoryRegion ecam_alias;
|
||||
uint64_t uuid;
|
||||
};
|
||||
|
||||
#define TYPE_VMAPPLE_MACHINE MACHINE_TYPE_NAME("vmapple")
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VMAppleMachineState, VMAPPLE_MACHINE)
|
||||
|
||||
/* Number of external interrupt lines to configure the GIC with */
|
||||
#define NUM_IRQS 256
|
||||
|
||||
enum {
|
||||
VMAPPLE_FIRMWARE,
|
||||
VMAPPLE_CONFIG,
|
||||
VMAPPLE_MEM,
|
||||
VMAPPLE_GIC_DIST,
|
||||
VMAPPLE_GIC_REDIST,
|
||||
VMAPPLE_UART,
|
||||
VMAPPLE_RTC,
|
||||
VMAPPLE_PCIE,
|
||||
VMAPPLE_PCIE_MMIO,
|
||||
VMAPPLE_PCIE_ECAM,
|
||||
VMAPPLE_GPIO,
|
||||
VMAPPLE_PVPANIC,
|
||||
VMAPPLE_APV_GFX,
|
||||
VMAPPLE_APV_IOSFC,
|
||||
VMAPPLE_AES_1,
|
||||
VMAPPLE_AES_2,
|
||||
VMAPPLE_BDOOR,
|
||||
VMAPPLE_MEMMAP_LAST,
|
||||
};
|
||||
|
||||
static const MemMapEntry memmap[] = {
|
||||
[VMAPPLE_FIRMWARE] = { 0x00100000, 0x00100000 },
|
||||
[VMAPPLE_CONFIG] = { 0x00400000, 0x00010000 },
|
||||
|
||||
[VMAPPLE_GIC_DIST] = { 0x10000000, 0x00010000 },
|
||||
[VMAPPLE_GIC_REDIST] = { 0x10010000, 0x00400000 },
|
||||
|
||||
[VMAPPLE_UART] = { 0x20010000, 0x00010000 },
|
||||
[VMAPPLE_RTC] = { 0x20050000, 0x00001000 },
|
||||
[VMAPPLE_GPIO] = { 0x20060000, 0x00001000 },
|
||||
[VMAPPLE_PVPANIC] = { 0x20070000, 0x00000002 },
|
||||
[VMAPPLE_BDOOR] = { 0x30000000, 0x00200000 },
|
||||
[VMAPPLE_APV_GFX] = { 0x30200000, 0x00010000 },
|
||||
[VMAPPLE_APV_IOSFC] = { 0x30210000, 0x00010000 },
|
||||
[VMAPPLE_AES_1] = { 0x30220000, 0x00004000 },
|
||||
[VMAPPLE_AES_2] = { 0x30230000, 0x00004000 },
|
||||
[VMAPPLE_PCIE_ECAM] = { 0x40000000, 0x10000000 },
|
||||
[VMAPPLE_PCIE_MMIO] = { 0x50000000, 0x1fff0000 },
|
||||
|
||||
/* Actual RAM size depends on configuration */
|
||||
[VMAPPLE_MEM] = { 0x70000000ULL, GiB},
|
||||
};
|
||||
|
||||
static const int irqmap[] = {
|
||||
[VMAPPLE_UART] = 1,
|
||||
[VMAPPLE_RTC] = 2,
|
||||
[VMAPPLE_GPIO] = 0x5,
|
||||
[VMAPPLE_APV_IOSFC] = 0x10,
|
||||
[VMAPPLE_APV_GFX] = 0x11,
|
||||
[VMAPPLE_AES_1] = 0x12,
|
||||
[VMAPPLE_PCIE] = 0x20,
|
||||
};
|
||||
|
||||
#define GPEX_NUM_IRQS 16
|
||||
|
||||
static void create_bdif(VMAppleMachineState *vms, MemoryRegion *mem)
|
||||
{
|
||||
DeviceState *bdif;
|
||||
SysBusDevice *bdif_sb;
|
||||
DriveInfo *di_aux = drive_get(IF_PFLASH, 0, 0);
|
||||
DriveInfo *di_root = drive_get(IF_PFLASH, 0, 1);
|
||||
|
||||
if (!di_aux) {
|
||||
error_report("No AUX device. Please specify one as pflash drive.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!di_root) {
|
||||
/* Fall back to the first IF_VIRTIO device as root device */
|
||||
di_root = drive_get(IF_VIRTIO, 0, 0);
|
||||
}
|
||||
|
||||
if (!di_root) {
|
||||
error_report("No root device. Please specify one as virtio drive.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* PV backdoor device */
|
||||
bdif = qdev_new(TYPE_VMAPPLE_BDIF);
|
||||
bdif_sb = SYS_BUS_DEVICE(bdif);
|
||||
sysbus_mmio_map(bdif_sb, 0, vms->memmap[VMAPPLE_BDOOR].base);
|
||||
|
||||
qdev_prop_set_drive(DEVICE(bdif), "aux", blk_by_legacy_dinfo(di_aux));
|
||||
qdev_prop_set_drive(DEVICE(bdif), "root", blk_by_legacy_dinfo(di_root));
|
||||
|
||||
sysbus_realize_and_unref(bdif_sb, &error_fatal);
|
||||
}
|
||||
|
||||
static void create_pvpanic(VMAppleMachineState *vms, MemoryRegion *mem)
|
||||
{
|
||||
SysBusDevice *pvpanic;
|
||||
|
||||
vms->pvpanic = qdev_new(TYPE_PVPANIC_MMIO_DEVICE);
|
||||
pvpanic = SYS_BUS_DEVICE(vms->pvpanic);
|
||||
sysbus_mmio_map(pvpanic, 0, vms->memmap[VMAPPLE_PVPANIC].base);
|
||||
|
||||
sysbus_realize_and_unref(pvpanic, &error_fatal);
|
||||
}
|
||||
|
||||
static bool create_cfg(VMAppleMachineState *vms, MemoryRegion *mem,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
SysBusDevice *cfg;
|
||||
MachineState *machine = MACHINE(vms);
|
||||
uint32_t rnd = 1;
|
||||
|
||||
vms->cfg = qdev_new(TYPE_VMAPPLE_CFG);
|
||||
cfg = SYS_BUS_DEVICE(vms->cfg);
|
||||
sysbus_mmio_map(cfg, 0, vms->memmap[VMAPPLE_CONFIG].base);
|
||||
|
||||
qemu_guest_getrandom_nofail(&rnd, sizeof(rnd));
|
||||
|
||||
qdev_prop_set_uint32(vms->cfg, "nr-cpus", machine->smp.cpus);
|
||||
qdev_prop_set_uint64(vms->cfg, "ecid", vms->uuid);
|
||||
qdev_prop_set_uint64(vms->cfg, "ram-size", machine->ram_size);
|
||||
qdev_prop_set_uint32(vms->cfg, "rnd", rnd);
|
||||
|
||||
if (!sysbus_realize_and_unref(cfg, errp)) {
|
||||
error_prepend(errp, "Error creating vmapple cfg device: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void create_gfx(VMAppleMachineState *vms, MemoryRegion *mem)
|
||||
{
|
||||
int irq_gfx = vms->irqmap[VMAPPLE_APV_GFX];
|
||||
int irq_iosfc = vms->irqmap[VMAPPLE_APV_IOSFC];
|
||||
SysBusDevice *gfx;
|
||||
|
||||
gfx = SYS_BUS_DEVICE(qdev_new("apple-gfx-mmio"));
|
||||
sysbus_mmio_map(gfx, 0, vms->memmap[VMAPPLE_APV_GFX].base);
|
||||
sysbus_mmio_map(gfx, 1, vms->memmap[VMAPPLE_APV_IOSFC].base);
|
||||
sysbus_connect_irq(gfx, 0, qdev_get_gpio_in(vms->gic, irq_gfx));
|
||||
sysbus_connect_irq(gfx, 1, qdev_get_gpio_in(vms->gic, irq_iosfc));
|
||||
sysbus_realize_and_unref(gfx, &error_fatal);
|
||||
}
|
||||
|
||||
static void create_aes(VMAppleMachineState *vms, MemoryRegion *mem)
|
||||
{
|
||||
int irq = vms->irqmap[VMAPPLE_AES_1];
|
||||
SysBusDevice *aes;
|
||||
|
||||
aes = SYS_BUS_DEVICE(qdev_new(TYPE_APPLE_AES));
|
||||
sysbus_mmio_map(aes, 0, vms->memmap[VMAPPLE_AES_1].base);
|
||||
sysbus_mmio_map(aes, 1, vms->memmap[VMAPPLE_AES_2].base);
|
||||
sysbus_connect_irq(aes, 0, qdev_get_gpio_in(vms->gic, irq));
|
||||
sysbus_realize_and_unref(aes, &error_fatal);
|
||||
}
|
||||
|
||||
static int arm_gic_ppi_index(int cpu_nr, int ppi_index)
|
||||
{
|
||||
return NUM_IRQS + cpu_nr * GIC_INTERNAL + ppi_index;
|
||||
}
|
||||
|
||||
static void create_gic(VMAppleMachineState *vms, MemoryRegion *mem)
|
||||
{
|
||||
MachineState *ms = MACHINE(vms);
|
||||
/* We create a standalone GIC */
|
||||
SysBusDevice *gicbusdev;
|
||||
QList *redist_region_count;
|
||||
int i;
|
||||
unsigned int smp_cpus = ms->smp.cpus;
|
||||
|
||||
vms->gic = qdev_new(gicv3_class_name());
|
||||
qdev_prop_set_uint32(vms->gic, "revision", 3);
|
||||
qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus);
|
||||
/*
|
||||
* Note that the num-irq property counts both internal and external
|
||||
* interrupts; there are always 32 of the former (mandated by GIC spec).
|
||||
*/
|
||||
qdev_prop_set_uint32(vms->gic, "num-irq", NUM_IRQS + 32);
|
||||
|
||||
uint32_t redist0_capacity =
|
||||
vms->memmap[VMAPPLE_GIC_REDIST].size / GICV3_REDIST_SIZE;
|
||||
uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
|
||||
|
||||
redist_region_count = qlist_new();
|
||||
qlist_append_int(redist_region_count, redist0_count);
|
||||
qdev_prop_set_array(vms->gic, "redist-region-count", redist_region_count);
|
||||
|
||||
gicbusdev = SYS_BUS_DEVICE(vms->gic);
|
||||
sysbus_realize_and_unref(gicbusdev, &error_fatal);
|
||||
sysbus_mmio_map(gicbusdev, 0, vms->memmap[VMAPPLE_GIC_DIST].base);
|
||||
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VMAPPLE_GIC_REDIST].base);
|
||||
|
||||
/*
|
||||
* Wire the outputs from each CPU's generic timer and the GICv3
|
||||
* maintenance interrupt signal to the appropriate GIC PPI inputs,
|
||||
* and the GIC's IRQ/FIQ/VIRQ/VFIQ interrupt outputs to the CPU's inputs.
|
||||
*/
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
|
||||
|
||||
/* Map the virt timer to PPI 27 */
|
||||
qdev_connect_gpio_out(cpudev, GTIMER_VIRT,
|
||||
qdev_get_gpio_in(vms->gic,
|
||||
arm_gic_ppi_index(i, 27)));
|
||||
|
||||
/* Map the GIC IRQ and FIQ lines to CPU */
|
||||
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
|
||||
sysbus_connect_irq(gicbusdev, i + smp_cpus,
|
||||
qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
|
||||
}
|
||||
}
|
||||
|
||||
static void create_uart(const VMAppleMachineState *vms, int uart,
|
||||
MemoryRegion *mem, Chardev *chr)
|
||||
{
|
||||
hwaddr base = vms->memmap[uart].base;
|
||||
int irq = vms->irqmap[uart];
|
||||
DeviceState *dev = qdev_new(TYPE_PL011);
|
||||
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||
|
||||
qdev_prop_set_chr(dev, "chardev", chr);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
memory_region_add_subregion(mem, base,
|
||||
sysbus_mmio_get_region(s, 0));
|
||||
sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
|
||||
}
|
||||
|
||||
static void create_rtc(const VMAppleMachineState *vms)
|
||||
{
|
||||
hwaddr base = vms->memmap[VMAPPLE_RTC].base;
|
||||
int irq = vms->irqmap[VMAPPLE_RTC];
|
||||
|
||||
sysbus_create_simple("pl031", base, qdev_get_gpio_in(vms->gic, irq));
|
||||
}
|
||||
|
||||
static DeviceState *gpio_key_dev;
|
||||
static void vmapple_powerdown_req(Notifier *n, void *opaque)
|
||||
{
|
||||
/* use gpio Pin 3 for power button event */
|
||||
qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1);
|
||||
}
|
||||
|
||||
static void create_gpio_devices(const VMAppleMachineState *vms, int gpio,
|
||||
MemoryRegion *mem)
|
||||
{
|
||||
DeviceState *pl061_dev;
|
||||
hwaddr base = vms->memmap[gpio].base;
|
||||
int irq = vms->irqmap[gpio];
|
||||
SysBusDevice *s;
|
||||
|
||||
pl061_dev = qdev_new("pl061");
|
||||
/* Pull lines down to 0 if not driven by the PL061 */
|
||||
qdev_prop_set_uint32(pl061_dev, "pullups", 0);
|
||||
qdev_prop_set_uint32(pl061_dev, "pulldowns", 0xff);
|
||||
s = SYS_BUS_DEVICE(pl061_dev);
|
||||
sysbus_realize_and_unref(s, &error_fatal);
|
||||
memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0));
|
||||
sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
|
||||
gpio_key_dev = sysbus_create_simple("gpio-key", -1,
|
||||
qdev_get_gpio_in(pl061_dev, 3));
|
||||
}
|
||||
|
||||
static void vmapple_firmware_init(VMAppleMachineState *vms,
|
||||
MemoryRegion *sysmem)
|
||||
{
|
||||
hwaddr size = vms->memmap[VMAPPLE_FIRMWARE].size;
|
||||
hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base;
|
||||
const char *bios_name;
|
||||
int image_size;
|
||||
char *fname;
|
||||
|
||||
bios_name = MACHINE(vms)->firmware;
|
||||
if (!bios_name) {
|
||||
error_report("No firmware specified");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (!fname) {
|
||||
error_report("Could not find ROM image '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memory_region_init_ram(&vms->fw_mr, NULL, "firmware", size, &error_fatal);
|
||||
image_size = load_image_mr(fname, &vms->fw_mr);
|
||||
|
||||
g_free(fname);
|
||||
if (image_size < 0) {
|
||||
error_report("Could not load ROM image '%s'", bios_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memory_region_add_subregion(get_system_memory(), base, &vms->fw_mr);
|
||||
}
|
||||
|
||||
static void create_pcie(VMAppleMachineState *vms)
|
||||
{
|
||||
hwaddr base_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].base;
|
||||
hwaddr size_mmio = vms->memmap[VMAPPLE_PCIE_MMIO].size;
|
||||
hwaddr base_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].base;
|
||||
hwaddr size_ecam = vms->memmap[VMAPPLE_PCIE_ECAM].size;
|
||||
int irq = vms->irqmap[VMAPPLE_PCIE];
|
||||
MemoryRegion *mmio_alias;
|
||||
MemoryRegion *mmio_reg;
|
||||
MemoryRegion *ecam_reg;
|
||||
DeviceState *dev;
|
||||
int i;
|
||||
PCIHostState *pci;
|
||||
DeviceState *usb_controller;
|
||||
USBBus *usb_bus;
|
||||
|
||||
dev = qdev_new(TYPE_GPEX_HOST);
|
||||
qdev_prop_set_uint32(dev, "num-irqs", GPEX_NUM_IRQS);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
/* Map only the first size_ecam bytes of ECAM space */
|
||||
ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
|
||||
memory_region_init_alias(&vms->ecam_alias, OBJECT(dev), "pcie-ecam",
|
||||
ecam_reg, 0, size_ecam);
|
||||
memory_region_add_subregion(get_system_memory(), base_ecam,
|
||||
&vms->ecam_alias);
|
||||
|
||||
/*
|
||||
* Map the MMIO window from [0x50000000-0x7fff0000] in PCI space into
|
||||
* system address space at [0x50000000-0x7fff0000].
|
||||
*/
|
||||
mmio_alias = g_new0(MemoryRegion, 1);
|
||||
mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
|
||||
memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio",
|
||||
mmio_reg, base_mmio, size_mmio);
|
||||
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
|
||||
|
||||
for (i = 0; i < GPEX_NUM_IRQS; i++) {
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
|
||||
qdev_get_gpio_in(vms->gic, irq + i));
|
||||
gpex_set_irq_num(GPEX_HOST(dev), i, irq + i);
|
||||
}
|
||||
|
||||
pci = PCI_HOST_BRIDGE(dev);
|
||||
vms->bus = pci->bus;
|
||||
g_assert(vms->bus);
|
||||
|
||||
while ((dev = qemu_create_nic_device("virtio-net-pci", true, NULL))) {
|
||||
qdev_realize_and_unref(dev, BUS(vms->bus), &error_fatal);
|
||||
}
|
||||
|
||||
if (defaults_enabled()) {
|
||||
usb_controller = qdev_new(TYPE_QEMU_XHCI);
|
||||
qdev_realize_and_unref(usb_controller, BUS(pci->bus), &error_fatal);
|
||||
|
||||
usb_bus = USB_BUS(object_resolve_type_unambiguous(TYPE_USB_BUS,
|
||||
&error_fatal));
|
||||
usb_create_simple(usb_bus, "usb-kbd");
|
||||
usb_create_simple(usb_bus, "usb-tablet");
|
||||
}
|
||||
}
|
||||
|
||||
static void vmapple_reset(void *opaque)
|
||||
{
|
||||
VMAppleMachineState *vms = opaque;
|
||||
hwaddr base = vms->memmap[VMAPPLE_FIRMWARE].base;
|
||||
|
||||
cpu_set_pc(first_cpu, base);
|
||||
}
|
||||
|
||||
static void mach_vmapple_init(MachineState *machine)
|
||||
{
|
||||
VMAppleMachineState *vms = VMAPPLE_MACHINE(machine);
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
const CPUArchIdList *possible_cpus;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
int n;
|
||||
unsigned int smp_cpus = machine->smp.cpus;
|
||||
unsigned int max_cpus = machine->smp.max_cpus;
|
||||
|
||||
vms->memmap = memmap;
|
||||
machine->usb = true;
|
||||
|
||||
possible_cpus = mc->possible_cpu_arch_ids(machine);
|
||||
assert(possible_cpus->len == max_cpus);
|
||||
for (n = 0; n < possible_cpus->len; n++) {
|
||||
Object *cpu;
|
||||
CPUState *cs;
|
||||
|
||||
if (n >= smp_cpus) {
|
||||
break;
|
||||
}
|
||||
|
||||
cpu = object_new(possible_cpus->cpus[n].type);
|
||||
object_property_set_int(cpu, "mp-affinity",
|
||||
possible_cpus->cpus[n].arch_id, &error_fatal);
|
||||
|
||||
cs = CPU(cpu);
|
||||
cs->cpu_index = n;
|
||||
|
||||
numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpu),
|
||||
&error_fatal);
|
||||
|
||||
if (object_property_find(cpu, "has_el3")) {
|
||||
object_property_set_bool(cpu, "has_el3", false, &error_fatal);
|
||||
}
|
||||
if (object_property_find(cpu, "has_el2")) {
|
||||
object_property_set_bool(cpu, "has_el2", false, &error_fatal);
|
||||
}
|
||||
object_property_set_int(cpu, "psci-conduit", QEMU_PSCI_CONDUIT_HVC,
|
||||
&error_fatal);
|
||||
|
||||
/* Secondary CPUs start in PSCI powered-down state */
|
||||
if (n > 0) {
|
||||
object_property_set_bool(cpu, "start-powered-off", true,
|
||||
&error_fatal);
|
||||
}
|
||||
|
||||
object_property_set_link(cpu, "memory", OBJECT(sysmem), &error_abort);
|
||||
qdev_realize(DEVICE(cpu), NULL, &error_fatal);
|
||||
object_unref(cpu);
|
||||
}
|
||||
|
||||
memory_region_add_subregion(sysmem, vms->memmap[VMAPPLE_MEM].base,
|
||||
machine->ram);
|
||||
|
||||
create_gic(vms, sysmem);
|
||||
create_bdif(vms, sysmem);
|
||||
create_pvpanic(vms, sysmem);
|
||||
create_aes(vms, sysmem);
|
||||
create_gfx(vms, sysmem);
|
||||
create_uart(vms, VMAPPLE_UART, sysmem, serial_hd(0));
|
||||
create_rtc(vms);
|
||||
create_pcie(vms);
|
||||
|
||||
create_gpio_devices(vms, VMAPPLE_GPIO, sysmem);
|
||||
|
||||
vmapple_firmware_init(vms, sysmem);
|
||||
create_cfg(vms, sysmem, &error_fatal);
|
||||
|
||||
/* connect powerdown request */
|
||||
vms->powerdown_notifier.notify = vmapple_powerdown_req;
|
||||
qemu_register_powerdown_notifier(&vms->powerdown_notifier);
|
||||
|
||||
vms->bootinfo.ram_size = machine->ram_size;
|
||||
vms->bootinfo.board_id = -1;
|
||||
vms->bootinfo.loader_start = vms->memmap[VMAPPLE_MEM].base;
|
||||
vms->bootinfo.skip_dtb_autoload = true;
|
||||
vms->bootinfo.firmware_loaded = true;
|
||||
arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo);
|
||||
|
||||
qemu_register_reset(vmapple_reset, vms);
|
||||
}
|
||||
|
||||
static CpuInstanceProperties
|
||||
vmapple_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(ms);
|
||||
const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
|
||||
|
||||
assert(cpu_index < possible_cpus->len);
|
||||
return possible_cpus->cpus[cpu_index].props;
|
||||
}
|
||||
|
||||
|
||||
static int64_t vmapple_get_default_cpu_node_id(const MachineState *ms, int idx)
|
||||
{
|
||||
return idx % ms->numa_state->num_nodes;
|
||||
}
|
||||
|
||||
static const CPUArchIdList *vmapple_possible_cpu_arch_ids(MachineState *ms)
|
||||
{
|
||||
int n;
|
||||
unsigned int max_cpus = ms->smp.max_cpus;
|
||||
|
||||
if (ms->possible_cpus) {
|
||||
assert(ms->possible_cpus->len == max_cpus);
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
|
||||
ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
|
||||
sizeof(CPUArchId) * max_cpus);
|
||||
ms->possible_cpus->len = max_cpus;
|
||||
for (n = 0; n < ms->possible_cpus->len; n++) {
|
||||
ms->possible_cpus->cpus[n].type = ms->cpu_type;
|
||||
ms->possible_cpus->cpus[n].arch_id =
|
||||
arm_build_mp_affinity(n, GICV3_TARGETLIST_BITS);
|
||||
ms->possible_cpus->cpus[n].props.has_thread_id = true;
|
||||
ms->possible_cpus->cpus[n].props.thread_id = n;
|
||||
}
|
||||
return ms->possible_cpus;
|
||||
}
|
||||
|
||||
static GlobalProperty vmapple_compat_defaults[] = {
|
||||
{ TYPE_VIRTIO_PCI, "disable-legacy", "on" },
|
||||
/*
|
||||
* macOS XHCI driver attempts to schedule events onto even rings 1 & 2
|
||||
* even when (as here) there is no MSI(-X) support. Disabling interrupter
|
||||
* mapping in the XHCI controller works around the problem.
|
||||
*/
|
||||
{ TYPE_XHCI_PCI, "conditional-intr-mapping", "on" },
|
||||
};
|
||||
|
||||
static void vmapple_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->init = mach_vmapple_init;
|
||||
mc->max_cpus = 32;
|
||||
mc->block_default_type = IF_VIRTIO;
|
||||
mc->no_cdrom = 1;
|
||||
mc->pci_allow_0_address = true;
|
||||
mc->minimum_page_bits = 12;
|
||||
mc->possible_cpu_arch_ids = vmapple_possible_cpu_arch_ids;
|
||||
mc->cpu_index_to_instance_props = vmapple_cpu_index_to_props;
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("host");
|
||||
mc->get_default_cpu_node_id = vmapple_get_default_cpu_node_id;
|
||||
mc->default_ram_id = "mach-vmapple.ram";
|
||||
mc->desc = "Apple aarch64 Virtual Machine";
|
||||
|
||||
compat_props_add(mc->compat_props, vmapple_compat_defaults,
|
||||
G_N_ELEMENTS(vmapple_compat_defaults));
|
||||
}
|
||||
|
||||
static void vmapple_instance_init(Object *obj)
|
||||
{
|
||||
VMAppleMachineState *vms = VMAPPLE_MACHINE(obj);
|
||||
|
||||
vms->irqmap = irqmap;
|
||||
|
||||
object_property_add_uint64_ptr(obj, "uuid", &vms->uuid,
|
||||
OBJ_PROP_FLAG_READWRITE);
|
||||
object_property_set_description(obj, "uuid", "Machine UUID (SDOM)");
|
||||
}
|
||||
|
||||
static const TypeInfo vmapple_machine_info = {
|
||||
.name = TYPE_VMAPPLE_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(VMAppleMachineState),
|
||||
.class_init = vmapple_machine_class_init,
|
||||
.instance_init = vmapple_instance_init,
|
||||
};
|
||||
|
||||
static void machvmapple_machine_init(void)
|
||||
{
|
||||
type_register_static(&vmapple_machine_info);
|
||||
}
|
||||
type_init(machvmapple_machine_init);
|
||||
|
|
@ -1,14 +1,21 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/target_long.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/xen/xen-hvm-common.h"
|
||||
#include "hw/xen/xen-bus.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/xen/arch_hvm.h"
|
||||
#include "system/runstate.h"
|
||||
#include "system/system.h"
|
||||
#include "system/xen.h"
|
||||
#include "system/xen-mapcache.h"
|
||||
|
||||
MemoryRegion xen_memory, xen_grants;
|
||||
|
||||
|
|
|
@ -8,14 +8,13 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "system/system.h"
|
||||
#include "system/tpm.h"
|
||||
#include "system/tpm_backend.h"
|
||||
#include "system/runstate.h"
|
||||
#include "hw/xen/xen-pvh-common.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
|
|
@ -461,7 +461,7 @@ typedef struct Attributes {
|
|||
uint8_t psa_state;
|
||||
uint32_t psa_data_size;
|
||||
uint8_t ref_clk_gating_wait_time;
|
||||
uint8_t device_case_rough_temperaure;
|
||||
uint8_t device_case_rough_temperature;
|
||||
uint8_t device_too_high_temp_boundary;
|
||||
uint8_t device_too_low_temp_boundary;
|
||||
uint8_t throttling_status;
|
||||
|
@ -1073,6 +1073,11 @@ enum health_desc_param {
|
|||
UFS_HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
|
||||
};
|
||||
|
||||
enum {
|
||||
UFS_DEV_HIGH_TEMP_NOTIF = BIT(4),
|
||||
UFS_DEV_LOW_TEMP_NOTIF = BIT(5),
|
||||
};
|
||||
|
||||
/* WriteBooster buffer mode */
|
||||
enum {
|
||||
UFS_WB_BUF_MODE_LU_DEDICATED = 0x0,
|
||||
|
@ -1091,6 +1096,12 @@ enum ufs_lu_wp_type {
|
|||
UFS_LU_PERM_WP = 0x02,
|
||||
};
|
||||
|
||||
/* Exception event mask values */
|
||||
enum {
|
||||
MASK_EE_TOO_HIGH_TEMP = BIT(3),
|
||||
MASK_EE_TOO_LOW_TEMP = BIT(4),
|
||||
};
|
||||
|
||||
/* UTP QUERY Transaction Specific Fields OpCode */
|
||||
enum query_opcode {
|
||||
UFS_UPIU_QUERY_OPCODE_NOP = 0x0,
|
||||
|
|
|
@ -75,8 +75,6 @@ void acpi_build_hest(GArray *table_data, GArray *hardware_errors,
|
|||
void acpi_ghes_add_fw_cfg(AcpiGhesState *vms, FWCfgState *s,
|
||||
GArray *hardware_errors);
|
||||
int acpi_ghes_memory_errors(uint16_t source_id, uint64_t error_physical_addr);
|
||||
void ghes_record_cper_errors(const void *cper, size_t len,
|
||||
uint16_t source_id, Error **errp);
|
||||
|
||||
/**
|
||||
* acpi_ghes_present: Report whether ACPI GHES table is present
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#define TYPE_PVPANIC_ISA_DEVICE "pvpanic"
|
||||
#define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci"
|
||||
#define TYPE_PVPANIC_MMIO_DEVICE "pvpanic-mmio"
|
||||
|
||||
#define PVPANIC_IOPORT_PROP "ioport"
|
||||
|
||||
|
|
|
@ -191,6 +191,7 @@
|
|||
#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020
|
||||
#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b
|
||||
#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021
|
||||
#define PCI_DEVICE_ID_APPLE_VIRTIO_BLK 0x1a00
|
||||
|
||||
#define PCI_VENDOR_ID_SUN 0x108e
|
||||
#define PCI_DEVICE_ID_SUN_EBUS 0x1000
|
||||
|
|
|
@ -31,6 +31,7 @@ extern const PropertyInfo qdev_prop_pcie_link_width;
|
|||
extern const PropertyInfo qdev_prop_cpus390entitlement;
|
||||
extern const PropertyInfo qdev_prop_iothread_vq_mapping_list;
|
||||
extern const PropertyInfo qdev_prop_endian_mode;
|
||||
extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant;
|
||||
|
||||
#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \
|
||||
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t)
|
||||
|
@ -104,4 +105,9 @@ extern const PropertyInfo qdev_prop_endian_mode;
|
|||
#define DEFINE_PROP_ENDIAN_NODEFAULT(_name, _state, _field) \
|
||||
DEFINE_PROP_ENDIAN(_name, _state, _field, ENDIAN_MODE_UNSPECIFIED)
|
||||
|
||||
#define DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT(_name, _state, _fld, _default) \
|
||||
DEFINE_PROP_UNSIGNED(_name, _state, _fld, _default, \
|
||||
qdev_prop_vmapple_virtio_blk_variant, \
|
||||
VMAppleVirtioBlkVariant)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "qapi/qapi-types-virtio.h"
|
||||
|
||||
#define TYPE_VIRTIO_BLK "virtio-blk-device"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VirtIOBlock, VIRTIO_BLK)
|
||||
OBJECT_DECLARE_TYPE(VirtIOBlock, VirtIOBlkClass, VIRTIO_BLK)
|
||||
|
||||
/* This is the last element of the write scatter-gather list */
|
||||
struct virtio_blk_inhdr
|
||||
|
@ -100,6 +100,15 @@ typedef struct MultiReqBuffer {
|
|||
bool is_write;
|
||||
} MultiReqBuffer;
|
||||
|
||||
typedef struct VirtIOBlkClass {
|
||||
/*< private >*/
|
||||
VirtioDeviceClass parent;
|
||||
/*< public >*/
|
||||
bool (*handle_unknown_request)(VirtIOBlockReq *req, MultiReqBuffer *mrb,
|
||||
uint32_t type);
|
||||
} VirtIOBlkClass;
|
||||
|
||||
void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq);
|
||||
void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status);
|
||||
|
||||
#endif
|
||||
|
|
23
include/hw/vmapple/vmapple.h
Normal file
23
include/hw/vmapple/vmapple.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Devices specific to the VMApple machine type
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_VMAPPLE_VMAPPLE_H
|
||||
#define HW_VMAPPLE_VMAPPLE_H
|
||||
|
||||
#define TYPE_APPLE_AES "apple-aes"
|
||||
|
||||
#define TYPE_VMAPPLE_BDIF "vmapple-bdif"
|
||||
|
||||
#define TYPE_VMAPPLE_CFG "vmapple-cfg"
|
||||
|
||||
#define TYPE_VMAPPLE_VIRTIO_BLK_PCI "vmapple-virtio-blk-pci"
|
||||
|
||||
#endif /* HW_VMAPPLE_VMAPPLE_H */
|
|
@ -8,9 +8,10 @@
|
|||
#ifndef HW_XEN_BUS_H
|
||||
#define HW_XEN_BUS_H
|
||||
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/xen/xen_backend_ops.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
typedef struct XenEventChannel XenEventChannel;
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
#ifndef HW_XEN_HVM_COMMON_H
|
||||
#define HW_XEN_HVM_COMMON_H
|
||||
|
||||
#include "qemu/units.h"
|
||||
|
||||
#include "cpu.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "hw/xen/xen_native.h"
|
||||
#include "hw/xen/xen-legacy-backend.h"
|
||||
#include "system/runstate.h"
|
||||
#include "system/system.h"
|
||||
#include "system/xen.h"
|
||||
#include "system/xen-mapcache.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/xen/xen_backend_ops.h"
|
||||
#include <xen/hvm/ioreq.h>
|
||||
|
||||
extern MemoryRegion xen_memory;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "hw/xen/xen_backend_ops.h"
|
||||
#include "hw/xen/xen_pvdev.h"
|
||||
#include "net/net.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_XENSYSDEV "xen-sysdev"
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
#ifndef XEN_PVH_COMMON_H__
|
||||
#define XEN_PVH_COMMON_H__
|
||||
|
||||
#include <assert.h>
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/xen/xen-hvm-common.h"
|
||||
#include "exec/memory.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/pci-host/gpex.h"
|
||||
#include "hw/xen/xen-hvm-common.h"
|
||||
|
||||
#define TYPE_XEN_PVH_MACHINE MACHINE_TYPE_NAME("xen-pvh-base")
|
||||
OBJECT_DECLARE_TYPE(XenPVHMachineState, XenPVHMachineClass,
|
||||
|
|
|
@ -302,4 +302,19 @@ GString *qemu_hexdump_line(GString *str, const void *buf, size_t len,
|
|||
void qemu_hexdump(FILE *fp, const char *prefix,
|
||||
const void *bufptr, size_t size);
|
||||
|
||||
/**
|
||||
* qemu_hexdump_to_buffer:
|
||||
* @buffer: output string buffer
|
||||
* @buffer_size: amount of available space in buffer. Must be at least
|
||||
* data_size*2+1.
|
||||
* @data: input bytes
|
||||
* @data_size: number of bytes in data
|
||||
*
|
||||
* Converts the @data_size bytes in @data into hex digit pairs, writing them to
|
||||
* @buffer. Finally, a nul terminating character is written; @buffer therefore
|
||||
* needs space for (data_size*2+1) chars.
|
||||
*/
|
||||
void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size,
|
||||
const uint8_t *restrict data, size_t data_size);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3606,6 +3606,7 @@ if have_system
|
|||
'hw/usb',
|
||||
'hw/vfio',
|
||||
'hw/virtio',
|
||||
'hw/vmapple',
|
||||
'hw/watchdog',
|
||||
'hw/xen',
|
||||
'hw/gpio',
|
||||
|
|
|
@ -992,3 +992,17 @@
|
|||
##
|
||||
{ 'enum': 'GranuleMode',
|
||||
'data': [ '4k', '8k', '16k', '64k', 'host' ] }
|
||||
|
||||
##
|
||||
# @VMAppleVirtioBlkVariant:
|
||||
#
|
||||
# @unspecified: The default, not a valid setting.
|
||||
#
|
||||
# @root: Block device holding the root volume
|
||||
#
|
||||
# @aux: Block device holding auxiliary data required for boot
|
||||
#
|
||||
# Since: 9.2
|
||||
##
|
||||
{ 'enum': 'VMAppleVirtioBlkVariant',
|
||||
'data': [ 'unspecified', 'root', 'aux' ] }
|
||||
|
|
|
@ -784,6 +784,30 @@ static void ufstest_query_attr_request(void *obj, void *data,
|
|||
g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
|
||||
|
||||
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
|
||||
UFS_UPIU_QUERY_OPCODE_READ_ATTR,
|
||||
UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP, 0, 0, 0,
|
||||
&rsp_upiu);
|
||||
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00));
|
||||
|
||||
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
|
||||
UFS_UPIU_QUERY_OPCODE_READ_ATTR,
|
||||
UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND, 0, 0, 0,
|
||||
&rsp_upiu);
|
||||
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(160));
|
||||
|
||||
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
|
||||
UFS_UPIU_QUERY_OPCODE_READ_ATTR,
|
||||
UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND, 0, 0, 0,
|
||||
&rsp_upiu);
|
||||
g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS);
|
||||
g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(60));
|
||||
|
||||
/* Write Writable Attributes & Read Again */
|
||||
ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
|
||||
UFS_UPIU_QUERY_OPCODE_WRITE_ATTR,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
static inline char hexdump_nibble(unsigned x)
|
||||
{
|
||||
|
@ -97,3 +98,20 @@ void qemu_hexdump(FILE *fp, const char *prefix,
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
void qemu_hexdump_to_buffer(char *restrict buffer, size_t buffer_size,
|
||||
const uint8_t *restrict data, size_t data_size)
|
||||
{
|
||||
size_t i;
|
||||
uint64_t required_buffer_size;
|
||||
bool overflow = umul64_overflow(data_size, 2, &required_buffer_size);
|
||||
overflow |= uadd64_overflow(required_buffer_size, 1, &required_buffer_size);
|
||||
assert(!overflow && buffer_size >= required_buffer_size);
|
||||
|
||||
for (i = 0; i < data_size; i++) {
|
||||
uint8_t val = data[i];
|
||||
*(buffer++) = hexdump_nibble(val >> 4);
|
||||
*(buffer++) = hexdump_nibble(val & 0xf);
|
||||
}
|
||||
*buffer = '\0';
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue