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:
Stefan Hajnoczi 2025-03-05 21:54:58 +08:00
commit f5e6e13124
68 changed files with 2558 additions and 204 deletions

View file

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

View file

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

View 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

View file

@ -103,6 +103,7 @@ Board-specific documentation
arm/stellaris
arm/stm32
arm/virt
arm/vmapple
arm/xenpvh
arm/xlnx-versal-virt
arm/xlnx-zynq

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,6 +40,7 @@ subdir('ufs')
subdir('usb')
subdir('vfio')
subdir('virtio')
subdir('vmapple')
subdir('watchdog')
subdir('xen')
subdir('xenpv')

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,6 +40,7 @@ typedef struct XHCIPciState {
XHCIState xhci;
OnOffAuto msi;
OnOffAuto msix;
bool conditional_intr_mapping;
} XHCIPciState;
#endif

View file

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

View file

@ -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
View 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
View 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
View 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,
&sector, sizeof(sector),
MEMTXATTRS_UNSPECIFIED);
if (dma_result != MEMTX_OK) {
goto out;
}
le2cpu_sector(&sector);
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
View 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
View 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
View 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
View 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
View 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
View 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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3606,6 +3606,7 @@ if have_system
'hw/usb',
'hw/vfio',
'hw/virtio',
'hw/vmapple',
'hw/watchdog',
'hw/xen',
'hw/gpio',

View file

@ -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' ] }

View file

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

View file

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