Merge tag 'pull-parallels-2023-09-06' of https://src.openvz.org/scm/~den/qemu into staging
Parallels format driver changes:
* Fix comments formatting inside parallels driver
* Incorrect data end calculation in parallels_open()
* Check if data_end greater than the file size
* Add "explicit" argument to parallels_check_leak()
* Add data_start field to BDRVParallelsState
* Add checking and repairing duplicate offsets in BAT
* Image repairing in parallels_open()
* Use bdrv_co_getlength() in parallels_check_outside_image()
* Add data_off check
* Add data_off repairing to parallels_open()
* Fix record in MAINTAINERS
Parallels format driver tests:
* Add out-of-image check test for parallels format
* Add leak check test for parallels format
* Add test for BAT entries duplication check
* Refactor tests of parallels images checks (131)
* Fix cluster size in parallels images tests (131)
* Fix test 131 after repair was added to parallels_open()
* Add test for data_off check
# -----BEGIN PGP SIGNATURE-----
#
# iQHDBAABCgAtFiEE9vE2f3B8+RUZInytPzClrpN3nJ8FAmT4nUgPHGRlbkBvcGVu
# dnoub3JnAAoJED8wpa6Td5yf1F4L/j4RsGv+NRJRqZb9JNn2wUm4JdWGyv6ftuuh
# hT25F44B5S6J3tR3LalDFxHpr+kCXD1Xa3ZJNK14d1G9atw7Bsp5ntxpCmzEALBk
# 0PH+5fvNuhvt4ZnuYwQX70n3ZmalgzGpwf/jbs9mXUhdLinEr1RWi2f9yfCLmeZU
# x+0MSOhAdC6ZVsJOTJhGuRWWKL1q5KteuTwQlRCwDay8KF/Mc1OS/iPFqfmlWenM
# dc88PZBlg2Le15sWWNLc1AZHYguO+4xEPw6fk6RcswccILB2gCUPS6BJB0AuKNOO
# STPIgzUFMXfgIFhNUOvz58A7UnQGI4dMsRe/2UJIG+Y3qkM4DpjcZ7U/rHxhR6t0
# +GeeLS+a+aObz79TpB3gZi7leX2bpRUZ8nLkaAnL2umhtdFo5sdqD3xo4xcg4Ebk
# TbYSmgIM0eZ75d+48g7A+ddkyKYCmworGS9g9Cry6udclbs8yXhVB8KkUbYwtJlC
# HtNzgaWlw6J7n0MoSpz4OQVKq3bY0A==
# =grCk
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 06 Sep 2023 11:39:52 EDT
# gpg: using RSA key F6F1367F707CF91519227CAD3F30A5AE93779C9F
# gpg: issuer "den@openvz.org"
# gpg: Good signature from "Denis V. Lunev <den@openvz.org>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: F6F1 367F 707C F915 1922 7CAD 3F30 A5AE 9377 9C9F
* tag 'pull-parallels-2023-09-06' of https://src.openvz.org/scm/~den/qemu:
iotests: Add test for data_off check
iotests: Fix test 131 after repair was added to parallels_open()
iotests: Fix cluster size in parallels images tests (131)
iotests: Refactor tests of parallels images checks (131)
iotests: Add test for BAT entries duplication check
iotests: Add leak check test for parallels format
iotests: Add out-of-image check test for parallels format
parallels: Add data_off repairing to parallels_open()
parallels: Add data_off check
parallels: Use bdrv_co_getlength() in parallels_check_outside_image()
parallels: Image repairing in parallels_open()
parallels: Add checking and repairing duplicate offsets in BAT
parallels: Add data_start field to BDRVParallelsState
parallels: Add "explicit" argument to parallels_check_leak()
parallels: Check if data_end greater than the file size
parallels: Incorrect data end calculation in parallels_open()
parallels: Fix comments formatting inside parallels driver
MAINTAINERS: add tree to keep parallels format driver changes
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst
index 8053842..b2dea54 100644
--- a/docs/system/arm/aspeed.rst
+++ b/docs/system/arm/aspeed.rst
@@ -104,7 +104,7 @@
-dtb arch/arm/boot/dts/aspeed-ast2600-evb.dtb \
-initrd rootfs.cpio
-The image should be attached as an MTD drive. Run :
+To boot the machine from the flash image, use an MTD drive :
.. code-block:: bash
@@ -117,23 +117,46 @@
device by using the FMC controller to load the instructions, and
not simply from RAM. This takes a little longer.
- * ``fmc-model`` to change the FMC Flash model. FW needs support for
- the chip model to boot.
+ * ``fmc-model`` to change the default FMC Flash model. FW needs
+ support for the chip model to boot.
- * ``spi-model`` to change the SPI Flash model.
+ * ``spi-model`` to change the default SPI Flash model.
* ``bmc-console`` to change the default console device. Most of the
machines use the ``UART5`` device for a boot console, which is
mapped on ``/dev/ttyS4`` under Linux, but it is not always the
case.
-For instance, to start the ``ast2500-evb`` machine with a different
-FMC chip and a bigger (64M) SPI chip, use :
+To use other flash models, for instance a different FMC chip and a
+bigger (64M) SPI for the ``ast2500-evb`` machine, run :
.. code-block:: bash
-M ast2500-evb,fmc-model=mx25l25635e,spi-model=mx66u51235f
+When more flexibility is needed to define the flash devices, to use
+different flash models or define all flash devices (up to 8), the
+``-nodefaults`` QEMU option can be used to avoid creating the default
+flash devices.
+
+Flash devices should then be created from the command line and attached
+to a block device :
+
+.. code-block:: bash
+
+ $ qemu-system-arm -M ast2600-evb \
+ -blockdev node-name=fmc0,driver=file,filename=/path/to/fmc0.img \
+ -device mx66u51235f,bus=ssi.0,cs=0x0,drive=fmc0 \
+ -blockdev node-name=fmc1,driver=file,filename=/path/to/fmc1.img \
+ -device mx66u51235f,bus=ssi.0,cs=0x1,drive=fmc1 \
+ -blockdev node-name=spi1,driver=file,filename=/path/to/spi1.img \
+ -device mx66u51235f,cs=0x0,bus=ssi.1,drive=spi1 \
+ -nographic -nodefaults
+
+In that case, the machine boots fetching instructions from the FMC0
+device. It is slower to start but closer to what HW does. Using the
+machine option ``execute-in-place`` has a similar effect.
+
To change the boot console and use device ``UART3`` (``/dev/ttyS2``
under Linux), use :
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 263626a..f8ba675 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -15,6 +15,7 @@
#include "hw/arm/aspeed.h"
#include "hw/arm/aspeed_soc.h"
#include "hw/arm/aspeed_eeprom.h"
+#include "hw/block/flash.h"
#include "hw/i2c/i2c_mux_pca954x.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/misc/pca9552.h"
@@ -47,6 +48,13 @@
char *spi_model;
};
+/* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */
+#if HOST_LONG_BITS == 32
+#define ASPEED_RAM_SIZE(sz) MIN((sz), 1 * GiB)
+#else
+#define ASPEED_RAM_SIZE(sz) (sz)
+#endif
+
/* Palmetto hardware value: 0x120CE416 */
#define PALMETTO_BMC_HW_STRAP1 ( \
SCU_AST2400_HW_STRAP_DRAM_SIZE(DRAM_SIZE_256MB) | \
@@ -300,17 +308,14 @@
for (i = 0; i < count; ++i) {
DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i);
- qemu_irq cs_line;
DeviceState *dev;
dev = qdev_new(flashtype);
if (dinfo) {
qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo));
}
+ qdev_prop_set_uint8(dev, "cs", i);
qdev_realize_and_unref(dev, BUS(s->spi), &error_fatal);
-
- cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0);
- qdev_connect_gpio_out_named(DEVICE(s), "cs", i, cs_line);
}
}
@@ -392,12 +397,14 @@
connect_serial_hds_to_uarts(bmc);
qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort);
- aspeed_board_init_flashes(&bmc->soc.fmc,
+ if (defaults_enabled()) {
+ aspeed_board_init_flashes(&bmc->soc.fmc,
bmc->fmc_model ? bmc->fmc_model : amc->fmc_model,
amc->num_cs, 0);
- aspeed_board_init_flashes(&bmc->soc.spi[0],
+ aspeed_board_init_flashes(&bmc->soc.spi[0],
bmc->spi_model ? bmc->spi_model : amc->spi_model,
1, amc->num_cs);
+ }
if (machine->kernel_filename && sc->num_cpus > 1) {
/* With no u-boot we must set up a boot stub for the secondary CPU */
@@ -430,11 +437,12 @@
}
if (!bmc->mmio_exec) {
- DriveInfo *mtd0 = drive_get(IF_MTD, 0, 0);
+ DeviceState *dev = ssi_get_cs(bmc->soc.fmc.spi, 0);
+ BlockBackend *fmc0 = dev ? m25p80_get_blk(dev) : NULL;
- if (mtd0) {
+ if (fmc0) {
uint64_t rom_size = memory_region_size(&bmc->soc.spi_boot);
- aspeed_install_boot_rom(bmc, blk_by_legacy_dinfo(mtd0), rom_size);
+ aspeed_install_boot_rom(bmc, fmc0, rom_size);
}
}
@@ -1423,12 +1431,7 @@
aspeed_soc_num_cpus(amc->soc_name);
};
-/* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */
-#if HOST_LONG_BITS == 32
-#define FUJI_BMC_RAM_SIZE (1 * GiB)
-#else
-#define FUJI_BMC_RAM_SIZE (2 * GiB)
-#endif
+#define FUJI_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data)
{
@@ -1450,12 +1453,7 @@
aspeed_soc_num_cpus(amc->soc_name);
};
-/* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */
-#if HOST_LONG_BITS == 32
-#define BLETCHLEY_BMC_RAM_SIZE (1 * GiB)
-#else
-#define BLETCHLEY_BMC_RAM_SIZE (2 * GiB)
-#endif
+#define BLETCHLEY_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB)
static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data)
{
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index f7e99ba..aa5b0dd 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -1235,14 +1235,15 @@
dinfo = drive_get(IF_SD, 0, 0);
blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
- carddev = qdev_new(TYPE_SD_CARD);
+ carddev = qdev_new(TYPE_SD_CARD_SPI);
qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
- qdev_prop_set_bit(carddev, "spi", true);
qdev_realize_and_unref(carddev,
qdev_get_child_bus(sddev, "sd-bus"),
&error_fatal);
- ssddev = ssi_create_peripheral(bus, "ssd0323");
+ ssddev = qdev_new("ssd0323");
+ qdev_prop_set_uint8(ssddev, "cs", 1);
+ qdev_realize_and_unref(ssddev, bus, &error_fatal);
gpio_d_splitter = qdev_new(TYPE_SPLIT_IRQ);
qdev_prop_set_uint32(gpio_d_splitter, "num-lines", 2);
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 3190cc0..8dc2ea8 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -164,6 +164,7 @@
blk_by_legacy_dinfo(dinfo),
&error_fatal);
}
+ qdev_prop_set_uint8(flash_dev, "cs", j);
qdev_realize_and_unref(flash_dev, BUS(spi), &error_fatal);
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c
index 1ee2b86..88c561f 100644
--- a/hw/arm/xlnx-versal-virt.c
+++ b/hw/arm/xlnx-versal-virt.c
@@ -740,6 +740,7 @@
qdev_prop_set_drive_err(flash_dev, "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
}
+ qdev_prop_set_uint8(flash_dev, "cs", i);
qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal);
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index 4c84bb9..21483f7 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -201,6 +201,7 @@
qdev_prop_set_drive_err(flash_dev, "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
}
+ qdev_prop_set_uint8(flash_dev, "cs", i);
qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal);
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
@@ -224,6 +225,7 @@
qdev_prop_set_drive_err(flash_dev, "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
}
+ qdev_prop_set_uint8(flash_dev, "cs", i);
qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal);
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index dc5ffbc..afc3fdf 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -25,6 +25,7 @@
#include "qemu/units.h"
#include "sysemu/block-backend.h"
#include "hw/block/block.h"
+#include "hw/block/flash.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/ssi/ssi.h"
@@ -1830,3 +1831,8 @@
}
type_init(m25p80_register_types)
+
+BlockBackend *m25p80_get_blk(DeviceState *dev)
+{
+ return M25P80(dev)->blk;
+}
diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c
index 1f071a3..7275d40 100644
--- a/hw/i2c/aspeed_i2c.c
+++ b/hw/i2c/aspeed_i2c.c
@@ -226,7 +226,7 @@
return 0;
}
-static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start)
+static int aspeed_i2c_bus_send(AspeedI2CBus *bus)
{
AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
int ret = -1;
@@ -236,10 +236,10 @@
uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus);
uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus);
int pool_tx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl,
- TX_COUNT);
+ TX_COUNT) + 1;
if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) {
- for (i = pool_start; i < pool_tx_count; i++) {
+ for (i = 0; i < pool_tx_count; i++) {
uint8_t *pool_base = aic->bus_pool_base(bus);
trace_aspeed_i2c_bus_send("BUF", i + 1, pool_tx_count,
@@ -273,7 +273,7 @@
}
SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0);
} else {
- trace_aspeed_i2c_bus_send("BYTE", pool_start, 1,
+ trace_aspeed_i2c_bus_send("BYTE", 0, 1,
bus->regs[reg_byte_buf]);
ret = i2c_send(bus->bus, bus->regs[reg_byte_buf]);
}
@@ -293,10 +293,14 @@
uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus);
uint32_t reg_dma_addr = aspeed_i2c_bus_dma_addr_offset(bus);
int pool_rx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl,
- RX_COUNT);
+ RX_SIZE) + 1;
if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) {
uint8_t *pool_base = aic->bus_pool_base(bus);
+ if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl,
+ BUF_ORGANIZATION)) {
+ pool_base += 16;
+ }
for (i = 0; i < pool_rx_count; i++) {
pool_base[i] = i2c_recv(bus->bus);
@@ -418,7 +422,7 @@
uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus);
uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus);
if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_BUFF_EN)) {
- count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT);
+ count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) + 1;
} else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) {
count = bus->regs[reg_dma_len];
} else { /* BYTE mode */
@@ -446,10 +450,8 @@
*/
static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value)
{
- uint8_t pool_start = 0;
uint32_t reg_intr_sts = aspeed_i2c_bus_intr_sts_offset(bus);
uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus);
- uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus);
uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus);
if (!aspeed_i2c_check_sram(bus)) {
@@ -483,27 +485,11 @@
SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_START_CMD, 0);
- /*
- * The START command is also a TX command, as the slave
- * address is sent on the bus. Drop the TX flag if nothing
- * else needs to be sent in this sequence.
- */
- if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) {
- if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT)
- == 1) {
- SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0);
- } else {
- /*
- * Increase the start index in the TX pool buffer to
- * skip the address byte.
- */
- pool_start++;
- }
- } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) {
+ if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) {
if (bus->regs[reg_dma_len] == 0) {
SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0);
}
- } else {
+ } else if (!SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) {
SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0);
}
@@ -520,7 +506,7 @@
if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, M_TX_CMD)) {
aspeed_i2c_set_state(bus, I2CD_MTXD);
- if (aspeed_i2c_bus_send(bus, pool_start)) {
+ if (aspeed_i2c_bus_send(bus)) {
SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1);
i2c_end_transfer(bus->bus);
} else {
diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c
index e536b3e..9b10e90 100644
--- a/hw/intc/pnv_xive.c
+++ b/hw/intc/pnv_xive.c
@@ -85,28 +85,6 @@
}
/*
- * Remote access to controllers. HW uses MMIOs. For now, a simple scan
- * of the chips is good enough.
- *
- * TODO: Block scope support
- */
-static PnvXive *pnv_xive_get_remote(uint8_t blk)
-{
- PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
- int i;
-
- for (i = 0; i < pnv->num_chips; i++) {
- Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]);
- PnvXive *xive = &chip9->xive;
-
- if (pnv_xive_block_id(xive) == blk) {
- return xive;
- }
- }
- return NULL;
-}
-
-/*
* VST accessors for SBE, EAT, ENDT, NVT
*
* Indirect VST tables are arrays of VSDs pointing to a page (of same
@@ -209,6 +187,42 @@
return pnv_xive_vst_addr_direct(xive, type, vsd, (idx % vst_per_page));
}
+/*
+ * This is a simplified model of operation forwarding on a remote IC.
+ *
+ * A PC MMIO address is built to identify the NVT structure. The load
+ * on the remote IC will return the address of the structure in RAM,
+ * which will then be used by pnv_xive_vst_write/read to perform the
+ * RAM operation.
+ */
+static uint64_t pnv_xive_vst_addr_remote(PnvXive *xive, uint32_t type,
+ uint64_t vsd, uint8_t blk,
+ uint32_t idx)
+{
+ const XiveVstInfo *info = &vst_infos[type];
+ uint64_t remote_addr = vsd & VSD_ADDRESS_MASK;
+ uint64_t vst_addr;
+ MemTxResult result;
+
+ if (type != VST_TSEL_VPDT) {
+ xive_error(xive, "VST: invalid access on remote VST %s %x/%x !?",
+ info->name, blk, idx);
+ return 0;
+ }
+
+ remote_addr |= idx << xive->pc_shift;
+
+ vst_addr = address_space_ldq_be(&address_space_memory, remote_addr,
+ MEMTXATTRS_UNSPECIFIED, &result);
+ if (result != MEMTX_OK) {
+ xive_error(xive, "VST: read failed at @0x%" HWADDR_PRIx
+ " for NVT %x/%x\n", remote_addr, blk, idx);
+ return 0;
+ }
+
+ return vst_addr;
+}
+
static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk,
uint32_t idx)
{
@@ -225,9 +239,7 @@
/* Remote VST access */
if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) {
- xive = pnv_xive_get_remote(blk);
-
- return xive ? pnv_xive_vst_addr(xive, type, blk, idx) : 0;
+ return pnv_xive_vst_addr_remote(xive, type, vsd, blk, idx);
}
if (VSD_INDIRECT & vsd) {
@@ -242,12 +254,20 @@
{
const XiveVstInfo *info = &vst_infos[type];
uint64_t addr = pnv_xive_vst_addr(xive, type, blk, idx);
+ MemTxResult result;
if (!addr) {
return -1;
}
- cpu_physical_memory_read(addr, data, info->size);
+ result = address_space_read(&address_space_memory, addr,
+ MEMTXATTRS_UNSPECIFIED, data,
+ info->size);
+ if (result != MEMTX_OK) {
+ xive_error(xive, "VST: read failed at @0x%" HWADDR_PRIx
+ " for VST %s %x/%x\n", addr, info->name, blk, idx);
+ return -1;
+ }
return 0;
}
@@ -258,16 +278,27 @@
{
const XiveVstInfo *info = &vst_infos[type];
uint64_t addr = pnv_xive_vst_addr(xive, type, blk, idx);
+ MemTxResult result;
if (!addr) {
return -1;
}
if (word_number == XIVE_VST_WORD_ALL) {
- cpu_physical_memory_write(addr, data, info->size);
+ result = address_space_write(&address_space_memory, addr,
+ MEMTXATTRS_UNSPECIFIED, data,
+ info->size);
} else {
- cpu_physical_memory_write(addr + word_number * 4,
- data + word_number * 4, 4);
+ result = address_space_write(&address_space_memory,
+ addr + word_number * 4,
+ MEMTXATTRS_UNSPECIFIED,
+ data + word_number * 4, 4);
+ }
+
+ if (result != MEMTX_OK) {
+ xive_error(xive, "VST: write failed at @0x%" HWADDR_PRIx
+ "for VST %s %x/%x\n", addr, info->name, blk, idx);
+ return -1;
}
return 0;
}
@@ -275,12 +306,26 @@
static int pnv_xive_get_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
XiveEND *end)
{
+ PnvXive *xive = PNV_XIVE(xrtr);
+
+ if (pnv_xive_block_id(xive) != blk) {
+ xive_error(xive, "VST: END %x/%x is remote !?", blk, idx);
+ return -1;
+ }
+
return pnv_xive_vst_read(PNV_XIVE(xrtr), VST_TSEL_EQDT, blk, idx, end);
}
static int pnv_xive_write_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx,
XiveEND *end, uint8_t word_number)
{
+ PnvXive *xive = PNV_XIVE(xrtr);
+
+ if (pnv_xive_block_id(xive) != blk) {
+ xive_error(xive, "VST: END %x/%x is remote !?", blk, idx);
+ return -1;
+ }
+
return pnv_xive_vst_write(PNV_XIVE(xrtr), VST_TSEL_EQDT, blk, idx, end,
word_number);
}
@@ -1349,6 +1394,50 @@
#define PNV_XIVE_SYNC_PUSH 0xf00 /* Sync push context */
#define PNV_XIVE_SYNC_VPC 0xf80 /* Sync remove VPC store */
+static void pnv_xive_end_notify(XiveRouter *xrtr, XiveEAS *eas)
+{
+ PnvXive *xive = PNV_XIVE(xrtr);
+ uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w);
+ uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w);
+ uint32_t end_data = xive_get_field64(EAS_END_DATA, eas->w);
+ uint64_t end_vsd = xive->vsds[VST_TSEL_EQDT][end_blk];
+
+ switch (GETFIELD(VSD_MODE, end_vsd)) {
+ case VSD_MODE_EXCLUSIVE:
+ /* Perform the END notification on the local IC. */
+ xive_router_end_notify(xrtr, eas);
+ break;
+
+ case VSD_MODE_FORWARD: {
+ MemTxResult result;
+ uint64_t notif_port = end_vsd & VSD_ADDRESS_MASK;
+ uint64_t data = XIVE_TRIGGER_END | XIVE_TRIGGER_PQ |
+ be64_to_cpu(eas->w);
+
+ /* Forward the store on the remote IC notify page. */
+ address_space_stq_be(&address_space_memory, notif_port, data,
+ MEMTXATTRS_UNSPECIFIED, &result);
+ if (result != MEMTX_OK) {
+ xive_error(xive, "IC: Forward notif END %x/%x [%x] failed @%"
+ HWADDR_PRIx, end_blk, end_idx, end_data, notif_port);
+ return;
+ }
+ break;
+ }
+
+ case VSD_MODE_INVALID:
+ default:
+ /* Set FIR */
+ xive_error(xive, "IC: Invalid END VSD for block %x", end_blk);
+ return;
+ }
+}
+
+/*
+ * The notify page can either be used to receive trigger events from
+ * the HW controllers (PHB, PSI) or to reroute interrupts between
+ * Interrupt controllers.
+ */
static void pnv_xive_ic_hw_trigger(PnvXive *xive, hwaddr addr, uint64_t val)
{
uint8_t blk;
@@ -1357,8 +1446,8 @@
trace_pnv_xive_ic_hw_trigger(addr, val);
if (val & XIVE_TRIGGER_END) {
- xive_error(xive, "IC: END trigger at @0x%"HWADDR_PRIx" data 0x%"PRIx64,
- addr, val);
+ val = cpu_to_be64(val);
+ pnv_xive_end_notify(XIVE_ROUTER(xive), (XiveEAS *) &val);
return;
}
@@ -1703,16 +1792,20 @@
};
/*
- * Presenter Controller MMIO region. The Virtualization Controller
- * updates the IPB in the NVT table when required. Not modeled.
+ * Presenter Controller MMIO region. Points to the NVT sets.
+ *
+ * HW implements all possible mem ops to the underlying NVT structure
+ * but QEMU does not need to be so precise. The model implementation
+ * simply returns the RAM address of the NVT structure which is then
+ * used by pnv_xive_vst_write/read to perform the RAM operation.
*/
-static uint64_t pnv_xive_pc_read(void *opaque, hwaddr addr,
- unsigned size)
+static uint64_t pnv_xive_pc_read(void *opaque, hwaddr offset, unsigned size)
{
PnvXive *xive = PNV_XIVE(opaque);
+ uint32_t nvt_idx = offset >> xive->pc_shift;
+ uint8_t blk = pnv_xive_block_id(xive); /* TODO: VDT -> block xlate */
- xive_error(xive, "PC: invalid read @%"HWADDR_PRIx, addr);
- return -1;
+ return pnv_xive_vst_addr(xive, VST_TSEL_VPDT, blk, nvt_idx);
}
static void pnv_xive_pc_write(void *opaque, hwaddr addr,
@@ -1898,6 +1991,7 @@
memory_region_init_io(&xive->ic_notify_mmio, OBJECT(dev),
&pnv_xive_ic_notify_ops,
xive, "xive-ic-notify", 1 << xive->ic_shift);
+ xive->ic_notify_mmio.disable_reentrancy_guard = true;
/* The Pervasive LSI trigger and EOI pages (not modeled) */
memory_region_init_io(&xive->ic_lsi_mmio, OBJECT(dev), &pnv_xive_ic_lsi_ops,
@@ -1933,6 +2027,7 @@
/* Presenter Controller MMIO region (not modeled) */
memory_region_init_io(&xive->pc_mmio, OBJECT(xive), &pnv_xive_pc_ops, xive,
"xive-pc", PNV9_XIVE_PC_SIZE);
+ xive->pc_mmio.disable_reentrancy_guard = true;
/* Thread Interrupt Management Area (Direct) */
memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &pnv_xive_tm_ops,
@@ -1998,6 +2093,7 @@
xrc->get_nvt = pnv_xive_get_nvt;
xrc->write_nvt = pnv_xive_write_nvt;
xrc->get_block_id = pnv_xive_get_block_id;
+ xrc->end_notify = pnv_xive_end_notify;
xnc->notify = pnv_xive_notify;
xpc->match_nvt = pnv_xive_match_nvt;
diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c
index bbb44a5..4b8d0a5 100644
--- a/hw/intc/pnv_xive2.c
+++ b/hw/intc/pnv_xive2.c
@@ -240,12 +240,20 @@
{
const XiveVstInfo *info = &vst_infos[type];
uint64_t addr = pnv_xive2_vst_addr(xive, type, blk, idx);
+ MemTxResult result;
if (!addr) {
return -1;
}
- cpu_physical_memory_read(addr, data, info->size);
+ result = address_space_read(&address_space_memory, addr,
+ MEMTXATTRS_UNSPECIFIED, data,
+ info->size);
+ if (result != MEMTX_OK) {
+ xive2_error(xive, "VST: read failed at @0x%" HWADDR_PRIx
+ " for VST %s %x/%x\n", addr, info->name, blk, idx);
+ return -1;
+ }
return 0;
}
@@ -256,16 +264,27 @@
{
const XiveVstInfo *info = &vst_infos[type];
uint64_t addr = pnv_xive2_vst_addr(xive, type, blk, idx);
+ MemTxResult result;
if (!addr) {
return -1;
}
if (word_number == XIVE_VST_WORD_ALL) {
- cpu_physical_memory_write(addr, data, info->size);
+ result = address_space_write(&address_space_memory, addr,
+ MEMTXATTRS_UNSPECIFIED, data,
+ info->size);
} else {
- cpu_physical_memory_write(addr + word_number * 4,
- data + word_number * 4, 4);
+ result = address_space_write(&address_space_memory,
+ addr + word_number * 4,
+ MEMTXATTRS_UNSPECIFIED,
+ data + word_number * 4, 4);
+ }
+
+ if (result != MEMTX_OK) {
+ xive2_error(xive, "VST: write failed at @0x%" HWADDR_PRIx
+ "for VST %s %x/%x\n", addr, info->name, blk, idx);
+ return -1;
}
return 0;
}
diff --git a/hw/intc/pnv_xive_regs.h b/hw/intc/pnv_xive_regs.h
index c78f030..7938476 100644
--- a/hw/intc/pnv_xive_regs.h
+++ b/hw/intc/pnv_xive_regs.h
@@ -228,6 +228,7 @@
* VSD and is only meant to be used in indirect mode !
*/
#define VSD_MODE PPC_BITMASK(0, 1)
+#define VSD_MODE_INVALID 0
#define VSD_MODE_SHARED 1
#define VSD_MODE_EXCLUSIVE 2
#define VSD_MODE_FORWARD 3
diff --git a/hw/intc/xive.c b/hw/intc/xive.c
index 56670b2..df3ee04 100644
--- a/hw/intc/xive.c
+++ b/hw/intc/xive.c
@@ -1518,6 +1518,13 @@
assert(xrtr->xfb);
}
+static void xive_router_end_notify_handler(XiveRouter *xrtr, XiveEAS *eas)
+{
+ XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr);
+
+ return xrc->end_notify(xrtr, eas);
+}
+
/*
* Encode the HW CAM line in the block group mode format :
*
@@ -1664,8 +1671,7 @@
* another chip. We don't model the PowerBus but the END trigger
* message has the same parameters than in the function below.
*/
-static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk,
- uint32_t end_idx, uint32_t end_data)
+void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas)
{
XiveEND end;
uint8_t priority;
@@ -1675,6 +1681,10 @@
XiveNVT nvt;
bool found;
+ uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w);
+ uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w);
+ uint32_t end_data = xive_get_field64(EAS_END_DATA, eas->w);
+
/* END cache lookup */
if (xive_router_get_end(xrtr, end_blk, end_idx, &end)) {
qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk,
@@ -1817,10 +1827,7 @@
/*
* The END trigger becomes an Escalation trigger
*/
- xive_router_end_notify(xrtr,
- xive_get_field32(END_W4_ESC_END_BLOCK, end.w4),
- xive_get_field32(END_W4_ESC_END_INDEX, end.w4),
- xive_get_field32(END_W5_ESC_END_DATA, end.w5));
+ xive_router_end_notify_handler(xrtr, (XiveEAS *) &end.w4);
}
void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked)
@@ -1871,10 +1878,7 @@
/*
* The event trigger becomes an END trigger
*/
- xive_router_end_notify(xrtr,
- xive_get_field64(EAS_END_BLOCK, eas.w),
- xive_get_field64(EAS_END_INDEX, eas.w),
- xive_get_field64(EAS_END_DATA, eas.w));
+ xive_router_end_notify_handler(xrtr, &eas);
}
static Property xive_router_properties[] = {
@@ -1887,12 +1891,16 @@
{
DeviceClass *dc = DEVICE_CLASS(klass);
XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass);
+ XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass);
dc->desc = "XIVE Router Engine";
device_class_set_props(dc, xive_router_properties);
/* Parent is SysBusDeviceClass. No need to call its realize hook */
dc->realize = xive_router_realize;
xnc->notify = xive_router_notify;
+
+ /* By default, the router handles END triggers locally */
+ xrc->end_notify = xive_router_end_notify;
}
static const TypeInfo xive_router_info = {
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index babb053..ea0fb68 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -192,6 +192,7 @@
blk_by_legacy_dinfo(dinfo),
&error_fatal);
}
+ qdev_prop_set_uint8(dev, "cs", i);
qdev_realize_and_unref(dev, BUS(spi), &error_fatal);
cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0);
diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c
index 67793a8..d5b6820 100644
--- a/hw/ppc/e500.c
+++ b/hw/ppc/e500.c
@@ -712,7 +712,7 @@
p->kernel_base = kernel_base;
p->kernel_size = kernel_size;
- qemu_register_reset(ppce500_reset_device_tree, p);
+ qemu_register_reset_nosnapshotload(ppce500_reset_device_tree, p);
p->notifier.notify = ppce500_init_notify;
qemu_add_machine_init_done_notifier(&p->notifier);
diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c
index 510ff0e..9acc7ad 100644
--- a/hw/ppc/mac_oldworld.c
+++ b/hw/ppc/mac_oldworld.c
@@ -81,6 +81,7 @@
{
PowerPCCPU *cpu = opaque;
+ cpu_ppc_tb_reset(&cpu->env);
cpu_reset(CPU(cpu));
}
diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c
index 075367d..bd397cf 100644
--- a/hw/ppc/pegasos2.c
+++ b/hw/ppc/pegasos2.c
@@ -99,6 +99,7 @@
cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20;
cpu->env.nip = 0x100;
}
+ cpu_ppc_tb_reset(&cpu->env);
}
static void pegasos2_pci_irq(void *opaque, int n, int level)
diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c
index 9b39d52..8c7afe0 100644
--- a/hw/ppc/pnv_core.c
+++ b/hw/ppc/pnv_core.c
@@ -61,6 +61,8 @@
hreg_compute_hflags(env);
ppc_maybe_interrupt(env);
+ cpu_ppc_tb_reset(env);
+
pcc->intc_reset(pc->chip, cpu);
}
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index 0e0a3d9..aeb116d 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -32,6 +32,7 @@
#include "qemu/main-loop.h"
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
+#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "kvm_ppc.h"
#include "migration/vmstate.h"
@@ -58,7 +59,9 @@
if (old_pending != env->pending_interrupts) {
ppc_maybe_interrupt(env);
- kvmppc_set_interrupt(cpu, irq, level);
+ if (kvm_enabled()) {
+ kvmppc_set_interrupt(cpu, irq, level);
+ }
}
trace_ppc_irq_set_exit(env, irq, level, env->pending_interrupts,
@@ -482,10 +485,32 @@
/*****************************************************************************/
/* PowerPC time base and decrementer emulation */
+/*
+ * Conversion between QEMU_CLOCK_VIRTUAL ns and timebase (TB) ticks:
+ * TB ticks are arrived at by multiplying tb_freq then dividing by
+ * ns per second, and rounding down. TB ticks drive all clocks and
+ * timers in the target machine.
+ *
+ * Converting TB intervals to ns for the purpose of setting a
+ * QEMU_CLOCK_VIRTUAL timer should go the other way, but rounding
+ * up. Rounding down could cause the timer to fire before the TB
+ * value has been reached.
+ */
+static uint64_t ns_to_tb(uint32_t freq, int64_t clock)
+{
+ return muldiv64(clock, freq, NANOSECONDS_PER_SECOND);
+}
+
+/* virtual clock in TB ticks, not adjusted by TB offset */
+static int64_t tb_to_ns_round_up(uint32_t freq, uint64_t tb)
+{
+ return muldiv64_round_up(tb, NANOSECONDS_PER_SECOND, freq);
+}
+
uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
{
/* TB time in tb periods */
- return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset;
+ return ns_to_tb(tb_env->tb_freq, vmclk) + tb_offset;
}
uint64_t cpu_ppc_load_tbl (CPUPPCState *env)
@@ -497,7 +522,8 @@
return env->spr[SPR_TBL];
}
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset);
+ tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ tb_env->tb_offset);
trace_ppc_tb_load(tb);
return tb;
@@ -508,7 +534,8 @@
ppc_tb_t *tb_env = env->tb_env;
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset);
+ tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ tb_env->tb_offset);
trace_ppc_tb_load(tb);
return tb >> 32;
@@ -526,8 +553,7 @@
static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
int64_t *tb_offsetp, uint64_t value)
{
- *tb_offsetp = value -
- muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND);
+ *tb_offsetp = value - ns_to_tb(tb_env->tb_freq, vmclk);
trace_ppc_tb_store(value, *tb_offsetp);
}
@@ -565,7 +591,8 @@
ppc_tb_t *tb_env = env->tb_env;
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset);
+ tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ tb_env->atb_offset);
trace_ppc_tb_load(tb);
return tb;
@@ -576,7 +603,8 @@
ppc_tb_t *tb_env = env->tb_env;
uint64_t tb;
- tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset);
+ tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
+ tb_env->atb_offset);
trace_ppc_tb_load(tb);
return tb >> 32;
@@ -683,64 +711,77 @@
return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED);
}
-static inline int64_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next)
+static inline int64_t __cpu_ppc_load_decr(CPUPPCState *env, int64_t now,
+ uint64_t next)
{
ppc_tb_t *tb_env = env->tb_env;
- int64_t decr, diff;
+ uint64_t n;
+ int64_t decr;
- diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- if (diff >= 0) {
- decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND);
- } else if (tb_env->flags & PPC_TIMER_BOOKE) {
+ n = ns_to_tb(tb_env->decr_freq, now);
+ if (next > n && tb_env->flags & PPC_TIMER_BOOKE) {
decr = 0;
- } else {
- decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND);
+ } else {
+ decr = next - n;
}
+
trace_ppc_decr_load(decr);
return decr;
}
-target_ulong cpu_ppc_load_decr(CPUPPCState *env)
+static target_ulong _cpu_ppc_load_decr(CPUPPCState *env, int64_t now)
{
ppc_tb_t *tb_env = env->tb_env;
uint64_t decr;
- if (kvm_enabled()) {
- return env->spr[SPR_DECR];
- }
-
- decr = _cpu_ppc_load_decr(env, tb_env->decr_next);
+ decr = __cpu_ppc_load_decr(env, now, tb_env->decr_next);
/*
* If large decrementer is enabled then the decrementer is signed extened
* to 64 bits, otherwise it is a 32 bit value.
*/
if (env->spr[SPR_LPCR] & LPCR_LD) {
- return decr;
+ PowerPCCPU *cpu = env_archcpu(env);
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ return sextract64(decr, 0, pcc->lrg_decr_bits);
}
return (uint32_t) decr;
}
-target_ulong cpu_ppc_load_hdecr(CPUPPCState *env)
+target_ulong cpu_ppc_load_decr(CPUPPCState *env)
+{
+ if (kvm_enabled()) {
+ return env->spr[SPR_DECR];
+ } else {
+ return _cpu_ppc_load_decr(env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+ }
+}
+
+static target_ulong _cpu_ppc_load_hdecr(CPUPPCState *env, int64_t now)
{
PowerPCCPU *cpu = env_archcpu(env);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
ppc_tb_t *tb_env = env->tb_env;
uint64_t hdecr;
- hdecr = _cpu_ppc_load_decr(env, tb_env->hdecr_next);
+ hdecr = __cpu_ppc_load_decr(env, now, tb_env->hdecr_next);
/*
* If we have a large decrementer (POWER9 or later) then hdecr is sign
* extended to 64 bits, otherwise it is 32 bits.
*/
if (pcc->lrg_decr_bits > 32) {
- return hdecr;
+ return sextract64(hdecr, 0, pcc->lrg_decr_bits);
}
return (uint32_t) hdecr;
}
+target_ulong cpu_ppc_load_hdecr(CPUPPCState *env)
+{
+ return _cpu_ppc_load_hdecr(env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
+}
+
uint64_t cpu_ppc_load_purr (CPUPPCState *env)
{
ppc_tb_t *tb_env = env->tb_env;
@@ -785,7 +826,7 @@
ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0);
}
-static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp,
+static void __cpu_ppc_store_decr(PowerPCCPU *cpu, int64_t now, uint64_t *nextp,
QEMUTimer *timer,
void (*raise_excp)(void *),
void (*lower_excp)(PowerPCCPU *),
@@ -794,7 +835,7 @@
{
CPUPPCState *env = &cpu->env;
ppc_tb_t *tb_env = env->tb_env;
- uint64_t now, next;
+ uint64_t next;
int64_t signed_value;
int64_t signed_decr;
@@ -806,10 +847,14 @@
trace_ppc_decr_store(nr_bits, decr, value);
- if (kvm_enabled()) {
- /* KVM handles decrementer exceptions, we don't need our own timer */
- return;
- }
+ /*
+ * Calculate the next decrementer event and set a timer.
+ * decr_next is in timebase units to keep rounding simple. Note it is
+ * not adjusted by tb_offset because if TB changes via tb_offset changing,
+ * decrementer does not change, so not directly comparable with TB.
+ */
+ next = ns_to_tb(tb_env->decr_freq, now) + value;
+ *nextp = next; /* nextp is in timebase units */
/*
* Going from 1 -> 0 or 0 -> -1 is the event to generate a DEC interrupt.
@@ -832,21 +877,17 @@
(*lower_excp)(cpu);
}
- /* Calculate the next timer event */
- now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq);
- *nextp = next;
-
/* Adjust timer */
- timer_mod(timer, next);
+ timer_mod(timer, tb_to_ns_round_up(tb_env->decr_freq, next));
}
-static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, target_ulong decr,
- target_ulong value, int nr_bits)
+static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, int64_t now,
+ target_ulong decr, target_ulong value,
+ int nr_bits)
{
ppc_tb_t *tb_env = cpu->env.tb_env;
- __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer,
+ __cpu_ppc_store_decr(cpu, now, &tb_env->decr_next, tb_env->decr_timer,
tb_env->decr_timer->cb, &cpu_ppc_decr_lower,
tb_env->flags, decr, value, nr_bits);
}
@@ -855,13 +896,22 @@
{
PowerPCCPU *cpu = env_archcpu(env);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ int64_t now;
+ target_ulong decr;
int nr_bits = 32;
+ if (kvm_enabled()) {
+ /* KVM handles decrementer exceptions, we don't need our own timer */
+ return;
+ }
+
if (env->spr[SPR_LPCR] & LPCR_LD) {
nr_bits = pcc->lrg_decr_bits;
}
- _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, nr_bits);
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ decr = _cpu_ppc_load_decr(env, now);
+ _cpu_ppc_store_decr(cpu, now, decr, value, nr_bits);
}
static void cpu_ppc_decr_cb(void *opaque)
@@ -871,14 +921,15 @@
cpu_ppc_decr_excp(cpu);
}
-static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, target_ulong hdecr,
- target_ulong value, int nr_bits)
+static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, int64_t now,
+ target_ulong hdecr, target_ulong value,
+ int nr_bits)
{
ppc_tb_t *tb_env = cpu->env.tb_env;
if (tb_env->hdecr_timer != NULL) {
/* HDECR (Book3S 64bit) is edge-based, not level like DECR */
- __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer,
+ __cpu_ppc_store_decr(cpu, now, &tb_env->hdecr_next, tb_env->hdecr_timer,
tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower,
PPC_DECR_UNDERFLOW_TRIGGERED,
hdecr, value, nr_bits);
@@ -889,9 +940,12 @@
{
PowerPCCPU *cpu = env_archcpu(env);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ int64_t now;
+ target_ulong hdecr;
- _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value,
- pcc->lrg_decr_bits);
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ hdecr = _cpu_ppc_load_hdecr(env, now);
+ _cpu_ppc_store_hdecr(cpu, now, hdecr, value, pcc->lrg_decr_bits);
}
static void cpu_ppc_hdecr_cb(void *opaque)
@@ -901,29 +955,16 @@
cpu_ppc_hdecr_excp(cpu);
}
-void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value)
+static void _cpu_ppc_store_purr(CPUPPCState *env, int64_t now, uint64_t value)
{
ppc_tb_t *tb_env = env->tb_env;
- cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
- &tb_env->purr_offset, value);
+ cpu_ppc_store_tb(tb_env, now, &tb_env->purr_offset, value);
}
-static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
+void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value)
{
- CPUPPCState *env = opaque;
- PowerPCCPU *cpu = env_archcpu(env);
- ppc_tb_t *tb_env = env->tb_env;
-
- tb_env->tb_freq = freq;
- tb_env->decr_freq = freq;
- /* There is a bug in Linux 2.4 kernels:
- * if a decrementer exception is pending when it enables msr_ee at startup,
- * it's not ready to handle it...
- */
- _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32);
- _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32);
- cpu_ppc_store_purr(env, 0x0000000000000000ULL);
+ _cpu_ppc_store_purr(env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), value);
}
static void timebase_save(PPCTimebase *tb)
@@ -936,8 +977,14 @@
return;
}
- /* not used anymore, we keep it for compatibility */
- tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+ if (replay_mode == REPLAY_MODE_NONE) {
+ /* not used anymore, we keep it for compatibility */
+ tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+ } else {
+ /* simpler for record-replay to avoid this event, compat not needed */
+ tb->time_of_the_day_ns = 0;
+ }
+
/*
* tb_offset is only expected to be changed by QEMU so
* there is no need to update it from KVM here
@@ -1027,7 +1074,7 @@
};
/* Set up (once) timebase frequency (in Hz) */
-clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
+void cpu_ppc_tb_init(CPUPPCState *env, uint32_t freq)
{
PowerPCCPU *cpu = env_archcpu(env);
ppc_tb_t *tb_env;
@@ -1040,16 +1087,41 @@
tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL;
}
/* Create new timer */
- tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu);
+ tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &cpu_ppc_decr_cb, cpu);
if (env->has_hv_mode && !cpu->vhyp) {
- tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb,
- cpu);
+ tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ &cpu_ppc_hdecr_cb, cpu);
} else {
tb_env->hdecr_timer = NULL;
}
- cpu_ppc_set_tb_clk(env, freq);
- return &cpu_ppc_set_tb_clk;
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+}
+
+void cpu_ppc_tb_reset(CPUPPCState *env)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ ppc_tb_t *tb_env = env->tb_env;
+
+ timer_del(tb_env->decr_timer);
+ ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 0);
+ tb_env->decr_next = 0;
+ if (tb_env->hdecr_timer != NULL) {
+ timer_del(tb_env->hdecr_timer);
+ ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0);
+ tb_env->hdecr_next = 0;
+ }
+
+ /*
+ * There is a bug in Linux 2.4 kernels:
+ * if a decrementer exception is pending when it enables msr_ee at startup,
+ * it's not ready to handle it...
+ */
+ cpu_ppc_store_decr(env, -1);
+ cpu_ppc_store_hdecr(env, -1);
+ cpu_ppc_store_purr(env, 0x0000000000000000ULL);
}
void cpu_ppc_tb_free(CPUPPCState *env)
@@ -1125,9 +1197,7 @@
/* Cannot occur, but makes gcc happy */
return;
}
- next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq);
- if (next == now)
- next++;
+ next = now + tb_to_ns_round_up(tb_env->tb_freq, next);
timer_mod(ppc40x_timer->fit_timer, next);
env->spr[SPR_40x_TSR] |= 1 << 26;
if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) {
@@ -1153,14 +1223,15 @@
} else {
trace_ppc4xx_pit_start(ppc40x_timer->pit_reload);
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- next = now + muldiv64(ppc40x_timer->pit_reload,
- NANOSECONDS_PER_SECOND, tb_env->decr_freq);
- if (is_excp)
- next += tb_env->decr_next - now;
- if (next == now)
- next++;
+
+ if (is_excp) {
+ tb_env->decr_next += ppc40x_timer->pit_reload;
+ } else {
+ tb_env->decr_next = ns_to_tb(tb_env->decr_freq, now)
+ + ppc40x_timer->pit_reload;
+ }
+ next = tb_to_ns_round_up(tb_env->decr_freq, tb_env->decr_next);
timer_mod(tb_env->decr_timer, next);
- tb_env->decr_next = next;
}
}
@@ -1213,9 +1284,7 @@
/* Cannot occur, but makes gcc happy */
return;
}
- next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq);
- if (next == now)
- next++;
+ next = now + tb_to_ns_round_up(tb_env->decr_freq, next);
trace_ppc4xx_wdt(env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
case 0x0:
@@ -1465,5 +1534,7 @@
CPUPPCState *env = &cpu->env;
env->irq_input_state = 0;
- kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0);
+ if (kvm_enabled()) {
+ kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0);
+ }
}
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index d9231c7..f6fd35f 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -67,6 +67,7 @@
PowerPCCPU *cpu = opaque;
cpu_reset(CPU(cpu));
+ cpu_ppc_tb_reset(&cpu->env);
}
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index 07e91e3..f7cc6a8 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1022,7 +1022,6 @@
{
MachineState *machine = MACHINE(spapr);
SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine);
- uint8_t rng_seed[32];
int chosen;
_FDT(chosen = fdt_add_subnode(fdt, 0, "chosen"));
@@ -1100,8 +1099,7 @@
spapr_dt_ov5_platform_support(spapr, fdt, chosen);
}
- qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
- _FDT(fdt_setprop(fdt, chosen, "rng-seed", rng_seed, sizeof(rng_seed)));
+ _FDT(fdt_setprop(fdt, chosen, "rng-seed", spapr->fdt_rng_seed, 32));
_FDT(spapr_dt_ovec(fdt, chosen, spapr->ov5_cas, "ibm,architecture-vec-5"));
}
@@ -1322,6 +1320,22 @@
}
}
+/* May be used when the machine is not running */
+void spapr_init_all_lpcrs(target_ulong value, target_ulong mask)
+{
+ CPUState *cs;
+ CPU_FOREACH(cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+ target_ulong lpcr;
+
+ lpcr = env->spr[SPR_LPCR];
+ lpcr &= ~(LPCR_HR | LPCR_UPRT);
+ ppc_store_lpcr(cpu, lpcr);
+ }
+}
+
+
static bool spapr_get_pate(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu,
target_ulong lpid, ppc_v3_pate_t *entry)
{
@@ -1583,7 +1597,7 @@
}
/* We're setting up a hash table, so that means we're not radix */
spapr->patb_entry = 0;
- spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT);
+ spapr_init_all_lpcrs(0, LPCR_HR | LPCR_UPRT);
return 0;
}
@@ -1638,6 +1652,14 @@
void *fdt;
int rc;
+ if (reason != SHUTDOWN_CAUSE_SNAPSHOT_LOAD) {
+ /*
+ * Record-replay snapshot load must not consume random, this was
+ * already replayed from initial machine reset.
+ */
+ qemu_guest_getrandom_nofail(spapr->fdt_rng_seed, 32);
+ }
+
pef_kvm_reset(machine->cgs, &error_fatal);
spapr_caps_apply(spapr);
@@ -1661,7 +1683,7 @@
spapr_ovec_cleanup(spapr->ov5_cas);
spapr->ov5_cas = spapr_ovec_new();
- ppc_set_compat_all(spapr->max_compat_pvr, &error_fatal);
+ ppc_init_compat_all(spapr->max_compat_pvr, &error_fatal);
/*
* This is fixing some of the default configuration of the XIVE
diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c
index b482d97..91fae56 100644
--- a/hw/ppc/spapr_cpu_core.c
+++ b/hw/ppc/spapr_cpu_core.c
@@ -74,6 +74,8 @@
kvm_check_mmu(cpu, &error_fatal);
+ cpu_ppc_tb_reset(env);
+
spapr_irq_cpu_intc_reset(spapr, cpu);
}
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 9b1f225..b7dc388 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -3,6 +3,7 @@
#include "qapi/error.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
+#include "sysemu/tcg.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
@@ -789,6 +790,54 @@
return H_SUCCESS;
}
+static target_ulong h_set_mode_resource_set_ciabr(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong mflags,
+ target_ulong value1,
+ target_ulong value2)
+{
+ CPUPPCState *env = &cpu->env;
+
+ assert(tcg_enabled()); /* KVM will have handled this */
+
+ if (mflags) {
+ return H_UNSUPPORTED_FLAG;
+ }
+ if (value2) {
+ return H_P4;
+ }
+ if ((value1 & PPC_BITMASK(62, 63)) == 0x3) {
+ return H_P3;
+ }
+
+ ppc_store_ciabr(env, value1);
+
+ return H_SUCCESS;
+}
+
+static target_ulong h_set_mode_resource_set_dawr0(PowerPCCPU *cpu,
+ SpaprMachineState *spapr,
+ target_ulong mflags,
+ target_ulong value1,
+ target_ulong value2)
+{
+ CPUPPCState *env = &cpu->env;
+
+ assert(tcg_enabled()); /* KVM will have handled this */
+
+ if (mflags) {
+ return H_UNSUPPORTED_FLAG;
+ }
+ if (value2 & PPC_BIT(61)) {
+ return H_P4;
+ }
+
+ ppc_store_dawr0(env, value1);
+ ppc_store_dawrx0(env, value2);
+
+ return H_SUCCESS;
+}
+
static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong mflags,
@@ -858,6 +907,14 @@
target_ulong ret = H_P2;
switch (resource) {
+ case H_SET_MODE_RESOURCE_SET_CIABR:
+ ret = h_set_mode_resource_set_ciabr(cpu, spapr, args[0], args[2],
+ args[3]);
+ break;
+ case H_SET_MODE_RESOURCE_SET_DAWR0:
+ ret = h_set_mode_resource_set_dawr0(cpu, spapr, args[0], args[2],
+ args[3]);
+ break;
case H_SET_MODE_RESOURCE_LE:
ret = h_set_mode_resource_le(cpu, spapr, args[0], args[2], args[3]);
break;
diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c
index 18c3f92..e3b430a 100644
--- a/hw/ppc/vof.c
+++ b/hw/ppc/vof.c
@@ -1024,6 +1024,8 @@
}
vof->claimed = NULL;
vof->of_instances = NULL;
+ vof->of_instance_last = 0;
+ vof->claimed_base = 0;
}
void vof_build_dt(void *fdt, Vof *vof)
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 35a335b..ec76dce 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -674,9 +674,8 @@
dinfo = drive_get(IF_SD, 0, 0);
blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
- card_dev = qdev_new(TYPE_SD_CARD);
+ card_dev = qdev_new(TYPE_SD_CARD_SPI);
qdev_prop_set_drive_err(card_dev, "drive", blk, &error_fatal);
- qdev_prop_set_bit(card_dev, "spi", true);
qdev_realize_and_unref(card_dev,
qdev_get_child_bus(sd_dev, "sd-bus"),
&error_fatal);
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 43c374e..4823bef 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -87,6 +87,14 @@
sd_disconnect_state,
};
+typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req);
+
+typedef struct SDProto {
+ const char *name;
+ sd_cmd_handler cmd[SDMMC_CMD_MAX];
+ sd_cmd_handler acmd[SDMMC_CMD_MAX];
+} SDProto;
+
struct SDState {
DeviceState parent_obj;
@@ -107,7 +115,6 @@
uint8_t spec_version;
BlockBackend *blk;
- bool spi;
/* Runtime changeables */
@@ -137,7 +144,6 @@
qemu_irq readonly_cb;
qemu_irq inserted_cb;
QEMUTimer *ocr_power_timer;
- const char *proto_name;
bool enable;
uint8_t dat_lines;
bool cmd_line;
@@ -145,6 +151,33 @@
static void sd_realize(DeviceState *dev, Error **errp);
+static const struct SDProto *sd_proto(SDState *sd)
+{
+ SDCardClass *sc = SD_CARD_GET_CLASS(sd);
+
+ return sc->proto;
+}
+
+static const SDProto sd_proto_spi;
+
+static bool sd_is_spi(SDState *sd)
+{
+ return sd_proto(sd) == &sd_proto_spi;
+}
+
+static const char *sd_version_str(enum SDPhySpecificationVersion version)
+{
+ static const char *sdphy_version[] = {
+ [SD_PHY_SPECv1_10_VERS] = "v1.10",
+ [SD_PHY_SPECv2_00_VERS] = "v2.00",
+ [SD_PHY_SPECv3_01_VERS] = "v3.01",
+ };
+ if (version >= ARRAY_SIZE(sdphy_version)) {
+ return "unsupported version";
+ }
+ return sdphy_version[version];
+}
+
static const char *sd_state_name(enum SDCardStates state)
{
static const char *state_name[] = {
@@ -309,7 +342,7 @@
/* All voltages OK */
sd->ocr = R_OCR_VDD_VOLTAGE_WIN_HI_MASK;
- if (sd->spi) {
+ if (sd_is_spi(sd)) {
/*
* We don't need to emulate power up sequence in SPI-mode.
* Thus, the card's power up status bit should be set to 1 when reset.
@@ -714,13 +747,12 @@
SDState *sd;
Error *err = NULL;
- obj = object_new(TYPE_SD_CARD);
+ obj = object_new(is_spi ? TYPE_SD_CARD_SPI : TYPE_SD_CARD);
dev = DEVICE(obj);
if (!qdev_prop_set_drive_err(dev, "drive", blk, &err)) {
error_reportf_err(err, "sd_init failed: ");
return NULL;
}
- qdev_prop_set_bit(dev, "spi", is_spi);
/*
* Realizing the device properly would put it into the QOM
@@ -966,6 +998,106 @@
return true;
}
+static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong state: %s (spec %s)\n",
+ sd_proto(sd)->name, req.cmd, sd_state_name(sd->state),
+ sd_version_str(sd->spec_version));
+
+ return sd_illegal;
+}
+
+static sd_rsp_type_t sd_cmd_illegal(SDState *sd, SDRequest req)
+{
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Unknown CMD%i for spec %s\n",
+ sd_proto(sd)->name, req.cmd,
+ sd_version_str(sd->spec_version));
+
+ return sd_illegal;
+}
+
+/* Commands that are recognised but not yet implemented. */
+static sd_rsp_type_t sd_cmd_unimplemented(SDState *sd, SDRequest req)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: CMD%i not implemented\n",
+ sd_proto(sd)->name, req.cmd);
+
+ return sd_illegal;
+}
+
+static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_inactive_state) {
+ sd->state = sd_idle_state;
+ sd_reset(DEVICE(sd));
+ }
+
+ return sd_is_spi(sd) ? sd_r1 : sd_r0;
+}
+
+static sd_rsp_type_t sd_cmd_SEND_OP_CMD(SDState *sd, SDRequest req)
+{
+ sd->state = sd_transfer_state;
+
+ return sd_r1;
+}
+
+static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req)
+{
+ if (sd->state != sd_ready_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ sd->state = sd_identification_state;
+
+ return sd_r2_i;
+}
+
+static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req)
+{
+ switch (sd->state) {
+ case sd_identification_state:
+ case sd_standby_state:
+ sd->state = sd_standby_state;
+ sd_set_rca(sd);
+ return sd_r6;
+
+ default:
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+}
+
+static sd_rsp_type_t sd_cmd_SEND_TUNING_BLOCK(SDState *sd, SDRequest req)
+{
+ if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
+ return sd_cmd_illegal(sd, req);
+ }
+
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ sd->state = sd_sendingdata_state;
+ sd->data_offset = 0;
+
+ return sd_r1;
+}
+
+static sd_rsp_type_t sd_cmd_SET_BLOCK_COUNT(SDState *sd, SDRequest req)
+{
+ if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
+ return sd_cmd_illegal(sd, req);
+ }
+
+ if (sd->state != sd_transfer_state) {
+ return sd_invalid_state_for_cmd(sd, req);
+ }
+
+ sd->multi_blk_cnt = req.arg;
+
+ return sd_r1;
+}
+
static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
{
uint32_t rca = 0x0000;
@@ -975,7 +1107,7 @@
* However there is no ACMD55, so we want to trace this particular case.
*/
if (req.cmd != 55 || sd->expecting_acmd) {
- trace_sdcard_normal_command(sd->proto_name,
+ trace_sdcard_normal_command(sd_proto(sd)->name,
sd_cmd_name(req.cmd), req.cmd,
req.arg, sd_state_name(sd->state));
}
@@ -999,58 +1131,13 @@
return sd_illegal;
}
+ if (sd_proto(sd)->cmd[req.cmd]) {
+ return sd_proto(sd)->cmd[req.cmd](sd, req);
+ }
+
switch (req.cmd) {
/* Basic commands (Class 0 and Class 1) */
- case 0: /* CMD0: GO_IDLE_STATE */
- switch (sd->state) {
- case sd_inactive_state:
- return sd->spi ? sd_r1 : sd_r0;
-
- default:
- sd->state = sd_idle_state;
- sd_reset(DEVICE(sd));
- return sd->spi ? sd_r1 : sd_r0;
- }
- break;
-
- case 1: /* CMD1: SEND_OP_CMD */
- if (!sd->spi)
- goto bad_cmd;
-
- sd->state = sd_transfer_state;
- return sd_r1;
-
- case 2: /* CMD2: ALL_SEND_CID */
- if (sd->spi)
- goto bad_cmd;
- switch (sd->state) {
- case sd_ready_state:
- sd->state = sd_identification_state;
- return sd_r2_i;
-
- default:
- break;
- }
- break;
-
- case 3: /* CMD3: SEND_RELATIVE_ADDR */
- if (sd->spi)
- goto bad_cmd;
- switch (sd->state) {
- case sd_identification_state:
- case sd_standby_state:
- sd->state = sd_standby_state;
- sd_set_rca(sd);
- return sd_r6;
-
- default:
- break;
- }
- break;
-
case 4: /* CMD4: SEND_DSR */
- if (sd->spi)
- goto bad_cmd;
switch (sd->state) {
case sd_standby_state:
break;
@@ -1060,9 +1147,6 @@
}
break;
- case 5: /* CMD5: reserved for SDIO cards */
- return sd_illegal;
-
case 6: /* CMD6: SWITCH_FUNCTION */
switch (sd->mode) {
case sd_data_transfer_mode:
@@ -1078,8 +1162,6 @@
break;
case 7: /* CMD7: SELECT/DESELECT_CARD */
- if (sd->spi)
- goto bad_cmd;
switch (sd->state) {
case sd_standby_state:
if (sd->rca != rca)
@@ -1126,7 +1208,7 @@
/* No response if not exactly one VHS bit is set. */
if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) {
- return sd->spi ? sd_r7 : sd_r0;
+ return sd_is_spi(sd) ? sd_r7 : sd_r0;
}
/* Accept. */
@@ -1142,8 +1224,9 @@
return sd_r2_s;
case sd_transfer_state:
- if (!sd->spi)
+ if (!sd_is_spi(sd)) {
break;
+ }
sd->state = sd_sendingdata_state;
memcpy(sd->data, sd->csd, 16);
sd->data_start = addr;
@@ -1164,8 +1247,9 @@
return sd_r2_i;
case sd_transfer_state:
- if (!sd->spi)
+ if (!sd_is_spi(sd)) {
break;
+ }
sd->state = sd_sendingdata_state;
memcpy(sd->data, sd->cid, 16);
sd->data_start = addr;
@@ -1197,7 +1281,7 @@
case 13: /* CMD13: SEND_STATUS */
switch (sd->mode) {
case sd_data_transfer_mode:
- if (!sd->spi && sd->rca != rca) {
+ if (!sd_is_spi(sd) && sd->rca != rca) {
return sd_r0;
}
@@ -1209,8 +1293,6 @@
break;
case 15: /* CMD15: GO_INACTIVE_STATE */
- if (sd->spi)
- goto bad_cmd;
switch (sd->mode) {
case sd_data_transfer_mode:
if (sd->rca != rca)
@@ -1261,31 +1343,6 @@
}
break;
- case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */
- if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
- break;
- }
- if (sd->state == sd_transfer_state) {
- sd->state = sd_sendingdata_state;
- sd->data_offset = 0;
- return sd_r1;
- }
- break;
-
- case 23: /* CMD23: SET_BLOCK_COUNT */
- if (sd->spec_version < SD_PHY_SPECv3_01_VERS) {
- break;
- }
- switch (sd->state) {
- case sd_transfer_state:
- sd->multi_blk_cnt = req.arg;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
/* Block write commands (Class 4) */
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
@@ -1317,8 +1374,6 @@
break;
case 26: /* CMD26: PROGRAM_CID */
- if (sd->spi)
- goto bad_cmd;
switch (sd->state) {
case sd_transfer_state:
sd->state = sd_receivingdata_state;
@@ -1468,15 +1523,6 @@
}
break;
- case 52 ... 54:
- /* CMD52, CMD53, CMD54: reserved for SDIO cards
- * (see the SDIO Simplified Specification V2.0)
- * Handle as illegal command but do not complain
- * on stderr, as some OSes may use these in their
- * probing for presence of an SDIO card.
- */
- return sd_illegal;
-
/* Application specific commands (Class 8) */
case 55: /* CMD55: APP_CMD */
switch (sd->state) {
@@ -1492,7 +1538,7 @@
default:
break;
}
- if (!sd->spi) {
+ if (!sd_is_spi(sd)) {
if (sd->rca != rca) {
return sd_r0;
}
@@ -1517,39 +1563,32 @@
break;
case 58: /* CMD58: READ_OCR (SPI) */
- if (!sd->spi) {
- goto bad_cmd;
- }
return sd_r3;
case 59: /* CMD59: CRC_ON_OFF (SPI) */
- if (!sd->spi) {
- goto bad_cmd;
- }
return sd_r1;
default:
- bad_cmd:
qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd);
return sd_illegal;
}
- qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state: %s\n",
- req.cmd, sd_state_name(sd->state));
- return sd_illegal;
+ return sd_invalid_state_for_cmd(sd, req);
}
static sd_rsp_type_t sd_app_command(SDState *sd,
SDRequest req)
{
- trace_sdcard_app_command(sd->proto_name, sd_acmd_name(req.cmd),
+ trace_sdcard_app_command(sd_proto(sd)->name, sd_acmd_name(req.cmd),
req.cmd, req.arg, sd_state_name(sd->state));
sd->card_status |= APP_CMD;
+
+ if (sd_proto(sd)->acmd[req.cmd]) {
+ return sd_proto(sd)->acmd[req.cmd](sd, req);
+ }
+
switch (req.cmd) {
case 6: /* ACMD6: SET_BUS_WIDTH */
- if (sd->spi) {
- goto unimplemented_spi_cmd;
- }
switch (sd->state) {
case sd_transfer_state:
sd->sd_status[0] &= 0x3f;
@@ -1600,11 +1639,6 @@
break;
case 41: /* ACMD41: SD_APP_OP_COND */
- if (sd->spi) {
- /* SEND_OP_CMD */
- sd->state = sd_transfer_state;
- return sd_r1;
- }
if (sd->state != sd_idle_state) {
break;
}
@@ -1680,12 +1714,6 @@
default:
/* Fall back to standard commands. */
return sd_normal_command(sd, req);
-
- unimplemented_spi_cmd:
- /* Commands that are recognised but not yet implemented in SPI mode. */
- qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n",
- req.cmd);
- return sd_illegal;
}
qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd);
@@ -1836,7 +1864,7 @@
if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
return;
- trace_sdcard_write_data(sd->proto_name,
+ trace_sdcard_write_data(sd_proto(sd)->name,
sd_acmd_name(sd->current_cmd),
sd->current_cmd, value);
switch (sd->current_cmd) {
@@ -1992,7 +2020,7 @@
io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
- trace_sdcard_read_data(sd->proto_name,
+ trace_sdcard_read_data(sd_proto(sd)->name,
sd_acmd_name(sd->current_cmd),
sd->current_cmd, io_len);
switch (sd->current_cmd) {
@@ -2111,6 +2139,40 @@
sd->enable = enable;
}
+static const SDProto sd_proto_spi = {
+ .name = "SPI",
+ .cmd = {
+ [0] = sd_cmd_GO_IDLE_STATE,
+ [1] = sd_cmd_SEND_OP_CMD,
+ [2 ... 4] = sd_cmd_illegal,
+ [5] = sd_cmd_illegal,
+ [7] = sd_cmd_illegal,
+ [15] = sd_cmd_illegal,
+ [26] = sd_cmd_illegal,
+ [52 ... 54] = sd_cmd_illegal,
+ },
+ .acmd = {
+ [6] = sd_cmd_unimplemented,
+ [41] = sd_cmd_SEND_OP_CMD,
+ },
+};
+
+static const SDProto sd_proto_sd = {
+ .name = "SD",
+ .cmd = {
+ [0] = sd_cmd_GO_IDLE_STATE,
+ [1] = sd_cmd_illegal,
+ [2] = sd_cmd_ALL_SEND_CID,
+ [3] = sd_cmd_SEND_RELATIVE_ADDR,
+ [5] = sd_cmd_illegal,
+ [19] = sd_cmd_SEND_TUNING_BLOCK,
+ [23] = sd_cmd_SET_BLOCK_COUNT,
+ [52 ... 54] = sd_cmd_illegal,
+ [58] = sd_cmd_illegal,
+ [59] = sd_cmd_illegal,
+ },
+};
+
static void sd_instance_init(Object *obj)
{
SDState *sd = SD_CARD(obj);
@@ -2131,8 +2193,6 @@
SDState *sd = SD_CARD(dev);
int ret;
- sd->proto_name = sd->spi ? "SPI" : "SD";
-
switch (sd->spec_version) {
case SD_PHY_SPECv1_10_VERS
... SD_PHY_SPECv3_01_VERS:
@@ -2189,7 +2249,6 @@
* whether card should be in SSI or MMC/SD mode. It is also up to the
* board to ensure that ssi transfers only occur when the chip select
* is asserted. */
- DEFINE_PROP_BOOL("spi", SDState, spi, false),
DEFINE_PROP_END_OF_LIST()
};
@@ -2216,6 +2275,7 @@
sc->enable = sd_enable;
sc->get_inserted = sd_get_inserted;
sc->get_readonly = sd_get_readonly;
+ sc->proto = &sd_proto_sd;
}
static const TypeInfo sd_info = {
@@ -2228,9 +2288,31 @@
.instance_finalize = sd_instance_finalize,
};
+/*
+ * We do not model the chip select pin, so allow the board to select
+ * whether card should be in SSI or MMC/SD mode. It is also up to the
+ * board to ensure that ssi transfers only occur when the chip select
+ * is asserted.
+ */
+static void sd_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SDCardClass *sc = SD_CARD_CLASS(klass);
+
+ dc->desc = "SD SPI";
+ sc->proto = &sd_proto_spi;
+}
+
+static const TypeInfo sd_spi_info = {
+ .name = TYPE_SD_CARD_SPI,
+ .parent = TYPE_SD_CARD,
+ .class_init = sd_spi_class_init,
+};
+
static void sd_register_types(void)
{
type_register_static(&sd_info);
+ type_register_static(&sd_spi_info);
}
type_init(sd_register_types)
diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c
index 2053def..8648a78 100644
--- a/hw/sd/sdmmc-internal.c
+++ b/hw/sd/sdmmc-internal.c
@@ -14,7 +14,7 @@
const char *sd_cmd_name(uint8_t cmd)
{
static const char *cmd_abbrev[SDMMC_CMD_MAX] = {
- [0] = "GO_IDLE_STATE",
+ [0] = "GO_IDLE_STATE", [1] = "SEND_OP_CMD",
[2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR",
[4] = "SET_DSR", [5] = "IO_SEND_OP_COND",
[6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD",
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 7281169..2a4001b 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -692,6 +692,14 @@
memset(s->regs, 0, sizeof s->regs);
}
+ for (i = 0; i < asc->cs_num_max; i++) {
+ DeviceState *dev = ssi_get_cs(s->spi, i);
+ if (dev) {
+ qemu_irq cs_line = qdev_get_gpio_in_named(dev, SSI_GPIO_CS, 0);
+ qdev_connect_gpio_out_named(DEVICE(s), "cs", i, cs_line);
+ }
+ }
+
/* Unselect all peripherals */
for (i = 0; i < asc->cs_num_max; ++i) {
s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c
index d54a109..1f3e540 100644
--- a/hw/ssi/ssi.c
+++ b/hw/ssi/ssi.c
@@ -13,6 +13,7 @@
*/
#include "qemu/osdep.h"
+#include "hw/qdev-properties.h"
#include "hw/ssi/ssi.h"
#include "migration/vmstate.h"
#include "qemu/module.h"
@@ -26,10 +27,46 @@
#define TYPE_SSI_BUS "SSI"
OBJECT_DECLARE_SIMPLE_TYPE(SSIBus, SSI_BUS)
+DeviceState *ssi_get_cs(SSIBus *bus, uint8_t cs_index)
+{
+ BusState *b = BUS(bus);
+ BusChild *kid;
+
+ QTAILQ_FOREACH(kid, &b->children, sibling) {
+ SSIPeripheral *kid_ssi = SSI_PERIPHERAL(kid->child);
+ if (kid_ssi->cs_index == cs_index) {
+ return kid->child;
+ }
+ }
+
+ return NULL;
+}
+
+static bool ssi_bus_check_address(BusState *b, DeviceState *dev, Error **errp)
+{
+ SSIPeripheral *s = SSI_PERIPHERAL(dev);
+
+ if (ssi_get_cs(SSI_BUS(b), s->cs_index)) {
+ error_setg(errp, "CS index '0x%x' in use by a %s device", s->cs_index,
+ object_get_typename(OBJECT(dev)));
+ return false;
+ }
+
+ return true;
+}
+
+static void ssi_bus_class_init(ObjectClass *klass, void *data)
+{
+ BusClass *k = BUS_CLASS(klass);
+
+ k->check_address = ssi_bus_check_address;
+}
+
static const TypeInfo ssi_bus_info = {
.name = TYPE_SSI_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(SSIBus),
+ .class_init = ssi_bus_class_init,
};
static void ssi_cs_default(void *opaque, int n, int level)
@@ -71,6 +108,11 @@
ssc->realize(s, errp);
}
+static Property ssi_peripheral_properties[] = {
+ DEFINE_PROP_UINT8("cs", SSIPeripheral, cs_index, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void ssi_peripheral_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *ssc = SSI_PERIPHERAL_CLASS(klass);
@@ -81,6 +123,7 @@
if (!ssc->transfer_raw) {
ssc->transfer_raw = ssi_transfer_raw_default;
}
+ device_class_set_props(dc, ssi_peripheral_properties);
}
static const TypeInfo ssi_peripheral_info = {
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 44cd82e..01df55f 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -320,7 +320,4 @@
void suspend_mux_open(void);
void resume_mux_open(void);
-/* console.c */
-void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp);
-
#endif
diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h
index 7198953..de93756 100644
--- a/include/hw/block/flash.h
+++ b/include/hw/block/flash.h
@@ -76,4 +76,8 @@
void ecc_reset(ECCState *s);
extern const VMStateDescription vmstate_ecc_state;
+/* m25p80.c */
+
+BlockBackend *m25p80_get_blk(DeviceState *dev);
+
#endif
diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h
index 51c944e..a064479 100644
--- a/include/hw/i2c/aspeed_i2c.h
+++ b/include/hw/i2c/aspeed_i2c.h
@@ -139,10 +139,11 @@
REG32(I2CD_DEV_ADDR, 0x18) /* Slave Device Address */
SHARED_FIELD(SLAVE_DEV_ADDR1, 0, 7)
REG32(I2CD_POOL_CTRL, 0x1C) /* Pool Buffer Control */
- SHARED_FIELD(RX_COUNT, 24, 5)
+ SHARED_FIELD(RX_COUNT, 24, 6)
SHARED_FIELD(RX_SIZE, 16, 5)
- SHARED_FIELD(TX_COUNT, 9, 5)
+ SHARED_FIELD(TX_COUNT, 8, 5)
FIELD(I2CD_POOL_CTRL, OFFSET, 2, 6) /* AST2400 */
+ SHARED_FIELD(BUF_ORGANIZATION, 0, 1) /* AST2600 */
REG32(I2CD_BYTE_BUF, 0x20) /* Transmit/Receive Byte Buffer */
SHARED_FIELD(RX_BUF, 8, 8)
SHARED_FIELD(TX_BUF, 0, 8)
diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h
index e095c00..17a8dfc 100644
--- a/include/hw/ppc/ppc.h
+++ b/include/hw/ppc/ppc.h
@@ -54,7 +54,8 @@
*/
uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset);
-clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq);
+void cpu_ppc_tb_init(CPUPPCState *env, uint32_t freq);
+void cpu_ppc_tb_reset(CPUPPCState *env);
void cpu_ppc_tb_free(CPUPPCState *env);
void cpu_ppc_hdecr_init(CPUPPCState *env);
void cpu_ppc_hdecr_exit(CPUPPCState *env);
diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h
index 538b2df..f4bd204 100644
--- a/include/hw/ppc/spapr.h
+++ b/include/hw/ppc/spapr.h
@@ -204,6 +204,7 @@
uint32_t fdt_size;
uint32_t fdt_initial_size;
void *fdt_blob;
+ uint8_t fdt_rng_seed[32];
long kernel_size;
bool kernel_le;
uint64_t kernel_addr;
@@ -1012,6 +1013,7 @@
#define SPAPR_OV5_XIVE_BOTH 0x80 /* Only to advertise on the platform */
void spapr_set_all_lpcrs(target_ulong value, target_ulong mask);
+void spapr_init_all_lpcrs(target_ulong value, target_ulong mask);
hwaddr spapr_get_rtas_addr(void);
bool spapr_memory_hot_unplug_supported(SpaprMachineState *spapr);
diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h
index 9f580a2..f120874 100644
--- a/include/hw/ppc/xive.h
+++ b/include/hw/ppc/xive.h
@@ -401,6 +401,7 @@
int (*write_nvt)(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx,
XiveNVT *nvt, uint8_t word_number);
uint8_t (*get_block_id)(XiveRouter *xrtr);
+ void (*end_notify)(XiveRouter *xrtr, XiveEAS *eas);
};
int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx,
@@ -414,6 +415,7 @@
int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx,
XiveNVT *nvt, uint8_t word_number);
void xive_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked);
+void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas);
/*
* XIVE Presenter
diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
index 3047adb..2c8748f 100644
--- a/include/hw/sd/sd.h
+++ b/include/hw/sd/sd.h
@@ -93,6 +93,9 @@
#define TYPE_SD_CARD "sd-card"
OBJECT_DECLARE_TYPE(SDState, SDCardClass, SD_CARD)
+#define TYPE_SD_CARD_SPI "sd-card-spi"
+DECLARE_INSTANCE_CHECKER(SDState, SD_CARD_SPI, TYPE_SD_CARD_SPI)
+
struct SDCardClass {
/*< private >*/
DeviceClass parent_class;
@@ -124,6 +127,8 @@
void (*enable)(SDState *sd, bool enable);
bool (*get_inserted)(SDState *sd);
bool (*get_readonly)(SDState *sd);
+
+ const struct SDProto *proto;
};
#define TYPE_SD_BUS "sd-bus"
diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h
index 6950f86..3cdcbd5 100644
--- a/include/hw/ssi/ssi.h
+++ b/include/hw/ssi/ssi.h
@@ -64,6 +64,9 @@
/* Chip select state */
bool cs;
+
+ /* Chip select index */
+ uint8_t cs_index;
};
extern const VMStateDescription vmstate_ssi_peripheral;
@@ -109,4 +112,6 @@
uint32_t ssi_transfer(SSIBus *bus, uint32_t val);
+DeviceState *ssi_get_cs(SSIBus *bus, uint8_t cs_index);
+
#endif
diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h
index 0116183..ead97d3 100644
--- a/include/qemu/host-utils.h
+++ b/include/qemu/host-utils.h
@@ -56,6 +56,11 @@
return (__int128_t)a * b / c;
}
+static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c)
+{
+ return ((__int128_t)a * b + c - 1) / c;
+}
+
static inline uint64_t divu128(uint64_t *plow, uint64_t *phigh,
uint64_t divisor)
{
@@ -83,7 +88,8 @@
uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor);
int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor);
-static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+static inline uint64_t muldiv64_rounding(uint64_t a, uint32_t b, uint32_t c,
+ bool round_up)
{
union {
uint64_t ll;
@@ -99,12 +105,25 @@
u.ll = a;
rl = (uint64_t)u.l.low * (uint64_t)b;
+ if (round_up) {
+ rl += c - 1;
+ }
rh = (uint64_t)u.l.high * (uint64_t)b;
rh += (rl >> 32);
res.l.high = rh / c;
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
return res.ll;
}
+
+static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+{
+ return muldiv64_rounding(a, b, c, false);
+}
+
+static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c)
+{
+ return muldiv64_rounding(a, b, c, true);
+}
#endif
/**
diff --git a/include/qemu/selfmap.h b/include/qemu/selfmap.h
index 7d93894..1690a74 100644
--- a/include/qemu/selfmap.h
+++ b/include/qemu/selfmap.h
@@ -20,10 +20,10 @@
bool is_exec;
bool is_priv;
+ dev_t dev;
+ ino_t inode;
uint64_t offset;
- uint64_t inode;
const char *path;
- char dev[];
} MapInfo;
/**
diff --git a/include/ui/console.h b/include/ui/console.h
index 3e8b22d..1ccd432 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -12,6 +12,27 @@
# include "ui/shader.h"
#endif
+#define TYPE_QEMU_CONSOLE "qemu-console"
+OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE)
+
+#define TYPE_QEMU_GRAPHIC_CONSOLE "qemu-graphic-console"
+OBJECT_DECLARE_SIMPLE_TYPE(QemuGraphicConsole, QEMU_GRAPHIC_CONSOLE)
+
+#define TYPE_QEMU_TEXT_CONSOLE "qemu-text-console"
+OBJECT_DECLARE_SIMPLE_TYPE(QemuTextConsole, QEMU_TEXT_CONSOLE)
+
+#define TYPE_QEMU_FIXED_TEXT_CONSOLE "qemu-fixed-text-console"
+OBJECT_DECLARE_SIMPLE_TYPE(QemuFixedTextConsole, QEMU_FIXED_TEXT_CONSOLE)
+
+#define QEMU_IS_GRAPHIC_CONSOLE(c) \
+ object_dynamic_cast(OBJECT(c), TYPE_QEMU_GRAPHIC_CONSOLE)
+
+#define QEMU_IS_TEXT_CONSOLE(c) \
+ object_dynamic_cast(OBJECT(c), TYPE_QEMU_TEXT_CONSOLE)
+
+#define QEMU_IS_FIXED_TEXT_CONSOLE(c) \
+ object_dynamic_cast(OBJECT(c), TYPE_QEMU_FIXED_TEXT_CONSOLE)
+
/* keyboard/mouse support */
#define MOUSE_EVENT_LBUTTON 0x01
@@ -91,9 +112,9 @@
#define QEMU_KEY_CTRL_PAGEUP 0xe406
#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
-void kbd_put_keysym_console(QemuConsole *s, int keysym);
-bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl);
-void kbd_put_string_console(QemuConsole *s, const char *str, int len);
+void kbd_put_keysym_console(QemuTextConsole *s, int keysym);
+bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl);
+void kbd_put_string_console(QemuTextConsole *s, const char *str, int len);
void kbd_put_keysym(int keysym);
/* Touch devices */
@@ -112,10 +133,6 @@
Error **errp);
/* consoles */
-#define TYPE_QEMU_CONSOLE "qemu-console"
-OBJECT_DECLARE_TYPE(QemuConsole, QemuConsoleClass, QEMU_CONSOLE)
-
-
struct QemuConsoleClass {
ObjectClass parent_class;
};
@@ -484,7 +501,6 @@
QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head);
QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
uint32_t head, Error **errp);
-QemuConsole *qemu_console_lookup_unused(void);
QEMUCursor *qemu_console_get_cursor(QemuConsole *con);
bool qemu_console_is_visible(QemuConsole *con);
bool qemu_console_is_graphic(QemuConsole *con);
@@ -504,6 +520,8 @@
void console_select(unsigned int index);
void qemu_console_resize(QemuConsole *con, int width, int height);
DisplaySurface *qemu_console_surface(QemuConsole *con);
+void coroutine_fn qemu_console_co_wait_update(QemuConsole *con);
+int qemu_invalidate_text_consoles(void);
/* console-gl.c */
#ifdef CONFIG_OPENGL
diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index 0c77560..51f8709 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -47,6 +47,12 @@
# define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8
#endif
+#define QEMU_PIXMAN_COLOR(r, g, b) \
+ { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
+
+#define QEMU_PIXMAN_COLOR_BLACK QEMU_PIXMAN_COLOR(0x00, 0x00, 0x00)
+#define QEMU_PIXMAN_COLOR_GRAY QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0xaa)
+
/* -------------------------------------------------------------------- */
typedef struct PixelFormat {
@@ -72,13 +78,10 @@
int width);
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
int width, int x, int y);
-void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y,
- pixman_image_t *linebuf);
pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
pixman_image_t *image);
void qemu_pixman_image_unref(pixman_image_t *image);
-pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color);
pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
unsigned int ch);
void qemu_pixman_glyph_render(pixman_image_t *glyph,
diff --git a/linux-user/aarch64/target_proc.h b/linux-user/aarch64/target_proc.h
new file mode 100644
index 0000000..907df4d
--- /dev/null
+++ b/linux-user/aarch64/target_proc.h
@@ -0,0 +1 @@
+#include "../arm/target_proc.h"
diff --git a/linux-user/alpha/target_proc.h b/linux-user/alpha/target_proc.h
new file mode 100644
index 0000000..dac37df
--- /dev/null
+++ b/linux-user/alpha/target_proc.h
@@ -0,0 +1,67 @@
+/*
+ * Alpha specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef ALPHA_TARGET_PROC_H
+#define ALPHA_TARGET_PROC_H
+
+static int open_cpuinfo(CPUArchState *cpu_env, int fd)
+{
+ int max_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ unsigned long cpu_mask;
+ char model[32];
+ const char *p, *q;
+ int t;
+
+ p = object_class_get_name(OBJECT_CLASS(CPU_GET_CLASS(env_cpu(cpu_env))));
+ q = strchr(p, '-');
+ t = q - p;
+ assert(t < sizeof(model));
+ memcpy(model, p, t);
+ model[t] = 0;
+
+ t = sched_getaffinity(getpid(), sizeof(cpu_mask), (cpu_set_t *)&cpu_mask);
+ if (t < 0) {
+ if (num_cpus >= sizeof(cpu_mask) * 8) {
+ cpu_mask = -1;
+ } else {
+ cpu_mask = (1UL << num_cpus) - 1;
+ }
+ }
+
+ dprintf(fd,
+ "cpu\t\t\t: Alpha\n"
+ "cpu model\t\t: %s\n"
+ "cpu variation\t\t: 0\n"
+ "cpu revision\t\t: 0\n"
+ "cpu serial number\t: JA00000000\n"
+ "system type\t\t: QEMU\n"
+ "system variation\t: QEMU_v" QEMU_VERSION "\n"
+ "system revision\t\t: 0\n"
+ "system serial number\t: AY00000000\n"
+ "cycle frequency [Hz]\t: 250000000\n"
+ "timer frequency [Hz]\t: 250.00\n"
+ "page size [bytes]\t: %d\n"
+ "phys. address bits\t: %d\n"
+ "max. addr. space #\t: 255\n"
+ "BogoMIPS\t\t: 2500.00\n"
+ "kernel unaligned acc\t: 0 (pc=0,va=0)\n"
+ "user unaligned acc\t: 0 (pc=0,va=0)\n"
+ "platform string\t\t: AlphaServer QEMU user-mode VM\n"
+ "cpus detected\t\t: %d\n"
+ "cpus active\t\t: %d\n"
+ "cpu active mask\t\t: %016lx\n"
+ "L1 Icache\t\t: n/a\n"
+ "L1 Dcache\t\t: n/a\n"
+ "L2 cache\t\t: n/a\n"
+ "L3 cache\t\t: n/a\n",
+ model, TARGET_PAGE_SIZE, TARGET_PHYS_ADDR_SPACE_BITS,
+ max_cpus, num_cpus, cpu_mask);
+
+ return 0;
+}
+#define HAVE_ARCH_PROC_CPUINFO
+
+#endif /* ALPHA_TARGET_PROC_H */
diff --git a/linux-user/arm/target_proc.h b/linux-user/arm/target_proc.h
new file mode 100644
index 0000000..ac75af9
--- /dev/null
+++ b/linux-user/arm/target_proc.h
@@ -0,0 +1,101 @@
+/*
+ * Arm specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef ARM_TARGET_PROC_H
+#define ARM_TARGET_PROC_H
+
+static int open_cpuinfo(CPUArchState *cpu_env, int fd)
+{
+ ARMCPU *cpu = env_archcpu(cpu_env);
+ int arch, midr_rev, midr_part, midr_var, midr_impl;
+ target_ulong elf_hwcap = get_elf_hwcap();
+ target_ulong elf_hwcap2 = get_elf_hwcap2();
+ const char *elf_name;
+ int num_cpus, len_part, len_var;
+
+#if TARGET_BIG_ENDIAN
+# define END_SUFFIX "b"
+#else
+# define END_SUFFIX "l"
+#endif
+
+ arch = 8;
+ elf_name = "v8" END_SUFFIX;
+ midr_rev = FIELD_EX32(cpu->midr, MIDR_EL1, REVISION);
+ midr_part = FIELD_EX32(cpu->midr, MIDR_EL1, PARTNUM);
+ midr_var = FIELD_EX32(cpu->midr, MIDR_EL1, VARIANT);
+ midr_impl = FIELD_EX32(cpu->midr, MIDR_EL1, IMPLEMENTER);
+ len_part = 3;
+ len_var = 1;
+
+#ifndef TARGET_AARCH64
+ /* For simplicity, treat ARMv8 as an arm64 kernel with CONFIG_COMPAT. */
+ if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) {
+ if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
+ arch = 7;
+ midr_var = (cpu->midr >> 16) & 0x7f;
+ len_var = 2;
+ if (arm_feature(&cpu->env, ARM_FEATURE_M)) {
+ elf_name = "armv7m" END_SUFFIX;
+ } else {
+ elf_name = "armv7" END_SUFFIX;
+ }
+ } else {
+ midr_part = cpu->midr >> 4;
+ len_part = 7;
+ if (arm_feature(&cpu->env, ARM_FEATURE_V6)) {
+ arch = 6;
+ elf_name = "armv6" END_SUFFIX;
+ } else if (arm_feature(&cpu->env, ARM_FEATURE_V5)) {
+ arch = 5;
+ elf_name = "armv5t" END_SUFFIX;
+ } else {
+ arch = 4;
+ elf_name = "armv4" END_SUFFIX;
+ }
+ }
+ }
+#endif
+
+#undef END_SUFFIX
+
+ num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ for (int i = 0; i < num_cpus; i++) {
+ dprintf(fd,
+ "processor\t: %d\n"
+ "model name\t: ARMv%d Processor rev %d (%s)\n"
+ "BogoMIPS\t: 100.00\n"
+ "Features\t:",
+ i, arch, midr_rev, elf_name);
+
+ for (target_ulong j = elf_hwcap; j ; j &= j - 1) {
+ dprintf(fd, " %s", elf_hwcap_str(ctz64(j)));
+ }
+ for (target_ulong j = elf_hwcap2; j ; j &= j - 1) {
+ dprintf(fd, " %s", elf_hwcap2_str(ctz64(j)));
+ }
+
+ dprintf(fd, "\n"
+ "CPU implementer\t: 0x%02x\n"
+ "CPU architecture: %d\n"
+ "CPU variant\t: 0x%0*x\n",
+ midr_impl, arch, len_var, midr_var);
+ if (arch >= 7) {
+ dprintf(fd, "CPU part\t: 0x%0*x\n", len_part, midr_part);
+ }
+ dprintf(fd, "CPU revision\t: %d\n\n", midr_rev);
+ }
+
+ if (arch < 8) {
+ dprintf(fd, "Hardware\t: QEMU v%s %s\n", QEMU_VERSION,
+ cpu->dtb_compatible ? : "");
+ dprintf(fd, "Revision\t: 0000\n");
+ dprintf(fd, "Serial\t\t: 0000000000000000\n");
+ }
+ return 0;
+}
+#define HAVE_ARCH_PROC_CPUINFO
+
+#endif /* ARM_TARGET_PROC_H */
diff --git a/linux-user/cris/target_proc.h b/linux-user/cris/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/cris/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 92b981c..a5b28fa 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -143,8 +143,6 @@
}
#ifdef TARGET_X86_64
-#define ELF_START_MMAP 0x2aaaaab000ULL
-
#define ELF_CLASS ELFCLASS64
#define ELF_ARCH EM_X86_64
@@ -221,8 +219,6 @@
#endif
#else
-#define ELF_START_MMAP 0x80000000
-
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
@@ -308,8 +304,6 @@
#ifndef TARGET_AARCH64
/* 32 bit ARM definitions */
-#define ELF_START_MMAP 0x80000000
-
#define ELF_ARCH EM_ARM
#define ELF_CLASS ELFCLASS32
#define EXSTACK_DEFAULT true
@@ -466,7 +460,7 @@
#define ELF_HWCAP get_elf_hwcap()
#define ELF_HWCAP2 get_elf_hwcap2()
-static uint32_t get_elf_hwcap(void)
+uint32_t get_elf_hwcap(void)
{
ARMCPU *cpu = ARM_CPU(thread_cpu);
uint32_t hwcaps = 0;
@@ -508,7 +502,7 @@
return hwcaps;
}
-static uint32_t get_elf_hwcap2(void)
+uint32_t get_elf_hwcap2(void)
{
ARMCPU *cpu = ARM_CPU(thread_cpu);
uint32_t hwcaps = 0;
@@ -521,6 +515,49 @@
return hwcaps;
}
+const char *elf_hwcap_str(uint32_t bit)
+{
+ static const char *hwcap_str[] = {
+ [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp",
+ [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half",
+ [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb",
+ [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit",
+ [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult",
+ [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa",
+ [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp",
+ [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp",
+ [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java",
+ [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt",
+ [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch",
+ [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee",
+ [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon",
+ [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3",
+ [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16",
+ [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls",
+ [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4",
+ [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva",
+ [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt",
+ [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32",
+ [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae",
+ [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm",
+ };
+
+ return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL;
+}
+
+const char *elf_hwcap2_str(uint32_t bit)
+{
+ static const char *hwcap_str[] = {
+ [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes",
+ [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull",
+ [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1",
+ [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2",
+ [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32",
+ };
+
+ return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL;
+}
+
#undef GET_FEATURE
#undef GET_FEATURE_ID
@@ -557,7 +594,6 @@
#else
/* 64 bit ARM definitions */
-#define ELF_START_MMAP 0x80000000
#define ELF_ARCH EM_AARCH64
#define ELF_CLASS ELFCLASS64
@@ -668,7 +704,7 @@
#define GET_FEATURE_ID(feat, hwcap) \
do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0)
-static uint32_t get_elf_hwcap(void)
+uint32_t get_elf_hwcap(void)
{
ARMCPU *cpu = ARM_CPU(thread_cpu);
uint32_t hwcaps = 0;
@@ -706,7 +742,7 @@
return hwcaps;
}
-static uint32_t get_elf_hwcap2(void)
+uint32_t get_elf_hwcap2(void)
{
ARMCPU *cpu = ARM_CPU(thread_cpu);
uint32_t hwcaps = 0;
@@ -741,6 +777,85 @@
return hwcaps;
}
+const char *elf_hwcap_str(uint32_t bit)
+{
+ static const char *hwcap_str[] = {
+ [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp",
+ [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd",
+ [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm",
+ [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes",
+ [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull",
+ [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1",
+ [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2",
+ [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32",
+ [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics",
+ [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp",
+ [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp",
+ [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid",
+ [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm",
+ [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt",
+ [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma",
+ [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc",
+ [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop",
+ [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3",
+ [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3",
+ [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4",
+ [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp",
+ [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512",
+ [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve",
+ [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm",
+ [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit",
+ [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat",
+ [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc",
+ [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm",
+ [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs",
+ [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb",
+ [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca",
+ [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg",
+ };
+
+ return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL;
+}
+
+const char *elf_hwcap2_str(uint32_t bit)
+{
+ static const char *hwcap_str[] = {
+ [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4",
+ [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2",
+ [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm",
+ [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16",
+ [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm",
+ [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16",
+ [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh",
+ [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng",
+ [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti",
+ [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte",
+ [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv",
+ [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp",
+ [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres",
+ [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "sme_i16i64",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "sme_f64f64",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "sme_i8i32",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "sme_f16f32",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "sme_b16f32",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "sme_f32f32",
+ [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "sme_fa64",
+ };
+
+ return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL;
+}
+
#undef GET_FEATURE_ID
#endif /* not TARGET_AARCH64 */
@@ -749,7 +864,6 @@
#ifdef TARGET_SPARC
#ifdef TARGET_SPARC64
-#define ELF_START_MMAP 0x80000000
#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \
| HWCAP_SPARC_MULDIV | HWCAP_SPARC_V9)
#ifndef TARGET_ABI32
@@ -761,7 +875,6 @@
#define ELF_CLASS ELFCLASS64
#define ELF_ARCH EM_SPARCV9
#else
-#define ELF_START_MMAP 0x80000000
#define ELF_HWCAP (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | HWCAP_SPARC_SWAP \
| HWCAP_SPARC_MULDIV)
#define ELF_CLASS ELFCLASS32
@@ -783,7 +896,6 @@
#ifdef TARGET_PPC
#define ELF_MACHINE PPC_ELF_MACHINE
-#define ELF_START_MMAP 0x80000000
#if defined(TARGET_PPC64)
@@ -986,8 +1098,6 @@
#ifdef TARGET_LOONGARCH64
-#define ELF_START_MMAP 0x80000000
-
#define ELF_CLASS ELFCLASS64
#define ELF_ARCH EM_LOONGARCH
#define EXSTACK_DEFAULT true
@@ -1078,8 +1188,6 @@
#ifdef TARGET_MIPS
-#define ELF_START_MMAP 0x80000000
-
#ifdef TARGET_MIPS64
#define ELF_CLASS ELFCLASS64
#else
@@ -1237,8 +1345,6 @@
#ifdef TARGET_MICROBLAZE
-#define ELF_START_MMAP 0x80000000
-
#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD)
#define ELF_CLASS ELFCLASS32
@@ -1279,8 +1385,6 @@
#ifdef TARGET_NIOS2
-#define ELF_START_MMAP 0x80000000
-
#define elf_check_arch(x) ((x) == EM_ALTERA_NIOS2)
#define ELF_CLASS ELFCLASS32
@@ -1376,8 +1480,6 @@
#ifdef TARGET_OPENRISC
-#define ELF_START_MMAP 0x08000000
-
#define ELF_ARCH EM_OPENRISC
#define ELF_CLASS ELFCLASS32
#define ELF_DATA ELFDATA2MSB
@@ -1414,8 +1516,6 @@
#ifdef TARGET_SH4
-#define ELF_START_MMAP 0x80000000
-
#define ELF_CLASS ELFCLASS32
#define ELF_ARCH EM_SH
@@ -1496,8 +1596,6 @@
#ifdef TARGET_CRIS
-#define ELF_START_MMAP 0x80000000
-
#define ELF_CLASS ELFCLASS32
#define ELF_ARCH EM_CRIS
@@ -1513,8 +1611,6 @@
#ifdef TARGET_M68K
-#define ELF_START_MMAP 0x80000000
-
#define ELF_CLASS ELFCLASS32
#define ELF_ARCH EM_68K
@@ -1564,8 +1660,6 @@
#ifdef TARGET_ALPHA
-#define ELF_START_MMAP (0x30000000000ULL)
-
#define ELF_CLASS ELFCLASS64
#define ELF_ARCH EM_ALPHA
@@ -1583,8 +1677,6 @@
#ifdef TARGET_S390X
-#define ELF_START_MMAP (0x20000000000ULL)
-
#define ELF_CLASS ELFCLASS64
#define ELF_DATA ELFDATA2MSB
#define ELF_ARCH EM_S390
@@ -1695,7 +1787,6 @@
#ifdef TARGET_RISCV
-#define ELF_START_MMAP 0x80000000
#define ELF_ARCH EM_RISCV
#ifdef TARGET_RISCV32
@@ -1731,7 +1822,6 @@
#ifdef TARGET_HPPA
-#define ELF_START_MMAP 0x80000000
#define ELF_CLASS ELFCLASS32
#define ELF_ARCH EM_PARISC
#define ELF_PLATFORM "PARISC"
@@ -1783,8 +1873,6 @@
#ifdef TARGET_XTENSA
-#define ELF_START_MMAP 0x20000000
-
#define ELF_CLASS ELFCLASS32
#define ELF_ARCH EM_XTENSA
@@ -1850,8 +1938,6 @@
#ifdef TARGET_HEXAGON
-#define ELF_START_MMAP 0x20000000
-
#define ELF_CLASS ELFCLASS32
#define ELF_ARCH EM_HEXAGON
@@ -3205,7 +3291,7 @@
info->start_data = -1;
info->end_data = 0;
/* Usual start for brk is after all sections of the main executable. */
- info->brk = TARGET_PAGE_ALIGN(hiaddr);
+ info->brk = TARGET_PAGE_ALIGN(hiaddr + load_bias);
info->elf_flags = ehdr->e_flags;
prot_exec = PROT_EXEC;
@@ -3568,8 +3654,6 @@
interp_info.fp_abi = MIPS_ABI_FP_UNKNOWN;
#endif
- info->start_mmap = (abi_ulong)ELF_START_MMAP;
-
load_elf_image(bprm->filename, bprm->fd, info,
&elf_interpreter, bprm->buf);
diff --git a/linux-user/hexagon/target_proc.h b/linux-user/hexagon/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/hexagon/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/hppa/target_proc.h b/linux-user/hppa/target_proc.h
new file mode 100644
index 0000000..9340c3b
--- /dev/null
+++ b/linux-user/hppa/target_proc.h
@@ -0,0 +1,26 @@
+/*
+ * HPPA specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HPPA_TARGET_PROC_H
+#define HPPA_TARGET_PROC_H
+
+static int open_cpuinfo(CPUArchState *cpu_env, int fd)
+{
+ int i, num_cpus;
+
+ num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ for (i = 0; i < num_cpus; i++) {
+ dprintf(fd, "processor\t: %d\n", i);
+ dprintf(fd, "cpu family\t: PA-RISC 1.1e\n");
+ dprintf(fd, "cpu\t\t: PA7300LC (PCX-L2)\n");
+ dprintf(fd, "capabilities\t: os32\n");
+ dprintf(fd, "model\t\t: 9000/778/B160L - "
+ "Merlin L2 160 QEMU (9000/778/B160L)\n\n");
+ }
+ return 0;
+}
+#define HAVE_ARCH_PROC_CPUINFO
+
+#endif /* HPPA_TARGET_PROC_H */
diff --git a/linux-user/i386/target_proc.h b/linux-user/i386/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/i386/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/loader.h b/linux-user/loader.h
index 59cbeac..324e5c8 100644
--- a/linux-user/loader.h
+++ b/linux-user/loader.h
@@ -56,9 +56,13 @@
extern unsigned long guest_stack_size;
-#ifdef TARGET_S390X
+#if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM)
uint32_t get_elf_hwcap(void);
const char *elf_hwcap_str(uint32_t bit);
#endif
+#if defined(TARGET_AARCH64) || defined(TARGET_ARM)
+uint32_t get_elf_hwcap2(void);
+const char *elf_hwcap2_str(uint32_t bit);
+#endif
#endif /* LINUX_USER_LOADER_H */
diff --git a/linux-user/loongarch64/target_proc.h b/linux-user/loongarch64/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/loongarch64/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/m68k/target_proc.h b/linux-user/m68k/target_proc.h
new file mode 100644
index 0000000..3df8f28
--- /dev/null
+++ b/linux-user/m68k/target_proc.h
@@ -0,0 +1,16 @@
+/*
+ * M68K specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef M68K_TARGET_PROC_H
+#define M68K_TARGET_PROC_H
+
+static int open_hardware(CPUArchState *cpu_env, int fd)
+{
+ dprintf(fd, "Model:\t\tqemu-m68k\n");
+ return 0;
+}
+#define HAVE_ARCH_PROC_HARDWARE
+
+#endif /* M68K_TARGET_PROC_H */
diff --git a/linux-user/microblaze/target_proc.h b/linux-user/microblaze/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/microblaze/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/mips/target_proc.h b/linux-user/mips/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/mips/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/mips64/target_proc.h b/linux-user/mips64/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/mips64/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 9aab48d..8eaf57b 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -17,12 +17,14 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
+#include <sys/shm.h>
#include "trace.h"
#include "exec/log.h"
#include "qemu.h"
#include "user-internals.h"
#include "user-mmap.h"
#include "target_mman.h"
+#include "qemu/interval-tree.h"
static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
static __thread int mmap_lock_count;
@@ -64,6 +66,44 @@
}
}
+/* Protected by mmap_lock. */
+static IntervalTreeRoot shm_regions;
+
+static void shm_region_add(abi_ptr start, abi_ptr last)
+{
+ IntervalTreeNode *i = g_new0(IntervalTreeNode, 1);
+
+ i->start = start;
+ i->last = last;
+ interval_tree_insert(i, &shm_regions);
+}
+
+static abi_ptr shm_region_find(abi_ptr start)
+{
+ IntervalTreeNode *i;
+
+ for (i = interval_tree_iter_first(&shm_regions, start, start); i;
+ i = interval_tree_iter_next(i, start, start)) {
+ if (i->start == start) {
+ return i->last;
+ }
+ }
+ return 0;
+}
+
+static void shm_region_rm_complete(abi_ptr start, abi_ptr last)
+{
+ IntervalTreeNode *i, *n;
+
+ for (i = interval_tree_iter_first(&shm_regions, start, last); i; i = n) {
+ n = interval_tree_iter_next(i, start, last);
+ if (i->start >= start && i->last <= last) {
+ interval_tree_remove(i, &shm_regions);
+ g_free(i);
+ }
+ }
+}
+
/*
* Validate target prot bitmask.
* Return the prot bitmask for the host in *HOST_PROT.
@@ -720,6 +760,7 @@
page_set_flags(passthrough_last + 1, last, page_flags);
}
}
+ shm_region_rm_complete(start, last);
the_end:
trace_target_mmap_complete(start);
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
@@ -817,6 +858,7 @@
mmap_lock();
mmap_reserve_or_unmap(start, len);
page_set_flags(start, start + len - 1, 0);
+ shm_region_rm_complete(start, start + len - 1);
mmap_unlock();
return 0;
@@ -906,8 +948,10 @@
new_addr = h2g(host_addr);
prot = page_get_flags(old_addr);
page_set_flags(old_addr, old_addr + old_size - 1, 0);
+ shm_region_rm_complete(old_addr, old_addr + old_size - 1);
page_set_flags(new_addr, new_addr + new_size - 1,
prot | PAGE_VALID | PAGE_RESET);
+ shm_region_rm_complete(new_addr, new_addr + new_size - 1);
}
mmap_unlock();
return new_addr;
@@ -981,3 +1025,127 @@
return ret;
}
+
+#ifndef TARGET_FORCE_SHMLBA
+/*
+ * For most architectures, SHMLBA is the same as the page size;
+ * some architectures have larger values, in which case they should
+ * define TARGET_FORCE_SHMLBA and provide a target_shmlba() function.
+ * This corresponds to the kernel arch code defining __ARCH_FORCE_SHMLBA
+ * and defining its own value for SHMLBA.
+ *
+ * The kernel also permits SHMLBA to be set by the architecture to a
+ * value larger than the page size without setting __ARCH_FORCE_SHMLBA;
+ * this means that addresses are rounded to the large size if
+ * SHM_RND is set but addresses not aligned to that size are not rejected
+ * as long as they are at least page-aligned. Since the only architecture
+ * which uses this is ia64 this code doesn't provide for that oddity.
+ */
+static inline abi_ulong target_shmlba(CPUArchState *cpu_env)
+{
+ return TARGET_PAGE_SIZE;
+}
+#endif
+
+abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
+ abi_ulong shmaddr, int shmflg)
+{
+ CPUState *cpu = env_cpu(cpu_env);
+ abi_ulong raddr;
+ struct shmid_ds shm_info;
+ int ret;
+ abi_ulong shmlba;
+
+ /* shmat pointers are always untagged */
+
+ /* find out the length of the shared memory segment */
+ ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info));
+ if (is_error(ret)) {
+ /* can't get length, bail out */
+ return ret;
+ }
+
+ shmlba = target_shmlba(cpu_env);
+
+ if (shmaddr & (shmlba - 1)) {
+ if (shmflg & SHM_RND) {
+ shmaddr &= ~(shmlba - 1);
+ } else {
+ return -TARGET_EINVAL;
+ }
+ }
+ if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) {
+ return -TARGET_EINVAL;
+ }
+
+ WITH_MMAP_LOCK_GUARD() {
+ void *host_raddr;
+ abi_ulong last;
+
+ if (shmaddr) {
+ host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg);
+ } else {
+ abi_ulong mmap_start;
+
+ /* In order to use the host shmat, we need to honor host SHMLBA. */
+ mmap_start = mmap_find_vma(0, shm_info.shm_segsz,
+ MAX(SHMLBA, shmlba));
+
+ if (mmap_start == -1) {
+ return -TARGET_ENOMEM;
+ }
+ host_raddr = shmat(shmid, g2h_untagged(mmap_start),
+ shmflg | SHM_REMAP);
+ }
+
+ if (host_raddr == (void *)-1) {
+ return get_errno(-1);
+ }
+ raddr = h2g(host_raddr);
+ last = raddr + shm_info.shm_segsz - 1;
+
+ page_set_flags(raddr, last,
+ PAGE_VALID | PAGE_RESET | PAGE_READ |
+ (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE));
+
+ shm_region_rm_complete(raddr, last);
+ shm_region_add(raddr, last);
+ }
+
+ /*
+ * We're mapping shared memory, so ensure we generate code for parallel
+ * execution and flush old translations. This will work up to the level
+ * supported by the host -- anything that requires EXCP_ATOMIC will not
+ * be atomic with respect to an external process.
+ */
+ if (!(cpu->tcg_cflags & CF_PARALLEL)) {
+ cpu->tcg_cflags |= CF_PARALLEL;
+ tb_flush(cpu);
+ }
+
+ return raddr;
+}
+
+abi_long target_shmdt(abi_ulong shmaddr)
+{
+ abi_long rv;
+
+ /* shmdt pointers are always untagged */
+
+ WITH_MMAP_LOCK_GUARD() {
+ abi_ulong last = shm_region_find(shmaddr);
+ if (last == 0) {
+ return -TARGET_EINVAL;
+ }
+
+ rv = get_errno(shmdt(g2h_untagged(shmaddr)));
+ if (rv == 0) {
+ abi_ulong size = last - shmaddr + 1;
+
+ page_set_flags(shmaddr, last, 0);
+ shm_region_rm_complete(shmaddr, last);
+ mmap_reserve_or_unmap(shmaddr, size);
+ }
+ }
+ return rv;
+}
diff --git a/linux-user/nios2/target_proc.h b/linux-user/nios2/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/nios2/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/openrisc/target_proc.h b/linux-user/openrisc/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/openrisc/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/ppc/target_proc.h b/linux-user/ppc/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/ppc/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 4f8b55e..12f6383 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -30,7 +30,6 @@
abi_ulong start_data;
abi_ulong end_data;
abi_ulong brk;
- abi_ulong start_mmap;
abi_ulong start_stack;
abi_ulong stack_limit;
abi_ulong entry;
diff --git a/linux-user/riscv/target_proc.h b/linux-user/riscv/target_proc.h
new file mode 100644
index 0000000..c77c003
--- /dev/null
+++ b/linux-user/riscv/target_proc.h
@@ -0,0 +1,37 @@
+/*
+ * RISC-V specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef RISCV_TARGET_PROC_H
+#define RISCV_TARGET_PROC_H
+
+static int open_cpuinfo(CPUArchState *cpu_env, int fd)
+{
+ int i;
+ int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ RISCVCPU *cpu = env_archcpu(cpu_env);
+ const RISCVCPUConfig *cfg = riscv_cpu_cfg((CPURISCVState *) cpu_env);
+ char *isa_string = riscv_isa_string(cpu);
+ const char *mmu;
+
+ if (cfg->mmu) {
+ mmu = (cpu_env->xl == MXL_RV32) ? "sv32" : "sv48";
+ } else {
+ mmu = "none";
+ }
+
+ for (i = 0; i < num_cpus; i++) {
+ dprintf(fd, "processor\t: %d\n", i);
+ dprintf(fd, "hart\t\t: %d\n", i);
+ dprintf(fd, "isa\t\t: %s\n", isa_string);
+ dprintf(fd, "mmu\t\t: %s\n", mmu);
+ dprintf(fd, "uarch\t\t: qemu\n\n");
+ }
+
+ g_free(isa_string);
+ return 0;
+}
+#define HAVE_ARCH_PROC_CPUINFO
+
+#endif /* RISCV_TARGET_PROC_H */
diff --git a/linux-user/s390x/target_proc.h b/linux-user/s390x/target_proc.h
new file mode 100644
index 0000000..a4a4821
--- /dev/null
+++ b/linux-user/s390x/target_proc.h
@@ -0,0 +1,109 @@
+/*
+ * S390X specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef S390X_TARGET_PROC_H
+#define S390X_TARGET_PROC_H
+
+/*
+ * Emulate what a Linux kernel running in qemu-system-s390x -M accel=tcg would
+ * show in /proc/cpuinfo.
+ *
+ * Skip the following in order to match the missing support in op_ecag():
+ * - show_cacheinfo().
+ * - show_cpu_topology().
+ * - show_cpu_mhz().
+ *
+ * Use fixed values for certain fields:
+ * - bogomips per cpu - from a qemu-system-s390x run.
+ * - max thread id = 0, since SMT / SIGP_SET_MULTI_THREADING is not supported.
+ *
+ * Keep the code structure close to arch/s390/kernel/processor.c.
+ */
+
+static void show_facilities(int fd)
+{
+ size_t sizeof_stfl_bytes = 2048;
+ g_autofree uint8_t *stfl_bytes = g_new0(uint8_t, sizeof_stfl_bytes);
+ unsigned int bit;
+
+ dprintf(fd, "facilities :");
+ s390_get_feat_block(S390_FEAT_TYPE_STFL, stfl_bytes);
+ for (bit = 0; bit < sizeof_stfl_bytes * 8; bit++) {
+ if (test_be_bit(bit, stfl_bytes)) {
+ dprintf(fd, " %d", bit);
+ }
+ }
+ dprintf(fd, "\n");
+}
+
+static int cpu_ident(unsigned long n)
+{
+ return deposit32(0, CPU_ID_BITS - CPU_PHYS_ADDR_BITS, CPU_PHYS_ADDR_BITS,
+ n);
+}
+
+static void show_cpu_summary(CPUArchState *cpu_env, int fd)
+{
+ S390CPUModel *model = env_archcpu(cpu_env)->model;
+ int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ uint32_t elf_hwcap = get_elf_hwcap();
+ const char *hwcap_str;
+ int i;
+
+ dprintf(fd, "vendor_id : IBM/S390\n"
+ "# processors : %i\n"
+ "bogomips per cpu: 13370.00\n",
+ num_cpus);
+ dprintf(fd, "max thread id : 0\n");
+ dprintf(fd, "features\t: ");
+ for (i = 0; i < sizeof(elf_hwcap) * 8; i++) {
+ if (!(elf_hwcap & (1 << i))) {
+ continue;
+ }
+ hwcap_str = elf_hwcap_str(i);
+ if (hwcap_str) {
+ dprintf(fd, "%s ", hwcap_str);
+ }
+ }
+ dprintf(fd, "\n");
+ show_facilities(fd);
+ for (i = 0; i < num_cpus; i++) {
+ dprintf(fd, "processor %d: "
+ "version = %02X, "
+ "identification = %06X, "
+ "machine = %04X\n",
+ i, model->cpu_ver, cpu_ident(i), model->def->type);
+ }
+}
+
+static void show_cpu_ids(CPUArchState *cpu_env, int fd, unsigned long n)
+{
+ S390CPUModel *model = env_archcpu(cpu_env)->model;
+
+ dprintf(fd, "version : %02X\n", model->cpu_ver);
+ dprintf(fd, "identification : %06X\n", cpu_ident(n));
+ dprintf(fd, "machine : %04X\n", model->def->type);
+}
+
+static void show_cpuinfo(CPUArchState *cpu_env, int fd, unsigned long n)
+{
+ dprintf(fd, "\ncpu number : %ld\n", n);
+ show_cpu_ids(cpu_env, fd, n);
+}
+
+static int open_cpuinfo(CPUArchState *cpu_env, int fd)
+{
+ int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
+ int i;
+
+ show_cpu_summary(cpu_env, fd);
+ for (i = 0; i < num_cpus; i++) {
+ show_cpuinfo(cpu_env, fd, i);
+ }
+ return 0;
+}
+#define HAVE_ARCH_PROC_CPUINFO
+
+#endif /* S390X_TARGET_PROC_H */
diff --git a/linux-user/sh4/target_proc.h b/linux-user/sh4/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/sh4/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/sparc/target_proc.h b/linux-user/sparc/target_proc.h
new file mode 100644
index 0000000..3bb3134
--- /dev/null
+++ b/linux-user/sparc/target_proc.h
@@ -0,0 +1,16 @@
+/*
+ * Sparc specific proc functions for linux-user
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef SPARC_TARGET_PROC_H
+#define SPARC_TARGET_PROC_H
+
+static int open_cpuinfo(CPUArchState *cpu_env, int fd)
+{
+ dprintf(fd, "type\t\t: sun4u\n");
+ return 0;
+}
+#define HAVE_ARCH_PROC_CPUINFO
+
+#endif /* SPARC_TARGET_PROC_H */
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 7ccd3af..dac0641 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -3725,14 +3725,6 @@
}
#endif
-#define N_SHM_REGIONS 32
-
-static struct shm_region {
- abi_ulong start;
- abi_ulong size;
- bool in_use;
-} shm_regions[N_SHM_REGIONS];
-
#ifndef TARGET_SEMID64_DS
/* asm-generic version of this struct */
struct target_semid64_ds
@@ -4482,133 +4474,6 @@
return ret;
}
-#ifndef TARGET_FORCE_SHMLBA
-/* For most architectures, SHMLBA is the same as the page size;
- * some architectures have larger values, in which case they should
- * define TARGET_FORCE_SHMLBA and provide a target_shmlba() function.
- * This corresponds to the kernel arch code defining __ARCH_FORCE_SHMLBA
- * and defining its own value for SHMLBA.
- *
- * The kernel also permits SHMLBA to be set by the architecture to a
- * value larger than the page size without setting __ARCH_FORCE_SHMLBA;
- * this means that addresses are rounded to the large size if
- * SHM_RND is set but addresses not aligned to that size are not rejected
- * as long as they are at least page-aligned. Since the only architecture
- * which uses this is ia64 this code doesn't provide for that oddity.
- */
-static inline abi_ulong target_shmlba(CPUArchState *cpu_env)
-{
- return TARGET_PAGE_SIZE;
-}
-#endif
-
-static abi_ulong do_shmat(CPUArchState *cpu_env, int shmid,
- abi_ulong shmaddr, int shmflg)
-{
- CPUState *cpu = env_cpu(cpu_env);
- abi_ulong raddr;
- void *host_raddr;
- struct shmid_ds shm_info;
- int i, ret;
- abi_ulong shmlba;
-
- /* shmat pointers are always untagged */
-
- /* find out the length of the shared memory segment */
- ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info));
- if (is_error(ret)) {
- /* can't get length, bail out */
- return ret;
- }
-
- shmlba = target_shmlba(cpu_env);
-
- if (shmaddr & (shmlba - 1)) {
- if (shmflg & SHM_RND) {
- shmaddr &= ~(shmlba - 1);
- } else {
- return -TARGET_EINVAL;
- }
- }
- if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) {
- return -TARGET_EINVAL;
- }
-
- mmap_lock();
-
- /*
- * We're mapping shared memory, so ensure we generate code for parallel
- * execution and flush old translations. This will work up to the level
- * supported by the host -- anything that requires EXCP_ATOMIC will not
- * be atomic with respect to an external process.
- */
- if (!(cpu->tcg_cflags & CF_PARALLEL)) {
- cpu->tcg_cflags |= CF_PARALLEL;
- tb_flush(cpu);
- }
-
- if (shmaddr)
- host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg);
- else {
- abi_ulong mmap_start;
-
- /* In order to use the host shmat, we need to honor host SHMLBA. */
- mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba));
-
- if (mmap_start == -1) {
- errno = ENOMEM;
- host_raddr = (void *)-1;
- } else
- host_raddr = shmat(shmid, g2h_untagged(mmap_start),
- shmflg | SHM_REMAP);
- }
-
- if (host_raddr == (void *)-1) {
- mmap_unlock();
- return get_errno((intptr_t)host_raddr);
- }
- raddr = h2g((uintptr_t)host_raddr);
-
- page_set_flags(raddr, raddr + shm_info.shm_segsz - 1,
- PAGE_VALID | PAGE_RESET | PAGE_READ |
- (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE));
-
- for (i = 0; i < N_SHM_REGIONS; i++) {
- if (!shm_regions[i].in_use) {
- shm_regions[i].in_use = true;
- shm_regions[i].start = raddr;
- shm_regions[i].size = shm_info.shm_segsz;
- break;
- }
- }
-
- mmap_unlock();
- return raddr;
-}
-
-static inline abi_long do_shmdt(abi_ulong shmaddr)
-{
- int i;
- abi_long rv;
-
- /* shmdt pointers are always untagged */
-
- mmap_lock();
-
- for (i = 0; i < N_SHM_REGIONS; ++i) {
- if (shm_regions[i].in_use && shm_regions[i].start == shmaddr) {
- shm_regions[i].in_use = false;
- page_set_flags(shmaddr, shmaddr + shm_regions[i].size - 1, 0);
- break;
- }
- }
- rv = get_errno(shmdt(g2h_untagged(shmaddr)));
-
- mmap_unlock();
-
- return rv;
-}
-
#ifdef TARGET_NR_ipc
/* ??? This only works with linear mappings. */
/* do_ipc() must return target values and target errnos. */
@@ -4695,7 +4560,7 @@
default:
{
abi_ulong raddr;
- raddr = do_shmat(cpu_env, first, ptr, second);
+ raddr = target_shmat(cpu_env, first, ptr, second);
if (is_error(raddr))
return get_errno(raddr);
if (put_user_ual(raddr, third))
@@ -4708,7 +4573,7 @@
}
break;
case IPCOP_shmdt:
- ret = do_shmdt(ptr);
+ ret = target_shmdt(ptr);
break;
case IPCOP_shmget:
@@ -8095,12 +7960,68 @@
return 0;
}
-static void show_smaps(int fd, unsigned long size)
-{
- unsigned long page_size_kb = TARGET_PAGE_SIZE >> 10;
- unsigned long size_kb = size >> 10;
+struct open_self_maps_data {
+ TaskState *ts;
+ IntervalTreeRoot *host_maps;
+ int fd;
+ bool smaps;
+};
- dprintf(fd, "Size: %lu kB\n"
+/*
+ * Subroutine to output one line of /proc/self/maps,
+ * or one region of /proc/self/smaps.
+ */
+
+#ifdef TARGET_HPPA
+# define test_stack(S, E, L) (E == L)
+#else
+# define test_stack(S, E, L) (S == L)
+#endif
+
+static void open_self_maps_4(const struct open_self_maps_data *d,
+ const MapInfo *mi, abi_ptr start,
+ abi_ptr end, unsigned flags)
+{
+ const struct image_info *info = d->ts->info;
+ const char *path = mi->path;
+ uint64_t offset;
+ int fd = d->fd;
+ int count;
+
+ if (test_stack(start, end, info->stack_limit)) {
+ path = "[stack]";
+ } else if (start == info->brk) {
+ path = "[heap]";
+ }
+
+ /* Except null device (MAP_ANON), adjust offset for this fragment. */
+ offset = mi->offset;
+ if (mi->dev) {
+ uintptr_t hstart = (uintptr_t)g2h_untagged(start);
+ offset += hstart - mi->itree.start;
+ }
+
+ count = dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr
+ " %c%c%c%c %08" PRIx64 " %02x:%02x %"PRId64,
+ start, end,
+ (flags & PAGE_READ) ? 'r' : '-',
+ (flags & PAGE_WRITE_ORG) ? 'w' : '-',
+ (flags & PAGE_EXEC) ? 'x' : '-',
+ mi->is_priv ? 'p' : 's',
+ offset, major(mi->dev), minor(mi->dev),
+ (uint64_t)mi->inode);
+ if (path) {
+ dprintf(fd, "%*s%s\n", 73 - count, "", path);
+ } else {
+ dprintf(fd, "\n");
+ }
+
+ if (d->smaps) {
+ unsigned long size = end - start;
+ unsigned long page_size_kb = TARGET_PAGE_SIZE >> 10;
+ unsigned long size_kb = size >> 10;
+
+ dprintf(fd, "Size: %lu kB\n"
"KernelPageSize: %lu kB\n"
"MMUPageSize: %lu kB\n"
"Rss: 0 kB\n"
@@ -8111,7 +8032,7 @@
"Private_Clean: 0 kB\n"
"Private_Dirty: 0 kB\n"
"Referenced: 0 kB\n"
- "Anonymous: 0 kB\n"
+ "Anonymous: %lu kB\n"
"LazyFree: 0 kB\n"
"AnonHugePages: 0 kB\n"
"ShmemPmdMapped: 0 kB\n"
@@ -8121,89 +8042,76 @@
"Swap: 0 kB\n"
"SwapPss: 0 kB\n"
"Locked: 0 kB\n"
- "THPeligible: 0\n", size_kb, page_size_kb, page_size_kb);
+ "THPeligible: 0\n"
+ "VmFlags:%s%s%s%s%s%s%s%s\n",
+ size_kb, page_size_kb, page_size_kb,
+ (flags & PAGE_ANON ? size_kb : 0),
+ (flags & PAGE_READ) ? " rd" : "",
+ (flags & PAGE_WRITE_ORG) ? " wr" : "",
+ (flags & PAGE_EXEC) ? " ex" : "",
+ mi->is_priv ? "" : " sh",
+ (flags & PAGE_READ) ? " mr" : "",
+ (flags & PAGE_WRITE_ORG) ? " mw" : "",
+ (flags & PAGE_EXEC) ? " me" : "",
+ mi->is_priv ? "" : " ms");
+ }
}
-static int open_self_maps_1(CPUArchState *cpu_env, int fd, bool smaps)
+/*
+ * Callback for walk_memory_regions, when read_self_maps() fails.
+ * Proceed without the benefit of host /proc/self/maps cross-check.
+ */
+static int open_self_maps_3(void *opaque, target_ulong guest_start,
+ target_ulong guest_end, unsigned long flags)
{
- CPUState *cpu = env_cpu(cpu_env);
- TaskState *ts = cpu->opaque;
- IntervalTreeRoot *map_info = read_self_maps();
- IntervalTreeNode *s;
- int count;
+ static const MapInfo mi = { .is_priv = true };
- for (s = interval_tree_iter_first(map_info, 0, -1); s;
- s = interval_tree_iter_next(s, 0, -1)) {
- MapInfo *e = container_of(s, MapInfo, itree);
+ open_self_maps_4(opaque, &mi, guest_start, guest_end, flags);
+ return 0;
+}
- if (h2g_valid(e->itree.start)) {
- unsigned long min = e->itree.start;
- unsigned long max = e->itree.last + 1;
- int flags = page_get_flags(h2g(min));
- const char *path;
+/*
+ * Callback for walk_memory_regions, when read_self_maps() succeeds.
+ */
+static int open_self_maps_2(void *opaque, target_ulong guest_start,
+ target_ulong guest_end, unsigned long flags)
+{
+ const struct open_self_maps_data *d = opaque;
+ uintptr_t host_start = (uintptr_t)g2h_untagged(guest_start);
+ uintptr_t host_last = (uintptr_t)g2h_untagged(guest_end - 1);
- max = h2g_valid(max - 1) ?
- max : (uintptr_t) g2h_untagged(GUEST_ADDR_MAX) + 1;
+ while (1) {
+ IntervalTreeNode *n =
+ interval_tree_iter_first(d->host_maps, host_start, host_start);
+ MapInfo *mi = container_of(n, MapInfo, itree);
+ uintptr_t this_hlast = MIN(host_last, n->last);
+ target_ulong this_gend = h2g(this_hlast) + 1;
- if (!page_check_range(h2g(min), max - min, flags)) {
- continue;
- }
+ open_self_maps_4(d, mi, guest_start, this_gend, flags);
-#ifdef TARGET_HPPA
- if (h2g(max) == ts->info->stack_limit) {
-#else
- if (h2g(min) == ts->info->stack_limit) {
-#endif
- path = "[stack]";
- } else {
- path = e->path;
- }
-
- count = dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr
- " %c%c%c%c %08" PRIx64 " %s %"PRId64,
- h2g(min), h2g(max - 1) + 1,
- (flags & PAGE_READ) ? 'r' : '-',
- (flags & PAGE_WRITE_ORG) ? 'w' : '-',
- (flags & PAGE_EXEC) ? 'x' : '-',
- e->is_priv ? 'p' : 's',
- (uint64_t) e->offset, e->dev, e->inode);
- if (path) {
- dprintf(fd, "%*s%s\n", 73 - count, "", path);
- } else {
- dprintf(fd, "\n");
- }
- if (smaps) {
- show_smaps(fd, max - min);
- dprintf(fd, "VmFlags:%s%s%s%s%s%s%s%s\n",
- (flags & PAGE_READ) ? " rd" : "",
- (flags & PAGE_WRITE_ORG) ? " wr" : "",
- (flags & PAGE_EXEC) ? " ex" : "",
- e->is_priv ? "" : " sh",
- (flags & PAGE_READ) ? " mr" : "",
- (flags & PAGE_WRITE_ORG) ? " mw" : "",
- (flags & PAGE_EXEC) ? " me" : "",
- e->is_priv ? "" : " ms");
- }
+ if (this_hlast == host_last) {
+ return 0;
}
+ host_start = this_hlast + 1;
+ guest_start = h2g(host_start);
}
+}
- free_self_maps(map_info);
+static int open_self_maps_1(CPUArchState *env, int fd, bool smaps)
+{
+ struct open_self_maps_data d = {
+ .ts = env_cpu(env)->opaque,
+ .host_maps = read_self_maps(),
+ .fd = fd,
+ .smaps = smaps
+ };
-#ifdef TARGET_VSYSCALL_PAGE
- /*
- * We only support execution from the vsyscall page.
- * This is as if CONFIG_LEGACY_VSYSCALL_XONLY=y from v5.3.
- */
- count = dprintf(fd, TARGET_FMT_lx "-" TARGET_FMT_lx
- " --xp 00000000 00:00 0",
- TARGET_VSYSCALL_PAGE, TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE);
- dprintf(fd, "%*s%s\n", 73 - count, "", "[vsyscall]");
- if (smaps) {
- show_smaps(fd, TARGET_PAGE_SIZE);
- dprintf(fd, "VmFlags: ex\n");
+ if (d.host_maps) {
+ walk_memory_regions(&d, open_self_maps_2);
+ free_self_maps(d.host_maps);
+ } else {
+ walk_memory_regions(&d, open_self_maps_3);
}
-#endif
-
return 0;
}
@@ -8339,9 +8247,11 @@
}
}
+#include "target_proc.h"
+
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN || \
- defined(TARGET_SPARC) || defined(TARGET_M68K) || defined(TARGET_HPPA) || \
- defined(TARGET_RISCV) || defined(TARGET_S390X)
+ defined(HAVE_ARCH_PROC_CPUINFO) || \
+ defined(HAVE_ARCH_PROC_HARDWARE)
static int is_proc(const char *filename, const char *entry)
{
return strcmp(filename, entry) == 0;
@@ -8393,171 +8303,6 @@
}
#endif
-#if defined(TARGET_SPARC)
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- dprintf(fd, "type\t\t: sun4u\n");
- return 0;
-}
-#endif
-
-#if defined(TARGET_HPPA)
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- int i, num_cpus;
-
- num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- for (i = 0; i < num_cpus; i++) {
- dprintf(fd, "processor\t: %d\n", i);
- dprintf(fd, "cpu family\t: PA-RISC 1.1e\n");
- dprintf(fd, "cpu\t\t: PA7300LC (PCX-L2)\n");
- dprintf(fd, "capabilities\t: os32\n");
- dprintf(fd, "model\t\t: 9000/778/B160L - "
- "Merlin L2 160 QEMU (9000/778/B160L)\n\n");
- }
- return 0;
-}
-#endif
-
-#if defined(TARGET_RISCV)
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- int i;
- int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- RISCVCPU *cpu = env_archcpu(cpu_env);
- const RISCVCPUConfig *cfg = riscv_cpu_cfg((CPURISCVState *) cpu_env);
- char *isa_string = riscv_isa_string(cpu);
- const char *mmu;
-
- if (cfg->mmu) {
- mmu = (cpu_env->xl == MXL_RV32) ? "sv32" : "sv48";
- } else {
- mmu = "none";
- }
-
- for (i = 0; i < num_cpus; i++) {
- dprintf(fd, "processor\t: %d\n", i);
- dprintf(fd, "hart\t\t: %d\n", i);
- dprintf(fd, "isa\t\t: %s\n", isa_string);
- dprintf(fd, "mmu\t\t: %s\n", mmu);
- dprintf(fd, "uarch\t\t: qemu\n\n");
- }
-
- g_free(isa_string);
- return 0;
-}
-#endif
-
-#if defined(TARGET_S390X)
-/*
- * Emulate what a Linux kernel running in qemu-system-s390x -M accel=tcg would
- * show in /proc/cpuinfo.
- *
- * Skip the following in order to match the missing support in op_ecag():
- * - show_cacheinfo().
- * - show_cpu_topology().
- * - show_cpu_mhz().
- *
- * Use fixed values for certain fields:
- * - bogomips per cpu - from a qemu-system-s390x run.
- * - max thread id = 0, since SMT / SIGP_SET_MULTI_THREADING is not supported.
- *
- * Keep the code structure close to arch/s390/kernel/processor.c.
- */
-
-static void show_facilities(int fd)
-{
- size_t sizeof_stfl_bytes = 2048;
- g_autofree uint8_t *stfl_bytes = g_new0(uint8_t, sizeof_stfl_bytes);
- unsigned int bit;
-
- dprintf(fd, "facilities :");
- s390_get_feat_block(S390_FEAT_TYPE_STFL, stfl_bytes);
- for (bit = 0; bit < sizeof_stfl_bytes * 8; bit++) {
- if (test_be_bit(bit, stfl_bytes)) {
- dprintf(fd, " %d", bit);
- }
- }
- dprintf(fd, "\n");
-}
-
-static int cpu_ident(unsigned long n)
-{
- return deposit32(0, CPU_ID_BITS - CPU_PHYS_ADDR_BITS, CPU_PHYS_ADDR_BITS,
- n);
-}
-
-static void show_cpu_summary(CPUArchState *cpu_env, int fd)
-{
- S390CPUModel *model = env_archcpu(cpu_env)->model;
- int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- uint32_t elf_hwcap = get_elf_hwcap();
- const char *hwcap_str;
- int i;
-
- dprintf(fd, "vendor_id : IBM/S390\n"
- "# processors : %i\n"
- "bogomips per cpu: 13370.00\n",
- num_cpus);
- dprintf(fd, "max thread id : 0\n");
- dprintf(fd, "features\t: ");
- for (i = 0; i < sizeof(elf_hwcap) * 8; i++) {
- if (!(elf_hwcap & (1 << i))) {
- continue;
- }
- hwcap_str = elf_hwcap_str(i);
- if (hwcap_str) {
- dprintf(fd, "%s ", hwcap_str);
- }
- }
- dprintf(fd, "\n");
- show_facilities(fd);
- for (i = 0; i < num_cpus; i++) {
- dprintf(fd, "processor %d: "
- "version = %02X, "
- "identification = %06X, "
- "machine = %04X\n",
- i, model->cpu_ver, cpu_ident(i), model->def->type);
- }
-}
-
-static void show_cpu_ids(CPUArchState *cpu_env, int fd, unsigned long n)
-{
- S390CPUModel *model = env_archcpu(cpu_env)->model;
-
- dprintf(fd, "version : %02X\n", model->cpu_ver);
- dprintf(fd, "identification : %06X\n", cpu_ident(n));
- dprintf(fd, "machine : %04X\n", model->def->type);
-}
-
-static void show_cpuinfo(CPUArchState *cpu_env, int fd, unsigned long n)
-{
- dprintf(fd, "\ncpu number : %ld\n", n);
- show_cpu_ids(cpu_env, fd, n);
-}
-
-static int open_cpuinfo(CPUArchState *cpu_env, int fd)
-{
- int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
- int i;
-
- show_cpu_summary(cpu_env, fd);
- for (i = 0; i < num_cpus; i++) {
- show_cpuinfo(cpu_env, fd, i);
- }
- return 0;
-}
-#endif
-
-#if defined(TARGET_M68K)
-static int open_hardware(CPUArchState *cpu_env, int fd)
-{
- dprintf(fd, "Model:\t\tqemu-m68k\n");
- return 0;
-}
-#endif
-
-
int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname,
int flags, mode_t mode, bool safe)
{
@@ -8578,11 +8323,10 @@
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN
{ "/proc/net/route", open_net_route, is_proc },
#endif
-#if defined(TARGET_SPARC) || defined(TARGET_HPPA) || \
- defined(TARGET_RISCV) || defined(TARGET_S390X)
+#if defined(HAVE_ARCH_PROC_CPUINFO)
{ "/proc/cpuinfo", open_cpuinfo, is_proc },
#endif
-#if defined(TARGET_M68K)
+#if defined(HAVE_ARCH_PROC_HARDWARE)
{ "/proc/hardware", open_hardware, is_proc },
#endif
{ NULL, NULL, NULL }
@@ -11129,11 +10873,11 @@
#endif
#ifdef TARGET_NR_shmat
case TARGET_NR_shmat:
- return do_shmat(cpu_env, arg1, arg2, arg3);
+ return target_shmat(cpu_env, arg1, arg2, arg3);
#endif
#ifdef TARGET_NR_shmdt
case TARGET_NR_shmdt:
- return do_shmdt(arg1);
+ return target_shmdt(arg1);
#endif
case TARGET_NR_fsync:
return get_errno(fsync(arg1));
diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h
index 0f4883e..b94bcdc 100644
--- a/linux-user/user-mmap.h
+++ b/linux-user/user-mmap.h
@@ -58,4 +58,8 @@
void mmap_fork_start(void);
void mmap_fork_end(int child);
+abi_ulong target_shmat(CPUArchState *cpu_env, int shmid,
+ abi_ulong shmaddr, int shmflg);
+abi_long target_shmdt(abi_ulong shmaddr);
+
#endif /* LINUX_USER_USER_MMAP_H */
diff --git a/linux-user/x86_64/target_proc.h b/linux-user/x86_64/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/x86_64/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/linux-user/xtensa/target_proc.h b/linux-user/xtensa/target_proc.h
new file mode 100644
index 0000000..43fe29c
--- /dev/null
+++ b/linux-user/xtensa/target_proc.h
@@ -0,0 +1 @@
+/* No target-specific /proc support */
diff --git a/qapi/char.json b/qapi/char.json
index 52aaff2..c1bab7b 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -390,6 +390,10 @@
#
# @rows: console height, in chars
#
+# Note: the options are only effective when the VNC or SDL graphical
+# display backend is active. They are ignored with the GTK, Spice, VNC
+# and D-Bus display backends.
+#
# Since: 1.5
##
{ 'struct': 'ChardevVC',
diff --git a/target/ppc/compat.c b/target/ppc/compat.c
index 7949a24..ebef2cc 100644
--- a/target/ppc/compat.c
+++ b/target/ppc/compat.c
@@ -229,6 +229,25 @@
return 0;
}
+/* To be used when the machine is not running */
+int ppc_init_compat_all(uint32_t compat_pvr, Error **errp)
+{
+ CPUState *cs;
+
+ CPU_FOREACH(cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ int ret;
+
+ ret = ppc_set_compat(cpu, compat_pvr, errp);
+
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int ppc_compat_max_vthreads(PowerPCCPU *cpu)
{
const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr);
diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c
index 424f2e1..e3ad8e0 100644
--- a/target/ppc/cpu.c
+++ b/target/ppc/cpu.c
@@ -59,6 +59,7 @@
env->vscr_sat.u64[0] = vscr & (1u << VSCR_SAT);
env->vscr_sat.u64[1] = 0;
set_flush_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status);
+ set_flush_inputs_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status);
}
uint32_t ppc_get_vscr(CPUPPCState *env)
@@ -102,6 +103,92 @@
ppc_maybe_interrupt(env);
}
+
+#if defined(TARGET_PPC64)
+void ppc_update_ciabr(CPUPPCState *env)
+{
+ CPUState *cs = env_cpu(env);
+ target_ulong ciabr = env->spr[SPR_CIABR];
+ target_ulong ciea, priv;
+
+ ciea = ciabr & PPC_BITMASK(0, 61);
+ priv = ciabr & PPC_BITMASK(62, 63);
+
+ if (env->ciabr_breakpoint) {
+ cpu_breakpoint_remove_by_ref(cs, env->ciabr_breakpoint);
+ env->ciabr_breakpoint = NULL;
+ }
+
+ if (priv) {
+ cpu_breakpoint_insert(cs, ciea, BP_CPU, &env->ciabr_breakpoint);
+ }
+}
+
+void ppc_store_ciabr(CPUPPCState *env, target_ulong val)
+{
+ env->spr[SPR_CIABR] = val;
+ ppc_update_ciabr(env);
+}
+
+void ppc_update_daw0(CPUPPCState *env)
+{
+ CPUState *cs = env_cpu(env);
+ target_ulong deaw = env->spr[SPR_DAWR0] & PPC_BITMASK(0, 60);
+ uint32_t dawrx = env->spr[SPR_DAWRX0];
+ int mrd = extract32(dawrx, PPC_BIT_NR(48), 54 - 48);
+ bool dw = extract32(dawrx, PPC_BIT_NR(57), 1);
+ bool dr = extract32(dawrx, PPC_BIT_NR(58), 1);
+ bool hv = extract32(dawrx, PPC_BIT_NR(61), 1);
+ bool sv = extract32(dawrx, PPC_BIT_NR(62), 1);
+ bool pr = extract32(dawrx, PPC_BIT_NR(62), 1);
+ vaddr len;
+ int flags;
+
+ if (env->dawr0_watchpoint) {
+ cpu_watchpoint_remove_by_ref(cs, env->dawr0_watchpoint);
+ env->dawr0_watchpoint = NULL;
+ }
+
+ if (!dr && !dw) {
+ return;
+ }
+
+ if (!hv && !sv && !pr) {
+ return;
+ }
+
+ len = (mrd + 1) * 8;
+ flags = BP_CPU | BP_STOP_BEFORE_ACCESS;
+ if (dr) {
+ flags |= BP_MEM_READ;
+ }
+ if (dw) {
+ flags |= BP_MEM_WRITE;
+ }
+
+ cpu_watchpoint_insert(cs, deaw, len, flags, &env->dawr0_watchpoint);
+}
+
+void ppc_store_dawr0(CPUPPCState *env, target_ulong val)
+{
+ env->spr[SPR_DAWR0] = val;
+ ppc_update_daw0(env);
+}
+
+void ppc_store_dawrx0(CPUPPCState *env, uint32_t val)
+{
+ int hrammc = extract32(val, PPC_BIT_NR(56), 1);
+
+ if (hrammc) {
+ /* This might be done with a second watchpoint at the xor of DEAW[0] */
+ qemu_log_mask(LOG_UNIMP, "%s: DAWRX0[HRAMMC] is unimplemented\n",
+ __func__);
+ }
+
+ env->spr[SPR_DAWRX0] = val;
+ ppc_update_daw0(env);
+}
+#endif
#endif
static inline void fpscr_set_rounding_mode(CPUPPCState *env)
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 25fac95..173e4c3 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1121,7 +1121,9 @@
target_ulong reserve_addr; /* Reservation address */
target_ulong reserve_length; /* Reservation larx op size (bytes) */
target_ulong reserve_val; /* Reservation value */
+#if defined(TARGET_PPC64)
target_ulong reserve_val2;
+#endif
/* These are used in supervisor mode only */
target_ulong msr; /* machine state register */
@@ -1137,6 +1139,8 @@
/* MMU context, only relevant for full system emulation */
#if defined(TARGET_PPC64)
ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */
+ struct CPUBreakpoint *ciabr_breakpoint;
+ struct CPUWatchpoint *dawr0_watchpoint;
#endif
target_ulong sr[32]; /* segment registers */
uint32_t nb_BATs; /* number of BATs */
@@ -1403,6 +1407,11 @@
#if !defined(CONFIG_USER_ONLY)
void ppc_store_sdr1(CPUPPCState *env, target_ulong value);
void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val);
+void ppc_update_ciabr(CPUPPCState *env);
+void ppc_store_ciabr(CPUPPCState *env, target_ulong value);
+void ppc_update_daw0(CPUPPCState *env);
+void ppc_store_dawr0(CPUPPCState *env, target_ulong value);
+void ppc_store_dawrx0(CPUPPCState *env, uint32_t value);
#endif /* !defined(CONFIG_USER_ONLY) */
void ppc_store_msr(CPUPPCState *env, target_ulong value);
@@ -1495,6 +1504,7 @@
#if !defined(CONFIG_USER_ONLY)
int ppc_set_compat_all(uint32_t compat_pvr, Error **errp);
+int ppc_init_compat_all(uint32_t compat_pvr, Error **errp);
#endif
int ppc_compat_max_vthreads(PowerPCCPU *cpu);
void ppc_compat_add_property(Object *obj, const char *name,
@@ -1897,7 +1907,9 @@
#define SPR_PSSCR (0x357)
#define SPR_440_INV0 (0x370)
#define SPR_440_INV1 (0x371)
+#define SPR_TRIG1 (0x371)
#define SPR_440_INV2 (0x372)
+#define SPR_TRIG2 (0x372)
#define SPR_440_INV3 (0x373)
#define SPR_440_ITV0 (0x374)
#define SPR_440_ITV1 (0x375)
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 02b7aad..7ab5ee9 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -5117,17 +5117,17 @@
spr_register_kvm_hv(env, SPR_DAWR0, "DAWR0",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_dawr0,
KVM_REG_PPC_DAWR, 0x00000000);
spr_register_kvm_hv(env, SPR_DAWRX0, "DAWRX0",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic32,
+ &spr_read_generic, &spr_write_dawrx0,
KVM_REG_PPC_DAWRX, 0x00000000);
spr_register_kvm_hv(env, SPR_CIABR, "CIABR",
SPR_NOACCESS, SPR_NOACCESS,
SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_ciabr,
KVM_REG_PPC_CIABR, 0x00000000);
}
@@ -5660,6 +5660,16 @@
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_tfmr, &spr_write_tfmr,
0x00000000);
+ spr_register_hv(env, SPR_TRIG1, "TRIG1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_access_nop, &spr_write_generic,
+ &spr_access_nop, &spr_write_generic,
+ 0x00000000);
+ spr_register_hv(env, SPR_TRIG2, "TRIG2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_access_nop, &spr_write_generic,
+ &spr_access_nop, &spr_write_generic,
+ 0x00000000);
#endif
}
@@ -7149,6 +7159,8 @@
env->nip = env->hreset_vector | env->excp_prefix;
if (tcg_enabled()) {
+ cpu_breakpoint_remove_all(s, BP_CPU);
+ cpu_watchpoint_remove_all(s, BP_CPU);
if (env->mmu_model != POWERPC_MMU_REAL) {
ppc_tlb_invalidate_all(env);
}
@@ -7336,6 +7348,9 @@
.cpu_exec_exit = ppc_cpu_exec_exit,
.do_unaligned_access = ppc_cpu_do_unaligned_access,
.do_transaction_failed = ppc_cpu_do_transaction_failed,
+ .debug_excp_handler = ppc_cpu_debug_excp_handler,
+ .debug_check_breakpoint = ppc_cpu_debug_check_breakpoint,
+ .debug_check_watchpoint = ppc_cpu_debug_check_watchpoint,
#endif /* !CONFIG_USER_ONLY */
};
#endif /* CONFIG_TCG */
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 9aa8e46..72ec2be 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -843,6 +843,7 @@
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->hypercall(cpu->vhyp, cpu);
+ powerpc_reset_excp_state(cpu);
return;
}
@@ -1014,6 +1015,7 @@
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->hypercall(cpu->vhyp, cpu);
+ powerpc_reset_excp_state(cpu);
return;
}
@@ -1526,6 +1528,7 @@
PPCVirtualHypervisorClass *vhc =
PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
vhc->hypercall(cpu->vhyp, cpu);
+ powerpc_reset_excp_state(cpu);
return;
}
if (env->insns_flags2 & PPC2_ISA310) {
@@ -1571,9 +1574,11 @@
}
}
break;
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ msr |= env->error_code;
+ /* fall through */
case POWERPC_EXCP_DSEG: /* Data segment exception */
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
- case POWERPC_EXCP_TRACE: /* Trace exception */
case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */
case POWERPC_EXCP_PERFM: /* Performance monitor interrupt */
break;
@@ -3168,6 +3173,18 @@
}
#endif /* TARGET_PPC64 */
+/* Single-step tracing */
+void helper_book3s_trace(CPUPPCState *env, target_ulong prev_ip)
+{
+ uint32_t error_code = 0;
+ if (env->insns_flags2 & PPC2_ISA207S) {
+ /* Load/store reporting, SRR1[35, 36] and SDAR, are not implemented. */
+ env->spr[SPR_POWER_SIAR] = prev_ip;
+ error_code = PPC_BIT(33);
+ }
+ raise_exception_err(env, POWERPC_EXCP_TRACE, error_code);
+}
+
void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
MMUAccessType access_type,
int mmu_idx, uintptr_t retaddr)
@@ -3243,5 +3260,97 @@
cs->exception_index = POWERPC_EXCP_MCHECK;
cpu_loop_exit_restore(cs, retaddr);
}
+
+void ppc_cpu_debug_excp_handler(CPUState *cs)
+{
+#if defined(TARGET_PPC64)
+ CPUPPCState *env = cs->env_ptr;
+
+ if (env->insns_flags2 & PPC2_ISA207S) {
+ if (cs->watchpoint_hit) {
+ if (cs->watchpoint_hit->flags & BP_CPU) {
+ env->spr[SPR_DAR] = cs->watchpoint_hit->hitaddr;
+ env->spr[SPR_DSISR] = PPC_BIT(41);
+ cs->watchpoint_hit = NULL;
+ raise_exception(env, POWERPC_EXCP_DSI);
+ }
+ cs->watchpoint_hit = NULL;
+ } else if (cpu_breakpoint_test(cs, env->nip, BP_CPU)) {
+ raise_exception_err(env, POWERPC_EXCP_TRACE,
+ PPC_BIT(33) | PPC_BIT(43));
+ }
+ }
+#endif
+}
+
+bool ppc_cpu_debug_check_breakpoint(CPUState *cs)
+{
+#if defined(TARGET_PPC64)
+ CPUPPCState *env = cs->env_ptr;
+
+ if (env->insns_flags2 & PPC2_ISA207S) {
+ target_ulong priv;
+
+ priv = env->spr[SPR_CIABR] & PPC_BITMASK(62, 63);
+ switch (priv) {
+ case 0x1: /* problem */
+ return env->msr & ((target_ulong)1 << MSR_PR);
+ case 0x2: /* supervisor */
+ return (!(env->msr & ((target_ulong)1 << MSR_PR)) &&
+ !(env->msr & ((target_ulong)1 << MSR_HV)));
+ case 0x3: /* hypervisor */
+ return (!(env->msr & ((target_ulong)1 << MSR_PR)) &&
+ (env->msr & ((target_ulong)1 << MSR_HV)));
+ default:
+ g_assert_not_reached();
+ }
+ }
+#endif
+
+ return false;
+}
+
+bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
+{
+#if defined(TARGET_PPC64)
+ CPUPPCState *env = cs->env_ptr;
+
+ if (env->insns_flags2 & PPC2_ISA207S) {
+ if (wp == env->dawr0_watchpoint) {
+ uint32_t dawrx = env->spr[SPR_DAWRX0];
+ bool wt = extract32(dawrx, PPC_BIT_NR(59), 1);
+ bool wti = extract32(dawrx, PPC_BIT_NR(60), 1);
+ bool hv = extract32(dawrx, PPC_BIT_NR(61), 1);
+ bool sv = extract32(dawrx, PPC_BIT_NR(62), 1);
+ bool pr = extract32(dawrx, PPC_BIT_NR(62), 1);
+
+ if ((env->msr & ((target_ulong)1 << MSR_PR)) && !pr) {
+ return false;
+ } else if ((env->msr & ((target_ulong)1 << MSR_HV)) && !hv) {
+ return false;
+ } else if (!sv) {
+ return false;
+ }
+
+ if (!wti) {
+ if (env->msr & ((target_ulong)1 << MSR_DR)) {
+ if (!wt) {
+ return false;
+ }
+ } else {
+ if (wt) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+#endif
+
+ return false;
+}
+
#endif /* CONFIG_TCG */
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index abec6fe..86f97ee 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -25,6 +25,9 @@
DEF_HELPER_2(rfebb, void, env, tl)
DEF_HELPER_2(store_lpcr, void, env, tl)
DEF_HELPER_2(store_pcr, void, env, tl)
+DEF_HELPER_2(store_ciabr, void, env, tl)
+DEF_HELPER_2(store_dawr0, void, env, tl)
+DEF_HELPER_2(store_dawrx0, void, env, tl)
DEF_HELPER_2(store_mmcr0, void, env, tl)
DEF_HELPER_2(store_mmcr1, void, env, tl)
DEF_HELPER_3(store_pmc, void, env, i32, i64)
@@ -32,6 +35,7 @@
DEF_HELPER_2(insns_inc, void, env, i32)
DEF_HELPER_1(handle_pmc5_overflow, void, env)
#endif
+DEF_HELPER_2(book3s_trace, void, env, tl)
DEF_HELPER_1(check_tlb_flush_local, void, env)
DEF_HELPER_1(check_tlb_flush_global, void, env)
#endif
diff --git a/target/ppc/internal.h b/target/ppc/internal.h
index 57acb32..15803bc 100644
--- a/target/ppc/internal.h
+++ b/target/ppc/internal.h
@@ -301,6 +301,9 @@
MMUAccessType access_type,
int mmu_idx, MemTxAttrs attrs,
MemTxResult response, uintptr_t retaddr);
+void ppc_cpu_debug_excp_handler(CPUState *cs);
+bool ppc_cpu_debug_check_breakpoint(CPUState *cs);
+bool ppc_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp);
#endif
FIELD(GER_MSK, XMSK, 0, 4)
diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c
index 7698501..51112bd 100644
--- a/target/ppc/kvm.c
+++ b/target/ppc/kvm.c
@@ -1320,7 +1320,7 @@
return 0;
}
- if (!kvm_enabled() || !cap_interrupt_unset) {
+ if (!cap_interrupt_unset) {
return 0;
}
diff --git a/target/ppc/machine.c b/target/ppc/machine.c
index 1270a1f..68cbdff 100644
--- a/target/ppc/machine.c
+++ b/target/ppc/machine.c
@@ -9,6 +9,7 @@
#include "qapi/error.h"
#include "kvm_ppc.h"
#include "power8-pmu.h"
+#include "sysemu/replay.h"
static void post_load_update_msr(CPUPPCState *env)
{
@@ -208,6 +209,14 @@
/* Used to retain migration compatibility for pre 6.0 for 601 machines. */
env->hflags_compat_nmsr = 0;
+ if (tcg_enabled()) {
+ /*
+ * TCG does not maintain the DECR spr (unlike KVM) so have to save
+ * it here.
+ */
+ env->spr[SPR_DECR] = cpu_ppc_load_decr(env);
+ }
+
return 0;
}
@@ -313,6 +322,17 @@
post_load_update_msr(env);
if (tcg_enabled()) {
+ /* Re-set breaks based on regs */
+#if defined(TARGET_PPC64)
+ ppc_update_ciabr(env);
+ ppc_update_daw0(env);
+#endif
+ /*
+ * TCG needs to re-start the decrementer timer and/or raise the
+ * interrupt. This works for level-triggered decrementer. Edge
+ * triggered types (including HDEC) would need to carry more state.
+ */
+ cpu_ppc_store_decr(env, env->spr[SPR_DECR]);
pmu_mmcr01_updated(env);
}
@@ -670,6 +690,27 @@
}
};
+static bool reservation_needed(void *opaque)
+{
+ return (replay_mode != REPLAY_MODE_NONE);
+}
+
+static const VMStateDescription vmstate_reservation = {
+ .name = "cpu/reservation",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = reservation_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINTTL(env.reserve_addr, PowerPCCPU),
+ VMSTATE_UINTTL(env.reserve_length, PowerPCCPU),
+ VMSTATE_UINTTL(env.reserve_val, PowerPCCPU),
+#if defined(TARGET_PPC64)
+ VMSTATE_UINTTL(env.reserve_val2, PowerPCCPU),
+#endif
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_ppc_cpu = {
.name = "cpu",
.version_id = 5,
@@ -691,8 +732,7 @@
VMSTATE_UINTTL_ARRAY(env.spr, PowerPCCPU, 1024),
VMSTATE_UINT64(env.spe_acc, PowerPCCPU),
- /* Reservation */
- VMSTATE_UINTTL(env.reserve_addr, PowerPCCPU),
+ VMSTATE_UNUSED(sizeof(target_ulong)), /* was env.reserve_addr */
/* Supervisor mode architected state */
VMSTATE_UINTTL(env.msr, PowerPCCPU),
@@ -721,6 +761,7 @@
&vmstate_tlbemb,
&vmstate_tlbmas,
&vmstate_compat,
+ &vmstate_reservation,
NULL
}
};
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index 692d058..a05bdf7 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -199,6 +199,21 @@
env->spr[SPR_PCR] = value & pcc->pcr_mask;
}
+void helper_store_ciabr(CPUPPCState *env, target_ulong value)
+{
+ ppc_store_ciabr(env, value);
+}
+
+void helper_store_dawr0(CPUPPCState *env, target_ulong value)
+{
+ ppc_store_dawr0(env, value);
+}
+
+void helper_store_dawrx0(CPUPPCState *env, target_ulong value)
+{
+ ppc_store_dawrx0(env, value);
+}
+
/*
* DPDES register is shared. Each bit reflects the state of the
* doorbell interrupt of a thread of the same core.
diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c
index 920084b..5823e03 100644
--- a/target/ppc/mmu-radix64.c
+++ b/target/ppc/mmu-radix64.c
@@ -219,27 +219,25 @@
return false;
}
-static void ppc_radix64_set_rc(PowerPCCPU *cpu, MMUAccessType access_type,
- uint64_t pte, hwaddr pte_addr, int *prot)
+static int ppc_radix64_check_rc(MMUAccessType access_type, uint64_t pte)
{
- CPUState *cs = CPU(cpu);
- uint64_t npte;
+ switch (access_type) {
+ case MMU_DATA_STORE:
+ if (!(pte & R_PTE_C)) {
+ break;
+ }
+ /* fall through */
+ case MMU_INST_FETCH:
+ case MMU_DATA_LOAD:
+ if (!(pte & R_PTE_R)) {
+ break;
+ }
- npte = pte | R_PTE_R; /* Always set reference bit */
-
- if (access_type == MMU_DATA_STORE) { /* Store/Write */
- npte |= R_PTE_C; /* Set change bit */
- } else {
- /*
- * Treat the page as read-only for now, so that a later write
- * will pass through this function again to set the C bit.
- */
- *prot &= ~PAGE_WRITE;
+ /* R/C bits are already set appropriately for this access */
+ return 0;
}
- if (pte ^ npte) { /* If pte has changed then write it back */
- stq_phys(cs->as, pte_addr, npte);
- }
+ return 1;
}
static bool ppc_radix64_is_valid_level(int level, int psize, uint64_t nls)
@@ -380,7 +378,8 @@
ppc_v3_pate_t pate,
hwaddr *h_raddr, int *h_prot,
int *h_page_size, bool pde_addr,
- int mmu_idx, bool guest_visible)
+ int mmu_idx, uint64_t lpid,
+ bool guest_visible)
{
MMUAccessType access_type = orig_access_type;
int fault_cause = 0;
@@ -418,7 +417,24 @@
}
if (guest_visible) {
- ppc_radix64_set_rc(cpu, access_type, pte, pte_addr, h_prot);
+ if (ppc_radix64_check_rc(access_type, pte)) {
+ /*
+ * Per ISA 3.1 Book III, 7.5.3 and 7.5.5, failure to set R/C during
+ * partition-scoped translation when effLPID = 0 results in normal
+ * (non-Hypervisor) Data and Instruction Storage Interrupts
+ * respectively.
+ *
+ * ISA 3.0 is ambiguous about this, but tests on POWER9 hardware
+ * seem to exhibit the same behavior.
+ */
+ if (lpid > 0) {
+ ppc_radix64_raise_hsi(cpu, access_type, eaddr, g_raddr,
+ DSISR_ATOMIC_RC);
+ } else {
+ ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_ATOMIC_RC);
+ }
+ return 1;
+ }
}
return 0;
@@ -447,7 +463,8 @@
vaddr eaddr, uint64_t pid,
ppc_v3_pate_t pate, hwaddr *g_raddr,
int *g_prot, int *g_page_size,
- int mmu_idx, bool guest_visible)
+ int mmu_idx, uint64_t lpid,
+ bool guest_visible)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
@@ -497,7 +514,7 @@
ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr,
prtbe_addr, pate, &h_raddr,
&h_prot, &h_page_size, true,
- 5, guest_visible);
+ 5, lpid, guest_visible);
if (ret) {
return ret;
}
@@ -539,7 +556,8 @@
ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr,
pte_addr, pate, &h_raddr,
&h_prot, &h_page_size,
- true, 5, guest_visible);
+ true, 5, lpid,
+ guest_visible);
if (ret) {
return ret;
}
@@ -580,7 +598,11 @@
}
if (guest_visible) {
- ppc_radix64_set_rc(cpu, access_type, pte, pte_addr, g_prot);
+ /* R/C bits not appropriately set for access */
+ if (ppc_radix64_check_rc(access_type, pte)) {
+ ppc_radix64_raise_si(cpu, access_type, eaddr, DSISR_ATOMIC_RC);
+ return 1;
+ }
}
return 0;
@@ -695,7 +717,8 @@
if (relocation) {
int ret = ppc_radix64_process_scoped_xlate(cpu, access_type, eaddr, pid,
pate, &g_raddr, &prot,
- &psize, mmu_idx, guest_visible);
+ &psize, mmu_idx, lpid,
+ guest_visible);
if (ret) {
return false;
}
@@ -719,7 +742,8 @@
ret = ppc_radix64_partition_scoped_xlate(cpu, access_type, eaddr,
g_raddr, pate, raddr,
&prot, &psize, false,
- mmu_idx, guest_visible);
+ mmu_idx, lpid,
+ guest_visible);
if (ret) {
return false;
}
diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h
index 5995070..8a9d6cd 100644
--- a/target/ppc/spr_common.h
+++ b/target/ppc/spr_common.h
@@ -159,6 +159,9 @@
#ifdef TARGET_PPC64
void spr_read_cfar(DisasContext *ctx, int gprn, int sprn);
void spr_write_cfar(DisasContext *ctx, int sprn, int gprn);
+void spr_write_ciabr(DisasContext *ctx, int sprn, int gprn);
+void spr_write_dawr0(DisasContext *ctx, int sprn, int gprn);
+void spr_write_dawrx0(DisasContext *ctx, int sprn, int gprn);
void spr_write_ureg(DisasContext *ctx, int sprn, int gprn);
void spr_read_purr(DisasContext *ctx, int gprn, int sprn);
void spr_write_purr(DisasContext *ctx, int sprn, int gprn);
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 7111b34..6b242ae 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -75,7 +75,9 @@
static TCGv cpu_reserve;
static TCGv cpu_reserve_length;
static TCGv cpu_reserve_val;
+#if defined(TARGET_PPC64)
static TCGv cpu_reserve_val2;
+#endif
static TCGv cpu_fpscr;
static TCGv_i32 cpu_access_type;
@@ -149,9 +151,11 @@
cpu_reserve_val = tcg_global_mem_new(cpu_env,
offsetof(CPUPPCState, reserve_val),
"reserve_val");
+#if defined(TARGET_PPC64)
cpu_reserve_val2 = tcg_global_mem_new(cpu_env,
offsetof(CPUPPCState, reserve_val2),
"reserve_val2");
+#endif
cpu_fpscr = tcg_global_mem_new(cpu_env,
offsetof(CPUPPCState, fpscr), "fpscr");
@@ -336,8 +340,9 @@
* The exception can be either POWERPC_EXCP_TRACE (on most PowerPCs) or
* POWERPC_EXCP_DEBUG (on BookE).
*/
-static uint32_t gen_prep_dbgex(DisasContext *ctx)
+static void gen_debug_exception(DisasContext *ctx, bool rfi_type)
{
+#if !defined(CONFIG_USER_ONLY)
if (ctx->flags & POWERPC_FLAG_DE) {
target_ulong dbsr = 0;
if (ctx->singlestep_enabled & CPU_SINGLE_STEP) {
@@ -350,16 +355,18 @@
gen_load_spr(t0, SPR_BOOKE_DBSR);
tcg_gen_ori_tl(t0, t0, dbsr);
gen_store_spr(SPR_BOOKE_DBSR, t0);
- return POWERPC_EXCP_DEBUG;
+ gen_helper_raise_exception(cpu_env,
+ tcg_constant_i32(POWERPC_EXCP_DEBUG));
+ ctx->base.is_jmp = DISAS_NORETURN;
} else {
- return POWERPC_EXCP_TRACE;
+ if (!rfi_type) { /* BookS does not single step rfi type instructions */
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_movi_tl(t0, ctx->cia);
+ gen_helper_book3s_trace(cpu_env, t0);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ }
}
-}
-
-static void gen_debug_exception(DisasContext *ctx)
-{
- gen_helper_raise_exception(cpu_env, tcg_constant_i32(gen_prep_dbgex(ctx)));
- ctx->base.is_jmp = DISAS_NORETURN;
+#endif
}
static inline void gen_inval_exception(DisasContext *ctx, uint32_t error)
@@ -554,8 +561,9 @@
tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]);
}
-/* CFAR */
#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
+/* Debug facilities */
+/* CFAR */
void spr_read_cfar(DisasContext *ctx, int gprn, int sprn)
{
tcg_gen_mov_tl(cpu_gpr[gprn], cpu_cfar);
@@ -565,6 +573,26 @@
{
tcg_gen_mov_tl(cpu_cfar, cpu_gpr[gprn]);
}
+
+/* Breakpoint */
+void spr_write_ciabr(DisasContext *ctx, int sprn, int gprn)
+{
+ translator_io_start(&ctx->base);
+ gen_helper_store_ciabr(cpu_env, cpu_gpr[gprn]);
+}
+
+/* Watchpoint */
+void spr_write_dawr0(DisasContext *ctx, int sprn, int gprn)
+{
+ translator_io_start(&ctx->base);
+ gen_helper_store_dawr0(cpu_env, cpu_gpr[gprn]);
+}
+
+void spr_write_dawrx0(DisasContext *ctx, int sprn, int gprn)
+{
+ translator_io_start(&ctx->base);
+ gen_helper_store_dawrx0(cpu_env, cpu_gpr[gprn]);
+}
#endif /* defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) */
/* CTR */
@@ -4182,7 +4210,7 @@
static void gen_lookup_and_goto_ptr(DisasContext *ctx)
{
if (unlikely(ctx->singlestep_enabled)) {
- gen_debug_exception(ctx);
+ gen_debug_exception(ctx, false);
} else {
/*
* tcg_gen_lookup_and_goto_ptr will exit the TB if
@@ -7408,8 +7436,9 @@
}
/* Honor single stepping. */
- if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP)
- && (nip <= 0x100 || nip > 0xf00)) {
+ if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP)) {
+ bool rfi_type = false;
+
switch (is_jmp) {
case DISAS_TOO_MANY:
case DISAS_EXIT_UPDATE:
@@ -7418,12 +7447,19 @@
break;
case DISAS_EXIT:
case DISAS_CHAIN:
+ /*
+ * This is a heuristic, to put it kindly. The rfi class of
+ * instructions are among the few outside branches that change
+ * NIP without taking an interrupt. Single step trace interrupts
+ * do not fire on completion of these instructions.
+ */
+ rfi_type = true;
break;
default:
g_assert_not_reached();
}
- gen_debug_exception(ctx);
+ gen_debug_exception(ctx, rfi_type);
return;
}
diff --git a/target/ppc/translate/fixedpoint-impl.c.inc b/target/ppc/translate/fixedpoint-impl.c.inc
index 4ce02fd..7ff7e1e 100644
--- a/target/ppc/translate/fixedpoint-impl.c.inc
+++ b/target/ppc/translate/fixedpoint-impl.c.inc
@@ -71,7 +71,7 @@
{
#if defined(TARGET_PPC64)
TCGv ea;
- TCGv_i64 low_addr_gpr, high_addr_gpr;
+ TCGv_i64 lo, hi;
TCGv_i128 t16;
REQUIRE_INSNS_FLAGS(ctx, 64BX);
@@ -94,21 +94,21 @@
gen_set_access_type(ctx, ACCESS_INT);
ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->si));
- if (prefixed || !ctx->le_mode) {
- low_addr_gpr = cpu_gpr[a->rt];
- high_addr_gpr = cpu_gpr[a->rt + 1];
+ if (ctx->le_mode && prefixed) {
+ lo = cpu_gpr[a->rt];
+ hi = cpu_gpr[a->rt + 1];
} else {
- low_addr_gpr = cpu_gpr[a->rt + 1];
- high_addr_gpr = cpu_gpr[a->rt];
+ lo = cpu_gpr[a->rt + 1];
+ hi = cpu_gpr[a->rt];
}
t16 = tcg_temp_new_i128();
if (store) {
- tcg_gen_concat_i64_i128(t16, low_addr_gpr, high_addr_gpr);
+ tcg_gen_concat_i64_i128(t16, lo, hi);
tcg_gen_qemu_st_i128(t16, ea, ctx->mem_idx, DEF_MEMOP(MO_128));
} else {
tcg_gen_qemu_ld_i128(t16, ea, ctx->mem_idx, DEF_MEMOP(MO_128));
- tcg_gen_extr_i128_i64(low_addr_gpr, high_addr_gpr, t16);
+ tcg_gen_extr_i128_i64(lo, hi, t16);
}
#else
qemu_build_not_reached();
diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py
index 724ee72..90f1b7c 100644
--- a/tests/avocado/machine_aspeed.py
+++ b/tests/avocado/machine_aspeed.py
@@ -316,8 +316,8 @@
"""
image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/'
- 'download/v08.01/ast2500-default-obmc.tar.gz')
- image_hash = ('5375f82b4c43a79427909342a1e18b4e48bd663e38466862145d27bb358796fd')
+ 'download/v08.06/ast2500-default-obmc.tar.gz')
+ image_hash = ('e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca')
image_path = self.fetch_asset(image_url, asset_hash=image_hash,
algorithm='sha256')
archive.extract(image_path, self.workdir)
@@ -334,8 +334,8 @@
"""
image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/'
- 'download/v08.01/ast2600-default-obmc.tar.gz')
- image_hash = ('f12ef15e8c1f03a214df3b91c814515c5e2b2f56119021398c1dbdd626817d15')
+ 'download/v08.06/ast2600-a2-obmc.tar.gz')
+ image_hash = ('9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4')
image_path = self.fetch_asset(image_url, asset_hash=image_hash,
algorithm='sha256')
archive.extract(image_path, self.workdir)
@@ -345,8 +345,8 @@
self.vm.add_args('-device',
'ds1338,bus=aspeed.i2c.bus.5,address=0x32');
self.do_test_arm_aspeed_sdk_start(
- self.workdir + '/ast2600-default/image-bmc')
- self.wait_for_console_pattern('nodistro.0 ast2600-default ttyS4')
+ self.workdir + '/ast2600-a2/image-bmc')
+ self.wait_for_console_pattern('nodistro.0 ast2600-a2 ttyS4')
self.ssh_connect('root', '0penBmc', False)
self.ssh_command('dmesg -c > /dev/null')
diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py
index 79c607b..a186105 100644
--- a/tests/avocado/replay_kernel.py
+++ b/tests/avocado/replay_kernel.py
@@ -255,8 +255,7 @@
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0'
- # icount is not good enough for PPC64 for complete boot yet
- console_pattern = 'Kernel command line: %s' % kernel_command_line
+ console_pattern = 'VFS: Cannot open root device'
self.run_rr(kernel_path, kernel_command_line, console_pattern)
def test_ppc64_powernv(self):
diff --git a/tests/avocado/reverse_debugging.py b/tests/avocado/reverse_debugging.py
index 680c314..fc47874 100644
--- a/tests/avocado/reverse_debugging.py
+++ b/tests/avocado/reverse_debugging.py
@@ -150,16 +150,33 @@
self.check_pc(g, addr)
logger.info('found position %x' % addr)
- logger.info('seeking to the end (icount %s)' % (last_icount - 1))
- vm.qmp('replay-break', icount=last_icount - 1)
- # continue - will return after pausing
- g.cmd(b'c', b'T02thread:01;')
+ # visit the recorded instruction in forward order
+ logger.info('stepping forward')
+ for addr in steps:
+ self.check_pc(g, addr)
+ self.gdb_step(g)
+ logger.info('found position %x' % addr)
+ # set breakpoints for the instructions just stepped over
logger.info('setting breakpoints')
for addr in steps:
# hardware breakpoint at addr with len=1
g.cmd(b'Z1,%x,1' % addr, b'OK')
+ # this may hit a breakpoint if first instructions are executed
+ # again
+ logger.info('continuing execution')
+ vm.qmp('replay-break', icount=last_icount - 1)
+ # continue - will return after pausing
+ # This could stop at the end and get a T02 return, or by
+ # re-executing one of the breakpoints and get a T05 return.
+ g.cmd(b'c')
+ if self.vm_get_icount(vm) == last_icount - 1:
+ logger.info('reached the end (icount %s)' % (last_icount - 1))
+ else:
+ logger.info('hit a breakpoint again at %x (icount %s)' %
+ (self.get_pc(g), self.vm_get_icount(vm)))
+
logger.info('running reverse continue to reach %x' % steps[-1])
# reverse continue - will return after stopping at the breakpoint
g.cmd(b'bc', b'T05thread:01;')
@@ -216,3 +233,32 @@
self.reverse_debugging(
args=('-kernel', kernel_path))
+
+class ReverseDebugging_ppc64(ReverseDebugging):
+ """
+ :avocado: tags=accel:tcg
+ """
+
+ REG_PC = 0x40
+
+ # unidentified gitlab timeout problem
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
+ def test_ppc64_pseries(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:pseries
+ """
+ # SLOF branches back to its entry point, which causes this test
+ # to take the 'hit a breakpoint again' path. That's not a problem,
+ # just slightly different than the other machines.
+ self.endian_is_le = False
+ self.reverse_debugging()
+
+ @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab')
+ def test_ppc64_powernv(self):
+ """
+ :avocado: tags=arch:ppc64
+ :avocado: tags=machine:powernv
+ """
+ self.endian_is_le = False
+ self.reverse_debugging()
diff --git a/ui/console.c b/ui/console.c
index 8da2170..e4d6179 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -27,9 +27,10 @@
#include "hw/qdev-core.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-ui.h"
+#include "qapi/visitor.h"
#include "qemu/coroutine.h"
-#include "qemu/error-report.h"
#include "qemu/fifo8.h"
+#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -37,11 +38,7 @@
#include "chardev/char.h"
#include "trace.h"
#include "exec/memory.h"
-#include "io/channel-file.h"
#include "qom/object.h"
-#ifdef CONFIG_PNG
-#include <png.h>
-#endif
#define DEFAULT_BACKSCROLL 512
#define CONSOLE_CURSOR_PERIOD 500
@@ -56,6 +53,11 @@
uint8_t unvisible:1;
} TextAttributes;
+#define TEXT_ATTRIBUTES_DEFAULT ((TextAttributes) { \
+ .fgcol = QEMU_COLOR_WHITE, \
+ .bgcol = QEMU_COLOR_BLACK \
+})
+
typedef struct TextCell {
uint8_t ch;
TextAttributes t_attrib;
@@ -69,17 +71,10 @@
TTY_STATE_CSI,
};
-typedef enum {
- GRAPHIC_CONSOLE,
- TEXT_CONSOLE,
- TEXT_CONSOLE_FIXED_SIZE
-} console_type_t;
-
struct QemuConsole {
Object parent;
int index;
- console_type_t console_type;
DisplayState *ds;
DisplaySurface *surface;
DisplayScanout scanout;
@@ -88,28 +83,41 @@
int gl_block;
QEMUTimer *gl_unblock_timer;
int window_id;
-
- /* Graphic console state. */
- Object *device;
- uint32_t head;
QemuUIInfo ui_info;
QEMUTimer *ui_timer;
- QEMUCursor *cursor;
- int cursor_x, cursor_y, cursor_on;
const GraphicHwOps *hw_ops;
void *hw;
+ CoQueue dump_queue;
- /* Text console state */
+ QTAILQ_ENTRY(QemuConsole) next;
+};
+
+OBJECT_DEFINE_ABSTRACT_TYPE(QemuConsole, qemu_console, QEMU_CONSOLE, OBJECT)
+
+typedef struct QemuGraphicConsole {
+ QemuConsole parent;
+
+ Object *device;
+ uint32_t head;
+
+ QEMUCursor *cursor;
+ int cursor_x, cursor_y, cursor_on;
+} QemuGraphicConsole;
+
+typedef QemuConsoleClass QemuGraphicConsoleClass;
+
+OBJECT_DEFINE_TYPE(QemuGraphicConsole, qemu_graphic_console, QEMU_GRAPHIC_CONSOLE, QEMU_CONSOLE)
+
+typedef struct QemuTextConsole {
+ QemuConsole parent;
+
int width;
int height;
int total_height;
int backscroll_height;
int x, y;
- int x_saved, y_saved;
int y_displayed;
int y_base;
- TextAttributes t_attrib_default; /* default text attributes */
- TextAttributes t_attrib; /* currently active text attributes */
TextCell *cells;
int text_x[2], text_y[2], cursor_invalidate;
int echo;
@@ -119,25 +127,40 @@
int update_x1;
int update_y1;
- enum TTYState state;
- int esc_params[MAX_ESC_PARAMS];
- int nb_esc_params;
-
Chardev *chr;
/* fifo for key pressed */
Fifo8 out_fifo;
- CoQueue dump_queue;
+} QemuTextConsole;
- QTAILQ_ENTRY(QemuConsole) next;
+typedef QemuConsoleClass QemuTextConsoleClass;
+
+OBJECT_DEFINE_TYPE(QemuTextConsole, qemu_text_console, QEMU_TEXT_CONSOLE, QEMU_CONSOLE)
+
+typedef struct QemuFixedTextConsole {
+ QemuTextConsole parent;
+} QemuFixedTextConsole;
+
+typedef QemuTextConsoleClass QemuFixedTextConsoleClass;
+
+OBJECT_DEFINE_TYPE(QemuFixedTextConsole, qemu_fixed_text_console, QEMU_FIXED_TEXT_CONSOLE, QEMU_TEXT_CONSOLE)
+
+struct VCChardev {
+ Chardev parent;
+ QemuTextConsole *console;
+
+ enum TTYState state;
+ int esc_params[MAX_ESC_PARAMS];
+ int nb_esc_params;
+ TextAttributes t_attrib; /* currently active text attributes */
+ int x_saved, y_saved;
};
+typedef struct VCChardev VCChardev;
struct DisplayState {
QEMUTimer *gui_timer;
uint64_t last_update;
uint64_t update_interval;
bool refreshing;
- bool have_gfx;
- bool have_text;
QLIST_HEAD(, DisplayChangeListener) listeners;
};
@@ -149,14 +172,14 @@
static bool cursor_visible_phase;
static QEMUTimer *cursor_timer;
-static void text_console_do_init(Chardev *chr, DisplayState *ds);
static void dpy_refresh(DisplayState *s);
static DisplayState *get_alloc_displaystate(void);
-static void text_console_update_cursor_timer(void);
static void text_console_update_cursor(void *opaque);
static bool displaychangelistener_has_dmabuf(DisplayChangeListener *dcl);
static bool console_compatible_with(QemuConsole *con,
DisplayChangeListener *dcl, Error **errp);
+static QemuConsole *qemu_graphic_console_lookup_unused(void);
+static void dpy_set_ui_info_timer(void *opaque);
static void gui_update(void *opaque)
{
@@ -188,19 +211,11 @@
{
DisplayChangeListener *dcl;
bool need_timer = false;
- bool have_gfx = false;
- bool have_text = false;
QLIST_FOREACH(dcl, &ds->listeners, next) {
if (dcl->ops->dpy_refresh != NULL) {
need_timer = true;
}
- if (dcl->ops->dpy_gfx_update != NULL) {
- have_gfx = true;
- }
- if (dcl->ops->dpy_text_update != NULL) {
- have_text = true;
- }
}
if (need_timer && ds->gui_timer == NULL) {
@@ -211,9 +226,6 @@
timer_free(ds->gui_timer);
ds->gui_timer = NULL;
}
-
- ds->have_gfx = have_gfx;
- ds->have_text = have_text;
}
void graphic_hw_update_done(QemuConsole *con)
@@ -239,6 +251,22 @@
}
}
+static void graphic_hw_update_bh(void *con)
+{
+ graphic_hw_update(con);
+}
+
+void qemu_console_co_wait_update(QemuConsole *con)
+{
+ if (qemu_co_queue_empty(&con->dump_queue)) {
+ /* Defer the update, it will restart the pending coroutines */
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
+ graphic_hw_update_bh, con);
+ }
+ qemu_co_queue_wait(&con->dump_queue, NULL);
+
+}
+
static void graphic_hw_gl_unblock_timer(void *opaque)
{
warn_report("console: no gl-unblock within one second");
@@ -292,196 +320,6 @@
}
}
-#ifdef CONFIG_PNG
-/**
- * png_save: Take a screenshot as PNG
- *
- * Saves screendump as a PNG file
- *
- * Returns true for success or false for error.
- *
- * @fd: File descriptor for PNG file.
- * @image: Image data in pixman format.
- * @errp: Pointer to an error.
- */
-static bool png_save(int fd, pixman_image_t *image, Error **errp)
-{
- int width = pixman_image_get_width(image);
- int height = pixman_image_get_height(image);
- png_struct *png_ptr;
- png_info *info_ptr;
- g_autoptr(pixman_image_t) linebuf =
- qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
- uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf);
- FILE *f = fdopen(fd, "wb");
- int y;
- if (!f) {
- error_setg_errno(errp, errno,
- "Failed to create file from file descriptor");
- return false;
- }
-
- png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
- NULL, NULL);
- if (!png_ptr) {
- error_setg(errp, "PNG creation failed. Unable to write struct");
- fclose(f);
- return false;
- }
-
- info_ptr = png_create_info_struct(png_ptr);
-
- if (!info_ptr) {
- error_setg(errp, "PNG creation failed. Unable to write info");
- fclose(f);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- return false;
- }
-
- png_init_io(png_ptr, f);
-
- png_set_IHDR(png_ptr, info_ptr, width, height, 8,
- PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
-
- png_write_info(png_ptr, info_ptr);
-
- for (y = 0; y < height; ++y) {
- qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
- png_write_row(png_ptr, buf);
- }
-
- png_write_end(png_ptr, NULL);
-
- png_destroy_write_struct(&png_ptr, &info_ptr);
-
- if (fclose(f) != 0) {
- error_setg_errno(errp, errno,
- "PNG creation failed. Unable to close file");
- return false;
- }
-
- return true;
-}
-
-#else /* no png support */
-
-static bool png_save(int fd, pixman_image_t *image, Error **errp)
-{
- error_setg(errp, "Enable PNG support with libpng for screendump");
- return false;
-}
-
-#endif /* CONFIG_PNG */
-
-static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
-{
- int width = pixman_image_get_width(image);
- int height = pixman_image_get_height(image);
- g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd));
- g_autofree char *header = NULL;
- g_autoptr(pixman_image_t) linebuf = NULL;
- int y;
-
- trace_ppm_save(fd, image);
-
- header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255);
- if (qio_channel_write_all(QIO_CHANNEL(ioc),
- header, strlen(header), errp) < 0) {
- return false;
- }
-
- linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
- for (y = 0; y < height; y++) {
- qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
- if (qio_channel_write_all(QIO_CHANNEL(ioc),
- (char *)pixman_image_get_data(linebuf),
- pixman_image_get_stride(linebuf), errp) < 0) {
- return false;
- }
- }
-
- return true;
-}
-
-static void graphic_hw_update_bh(void *con)
-{
- graphic_hw_update(con);
-}
-
-/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
-void coroutine_fn
-qmp_screendump(const char *filename, const char *device,
- bool has_head, int64_t head,
- bool has_format, ImageFormat format, Error **errp)
-{
- g_autoptr(pixman_image_t) image = NULL;
- QemuConsole *con;
- DisplaySurface *surface;
- int fd;
-
- if (device) {
- con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
- errp);
- if (!con) {
- return;
- }
- } else {
- if (has_head) {
- error_setg(errp, "'head' must be specified together with 'device'");
- return;
- }
- con = qemu_console_lookup_by_index(0);
- if (!con) {
- error_setg(errp, "There is no console to take a screendump from");
- return;
- }
- }
-
- if (qemu_co_queue_empty(&con->dump_queue)) {
- /* Defer the update, it will restart the pending coroutines */
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
- graphic_hw_update_bh, con);
- }
- qemu_co_queue_wait(&con->dump_queue, NULL);
-
- /*
- * All pending coroutines are woken up, while the BQL is held. No
- * further graphic update are possible until it is released. Take
- * an image ref before that.
- */
- surface = qemu_console_surface(con);
- if (!surface) {
- error_setg(errp, "no surface");
- return;
- }
- image = pixman_image_ref(surface->image);
-
- fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
- if (fd == -1) {
- error_setg(errp, "failed to open file '%s': %s", filename,
- strerror(errno));
- return;
- }
-
- /*
- * The image content could potentially be updated as the coroutine
- * yields and releases the BQL. It could produce corrupted dump, but
- * it should be otherwise safe.
- */
- if (has_format && format == IMAGE_FORMAT_PNG) {
- /* PNG format specified for screendump */
- if (!png_save(fd, image, errp)) {
- qemu_unlink(filename);
- }
- } else {
- /* PPM format specified/default for screendump */
- if (!ppm_save(fd, image, errp)) {
- qemu_unlink(filename);
- }
- }
-}
-
void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
{
if (!con) {
@@ -492,25 +330,26 @@
}
}
-static void vga_fill_rect(QemuConsole *con,
- int posx, int posy, int width, int height,
- pixman_color_t color)
+static void qemu_console_fill_rect(QemuConsole *con, int posx, int posy,
+ int width, int height, pixman_color_t color)
{
DisplaySurface *surface = qemu_console_surface(con);
pixman_rectangle16_t rect = {
.x = posx, .y = posy, .width = width, .height = height
};
+ assert(surface);
pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
&color, 1, &rect);
}
/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
-static void vga_bitblt(QemuConsole *con,
- int xs, int ys, int xd, int yd, int w, int h)
+static void qemu_console_bitblt(QemuConsole *con,
+ int xs, int ys, int xd, int yd, int w, int h)
{
DisplaySurface *surface = qemu_console_surface(con);
+ assert(surface);
pixman_image_composite(PIXMAN_OP_SRC,
surface->image, NULL, surface->image,
xs, ys, 0, 0, xd, yd, w, h);
@@ -524,29 +363,26 @@
#include "vgafont.h"
-#define QEMU_RGB(r, g, b) \
- { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
-
static const pixman_color_t color_table_rgb[2][8] = {
{ /* dark */
- [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
- [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
- [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */
- [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
- [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */
- [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
- [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
- [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
+ [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
+ [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xaa), /* blue */
+ [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0x00), /* green */
+ [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xaa, 0xaa), /* cyan */
+ [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0x00), /* red */
+ [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xaa, 0x00, 0xaa), /* magenta */
+ [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xaa, 0xaa, 0x00), /* yellow */
+ [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR_GRAY,
},
{ /* bright */
- [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
- [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */
- [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */
- [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
- [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */
- [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
- [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
- [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */
+ [QEMU_COLOR_BLACK] = QEMU_PIXMAN_COLOR_BLACK,
+ [QEMU_COLOR_BLUE] = QEMU_PIXMAN_COLOR(0x00, 0x00, 0xff), /* blue */
+ [QEMU_COLOR_GREEN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0x00), /* green */
+ [QEMU_COLOR_CYAN] = QEMU_PIXMAN_COLOR(0x00, 0xff, 0xff), /* cyan */
+ [QEMU_COLOR_RED] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0x00), /* red */
+ [QEMU_COLOR_MAGENTA] = QEMU_PIXMAN_COLOR(0xff, 0x00, 0xff), /* magenta */
+ [QEMU_COLOR_YELLOW] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0x00), /* yellow */
+ [QEMU_COLOR_WHITE] = QEMU_PIXMAN_COLOR(0xff, 0xff, 0xff), /* white */
}
};
@@ -557,6 +393,7 @@
DisplaySurface *surface = qemu_console_surface(s);
pixman_color_t fgcol, bgcol;
+ assert(surface);
if (t_attrib->invers) {
bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
fgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
@@ -572,51 +409,48 @@
&fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
}
-static void text_console_resize(QemuConsole *s)
+static void text_console_resize(QemuTextConsole *t)
{
+ QemuConsole *s = QEMU_CONSOLE(t);
TextCell *cells, *c, *c1;
- int w1, x, y, last_width;
+ int w1, x, y, last_width, w, h;
assert(s->scanout.kind == SCANOUT_SURFACE);
- last_width = s->width;
- s->width = surface_width(s->surface) / FONT_WIDTH;
- s->height = surface_height(s->surface) / FONT_HEIGHT;
+ w = surface_width(s->surface) / FONT_WIDTH;
+ h = surface_height(s->surface) / FONT_HEIGHT;
+ if (w == t->width && h == t->height) {
+ return;
+ }
- w1 = last_width;
- if (s->width < w1)
- w1 = s->width;
+ last_width = t->width;
+ t->width = w;
+ t->height = h;
- cells = g_new(TextCell, s->width * s->total_height + 1);
- for(y = 0; y < s->total_height; y++) {
- c = &cells[y * s->width];
+ w1 = MIN(t->width, last_width);
+
+ cells = g_new(TextCell, t->width * t->total_height + 1);
+ for (y = 0; y < t->total_height; y++) {
+ c = &cells[y * t->width];
if (w1 > 0) {
- c1 = &s->cells[y * last_width];
- for(x = 0; x < w1; x++) {
+ c1 = &t->cells[y * last_width];
+ for (x = 0; x < w1; x++) {
*c++ = *c1++;
}
}
- for(x = w1; x < s->width; x++) {
+ for (x = w1; x < t->width; x++) {
c->ch = ' ';
- c->t_attrib = s->t_attrib_default;
+ c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
c++;
}
}
- g_free(s->cells);
- s->cells = cells;
+ g_free(t->cells);
+ t->cells = cells;
}
-static inline void text_update_xy(QemuConsole *s, int x, int y)
+static void invalidate_xy(QemuTextConsole *s, int x, int y)
{
- s->text_x[0] = MIN(s->text_x[0], x);
- s->text_x[1] = MAX(s->text_x[1], x);
- s->text_y[0] = MIN(s->text_y[0], y);
- s->text_y[1] = MAX(s->text_y[1], y);
-}
-
-static void invalidate_xy(QemuConsole *s, int x, int y)
-{
- if (!qemu_console_is_visible(s)) {
+ if (!qemu_console_is_visible(QEMU_CONSOLE(s))) {
return;
}
if (s->update_x0 > x * FONT_WIDTH)
@@ -629,14 +463,16 @@
s->update_y1 = (y + 1) * FONT_HEIGHT;
}
-static void update_xy(QemuConsole *s, int x, int y)
+static void vc_update_xy(VCChardev *vc, int x, int y)
{
+ QemuTextConsole *s = vc->console;
TextCell *c;
int y1, y2;
- if (s->ds->have_text) {
- text_update_xy(s, x, y);
- }
+ s->text_x[0] = MIN(s->text_x[0], x);
+ s->text_x[1] = MAX(s->text_x[1], x);
+ s->text_y[0] = MIN(s->text_y[0], y);
+ s->text_y[1] = MAX(s->text_y[1], y);
y1 = (s->y_base + y) % s->total_height;
y2 = y1 - s->y_displayed;
@@ -648,21 +484,19 @@
x = s->width - 1;
}
c = &s->cells[y1 * s->width + x];
- vga_putcharxy(s, x, y2, c->ch,
+ vga_putcharxy(QEMU_CONSOLE(s), x, y2, c->ch,
&(c->t_attrib));
invalidate_xy(s, x, y2);
}
}
-static void console_show_cursor(QemuConsole *s, int show)
+static void console_show_cursor(QemuTextConsole *s, int show)
{
TextCell *c;
int y, y1;
int x = s->x;
- if (s->ds->have_text) {
- s->cursor_invalidate = 1;
- }
+ s->cursor_invalidate = 1;
if (x >= s->width) {
x = s->width - 1;
@@ -675,37 +509,36 @@
if (y < s->height) {
c = &s->cells[y1 * s->width + x];
if (show && cursor_visible_phase) {
- TextAttributes t_attrib = s->t_attrib_default;
+ TextAttributes t_attrib = TEXT_ATTRIBUTES_DEFAULT;
t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
- vga_putcharxy(s, x, y, c->ch, &t_attrib);
+ vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &t_attrib);
} else {
- vga_putcharxy(s, x, y, c->ch, &(c->t_attrib));
+ vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch, &(c->t_attrib));
}
invalidate_xy(s, x, y);
}
}
-static void console_refresh(QemuConsole *s)
+static void console_refresh(QemuTextConsole *s)
{
- DisplaySurface *surface = qemu_console_surface(s);
+ DisplaySurface *surface = qemu_console_surface(QEMU_CONSOLE(s));
TextCell *c;
int x, y, y1;
- if (s->ds->have_text) {
- s->text_x[0] = 0;
- s->text_y[0] = 0;
- s->text_x[1] = s->width - 1;
- s->text_y[1] = s->height - 1;
- s->cursor_invalidate = 1;
- }
+ assert(surface);
+ s->text_x[0] = 0;
+ s->text_y[0] = 0;
+ s->text_x[1] = s->width - 1;
+ s->text_y[1] = s->height - 1;
+ s->cursor_invalidate = 1;
- vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
- color_table_rgb[0][QEMU_COLOR_BLACK]);
+ qemu_console_fill_rect(QEMU_CONSOLE(s), 0, 0, surface_width(surface), surface_height(surface),
+ color_table_rgb[0][QEMU_COLOR_BLACK]);
y1 = s->y_displayed;
for (y = 0; y < s->height; y++) {
c = s->cells + y1 * s->width;
for (x = 0; x < s->width; x++) {
- vga_putcharxy(s, x, y, c->ch,
+ vga_putcharxy(QEMU_CONSOLE(s), x, y, c->ch,
&(c->t_attrib));
c++;
}
@@ -714,11 +547,11 @@
}
}
console_show_cursor(s, 1);
- dpy_gfx_update(s, 0, 0,
+ dpy_gfx_update(QEMU_CONSOLE(s), 0, 0,
surface_width(surface), surface_height(surface));
}
-static void console_scroll(QemuConsole *s, int ydelta)
+static void console_scroll(QemuTextConsole *s, int ydelta)
{
int i, y1;
@@ -747,8 +580,9 @@
console_refresh(s);
}
-static void console_put_lf(QemuConsole *s)
+static void vc_put_lf(VCChardev *vc)
{
+ QemuTextConsole *s = vc->console;
TextCell *c;
int x, y1;
@@ -768,23 +602,21 @@
c = &s->cells[y1 * s->width];
for(x = 0; x < s->width; x++) {
c->ch = ' ';
- c->t_attrib = s->t_attrib_default;
+ c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
c++;
}
if (s->y_displayed == s->y_base) {
- if (s->ds->have_text) {
- s->text_x[0] = 0;
- s->text_y[0] = 0;
- s->text_x[1] = s->width - 1;
- s->text_y[1] = s->height - 1;
- }
+ s->text_x[0] = 0;
+ s->text_y[0] = 0;
+ s->text_x[1] = s->width - 1;
+ s->text_y[1] = s->height - 1;
- vga_bitblt(s, 0, FONT_HEIGHT, 0, 0,
- s->width * FONT_WIDTH,
- (s->height - 1) * FONT_HEIGHT);
- vga_fill_rect(s, 0, (s->height - 1) * FONT_HEIGHT,
- s->width * FONT_WIDTH, FONT_HEIGHT,
- color_table_rgb[0][s->t_attrib_default.bgcol]);
+ qemu_console_bitblt(QEMU_CONSOLE(s), 0, FONT_HEIGHT, 0, 0,
+ s->width * FONT_WIDTH,
+ (s->height - 1) * FONT_HEIGHT);
+ qemu_console_fill_rect(QEMU_CONSOLE(s), 0, (s->height - 1) * FONT_HEIGHT,
+ s->width * FONT_WIDTH, FONT_HEIGHT,
+ color_table_rgb[0][TEXT_ATTRIBUTES_DEFAULT.bgcol]);
s->update_x0 = 0;
s->update_y0 = 0;
s->update_x1 = s->width * FONT_WIDTH;
@@ -797,139 +629,143 @@
* NOTE: I know this code is not very efficient (checking every color for it
* self) but it is more readable and better maintainable.
*/
-static void console_handle_escape(QemuConsole *s)
+static void vc_handle_escape(VCChardev *vc)
{
int i;
- for (i=0; i<s->nb_esc_params; i++) {
- switch (s->esc_params[i]) {
+ for (i = 0; i < vc->nb_esc_params; i++) {
+ switch (vc->esc_params[i]) {
case 0: /* reset all console attributes to default */
- s->t_attrib = s->t_attrib_default;
+ vc->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
break;
case 1:
- s->t_attrib.bold = 1;
+ vc->t_attrib.bold = 1;
break;
case 4:
- s->t_attrib.uline = 1;
+ vc->t_attrib.uline = 1;
break;
case 5:
- s->t_attrib.blink = 1;
+ vc->t_attrib.blink = 1;
break;
case 7:
- s->t_attrib.invers = 1;
+ vc->t_attrib.invers = 1;
break;
case 8:
- s->t_attrib.unvisible = 1;
+ vc->t_attrib.unvisible = 1;
break;
case 22:
- s->t_attrib.bold = 0;
+ vc->t_attrib.bold = 0;
break;
case 24:
- s->t_attrib.uline = 0;
+ vc->t_attrib.uline = 0;
break;
case 25:
- s->t_attrib.blink = 0;
+ vc->t_attrib.blink = 0;
break;
case 27:
- s->t_attrib.invers = 0;
+ vc->t_attrib.invers = 0;
break;
case 28:
- s->t_attrib.unvisible = 0;
+ vc->t_attrib.unvisible = 0;
break;
/* set foreground color */
case 30:
- s->t_attrib.fgcol = QEMU_COLOR_BLACK;
+ vc->t_attrib.fgcol = QEMU_COLOR_BLACK;
break;
case 31:
- s->t_attrib.fgcol = QEMU_COLOR_RED;
+ vc->t_attrib.fgcol = QEMU_COLOR_RED;
break;
case 32:
- s->t_attrib.fgcol = QEMU_COLOR_GREEN;
+ vc->t_attrib.fgcol = QEMU_COLOR_GREEN;
break;
case 33:
- s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
+ vc->t_attrib.fgcol = QEMU_COLOR_YELLOW;
break;
case 34:
- s->t_attrib.fgcol = QEMU_COLOR_BLUE;
+ vc->t_attrib.fgcol = QEMU_COLOR_BLUE;
break;
case 35:
- s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
+ vc->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
break;
case 36:
- s->t_attrib.fgcol = QEMU_COLOR_CYAN;
+ vc->t_attrib.fgcol = QEMU_COLOR_CYAN;
break;
case 37:
- s->t_attrib.fgcol = QEMU_COLOR_WHITE;
+ vc->t_attrib.fgcol = QEMU_COLOR_WHITE;
break;
/* set background color */
case 40:
- s->t_attrib.bgcol = QEMU_COLOR_BLACK;
+ vc->t_attrib.bgcol = QEMU_COLOR_BLACK;
break;
case 41:
- s->t_attrib.bgcol = QEMU_COLOR_RED;
+ vc->t_attrib.bgcol = QEMU_COLOR_RED;
break;
case 42:
- s->t_attrib.bgcol = QEMU_COLOR_GREEN;
+ vc->t_attrib.bgcol = QEMU_COLOR_GREEN;
break;
case 43:
- s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
+ vc->t_attrib.bgcol = QEMU_COLOR_YELLOW;
break;
case 44:
- s->t_attrib.bgcol = QEMU_COLOR_BLUE;
+ vc->t_attrib.bgcol = QEMU_COLOR_BLUE;
break;
case 45:
- s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
+ vc->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
break;
case 46:
- s->t_attrib.bgcol = QEMU_COLOR_CYAN;
+ vc->t_attrib.bgcol = QEMU_COLOR_CYAN;
break;
case 47:
- s->t_attrib.bgcol = QEMU_COLOR_WHITE;
+ vc->t_attrib.bgcol = QEMU_COLOR_WHITE;
break;
}
}
}
-static void console_clear_xy(QemuConsole *s, int x, int y)
+static void vc_clear_xy(VCChardev *vc, int x, int y)
{
+ QemuTextConsole *s = vc->console;
int y1 = (s->y_base + y) % s->total_height;
if (x >= s->width) {
x = s->width - 1;
}
TextCell *c = &s->cells[y1 * s->width + x];
c->ch = ' ';
- c->t_attrib = s->t_attrib_default;
- update_xy(s, x, y);
+ c->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+ vc_update_xy(vc, x, y);
}
-static void console_put_one(QemuConsole *s, int ch)
+static void vc_put_one(VCChardev *vc, int ch)
{
+ QemuTextConsole *s = vc->console;
TextCell *c;
int y1;
if (s->x >= s->width) {
/* line wrap */
s->x = 0;
- console_put_lf(s);
+ vc_put_lf(vc);
}
y1 = (s->y_base + s->y) % s->total_height;
c = &s->cells[y1 * s->width + s->x];
c->ch = ch;
- c->t_attrib = s->t_attrib;
- update_xy(s, s->x, s->y);
+ c->t_attrib = vc->t_attrib;
+ vc_update_xy(vc, s->x, s->y);
s->x++;
}
-static void console_respond_str(QemuConsole *s, const char *buf)
+static void vc_respond_str(VCChardev *vc, const char *buf)
{
while (*buf) {
- console_put_one(s, *buf);
+ vc_put_one(vc, *buf);
buf++;
}
}
/* set cursor, checking bounds */
-static void set_cursor(QemuConsole *s, int x, int y)
+static void vc_set_cursor(VCChardev *vc, int x, int y)
{
+ QemuTextConsole *s = vc->console;
+
if (x < 0) {
x = 0;
}
@@ -947,20 +783,21 @@
s->y = y;
}
-static void console_putchar(QemuConsole *s, int ch)
+static void vc_putchar(VCChardev *vc, int ch)
{
+ QemuTextConsole *s = vc->console;
int i;
int x, y;
char response[40];
- switch(s->state) {
+ switch(vc->state) {
case TTY_STATE_NORM:
switch(ch) {
case '\r': /* carriage return */
s->x = 0;
break;
case '\n': /* newline */
- console_put_lf(s);
+ vc_put_lf(vc);
break;
case '\b': /* backspace */
if (s->x > 0)
@@ -969,7 +806,7 @@
case '\t': /* tabspace */
if (s->x + (8 - (s->x % 8)) > s->width) {
s->x = 0;
- console_put_lf(s);
+ vc_put_lf(vc);
} else {
s->x = s->x + (8 - (s->x % 8));
}
@@ -984,81 +821,81 @@
/* SO (shift out), character set 1 (ignored) */
break;
case 27: /* esc (introducing an escape sequence) */
- s->state = TTY_STATE_ESC;
+ vc->state = TTY_STATE_ESC;
break;
default:
- console_put_one(s, ch);
+ vc_put_one(vc, ch);
break;
}
break;
case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
if (ch == '[') {
for(i=0;i<MAX_ESC_PARAMS;i++)
- s->esc_params[i] = 0;
- s->nb_esc_params = 0;
- s->state = TTY_STATE_CSI;
+ vc->esc_params[i] = 0;
+ vc->nb_esc_params = 0;
+ vc->state = TTY_STATE_CSI;
} else {
- s->state = TTY_STATE_NORM;
+ vc->state = TTY_STATE_NORM;
}
break;
case TTY_STATE_CSI: /* handle escape sequence parameters */
if (ch >= '0' && ch <= '9') {
- if (s->nb_esc_params < MAX_ESC_PARAMS) {
- int *param = &s->esc_params[s->nb_esc_params];
+ if (vc->nb_esc_params < MAX_ESC_PARAMS) {
+ int *param = &vc->esc_params[vc->nb_esc_params];
int digit = (ch - '0');
*param = (*param <= (INT_MAX - digit) / 10) ?
*param * 10 + digit : INT_MAX;
}
} else {
- if (s->nb_esc_params < MAX_ESC_PARAMS)
- s->nb_esc_params++;
+ if (vc->nb_esc_params < MAX_ESC_PARAMS)
+ vc->nb_esc_params++;
if (ch == ';' || ch == '?') {
break;
}
- trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
- ch, s->nb_esc_params);
- s->state = TTY_STATE_NORM;
+ trace_console_putchar_csi(vc->esc_params[0], vc->esc_params[1],
+ ch, vc->nb_esc_params);
+ vc->state = TTY_STATE_NORM;
switch(ch) {
case 'A':
/* move cursor up */
- if (s->esc_params[0] == 0) {
- s->esc_params[0] = 1;
+ if (vc->esc_params[0] == 0) {
+ vc->esc_params[0] = 1;
}
- set_cursor(s, s->x, s->y - s->esc_params[0]);
+ vc_set_cursor(vc, s->x, s->y - vc->esc_params[0]);
break;
case 'B':
/* move cursor down */
- if (s->esc_params[0] == 0) {
- s->esc_params[0] = 1;
+ if (vc->esc_params[0] == 0) {
+ vc->esc_params[0] = 1;
}
- set_cursor(s, s->x, s->y + s->esc_params[0]);
+ vc_set_cursor(vc, s->x, s->y + vc->esc_params[0]);
break;
case 'C':
/* move cursor right */
- if (s->esc_params[0] == 0) {
- s->esc_params[0] = 1;
+ if (vc->esc_params[0] == 0) {
+ vc->esc_params[0] = 1;
}
- set_cursor(s, s->x + s->esc_params[0], s->y);
+ vc_set_cursor(vc, s->x + vc->esc_params[0], s->y);
break;
case 'D':
/* move cursor left */
- if (s->esc_params[0] == 0) {
- s->esc_params[0] = 1;
+ if (vc->esc_params[0] == 0) {
+ vc->esc_params[0] = 1;
}
- set_cursor(s, s->x - s->esc_params[0], s->y);
+ vc_set_cursor(vc, s->x - vc->esc_params[0], s->y);
break;
case 'G':
/* move cursor to column */
- set_cursor(s, s->esc_params[0] - 1, s->y);
+ vc_set_cursor(vc, vc->esc_params[0] - 1, s->y);
break;
case 'f':
case 'H':
/* move cursor to row, column */
- set_cursor(s, s->esc_params[1] - 1, s->esc_params[0] - 1);
+ vc_set_cursor(vc, vc->esc_params[1] - 1, vc->esc_params[0] - 1);
break;
case 'J':
- switch (s->esc_params[0]) {
+ switch (vc->esc_params[0]) {
case 0:
/* clear to end of screen */
for (y = s->y; y < s->height; y++) {
@@ -1066,7 +903,7 @@
if (y == s->y && x < s->x) {
continue;
}
- console_clear_xy(s, x, y);
+ vc_clear_xy(vc, x, y);
}
}
break;
@@ -1077,7 +914,7 @@
if (y == s->y && x > s->x) {
break;
}
- console_clear_xy(s, x, y);
+ vc_clear_xy(vc, x, y);
}
}
break;
@@ -1085,61 +922,61 @@
/* clear entire screen */
for (y = 0; y <= s->height; y++) {
for (x = 0; x < s->width; x++) {
- console_clear_xy(s, x, y);
+ vc_clear_xy(vc, x, y);
}
}
break;
}
break;
case 'K':
- switch (s->esc_params[0]) {
+ switch (vc->esc_params[0]) {
case 0:
/* clear to eol */
for(x = s->x; x < s->width; x++) {
- console_clear_xy(s, x, s->y);
+ vc_clear_xy(vc, x, s->y);
}
break;
case 1:
/* clear from beginning of line */
for (x = 0; x <= s->x && x < s->width; x++) {
- console_clear_xy(s, x, s->y);
+ vc_clear_xy(vc, x, s->y);
}
break;
case 2:
/* clear entire line */
for(x = 0; x < s->width; x++) {
- console_clear_xy(s, x, s->y);
+ vc_clear_xy(vc, x, s->y);
}
break;
}
break;
case 'm':
- console_handle_escape(s);
+ vc_handle_escape(vc);
break;
case 'n':
- switch (s->esc_params[0]) {
+ switch (vc->esc_params[0]) {
case 5:
/* report console status (always succeed)*/
- console_respond_str(s, "\033[0n");
+ vc_respond_str(vc, "\033[0n");
break;
case 6:
/* report cursor position */
sprintf(response, "\033[%d;%dR",
(s->y_base + s->y) % s->total_height + 1,
s->x + 1);
- console_respond_str(s, response);
+ vc_respond_str(vc, response);
break;
}
break;
case 's':
/* save cursor position */
- s->x_saved = s->x;
- s->y_saved = s->y;
+ vc->x_saved = s->x;
+ vc->y_saved = s->y;
break;
case 'u':
/* restore cursor position */
- s->x = s->x_saved;
- s->y = s->y_saved;
+ s->x = vc->x_saved;
+ s->y = vc->y_saved;
break;
default:
trace_console_putchar_unhandled(ch);
@@ -1239,27 +1076,20 @@
DisplayState *ds = s->ds;
active_console = s;
- if (ds->have_gfx) {
- QLIST_FOREACH(dcl, &ds->listeners, next) {
- if (dcl->con != NULL) {
- continue;
- }
- displaychangelistener_display_console(dcl, s, NULL);
+ QLIST_FOREACH (dcl, &ds->listeners, next) {
+ if (dcl->con != NULL) {
+ continue;
}
+ displaychangelistener_display_console(dcl, s, NULL);
}
- if (ds->have_text) {
- dpy_text_resize(s, s->width, s->height);
+
+ if (QEMU_IS_TEXT_CONSOLE(s)) {
+ dpy_text_resize(s, QEMU_TEXT_CONSOLE(s)->width, QEMU_TEXT_CONSOLE(s)->height);
+ text_console_update_cursor(NULL);
}
- text_console_update_cursor(NULL);
}
}
-struct VCChardev {
- Chardev parent;
- QemuConsole *console;
-};
-typedef struct VCChardev VCChardev;
-
#define TYPE_CHARDEV_VC "chardev-vc"
DECLARE_INSTANCE_CHECKER(VCChardev, VC_CHARDEV,
TYPE_CHARDEV_VC)
@@ -1267,31 +1097,27 @@
static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
{
VCChardev *drv = VC_CHARDEV(chr);
- QemuConsole *s = drv->console;
+ QemuTextConsole *s = drv->console;
int i;
- if (!s->ds) {
- return 0;
- }
-
s->update_x0 = s->width * FONT_WIDTH;
s->update_y0 = s->height * FONT_HEIGHT;
s->update_x1 = 0;
s->update_y1 = 0;
console_show_cursor(s, 0);
for(i = 0; i < len; i++) {
- console_putchar(s, buf[i]);
+ vc_putchar(drv, buf[i]);
}
console_show_cursor(s, 1);
- if (s->ds->have_gfx && s->update_x0 < s->update_x1) {
- dpy_gfx_update(s, s->update_x0, s->update_y0,
+ if (s->update_x0 < s->update_x1) {
+ dpy_gfx_update(QEMU_CONSOLE(s), s->update_x0, s->update_y0,
s->update_x1 - s->update_x0,
s->update_y1 - s->update_y0);
}
return len;
}
-static void kbd_send_chars(QemuConsole *s)
+static void kbd_send_chars(QemuTextConsole *s)
{
uint32_t len, avail;
@@ -1309,15 +1135,12 @@
}
/* called when an ascii key is pressed */
-void kbd_put_keysym_console(QemuConsole *s, int keysym)
+void kbd_put_keysym_console(QemuTextConsole *s, int keysym)
{
uint8_t buf[16], *q;
int c;
uint32_t num_free;
- if (!s || (s->console_type == GRAPHIC_CONSOLE))
- return;
-
switch(keysym) {
case QEMU_KEY_CTRL_UP:
console_scroll(s, -1);
@@ -1347,13 +1170,13 @@
*q++ = '[';
*q++ = keysym & 0xff;
} else if (s->echo && (keysym == '\r' || keysym == '\n')) {
- vc_chr_write(s->chr, (const uint8_t *) "\r", 1);
+ qemu_chr_write(s->chr, (uint8_t *)"\r", 1, true);
*q++ = '\n';
} else {
*q++ = keysym;
}
if (s->echo) {
- vc_chr_write(s->chr, buf, q - buf);
+ qemu_chr_write(s->chr, buf, q - buf, true);
}
num_free = fifo8_num_free(&s->out_fifo);
fifo8_push_all(&s->out_fifo, buf, MIN(num_free, q - buf));
@@ -1387,7 +1210,7 @@
[Q_KEY_CODE_PGDN] = QEMU_KEY_CTRL_PAGEDOWN,
};
-bool kbd_put_qcode_console(QemuConsole *s, int qcode, bool ctrl)
+bool kbd_put_qcode_console(QemuTextConsole *s, int qcode, bool ctrl)
{
int keysym;
@@ -1399,7 +1222,7 @@
return true;
}
-void kbd_put_string_console(QemuConsole *s, const char *str, int len)
+void kbd_put_string_console(QemuTextConsole *s, const char *str, int len)
{
int i;
@@ -1410,22 +1233,24 @@
void kbd_put_keysym(int keysym)
{
- kbd_put_keysym_console(active_console, keysym);
+ if (QEMU_IS_TEXT_CONSOLE(active_console)) {
+ kbd_put_keysym_console(QEMU_TEXT_CONSOLE(active_console), keysym);
+ }
}
static void text_console_invalidate(void *opaque)
{
- QemuConsole *s = (QemuConsole *) opaque;
+ QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
- if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
- text_console_resize(s);
+ if (!QEMU_IS_FIXED_TEXT_CONSOLE(s)) {
+ text_console_resize(QEMU_TEXT_CONSOLE(s));
}
console_refresh(s);
}
static void text_console_update(void *opaque, console_ch_t *chardata)
{
- QemuConsole *s = (QemuConsole *) opaque;
+ QemuTextConsole *s = QEMU_TEXT_CONSOLE(opaque);
int i, j, src;
if (s->text_x[0] <= s->text_x[1]) {
@@ -1439,7 +1264,7 @@
s->cells[src].t_attrib.bgcol,
s->cells[src].t_attrib.bold));
}
- dpy_text_update(s, s->text_x[0], s->text_y[0],
+ dpy_text_update(QEMU_CONSOLE(s), s->text_x[0], s->text_y[0],
s->text_x[1] - s->text_x[0], i - s->text_y[0]);
s->text_x[0] = s->width;
s->text_y[0] = s->height;
@@ -1447,44 +1272,28 @@
s->text_y[1] = 0;
}
if (s->cursor_invalidate) {
- dpy_text_cursor(s, s->x, s->y);
+ dpy_text_cursor(QEMU_CONSOLE(s), s->x, s->y);
s->cursor_invalidate = 0;
}
}
-static QemuConsole *new_console(DisplayState *ds, console_type_t console_type,
- uint32_t head)
+static void
+qemu_console_register(QemuConsole *c)
{
- Object *obj;
- QemuConsole *s;
int i;
- obj = object_new(TYPE_QEMU_CONSOLE);
- s = QEMU_CONSOLE(obj);
- qemu_co_queue_init(&s->dump_queue);
- s->head = head;
- object_property_add_link(obj, "device", TYPE_DEVICE,
- (Object **)&s->device,
- object_property_allow_set_link,
- OBJ_PROP_LINK_STRONG);
- object_property_add_uint32_ptr(obj, "head", &s->head,
- OBJ_PROP_FLAG_READ);
-
- if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
- (console_type == GRAPHIC_CONSOLE))) {
- active_console = s;
+ if (!active_console || (!QEMU_IS_GRAPHIC_CONSOLE(active_console) &&
+ QEMU_IS_GRAPHIC_CONSOLE(c))) {
+ active_console = c;
}
- s->ds = ds;
- s->console_type = console_type;
- s->window_id = -1;
if (QTAILQ_EMPTY(&consoles)) {
- s->index = 0;
- QTAILQ_INSERT_TAIL(&consoles, s, next);
- } else if (console_type != GRAPHIC_CONSOLE || phase_check(PHASE_MACHINE_READY)) {
+ c->index = 0;
+ QTAILQ_INSERT_TAIL(&consoles, c, next);
+ } else if (!QEMU_IS_GRAPHIC_CONSOLE(c) || phase_check(PHASE_MACHINE_READY)) {
QemuConsole *last = QTAILQ_LAST(&consoles);
- s->index = last->index + 1;
- QTAILQ_INSERT_TAIL(&consoles, s, next);
+ c->index = last->index + 1;
+ QTAILQ_INSERT_TAIL(&consoles, c, next);
} else {
/*
* HACK: Put graphical consoles before text consoles.
@@ -1492,26 +1301,133 @@
* Only do that for coldplugged devices. After initial device
* initialization we will not renumber the consoles any more.
*/
- QemuConsole *c = QTAILQ_FIRST(&consoles);
+ QemuConsole *it = QTAILQ_FIRST(&consoles);
- while (QTAILQ_NEXT(c, next) != NULL &&
- c->console_type == GRAPHIC_CONSOLE) {
- c = QTAILQ_NEXT(c, next);
+ while (QTAILQ_NEXT(it, next) != NULL && QEMU_IS_GRAPHIC_CONSOLE(it)) {
+ it = QTAILQ_NEXT(it, next);
}
- if (c->console_type == GRAPHIC_CONSOLE) {
+ if (QEMU_IS_GRAPHIC_CONSOLE(it)) {
/* have no text consoles */
- s->index = c->index + 1;
- QTAILQ_INSERT_AFTER(&consoles, c, s, next);
+ c->index = it->index + 1;
+ QTAILQ_INSERT_AFTER(&consoles, it, c, next);
} else {
- s->index = c->index;
- QTAILQ_INSERT_BEFORE(c, s, next);
+ c->index = it->index;
+ QTAILQ_INSERT_BEFORE(it, c, next);
/* renumber text consoles */
- for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) {
- c->index = i;
+ for (i = c->index + 1; it != NULL; it = QTAILQ_NEXT(it, next), i++) {
+ it->index = i;
}
}
}
- return s;
+}
+
+static void
+qemu_console_finalize(Object *obj)
+{
+ QemuConsole *c = QEMU_CONSOLE(obj);
+
+ /* TODO: check this code path, and unregister from consoles */
+ g_clear_pointer(&c->surface, qemu_free_displaysurface);
+ g_clear_pointer(&c->gl_unblock_timer, timer_free);
+ g_clear_pointer(&c->ui_timer, timer_free);
+}
+
+static void
+qemu_console_class_init(ObjectClass *oc, void *data)
+{
+}
+
+static void
+qemu_console_init(Object *obj)
+{
+ QemuConsole *c = QEMU_CONSOLE(obj);
+ DisplayState *ds = get_alloc_displaystate();
+
+ qemu_co_queue_init(&c->dump_queue);
+ c->ds = ds;
+ c->window_id = -1;
+ c->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ dpy_set_ui_info_timer, c);
+ qemu_console_register(c);
+}
+
+static void
+qemu_graphic_console_finalize(Object *obj)
+{
+ QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
+
+ g_clear_pointer(&c->device, object_unref);
+}
+
+static void
+qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(obj);
+
+ visit_type_uint32(v, name, &c->head, errp);
+}
+
+static void
+qemu_graphic_console_class_init(ObjectClass *oc, void *data)
+{
+ object_class_property_add_link(oc, "device", TYPE_DEVICE,
+ offsetof(QemuGraphicConsole, device),
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+ object_class_property_add(oc, "head", "uint32",
+ qemu_graphic_console_prop_get_head,
+ NULL, NULL, NULL);
+}
+
+static void
+qemu_graphic_console_init(Object *obj)
+{
+}
+
+static void
+qemu_text_console_finalize(Object *obj)
+{
+}
+
+static void
+qemu_text_console_class_init(ObjectClass *oc, void *data)
+{
+ if (!cursor_timer) {
+ cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ text_console_update_cursor, NULL);
+ }
+}
+
+static const GraphicHwOps text_console_ops = {
+ .invalidate = text_console_invalidate,
+ .text_update = text_console_update,
+};
+
+static void
+qemu_text_console_init(Object *obj)
+{
+ QemuTextConsole *c = QEMU_TEXT_CONSOLE(obj);
+
+ fifo8_create(&c->out_fifo, 16);
+ c->total_height = DEFAULT_BACKSCROLL;
+ QEMU_CONSOLE(c)->hw_ops = &text_console_ops;
+ QEMU_CONSOLE(c)->hw = c;
+}
+
+static void
+qemu_fixed_text_console_finalize(Object *obj)
+{
+}
+
+static void
+qemu_fixed_text_console_class_init(ObjectClass *oc, void *data)
+{
+}
+
+static void
+qemu_fixed_text_console_init(Object *obj)
+{
}
#ifdef WIN32
@@ -1605,8 +1521,8 @@
const char *msg)
{
DisplaySurface *surface = qemu_create_displaysurface(w, h);
- pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
- pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
+ pixman_color_t bg = QEMU_PIXMAN_COLOR_BLACK;
+ pixman_color_t fg = QEMU_PIXMAN_COLOR_GRAY;
pixman_image_t *glyph;
int len, x, y, i;
@@ -1757,6 +1673,16 @@
con->gl = gl;
}
+static void
+dcl_set_graphic_cursor(DisplayChangeListener *dcl, QemuGraphicConsole *con)
+{
+ if (con && con->cursor && dcl->ops->dpy_cursor_define) {
+ dcl->ops->dpy_cursor_define(dcl, con->cursor);
+ }
+ if (con && dcl->ops->dpy_mouse_set) {
+ dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
+ }
+}
void register_displaychangelistener(DisplayChangeListener *dcl)
{
QemuConsole *con;
@@ -1774,11 +1700,8 @@
con = active_console;
}
displaychangelistener_display_console(dcl, con, dcl->con ? &error_fatal : NULL);
- if (con && con->cursor && dcl->ops->dpy_cursor_define) {
- dcl->ops->dpy_cursor_define(dcl, con->cursor);
- }
- if (con && dcl->ops->dpy_mouse_set) {
- dcl->ops->dpy_mouse_set(dcl, con->cursor_x, con->cursor_y, con->cursor_on);
+ if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
+ dcl_set_graphic_cursor(dcl, QEMU_GRAPHIC_CONSOLE(con));
}
text_console_update_cursor(NULL);
}
@@ -1809,8 +1732,9 @@
static void dpy_set_ui_info_timer(void *opaque)
{
QemuConsole *con = opaque;
+ uint32_t head = qemu_console_get_head(con);
- con->hw_ops->ui_info(con->hw, con->head, &con->ui_info);
+ con->hw_ops->ui_info(con->hw, head, &con->ui_info);
}
bool dpy_ui_info_supported(QemuConsole *con)
@@ -2020,19 +1944,20 @@
}
}
-void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
+void dpy_mouse_set(QemuConsole *c, int x, int y, int on)
{
- DisplayState *s = con->ds;
+ QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
+ DisplayState *s = c->ds;
DisplayChangeListener *dcl;
con->cursor_x = x;
con->cursor_y = y;
con->cursor_on = on;
- if (!qemu_console_is_visible(con)) {
+ if (!qemu_console_is_visible(c)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
- if (con != (dcl->con ? dcl->con : active_console)) {
+ if (c != (dcl->con ? dcl->con : active_console)) {
continue;
}
if (dcl->ops->dpy_mouse_set) {
@@ -2041,18 +1966,19 @@
}
}
-void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
+void dpy_cursor_define(QemuConsole *c, QEMUCursor *cursor)
{
- DisplayState *s = con->ds;
+ QemuGraphicConsole *con = QEMU_GRAPHIC_CONSOLE(c);
+ DisplayState *s = c->ds;
DisplayChangeListener *dcl;
cursor_unref(con->cursor);
con->cursor = cursor_ref(cursor);
- if (!qemu_console_is_visible(con)) {
+ if (!qemu_console_is_visible(c)) {
return;
}
QLIST_FOREACH(dcl, &s->listeners, next) {
- if (con != (dcl->con ? dcl->con : active_console)) {
+ if (c != (dcl->con ? dcl->con : active_console)) {
continue;
}
if (dcl->ops->dpy_cursor_define) {
@@ -2237,8 +2163,6 @@
{
if (!display_state) {
display_state = g_new0(DisplayState, 1);
- cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
- text_console_update_cursor, NULL);
}
return display_state;
}
@@ -2252,14 +2176,8 @@
gchar *name;
QemuConsole *con;
- get_alloc_displaystate();
QTAILQ_FOREACH(con, &consoles, next) {
- if (con->console_type != GRAPHIC_CONSOLE &&
- con->ds == NULL) {
- text_console_do_init(con->chr, display_state);
- }
-
- /* Hook up into the qom tree here (not in new_console()), once
+ /* Hook up into the qom tree here (not in object_new()), once
* all QemuConsoles are created and the order / numbering
* doesn't change any more */
name = g_strdup_printf("console[%d]", con->index);
@@ -2288,21 +2206,18 @@
int width = 640;
int height = 480;
QemuConsole *s;
- DisplayState *ds;
DisplaySurface *surface;
- ds = get_alloc_displaystate();
- s = qemu_console_lookup_unused();
+ s = qemu_graphic_console_lookup_unused();
if (s) {
trace_console_gfx_reuse(s->index);
width = qemu_console_get_width(s, 0);
height = qemu_console_get_height(s, 0);
} else {
trace_console_gfx_new();
- s = new_console(ds, GRAPHIC_CONSOLE, head);
- s->ui_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
- dpy_set_ui_info_timer, s);
+ s = (QemuConsole *)object_new(TYPE_QEMU_GRAPHIC_CONSOLE);
}
+ QEMU_GRAPHIC_CONSOLE(s)->head = head;
graphic_console_set_hwops(s, hw_ops, opaque);
if (dev) {
object_property_set_link(OBJECT(s), "device", OBJECT(dev),
@@ -2396,13 +2311,13 @@
return con;
}
-QemuConsole *qemu_console_lookup_unused(void)
+static QemuConsole *qemu_graphic_console_lookup_unused(void)
{
QemuConsole *con;
Object *obj;
QTAILQ_FOREACH(con, &consoles, next) {
- if (con->hw_ops != &unused_ops) {
+ if (!QEMU_IS_GRAPHIC_CONSOLE(con) || con->hw_ops != &unused_ops) {
continue;
}
obj = object_property_get_link(OBJECT(con),
@@ -2420,7 +2335,7 @@
if (con == NULL) {
con = active_console;
}
- return con ? con->cursor : NULL;
+ return QEMU_IS_GRAPHIC_CONSOLE(con) ? QEMU_GRAPHIC_CONSOLE(con)->cursor : NULL;
}
bool qemu_console_is_visible(QemuConsole *con)
@@ -2433,7 +2348,7 @@
if (con == NULL) {
con = active_console;
}
- return con && (con->console_type == GRAPHIC_CONSOLE);
+ return con && QEMU_IS_GRAPHIC_CONSOLE(con);
}
bool qemu_console_is_fixedsize(QemuConsole *con)
@@ -2441,7 +2356,7 @@
if (con == NULL) {
con = active_console;
}
- return con && (con->console_type != TEXT_CONSOLE);
+ return con && (QEMU_IS_GRAPHIC_CONSOLE(con) || QEMU_IS_FIXED_TEXT_CONSOLE(con));
}
bool qemu_console_is_gl_blocked(QemuConsole *con)
@@ -2477,31 +2392,34 @@
char *qemu_console_get_label(QemuConsole *con)
{
- if (con->console_type == GRAPHIC_CONSOLE) {
- if (con->device) {
+ if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
+ QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con);
+ if (c->device) {
DeviceState *dev;
bool multihead;
- dev = DEVICE(con->device);
+ dev = DEVICE(c->device);
multihead = qemu_console_is_multihead(dev);
if (multihead) {
return g_strdup_printf("%s.%d", dev->id ?
dev->id :
- object_get_typename(con->device),
- con->head);
+ object_get_typename(c->device),
+ c->head);
} else {
return g_strdup_printf("%s", dev->id ?
dev->id :
- object_get_typename(con->device));
+ object_get_typename(c->device));
}
}
return g_strdup("VGA");
- } else {
- if (con->chr && con->chr->label) {
- return g_strdup(con->chr->label);
+ } else if (QEMU_IS_TEXT_CONSOLE(con)) {
+ QemuTextConsole *c = QEMU_TEXT_CONSOLE(con);
+ if (c->chr && c->chr->label) {
+ return g_strdup(c->chr->label);
}
- return g_strdup_printf("vc%d", con->index);
}
+
+ return g_strdup_printf("vc%d", con->index);
}
int qemu_console_get_index(QemuConsole *con)
@@ -2517,7 +2435,13 @@
if (con == NULL) {
con = active_console;
}
- return con ? con->head : -1;
+ if (con == NULL) {
+ return -1;
+ }
+ if (QEMU_IS_GRAPHIC_CONSOLE(con)) {
+ return QEMU_GRAPHIC_CONSOLE(con)->head;
+ }
+ return 0;
}
int qemu_console_get_width(QemuConsole *con, int fallback)
@@ -2563,32 +2487,22 @@
static void vc_chr_accept_input(Chardev *chr)
{
VCChardev *drv = VC_CHARDEV(chr);
- QemuConsole *s = drv->console;
- kbd_send_chars(s);
+ kbd_send_chars(drv->console);
}
static void vc_chr_set_echo(Chardev *chr, bool echo)
{
VCChardev *drv = VC_CHARDEV(chr);
- QemuConsole *s = drv->console;
- s->echo = echo;
+ drv->console->echo = echo;
}
-static void text_console_update_cursor_timer(void)
-{
- timer_mod(cursor_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
- + CONSOLE_CURSOR_PERIOD / 2);
-}
-
-static void text_console_update_cursor(void *opaque)
+int qemu_invalidate_text_consoles(void)
{
QemuConsole *s;
int count = 0;
- cursor_visible_phase = !cursor_visible_phase;
-
QTAILQ_FOREACH(s, &consoles, next) {
if (qemu_console_is_graphic(s) ||
!qemu_console_is_visible(s)) {
@@ -2598,66 +2512,17 @@
graphic_hw_invalidate(s);
}
- if (count) {
- text_console_update_cursor_timer();
- }
+ return count;
}
-static const GraphicHwOps text_console_ops = {
- .invalidate = text_console_invalidate,
- .text_update = text_console_update,
-};
-
-static void text_console_do_init(Chardev *chr, DisplayState *ds)
+static void text_console_update_cursor(void *opaque)
{
- VCChardev *drv = VC_CHARDEV(chr);
- QemuConsole *s = drv->console;
- int g_width = 80 * FONT_WIDTH;
- int g_height = 24 * FONT_HEIGHT;
+ cursor_visible_phase = !cursor_visible_phase;
- fifo8_create(&s->out_fifo, 16);
- s->ds = ds;
-
- s->y_displayed = 0;
- s->y_base = 0;
- s->total_height = DEFAULT_BACKSCROLL;
- s->x = 0;
- s->y = 0;
- if (s->scanout.kind != SCANOUT_SURFACE) {
- if (active_console && active_console->scanout.kind == SCANOUT_SURFACE) {
- g_width = qemu_console_get_width(active_console, g_width);
- g_height = qemu_console_get_height(active_console, g_height);
- }
- s->surface = qemu_create_displaysurface(g_width, g_height);
- s->scanout.kind = SCANOUT_SURFACE;
+ if (qemu_invalidate_text_consoles()) {
+ timer_mod(cursor_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
}
-
- s->hw_ops = &text_console_ops;
- s->hw = s;
-
- /* Set text attribute defaults */
- s->t_attrib_default.bold = 0;
- s->t_attrib_default.uline = 0;
- s->t_attrib_default.blink = 0;
- s->t_attrib_default.invers = 0;
- s->t_attrib_default.unvisible = 0;
- s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
- s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
- /* set current text attributes to default */
- s->t_attrib = s->t_attrib_default;
- text_console_resize(s);
-
- if (chr->label) {
- char *msg;
-
- s->t_attrib.bgcol = QEMU_COLOR_BLUE;
- msg = g_strdup_printf("%s console\r\n", chr->label);
- vc_chr_write(chr, (uint8_t *)msg, strlen(msg));
- g_free(msg);
- s->t_attrib = s->t_attrib_default;
- }
-
- qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
static void vc_chr_open(Chardev *chr,
@@ -2667,7 +2532,7 @@
{
ChardevVC *vc = backend->u.vc.data;
VCChardev *drv = VC_CHARDEV(chr);
- QemuConsole *s;
+ QemuTextConsole *s;
unsigned width = 0;
unsigned height = 0;
@@ -2685,36 +2550,40 @@
trace_console_txt_new(width, height);
if (width == 0 || height == 0) {
- s = new_console(NULL, TEXT_CONSOLE, 0);
+ s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_TEXT_CONSOLE));
+ width = qemu_console_get_width(NULL, 80 * FONT_WIDTH);
+ height = qemu_console_get_height(NULL, 24 * FONT_HEIGHT);
} else {
- s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE, 0);
- s->scanout.kind = SCANOUT_SURFACE;
- s->surface = qemu_create_displaysurface(width, height);
+ s = QEMU_TEXT_CONSOLE(object_new(TYPE_QEMU_FIXED_TEXT_CONSOLE));
}
- if (!s) {
- error_setg(errp, "cannot create text console");
- return;
- }
+ dpy_gfx_replace_surface(QEMU_CONSOLE(s), qemu_create_displaysurface(width, height));
s->chr = chr;
drv->console = s;
- if (display_state) {
- text_console_do_init(chr, display_state);
+ /* set current text attributes to default */
+ drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
+ text_console_resize(s);
+
+ if (chr->label) {
+ char *msg;
+
+ drv->t_attrib.bgcol = QEMU_COLOR_BLUE;
+ msg = g_strdup_printf("%s console\r\n", chr->label);
+ qemu_chr_write(chr, (uint8_t *)msg, strlen(msg), true);
+ g_free(msg);
+ drv->t_attrib = TEXT_ATTRIBUTES_DEFAULT;
}
- /* console/chardev init sometimes completes elsewhere in a 2nd
- * stage, so defer OPENED events until they are fully initialized
- */
- *be_opened = false;
+ *be_opened = true;
}
void qemu_console_resize(QemuConsole *s, int width, int height)
{
DisplaySurface *surface = qemu_console_surface(s);
- assert(s->console_type == GRAPHIC_CONSOLE);
+ assert(QEMU_IS_GRAPHIC_CONSOLE(s));
if ((s->scanout.kind != SCANOUT_SURFACE ||
(surface && surface->flags & QEMU_ALLOCATED_FLAG)) &&
@@ -2837,7 +2706,7 @@
}
}
-void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp)
+static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
{
int val;
ChardevVC *vc;
@@ -2871,18 +2740,11 @@
}
}
-static const TypeInfo qemu_console_info = {
- .name = TYPE_QEMU_CONSOLE,
- .parent = TYPE_OBJECT,
- .instance_size = sizeof(QemuConsole),
- .class_size = sizeof(QemuConsoleClass),
-};
-
static void char_vc_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
- cc->parse = qemu_chr_parse_vc;
+ cc->parse = vc_chr_parse;
cc->open = vc_chr_open;
cc->chr_write = vc_chr_write;
cc->chr_accept_input = vc_chr_accept_input;
@@ -2903,10 +2765,3 @@
type_register(&char_vc_type_info);
}
}
-
-static void register_types(void)
-{
- type_register_static(&qemu_console_info);
-}
-
-type_init(register_types);
diff --git a/ui/dbus-console.c b/ui/dbus-console.c
index e19774f..36f7349 100644
--- a/ui/dbus-console.c
+++ b/ui/dbus-console.c
@@ -150,6 +150,8 @@
DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object);
unregister_displaychangelistener(&ddc->dcl);
+ g_clear_object(&ddc->iface_touch);
+ g_clear_object(&ddc->iface_mouse);
g_clear_object(&ddc->iface_kbd);
g_clear_object(&ddc->iface);
g_clear_pointer(&ddc->listeners, g_hash_table_unref);
diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c
index 3091727..36548a7 100644
--- a/ui/dbus-listener.c
+++ b/ui/dbus-listener.c
@@ -26,6 +26,9 @@
#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "dbus.h"
+#ifdef CONFIG_OPENGL
+#include <pixman.h>
+#endif
#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
#endif
@@ -59,12 +62,15 @@
QemuDBusDisplay1Listener *proxy;
+#ifdef CONFIG_OPENGL
+ /* Keep track of the damage region */
+ pixman_region32_t gl_damage;
+#endif
+
DisplayChangeListener dcl;
DisplaySurface *ds;
enum share_kind ds_share;
- int gl_updates;
-
bool ds_mapped;
bool can_share_map;
@@ -539,11 +545,16 @@
return;
}
- if (ddl->gl_updates) {
- dbus_call_update_gl(dcl, 0, 0,
- surface_width(ddl->ds), surface_height(ddl->ds));
- ddl->gl_updates = 0;
+ int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
+
+ for (int i = 0; i < n_rects; i++) {
+ pixman_box32_t *box;
+ box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i;
+ /* TODO: Add a UpdateList call to send multiple updates at once */
+ dbus_call_update_gl(dcl, box->x1, box->y1,
+ box->x2 - box->x1, box->y2 - box->y1);
}
+ pixman_region32_clear(&ddl->gl_damage);
}
#endif /* OPENGL */
@@ -558,7 +569,10 @@
{
DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
- ddl->gl_updates++;
+ pixman_region32_t rect_region;
+ pixman_region32_init_rect(&rect_region, x, y, w, h);
+ pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region);
+ pixman_region32_fini(&rect_region);
}
#endif
@@ -738,6 +752,7 @@
g_clear_object(&ddl->d3d11_proxy);
g_clear_pointer(&ddl->peer_process, CloseHandle);
#ifdef CONFIG_OPENGL
+ pixman_region32_fini(&ddl->gl_damage);
egl_fb_destroy(&ddl->fb);
#endif
#endif
@@ -772,6 +787,9 @@
static void
dbus_display_listener_init(DBusDisplayListener *ddl)
{
+#ifdef CONFIG_OPENGL
+ pixman_region32_init(&ddl->gl_damage);
+#endif
}
const char *
diff --git a/ui/gtk.c b/ui/gtk.c
index 8ba41c8..a14d561 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -1187,7 +1187,7 @@
GdkEventKey *key, void *opaque)
{
VirtualConsole *vc = opaque;
- QemuConsole *con = vc->gfx.dcl.con;
+ QemuTextConsole *con = QEMU_TEXT_CONSOLE(vc->gfx.dcl.con);
if (key->keyval == GDK_KEY_Delete) {
kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
@@ -1860,7 +1860,6 @@
{
ChardevClass *cc = CHARDEV_CLASS(oc);
- cc->parse = qemu_chr_parse_vc;
cc->open = gd_vc_open;
cc->chr_write = gd_vc_chr_write;
cc->chr_accept_input = gd_vc_chr_accept_input;
@@ -2360,7 +2359,7 @@
{
VirtualConsole *vc;
- GtkDisplayState *s = g_malloc0(sizeof(*s));
+ GtkDisplayState *s;
GdkDisplay *window_display;
GtkIconTheme *theme;
char *dir;
@@ -2370,6 +2369,7 @@
exit(1);
}
assert(opts->type == DISPLAY_TYPE_GTK);
+ s = g_malloc0(sizeof(*s));
s->opts = opts;
theme = gtk_icon_theme_get_default();
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index e4f024a..be00a96 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -200,14 +200,6 @@
x, y, 0, 0, 0, 0, width, 1);
}
-/* copy linebuf to framebuffer */
-void qemu_pixman_linebuf_copy(pixman_image_t *fb, int width, int x, int y,
- pixman_image_t *linebuf)
-{
- pixman_image_composite(PIXMAN_OP_SRC, linebuf, NULL, fb,
- 0, 0, 0, 0, x, y, width, 1);
-}
-
pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
pixman_image_t *image)
{
@@ -226,17 +218,6 @@
pixman_image_unref(image);
}
-pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color)
-{
- pixman_color_t c;
-
- c.red = ((color & pf->rmask) >> pf->rshift) << (16 - pf->rbits);
- c.green = ((color & pf->gmask) >> pf->gshift) << (16 - pf->gbits);
- c.blue = ((color & pf->bmask) >> pf->bshift) << (16 - pf->bbits);
- c.alpha = ((color & pf->amask) >> pf->ashift) << (16 - pf->abits);
- return c;
-}
-
pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
unsigned int ch)
{
diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c
index f068382..a318cc5 100644
--- a/ui/sdl2-input.c
+++ b/ui/sdl2-input.c
@@ -43,15 +43,16 @@
ev->type == SDL_KEYDOWN ? "down" : "up");
qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN);
- if (!qemu_console_is_graphic(con)) {
+ if (QEMU_IS_TEXT_CONSOLE(con)) {
+ QemuTextConsole *s = QEMU_TEXT_CONSOLE(con);
bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL);
if (ev->type == SDL_KEYDOWN) {
switch (qcode) {
case Q_KEY_CODE_RET:
- kbd_put_keysym_console(con, '\n');
+ kbd_put_keysym_console(s, '\n');
break;
default:
- kbd_put_qcode_console(con, qcode, ctrl);
+ kbd_put_qcode_console(s, qcode, ctrl);
break;
}
}
diff --git a/ui/sdl2.c b/ui/sdl2.c
index ea4a92c..dfa260a 100644
--- a/ui/sdl2.c
+++ b/ui/sdl2.c
@@ -483,10 +483,9 @@
return;
}
- if (qemu_console_is_graphic(con)) {
- return;
+ if (QEMU_IS_TEXT_CONSOLE(con)) {
+ kbd_put_string_console(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text));
}
- kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
}
static void handle_mousemotion(SDL_Event *ev)
diff --git a/ui/spice-app.c b/ui/spice-app.c
index ad7f055..405fb7f 100644
--- a/ui/spice-app.c
+++ b/ui/spice-app.c
@@ -96,6 +96,11 @@
/* TODO: set echo for frontends QMP and qtest */
}
+static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
+{
+ /* fqdn is dealt with in vc_chr_open() */
+}
+
static void char_vc_class_init(ObjectClass *oc, void *data)
{
VCChardevClass *vc = CHARDEV_VC_CLASS(oc);
@@ -103,7 +108,7 @@
vc->parent_open = cc->open;
- cc->parse = qemu_chr_parse_vc;
+ cc->parse = vc_chr_parse;
cc->open = vc_chr_open;
cc->chr_set_echo = vc_chr_set_echo;
}
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 3f3f801..0e2fbfb 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -189,7 +189,7 @@
{
static const int blksize = 32;
int blocks = DIV_ROUND_UP(surface_width(ssd->ds), blksize);
- int dirty_top[blocks];
+ g_autofree int *dirty_top = NULL;
int y, yoff1, yoff2, x, xoff, blk, bw;
int bpp = surface_bytes_per_pixel(ssd->ds);
uint8_t *guest, *mirror;
@@ -198,6 +198,7 @@
return;
};
+ dirty_top = g_new(int, blocks);
for (blk = 0; blk < blocks; blk++) {
dirty_top[blk] = -1;
}
diff --git a/ui/ui-qmp-cmds.c b/ui/ui-qmp-cmds.c
index a37a702..debc07d 100644
--- a/ui/ui-qmp-cmds.c
+++ b/ui/ui-qmp-cmds.c
@@ -14,13 +14,20 @@
*/
#include "qemu/osdep.h"
+
+#include "io/channel-file.h"
#include "monitor/qmp-helpers.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/coroutine.h"
#include "qemu/cutils.h"
+#include "trace.h"
#include "ui/console.h"
#include "ui/dbus-display.h"
#include "ui/qemu-spice.h"
+#ifdef CONFIG_PNG
+#include <png.h>
+#endif
void qmp_set_password(SetPasswordOptions *opts, Error **errp)
{
@@ -204,3 +211,183 @@
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'");
}
+
+#ifdef CONFIG_PNG
+/**
+ * png_save: Take a screenshot as PNG
+ *
+ * Saves screendump as a PNG file
+ *
+ * Returns true for success or false for error.
+ *
+ * @fd: File descriptor for PNG file.
+ * @image: Image data in pixman format.
+ * @errp: Pointer to an error.
+ */
+static bool png_save(int fd, pixman_image_t *image, Error **errp)
+{
+ int width = pixman_image_get_width(image);
+ int height = pixman_image_get_height(image);
+ png_struct *png_ptr;
+ png_info *info_ptr;
+ g_autoptr(pixman_image_t) linebuf =
+ qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
+ uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf);
+ FILE *f = fdopen(fd, "wb");
+ int y;
+ if (!f) {
+ error_setg_errno(errp, errno,
+ "Failed to create file from file descriptor");
+ return false;
+ }
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
+ NULL, NULL);
+ if (!png_ptr) {
+ error_setg(errp, "PNG creation failed. Unable to write struct");
+ fclose(f);
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (!info_ptr) {
+ error_setg(errp, "PNG creation failed. Unable to write info");
+ fclose(f);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return false;
+ }
+
+ png_init_io(png_ptr, f);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8,
+ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ png_write_info(png_ptr, info_ptr);
+
+ for (y = 0; y < height; ++y) {
+ qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
+ png_write_row(png_ptr, buf);
+ }
+
+ png_write_end(png_ptr, NULL);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ if (fclose(f) != 0) {
+ error_setg_errno(errp, errno,
+ "PNG creation failed. Unable to close file");
+ return false;
+ }
+
+ return true;
+}
+
+#else /* no png support */
+
+static bool png_save(int fd, pixman_image_t *image, Error **errp)
+{
+ error_setg(errp, "Enable PNG support with libpng for screendump");
+ return false;
+}
+
+#endif /* CONFIG_PNG */
+
+static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
+{
+ int width = pixman_image_get_width(image);
+ int height = pixman_image_get_height(image);
+ g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd));
+ g_autofree char *header = NULL;
+ g_autoptr(pixman_image_t) linebuf = NULL;
+ int y;
+
+ trace_ppm_save(fd, image);
+
+ header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255);
+ if (qio_channel_write_all(QIO_CHANNEL(ioc),
+ header, strlen(header), errp) < 0) {
+ return false;
+ }
+
+ linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
+ for (y = 0; y < height; y++) {
+ qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
+ if (qio_channel_write_all(QIO_CHANNEL(ioc),
+ (char *)pixman_image_get_data(linebuf),
+ pixman_image_get_stride(linebuf), errp) < 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
+void coroutine_fn
+qmp_screendump(const char *filename, const char *device,
+ bool has_head, int64_t head,
+ bool has_format, ImageFormat format, Error **errp)
+{
+ g_autoptr(pixman_image_t) image = NULL;
+ QemuConsole *con;
+ DisplaySurface *surface;
+ int fd;
+
+ if (device) {
+ con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
+ errp);
+ if (!con) {
+ return;
+ }
+ } else {
+ if (has_head) {
+ error_setg(errp, "'head' must be specified together with 'device'");
+ return;
+ }
+ con = qemu_console_lookup_by_index(0);
+ if (!con) {
+ error_setg(errp, "There is no console to take a screendump from");
+ return;
+ }
+ }
+
+ qemu_console_co_wait_update(con);
+
+ /*
+ * All pending coroutines are woken up, while the BQL is held. No
+ * further graphic update are possible until it is released. Take
+ * an image ref before that.
+ */
+ surface = qemu_console_surface(con);
+ if (!surface) {
+ error_setg(errp, "no surface");
+ return;
+ }
+ image = pixman_image_ref(surface->image);
+
+ fd = qemu_open_old(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
+ if (fd == -1) {
+ error_setg(errp, "failed to open file '%s': %s", filename,
+ strerror(errno));
+ return;
+ }
+
+ /*
+ * The image content could potentially be updated as the coroutine
+ * yields and releases the BQL. It could produce corrupted dump, but
+ * it should be otherwise safe.
+ */
+ if (has_format && format == IMAGE_FORMAT_PNG) {
+ /* PNG format specified for screendump */
+ if (!png_save(fd, image, errp)) {
+ qemu_unlink(filename);
+ }
+ } else {
+ /* PPM format specified/default for screendump */
+ if (!ppm_save(fd, image, errp)) {
+ qemu_unlink(filename);
+ }
+ }
+}
diff --git a/ui/vdagent.c b/ui/vdagent.c
index 8a65149..00d36a8 100644
--- a/ui/vdagent.c
+++ b/ui/vdagent.c
@@ -870,8 +870,11 @@
static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
{
+ VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
+
if (!fe_open) {
trace_vdagent_close();
+ vdagent_disconnect(vd);
/* To reset_serial, we CLOSED our side. Make sure the other end knows we
* are ready again. */
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
@@ -923,6 +926,9 @@
migrate_del_blocker(vd->migration_blocker);
vdagent_disconnect(vd);
+ if (vd->mouse_hs) {
+ qemu_input_handler_unregister(vd->mouse_hs);
+ }
buffer_free(&vd->outbuf);
error_free(vd->migration_blocker);
}
diff --git a/ui/vnc-enc-hextile-template.h b/ui/vnc-enc-hextile-template.h
index 0c56262..8ee9208 100644
--- a/ui/vnc-enc-hextile-template.h
+++ b/ui/vnc-enc-hextile-template.h
@@ -7,6 +7,8 @@
#define NAME BPP
#endif
+#define MAX_BYTES_PER_PIXEL 4
+
static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
int x, int y, int w, int h,
void *last_bg_,
@@ -25,10 +27,13 @@
int bg_count = 0;
int fg_count = 0;
int flags = 0;
- uint8_t data[(vs->client_pf.bytes_per_pixel + 2) * 16 * 16];
+ uint8_t data[(MAX_BYTES_PER_PIXEL + 2) * 16 * 16];
int n_data = 0;
int n_subtiles = 0;
+ /* Enforced by set_pixel_format() */
+ assert(vs->client_pf.bytes_per_pixel <= MAX_BYTES_PER_PIXEL);
+
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
switch (n_colors) {
@@ -205,6 +210,7 @@
}
}
+#undef MAX_BYTES_PER_PIXEL
#undef NAME
#undef pixel_t
#undef CONCAT_I
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
index ee853dc..41f559e 100644
--- a/ui/vnc-enc-tight.c
+++ b/ui/vnc-enc-tight.c
@@ -1097,13 +1097,13 @@
switch (vs->client_pf.bytes_per_pixel) {
case 4:
{
- size_t old_offset, offset;
- uint32_t header[palette_size(palette)];
+ size_t old_offset, offset, palette_sz = palette_size(palette);
+ g_autofree uint32_t *header = g_new(uint32_t, palette_sz);
struct palette_cb_priv priv = { vs, (uint8_t *)header };
old_offset = vs->output.offset;
palette_iter(palette, write_palette, &priv);
- vnc_write(vs, header, sizeof(header));
+ vnc_write(vs, header, palette_sz * sizeof(uint32_t));
if (vs->tight->pixel24) {
tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
@@ -1115,11 +1115,12 @@
}
case 2:
{
- uint16_t header[palette_size(palette)];
+ size_t palette_sz = palette_size(palette);
+ g_autofree uint16_t *header = g_new(uint16_t, palette_sz);
struct palette_cb_priv priv = { vs, (uint8_t *)header };
palette_iter(palette, write_palette, &priv);
- vnc_write(vs, header, sizeof(header));
+ vnc_write(vs, header, palette_sz * sizeof(uint16_t));
tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette);
break;
}
diff --git a/util/selfmap.c b/util/selfmap.c
index 4db5b42..483cb61 100644
--- a/util/selfmap.c
+++ b/util/selfmap.c
@@ -30,19 +30,21 @@
if (nfields > 4) {
uint64_t start, end, offset, inode;
+ unsigned dev_maj, dev_min;
int errors = 0;
const char *p;
errors |= qemu_strtou64(fields[0], &p, 16, &start);
errors |= qemu_strtou64(p + 1, NULL, 16, &end);
errors |= qemu_strtou64(fields[2], NULL, 16, &offset);
+ errors |= qemu_strtoui(fields[3], &p, 16, &dev_maj);
+ errors |= qemu_strtoui(p + 1, NULL, 16, &dev_min);
errors |= qemu_strtou64(fields[4], NULL, 10, &inode);
if (!errors) {
- size_t dev_len, path_len;
+ size_t path_len;
MapInfo *e;
- dev_len = strlen(fields[3]) + 1;
if (nfields == 6) {
p = fields[5];
p += strspn(p, " ");
@@ -52,11 +54,12 @@
path_len = 0;
}
- e = g_malloc0(sizeof(*e) + dev_len + path_len);
+ e = g_malloc0(sizeof(*e) + path_len);
e->itree.start = start;
e->itree.last = end - 1;
e->offset = offset;
+ e->dev = makedev(dev_maj, dev_min);
e->inode = inode;
e->is_read = fields[1][0] == 'r';
@@ -64,9 +67,8 @@
e->is_exec = fields[1][2] == 'x';
e->is_priv = fields[1][3] == 'p';
- memcpy(e->dev, fields[3], dev_len);
if (path_len) {
- e->path = memcpy(e->dev + dev_len, p, path_len);
+ e->path = memcpy(e + 1, p, path_len);
}
interval_tree_insert(&e->itree, root);