mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-28 19:41:16 +00:00
Modify virCPUarmCompare in cpu_arm.c to perform compare action. This patch only adds host to host CPU compare, the rest cases remains the same. This is useful for source and destination host compare during migrations to avoid migration between different CPU models that have different CPU freatures. Signed-off-by: Zhenyu Zheng <zheng.zhenyu@outlook.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
686 lines
17 KiB
C
686 lines
17 KiB
C
/*
|
|
* cpu_arm.c: CPU driver for arm CPUs
|
|
*
|
|
* Copyright (C) 2020 Huawei Technologies Co., Ltd.
|
|
* Copyright (C) 2013 Red Hat, Inc.
|
|
* Copyright (C) Canonical Ltd. 2012
|
|
*
|
|
* 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>
|
|
#if defined(__aarch64__)
|
|
# if defined(WITH_ASM_HWCAP_H)
|
|
# include <asm/hwcap.h>
|
|
# endif
|
|
# include <sys/auxv.h>
|
|
#endif
|
|
|
|
#include "viralloc.h"
|
|
#include "cpu.h"
|
|
#include "cpu_arm.h"
|
|
#include "cpu_map.h"
|
|
#include "virlog.h"
|
|
#include "virstring.h"
|
|
#include "virxml.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_CPU
|
|
#if defined(__aarch64__)
|
|
/* Shift bit mask for parsing cpu flags */
|
|
# define BIT_SHIFTS(n) (1UL << (n))
|
|
/* The current max number of cpu flags on ARM is 32 */
|
|
# define MAX_CPU_FLAGS 32
|
|
#endif
|
|
|
|
|
|
VIR_LOG_INIT("cpu.cpu_arm");
|
|
|
|
static const virArch archs[] = {
|
|
VIR_ARCH_ARMV6L,
|
|
VIR_ARCH_ARMV7B,
|
|
VIR_ARCH_ARMV7L,
|
|
VIR_ARCH_AARCH64,
|
|
};
|
|
|
|
typedef struct _virCPUarmVendor virCPUarmVendor;
|
|
typedef virCPUarmVendor *virCPUarmVendorPtr;
|
|
struct _virCPUarmVendor {
|
|
char *name;
|
|
unsigned long value;
|
|
};
|
|
|
|
typedef struct _virCPUarmModel virCPUarmModel;
|
|
typedef virCPUarmModel *virCPUarmModelPtr;
|
|
struct _virCPUarmModel {
|
|
char *name;
|
|
virCPUarmVendorPtr vendor;
|
|
virCPUarmData data;
|
|
};
|
|
|
|
typedef struct _virCPUarmFeature virCPUarmFeature;
|
|
typedef virCPUarmFeature *virCPUarmFeaturePtr;
|
|
struct _virCPUarmFeature {
|
|
char *name;
|
|
};
|
|
|
|
static virCPUarmFeaturePtr
|
|
virCPUarmFeatureNew(void)
|
|
{
|
|
return g_new0(virCPUarmFeature, 1);
|
|
}
|
|
|
|
static void
|
|
virCPUarmFeatureFree(virCPUarmFeaturePtr feature)
|
|
{
|
|
if (!feature)
|
|
return;
|
|
|
|
g_free(feature->name);
|
|
|
|
g_free(feature);
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPUarmFeature, virCPUarmFeatureFree);
|
|
|
|
typedef struct _virCPUarmMap virCPUarmMap;
|
|
typedef virCPUarmMap *virCPUarmMapPtr;
|
|
struct _virCPUarmMap {
|
|
size_t nvendors;
|
|
virCPUarmVendorPtr *vendors;
|
|
size_t nmodels;
|
|
virCPUarmModelPtr *models;
|
|
GPtrArray *features;
|
|
};
|
|
|
|
static virCPUarmMapPtr
|
|
virCPUarmMapNew(void)
|
|
{
|
|
virCPUarmMapPtr map;
|
|
|
|
map = g_new0(virCPUarmMap, 1);
|
|
|
|
map->features = g_ptr_array_new();
|
|
g_ptr_array_set_free_func(map->features,
|
|
(GDestroyNotify) virCPUarmFeatureFree);
|
|
|
|
return map;
|
|
}
|
|
|
|
static void
|
|
virCPUarmDataClear(virCPUarmData *data)
|
|
{
|
|
if (!data)
|
|
return;
|
|
|
|
g_strfreev(data->features);
|
|
}
|
|
|
|
static void
|
|
virCPUarmDataFree(virCPUDataPtr cpuData)
|
|
{
|
|
if (!cpuData)
|
|
return;
|
|
|
|
virCPUarmDataClear(&cpuData->data.arm);
|
|
g_free(cpuData);
|
|
}
|
|
|
|
static void
|
|
virCPUarmModelFree(virCPUarmModelPtr model)
|
|
{
|
|
if (!model)
|
|
return;
|
|
|
|
virCPUarmDataClear(&model->data);
|
|
g_free(model->name);
|
|
g_free(model);
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPUarmModel, virCPUarmModelFree);
|
|
|
|
static void
|
|
virCPUarmVendorFree(virCPUarmVendorPtr vendor)
|
|
{
|
|
if (!vendor)
|
|
return;
|
|
|
|
g_free(vendor->name);
|
|
g_free(vendor);
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPUarmVendor, virCPUarmVendorFree);
|
|
|
|
static void
|
|
virCPUarmMapFree(virCPUarmMapPtr map)
|
|
{
|
|
size_t i;
|
|
|
|
if (!map)
|
|
return;
|
|
|
|
for (i = 0; i < map->nmodels; i++)
|
|
virCPUarmModelFree(map->models[i]);
|
|
g_free(map->models);
|
|
|
|
for (i = 0; i < map->nvendors; i++)
|
|
virCPUarmVendorFree(map->vendors[i]);
|
|
g_free(map->vendors);
|
|
|
|
g_ptr_array_free(map->features, TRUE);
|
|
|
|
g_free(map);
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPUarmMap, virCPUarmMapFree);
|
|
|
|
static virCPUarmFeaturePtr
|
|
virCPUarmMapFeatureFind(virCPUarmMapPtr map,
|
|
const char *name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < map->features->len; i++) {
|
|
virCPUarmFeaturePtr feature = g_ptr_array_index(map->features, i);
|
|
|
|
if (STREQ(feature->name, name))
|
|
return feature;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
virCPUarmMapFeatureParse(xmlXPathContextPtr ctxt G_GNUC_UNUSED,
|
|
const char *name,
|
|
void *data)
|
|
{
|
|
g_autoptr(virCPUarmFeature) feature = NULL;
|
|
virCPUarmMapPtr map = data;
|
|
|
|
if (virCPUarmMapFeatureFind(map, name)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("CPU feature %s already defined"), name);
|
|
return -1;
|
|
}
|
|
|
|
feature = virCPUarmFeatureNew();
|
|
feature->name = g_strdup(name);
|
|
|
|
g_ptr_array_add(map->features, g_steal_pointer(&feature));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static virCPUarmVendorPtr
|
|
virCPUarmVendorFindByID(virCPUarmMapPtr map,
|
|
unsigned long vendor_id)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < map->nvendors; i++) {
|
|
if (map->vendors[i]->value == vendor_id)
|
|
return map->vendors[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static virCPUarmVendorPtr
|
|
virCPUarmVendorFindByName(virCPUarmMapPtr map,
|
|
const char *name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < map->nvendors; i++) {
|
|
if (STREQ(map->vendors[i]->name, name))
|
|
return map->vendors[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
virCPUarmVendorParse(xmlXPathContextPtr ctxt,
|
|
const char *name,
|
|
void *data)
|
|
{
|
|
virCPUarmMapPtr map = data;
|
|
g_autoptr(virCPUarmVendor) vendor = NULL;
|
|
|
|
vendor = g_new0(virCPUarmVendor, 1);
|
|
vendor->name = g_strdup(name);
|
|
|
|
if (virCPUarmVendorFindByName(map, vendor->name)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("CPU vendor %s already defined"),
|
|
vendor->name);
|
|
return -1;
|
|
}
|
|
|
|
if (virXPathULongHex("string(@value)", ctxt, &vendor->value) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Missing CPU vendor value"));
|
|
return -1;
|
|
}
|
|
|
|
if (virCPUarmVendorFindByID(map, vendor->value)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("CPU vendor value 0x%2lx already defined"),
|
|
vendor->value);
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_APPEND_ELEMENT(map->vendors, map->nvendors, vendor) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static virCPUarmModelPtr
|
|
virCPUarmModelFind(virCPUarmMapPtr map,
|
|
const char *name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < map->nmodels; i++) {
|
|
if (STREQ(map->models[i]->name, name))
|
|
return map->models[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(__aarch64__)
|
|
static virCPUarmModelPtr
|
|
virCPUarmModelFindByPVR(virCPUarmMapPtr map,
|
|
unsigned long pvr)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < map->nmodels; i++) {
|
|
if (map->models[i]->data.pvr == pvr)
|
|
return map->models[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
virCPUarmModelParse(xmlXPathContextPtr ctxt,
|
|
const char *name,
|
|
void *data)
|
|
{
|
|
virCPUarmMapPtr map = data;
|
|
g_autoptr(virCPUarmModel) model = NULL;
|
|
g_autofree char *vendor = NULL;
|
|
|
|
model = g_new0(virCPUarmModel, 1);
|
|
model->name = g_strdup(name);
|
|
|
|
if (virCPUarmModelFind(map, model->name)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("CPU model %s already defined"),
|
|
model->name);
|
|
return -1;
|
|
}
|
|
|
|
if (virXPathBoolean("boolean(./vendor)", ctxt)) {
|
|
vendor = virXPathString("string(./vendor/@name)", ctxt);
|
|
if (!vendor) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid vendor element in CPU model %s"),
|
|
model->name);
|
|
return -1;
|
|
}
|
|
|
|
if (!(model->vendor = virCPUarmVendorFindByName(map, vendor))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unknown vendor %s referenced by CPU model %s"),
|
|
vendor, model->name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (!virXPathBoolean("boolean(./pvr)", ctxt)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Missing PVR information for CPU model %s"),
|
|
model->name);
|
|
return -1;
|
|
}
|
|
|
|
if (virXPathULongHex("string(./pvr/@value)", ctxt, &model->data.pvr) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Missing or invalid PVR value in CPU model %s"),
|
|
model->name);
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_APPEND_ELEMENT(map->models, map->nmodels, model) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static virCPUarmMapPtr
|
|
virCPUarmLoadMap(void)
|
|
{
|
|
g_autoptr(virCPUarmMap) map = NULL;
|
|
|
|
map = virCPUarmMapNew();
|
|
|
|
if (cpuMapLoad("arm", virCPUarmVendorParse, virCPUarmMapFeatureParse,
|
|
virCPUarmModelParse, map) < 0)
|
|
return NULL;
|
|
|
|
return g_steal_pointer(&map);
|
|
}
|
|
|
|
static virCPUarmMapPtr cpuMap;
|
|
|
|
int virCPUarmDriverOnceInit(void);
|
|
VIR_ONCE_GLOBAL_INIT(virCPUarmDriver);
|
|
|
|
int
|
|
virCPUarmDriverOnceInit(void)
|
|
{
|
|
if (!(cpuMap = virCPUarmLoadMap()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static virCPUarmMapPtr
|
|
virCPUarmGetMap(void)
|
|
{
|
|
if (virCPUarmDriverInitialize() < 0)
|
|
return NULL;
|
|
|
|
return cpuMap;
|
|
}
|
|
|
|
static int
|
|
virCPUarmUpdate(virCPUDefPtr guest,
|
|
const virCPUDef *host)
|
|
{
|
|
g_autoptr(virCPUDef) updated = NULL;
|
|
|
|
if (guest->mode != VIR_CPU_MODE_HOST_MODEL)
|
|
return 0;
|
|
|
|
if (!host) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unknown host CPU model"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(updated = virCPUDefCopyWithoutModel(guest)))
|
|
return -1;
|
|
|
|
updated->mode = VIR_CPU_MODE_CUSTOM;
|
|
if (virCPUDefCopyModel(updated, host, true) < 0)
|
|
return -1;
|
|
|
|
virCPUDefStealModel(guest, updated, false);
|
|
guest->mode = VIR_CPU_MODE_CUSTOM;
|
|
guest->match = VIR_CPU_MATCH_EXACT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static virCPUDefPtr
|
|
virCPUarmBaseline(virCPUDefPtr *cpus,
|
|
unsigned int ncpus G_GNUC_UNUSED,
|
|
virDomainCapsCPUModelsPtr models G_GNUC_UNUSED,
|
|
const char **features G_GNUC_UNUSED,
|
|
bool migratable G_GNUC_UNUSED)
|
|
{
|
|
virCPUDefPtr cpu = NULL;
|
|
|
|
cpu = virCPUDefNew();
|
|
|
|
cpu->model = g_strdup(cpus[0]->model);
|
|
|
|
cpu->type = VIR_CPU_TYPE_GUEST;
|
|
cpu->match = VIR_CPU_MATCH_EXACT;
|
|
|
|
return cpu;
|
|
}
|
|
|
|
static virCPUCompareResult
|
|
virCPUarmCompare(virCPUDefPtr host,
|
|
virCPUDefPtr cpu,
|
|
bool failIncompatible)
|
|
{
|
|
/* Only support host to host CPU compare for ARM */
|
|
if (cpu->type != VIR_CPU_TYPE_HOST)
|
|
return VIR_CPU_COMPARE_IDENTICAL;
|
|
|
|
if (!host || !host->model) {
|
|
if (failIncompatible) {
|
|
virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
|
|
_("unknown host CPU"));
|
|
return VIR_CPU_COMPARE_ERROR;
|
|
}
|
|
|
|
VIR_WARN("unknown host CPU");
|
|
return VIR_CPU_COMPARE_INCOMPATIBLE;
|
|
}
|
|
|
|
/* Compare vendor and model to check if CPUs are identical */
|
|
if (STRNEQ_NULLABLE(host->vendor, cpu->vendor) ||
|
|
STRNEQ_NULLABLE(host->model, cpu->model)) {
|
|
VIR_DEBUG("Host CPU model does not match required CPU "
|
|
"vendor %s or(and) model %s",
|
|
NULLSTR(cpu->vendor), NULLSTR(cpu->model));
|
|
|
|
if (failIncompatible) {
|
|
virReportError(VIR_ERR_CPU_INCOMPATIBLE,
|
|
_("Host CPU model does not match required CPU "
|
|
"vendor %s or(and) model %s"),
|
|
NULLSTR(cpu->vendor), NULLSTR(cpu->model));
|
|
return VIR_CPU_COMPARE_ERROR;
|
|
}
|
|
|
|
return VIR_CPU_COMPARE_INCOMPATIBLE;
|
|
}
|
|
|
|
return VIR_CPU_COMPARE_IDENTICAL;
|
|
}
|
|
|
|
static int
|
|
virCPUarmValidateFeatures(virCPUDefPtr cpu)
|
|
{
|
|
virCPUarmMapPtr map;
|
|
size_t i;
|
|
|
|
if (!(map = virCPUarmGetMap()))
|
|
return -1;
|
|
|
|
for (i = 0; i < cpu->nfeatures; i++) {
|
|
virCPUFeatureDefPtr feature = &cpu->features[i];
|
|
|
|
if (!virCPUarmMapFeatureFind(map, feature->name)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unknown CPU feature: %s"),
|
|
feature->name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(__aarch64__)
|
|
/* Generate human readable flag list according to the order of */
|
|
/* AT_HWCAP bit map */
|
|
const char *aarch64_cpu_flags[MAX_CPU_FLAGS] = {
|
|
"fp", "asimd", "evtstrm", "aes", "pmull", "sha1", "sha2",
|
|
"crc32", "atomics", "fphp", "asimdhp", "cpuid", "asimdrdm",
|
|
"jscvt", "fcma", "lrcpc", "dcpop", "sha3", "sm3", "sm4",
|
|
"asimddp", "sha512", "sve", "asimdfhm", "dit", "uscat",
|
|
"ilrcpc", "flagm", "ssbs", "sb", "paca", "pacg"};
|
|
/**
|
|
* virCPUarmCpuDataFromRegs:
|
|
*
|
|
* @data: 64-bit arm CPU specific data
|
|
*
|
|
* Fetches CPU vendor_id and part_id from MIDR_EL1 register, parse CPU
|
|
* flags from AT_HWCAP. There are currently 32 valid flags on ARM arch
|
|
* represented by each bit.
|
|
*/
|
|
static int
|
|
virCPUarmCpuDataFromRegs(virCPUarmData *data)
|
|
{
|
|
unsigned long cpuid;
|
|
unsigned long hwcaps;
|
|
VIR_AUTOSTRINGLIST features = NULL;
|
|
int cpu_feature_index = 0;
|
|
size_t i;
|
|
|
|
# if defined(WITH_GETAUXVAL)
|
|
if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("CPUID registers unavailable"));
|
|
return -1;
|
|
}
|
|
# endif
|
|
|
|
/* read the cpuid data from MIDR_EL1 register */
|
|
asm("mrs %0, MIDR_EL1" : "=r" (cpuid));
|
|
VIR_DEBUG("CPUID read from register: 0x%016lx", cpuid);
|
|
|
|
/* parse the corresponding part_id bits */
|
|
data->pvr = (cpuid >> 4) & 0xfff;
|
|
/* parse the corresponding vendor_id bits */
|
|
data->vendor_id = (cpuid >> 24) & 0xff;
|
|
|
|
# if defined(WITH_GETAUXVAL)
|
|
hwcaps = getauxval(AT_HWCAP);
|
|
# elif defined(WITH_ELF_AUX_INFO)
|
|
elf_aux_info(AT_HWCAP, &hwcaps, sizeof(u_long));
|
|
# else
|
|
# error No routines to retrieve a value from the auxiliary vector
|
|
# endif
|
|
VIR_DEBUG("CPU flags read from register: 0x%016lx", hwcaps);
|
|
|
|
features = g_new0(char *, MAX_CPU_FLAGS + 1);
|
|
|
|
/* shift bit map mask to parse for CPU flags */
|
|
for (i = 0; i < MAX_CPU_FLAGS; i++) {
|
|
if (hwcaps & BIT_SHIFTS(i)) {
|
|
features[cpu_feature_index] = g_strdup(aarch64_cpu_flags[i]);
|
|
cpu_feature_index++;
|
|
}
|
|
}
|
|
|
|
if (cpu_feature_index > 0)
|
|
data->features = g_steal_pointer(&features);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virCPUarmDecode(virCPUDefPtr cpu,
|
|
const virCPUarmData *cpuData,
|
|
virDomainCapsCPUModelsPtr models)
|
|
{
|
|
size_t i;
|
|
virCPUarmMapPtr map;
|
|
virCPUarmModelPtr model;
|
|
virCPUarmVendorPtr vendor = NULL;
|
|
|
|
if (!cpuData || !(map = virCPUarmGetMap()))
|
|
return -1;
|
|
|
|
if (!(model = virCPUarmModelFindByPVR(map, cpuData->pvr))) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Cannot find CPU model with PVR 0x%03lx"),
|
|
cpuData->pvr);
|
|
return -1;
|
|
}
|
|
|
|
if (!virCPUModelIsAllowed(model->name, models)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("CPU model %s is not supported by hypervisor"),
|
|
model->name);
|
|
return -1;
|
|
}
|
|
|
|
cpu->model = g_strdup(model->name);
|
|
|
|
if (cpuData->vendor_id &&
|
|
!(vendor = virCPUarmVendorFindByID(map, cpuData->vendor_id))) {
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
|
_("Cannot find CPU vendor with vendor id 0x%02lx"),
|
|
cpuData->vendor_id);
|
|
return -1;
|
|
}
|
|
|
|
if (vendor)
|
|
cpu->vendor = g_strdup(vendor->name);
|
|
|
|
if (cpuData->features) {
|
|
cpu->nfeatures = virStringListLength((const char **)cpuData->features);
|
|
cpu->features = g_new0(virCPUFeatureDef, cpu->nfeatures);
|
|
|
|
for (i = 0; i < cpu->nfeatures; i++) {
|
|
cpu->features[i].policy = VIR_CPU_FEATURE_REQUIRE;
|
|
cpu->features[i].name = g_strdup(cpuData->features[i]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virCPUarmGetHost(virCPUDefPtr cpu,
|
|
virDomainCapsCPUModelsPtr models)
|
|
{
|
|
g_autoptr(virCPUData) cpuData = NULL;
|
|
|
|
if (virCPUarmDriverInitialize() < 0)
|
|
return -1;
|
|
|
|
if (!(cpuData = virCPUDataNew(archs[0])))
|
|
return -1;
|
|
|
|
if (virCPUarmCpuDataFromRegs(&cpuData->data.arm) < 0)
|
|
return -1;
|
|
|
|
return virCPUarmDecode(cpu, &cpuData->data.arm, models);
|
|
}
|
|
#endif
|
|
|
|
|
|
struct cpuArchDriver cpuDriverArm = {
|
|
.name = "arm",
|
|
.arch = archs,
|
|
.narch = G_N_ELEMENTS(archs),
|
|
.compare = virCPUarmCompare,
|
|
#if defined(__aarch64__)
|
|
.getHost = virCPUarmGetHost,
|
|
#endif
|
|
.decode = NULL,
|
|
.encode = NULL,
|
|
.dataFree = virCPUarmDataFree,
|
|
.baseline = virCPUarmBaseline,
|
|
.update = virCPUarmUpdate,
|
|
.validateFeatures = virCPUarmValidateFeatures,
|
|
};
|