summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS15
-rw-r--r--backends/cryptodev-builtin.c69
-rw-r--r--backends/cryptodev-lkcf.c645
-rw-r--r--backends/cryptodev-vhost-user.c51
-rw-r--r--backends/cryptodev.c44
-rw-r--r--backends/meson.build3
-rw-r--r--crypto/akcipher.c18
-rw-r--r--crypto/der.c307
-rw-r--r--crypto/der.h211
-rw-r--r--crypto/rsakey.c42
-rw-r--r--crypto/rsakey.h11
-rw-r--r--docs/devel/acpi-bits.rst145
-rw-r--r--docs/devel/index-build.rst1
-rw-r--r--hw/acpi/aml-build-stub.c10
-rw-r--r--hw/acpi/aml-build.c13
-rw-r--r--hw/acpi/erst.c6
-rw-r--r--hw/acpi/nvdimm.c106
-rw-r--r--hw/arm/Kconfig1
-rw-r--r--hw/arm/virt-acpi-build.c33
-rw-r--r--hw/block/vhost-user-blk.c24
-rw-r--r--hw/core/machine.c8
-rw-r--r--hw/cxl/cxl-cdat.c224
-rw-r--r--hw/cxl/meson.build1
-rw-r--r--hw/display/acpi-vga-stub.c7
-rw-r--r--hw/display/acpi-vga.c26
-rw-r--r--hw/display/meson.build17
-rw-r--r--hw/display/vga-pci.c4
-rw-r--r--hw/display/vga_int.h2
-rw-r--r--hw/i386/acpi-build.c203
-rw-r--r--hw/i386/e820_memory_layout.c20
-rw-r--r--hw/i386/e820_memory_layout.h8
-rw-r--r--hw/i386/fw_cfg.c3
-rw-r--r--hw/i386/fw_cfg.h1
-rw-r--r--hw/i386/intel_iommu.c694
-rw-r--r--hw/i386/intel_iommu_internal.h16
-rw-r--r--hw/i386/microvm.c2
-rw-r--r--hw/i386/pc.c2
-rw-r--r--hw/i386/trace-events2
-rw-r--r--hw/isa/lpc_ich9.c23
-rw-r--r--hw/isa/piix3.c17
-rw-r--r--hw/mem/cxl_type3.c264
-rw-r--r--hw/net/e1000e.c15
-rw-r--r--hw/net/rocker/rocker.c23
-rw-r--r--hw/net/vhost_net-stub.c12
-rw-r--r--hw/net/vhost_net.c90
-rw-r--r--hw/net/virtio-net.c57
-rw-r--r--hw/net/vmxnet3.c27
-rw-r--r--hw/nvme/ctrl.c5
-rw-r--r--hw/pci-bridge/cxl_upstream.c195
-rw-r--r--hw/pci/meson.build1
-rw-r--r--hw/pci/msix.c24
-rw-r--r--hw/pci/pcie_doe.c367
-rw-r--r--hw/rdma/vmw/pvrdma_main.c7
-rw-r--r--hw/remote/vfio-user-obj.c9
-rw-r--r--hw/smbios/smbios.c19
-rw-r--r--hw/smbios/smbios_build.h9
-rw-r--r--hw/vfio/common.c66
-rw-r--r--hw/virtio/vhost-user-fs.c2
-rw-r--r--hw/virtio/vhost-user-gpio.c2
-rw-r--r--hw/virtio/vhost-user-i2c.c2
-rw-r--r--hw/virtio/vhost-user-rng.c2
-rw-r--r--hw/virtio/vhost-user-vsock.c2
-rw-r--r--hw/virtio/vhost-user.c79
-rw-r--r--hw/virtio/vhost-vsock.c2
-rw-r--r--hw/virtio/vhost.c16
-rw-r--r--hw/virtio/virtio-crypto.c337
-rw-r--r--hw/virtio/virtio-iommu-pci.c12
-rw-r--r--hw/virtio/virtio-pci.c83
-rw-r--r--hw/virtio/virtio-rng-pci.c14
-rw-r--r--hw/virtio/virtio.c62
-rw-r--r--include/crypto/akcipher.h21
-rw-r--r--include/exec/memory.h4
-rw-r--r--include/hw/acpi/acpi_aml_interface.h13
-rw-r--r--include/hw/cxl/cxl_cdat.h166
-rw-r--r--include/hw/cxl/cxl_component.h7
-rw-r--r--include/hw/cxl/cxl_device.h3
-rw-r--r--include/hw/cxl/cxl_pci.h1
-rw-r--r--include/hw/firmware/smbios.h12
-rw-r--r--include/hw/i386/intel_iommu.h18
-rw-r--r--include/hw/pci/msix.h4
-rw-r--r--include/hw/pci/pci_bus.h2
-rw-r--r--include/hw/pci/pci_ids.h3
-rw-r--r--include/hw/pci/pcie.h1
-rw-r--r--include/hw/pci/pcie_doe.h123
-rw-r--r--include/hw/pci/pcie_regs.h4
-rw-r--r--include/hw/virtio/vhost.h5
-rw-r--r--include/hw/virtio/virtio-pci.h5
-rw-r--r--include/hw/virtio/virtio.h26
-rw-r--r--include/net/vhost_net.h4
-rw-r--r--include/sysemu/cryptodev.h61
-rw-r--r--qapi/qom.json2
-rwxr-xr-xscripts/checkpatch.pl6
-rw-r--r--softmmu/memory.c72
-rw-r--r--tests/avocado/acpi-bits.py396
-rw-r--r--tests/avocado/acpi-bits/bits-config/bits-cfg.txt18
-rw-r--r--tests/avocado/acpi-bits/bits-tests/smbios.py22430
-rw-r--r--tests/avocado/acpi-bits/bits-tests/testacpi.py2283
-rw-r--r--tests/avocado/acpi-bits/bits-tests/testcpuid.py283
-rw-r--r--tests/data/acpi/pc/DSDTbin6422 -> 6501 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.acpierstbin6382 -> 6461 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.acpihmatbin7747 -> 7826 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.bridgebin9496 -> 9575 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.cphpbin6886 -> 6965 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.dimmpxmbin8076 -> 8155 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.hpbridgebin6382 -> 6461 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.hpbrrootbin3069 -> 3107 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.ipmikcsbin6494 -> 6573 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.memhpbin7781 -> 7860 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.nohpetbin6280 -> 6359 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.numamembin6428 -> 6507 bytes
-rw-r--r--tests/data/acpi/pc/DSDT.roothpbin6656 -> 6699 bytes
-rw-r--r--tests/data/acpi/pc/SSDT.dimmpxmbin734 -> 1815 bytes
-rw-r--r--tests/data/acpi/q35/APIC.acpihmat-noinitiatorbin0 -> 144 bytes
-rw-r--r--tests/data/acpi/q35/APIC.core-count2bin0 -> 2478 bytes
-rw-r--r--tests/data/acpi/q35/DSDTbin8320 -> 8412 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.acpierstbin8337 -> 8429 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.acpihmatbin9645 -> 9737 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.acpihmat-noinitiatorbin0 -> 8691 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.applesmcbin8366 -> 8458 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.bridgebin11449 -> 11541 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.core-count2bin0 -> 32552 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.cphpbin8784 -> 8876 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.cxlbin9646 -> 9738 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.dimmpxmbin9974 -> 10066 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.ipmibtbin8395 -> 8487 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.ipmismbusbin8409 -> 8500 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.ivrsbin8337 -> 8429 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.memhpbin9679 -> 9771 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.mmio64bin9450 -> 9542 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.multi-bridgebin8640 -> 8732 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.nohpetbin8178 -> 8270 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.numamembin8326 -> 8418 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.pvpanic-isabin8421 -> 8513 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.tis.tpm12bin8926 -> 9018 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.tis.tpm2bin8952 -> 9044 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.viotbin9429 -> 9521 bytes
-rw-r--r--tests/data/acpi/q35/DSDT.xapicbin35683 -> 35775 bytes
-rw-r--r--tests/data/acpi/q35/FACP.core-count2bin0 -> 244 bytes
-rw-r--r--tests/data/acpi/q35/HMAT.acpihmat-noinitiatorbin0 -> 288 bytes
-rw-r--r--tests/data/acpi/q35/SRAT.acpihmat-noinitiatorbin0 -> 312 bytes
-rw-r--r--tests/data/acpi/q35/SSDT.dimmpxmbin734 -> 1815 bytes
-rw-r--r--tests/data/acpi/virt/APICbin168 -> 172 bytes
-rw-r--r--tests/data/acpi/virt/APIC.acpihmatvirtbin0 -> 412 bytes
-rw-r--r--tests/data/acpi/virt/APIC.memhpbin168 -> 172 bytes
-rw-r--r--tests/data/acpi/virt/APIC.numamembin168 -> 172 bytes
-rw-r--r--tests/data/acpi/virt/DSDT.acpihmatvirtbin0 -> 5282 bytes
-rw-r--r--tests/data/acpi/virt/FACPbin268 -> 276 bytes
-rw-r--r--tests/data/acpi/virt/FACP.memhpbin268 -> 276 bytes
-rw-r--r--tests/data/acpi/virt/FACP.numamembin268 -> 276 bytes
-rw-r--r--tests/data/acpi/virt/HMAT.acpihmatvirtbin0 -> 288 bytes
-rw-r--r--tests/data/acpi/virt/PPTT.acpihmatvirtbin0 -> 196 bytes
-rw-r--r--tests/data/acpi/virt/SRAT.acpihmatvirtbin0 -> 240 bytes
-rw-r--r--tests/data/acpi/virt/SSDT.memhpbin736 -> 1817 bytes
-rw-r--r--tests/qtest/bios-tables-test.c267
-rw-r--r--tests/unit/test-crypto-der.c126
155 files changed, 7991 insertions, 1015 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 4adf8c65db..28cc70c25f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1835,6 +1835,13 @@ F: qapi/pci.json
F: docs/pci*
F: docs/specs/*pci*
+PCIE DOE
+M: Huai-Cheng Kuo <hchkuo@avery-design.com.tw>
+M: Chris Browy <cbrowy@avery-design.com>
+S: Supported
+F: include/hw/pci/pcie_doe.h
+F: hw/pci/pcie_doe.c
+
ACPI/SMBIOS
M: Michael S. Tsirkin <mst@redhat.com>
M: Igor Mammedov <imammedo@redhat.com>
@@ -1862,6 +1869,13 @@ S: Supported
F: hw/acpi/viot.c
F: hw/acpi/viot.h
+ACPI/AVOCADO/BIOSBITS
+M: Ani Sinha <ani@anisinha.ca>
+S: Supported
+F: tests/avocado/acpi-bits/*
+F: tests/avocado/acpi-bits.py
+F: docs/devel/acpi-bits.rst
+
ACPI/HEST/GHES
R: Dongjiu Geng <gengdongjiu1@gmail.com>
L: qemu-arm@nongnu.org
@@ -2003,6 +2017,7 @@ S: Supported
F: hw/*/virtio*
F: hw/virtio/Makefile.objs
F: hw/virtio/trace-events
+F: qapi/virtio.json
F: net/vhost-user.c
F: include/hw/virtio/
diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c
index 125cbad1d3..cda6ca3b71 100644
--- a/backends/cryptodev-builtin.c
+++ b/backends/cryptodev-builtin.c
@@ -355,42 +355,62 @@ static int cryptodev_builtin_create_akcipher_session(
return index;
}
-static int64_t cryptodev_builtin_create_session(
+static int cryptodev_builtin_create_session(
CryptoDevBackend *backend,
CryptoDevBackendSessionInfo *sess_info,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendBuiltin *builtin =
CRYPTODEV_BACKEND_BUILTIN(backend);
CryptoDevBackendSymSessionInfo *sym_sess_info;
CryptoDevBackendAsymSessionInfo *asym_sess_info;
+ int ret, status;
+ Error *local_error = NULL;
switch (sess_info->op_code) {
case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
sym_sess_info = &sess_info->u.sym_sess_info;
- return cryptodev_builtin_create_cipher_session(
- builtin, sym_sess_info, errp);
+ ret = cryptodev_builtin_create_cipher_session(
+ builtin, sym_sess_info, &local_error);
+ break;
case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
asym_sess_info = &sess_info->u.asym_sess_info;
- return cryptodev_builtin_create_akcipher_session(
- builtin, asym_sess_info, errp);
+ ret = cryptodev_builtin_create_akcipher_session(
+ builtin, asym_sess_info, &local_error);
+ break;
case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
default:
- error_setg(errp, "Unsupported opcode :%" PRIu32 "",
+ error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
sess_info->op_code);
- return -1;
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
- return -1;
+ if (local_error) {
+ error_report_err(local_error);
+ }
+ if (ret < 0) {
+ status = -VIRTIO_CRYPTO_ERR;
+ } else {
+ sess_info->session_id = ret;
+ status = VIRTIO_CRYPTO_OK;
+ }
+ if (cb) {
+ cb(opaque, status);
+ }
+ return 0;
}
static int cryptodev_builtin_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendBuiltin *builtin =
CRYPTODEV_BACKEND_BUILTIN(backend);
@@ -407,6 +427,9 @@ static int cryptodev_builtin_close_session(
g_free(session);
builtin->sessions[session_id] = NULL;
+ if (cb) {
+ cb(opaque, VIRTIO_CRYPTO_OK);
+ }
return 0;
}
@@ -506,7 +529,9 @@ static int cryptodev_builtin_asym_operation(
static int cryptodev_builtin_operation(
CryptoDevBackend *backend,
CryptoDevBackendOpInfo *op_info,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendBuiltin *builtin =
CRYPTODEV_BACKEND_BUILTIN(backend);
@@ -514,11 +539,12 @@ static int cryptodev_builtin_operation(
CryptoDevBackendSymOpInfo *sym_op_info;
CryptoDevBackendAsymOpInfo *asym_op_info;
enum CryptoDevBackendAlgType algtype = op_info->algtype;
- int ret = -VIRTIO_CRYPTO_ERR;
+ int status = -VIRTIO_CRYPTO_ERR;
+ Error *local_error = NULL;
if (op_info->session_id >= MAX_NUM_SESSIONS ||
builtin->sessions[op_info->session_id] == NULL) {
- error_setg(errp, "Cannot find a valid session id: %" PRIu64 "",
+ error_setg(&local_error, "Cannot find a valid session id: %" PRIu64 "",
op_info->session_id);
return -VIRTIO_CRYPTO_INVSESS;
}
@@ -526,14 +552,21 @@ static int cryptodev_builtin_operation(
sess = builtin->sessions[op_info->session_id];
if (algtype == CRYPTODEV_BACKEND_ALG_SYM) {
sym_op_info = op_info->u.sym_op_info;
- ret = cryptodev_builtin_sym_operation(sess, sym_op_info, errp);
+ status = cryptodev_builtin_sym_operation(sess, sym_op_info,
+ &local_error);
} else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) {
asym_op_info = op_info->u.asym_op_info;
- ret = cryptodev_builtin_asym_operation(sess, op_info->op_code,
- asym_op_info, errp);
+ status = cryptodev_builtin_asym_operation(sess, op_info->op_code,
+ asym_op_info, &local_error);
}
- return ret;
+ if (local_error) {
+ error_report_err(local_error);
+ }
+ if (cb) {
+ cb(opaque, status);
+ }
+ return 0;
}
static void cryptodev_builtin_cleanup(
@@ -548,7 +581,7 @@ static void cryptodev_builtin_cleanup(
for (i = 0; i < MAX_NUM_SESSIONS; i++) {
if (builtin->sessions[i] != NULL) {
- cryptodev_builtin_close_session(backend, i, 0, &error_abort);
+ cryptodev_builtin_close_session(backend, i, 0, NULL, NULL);
}
}
diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c
new file mode 100644
index 0000000000..133bd706a4
--- /dev/null
+++ b/backends/cryptodev-lkcf.c
@@ -0,0 +1,645 @@
+/*
+ * QEMU Cryptodev backend for QEMU cipher APIs
+ *
+ * Copyright (c) 2022 Bytedance.Inc
+ *
+ * Authors:
+ * lei he <helei.sig11@bytedance.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "crypto/cipher.h"
+#include "crypto/akcipher.h"
+#include "qapi/error.h"
+#include "qemu/main-loop.h"
+#include "qemu/thread.h"
+#include "qemu/error-report.h"
+#include "qemu/queue.h"
+#include "qom/object.h"
+#include "sysemu/cryptodev.h"
+#include "standard-headers/linux/virtio_crypto.h"
+
+#include <keyutils.h>
+#include <sys/eventfd.h>
+
+/**
+ * @TYPE_CRYPTODEV_BACKEND_LKCF:
+ * name of backend that uses linux kernel crypto framework
+ */
+#define TYPE_CRYPTODEV_BACKEND_LKCF "cryptodev-backend-lkcf"
+
+OBJECT_DECLARE_SIMPLE_TYPE(CryptoDevBackendLKCF, CRYPTODEV_BACKEND_LKCF)
+
+#define INVALID_KEY_ID -1
+#define MAX_SESSIONS 256
+#define NR_WORKER_THREAD 64
+
+#define KCTL_KEY_TYPE_PKEY "asymmetric"
+/**
+ * Here the key is uploaded to the thread-keyring of worker thread, at least
+ * util linux-6.0:
+ * 1. process keyring seems to behave unexpectedly if main-thread does not
+ * create the keyring before creating any other thread.
+ * 2. at present, the guest kernel never perform multiple operations on a
+ * session.
+ * 3. it can reduce the load of the main-loop because the key passed by the
+ * guest kernel has been already checked.
+ */
+#define KCTL_KEY_RING KEY_SPEC_THREAD_KEYRING
+
+typedef struct CryptoDevBackendLKCFSession {
+ uint8_t *key;
+ size_t keylen;
+ QCryptoAkCipherKeyType keytype;
+ QCryptoAkCipherOptions akcipher_opts;
+} CryptoDevBackendLKCFSession;
+
+typedef struct CryptoDevBackendLKCF CryptoDevBackendLKCF;
+typedef struct CryptoDevLKCFTask CryptoDevLKCFTask;
+struct CryptoDevLKCFTask {
+ CryptoDevBackendLKCFSession *sess;
+ CryptoDevBackendOpInfo *op_info;
+ CryptoDevCompletionFunc cb;
+ void *opaque;
+ int status;
+ CryptoDevBackendLKCF *lkcf;
+ QSIMPLEQ_ENTRY(CryptoDevLKCFTask) queue;
+};
+
+typedef struct CryptoDevBackendLKCF {
+ CryptoDevBackend parent_obj;
+ CryptoDevBackendLKCFSession *sess[MAX_SESSIONS];
+ QSIMPLEQ_HEAD(, CryptoDevLKCFTask) requests;
+ QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses;
+ QemuMutex mutex;
+ QemuCond cond;
+ QemuMutex rsp_mutex;
+
+ /**
+ * There is no async interface for asymmetric keys like AF_ALG sockets,
+ * we don't seem to have better way than create a lots of thread.
+ */
+ QemuThread worker_threads[NR_WORKER_THREAD];
+ bool running;
+ int eventfd;
+} CryptoDevBackendLKCF;
+
+static void *cryptodev_lkcf_worker(void *arg);
+static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
+ uint64_t session_id,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque);
+
+static void cryptodev_lkcf_handle_response(void *opaque)
+{
+ CryptoDevBackendLKCF *lkcf = (CryptoDevBackendLKCF *)opaque;
+ QSIMPLEQ_HEAD(, CryptoDevLKCFTask) responses;
+ CryptoDevLKCFTask *task, *next;
+ eventfd_t nevent;
+
+ QSIMPLEQ_INIT(&responses);
+ eventfd_read(lkcf->eventfd, &nevent);
+
+ qemu_mutex_lock(&lkcf->rsp_mutex);
+ QSIMPLEQ_PREPEND(&responses, &lkcf->responses);
+ qemu_mutex_unlock(&lkcf->rsp_mutex);
+
+ QSIMPLEQ_FOREACH_SAFE(task, &responses, queue, next) {
+ if (task->cb) {
+ task->cb(task->opaque, task->status);
+ }
+ g_free(task);
+ }
+}
+
+static int cryptodev_lkcf_set_op_desc(QCryptoAkCipherOptions *opts,
+ char *key_desc,
+ size_t desc_len,
+ Error **errp)
+{
+ QCryptoAkCipherOptionsRSA *rsa_opt;
+ if (opts->alg != QCRYPTO_AKCIPHER_ALG_RSA) {
+ error_setg(errp, "Unsupported alg: %u", opts->alg);
+ return -1;
+ }
+
+ rsa_opt = &opts->u.rsa;
+ if (rsa_opt->padding_alg == QCRYPTO_RSA_PADDING_ALG_PKCS1) {
+ snprintf(key_desc, desc_len, "enc=%s hash=%s",
+ QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg),
+ QCryptoHashAlgorithm_str(rsa_opt->hash_alg));
+
+ } else {
+ snprintf(key_desc, desc_len, "enc=%s",
+ QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg));
+ }
+ return 0;
+}
+
+static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg,
+ int virtio_hash_alg,
+ QCryptoAkCipherOptionsRSA *opt,
+ Error **errp)
+{
+ if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) {
+ opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1;
+
+ switch (virtio_hash_alg) {
+ case VIRTIO_CRYPTO_RSA_MD5:
+ opt->hash_alg = QCRYPTO_HASH_ALG_MD5;
+ break;
+
+ case VIRTIO_CRYPTO_RSA_SHA1:
+ opt->hash_alg = QCRYPTO_HASH_ALG_SHA1;
+ break;
+
+ case VIRTIO_CRYPTO_RSA_SHA256:
+ opt->hash_alg = QCRYPTO_HASH_ALG_SHA256;
+ break;
+
+ case VIRTIO_CRYPTO_RSA_SHA512:
+ opt->hash_alg = QCRYPTO_HASH_ALG_SHA512;
+ break;
+
+ default:
+ error_setg(errp, "Unsupported rsa hash algo: %d", virtio_hash_alg);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_RAW_PADDING) {
+ opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW;
+ return 0;
+ }
+
+ error_setg(errp, "Unsupported rsa padding algo: %u", virtio_padding_alg);
+ return -1;
+}
+
+static int cryptodev_lkcf_get_unused_session_index(CryptoDevBackendLKCF *lkcf)
+{
+ size_t i;
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ if (lkcf->sess[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void cryptodev_lkcf_init(CryptoDevBackend *backend, Error **errp)
+{
+ /* Only support one queue */
+ int queues = backend->conf.peers.queues, i;
+ CryptoDevBackendClient *cc;
+ CryptoDevBackendLKCF *lkcf =
+ CRYPTODEV_BACKEND_LKCF(backend);
+
+ if (queues != 1) {
+ error_setg(errp,
+ "Only support one queue in cryptodev-builtin backend");
+ return;
+ }
+ lkcf->eventfd = eventfd(0, 0);
+ if (lkcf->eventfd < 0) {
+ error_setg(errp, "Failed to create eventfd: %d", errno);
+ return;
+ }
+
+ cc = cryptodev_backend_new_client("cryptodev-lkcf", NULL);
+ cc->info_str = g_strdup_printf("cryptodev-lkcf0");
+ cc->queue_index = 0;
+ cc->type = CRYPTODEV_BACKEND_TYPE_LKCF;
+ backend->conf.peers.ccs[0] = cc;
+
+ backend->conf.crypto_services =
+ 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER;
+ backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA;
+ lkcf->running = true;
+
+ QSIMPLEQ_INIT(&lkcf->requests);
+ QSIMPLEQ_INIT(&lkcf->responses);
+ qemu_mutex_init(&lkcf->mutex);
+ qemu_mutex_init(&lkcf->rsp_mutex);
+ qemu_cond_init(&lkcf->cond);
+ for (i = 0; i < NR_WORKER_THREAD; i++) {
+ qemu_thread_create(&lkcf->worker_threads[i], "lkcf-worker",
+ cryptodev_lkcf_worker, lkcf, 0);
+ }
+ qemu_set_fd_handler(
+ lkcf->eventfd, cryptodev_lkcf_handle_response, NULL, lkcf);
+ cryptodev_backend_set_ready(backend, true);
+}
+
+static void cryptodev_lkcf_cleanup(CryptoDevBackend *backend, Error **errp)
+{
+ CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend);
+ size_t i;
+ int queues = backend->conf.peers.queues;
+ CryptoDevBackendClient *cc;
+ CryptoDevLKCFTask *task, *next;
+
+ qemu_mutex_lock(&lkcf->mutex);
+ lkcf->running = false;
+ qemu_mutex_unlock(&lkcf->mutex);
+ qemu_cond_broadcast(&lkcf->cond);
+
+ close(lkcf->eventfd);
+ for (i = 0; i < NR_WORKER_THREAD; i++) {
+ qemu_thread_join(&lkcf->worker_threads[i]);
+ }
+
+ QSIMPLEQ_FOREACH_SAFE(task, &lkcf->requests, queue, next) {
+ if (task->cb) {
+ task->cb(task->opaque, task->status);
+ }
+ g_free(task);
+ }
+
+ QSIMPLEQ_FOREACH_SAFE(task, &lkcf->responses, queue, next) {
+ if (task->cb) {
+ task->cb(task->opaque, task->status);
+ }
+ g_free(task);
+ }
+
+ qemu_mutex_destroy(&lkcf->mutex);
+ qemu_cond_destroy(&lkcf->cond);
+ qemu_mutex_destroy(&lkcf->rsp_mutex);
+
+ for (i = 0; i < MAX_SESSIONS; i++) {
+ if (lkcf->sess[i] != NULL) {
+ cryptodev_lkcf_close_session(backend, i, 0, NULL, NULL);
+ }
+ }
+
+ for (i = 0; i < queues; i++) {
+ cc = backend->conf.peers.ccs[i];
+ if (cc) {
+ cryptodev_backend_free_client(cc);
+ backend->conf.peers.ccs[i] = NULL;
+ }
+ }
+
+ cryptodev_backend_set_ready(backend, false);
+}
+
+static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task)
+{
+ CryptoDevBackendLKCFSession *session = task->sess;
+ CryptoDevBackendAsymOpInfo *asym_op_info;
+ bool kick = false;
+ int ret, status, op_code = task->op_info->op_code;
+ size_t p8info_len;
+ g_autofree uint8_t *p8info = NULL;
+ Error *local_error = NULL;
+ key_serial_t key_id = INVALID_KEY_ID;
+ char op_desc[64];
+ g_autoptr(QCryptoAkCipher) akcipher = NULL;
+
+ /**
+ * We only offload private key session:
+ * 1. currently, the Linux kernel can only accept public key wrapped
+ * with X.509 certificates, but unfortunately the cost of making a
+ * ceritificate with public key is too expensive.
+ * 2. generally, public key related compution is fast, just compute it with
+ * thread-pool.
+ */
+ if (session->keytype == QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE) {
+ if (qcrypto_akcipher_export_p8info(&session->akcipher_opts,
+ session->key, session->keylen,
+ &p8info, &p8info_len,
+ &local_error) != 0 ||
+ cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc,
+ sizeof(op_desc), &local_error) != 0) {
+ error_report_err(local_error);
+ } else {
+ key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key",
+ p8info, p8info_len, KCTL_KEY_RING);
+ }
+ }
+
+ if (key_id < 0) {
+ if (!qcrypto_akcipher_supports(&session->akcipher_opts)) {
+ status = -VIRTIO_CRYPTO_NOTSUPP;
+ goto out;
+ }
+ akcipher = qcrypto_akcipher_new(&session->akcipher_opts,
+ session->keytype,
+ session->key, session->keylen,
+ &local_error);
+ if (!akcipher) {
+ status = -VIRTIO_CRYPTO_ERR;
+ goto out;
+ }
+ }
+
+ asym_op_info = task->op_info->u.asym_op_info;
+ switch (op_code) {
+ case VIRTIO_CRYPTO_AKCIPHER_ENCRYPT:
+ if (key_id >= 0) {
+ ret = keyctl_pkey_encrypt(key_id, op_desc,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len);
+ } else {
+ ret = qcrypto_akcipher_encrypt(akcipher,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len, &local_error);
+ }
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_DECRYPT:
+ if (key_id >= 0) {
+ ret = keyctl_pkey_decrypt(key_id, op_desc,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len);
+ } else {
+ ret = qcrypto_akcipher_decrypt(akcipher,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len, &local_error);
+ }
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_SIGN:
+ if (key_id >= 0) {
+ ret = keyctl_pkey_sign(key_id, op_desc,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len);
+ } else {
+ ret = qcrypto_akcipher_sign(akcipher,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len, &local_error);
+ }
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_VERIFY:
+ if (key_id >= 0) {
+ ret = keyctl_pkey_verify(key_id, op_desc,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len);
+ } else {
+ ret = qcrypto_akcipher_verify(akcipher,
+ asym_op_info->src, asym_op_info->src_len,
+ asym_op_info->dst, asym_op_info->dst_len, &local_error);
+ }
+ break;
+
+ default:
+ error_setg(&local_error, "Unknown opcode: %u", op_code);
+ status = -VIRTIO_CRYPTO_ERR;
+ goto out;
+ }
+
+ if (ret < 0) {
+ if (!local_error) {
+ if (errno != EKEYREJECTED) {
+ error_report("Failed do operation with keyctl: %d", errno);
+ }
+ } else {
+ error_report_err(local_error);
+ }
+ status = op_code == VIRTIO_CRYPTO_AKCIPHER_VERIFY ?
+ -VIRTIO_CRYPTO_KEY_REJECTED : -VIRTIO_CRYPTO_ERR;
+ } else {
+ status = VIRTIO_CRYPTO_OK;
+ asym_op_info->dst_len = ret;
+ }
+
+out:
+ if (key_id >= 0) {
+ keyctl_unlink(key_id, KCTL_KEY_RING);
+ }
+ task->status = status;
+
+ qemu_mutex_lock(&task->lkcf->rsp_mutex);
+ if (QSIMPLEQ_EMPTY(&task->lkcf->responses)) {
+ kick = true;
+ }
+ QSIMPLEQ_INSERT_TAIL(&task->lkcf->responses, task, queue);
+ qemu_mutex_unlock(&task->lkcf->rsp_mutex);
+
+ if (kick) {
+ eventfd_write(task->lkcf->eventfd, 1);
+ }
+}
+
+static void *cryptodev_lkcf_worker(void *arg)
+{
+ CryptoDevBackendLKCF *backend = (CryptoDevBackendLKCF *)arg;
+ CryptoDevLKCFTask *task;
+
+ for (;;) {
+ task = NULL;
+ qemu_mutex_lock(&backend->mutex);
+ while (backend->running && QSIMPLEQ_EMPTY(&backend->requests)) {
+ qemu_cond_wait(&backend->cond, &backend->mutex);
+ }
+ if (backend->running) {
+ task = QSIMPLEQ_FIRST(&backend->requests);
+ QSIMPLEQ_REMOVE_HEAD(&backend->requests, queue);
+ }
+ qemu_mutex_unlock(&backend->mutex);
+
+ /* stopped */
+ if (!task) {
+ break;
+ }
+ cryptodev_lkcf_execute_task(task);
+ }
+
+ return NULL;
+}
+
+static int cryptodev_lkcf_operation(
+ CryptoDevBackend *backend,
+ CryptoDevBackendOpInfo *op_info,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
+{
+ CryptoDevBackendLKCF *lkcf =
+ CRYPTODEV_BACKEND_LKCF(backend);
+ CryptoDevBackendLKCFSession *sess;
+ enum CryptoDevBackendAlgType algtype = op_info->algtype;
+ CryptoDevLKCFTask *task;
+
+ if (op_info->session_id >= MAX_SESSIONS ||
+ lkcf->sess[op_info->session_id] == NULL) {
+ error_report("Cannot find a valid session id: %" PRIu64 "",
+ op_info->session_id);
+ return -VIRTIO_CRYPTO_INVSESS;
+ }
+
+ sess = lkcf->sess[op_info->session_id];
+ if (algtype != CRYPTODEV_BACKEND_ALG_ASYM) {
+ error_report("algtype not supported: %u", algtype);
+ return -VIRTIO_CRYPTO_NOTSUPP;
+ }
+
+ task = g_new0(CryptoDevLKCFTask, 1);
+ task->op_info = op_info;
+ task->cb = cb;
+ task->opaque = opaque;
+ task->sess = sess;
+ task->lkcf = lkcf;
+ task->status = -VIRTIO_CRYPTO_ERR;
+
+ qemu_mutex_lock(&lkcf->mutex);
+ QSIMPLEQ_INSERT_TAIL(&lkcf->requests, task, queue);
+ qemu_mutex_unlock(&lkcf->mutex);
+ qemu_cond_signal(&lkcf->cond);
+
+ return VIRTIO_CRYPTO_OK;
+}
+
+static int cryptodev_lkcf_create_asym_session(
+ CryptoDevBackendLKCF *lkcf,
+ CryptoDevBackendAsymSessionInfo *sess_info,
+ uint64_t *session_id)
+{
+ Error *local_error = NULL;
+ int index;
+ g_autofree CryptoDevBackendLKCFSession *sess =
+ g_new0(CryptoDevBackendLKCFSession, 1);
+
+ switch (sess_info->algo) {
+ case VIRTIO_CRYPTO_AKCIPHER_RSA:
+ sess->akcipher_opts.alg = QCRYPTO_AKCIPHER_ALG_RSA;
+ if (cryptodev_lkcf_set_rsa_opt(
+ sess_info->u.rsa.padding_algo, sess_info->u.rsa.hash_algo,
+ &sess->akcipher_opts.u.rsa, &local_error) != 0) {
+ error_report_err(local_error);
+ return -VIRTIO_CRYPTO_ERR;
+ }
+ break;
+
+ default:
+ error_report("Unsupported asym alg %u", sess_info->algo);
+ return -VIRTIO_CRYPTO_NOTSUPP;
+ }
+
+ switch (sess_info->keytype) {
+ case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC:
+ sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC;
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE:
+ sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE;
+ break;
+
+ default:
+ error_report("Unknown akcipher keytype: %u", sess_info->keytype);
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ index = cryptodev_lkcf_get_unused_session_index(lkcf);
+ if (index < 0) {
+ error_report("Total number of sessions created exceeds %u",
+ MAX_SESSIONS);
+ return -VIRTIO_CRYPTO_ERR;
+ }
+
+ sess->keylen = sess_info->keylen;
+ sess->key = g_malloc(sess_info->keylen);
+ memcpy(sess->key, sess_info->key, sess_info->keylen);
+
+ lkcf->sess[index] = g_steal_pointer(&sess);
+ *session_id = index;
+
+ return VIRTIO_CRYPTO_OK;
+}
+
+static int cryptodev_lkcf_create_session(
+ CryptoDevBackend *backend,
+ CryptoDevBackendSessionInfo *sess_info,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
+{
+ CryptoDevBackendAsymSessionInfo *asym_sess_info;
+ CryptoDevBackendLKCF *lkcf =
+ CRYPTODEV_BACKEND_LKCF(backend);
+ int ret;
+
+ switch (sess_info->op_code) {
+ case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+ asym_sess_info = &sess_info->u.asym_sess_info;
+ ret = cryptodev_lkcf_create_asym_session(
+ lkcf, asym_sess_info, &sess_info->session_id);
+ break;
+
+ default:
+ ret = -VIRTIO_CRYPTO_NOTSUPP;
+ error_report("Unsupported opcode: %" PRIu32 "",
+ sess_info->op_code);
+ break;
+ }
+ if (cb) {
+ cb(opaque, ret);
+ }
+ return 0;
+}
+
+static int cryptodev_lkcf_close_session(CryptoDevBackend *backend,
+ uint64_t session_id,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
+{
+ CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend);
+ CryptoDevBackendLKCFSession *session;
+
+ assert(session_id < MAX_SESSIONS && lkcf->sess[session_id]);
+ session = lkcf->sess[session_id];
+ lkcf->sess[session_id] = NULL;
+
+ g_free(session->key);
+ g_free(session);
+
+ if (cb) {
+ cb(opaque, VIRTIO_CRYPTO_OK);
+ }
+ return 0;
+}
+
+static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data)
+{
+ CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
+
+ bc->init = cryptodev_lkcf_init;
+ bc->cleanup = cryptodev_lkcf_cleanup;
+ bc->create_session = cryptodev_lkcf_create_session;
+ bc->close_session = cryptodev_lkcf_close_session;
+ bc->do_op = cryptodev_lkcf_operation;
+}
+
+static const TypeInfo cryptodev_builtin_info = {
+ .name = TYPE_CRYPTODEV_BACKEND_LKCF,
+ .parent = TYPE_CRYPTODEV_BACKEND,
+ .class_init = cryptodev_lkcf_class_init,
+ .instance_size = sizeof(CryptoDevBackendLKCF),
+};
+
+static void cryptodev_lkcf_register_types(void)
+{
+ type_register_static(&cryptodev_builtin_info);
+}
+
+type_init(cryptodev_lkcf_register_types);
diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index f9c5867e38..ab3028e045 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -259,13 +259,18 @@ static int64_t cryptodev_vhost_user_sym_create_session(
return -1;
}
-static int64_t cryptodev_vhost_user_create_session(
+static int cryptodev_vhost_user_create_session(
CryptoDevBackend *backend,
CryptoDevBackendSessionInfo *sess_info,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
uint32_t op_code = sess_info->op_code;
CryptoDevBackendSymSessionInfo *sym_sess_info;
+ int64_t ret;
+ Error *local_error = NULL;
+ int status;
switch (op_code) {
case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
@@ -273,27 +278,42 @@ static int64_t cryptodev_vhost_user_create_session(
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
sym_sess_info = &sess_info->u.sym_sess_info;
- return cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
- queue_index, errp);
+ ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info,
+ queue_index, &local_error);
+ break;
+
default:
- error_setg(errp, "Unsupported opcode :%" PRIu32 "",
+ error_setg(&local_error, "Unsupported opcode :%" PRIu32 "",
sess_info->op_code);
- return -1;
-
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
- return -1;
+ if (local_error) {
+ error_report_err(local_error);
+ }
+ if (ret < 0) {
+ status = -VIRTIO_CRYPTO_ERR;
+ } else {
+ sess_info->session_id = ret;
+ status = VIRTIO_CRYPTO_OK;
+ }
+ if (cb) {
+ cb(opaque, status);
+ }
+ return 0;
}
static int cryptodev_vhost_user_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendClient *cc =
backend->conf.peers.ccs[queue_index];
CryptoDevBackendVhost *vhost_crypto;
- int ret;
+ int ret = -1, status;
vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
if (vhost_crypto) {
@@ -301,12 +321,17 @@ static int cryptodev_vhost_user_close_session(
ret = dev->vhost_ops->vhost_crypto_close_session(dev,
session_id);
if (ret < 0) {
- return -1;
+ status = -VIRTIO_CRYPTO_ERR;
} else {
- return 0;
+ status = VIRTIO_CRYPTO_OK;
}
+ } else {
+ status = -VIRTIO_CRYPTO_NOTSUPP;
}
- return -1;
+ if (cb) {
+ cb(opaque, status);
+ }
+ return 0;
}
static void cryptodev_vhost_user_cleanup(
diff --git a/backends/cryptodev.c b/backends/cryptodev.c
index 33eb4e1a70..54ee8c81f5 100644
--- a/backends/cryptodev.c
+++ b/backends/cryptodev.c
@@ -26,6 +26,7 @@
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "qemu/config-file.h"
+#include "qemu/error-report.h"
#include "qom/object_interfaces.h"
#include "hw/virtio/virtio-crypto.h"
@@ -72,69 +73,72 @@ void cryptodev_backend_cleanup(
}
}
-int64_t cryptodev_backend_create_session(
+int cryptodev_backend_create_session(
CryptoDevBackend *backend,
CryptoDevBackendSessionInfo *sess_info,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendClass *bc =
CRYPTODEV_BACKEND_GET_CLASS(backend);
if (bc->create_session) {
- return bc->create_session(backend, sess_info, queue_index, errp);
+ return bc->create_session(backend, sess_info, queue_index, cb, opaque);
}
-
- return -1;
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
int cryptodev_backend_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendClass *bc =
CRYPTODEV_BACKEND_GET_CLASS(backend);
if (bc->close_session) {
- return bc->close_session(backend, session_id, queue_index, errp);
+ return bc->close_session(backend, session_id, queue_index, cb, opaque);
}
-
- return -1;
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
static int cryptodev_backend_operation(
CryptoDevBackend *backend,
CryptoDevBackendOpInfo *op_info,
- uint32_t queue_index, Error **errp)
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque)
{
CryptoDevBackendClass *bc =
CRYPTODEV_BACKEND_GET_CLASS(backend);
if (bc->do_op) {
- return bc->do_op(backend, op_info, queue_index, errp);
+ return bc->do_op(backend, op_info, queue_index, cb, opaque);
}
-
- return -VIRTIO_CRYPTO_ERR;
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
int cryptodev_backend_crypto_operation(
CryptoDevBackend *backend,
- void *opaque,
- uint32_t queue_index, Error **errp)
+ void *opaque1,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb, void *opaque2)
{
- VirtIOCryptoReq *req = opaque;
+ VirtIOCryptoReq *req = opaque1;
CryptoDevBackendOpInfo *op_info = &req->op_info;
enum CryptoDevBackendAlgType algtype = req->flags;
if ((algtype != CRYPTODEV_BACKEND_ALG_SYM)
&& (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) {
- error_setg(errp, "Unsupported cryptodev alg type: %" PRIu32 "",
- algtype);
-
+ error_report("Unsupported cryptodev alg type: %" PRIu32 "", algtype);
return -VIRTIO_CRYPTO_NOTSUPP;
}
- return cryptodev_backend_operation(backend, op_info, queue_index, errp);
+ return cryptodev_backend_operation(backend, op_info, queue_index,
+ cb, opaque2);
}
static void
diff --git a/backends/meson.build b/backends/meson.build
index b1884a88ec..954e658b25 100644
--- a/backends/meson.build
+++ b/backends/meson.build
@@ -12,6 +12,9 @@ softmmu_ss.add([files(
softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('rng-random.c'))
softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files('hostmem-file.c'))
softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('hostmem-memfd.c'))
+if keyutils.found()
+ softmmu_ss.add(keyutils, files('cryptodev-lkcf.c'))
+endif
if have_vhost_user
softmmu_ss.add(when: 'CONFIG_VIRTIO', if_true: files('vhost-user.c'))
endif
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
index ad88379c1e..e4bbc6e5f1 100644
--- a/crypto/akcipher.c
+++ b/crypto/akcipher.c
@@ -22,6 +22,8 @@
#include "qemu/osdep.h"
#include "crypto/akcipher.h"
#include "akcipherpriv.h"
+#include "der.h"
+#include "rsakey.h"
#if defined(CONFIG_GCRYPT)
#include "akcipher-gcrypt.c.inc"
@@ -106,3 +108,19 @@ void qcrypto_akcipher_free(QCryptoAkCipher *akcipher)
drv->free(akcipher);
}
+
+int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts,
+ uint8_t *key, size_t keylen,
+ uint8_t **dst, size_t *dst_len,
+ Error **errp)
+{
+ switch (opts->alg) {
+ case QCRYPTO_AKCIPHER_ALG_RSA:
+ qcrypto_akcipher_rsakey_export_p8info(key, keylen, dst, dst_len);
+ return 0;
+
+ default:
+ error_setg(errp, "Unsupported algorithm: %u", opts->alg);
+ return -1;
+ }
+}
diff --git a/crypto/der.c b/crypto/der.c
index f877390bbb..dab3fe4f24 100644
--- a/crypto/der.c
+++ b/crypto/der.c
@@ -22,20 +22,93 @@
#include "qemu/osdep.h"
#include "crypto/der.h"
+typedef struct QCryptoDerEncodeNode {
+ uint8_t tag;
+ struct QCryptoDerEncodeNode *parent;
+ struct QCryptoDerEncodeNode *next;
+ /* for constructed type, data is null */
+ const uint8_t *data;
+ size_t dlen;
+} QCryptoDerEncodeNode;
+
+typedef struct QCryptoEncodeContext {
+ QCryptoDerEncodeNode root;
+ QCryptoDerEncodeNode *current_parent;
+ QCryptoDerEncodeNode *tail;
+} QCryptoEncodeContext;
+
enum QCryptoDERTypeTag {
QCRYPTO_DER_TYPE_TAG_BOOL = 0x1,
QCRYPTO_DER_TYPE_TAG_INT = 0x2,
QCRYPTO_DER_TYPE_TAG_BIT_STR = 0x3,
QCRYPTO_DER_TYPE_TAG_OCT_STR = 0x4,
- QCRYPTO_DER_TYPE_TAG_OCT_NULL = 0x5,
- QCRYPTO_DER_TYPE_TAG_OCT_OID = 0x6,
+ QCRYPTO_DER_TYPE_TAG_NULL = 0x5,
+ QCRYPTO_DER_TYPE_TAG_OID = 0x6,
QCRYPTO_DER_TYPE_TAG_SEQ = 0x10,
QCRYPTO_DER_TYPE_TAG_SET = 0x11,
};
-#define QCRYPTO_DER_CONSTRUCTED_MASK 0x20
+enum QCryptoDERTagClass {
+ QCRYPTO_DER_TAG_CLASS_UNIV = 0x0,
+ QCRYPTO_DER_TAG_CLASS_APPL = 0x1,
+ QCRYPTO_DER_TAG_CLASS_CONT = 0x2,
+ QCRYPTO_DER_TAG_CLASS_PRIV = 0x3,
+};
+
+enum QCryptoDERTagEnc {
+ QCRYPTO_DER_TAG_ENC_PRIM = 0x0,
+ QCRYPTO_DER_TAG_ENC_CONS = 0x1,
+};
+
+#define QCRYPTO_DER_TAG_ENC_MASK 0x20
+#define QCRYPTO_DER_TAG_ENC_SHIFT 5
+
+#define QCRYPTO_DER_TAG_CLASS_MASK 0xc0
+#define QCRYPTO_DER_TAG_CLASS_SHIFT 6
+
+#define QCRYPTO_DER_TAG_VAL_MASK 0x1f
#define QCRYPTO_DER_SHORT_LEN_MASK 0x80
+#define QCRYPTO_DER_TAG(class, enc, val) \
+ (((class) << QCRYPTO_DER_TAG_CLASS_SHIFT) | \
+ ((enc) << QCRYPTO_DER_TAG_ENC_SHIFT) | (val))
+
+/**
+ * qcrypto_der_encode_length:
+ * @src_len: the length of source data
+ * @dst: distination to save the encoded 'length', if dst is NULL, only compute
+ * the expected buffer size in bytes.
+ * @dst_len: output parameter, indicates how many bytes wrote.
+ *
+ * Encode the 'length' part of TLV tuple.
+ */
+static void qcrypto_der_encode_length(size_t src_len,
+ uint8_t *dst, size_t *dst_len)
+{
+ size_t max_length = 0xFF;
+ uint8_t length_bytes = 0, header_byte;
+
+ if (src_len < QCRYPTO_DER_SHORT_LEN_MASK) {
+ header_byte = src_len;
+ *dst_len = 1;
+ } else {
+ for (length_bytes = 1; max_length < src_len; length_bytes++) {
+ max_length = (max_length << 8) + max_length;
+ }
+ header_byte = length_bytes;
+ header_byte |= QCRYPTO_DER_SHORT_LEN_MASK;
+ *dst_len = length_bytes + 1;
+ }
+ if (!dst) {
+ return;
+ }
+ *dst++ = header_byte;
+ /* Bigendian length bytes */
+ for (; length_bytes > 0; length_bytes--) {
+ *dst++ = ((src_len >> (length_bytes - 1) * 8) & 0xFF);
+ }
+}
+
static uint8_t qcrypto_der_peek_byte(const uint8_t **data, size_t *dlen)
{
return **data;
@@ -150,40 +223,230 @@ static int qcrypto_der_extract_data(const uint8_t **data, size_t *dlen,
return qcrypto_der_extract_definite_data(data, dlen, cb, ctx, errp);
}
-int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
- QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+static int qcrypto_der_decode_tlv(const uint8_t expected_tag,
+ const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb,
+ void *ctx, Error **errp)
{
+ const uint8_t *saved_data = *data;
+ size_t saved_dlen = *dlen;
uint8_t tag;
+ int data_length;
+
if (*dlen < 1) {
error_setg(errp, "Need more data");
return -1;
}
tag = qcrypto_der_cut_byte(data, dlen);
+ if (tag != expected_tag) {
+ error_setg(errp, "Unexpected tag: expected: %u, actual: %u",
+ expected_tag, tag);
+ goto error;
+ }
- /* INTEGER must encoded in primitive-form */
- if (tag != QCRYPTO_DER_TYPE_TAG_INT) {
- error_setg(errp, "Invalid integer type tag: %u", tag);
- return -1;
+ data_length = qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+ if (data_length < 0) {
+ goto error;
}
+ return data_length;
- return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+error:
+ *data = saved_data;
+ *dlen = saved_dlen;
+ return -1;
+}
+
+int qcrypto_der_decode_int(const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+ const uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_INT);
+ return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
}
int qcrypto_der_decode_seq(const uint8_t **data, size_t *dlen,
QCryptoDERDecodeCb cb, void *ctx, Error **errp)
{
- uint8_t tag;
- if (*dlen < 1) {
- error_setg(errp, "Need more data");
- return -1;
- }
- tag = qcrypto_der_cut_byte(data, dlen);
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_CONS,
+ QCRYPTO_DER_TYPE_TAG_SEQ);
+ return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
- /* SEQUENCE must use constructed form */
- if (tag != (QCRYPTO_DER_TYPE_TAG_SEQ | QCRYPTO_DER_CONSTRUCTED_MASK)) {
- error_setg(errp, "Invalid type sequence tag: %u", tag);
- return -1;
- }
+int qcrypto_der_decode_octet_str(const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_OCT_STR);
+ return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_bit_str(const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_BIT_STR);
+ return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_oid(const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_OID);
+ return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
+
+int qcrypto_der_decode_ctx_tag(const uint8_t **data, size_t *dlen, int tag_id,
+ QCryptoDERDecodeCb cb, void *ctx, Error **errp)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_CONT,
+ QCRYPTO_DER_TAG_ENC_CONS,
+ tag_id);
+ return qcrypto_der_decode_tlv(tag, data, dlen, cb, ctx, errp);
+}
- return qcrypto_der_extract_data(data, dlen, cb, ctx, errp);
+static void qcrypto_der_encode_prim(QCryptoEncodeContext *ctx, uint8_t tag,
+ const uint8_t *data, size_t dlen)
+{
+ QCryptoDerEncodeNode *node = g_new0(QCryptoDerEncodeNode, 1);
+ size_t nbytes_len;
+
+ node->tag = tag;
+ node->data = data;
+ node->dlen = dlen;
+ node->parent = ctx->current_parent;
+
+ qcrypto_der_encode_length(dlen, NULL, &nbytes_len);
+ /* 1 byte for Tag, nbyte_len for Length, and dlen for Value */
+ node->parent->dlen += 1 + nbytes_len + dlen;
+
+ ctx->tail->next = node;
+ ctx->tail = node;
+}
+
+QCryptoEncodeContext *qcrypto_der_encode_ctx_new(void)
+{
+ QCryptoEncodeContext *ctx = g_new0(QCryptoEncodeContext, 1);
+ ctx->current_parent = &ctx->root;
+ ctx->tail = &ctx->root;
+ return ctx;
+}
+
+static void qcrypto_der_encode_cons_begin(QCryptoEncodeContext *ctx,
+ uint8_t tag)
+{
+ QCryptoDerEncodeNode *node = g_new0(QCryptoDerEncodeNode, 1);
+
+ node->tag = tag;
+ node->parent = ctx->current_parent;
+ ctx->current_parent = node;
+ ctx->tail->next = node;
+ ctx->tail = node;
+}
+
+static void qcrypto_der_encode_cons_end(QCryptoEncodeContext *ctx)
+{
+ QCryptoDerEncodeNode *cons_node = ctx->current_parent;
+ size_t nbytes_len;
+
+ qcrypto_der_encode_length(cons_node->dlen, NULL, &nbytes_len);
+ /* 1 byte for Tag, nbyte_len for Length, and dlen for Value */
+ cons_node->parent->dlen += 1 + nbytes_len + cons_node->dlen;
+ ctx->current_parent = cons_node->parent;
+}
+
+void qcrypto_der_encode_seq_begin(QCryptoEncodeContext *ctx)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_CONS,
+ QCRYPTO_DER_TYPE_TAG_SEQ);
+ qcrypto_der_encode_cons_begin(ctx, tag);
+}
+
+void qcrypto_der_encode_seq_end(QCryptoEncodeContext *ctx)
+{
+ qcrypto_der_encode_cons_end(ctx);
+}
+
+void qcrypto_der_encode_oid(QCryptoEncodeContext *ctx,
+ const uint8_t *src, size_t src_len)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_OID);
+ qcrypto_der_encode_prim(ctx, tag, src, src_len);
+}
+
+void qcrypto_der_encode_int(QCryptoEncodeContext *ctx,
+ const uint8_t *src, size_t src_len)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_INT);
+ qcrypto_der_encode_prim(ctx, tag, src, src_len);
+}
+
+void qcrypto_der_encode_null(QCryptoEncodeContext *ctx)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_NULL);
+ qcrypto_der_encode_prim(ctx, tag, NULL, 0);
+}
+
+void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx,
+ const uint8_t *src, size_t src_len)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_OCT_STR);
+ qcrypto_der_encode_prim(ctx, tag, src, src_len);
+}
+
+void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx)
+{
+ uint8_t tag = QCRYPTO_DER_TAG(QCRYPTO_DER_TAG_CLASS_UNIV,
+ QCRYPTO_DER_TAG_ENC_PRIM,
+ QCRYPTO_DER_TYPE_TAG_OCT_STR);
+ qcrypto_der_encode_cons_begin(ctx, tag);
+}
+
+void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx)
+{
+ qcrypto_der_encode_cons_end(ctx);
+}
+
+size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx)
+{
+ return ctx->root.dlen;
+}
+
+void qcrypto_der_encode_ctx_flush_and_free(QCryptoEncodeContext *ctx,
+ uint8_t *dst)
+{
+ QCryptoDerEncodeNode *node, *prev;
+ size_t len;
+
+ for (prev = &ctx->root;
+ (node = prev->next) && (prev->next = node->next, 1);) {
+ /* Tag */
+ *dst++ = node->tag;
+
+ /* Length */
+ qcrypto_der_encode_length(node->dlen, dst, &len);
+ dst += len;
+
+ /* Value */
+ if (node->data) {
+ memcpy(dst, node->data, node->dlen);
+ dst += node->dlen;
+ }
+ g_free(node);
+ }
+ g_free(ctx);
}
diff --git a/crypto/der.h b/crypto/der.h
index e3d3aeacdc..0e895bbeec 100644
--- a/crypto/der.h
+++ b/crypto/der.h
@@ -22,6 +22,11 @@
#include "qapi/error.h"
+typedef struct QCryptoEncodeContext QCryptoEncodeContext;
+
+/* rsaEncryption: 1.2.840.113549.1.1.1 */
+#define QCRYPTO_OID_rsaEncryption "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"
+
/* Simple decoder used to parse DER encoded rsa keys. */
/**
@@ -47,14 +52,13 @@ typedef int (*QCryptoDERDecodeCb) (void *opaque, const uint8_t *value,
* will be set to the rest length of data, if cb is not NULL, must
* return 0 to make decode success, at last, the length of the data
* part of the decoded INTEGER will be returned. Otherwise, -1 is
- * returned.
+ * returned and the valued of *data and *dlen keep unchanged.
*/
int qcrypto_der_decode_int(const uint8_t **data,
size_t *dlen,
QCryptoDERDecodeCb cb,
void *opaque,
Error **errp);
-
/**
* qcrypto_der_decode_seq:
*
@@ -70,7 +74,7 @@ int qcrypto_der_decode_int(const uint8_t **data,
* will be set to the rest length of data, if cb is not NULL, must
* return 0 to make decode success, at last, the length of the data
* part of the decoded SEQUENCE will be returned. Otherwise, -1 is
- * returned.
+ * returned and the valued of *data and *dlen keep unchanged.
*/
int qcrypto_der_decode_seq(const uint8_t **data,
size_t *dlen,
@@ -78,4 +82,205 @@ int qcrypto_der_decode_seq(const uint8_t **data,
void *opaque,
Error **errp);
+/**
+ * qcrypto_der_decode_oid:
+ *
+ * Decode OID from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded OID will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_oid(const uint8_t **data,
+ size_t *dlen,
+ QCryptoDERDecodeCb cb,
+ void *opaque,
+ Error **errp);
+
+/**
+ * qcrypto_der_decode_octet_str:
+ *
+ * Decode OCTET STRING from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded OCTET STRING will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_octet_str(const uint8_t **data,
+ size_t *dlen,
+ QCryptoDERDecodeCb cb,
+ void *opaque,
+ Error **errp);
+
+/**
+ * qcrypto_der_decode_bit_str:
+ *
+ * Decode BIT STRING from DER-encoded data, similar with der_decode_int.
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded BIT STRING will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_bit_str(const uint8_t **data,
+ size_t *dlen,
+ QCryptoDERDecodeCb cb,
+ void *opaque,
+ Error **errp);
+
+
+/**
+ * qcrypto_der_decode_ctx_tag:
+ *
+ * Decode context specific tag
+ *
+ * @data: pointer to address of input data
+ * @dlen: pointer to length of input data
+ * @tag: expected value of context specific tag
+ * @cb: callback invoked when decode succeed, if cb equals NULL, no
+ * callback will be invoked
+ * @opaque: parameter passed to cb
+ *
+ * Returns: On success, *data points to rest data, and *dlen
+ * will be set to the rest length of data, if cb is not NULL, must
+ * return 0 to make decode success, at last, the length of the data
+ * part of the decoded BIT STRING will be returned. Otherwise, -1 is
+ * returned and the valued of *data and *dlen keep unchanged.
+ */
+int qcrypto_der_decode_ctx_tag(const uint8_t **data,
+ size_t *dlen, int tag_id,
+ QCryptoDERDecodeCb cb,
+ void *opaque,
+ Error **errp);
+
+/**
+ * qcrypto_der_encode_ctx_new:
+ *
+ * Allocate a context used for der encoding.
+ */
+QCryptoEncodeContext *qcrypto_der_encode_ctx_new(void);
+
+/**
+ * qcrypto_der_encode_seq_begin:
+ * @ctx: the encode context.
+ *
+ * Start encoding a SEQUENCE for ctx.
+ *
+ */
+void qcrypto_der_encode_seq_begin(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_seq_begin:
+ * @ctx: the encode context.
+ *
+ * Finish uencoding a SEQUENCE for ctx.
+ *
+ */
+void qcrypto_der_encode_seq_end(QCryptoEncodeContext *ctx);
+
+
+/**
+ * qcrypto_der_encode_oid:
+ * @ctx: the encode context.
+ * @src: the source data of oid, note it should be already encoded, this
+ * function only add tag and length part for it.
+ *
+ * Encode an oid into ctx.
+ */
+void qcrypto_der_encode_oid(QCryptoEncodeContext *ctx,
+ const uint8_t *src, size_t src_len);
+
+/**
+ * qcrypto_der_encode_int:
+ * @ctx: the encode context.
+ * @src: the source data of integer, note it should be already encoded, this
+ * function only add tag and length part for it.
+ *
+ * Encode an integer into ctx.
+ */
+void qcrypto_der_encode_int(QCryptoEncodeContext *ctx,
+ const uint8_t *src, size_t src_len);
+
+/**
+ * qcrypto_der_encode_null:
+ * @ctx: the encode context.
+ *
+ * Encode a null into ctx.
+ */
+void qcrypto_der_encode_null(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_octet_str:
+ * @ctx: the encode context.
+ * @src: the source data of the octet string.
+ *
+ * Encode a octet string into ctx.
+ */
+void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx,
+ const uint8_t *src, size_t src_len);
+
+/**
+ * qcrypto_der_encode_octet_str_begin:
+ * @ctx: the encode context.
+ *
+ * Start encoding a octet string, All fields between
+ * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end
+ * are encoded as an octet string. This is useful when we need to encode a
+ * encoded SEQUNCE as OCTET STRING.
+ */
+void qcrypto_der_encode_octet_str_begin(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_octet_str_end:
+ * @ctx: the encode context.
+ *
+ * Finish encoding a octet string, All fields between
+ * qcrypto_der_encode_octet_str_begin and qcrypto_der_encode_octet_str_end
+ * are encoded as an octet string. This is useful when we need to encode a
+ * encoded SEQUNCE as OCTET STRING.
+ */
+void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_ctx_buffer_len:
+ * @ctx: the encode context.
+ *
+ * Compute the expected buffer size to save all encoded things.
+ */
+size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx);
+
+/**
+ * qcrypto_der_encode_ctx_flush_and_free:
+ * @ctx: the encode context.
+ * @dst: the distination to save the encoded data, the length of dst should
+ * not less than qcrypto_der_encode_cxt_buffer_len
+ *
+ * Flush all encoded data into dst, then free ctx.
+ */
+void qcrypto_der_encode_ctx_flush_and_free(QCryptoEncodeContext *ctx,
+ uint8_t *dst);
+
#endif /* QCRYPTO_ASN1_DECODER_H */
diff --git a/crypto/rsakey.c b/crypto/rsakey.c
index cc40e072f0..7d6f273aef 100644
--- a/crypto/rsakey.c
+++ b/crypto/rsakey.c
@@ -19,6 +19,8 @@
*
*/
+#include "qemu/osdep.h"
+#include "der.h"
#include "rsakey.h"
void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
@@ -37,6 +39,46 @@ void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *rsa_key)
g_free(rsa_key);
}
+/**
+ * PKCS#8 private key info for RSA
+ *
+ * PrivateKeyInfo ::= SEQUENCE {
+ * version INTEGER,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey OCTET STRING,
+ * attributes [0] IMPLICIT Attributes OPTIONAL
+ * }
+ */
+void qcrypto_akcipher_rsakey_export_p8info(const uint8_t *key,
+ size_t keylen,
+ uint8_t **dst,
+ size_t *dlen)
+{
+ QCryptoEncodeContext *ctx = qcrypto_der_encode_ctx_new();
+ uint8_t version = 0;
+
+ qcrypto_der_encode_seq_begin(ctx);
+
+ /* version */
+ qcrypto_der_encode_int(ctx, &version, sizeof(version));
+
+ /* algorithm identifier */
+ qcrypto_der_encode_seq_begin(ctx);
+ qcrypto_der_encode_oid(ctx, (uint8_t *)QCRYPTO_OID_rsaEncryption,
+ sizeof(QCRYPTO_OID_rsaEncryption) - 1);
+ qcrypto_der_encode_null(ctx);
+ qcrypto_der_encode_seq_end(ctx);
+
+ /* RSA private key */
+ qcrypto_der_encode_octet_str(ctx, key, keylen);
+
+ qcrypto_der_encode_seq_end(ctx);
+
+ *dlen = qcrypto_der_encode_ctx_buffer_len(ctx);
+ *dst = g_malloc(*dlen);
+ qcrypto_der_encode_ctx_flush_and_free(ctx, *dst);
+}
+
#if defined(CONFIG_NETTLE) && defined(CONFIG_HOGWEED)
#include "rsakey-nettle.c.inc"
#else
diff --git a/crypto/rsakey.h b/crypto/rsakey.h
index 974b76f659..00b3eccec7 100644
--- a/crypto/rsakey.h
+++ b/crypto/rsakey.h
@@ -22,7 +22,6 @@
#ifndef QCRYPTO_RSAKEY_H
#define QCRYPTO_RSAKEY_H
-#include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "crypto/akcipher.h"
@@ -84,6 +83,16 @@ QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse(
QCryptoAkCipherKeyType type,
const uint8_t *key, size_t keylen, Error **errp);
+/**
+ * qcrypto_akcipher_rsakey_export_as_p8info:
+ *
+ * Export RSA private key to PKCS#8 private key info.
+ */
+void qcrypto_akcipher_rsakey_export_p8info(const uint8_t *key,
+ size_t keylen,
+ uint8_t **dst,
+ size_t *dlen);
+
void qcrypto_akcipher_rsakey_free(QCryptoAkCipherRSAKey *key);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherRSAKey,
diff --git a/docs/devel/acpi-bits.rst b/docs/devel/acpi-bits.rst
new file mode 100644
index 0000000000..c9564d871a
--- /dev/null
+++ b/docs/devel/acpi-bits.rst
@@ -0,0 +1,145 @@
+=============================================================================
+ACPI/SMBIOS avocado tests using biosbits
+=============================================================================
+
+Biosbits is a software written by Josh Triplett that can be downloaded
+from https://biosbits.org/. The github codebase can be found
+`here <https://github.com/biosbits/bits/tree/master>`__. It is a software that executes
+the bios components such as acpi and smbios tables directly through acpica
+bios interpreter (a freely available C based library written by Intel,
+downloadable from https://acpica.org/ and is included with biosbits) without an
+operating system getting involved in between.
+There are several advantages to directly testing the bios in a real physical
+machine or VM as opposed to indirectly discovering bios issues through the
+operating system. For one thing, the OSes tend to hide bios problems from the
+end user. The other is that we have more control of what we wanted to test
+and how by directly using acpica interpreter on top of the bios on a running
+system. More details on the inspiration for developing biosbits and its real
+life uses can be found in [#a]_ and [#b]_.
+This directory contains tests written in python using avocado framework that
+exercises the QEMU bios components using biosbits and reports test failures.
+For QEMU, we maintain a fork of bios bits in gitlab along with all the
+dependent submodules:
+https://gitlab.com/qemu-project/biosbits-bits
+This fork contains numerous fixes, a newer acpica and changes specific to
+running this avocado QEMU tests using bits. The author of this document
+is the sole maintainer of the QEMU fork of bios bits repo.
+
+Under the directory ``tests/avocado/``, ``acpi-bits.py`` is a QEMU avocado
+test that drives all this.
+
+A brief description of the various test files follows.
+
+Under ``tests/avocado/`` as the root we have:
+
+::
+
+ ├── acpi-bits
+ │ ├── bits-config
+ │ │ └── bits-cfg.txt
+ │ ├── bits-tests
+ │ │ ├── smbios.py2
+ │ │ ├── testacpi.py2
+ │ │ └── testcpuid.py2
+ │ └── README
+ ├── acpi-bits.py
+
+* ``tests/avocado``:
+
+ ``acpi-bits.py``:
+ This is the main python avocado test script that generates a
+ biosbits iso. It then spawns a QEMU VM with it, collects the log and reports
+ test failures. This is the script one would be interested in if they wanted
+ to add or change some component of the log parsing, add a new command line
+ to alter how QEMU is spawned etc. Test writers typically would not need to
+ modify this script unless they wanted to enhance or change the log parsing
+ for their tests. In order to enable debugging, you can set **V=1**
+ environment variable. This enables verbose mode for the test and also dumps
+ the entire log from bios bits and more information in case failure happens.
+
+ In order to run this test, please perform the following steps from the QEMU
+ build directory:
+ ::
+
+ $ make check-venv (needed only the first time to create the venv)
+ $ ./tests/venv/bin/avocado run -t acpi tests/avocado
+
+ The above will run all acpi avocado tests including this one.
+ In order to run the individual tests, perform the following:
+ ::
+
+ $ ./tests/venv/bin/avocado run tests/avocado/acpi-bits.py --tap -
+
+ The above will produce output in tap format. You can omit "--tap -" in the
+ end and it will produce output like the following:
+ ::
+
+ $ ./tests/venv/bin/avocado run tests/avocado/acpi-bits.py
+ Fetching asset from tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits
+ JOB ID : eab225724da7b64c012c65705dc2fa14ab1defef
+ JOB LOG : /home/anisinha/avocado/job-results/job-2022-10-10T17.58-eab2257/job.log
+ (1/1) tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits: PASS (33.09 s)
+ RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0
+ JOB TIME : 39.22 s
+
+ You can inspect the log file for more information about the run or in order
+ to diagnoze issues. If you pass V=1 in the environment, more diagnostic logs
+ would be found in the test log.
+
+* ``tests/avocado/acpi-bits/bits-config``:
+
+ This location contains biosbits configuration files that determine how the
+ software runs the tests.
+
+ ``bits-config.txt``:
+ This is the biosbits config file that determines what tests
+ or actions are performed by bits. The description of the config options are
+ provided in the file itself.
+
+* ``tests/avocado/acpi-bits/bits-tests``:
+
+ This directory contains biosbits python based tests that are run from within
+ the biosbits environment in the spawned VM. New additions of test cases can
+ be made in the appropriate test file. For example, new acpi tests can go
+ into testacpi.py2 and one would call testsuite.add_test() to register the new
+ test so that it gets executed as a part of the ACPI tests.
+ It might be occasionally necessary to disable some subtests or add a new
+ test that belongs to a test suite not already present in this directory. To
+ do this, please clone the bits source from
+ https://gitlab.com/qemu-project/biosbits-bits/-/tree/qemu-bits.
+ Note that this is the "qemu-bits" branch and not the "bits" branch of the
+ repository. "qemu-bits" is the branch where we have made all the QEMU
+ specific enhancements and we must use the source from this branch only.
+ Copy the test suite/script that needs modification (addition of new tests
+ or disabling them) from python directory into this directory. For
+ example, in order to change cpuid related tests, copy the following
+ file into this directory and rename it with .py2 extension:
+ https://gitlab.com/qemu-project/biosbits-bits/-/blob/qemu-bits/python/testcpuid.py
+ Then make your additions and changes here. Therefore, the steps are:
+
+ (a) Copy unmodified test script to this directory from bits source.
+ (b) Add a SPDX license header.
+ (c) Perform modifications to the test.
+
+ Commits (a), (b) and (c) should go under separate commits so that the original
+ test script and the changes we have made are separated and clear.
+
+ The test framework will then use your modified test script to run the test.
+ No further changes would be needed. Please check the logs to make sure that
+ appropriate changes have taken effect.
+
+ The tests have an extension .py2 in order to indicate that:
+
+ (a) They are python2.7 based scripts and not python 3 scripts.
+ (b) They are run from within the bios bits VM and is not subjected to QEMU
+ build/test python script maintainance and dependency resolutions.
+ (c) They need not be loaded by avocado framework when running tests.
+
+
+Author: Ani Sinha <ani@anisinha.ca>
+
+References:
+-----------
+.. [#a] https://blog.linuxplumbersconf.org/2011/ocw/system/presentations/867/original/bits.pdf
+.. [#b] https://www.youtube.com/watch?v=36QIepyUuhg
+
diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst
index 1002a533a6..57e8d39d98 100644
--- a/docs/devel/index-build.rst
+++ b/docs/devel/index-build.rst
@@ -11,6 +11,7 @@ the basics if you are adding new files and targets to the build.
build-system
kconfig
testing
+ acpi-bits
qtest
ci
qapi-code-gen
diff --git a/hw/acpi/aml-build-stub.c b/hw/acpi/aml-build-stub.c
index 8d8ad1a314..89a8fec4af 100644
--- a/hw/acpi/aml-build-stub.c
+++ b/hw/acpi/aml-build-stub.c
@@ -26,6 +26,16 @@ void aml_append(Aml *parent_ctx, Aml *child)
{
}
+Aml *aml_return(Aml *val)
+{
+ return NULL;
+}
+
+Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag)
+{
+ return NULL;
+}
+
Aml *aml_resource_template(void)
{
return NULL;
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index e6bfac95c7..42feb4d4d7 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -2070,7 +2070,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
acpi_table_end(linker, &table);
}
-/* build rev1/rev3/rev5.1 FADT */
+/* build rev1/rev3/rev5.1/rev6.0 FADT */
void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
const char *oem_id, const char *oem_table_id)
{
@@ -2193,8 +2193,15 @@ void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
/* SLEEP_STATUS_REG */
build_append_gas_from_struct(tbl, &f->sleep_sts);
- /* TODO: extra fields need to be added to support revisions above rev5 */
- assert(f->rev == 5);
+ if (f->rev == 5) {
+ goto done;
+ }
+
+ /* Hypervisor Vendor Identity */
+ build_append_padded_str(tbl, "QEMU", 8, '\0');
+
+ /* TODO: extra fields need to be added to support revisions above rev6 */
+ assert(f->rev == 6);
done:
acpi_table_end(linker, &table);
diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c
index df856b2669..aefcc03ad6 100644
--- a/hw/acpi/erst.c
+++ b/hw/acpi/erst.c
@@ -635,7 +635,7 @@ static unsigned read_erst_record(ERSTDeviceState *s)
if (record_length < UEFI_CPER_RECORD_MIN_SIZE) {
rc = STATUS_FAILED;
}
- if ((s->record_offset + record_length) > exchange_length) {
+ if (record_length > exchange_length - s->record_offset) {
rc = STATUS_FAILED;
}
/* If all is ok, copy the record to the exchange buffer */
@@ -684,7 +684,7 @@ static unsigned write_erst_record(ERSTDeviceState *s)
if (record_length < UEFI_CPER_RECORD_MIN_SIZE) {
return STATUS_FAILED;
}
- if ((s->record_offset + record_length) > exchange_length) {
+ if (record_length > exchange_length - s->record_offset) {
return STATUS_FAILED;
}
@@ -716,7 +716,7 @@ static unsigned write_erst_record(ERSTDeviceState *s)
if (nvram) {
/* Write the record into the slot */
memcpy(nvram, exchange, record_length);
- memset(nvram + record_length, exchange_length - record_length, 0xFF);
+ memset(nvram + record_length, 0xFF, exchange_length - record_length);
/* If a new record, increment the record_count */
if (!record_found) {
uint32_t record_count;
diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c
index 31e46df0bd..a3b25a92f3 100644
--- a/hw/acpi/nvdimm.c
+++ b/hw/acpi/nvdimm.c
@@ -922,6 +922,7 @@ void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io,
#define NVDIMM_DSM_RFIT_STATUS "RSTA"
#define NVDIMM_QEMU_RSVD_UUID "648B9CF2-CDA1-4312-8AD9-49C4AF32BD62"
+#define NVDIMM_DEVICE_DSM_UUID "4309AC30-0D11-11E4-9191-0800200C9A66"
static void nvdimm_build_common_dsm(Aml *dev,
NVDIMMState *nvdimm_state)
@@ -1029,15 +1030,14 @@ static void nvdimm_build_common_dsm(Aml *dev,
/* UUID for QEMU internal use */), expected_uuid));
aml_append(elsectx, ifctx);
elsectx2 = aml_else();
- aml_append(elsectx2, aml_store(
- aml_touuid("4309AC30-0D11-11E4-9191-0800200C9A66")
+ aml_append(elsectx2, aml_store(aml_touuid(NVDIMM_DEVICE_DSM_UUID)
/* UUID for NVDIMM Devices */, expected_uuid));
aml_append(elsectx, elsectx2);
aml_append(method, elsectx);
uuid_invalid = aml_lnot(aml_equal(uuid, expected_uuid));
- unsupport = aml_if(aml_or(unpatched, uuid_invalid, NULL));
+ unsupport = aml_if(aml_lor(unpatched, uuid_invalid));
/*
* function 0 is called to inquire what functions are supported by
@@ -1069,10 +1069,9 @@ static void nvdimm_build_common_dsm(Aml *dev,
* in the DSM Spec.
*/
pckg = aml_arg(3);
- ifctx = aml_if(aml_and(aml_equal(aml_object_type(pckg),
+ ifctx = aml_if(aml_land(aml_equal(aml_object_type(pckg),
aml_int(4 /* Package */)) /* It is a Package? */,
- aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */,
- NULL));
+ aml_equal(aml_sizeof(pckg), aml_int(1)) /* 1 element? */));
pckg_index = aml_local(2);
pckg_buf = aml_local(3);
@@ -1244,6 +1243,7 @@ static void nvdimm_build_fit(Aml *dev)
static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots)
{
uint32_t slot;
+ Aml *method, *pkg, *field, *com_call;
for (slot = 0; slot < ram_slots; slot++) {
uint32_t handle = nvdimm_slot_to_handle(slot);
@@ -1261,6 +1261,100 @@ static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots)
*/
aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
+ /*
+ * ACPI v6.4: Section 6.5.10 NVDIMM Label Methods
+ */
+ /* _LSI */
+ method = aml_method("_LSI", 0, AML_SERIALIZED);
+ com_call = aml_call5(NVDIMM_COMMON_DSM,
+ aml_touuid(NVDIMM_DEVICE_DSM_UUID),
+ aml_int(1), aml_int(4), aml_int(0),
+ aml_int(handle));
+ aml_append(method, aml_store(com_call, aml_local(0)));
+
+ aml_append(method, aml_create_dword_field(aml_local(0),
+ aml_int(0), "STTS"));
+ aml_append(method, aml_create_dword_field(aml_local(0), aml_int(4),
+ "SLSA"));
+ aml_append(method, aml_create_dword_field(aml_local(0), aml_int(8),
+ "MAXT"));
+
+ pkg = aml_package(3);
+ aml_append(pkg, aml_name("STTS"));
+ aml_append(pkg, aml_name("SLSA"));
+ aml_append(pkg, aml_name("MAXT"));
+ aml_append(method, aml_store(pkg, aml_local(1)));
+ aml_append(method, aml_return(aml_local(1)));
+
+ aml_append(nvdimm_dev, method);
+
+ /* _LSR */
+ method = aml_method("_LSR", 2, AML_SERIALIZED);
+ aml_append(method, aml_name_decl("INPT", aml_buffer(8, NULL)));
+
+ aml_append(method, aml_create_dword_field(aml_name("INPT"),
+ aml_int(0), "OFST"));
+ aml_append(method, aml_create_dword_field(aml_name("INPT"),
+ aml_int(4), "LEN"));
+ aml_append(method, aml_store(aml_arg(0), aml_name("OFST")));
+ aml_append(method, aml_store(aml_arg(1), aml_name("LEN")));
+
+ pkg = aml_package(1);
+ aml_append(pkg, aml_name("INPT"));
+ aml_append(method, aml_store(pkg, aml_local(0)));
+
+ com_call = aml_call5(NVDIMM_COMMON_DSM,
+ aml_touuid(NVDIMM_DEVICE_DSM_UUID),
+ aml_int(1), aml_int(5), aml_local(0),
+ aml_int(handle));
+ aml_append(method, aml_store(com_call, aml_local(3)));
+ field = aml_create_dword_field(aml_local(3), aml_int(0), "STTS");
+ aml_append(method, field);
+ field = aml_create_field(aml_local(3), aml_int(32),
+ aml_shiftleft(aml_name("LEN"), aml_int(3)),
+ "LDAT");
+ aml_append(method, field);
+ aml_append(method, aml_name_decl("LSA", aml_buffer(0, NULL)));
+ aml_append(method, aml_to_buffer(aml_name("LDAT"), aml_name("LSA")));
+
+ pkg = aml_package(2);
+ aml_append(pkg, aml_name("STTS"));
+ aml_append(pkg, aml_name("LSA"));
+
+ aml_append(method, aml_store(pkg, aml_local(1)));
+ aml_append(method, aml_return(aml_local(1)));
+
+ aml_append(nvdimm_dev, method);
+
+ /* _LSW */
+ method = aml_method("_LSW", 3, AML_SERIALIZED);
+ aml_append(method, aml_store(aml_arg(2), aml_local(2)));
+ aml_append(method, aml_name_decl("INPT", aml_buffer(8, NULL)));
+ field = aml_create_dword_field(aml_name("INPT"),
+ aml_int(0), "OFST");
+ aml_append(method, field);
+ field = aml_create_dword_field(aml_name("INPT"),
+ aml_int(4), "TLEN");
+ aml_append(method, field);
+ aml_append(method, aml_store(aml_arg(0), aml_name("OFST")));
+ aml_append(method, aml_store(aml_arg(1), aml_name("TLEN")));
+
+ aml_append(method, aml_concatenate(aml_name("INPT"), aml_local(2),
+ aml_name("INPT")));
+ pkg = aml_package(1);
+ aml_append(pkg, aml_name("INPT"));
+ aml_append(method, aml_store(pkg, aml_local(0)));
+ com_call = aml_call5(NVDIMM_COMMON_DSM,
+ aml_touuid(NVDIMM_DEVICE_DSM_UUID),
+ aml_int(1), aml_int(6), aml_local(0),
+ aml_int(handle));
+ aml_append(method, aml_store(com_call, aml_local(3)));
+ field = aml_create_dword_field(aml_local(3), aml_int(0), "STTS");
+ aml_append(method, field);
+ aml_append(method, aml_return(aml_name("STTS")));
+
+ aml_append(nvdimm_dev, method);
+
nvdimm_build_device_dsm(nvdimm_dev, handle);
aml_append(root_dev, nvdimm_dev);
}
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 15fa79afd3..17fcde8e1c 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -30,6 +30,7 @@ config ARM_VIRT
select ACPI_VIOT
select VIRTIO_MEM_SUPPORTED
select ACPI_CXL
+ select ACPI_HMAT
config CHEETAH
bool
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 13c6e3e468..4156111d49 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -42,6 +42,7 @@
#include "hw/acpi/memory_hotplug.h"
#include "hw/acpi/generic_event_device.h"
#include "hw/acpi/tpm.h"
+#include "hw/acpi/hmat.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
@@ -685,7 +686,7 @@ build_dbg2(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
};
/*
- * ACPI spec, Revision 5.1 Errata A
+ * ACPI spec, Revision 6.0 Errata A
* 5.2.12 Multiple APIC Description Table (MADT)
*/
static void build_append_gicr(GArray *table_data, uint64_t base, uint32_t size)
@@ -704,7 +705,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
int i;
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
const MemMapEntry *memmap = vms->memmap;
- AcpiTable table = { .sig = "APIC", .rev = 3, .oem_id = vms->oem_id,
+ AcpiTable table = { .sig = "APIC", .rev = 4, .oem_id = vms->oem_id,
.oem_table_id = vms->oem_table_id };
acpi_table_begin(&table, table_data);
@@ -739,7 +740,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
/* 5.2.12.14 GIC Structure */
build_append_int_noprefix(table_data, 0xB, 1); /* Type */
- build_append_int_noprefix(table_data, 76, 1); /* Length */
+ build_append_int_noprefix(table_data, 80, 1); /* Length */
build_append_int_noprefix(table_data, 0, 2); /* Reserved */
build_append_int_noprefix(table_data, i, 4); /* GIC ID */
build_append_int_noprefix(table_data, i, 4); /* ACPI Processor UID */
@@ -759,6 +760,10 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, 0, 8); /* GICR Base Address*/
/* MPIDR */
build_append_int_noprefix(table_data, armcpu->mp_affinity, 8);
+ /* Processor Power Efficiency Class */
+ build_append_int_noprefix(table_data, 0, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 3);
}
if (vms->gic_version != VIRT_GIC_VERSION_2) {
@@ -771,12 +776,6 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
if (its_class_name() && !vmc->no_its) {
/*
- * FIXME: Structure is from Revision 6.0 where 'GIC Structure'
- * has additional fields on top of implemented 5.1 Errata A,
- * to make it consistent with v6.0 we need to bump everything
- * to v6.0
- */
- /*
* ACPI spec, Revision 6.0 Errata A
* (original 6.0 definition has invalid Length)
* 5.2.12.18 GIC ITS Structure
@@ -808,13 +807,13 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
/* FADT */
-static void build_fadt_rev5(GArray *table_data, BIOSLinker *linker,
+static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker,
VirtMachineState *vms, unsigned dsdt_tbl_offset)
{
- /* ACPI v5.1 */
+ /* ACPI v6.0 */
AcpiFadtData fadt = {
- .rev = 5,
- .minor_ver = 1,
+ .rev = 6,
+ .minor_ver = 0,
.flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
.xdsdt_tbl_offset = &dsdt_tbl_offset,
};
@@ -944,7 +943,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
/* FADT MADT PPTT GTDT MCFG SPCR DBG2 pointed to by RSDT */
acpi_add_table(table_offsets, tables_blob);
- build_fadt_rev5(tables_blob, tables->linker, vms, dsdt);
+ build_fadt_rev6(tables_blob, tables->linker, vms, dsdt);
acpi_add_table(table_offsets, tables_blob);
build_madt(tables_blob, tables->linker, vms);
@@ -989,6 +988,12 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
build_slit(tables_blob, tables->linker, ms, vms->oem_id,
vms->oem_table_id);
}
+
+ if (ms->numa_state->hmat_enabled) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_hmat(tables_blob, tables->linker, ms->numa_state,
+ vms->oem_id, vms->oem_table_id);
+ }
}
if (ms->nvdimms_state->is_enabled) {
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 13bf5cc47a..16ad400889 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -168,13 +168,6 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp)
goto err_guest_notifiers;
}
- ret = vhost_dev_start(&s->dev, vdev);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Error starting vhost");
- goto err_guest_notifiers;
- }
- s->started_vu = true;
-
/* guest_notifier_mask/pending not used yet, so just unmask
* everything here. virtio-pci will do the right thing by
* enabling/disabling irqfd.
@@ -183,9 +176,20 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp)
vhost_virtqueue_mask(&s->dev, vdev, i, false);
}
+ s->dev.vq_index_end = s->dev.nvqs;
+ ret = vhost_dev_start(&s->dev, vdev);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Error starting vhost");
+ goto err_guest_notifiers;
+ }
+ s->started_vu = true;
+
return ret;
err_guest_notifiers:
+ for (i = 0; i < s->dev.nvqs; i++) {
+ vhost_virtqueue_mask(&s->dev, vdev, i, true);
+ }
k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false);
err_host_notifiers:
vhost_dev_disable_notifiers(&s->dev, vdev);
@@ -222,14 +226,10 @@ static void vhost_user_blk_stop(VirtIODevice *vdev)
static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserBlk *s = VHOST_USER_BLK(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
Error *local_err = NULL;
int ret;
- if (!vdev->vm_running) {
- should_start = false;
- }
-
if (!s->connected) {
return;
}
diff --git a/hw/core/machine.c b/hw/core/machine.c
index aa520e74a8..8d34caa31d 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -40,7 +40,9 @@
#include "hw/virtio/virtio-pci.h"
#include "qom/object_interfaces.h"
-GlobalProperty hw_compat_7_1[] = {};
+GlobalProperty hw_compat_7_1[] = {
+ { "virtio-device", "queue_reset", "false" },
+};
const size_t hw_compat_7_1_len = G_N_ELEMENTS(hw_compat_7_1);
GlobalProperty hw_compat_7_0[] = {
@@ -1176,9 +1178,7 @@ static void numa_validate_initiator(NumaState *numa_state)
for (i = 0; i < numa_state->num_nodes; i++) {
if (numa_info[i].initiator == MAX_NODES) {
- error_report("The initiator of NUMA node %d is missing, use "
- "'-numa node,initiator' option to declare it", i);
- exit(1);
+ continue;
}
if (!numa_info[numa_info[i].initiator].present) {
diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c
new file mode 100644
index 0000000000..3653aa56f0
--- /dev/null
+++ b/hw/cxl/cxl-cdat.c
@@ -0,0 +1,224 @@
+/*
+ * CXL CDAT Structure
+ *
+ * Copyright (C) 2021 Avery Design Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/cxl/cxl.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+
+static void cdat_len_check(CDATSubHeader *hdr, Error **errp)
+{
+ assert(hdr->length);
+ assert(hdr->reserved == 0);
+
+ switch (hdr->type) {
+ case CDAT_TYPE_DSMAS:
+ assert(hdr->length == sizeof(CDATDsmas));
+ break;
+ case CDAT_TYPE_DSLBIS:
+ assert(hdr->length == sizeof(CDATDslbis));
+ break;
+ case CDAT_TYPE_DSMSCIS:
+ assert(hdr->length == sizeof(CDATDsmscis));
+ break;
+ case CDAT_TYPE_DSIS:
+ assert(hdr->length == sizeof(CDATDsis));
+ break;
+ case CDAT_TYPE_DSEMTS:
+ assert(hdr->length == sizeof(CDATDsemts));
+ break;
+ case CDAT_TYPE_SSLBIS:
+ assert(hdr->length >= sizeof(CDATSslbisHeader));
+ assert((hdr->length - sizeof(CDATSslbisHeader)) %
+ sizeof(CDATSslbe) == 0);
+ break;
+ default:
+ error_setg(errp, "Type %d is reserved", hdr->type);
+ }
+}
+
+static void ct3_build_cdat(CDATObject *cdat, Error **errp)
+{
+ g_autofree CDATTableHeader *cdat_header = NULL;
+ g_autofree CDATEntry *cdat_st = NULL;
+ uint8_t sum = 0;
+ int ent, i;
+
+ /* Use default table if fopen == NULL */
+ assert(cdat->build_cdat_table);
+
+ cdat_header = g_malloc0(sizeof(*cdat_header));
+ if (!cdat_header) {
+ error_setg(errp, "Failed to allocate CDAT header");
+ return;
+ }
+
+ cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private);
+
+ if (!cdat->built_buf_len) {
+ /* Build later as not all data available yet */
+ cdat->to_update = true;
+ return;
+ }
+ cdat->to_update = false;
+
+ cdat_st = g_malloc0(sizeof(*cdat_st) * (cdat->built_buf_len + 1));
+ if (!cdat_st) {
+ error_setg(errp, "Failed to allocate CDAT entry array");
+ return;
+ }
+
+ /* Entry 0 for CDAT header, starts with Entry 1 */
+ for (ent = 1; ent < cdat->built_buf_len + 1; ent++) {
+ CDATSubHeader *hdr = cdat->built_buf[ent - 1];
+ uint8_t *buf = (uint8_t *)cdat->built_buf[ent - 1];
+
+ cdat_st[ent].base = hdr;
+ cdat_st[ent].length = hdr->length;
+
+ cdat_header->length += hdr->length;
+ for (i = 0; i < hdr->length; i++) {
+ sum += buf[i];
+ }
+ }
+
+ /* CDAT header */
+ cdat_header->revision = CXL_CDAT_REV;
+ /* For now, no runtime updates */
+ cdat_header->sequence = 0;
+ cdat_header->length += sizeof(CDATTableHeader);
+ sum += cdat_header->revision + cdat_header->sequence +
+ cdat_header->length;
+ /* Sum of all bytes including checksum must be 0 */
+ cdat_header->checksum = ~sum + 1;
+
+ cdat_st[0].base = g_steal_pointer(&cdat_header);
+ cdat_st[0].length = sizeof(*cdat_header);
+ cdat->entry_len = 1 + cdat->built_buf_len;
+ cdat->entry = g_steal_pointer(&cdat_st);
+}
+
+static void ct3_load_cdat(CDATObject *cdat, Error **errp)
+{
+ g_autofree CDATEntry *cdat_st = NULL;
+ uint8_t sum = 0;
+ int num_ent;
+ int i = 0, ent = 1, file_size = 0;
+ CDATSubHeader *hdr;
+ FILE *fp = NULL;
+
+ /* Read CDAT file and create its cache */
+ fp = fopen(cdat->filename, "r");
+ if (!fp) {
+ error_setg(errp, "CDAT: Unable to open file");
+ return;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ file_size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ cdat->buf = g_malloc0(file_size);
+
+ if (fread(cdat->buf, file_size, 1, fp) == 0) {
+ error_setg(errp, "CDAT: File read failed");
+ return;
+ }
+
+ fclose(fp);
+
+ if (file_size < sizeof(CDATTableHeader)) {
+ error_setg(errp, "CDAT: File too short");
+ return;
+ }
+ i = sizeof(CDATTableHeader);
+ num_ent = 1;
+ while (i < file_size) {
+ hdr = (CDATSubHeader *)(cdat->buf + i);
+ cdat_len_check(hdr, errp);
+ i += hdr->length;
+ num_ent++;
+ }
+ if (i != file_size) {
+ error_setg(errp, "CDAT: File length missmatch");
+ return;
+ }
+
+ cdat_st = g_malloc0(sizeof(*cdat_st) * num_ent);
+ if (!cdat_st) {
+ error_setg(errp, "CDAT: Failed to allocate entry array");
+ return;
+ }
+
+ /* Set CDAT header, Entry = 0 */
+ cdat_st[0].base = cdat->buf;
+ cdat_st[0].length = sizeof(CDATTableHeader);
+ i = 0;
+
+ while (i < cdat_st[0].length) {
+ sum += cdat->buf[i++];
+ }
+
+ /* Read CDAT structures */
+ while (i < file_size) {
+ hdr = (CDATSubHeader *)(cdat->buf + i);
+ cdat_len_check(hdr, errp);
+
+ cdat_st[ent].base = hdr;
+ cdat_st[ent].length = hdr->length;
+
+ while (cdat->buf + i <
+ (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) {
+ assert(i < file_size);
+ sum += cdat->buf[i++];
+ }
+
+ ent++;
+ }
+
+ if (sum != 0) {
+ warn_report("CDAT: Found checksum mismatch in %s", cdat->filename);
+ }
+ cdat->entry_len = num_ent;
+ cdat->entry = g_steal_pointer(&cdat_st);
+}
+
+void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp)
+{
+ CDATObject *cdat = &cxl_cstate->cdat;
+
+ if (cdat->filename) {
+ ct3_load_cdat(cdat, errp);
+ } else {
+ ct3_build_cdat(cdat, errp);
+ }
+}
+
+void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp)
+{
+ CDATObject *cdat = &cxl_cstate->cdat;
+
+ if (cdat->to_update) {
+ ct3_build_cdat(cdat, errp);
+ }
+}
+
+void cxl_doe_cdat_release(CXLComponentState *cxl_cstate)
+{
+ CDATObject *cdat = &cxl_cstate->cdat;
+
+ free(cdat->entry);
+ if (cdat->built_buf) {
+ cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len,
+ cdat->private);
+ }
+ if (cdat->buf) {
+ free(cdat->buf);
+ }
+}
diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build
index f117b99949..cfa95ffd40 100644
--- a/hw/cxl/meson.build
+++ b/hw/cxl/meson.build
@@ -4,6 +4,7 @@ softmmu_ss.add(when: 'CONFIG_CXL',
'cxl-device-utils.c',
'cxl-mailbox-utils.c',
'cxl-host.c',
+ 'cxl-cdat.c',
),
if_false: files(
'cxl-host-stubs.c',
diff --git a/hw/display/acpi-vga-stub.c b/hw/display/acpi-vga-stub.c
new file mode 100644
index 0000000000..a9b0ecf76d
--- /dev/null
+++ b/hw/display/acpi-vga-stub.c
@@ -0,0 +1,7 @@
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi_aml_interface.h"
+#include "vga_int.h"
+
+void build_vga_aml(AcpiDevAmlIf *adev, Aml *scope)
+{
+}
diff --git a/hw/display/acpi-vga.c b/hw/display/acpi-vga.c
new file mode 100644
index 0000000000..f0e9ef1fcf
--- /dev/null
+++ b/hw/display/acpi-vga.c
@@ -0,0 +1,26 @@
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi_aml_interface.h"
+#include "hw/pci/pci.h"
+#include "vga_int.h"
+
+void build_vga_aml(AcpiDevAmlIf *adev, Aml *scope)
+{
+ int s3d = 0;
+ Aml *method;
+
+ if (object_dynamic_cast(OBJECT(adev), "qxl-vga")) {
+ s3d = 3;
+ }
+
+ method = aml_method("_S1D", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_int(0)));
+ aml_append(scope, method);
+
+ method = aml_method("_S2D", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_int(0)));
+ aml_append(scope, method);
+
+ method = aml_method("_S3D", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_return(aml_int(s3d)));
+ aml_append(scope, method);
+}
diff --git a/hw/display/meson.build b/hw/display/meson.build
index adc53dd8b6..7a725ed80e 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -38,10 +38,21 @@ softmmu_ss.add(when: 'CONFIG_NEXTCUBE', if_true: files('next-fb.c'))
specific_ss.add(when: 'CONFIG_VGA', if_true: files('vga.c'))
+if (config_all_devices.has_key('CONFIG_VGA_CIRRUS') or
+ config_all_devices.has_key('CONFIG_VGA_PCI') or
+ config_all_devices.has_key('CONFIG_VMWARE_VGA') or
+ config_all_devices.has_key('CONFIG_ATI_VGA')
+ )
+ softmmu_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'),
+ if_false: files('acpi-vga-stub.c'))
+endif
+
if config_all_devices.has_key('CONFIG_QXL')
qxl_ss = ss.source_set()
qxl_ss.add(when: 'CONFIG_QXL', if_true: [files('qxl.c', 'qxl-logger.c', 'qxl-render.c'),
pixman, spice])
+ qxl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'),
+ if_false: files('acpi-vga-stub.c'))
hw_display_modules += {'qxl': qxl_ss}
endif
@@ -52,6 +63,7 @@ softmmu_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c'))
softmmu_ss.add(when: [pixman, 'CONFIG_ATI_VGA'], if_true: files('ati.c', 'ati_2d.c', 'ati_dbg.c'))
+
if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
virtio_gpu_ss = ss.source_set()
virtio_gpu_ss.add(when: 'CONFIG_VIRTIO_GPU',
@@ -87,14 +99,19 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA')
if_true: [files('virtio-vga.c'), pixman])
virtio_vga_ss.add(when: 'CONFIG_VHOST_USER_VGA',
if_true: files('vhost-user-vga.c'))
+ virtio_vga_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'),
+ if_false: files('acpi-vga-stub.c'))
hw_display_modules += {'virtio-vga': virtio_vga_ss}
virtio_vga_gl_ss = ss.source_set()
virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl],
if_true: [files('virtio-vga-gl.c'), pixman])
+ virtio_vga_gl_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'),
+ if_false: files('acpi-vga-stub.c'))
hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss}
endif
specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_lcdc.c'))
+softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-vga-stub.c'))
modules += { 'hw-display': hw_display_modules }
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index 3e5bc259f7..9a91de7ed1 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -35,6 +35,7 @@
#include "hw/loader.h"
#include "hw/display/edid.h"
#include "qom/object.h"
+#include "hw/acpi/acpi_aml_interface.h"
enum vga_pci_flags {
PCI_VGA_FLAG_ENABLE_MMIO = 1,
@@ -354,11 +355,13 @@ static void vga_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
k->vendor_id = PCI_VENDOR_ID_QEMU;
k->device_id = PCI_DEVICE_ID_QEMU_VGA;
dc->vmsd = &vmstate_vga_pci;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+ adevc->build_dev_aml = build_vga_aml;
}
static const TypeInfo vga_pci_type_info = {
@@ -369,6 +372,7 @@ static const TypeInfo vga_pci_type_info = {
.class_init = vga_pci_class_init,
.interfaces = (InterfaceInfo[]) {
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { TYPE_ACPI_DEV_AML_IF },
{ },
},
};
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 305e700014..330406ad9c 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -30,6 +30,7 @@
#include "ui/console.h"
#include "hw/display/bochs-vbe.h"
+#include "hw/acpi/acpi_aml_interface.h"
#define ST01_V_RETRACE 0x08
#define ST01_DISP_ENABLE 0x01
@@ -195,4 +196,5 @@ void pci_std_vga_mmio_region_init(VGACommonState *s,
MemoryRegion *subs,
bool qext, bool edid);
+void build_vga_aml(AcpiDevAmlIf *adev, Aml *scope);
#endif
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 4f54b61904..d9eaa5fc4d 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -60,6 +60,7 @@
#include "hw/i386/fw_cfg.h"
#include "hw/i386/ich9.h"
#include "hw/pci/pci_bus.h"
+#include "hw/pci-host/i440fx.h"
#include "hw/pci-host/q35.h"
#include "hw/i386/x86-iommu.h"
@@ -112,7 +113,6 @@ typedef struct AcpiPmInfo {
} AcpiPmInfo;
typedef struct AcpiMiscInfo {
- bool is_piix4;
bool has_hpet;
#ifdef CONFIG_TPM
TPMVersion tpm_version;
@@ -121,13 +121,6 @@ typedef struct AcpiMiscInfo {
unsigned dsdt_size;
} AcpiMiscInfo;
-typedef struct AcpiBuildPciBusHotplugState {
- GArray *device_table;
- GArray *notify_table;
- struct AcpiBuildPciBusHotplugState *parent;
- bool pcihp_bridge_en;
-} AcpiBuildPciBusHotplugState;
-
typedef struct FwCfgTPMConfig {
uint32_t tpmppi_address;
uint8_t tpm_version;
@@ -288,17 +281,6 @@ static void acpi_get_pm_info(MachineState *machine, AcpiPmInfo *pm)
static void acpi_get_misc_info(AcpiMiscInfo *info)
{
- Object *piix = object_resolve_type_unambiguous(TYPE_PIIX4_PM);
- Object *lpc = object_resolve_type_unambiguous(TYPE_ICH9_LPC_DEVICE);
- assert(!!piix != !!lpc);
-
- if (piix) {
- info->is_piix4 = true;
- }
- if (lpc) {
- info->is_piix4 = false;
- }
-
info->has_hpet = hpet_find();
#ifdef CONFIG_TPM
info->tpm_version = tpm_get_version(tpm_find());
@@ -430,18 +412,11 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
bool hotpluggbale_slot = false;
bool bridge_in_acpi = false;
bool cold_plugged_bridge = false;
- bool is_vga = false;
if (pdev) {
pc = PCI_DEVICE_GET_CLASS(pdev);
dc = DEVICE_GET_CLASS(pdev);
- if (pc->class_id == PCI_CLASS_BRIDGE_ISA) {
- continue;
- }
-
- is_vga = pc->class_id == PCI_CLASS_DISPLAY_VGA;
-
/*
* Cold plugged bridges aren't themselves hot-pluggable.
* Hotplugged bridges *are* hot-pluggable.
@@ -455,9 +430,10 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
/*
* allow describing coldplugged bridges in ACPI even if they are not
* on function 0, as they are not unpluggable, for all other devices
- * generate description only for function 0 per slot
+ * generate description only for function 0 per slot, and for other
+ * functions if device on function provides its own AML
*/
- if (func && !bridge_in_acpi) {
+ if (func && !bridge_in_acpi && !get_dev_aml_func(DEVICE(pdev))) {
continue;
}
} else {
@@ -489,28 +465,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
aml_append(dev, aml_pci_device_dsm());
}
- if (is_vga) {
- /* add VGA specific AML methods */
- int s3d;
-
- if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) {
- s3d = 3;
- } else {
- s3d = 0;
- }
-
- method = aml_method("_S1D", 0, AML_NOTSERIALIZED);
- aml_append(method, aml_return(aml_int(0)));
- aml_append(dev, method);
-
- method = aml_method("_S2D", 0, AML_NOTSERIALIZED);
- aml_append(method, aml_return(aml_int(0)));
- aml_append(dev, method);
-
- method = aml_method("_S3D", 0, AML_NOTSERIALIZED);
- aml_append(method, aml_return(aml_int(s3d)));
- aml_append(dev, method);
- }
+ call_dev_aml_func(DEVICE(pdev), dev);
bridge_in_acpi = cold_plugged_bridge && pcihp_bridge_en;
if (bridge_in_acpi) {
@@ -1030,7 +985,6 @@ static void build_piix4_pci0_int(Aml *table)
{
Aml *dev;
Aml *crs;
- Aml *field;
Aml *method;
uint32_t irqs;
Aml *sb_scope = aml_scope("_SB");
@@ -1039,13 +993,6 @@ static void build_piix4_pci0_int(Aml *table)
aml_append(pci0_scope, build_prt(true));
aml_append(sb_scope, pci0_scope);
- field = aml_field("PCI0.ISA.P40C", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
- aml_append(field, aml_named_field("PRQ0", 8));
- aml_append(field, aml_named_field("PRQ1", 8));
- aml_append(field, aml_named_field("PRQ2", 8));
- aml_append(field, aml_named_field("PRQ3", 8));
- aml_append(sb_scope, field);
-
aml_append(sb_scope, build_irq_status_method());
aml_append(sb_scope, build_iqcr_method(true));
@@ -1149,7 +1096,6 @@ static Aml *build_q35_routing_table(const char *str)
static void build_q35_pci0_int(Aml *table)
{
- Aml *field;
Aml *method;
Aml *sb_scope = aml_scope("_SB");
Aml *pci0_scope = aml_scope("PCI0");
@@ -1186,18 +1132,6 @@ static void build_q35_pci0_int(Aml *table)
aml_append(pci0_scope, method);
aml_append(sb_scope, pci0_scope);
- field = aml_field("PCI0.ISA.PIRQ", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
- aml_append(field, aml_named_field("PRQA", 8));
- aml_append(field, aml_named_field("PRQB", 8));
- aml_append(field, aml_named_field("PRQC", 8));
- aml_append(field, aml_named_field("PRQD", 8));
- aml_append(field, aml_reserved_field(0x20));
- aml_append(field, aml_named_field("PRQE", 8));
- aml_append(field, aml_named_field("PRQF", 8));
- aml_append(field, aml_named_field("PRQG", 8));
- aml_append(field, aml_named_field("PRQH", 8));
- aml_append(sb_scope, field);
-
aml_append(sb_scope, build_irq_status_method());
aml_append(sb_scope, build_iqcr_method(false));
@@ -1262,54 +1196,6 @@ static Aml *build_q35_dram_controller(const AcpiMcfgInfo *mcfg)
return dev;
}
-static void build_q35_isa_bridge(Aml *table)
-{
- Aml *dev;
- Aml *scope;
- Object *obj;
- bool ambiguous;
-
- /*
- * temporarily fish out isa bridge, build_q35_isa_bridge() will be dropped
- * once PCI is converted to AcpiDevAmlIf and would be ble to generate
- * AML for bridge itself
- */
- obj = object_resolve_path_type("", TYPE_ICH9_LPC_DEVICE, &ambiguous);
- assert(obj && !ambiguous);
-
- scope = aml_scope("_SB.PCI0");
- dev = aml_device("ISA");
- aml_append(dev, aml_name_decl("_ADR", aml_int(0x001F0000)));
-
- call_dev_aml_func(DEVICE(obj), dev);
- aml_append(scope, dev);
- aml_append(table, scope);
-}
-
-static void build_piix4_isa_bridge(Aml *table)
-{
- Aml *dev;
- Aml *scope;
- Object *obj;
- bool ambiguous;
-
- /*
- * temporarily fish out isa bridge, build_piix4_isa_bridge() will be dropped
- * once PCI is converted to AcpiDevAmlIf and would be ble to generate
- * AML for bridge itself
- */
- obj = object_resolve_path_type("", TYPE_PIIX3_PCI_DEVICE, &ambiguous);
- assert(obj && !ambiguous);
-
- scope = aml_scope("_SB.PCI0");
- dev = aml_device("ISA");
- aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010000)));
-
- call_dev_aml_func(DEVICE(obj), dev);
- aml_append(scope, dev);
- aml_append(table, scope);
-}
-
static void build_x86_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr)
{
Aml *scope;
@@ -1416,25 +1302,6 @@ static Aml *build_q35_osc_method(bool enable_native_pcie_hotplug)
return method;
}
-static void build_smb0(Aml *table, int devnr, int func)
-{
- Aml *scope = aml_scope("_SB.PCI0");
- Aml *dev = aml_device("SMB0");
- bool ambiguous;
- Object *obj;
- /*
- * temporarily fish out device hosting SMBUS, build_smb0 will be gone once
- * PCI enumeration will be switched to call_dev_aml_func()
- */
- obj = object_resolve_path_type("", TYPE_ICH9_SMB_DEVICE, &ambiguous);
- assert(obj && !ambiguous);
-
- aml_append(dev, aml_name_decl("_ADR", aml_int(devnr << 16 | func)));
- call_dev_aml_func(DEVICE(obj), dev);
- aml_append(scope, dev);
- aml_append(table, scope);
-}
-
static void build_acpi0017(Aml *table)
{
Aml *dev, *scope, *method;
@@ -1456,6 +1323,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
AcpiPmInfo *pm, AcpiMiscInfo *misc,
Range *pci_hole, Range *pci_hole64, MachineState *machine)
{
+ Object *i440fx = object_resolve_type_unambiguous(TYPE_I440FX_PCI_HOST_BRIDGE);
+ Object *q35 = object_resolve_type_unambiguous(TYPE_Q35_HOST_DEVICE);
CrsRangeEntry *entry;
Aml *dsdt, *sb_scope, *scope, *dev, *method, *field, *pkg, *crs;
CrsRangeSet crs_range_set;
@@ -1476,11 +1345,13 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = x86ms->oem_id,
.oem_table_id = x86ms->oem_table_id };
+ assert(!!i440fx != !!q35);
+
acpi_table_begin(&table, table_data);
dsdt = init_aml_allocator();
build_dbg_aml(dsdt);
- if (misc->is_piix4) {
+ if (i440fx) {
sb_scope = aml_scope("_SB");
dev = aml_device("PCI0");
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03")));
@@ -1489,12 +1360,11 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(sb_scope, dev);
aml_append(dsdt, sb_scope);
- build_piix4_isa_bridge(dsdt);
if (pm->pcihp_bridge_en || pm->pcihp_root_en) {
build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base);
}
build_piix4_pci0_int(dsdt);
- } else {
+ } else if (q35) {
sb_scope = aml_scope("_SB");
dev = aml_device("PCI0");
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
@@ -1534,14 +1404,10 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(dsdt, sb_scope);
- build_q35_isa_bridge(dsdt);
if (pm->pcihp_bridge_en) {
build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base);
}
build_q35_pci0_int(dsdt);
- if (pcms->smbus) {
- build_smb0(dsdt, ICH9_SMB_DEV, ICH9_SMB_FUNC);
- }
}
if (misc->has_hpet) {
@@ -1554,6 +1420,18 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
aml_append(dsdt, sb_scope);
}
+ scope = aml_scope("_GPE");
+ {
+ aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
+ if (machine->nvdimms_state->is_enabled) {
+ method = aml_method("_E04", 0, AML_NOTSERIALIZED);
+ aml_append(method, aml_notify(aml_name("\\_SB.NVDR"),
+ aml_int(0x80)));
+ aml_append(scope, method);
+ }
+ }
+ aml_append(dsdt, scope);
+
if (pcmc->legacy_cpu_hotplug) {
build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base);
} else {
@@ -1572,28 +1450,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
pcms->memhp_io_base);
}
- scope = aml_scope("_GPE");
- {
- aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
-
- if (pm->pcihp_bridge_en || pm->pcihp_root_en) {
- method = aml_method("_E01", 0, AML_NOTSERIALIZED);
- aml_append(method,
- aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
- aml_append(method, aml_call0("\\_SB.PCI0.PCNT"));
- aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK")));
- aml_append(scope, method);
- }
-
- if (machine->nvdimms_state->is_enabled) {
- method = aml_method("_E04", 0, AML_NOTSERIALIZED);
- aml_append(method, aml_notify(aml_name("\\_SB.NVDR"),
- aml_int(0x80)));
- aml_append(scope, method);
- }
- }
- aml_append(dsdt, scope);
-
crs_range_set_init(&crs_range_set);
bus = PC_MACHINE(machine)->bus;
if (bus) {
@@ -1872,6 +1728,19 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
}
aml_append(dsdt, sb_scope);
+ if (pm->pcihp_bridge_en || pm->pcihp_root_en) {
+ scope = aml_scope("_GPE");
+ {
+ method = aml_method("_E01", 0, AML_NOTSERIALIZED);
+ aml_append(method,
+ aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF));
+ aml_append(method, aml_call0("\\_SB.PCI0.PCNT"));
+ aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK")));
+ aml_append(scope, method);
+ }
+ aml_append(dsdt, scope);
+ }
+
/* copy AML table into ACPI tables blob and patch header there */
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
acpi_table_end(linker, &table);
diff --git a/hw/i386/e820_memory_layout.c b/hw/i386/e820_memory_layout.c
index bcf9eaf837..06970ac44a 100644
--- a/hw/i386/e820_memory_layout.c
+++ b/hw/i386/e820_memory_layout.c
@@ -11,29 +11,11 @@
#include "e820_memory_layout.h"
static size_t e820_entries;
-struct e820_table e820_reserve;
struct e820_entry *e820_table;
int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
{
- int index = le32_to_cpu(e820_reserve.count);
- struct e820_entry *entry;
-
- if (type != E820_RAM) {
- /* old FW_CFG_E820_TABLE entry -- reservations only */
- if (index >= E820_NR_ENTRIES) {
- return -EBUSY;
- }
- entry = &e820_reserve.entry[index++];
-
- entry->address = cpu_to_le64(address);
- entry->length = cpu_to_le64(length);
- entry->type = cpu_to_le32(type);
-
- e820_reserve.count = cpu_to_le32(index);
- }
-
- /* new "etc/e820" file -- include ram too */
+ /* new "etc/e820" file -- include ram and reserved entries */
e820_table = g_renew(struct e820_entry, e820_table, e820_entries + 1);
e820_table[e820_entries].address = cpu_to_le64(address);
e820_table[e820_entries].length = cpu_to_le64(length);
diff --git a/hw/i386/e820_memory_layout.h b/hw/i386/e820_memory_layout.h
index 04f93780f9..7c239aa033 100644
--- a/hw/i386/e820_memory_layout.h
+++ b/hw/i386/e820_memory_layout.h
@@ -16,20 +16,12 @@
#define E820_NVS 4
#define E820_UNUSABLE 5
-#define E820_NR_ENTRIES 16
-
struct e820_entry {
uint64_t address;
uint64_t length;
uint32_t type;
} QEMU_PACKED __attribute((__aligned__(4)));
-struct e820_table {
- uint32_t count;
- struct e820_entry entry[E820_NR_ENTRIES];
-} QEMU_PACKED __attribute((__aligned__(4)));
-
-extern struct e820_table e820_reserve;
extern struct e820_entry *e820_table;
int e820_add_entry(uint64_t address, uint64_t length, uint32_t type);
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index a283785a8d..72a42f3c66 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -36,7 +36,6 @@ const char *fw_cfg_arch_key_name(uint16_t key)
{FW_CFG_ACPI_TABLES, "acpi_tables"},
{FW_CFG_SMBIOS_ENTRIES, "smbios_entries"},
{FW_CFG_IRQ0_OVERRIDE, "irq0_override"},
- {FW_CFG_E820_TABLE, "e820_table"},
{FW_CFG_HPET, "hpet"},
};
@@ -127,8 +126,6 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms,
#endif
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
- &e820_reserve, sizeof(e820_reserve));
fw_cfg_add_file(fw_cfg, "etc/e820", e820_table,
sizeof(struct e820_entry) * e820_get_num_entries());
diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h
index 275f15c1c5..86ca7c1c0c 100644
--- a/hw/i386/fw_cfg.h
+++ b/hw/i386/fw_cfg.h
@@ -17,7 +17,6 @@
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
-#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3)
#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4)
FWCfgState *fw_cfg_arch_create(MachineState *ms,
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index 6524c2ee32..a08ee85edf 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -49,17 +49,24 @@
/* pe operations */
#define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT)
#define VTD_PE_GET_LEVEL(pe) (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW))
-#define VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write) {\
- if (ret_fr) { \
- ret_fr = -ret_fr; \
- if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { \
- trace_vtd_fault_disabled(); \
- } else { \
- vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); \
- } \
- goto error; \
- } \
-}
+
+/*
+ * PCI bus number (or SID) is not reliable since the device is usaully
+ * initalized before guest can configure the PCI bridge
+ * (SECONDARY_BUS_NUMBER).
+ */
+struct vtd_as_key {
+ PCIBus *bus;
+ uint8_t devfn;
+ uint32_t pasid;
+};
+
+struct vtd_iotlb_key {
+ uint64_t gfn;
+ uint32_t pasid;
+ uint32_t level;
+ uint16_t sid;
+};
static void vtd_address_space_refresh_all(IntelIOMMUState *s);
static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n);
@@ -200,14 +207,46 @@ static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as)
}
/* GHashTable functions */
-static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2)
+static gboolean vtd_iotlb_equal(gconstpointer v1, gconstpointer v2)
{
- return *((const uint64_t *)v1) == *((const uint64_t *)v2);
+ const struct vtd_iotlb_key *key1 = v1;
+ const struct vtd_iotlb_key *key2 = v2;
+
+ return key1->sid == key2->sid &&
+ key1->pasid == key2->pasid &&
+ key1->level == key2->level &&
+ key1->gfn == key2->gfn;
+}
+
+static guint vtd_iotlb_hash(gconstpointer v)
+{
+ const struct vtd_iotlb_key *key = v;
+
+ return key->gfn | ((key->sid) << VTD_IOTLB_SID_SHIFT) |
+ (key->level) << VTD_IOTLB_LVL_SHIFT |
+ (key->pasid) << VTD_IOTLB_PASID_SHIFT;
}
-static guint vtd_uint64_hash(gconstpointer v)
+static gboolean vtd_as_equal(gconstpointer v1, gconstpointer v2)
+{
+ const struct vtd_as_key *key1 = v1;
+ const struct vtd_as_key *key2 = v2;
+
+ return (key1->bus == key2->bus) && (key1->devfn == key2->devfn) &&
+ (key1->pasid == key2->pasid);
+}
+
+/*
+ * Note that we use pointer to PCIBus as the key, so hashing/shifting
+ * based on the pointer value is intended. Note that we deal with
+ * collisions through vtd_as_equal().
+ */
+static guint vtd_as_hash(gconstpointer v)
{
- return (guint)*(const uint64_t *)v;
+ const struct vtd_as_key *key = v;
+ guint value = (guint)(uintptr_t)key->bus;
+
+ return (guint)(value << 8 | key->devfn);
}
static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value,
@@ -248,22 +287,14 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value,
static void vtd_reset_context_cache_locked(IntelIOMMUState *s)
{
VTDAddressSpace *vtd_as;
- VTDBus *vtd_bus;
- GHashTableIter bus_it;
- uint32_t devfn_it;
+ GHashTableIter as_it;
trace_vtd_context_cache_reset();
- g_hash_table_iter_init(&bus_it, s->vtd_as_by_busptr);
+ g_hash_table_iter_init(&as_it, s->vtd_address_spaces);
- while (g_hash_table_iter_next (&bus_it, NULL, (void**)&vtd_bus)) {
- for (devfn_it = 0; devfn_it < PCI_DEVFN_MAX; ++devfn_it) {
- vtd_as = vtd_bus->dev_as[devfn_it];
- if (!vtd_as) {
- continue;
- }
- vtd_as->context_cache_entry.context_cache_gen = 0;
- }
+ while (g_hash_table_iter_next(&as_it, NULL, (void **)&vtd_as)) {
+ vtd_as->context_cache_entry.context_cache_gen = 0;
}
s->context_cache_gen = 1;
}
@@ -290,13 +321,6 @@ static void vtd_reset_caches(IntelIOMMUState *s)
vtd_iommu_unlock(s);
}
-static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id,
- uint32_t level)
-{
- return gfn | ((uint64_t)(source_id) << VTD_IOTLB_SID_SHIFT) |
- ((uint64_t)(level) << VTD_IOTLB_LVL_SHIFT);
-}
-
static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level)
{
return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K;
@@ -304,15 +328,17 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level)
/* Must be called with IOMMU lock held */
static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id,
- hwaddr addr)
+ uint32_t pasid, hwaddr addr)
{
+ struct vtd_iotlb_key key;
VTDIOTLBEntry *entry;
- uint64_t key;
int level;
for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) {
- key = vtd_get_iotlb_key(vtd_get_iotlb_gfn(addr, level),
- source_id, level);
+ key.gfn = vtd_get_iotlb_gfn(addr, level);
+ key.level = level;
+ key.sid = source_id;
+ key.pasid = pasid;
entry = g_hash_table_lookup(s->iotlb, &key);
if (entry) {
goto out;
@@ -326,10 +352,11 @@ out:
/* Must be with IOMMU lock held */
static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
uint16_t domain_id, hwaddr addr, uint64_t slpte,
- uint8_t access_flags, uint32_t level)
+ uint8_t access_flags, uint32_t level,
+ uint32_t pasid)
{
VTDIOTLBEntry *entry = g_malloc(sizeof(*entry));
- uint64_t *key = g_malloc(sizeof(*key));
+ struct vtd_iotlb_key *key = g_malloc(sizeof(*key));
uint64_t gfn = vtd_get_iotlb_gfn(addr, level);
trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id);
@@ -343,7 +370,13 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id,
entry->slpte = slpte;
entry->access_flags = access_flags;
entry->mask = vtd_slpt_level_page_mask(level);
- *key = vtd_get_iotlb_key(gfn, source_id, level);
+ entry->pasid = pasid;
+
+ key->gfn = gfn;
+ key->sid = source_id;
+ key->level = level;
+ key->pasid = pasid;
+
g_hash_table_replace(s->iotlb, key, entry);
}
@@ -436,7 +469,8 @@ static void vtd_set_frcd_and_update_ppf(IntelIOMMUState *s, uint16_t index)
/* Must not update F field now, should be done later */
static void vtd_record_frcd(IntelIOMMUState *s, uint16_t index,
uint16_t source_id, hwaddr addr,
- VTDFaultReason fault, bool is_write)
+ VTDFaultReason fault, bool is_write,
+ bool is_pasid, uint32_t pasid)
{
uint64_t hi = 0, lo;
hwaddr frcd_reg_addr = DMAR_FRCD_REG_OFFSET + (((uint64_t)index) << 4);
@@ -444,7 +478,8 @@ static void vtd_record_frcd(IntelIOMMUState *s, uint16_t index,
assert(index < DMAR_FRCD_REG_NR);
lo = VTD_FRCD_FI(addr);
- hi = VTD_FRCD_SID(source_id) | VTD_FRCD_FR(fault);
+ hi = VTD_FRCD_SID(source_id) | VTD_FRCD_FR(fault) |
+ VTD_FRCD_PV(pasid) | VTD_FRCD_PP(is_pasid);
if (!is_write) {
hi |= VTD_FRCD_T;
}
@@ -475,7 +510,8 @@ static bool vtd_try_collapse_fault(IntelIOMMUState *s, uint16_t source_id)
/* Log and report an DMAR (address translation) fault to software */
static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id,
hwaddr addr, VTDFaultReason fault,
- bool is_write)
+ bool is_write, bool is_pasid,
+ uint32_t pasid)
{
uint32_t fsts_reg = vtd_get_long_raw(s, DMAR_FSTS_REG);
@@ -502,7 +538,8 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id,
return;
}
- vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault, is_write);
+ vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault,
+ is_write, is_pasid, pasid);
if (fsts_reg & VTD_FSTS_PPF) {
error_report_once("There are pending faults already, "
@@ -807,13 +844,15 @@ static int vtd_get_pe_from_pasid_table(IntelIOMMUState *s,
static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s,
VTDContextEntry *ce,
- VTDPASIDEntry *pe)
+ VTDPASIDEntry *pe,
+ uint32_t pasid)
{
- uint32_t pasid;
dma_addr_t pasid_dir_base;
int ret = 0;
- pasid = VTD_CE_GET_RID2PASID(ce);
+ if (pasid == PCI_NO_PASID) {
+ pasid = VTD_CE_GET_RID2PASID(ce);
+ }
pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce);
ret = vtd_get_pe_from_pasid_table(s, pasid_dir_base, pasid, pe);
@@ -822,15 +861,17 @@ static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s,
static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s,
VTDContextEntry *ce,
- bool *pe_fpd_set)
+ bool *pe_fpd_set,
+ uint32_t pasid)
{
int ret;
- uint32_t pasid;
dma_addr_t pasid_dir_base;
VTDPASIDDirEntry pdire;
VTDPASIDEntry pe;
- pasid = VTD_CE_GET_RID2PASID(ce);
+ if (pasid == PCI_NO_PASID) {
+ pasid = VTD_CE_GET_RID2PASID(ce);
+ }
pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce);
/*
@@ -876,12 +917,13 @@ static inline uint32_t vtd_ce_get_level(VTDContextEntry *ce)
}
static uint32_t vtd_get_iova_level(IntelIOMMUState *s,
- VTDContextEntry *ce)
+ VTDContextEntry *ce,
+ uint32_t pasid)
{
VTDPASIDEntry pe;
if (s->root_scalable) {
- vtd_ce_get_rid2pasid_entry(s, ce, &pe);
+ vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
return VTD_PE_GET_LEVEL(&pe);
}
@@ -894,12 +936,13 @@ static inline uint32_t vtd_ce_get_agaw(VTDContextEntry *ce)
}
static uint32_t vtd_get_iova_agaw(IntelIOMMUState *s,
- VTDContextEntry *ce)
+ VTDContextEntry *ce,
+ uint32_t pasid)
{
VTDPASIDEntry pe;
if (s->root_scalable) {
- vtd_ce_get_rid2pasid_entry(s, ce, &pe);
+ vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
return 30 + ((pe.val[0] >> 2) & VTD_SM_PASID_ENTRY_AW) * 9;
}
@@ -941,31 +984,33 @@ static inline bool vtd_ce_type_check(X86IOMMUState *x86_iommu,
}
static inline uint64_t vtd_iova_limit(IntelIOMMUState *s,
- VTDContextEntry *ce, uint8_t aw)
+ VTDContextEntry *ce, uint8_t aw,
+ uint32_t pasid)
{
- uint32_t ce_agaw = vtd_get_iova_agaw(s, ce);
+ uint32_t ce_agaw = vtd_get_iova_agaw(s, ce, pasid);
return 1ULL << MIN(ce_agaw, aw);
}
/* Return true if IOVA passes range check, otherwise false. */
static inline bool vtd_iova_range_check(IntelIOMMUState *s,
uint64_t iova, VTDContextEntry *ce,
- uint8_t aw)
+ uint8_t aw, uint32_t pasid)
{
/*
* Check if @iova is above 2^X-1, where X is the minimum of MGAW
* in CAP_REG and AW in context-entry.
*/
- return !(iova & ~(vtd_iova_limit(s, ce, aw) - 1));
+ return !(iova & ~(vtd_iova_limit(s, ce, aw, pasid) - 1));
}
static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s,
- VTDContextEntry *ce)
+ VTDContextEntry *ce,
+ uint32_t pasid)
{
VTDPASIDEntry pe;
if (s->root_scalable) {
- vtd_ce_get_rid2pasid_entry(s, ce, &pe);
+ vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR;
}
@@ -993,50 +1038,25 @@ static bool vtd_slpte_nonzero_rsvd(uint64_t slpte, uint32_t level)
return slpte & rsvd_mask;
}
-/* Find the VTD address space associated with a given bus number */
-static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num)
-{
- VTDBus *vtd_bus = s->vtd_as_by_bus_num[bus_num];
- GHashTableIter iter;
-
- if (vtd_bus) {
- return vtd_bus;
- }
-
- /*
- * Iterate over the registered buses to find the one which
- * currently holds this bus number and update the bus_num
- * lookup table.
- */
- g_hash_table_iter_init(&iter, s->vtd_as_by_busptr);
- while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) {
- if (pci_bus_num(vtd_bus->bus) == bus_num) {
- s->vtd_as_by_bus_num[bus_num] = vtd_bus;
- return vtd_bus;
- }
- }
-
- return NULL;
-}
-
/* Given the @iova, get relevant @slptep. @slpte_level will be the last level
* of the translation, can be used for deciding the size of large page.
*/
static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
uint64_t iova, bool is_write,
uint64_t *slptep, uint32_t *slpte_level,
- bool *reads, bool *writes, uint8_t aw_bits)
+ bool *reads, bool *writes, uint8_t aw_bits,
+ uint32_t pasid)
{
- dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce);
- uint32_t level = vtd_get_iova_level(s, ce);
+ dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid);
+ uint32_t level = vtd_get_iova_level(s, ce, pasid);
uint32_t offset;
uint64_t slpte;
uint64_t access_right_check;
uint64_t xlat, size;
- if (!vtd_iova_range_check(s, iova, ce, aw_bits)) {
- error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 ")",
- __func__, iova);
+ if (!vtd_iova_range_check(s, iova, ce, aw_bits, pasid)) {
+ error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 ","
+ "pasid=0x%" PRIx32 ")", __func__, iova, pasid);
return -VTD_FR_ADDR_BEYOND_MGAW;
}
@@ -1049,8 +1069,9 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
if (slpte == (uint64_t)-1) {
error_report_once("%s: detected read error on DMAR slpte "
- "(iova=0x%" PRIx64 ")", __func__, iova);
- if (level == vtd_get_iova_level(s, ce)) {
+ "(iova=0x%" PRIx64 ", pasid=0x%" PRIx32 ")",
+ __func__, iova, pasid);
+ if (level == vtd_get_iova_level(s, ce, pasid)) {
/* Invalid programming of context-entry */
return -VTD_FR_CONTEXT_ENTRY_INV;
} else {
@@ -1062,15 +1083,16 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
if (!(slpte & access_right_check)) {
error_report_once("%s: detected slpte permission error "
"(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", "
- "slpte=0x%" PRIx64 ", write=%d)", __func__,
- iova, level, slpte, is_write);
+ "slpte=0x%" PRIx64 ", write=%d, pasid=0x%"
+ PRIx32 ")", __func__, iova, level,
+ slpte, is_write, pasid);
return is_write ? -VTD_FR_WRITE : -VTD_FR_READ;
}
if (vtd_slpte_nonzero_rsvd(slpte, level)) {
error_report_once("%s: detected splte reserve non-zero "
"iova=0x%" PRIx64 ", level=0x%" PRIx32
- "slpte=0x%" PRIx64 ")", __func__, iova,
- level, slpte);
+ "slpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")",
+ __func__, iova, level, slpte, pasid);
return -VTD_FR_PAGING_ENTRY_RSVD;
}
@@ -1098,9 +1120,10 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce,
error_report_once("%s: xlat address is in interrupt range "
"(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", "
"slpte=0x%" PRIx64 ", write=%d, "
- "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ")",
+ "xlat=0x%" PRIx64 ", size=0x%" PRIx64 ", "
+ "pasid=0x%" PRIx32 ")",
__func__, iova, level, slpte, is_write,
- xlat, size);
+ xlat, size, pasid);
return s->scalable_mode ? -VTD_FR_SM_INTERRUPT_ADDR :
-VTD_FR_INTERRUPT_ADDR;
}
@@ -1314,18 +1337,19 @@ next:
*/
static int vtd_page_walk(IntelIOMMUState *s, VTDContextEntry *ce,
uint64_t start, uint64_t end,
- vtd_page_walk_info *info)
+ vtd_page_walk_info *info,
+ uint32_t pasid)
{
- dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce);
- uint32_t level = vtd_get_iova_level(s, ce);
+ dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid);
+ uint32_t level = vtd_get_iova_level(s, ce, pasid);
- if (!vtd_iova_range_check(s, start, ce, info->aw)) {
+ if (!vtd_iova_range_check(s, start, ce, info->aw, pasid)) {
return -VTD_FR_ADDR_BEYOND_MGAW;
}
- if (!vtd_iova_range_check(s, end, ce, info->aw)) {
+ if (!vtd_iova_range_check(s, end, ce, info->aw, pasid)) {
/* Fix end so that it reaches the maximum */
- end = vtd_iova_limit(s, ce, info->aw);
+ end = vtd_iova_limit(s, ce, info->aw, pasid);
}
return vtd_page_walk_level(addr, start, end, level, true, true, info);
@@ -1393,7 +1417,7 @@ static int vtd_ce_rid2pasid_check(IntelIOMMUState *s,
* has valid rid2pasid setting, which includes valid
* rid2pasid field and corresponding pasid entry setting
*/
- return vtd_ce_get_rid2pasid_entry(s, ce, &pe);
+ return vtd_ce_get_rid2pasid_entry(s, ce, &pe, PCI_NO_PASID);
}
/* Map a device to its corresponding domain (context-entry) */
@@ -1476,12 +1500,13 @@ static int vtd_sync_shadow_page_hook(IOMMUTLBEvent *event,
}
static uint16_t vtd_get_domain_id(IntelIOMMUState *s,
- VTDContextEntry *ce)
+ VTDContextEntry *ce,
+ uint32_t pasid)
{
VTDPASIDEntry pe;
if (s->root_scalable) {
- vtd_ce_get_rid2pasid_entry(s, ce, &pe);
+ vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
return VTD_SM_PASID_ENTRY_DID(pe.val[1]);
}
@@ -1499,10 +1524,10 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as,
.notify_unmap = true,
.aw = s->aw_bits,
.as = vtd_as,
- .domain_id = vtd_get_domain_id(s, ce),
+ .domain_id = vtd_get_domain_id(s, ce, vtd_as->pasid),
};
- return vtd_page_walk(s, ce, addr, addr + size, &info);
+ return vtd_page_walk(s, ce, addr, addr + size, &info, vtd_as->pasid);
}
static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as)
@@ -1546,16 +1571,19 @@ static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as)
* 1st-level translation or 2nd-level translation, it depends
* on PGTT setting.
*/
-static bool vtd_dev_pt_enabled(IntelIOMMUState *s, VTDContextEntry *ce)
+static bool vtd_dev_pt_enabled(IntelIOMMUState *s, VTDContextEntry *ce,
+ uint32_t pasid)
{
VTDPASIDEntry pe;
int ret;
if (s->root_scalable) {
- ret = vtd_ce_get_rid2pasid_entry(s, ce, &pe);
+ ret = vtd_ce_get_rid2pasid_entry(s, ce, &pe, pasid);
if (ret) {
- error_report_once("%s: vtd_ce_get_rid2pasid_entry error: %"PRId32,
- __func__, ret);
+ /*
+ * This error is guest triggerable. We should assumt PT
+ * not enabled for safety.
+ */
return false;
}
return (VTD_PE_GET_TYPE(&pe) == VTD_SM_PASID_ENTRY_PT);
@@ -1569,14 +1597,12 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as)
{
IntelIOMMUState *s;
VTDContextEntry ce;
- int ret;
assert(as);
s = as->iommu_state;
- ret = vtd_dev_to_context_entry(s, pci_bus_num(as->bus),
- as->devfn, &ce);
- if (ret) {
+ if (vtd_dev_to_context_entry(s, pci_bus_num(as->bus), as->devfn,
+ &ce)) {
/*
* Possibly failed to parse the context entry for some reason
* (e.g., during init, or any guest configuration errors on
@@ -1586,19 +1612,20 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as)
return false;
}
- return vtd_dev_pt_enabled(s, &ce);
+ return vtd_dev_pt_enabled(s, &ce, as->pasid);
}
/* Return whether the device is using IOMMU translation. */
static bool vtd_switch_address_space(VTDAddressSpace *as)
{
- bool use_iommu;
+ bool use_iommu, pt;
/* Whether we need to take the BQL on our own */
bool take_bql = !qemu_mutex_iothread_locked();
assert(as);
use_iommu = as->iommu_state->dmar_enabled && !vtd_as_pt_enabled(as);
+ pt = as->iommu_state->dmar_enabled && vtd_as_pt_enabled(as);
trace_vtd_switch_address_space(pci_bus_num(as->bus),
VTD_PCI_SLOT(as->devfn),
@@ -1618,11 +1645,53 @@ static bool vtd_switch_address_space(VTDAddressSpace *as)
if (use_iommu) {
memory_region_set_enabled(&as->nodmar, false);
memory_region_set_enabled(MEMORY_REGION(&as->iommu), true);
+ /*
+ * vt-d spec v3.4 3.14:
+ *
+ * """
+ * Requests-with-PASID with input address in range 0xFEEx_xxxx
+ * are translated normally like any other request-with-PASID
+ * through DMA-remapping hardware.
+ * """
+ *
+ * Need to disable ir for as with PASID.
+ */
+ if (as->pasid != PCI_NO_PASID) {
+ memory_region_set_enabled(&as->iommu_ir, false);
+ } else {
+ memory_region_set_enabled(&as->iommu_ir, true);
+ }
} else {
memory_region_set_enabled(MEMORY_REGION(&as->iommu), false);
memory_region_set_enabled(&as->nodmar, true);
}
+ /*
+ * vtd-spec v3.4 3.14:
+ *
+ * """
+ * Requests-with-PASID with input address in range 0xFEEx_xxxx are
+ * translated normally like any other request-with-PASID through
+ * DMA-remapping hardware. However, if such a request is processed
+ * using pass-through translation, it will be blocked as described
+ * in the paragraph below.
+ *
+ * Software must not program paging-structure entries to remap any
+ * address to the interrupt address range. Untranslated requests
+ * and translation requests that result in an address in the
+ * interrupt range will be blocked with condition code LGN.4 or
+ * SGN.8.
+ * """
+ *
+ * We enable per as memory region (iommu_ir_fault) for catching
+ * the tranlsation for interrupt range through PASID + PT.
+ */
+ if (pt && as->pasid != PCI_NO_PASID) {
+ memory_region_set_enabled(&as->iommu_ir_fault, true);
+ } else {
+ memory_region_set_enabled(&as->iommu_ir_fault, false);
+ }
+
if (take_bql) {
qemu_mutex_unlock_iothread();
}
@@ -1632,24 +1701,13 @@ static bool vtd_switch_address_space(VTDAddressSpace *as)
static void vtd_switch_address_space_all(IntelIOMMUState *s)
{
+ VTDAddressSpace *vtd_as;
GHashTableIter iter;
- VTDBus *vtd_bus;
- int i;
-
- g_hash_table_iter_init(&iter, s->vtd_as_by_busptr);
- while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_bus)) {
- for (i = 0; i < PCI_DEVFN_MAX; i++) {
- if (!vtd_bus->dev_as[i]) {
- continue;
- }
- vtd_switch_address_space(vtd_bus->dev_as[i]);
- }
- }
-}
-static inline uint16_t vtd_make_source_id(uint8_t bus_num, uint8_t devfn)
-{
- return ((bus_num & 0xffUL) << 8) | (devfn & 0xffUL);
+ g_hash_table_iter_init(&iter, s->vtd_address_spaces);
+ while (g_hash_table_iter_next(&iter, NULL, (void **)&vtd_as)) {
+ vtd_switch_address_space(vtd_as);
+ }
}
static const bool vtd_qualified_faults[] = {
@@ -1686,18 +1744,37 @@ static inline bool vtd_is_interrupt_addr(hwaddr addr)
return VTD_INTERRUPT_ADDR_FIRST <= addr && addr <= VTD_INTERRUPT_ADDR_LAST;
}
+static gboolean vtd_find_as_by_sid(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ struct vtd_as_key *as_key = (struct vtd_as_key *)key;
+ uint16_t target_sid = *(uint16_t *)user_data;
+ uint16_t sid = PCI_BUILD_BDF(pci_bus_num(as_key->bus), as_key->devfn);
+ return sid == target_sid;
+}
+
+static VTDAddressSpace *vtd_get_as_by_sid(IntelIOMMUState *s, uint16_t sid)
+{
+ uint8_t bus_num = PCI_BUS_NUM(sid);
+ VTDAddressSpace *vtd_as = s->vtd_as_cache[bus_num];
+
+ if (vtd_as &&
+ (sid == PCI_BUILD_BDF(pci_bus_num(vtd_as->bus), vtd_as->devfn))) {
+ return vtd_as;
+ }
+
+ vtd_as = g_hash_table_find(s->vtd_address_spaces, vtd_find_as_by_sid, &sid);
+ s->vtd_as_cache[bus_num] = vtd_as;
+
+ return vtd_as;
+}
+
static void vtd_pt_enable_fast_path(IntelIOMMUState *s, uint16_t source_id)
{
- VTDBus *vtd_bus;
VTDAddressSpace *vtd_as;
bool success = false;
- vtd_bus = vtd_find_as_from_bus_num(s, VTD_SID_TO_BUS(source_id));
- if (!vtd_bus) {
- goto out;
- }
-
- vtd_as = vtd_bus->dev_as[VTD_SID_TO_DEVFN(source_id)];
+ vtd_as = vtd_get_as_by_sid(s, source_id);
if (!vtd_as) {
goto out;
}
@@ -1711,6 +1788,22 @@ out:
trace_vtd_pt_enable_fast_path(source_id, success);
}
+static void vtd_report_fault(IntelIOMMUState *s,
+ int err, bool is_fpd_set,
+ uint16_t source_id,
+ hwaddr addr,
+ bool is_write,
+ bool is_pasid,
+ uint32_t pasid)
+{
+ if (is_fpd_set && vtd_is_qualified_fault(err)) {
+ trace_vtd_fault_disabled();
+ } else {
+ vtd_report_dmar_fault(s, source_id, addr, err, is_write,
+ is_pasid, pasid);
+ }
+}
+
/* Map dev to context-entry then do a paging-structures walk to do a iommu
* translation.
*
@@ -1732,13 +1825,14 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
uint8_t bus_num = pci_bus_num(bus);
VTDContextCacheEntry *cc_entry;
uint64_t slpte, page_mask;
- uint32_t level;
- uint16_t source_id = vtd_make_source_id(bus_num, devfn);
+ uint32_t level, pasid = vtd_as->pasid;
+ uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn);
int ret_fr;
bool is_fpd_set = false;
bool reads = true;
bool writes = true;
uint8_t access_flags;
+ bool rid2pasid = (pasid == PCI_NO_PASID) && s->root_scalable;
VTDIOTLBEntry *iotlb_entry;
/*
@@ -1751,15 +1845,17 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
cc_entry = &vtd_as->context_cache_entry;
- /* Try to fetch slpte form IOTLB */
- iotlb_entry = vtd_lookup_iotlb(s, source_id, addr);
- if (iotlb_entry) {
- trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte,
- iotlb_entry->domain_id);
- slpte = iotlb_entry->slpte;
- access_flags = iotlb_entry->access_flags;
- page_mask = iotlb_entry->mask;
- goto out;
+ /* Try to fetch slpte form IOTLB, we don't need RID2PASID logic */
+ if (!rid2pasid) {
+ iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr);
+ if (iotlb_entry) {
+ trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte,
+ iotlb_entry->domain_id);
+ slpte = iotlb_entry->slpte;
+ access_flags = iotlb_entry->access_flags;
+ page_mask = iotlb_entry->mask;
+ goto out;
+ }
}
/* Try to fetch context-entry from cache first */
@@ -1770,16 +1866,26 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
ce = cc_entry->context_entry;
is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD;
if (!is_fpd_set && s->root_scalable) {
- ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set);
- VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write);
+ ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, pasid);
+ if (ret_fr) {
+ vtd_report_fault(s, -ret_fr, is_fpd_set,
+ source_id, addr, is_write,
+ false, 0);
+ goto error;
+ }
}
} else {
ret_fr = vtd_dev_to_context_entry(s, bus_num, devfn, &ce);
is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD;
if (!ret_fr && !is_fpd_set && s->root_scalable) {
- ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set);
+ ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, pasid);
+ }
+ if (ret_fr) {
+ vtd_report_fault(s, -ret_fr, is_fpd_set,
+ source_id, addr, is_write,
+ false, 0);
+ goto error;
}
- VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write);
/* Update context-cache */
trace_vtd_iotlb_cc_update(bus_num, devfn, ce.hi, ce.lo,
cc_entry->context_cache_gen,
@@ -1788,11 +1894,15 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
cc_entry->context_cache_gen = s->context_cache_gen;
}
+ if (rid2pasid) {
+ pasid = VTD_CE_GET_RID2PASID(&ce);
+ }
+
/*
* We don't need to translate for pass-through context entries.
* Also, let's ignore IOTLB caching as well for PT devices.
*/
- if (vtd_dev_pt_enabled(s, &ce)) {
+ if (vtd_dev_pt_enabled(s, &ce, pasid)) {
entry->iova = addr & VTD_PAGE_MASK_4K;
entry->translated_addr = entry->iova;
entry->addr_mask = ~VTD_PAGE_MASK_4K;
@@ -1813,14 +1923,31 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus,
return true;
}
+ /* Try to fetch slpte form IOTLB for RID2PASID slow path */
+ if (rid2pasid) {
+ iotlb_entry = vtd_lookup_iotlb(s, source_id, pasid, addr);
+ if (iotlb_entry) {
+ trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte,
+ iotlb_entry->domain_id);
+ slpte = iotlb_entry->slpte;
+ access_flags = iotlb_entry->access_flags;
+ page_mask = iotlb_entry->mask;
+ goto out;
+ }
+ }
+
ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &slpte, &level,
- &reads, &writes, s->aw_bits);
- VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write);
+ &reads, &writes, s->aw_bits, pasid);
+ if (ret_fr) {
+ vtd_report_fault(s, -ret_fr, is_fpd_set, source_id,
+ addr, is_write, pasid != PCI_NO_PASID, pasid);
+ goto error;
+ }
page_mask = vtd_slpt_level_page_mask(level);
access_flags = IOMMU_ACCESS_FLAG(reads, writes);
- vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce), addr, slpte,
- access_flags, level);
+ vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce, pasid),
+ addr, slpte, access_flags, level, pasid);
out:
vtd_iommu_unlock(s);
entry->iova = addr & page_mask;
@@ -1905,11 +2032,10 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
uint16_t source_id,
uint16_t func_mask)
{
+ GHashTableIter as_it;
uint16_t mask;
- VTDBus *vtd_bus;
VTDAddressSpace *vtd_as;
uint8_t bus_n, devfn;
- uint16_t devfn_it;
trace_vtd_inv_desc_cc_devices(source_id, func_mask);
@@ -1932,32 +2058,31 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s,
mask = ~mask;
bus_n = VTD_SID_TO_BUS(source_id);
- vtd_bus = vtd_find_as_from_bus_num(s, bus_n);
- if (vtd_bus) {
- devfn = VTD_SID_TO_DEVFN(source_id);
- for (devfn_it = 0; devfn_it < PCI_DEVFN_MAX; ++devfn_it) {
- vtd_as = vtd_bus->dev_as[devfn_it];
- if (vtd_as && ((devfn_it & mask) == (devfn & mask))) {
- trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it),
- VTD_PCI_FUNC(devfn_it));
- vtd_iommu_lock(s);
- vtd_as->context_cache_entry.context_cache_gen = 0;
- vtd_iommu_unlock(s);
- /*
- * Do switch address space when needed, in case if the
- * device passthrough bit is switched.
- */
- vtd_switch_address_space(vtd_as);
- /*
- * So a device is moving out of (or moving into) a
- * domain, resync the shadow page table.
- * This won't bring bad even if we have no such
- * notifier registered - the IOMMU notification
- * framework will skip MAP notifications if that
- * happened.
- */
- vtd_sync_shadow_page_table(vtd_as);
- }
+ devfn = VTD_SID_TO_DEVFN(source_id);
+
+ g_hash_table_iter_init(&as_it, s->vtd_address_spaces);
+ while (g_hash_table_iter_next(&as_it, NULL, (void **)&vtd_as)) {
+ if ((pci_bus_num(vtd_as->bus) == bus_n) &&
+ (vtd_as->devfn & mask) == (devfn & mask)) {
+ trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(vtd_as->devfn),
+ VTD_PCI_FUNC(vtd_as->devfn));
+ vtd_iommu_lock(s);
+ vtd_as->context_cache_entry.context_cache_gen = 0;
+ vtd_iommu_unlock(s);
+ /*
+ * Do switch address space when needed, in case if the
+ * device passthrough bit is switched.
+ */
+ vtd_switch_address_space(vtd_as);
+ /*
+ * So a device is moving out of (or moving into) a
+ * domain, resync the shadow page table.
+ * This won't bring bad even if we have no such
+ * notifier registered - the IOMMU notification
+ * framework will skip MAP notifications if that
+ * happened.
+ */
+ vtd_sync_shadow_page_table(vtd_as);
}
}
}
@@ -2014,7 +2139,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) {
if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
vtd_as->devfn, &ce) &&
- domain_id == vtd_get_domain_id(s, &ce)) {
+ domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) {
vtd_sync_shadow_page_table(vtd_as);
}
}
@@ -2022,7 +2147,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id)
static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
uint16_t domain_id, hwaddr addr,
- uint8_t am)
+ uint8_t am, uint32_t pasid)
{
VTDAddressSpace *vtd_as;
VTDContextEntry ce;
@@ -2030,9 +2155,12 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s,
hwaddr size = (1 << am) * VTD_PAGE_SIZE;
QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) {
+ if (pasid != PCI_NO_PASID && pasid != vtd_as->pasid) {
+ continue;
+ }
ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus),
vtd_as->devfn, &ce);
- if (!ret && domain_id == vtd_get_domain_id(s, &ce)) {
+ if (!ret && domain_id == vtd_get_domain_id(s, &ce, vtd_as->pasid)) {
if (vtd_as_has_map_notifier(vtd_as)) {
/*
* As long as we have MAP notifications registered in
@@ -2076,7 +2204,7 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id,
vtd_iommu_lock(s);
g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info);
vtd_iommu_unlock(s);
- vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am);
+ vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am, PCI_NO_PASID);
}
/* Flush IOTLB
@@ -2473,18 +2601,13 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
{
VTDAddressSpace *vtd_dev_as;
IOMMUTLBEvent event;
- struct VTDBus *vtd_bus;
hwaddr addr;
uint64_t sz;
uint16_t sid;
- uint8_t devfn;
bool size;
- uint8_t bus_num;
addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi);
sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo);
- devfn = sid & 0xff;
- bus_num = sid >> 8;
size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi);
if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) ||
@@ -2495,12 +2618,11 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s,
return false;
}
- vtd_bus = vtd_find_as_from_bus_num(s, bus_num);
- if (!vtd_bus) {
- goto done;
- }
-
- vtd_dev_as = vtd_bus->dev_as[devfn];
+ /*
+ * Using sid is OK since the guest should have finished the
+ * initialization of both the bus and device.
+ */
+ vtd_dev_as = vtd_get_as_by_sid(s, sid);
if (!vtd_dev_as) {
goto done;
}
@@ -3151,6 +3273,7 @@ static Property vtd_properties[] = {
DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE),
DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE),
DEFINE_PROP_BOOL("snoop-control", IntelIOMMUState, snoop_control, false),
+ DEFINE_PROP_BOOL("x-pasid-mode", IntelIOMMUState, pasid, false),
DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true),
DEFINE_PROP_BOOL("dma-translation", IntelIOMMUState, dma_translation, true),
DEFINE_PROP_END_OF_LIST(),
@@ -3425,32 +3548,98 @@ static const MemoryRegionOps vtd_mem_ir_ops = {
},
};
-VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
+static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as,
+ hwaddr addr, bool is_write)
{
- uintptr_t key = (uintptr_t)bus;
- VTDBus *vtd_bus = g_hash_table_lookup(s->vtd_as_by_busptr, &key);
- VTDAddressSpace *vtd_dev_as;
- char name[128];
+ IntelIOMMUState *s = vtd_as->iommu_state;
+ uint8_t bus_n = pci_bus_num(vtd_as->bus);
+ uint16_t sid = PCI_BUILD_BDF(bus_n, vtd_as->devfn);
+ bool is_fpd_set = false;
+ VTDContextEntry ce;
- if (!vtd_bus) {
- uintptr_t *new_key = g_malloc(sizeof(*new_key));
- *new_key = (uintptr_t)bus;
- /* No corresponding free() */
- vtd_bus = g_malloc0(sizeof(VTDBus) + sizeof(VTDAddressSpace *) * \
- PCI_DEVFN_MAX);
- vtd_bus->bus = bus;
- g_hash_table_insert(s->vtd_as_by_busptr, new_key, vtd_bus);
+ assert(vtd_as->pasid != PCI_NO_PASID);
+
+ /* Try out best to fetch FPD, we can't do anything more */
+ if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) {
+ is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD;
+ if (!is_fpd_set && s->root_scalable) {
+ vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, vtd_as->pasid);
+ }
}
- vtd_dev_as = vtd_bus->dev_as[devfn];
+ vtd_report_fault(s, VTD_FR_SM_INTERRUPT_ADDR,
+ is_fpd_set, sid, addr, is_write,
+ true, vtd_as->pasid);
+}
+
+static MemTxResult vtd_mem_ir_fault_read(void *opaque, hwaddr addr,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ vtd_report_ir_illegal_access(opaque, addr, false);
+ return MEMTX_ERROR;
+}
+
+static MemTxResult vtd_mem_ir_fault_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size,
+ MemTxAttrs attrs)
+{
+ vtd_report_ir_illegal_access(opaque, addr, true);
+
+ return MEMTX_ERROR;
+}
+
+static const MemoryRegionOps vtd_mem_ir_fault_ops = {
+ .read_with_attrs = vtd_mem_ir_fault_read,
+ .write_with_attrs = vtd_mem_ir_fault_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+};
+
+VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus,
+ int devfn, unsigned int pasid)
+{
+ /*
+ * We can't simply use sid here since the bus number might not be
+ * initialized by the guest.
+ */
+ struct vtd_as_key key = {
+ .bus = bus,
+ .devfn = devfn,
+ .pasid = pasid,
+ };
+ VTDAddressSpace *vtd_dev_as;
+ char name[128];
+
+ vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key);
if (!vtd_dev_as) {
- snprintf(name, sizeof(name), "vtd-%02x.%x", PCI_SLOT(devfn),
- PCI_FUNC(devfn));
- vtd_bus->dev_as[devfn] = vtd_dev_as = g_new0(VTDAddressSpace, 1);
+ struct vtd_as_key *new_key = g_malloc(sizeof(*new_key));
+
+ new_key->bus = bus;
+ new_key->devfn = devfn;
+ new_key->pasid = pasid;
+
+ if (pasid == PCI_NO_PASID) {
+ snprintf(name, sizeof(name), "vtd-%02x.%x", PCI_SLOT(devfn),
+ PCI_FUNC(devfn));
+ } else {
+ snprintf(name, sizeof(name), "vtd-%02x.%x-pasid-%x", PCI_SLOT(devfn),
+ PCI_FUNC(devfn), pasid);
+ }
+
+ vtd_dev_as = g_new0(VTDAddressSpace, 1);
vtd_dev_as->bus = bus;
vtd_dev_as->devfn = (uint8_t)devfn;
+ vtd_dev_as->pasid = pasid;
vtd_dev_as->iommu_state = s;
vtd_dev_as->context_cache_entry.context_cache_gen = 0;
vtd_dev_as->iova_tree = iova_tree_new();
@@ -3492,6 +3681,24 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
&vtd_dev_as->iommu_ir, 1);
/*
+ * This region is used for catching fault to access interrupt
+ * range via passthrough + PASID. See also
+ * vtd_switch_address_space(). We can't use alias since we
+ * need to know the sid which is valid for MSI who uses
+ * bus_master_as (see msi_send_message()).
+ */
+ memory_region_init_io(&vtd_dev_as->iommu_ir_fault, OBJECT(s),
+ &vtd_mem_ir_fault_ops, vtd_dev_as, "vtd-no-ir",
+ VTD_INTERRUPT_ADDR_SIZE);
+ /*
+ * Hook to root since when PT is enabled vtd_dev_as->iommu
+ * will be disabled.
+ */
+ memory_region_add_subregion_overlap(MEMORY_REGION(&vtd_dev_as->root),
+ VTD_INTERRUPT_ADDR_FIRST,
+ &vtd_dev_as->iommu_ir_fault, 2);
+
+ /*
* Hook both the containers under the root container, we
* switch between DMAR & noDMAR by enable/disable
* corresponding sub-containers
@@ -3503,6 +3710,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn)
&vtd_dev_as->nodmar, 0);
vtd_switch_address_space(vtd_dev_as);
+
+ g_hash_table_insert(s->vtd_address_spaces, new_key, vtd_dev_as);
}
return vtd_dev_as;
}
@@ -3609,7 +3818,7 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
"legacy mode",
bus_n, PCI_SLOT(vtd_as->devfn),
PCI_FUNC(vtd_as->devfn),
- vtd_get_domain_id(s, &ce),
+ vtd_get_domain_id(s, &ce, vtd_as->pasid),
ce.hi, ce.lo);
if (vtd_as_has_map_notifier(vtd_as)) {
/* This is required only for MAP typed notifiers */
@@ -3619,10 +3828,10 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n)
.notify_unmap = false,
.aw = s->aw_bits,
.as = vtd_as,
- .domain_id = vtd_get_domain_id(s, &ce),
+ .domain_id = vtd_get_domain_id(s, &ce, vtd_as->pasid),
};
- vtd_page_walk(s, &ce, 0, ~0ULL, &info);
+ vtd_page_walk(s, &ce, 0, ~0ULL, &info, vtd_as->pasid);
}
} else {
trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn),
@@ -3722,6 +3931,10 @@ static void vtd_init(IntelIOMMUState *s)
s->ecap |= VTD_ECAP_SC;
}
+ if (s->pasid) {
+ s->ecap |= VTD_ECAP_PASID;
+ }
+
vtd_reset_caches(s);
/* Define registers with default values and bit semantics */
@@ -3795,7 +4008,7 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn)
assert(0 <= devfn && devfn < PCI_DEVFN_MAX);
- vtd_as = vtd_find_add_as(s, bus, devfn);
+ vtd_as = vtd_find_add_as(s, bus, devfn, PCI_NO_PASID);
return &vtd_as->as;
}
@@ -3838,6 +4051,11 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp)
return false;
}
+ if (s->pasid && !s->scalable_mode) {
+ error_setg(errp, "Need to set scalable mode for PASID");
+ return false;
+ }
+
return true;
}
@@ -3874,6 +4092,17 @@ static void vtd_realize(DeviceState *dev, Error **errp)
X86MachineState *x86ms = X86_MACHINE(ms);
PCIBus *bus = pcms->bus;
IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev);
+ X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s);
+
+ if (s->pasid && x86_iommu->dt_supported) {
+ /*
+ * PASID-based-Device-TLB Invalidate Descriptor is not
+ * implemented and it requires support from vhost layer which
+ * needs to be implemented in the future.
+ */
+ error_setg(errp, "PASID based device IOTLB is not supported");
+ return;
+ }
if (!vtd_decide_config(s, errp)) {
return;
@@ -3881,7 +4110,6 @@ static void vtd_realize(DeviceState *dev, Error **errp)
QLIST_INIT(&s->vtd_as_with_notifiers);
qemu_mutex_init(&s->iommu_lock);
- memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num));
memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s,
"intel_iommu", DMAR_REG_SIZE);
@@ -3901,10 +4129,10 @@ static void vtd_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->csrmem);
/* No corresponding destroy */
- s->iotlb = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal,
+ s->iotlb = g_hash_table_new_full(vtd_iotlb_hash, vtd_iotlb_equal,
g_free, g_free);
- s->vtd_as_by_busptr = g_hash_table_new_full(vtd_uint64_hash, vtd_uint64_equal,
- g_free, g_free);
+ s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal,
+ g_free, g_free);
vtd_init(s);
sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, Q35_HOST_BRIDGE_IOMMU_ADDR);
pci_setup_iommu(bus, vtd_host_dma_iommu, dev);
diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h
index 930ce61feb..f090e61e11 100644
--- a/hw/i386/intel_iommu_internal.h
+++ b/hw/i386/intel_iommu_internal.h
@@ -114,8 +114,9 @@
VTD_INTERRUPT_ADDR_FIRST + 1)
/* The shift of source_id in the key of IOTLB hash table */
-#define VTD_IOTLB_SID_SHIFT 36
-#define VTD_IOTLB_LVL_SHIFT 52
+#define VTD_IOTLB_SID_SHIFT 20
+#define VTD_IOTLB_LVL_SHIFT 28
+#define VTD_IOTLB_PASID_SHIFT 30
#define VTD_IOTLB_MAX_SIZE 1024 /* Max size of the hash table */
/* IOTLB_REG */
@@ -191,6 +192,7 @@
#define VTD_ECAP_SC (1ULL << 7)
#define VTD_ECAP_MHMV (15ULL << 20)
#define VTD_ECAP_SRS (1ULL << 31)
+#define VTD_ECAP_PASID (1ULL << 40)
#define VTD_ECAP_SMTS (1ULL << 43)
#define VTD_ECAP_SLTS (1ULL << 46)
@@ -211,6 +213,8 @@
#define VTD_CAP_DRAIN_READ (1ULL << 55)
#define VTD_CAP_DRAIN (VTD_CAP_DRAIN_READ | VTD_CAP_DRAIN_WRITE)
#define VTD_CAP_CM (1ULL << 7)
+#define VTD_PASID_ID_SHIFT 20
+#define VTD_PASID_ID_MASK ((1ULL << VTD_PASID_ID_SHIFT) - 1)
/* Supported Adjusted Guest Address Widths */
#define VTD_CAP_SAGAW_SHIFT 8
@@ -262,6 +266,8 @@
#define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK)
/* For the low 64-bit of 128-bit */
#define VTD_FRCD_FI(val) ((val) & ~0xfffULL)
+#define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40)
+#define VTD_FRCD_PP(val) (((val) & 0x1) << 31)
/* DMA Remapping Fault Conditions */
typedef enum VTDFaultReason {
@@ -379,6 +385,11 @@ typedef union VTDInvDesc VTDInvDesc;
#define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL)
#define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL
#define VTD_INV_DESC_IOTLB_RSVD_HI 0xf80ULL
+#define VTD_INV_DESC_IOTLB_PASID_PASID (2ULL << 4)
+#define VTD_INV_DESC_IOTLB_PASID_PAGE (3ULL << 4)
+#define VTD_INV_DESC_IOTLB_PASID(val) (((val) >> 32) & VTD_PASID_ID_MASK)
+#define VTD_INV_DESC_IOTLB_PASID_RSVD_LO 0xfff00000000001c0ULL
+#define VTD_INV_DESC_IOTLB_PASID_RSVD_HI 0xf80ULL
/* Mask for Device IOTLB Invalidate Descriptor */
#define VTD_INV_DESC_DEVICE_IOTLB_ADDR(val) ((val) & 0xfffffffffffff000ULL)
@@ -413,6 +424,7 @@ typedef union VTDInvDesc VTDInvDesc;
/* Information about page-selective IOTLB invalidate */
struct VTDIOTLBPageInvInfo {
uint16_t domain_id;
+ uint32_t pasid;
uint64_t addr;
uint8_t mask;
};
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c
index ffd1884100..170a331e3f 100644
--- a/hw/i386/microvm.c
+++ b/hw/i386/microvm.c
@@ -324,8 +324,6 @@ static void microvm_memory_init(MicrovmMachineState *mms)
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, machine->smp.max_cpus);
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size);
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1);
- fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
- &e820_reserve, sizeof(e820_reserve));
fw_cfg_add_file(fw_cfg, "etc/e820", e820_table,
sizeof(struct e820_entry) * e820_get_num_entries());
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index ef14da5094..546b703cb4 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1061,7 +1061,6 @@ void pc_memory_init(PCMachineState *pcms,
hwaddr cxl_size = MiB;
cxl_base = pc_get_cxl_range_start(pcms);
- e820_add_entry(cxl_base, cxl_size, E820_RESERVED);
memory_region_init(mr, OBJECT(machine), "cxl_host_reg", cxl_size);
memory_region_add_subregion(system_memory, cxl_base, mr);
cxl_resv_end = cxl_base + cxl_size;
@@ -1077,7 +1076,6 @@ void pc_memory_init(PCMachineState *pcms,
memory_region_init_io(&fw->mr, OBJECT(machine), &cfmws_ops, fw,
"cxl-fixed-memory-region", fw->size);
memory_region_add_subregion(system_memory, fw->base, &fw->mr);
- e820_add_entry(fw->base, fw->size, E820_RESERVED);
cxl_fmw_base += fw->size;
cxl_resv_end = cxl_fmw_base;
}
diff --git a/hw/i386/trace-events b/hw/i386/trace-events
index e49814dd64..04fd71bfc4 100644
--- a/hw/i386/trace-events
+++ b/hw/i386/trace-events
@@ -12,6 +12,8 @@ vtd_inv_desc_cc_devices(uint16_t sid, uint16_t fmask) "context invalidate device
vtd_inv_desc_iotlb_global(void) "iotlb invalidate global"
vtd_inv_desc_iotlb_domain(uint16_t domain) "iotlb invalidate whole domain 0x%"PRIx16
vtd_inv_desc_iotlb_pages(uint16_t domain, uint64_t addr, uint8_t mask) "iotlb invalidate domain 0x%"PRIx16" addr 0x%"PRIx64" mask 0x%"PRIx8
+vtd_inv_desc_iotlb_pasid_pages(uint16_t domain, uint64_t addr, uint8_t mask, uint32_t pasid) "iotlb invalidate domain 0x%"PRIx16" addr 0x%"PRIx64" mask 0x%"PRIx8" pasid 0x%"PRIx32
+vtd_inv_desc_iotlb_pasid(uint16_t domain, uint32_t pasid) "iotlb invalidate domain 0x%"PRIx16" pasid 0x%"PRIx32
vtd_inv_desc_wait_sw(uint64_t addr, uint32_t data) "wait invalidate status write addr 0x%"PRIx64" data 0x%"PRIx32
vtd_inv_desc_wait_irq(const char *msg) "%s"
vtd_inv_desc_wait_write_fail(uint64_t hi, uint64_t lo) "write fail for wait desc hi 0x%"PRIx64" lo 0x%"PRIx64
diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c
index 8694e58b21..0b0a83e080 100644
--- a/hw/isa/lpc_ich9.c
+++ b/hw/isa/lpc_ich9.c
@@ -809,6 +809,7 @@ static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
{
+ Aml *field;
BusChild *kid;
ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
BusState *bus = BUS(s->isa_bus);
@@ -816,6 +817,28 @@ static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
/* ICH9 PCI to ISA irq remapping */
aml_append(scope, aml_operation_region("PIRQ", AML_PCI_CONFIG,
aml_int(0x60), 0x0C));
+ /* Fields declarion has to happen *after* operation region */
+ field = aml_field("PIRQ", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("PRQA", 8));
+ aml_append(field, aml_named_field("PRQB", 8));
+ aml_append(field, aml_named_field("PRQC", 8));
+ aml_append(field, aml_named_field("PRQD", 8));
+ aml_append(field, aml_reserved_field(0x20));
+ aml_append(field, aml_named_field("PRQE", 8));
+ aml_append(field, aml_named_field("PRQF", 8));
+ aml_append(field, aml_named_field("PRQG", 8));
+ aml_append(field, aml_named_field("PRQH", 8));
+ aml_append(scope, field);
+
+ /* hack: put fields into _SB scope for LNKx to find them */
+ aml_append(scope, aml_alias("PRQA", "\\_SB.PRQA"));
+ aml_append(scope, aml_alias("PRQB", "\\_SB.PRQB"));
+ aml_append(scope, aml_alias("PRQC", "\\_SB.PRQC"));
+ aml_append(scope, aml_alias("PRQD", "\\_SB.PRQD"));
+ aml_append(scope, aml_alias("PRQE", "\\_SB.PRQE"));
+ aml_append(scope, aml_alias("PRQF", "\\_SB.PRQF"));
+ aml_append(scope, aml_alias("PRQG", "\\_SB.PRQG"));
+ aml_append(scope, aml_alias("PRQH", "\\_SB.PRQH"));
QTAILQ_FOREACH(kid, &bus->children, sibling) {
call_dev_aml_func(DEVICE(kid->child), scope);
diff --git a/hw/isa/piix3.c b/hw/isa/piix3.c
index 808fd4eadf..f9b4af5c05 100644
--- a/hw/isa/piix3.c
+++ b/hw/isa/piix3.c
@@ -316,12 +316,27 @@ static void pci_piix3_realize(PCIDevice *dev, Error **errp)
static void build_pci_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
{
+ Aml *field;
BusChild *kid;
BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0");
/* PIIX PCI to ISA irq remapping */
aml_append(scope, aml_operation_region("P40C", AML_PCI_CONFIG,
- aml_int(0x60), 0x04));
+ aml_int(0x60), 0x04));
+ /* Fields declarion has to happen *after* operation region */
+ field = aml_field("P40C", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("PRQ0", 8));
+ aml_append(field, aml_named_field("PRQ1", 8));
+ aml_append(field, aml_named_field("PRQ2", 8));
+ aml_append(field, aml_named_field("PRQ3", 8));
+ aml_append(scope, field);
+
+ /* hack: put fields into _SB scope for LNKx to find them */
+ aml_append(scope, aml_alias("PRQ0", "\\_SB.PRQ0"));
+ aml_append(scope, aml_alias("PRQ1", "\\_SB.PRQ1"));
+ aml_append(scope, aml_alias("PRQ2", "\\_SB.PRQ2"));
+ aml_append(scope, aml_alias("PRQ3", "\\_SB.PRQ3"));
+
QTAILQ_FOREACH(kid, &bus->children, sibling) {
call_dev_aml_func(DEVICE(kid->child), scope);
}
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index a71bf1afeb..255590201a 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -12,7 +12,245 @@
#include "qemu/range.h"
#include "qemu/rcu.h"
#include "sysemu/hostmem.h"
+#include "sysemu/numa.h"
#include "hw/cxl/cxl.h"
+#include "hw/pci/msix.h"
+
+#define DWORD_BYTE 4
+
+/* Default CDAT entries for a memory region */
+enum {
+ CT3_CDAT_DSMAS,
+ CT3_CDAT_DSLBIS0,
+ CT3_CDAT_DSLBIS1,
+ CT3_CDAT_DSLBIS2,
+ CT3_CDAT_DSLBIS3,
+ CT3_CDAT_DSEMTS,
+ CT3_CDAT_NUM_ENTRIES
+};
+
+static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table,
+ int dsmad_handle, MemoryRegion *mr)
+{
+ g_autofree CDATDsmas *dsmas = NULL;
+ g_autofree CDATDslbis *dslbis0 = NULL;
+ g_autofree CDATDslbis *dslbis1 = NULL;
+ g_autofree CDATDslbis *dslbis2 = NULL;
+ g_autofree CDATDslbis *dslbis3 = NULL;
+ g_autofree CDATDsemts *dsemts = NULL;
+
+ dsmas = g_malloc(sizeof(*dsmas));
+ if (!dsmas) {
+ return -ENOMEM;
+ }
+ *dsmas = (CDATDsmas) {
+ .header = {
+ .type = CDAT_TYPE_DSMAS,
+ .length = sizeof(*dsmas),
+ },
+ .DSMADhandle = dsmad_handle,
+ .flags = CDAT_DSMAS_FLAG_NV,
+ .DPA_base = 0,
+ .DPA_length = int128_get64(mr->size),
+ };
+
+ /* For now, no memory side cache, plausiblish numbers */
+ dslbis0 = g_malloc(sizeof(*dslbis0));
+ if (!dslbis0) {
+ return -ENOMEM;
+ }
+ *dslbis0 = (CDATDslbis) {
+ .header = {
+ .type = CDAT_TYPE_DSLBIS,
+ .length = sizeof(*dslbis0),
+ },
+ .handle = dsmad_handle,
+ .flags = HMAT_LB_MEM_MEMORY,
+ .data_type = HMAT_LB_DATA_READ_LATENCY,
+ .entry_base_unit = 10000, /* 10ns base */
+ .entry[0] = 15, /* 150ns */
+ };
+
+ dslbis1 = g_malloc(sizeof(*dslbis1));
+ if (!dslbis1) {
+ return -ENOMEM;
+ }
+ *dslbis1 = (CDATDslbis) {
+ .header = {
+ .type = CDAT_TYPE_DSLBIS,
+ .length = sizeof(*dslbis1),
+ },
+ .handle = dsmad_handle,
+ .flags = HMAT_LB_MEM_MEMORY,
+ .data_type = HMAT_LB_DATA_WRITE_LATENCY,
+ .entry_base_unit = 10000,
+ .entry[0] = 25, /* 250ns */
+ };
+
+ dslbis2 = g_malloc(sizeof(*dslbis2));
+ if (!dslbis2) {
+ return -ENOMEM;
+ }
+ *dslbis2 = (CDATDslbis) {
+ .header = {
+ .type = CDAT_TYPE_DSLBIS,
+ .length = sizeof(*dslbis2),
+ },
+ .handle = dsmad_handle,
+ .flags = HMAT_LB_MEM_MEMORY,
+ .data_type = HMAT_LB_DATA_READ_BANDWIDTH,
+ .entry_base_unit = 1000, /* GB/s */
+ .entry[0] = 16,
+ };
+
+ dslbis3 = g_malloc(sizeof(*dslbis3));
+ if (!dslbis3) {
+ return -ENOMEM;
+ }
+ *dslbis3 = (CDATDslbis) {
+ .header = {
+ .type = CDAT_TYPE_DSLBIS,
+ .length = sizeof(*dslbis3),
+ },
+ .handle = dsmad_handle,
+ .flags = HMAT_LB_MEM_MEMORY,
+ .data_type = HMAT_LB_DATA_WRITE_BANDWIDTH,
+ .entry_base_unit = 1000, /* GB/s */
+ .entry[0] = 16,
+ };
+
+ dsemts = g_malloc(sizeof(*dsemts));
+ if (!dsemts) {
+ return -ENOMEM;
+ }
+ *dsemts = (CDATDsemts) {
+ .header = {
+ .type = CDAT_TYPE_DSEMTS,
+ .length = sizeof(*dsemts),
+ },
+ .DSMAS_handle = dsmad_handle,
+ /* Reserved - the non volatile from DSMAS matters */
+ .EFI_memory_type_attr = 2,
+ .DPA_offset = 0,
+ .DPA_length = int128_get64(mr->size),
+ };
+
+ /* Header always at start of structure */
+ cdat_table[CT3_CDAT_DSMAS] = g_steal_pointer(&dsmas);
+ cdat_table[CT3_CDAT_DSLBIS0] = g_steal_pointer(&dslbis0);
+ cdat_table[CT3_CDAT_DSLBIS1] = g_steal_pointer(&dslbis1);
+ cdat_table[CT3_CDAT_DSLBIS2] = g_steal_pointer(&dslbis2);
+ cdat_table[CT3_CDAT_DSLBIS3] = g_steal_pointer(&dslbis3);
+ cdat_table[CT3_CDAT_DSEMTS] = g_steal_pointer(&dsemts);
+
+ return 0;
+}
+
+static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv)
+{
+ g_autofree CDATSubHeader **table = NULL;
+ MemoryRegion *nonvolatile_mr;
+ CXLType3Dev *ct3d = priv;
+ int dsmad_handle = 0;
+ int rc;
+
+ if (!ct3d->hostmem) {
+ return 0;
+ }
+
+ nonvolatile_mr = host_memory_backend_get_memory(ct3d->hostmem);
+ if (!nonvolatile_mr) {
+ return -EINVAL;
+ }
+
+ table = g_malloc0(CT3_CDAT_NUM_ENTRIES * sizeof(*table));
+ if (!table) {
+ return -ENOMEM;
+ }
+
+ rc = ct3_build_cdat_entries_for_mr(table, dsmad_handle++, nonvolatile_mr);
+ if (rc < 0) {
+ return rc;
+ }
+
+ *cdat_table = g_steal_pointer(&table);
+
+ return CT3_CDAT_NUM_ENTRIES;
+}
+
+static void ct3_free_cdat_table(CDATSubHeader **cdat_table, int num, void *priv)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ g_free(cdat_table[i]);
+ }
+ g_free(cdat_table);
+}
+
+static bool cxl_doe_cdat_rsp(DOECap *doe_cap)
+{
+ CDATObject *cdat = &CXL_TYPE3(doe_cap->pdev)->cxl_cstate.cdat;
+ uint16_t ent;
+ void *base;
+ uint32_t len;
+ CDATReq *req = pcie_doe_get_write_mbox_ptr(doe_cap);
+ CDATRsp rsp;
+
+ assert(cdat->entry_len);
+
+ /* Discard if request length mismatched */
+ if (pcie_doe_get_obj_len(req) <
+ DIV_ROUND_UP(sizeof(CDATReq), DWORD_BYTE)) {
+ return false;
+ }
+
+ ent = req->entry_handle;
+ base = cdat->entry[ent].base;
+ len = cdat->entry[ent].length;
+
+ rsp = (CDATRsp) {
+ .header = {
+ .vendor_id = CXL_VENDOR_ID,
+ .data_obj_type = CXL_DOE_TABLE_ACCESS,
+ .reserved = 0x0,
+ .length = DIV_ROUND_UP((sizeof(rsp) + len), DWORD_BYTE),
+ },
+ .rsp_code = CXL_DOE_TAB_RSP,
+ .table_type = CXL_DOE_TAB_TYPE_CDAT,
+ .entry_handle = (ent < cdat->entry_len - 1) ?
+ ent + 1 : CXL_DOE_TAB_ENT_MAX,
+ };
+
+ memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp));
+ memcpy(doe_cap->read_mbox + DIV_ROUND_UP(sizeof(rsp), DWORD_BYTE),
+ base, len);
+
+ doe_cap->read_mbox_len += rsp.header.length;
+
+ return true;
+}
+
+static uint32_t ct3d_config_read(PCIDevice *pci_dev, uint32_t addr, int size)
+{
+ CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
+ uint32_t val;
+
+ if (pcie_doe_read_config(&ct3d->doe_cdat, addr, size, &val)) {
+ return val;
+ }
+
+ return pci_default_read_config(pci_dev, addr, size);
+}
+
+static void ct3d_config_write(PCIDevice *pci_dev, uint32_t addr, uint32_t val,
+ int size)
+{
+ CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
+
+ pcie_doe_write_config(&ct3d->doe_cdat, addr, val, size);
+ pci_default_write_config(pci_dev, addr, val, size);
+}
/*
* Null value of all Fs suggested by IEEE RA guidelines for use of
@@ -139,6 +377,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
return true;
}
+static DOEProtocol doe_cdat_prot[] = {
+ { CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp },
+ { }
+};
+
static void ct3_realize(PCIDevice *pci_dev, Error **errp)
{
CXLType3Dev *ct3d = CXL_TYPE3(pci_dev);
@@ -146,6 +389,8 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
ComponentRegisters *regs = &cxl_cstate->crb;
MemoryRegion *mr = &regs->component_registers;
uint8_t *pci_conf = pci_dev->config;
+ unsigned short msix_num = 1;
+ int i;
if (!cxl_setup_memory(ct3d, errp)) {
return;
@@ -180,6 +425,20 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
PCI_BASE_ADDRESS_SPACE_MEMORY |
PCI_BASE_ADDRESS_MEM_TYPE_64,
&ct3d->cxl_dstate.device_registers);
+
+ /* MSI(-X) Initailization */
+ msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL);
+ for (i = 0; i < msix_num; i++) {
+ msix_vector_use(pci_dev, i);
+ }
+
+ /* DOE Initailization */
+ pcie_doe_init(pci_dev, &ct3d->doe_cdat, 0x190, doe_cdat_prot, true, 0);
+
+ cxl_cstate->cdat.build_cdat_table = ct3_build_cdat_table;
+ cxl_cstate->cdat.free_cdat_table = ct3_free_cdat_table;
+ cxl_cstate->cdat.private = ct3d;
+ cxl_doe_cdat_init(cxl_cstate, errp);
}
static void ct3_exit(PCIDevice *pci_dev)
@@ -188,6 +447,7 @@ static void ct3_exit(PCIDevice *pci_dev)
CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
ComponentRegisters *regs = &cxl_cstate->crb;
+ cxl_doe_cdat_release(cxl_cstate);
g_free(regs->special_ops);
address_space_destroy(&ct3d->hostmem_as);
}
@@ -287,6 +547,7 @@ static Property ct3_props[] = {
DEFINE_PROP_LINK("lsa", CXLType3Dev, lsa, TYPE_MEMORY_BACKEND,
HostMemoryBackend *),
DEFINE_PROP_UINT64("sn", CXLType3Dev, sn, UI64_NULL),
+ DEFINE_PROP_STRING("cdat", CXLType3Dev, cxl_cstate.cdat.filename),
DEFINE_PROP_END_OF_LIST(),
};
@@ -352,6 +613,9 @@ static void ct3_class_init(ObjectClass *oc, void *data)
pc->device_id = 0xd93; /* LVF for now */
pc->revision = 1;
+ pc->config_write = ct3d_config_write;
+ pc->config_read = ct3d_config_read;
+
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->desc = "CXL PMEM Device (Type 3)";
dc->reset = ct3d_reset;
diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c
index ac96f7665a..7523e9f5d2 100644
--- a/hw/net/e1000e.c
+++ b/hw/net/e1000e.c
@@ -276,25 +276,18 @@ e1000e_unuse_msix_vectors(E1000EState *s, int num_vectors)
}
}
-static bool
+static void
e1000e_use_msix_vectors(E1000EState *s, int num_vectors)
{
int i;
for (i = 0; i < num_vectors; i++) {
- int res = msix_vector_use(PCI_DEVICE(s), i);
- if (res < 0) {
- trace_e1000e_msix_use_vector_fail(i, res);
- e1000e_unuse_msix_vectors(s, i);
- return false;
- }
+ msix_vector_use(PCI_DEVICE(s), i);
}
- return true;
}
static void
e1000e_init_msix(E1000EState *s)
{
- PCIDevice *d = PCI_DEVICE(s);
int res = msix_init(PCI_DEVICE(s), E1000E_MSIX_VEC_NUM,
&s->msix,
E1000E_MSIX_IDX, E1000E_MSIX_TABLE,
@@ -305,9 +298,7 @@ e1000e_init_msix(E1000EState *s)
if (res < 0) {
trace_e1000e_msix_init_fail(res);
} else {
- if (!e1000e_use_msix_vectors(s, E1000E_MSIX_VEC_NUM)) {
- msix_uninit(d, &s->msix, &s->msix);
- }
+ e1000e_use_msix_vectors(s, E1000E_MSIX_VEC_NUM);
}
}
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
index d8f3f16fe8..281d43e6cf 100644
--- a/hw/net/rocker/rocker.c
+++ b/hw/net/rocker/rocker.c
@@ -1212,24 +1212,14 @@ static void rocker_msix_vectors_unuse(Rocker *r,
}
}
-static int rocker_msix_vectors_use(Rocker *r,
- unsigned int num_vectors)
+static void rocker_msix_vectors_use(Rocker *r, unsigned int num_vectors)
{
PCIDevice *dev = PCI_DEVICE(r);
- int err;
int i;
for (i = 0; i < num_vectors; i++) {
- err = msix_vector_use(dev, i);
- if (err) {
- goto rollback;
- }
+ msix_vector_use(dev, i);
}
- return 0;
-
-rollback:
- rocker_msix_vectors_unuse(r, i);
- return err;
}
static int rocker_msix_init(Rocker *r, Error **errp)
@@ -1247,16 +1237,9 @@ static int rocker_msix_init(Rocker *r, Error **errp)
return err;
}
- err = rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
- if (err) {
- goto err_msix_vectors_use;
- }
+ rocker_msix_vectors_use(r, ROCKER_MSIX_VEC_COUNT(r->fp_ports));
return 0;
-
-err_msix_vectors_use:
- msix_uninit(dev, &r->msix_bar, &r->msix_bar);
- return err;
}
static void rocker_msix_uninit(Rocker *r)
diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c
index 89d71cfb8e..9f7daae99c 100644
--- a/hw/net/vhost_net-stub.c
+++ b/hw/net/vhost_net-stub.c
@@ -101,3 +101,15 @@ int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu)
{
return 0;
}
+
+void vhost_net_virtqueue_reset(VirtIODevice *vdev, NetClientState *nc,
+ int vq_index)
+{
+
+}
+
+int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc,
+ int vq_index)
+{
+ return 0;
+}
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index d28f8b974b..feda448878 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -34,6 +34,7 @@
#include "standard-headers/linux/virtio_ring.h"
#include "hw/virtio/vhost.h"
#include "hw/virtio/virtio-bus.h"
+#include "linux-headers/linux/vhost.h"
/* Features supported by host kernel. */
@@ -46,6 +47,7 @@ static const int kernel_feature_bits[] = {
VIRTIO_NET_F_MTU,
VIRTIO_F_IOMMU_PLATFORM,
VIRTIO_F_RING_PACKED,
+ VIRTIO_F_RING_RESET,
VIRTIO_NET_F_HASH_REPORT,
VHOST_INVALID_FEATURE_BIT
};
@@ -387,21 +389,20 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs,
} else {
peer = qemu_get_peer(ncs, n->max_queue_pairs);
}
- r = vhost_net_start_one(get_vhost_net(peer), dev);
-
- if (r < 0) {
- goto err_start;
- }
if (peer->vring_enable) {
/* restore vring enable state */
r = vhost_set_vring_enable(peer, peer->vring_enable);
if (r < 0) {
- vhost_net_stop_one(get_vhost_net(peer), dev);
goto err_start;
}
}
+
+ r = vhost_net_start_one(get_vhost_net(peer), dev);
+ if (r < 0) {
+ goto err_start;
+ }
}
return 0;
@@ -531,3 +532,80 @@ int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu)
return vhost_ops->vhost_net_set_mtu(&net->dev, mtu);
}
+
+void vhost_net_virtqueue_reset(VirtIODevice *vdev, NetClientState *nc,
+ int vq_index)
+{
+ VHostNetState *net = get_vhost_net(nc->peer);
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+ struct vhost_vring_file file = { .fd = -1 };
+ int idx;
+
+ /* should only be called after backend is connected */
+ assert(vhost_ops);
+
+ idx = vhost_ops->vhost_get_vq_index(&net->dev, vq_index);
+
+ if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
+ file.index = idx;
+ int r = vhost_net_set_backend(&net->dev, &file);
+ assert(r >= 0);
+ }
+
+ vhost_virtqueue_stop(&net->dev,
+ vdev,
+ net->dev.vqs + idx,
+ net->dev.vq_index + idx);
+}
+
+int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc,
+ int vq_index)
+{
+ VHostNetState *net = get_vhost_net(nc->peer);
+ const VhostOps *vhost_ops = net->dev.vhost_ops;
+ struct vhost_vring_file file = { };
+ int idx, r;
+
+ if (!net->dev.started) {
+ return -EBUSY;
+ }
+
+ /* should only be called after backend is connected */
+ assert(vhost_ops);
+
+ idx = vhost_ops->vhost_get_vq_index(&net->dev, vq_index);
+
+ r = vhost_virtqueue_start(&net->dev,
+ vdev,
+ net->dev.vqs + idx,
+ net->dev.vq_index + idx);
+ if (r < 0) {
+ goto err_start;
+ }
+
+ if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
+ file.index = idx;
+ file.fd = net->backend;
+ r = vhost_net_set_backend(&net->dev, &file);
+ if (r < 0) {
+ r = -errno;
+ goto err_start;
+ }
+ }
+
+ return 0;
+
+err_start:
+ error_report("Error when restarting the queue.");
+
+ if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
+ file.fd = VHOST_FILE_UNBIND;
+ file.index = idx;
+ int r = vhost_net_set_backend(&net->dev, &file);
+ assert(r >= 0);
+ }
+
+ vhost_dev_stop(&net->dev, vdev);
+
+ return r;
+}
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index b6903aea54..8b32339b76 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -124,6 +124,16 @@ static int vq2q(int queue_index)
return queue_index / 2;
}
+static void flush_or_purge_queued_packets(NetClientState *nc)
+{
+ if (!nc->peer) {
+ return;
+ }
+
+ qemu_flush_or_purge_queued_packets(nc->peer, true);
+ assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
+}
+
/* TODO
* - we could suppress RX interrupt if we were so inclined.
*/
@@ -536,6 +546,43 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
return info;
}
+static void virtio_net_queue_reset(VirtIODevice *vdev, uint32_t queue_index)
+{
+ VirtIONet *n = VIRTIO_NET(vdev);
+ NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(queue_index));
+
+ if (!nc->peer) {
+ return;
+ }
+
+ if (get_vhost_net(nc->peer) &&
+ nc->peer->info->type == NET_CLIENT_DRIVER_TAP) {
+ vhost_net_virtqueue_reset(vdev, nc, queue_index);
+ }
+
+ flush_or_purge_queued_packets(nc);
+}
+
+static void virtio_net_queue_enable(VirtIODevice *vdev, uint32_t queue_index)
+{
+ VirtIONet *n = VIRTIO_NET(vdev);
+ NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(queue_index));
+ int r;
+
+ if (!nc->peer || !vdev->vhost_started) {
+ return;
+ }
+
+ if (get_vhost_net(nc->peer) &&
+ nc->peer->info->type == NET_CLIENT_DRIVER_TAP) {
+ r = vhost_net_virtqueue_restart(vdev, nc, queue_index);
+ if (r < 0) {
+ error_report("unable to restart vhost net virtqueue: %d, "
+ "when resetting the queue", queue_index);
+ }
+ }
+}
+
static void virtio_net_reset(VirtIODevice *vdev)
{
VirtIONet *n = VIRTIO_NET(vdev);
@@ -566,12 +613,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
/* Flush any async TX */
for (i = 0; i < n->max_queue_pairs; i++) {
- NetClientState *nc = qemu_get_subqueue(n->nic, i);
-
- if (nc->peer) {
- qemu_flush_or_purge_queued_packets(nc->peer, true);
- assert(!virtio_net_get_subqueue(nc)->async_tx.elem);
- }
+ flush_or_purge_queued_packets(qemu_get_subqueue(n->nic, i));
}
}
@@ -746,6 +788,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features,
}
if (!get_vhost_net(nc->peer)) {
+ virtio_add_feature(&features, VIRTIO_F_RING_RESET);
return features;
}
@@ -3822,6 +3865,8 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
vdc->set_features = virtio_net_set_features;
vdc->bad_features = virtio_net_bad_features;
vdc->reset = virtio_net_reset;
+ vdc->queue_reset = virtio_net_queue_reset;
+ vdc->queue_enable = virtio_net_queue_enable;
vdc->set_status = virtio_net_set_status;
vdc->guest_notifier_mask = virtio_net_guest_notifier_mask;
vdc->guest_notifier_pending = virtio_net_guest_notifier_pending;
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 0b7acf7f89..d2ab527ef4 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -2110,20 +2110,14 @@ vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors)
}
}
-static bool
+static void
vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors)
{
PCIDevice *d = PCI_DEVICE(s);
int i;
for (i = 0; i < num_vectors; i++) {
- int res = msix_vector_use(d, i);
- if (0 > res) {
- VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res);
- vmxnet3_unuse_msix_vectors(s, i);
- return false;
- }
+ msix_vector_use(d, i);
}
- return true;
}
static bool
@@ -2141,13 +2135,8 @@ vmxnet3_init_msix(VMXNET3State *s)
VMW_WRPRN("Failed to initialize MSI-X, error %d", res);
s->msix_used = false;
} else {
- if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) {
- VMW_WRPRN("Failed to use MSI-X vectors, error %d", res);
- msix_uninit(d, &s->msix_bar, &s->msix_bar);
- s->msix_used = false;
- } else {
- s->msix_used = true;
- }
+ vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS);
+ s->msix_used = true;
}
return s->msix_used;
}
@@ -2412,19 +2401,13 @@ static const VMStateDescription vmstate_vmxnet3_rxq_descr = {
static int vmxnet3_post_load(void *opaque, int version_id)
{
VMXNET3State *s = opaque;
- PCIDevice *d = PCI_DEVICE(s);
net_tx_pkt_init(&s->tx_pkt, PCI_DEVICE(s),
s->max_tx_frags, s->peer_has_vhdr);
net_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr);
if (s->msix_used) {
- if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) {
- VMW_WRPRN("Failed to re-use MSI-X vectors");
- msix_uninit(d, &s->msix_bar, &s->msix_bar);
- s->msix_used = false;
- return -1;
- }
+ vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS);
}
if (!vmxnet3_validate_queues(s)) {
diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 9a9857ccf8..ac3885ce50 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -4740,11 +4740,8 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr,
uint16_t cqid, uint16_t vector, uint16_t size,
uint16_t irq_enabled)
{
- int ret;
-
if (msix_enabled(&n->parent_obj)) {
- ret = msix_vector_use(&n->parent_obj, vector);
- assert(ret == 0);
+ msix_vector_use(&n->parent_obj, vector);
}
cq->ctrl = n;
cq->cqid = cqid;
diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c
index a83a3e81e4..9b8b57df9d 100644
--- a/hw/pci-bridge/cxl_upstream.c
+++ b/hw/pci-bridge/cxl_upstream.c
@@ -10,11 +10,12 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "hw/qdev-properties.h"
#include "hw/pci/msi.h"
#include "hw/pci/pcie.h"
#include "hw/pci/pcie_port.h"
-#define CXL_UPSTREAM_PORT_MSI_NR_VECTOR 1
+#define CXL_UPSTREAM_PORT_MSI_NR_VECTOR 2
#define CXL_UPSTREAM_PORT_MSI_OFFSET 0x70
#define CXL_UPSTREAM_PORT_PCIE_CAP_OFFSET 0x90
@@ -28,6 +29,7 @@ typedef struct CXLUpstreamPort {
/*< public >*/
CXLComponentState cxl_cstate;
+ DOECap doe_cdat;
} CXLUpstreamPort;
CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp)
@@ -60,6 +62,9 @@ static void cxl_usp_dvsec_write_config(PCIDevice *dev, uint32_t addr,
static void cxl_usp_write_config(PCIDevice *d, uint32_t address,
uint32_t val, int len)
{
+ CXLUpstreamPort *usp = CXL_USP(d);
+
+ pcie_doe_write_config(&usp->doe_cdat, address, val, len);
pci_bridge_write_config(d, address, val, len);
pcie_cap_flr_write_config(d, address, val, len);
pcie_aer_write_config(d, address, val, len);
@@ -67,6 +72,18 @@ static void cxl_usp_write_config(PCIDevice *d, uint32_t address,
cxl_usp_dvsec_write_config(d, address, val, len);
}
+static uint32_t cxl_usp_read_config(PCIDevice *d, uint32_t address, int len)
+{
+ CXLUpstreamPort *usp = CXL_USP(d);
+ uint32_t val;
+
+ if (pcie_doe_read_config(&usp->doe_cdat, address, len, &val)) {
+ return val;
+ }
+
+ return pci_default_read_config(d, address, len);
+}
+
static void latch_registers(CXLUpstreamPort *usp)
{
uint32_t *reg_state = usp->cxl_cstate.crb.cache_mem_registers;
@@ -119,6 +136,167 @@ static void build_dvsecs(CXLComponentState *cxl)
REG_LOC_DVSEC_REVID, dvsec);
}
+static bool cxl_doe_cdat_rsp(DOECap *doe_cap)
+{
+ CDATObject *cdat = &CXL_USP(doe_cap->pdev)->cxl_cstate.cdat;
+ uint16_t ent;
+ void *base;
+ uint32_t len;
+ CDATReq *req = pcie_doe_get_write_mbox_ptr(doe_cap);
+ CDATRsp rsp;
+
+ cxl_doe_cdat_update(&CXL_USP(doe_cap->pdev)->cxl_cstate, &error_fatal);
+ assert(cdat->entry_len);
+
+ /* Discard if request length mismatched */
+ if (pcie_doe_get_obj_len(req) <
+ DIV_ROUND_UP(sizeof(CDATReq), sizeof(uint32_t))) {
+ return false;
+ }
+
+ ent = req->entry_handle;
+ base = cdat->entry[ent].base;
+ len = cdat->entry[ent].length;
+
+ rsp = (CDATRsp) {
+ .header = {
+ .vendor_id = CXL_VENDOR_ID,
+ .data_obj_type = CXL_DOE_TABLE_ACCESS,
+ .reserved = 0x0,
+ .length = DIV_ROUND_UP((sizeof(rsp) + len), sizeof(uint32_t)),
+ },
+ .rsp_code = CXL_DOE_TAB_RSP,
+ .table_type = CXL_DOE_TAB_TYPE_CDAT,
+ .entry_handle = (ent < cdat->entry_len - 1) ?
+ ent + 1 : CXL_DOE_TAB_ENT_MAX,
+ };
+
+ memcpy(doe_cap->read_mbox, &rsp, sizeof(rsp));
+ memcpy(doe_cap->read_mbox + DIV_ROUND_UP(sizeof(rsp), sizeof(uint32_t)),
+ base, len);
+
+ doe_cap->read_mbox_len += rsp.header.length;
+
+ return true;
+}
+
+static DOEProtocol doe_cdat_prot[] = {
+ { CXL_VENDOR_ID, CXL_DOE_TABLE_ACCESS, cxl_doe_cdat_rsp },
+ { }
+};
+
+enum {
+ CXL_USP_CDAT_SSLBIS_LAT,
+ CXL_USP_CDAT_SSLBIS_BW,
+ CXL_USP_CDAT_NUM_ENTRIES
+};
+
+static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv)
+{
+ g_autofree CDATSslbis *sslbis_latency = NULL;
+ g_autofree CDATSslbis *sslbis_bandwidth = NULL;
+ CXLUpstreamPort *us = CXL_USP(priv);
+ PCIBus *bus = &PCI_BRIDGE(us)->sec_bus;
+ int devfn, sslbis_size, i;
+ int count = 0;
+ uint16_t port_ids[256];
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ PCIDevice *d = bus->devices[devfn];
+ PCIEPort *port;
+
+ if (!d || !pci_is_express(d) || !d->exp.exp_cap) {
+ continue;
+ }
+
+ /*
+ * Whilst the PCI express spec doesn't allow anything other than
+ * downstream ports on this bus, let us be a little paranoid
+ */
+ if (!object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) {
+ continue;
+ }
+
+ port = PCIE_PORT(d);
+ port_ids[count] = port->port;
+ count++;
+ }
+
+ /* May not yet have any ports - try again later */
+ if (count == 0) {
+ return 0;
+ }
+
+ sslbis_size = sizeof(CDATSslbis) + sizeof(*sslbis_latency->sslbe) * count;
+ sslbis_latency = g_malloc(sslbis_size);
+ if (!sslbis_latency) {
+ return -ENOMEM;
+ }
+ *sslbis_latency = (CDATSslbis) {
+ .sslbis_header = {
+ .header = {
+ .type = CDAT_TYPE_SSLBIS,
+ .length = sslbis_size,
+ },
+ .data_type = HMATLB_DATA_TYPE_ACCESS_LATENCY,
+ .entry_base_unit = 10000,
+ },
+ };
+
+ for (i = 0; i < count; i++) {
+ sslbis_latency->sslbe[i] = (CDATSslbe) {
+ .port_x_id = CDAT_PORT_ID_USP,
+ .port_y_id = port_ids[i],
+ .latency_bandwidth = 15, /* 150ns */
+ };
+ }
+
+ sslbis_bandwidth = g_malloc(sslbis_size);
+ if (!sslbis_bandwidth) {
+ return 0;
+ }
+ *sslbis_bandwidth = (CDATSslbis) {
+ .sslbis_header = {
+ .header = {
+ .type = CDAT_TYPE_SSLBIS,
+ .length = sslbis_size,
+ },
+ .data_type = HMATLB_DATA_TYPE_ACCESS_BANDWIDTH,
+ .entry_base_unit = 1000,
+ },
+ };
+
+ for (i = 0; i < count; i++) {
+ sslbis_bandwidth->sslbe[i] = (CDATSslbe) {
+ .port_x_id = CDAT_PORT_ID_USP,
+ .port_y_id = port_ids[i],
+ .latency_bandwidth = 16, /* 16 GB/s */
+ };
+ }
+
+ *cdat_table = g_malloc0(sizeof(*cdat_table) * CXL_USP_CDAT_NUM_ENTRIES);
+ if (!*cdat_table) {
+ return -ENOMEM;
+ }
+
+ /* Header always at start of structure */
+ (*cdat_table)[CXL_USP_CDAT_SSLBIS_LAT] = g_steal_pointer(&sslbis_latency);
+ (*cdat_table)[CXL_USP_CDAT_SSLBIS_BW] = g_steal_pointer(&sslbis_bandwidth);
+
+ return CXL_USP_CDAT_NUM_ENTRIES;
+}
+
+static void free_default_cdat_table(CDATSubHeader **cdat_table, int num,
+ void *priv)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ g_free(cdat_table[i]);
+ }
+ g_free(cdat_table);
+}
+
static void cxl_usp_realize(PCIDevice *d, Error **errp)
{
PCIEPort *p = PCIE_PORT(d);
@@ -161,6 +339,14 @@ static void cxl_usp_realize(PCIDevice *d, Error **errp)
PCI_BASE_ADDRESS_MEM_TYPE_64,
component_bar);
+ pcie_doe_init(d, &usp->doe_cdat, cxl_cstate->dvsec_offset, doe_cdat_prot,
+ true, 1);
+
+ cxl_cstate->cdat.build_cdat_table = build_cdat_table;
+ cxl_cstate->cdat.free_cdat_table = free_default_cdat_table;
+ cxl_cstate->cdat.private = d;
+ cxl_doe_cdat_init(cxl_cstate, errp);
+
return;
err_cap:
@@ -179,6 +365,11 @@ static void cxl_usp_exitfn(PCIDevice *d)
pci_bridge_exitfn(d);
}
+static Property cxl_upstream_props[] = {
+ DEFINE_PROP_STRING("cdat", CXLUpstreamPort, cxl_cstate.cdat.filename),
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void cxl_upstream_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
@@ -186,6 +377,7 @@ static void cxl_upstream_class_init(ObjectClass *oc, void *data)
k->is_bridge = true;
k->config_write = cxl_usp_write_config;
+ k->config_read = cxl_usp_read_config;
k->realize = cxl_usp_realize;
k->exit = cxl_usp_exitfn;
k->vendor_id = 0x19e5; /* Huawei */
@@ -194,6 +386,7 @@ static void cxl_upstream_class_init(ObjectClass *oc, void *data)
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->desc = "CXL Switch Upstream Port";
dc->reset = cxl_usp_reset;
+ device_class_set_props(dc, cxl_upstream_props);
}
static const TypeInfo cxl_usp_info = {
diff --git a/hw/pci/meson.build b/hw/pci/meson.build
index bcc9c75919..5aff7ed1c6 100644
--- a/hw/pci/meson.build
+++ b/hw/pci/meson.build
@@ -13,6 +13,7 @@ pci_ss.add(files(
# allow plugging PCIe devices into PCI buses, include them even if
# CONFIG_PCI_EXPRESS=n.
pci_ss.add(files('pcie.c', 'pcie_aer.c'))
+pci_ss.add(files('pcie_doe.c'))
softmmu_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c'))
softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
diff --git a/hw/pci/msix.c b/hw/pci/msix.c
index 1e381a9813..9e70fcd6fa 100644
--- a/hw/pci/msix.c
+++ b/hw/pci/msix.c
@@ -136,17 +136,12 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
}
}
-void msix_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp)
+void msix_set_mask(PCIDevice *dev, int vector, bool mask)
{
- ERRP_GUARD();
unsigned offset;
bool was_masked;
- if (vector > dev->msix_entries_nr) {
- error_setg(errp, "msix: vector %d not allocated. max vector is %d",
- vector, dev->msix_entries_nr);
- return;
- }
+ assert(vector < dev->msix_entries_nr);
offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
@@ -522,7 +517,9 @@ void msix_notify(PCIDevice *dev, unsigned vector)
{
MSIMessage msg;
- if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
+ assert(vector < dev->msix_entries_nr);
+
+ if (!dev->msix_entry_used[vector]) {
return;
}
@@ -558,20 +555,17 @@ void msix_reset(PCIDevice *dev)
* don't want to follow the spec suggestion can declare all vectors as used. */
/* Mark vector as used. */
-int msix_vector_use(PCIDevice *dev, unsigned vector)
+void msix_vector_use(PCIDevice *dev, unsigned vector)
{
- if (vector >= dev->msix_entries_nr) {
- return -EINVAL;
- }
-
+ assert(vector < dev->msix_entries_nr);
dev->msix_entry_used[vector]++;
- return 0;
}
/* Mark vector as unused. */
void msix_vector_unuse(PCIDevice *dev, unsigned vector)
{
- if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
+ assert(vector < dev->msix_entries_nr);
+ if (!dev->msix_entry_used[vector]) {
return;
}
if (--dev->msix_entry_used[vector]) {
diff --git a/hw/pci/pcie_doe.c b/hw/pci/pcie_doe.c
new file mode 100644
index 0000000000..2210f86968
--- /dev/null
+++ b/hw/pci/pcie_doe.c
@@ -0,0 +1,367 @@
+/*
+ * PCIe Data Object Exchange
+ *
+ * Copyright (C) 2021 Avery Design Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qemu/range.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_doe.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/msix.h"
+
+#define DWORD_BYTE 4
+
+typedef struct DoeDiscoveryReq {
+ DOEHeader header;
+ uint8_t index;
+ uint8_t reserved[3];
+} QEMU_PACKED DoeDiscoveryReq;
+
+typedef struct DoeDiscoveryRsp {
+ DOEHeader header;
+ uint16_t vendor_id;
+ uint8_t data_obj_type;
+ uint8_t next_index;
+} QEMU_PACKED DoeDiscoveryRsp;
+
+static bool pcie_doe_discovery(DOECap *doe_cap)
+{
+ DoeDiscoveryReq *req = pcie_doe_get_write_mbox_ptr(doe_cap);
+ DoeDiscoveryRsp rsp;
+ uint8_t index = req->index;
+ DOEProtocol *prot;
+
+ /* Discard request if length does not match DoeDiscoveryReq */
+ if (pcie_doe_get_obj_len(req) <
+ DIV_ROUND_UP(sizeof(DoeDiscoveryReq), DWORD_BYTE)) {
+ return false;
+ }
+
+ rsp.header = (DOEHeader) {
+ .vendor_id = PCI_VENDOR_ID_PCI_SIG,
+ .data_obj_type = PCI_SIG_DOE_DISCOVERY,
+ .length = DIV_ROUND_UP(sizeof(DoeDiscoveryRsp), DWORD_BYTE),
+ };
+
+ /* Point to the requested protocol, index 0 must be Discovery */
+ if (index == 0) {
+ rsp.vendor_id = PCI_VENDOR_ID_PCI_SIG;
+ rsp.data_obj_type = PCI_SIG_DOE_DISCOVERY;
+ } else {
+ if (index < doe_cap->protocol_num) {
+ prot = &doe_cap->protocols[index - 1];
+ rsp.vendor_id = prot->vendor_id;
+ rsp.data_obj_type = prot->data_obj_type;
+ } else {
+ rsp.vendor_id = 0xFFFF;
+ rsp.data_obj_type = 0xFF;
+ }
+ }
+
+ if (index + 1 == doe_cap->protocol_num) {
+ rsp.next_index = 0;
+ } else {
+ rsp.next_index = index + 1;
+ }
+
+ pcie_doe_set_rsp(doe_cap, &rsp);
+
+ return true;
+}
+
+static void pcie_doe_reset_mbox(DOECap *st)
+{
+ st->read_mbox_idx = 0;
+ st->read_mbox_len = 0;
+ st->write_mbox_len = 0;
+
+ memset(st->read_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
+ memset(st->write_mbox, 0, PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
+}
+
+void pcie_doe_init(PCIDevice *dev, DOECap *doe_cap, uint16_t offset,
+ DOEProtocol *protocols, bool intr, uint16_t vec)
+{
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_DOE, 0x1, offset,
+ PCI_DOE_SIZEOF);
+
+ doe_cap->pdev = dev;
+ doe_cap->offset = offset;
+
+ if (intr && (msi_present(dev) || msix_present(dev))) {
+ doe_cap->cap.intr = intr;
+ doe_cap->cap.vec = vec;
+ }
+
+ doe_cap->write_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
+ doe_cap->read_mbox = g_malloc0(PCI_DOE_DW_SIZE_MAX * DWORD_BYTE);
+
+ pcie_doe_reset_mbox(doe_cap);
+
+ doe_cap->protocols = protocols;
+ for (; protocols->vendor_id; protocols++) {
+ doe_cap->protocol_num++;
+ }
+ assert(doe_cap->protocol_num < PCI_DOE_PROTOCOL_NUM_MAX);
+
+ /* Increment to allow for the discovery protocol */
+ doe_cap->protocol_num++;
+}
+
+void pcie_doe_fini(DOECap *doe_cap)
+{
+ g_free(doe_cap->read_mbox);
+ g_free(doe_cap->write_mbox);
+ g_free(doe_cap);
+}
+
+uint32_t pcie_doe_build_protocol(DOEProtocol *p)
+{
+ return DATA_OBJ_BUILD_HEADER1(p->vendor_id, p->data_obj_type);
+}
+
+void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap)
+{
+ return doe_cap->write_mbox;
+}
+
+/*
+ * Copy the response to read mailbox buffer
+ * This might be called in self-defined handle_request() if a DOE response is
+ * required in the corresponding protocol
+ */
+void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp)
+{
+ uint32_t len = pcie_doe_get_obj_len(rsp);
+
+ memcpy(doe_cap->read_mbox + doe_cap->read_mbox_len, rsp, len * DWORD_BYTE);
+ doe_cap->read_mbox_len += len;
+}
+
+uint32_t pcie_doe_get_obj_len(void *obj)
+{
+ uint32_t len;
+
+ if (!obj) {
+ return 0;
+ }
+
+ /* Only lower 18 bits are valid */
+ len = DATA_OBJ_LEN_MASK(((DOEHeader *)obj)->length);
+
+ /* PCIe r6.0 Table 6.29: a value of 00000h indicates 2^18 DW */
+ return (len) ? len : PCI_DOE_DW_SIZE_MAX;
+}
+
+static void pcie_doe_irq_assert(DOECap *doe_cap)
+{
+ PCIDevice *dev = doe_cap->pdev;
+
+ if (doe_cap->cap.intr && doe_cap->ctrl.intr) {
+ if (doe_cap->status.intr) {
+ return;
+ }
+ doe_cap->status.intr = 1;
+
+ if (msix_enabled(dev)) {
+ msix_notify(dev, doe_cap->cap.vec);
+ } else if (msi_enabled(dev)) {
+ msi_notify(dev, doe_cap->cap.vec);
+ }
+ }
+}
+
+static void pcie_doe_set_ready(DOECap *doe_cap, bool rdy)
+{
+ doe_cap->status.ready = rdy;
+
+ if (rdy) {
+ pcie_doe_irq_assert(doe_cap);
+ }
+}
+
+static void pcie_doe_set_error(DOECap *doe_cap, bool err)
+{
+ doe_cap->status.error = err;
+
+ if (err) {
+ pcie_doe_irq_assert(doe_cap);
+ }
+}
+
+/*
+ * Check incoming request in write_mbox for protocol format
+ */
+static void pcie_doe_prepare_rsp(DOECap *doe_cap)
+{
+ bool success = false;
+ int p;
+ bool (*handle_request)(DOECap *) = NULL;
+
+ if (doe_cap->status.error) {
+ return;
+ }
+
+ if (doe_cap->write_mbox[0] ==
+ DATA_OBJ_BUILD_HEADER1(PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_DISCOVERY)) {
+ handle_request = pcie_doe_discovery;
+ } else {
+ for (p = 0; p < doe_cap->protocol_num - 1; p++) {
+ if (doe_cap->write_mbox[0] ==
+ pcie_doe_build_protocol(&doe_cap->protocols[p])) {
+ handle_request = doe_cap->protocols[p].handle_request;
+ break;
+ }
+ }
+ }
+
+ /*
+ * PCIe r6 DOE 6.30.1:
+ * If the number of DW transferred does not match the
+ * indicated Length for a data object, then the
+ * data object must be silently discarded.
+ */
+ if (handle_request && (doe_cap->write_mbox_len ==
+ pcie_doe_get_obj_len(pcie_doe_get_write_mbox_ptr(doe_cap)))) {
+ success = handle_request(doe_cap);
+ }
+
+ if (success) {
+ pcie_doe_set_ready(doe_cap, 1);
+ } else {
+ pcie_doe_reset_mbox(doe_cap);
+ }
+}
+
+/*
+ * Read from DOE config space.
+ * Return false if the address not within DOE_CAP range.
+ */
+bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size,
+ uint32_t *buf)
+{
+ uint32_t shift;
+ uint16_t doe_offset = doe_cap->offset;
+
+ if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP,
+ PCI_DOE_SIZEOF - 4, addr)) {
+ return false;
+ }
+
+ addr -= doe_offset;
+ *buf = 0;
+
+ if (range_covers_byte(PCI_EXP_DOE_CAP, DWORD_BYTE, addr)) {
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, INTR_SUPP,
+ doe_cap->cap.intr);
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM,
+ doe_cap->cap.vec);
+ } else if (range_covers_byte(PCI_EXP_DOE_CTRL, DWORD_BYTE, addr)) {
+ /* Must return ABORT=0 and GO=0 */
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_CONTROL, DOE_INTR_EN,
+ doe_cap->ctrl.intr);
+ } else if (range_covers_byte(PCI_EXP_DOE_STATUS, DWORD_BYTE, addr)) {
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_BUSY,
+ doe_cap->status.busy);
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS,
+ doe_cap->status.intr);
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DOE_ERROR,
+ doe_cap->status.error);
+ *buf = FIELD_DP32(*buf, PCI_DOE_CAP_STATUS, DATA_OBJ_RDY,
+ doe_cap->status.ready);
+ /* Mailbox should be DW accessed */
+ } else if (addr == PCI_EXP_DOE_RD_DATA_MBOX && size == DWORD_BYTE) {
+ if (doe_cap->status.ready && !doe_cap->status.error) {
+ *buf = doe_cap->read_mbox[doe_cap->read_mbox_idx];
+ }
+ }
+
+ /* Process Alignment */
+ shift = addr % DWORD_BYTE;
+ *buf = extract32(*buf, shift * 8, size * 8);
+
+ return true;
+}
+
+/*
+ * Write to DOE config space.
+ * Return if the address not within DOE_CAP range or receives an abort
+ */
+void pcie_doe_write_config(DOECap *doe_cap,
+ uint32_t addr, uint32_t val, int size)
+{
+ uint16_t doe_offset = doe_cap->offset;
+ uint32_t shift;
+
+ if (!range_covers_byte(doe_offset + PCI_EXP_DOE_CAP,
+ PCI_DOE_SIZEOF - 4, addr)) {
+ return;
+ }
+
+ /* Process Alignment */
+ shift = addr % DWORD_BYTE;
+ addr -= (doe_offset + shift);
+ val = deposit32(val, shift * 8, size * 8, val);
+
+ switch (addr) {
+ case PCI_EXP_DOE_CTRL:
+ if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_ABORT)) {
+ pcie_doe_set_ready(doe_cap, 0);
+ pcie_doe_set_error(doe_cap, 0);
+ pcie_doe_reset_mbox(doe_cap);
+ return;
+ }
+
+ if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_GO)) {
+ pcie_doe_prepare_rsp(doe_cap);
+ }
+
+ if (FIELD_EX32(val, PCI_DOE_CAP_CONTROL, DOE_INTR_EN)) {
+ doe_cap->ctrl.intr = 1;
+ /* Clear interrupt bit located within the first byte */
+ } else if (shift == 0) {
+ doe_cap->ctrl.intr = 0;
+ }
+ break;
+ case PCI_EXP_DOE_STATUS:
+ if (FIELD_EX32(val, PCI_DOE_CAP_STATUS, DOE_INTR_STATUS)) {
+ doe_cap->status.intr = 0;
+ }
+ break;
+ case PCI_EXP_DOE_RD_DATA_MBOX:
+ /* Mailbox should be DW accessed */
+ if (size != DWORD_BYTE) {
+ return;
+ }
+ doe_cap->read_mbox_idx++;
+ if (doe_cap->read_mbox_idx == doe_cap->read_mbox_len) {
+ pcie_doe_reset_mbox(doe_cap);
+ pcie_doe_set_ready(doe_cap, 0);
+ } else if (doe_cap->read_mbox_idx > doe_cap->read_mbox_len) {
+ /* Underflow */
+ pcie_doe_set_error(doe_cap, 1);
+ }
+ break;
+ case PCI_EXP_DOE_WR_DATA_MBOX:
+ /* Mailbox should be DW accessed */
+ if (size != DWORD_BYTE) {
+ return;
+ }
+ doe_cap->write_mbox[doe_cap->write_mbox_len] = val;
+ doe_cap->write_mbox_len++;
+ break;
+ case PCI_EXP_DOE_CAP:
+ /* fallthrough */
+ default:
+ break;
+ }
+}
diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c
index 58db0b8e3b..4fc6712025 100644
--- a/hw/rdma/vmw/pvrdma_main.c
+++ b/hw/rdma/vmw/pvrdma_main.c
@@ -307,12 +307,7 @@ static int init_msix(PCIDevice *pdev)
}
for (i = 0; i < RDMA_MAX_INTRS; i++) {
- rc = msix_vector_use(PCI_DEVICE(dev), i);
- if (rc < 0) {
- rdma_error_report("Fail mark MSI-X vector %d", i);
- uninit_msix(pdev, i);
- return rc;
- }
+ msix_vector_use(PCI_DEVICE(dev), i);
}
return 0;
diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c
index c6cc53acf2..4e36bb8bcf 100644
--- a/hw/remote/vfio-user-obj.c
+++ b/hw/remote/vfio-user-obj.c
@@ -602,17 +602,10 @@ static void vfu_msix_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
uint32_t count, bool mask)
{
VfuObject *o = vfu_get_private(vfu_ctx);
- Error *err = NULL;
uint32_t vector;
for (vector = start; vector < count; vector++) {
- msix_set_mask(o->pci_dev, vector, mask, &err);
- if (err) {
- VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device,
- error_get_pretty(err));
- error_free(err);
- err = NULL;
- }
+ msix_set_mask(o->pci_dev, vector, mask);
}
}
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 51437ca09f..b4243de735 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -711,8 +711,14 @@ static void smbios_build_type_3_table(void)
static void smbios_build_type_4_table(MachineState *ms, unsigned instance)
{
char sock_str[128];
+ size_t tbl_len = SMBIOS_TYPE_4_LEN_V28;
- SMBIOS_BUILD_TABLE_PRE(4, T4_BASE + instance, true); /* required */
+ if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_64) {
+ tbl_len = SMBIOS_TYPE_4_LEN_V30;
+ }
+
+ SMBIOS_BUILD_TABLE_PRE_SIZE(4, T4_BASE + instance,
+ true, tbl_len); /* required */
snprintf(sock_str, sizeof(sock_str), "%s%2x", type4.sock_pfx, instance);
SMBIOS_TABLE_SET_STR(4, socket_designation_str, sock_str);
@@ -739,8 +745,15 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance)
SMBIOS_TABLE_SET_STR(4, serial_number_str, type4.serial);
SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset);
SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part);
- t->core_count = t->core_enabled = ms->smp.cores;
- t->thread_count = ms->smp.threads;
+
+ t->core_count = (ms->smp.cores > 255) ? 0xFF : ms->smp.cores;
+ t->core_enabled = t->core_count;
+
+ t->core_count2 = t->core_enabled2 = cpu_to_le16(ms->smp.cores);
+
+ t->thread_count = (ms->smp.threads > 255) ? 0xFF : ms->smp.threads;
+ t->thread_count2 = cpu_to_le16(ms->smp.threads);
+
t->processor_characteristics = cpu_to_le16(0x02); /* Unknown */
t->processor_family2 = cpu_to_le16(0x01); /* Other */
diff --git a/hw/smbios/smbios_build.h b/hw/smbios/smbios_build.h
index 56b5a1e3f3..351660024e 100644
--- a/hw/smbios/smbios_build.h
+++ b/hw/smbios/smbios_build.h
@@ -27,6 +27,11 @@ extern unsigned smbios_table_max;
extern unsigned smbios_table_cnt;
#define SMBIOS_BUILD_TABLE_PRE(tbl_type, tbl_handle, tbl_required) \
+ SMBIOS_BUILD_TABLE_PRE_SIZE(tbl_type, tbl_handle, tbl_required, \
+ sizeof(struct smbios_type_##tbl_type))\
+
+#define SMBIOS_BUILD_TABLE_PRE_SIZE(tbl_type, tbl_handle, \
+ tbl_required, tbl_len) \
struct smbios_type_##tbl_type *t; \
size_t t_off; /* table offset into smbios_tables */ \
int str_index = 0; \
@@ -39,12 +44,12 @@ extern unsigned smbios_table_cnt;
/* use offset of table t within smbios_tables */ \
/* (pointer must be updated after each realloc) */ \
t_off = smbios_tables_len; \
- smbios_tables_len += sizeof(*t); \
+ smbios_tables_len += tbl_len; \
smbios_tables = g_realloc(smbios_tables, smbios_tables_len); \
t = (struct smbios_type_##tbl_type *)(smbios_tables + t_off); \
\
t->header.type = tbl_type; \
- t->header.length = sizeof(*t); \
+ t->header.length = tbl_len; \
t->header.handle = cpu_to_le16(tbl_handle); \
} while (0)
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 6b5d8c0bf6..130e5d1dc7 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -578,45 +578,11 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
ram_addr_t *ram_addr, bool *read_only)
{
- MemoryRegion *mr;
- hwaddr xlat;
- hwaddr len = iotlb->addr_mask + 1;
- bool writable = iotlb->perm & IOMMU_WO;
-
- /*
- * The IOMMU TLB entry we have just covers translation through
- * this IOMMU to its immediate target. We need to translate
- * it the rest of the way through to memory.
- */
- mr = address_space_translate(&address_space_memory,
- iotlb->translated_addr,
- &xlat, &len, writable,
- MEMTXATTRS_UNSPECIFIED);
- if (!memory_region_is_ram(mr)) {
- error_report("iommu map to non memory area %"HWADDR_PRIx"",
- xlat);
- return false;
- } else if (memory_region_has_ram_discard_manager(mr)) {
- RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
- MemoryRegionSection tmp = {
- .mr = mr,
- .offset_within_region = xlat,
- .size = int128_make64(len),
- };
-
- /*
- * Malicious VMs can map memory into the IOMMU, which is expected
- * to remain discarded. vfio will pin all pages, populating memory.
- * Disallow that. vmstate priorities make sure any RamDiscardManager
- * were already restored before IOMMUs are restored.
- */
- if (!ram_discard_manager_is_populated(rdm, &tmp)) {
- error_report("iommu map to discarded memory (e.g., unplugged via"
- " virtio-mem): %"HWADDR_PRIx"",
- iotlb->translated_addr);
- return false;
- }
+ bool ret, mr_has_discard_manager;
+ ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only,
+ &mr_has_discard_manager);
+ if (ret && mr_has_discard_manager) {
/*
* Malicious VMs might trigger discarding of IOMMU-mapped memory. The
* pages will remain pinned inside vfio until unmapped, resulting in a
@@ -635,29 +601,7 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
" intended via an IOMMU. It's possible to mitigate "
" by setting/adjusting RLIMIT_MEMLOCK.");
}
-
- /*
- * Translation truncates length to the IOMMU page size,
- * check that it did not truncate too much.
- */
- if (len & iotlb->addr_mask) {
- error_report("iommu has granularity incompatible with target AS");
- return false;
- }
-
- if (vaddr) {
- *vaddr = memory_region_get_ram_ptr(mr) + xlat;
- }
-
- if (ram_addr) {
- *ram_addr = memory_region_get_ram_addr(mr) + xlat;
- }
-
- if (read_only) {
- *read_only = !writable || mr->readonly;
- }
-
- return true;
+ return ret;
}
static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c
index ad0f91c607..1c40f42045 100644
--- a/hw/virtio/vhost-user-fs.c
+++ b/hw/virtio/vhost-user-fs.c
@@ -123,7 +123,7 @@ static void vuf_stop(VirtIODevice *vdev)
static void vuf_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserFS *fs = VHOST_USER_FS(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
if (vhost_dev_is_started(&fs->vhost_dev) == should_start) {
return;
diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c
index 8b40fe450c..677d1c7730 100644
--- a/hw/virtio/vhost-user-gpio.c
+++ b/hw/virtio/vhost-user-gpio.c
@@ -152,7 +152,7 @@ static void vu_gpio_stop(VirtIODevice *vdev)
static void vu_gpio_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
trace_virtio_gpio_set_status(status);
diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c
index bc58b6c0d1..864eba695e 100644
--- a/hw/virtio/vhost-user-i2c.c
+++ b/hw/virtio/vhost-user-i2c.c
@@ -93,7 +93,7 @@ static void vu_i2c_stop(VirtIODevice *vdev)
static void vu_i2c_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserI2C *i2c = VHOST_USER_I2C(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
if (vhost_dev_is_started(&i2c->vhost_dev) == should_start) {
return;
diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c
index bc1f36c5ac..8b47287875 100644
--- a/hw/virtio/vhost-user-rng.c
+++ b/hw/virtio/vhost-user-rng.c
@@ -90,7 +90,7 @@ static void vu_rng_stop(VirtIODevice *vdev)
static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostUserRNG *rng = VHOST_USER_RNG(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
if (vhost_dev_is_started(&rng->vhost_dev) == should_start) {
return;
diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c
index 7b67e29d83..9431b9792c 100644
--- a/hw/virtio/vhost-user-vsock.c
+++ b/hw/virtio/vhost-user-vsock.c
@@ -55,7 +55,7 @@ const VhostDevConfigOps vsock_ops = {
static void vuv_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) {
return;
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 03415b6c95..abe23d4ebe 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -81,6 +81,7 @@ enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13,
/* Feature 14 reserved for VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS. */
VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15,
+ VHOST_USER_PROTOCOL_F_STATUS = 16,
VHOST_USER_PROTOCOL_F_MAX
};
@@ -126,6 +127,8 @@ typedef enum VhostUserRequest {
VHOST_USER_GET_MAX_MEM_SLOTS = 36,
VHOST_USER_ADD_MEM_REG = 37,
VHOST_USER_REM_MEM_REG = 38,
+ VHOST_USER_SET_STATUS = 39,
+ VHOST_USER_GET_STATUS = 40,
VHOST_USER_MAX
} VhostUserRequest;
@@ -1452,6 +1455,43 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64,
return 0;
}
+static int vhost_user_set_status(struct vhost_dev *dev, uint8_t status)
+{
+ return vhost_user_set_u64(dev, VHOST_USER_SET_STATUS, status, false);
+}
+
+static int vhost_user_get_status(struct vhost_dev *dev, uint8_t *status)
+{
+ uint64_t value;
+ int ret;
+
+ ret = vhost_user_get_u64(dev, VHOST_USER_GET_STATUS, &value);
+ if (ret < 0) {
+ return ret;
+ }
+ *status = value;
+
+ return 0;
+}
+
+static int vhost_user_add_status(struct vhost_dev *dev, uint8_t status)
+{
+ uint8_t s;
+ int ret;
+
+ ret = vhost_user_get_status(dev, &s);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if ((s & status) == status) {
+ return 0;
+ }
+ s |= status;
+
+ return vhost_user_set_status(dev, s);
+}
+
static int vhost_user_set_features(struct vhost_dev *dev,
uint64_t features)
{
@@ -1460,6 +1500,7 @@ static int vhost_user_set_features(struct vhost_dev *dev,
* backend is actually logging changes
*/
bool log_enabled = features & (0x1ULL << VHOST_F_LOG_ALL);
+ int ret;
/*
* We need to include any extra backend only feature bits that
@@ -1467,9 +1508,18 @@ static int vhost_user_set_features(struct vhost_dev *dev,
* VHOST_USER_F_PROTOCOL_FEATURES bit for enabling protocol
* features.
*/
- return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES,
+ ret = vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES,
features | dev->backend_features,
log_enabled);
+
+ if (virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_STATUS)) {
+ if (!ret) {
+ return vhost_user_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
+ }
+ }
+
+ return ret;
}
static int vhost_user_set_protocol_features(struct vhost_dev *dev,
@@ -1543,6 +1593,11 @@ static VhostUserHostNotifier *fetch_or_create_notifier(VhostUserState *u,
n = g_ptr_array_index(u->notifiers, idx);
if (!n) {
+ /*
+ * In case notification arrive out-of-order,
+ * make room for current index.
+ */
+ g_ptr_array_remove_index(u->notifiers, idx);
n = g_new0(VhostUserHostNotifier, 1);
n->idx = idx;
g_ptr_array_insert(u->notifiers, idx, n);
@@ -2615,6 +2670,27 @@ void vhost_user_cleanup(VhostUserState *user)
user->chr = NULL;
}
+static int vhost_user_dev_start(struct vhost_dev *dev, bool started)
+{
+ if (!virtio_has_feature(dev->protocol_features,
+ VHOST_USER_PROTOCOL_F_STATUS)) {
+ return 0;
+ }
+
+ /* Set device status only for last queue pair */
+ if (dev->vq_index + dev->nvqs != dev->vq_index_end) {
+ return 0;
+ }
+
+ if (started) {
+ return vhost_user_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE |
+ VIRTIO_CONFIG_S_DRIVER |
+ VIRTIO_CONFIG_S_DRIVER_OK);
+ } else {
+ return vhost_user_set_status(dev, 0);
+ }
+}
+
const VhostOps user_ops = {
.backend_type = VHOST_BACKEND_TYPE_USER,
.vhost_backend_init = vhost_user_backend_init,
@@ -2649,4 +2725,5 @@ const VhostOps user_ops = {
.vhost_backend_mem_section_filter = vhost_user_mem_section_filter,
.vhost_get_inflight_fd = vhost_user_get_inflight_fd,
.vhost_set_inflight_fd = vhost_user_set_inflight_fd,
+ .vhost_dev_start = vhost_user_dev_start,
};
diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c
index 7dc3c73931..aa16d584ee 100644
--- a/hw/virtio/vhost-vsock.c
+++ b/hw/virtio/vhost-vsock.c
@@ -70,7 +70,7 @@ static int vhost_vsock_set_running(VirtIODevice *vdev, int start)
static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
{
VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
- bool should_start = virtio_device_started(vdev, status);
+ bool should_start = virtio_device_should_start(vdev, status);
int ret;
if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) {
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 5185c15295..d1c4c20b8c 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -1081,10 +1081,10 @@ out:
return ret;
}
-static int vhost_virtqueue_start(struct vhost_dev *dev,
- struct VirtIODevice *vdev,
- struct vhost_virtqueue *vq,
- unsigned idx)
+int vhost_virtqueue_start(struct vhost_dev *dev,
+ struct VirtIODevice *vdev,
+ struct vhost_virtqueue *vq,
+ unsigned idx)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
VirtioBusState *vbus = VIRTIO_BUS(qbus);
@@ -1201,10 +1201,10 @@ fail_alloc_desc:
return r;
}
-static void vhost_virtqueue_stop(struct vhost_dev *dev,
- struct VirtIODevice *vdev,
- struct vhost_virtqueue *vq,
- unsigned idx)
+void vhost_virtqueue_stop(struct vhost_dev *dev,
+ struct VirtIODevice *vdev,
+ struct vhost_virtqueue *vq,
+ unsigned idx)
{
int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx);
struct vhost_vring_state state = {
diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c
index df4bde210b..97da74e719 100644
--- a/hw/virtio/virtio-crypto.c
+++ b/hw/virtio/virtio-crypto.c
@@ -27,6 +27,39 @@
#define VIRTIO_CRYPTO_VM_VERSION 1
+typedef struct VirtIOCryptoSessionReq {
+ VirtIODevice *vdev;
+ VirtQueue *vq;
+ VirtQueueElement *elem;
+ CryptoDevBackendSessionInfo info;
+ CryptoDevCompletionFunc cb;
+} VirtIOCryptoSessionReq;
+
+static void virtio_crypto_free_create_session_req(VirtIOCryptoSessionReq *sreq)
+{
+ switch (sreq->info.op_code) {
+ case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
+ g_free(sreq->info.u.sym_sess_info.cipher_key);
+ g_free(sreq->info.u.sym_sess_info.auth_key);
+ break;
+
+ case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
+ g_free(sreq->info.u.asym_sess_info.key);
+ break;
+
+ case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
+ case VIRTIO_CRYPTO_HASH_DESTROY_SESSION:
+ case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
+ case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
+ case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
+ break;
+
+ default:
+ error_report("Unknown opcode: %u", sreq->info.op_code);
+ }
+ g_free(sreq);
+}
+
/*
* Transfer virtqueue index to crypto queue index.
* The control virtqueue is after the data virtqueues
@@ -75,27 +108,24 @@ virtio_crypto_cipher_session_helper(VirtIODevice *vdev,
return 0;
}
-static int64_t
+static int
virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
struct virtio_crypto_sym_create_session_req *sess_req,
uint32_t queue_id,
uint32_t opcode,
- struct iovec *iov, unsigned int out_num)
+ struct iovec *iov, unsigned int out_num,
+ VirtIOCryptoSessionReq *sreq)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
- CryptoDevBackendSessionInfo info;
- CryptoDevBackendSymSessionInfo *sym_info;
- int64_t session_id;
+ CryptoDevBackendSymSessionInfo *sym_info = &sreq->info.u.sym_sess_info;
int queue_index;
uint32_t op_type;
- Error *local_err = NULL;
int ret;
- memset(&info, 0, sizeof(info));
op_type = ldl_le_p(&sess_req->op_type);
- info.op_code = opcode;
+ sreq->info.op_code = opcode;
- sym_info = &info.u.sym_sess_info;
+ sym_info = &sreq->info.u.sym_sess_info;
sym_info->op_type = op_type;
if (op_type == VIRTIO_CRYPTO_SYM_OP_CIPHER) {
@@ -103,7 +133,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
&sess_req->u.cipher.para,
&iov, &out_num);
if (ret < 0) {
- goto err;
+ return ret;
}
} else if (op_type == VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
size_t s;
@@ -112,7 +142,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
&sess_req->u.chain.para.cipher_param,
&iov, &out_num);
if (ret < 0) {
- goto err;
+ return ret;
}
/* hash part */
sym_info->alg_chain_order = ldl_le_p(
@@ -129,8 +159,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
if (sym_info->auth_key_len > vcrypto->conf.max_auth_key_len) {
error_report("virtio-crypto length of auth key is too big: %u",
sym_info->auth_key_len);
- ret = -VIRTIO_CRYPTO_ERR;
- goto err;
+ return -VIRTIO_CRYPTO_ERR;
}
/* get auth key */
if (sym_info->auth_key_len > 0) {
@@ -140,8 +169,7 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
if (unlikely(s != sym_info->auth_key_len)) {
virtio_error(vdev,
"virtio-crypto authenticated key incorrect");
- ret = -EFAULT;
- goto err;
+ return -EFAULT;
}
iov_discard_front(&iov, &out_num, sym_info->auth_key_len);
}
@@ -153,49 +181,30 @@ virtio_crypto_create_sym_session(VirtIOCrypto *vcrypto,
} else {
/* VIRTIO_CRYPTO_SYM_HASH_MODE_NESTED */
error_report("unsupported hash mode");
- ret = -VIRTIO_CRYPTO_NOTSUPP;
- goto err;
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
} else {
/* VIRTIO_CRYPTO_SYM_OP_NONE */
error_report("unsupported cipher op_type: VIRTIO_CRYPTO_SYM_OP_NONE");
- ret = -VIRTIO_CRYPTO_NOTSUPP;
- goto err;
+ return -VIRTIO_CRYPTO_NOTSUPP;
}
queue_index = virtio_crypto_vq2q(queue_id);
- session_id = cryptodev_backend_create_session(
- vcrypto->cryptodev,
- &info, queue_index, &local_err);
- if (session_id >= 0) {
- ret = session_id;
- } else {
- if (local_err) {
- error_report_err(local_err);
- }
- ret = -VIRTIO_CRYPTO_ERR;
- }
-
-err:
- g_free(sym_info->cipher_key);
- g_free(sym_info->auth_key);
- return ret;
+ return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
+ queue_index, sreq->cb, sreq);
}
-static int64_t
+static int
virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
struct virtio_crypto_akcipher_create_session_req *sess_req,
uint32_t queue_id, uint32_t opcode,
- struct iovec *iov, unsigned int out_num)
+ struct iovec *iov, unsigned int out_num,
+ VirtIOCryptoSessionReq *sreq)
{
VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
- CryptoDevBackendSessionInfo info = {0};
- CryptoDevBackendAsymSessionInfo *asym_info;
- int64_t session_id;
+ CryptoDevBackendAsymSessionInfo *asym_info = &sreq->info.u.asym_sess_info;
int queue_index;
uint32_t algo, keytype, keylen;
- g_autofree uint8_t *key = NULL;
- Error *local_err = NULL;
algo = ldl_le_p(&sess_req->para.algo);
keytype = ldl_le_p(&sess_req->para.keytype);
@@ -208,20 +217,19 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
}
if (keylen) {
- key = g_malloc(keylen);
- if (iov_to_buf(iov, out_num, 0, key, keylen) != keylen) {
+ asym_info->key = g_malloc(keylen);
+ if (iov_to_buf(iov, out_num, 0, asym_info->key, keylen) != keylen) {
virtio_error(vdev, "virtio-crypto asym key incorrect");
return -EFAULT;
}
iov_discard_front(&iov, &out_num, keylen);
}
- info.op_code = opcode;
- asym_info = &info.u.asym_sess_info;
+ sreq->info.op_code = opcode;
+ asym_info = &sreq->info.u.asym_sess_info;
asym_info->algo = algo;
asym_info->keytype = keytype;
asym_info->keylen = keylen;
- asym_info->key = key;
switch (asym_info->algo) {
case VIRTIO_CRYPTO_AKCIPHER_RSA:
asym_info->u.rsa.padding_algo =
@@ -237,45 +245,95 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto,
}
queue_index = virtio_crypto_vq2q(queue_id);
- session_id = cryptodev_backend_create_session(vcrypto->cryptodev, &info,
- queue_index, &local_err);
- if (session_id < 0) {
- if (local_err) {
- error_report_err(local_err);
- }
- return -VIRTIO_CRYPTO_ERR;
- }
-
- return session_id;
+ return cryptodev_backend_create_session(vcrypto->cryptodev, &sreq->info,
+ queue_index, sreq->cb, sreq);
}
-static uint8_t
+static int
virtio_crypto_handle_close_session(VirtIOCrypto *vcrypto,
struct virtio_crypto_destroy_session_req *close_sess_req,
- uint32_t queue_id)
+ uint32_t queue_id,
+ VirtIOCryptoSessionReq *sreq)
{
- int ret;
uint64_t session_id;
- uint32_t status;
- Error *local_err = NULL;
session_id = ldq_le_p(&close_sess_req->session_id);
DPRINTF("close session, id=%" PRIu64 "\n", session_id);
- ret = cryptodev_backend_close_session(
- vcrypto->cryptodev, session_id, queue_id, &local_err);
- if (ret == 0) {
- status = VIRTIO_CRYPTO_OK;
+ return cryptodev_backend_close_session(
+ vcrypto->cryptodev, session_id, queue_id, sreq->cb, sreq);
+}
+
+static void virtio_crypto_create_session_completion(void *opaque, int ret)
+{
+ VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
+ VirtQueue *vq = sreq->vq;
+ VirtQueueElement *elem = sreq->elem;
+ VirtIODevice *vdev = sreq->vdev;
+ struct virtio_crypto_session_input input;
+ struct iovec *in_iov = elem->in_sg;
+ unsigned in_num = elem->in_num;
+ size_t s;
+
+ memset(&input, 0, sizeof(input));
+ /* Serious errors, need to reset virtio crypto device */
+ if (ret == -EFAULT) {
+ virtqueue_detach_element(vq, elem, 0);
+ goto out;
+ } else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
+ stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
+ } else if (ret == -VIRTIO_CRYPTO_KEY_REJECTED) {
+ stl_le_p(&input.status, VIRTIO_CRYPTO_KEY_REJECTED);
+ } else if (ret != VIRTIO_CRYPTO_OK) {
+ stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
} else {
- if (local_err) {
- error_report_err(local_err);
- } else {
- error_report("destroy session failed");
- }
+ /* Set the session id */
+ stq_le_p(&input.session_id, sreq->info.session_id);
+ stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
+ }
+
+ s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
+ if (unlikely(s != sizeof(input))) {
+ virtio_error(vdev, "virtio-crypto input incorrect");
+ virtqueue_detach_element(vq, elem, 0);
+ goto out;
+ }
+ virtqueue_push(vq, elem, sizeof(input));
+ virtio_notify(vdev, vq);
+
+out:
+ g_free(elem);
+ virtio_crypto_free_create_session_req(sreq);
+}
+
+static void virtio_crypto_destroy_session_completion(void *opaque, int ret)
+{
+ VirtIOCryptoSessionReq *sreq = (VirtIOCryptoSessionReq *)opaque;
+ VirtQueue *vq = sreq->vq;
+ VirtQueueElement *elem = sreq->elem;
+ VirtIODevice *vdev = sreq->vdev;
+ struct iovec *in_iov = elem->in_sg;
+ unsigned in_num = elem->in_num;
+ uint8_t status;
+ size_t s;
+
+ if (ret < 0) {
status = VIRTIO_CRYPTO_ERR;
+ } else {
+ status = VIRTIO_CRYPTO_OK;
+ }
+ s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
+ if (unlikely(s != sizeof(status))) {
+ virtio_error(vdev, "virtio-crypto status incorrect");
+ virtqueue_detach_element(vq, elem, 0);
+ goto out;
}
+ virtqueue_push(vq, elem, sizeof(status));
+ virtio_notify(vdev, vq);
- return status;
+out:
+ g_free(elem);
+ g_free(sreq);
}
static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
@@ -283,16 +341,16 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev);
struct virtio_crypto_op_ctrl_req ctrl;
VirtQueueElement *elem;
- struct iovec *in_iov;
- struct iovec *out_iov;
- unsigned in_num;
+ VirtIOCryptoSessionReq *sreq;
unsigned out_num;
+ unsigned in_num;
uint32_t queue_id;
uint32_t opcode;
struct virtio_crypto_session_input input;
- int64_t session_id;
- uint8_t status;
size_t s;
+ int ret;
+ struct iovec *out_iov;
+ struct iovec *in_iov;
for (;;) {
g_autofree struct iovec *out_iov_copy = NULL;
@@ -327,44 +385,34 @@ static void virtio_crypto_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
opcode = ldl_le_p(&ctrl.header.opcode);
queue_id = ldl_le_p(&ctrl.header.queue_id);
- memset(&input, 0, sizeof(input));
+ sreq = g_new0(VirtIOCryptoSessionReq, 1);
+ sreq->vdev = vdev;
+ sreq->vq = vq;
+ sreq->elem = elem;
+
switch (opcode) {
case VIRTIO_CRYPTO_CIPHER_CREATE_SESSION:
- session_id = virtio_crypto_create_sym_session(vcrypto,
- &ctrl.u.sym_create_session,
- queue_id, opcode,
- out_iov, out_num);
- goto check_session;
+ sreq->cb = virtio_crypto_create_session_completion;
+ ret = virtio_crypto_create_sym_session(vcrypto,
+ &ctrl.u.sym_create_session,
+ queue_id, opcode,
+ out_iov, out_num,
+ sreq);
+ if (ret < 0) {
+ virtio_crypto_create_session_completion(sreq, ret);
+ }
+ break;
case VIRTIO_CRYPTO_AKCIPHER_CREATE_SESSION:
- session_id = virtio_crypto_create_asym_session(vcrypto,
+ sreq->cb = virtio_crypto_create_session_completion;
+ ret = virtio_crypto_create_asym_session(vcrypto,
&ctrl.u.akcipher_create_session,
queue_id, opcode,
- out_iov, out_num);
-
-check_session:
- /* Serious errors, need to reset virtio crypto device */
- if (session_id == -EFAULT) {
- virtqueue_detach_element(vq, elem, 0);
- break;
- } else if (session_id == -VIRTIO_CRYPTO_NOTSUPP) {
- stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
- } else if (session_id == -VIRTIO_CRYPTO_ERR) {
- stl_le_p(&input.status, VIRTIO_CRYPTO_ERR);
- } else {
- /* Set the session id */
- stq_le_p(&input.session_id, session_id);
- stl_le_p(&input.status, VIRTIO_CRYPTO_OK);
- }
-
- s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
- if (unlikely(s != sizeof(input))) {
- virtio_error(vdev, "virtio-crypto input incorrect");
- virtqueue_detach_element(vq, elem, 0);
- break;
+ out_iov, out_num,
+ sreq);
+ if (ret < 0) {
+ virtio_crypto_create_session_completion(sreq, ret);
}
- virtqueue_push(vq, elem, sizeof(input));
- virtio_notify(vdev, vq);
break;
case VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION:
@@ -372,37 +420,36 @@ check_session:
case VIRTIO_CRYPTO_MAC_DESTROY_SESSION:
case VIRTIO_CRYPTO_AEAD_DESTROY_SESSION:
case VIRTIO_CRYPTO_AKCIPHER_DESTROY_SESSION:
- status = virtio_crypto_handle_close_session(vcrypto,
- &ctrl.u.destroy_session, queue_id);
- /* The status only occupy one byte, we can directly use it */
- s = iov_from_buf(in_iov, in_num, 0, &status, sizeof(status));
- if (unlikely(s != sizeof(status))) {
- virtio_error(vdev, "virtio-crypto status incorrect");
- virtqueue_detach_element(vq, elem, 0);
- break;
+ sreq->cb = virtio_crypto_destroy_session_completion;
+ ret = virtio_crypto_handle_close_session(vcrypto,
+ &ctrl.u.destroy_session, queue_id,
+ sreq);
+ if (ret < 0) {
+ virtio_crypto_destroy_session_completion(sreq, ret);
}
- virtqueue_push(vq, elem, sizeof(status));
- virtio_notify(vdev, vq);
break;
+
case VIRTIO_CRYPTO_HASH_CREATE_SESSION:
case VIRTIO_CRYPTO_MAC_CREATE_SESSION:
case VIRTIO_CRYPTO_AEAD_CREATE_SESSION:
default:
+ memset(&input, 0, sizeof(input));
error_report("virtio-crypto unsupported ctrl opcode: %d", opcode);
stl_le_p(&input.status, VIRTIO_CRYPTO_NOTSUPP);
s = iov_from_buf(in_iov, in_num, 0, &input, sizeof(input));
if (unlikely(s != sizeof(input))) {
virtio_error(vdev, "virtio-crypto input incorrect");
virtqueue_detach_element(vq, elem, 0);
- break;
+ } else {
+ virtqueue_push(vq, elem, sizeof(input));
+ virtio_notify(vdev, vq);
}
- virtqueue_push(vq, elem, sizeof(input));
- virtio_notify(vdev, vq);
+ g_free(sreq);
+ g_free(elem);
break;
} /* end switch case */
- g_free(elem);
} /* end for loop */
}
@@ -448,6 +495,7 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req)
}
}
+ g_free(req->in_iov);
g_free(req);
}
@@ -458,6 +506,7 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev,
CryptoDevBackendSymOpInfo *sym_op_info)
{
size_t s, len;
+ struct iovec *in_iov = req->in_iov;
if (status != VIRTIO_CRYPTO_OK) {
return;
@@ -465,18 +514,18 @@ virtio_crypto_sym_input_data_helper(VirtIODevice *vdev,
len = sym_op_info->src_len;
/* Save the cipher result */
- s = iov_from_buf(req->in_iov, req->in_num, 0, sym_op_info->dst, len);
+ s = iov_from_buf(in_iov, req->in_num, 0, sym_op_info->dst, len);
if (s != len) {
virtio_error(vdev, "virtio-crypto dest data incorrect");
return;
}
- iov_discard_front(&req->in_iov, &req->in_num, len);
+ iov_discard_front(&in_iov, &req->in_num, len);
if (sym_op_info->op_type ==
VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING) {
/* Save the digest result */
- s = iov_from_buf(req->in_iov, req->in_num, 0,
+ s = iov_from_buf(in_iov, req->in_num, 0,
sym_op_info->digest_result,
sym_op_info->digest_result_len);
if (s != sym_op_info->digest_result_len) {
@@ -491,6 +540,7 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
CryptoDevBackendAsymOpInfo *asym_op_info)
{
size_t s, len;
+ struct iovec *in_iov = req->in_iov;
if (status != VIRTIO_CRYPTO_OK) {
return;
@@ -501,23 +551,24 @@ virtio_crypto_akcipher_input_data_helper(VirtIODevice *vdev,
return;
}
- s = iov_from_buf(req->in_iov, req->in_num, 0, asym_op_info->dst, len);
+ s = iov_from_buf(in_iov, req->in_num, 0, asym_op_info->dst, len);
if (s != len) {
virtio_error(vdev, "virtio-crypto asym dest data incorrect");
return;
}
- iov_discard_front(&req->in_iov, &req->in_num, len);
+ iov_discard_front(&in_iov, &req->in_num, len);
/* For akcipher, dst_len may be changed after operation */
req->in_len = sizeof(struct virtio_crypto_inhdr) + asym_op_info->dst_len;
}
-
-static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
+static void virtio_crypto_req_complete(void *opaque, int ret)
{
+ VirtIOCryptoReq *req = (VirtIOCryptoReq *)opaque;
VirtIOCrypto *vcrypto = req->vcrypto;
VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto);
+ uint8_t status = -ret;
if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) {
virtio_crypto_sym_input_data_helper(vdev, req, status,
@@ -529,6 +580,7 @@ static void virtio_crypto_req_complete(VirtIOCryptoReq *req, uint8_t status)
stb_p(&req->in->status, status);
virtqueue_push(req->vq, &req->elem, req->in_len);
virtio_notify(vdev, req->vq);
+ virtio_crypto_free_request(req);
}
static VirtIOCryptoReq *
@@ -773,9 +825,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
unsigned in_num;
unsigned out_num;
uint32_t opcode;
- uint8_t status = VIRTIO_CRYPTO_ERR;
CryptoDevBackendOpInfo *op_info = &request->op_info;
- Error *local_err = NULL;
if (elem->out_num < 1 || elem->in_num < 1) {
virtio_error(vdev, "virtio-crypto dataq missing headers");
@@ -815,6 +865,8 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request)
*/
request->in_num = in_num;
request->in_iov = in_iov;
+ /* now, we free the in_iov_copy inside virtio_crypto_free_request */
+ in_iov_copy = NULL;
opcode = ldl_le_p(&req.header.opcode);
op_info->session_id = ldq_le_p(&req.header.session_id);
@@ -843,23 +895,15 @@ check_result:
if (ret == -EFAULT) {
return -1;
} else if (ret == -VIRTIO_CRYPTO_NOTSUPP) {
- virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
- virtio_crypto_free_request(request);
+ virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP);
} else {
-
- /* Set request's parameter */
ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev,
- request, queue_index, &local_err);
+ request, queue_index,
+ virtio_crypto_req_complete,
+ request);
if (ret < 0) {
- status = -ret;
- if (local_err) {
- error_report_err(local_err);
- }
- } else { /* ret == VIRTIO_CRYPTO_OK */
- status = ret;
+ virtio_crypto_req_complete(request, ret);
}
- virtio_crypto_req_complete(request, status);
- virtio_crypto_free_request(request);
}
break;
@@ -870,8 +914,7 @@ check_result:
default:
error_report("virtio-crypto unsupported dataq opcode: %u",
opcode);
- virtio_crypto_req_complete(request, VIRTIO_CRYPTO_NOTSUPP);
- virtio_crypto_free_request(request);
+ virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP);
}
return 0;
@@ -1011,7 +1054,7 @@ static void virtio_crypto_device_realize(DeviceState *dev, Error **errp)
vcrypto->vqs[i].vcrypto = vcrypto;
}
- vcrypto->ctrl_vq = virtio_add_queue(vdev, 64, virtio_crypto_handle_ctrl);
+ vcrypto->ctrl_vq = virtio_add_queue(vdev, 1024, virtio_crypto_handle_ctrl);
if (!cryptodev_backend_is_ready(vcrypto->cryptodev)) {
vcrypto->status &= ~VIRTIO_CRYPTO_S_HW_READY;
} else {
diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c
index 79ea8334f0..7ef2f9dcdb 100644
--- a/hw/virtio/virtio-iommu-pci.c
+++ b/hw/virtio/virtio-iommu-pci.c
@@ -17,6 +17,7 @@
#include "hw/qdev-properties-system.h"
#include "qapi/error.h"
#include "hw/boards.h"
+#include "hw/pci/pci_bus.h"
#include "qom/object.h"
typedef struct VirtIOIOMMUPCI VirtIOIOMMUPCI;
@@ -44,6 +45,7 @@ static Property virtio_iommu_pci_properties[] = {
static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIOIOMMUPCI *dev = VIRTIO_IOMMU_PCI(vpci_dev);
+ PCIBus *pbus = pci_get_bus(&vpci_dev->pci_dev);
DeviceState *vdev = DEVICE(&dev->vdev);
VirtIOIOMMU *s = VIRTIO_IOMMU(vdev);
@@ -57,11 +59,17 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
s->reserved_regions[i].type != VIRTIO_IOMMU_RESV_MEM_T_MSI) {
error_setg(errp, "reserved region %d has an invalid type", i);
error_append_hint(errp, "Valid values are 0 and 1\n");
+ return;
}
}
+ if (!pci_bus_is_root(pbus)) {
+ error_setg(errp, "virtio-iommu-pci must be plugged on the root bus");
+ return;
+ }
+
object_property_set_link(OBJECT(dev), "primary-bus",
- OBJECT(pci_get_bus(&vpci_dev->pci_dev)),
- &error_abort);
+ OBJECT(pbus), &error_abort);
+
virtio_pci_force_virtio_1(vpci_dev);
qdev_realize(vdev, BUS(&vpci_dev->bus), errp);
}
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 34db51e241..a1c9dfa7bb 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -71,9 +71,11 @@ static void virtio_pci_notify(DeviceState *d, uint16_t vector)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
- if (msix_enabled(&proxy->pci_dev))
- msix_notify(&proxy->pci_dev, vector);
- else {
+ if (msix_enabled(&proxy->pci_dev)) {
+ if (vector != VIRTIO_NO_VECTOR) {
+ msix_notify(&proxy->pci_dev, vector);
+ }
+ } else {
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
pci_set_irq(&proxy->pci_dev, qatomic_read(&vdev->isr) & 1);
}
@@ -175,6 +177,7 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
{
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ uint16_t vector;
int ret;
ret = pci_device_load(&proxy->pci_dev, f);
@@ -184,12 +187,17 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
msix_unuse_all_vectors(&proxy->pci_dev);
msix_load(&proxy->pci_dev, f);
if (msix_present(&proxy->pci_dev)) {
- qemu_get_be16s(f, &vdev->config_vector);
+ qemu_get_be16s(f, &vector);
+
+ if (vector != VIRTIO_NO_VECTOR && vector >= proxy->nvectors) {
+ return -EINVAL;
+ }
} else {
- vdev->config_vector = VIRTIO_NO_VECTOR;
+ vector = VIRTIO_NO_VECTOR;
}
- if (vdev->config_vector != VIRTIO_NO_VECTOR) {
- return msix_vector_use(&proxy->pci_dev, vdev->config_vector);
+ vdev->config_vector = vector;
+ if (vector != VIRTIO_NO_VECTOR) {
+ msix_vector_use(&proxy->pci_dev, vector);
}
return 0;
}
@@ -202,12 +210,15 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
uint16_t vector;
if (msix_present(&proxy->pci_dev)) {
qemu_get_be16s(f, &vector);
+ if (vector != VIRTIO_NO_VECTOR && vector >= proxy->nvectors) {
+ return -EINVAL;
+ }
} else {
vector = VIRTIO_NO_VECTOR;
}
virtio_queue_set_vector(vdev, n, vector);
if (vector != VIRTIO_NO_VECTOR) {
- return msix_vector_use(&proxy->pci_dev, vector);
+ msix_vector_use(&proxy->pci_dev, vector);
}
return 0;
@@ -299,6 +310,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
VirtIOPCIProxy *proxy = opaque;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ uint16_t vector;
hwaddr pa;
switch (addr) {
@@ -352,18 +364,28 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
}
break;
case VIRTIO_MSI_CONFIG_VECTOR:
- msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+ if (vdev->config_vector != VIRTIO_NO_VECTOR) {
+ msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+ }
/* Make it possible for guest to discover an error took place. */
- if (msix_vector_use(&proxy->pci_dev, val) < 0)
+ if (val < proxy->nvectors) {
+ msix_vector_use(&proxy->pci_dev, val);
+ } else {
val = VIRTIO_NO_VECTOR;
+ }
vdev->config_vector = val;
break;
case VIRTIO_MSI_QUEUE_VECTOR:
- msix_vector_unuse(&proxy->pci_dev,
- virtio_queue_vector(vdev, vdev->queue_sel));
+ vector = virtio_queue_vector(vdev, vdev->queue_sel);
+ if (vector != VIRTIO_NO_VECTOR) {
+ msix_vector_unuse(&proxy->pci_dev, vector);
+ }
/* Make it possible for guest to discover an error took place. */
- if (msix_vector_use(&proxy->pci_dev, val) < 0)
+ if (val < proxy->nvectors) {
+ msix_vector_use(&proxy->pci_dev, val);
+ } else {
val = VIRTIO_NO_VECTOR;
+ }
virtio_queue_set_vector(vdev, vdev->queue_sel, val);
break;
default:
@@ -1251,6 +1273,9 @@ static uint64_t virtio_pci_common_read(void *opaque, hwaddr addr,
case VIRTIO_PCI_COMMON_Q_USEDHI:
val = proxy->vqs[vdev->queue_sel].used[1];
break;
+ case VIRTIO_PCI_COMMON_Q_RESET:
+ val = proxy->vqs[vdev->queue_sel].reset;
+ break;
default:
val = 0;
}
@@ -1263,6 +1288,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
{
VirtIOPCIProxy *proxy = opaque;
VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
+ uint16_t vector;
if (vdev == NULL) {
return;
@@ -1284,9 +1310,13 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
}
break;
case VIRTIO_PCI_COMMON_MSIX:
- msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+ if (vdev->config_vector != VIRTIO_NO_VECTOR) {
+ msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+ }
/* Make it possible for guest to discover an error took place. */
- if (msix_vector_use(&proxy->pci_dev, val) < 0) {
+ if (val < proxy->nvectors) {
+ msix_vector_use(&proxy->pci_dev, val);
+ } else {
val = VIRTIO_NO_VECTOR;
}
vdev->config_vector = val;
@@ -1318,10 +1348,14 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
proxy->vqs[vdev->queue_sel].num);
break;
case VIRTIO_PCI_COMMON_Q_MSIX:
- msix_vector_unuse(&proxy->pci_dev,
- virtio_queue_vector(vdev, vdev->queue_sel));
+ vector = virtio_queue_vector(vdev, vdev->queue_sel);
+ if (vector != VIRTIO_NO_VECTOR) {
+ msix_vector_unuse(&proxy->pci_dev, vector);
+ }
/* Make it possible for guest to discover an error took place. */
- if (msix_vector_use(&proxy->pci_dev, val) < 0) {
+ if (val < proxy->nvectors) {
+ msix_vector_use(&proxy->pci_dev, val);
+ } else {
val = VIRTIO_NO_VECTOR;
}
virtio_queue_set_vector(vdev, vdev->queue_sel, val);
@@ -1338,6 +1372,8 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 |
proxy->vqs[vdev->queue_sel].used[0]);
proxy->vqs[vdev->queue_sel].enabled = 1;
+ proxy->vqs[vdev->queue_sel].reset = 0;
+ virtio_queue_enable(vdev, vdev->queue_sel);
} else {
virtio_error(vdev, "wrong value for queue_enable %"PRIx64, val);
}
@@ -1360,6 +1396,16 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
case VIRTIO_PCI_COMMON_Q_USEDHI:
proxy->vqs[vdev->queue_sel].used[1] = val;
break;
+ case VIRTIO_PCI_COMMON_Q_RESET:
+ if (val == 1) {
+ proxy->vqs[vdev->queue_sel].reset = 1;
+
+ virtio_queue_reset(vdev, vdev->queue_sel);
+
+ proxy->vqs[vdev->queue_sel].reset = 0;
+ proxy->vqs[vdev->queue_sel].enabled = 0;
+ }
+ break;
default:
break;
}
@@ -1954,6 +2000,7 @@ static void virtio_pci_reset(DeviceState *qdev)
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
proxy->vqs[i].enabled = 0;
+ proxy->vqs[i].reset = 0;
proxy->vqs[i].num = 0;
proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0;
proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0;
diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c
index 151ece6f94..6e76f8b57b 100644
--- a/hw/virtio/virtio-rng-pci.c
+++ b/hw/virtio/virtio-rng-pci.c
@@ -13,6 +13,7 @@
#include "hw/virtio/virtio-pci.h"
#include "hw/virtio/virtio-rng.h"
+#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qom/object.h"
@@ -31,11 +32,23 @@ struct VirtIORngPCI {
VirtIORNG vdev;
};
+static Property virtio_rng_properties[] = {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev);
DeviceState *vdev = DEVICE(&vrng->vdev);
+ if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+ vpci_dev->nvectors = 2;
+ }
+
if (!qdev_realize(vdev, BUS(&vpci_dev->bus), errp)) {
return;
}
@@ -54,6 +67,7 @@ static void virtio_rng_pci_class_init(ObjectClass *klass, void *data)
pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
pcidev_k->class_id = PCI_CLASS_OTHERS;
+ device_class_set_props(dc, virtio_rng_properties);
}
static void virtio_rng_initfn(Object *obj)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 808446b4c9..9683b2e158 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -2464,6 +2464,51 @@ static enum virtio_device_endian virtio_current_cpu_endian(void)
}
}
+static void __virtio_queue_reset(VirtIODevice *vdev, uint32_t i)
+{
+ vdev->vq[i].vring.desc = 0;
+ vdev->vq[i].vring.avail = 0;
+ vdev->vq[i].vring.used = 0;
+ vdev->vq[i].last_avail_idx = 0;
+ vdev->vq[i].shadow_avail_idx = 0;
+ vdev->vq[i].used_idx = 0;
+ vdev->vq[i].last_avail_wrap_counter = true;
+ vdev->vq[i].shadow_avail_wrap_counter = true;
+ vdev->vq[i].used_wrap_counter = true;
+ virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR);
+ vdev->vq[i].signalled_used = 0;
+ vdev->vq[i].signalled_used_valid = false;
+ vdev->vq[i].notification = true;
+ vdev->vq[i].vring.num = vdev->vq[i].vring.num_default;
+ vdev->vq[i].inuse = 0;
+ virtio_virtqueue_reset_region_cache(&vdev->vq[i]);
+}
+
+void virtio_queue_reset(VirtIODevice *vdev, uint32_t queue_index)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+
+ if (k->queue_reset) {
+ k->queue_reset(vdev, queue_index);
+ }
+
+ __virtio_queue_reset(vdev, queue_index);
+}
+
+void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index)
+{
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+
+ if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+ error_report("queue_enable is only suppported in devices of virtio "
+ "1.0 or later.");
+ }
+
+ if (k->queue_enable) {
+ k->queue_enable(vdev, queue_index);
+ }
+}
+
void virtio_reset(void *opaque)
{
VirtIODevice *vdev = opaque;
@@ -2495,22 +2540,7 @@ void virtio_reset(void *opaque)
virtio_notify_vector(vdev, vdev->config_vector);
for(i = 0; i < VIRTIO_QUEUE_MAX; i++) {
- vdev->vq[i].vring.desc = 0;
- vdev->vq[i].vring.avail = 0;
- vdev->vq[i].vring.used = 0;
- vdev->vq[i].last_avail_idx = 0;
- vdev->vq[i].shadow_avail_idx = 0;
- vdev->vq[i].used_idx = 0;
- vdev->vq[i].last_avail_wrap_counter = true;
- vdev->vq[i].shadow_avail_wrap_counter = true;
- vdev->vq[i].used_wrap_counter = true;
- virtio_queue_set_vector(vdev, i, VIRTIO_NO_VECTOR);
- vdev->vq[i].signalled_used = 0;
- vdev->vq[i].signalled_used_valid = false;
- vdev->vq[i].notification = true;
- vdev->vq[i].vring.num = vdev->vq[i].vring.num_default;
- vdev->vq[i].inuse = 0;
- virtio_virtqueue_reset_region_cache(&vdev->vq[i]);
+ __virtio_queue_reset(vdev, i);
}
}
diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
index 51f5fa2774..214e58ca47 100644
--- a/include/crypto/akcipher.h
+++ b/include/crypto/akcipher.h
@@ -153,6 +153,27 @@ int qcrypto_akcipher_max_dgst_len(QCryptoAkCipher *akcipher);
*/
void qcrypto_akcipher_free(QCryptoAkCipher *akcipher);
+/**
+ * qcrypto_akcipher_export_p8info:
+ * @opts: the options of the akcipher to be exported.
+ * @key: the original key of the akcipher to be exported.
+ * @keylen: length of the 'key'
+ * @dst: output parameter, if export succeed, *dst is set to the
+ * PKCS#8 encoded private key, caller MUST free this key with
+ * g_free after use.
+ * @dst_len: output parameter, indicates the length of PKCS#8 encoded
+ * key.
+ *
+ * Export the akcipher into DER encoded pkcs#8 private key info, expects
+ * |key| stores a valid asymmetric PRIVATE key.
+ *
+ * Returns: 0 for succeed, otherwise -1 is returned.
+ */
+int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts,
+ uint8_t *key, size_t keylen,
+ uint8_t **dst, size_t *dst_len,
+ Error **errp);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipher, qcrypto_akcipher_free)
#endif /* QCRYPTO_AKCIPHER_H */
diff --git a/include/exec/memory.h b/include/exec/memory.h
index a751c111bd..80fa75baa1 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -713,6 +713,10 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm,
void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
RamDiscardListener *rdl);
+bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
+ ram_addr_t *ram_addr, bool *read_only,
+ bool *mr_has_discard_manager);
+
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
diff --git a/include/hw/acpi/acpi_aml_interface.h b/include/hw/acpi/acpi_aml_interface.h
index ab76f0e55d..436da069d6 100644
--- a/include/hw/acpi/acpi_aml_interface.h
+++ b/include/hw/acpi/acpi_aml_interface.h
@@ -29,11 +29,20 @@ struct AcpiDevAmlIfClass {
dev_aml_fn build_dev_aml;
};
-static inline void call_dev_aml_func(DeviceState *dev, Aml *scope)
+static inline dev_aml_fn get_dev_aml_func(DeviceState *dev)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_ACPI_DEV_AML_IF)) {
AcpiDevAmlIfClass *klass = ACPI_DEV_AML_IF_GET_CLASS(dev);
- klass->build_dev_aml(ACPI_DEV_AML_IF(dev), scope);
+ return klass->build_dev_aml;
+ }
+ return NULL;
+}
+
+static inline void call_dev_aml_func(DeviceState *dev, Aml *scope)
+{
+ dev_aml_fn fn = get_dev_aml_func(dev);
+ if (fn) {
+ fn(ACPI_DEV_AML_IF(dev), scope);
}
}
diff --git a/include/hw/cxl/cxl_cdat.h b/include/hw/cxl/cxl_cdat.h
new file mode 100644
index 0000000000..e9eda00142
--- /dev/null
+++ b/include/hw/cxl/cxl_cdat.h
@@ -0,0 +1,166 @@
+/*
+ * CXL CDAT Structure
+ *
+ * Copyright (C) 2021 Avery Design Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef CXL_CDAT_H
+#define CXL_CDAT_H
+
+#include "hw/cxl/cxl_pci.h"
+
+/*
+ * Reference:
+ * Coherent Device Attribute Table (CDAT) Specification, Rev. 1.03, July. 2022
+ * Compute Express Link (CXL) Specification, Rev. 3.0, Aug. 2022
+ */
+/* Table Access DOE - CXL r3.0 8.1.11 */
+#define CXL_DOE_TABLE_ACCESS 2
+#define CXL_DOE_PROTOCOL_CDAT ((CXL_DOE_TABLE_ACCESS << 16) | CXL_VENDOR_ID)
+
+/* Read Entry - CXL r3.0 8.1.11.1 */
+#define CXL_DOE_TAB_TYPE_CDAT 0
+#define CXL_DOE_TAB_ENT_MAX 0xFFFF
+
+/* Read Entry Request - CXL r3.0 8.1.11.1 Table 8-13 */
+#define CXL_DOE_TAB_REQ 0
+typedef struct CDATReq {
+ DOEHeader header;
+ uint8_t req_code;
+ uint8_t table_type;
+ uint16_t entry_handle;
+} QEMU_PACKED CDATReq;
+
+/* Read Entry Response - CXL r3.0 8.1.11.1 Table 8-14 */
+#define CXL_DOE_TAB_RSP 0
+typedef struct CDATRsp {
+ DOEHeader header;
+ uint8_t rsp_code;
+ uint8_t table_type;
+ uint16_t entry_handle;
+} QEMU_PACKED CDATRsp;
+
+/* CDAT Table Format - CDAT Table 1 */
+#define CXL_CDAT_REV 2
+typedef struct CDATTableHeader {
+ uint32_t length;
+ uint8_t revision;
+ uint8_t checksum;
+ uint8_t reserved[6];
+ uint32_t sequence;
+} QEMU_PACKED CDATTableHeader;
+
+/* CDAT Structure Types - CDAT Table 2 */
+typedef enum {
+ CDAT_TYPE_DSMAS = 0,
+ CDAT_TYPE_DSLBIS = 1,
+ CDAT_TYPE_DSMSCIS = 2,
+ CDAT_TYPE_DSIS = 3,
+ CDAT_TYPE_DSEMTS = 4,
+ CDAT_TYPE_SSLBIS = 5,
+} CDATType;
+
+typedef struct CDATSubHeader {
+ uint8_t type;
+ uint8_t reserved;
+ uint16_t length;
+} CDATSubHeader;
+
+/* Device Scoped Memory Affinity Structure - CDAT Table 3 */
+typedef struct CDATDsmas {
+ CDATSubHeader header;
+ uint8_t DSMADhandle;
+ uint8_t flags;
+#define CDAT_DSMAS_FLAG_NV (1 << 2)
+#define CDAT_DSMAS_FLAG_SHAREABLE (1 << 3)
+#define CDAT_DSMAS_FLAG_HW_COHERENT (1 << 4)
+#define CDAT_DSMAS_FLAG_DYNAMIC_CAP (1 << 5)
+ uint16_t reserved;
+ uint64_t DPA_base;
+ uint64_t DPA_length;
+} QEMU_PACKED CDATDsmas;
+
+/* Device Scoped Latency and Bandwidth Information Structure - CDAT Table 5 */
+typedef struct CDATDslbis {
+ CDATSubHeader header;
+ uint8_t handle;
+ /* Definitions of these fields refer directly to HMAT fields */
+ uint8_t flags;
+ uint8_t data_type;
+ uint8_t reserved;
+ uint64_t entry_base_unit;
+ uint16_t entry[3];
+ uint16_t reserved2;
+} QEMU_PACKED CDATDslbis;
+
+/* Device Scoped Memory Side Cache Information Structure - CDAT Table 6 */
+typedef struct CDATDsmscis {
+ CDATSubHeader header;
+ uint8_t DSMAS_handle;
+ uint8_t reserved[3];
+ uint64_t memory_side_cache_size;
+ uint32_t cache_attributes;
+} QEMU_PACKED CDATDsmscis;
+
+/* Device Scoped Initiator Structure - CDAT Table 7 */
+typedef struct CDATDsis {
+ CDATSubHeader header;
+ uint8_t flags;
+ uint8_t handle;
+ uint16_t reserved;
+} QEMU_PACKED CDATDsis;
+
+/* Device Scoped EFI Memory Type Structure - CDAT Table 8 */
+typedef struct CDATDsemts {
+ CDATSubHeader header;
+ uint8_t DSMAS_handle;
+ uint8_t EFI_memory_type_attr;
+ uint16_t reserved;
+ uint64_t DPA_offset;
+ uint64_t DPA_length;
+} QEMU_PACKED CDATDsemts;
+
+/* Switch Scoped Latency and Bandwidth Information Structure - CDAT Table 9 */
+typedef struct CDATSslbisHeader {
+ CDATSubHeader header;
+ uint8_t data_type;
+ uint8_t reserved[3];
+ uint64_t entry_base_unit;
+} QEMU_PACKED CDATSslbisHeader;
+
+#define CDAT_PORT_ID_USP 0x100
+/* Switch Scoped Latency and Bandwidth Entry - CDAT Table 10 */
+typedef struct CDATSslbe {
+ uint16_t port_x_id;
+ uint16_t port_y_id;
+ uint16_t latency_bandwidth;
+ uint16_t reserved;
+} QEMU_PACKED CDATSslbe;
+
+typedef struct CDATSslbis {
+ CDATSslbisHeader sslbis_header;
+ CDATSslbe sslbe[];
+} QEMU_PACKED CDATSslbis;
+
+typedef struct CDATEntry {
+ void *base;
+ uint32_t length;
+} CDATEntry;
+
+typedef struct CDATObject {
+ CDATEntry *entry;
+ int entry_len;
+
+ int (*build_cdat_table)(CDATSubHeader ***cdat_table, void *priv);
+ void (*free_cdat_table)(CDATSubHeader **cdat_table, int num, void *priv);
+ bool to_update;
+ void *private;
+ char *filename;
+ uint8_t *buf;
+ struct CDATSubHeader **built_buf;
+ int built_buf_len;
+} CDATObject;
+#endif /* CXL_CDAT_H */
diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h
index 94ec2f07d7..34075cfb72 100644
--- a/include/hw/cxl/cxl_component.h
+++ b/include/hw/cxl/cxl_component.h
@@ -19,6 +19,7 @@
#include "qemu/range.h"
#include "qemu/typedefs.h"
#include "hw/register.h"
+#include "qapi/error.h"
enum reg_type {
CXL2_DEVICE,
@@ -184,6 +185,8 @@ typedef struct cxl_component {
struct PCIDevice *pdev;
};
};
+
+ CDATObject cdat;
} CXLComponentState;
void cxl_component_register_block_init(Object *obj,
@@ -220,4 +223,8 @@ static inline hwaddr cxl_decode_ig(int ig)
CXLComponentState *cxl_get_hb_cstate(PCIHostState *hb);
+void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp);
+void cxl_doe_cdat_release(CXLComponentState *cxl_cstate);
+void cxl_doe_cdat_update(CXLComponentState *cxl_cstate, Error **errp);
+
#endif
diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h
index e4d221cdb3..449b0edfe9 100644
--- a/include/hw/cxl/cxl_device.h
+++ b/include/hw/cxl/cxl_device.h
@@ -243,6 +243,9 @@ struct CXLType3Dev {
AddressSpace hostmem_as;
CXLComponentState cxl_cstate;
CXLDeviceState cxl_dstate;
+
+ /* DOE */
+ DOECap doe_cdat;
};
#define TYPE_CXL_TYPE3 "cxl-type3"
diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h
index 01cf002096..3cb79eca1e 100644
--- a/include/hw/cxl/cxl_pci.h
+++ b/include/hw/cxl/cxl_pci.h
@@ -13,6 +13,7 @@
#include "qemu/compiler.h"
#include "hw/pci/pci.h"
#include "hw/pci/pcie.h"
+#include "hw/cxl/cxl_cdat.h"
#define CXL_VENDOR_ID 0x1e98
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index e7d386f7c8..7f3259a630 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -18,6 +18,8 @@
#define SMBIOS_MAX_TYPE 127
+#define offsetofend(TYPE, MEMBER) \
+ (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
/* memory area description, used by type 19 table */
struct smbios_phys_mem_area {
@@ -187,8 +189,18 @@ struct smbios_type_4 {
uint8_t thread_count;
uint16_t processor_characteristics;
uint16_t processor_family2;
+ /* SMBIOS spec 3.0.0, Table 21 */
+ uint16_t core_count2;
+ uint16_t core_enabled2;
+ uint16_t thread_count2;
} QEMU_PACKED;
+typedef enum smbios_type_4_len_ver {
+ SMBIOS_TYPE_4_LEN_V28 = offsetofend(struct smbios_type_4,
+ processor_family2),
+ SMBIOS_TYPE_4_LEN_V30 = offsetofend(struct smbios_type_4, thread_count2),
+} smbios_type_4_len_ver;
+
/* SMBIOS type 8 - Port Connector Information */
struct smbios_type_8 {
struct smbios_structure_header header;
diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h
index 67653b0f9b..46d973e629 100644
--- a/include/hw/i386/intel_iommu.h
+++ b/include/hw/i386/intel_iommu.h
@@ -58,7 +58,6 @@ typedef struct VTDContextEntry VTDContextEntry;
typedef struct VTDContextCacheEntry VTDContextCacheEntry;
typedef struct VTDAddressSpace VTDAddressSpace;
typedef struct VTDIOTLBEntry VTDIOTLBEntry;
-typedef struct VTDBus VTDBus;
typedef union VTD_IR_TableEntry VTD_IR_TableEntry;
typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress;
typedef struct VTDPASIDDirEntry VTDPASIDDirEntry;
@@ -98,11 +97,13 @@ struct VTDPASIDEntry {
struct VTDAddressSpace {
PCIBus *bus;
uint8_t devfn;
+ uint32_t pasid;
AddressSpace as;
IOMMUMemoryRegion iommu;
MemoryRegion root; /* The root container of the device */
MemoryRegion nodmar; /* The alias of shared nodmar MR */
MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */
+ MemoryRegion iommu_ir_fault; /* Interrupt region for catching fault */
IntelIOMMUState *iommu_state;
VTDContextCacheEntry context_cache_entry;
QLIST_ENTRY(VTDAddressSpace) next;
@@ -111,15 +112,10 @@ struct VTDAddressSpace {
IOVATree *iova_tree; /* Traces mapped IOVA ranges */
};
-struct VTDBus {
- PCIBus* bus; /* A reference to the bus to provide translation for */
- /* A table of VTDAddressSpace objects indexed by devfn */
- VTDAddressSpace *dev_as[];
-};
-
struct VTDIOTLBEntry {
uint64_t gfn;
uint16_t domain_id;
+ uint32_t pasid;
uint64_t slpte;
uint64_t mask;
uint8_t access_flags;
@@ -253,8 +249,8 @@ struct IntelIOMMUState {
uint32_t context_cache_gen; /* Should be in [1,MAX] */
GHashTable *iotlb; /* IOTLB */
- GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */
- VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */
+ GHashTable *vtd_address_spaces; /* VTD address spaces */
+ VTDAddressSpace *vtd_as_cache[VTD_PCI_BUS_MAX]; /* VTD address space cache */
/* list of registered notifiers */
QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers;
@@ -268,6 +264,7 @@ struct IntelIOMMUState {
uint8_t aw_bits; /* Host/IOVA address width (in bits) */
bool dma_drain; /* Whether DMA r/w draining enabled */
bool dma_translation; /* Whether DMA translation supported */
+ bool pasid; /* Whether to support PASID */
/*
* Protects IOMMU states in general. Currently it protects the
@@ -279,6 +276,7 @@ struct IntelIOMMUState {
/* Find the VTD Address space associated with the given bus pointer,
* create a new one if none exists
*/
-VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn);
+VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus,
+ int devfn, unsigned int pasid);
#endif
diff --git a/include/hw/pci/msix.h b/include/hw/pci/msix.h
index 4f1cda0ebe..0e6f257e45 100644
--- a/include/hw/pci/msix.h
+++ b/include/hw/pci/msix.h
@@ -33,10 +33,10 @@ bool msix_is_masked(PCIDevice *dev, unsigned vector);
void msix_set_pending(PCIDevice *dev, unsigned vector);
void msix_clr_pending(PCIDevice *dev, int vector);
-int msix_vector_use(PCIDevice *dev, unsigned vector);
+void msix_vector_use(PCIDevice *dev, unsigned vector);
void msix_vector_unuse(PCIDevice *dev, unsigned vector);
void msix_unuse_all_vectors(PCIDevice *dev);
-void msix_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp);
+void msix_set_mask(PCIDevice *dev, int vector, bool mask);
void msix_notify(PCIDevice *dev, unsigned vector);
diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h
index eb94e7e85c..5653175957 100644
--- a/include/hw/pci/pci_bus.h
+++ b/include/hw/pci/pci_bus.h
@@ -28,6 +28,8 @@ enum PCIBusFlags {
PCI_BUS_CXL = 0x0004,
};
+#define PCI_NO_PASID UINT32_MAX
+
struct PCIBus {
BusState qbus;
enum PCIBusFlags flags;
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index d5ddea558b..bc9f834fd1 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -157,6 +157,9 @@
/* Vendors and devices. Sort key: vendor first, device next. */
+/* Ref: PCIe r6.0 Table 6-32 */
+#define PCI_VENDOR_ID_PCI_SIG 0x0001
+
#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
#define PCI_DEVICE_ID_LSI_53C810 0x0001
#define PCI_DEVICE_ID_LSI_53C895A 0x0012
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
index 798a262a0a..698d3de851 100644
--- a/include/hw/pci/pcie.h
+++ b/include/hw/pci/pcie.h
@@ -26,6 +26,7 @@
#include "hw/pci/pcie_aer.h"
#include "hw/pci/pcie_sriov.h"
#include "hw/hotplug.h"
+#include "hw/pci/pcie_doe.h"
typedef enum {
/* for attention and power indicator */
diff --git a/include/hw/pci/pcie_doe.h b/include/hw/pci/pcie_doe.h
new file mode 100644
index 0000000000..ba4d8b03bd
--- /dev/null
+++ b/include/hw/pci/pcie_doe.h
@@ -0,0 +1,123 @@
+/*
+ * PCIe Data Object Exchange
+ *
+ * Copyright (C) 2021 Avery Design Systems, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef PCIE_DOE_H
+#define PCIE_DOE_H
+
+#include "qemu/range.h"
+#include "qemu/typedefs.h"
+#include "hw/register.h"
+
+/*
+ * Reference:
+ * PCIe r6.0 - 7.9.24 Data Object Exchange Extended Capability
+ */
+/* Capabilities Register - r6.0 7.9.24.2 */
+#define PCI_EXP_DOE_CAP 0x04
+REG32(PCI_DOE_CAP_REG, 0)
+ FIELD(PCI_DOE_CAP_REG, INTR_SUPP, 0, 1)
+ FIELD(PCI_DOE_CAP_REG, DOE_INTR_MSG_NUM, 1, 11)
+
+/* Control Register - r6.0 7.9.24.3 */
+#define PCI_EXP_DOE_CTRL 0x08
+REG32(PCI_DOE_CAP_CONTROL, 0)
+ FIELD(PCI_DOE_CAP_CONTROL, DOE_ABORT, 0, 1)
+ FIELD(PCI_DOE_CAP_CONTROL, DOE_INTR_EN, 1, 1)
+ FIELD(PCI_DOE_CAP_CONTROL, DOE_GO, 31, 1)
+
+/* Status Register - r6.0 7.9.24.4 */
+#define PCI_EXP_DOE_STATUS 0x0c
+REG32(PCI_DOE_CAP_STATUS, 0)
+ FIELD(PCI_DOE_CAP_STATUS, DOE_BUSY, 0, 1)
+ FIELD(PCI_DOE_CAP_STATUS, DOE_INTR_STATUS, 1, 1)
+ FIELD(PCI_DOE_CAP_STATUS, DOE_ERROR, 2, 1)
+ FIELD(PCI_DOE_CAP_STATUS, DATA_OBJ_RDY, 31, 1)
+
+/* Write Data Mailbox Register - r6.0 7.9.24.5 */
+#define PCI_EXP_DOE_WR_DATA_MBOX 0x10
+
+/* Read Data Mailbox Register - 7.9.xx.6 */
+#define PCI_EXP_DOE_RD_DATA_MBOX 0x14
+
+/* PCI-SIG defined Data Object Types - r6.0 Table 6-32 */
+#define PCI_SIG_DOE_DISCOVERY 0x00
+
+#define PCI_DOE_DW_SIZE_MAX (1 << 18)
+#define PCI_DOE_PROTOCOL_NUM_MAX 256
+
+#define DATA_OBJ_BUILD_HEADER1(v, p) (((p) << 16) | (v))
+#define DATA_OBJ_LEN_MASK(len) ((len) & (PCI_DOE_DW_SIZE_MAX - 1))
+
+typedef struct DOEHeader DOEHeader;
+typedef struct DOEProtocol DOEProtocol;
+typedef struct DOECap DOECap;
+
+struct DOEHeader {
+ uint16_t vendor_id;
+ uint8_t data_obj_type;
+ uint8_t reserved;
+ uint32_t length;
+} QEMU_PACKED;
+
+/* Protocol infos and rsp function callback */
+struct DOEProtocol {
+ uint16_t vendor_id;
+ uint8_t data_obj_type;
+ bool (*handle_request)(DOECap *);
+};
+
+struct DOECap {
+ /* Owner */
+ PCIDevice *pdev;
+
+ uint16_t offset;
+
+ struct {
+ bool intr;
+ uint16_t vec;
+ } cap;
+
+ struct {
+ bool abort;
+ bool intr;
+ bool go;
+ } ctrl;
+
+ struct {
+ bool busy;
+ bool intr;
+ bool error;
+ bool ready;
+ } status;
+
+ uint32_t *write_mbox;
+ uint32_t *read_mbox;
+
+ /* Mailbox position indicator */
+ uint32_t read_mbox_idx;
+ uint32_t read_mbox_len;
+ uint32_t write_mbox_len;
+
+ /* Protocols and its callback response */
+ DOEProtocol *protocols;
+ uint16_t protocol_num;
+};
+
+void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset,
+ DOEProtocol *protocols, bool intr, uint16_t vec);
+void pcie_doe_fini(DOECap *doe_cap);
+bool pcie_doe_read_config(DOECap *doe_cap, uint32_t addr, int size,
+ uint32_t *buf);
+void pcie_doe_write_config(DOECap *doe_cap, uint32_t addr,
+ uint32_t val, int size);
+uint32_t pcie_doe_build_protocol(DOEProtocol *p);
+void *pcie_doe_get_write_mbox_ptr(DOECap *doe_cap);
+void pcie_doe_set_rsp(DOECap *doe_cap, void *rsp);
+uint32_t pcie_doe_get_obj_len(void *obj);
+#endif /* PCIE_DOE_H */
diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h
index 1db86b0ec4..963dc2e170 100644
--- a/include/hw/pci/pcie_regs.h
+++ b/include/hw/pci/pcie_regs.h
@@ -179,4 +179,8 @@ typedef enum PCIExpLinkWidth {
#define PCI_ACS_VER 0x1
#define PCI_ACS_SIZEOF 8
+/* DOE Capability Register Fields */
+#define PCI_DOE_VER 0x1
+#define PCI_DOE_SIZEOF 24
+
#endif /* QEMU_PCIE_REGS_H */
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index d7eb557885..353252ac3e 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -297,6 +297,11 @@ int vhost_net_set_backend(struct vhost_dev *hdev,
int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write);
+int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev,
+ struct vhost_virtqueue *vq, unsigned idx);
+void vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev,
+ struct vhost_virtqueue *vq, unsigned idx);
+
void vhost_dev_reset_inflight(struct vhost_inflight *inflight);
void vhost_dev_free_inflight(struct vhost_inflight *inflight);
void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f);
diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h
index 2446dcd9ae..938799e8f6 100644
--- a/include/hw/virtio/virtio-pci.h
+++ b/include/hw/virtio/virtio-pci.h
@@ -117,6 +117,11 @@ typedef struct VirtIOPCIRegion {
typedef struct VirtIOPCIQueue {
uint16_t num;
bool enabled;
+ /*
+ * No need to migrate the reset status, because it is always 0
+ * when the migration starts.
+ */
+ bool reset;
uint32_t desc[2];
uint32_t avail[2];
uint32_t used[2];
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index f41b4a7e64..141a253a2c 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -148,6 +148,8 @@ struct VirtioDeviceClass {
void (*set_config)(VirtIODevice *vdev, const uint8_t *config);
void (*reset)(VirtIODevice *vdev);
void (*set_status)(VirtIODevice *vdev, uint8_t val);
+ void (*queue_reset)(VirtIODevice *vdev, uint32_t queue_index);
+ void (*queue_enable)(VirtIODevice *vdev, uint32_t queue_index);
/* For transitional devices, this is a bitmap of features
* that are only exposed on the legacy interface but not
* the modern one.
@@ -286,6 +288,8 @@ int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n,
MemoryRegion *mr, bool assign);
int virtio_set_status(VirtIODevice *vdev, uint8_t val);
void virtio_reset(void *opaque);
+void virtio_queue_reset(VirtIODevice *vdev, uint32_t queue_index);
+void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index);
void virtio_update_irq(VirtIODevice *vdev);
int virtio_set_features(VirtIODevice *vdev, uint64_t val);
@@ -309,7 +313,9 @@ typedef struct VirtIORNGConf VirtIORNGConf;
DEFINE_PROP_BIT64("iommu_platform", _state, _field, \
VIRTIO_F_IOMMU_PLATFORM, false), \
DEFINE_PROP_BIT64("packed", _state, _field, \
- VIRTIO_F_RING_PACKED, false)
+ VIRTIO_F_RING_PACKED, false), \
+ DEFINE_PROP_BIT64("queue_reset", _state, _field, \
+ VIRTIO_F_RING_RESET, true)
hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
bool virtio_queue_enabled_legacy(VirtIODevice *vdev, int n);
@@ -389,6 +395,24 @@ static inline bool virtio_device_started(VirtIODevice *vdev, uint8_t status)
return vdev->started;
}
+ return status & VIRTIO_CONFIG_S_DRIVER_OK;
+}
+
+/**
+ * virtio_device_should_start() - check if device startable
+ * @vdev - the VirtIO device
+ * @status - the devices status bits
+ *
+ * This is similar to virtio_device_started() but also encapsulates a
+ * check on the VM status which would prevent a device starting
+ * anyway.
+ */
+static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status)
+{
+ if (vdev->use_started) {
+ return vdev->started;
+ }
+
if (!vdev->vm_running) {
return false;
}
diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h
index 387e913e4e..40b9a40074 100644
--- a/include/net/vhost_net.h
+++ b/include/net/vhost_net.h
@@ -48,4 +48,8 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net);
int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu);
+void vhost_net_virtqueue_reset(VirtIODevice *vdev, NetClientState *nc,
+ int vq_index);
+int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc,
+ int vq_index);
#endif
diff --git a/include/sysemu/cryptodev.h b/include/sysemu/cryptodev.h
index 37c3a360fd..cf9b3f07fe 100644
--- a/include/sysemu/cryptodev.h
+++ b/include/sysemu/cryptodev.h
@@ -113,6 +113,7 @@ typedef struct CryptoDevBackendSessionInfo {
CryptoDevBackendSymSessionInfo sym_sess_info;
CryptoDevBackendAsymSessionInfo asym_sess_info;
} u;
+ uint64_t session_id;
} CryptoDevBackendSessionInfo;
/**
@@ -188,27 +189,37 @@ typedef struct CryptoDevBackendOpInfo {
} u;
} CryptoDevBackendOpInfo;
+typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret);
struct CryptoDevBackendClass {
ObjectClass parent_class;
void (*init)(CryptoDevBackend *backend, Error **errp);
void (*cleanup)(CryptoDevBackend *backend, Error **errp);
- int64_t (*create_session)(CryptoDevBackend *backend,
- CryptoDevBackendSessionInfo *sess_info,
- uint32_t queue_index, Error **errp);
+ int (*create_session)(CryptoDevBackend *backend,
+ CryptoDevBackendSessionInfo *sess_info,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque);
+
int (*close_session)(CryptoDevBackend *backend,
- uint64_t session_id,
- uint32_t queue_index, Error **errp);
+ uint64_t session_id,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque);
+
int (*do_op)(CryptoDevBackend *backend,
- CryptoDevBackendOpInfo *op_info,
- uint32_t queue_index, Error **errp);
+ CryptoDevBackendOpInfo *op_info,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque);
};
typedef enum CryptoDevBackendOptionsType {
CRYPTODEV_BACKEND_TYPE_NONE = 0,
CRYPTODEV_BACKEND_TYPE_BUILTIN = 1,
CRYPTODEV_BACKEND_TYPE_VHOST_USER = 2,
+ CRYPTODEV_BACKEND_TYPE_LKCF = 3,
CRYPTODEV_BACKEND_TYPE__MAX,
} CryptoDevBackendOptionsType;
@@ -303,15 +314,20 @@ void cryptodev_backend_cleanup(
* @sess_info: parameters needed by session creating
* @queue_index: queue index of cryptodev backend client
* @errp: pointer to a NULL-initialized error object
+ * @cb: callback when session create is compeleted
+ * @opaque: parameter passed to callback
*
- * Create a session for symmetric/symmetric algorithms
+ * Create a session for symmetric/asymmetric algorithms
*
- * Returns: session id on success, or -1 on error
+ * Returns: 0 for success and cb will be called when creation is completed,
+ * negative value for error, and cb will not be called.
*/
-int64_t cryptodev_backend_create_session(
+int cryptodev_backend_create_session(
CryptoDevBackend *backend,
CryptoDevBackendSessionInfo *sess_info,
- uint32_t queue_index, Error **errp);
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque);
/**
* cryptodev_backend_close_session:
@@ -319,34 +335,43 @@ int64_t cryptodev_backend_create_session(
* @session_id: the session id
* @queue_index: queue index of cryptodev backend client
* @errp: pointer to a NULL-initialized error object
+ * @cb: callback when session create is compeleted
+ * @opaque: parameter passed to callback
*
* Close a session for which was previously
* created by cryptodev_backend_create_session()
*
- * Returns: 0 on success, or Negative on error
+ * Returns: 0 for success and cb will be called when creation is completed,
+ * negative value for error, and cb will not be called.
*/
int cryptodev_backend_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
- uint32_t queue_index, Error **errp);
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque);
/**
* cryptodev_backend_crypto_operation:
* @backend: the cryptodev backend object
- * @opaque: pointer to a VirtIOCryptoReq object
+ * @opaque1: pointer to a VirtIOCryptoReq object
* @queue_index: queue index of cryptodev backend client
* @errp: pointer to a NULL-initialized error object
+ * @cb: callbacks when operation is completed
+ * @opaque2: parameter passed to cb
*
* Do crypto operation, such as encryption and
* decryption
*
- * Returns: VIRTIO_CRYPTO_OK on success,
- * or -VIRTIO_CRYPTO_* on error
+ * Returns: 0 for success and cb will be called when creation is completed,
+ * negative value for error, and cb will not be called.
*/
int cryptodev_backend_crypto_operation(
CryptoDevBackend *backend,
- void *opaque,
- uint32_t queue_index, Error **errp);
+ void *opaque1,
+ uint32_t queue_index,
+ CryptoDevCompletionFunc cb,
+ void *opaque2);
/**
* cryptodev_backend_set_used:
diff --git a/qapi/qom.json b/qapi/qom.json
index 4db956f07e..30e76653ad 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -876,6 +876,7 @@
'colo-compare',
'cryptodev-backend',
'cryptodev-backend-builtin',
+ 'cryptodev-backend-lkcf',
{ 'name': 'cryptodev-vhost-user',
'if': 'CONFIG_VHOST_CRYPTO' },
'dbus-vmstate',
@@ -944,6 +945,7 @@
'colo-compare': 'ColoCompareProperties',
'cryptodev-backend': 'CryptodevBackendProperties',
'cryptodev-backend-builtin': 'CryptodevBackendProperties',
+ 'cryptodev-backend-lkcf': 'CryptodevBackendProperties',
'cryptodev-vhost-user': { 'type': 'CryptodevVhostUserProperties',
'if': 'CONFIG_VHOST_CRYPTO' },
'dbus-vmstate': 'DBusVMStateProperties',
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index e3e3b43076..bc7d4780ec 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -1681,8 +1681,10 @@ sub process {
# Block comment styles
# Block comments use /* on a line of its own
- if ($rawline !~ m@^\+.*/\*.*\*/[ \t)}]*$@ && #inline /*...*/
- $rawline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank
+ my $commentline = $rawline;
+ while ($commentline =~ s@^(\+.*)/\*.*\*/@$1@o) { # remove inline #inline /*...*/
+ }
+ if ($commentline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank
WARN("Block comments use a leading /* on a separate line\n" . $herecurr);
}
diff --git a/softmmu/memory.c b/softmmu/memory.c
index 7ba2048836..bc0be3f62c 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -33,6 +33,7 @@
#include "qemu/accel.h"
#include "hw/boards.h"
#include "migration/vmstate.h"
+#include "exec/address-spaces.h"
//#define DEBUG_UNASSIGNED
@@ -2121,6 +2122,77 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm,
rdmc->unregister_listener(rdm, rdl);
}
+/* Called with rcu_read_lock held. */
+bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr,
+ ram_addr_t *ram_addr, bool *read_only,
+ bool *mr_has_discard_manager)
+{
+ MemoryRegion *mr;
+ hwaddr xlat;
+ hwaddr len = iotlb->addr_mask + 1;
+ bool writable = iotlb->perm & IOMMU_WO;
+
+ if (mr_has_discard_manager) {
+ *mr_has_discard_manager = false;
+ }
+ /*
+ * The IOMMU TLB entry we have just covers translation through
+ * this IOMMU to its immediate target. We need to translate
+ * it the rest of the way through to memory.
+ */
+ mr = address_space_translate(&address_space_memory, iotlb->translated_addr,
+ &xlat, &len, writable, MEMTXATTRS_UNSPECIFIED);
+ if (!memory_region_is_ram(mr)) {
+ error_report("iommu map to non memory area %" HWADDR_PRIx "", xlat);
+ return false;
+ } else if (memory_region_has_ram_discard_manager(mr)) {
+ RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr);
+ MemoryRegionSection tmp = {
+ .mr = mr,
+ .offset_within_region = xlat,
+ .size = int128_make64(len),
+ };
+ if (mr_has_discard_manager) {
+ *mr_has_discard_manager = true;
+ }
+ /*
+ * Malicious VMs can map memory into the IOMMU, which is expected
+ * to remain discarded. vfio will pin all pages, populating memory.
+ * Disallow that. vmstate priorities make sure any RamDiscardManager
+ * were already restored before IOMMUs are restored.
+ */
+ if (!ram_discard_manager_is_populated(rdm, &tmp)) {
+ error_report("iommu map to discarded memory (e.g., unplugged via"
+ " virtio-mem): %" HWADDR_PRIx "",
+ iotlb->translated_addr);
+ return false;
+ }
+ }
+
+ /*
+ * Translation truncates length to the IOMMU page size,
+ * check that it did not truncate too much.
+ */
+ if (len & iotlb->addr_mask) {
+ error_report("iommu has granularity incompatible with target AS");
+ return false;
+ }
+
+ if (vaddr) {
+ *vaddr = memory_region_get_ram_ptr(mr) + xlat;
+ }
+
+ if (ram_addr) {
+ *ram_addr = memory_region_get_ram_addr(mr) + xlat;
+ }
+
+ if (read_only) {
+ *read_only = !writable || mr->readonly;
+ }
+
+ return true;
+}
+
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
{
uint8_t mask = 1 << client;
diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py
new file mode 100644
index 0000000000..8745a58a76
--- /dev/null
+++ b/tests/avocado/acpi-bits.py
@@ -0,0 +1,396 @@
+#!/usr/bin/env python3
+# group: rw quick
+# Exercize QEMU generated ACPI/SMBIOS tables using biosbits,
+# https://biosbits.org/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Author:
+# Ani Sinha <ani@anisinha.ca>
+
+# pylint: disable=invalid-name
+# pylint: disable=consider-using-f-string
+
+"""
+This is QEMU ACPI/SMBIOS avocado tests using biosbits.
+Biosbits is available originally at https://biosbits.org/.
+This test uses a fork of the upstream bits and has numerous fixes
+including an upgraded acpica. The fork is located here:
+https://gitlab.com/qemu-project/biosbits-bits .
+"""
+
+import logging
+import os
+import platform
+import re
+import shutil
+import subprocess
+import tarfile
+import tempfile
+import time
+import zipfile
+from typing import (
+ List,
+ Optional,
+ Sequence,
+)
+from qemu.machine import QEMUMachine
+from avocado import skipIf
+from avocado_qemu import QemuBaseTest
+
+deps = ["xorriso"] # dependent tools needed in the test setup/box.
+supported_platforms = ['x86_64'] # supported test platforms.
+
+
+def which(tool):
+ """ looks up the full path for @tool, returns None if not found
+ or if @tool does not have executable permissions.
+ """
+ paths=os.getenv('PATH')
+ for p in paths.split(os.path.pathsep):
+ p = os.path.join(p, tool)
+ if os.path.exists(p) and os.access(p, os.X_OK):
+ return p
+ return None
+
+def missing_deps():
+ """ returns True if any of the test dependent tools are absent.
+ """
+ for dep in deps:
+ if which(dep) is None:
+ return True
+ return False
+
+def supported_platform():
+ """ checks if the test is running on a supported platform.
+ """
+ return platform.machine() in supported_platforms
+
+class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods
+ """
+ A QEMU VM, with isa-debugcon enabled and bits iso passed
+ using -cdrom to QEMU commandline.
+
+ """
+ def __init__(self,
+ binary: str,
+ args: Sequence[str] = (),
+ wrapper: Sequence[str] = (),
+ name: Optional[str] = None,
+ base_temp_dir: str = "/var/tmp",
+ debugcon_log: str = "debugcon-log.txt",
+ debugcon_addr: str = "0x403",
+ sock_dir: Optional[str] = None,
+ qmp_timer: Optional[float] = None):
+ # pylint: disable=too-many-arguments
+
+ if name is None:
+ name = "qemu-bits-%d" % os.getpid()
+ if sock_dir is None:
+ sock_dir = base_temp_dir
+ super().__init__(binary, args, wrapper=wrapper, name=name,
+ base_temp_dir=base_temp_dir,
+ sock_dir=sock_dir, qmp_timer=qmp_timer)
+ self.debugcon_log = debugcon_log
+ self.debugcon_addr = debugcon_addr
+ self.base_temp_dir = base_temp_dir
+
+ @property
+ def _base_args(self) -> List[str]:
+ args = super()._base_args
+ args.extend([
+ '-chardev',
+ 'file,path=%s,id=debugcon' %os.path.join(self.base_temp_dir,
+ self.debugcon_log),
+ '-device',
+ 'isa-debugcon,iobase=%s,chardev=debugcon' %self.debugcon_addr,
+ ])
+ return args
+
+ def base_args(self):
+ """return the base argument to QEMU binary"""
+ return self._base_args
+
+@skipIf(not supported_platform() or missing_deps() or os.getenv('GITLAB_CI'),
+ 'incorrect platform or dependencies (%s) not installed ' \
+ 'or running on GitLab' % ','.join(deps))
+class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes
+ """
+ ACPI and SMBIOS tests using biosbits.
+
+ :avocado: tags=arch:x86_64
+ :avocado: tags=acpi
+
+ """
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._vm = None
+ self._workDir = None
+ self._baseDir = None
+
+ # following are some standard configuration constants
+ self._bitsInternalVer = 2020
+ self._bitsCommitHash = 'b48b88ff' # commit hash must match
+ # the artifact tag below
+ self._bitsTag = "qemu-bits-10182022" # this is the latest bits
+ # release as of today.
+ self._bitsArtSHA1Hash = 'b04790ac9b99b5662d0416392c73b97580641fe5'
+ self._bitsArtURL = ("https://gitlab.com/qemu-project/"
+ "biosbits-bits/-/jobs/artifacts/%s/"
+ "download?job=qemu-bits-build" %self._bitsTag)
+ self._debugcon_addr = '0x403'
+ self._debugcon_log = 'debugcon-log.txt'
+ logging.basicConfig(level=logging.INFO)
+ self.logger = logging.getLogger('acpi-bits')
+
+ def _print_log(self, log):
+ self.logger.info('\nlogs from biosbits follows:')
+ self.logger.info('==========================================\n')
+ self.logger.info(log)
+ self.logger.info('==========================================\n')
+
+ def copy_bits_config(self):
+ """ copies the bios bits config file into bits.
+ """
+ config_file = 'bits-cfg.txt'
+ bits_config_dir = os.path.join(self._baseDir, 'acpi-bits',
+ 'bits-config')
+ target_config_dir = os.path.join(self._workDir,
+ 'bits-%d' %self._bitsInternalVer,
+ 'boot')
+ self.assertTrue(os.path.exists(bits_config_dir))
+ self.assertTrue(os.path.exists(target_config_dir))
+ self.assertTrue(os.access(os.path.join(bits_config_dir,
+ config_file), os.R_OK))
+ shutil.copy2(os.path.join(bits_config_dir, config_file),
+ target_config_dir)
+ self.logger.info('copied config file %s to %s',
+ config_file, target_config_dir)
+
+ def copy_test_scripts(self):
+ """copies the python test scripts into bits. """
+
+ bits_test_dir = os.path.join(self._baseDir, 'acpi-bits',
+ 'bits-tests')
+ target_test_dir = os.path.join(self._workDir,
+ 'bits-%d' %self._bitsInternalVer,
+ 'boot', 'python')
+
+ self.assertTrue(os.path.exists(bits_test_dir))
+ self.assertTrue(os.path.exists(target_test_dir))
+
+ for filename in os.listdir(bits_test_dir):
+ if os.path.isfile(os.path.join(bits_test_dir, filename)) and \
+ filename.endswith('.py2'):
+ # all test scripts are named with extension .py2 so that
+ # avocado does not try to load them. These scripts are
+ # written for python 2.7 not python 3 and hence if avocado
+ # loaded them, it would complain about python 3 specific
+ # syntaxes.
+ newfilename = os.path.splitext(filename)[0] + '.py'
+ shutil.copy2(os.path.join(bits_test_dir, filename),
+ os.path.join(target_test_dir, newfilename))
+ self.logger.info('copied test file %s to %s',
+ filename, target_test_dir)
+
+ # now remove the pyc test file if it exists, otherwise the
+ # changes in the python test script won't be executed.
+ testfile_pyc = os.path.splitext(filename)[0] + '.pyc'
+ if os.access(os.path.join(target_test_dir, testfile_pyc),
+ os.F_OK):
+ os.remove(os.path.join(target_test_dir, testfile_pyc))
+ self.logger.info('removed compiled file %s',
+ os.path.join(target_test_dir,
+ testfile_pyc))
+
+ def fix_mkrescue(self, mkrescue):
+ """ grub-mkrescue is a bash script with two variables, 'prefix' and
+ 'libdir'. They must be pointed to the right location so that the
+ iso can be generated appropriately. We point the two variables to
+ the directory where we have extracted our pre-built bits grub
+ tarball.
+ """
+ grub_x86_64_mods = os.path.join(self._workDir, 'grub-inst-x86_64-efi')
+ grub_i386_mods = os.path.join(self._workDir, 'grub-inst')
+
+ self.assertTrue(os.path.exists(grub_x86_64_mods))
+ self.assertTrue(os.path.exists(grub_i386_mods))
+
+ new_script = ""
+ with open(mkrescue, 'r', encoding='utf-8') as filehandle:
+ orig_script = filehandle.read()
+ new_script = re.sub('(^prefix=)(.*)',
+ r'\1"%s"' %grub_x86_64_mods,
+ orig_script, flags=re.M)
+ new_script = re.sub('(^libdir=)(.*)', r'\1"%s/lib"' %grub_i386_mods,
+ new_script, flags=re.M)
+
+ with open(mkrescue, 'w', encoding='utf-8') as filehandle:
+ filehandle.write(new_script)
+
+ def generate_bits_iso(self):
+ """ Uses grub-mkrescue to generate a fresh bits iso with the python
+ test scripts
+ """
+ bits_dir = os.path.join(self._workDir,
+ 'bits-%d' %self._bitsInternalVer)
+ iso_file = os.path.join(self._workDir,
+ 'bits-%d.iso' %self._bitsInternalVer)
+ mkrescue_script = os.path.join(self._workDir,
+ 'grub-inst-x86_64-efi', 'bin',
+ 'grub-mkrescue')
+
+ self.assertTrue(os.access(mkrescue_script,
+ os.R_OK | os.W_OK | os.X_OK))
+
+ self.fix_mkrescue(mkrescue_script)
+
+ self.logger.info('using grub-mkrescue for generating biosbits iso ...')
+
+ try:
+ if os.getenv('V'):
+ subprocess.check_call([mkrescue_script, '-o', iso_file,
+ bits_dir], stderr=subprocess.STDOUT)
+ else:
+ subprocess.check_call([mkrescue_script, '-o',
+ iso_file, bits_dir],
+ stderr=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL)
+ except Exception as e: # pylint: disable=broad-except
+ self.skipTest("Error while generating the bits iso. "
+ "Pass V=1 in the environment to get more details. "
+ + str(e))
+
+ self.assertTrue(os.access(iso_file, os.R_OK))
+
+ self.logger.info('iso file %s successfully generated.', iso_file)
+
+ def setUp(self): # pylint: disable=arguments-differ
+ super().setUp('qemu-system-')
+
+ self._baseDir = os.getenv('AVOCADO_TEST_BASEDIR')
+
+ # workdir could also be avocado's own workdir in self.workdir.
+ # At present, I prefer to maintain my own temporary working
+ # directory. It gives us more control over the generated bits
+ # log files and also for debugging, we may chose not to remove
+ # this working directory so that the logs and iso can be
+ # inspected manually and archived if needed.
+ self._workDir = tempfile.mkdtemp(prefix='acpi-bits-',
+ suffix='.tmp')
+ self.logger.info('working dir: %s', self._workDir)
+
+ prebuiltDir = os.path.join(self._workDir, 'prebuilt')
+ if not os.path.isdir(prebuiltDir):
+ os.mkdir(prebuiltDir, mode=0o775)
+
+ bits_zip_file = os.path.join(prebuiltDir, 'bits-%d-%s.zip'
+ %(self._bitsInternalVer,
+ self._bitsCommitHash))
+ grub_tar_file = os.path.join(prebuiltDir,
+ 'bits-%d-%s-grub.tar.gz'
+ %(self._bitsInternalVer,
+ self._bitsCommitHash))
+
+ bitsLocalArtLoc = self.fetch_asset(self._bitsArtURL,
+ asset_hash=self._bitsArtSHA1Hash)
+ self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc)
+
+ # extract the bits artifact in the temp working directory
+ with zipfile.ZipFile(bitsLocalArtLoc, 'r') as zref:
+ zref.extractall(prebuiltDir)
+
+ # extract the bits software in the temp working directory
+ with zipfile.ZipFile(bits_zip_file, 'r') as zref:
+ zref.extractall(self._workDir)
+
+ with tarfile.open(grub_tar_file, 'r', encoding='utf-8') as tarball:
+ tarball.extractall(self._workDir)
+
+ self.copy_test_scripts()
+ self.copy_bits_config()
+ self.generate_bits_iso()
+
+ def parse_log(self):
+ """parse the log generated by running bits tests and
+ check for failures.
+ """
+ debugconf = os.path.join(self._workDir, self._debugcon_log)
+ log = ""
+ with open(debugconf, 'r', encoding='utf-8') as filehandle:
+ log = filehandle.read()
+
+ matchiter = re.finditer(r'(.*Summary: )(\d+ passed), (\d+ failed).*',
+ log)
+ for match in matchiter:
+ # verify that no test cases failed.
+ try:
+ self.assertEqual(match.group(3).split()[0], '0',
+ 'Some bits tests seems to have failed. ' \
+ 'Please check the test logs for more info.')
+ except AssertionError as e:
+ self._print_log(log)
+ raise e
+ else:
+ if os.getenv('V'):
+ self._print_log(log)
+
+ def tearDown(self):
+ """
+ Lets do some cleanups.
+ """
+ if self._vm:
+ self.assertFalse(not self._vm.is_running)
+ self.logger.info('removing the work directory %s', self._workDir)
+ shutil.rmtree(self._workDir)
+ super().tearDown()
+
+ def test_acpi_smbios_bits(self):
+ """The main test case implementaion."""
+
+ iso_file = os.path.join(self._workDir,
+ 'bits-%d.iso' %self._bitsInternalVer)
+
+ self.assertTrue(os.access(iso_file, os.R_OK))
+
+ self._vm = QEMUBitsMachine(binary=self.qemu_bin,
+ base_temp_dir=self._workDir,
+ debugcon_log=self._debugcon_log,
+ debugcon_addr=self._debugcon_addr)
+
+ self._vm.add_args('-cdrom', '%s' %iso_file)
+ # the vm needs to be run under icount so that TCG emulation is
+ # consistent in terms of timing. smilatency tests have consistent
+ # timing requirements.
+ self._vm.add_args('-icount', 'auto')
+
+ args = " ".join(str(arg) for arg in self._vm.base_args()) + \
+ " " + " ".join(str(arg) for arg in self._vm.args)
+
+ self.logger.info("launching QEMU vm with the following arguments: %s",
+ args)
+
+ self._vm.launch()
+ # biosbits has been configured to run all the specified test suites
+ # in batch mode and then automatically initiate a vm shutdown.
+ # sleep for maximum of one minute
+ max_sleep_time = time.monotonic() + 60
+ while self._vm.is_running() and time.monotonic() < max_sleep_time:
+ time.sleep(1)
+
+ self.assertFalse(time.monotonic() > max_sleep_time,
+ 'The VM seems to have failed to shutdown in time')
+
+ self.parse_log()
diff --git a/tests/avocado/acpi-bits/bits-config/bits-cfg.txt b/tests/avocado/acpi-bits/bits-config/bits-cfg.txt
new file mode 100644
index 0000000000..8010804453
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-config/bits-cfg.txt
@@ -0,0 +1,18 @@
+# BITS configuration file
+[bits]
+
+# To run BITS in batch mode, set batch to a list of one or more of the
+# following keywords; BITS will then run all of the requested operations, then
+# save the log file to disk.
+#
+# test: Run the full BITS testsuite.
+# acpi: Dump all ACPI structures.
+# smbios: Dump all SMBIOS structures.
+#
+# Leave batch set to an empty string to disable batch mode.
+# batch =
+
+# Uncomment the following to run all available batch operations
+# please take a look at boot/python/init.py in bits zip file
+# to see how these options are parsed and used.
+batch = test acpi smbios
diff --git a/tests/avocado/acpi-bits/bits-tests/smbios.py2 b/tests/avocado/acpi-bits/bits-tests/smbios.py2
new file mode 100644
index 0000000000..9667d0542c
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/smbios.py2
@@ -0,0 +1,2430 @@
+# Copyright (c) 2015, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""SMBIOS/DMI module."""
+
+import bits
+import bitfields
+import ctypes
+import redirect
+import struct
+import uuid
+import unpack
+import ttypager
+import sys
+
+class SMBIOS(unpack.Struct):
+ def __new__(cls):
+ if sys.platform == "BITS-EFI":
+ import efi
+ sm_ptr = efi.system_table.ConfigurationTableDict.get(efi.SMBIOS_TABLE_GUID)
+ else:
+ address = 0xF0000
+ mem = bits.memory(0xF0000, 0x10000)
+ for offset in range(0, len(mem), 16):
+ signature = (ctypes.c_char * 4).from_address(address + offset).value
+ if signature == "_SM_":
+ entry_point_length = ctypes.c_ubyte.from_address(address + offset + 5).value
+ csum = sum(map(ord, mem[offset:offset + entry_point_length])) & 0xff
+ if csum == 0:
+ sm_ptr = address + offset
+ break
+ else:
+ return None
+
+ if not sm_ptr:
+ return None
+
+ sm = super(SMBIOS, cls).__new__(cls)
+ sm._header_memory = bits.memory(sm_ptr, 0x1f)
+ return sm
+
+ def __init__(self):
+ super(SMBIOS, self).__init__()
+ u = unpack.Unpackable(self._header_memory)
+ self.add_field('header', Header(u))
+ self._structure_memory = bits.memory(self.header.structure_table_address, self.header.structure_table_length)
+ u = unpack.Unpackable(self._structure_memory)
+ self.add_field('structures', unpack.unpack_all(u, _smbios_structures, self), unpack.format_each("\n\n{!r}"))
+
+ def structure_type(self, num):
+ '''Dumps structure of given Type if present'''
+ try:
+ types_present = [self.structures[x].smbios_structure_type for x in range(len(self.structures))]
+ matrix = dict()
+ for index in range(len(types_present)):
+ if types_present.count(types_present[index]) == 1:
+ matrix[types_present[index]] = self.structures[index]
+ else: # if multiple structures of the same type, return a list of structures for the type number
+ if matrix.has_key(types_present[index]):
+ matrix[types_present[index]].append(self.structures[index])
+ else:
+ matrix[types_present[index]] = [self.structures[index]]
+ return matrix[num]
+ except:
+ print "Failure: Type {} - not found".format(num)
+
+class Header(unpack.Struct):
+ def __new__(cls, u):
+ return super(Header, cls).__new__(cls)
+
+ def __init__(self, u):
+ super(Header, self).__init__()
+ self.raw_data = u.unpack_rest()
+ u = unpack.Unpackable(self.raw_data)
+ self.add_field('anchor_string', u.unpack_one("4s"))
+ self.add_field('checksum', u.unpack_one("B"))
+ self.add_field('length', u.unpack_one("B"))
+ self.add_field('major_version', u.unpack_one("B"))
+ self.add_field('minor_version', u.unpack_one("B"))
+ self.add_field('max_structure_size', u.unpack_one("<H"))
+ self.add_field('entry_point_revision', u.unpack_one("B"))
+ self.add_field('formatted_area', u.unpack_one("5s"))
+ self.add_field('intermediate_anchor_string', u.unpack_one("5s"))
+ self.add_field('intermediate_checksum', u.unpack_one("B"))
+ self.add_field('structure_table_length', u.unpack_one("<H"))
+ self.add_field('structure_table_address', u.unpack_one("<I"))
+ self.add_field('number_structures', u.unpack_one("<H"))
+ self.add_field('bcd_revision', u.unpack_one("B"))
+ if not u.at_end():
+ self.add_field('data', u.unpack_rest())
+
+class SmbiosBaseStructure(unpack.Struct):
+ def __new__(cls, u, sm):
+ t = u.unpack_peek_one("B")
+ if cls.smbios_structure_type is not None and t != cls.smbios_structure_type:
+ return None
+ return super(SmbiosBaseStructure, cls).__new__(cls)
+
+ def __init__(self, u, sm):
+ super(SmbiosBaseStructure, self).__init__()
+ self.start_offset = u.offset
+ length = u.unpack_peek_one("<xB")
+ self.raw_data = u.unpack_raw(length)
+ self.u = unpack.Unpackable(self.raw_data)
+
+ self.strings_offset = u.offset
+ def unpack_string():
+ return "".join(iter(lambda: u.unpack_one("c"), "\x00"))
+ strings = list(iter(unpack_string, ""))
+ if not strings:
+ u.skip(1)
+
+ self.strings_length = u.offset - self.strings_offset
+ self.raw_strings = str(bits.memory(sm.header.structure_table_address + self.strings_offset, self.strings_length))
+
+ if len(strings):
+ self.strings = strings
+
+ self.add_field('type', self.u.unpack_one("B"))
+ self.add_field('length', self.u.unpack_one("B"))
+ self.add_field('handle', self.u.unpack_one("<H"))
+
+ def fini(self):
+ if not self.u.at_end():
+ self.add_field('data', self.u.unpack_rest())
+ del self.u
+
+ def fmtstr(self, i):
+ """Format the specified index and the associated string"""
+ return "{} '{}'".format(i, self.getstr(i))
+
+ def getstr(self, i):
+ """Get the string associated with the given index"""
+ if i == 0:
+ return "(none)"
+ if not hasattr(self, "strings"):
+ return "(error: structure has no strings)"
+ if i > len(self.strings):
+ return "(error: string index out of range)"
+ return self.strings[i - 1]
+
+class BIOSInformation(SmbiosBaseStructure):
+ smbios_structure_type = 0
+
+ def __init__(self, u, sm):
+ super(BIOSInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('vendor', u.unpack_one("B"), self.fmtstr)
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('starting_address_segment', u.unpack_one("<H"))
+ self.add_field('release_date', u.unpack_one("B"), self.fmtstr)
+ self.add_field('rom_size', u.unpack_one("B"))
+ self.add_field('characteristics', u.unpack_one("<Q"))
+ minor_version_str = str(sm.header.minor_version) # 34 is .34, 4 is .4, 41 is .41; compare ASCIIbetically to compare initial digits rather than numeric value
+ if (sm.header.major_version, minor_version_str) >= (2,"4"):
+ characteristic_bytes = 2
+ else:
+ characteristic_bytes = self.length - 0x12
+ self.add_field('characteristics_extensions', [u.unpack_one("B") for b in range(characteristic_bytes)])
+ if (sm.header.major_version, minor_version_str) >= (2,"4"):
+ self.add_field('major_release', u.unpack_one("B"))
+ self.add_field('minor_release', u.unpack_one("B"))
+ self.add_field('ec_major_release', u.unpack_one("B"))
+ self.add_field('ec_minor_release', u.unpack_one("B"))
+ except:
+ self.decode_failure = True
+ print "Error parsing BIOSInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemInformation(SmbiosBaseStructure):
+ smbios_structure_type = 1
+
+ def __init__(self, u, sm):
+ super(SystemInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('product_name', u.unpack_one("B"), self.fmtstr)
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x8:
+ self.add_field('uuid', uuid.UUID(bytes_le=u.unpack_one("16s")))
+ wakeup_types = {
+ 0: 'Reserved',
+ 1: 'Other',
+ 2: 'Unknown',
+ 3: 'APM Timer',
+ 4: 'Modem Ring',
+ 5: 'LAN Remote',
+ 6: 'Power Switch',
+ 7: 'PCI PME#',
+ 8: 'AC Power Restored'
+ }
+ self.add_field('wakeup_type', u.unpack_one("B"), unpack.format_table("{}", wakeup_types))
+ if self.length > 0x19:
+ self.add_field('sku_number', u.unpack_one("B"), self.fmtstr)
+ self.add_field('family', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decode_failure = True
+ print "Error parsing SystemInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+_board_types = {
+ 1: 'Unknown',
+ 2: 'Other',
+ 3: 'Server Blade',
+ 4: 'Connectivity Switch',
+ 5: 'System Management Module',
+ 6: 'Processor Module',
+ 7: 'I/O Module',
+ 8: 'Memory Module',
+ 9: 'Daughter Board',
+ 0xA: 'Motherboard',
+ 0xB: 'Processor/Memory Module',
+ 0xC: 'Processor/IO Module',
+ 0xD: 'Interconnect Board'
+}
+
+class BaseboardInformation(SmbiosBaseStructure):
+ smbios_structure_type = 2
+
+ def __init__(self, u, sm):
+ super(BaseboardInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('product', u.unpack_one("B"), self.fmtstr)
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+
+ if self.length > 0x8:
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+
+ if self.length > 0x9:
+ self.add_field('feature_flags', u.unpack_one("B"))
+ self.add_field('hosting_board', bool(bitfields.getbits(self.feature_flags, 0)), "feature_flags[0]={}")
+ self.add_field('requires_daughter_card', bool(bitfields.getbits(self.feature_flags, 1)), "feature_flags[1]={}")
+ self.add_field('removable', bool(bitfields.getbits(self.feature_flags, 2)), "feature_flags[2]={}")
+ self.add_field('replaceable', bool(bitfields.getbits(self.feature_flags, 3)), "feature_flags[3]={}")
+ self.add_field('hot_swappable', bool(bitfields.getbits(self.feature_flags, 4)), "feature_flags[4]={}")
+
+ if self.length > 0xA:
+ self.add_field('location', u.unpack_one("B"), self.fmtstr)
+
+ if self.length > 0xB:
+ self.add_field('chassis_handle', u.unpack_one("<H"))
+
+ if self.length > 0xD:
+ self.add_field('board_type', u.unpack_one("B"), unpack.format_table("{}", _board_types))
+
+ if self.length > 0xE:
+ self.add_field('handle_count', u.unpack_one("B"))
+ if self.handle_count > 0:
+ self.add_field('contained_object_handles', tuple(u.unpack_one("<H") for i in range(self.handle_count)))
+ except:
+ self.decode_failure = True
+ print "Error parsing BaseboardInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemEnclosure(SmbiosBaseStructure):
+ smbios_structure_type = 3
+
+ def __init__(self, u, sm):
+ super(SystemEnclosure, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('enumerated_type', u.unpack_one("B"))
+ self.add_field('chassis_lock_present', bool(bitfields.getbits(self.enumerated_type, 7)), "enumerated_type[7]={}")
+ board_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Desktop',
+ 0x04: 'Low Profile Desktop',
+ 0x05: 'Pizza Box',
+ 0x06: 'Mini Tower',
+ 0x07: 'Tower',
+ 0x08: 'Portable',
+ 0x09: 'Laptop',
+ 0x0A: 'Notebook',
+ 0x0B: 'Hand Held',
+ 0x0C: 'Docking Station',
+ 0x0D: 'All in One',
+ 0x0E: 'Sub Notebook',
+ 0x0F: 'Space-saving',
+ 0x10: 'Lunch Box',
+ 0x11: 'Main Server Chassis',
+ 0x12: 'Expansion Chassis',
+ 0x13: 'SubChassis',
+ 0x14: 'Bus Expansion Chassis',
+ 0x15: 'Peripheral Chassis',
+ 0x16: 'RAID Chassis',
+ 0x17: 'Rack Mount Chassis',
+ 0x18: 'Sealed-case PC',
+ 0x19: 'Multi-system chassis W',
+ 0x1A: 'Compact PCI',
+ 0x1B: 'Advanced TCA',
+ 0x1C: 'Blade',
+ 0x1D: 'Blade Enclosure',
+ }
+ self.add_field('system_enclosure_type', bitfields.getbits(self.enumerated_type, 6, 0), unpack.format_table("enumerated_type[6:0]={}", board_types))
+ self.add_field('version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ minor_version_str = str(sm.header.minor_version) # 34 is .34, 4 is .4, 41 is .41; compare ASCIIbetically to compare initial digits rather than numeric value
+ if self.length > 9:
+ chassis_states = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Safe',
+ 0x04: 'Warning',
+ 0x05: 'Critical',
+ 0x06: 'Non-recoverable',
+ }
+ self.add_field('bootup_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states))
+ self.add_field('power_supply_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states))
+ self.add_field('thermal_state', u.unpack_one("B"), unpack.format_table("{}", chassis_states))
+ security_states = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'None',
+ 0x04: 'External interface locked out',
+ 0x05: 'External interface enabled',
+ }
+ self.add_field('security_status', u.unpack_one("B"), unpack.format_table("{}", security_states))
+ if self.length > 0xd:
+ self.add_field('oem_defined', u.unpack_one("<I"))
+ if self.length > 0x11:
+ self.add_field('height', u.unpack_one("B"))
+ self.add_field('num_power_cords', u.unpack_one("B"))
+ self.add_field('contained_element_count', u.unpack_one("B"))
+ self.add_field('contained_element_length', u.unpack_one("B"))
+ if getattr(self, 'contained_element_count', 0):
+ self.add_field('contained_elements', tuple(SystemEnclosureContainedElement(u, self.contained_element_length) for i in range(self.contained_element_count)))
+ if self.length > (0x15 + (getattr(self, 'contained_element_count', 0) * getattr(self, 'contained_element_length', 0))):
+ self.add_field('sku_number', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decode_failure = True
+ print "Error parsing SystemEnclosure"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemEnclosureContainedElement(unpack.Struct):
+ def __init__(self, u, length):
+ super(SystemEnclosureContainedElement, self).__init__()
+ self.start_offset = u.offset
+ self.raw_data = u.unpack_raw(length)
+ self.u = unpack.Unpackable(self.raw_data)
+ u = self.u
+ self.add_field('contained_element_type', u.unpack_one("B"))
+ type_selections = {
+ 0: 'SMBIOS baseboard type enumeration',
+ 1: 'SMBIOS structure type enumeration',
+ }
+ self.add_field('type_select', bitfields.getbits(self.contained_element_type, 7), unpack.format_table("contained_element_type[7]={}", type_selections))
+ self.add_field('type', bitfields.getbits(self.contained_element_type, 6, 0))
+ if self.type_select == 0:
+ self.add_field('smbios_board_type', self.type, unpack.format_table("{}", _board_types))
+ else:
+ self.add_field('smbios_structure_type', self.type)
+ self.add_field('minimum', u.unpack_one("B"))
+ self.add_field('maximum', u.unpack_one("B"))
+ if not u.at_end():
+ self.add_field('data', u.unpack_rest())
+ del self.u
+
+class ProcessorInformation(SmbiosBaseStructure):
+ smbios_structure_type = 4
+
+ def __init__(self, u, sm):
+ super(ProcessorInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr)
+ processor_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Central Processor',
+ 0x04: 'Math Processor',
+ 0x05: 'DSP Processor',
+ 0x06: 'Video Processor',
+ }
+ self.add_field('processor_type', u.unpack_one("B"), unpack.format_table("{}", processor_types))
+ self.add_field('processor_family', u.unpack_one("B"))
+ self.add_field('processor_manufacturer', u.unpack_one("B"), self.fmtstr)
+ self.add_field('processor_id', u.unpack_one("<Q"))
+ self.add_field('processor_version', u.unpack_one("B"), self.fmtstr)
+ self.add_field('voltage', u.unpack_one("B"))
+ self.add_field('external_clock', u.unpack_one("<H"))
+ self.add_field('max_speed', u.unpack_one("<H"))
+ self.add_field('current_speed', u.unpack_one("<H"))
+ self.add_field('status', u.unpack_one("B"))
+ processor_upgrades = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Daughter Board',
+ 0x04: 'ZIF Socket',
+ 0x05: 'Replaceable Piggy Back',
+ 0x06: 'None',
+ 0x07: 'LIF Socket',
+ 0x08: 'Slot 1',
+ 0x09: 'Slot 2',
+ 0x0A: '370-pin socket',
+ 0x0B: 'Slot A',
+ 0x0C: 'Slot M',
+ 0x0D: 'Socket 423',
+ 0x0E: 'Socket A (Socket 462)',
+ 0x0F: 'Socket 478',
+ 0x10: 'Socket 754',
+ 0x11: 'Socket 940',
+ 0x12: 'Socket 939',
+ 0x13: 'Socket mPGA604',
+ 0x14: 'Socket LGA771',
+ 0x15: 'Socket LGA775',
+ 0x16: 'Socket S1',
+ 0x17: 'Socket AM2',
+ 0x18: 'Socket F (1207)',
+ 0x19: 'Socket LGA1366',
+ 0x1A: 'Socket G34',
+ 0x1B: 'Socket AM3',
+ 0x1C: 'Socket C32',
+ 0x1D: 'Socket LGA1156',
+ 0x1E: 'Socket LGA1567',
+ 0x1F: 'Socket PGA988A',
+ 0x20: 'Socket BGA1288',
+ 0x21: 'Socket rPGA988B',
+ 0x22: 'Socket BGA1023',
+ 0x23: 'Socket BGA1224',
+ 0x24: 'Socket BGA1155',
+ 0x25: 'Socket LGA1356',
+ 0x26: 'Socket LGA2011',
+ 0x27: 'Socket FS1',
+ 0x28: 'Socket FS2',
+ 0x29: 'Socket FM1',
+ 0x2A: 'Socket FM2',
+ }
+ self.add_field('processor_upgrade', u.unpack_one("B"), unpack.format_table("{}", processor_upgrades))
+ if self.length > 0x1A:
+ self.add_field('l1_cache_handle', u.unpack_one("<H"))
+ self.add_field('l2_cache_handle', u.unpack_one("<H"))
+ self.add_field('l3_cache_handle', u.unpack_one("<H"))
+ if self.length > 0x20:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ self.add_field('part_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x24:
+ self.add_field('core_count', u.unpack_one("B"))
+ self.add_field('core_enabled', u.unpack_one("B"))
+ self.add_field('thread_count', u.unpack_one("B"))
+ self.add_field('processor_characteristics', u.unpack_one("<H"))
+ if self.length > 0x28:
+ self.add_field('processor_family_2', u.unpack_one("<H"))
+ if self.length > 0x2A:
+ self.add_field('core_count2', u.unpack_one("<H"))
+ self.add_field('core_enabled2', u.unpack_one("<H"))
+ self.add_field('thread_count2', u.unpack_one("<H"))
+ except:
+ self.decode_failure = True
+ print "Error parsing Processor Information"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryControllerInformation(SmbiosBaseStructure): #obsolete starting with v2.1
+ smbios_structure_type = 5
+
+ def __init__(self, u, sm):
+ super(MemoryControllerInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ _error_detecting_method = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'None',
+ 0x04: '8-bit Parity',
+ 0x05: '32-bit ECC',
+ 0x06: '64-bit ECC',
+ 0x07: '128-bit ECC',
+ 0x08: 'CRC'
+ }
+ self.add_field('error_detecting_method', u.unpack_one("B"), unpack.format_table("{}", _error_detecting_method))
+ self.add_field('error_correcting_capability', u.unpack_one("B"))
+ _interleaves = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'One-Way Interleave',
+ 0x04: 'Two-Way Interleave',
+ 0x05: 'Four-Way Interleave',
+ 0x06: 'Eight-Way Interleave',
+ 0x07: 'Sixteen-Way Interleave'
+ }
+ self.add_field('supported_interleave', u.unpack_one("B"), unpack.format_table("{}", _interleaves))
+ self.add_field('current_interleave', u.unpack_one("B"), unpack.format_table("{}", _interleaves))
+ self.add_field('max_memory_module_size', u.unpack_one("B"), self.fmtstr)
+ self.add_field('supported_speeds', u.unpack_one("<H"))
+ self.add_field('supported_memory_types', u.unpack_one("<H"))
+ self.add_field('memory_module_voltage', u.unpack_one("B"))
+ self.add_field('req_voltage_b2', bitfields.getbits(self.memory_module_voltage, 2), "memory_module_voltage[2]={}")
+ self.add_field('req_voltage_b1', bitfields.getbits(self.memory_module_voltage, 1), "memory_module_voltage[1]={}")
+ self.add_field('req_voltage_b0', bitfields.getbits(self.memory_module_voltage, 0), "memory_module_voltage[0]={}")
+ self.add_field('num_associated_memory_slots', u.unpack_one("B"))
+ self.add_field('memory_module_configuration_handles', u.unpack_one("<(self.num_associated_memory_slots)H"))
+ self.add_field('enabled_error_correcting_capabilities', u.unpack_one("B"))
+ except:
+ self.decode_failure = True
+ print "Error parsing MemoryControllerInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryModuleInformation(SmbiosBaseStructure): #obsolete starting with v2.1
+ smbios_structure_type = 6
+
+ def __init__(self, u, sm):
+ super(MemoryModuleInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr)
+ self.add_field('bank_connections', u.unpack_one("B"))
+ self.add_field('current_speed', u.unpack_one("B"))
+ self.add_field('current_memory_type', u.unpack_one("<H"))
+ _mem_connection = {
+ 0: 'single',
+ 1: 'double-bank'
+ }
+ self.add_field('installed_mem', u.unpack_one("B"))
+ self.add_field('installed_size', bitfields.getbits(self.installed_mem, 6, 0), "installed_mem[6:0]={}")
+ self.add_field('installed_memory_module_connection', bitfields.getbits(self.installed_mem, 7), unpack.format_table("installed_mem[7]={}", _mem_connection))
+ self.add_field('enabled_mem', u.unpack_one("B"))
+ self.add_field('enabled_size', bitfields.getbits(self.installed_mem, 6, 0), "enabled_mem[6:0]={}")
+ self.add_field('enabled_memory_module_connection', bitfields.getbits(self.installed_mem, 7), unpack.format_table("enabled_mem[7]={}", _mem_connection))
+ self.add_field('error_status', u.unpack_one("B"))
+ self.add_field('error_status_info_obstained_from_event_log', bool(bitfields.getbits(self.error_status, 2)), unpack.format_table("error_status[2]={}", _mem_connection))
+ self.add_field('correctable_errors_received', bool(bitfields.getbits(self.error_status, 1)), unpack.format_table("error_status[1]={}", _mem_connection))
+ self.add_field('uncorrectable_errors_received', bool(bitfields.getbits(self.error_status, 0)), unpack.format_table("error_status[0]={}", _mem_connection))
+ except:
+ self.decode_failure = True
+ print "Error parsing MemoryModuleInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class CacheInformation(SmbiosBaseStructure):
+ smbios_structure_type = 7
+
+ def __init__(self, u, sm):
+ super(CacheInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('socket_designation', u.unpack_one("B"), self.fmtstr)
+ processor_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Central Processor',
+ 0x04: 'Math Processor',
+ 0x05: 'DSP Processor',
+ 0x06: 'Video Processor',
+ }
+ self.add_field('cache_configuration', u.unpack_one("<H"))
+ _operational_mode = {
+ 0b00: 'Write Through',
+ 0b01: 'Write Back',
+ 0b10: 'Varies with Memory Address',
+ 0b11: 'Unknown'
+ }
+ self.add_field('operational_mode', bitfields.getbits(self.cache_configuration, 9, 8), unpack.format_table("cache_configuration[9:8]={}", _operational_mode))
+ self.add_field('enabled_at_boot_time', bool(bitfields.getbits(self.cache_configuration, 7)), "cache_configuration[7]={}")
+ _location = {
+ 0b00: 'Internal',
+ 0b01: 'External',
+ 0b10: 'Reserved',
+ 0b11: 'Unknown'
+ }
+ self.add_field('location_relative_to_cpu_module', bitfields.getbits(self.cache_configuration, 6, 5), unpack.format_table("cache_configuration[6:5]={}", _location))
+ self.add_field('cache_socketed', bool(bitfields.getbits(self.cache_configuration, 3)), "cache_configuration[3]={}")
+ self.add_field('cache_level', bitfields.getbits(self.cache_configuration, 2, 0), "cache_configuration[2:0]={}")
+ self.add_field('max_cache_size', u.unpack_one("<H"))
+ _granularity = {
+ 0: '1K granularity',
+ 1: '64K granularity'
+ }
+ self.add_field('max_granularity', bitfields.getbits(self.cache_configuration, 15), unpack.format_table("max_cache_size[15]={}", _granularity))
+ self.add_field('max_size_in_granularity', bitfields.getbits(self.cache_configuration, 14, 0), "max_cache_size[14, 0]={}")
+ self.add_field('installed_size', u.unpack_one("<H"))
+ if self.installed_size != 0:
+ self.add_field('installed_granularity', bitfields.getbits(self.cache_configuration, 15), unpack.format_table("installed_size[15]={}", _granularity))
+ self.add_field('installed_size_in_granularity', bitfields.getbits(self.cache_configuration, 14, 0), "installed_size[14, 0]={}")
+ self.add_field('supported_sram_type', u.unpack_one("<H"))
+ self.add_field('current_sram_type', u.unpack_one("<H"))
+ if self.length > 0x0F:
+ self.add_field('cache_speed', u.unpack_one("B"))
+ if self.length > 0x10:
+ _error_correction = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'None',
+ 0x04: 'Parity',
+ 0x05: 'Single-bit ECC',
+ 0x06: 'Multi-bit ECC'
+ }
+ self.add_field('error_correction', u.unpack_one("B"), unpack.format_table("{}", _error_correction))
+ if self.length > 0x10:
+ _system_cache_type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Instruction',
+ 0x04: 'Data',
+ 0x05: 'Unified'
+ }
+ self.add_field('system_cache_type', u.unpack_one("B"), unpack.format_table("{}", _system_cache_type))
+ if self.length > 0x12:
+ _associativity = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Direct Mapped',
+ 0x04: '2-way Set-Associative',
+ 0x05: '4-way Set-Associative',
+ 0x06: 'Fully Associative',
+ 0x07: '8-way Set-Associative',
+ 0x08: '16-way Set-Associative',
+ 0x09: '12-way Set-Associative',
+ 0x0A: '24-way Set-Associative',
+ 0x0B: '32-way Set-Associative',
+ 0x0C: '48-way Set-Associative',
+ 0x0D: '64-way Set-Associative',
+ 0x0E: '20-way Set-Associative'
+ }
+ self.add_field('associativity', u.unpack_one("B"), unpack.format_table("{}", _associativity))
+
+ except:
+ self.decode_failure = True
+ print "Error parsing CacheInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class PortConnectorInfo(SmbiosBaseStructure):
+ smbios_structure_type = 8
+
+ def __init__(self, u, sm):
+ super(PortConnectorInfo, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('internal_reference_designator', u.unpack_one("B"), self.fmtstr)
+ connector_types = {
+ 0x00: 'None',
+ 0x01: 'Centronics',
+ 0x02: 'Mini Centronics',
+ 0x03: 'Proprietary',
+ 0x04: 'DB-25 pin male',
+ 0x05: 'DB-25 pin female',
+ 0x06: 'DB-15 pin male',
+ 0x07: 'DB-15 pin female',
+ 0x08: 'DB-9 pin male',
+ 0x09: 'DB-9 pin female',
+ 0x0A: 'RJ-11',
+ 0x0B: 'RJ-45',
+ 0x0C: '50-pin MiniSCSI',
+ 0x0D: 'Mini-DIN',
+ 0x0E: 'Micro-DIN',
+ 0x0F: 'PS/2',
+ 0x10: 'Infrared',
+ 0x11: 'HP-HIL',
+ 0x12: 'Access Bus (USB)',
+ 0x13: 'SSA SCSI',
+ 0x14: 'Circular DIN-8 male',
+ 0x15: 'Circular DIN-8 female',
+ 0x16: 'On Board IDE',
+ 0x17: 'On Board Floppy',
+ 0x18: '9-pin Dual Inline (pin 10 cut)',
+ 0x19: '25-pin Dual Inline (pin 26 cut)',
+ 0x1A: '50-pin Dual Inline',
+ 0x1B: '68-pin Dual Inline',
+ 0x1C: 'On Board Sound Input from CD-ROM',
+ 0x1D: 'Mini-Centronics Type-14',
+ 0x1E: 'Mini-Centronics Type-26',
+ 0x1F: 'Mini-jack (headphones)',
+ 0x20: 'BNC',
+ 0x21: '1394',
+ 0x22: 'SAS/SATA Plug Receptacle',
+ 0xA0: 'PC-98',
+ 0xA1: 'PC-98Hireso',
+ 0xA2: 'PC-H98',
+ 0xA3: 'PC-98Note',
+ 0xA4: 'PC-98Full',
+ 0xFF: 'Other',
+ }
+ self.add_field('internal_connector_type', u.unpack_one("B"), unpack.format_table("{}", connector_types))
+ self.add_field('external_reference_designator', u.unpack_one("B"), self.fmtstr)
+ self.add_field('external_connector_type', u.unpack_one("B"), unpack.format_table("{}", connector_types))
+ port_types = {
+ 0x00: 'None',
+ 0x01: 'Parallel Port XT/AT Compatible',
+ 0x02: 'Parallel Port PS/2',
+ 0x03: 'Parallel Port ECP',
+ 0x04: 'Parallel Port EPP',
+ 0x05: 'Parallel Port ECP/EPP',
+ 0x06: 'Serial Port XT/AT Compatible',
+ 0x07: 'Serial Port 16450 Compatible',
+ 0x08: 'Serial Port 16550 Compatible',
+ 0x09: 'Serial Port 16550A Compatible',
+ 0x0A: 'SCSI Port',
+ 0x0B: 'MIDI Port',
+ 0x0C: 'Joy Stick Port',
+ 0x0D: 'Keyboard Port',
+ 0x0E: 'Mouse Port',
+ 0x0F: 'SSA SCSI',
+ 0x10: 'USB',
+ 0x11: 'FireWire (IEEE P1394)',
+ 0x12: 'PCMCIA Type I2',
+ 0x13: 'PCMCIA Type II',
+ 0x14: 'PCMCIA Type III',
+ 0x15: 'Cardbus',
+ 0x16: 'Access Bus Port',
+ 0x17: 'SCSI II',
+ 0x18: 'SCSI Wide',
+ 0x19: 'PC-98',
+ 0x1A: 'PC-98-Hireso',
+ 0x1B: 'PC-H98',
+ 0x1C: 'Video Port',
+ 0x1D: 'Audio Port',
+ 0x1E: 'Modem Port',
+ 0x1F: 'Network Port',
+ 0x20: 'SATA',
+ 0x21: 'SAS',
+ 0xA0: '8251 Compatible',
+ 0xA1: '8251 FIFO Compatible',
+ 0xFF: 'Other',
+ }
+ self.add_field('port_type', u.unpack_one("B"), unpack.format_table("{}", port_types))
+ except:
+ self.decodeFailure = True
+ print "Error parsing PortConnectorInfo"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemSlots(SmbiosBaseStructure):
+ smbios_structure_type = 9
+
+ def __init__(self, u, sm):
+ super(SystemSlots, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('designation', u.unpack_one("B"), self.fmtstr)
+ _slot_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'ISA',
+ 0x04: 'MCA',
+ 0x05: 'EISA',
+ 0x06: 'PCI',
+ 0x07: 'PC Card (PCMCIA)',
+ 0x08: 'VL-VESA',
+ 0x09: 'Proprietary',
+ 0x0A: 'Processor Card Slot',
+ 0x0B: 'Proprietary Memory Card Slot',
+ 0x0C: 'I/O Riser Card Slot',
+ 0x0D: 'NuBus',
+ 0x0E: 'PCI 66MHz Capable',
+ 0x0F: 'AGP',
+ 0x10: 'AGP 2X',
+ 0x11: 'AGP 4X',
+ 0x12: 'PCI-X',
+ 0x13: 'AGP 8X',
+ 0xA0: 'PC-98/C20',
+ 0xA1: 'PC-98/C24',
+ 0xA2: 'PC-98/E',
+ 0xA3: 'PC-98/Local Bus',
+ 0xA4: 'PC-98/Card',
+ 0xA5: 'PCI Express',
+ 0xA6: 'PCI Express x1',
+ 0xA7: 'PCI Express x2',
+ 0xA8: 'PCI Express x4',
+ 0xA9: 'PCI Express x8',
+ 0xAA: 'PCI Express x16',
+ 0xAB: 'PCI Express Gen 2',
+ 0xAC: 'PCI Express Gen 2 x1',
+ 0xAD: 'PCI Express Gen 2 x2',
+ 0xAE: 'PCI Express Gen 2 x4',
+ 0xAF: 'PCI Express Gen 2 x8',
+ 0xB0: 'PCI Express Gen 2 x16',
+ 0xB1: 'PCI Express Gen 3',
+ 0xB2: 'PCI Express Gen 3 x1',
+ 0xB3: 'PCI Express Gen 3 x2',
+ 0xB4: 'PCI Express Gen 3 x4',
+ 0xB5: 'PCI Express Gen 3 x8',
+ 0xB6: 'PCI Express Gen 3 x16',
+ }
+ self.add_field('slot_type', u.unpack_one("B"), unpack.format_table("{}", _slot_types))
+ _slot_data_bus_widths = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: '8 bit',
+ 0x04: '16 bit',
+ 0x05: '32 bit',
+ 0x06: '64 bit',
+ 0x07: '128 bit',
+ 0x08: '1x or x1',
+ 0x09: '2x or x2',
+ 0x0A: '4x or x4',
+ 0x0B: '8x or x8',
+ 0x0C: '12x or x12',
+ 0x0D: '16x or x16',
+ 0x0E: '32x or x32',
+ }
+ self.add_field('slot_data_bus_width', u.unpack_one('B'), unpack.format_table("{}", _slot_data_bus_widths))
+ _current_usages = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Available',
+ 0x04: 'In use',
+ }
+ self.add_field('current_usage', u.unpack_one('B'), unpack.format_table("{}", _current_usages))
+ _slot_lengths = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Short Length',
+ 0x04: 'Long Length',
+ }
+ self.add_field('slot_length', u.unpack_one('B'), unpack.format_table("{}", _slot_lengths))
+ self.add_field('slot_id', u.unpack_one('<H'))
+ self.add_field('characteristics1', u.unpack_one('B'))
+ self.add_field('characteristics_unknown', bool(bitfields.getbits(self.characteristics1, 0)), "characteristics1[0]={}")
+ self.add_field('provides_5_0_volts', bool(bitfields.getbits(self.characteristics1, 1)), "characteristics1[1]={}")
+ self.add_field('provides_3_3_volts', bool(bitfields.getbits(self.characteristics1, 2)), "characteristics1[2]={}")
+ self.add_field('shared_slot', bool(bitfields.getbits(self.characteristics1, 3)), "characteristics1[3]={}")
+ self.add_field('supports_pc_card_16', bool(bitfields.getbits(self.characteristics1, 4)), "characteristics1[4]={}")
+ self.add_field('supports_cardbus', bool(bitfields.getbits(self.characteristics1, 5)), "characteristics1[5]={}")
+ self.add_field('supports_zoom_video', bool(bitfields.getbits(self.characteristics1, 6)), "characteristics1[6]={}")
+ self.add_field('supports_modem_ring_resume', bool(bitfields.getbits(self.characteristics1, 7)), "characteristics1[7]={}")
+ if self.length > 0x0C:
+ self.add_field('characteristics2', u.unpack_one('B'))
+ self.add_field('supports_PME', bool(bitfields.getbits(self.characteristics2, 0)), "characteristics2[0]={}")
+ self.add_field('supports_hot_plug', bool(bitfields.getbits(self.characteristics2, 1)), "characteristics2[1]={}")
+ self.add_field('supports_smbus', bool(bitfields.getbits(self.characteristics2, 2)), "characteristics2[2]={}")
+ if self.length > 0x0D:
+ self.add_field('segment_group_number', u.unpack_one('<H'))
+ self.add_field('bus_number', u.unpack_one('B'))
+ self.add_field('device_function_number', u.unpack_one('B'))
+ self.add_field('device_number', bitfields.getbits(self.device_function_number, 7, 3), "device_function_number[7:3]={}")
+ self.add_field('function_number', bitfields.getbits(self.device_function_number, 2, 0), "device_function_number[2:0]={}")
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemSlots"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OnBoardDevicesInformation(SmbiosBaseStructure):
+ smbios_structure_type = 10
+
+ def __init__(self, u, sm):
+ super(OnBoardDevicesInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('device_type', u.unpack_one("B"))
+ self.add_field('device_enabled', bool(bitfields.getbits(self.device_type, 7)), "device_type[7]={}")
+ _device_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Video',
+ 0x04: 'SCSI Controller',
+ 0x05: 'Ethernet',
+ 0x06: 'Token Ring',
+ 0x07: 'Sound',
+ 0x08: 'PATA Controller',
+ 0x09: 'SATA Controller',
+ 0x0A: 'SAS Controller'
+ }
+ self.add_field('type_of_device', bitfields.getbits(self.device_type, 6, 0), unpack.format_table("device_type[6:0]={}", _device_types))
+ self.add_field('description_string', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing OnBoardDevicesInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OEMStrings(SmbiosBaseStructure):
+ smbios_structure_type = 11
+
+ def __init__(self, u, sm):
+ super(OEMStrings, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('count', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing OEMStrings"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemConfigOptions(SmbiosBaseStructure):
+ smbios_structure_type = 12
+
+ def __init__(self, u, sm):
+ super(SystemConfigOptions, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('count', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemConfigOptions"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class BIOSLanguageInformation(SmbiosBaseStructure):
+ smbios_structure_type = 13
+
+ def __init__(self, u, sm):
+ super(BIOSLanguageInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('installable_languages', u.unpack_one("B"))
+ if self.length > 0x05:
+ self.add_field('flags', u.unpack_one('B'))
+ self.add_field('abbreviated_format', bool(bitfields.getbits(self.flags, 0)), "flags[0]={}")
+ if self.length > 0x6:
+ u.skip(15)
+ self.add_field('current_language', u.unpack_one('B'), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing BIOSLanguageInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class GroupAssociations(SmbiosBaseStructure):
+ smbios_structure_type = 14
+
+ def __init__(self, u, sm):
+ super(GroupAssociations, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('group_name', u.unpack_one("B"), self.fmtstr)
+ self.add_field('item_type', u.unpack_one('B'))
+ self.add_field('item_handle', u.unpack_one('<H'))
+ except:
+ self.decodeFailure = True
+ print "Error parsing GroupAssociations"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemEventLog(SmbiosBaseStructure):
+ smbios_structure_type = 15
+
+ def __init__(self, u, sm):
+ super(SystemEventLog, self).__init__(u, sm)
+ u = self.u
+ try:
+ self.add_field('log_area_length', u.unpack_one("<H"))
+ self.add_field('log_header_start_offset', u.unpack_one('<H'))
+ self.add_field('log_data_start_offset', u.unpack_one('<H'))
+ _access_method = {
+ 0x00: 'Indexed I/O: 1 8-bit index port, 1 8-bit data port',
+ 0x01: 'Indexed I/O: 2 8-bit index ports, 1 8-bit data port',
+ 0x02: 'Indexed I/O: 1 16-bit index port, 1 8-bit data port',
+ 0x03: 'Memory-mapped physical 32-bit address',
+ 0x04: 'Available through General-Purpose NonVolatile Data functions',
+ xrange(0x05, 0x07F): 'Available for future assignment',
+ xrange(0x80, 0xFF): 'BIOS Vendor/OEM-specific'
+ }
+ self.add_field('access_method', u.unpack_one('B'), unpack.format_table("{}", _access_method))
+ self.add_field('log_status', u.unpack_one('B'))
+ self.add_field('log_area_full', bool(bitfields.getbits(self.log_status, 1)), "log_status[1]={}")
+ self.add_field('log_area_valid', bool(bitfields.getbits(self.log_status, 0)), "log_status[0]={}")
+ self.add_field('log_change_token', u.unpack_one('<I'))
+ self.add_field('access_method_address', u.unpack_one('<I'))
+ if self.length > 0x14:
+ _log_header_formats = {
+ 0: 'No header',
+ 1: 'Type 1 log header',
+ xrange(2, 0x7f): 'Available for future assignment',
+ xrange(0x80, 0xff): 'BIOS vendor or OEM-specific format'
+ }
+ self.add_field('log_header_format', u.unpack_one("B"), unpack.format_table("{}", _log_header_formats))
+ if self.length > 0x15:
+ self.add_field('num_supported_log_type_descriptors', u.unpack_one('B'))
+ if self.length > 0x16:
+ self.add_field('length_log_type_descriptor', u.unpack_one('B'))
+ if self.length != (0x17 + (self.num_supported_log_type_descriptors * self.length_log_type_descriptor)):
+ print "Error: structure length ({}) != 0x17 + (num_supported_log_type_descriptors ({}) * length_log_type_descriptor({}))".format(self.length, self.num_supported_log_type_descriptors, self.length_log_type_descriptor)
+ print "structure length = {}".format(self.length)
+ print "num_supported_log_type_descriptors = {}".format(self.num_supported_log_type_descriptors)
+ print "length_log_type_descriptor = {}".format(self.length_log_type_descriptor)
+ self.decodeFailure = True
+ self.add_field('descriptors', tuple(EventLogDescriptor.unpack(u) for i in range(self.num_supported_log_type_descriptors)), unpack.format_each("\n{!r}"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemEventLog"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class EventLogDescriptor(unpack.Struct):
+ @staticmethod
+ def _unpack(u):
+ _event_log_type_descriptors = {
+ 0x00: 'Reserved',
+ 0x01: 'Single-bit ECC memory error',
+ 0x02: 'Multi-bit ECC memory error',
+ 0x03: 'Parity memory error',
+ 0x04: 'Bus time-out',
+ 0x05: 'I/O Channel Check',
+ 0x06: 'Software NMI',
+ 0x07: 'POST Memory Resize',
+ 0x08: 'POST Error',
+ 0x09: 'PCI Parity Error',
+ 0x0A: 'PCI System Error',
+ 0x0B: 'CPU Failure',
+ 0x0C: 'EISA FailSafe Timer time-out',
+ 0x0D: 'Correctable memory log disabled',
+ 0x0E: 'Logging disabled for a specific Event Type - too many errors of the same type received in a short amount of time',
+ 0x0F: 'Reserved',
+ 0x10: 'System Limit Exceeded',
+ 0x11: 'Asynchronous hardware timer expired and issued a system reset',
+ 0x12: 'System configuration information',
+ 0x13: 'Hard-disk information',
+ 0x14: 'System reconfigured',
+ 0x15: 'Uncorrectable CPU-complex error',
+ 0x16: 'Log Area Reset/Cleared',
+ 0x17: 'System boot',
+ xrange(0x18, 0x7F): 'Unused, available for assignment',
+ xrange(0x80, 0xFE): 'Availalbe for system- and OEM-specific assignments',
+ 0xFF: 'End of log'
+ }
+ yield 'log_type', u.unpack_one('B'), unpack.format_table("{}", _event_log_type_descriptors)
+ _event_log_format = {
+ 0x00: 'None',
+ 0x01: 'Handle',
+ 0x02: 'Multiple-Event',
+ 0x03: 'Multiple-Event Handle',
+ 0x04: 'POST Results Bitmap',
+ 0x05: 'System Management Type',
+ 0x06: 'Multiple-Event System Management Type',
+ xrange(0x80, 0xFF): 'OEM assigned'
+ }
+ yield 'variable_data_format_type', u.unpack_one('B'), unpack.format_table("{}", _event_log_format)
+
+class PhysicalMemoryArray(SmbiosBaseStructure):
+ smbios_structure_type = 16
+
+ def __init__(self, u, sm):
+ super(PhysicalMemoryArray, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _location_field = {
+ 0x01: "Other",
+ 0x02: "Unknown",
+ 0x03: "System board or motherboard",
+ 0x04: "ISA add-on card",
+ 0x05: "EISA add-on card",
+ 0x06: "PCI add-on card",
+ 0x07: "MCA add-on card",
+ 0x08: "PCMCIA add-on card",
+ 0x09: "Proprietary add-on card",
+ 0x0A: "NuBus",
+ 0xA0: "PC-98/C20 add-on card",
+ 0xA1: "PC-98/C24 add-on card",
+ 0xA2: "PC-98/E add-on card",
+ 0xA3: "PC-98/Local bus add-on card"
+ }
+ self.add_field('location', u.unpack_one("B"), unpack.format_table("{}", _location_field))
+ if self.length > 0x05:
+ _use = {
+ 0x01: "Other",
+ 0x02: "Unknown",
+ 0x03: "System memory",
+ 0x04: "Video memory",
+ 0x05: "Flash memory",
+ 0x06: "Non-volatile RAM",
+ 0x07: "Cache memory"
+ }
+ self.add_field('use', u.unpack_one('B'), unpack.format_table("{}", _use))
+ if self.length > 0x06:
+ _error_correction = {
+ 0x01: "Other",
+ 0x02: "Unknown",
+ 0x03: "None",
+ 0x04: "Parity",
+ 0x05: "Single-bit ECC",
+ 0x06: "Multi-bit ECC",
+ 0x07: "CRC"
+ }
+ self.add_field('memory_error_correction', u.unpack_one('B'), unpack.format_table("{}", _error_correction))
+ if self.length > 0x07:
+ self.add_field('maximum_capacity', u.unpack_one('<I'))
+ if self.length > 0x0B:
+ self.add_field('memory_error_information_handle', u.unpack_one('<H'))
+ if self.length > 0x0D:
+ self.add_field('num_memory_devices', u.unpack_one('<H'))
+ if self.length > 0x0F:
+ self.add_field('extended_maximum_capacity', u.unpack_one('<Q'))
+ except:
+ self.decodeFailure = True
+ print "Error parsing PhysicalMemoryArray"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryDevice(SmbiosBaseStructure):
+ smbios_structure_type = 17
+
+ def __init__(self, u, sm):
+ super(MemoryDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('physical_memory_array_handle', u.unpack_one("<H"))
+ if self.length > 0x6:
+ self.add_field('memory_error_information_handle', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('total_width', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('data_width', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('size', u.unpack_one("<H"))
+ if self.length > 0xE:
+ _form_factors = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'SIMM',
+ 0x04: 'SIP',
+ 0x05: 'Chip',
+ 0x06: 'DIP',
+ 0x07: 'ZIP',
+ 0x08: 'Proprietary Card',
+ 0x09: 'DIMM',
+ 0x0A: 'TSOP',
+ 0x0B: 'Row of chips',
+ 0x0C: 'RIMM',
+ 0x0D: 'SODIMM',
+ 0x0E: 'SRIMM',
+ 0x0F: 'FB-DIMM'
+ }
+ self.add_field('form_factor', u.unpack_one("B"), unpack.format_table("{}", _form_factors))
+ if self.length > 0xF:
+ self.add_field('device_set', u.unpack_one("B"))
+ if self.length > 0x10:
+ self.add_field('device_locator', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x11:
+ self.add_field('bank_locator', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x12:
+ _memory_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'DRAM',
+ 0x04: 'EDRAM',
+ 0x05: 'VRAM',
+ 0x06: 'SRAM',
+ 0x07: 'RAM',
+ 0x08: 'ROM',
+ 0x09: 'FLASH',
+ 0x0A: 'EEPROM',
+ 0x0B: 'FEPROM',
+ 0x0C: 'EPROM',
+ 0x0D: 'CDRAM',
+ 0x0E: '3DRAM',
+ 0x0F: 'SDRAM',
+ 0x10: 'SGRAM',
+ 0x11: 'RDRAM',
+ 0x12: 'DDR',
+ 0x13: 'DDR2',
+ 0x14: 'DDR2 FB-DIMM',
+ xrange(0x15, 0x17): 'Reserved',
+ 0x18: 'DDR3',
+ 0x19: 'FBD2'
+ }
+ self.add_field('memory_type', u.unpack_one("B"), unpack.format_table("{}", _memory_types))
+ if self.length > 0x13:
+ self.add_field('type_detail', u.unpack_one('<H'))
+ if self.length > 0x15:
+ self.add_field('speed', u.unpack_one("<H"))
+ if self.length > 0x17:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x18:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x19:
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x1A:
+ self.add_field('part_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x1B:
+ self.add_field('attributes', u.unpack_one("B"))
+ self.add_field('rank', bitfields.getbits(self.attributes, 3, 0), "attributes[3:0]={}")
+ if self.length > 0x1C:
+ if self.size == 0x7FFF:
+ self.add_field('extended_size', u.unpack_one('<I'))
+ self.add_field('mem_size', bitfields.getbits(self.type_detail, 30, 0), "type_detail[30:0]={}")
+ else:
+ u.skip(4)
+ if self.length > 0x20:
+ self.add_field('configured_memory_clock_speed', u.unpack_one("<H"))
+ if self.length > 0x22:
+ self.add_field('minimum_voltage', u.unpack_one("<H"))
+ if self.length > 0x24:
+ self.add_field('maximum_voltage', u.unpack_one("<H"))
+ if self.length > 0x26:
+ self.add_field('configured_voltage', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryErrorInfo32Bit(SmbiosBaseStructure):
+ smbios_structure_type = 18
+
+ def __init__(self, u, sm):
+ super(MemoryErrorInfo32Bit, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _error_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'OK',
+ 0x04: 'Bad read',
+ 0x05: 'Parity error',
+ 0x06: 'Single-bit error',
+ 0x07: 'Double-bit error',
+ 0x08: 'Multi-bit error',
+ 0x09: 'Nibble error',
+ 0x0A: 'Checksum error',
+ 0x0B: 'CRC error',
+ 0x0C: 'Corrected single-bit error',
+ 0x0D: 'Corrected error',
+ 0x0E: 'Uncorrectable error'
+ }
+ self.add_field('error_type', u.unpack_one("B"), unpack.format_table("{}", _error_types))
+ if self.length > 0x5:
+ _error_granularity_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Device level',
+ 0x04: 'Memory partition level'
+ }
+ self.add_field('error_granularity', u.unpack_one("B"), unpack.format_table("{}", _error_granularity_field))
+ if self.length > 0x6:
+ _error_operation_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Read',
+ 0x04: 'Write',
+ 0x05: 'Partial write'
+ }
+ self.add_field('error_operation', u.unpack_one("B"), unpack.format_table("{}", _error_operation_field))
+ if self.length > 0x7:
+ self.add_field('vendor_syndrome', u.unpack_one("<I"))
+ if self.length > 0xB:
+ self.add_field('memory_array_error_address', u.unpack_one("<I"))
+ if self.length > 0xF:
+ self.add_field('device_error_address', u.unpack_one("<I"))
+ if self.length > 0x13:
+ self.add_field('error_resolution', u.unpack_one("<I"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryErrorInfo32Bit"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryArrayMappedAddress(SmbiosBaseStructure):
+ smbios_structure_type = 19
+
+ def __init__(self, u, sm):
+ super(MemoryArrayMappedAddress, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('starting_address', u.unpack_one("<I"))
+ # if FFFF FFFF: address stored in Extended Starting Address
+ if self.length > 0x8:
+ self.add_field('ending_address', u.unpack_one("<I"))
+ if self.length > 0xC:
+ self.add_field('memory_array_handle', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('partition_width', u.unpack_one("B"))
+ if self.length > 0xF:
+ # valid if starting_address = FFFF FFFF
+ if self.starting_address == 0xFFFFFFFF:
+ self.add_field('extended_starting_address', u.unpack_one("<Q"))
+ if self.length > 0x17:
+ self.add_field('extended_ending_address', u.unpack_one("<Q"))
+ else:
+ u.skip(16)
+
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryArrayMappedAddress"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryDeviceMappedAddress(SmbiosBaseStructure):
+ smbios_structure_type = 20
+
+ def __init__(self, u, sm):
+ super(MemoryDeviceMappedAddress, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('starting_address', u.unpack_one("<I"))
+ # if FFFF FFFF: address stored in Extended Starting Address
+ if self.length > 0x8:
+ self.add_field('ending_address', u.unpack_one("<I"))
+ if self.length > 0xC:
+ self.add_field('memory_device_handle', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('memory_array_mapped_address_handle', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('partition_row_position', u.unpack_one("B"))
+ if self.length > 0x11:
+ self.add_field('interleave_position', u.unpack_one("B"))
+ if self.length > 0x12:
+ self.add_field('interleave_data_depth', u.unpack_one("B"))
+ if self.length > 0x13:
+ # valid if starting_address = FFFF FFFF
+ if self.starting_address == 0xFFFFFFFF:
+ self.add_field('extended_starting_address', u.unpack_one("<Q"))
+ if self.length > 0x1B:
+ self.add_field('extended_ending_address', u.unpack_one("<Q"))
+ else:
+ u.skip(16)
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryDeviceMappedAddress"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class BuiltInPointingDevice(SmbiosBaseStructure):
+ smbios_structure_type = 21
+
+ def __init__(self, u, sm):
+ super(BuiltInPointingDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _pointing_device_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Mouse',
+ 0x04: 'Track Ball',
+ 0x05: 'Track Point',
+ 0x06: 'Glide Point',
+ 0x07: 'Touch Pad',
+ 0x08: 'Touch Screen',
+ 0x09: 'Optical Sensor'
+ }
+ self.add_field('pointing_device_type', u.unpack_one("B"), unpack.format_table("{}", _pointing_device_types))
+ if self.length > 0x5:
+ _interfaces = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Serial',
+ 0x04: 'PS/2',
+ 0x05: 'Infared',
+ 0x06: 'HP-HIL',
+ 0x07: 'Bus mouse',
+ 0x08: 'ADB (Apple Desktop Bus)',
+ 0x09: 'Bus mouse DB-9',
+ 0x0A: 'Bus mouse micro-DIN',
+ 0x0B: 'USB'
+ }
+ self.add_field('interface', u.unpack_one("B"), unpack.format_table("{}", _interfaces))
+ if self.length > 0x6:
+ self.add_field('num_buttons', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing BuiltInPointingDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class PortableBattery(SmbiosBaseStructure):
+ smbios_structure_type = 22
+
+ def __init__(self, u, sm):
+ super(PortableBattery, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('location', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x6:
+ self.add_field('manufacturer_date', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x7:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x8:
+ self.add_field('device_name', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x9:
+ _device_chemistry = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Lead Acid',
+ 0x04: 'Nickel Cadmium',
+ 0x05: 'Nickel metal hydride',
+ 0x06: 'Lithium-ion',
+ 0x07: 'Zinc air',
+ 0x08: 'Lithium Polymer'
+ }
+ self.add_field('device_chemistry', u.unpack_one("B"), unpack.format_table("{}", _device_chemistry))
+ if self.length > 0xA:
+ self.add_field('design_capacity', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('design_voltage', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('sbds_version_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xF:
+ self.add_field('max_error_battery_data', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x10:
+ if self.serial_number == 0:
+ self.add_field('sbds_serial_number', u.unpack_one("<H"))
+ else:
+ u.skip(2)
+ if self.length > 0x12:
+ if self.manufacturer_date == 0:
+ self.add_field('sbds_manufacture_date', u.unpack_one("<H"))
+ self.add_field('year_biased_by_1980', bitfields.getbits(self.sbds_manufacture_date, 15, 9), "sbds_manufacture_date[15:9]={}")
+ self.add_field('month', bitfields.getbits(self.sbds_manufacture_date, 8, 5), "sbds_manufacture_date[8:5]={}")
+ self.add_field('date', bitfields.getbits(self.sbds_manufacture_date, 4, 0), "sbds_manufacture_date[4:0]={}")
+ else:
+ u.skip(2)
+ if self.length > 0x14:
+ if self.device_chemistry == 0x02:
+ self.add_field('sbds_device_chemistry', u.unpack_one("B"), self.fmtstr)
+ else:
+ u.skip(1)
+ if self.length > 0x15:
+ self.add_field('design_capacity_multiplier', u.unpack_one("B"))
+ if self.length > 0x16:
+ self.add_field('oem_specific', u.unpack_one("<I"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing PortableBattery"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemReset(SmbiosBaseStructure):
+ smbios_structure_type = 23
+
+ def __init__(self, u, sm):
+ super(SystemReset, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('capabilities', u.unpack_one("B"))
+ self.add_field('contains_watchdog_timer', bool(bitfields.getbits(self.capabilities, 5)), "capabilities[5]={}")
+ _boot_option = {
+ 0b00: 'Reserved, do not use',
+ 0b01: 'Operating System',
+ 0b10: 'System utilities',
+ 0b11: 'Do not reboot'
+ }
+ self.add_field('boot_option_on_limit', bitfields.getbits(self.capabilities, 4, 3), unpack.format_table("capabilities[4:3]={}", _boot_option))
+ self.add_field('boot_option_after_watchdog_reset', bitfields.getbits(self.capabilities, 2, 1), unpack.format_table("capabilities[2:1]={}", _boot_option))
+ self.add_field('system_reset_enabled_by_user', bool(bitfields.getbits(self.capabilities, 0)), "capabilities[0]={}")
+ if self.length > 0x5:
+ self.add_field('reset_count', u.unpack_one("<H"))
+ if self.length > 0x5:
+ self.add_field('reset_limit', u.unpack_one("<H"))
+ if self.length > 0x9:
+ self.add_field('timer_interval', u.unpack_one("<H"))
+ if self.length > 0xB:
+ self.add_field('timeout', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemReset"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class HardwareSecurity(SmbiosBaseStructure):
+ smbios_structure_type = 24
+
+ def __init__(self, u, sm):
+ super(HardwareSecurity, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('hardware_security_settings', u.unpack_one("B"))
+ _status = {
+ 0x00: 'Disabled',
+ 0x01: 'Enabled',
+ 0x02: 'Not Implemented',
+ 0x03: 'Unknown'
+ }
+ self.add_field('power_on_password_status', bitfields.getbits(self.hardware_security_settings, 7, 6), unpack.format_table("hardware_security_settings[7:6]={}", _status))
+ self.add_field('keyboard_password_status', bitfields.getbits(self.hardware_security_settings, 5, 4), unpack.format_table("hardware_security_settings[5:4]={}", _status))
+ self.add_field('admin_password_status', bitfields.getbits(self.hardware_security_settings, 3, 2), unpack.format_table("hardware_security_settings0[3:2]={}", _status))
+ self.add_field('front_panel_reset_status', bitfields.getbits(self.hardware_security_settings, 1, 0), unpack.format_table("hardware_security_settings[1:0]={}", _status))
+ except:
+ self.decodeFailure = True
+ print "Error parsing HardwareSecurity"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemPowerControls(SmbiosBaseStructure):
+ smbios_structure_type = 25
+
+ def __init__(self, u, sm):
+ super(SystemPowerControls, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('next_scheduled_poweron_month', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_day_of_month', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_hour', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_minute', u.unpack_one("B"))
+ self.add_field('next_scheduled_poweron_second', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemPowerControls"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class VoltageProbe(SmbiosBaseStructure):
+ smbios_structure_type = 26
+
+ def __init__(self, u, sm):
+ super(VoltageProbe, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('location_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _location = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Processor',
+ 0b00100: 'Disk',
+ 0b00101: 'Peripheral Bay',
+ 0b00110: 'System Management Module',
+ 0b00111: 'Motherboard',
+ 0b01000: 'Memory Module',
+ 0b01001: 'Processor Module',
+ 0b01010: 'Power Unit',
+ 0b01011: 'Add-in Card'
+ }
+ self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status))
+ self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location))
+ if self.length > 0x6:
+ self.add_field('max_value', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('min_value', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('resolution', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('tolerance', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('accuracy', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('oem_defined', u.unpack_one("<I"))
+ if self.length > 0x14:
+ self.add_field('nominal_value', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing VoltageProbe"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class CoolingDevice(SmbiosBaseStructure):
+ smbios_structure_type = 27
+
+ def __init__(self, u, sm):
+ super(CoolingDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('temperature_probe_handle', u.unpack_one("<H"))
+ if self.length > 0x6:
+ self.add_field('device_type_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _type = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Fan',
+ 0b00100: 'Centrifugal Blower',
+ 0b00101: 'Chip Fan',
+ 0b00110: 'Cabinet Fan',
+ 0b00111: 'Power Supply Fan',
+ 0b01000: 'Heat Pipe',
+ 0b01001: 'Integrated Refrigeration',
+ 0b10000: 'Active Cooling',
+ 0b10001: 'Passive Cooling'
+ }
+ self.add_field('status', bitfields.getbits(self.device_type_and_status, 7, 5), unpack.format_table("device_type_and_status[7:5]={}", _status))
+ self.add_field('device_type', bitfields.getbits(self.device_type_and_status, 4, 0), unpack.format_table("device_type_and_status[4:0]={}", _type))
+ if self.length > 0x7:
+ self.add_field('cooling_unit_group', u.unpack_one("B"))
+ if self.length > 0x8:
+ self.add_field('OEM_defined', u.unpack_one("<I"))
+ if self.length > 0xC:
+ self.add_field('nominal_speed', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing CoolingDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class TemperatureProbe(SmbiosBaseStructure):
+ smbios_structure_type = 28
+
+ def __init__(self, u, sm):
+ super(TemperatureProbe, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('location_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _location = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Processor',
+ 0b00100: 'Disk',
+ 0b00101: 'Peripheral Bay',
+ 0b00110: 'System Management Module',
+ 0b00111: 'Motherboard',
+ 0b01000: 'Memory Module',
+ 0b01001: 'Processor Module',
+ 0b01010: 'Power Unit',
+ 0b01011: 'Add-in Card',
+ 0b01100: 'Front Panel Board',
+ 0b01101: 'Back Panel Board',
+ 0b01110: 'Power System Board',
+ 0b01111: 'Drive Back Plane'
+ }
+ self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status))
+ self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location))
+ if self.length > 0x6:
+ self.add_field('maximum_value', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('minimum_value', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('resolution', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('tolerance', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('accuracy', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('OEM_defined', u.unpack_one("<I"))
+ if self.length > 0x14:
+ self.add_field('nominal_value', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing TemperatureProbe"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ElectricalCurrentProbe(SmbiosBaseStructure):
+ smbios_structure_type = 29
+
+ def __init__(self, u, sm):
+ super(ElectricalCurrentProbe, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('location_and_status', u.unpack_one("B"))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical',
+ 0b110: 'Non-recoverable'
+ }
+ _location = {
+ 0b00001: 'Other',
+ 0b00010: 'Unknown',
+ 0b00011: 'Processor',
+ 0b00100: 'Disk',
+ 0b00101: 'Peripheral Bay',
+ 0b00110: 'System Management Module',
+ 0b00111: 'Motherboard',
+ 0b01000: 'Memory Module',
+ 0b01001: 'Processor Module',
+ 0b01010: 'Power Unit',
+ 0b01011: 'Add-in Card',
+ 0b01100: 'Front Panel Board',
+ 0b01101: 'Back Panel Board',
+ 0b01110: 'Power System Board',
+ 0b01111: 'Drive Back Plane'
+ }
+ self.add_field('status', bitfields.getbits(self.location_and_status, 7, 5), unpack.format_table("location_and_status[7:5]={}", _status))
+ self.add_field('location', bitfields.getbits(self.location_and_status, 4, 0), unpack.format_table("location_and_status[4:0]={}", _location))
+ if self.length > 0x6:
+ self.add_field('maximum_value', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('minimum_value', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('resolution', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('tolerance', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('accuracy', u.unpack_one("<H"))
+ if self.length > 0x10:
+ self.add_field('OEM_defined', u.unpack_one("<I"))
+ if self.length > 0x14:
+ self.add_field('nominal_value', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ElectricalCurrentProbe"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OutOfBandRemoteAccess(SmbiosBaseStructure):
+ smbios_structure_type = 30
+
+ def __init__(self, u, sm):
+ super(OutOfBandRemoteAccess, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('manufacturer_name', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('connections', u.unpack_one("B"))
+ self.add_field('outbound_connection_enabled', bool(bitfields.getbits(self.connections, 1)), "connections[1]={}")
+ self.add_field('inbound_connection_enabled', bool(bitfields.getbits(self.connections, 0)), "connections[0]={}")
+ except:
+ self.decodeFailure = True
+ print "Error parsing OutOfBandRemoteAccess"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class BootIntegrityServicesEntryPoint(SmbiosBaseStructure):
+ smbios_structure_type = 31
+
+class SystemBootInformation(SmbiosBaseStructure):
+ smbios_structure_type = 32
+
+ def __init__(self, u, sm):
+ super(SystemBootInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0xA:
+ u.skip(6)
+ _boot_status = {
+ 0: 'No errors detected',
+ 1: 'No bootable media',
+ 2: '"normal" operating system failed to load',
+ 3: 'Firmware-detected hardware failure, including "unknown" failure types',
+ 4: 'Operating system-detected hardware failure',
+ 5: 'User-requested boot, usually through a keystroke',
+ 6: 'System security violation',
+ 7: 'Previously-requested image',
+ 8: 'System watchdog timer expired, causing the system to reboot',
+ xrange(9,127): 'Reserved for future assignment',
+ xrange(128, 191): 'Vendor/OEM-specific implementations',
+ xrange(192, 255): 'Product-specific implementations'
+ }
+ self.add_field('boot_status', u.unpack_one("B"), unpack.format_table("{}", _boot_status))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemBootInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryErrorInfo64Bit(SmbiosBaseStructure):
+ smbios_structure_type = 33
+
+ def __init__(self, u, sm):
+ super(MemoryErrorInfo64Bit, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _error_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'OK',
+ 0x04: 'Bad read',
+ 0x05: 'Parity error',
+ 0x06: 'Single-bit error',
+ 0x07: 'Double-bit error',
+ 0x08: 'Multi-bit error',
+ 0x09: 'Nibble error',
+ 0x0A: 'Checksum error',
+ 0x0B: 'CRC error',
+ 0x0C: 'Corrected single-bit error',
+ 0x0D: 'Corrected error',
+ 0x0E: 'Uncorrectable error'
+ }
+ self.add_field('error_type', u.unpack_one("B"), unpack.format_table("{}", _error_types))
+ if self.length > 0x5:
+ _error_granularity_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Device level',
+ 0x04: 'Memory partition level'
+ }
+ self.add_field('error_granularity', u.unpack_one("B"), unpack.format_table("{}", _error_granularity_field))
+ if self.length > 0x6:
+ _error_operation_field = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Read',
+ 0x04: 'Write',
+ 0x05: 'Partial write'
+ }
+ self.add_field('error_operation', u.unpack_one("B"), unpack.format_table("{}", _error_operation_field))
+ if self.length > 0x7:
+ self.add_field('vendor_syndrome', u.unpack_one("<I"))
+ if self.length > 0xB:
+ self.add_field('memory_array_error_address', u.unpack_one("<Q"))
+ if self.length > 0xF:
+ self.add_field('device_error_address', u.unpack_one("<Q"))
+ if self.length > 0x13:
+ self.add_field('error_resolution', u.unpack_one("<Q"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryErrorInfo64Bit"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementDevice(SmbiosBaseStructure):
+ smbios_structure_type = 34
+
+ def __init__(self, u, sm):
+ super(ManagementDevice, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ _type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'National Semiconductor LM75',
+ 0x04: 'National Semiconductor LM78',
+ 0x05: 'National Semiconductor LM79',
+ 0x06: 'National Semiconductor LM80',
+ 0x07: 'National Semiconductor LM81',
+ 0x08: 'Analog Devices ADM9240',
+ 0x09: 'Dallas Semiconductor DS1780',
+ 0x0A: 'Maxim 1617',
+ 0x0B: 'Genesys GL518SM',
+ 0x0C: 'Winbond W83781D',
+ 0x0D: 'Holtek HT82H791'
+ }
+ self.add_field('device_type', u.unpack_one("B"), unpack.format_table("{}", _type))
+ if self.length > 0x6:
+ self.add_field('address', u.unpack_one("<I"))
+ if self.length > 0xA:
+ _address_type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'I/O Port',
+ 0x04: 'Memory',
+ 0x05: 'SM Bus'
+ }
+ self.add_field('address_type', u.unpack_one("B"), unpack.format_table("{}", _address_type))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementDevice"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementDeviceComponent(SmbiosBaseStructure):
+ smbios_structure_type = 35
+
+ def __init__(self, u, sm):
+ super(ManagementDeviceComponent, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('description', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('management_device_handle', u.unpack_one("<H"))
+ if self.length > 0x7:
+ self.add_field('component_handle', u.unpack_one("<H"))
+ if self.length > 0x9:
+ self.add_field('threshold_handle', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementDeviceComponent"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementDeviceThresholdData(SmbiosBaseStructure):
+ smbios_structure_type = 36
+
+ def __init__(self, u, sm):
+ super(ManagementDeviceThresholdData, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('lower_threshold_noncritical', u.unpack_one("<H"))
+ if self.length > 0x6:
+ self.add_field('upper_threshold_noncritical', u.unpack_one("<H"))
+ if self.length > 0x8:
+ self.add_field('lower_threshold_critical', u.unpack_one("<H"))
+ if self.length > 0xA:
+ self.add_field('upper_threshold_critical', u.unpack_one("<H"))
+ if self.length > 0xC:
+ self.add_field('lower_threshold_nonrecoverable', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('upper_threshold_nonrecoverable', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementDeviceThresholdData"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class MemoryChannel(SmbiosBaseStructure):
+ smbios_structure_type = 37
+
+ def __init__(self, u, sm):
+ super(MemoryChannel, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _channel_type = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'RamBus',
+ 0x04: 'SyncLink'
+ }
+ self.add_field('channel_type', u.unpack_one("B"), unpack.format_table("{}", _channel_type))
+ if self.length > 0x6:
+ self.add_field('max_channel_load', u.unpack_one("B"))
+ if self.length > 0x8:
+ self.add_field('memory_device_count', u.unpack_one("B"))
+ if self.length > 0xA:
+ self.add_field('memory_device_load', u.unpack_one("B"))
+ if self.length > 0xC:
+ self.add_field('memory_device_handle', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing MemoryChannel"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class IPMIDeviceInformation(SmbiosBaseStructure):
+ smbios_structure_type = 38
+
+ def __init__(self, u, sm):
+ super(IPMIDeviceInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ _interface_type = {
+ 0x00: 'Unknown',
+ 0x01: 'KCS: Keyboard Controller Style',
+ 0x02: 'SMIC: Server Management Interface Chip',
+ 0x03: 'BT: Block Transfer',
+ xrange(0x04, 0xFF): 'Reserved'
+ }
+ self.add_field('interface_type', u.unpack_one("B"), unpack.format_table("{}", _interface_type))
+ self.add_field('ipmi_specification_revision', u.unpack_one("B"))
+ self.add_field('msd_revision', bitfields.getbits(self.ipmi_specification_revision, 7, 4), "ipmi_specification_revision[7:4]={}")
+ self.add_field('lsd_revision', bitfields.getbits(self.ipmi_specification_revision, 3, 0), "ipmi_specification_revision[3:0]={}")
+
+ self.add_field('i2c_slave_address', u.unpack_one("B"))
+ self.add_field('nv_storage_device_address', u.unpack_one("B"))
+ self.add_field('base_address', u.unpack_one("<Q"))
+ # if lsb is 1, address is in IO space. otherwise, memory-mapped
+ self.add_field('base_address_modifier_interrupt_info', u.unpack_one("B"))
+ _reg_spacing = {
+ 0b00: 'Interface registers are on successive byte boundaries',
+ 0b01: 'Interface registers are on 32-bit boundaries',
+ 0b10: 'Interface registers are on 16-byte boundaries',
+ 0b11: 'Reserved'
+ }
+ self.add_field('register_spacing', bitfields.getbits(self.base_address_modifier_interrupt_info, 7, 6), unpack.format_table("base_address_modifier_interrupt_info[7:6]={}", _reg_spacing))
+ self.add_field('ls_bit_for_addresses', bitfields.getbits(self.base_address_modifier_interrupt_info, 4), "base_address_modifier_interrupt_info[4]={}")
+ self.add_field('interrupt_info_specified', bool(bitfields.getbits(self.base_address_modifier_interrupt_info, 3)), "base_address_modifier_interrupt_info[3]={}")
+ _polarity = {
+ 0: 'active low',
+ 1: 'active high'
+ }
+ self.add_field('interrupt_polarity', bitfields.getbits(self.base_address_modifier_interrupt_info, 1), unpack.format_table("base_address_modifier_interrupt_info[1]={}", _polarity))
+ _interrupt_trigger = {
+ 0: 'edge',
+ 1: 'level'
+ }
+ self.add_field('interrupt_trigger_mode', bitfields.getbits(self.base_address_modifier_interrupt_info, 0), unpack.format_table("base_address_modifier_interrupt_info[0]={}", _interrupt_trigger))
+ self.add_field('interrupt_number', u.unpack_one("B"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing IPMIDeviceInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class SystemPowerSupply(SmbiosBaseStructure):
+ smbios_structure_type = 39
+
+ def __init__(self, u, sm):
+ super(SystemPowerSupply, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('power_unit_group', u.unpack_one("B"))
+ if self.length > 0x5:
+ self.add_field('location', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x6:
+ self.add_field('device_name', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x7:
+ self.add_field('manufacturer', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x8:
+ self.add_field('serial_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x9:
+ self.add_field('asset_tag', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xA:
+ self.add_field('model_part_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xB:
+ self.add_field('revision_level', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xC:
+ self.add_field('max_power_capacity', u.unpack_one("<H"))
+ if self.length > 0xE:
+ self.add_field('power_supply_characteristics', u.unpack_one("<H"))
+ _dmtf_power_supply_type = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'Linear',
+ 0b100: 'Switching',
+ 0b101: 'Battery',
+ 0b110: 'UPS',
+ 0b111: 'Converter',
+ 0b1000: 'Regulator',
+ xrange(0b1001, 0b1111): 'Reserved'
+ }
+ self.add_field('dmtf_power_supply_type', bitfields.getbits(self.power_supply_characteristics, 13, 10), unpack.format_table("power_supply_characteristics[13:10]={}", _dmtf_power_supply_type))
+ _status = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'OK',
+ 0b100: 'Non-critical',
+ 0b101: 'Critical; power supply has failed and has been taken off-line'
+ }
+ self.add_field('status', bitfields.getbits(self.power_supply_characteristics, 9, 7), unpack.format_table("power_supply_characteristics[9:7]={}", _status))
+ _dmtf_input_voltage_range_switching = {
+ 0b001: 'Other',
+ 0b010: 'Unknown',
+ 0b011: 'Manual',
+ 0b100: 'Auto-switch',
+ 0b101: 'Wide range',
+ 0b110: 'Not applicable',
+ xrange(0b0111, 0b1111): 'Reserved'
+ }
+ self.add_field('dmtf_input_voltage_range_switching', bitfields.getbits(self.power_supply_characteristics, 6, 3), unpack.format_table("power_supply_characteristics[6:3]={}", _dmtf_input_voltage_range_switching))
+ self.add_field('power_supply_unplugged', bool(bitfields.getbits(self.power_supply_characteristics, 2)), "power_supply_characteristics[2]={}")
+ self.add_field('power_supply_present', bool(bitfields.getbits(self.power_supply_characteristics, 1)), "power_supply_characteristics[1]={}")
+ self.add_field('power_supply_hot_replaceable', bool(bitfields.getbits(self.power_supply_characteristics, 0)), "power_supply_characteristics[0]={}")
+ if self.length > 0x10:
+ self.add_field('input_voltage_probe_handle', u.unpack_one("<H"))
+ if self.length > 0x12:
+ self.add_field('cooling_device_handle', u.unpack_one("<H"))
+ if self.length > 0x14:
+ self.add_field('input_current_probe_handle', u.unpack_one("<H"))
+ except:
+ self.decodeFailure = True
+ print "Error parsing SystemPowerSupply"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class AdditionalInformation(SmbiosBaseStructure):
+ smbios_structure_type = 40
+
+ def __init__(self, u, sm):
+ super(AdditionalInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('num_additional_information_entries', u.unpack_one("B"))
+ if self.length > 0x5:
+ self.add_field('additional_information_entry_length', u.unpack_one("B"))
+ self.add_field('referenced_handle', u.unpack_one("<H"))
+ self.add_field('referenced_offset', u.unpack_one("B"))
+ self.add_field('string', u.unpack_one("B"), self.fmtstr)
+ self.add_field('value', u.unpack_rest())
+ except:
+ self.decodeFailure = True
+ print "Error parsing AdditionalInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class OnboardDevicesExtendedInformation(SmbiosBaseStructure):
+ smbios_structure_type = 41
+
+ def __init__(self, u, sm):
+ super(OnboardDevicesExtendedInformation, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ self.add_field('reference_designation', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0x5:
+ self.add_field('device_type', u.unpack_one("B"))
+ self.add_field('device_enabled', bool(bitfields.getbits(self.device_type, 7)), "device_type[7]={}")
+ _device_types = {
+ 0x01: 'Other',
+ 0x02: 'Unknown',
+ 0x03: 'Video',
+ 0x04: 'SCSI Controller',
+ 0x05: 'Ethernet',
+ 0x06: 'Token Ring',
+ 0x07: 'Sound',
+ 0x08: 'PATA Controller',
+ 0x09: 'SATA Controller',
+ 0x0A: 'SAS Controller'
+ }
+ self.add_field('type_of_device', bitfields.getbits(self.device_type, 6, 0), unpack.format_table("device_type[6:0]={}", _device_types))
+ if self.length > 0x6:
+ self.add_field('device_type_instance', u.unpack_one("B"))
+ if self.length > 0x7:
+ self.add_field('segment_group_number', u.unpack_one("<H"))
+ if self.length > 0x9:
+ self.add_field('bus_number', u.unpack_one("B"), self.fmtstr)
+ if self.length > 0xA:
+ self.add_field('device_and_function_number', u.unpack_one("B"))
+ self.add_field('device_number', bitfields.getbits(self.device_type, 7, 3), "device_and_function_number[7:3]={}")
+ self.add_field('function_number', bitfields.getbits(self.device_type, 2, 0), "device_and_function_number[2:0]={}")
+ except:
+ self.decodeFailure = True
+ print "Error parsing OnboardDevicesExtendedInformation"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class ManagementControllerHostInterface(SmbiosBaseStructure):
+ smbios_structure_type = 42
+
+ def __init__(self, u, sm):
+ super(ManagementControllerHostInterface, self).__init__(u, sm)
+ u = self.u
+ try:
+ if self.length > 0x4:
+ _interface_types = {
+ 0x00: 'Reserved',
+ 0x01: 'Reserved',
+ 0x02: 'KCS: Keyboard Controller Style',
+ 0x03: '8250 UART Register Compatible',
+ 0x04: '16450 UART Register Compatible',
+ 0x05: '16550/16550A UART Register Compatible',
+ 0x06: '16650/16650A UART Register Compatible',
+ 0x07: '16750/16750A UART Register Compatible',
+ 0x08: '16850/16850A UART Register Compatible',
+ 0xF0: 'OEM'
+ }
+ self.add_field('interface_type', u.unpack_one("B"), unpack.format_table("{}", _interface_types))
+ if self.length > 0x5:
+ self.add_field('mc_host_interface_data', u.unpack_rest(), self.fmtstr)
+ except:
+ self.decodeFailure = True
+ print "Error parsing ManagementControllerHostInterface"
+ import traceback
+ traceback.print_exc()
+ self.fini()
+
+class Inactive(SmbiosBaseStructure):
+ smbios_structure_type = 126
+
+ def __init__(self, u, sm):
+ super(Inactive, self).__init__(u, sm)
+ self.fini()
+
+class EndOfTable(SmbiosBaseStructure):
+ smbios_structure_type = 127
+
+ def __init__(self, u, sm):
+ super(EndOfTable, self).__init__(u, sm)
+ self.fini()
+
+class SmbiosStructureUnknown(SmbiosBaseStructure):
+ smbios_structure_type = None
+
+ def __init__(self, u, sm):
+ super(SmbiosStructureUnknown, self).__init__(u, sm)
+ self.fini()
+
+_smbios_structures = [
+ BIOSInformation,
+ SystemInformation,
+ BaseboardInformation,
+ SystemEnclosure,
+ ProcessorInformation,
+ MemoryControllerInformation,
+ MemoryModuleInformation,
+ CacheInformation,
+ PortConnectorInfo,
+ SystemSlots,
+ OnBoardDevicesInformation,
+ OEMStrings,
+ SystemConfigOptions,
+ BIOSLanguageInformation,
+ GroupAssociations,
+ SystemEventLog,
+ PhysicalMemoryArray,
+ MemoryDevice,
+ MemoryErrorInfo32Bit,
+ MemoryArrayMappedAddress,
+ MemoryDeviceMappedAddress,
+ BuiltInPointingDevice,
+ PortableBattery,
+ SystemReset,
+ HardwareSecurity,
+ SystemPowerControls,
+ VoltageProbe,
+ CoolingDevice,
+ TemperatureProbe,
+ ElectricalCurrentProbe,
+ OutOfBandRemoteAccess,
+ BootIntegrityServicesEntryPoint,
+ SystemBootInformation,
+ MemoryErrorInfo64Bit,
+ ManagementDevice,
+ ManagementDeviceComponent,
+ ManagementDeviceThresholdData,
+ MemoryChannel,
+ IPMIDeviceInformation,
+ SystemPowerSupply,
+ AdditionalInformation,
+ OnboardDevicesExtendedInformation,
+ ManagementControllerHostInterface,
+ Inactive,
+ EndOfTable,
+ SmbiosStructureUnknown, # Must always come last
+]
+
+def log_smbios_info():
+ with redirect.logonly():
+ try:
+ sm = SMBIOS()
+ print
+ if sm is None:
+ print "No SMBIOS structures found"
+ return
+ output = {}
+ known_types = (0, 1)
+ for sm_struct in sm.structures:
+ if sm_struct.type in known_types:
+ output.setdefault(sm_struct.type, []).append(sm_struct)
+ if len(output) == len(known_types):
+ break
+
+ print "SMBIOS information:"
+ for key in sorted(known_types):
+ for s in output.get(key, ["No structure of type {} found".format(key)]):
+ print ttypager._wrap("{}: {}".format(key, s))
+ except:
+ print "Error parsing SMBIOS information:"
+ import traceback
+ traceback.print_exc()
+
+def dump_raw():
+ try:
+ sm = SMBIOS()
+ if sm:
+ s = "SMBIOS -- Raw bytes and structure decode.\n\n"
+
+ s += str(sm.header) + '\n'
+ s += bits.dumpmem(sm._header_memory) + '\n'
+
+ s += "Raw bytes for the SMBIOS structures\n"
+ s += bits.dumpmem(sm._structure_memory) + '\n'
+
+ for sm_struct in sm.structures:
+ s += str(sm_struct) + '\n'
+ s += bits.dumpmem(sm_struct.raw_data)
+
+ s += "Strings:\n"
+ for n in range(1, len(getattr(sm_struct, "strings", [])) + 1):
+ s += str(sm_struct.fmtstr(n)) + '\n'
+ s += bits.dumpmem(sm_struct.raw_strings) + '\n'
+ else:
+ s = "No SMBIOS structures found"
+ ttypager.ttypager_wrap(s, indent=False)
+ except:
+ print "Error parsing SMBIOS information:"
+ import traceback
+ traceback.print_exc()
+
+def dump():
+ try:
+ sm = SMBIOS()
+ if sm:
+ s = str(sm)
+ else:
+ s = "No SMBIOS structures found"
+ ttypager.ttypager_wrap(s, indent=False)
+ except:
+ print "Error parsing SMBIOS information:"
+ import traceback
+ traceback.print_exc()
+
+def annex_a_conformance():
+ try:
+ sm = SMBIOS()
+
+ # check: 1. The table anchor string "_SM_" is present in the address range 0xF0000 to 0xFFFFF on a 16-byte bound
+
+ def table_entry_point_verification():
+ ''' Verify table entry-point'''
+ if (sm.header.length < 0x1F):
+ print "Failure: Table entry-point - The entry-point Length must be at least 0x1F"
+ if sm.header.checksum != 0:
+ print "Failure: Table entry-point - The entry-point checksum must evaluate to 0"
+ if ((sm.header.major_version < 2) and (sm.header.minor_version < 4)):
+ print "Failure: Table entry-point - SMBIOS version must be at least 2.4"
+ if (sm.header.intermediate_anchor_string == '_DMI_'):
+ print "Failure: Table entry-point - The Intermediate Anchor String must be '_DMI_'"
+ if (sm.header.intermediate_checksum != 0):
+ print "Failure: Table entry-point - The Intermediate checksum must evaluate to 0"
+
+ #check: 3. The structure-table is traversable and conforms to the entry-point specifications:
+
+ def req_structures():
+ '''Checks for required structures and corresponding data'''
+ types_present = [sm.structures[x].smbios_structure_type for x in range(len(sm.structures))]
+ required = [0, 1, 4, 7, 9, 16, 17, 19, 31, 32]
+ for s in required:
+ if s not in set(types_present):
+ print "Failure: Type {} required but not found".format(s)
+
+ else:
+ if s == 0:
+ if types_present.count(s) > 1:
+ print "Failure: Type {} - One and only one structure of this type must be present.".format(s)
+ if sm.structure_type(s).length < 0x18:
+ print "Failure: Type {} - The structure Length field must be at least 0x18".format(s)
+ if sm.structure_type(s).version is None:
+ print "Failure: Type {} - BIOS Version string must be present and non-null.".format(s)
+ if sm.structure_type(s).release_date is None:
+ print "Failure: Type {} - BIOS Release Date string must be present, non-null, and include a 4-digit year".format(s)
+ if bitfields.getbits(sm.structure_type(s).characteristics, 3, 0) != 0 or bitfields.getbits(sm.structure_type(s).characteristics, 31, 4) == 0:
+ print "Failure: Type {} - BIOS Characteristics: bits 3:0 must all be 0, and at least one of bits 31:4 must be set to 1.".format(s)
+ elif s == 1:
+ if types_present.count(s) > 1:
+ print "Failure: Type {} - One and only one structure of this type must be present.".format(s)
+ if sm.structure_type(s).length < 0x1B:
+ print "Failure: Type {} - The structure Length field must be at least 0x1B".format(s)
+ if sm.structure_type(s).manufacturer == None:
+ print "Failure: Type {} - Manufacturer string must be present and non-null.".format(s)
+ if sm.structure_type(s).product_name == None:
+ print "Failure: Type {} - Product Name string must be present and non-null".format(s)
+ if sm.structure_type(s).uuid == '00000000 00000000' and sm.structure_type(s).uuid == 'FFFFFFFF FFFFFFFF':
+ print "Failure: Type {} - UUID field must be neither 00000000 00000000 nor FFFFFFFF FFFFFFFF.".format(s)
+ if sm.structure_type(s).wakeup_type == 00 and sm.structure_type(s).wakeup_type == 0x02:
+ print "Failure: Type {} - Wake-up Type field must be neither 00h (Reserved) nor 02h (Unknown).".format(s)
+ # continue for remaining required types
+
+ # check remaining conformance guidelines
+
+ table_entry_point_verification()
+ req_structures()
+ except:
+ print "Error checking ANNEX A conformance guidelines"
+ import traceback
+ traceback.print_exc()
diff --git a/tests/avocado/acpi-bits/bits-tests/testacpi.py2 b/tests/avocado/acpi-bits/bits-tests/testacpi.py2
new file mode 100644
index 0000000000..dbc150076e
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/testacpi.py2
@@ -0,0 +1,283 @@
+# Copyright (c) 2015, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests for ACPI"""
+
+import acpi
+import bits
+import bits.mwait
+import struct
+import testutil
+import testsuite
+import time
+
+def register_tests():
+ testsuite.add_test("ACPI _MAT (Multiple APIC Table Entry) under Processor objects", test_mat, submenu="ACPI Tests")
+# testsuite.add_test("ACPI _PSS (Pstate) table conformance tests", test_pss, submenu="ACPI Tests")
+# testsuite.add_test("ACPI _PSS (Pstate) runtime tests", test_pstates, submenu="ACPI Tests")
+ testsuite.add_test("ACPI DSDT (Differentiated System Description Table)", test_dsdt, submenu="ACPI Tests")
+ testsuite.add_test("ACPI FACP (Fixed ACPI Description Table)", test_facp, submenu="ACPI Tests")
+ testsuite.add_test("ACPI HPET (High Precision Event Timer Table)", test_hpet, submenu="ACPI Tests")
+ testsuite.add_test("ACPI MADT (Multiple APIC Description Table)", test_apic, submenu="ACPI Tests")
+ testsuite.add_test("ACPI MPST (Memory Power State Table)", test_mpst, submenu="ACPI Tests")
+ testsuite.add_test("ACPI RSDP (Root System Description Pointer Structure)", test_rsdp, submenu="ACPI Tests")
+ testsuite.add_test("ACPI XSDT (Extended System Description Table)", test_xsdt, submenu="ACPI Tests")
+
+def test_mat():
+ cpupaths = acpi.get_cpupaths()
+ apic = acpi.parse_apic()
+ procid_apicid = apic.procid_apicid
+ uid_x2apicid = apic.uid_x2apicid
+ for cpupath in cpupaths:
+ # Find the ProcId defined by the processor object
+ processor = acpi.evaluate(cpupath)
+ # Find the UID defined by the processor object's _UID method
+ uid = acpi.evaluate(cpupath + "._UID")
+ mat_buffer = acpi.evaluate(cpupath + "._MAT")
+ if mat_buffer is None:
+ continue
+ # Process each _MAT subtable
+ mat = acpi._MAT(mat_buffer)
+ for index, subtable in enumerate(mat):
+ if subtable.subtype == acpi.MADT_TYPE_LOCAL_APIC:
+ if subtable.flags.bits.enabled:
+ testsuite.test("{} Processor declaration ProcId = _MAT ProcId".format(cpupath), processor.ProcId == subtable.proc_id)
+ testsuite.print_detail("{} ProcId ({:#02x}) != _MAT ProcId ({:#02x})".format(cpupath, processor.ProcId, subtable.proc_id))
+ testsuite.print_detail("Processor Declaration: {}".format(processor))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+ if testsuite.test("{} with local APIC in _MAT has local APIC in MADT".format(cpupath), processor.ProcId in procid_apicid):
+ testsuite.test("{} ApicId derived using Processor declaration ProcId = _MAT ApicId".format(cpupath), procid_apicid[processor.ProcId] == subtable.apic_id)
+ testsuite.print_detail("{} ApicId derived from MADT ({:#02x}) != _MAT ApicId ({:#02x})".format(cpupath, procid_apicid[processor.ProcId], subtable.apic_id))
+ testsuite.print_detail("Processor Declaration: {}".format(processor))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+ if subtable.subtype == acpi.MADT_TYPE_LOCAL_X2APIC:
+ if subtable.flags.bits.enabled:
+ if testsuite.test("{} with x2Apic in _MAT has _UID".format(cpupath), uid is not None):
+ testsuite.test("{}._UID = _MAT UID".format(cpupath), uid == subtable.uid)
+ testsuite.print_detail("{}._UID ({:#x}) != _MAT UID ({:#x})".format(cpupath, uid, subtable.uid))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+ if testsuite.test("{} with _MAT x2Apic has x2Apic in MADT".format(cpupath), subtable.uid in uid_x2apicid):
+ testsuite.test("{} x2ApicId derived from MADT using UID = _MAT x2ApicId".format(cpupath), uid_x2apicid[subtable.uid] == subtable.x2apicid)
+ testsuite.print_detail("{} x2ApicId derived from MADT ({:#02x}) != _MAT x2ApicId ({:#02x})".format(cpupath, uid_x2apicid[subtable.uid], subtable.x2apicid))
+ testsuite.print_detail("_MAT entry[{}]: {}".format(index, subtable))
+
+def test_pss():
+ uniques = acpi.parse_cpu_method("_PSS")
+ # We special-case None here to avoid a double-failure for CPUs without a _PSS
+ testsuite.test("_PSS must be identical for all CPUs", len(uniques) <= 1 or (len(uniques) == 2 and None in uniques))
+ for pss, cpupaths in uniques.iteritems():
+ if not testsuite.test("_PSS must exist", pss is not None):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail('No _PSS exists')
+ continue
+
+ if not testsuite.test("_PSS must not be empty", pss.pstates):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail('_PSS is empty')
+ continue
+
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ for index, pstate in enumerate(pss.pstates):
+ testsuite.print_detail("P[{}]: {}".format(index, pstate))
+
+ testsuite.test("_PSS must contain at most 16 Pstates", len(pss.pstates) <= 16)
+ testsuite.test("_PSS must have no duplicate Pstates", len(pss.pstates) == len(set(pss.pstates)))
+
+ frequencies = [p.core_frequency for p in pss.pstates]
+ testsuite.test("_PSS must list Pstates in descending order of frequency", frequencies == sorted(frequencies, reverse=True))
+
+ testsuite.test("_PSS must have Pstates with no duplicate frequencies", len(frequencies) == len(set(frequencies)))
+
+ dissipations = [p.power for p in pss.pstates]
+ testsuite.test("_PSS must list Pstates in descending order of power dissipation", dissipations == sorted(dissipations, reverse=True))
+
+def test_pstates():
+ """Execute and verify frequency for each Pstate in the _PSS"""
+ IA32_PERF_CTL = 0x199
+ with bits.mwait.use_hint(), bits.preserve_msr(IA32_PERF_CTL):
+ cpupath_procid = acpi.find_procid()
+ cpupath_uid = acpi.find_uid()
+ apic = acpi.parse_apic()
+ procid_apicid = apic.procid_apicid
+ uid_x2apicid = apic.uid_x2apicid
+ def cpupath_apicid(cpupath):
+ if procid_apicid is not None:
+ procid = cpupath_procid.get(cpupath, None)
+ if procid is not None:
+ apicid = procid_apicid.get(procid, None)
+ if apicid is not None:
+ return apicid
+ if uid_x2apicid is not None:
+ uid = cpupath_uid.get(cpupath, None)
+ if uid is not None:
+ apicid = uid_x2apicid.get(uid, None)
+ if apicid is not None:
+ return apicid
+ return bits.cpus()[0]
+
+ bclk = testutil.adjust_to_nearest(bits.bclk(), 100.0/12) * 1000000
+
+ uniques = acpi.parse_cpu_method("_PSS")
+ for pss, cpupaths in uniques.iteritems():
+ if not testsuite.test("_PSS must exist", pss is not None):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail('No _PSS exists')
+ continue
+
+ for n, pstate in enumerate(pss.pstates):
+ for cpupath in cpupaths:
+ apicid = cpupath_apicid(cpupath)
+ if apicid is None:
+ print 'Failed to find apicid for cpupath {}'.format(cpupath)
+ continue
+ bits.wrmsr(apicid, IA32_PERF_CTL, pstate.control)
+
+ # Detecting Turbo frequency requires at least 2 pstates
+ # since turbo frequency = max non-turbo frequency + 1
+ turbo = False
+ if len(pss.pstates) >= 2:
+ turbo = (n == 0 and pstate.core_frequency == (pss.pstates[1].core_frequency + 1))
+ if turbo:
+ # Needs to busywait, not sleep
+ start = time.time()
+ while (time.time() - start < 2):
+ pass
+
+ for duration in (0.1, 1.0):
+ frequency_data = bits.cpu_frequency(duration)
+ # Abort the test if no cpu frequency is not available
+ if frequency_data is None:
+ continue
+ aperf = frequency_data[1]
+ aperf = testutil.adjust_to_nearest(aperf, bclk/2)
+ aperf = int(aperf / 1000000)
+ if turbo:
+ if aperf >= pstate.core_frequency:
+ break
+ else:
+ if aperf == pstate.core_frequency:
+ break
+
+ if turbo:
+ testsuite.test("P{}: Turbo measured frequency {} >= expected {} MHz".format(n, aperf, pstate.core_frequency), aperf >= pstate.core_frequency)
+ else:
+ testsuite.test("P{}: measured frequency {} MHz == expected {} MHz".format(n, aperf, pstate.core_frequency), aperf == pstate.core_frequency)
+
+def test_psd_thread_scope():
+ uniques = acpi.parse_cpu_method("_PSD")
+ if not testsuite.test("_PSD (P-State Dependency) must exist for each processor", None not in uniques):
+ testsuite.print_detail(acpi.factor_commonprefix(uniques[None]))
+ testsuite.print_detail('No _PSD exists')
+ return
+ unique_num_dependencies = {}
+ unique_num_entries = {}
+ unique_revision = {}
+ unique_domain = {}
+ unique_coordination_type = {}
+ unique_num_processors = {}
+ for value, cpupaths in uniques.iteritems():
+ unique_num_dependencies.setdefault(len(value.dependencies), []).extend(cpupaths)
+ unique_num_entries.setdefault(value.dependencies[0].num_entries, []).extend(cpupaths)
+ unique_revision.setdefault(value.dependencies[0].revision, []).extend(cpupaths)
+ unique_domain.setdefault(value.dependencies[0].domain, []).extend(cpupaths)
+ unique_coordination_type.setdefault(value.dependencies[0].coordination_type, []).extend(cpupaths)
+ unique_num_processors.setdefault(value.dependencies[0].num_processors, []).extend(cpupaths)
+ def detail(d, fmt):
+ for value, cpupaths in sorted(d.iteritems(), key=(lambda (k,v): v)):
+ testsuite.print_detail(acpi.factor_commonprefix(cpupaths))
+ testsuite.print_detail(fmt.format(value))
+
+ testsuite.test('Dependency count for each processor must be 1', unique_num_dependencies.keys() == [1])
+ detail(unique_num_dependencies, 'Dependency count for each processor = {} (Expected 1)')
+ testsuite.test('_PSD.num_entries must be 5', unique_num_entries.keys() == [5])
+ detail(unique_num_entries, 'num_entries = {} (Expected 5)')
+ testsuite.test('_PSD.revision must be 0', unique_revision.keys() == [0])
+ detail(unique_revision, 'revision = {}')
+ testsuite.test('_PSD.coordination_type must be 0xFE (HW_ALL)', unique_coordination_type.keys() == [0xfe])
+ detail(unique_coordination_type, 'coordination_type = {:#x} (Expected 0xFE HW_ALL)')
+ testsuite.test('_PSD.domain must be unique (thread-scoped) for each processor', len(unique_domain) == len(acpi.get_cpupaths()))
+ detail(unique_domain, 'domain = {:#x} (Expected a unique value for each processor)')
+ testsuite.test('_PSD.num_processors must be 1', unique_num_processors.keys() == [1])
+ detail(unique_num_processors, 'num_processors = {} (Expected 1)')
+
+def test_table_checksum(data):
+ csum = sum(ord(c) for c in data) % 0x100
+ testsuite.test('ACPI table cumulative checksum must equal 0', csum == 0)
+ testsuite.print_detail("Cumulative checksum = {} (Expected 0)".format(csum))
+
+def test_apic():
+ data = acpi.get_table("APIC")
+ if data is None:
+ return
+ test_table_checksum(data)
+ apic = acpi.parse_apic()
+
+def test_dsdt():
+ data = acpi.get_table("DSDT")
+ if data is None:
+ return
+ test_table_checksum(data)
+
+def test_facp():
+ data = acpi.get_table("FACP")
+ if data is None:
+ return
+ test_table_checksum(data)
+ facp = acpi.parse_facp()
+
+def test_hpet():
+ data = acpi.get_table("HPET")
+ if data is None:
+ return
+ test_table_checksum(data)
+ hpet = acpi.parse_hpet()
+
+def test_mpst():
+ data = acpi.get_table("MPST")
+ if data is None:
+ return
+ test_table_checksum(data)
+ mpst = acpi.MPST(data)
+
+def test_rsdp():
+ data = acpi.get_table("RSD PTR ")
+ if data is None:
+ return
+
+ # Checksum the first 20 bytes per ACPI 1.0
+ csum = sum(ord(c) for c in data[:20]) % 0x100
+ testsuite.test('ACPI 1.0 table first 20 bytes cummulative checksum must equal 0', csum == 0)
+ testsuite.print_detail("Cummulative checksum = {} (Expected 0)".format(csum))
+
+ test_table_checksum(data)
+ rsdp = acpi.parse_rsdp()
+
+def test_xsdt():
+ data = acpi.get_table("XSDT")
+ if data is None:
+ return
+ test_table_checksum(data)
+ xsdt = acpi.parse_xsdt()
diff --git a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 b/tests/avocado/acpi-bits/bits-tests/testcpuid.py2
new file mode 100644
index 0000000000..ac55d912e1
--- /dev/null
+++ b/tests/avocado/acpi-bits/bits-tests/testcpuid.py2
@@ -0,0 +1,83 @@
+# Copyright (c) 2012, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests and helpers for CPUID."""
+
+import bits
+import testsuite
+import testutil
+
+def cpuid_helper(function, index=None, shift=0, mask=~0, eax_mask=~0, ebx_mask=~0, ecx_mask=~0, edx_mask=~0):
+ if index is None:
+ index = 0
+ indexdesc = ""
+ else:
+ indexdesc = " index {0:#x}".format(index)
+
+ def find_mask(m):
+ if m == ~0:
+ return mask
+ return m
+ masks = map(find_mask, [eax_mask, ebx_mask, ecx_mask, edx_mask])
+
+ uniques = {}
+ for cpu in bits.cpus():
+ regs = bits.cpuid_result(*[(r >> shift) & m for r, m in zip(bits.cpuid(cpu, function, index), masks)])
+ uniques.setdefault(regs, []).append(cpu)
+
+ desc = ["CPUID function {:#x}{}".format(function, indexdesc)]
+
+ if shift != 0:
+ desc.append("Register values have been shifted by {}".format(shift))
+ if mask != ~0 or eax_mask != ~0 or ebx_mask != ~0 or ecx_mask != ~0 or edx_mask != ~0:
+ desc.append("Register values have been masked:")
+ shifted_masks = bits.cpuid_result(*[m << shift for m in masks])
+ desc.append("Masks: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**shifted_masks._asdict()))
+
+ if len(uniques) > 1:
+ regvalues = zip(*uniques.iterkeys())
+ common_masks = bits.cpuid_result(*map(testutil.find_common_mask, regvalues))
+ common_values = bits.cpuid_result(*[v[0] & m for v, m in zip(regvalues, common_masks)])
+ desc.append('Register values are not unique across all logical processors')
+ desc.append("Common bits: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**common_values._asdict()))
+ desc.append("Mask of common bits: {eax:#010x} {ebx:#010x} {ecx:#010x} {edx:#010x}".format(**common_masks._asdict()))
+
+ for regs in sorted(uniques.iterkeys()):
+ cpus = uniques[regs]
+ desc.append("Register value: eax={eax:#010x} ebx={ebx:#010x} ecx={ecx:#010x} edx={edx:#010x}".format(**regs._asdict()))
+ desc.append("On {0} CPUs: {1}".format(len(cpus), testutil.apicid_list(cpus)))
+
+ return uniques, desc
+
+def test_cpuid_consistency(text, function, index=None, shift=0, mask=~0, eax_mask=~0, ebx_mask=~0, ecx_mask=~0, edx_mask=~0):
+ uniques, desc = cpuid_helper(function, index, shift, mask, eax_mask, ebx_mask, ecx_mask, edx_mask)
+ desc[0] += " Consistency Check"
+ if text:
+ desc.insert(0, text)
+ status = testsuite.test(desc[0], len(uniques) == 1)
+ for line in desc[1:]:
+ testsuite.print_detail(line)
+ return status
diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT
index da2a3e5c05..f1cf7fab34 100644
--- a/tests/data/acpi/pc/DSDT
+++ b/tests/data/acpi/pc/DSDT
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/pc/DSDT.acpierst
index abcd6d9d30..5cb477625e 100644
--- a/tests/data/acpi/pc/DSDT.acpierst
+++ b/tests/data/acpi/pc/DSDT.acpierst
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/pc/DSDT.acpihmat
index 884d4871a2..76e8bef36f 100644
--- a/tests/data/acpi/pc/DSDT.acpihmat
+++ b/tests/data/acpi/pc/DSDT.acpihmat
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge
index 31a79aa476..c94c1b54b3 100644
--- a/tests/data/acpi/pc/DSDT.bridge
+++ b/tests/data/acpi/pc/DSDT.bridge
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.cphp b/tests/data/acpi/pc/DSDT.cphp
index 8b0cae4dbf..eb3da0e232 100644
--- a/tests/data/acpi/pc/DSDT.cphp
+++ b/tests/data/acpi/pc/DSDT.cphp
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm
index 38865fb667..6553e4c605 100644
--- a/tests/data/acpi/pc/DSDT.dimmpxm
+++ b/tests/data/acpi/pc/DSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/pc/DSDT.hpbridge
index abcd6d9d30..5cb477625e 100644
--- a/tests/data/acpi/pc/DSDT.hpbridge
+++ b/tests/data/acpi/pc/DSDT.hpbridge
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.hpbrroot b/tests/data/acpi/pc/DSDT.hpbrroot
index dd2c8c0c8c..ff04ad360b 100644
--- a/tests/data/acpi/pc/DSDT.hpbrroot
+++ b/tests/data/acpi/pc/DSDT.hpbrroot
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs
index e819ce6946..83eec58a52 100644
--- a/tests/data/acpi/pc/DSDT.ipmikcs
+++ b/tests/data/acpi/pc/DSDT.ipmikcs
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp
index 03a9decdc1..9e2201d170 100644
--- a/tests/data/acpi/pc/DSDT.memhp
+++ b/tests/data/acpi/pc/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/pc/DSDT.nohpet
index b413d9f31d..c969e0dae4 100644
--- a/tests/data/acpi/pc/DSDT.nohpet
+++ b/tests/data/acpi/pc/DSDT.nohpet
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.numamem b/tests/data/acpi/pc/DSDT.numamem
index 9e701b2983..1cecaa64e9 100644
--- a/tests/data/acpi/pc/DSDT.numamem
+++ b/tests/data/acpi/pc/DSDT.numamem
Binary files differ
diff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/pc/DSDT.roothp
index 8c3956c9ec..f57a14cd5c 100644
--- a/tests/data/acpi/pc/DSDT.roothp
+++ b/tests/data/acpi/pc/DSDT.roothp
Binary files differ
diff --git a/tests/data/acpi/pc/SSDT.dimmpxm b/tests/data/acpi/pc/SSDT.dimmpxm
index ac55387d57..70f133412f 100644
--- a/tests/data/acpi/pc/SSDT.dimmpxm
+++ b/tests/data/acpi/pc/SSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/q35/APIC.acpihmat-noinitiator b/tests/data/acpi/q35/APIC.acpihmat-noinitiator
new file mode 100644
index 0000000000..d904d4a70d
--- /dev/null
+++ b/tests/data/acpi/q35/APIC.acpihmat-noinitiator
Binary files differ
diff --git a/tests/data/acpi/q35/APIC.core-count2 b/tests/data/acpi/q35/APIC.core-count2
new file mode 100644
index 0000000000..a255082ef5
--- /dev/null
+++ b/tests/data/acpi/q35/APIC.core-count2
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT
index 3870958969..8e989819a5 100644
--- a/tests/data/acpi/q35/DSDT
+++ b/tests/data/acpi/q35/DSDT
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/q35/DSDT.acpierst
index c9c18fa4e4..03745d78de 100644
--- a/tests/data/acpi/q35/DSDT.acpierst
+++ b/tests/data/acpi/q35/DSDT.acpierst
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat
index a32e90b5d9..3ad9ba3c98 100644
--- a/tests/data/acpi/q35/DSDT.acpihmat
+++ b/tests/data/acpi/q35/DSDT.acpihmat
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator
new file mode 100644
index 0000000000..8efa1c5ded
--- /dev/null
+++ b/tests/data/acpi/q35/DSDT.acpihmat-noinitiator
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.applesmc b/tests/data/acpi/q35/DSDT.applesmc
index 5507b6b8f5..5f01572dc2 100644
--- a/tests/data/acpi/q35/DSDT.applesmc
+++ b/tests/data/acpi/q35/DSDT.applesmc
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge
index a42eb674fa..97141f9db2 100644
--- a/tests/data/acpi/q35/DSDT.bridge
+++ b/tests/data/acpi/q35/DSDT.bridge
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/q35/DSDT.core-count2
new file mode 100644
index 0000000000..ca309f6569
--- /dev/null
+++ b/tests/data/acpi/q35/DSDT.core-count2
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp
index 2d8cb603c9..622e8e5f37 100644
--- a/tests/data/acpi/q35/DSDT.cphp
+++ b/tests/data/acpi/q35/DSDT.cphp
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.cxl b/tests/data/acpi/q35/DSDT.cxl
index 20d0fb64ea..cecc1caaab 100644
--- a/tests/data/acpi/q35/DSDT.cxl
+++ b/tests/data/acpi/q35/DSDT.cxl
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm
index b23339513a..e5be00b4fa 100644
--- a/tests/data/acpi/q35/DSDT.dimmpxm
+++ b/tests/data/acpi/q35/DSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt
index 8af2695ede..c4f8212c63 100644
--- a/tests/data/acpi/q35/DSDT.ipmibt
+++ b/tests/data/acpi/q35/DSDT.ipmibt
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.ipmismbus b/tests/data/acpi/q35/DSDT.ipmismbus
index 479df48cd3..05fb38820f 100644
--- a/tests/data/acpi/q35/DSDT.ipmismbus
+++ b/tests/data/acpi/q35/DSDT.ipmismbus
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/q35/DSDT.ivrs
index c9c18fa4e4..03745d78de 100644
--- a/tests/data/acpi/q35/DSDT.ivrs
+++ b/tests/data/acpi/q35/DSDT.ivrs
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp
index a5730b8ab8..2a4635d48c 100644
--- a/tests/data/acpi/q35/DSDT.memhp
+++ b/tests/data/acpi/q35/DSDT.memhp
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64
index a4293c20fe..0491761dc7 100644
--- a/tests/data/acpi/q35/DSDT.mmio64
+++ b/tests/data/acpi/q35/DSDT.mmio64
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge
index 88bf47ab18..485f571afd 100644
--- a/tests/data/acpi/q35/DSDT.multi-bridge
+++ b/tests/data/acpi/q35/DSDT.multi-bridge
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.nohpet b/tests/data/acpi/q35/DSDT.nohpet
index 6feed2ee10..9c2ec9f2c9 100644
--- a/tests/data/acpi/q35/DSDT.nohpet
+++ b/tests/data/acpi/q35/DSDT.nohpet
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem
index 414b8af67b..2302de88e9 100644
--- a/tests/data/acpi/q35/DSDT.numamem
+++ b/tests/data/acpi/q35/DSDT.numamem
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.pvpanic-isa b/tests/data/acpi/q35/DSDT.pvpanic-isa
index 7277a01050..5e4b51d33b 100644
--- a/tests/data/acpi/q35/DSDT.pvpanic-isa
+++ b/tests/data/acpi/q35/DSDT.pvpanic-isa
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/q35/DSDT.tis.tpm12
index 253a66e658..1723fca446 100644
--- a/tests/data/acpi/q35/DSDT.tis.tpm12
+++ b/tests/data/acpi/q35/DSDT.tis.tpm12
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/q35/DSDT.tis.tpm2
index 76bd4661e6..1a0d6284da 100644
--- a/tests/data/acpi/q35/DSDT.tis.tpm2
+++ b/tests/data/acpi/q35/DSDT.tis.tpm2
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/q35/DSDT.viot
index 3f14b57f07..6927d1cc96 100644
--- a/tests/data/acpi/q35/DSDT.viot
+++ b/tests/data/acpi/q35/DSDT.viot
Binary files differ
diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/q35/DSDT.xapic
index baa88f6f21..4a8a4af625 100644
--- a/tests/data/acpi/q35/DSDT.xapic
+++ b/tests/data/acpi/q35/DSDT.xapic
Binary files differ
diff --git a/tests/data/acpi/q35/FACP.core-count2 b/tests/data/acpi/q35/FACP.core-count2
new file mode 100644
index 0000000000..31fa5dd19c
--- /dev/null
+++ b/tests/data/acpi/q35/FACP.core-count2
Binary files differ
diff --git a/tests/data/acpi/q35/HMAT.acpihmat-noinitiator b/tests/data/acpi/q35/HMAT.acpihmat-noinitiator
new file mode 100644
index 0000000000..6494d11b9f
--- /dev/null
+++ b/tests/data/acpi/q35/HMAT.acpihmat-noinitiator
Binary files differ
diff --git a/tests/data/acpi/q35/SRAT.acpihmat-noinitiator b/tests/data/acpi/q35/SRAT.acpihmat-noinitiator
new file mode 100644
index 0000000000..a11d3119ab
--- /dev/null
+++ b/tests/data/acpi/q35/SRAT.acpihmat-noinitiator
Binary files differ
diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm
index 98e6f0e3f3..9ea4e0d0ce 100644
--- a/tests/data/acpi/q35/SSDT.dimmpxm
+++ b/tests/data/acpi/q35/SSDT.dimmpxm
Binary files differ
diff --git a/tests/data/acpi/virt/APIC b/tests/data/acpi/virt/APIC
index 023f15f12e..179d274770 100644
--- a/tests/data/acpi/virt/APIC
+++ b/tests/data/acpi/virt/APIC
Binary files differ
diff --git a/tests/data/acpi/virt/APIC.acpihmatvirt b/tests/data/acpi/virt/APIC.acpihmatvirt
new file mode 100644
index 0000000000..68200204c6
--- /dev/null
+++ b/tests/data/acpi/virt/APIC.acpihmatvirt
Binary files differ
diff --git a/tests/data/acpi/virt/APIC.memhp b/tests/data/acpi/virt/APIC.memhp
index 023f15f12e..179d274770 100644
--- a/tests/data/acpi/virt/APIC.memhp
+++ b/tests/data/acpi/virt/APIC.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/APIC.numamem b/tests/data/acpi/virt/APIC.numamem
index 023f15f12e..179d274770 100644
--- a/tests/data/acpi/virt/APIC.numamem
+++ b/tests/data/acpi/virt/APIC.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/DSDT.acpihmatvirt b/tests/data/acpi/virt/DSDT.acpihmatvirt
new file mode 100644
index 0000000000..aee6ba017c
--- /dev/null
+++ b/tests/data/acpi/virt/DSDT.acpihmatvirt
Binary files differ
diff --git a/tests/data/acpi/virt/FACP b/tests/data/acpi/virt/FACP
index 1f764220f8..ac05c35a69 100644
--- a/tests/data/acpi/virt/FACP
+++ b/tests/data/acpi/virt/FACP
Binary files differ
diff --git a/tests/data/acpi/virt/FACP.memhp b/tests/data/acpi/virt/FACP.memhp
index 1f764220f8..ac05c35a69 100644
--- a/tests/data/acpi/virt/FACP.memhp
+++ b/tests/data/acpi/virt/FACP.memhp
Binary files differ
diff --git a/tests/data/acpi/virt/FACP.numamem b/tests/data/acpi/virt/FACP.numamem
index 1f764220f8..ac05c35a69 100644
--- a/tests/data/acpi/virt/FACP.numamem
+++ b/tests/data/acpi/virt/FACP.numamem
Binary files differ
diff --git a/tests/data/acpi/virt/HMAT.acpihmatvirt b/tests/data/acpi/virt/HMAT.acpihmatvirt
new file mode 100644
index 0000000000..6494d11b9f
--- /dev/null
+++ b/tests/data/acpi/virt/HMAT.acpihmatvirt
Binary files differ
diff --git a/tests/data/acpi/virt/PPTT.acpihmatvirt b/tests/data/acpi/virt/PPTT.acpihmatvirt
new file mode 100644
index 0000000000..710dba5e79
--- /dev/null
+++ b/tests/data/acpi/virt/PPTT.acpihmatvirt
Binary files differ
diff --git a/tests/data/acpi/virt/SRAT.acpihmatvirt b/tests/data/acpi/virt/SRAT.acpihmatvirt
new file mode 100644
index 0000000000..691ef56e34
--- /dev/null
+++ b/tests/data/acpi/virt/SRAT.acpihmatvirt
Binary files differ
diff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/virt/SSDT.memhp
index 4c363a6d95..2fcfc5fda9 100644
--- a/tests/data/acpi/virt/SSDT.memhp
+++ b/tests/data/acpi/virt/SSDT.memhp
Binary files differ
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index e6096e7f73..395d441212 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -88,10 +88,12 @@ typedef struct {
uint64_t rsdp_addr;
uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */];
GArray *tables;
- uint32_t smbios_ep_addr;
- struct smbios_21_entry_point smbios_ep_table;
+ uint64_t smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE__MAX];
+ SmbiosEntryPoint smbios_ep_table;
uint16_t smbios_cpu_max_speed;
uint16_t smbios_cpu_curr_speed;
+ uint8_t smbios_core_count;
+ uint16_t smbios_core_count2;
uint8_t *required_struct_types;
int required_struct_types_len;
QTestState *qts;
@@ -533,10 +535,9 @@ static void test_acpi_asl(test_data *data)
free_test_data(&exp_data);
}
-static bool smbios_ep_table_ok(test_data *data)
+static bool smbios_ep2_table_ok(test_data *data, uint32_t addr)
{
- struct smbios_21_entry_point *ep_table = &data->smbios_ep_table;
- uint32_t addr = data->smbios_ep_addr;
+ struct smbios_21_entry_point *ep_table = &data->smbios_ep_table.ep21;
qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table));
if (memcmp(ep_table->anchor_string, "_SM_", 4)) {
@@ -559,13 +560,29 @@ static bool smbios_ep_table_ok(test_data *data)
return true;
}
-static void test_smbios_entry_point(test_data *data)
+static bool smbios_ep3_table_ok(test_data *data, uint64_t addr)
+{
+ struct smbios_30_entry_point *ep_table = &data->smbios_ep_table.ep30;
+
+ qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table));
+ if (memcmp(ep_table->anchor_string, "_SM3_", 5)) {
+ return false;
+ }
+
+ if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table)) {
+ return false;
+ }
+
+ return true;
+}
+
+static SmbiosEntryPointType test_smbios_entry_point(test_data *data)
{
uint32_t off;
/* find smbios entry point structure */
for (off = 0xf0000; off < 0x100000; off += 0x10) {
- uint8_t sig[] = "_SM_";
+ uint8_t sig[] = "_SM_", sig3[] = "_SM3_";
int i;
for (i = 0; i < sizeof sig - 1; ++i) {
@@ -574,14 +591,30 @@ static void test_smbios_entry_point(test_data *data)
if (!memcmp(sig, "_SM_", sizeof sig)) {
/* signature match, but is this a valid entry point? */
- data->smbios_ep_addr = off;
- if (smbios_ep_table_ok(data)) {
+ if (smbios_ep2_table_ok(data, off)) {
+ data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_32] = off;
+ }
+ }
+
+ for (i = 0; i < sizeof sig3 - 1; ++i) {
+ sig3[i] = qtest_readb(data->qts, off + i);
+ }
+
+ if (!memcmp(sig3, "_SM3_", sizeof sig3)) {
+ if (smbios_ep3_table_ok(data, off)) {
+ data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_64] = off;
+ /* found 64-bit entry point, no need to look for 32-bit one */
break;
}
}
}
- g_assert_cmphex(off, <, 0x100000);
+ /* found at least one entry point */
+ g_assert_true(data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_32] ||
+ data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_64]);
+
+ return data->smbios_ep_addr[SMBIOS_ENTRY_POINT_TYPE_64] ?
+ SMBIOS_ENTRY_POINT_TYPE_64 : SMBIOS_ENTRY_POINT_TYPE_32;
}
static inline bool smbios_single_instance(uint8_t type)
@@ -600,41 +633,61 @@ static inline bool smbios_single_instance(uint8_t type)
}
}
-static bool smbios_cpu_test(test_data *data, uint32_t addr)
+static void smbios_cpu_test(test_data *data, uint32_t addr,
+ SmbiosEntryPointType ep_type)
{
- uint16_t expect_speed[2];
- uint16_t real;
+ uint8_t core_count, expected_core_count = data->smbios_core_count;
+ uint16_t speed, expected_speed[2];
+ uint16_t core_count2, expected_core_count2 = data->smbios_core_count2;
int offset[2];
int i;
/* Check CPU speed for backward compatibility */
offset[0] = offsetof(struct smbios_type_4, max_speed);
offset[1] = offsetof(struct smbios_type_4, current_speed);
- expect_speed[0] = data->smbios_cpu_max_speed ? : 2000;
- expect_speed[1] = data->smbios_cpu_curr_speed ? : 2000;
+ expected_speed[0] = data->smbios_cpu_max_speed ? : 2000;
+ expected_speed[1] = data->smbios_cpu_curr_speed ? : 2000;
for (i = 0; i < 2; i++) {
- real = qtest_readw(data->qts, addr + offset[i]);
- if (real != expect_speed[i]) {
- fprintf(stderr, "Unexpected SMBIOS CPU speed: real %u expect %u\n",
- real, expect_speed[i]);
- return false;
- }
+ speed = qtest_readw(data->qts, addr + offset[i]);
+ g_assert_cmpuint(speed, ==, expected_speed[i]);
}
- return true;
+ core_count = qtest_readb(data->qts,
+ addr + offsetof(struct smbios_type_4, core_count));
+
+ if (expected_core_count) {
+ g_assert_cmpuint(core_count, ==, expected_core_count);
+ }
+
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) {
+ core_count2 = qtest_readw(data->qts,
+ addr + offsetof(struct smbios_type_4, core_count2));
+
+ /* Core Count has reached its limit, checking Core Count 2 */
+ if (expected_core_count == 0xFF && expected_core_count2) {
+ g_assert_cmpuint(core_count2, ==, expected_core_count2);
+ }
+ }
}
-static void test_smbios_structs(test_data *data)
+static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type)
{
DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 };
- struct smbios_21_entry_point *ep_table = &data->smbios_ep_table;
- uint32_t addr = le32_to_cpu(ep_table->structure_table_address);
- int i, len, max_len = 0;
+
+ SmbiosEntryPoint *ep_table = &data->smbios_ep_table;
+ int i = 0, len, max_len = 0;
uint8_t type, prv, crt;
+ uint64_t addr;
+
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32) {
+ addr = le32_to_cpu(ep_table->ep21.structure_table_address);
+ } else {
+ addr = le64_to_cpu(ep_table->ep30.structure_table_address);
+ }
/* walk the smbios tables */
- for (i = 0; i < le16_to_cpu(ep_table->number_of_structures); i++) {
+ do {
/* grab type and formatted area length from struct header */
type = qtest_readb(data->qts, addr);
@@ -648,7 +701,7 @@ static void test_smbios_structs(test_data *data)
set_bit(type, struct_bitmap);
if (type == 4) {
- g_assert(smbios_cpu_test(data, addr));
+ smbios_cpu_test(data, addr, ep_type);
}
/* seek to end of unformatted string area of this struct ("\0\0") */
@@ -660,19 +713,33 @@ static void test_smbios_structs(test_data *data)
}
/* keep track of max. struct size */
- if (max_len < len) {
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 && max_len < len) {
max_len = len;
- g_assert_cmpuint(max_len, <=, ep_table->max_structure_size);
+ g_assert_cmpuint(max_len, <=, ep_table->ep21.max_structure_size);
}
/* start of next structure */
addr += len;
- }
- /* total table length and max struct size must match entry point values */
- g_assert_cmpuint(le16_to_cpu(ep_table->structure_table_length), ==,
- addr - le32_to_cpu(ep_table->structure_table_address));
- g_assert_cmpuint(le16_to_cpu(ep_table->max_structure_size), ==, max_len);
+ /*
+ * Until all structures have been scanned (ep21)
+ * or an EOF structure is found (ep30)
+ */
+ } while (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ?
+ ++i < le16_to_cpu(ep_table->ep21.number_of_structures) :
+ type != 127);
+
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32) {
+ /*
+ * Total table length and max struct size
+ * must match entry point values
+ */
+ g_assert_cmpuint(le16_to_cpu(ep_table->ep21.structure_table_length), ==,
+ addr - le32_to_cpu(ep_table->ep21.structure_table_address));
+
+ g_assert_cmpuint(le16_to_cpu(ep_table->ep21.max_structure_size), ==,
+ max_len);
+ }
/* required struct types must all be present */
for (i = 0; i < data->required_struct_types_len; i++) {
@@ -756,8 +823,8 @@ static void test_acpi_one(const char *params, test_data *data)
* https://bugs.launchpad.net/qemu/+bug/1821884
*/
if (!use_uefi) {
- test_smbios_entry_point(data);
- test_smbios_structs(data);
+ SmbiosEntryPointType ep_type = test_smbios_entry_point(data);
+ test_smbios_structs(data, ep_type);
}
qtest_quit(data->qts);
@@ -856,6 +923,21 @@ static void test_acpi_q35_tcg(void)
free_test_data(&data);
}
+static void test_acpi_q35_tcg_core_count2(void)
+{
+ test_data data = {
+ .machine = MACHINE_Q35,
+ .variant = ".core-count2",
+ .required_struct_types = base_required_struct_types,
+ .required_struct_types_len = ARRAY_SIZE(base_required_struct_types),
+ .smbios_core_count = 0xFF,
+ .smbios_core_count2 = 275,
+ };
+
+ test_acpi_one("-machine smbios-entry-point-type=64 -smp 275", &data);
+ free_test_data(&data);
+}
+
static void test_acpi_q35_tcg_bridge(void)
{
test_data data;
@@ -1461,6 +1543,111 @@ static void test_acpi_piix4_tcg_acpi_hmat(void)
test_acpi_tcg_acpi_hmat(MACHINE_PC);
}
+static void test_acpi_virt_tcg_acpi_hmat(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * 1024 * 1024,
+ };
+
+ data.variant = ".acpihmatvirt";
+
+ test_acpi_one(" -machine hmat=on"
+ " -cpu cortex-a57"
+ " -smp 4,sockets=2"
+ " -m 256M"
+ " -object memory-backend-ram,size=64M,id=ram0"
+ " -object memory-backend-ram,size=64M,id=ram1"
+ " -object memory-backend-ram,size=128M,id=ram2"
+ " -numa node,nodeid=0,memdev=ram0"
+ " -numa node,nodeid=1,memdev=ram1"
+ " -numa node,nodeid=2,memdev=ram2"
+ " -numa cpu,node-id=0,socket-id=0"
+ " -numa cpu,node-id=0,socket-id=0"
+ " -numa cpu,node-id=1,socket-id=1"
+ " -numa cpu,node-id=1,socket-id=1"
+ " -numa hmat-lb,initiator=0,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=10"
+ " -numa hmat-lb,initiator=0,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=10485760"
+ " -numa hmat-lb,initiator=0,target=1,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=0,target=1,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=5242880"
+ " -numa hmat-lb,initiator=0,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=30"
+ " -numa hmat-lb,initiator=0,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=1048576"
+ " -numa hmat-lb,initiator=1,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=1,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=5242880"
+ " -numa hmat-lb,initiator=1,target=1,hierarchy=memory,"
+ "data-type=access-latency,latency=10"
+ " -numa hmat-lb,initiator=1,target=1,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=10485760"
+ " -numa hmat-lb,initiator=1,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=30"
+ " -numa hmat-lb,initiator=1,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=1048576",
+ &data);
+
+ free_test_data(&data);
+}
+
+static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void)
+{
+ test_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.machine = MACHINE_Q35;
+ data.variant = ".acpihmat-noinitiator";
+ test_acpi_one(" -machine hmat=on"
+ " -smp 4,sockets=2"
+ " -m 128M"
+ " -object memory-backend-ram,size=32M,id=ram0"
+ " -object memory-backend-ram,size=32M,id=ram1"
+ " -object memory-backend-ram,size=64M,id=ram2"
+ " -numa node,nodeid=0,memdev=ram0"
+ " -numa node,nodeid=1,memdev=ram1"
+ " -numa node,nodeid=2,memdev=ram2"
+ " -numa cpu,node-id=0,socket-id=0"
+ " -numa cpu,node-id=0,socket-id=0"
+ " -numa cpu,node-id=1,socket-id=1"
+ " -numa cpu,node-id=1,socket-id=1"
+ " -numa hmat-lb,initiator=0,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=10"
+ " -numa hmat-lb,initiator=0,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=10485760"
+ " -numa hmat-lb,initiator=0,target=1,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=0,target=1,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=5242880"
+ " -numa hmat-lb,initiator=0,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=30"
+ " -numa hmat-lb,initiator=0,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=1048576"
+ " -numa hmat-lb,initiator=1,target=0,hierarchy=memory,"
+ "data-type=access-latency,latency=20"
+ " -numa hmat-lb,initiator=1,target=0,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=5242880"
+ " -numa hmat-lb,initiator=1,target=1,hierarchy=memory,"
+ "data-type=access-latency,latency=10"
+ " -numa hmat-lb,initiator=1,target=1,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=10485760"
+ " -numa hmat-lb,initiator=1,target=2,hierarchy=memory,"
+ "data-type=access-latency,latency=30"
+ " -numa hmat-lb,initiator=1,target=2,hierarchy=memory,"
+ "data-type=access-bandwidth,bandwidth=1048576",
+ &data);
+ free_test_data(&data);
+}
+
#ifdef CONFIG_POSIX
static void test_acpi_erst(const char *machine)
{
@@ -1824,6 +2011,8 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet);
qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm);
qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat);
+ qtest_add_func("acpi/q35/acpihmat-noinitiator",
+ test_acpi_q35_tcg_acpi_hmat_noinitiator);
#ifdef CONFIG_POSIX
qtest_add_func("acpi/q35/acpierst", test_acpi_q35_acpi_erst);
#endif
@@ -1835,6 +2024,8 @@ int main(int argc, char *argv[])
if (has_kvm) {
qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic);
qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar);
+ qtest_add_func("acpi/q35/core-count2",
+ test_acpi_q35_tcg_core_count2);
}
qtest_add_func("acpi/q35/viot", test_acpi_q35_viot);
#ifdef CONFIG_POSIX
@@ -1864,6 +2055,8 @@ int main(int argc, char *argv[])
} else if (strcmp(arch, "aarch64") == 0) {
if (has_tcg) {
qtest_add_func("acpi/virt", test_acpi_virt_tcg);
+ qtest_add_func("acpi/virt/acpihmatvirt",
+ test_acpi_virt_tcg_acpi_hmat);
qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem);
qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp);
qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb);
diff --git a/tests/unit/test-crypto-der.c b/tests/unit/test-crypto-der.c
index aed0f28d68..d218a7f170 100644
--- a/tests/unit/test-crypto-der.c
+++ b/tests/unit/test-crypto-der.c
@@ -147,13 +147,58 @@ static const uint8_t test_rsa2048_priv_key[] =
"\x4e\x2f\x4c\xf9\xab\x97\x38\xe4\x20\x32\x32\x96\xc8\x9e\x79\xd3"
"\x12";
+static const uint8_t test_ecdsa_p192_priv_key[] =
+ "\x30\x53" /* SEQUENCE, offset 0, length 83 */
+ "\x02\x01\x01" /* INTEGER, offset 2, length 1 */
+ "\x04\x18" /* OCTET STRING, offset 5, length 24 */
+ "\xcb\xc8\x86\x0e\x66\x3c\xf7\x5a\x44\x13\xb8\xef\xea\x1d\x7b\xa6"
+ "\x1c\xda\xf4\x1b\xc7\x67\x6b\x35"
+ "\xa1\x34" /* CONTEXT SPECIFIC 1, offset 31, length 52 */
+ "\x03\x32" /* BIT STRING, offset 33, length 50 */
+ "\x00\x04\xc4\x16\xb3\xff\xac\xd5\x87\x98\xf7\xd9\x45\xfe\xd3\x5c"
+ "\x17\x9d\xb2\x36\x22\xcc\x07\xb3\x6d\x3c\x4e\x04\x5f\xeb\xb6\x52"
+ "\x58\xfb\x36\x10\x52\xb7\x01\x62\x0e\x94\x51\x1d\xe2\xef\x10\x82"
+ "\x88\x78";
+
+static const uint8_t test_ecdsa_p256_priv_key[] =
+ "\x30\x77" /* SEQUENCE, offset 0, length 119 */
+ "\x02\x01\x01" /* INTEGER, offset 2, length 1 */
+ "\x04\x20" /* OCTET STRING, offset 5, length 32 */
+ "\xf6\x92\xdd\x29\x1c\x6e\xef\xb6\xb2\x73\x9f\x40\x1b\xb3\x2a\x28"
+ "\xd2\x37\xd6\x4a\x5b\xe4\x40\x4c\x6a\x95\x99\xfa\xf7\x92\x49\xbe"
+ "\xa0\x0a" /* CONTEXT SPECIFIC 0, offset 39, length 10 */
+ "\x06\x08" /* OID, offset 41, length 8 */
+ "\x2a\x86\x48\xce\x3d\x03\x01\x07"
+ "\xa1\x44" /* CONTEXT SPECIFIC 1, offset 51, length 68 */
+ "\x03\x42" /* BIT STRING, offset 53, length 66 */
+ "\x00\x04\xed\x42\x9c\x67\x79\xbe\x46\x83\x88\x3e\x8c\xc1\x33\xf3"
+ "\xc3\xf6\x2c\xf3\x13\x6a\x00\xc2\xc9\x3e\x87\x7f\x86\x39\xe6\xae"
+ "\xe3\xb9\xba\x2f\x58\x63\x32\x62\x62\x54\x07\x27\xf9\x5a\x3a\xc7"
+ "\x3a\x6b\x5b\xbc\x0d\x33\xba\xbb\xd4\xa3\xff\x4f\x9e\xdd\xf5\x59"
+ "\xc0\xf6";
+
#define MAX_CHECKER_COUNT 32
+static int qcrypto_wrapped_decode_ctx_tag0(const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb, void *opaque,
+ Error **errp)
+{
+ return qcrypto_der_decode_ctx_tag(data, dlen, 0, cb, opaque, errp);
+}
+
+static int qcrypto_wrapped_decode_ctx_tag1(const uint8_t **data, size_t *dlen,
+ QCryptoDERDecodeCb cb, void *opaque,
+ Error **errp)
+{
+ return qcrypto_der_decode_ctx_tag(data, dlen, 1, cb, opaque, errp);
+}
+
typedef struct QCryptoAns1DecoderResultChecker QCryptoAns1DecoderResultChecker;
struct QCryptoAns1DecoderResultChecker {
int (*action) (const uint8_t **data, size_t *dlen,
QCryptoDERDecodeCb cb, void *opaque, Error **errp);
QCryptoDERDecodeCb cb;
+ bool constructed;
const uint8_t *exp_value;
size_t exp_vlen;
};
@@ -204,7 +249,7 @@ static void test_ans1(const void *opaque)
g_assert(checker->action(&c->data, &c->dlen, checker_callback,
(void *)checker, &error_abort)
== checker->exp_vlen);
- if (checker->action == qcrypto_der_decode_seq) {
+ if (checker->constructed) {
++seq_depth;
ctx[seq_depth].data = checker->exp_value;
ctx[seq_depth].dlen = checker->exp_vlen;
@@ -225,25 +270,25 @@ static QCryptoAns1DecoderTestData test_data[] = {
.test_data = test_rsa512_priv_key,
.test_data_len = sizeof(test_rsa512_priv_key) - 1,
.checker = {
- { qcrypto_der_decode_seq, checker_callback,
+ { qcrypto_der_decode_seq, checker_callback, true,
test_rsa512_priv_key + 4, 313 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 4 + 2, 1 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 7 + 2, 65 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 74 + 2, 3 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 79 + 2, 64 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 145 + 2, 33 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 180 + 2, 33 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 215 + 2, 32 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 249 + 2, 32 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa512_priv_key + 283 + 2, 32 },
},
},
@@ -252,29 +297,66 @@ static QCryptoAns1DecoderTestData test_data[] = {
.test_data = test_rsa2048_priv_key,
.test_data_len = sizeof(test_rsa2048_priv_key) - 1,
.checker = {
- { qcrypto_der_decode_seq, checker_callback,
+ { qcrypto_der_decode_seq, checker_callback, true,
test_rsa2048_priv_key + 4, 1190 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 4 + 2, 1 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 7 + 4, 257 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 268 + 2, 3 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 273 + 4, 257 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 534 + 3, 129 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 666 + 3, 129 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 798 + 3, 129 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 930 + 3, 129 },
- { qcrypto_der_decode_int, checker_callback,
+ { qcrypto_der_decode_int, checker_callback, false,
test_rsa2048_priv_key + 1062 + 3, 129 },
},
},
-
+{
+ .path = "/crypto/der/parse-ecdsa-p192-priv-key",
+ .test_data = test_ecdsa_p192_priv_key,
+ .test_data_len = sizeof(test_ecdsa_p192_priv_key) - 1,
+ .checker = {
+ { qcrypto_der_decode_seq, checker_callback, true,
+ test_ecdsa_p192_priv_key + 2, 83 },
+ { qcrypto_der_decode_int, checker_callback, false,
+ test_ecdsa_p192_priv_key + 2 + 2, 1 },
+ { qcrypto_der_decode_octet_str, checker_callback, false,
+ test_ecdsa_p192_priv_key + 5 + 2, 24 },
+ { qcrypto_wrapped_decode_ctx_tag1, checker_callback, true,
+ test_ecdsa_p192_priv_key + 31 + 2, 52 },
+ { qcrypto_der_decode_bit_str , checker_callback, false,
+ test_ecdsa_p192_priv_key + 33 + 2, 50 },
+ },
+},
+{
+ .path = "/crypto/der/parse-ecdsa-p256-priv-key",
+ .test_data = test_ecdsa_p256_priv_key,
+ .test_data_len = sizeof(test_ecdsa_p256_priv_key) - 1,
+ .checker = {
+ { qcrypto_der_decode_seq, checker_callback, true,
+ test_ecdsa_p256_priv_key + 2, 119 },
+ { qcrypto_der_decode_int, checker_callback, false,
+ test_ecdsa_p256_priv_key + 2 + 2, 1 },
+ { qcrypto_der_decode_octet_str, checker_callback, false,
+ test_ecdsa_p256_priv_key + 5 + 2, 32 },
+ { qcrypto_wrapped_decode_ctx_tag0, checker_callback, true,
+ test_ecdsa_p256_priv_key + 39 + 2, 10 },
+ { qcrypto_der_decode_oid, checker_callback, false,
+ test_ecdsa_p256_priv_key + 41 + 2, 8 },
+ { qcrypto_wrapped_decode_ctx_tag1, checker_callback, true,
+ test_ecdsa_p256_priv_key + 51 + 2, 68 },
+ { qcrypto_der_decode_bit_str , checker_callback, false,
+ test_ecdsa_p256_priv_key + 53 + 2, 66 },
+ },
+},
};
int main(int argc, char **argv)