[PATCH v2 0/3] virt-host-validate: Detect SMMU presence on ARMs by parsing IORT table

v2 of: https://listman.redhat.com/archives/libvir-list/2023-April/239251.html diff to v1: - Renamed couple of enum values, - fixed their string translations, - Other small nits pointed out by Andrea Michal Prívozník (3): util: Introduce virAcpi module tests: Introduce viracpitest virt-host-validate: Detect SMMU presence on ARMs by parsing IORT table build-aux/syntax-check.mk | 2 +- po/POTFILES | 1 + src/libvirt_private.syms | 7 + src/util/meson.build | 1 + src/util/viracpi.c | 226 ++++++++++++++++++++++++++++ src/util/viracpi.h | 23 +++ src/util/viracpipriv.h | 58 +++++++ tests/meson.build | 1 + tests/viracpidata/IORT_ampere | Bin 0 -> 2304 bytes tests/viracpidata/IORT_empty | Bin 0 -> 65 bytes tests/viracpidata/IORT_gigabyte | Bin 0 -> 10100 bytes tests/viracpidata/IORT_qualcomm | Bin 0 -> 3560 bytes tests/viracpidata/IORT_virt_aarch64 | Bin 0 -> 128 bytes tests/viracpitest.c | 135 +++++++++++++++++ tools/virt-host-validate-common.c | 18 ++- 15 files changed, 468 insertions(+), 4 deletions(-) create mode 100644 src/util/viracpi.c create mode 100644 src/util/viracpi.h create mode 100644 src/util/viracpipriv.h create mode 100644 tests/viracpidata/IORT_ampere create mode 100644 tests/viracpidata/IORT_empty create mode 100644 tests/viracpidata/IORT_gigabyte create mode 100644 tests/viracpidata/IORT_qualcomm create mode 100644 tests/viracpidata/IORT_virt_aarch64 create mode 100644 tests/viracpitest.c -- 2.39.2

The aim of this new module is to contain code that's parsing ACPI tables. For now, only parsing of IORT table is implemented (it's ARM specific table). And since we only need to check whether the table contains SMMU record, the code is very simplified. I've followed the specification published here: https://developer.arm.com/documentation/den0049/latest/ Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- po/POTFILES | 1 + src/libvirt_private.syms | 7 ++ src/util/meson.build | 1 + src/util/viracpi.c | 226 +++++++++++++++++++++++++++++++++++++++ src/util/viracpi.h | 23 ++++ src/util/viracpipriv.h | 58 ++++++++++ 6 files changed, 316 insertions(+) create mode 100644 src/util/viracpi.c create mode 100644 src/util/viracpi.h create mode 100644 src/util/viracpipriv.h diff --git a/po/POTFILES b/po/POTFILES index fa769a8a95..b122f02818 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -244,6 +244,7 @@ src/storage_file/storage_source.c src/storage_file/storage_source_backingstore.c src/test/test_driver.c src/util/iohelper.c +src/util/viracpi.c src/util/viralloc.c src/util/virarptable.c src/util/viraudit.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e37373c3c9..1247b67a39 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1829,6 +1829,13 @@ vir_g_strdup_printf; vir_g_strdup_vprintf; +# util/viracpi.c +virAcpiHasSMMU; +virAcpiParseIORT; +virIORTNodeTypeTypeFromString; +virIORTNodeTypeTypeToString; + + # util/viralloc.h virAppendElement; virDeleteElementsN; diff --git a/src/util/meson.build b/src/util/meson.build index c81500ea04..2fe6f7699e 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -1,5 +1,6 @@ util_sources = [ 'glibcompat.c', + 'viracpi.c', 'viralloc.c', 'virarch.c', 'virarptable.c', diff --git a/src/util/viracpi.c b/src/util/viracpi.c new file mode 100644 index 0000000000..05a2b29f0f --- /dev/null +++ b/src/util/viracpi.c @@ -0,0 +1,226 @@ +/* + * viracpi.c: ACPI table(s) parser + * + * Copyright (C) 2023 Red Hat, Inc. + * + * 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 <config.h> + +#include <inttypes.h> +#include <fcntl.h> + +#define LIBVIRT_VIRACPIPRIV_H_ALLOW +#include "internal.h" +#include "viracpi.h" +#include "viracpipriv.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.acpi"); + +typedef struct virIORTHeader virIORTHeader; +struct virIORTHeader { + uint32_t signature; + uint32_t length; + uint8_t revision; + uint8_t checksum; + char oem_id[6]; + char oem_table_id[8]; + char oem_revision[4]; + char creator_id[4]; + char creator_revision[4]; + /* Technically, the following are not part of header, but + * they immediately follow the header and are in the table + * exactly once. */ + uint32_t nnodes; + uint32_t nodes_offset; + /* Here follows reserved and padding fields. Ain't nobody's + * interested in that. */ +} ATTRIBUTE_PACKED; + +VIR_ENUM_IMPL(virIORTNodeType, + VIR_IORT_NODE_TYPE_LAST, + "ITS Group", + "Named Component", + "Root Complex", + "SMMUv1 or SMMUv2", + "SMMUv3", + "PMCG", + "Memory range"); + + +static int +virAcpiParseIORTNodeHeader(int fd, + const char *filename, + virIORTNodeHeader *nodeHeader) +{ + g_autofree char *nodeHeaderBuf = NULL; + const char *typeStr = NULL; + int nodeHeaderLen; + + nodeHeaderLen = virFileReadHeaderFD(fd, sizeof(*nodeHeader), &nodeHeaderBuf); + if (nodeHeaderLen < 0) { + virReportSystemError(errno, + _("cannot read node header '%1$s'"), + filename); + return -1; + } + + if (nodeHeaderLen != sizeof(*nodeHeader)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("IORT table node header ended early")); + return -1; + } + + memcpy(nodeHeader, nodeHeaderBuf, nodeHeaderLen); + + typeStr = virIORTNodeTypeTypeToString(nodeHeader->type); + + VIR_DEBUG("IORT node header: type = %" PRIu8 " (%s) len = %" PRIu16, + nodeHeader->type, NULLSTR(typeStr), nodeHeader->len); + + /* Basic sanity check. While there's a type specific data + * that follows the node header, the node length should be at + * least size of header itself. */ + if (nodeHeader->len < sizeof(*nodeHeader)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("IORT table node type %1$s has invalid length: %2$u"), + NULLSTR(typeStr), (unsigned int)nodeHeader->len); + return -1; + } + + return 0; +} + + +static ssize_t +virAcpiParseIORTNodes(int fd, + const char *filename, + const virIORTHeader *header, + virIORTNodeHeader **nodesRet) +{ + g_autofree virIORTNodeHeader *nodes = NULL; + size_t nnodes = 0; + off_t pos; + + /* Firstly, reset position to the start of nodes. */ + if ((pos = lseek(fd, header->nodes_offset, SEEK_SET)) < 0) { + virReportSystemError(errno, + _("cannot seek in '%1$s'"), + filename); + return -1; + } + + for (; pos < header->length;) { + virIORTNodeHeader node; + + if (virAcpiParseIORTNodeHeader(fd, filename, &node) < 0) + return -1; + + if ((pos = lseek(fd, pos + node.len, SEEK_SET)) < 0) { + virReportSystemError(errno, + _("cannot seek in '%1$s'"), + filename); + return -1; + } + + VIR_APPEND_ELEMENT(nodes, nnodes, node); + } + + *nodesRet = g_steal_pointer(&nodes); + return nnodes; +} + + +ssize_t +virAcpiParseIORT(virIORTNodeHeader **nodesRet, + const char *filename) +{ + VIR_AUTOCLOSE fd = -1; + g_autofree char *headerBuf = NULL; + int headerLen; + virIORTHeader header; + + if ((fd = open(filename, O_RDONLY)) < 0) { + virReportSystemError(errno, + _("cannot open '%1$s'"), + filename); + return -1; + } + + headerLen = virFileReadHeaderFD(fd, sizeof(header), &headerBuf); + if (headerLen < 0) { + virReportSystemError(errno, + _("cannot read header '%1$s'"), + filename); + return -1; + } + + if (headerLen != sizeof(header)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("IORT table header ended early")); + return -1; + } + + memcpy(&header, headerBuf, headerLen); + + VIR_DEBUG("IORT header: len = %" PRIu32 " revision = %" PRIu8 + " nnodes = %" PRIu32 " OEM = %s", + header.length, header.revision, + header.nnodes, header.oem_id); + + return virAcpiParseIORTNodes(fd, filename, &header, nodesRet); +} + + +#define IORT_PATH "/sys/firmware/acpi/tables/IORT" + +/** + * virAcpiHasSMMU: + * + * Parse IORT table trying to find SMMU node entry. + * Since IORT is ARM specific ACPI table, it doesn't make much + * sense to call this function on other platforms and expect + * sensible result. + * + * Returns: 0 if no SMMU node was found, + * 1 if a SMMU node was found (i.e. host supports SMMU), + * -1 otherwise (with error reported). + */ +int +virAcpiHasSMMU(void) +{ + g_autofree virIORTNodeHeader *nodes = NULL; + ssize_t nnodes = -1; + size_t i; + + if ((nnodes = virAcpiParseIORT(&nodes, IORT_PATH)) < 0) + return -1; + + for (i = 0; i < nnodes; i++) { + if (nodes[i].type == VIR_IORT_NODE_TYPE_SMMUV1_OR_SMMUV2 || + nodes[i].type == VIR_IORT_NODE_TYPE_SMMUV3) { + return 1; + } + } + + return 0; +} diff --git a/src/util/viracpi.h b/src/util/viracpi.h new file mode 100644 index 0000000000..5a433b893a --- /dev/null +++ b/src/util/viracpi.h @@ -0,0 +1,23 @@ +/* + * viracpi.h: ACPI table(s) parser + * + * Copyright (C) 2023 Red Hat, Inc. + * + * 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/>. + */ + +#pragma once + +int virAcpiHasSMMU(void); diff --git a/src/util/viracpipriv.h b/src/util/viracpipriv.h new file mode 100644 index 0000000000..362a192ebf --- /dev/null +++ b/src/util/viracpipriv.h @@ -0,0 +1,58 @@ +/* + * viracpipriv.h: Functions for testing virAcpi APIs + * + * Copyright (C) 2023 Red Hat, Inc. + * + * 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/>. + * + */ + +#ifndef LIBVIRT_VIRACPIPRIV_H_ALLOW +# error "viracpipriv.h may only be included by viracpi.c or test suites" +#endif /* LIBVIRT_VIRACPIPRIV_H_ALLOW */ + +#pragma once + +#include <inttypes.h> + +#include "internal.h" +#include "virenum.h" + +typedef enum { + VIR_IORT_NODE_TYPE_ITS_GROUP = 0, + VIR_IORT_NODE_TYPE_NAMED_COMPONENT, + VIR_IORT_NODE_TYPE_ROOT_COMPLEX, + VIR_IORT_NODE_TYPE_SMMUV1_OR_SMMUV2, + VIR_IORT_NODE_TYPE_SMMUV3, + VIR_IORT_NODE_TYPE_PMCG, + VIR_IORT_NODE_TYPE_MEMORY_RANGE, + VIR_IORT_NODE_TYPE_LAST, +} virIORTNodeType; + +VIR_ENUM_DECL(virIORTNodeType); + +typedef struct virIORTNodeHeader virIORTNodeHeader; +struct virIORTNodeHeader { + uint8_t type; /* One of virIORTNodeType */ + uint16_t len; + uint8_t revision; + uint32_t identifier; + uint32_t nmappings; + uint32_t reference_id; +} ATTRIBUTE_PACKED; + +ssize_t +virAcpiParseIORT(virIORTNodeHeader **nodesRet, + const char *filename); -- 2.39.2

On Thu, Apr 06, 2023 at 10:57:02AM +0200, Michal Privoznik wrote:
+++ b/src/util/viracpi.c + /* Basic sanity check. While there's a type specific data + * that follows the node header, the node length should be at + * least size of header itself. */ + if (nodeHeader->len < sizeof(*nodeHeader)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("IORT table node type %1$s has invalid length: %2$u"), + NULLSTR(typeStr), (unsigned int)nodeHeader->len);
As mentioned in the other thread, consider changing this to virReportError(VIR_ERR_INTERNAL_ERROR, _("IORT table node type %1$s has invalid length: got %2$u, expected at least %3$lu"), NULLSTR(typeStr), (unsigned int)nodeHeader->len, sizeof(*nodeHeader)); for a slightly more useful error message. Either way, Reviewed-by: Andrea Bolognani <abologna@redhat.com> -- Andrea Bolognani / Red Hat / Virtualization

Introduce a test that checks newly introduced virAcpi module. There are three IORT tables from a real HW (IORT_ampere, IORT_gigabyte and IORT_qualcomm), then there's one from a VM (IORT_virt_aarch64) and one that I handcrafted to be empty (IORT_empty). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Andrea Bolognani <abologna@redhat.com> --- build-aux/syntax-check.mk | 2 +- tests/meson.build | 1 + tests/viracpidata/IORT_ampere | Bin 0 -> 2304 bytes tests/viracpidata/IORT_empty | Bin 0 -> 65 bytes tests/viracpidata/IORT_gigabyte | Bin 0 -> 10100 bytes tests/viracpidata/IORT_qualcomm | Bin 0 -> 3560 bytes tests/viracpidata/IORT_virt_aarch64 | Bin 0 -> 128 bytes tests/viracpitest.c | 135 ++++++++++++++++++++++++++++ 8 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 tests/viracpidata/IORT_ampere create mode 100644 tests/viracpidata/IORT_empty create mode 100644 tests/viracpidata/IORT_gigabyte create mode 100644 tests/viracpidata/IORT_qualcomm create mode 100644 tests/viracpidata/IORT_virt_aarch64 create mode 100644 tests/viracpitest.c diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index 5829dc9011..64c1e2773e 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -1370,7 +1370,7 @@ exclude_file_name_regexp--sc_prohibit_close = \ (\.p[yl]$$|\.spec\.in$$|^docs/|^(src/util/vir(file|event)\.c|src/libvirt-stream\.c|tests/(vir.+mock\.c|commandhelper\.c|qemusecuritymock\.c)|tools/nss/libvirt_nss_(leases|macs)\.c)|tools/virt-qemu-qmp-proxy$$) exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \ - (^tests/(nodedevmdevctl|virhostcpu|virpcitest|virstoragetest)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$) + (^tests/(nodedevmdevctl|viracpi|virhostcpu|virpcitest|virstoragetest)data/|docs/js/.*\.js|docs/fonts/.*\.woff|\.diff|tests/virconfdata/no-newline\.conf$$) exclude_file_name_regexp--sc_prohibit_fork_wrappers = \ (^(src/(util/(vircommand|virdaemon)|lxc/lxc_controller)|tests/testutils)\.c$$) diff --git a/tests/meson.build b/tests/meson.build index 24d08e4f97..35adbc2d56 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -269,6 +269,7 @@ tests += [ { 'name': 'storagevolxml2xmltest' }, { 'name': 'sysinfotest' }, { 'name': 'utiltest' }, + { 'name': 'viracpitest' }, { 'name': 'viralloctest' }, { 'name': 'virauthconfigtest' }, { 'name': 'virbitmaptest' }, diff --git a/tests/viracpidata/IORT_ampere b/tests/viracpidata/IORT_ampere new file mode 100644 index 0000000000000000000000000000000000000000..02e7fd98499729025346c6ac3ae5cf10b0f1074d GIT binary patch literal 2304 zcmbW1y-EW?6opUzV}1)8%LN4mK`kr-7P4TWg&4%rLXlu$L<trF3*SI2d;m)y!oo6- zU}@n4<PFw6a}$x7++mo7OlH2Bv)?d#caKg^(mGIla9fj0WlOVRV@!NluU+_qUdT z!dl+nA>g$lfAhAGcZwV=;3b8Z6<$$zRpB*-Hx%AfcuV1Jg?AL*&3Um09*JpqZe{?O zqhNo2j#<W4z|_q6b6jWV&dnkEb9^p#ZW55=mh9ZFc{qL_Z*Aw^0&-kp=UV2W`+Zzu z=f(j!){UL}3CPtK<oc!v{rlwWW&1t?a;#%Jx4}*1m#@xP=XUOOmMahCb!BZF@J7IQ zwg>YGkfFbw)ib1ii+>)bWKJ0S3tjuMt`ZLaZB1OlRM%>(tBecBB}{d#$GR#QJ1$|W zYctkW#f#$-rn<IcT{XNrE@7(cG0eq0f4rm)8~8LMX=F~A>UxTGHSy)RgsHBpSXT>E n$0ba4UB|lGsLR0_qjZI-uA5j_2Z`enrn>H8U0w7XmoVaAS)q0T literal 0 HcmV?d00001 diff --git a/tests/viracpidata/IORT_empty b/tests/viracpidata/IORT_empty new file mode 100644 index 0000000000000000000000000000000000000000..58b696fe935b432020377aabc76fe2b95376b48f GIT binary patch literal 65 ucmebD4+?Q$U|?Y6a`g=eiDdBcbPDqf3SnRbih&p)8U!4`B$SXqaJT?S&j#KA literal 0 HcmV?d00001 diff --git a/tests/viracpidata/IORT_gigabyte b/tests/viracpidata/IORT_gigabyte new file mode 100644 index 0000000000000000000000000000000000000000..b18b445b5de0911ba7190dfe396a72fec3fe6cf5 GIT binary patch literal 10100 zcmeI1OHRWu5Qg2REq(9^AS76TA~tNhV95qiBr266RV`x4f;U?ZP>CaO1rESrH~<IW z4#mG`Y%0}>oNhgq5>JSK2mi4?O{V>;(fI91sY?6o^twN}9A8X^z4OscMJd%Ejt6^< zo$Nrz$NF$>*F_2;#-b&+H51}AsNpZVy_<6t=eh}%I??@99|OL|*gKAGch7<JWQ6!2 zKEQ!k_5s#9o8kP=o+?8Ke981-U^Cb2&}l4Nd`wCMne>1O=h`F3(vuP5gZKakV%Y~+ zD>13!@}VLoSw2~g{cuiHq~RfsV<DP4{xRLkJf}gD1&xoFSs*W;cuw0rb09q#AwGx? za3Gd_fVC2nszJOoABj$*RNBT2{_o0eAd_Bb3U_|xNP03td=MYtKrH(JYb7QnK}<>@ z(9PynpX+p8l*6sikW!lF5}H<y#uVlFo~_Rz7xm?K>cb3_`W*WeOMOMwNiSS{Sk4Ei zh>1=0y-jlzF_BX_$HZUO_?VOiGD+%l<a;dj7g?vgZ#a;*4{(o|)Cyu!2Ljz}Za3xM zHH$dL>0c?$20f;fCe6|Kc$o$AQtESLYAp2^S*N^jIFPpwaF5v64&vo11iD#Xe(lq> zk4fD?CP{scJdCCOBI}g*4F~e}0qzlBIzdcYgFrXSq~0N27v*r9G^CVfYY7d1;x0Vr E57fpI9{>OV literal 0 HcmV?d00001 diff --git a/tests/viracpidata/IORT_qualcomm b/tests/viracpidata/IORT_qualcomm new file mode 100644 index 0000000000000000000000000000000000000000..5364a9968fa024f34b5e911513a08e8a7f88cf91 GIT binary patch literal 3560 zcmb7_yK7WI6vk)w?gfdc+XO5}jbI^i_hDy|WN$P9V~9y%A;c$GhzN?HK?NJFM9@YX zTmKYG!TbS&7AXbo*Yn+ZoZX!D-Z=xi^Z3p;bLMyV++Eway1jqI7;|ZJw6SjOW_htc zEDGDArL`;Dm*?|&yF%lFK2<v&2ChdOTIas0hHV;`)ylW=_^@${n9qH$<`_PppeG2V z_Q>~PpYhnO$Ma|k4ZC3t<Mxogs9|h<PhEz2XknjKzmVIYgnqBe=OLYs7@>C?blad8 z)4JsI;{czZk<Vkw>6z#9bS9q`KM%?s_r`81d;+7u=bm2>_cy;HAH)c~+o0P9y_nV| zpWg@g{E2*C5tCid<@HQHZNUe-rSS3FnZW0bUl8{<e<L5n2))~&+XlUu)+HaRh-2>A zSHEb~7tPV$Qa;ap-tT=s{oFIb2fL;4$=nf<&lkTS?r-+0eBROdh!HyKi}L-ij{2(8 zQC}0f<U<ufdbqclqxzyb+6T(%h3E2dCZCSrgWXd2blee<&sV=7?vMKN`6EW?sINL5 z^;M^%z9w|Zhbn?}=0o*GbF@#y<fZ5Gc_yD+@WE~=d~$b0<g@P=#QjlUK7Ygr9rabG zqrU2N)YpXGy5JH6k25|;jE~a)%Hs8%t>v9_<$8e|24;sU28c=X_M*3CwBCO7w$<K2 z^k!<W`=iAjJ8JLwcy6xs_L#F(dzteIwU;@cRC}58DYch5FQ~oD`Lx#CXU>aiFLPc} zdztgH+RL0rYA<sx)n4Yj(%`Lr`1D$c?~MU-zM=Lq=bLITbH1haGUwZBFLS=5_A=+Y zliu)B_HfbRy+kcraPQ}>a?~@eq_}=!+#oS-SQ~fN<#%m$R3ygL--Gw7zX!(+%s=~I BN)P}5 literal 0 HcmV?d00001 diff --git a/tests/viracpidata/IORT_virt_aarch64 b/tests/viracpidata/IORT_virt_aarch64 new file mode 100644 index 0000000000000000000000000000000000000000..7efd0ce8a6b3928efa7e1373f688ab4c5f50543b GIT binary patch literal 128 zcmebD4+?2uU|?Y0?Bwt45v<@85#X!<1dKp25F11@0kHuPgMkDCNC*yK93~3}W)K^M VRiHGGVg_O`aDdYP|3ers^8jQz3IPBB literal 0 HcmV?d00001 diff --git a/tests/viracpitest.c b/tests/viracpitest.c new file mode 100644 index 0000000000..f5dd542ffb --- /dev/null +++ b/tests/viracpitest.c @@ -0,0 +1,135 @@ +/* + * viracpitest.c: Test ACPI table parsing + * + * Copyright (C) 2023 Red Hat, Inc. + * + * 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 <config.h> + +#define LIBVIRT_VIRACPIPRIV_H_ALLOW +#include "testutils.h" +#include "viracpi.h" +#include "viracpipriv.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +typedef struct testAarch64SMMUData testAarch64SMMUData; +struct testAarch64SMMUData { + const char *filename; + ssize_t nnodes; + const virIORTNodeType *node_types; +}; + +static void +printBitmap(virBitmap *types) +{ + size_t i; + + for (i = 0; i < VIR_IORT_NODE_TYPE_LAST; i++) { + if (virBitmapIsBitSet(types, i)) { + fprintf(stderr, "%s\n", virIORTNodeTypeTypeToString(i)); + } + } +} + +static int +testAarch64SMMU(const void *opaque) +{ + const testAarch64SMMUData *data = opaque; + g_autofree char *path = NULL; + g_autofree virIORTNodeHeader *nodes = NULL; + ssize_t nnodes = 0; + + path = g_strdup_printf("%s/viracpidata/%s", + abs_srcdir, data->filename); + + nnodes = virAcpiParseIORT(&nodes, path); + + if (nnodes != data->nnodes) { + fprintf(stderr, + "virAcpiParseIORT() returned wrong number of nodes: %zd, expected %zd\n", + nnodes, data->nnodes); + return -1; + } + + if (nnodes > 0) { + g_autoptr(virBitmap) typesSeen = virBitmapNew(VIR_IORT_NODE_TYPE_LAST); + g_autoptr(virBitmap) typesExp = virBitmapNew(VIR_IORT_NODE_TYPE_LAST); + size_t i = 0; + + for (i = 0; data->node_types[i] != VIR_IORT_NODE_TYPE_LAST; i++) { + size_t type = data->node_types[i]; + + ignore_value(virBitmapSetBit(typesExp, type)); + } + + for (i = 0; i < nnodes; i++) { + virIORTNodeHeader *h = &nodes[i]; + + ignore_value(virBitmapSetBit(typesSeen, h->type)); + } + + if (!virBitmapEqual(typesSeen, typesExp)) { + fprintf(stderr, "node types mismatch.\n\nExpected:\n"); + printBitmap(typesExp); + fprintf(stderr, "\nActual:\n"); + printBitmap(typesSeen); + return -1; + } + } + + return 0; +} + + +static int +mymain(void) +{ + int ret = 0; + +#define DO_TEST(filename, nnodes, ...) \ + do { \ + const virIORTNodeType node_types[] = { __VA_ARGS__, VIR_IORT_NODE_TYPE_LAST }; \ + const testAarch64SMMUData data = {filename, nnodes, node_types }; \ + if (virTestRun("aarch64 SMMU " filename, testAarch64SMMU, &data) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST("IORT_empty", 0, VIR_IORT_NODE_TYPE_LAST); + DO_TEST("IORT_virt_aarch64", 2, + VIR_IORT_NODE_TYPE_ITS_GROUP, + VIR_IORT_NODE_TYPE_ROOT_COMPLEX); + DO_TEST("IORT_ampere", 36, + VIR_IORT_NODE_TYPE_ITS_GROUP, + VIR_IORT_NODE_TYPE_ROOT_COMPLEX, + VIR_IORT_NODE_TYPE_SMMUV3); + DO_TEST("IORT_gigabyte", 30, + VIR_IORT_NODE_TYPE_ITS_GROUP, + VIR_IORT_NODE_TYPE_ROOT_COMPLEX, + VIR_IORT_NODE_TYPE_SMMUV1_OR_SMMUV2); + DO_TEST("IORT_qualcomm", 69, + VIR_IORT_NODE_TYPE_ITS_GROUP, + VIR_IORT_NODE_TYPE_NAMED_COMPONENT, + VIR_IORT_NODE_TYPE_ROOT_COMPLEX, + VIR_IORT_NODE_TYPE_SMMUV3, + VIR_IORT_NODE_TYPE_PMCG); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain) -- 2.39.2

In my previous commit v9.2.0-rc1~3 I've made virt-host-validate to report host IOMMU check pass if IORT table is present. This is not sufficient though, because IORT describes much more than just IOMMU (well, it's called SMMU in ARM world). In fact, this can be seen in previous commit which adds test cases: there are tables (IORT_virt_aarch64) which does not contain any SMMU records. But after previous commits, we can parse the table so switch to that. Fixes: 2c13a2a7c9c368ea81eccd4ba12d9cf34bdd331b Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2178885 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Andrea Bolognani <abologna@redhat.com> --- tools/virt-host-validate-common.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/virt-host-validate-common.c b/tools/virt-host-validate-common.c index e96986bb48..c8a23e2e99 100644 --- a/tools/virt-host-validate-common.c +++ b/tools/virt-host-validate-common.c @@ -26,6 +26,7 @@ #include <sys/utsname.h> #include <sys/stat.h> +#include "viracpi.h" #include "viralloc.h" #include "vircgroup.h" #include "virfile.h" @@ -389,13 +390,24 @@ int virHostValidateIOMMU(const char *hvname, } virHostMsgPass(); } else if (ARCH_IS_ARM(arch)) { - if (access("/sys/firmware/acpi/tables/IORT", F_OK) == 0) { - virHostMsgPass(); - } else { + if (access("/sys/firmware/acpi/tables/IORT", F_OK) != 0) { virHostMsgFail(level, "No ACPI IORT table found, IOMMU not " "supported by this hardware platform"); return VIR_HOST_VALIDATE_FAILURE(level); + } else { + rc = virAcpiHasSMMU(); + if (rc < 0) { + virHostMsgFail(level, + "Failed to parse ACPI IORT table"); + return VIR_HOST_VALIDATE_FAILURE(level); + } else if (rc == 0) { + virHostMsgFail(level, + "No SMMU found"); + return VIR_HOST_VALIDATE_FAILURE(level); + } else { + virHostMsgPass(); + } } } else { virHostMsgFail(level, -- 2.39.2
participants (2)
-
Andrea Bolognani
-
Michal Privoznik