ea985d235b
These are the last users of the 128K SeaBIOS blob in the i440FX family. Removing them allows us to drop PCI support from the 128K blob, thus making it easier to update SeaBIOS to newer versions. Reviewed-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
384 lines
14 KiB
C
384 lines
14 KiB
C
#include "qemu/osdep.h"
|
|
#include "qapi/qmp/qdict.h"
|
|
#include "qapi/qmp/qlist.h"
|
|
#include "qapi/qmp/qnum.h"
|
|
#include "qapi/qmp/qbool.h"
|
|
#include "libqtest-single.h"
|
|
|
|
static char *get_cpu0_qom_path(void)
|
|
{
|
|
QDict *resp;
|
|
QList *ret;
|
|
QDict *cpu0;
|
|
char *path;
|
|
|
|
resp = qmp("{'execute': 'query-cpus-fast', 'arguments': {}}");
|
|
g_assert(qdict_haskey(resp, "return"));
|
|
ret = qdict_get_qlist(resp, "return");
|
|
|
|
cpu0 = qobject_to(QDict, qlist_peek(ret));
|
|
path = g_strdup(qdict_get_str(cpu0, "qom-path"));
|
|
qobject_unref(resp);
|
|
return path;
|
|
}
|
|
|
|
static QObject *qom_get(const char *path, const char *prop)
|
|
{
|
|
QDict *resp = qmp("{ 'execute': 'qom-get',"
|
|
" 'arguments': { 'path': %s,"
|
|
" 'property': %s } }",
|
|
path, prop);
|
|
QObject *ret = qdict_get(resp, "return");
|
|
qobject_ref(ret);
|
|
qobject_unref(resp);
|
|
return ret;
|
|
}
|
|
|
|
static bool qom_get_bool(const char *path, const char *prop)
|
|
{
|
|
QBool *value = qobject_to(QBool, qom_get(path, prop));
|
|
bool b = qbool_get_bool(value);
|
|
|
|
qobject_unref(value);
|
|
return b;
|
|
}
|
|
|
|
typedef struct CpuidTestArgs {
|
|
const char *cmdline;
|
|
const char *property;
|
|
int64_t expected_value;
|
|
} CpuidTestArgs;
|
|
|
|
static void test_cpuid_prop(const void *data)
|
|
{
|
|
const CpuidTestArgs *args = data;
|
|
char *path;
|
|
QNum *value;
|
|
int64_t val;
|
|
|
|
qtest_start(args->cmdline);
|
|
path = get_cpu0_qom_path();
|
|
value = qobject_to(QNum, qom_get(path, args->property));
|
|
g_assert(qnum_get_try_int(value, &val));
|
|
g_assert_cmpint(val, ==, args->expected_value);
|
|
qtest_end();
|
|
|
|
qobject_unref(value);
|
|
g_free(path);
|
|
}
|
|
|
|
static void add_cpuid_test(const char *name, const char *cmdline,
|
|
const char *property, int64_t expected_value)
|
|
{
|
|
CpuidTestArgs *args = g_new0(CpuidTestArgs, 1);
|
|
args->cmdline = cmdline;
|
|
args->property = property;
|
|
args->expected_value = expected_value;
|
|
qtest_add_data_func(name, args, test_cpuid_prop);
|
|
}
|
|
|
|
|
|
/* Parameters to a add_feature_test() test case */
|
|
typedef struct FeatureTestArgs {
|
|
/* cmdline to start QEMU */
|
|
const char *cmdline;
|
|
/*
|
|
* cpuid-input-eax and cpuid-input-ecx values to look for,
|
|
* in "feature-words" and "filtered-features" properties.
|
|
*/
|
|
uint32_t in_eax, in_ecx;
|
|
/* The register name to look for, in the X86CPUFeatureWordInfo array */
|
|
const char *reg;
|
|
/* The bit to check in X86CPUFeatureWordInfo.features */
|
|
int bitnr;
|
|
/* The expected value for the bit in (X86CPUFeatureWordInfo.features) */
|
|
bool expected_value;
|
|
} FeatureTestArgs;
|
|
|
|
/* Get the value for a feature word in a X86CPUFeatureWordInfo list */
|
|
static uint32_t get_feature_word(QList *features, uint32_t eax, uint32_t ecx,
|
|
const char *reg)
|
|
{
|
|
const QListEntry *e;
|
|
|
|
for (e = qlist_first(features); e; e = qlist_next(e)) {
|
|
QDict *w = qobject_to(QDict, qlist_entry_obj(e));
|
|
const char *rreg = qdict_get_str(w, "cpuid-register");
|
|
uint32_t reax = qdict_get_int(w, "cpuid-input-eax");
|
|
bool has_ecx = qdict_haskey(w, "cpuid-input-ecx");
|
|
uint32_t recx = 0;
|
|
int64_t val;
|
|
|
|
if (has_ecx) {
|
|
recx = qdict_get_int(w, "cpuid-input-ecx");
|
|
}
|
|
if (eax == reax && (!has_ecx || ecx == recx) && !strcmp(rreg, reg)) {
|
|
g_assert(qnum_get_try_int(qobject_to(QNum,
|
|
qdict_get(w, "features")),
|
|
&val));
|
|
return val;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void test_feature_flag(const void *data)
|
|
{
|
|
const FeatureTestArgs *args = data;
|
|
char *path;
|
|
QList *present, *filtered;
|
|
uint32_t value;
|
|
|
|
qtest_start(args->cmdline);
|
|
path = get_cpu0_qom_path();
|
|
present = qobject_to(QList, qom_get(path, "feature-words"));
|
|
filtered = qobject_to(QList, qom_get(path, "filtered-features"));
|
|
value = get_feature_word(present, args->in_eax, args->in_ecx, args->reg);
|
|
value |= get_feature_word(filtered, args->in_eax, args->in_ecx, args->reg);
|
|
qtest_end();
|
|
|
|
g_assert(!!(value & (1U << args->bitnr)) == args->expected_value);
|
|
|
|
qobject_unref(present);
|
|
qobject_unref(filtered);
|
|
g_free(path);
|
|
}
|
|
|
|
/*
|
|
* Add test case to ensure that a given feature flag is set in
|
|
* either "feature-words" or "filtered-features", when running QEMU
|
|
* using cmdline
|
|
*/
|
|
static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline,
|
|
uint32_t eax, uint32_t ecx,
|
|
const char *reg, int bitnr,
|
|
bool expected_value)
|
|
{
|
|
FeatureTestArgs *args = g_new0(FeatureTestArgs, 1);
|
|
args->cmdline = cmdline;
|
|
args->in_eax = eax;
|
|
args->in_ecx = ecx;
|
|
args->reg = reg;
|
|
args->bitnr = bitnr;
|
|
args->expected_value = expected_value;
|
|
qtest_add_data_func(name, args, test_feature_flag);
|
|
return args;
|
|
}
|
|
|
|
static void test_plus_minus_subprocess(void)
|
|
{
|
|
char *path;
|
|
|
|
/* Rules:
|
|
* 1)"-foo" overrides "+foo"
|
|
* 2) "[+-]foo" overrides "foo=..."
|
|
* 3) Old feature names with underscores (e.g. "sse4_2")
|
|
* should keep working
|
|
*
|
|
* Note: rules 1 and 2 are planned to be removed soon, and
|
|
* should generate a warning.
|
|
*/
|
|
qtest_start("-cpu pentium,-fpu,+fpu,-mce,mce=on,+cx8,cx8=off,+sse4_1,sse4_2=on");
|
|
path = get_cpu0_qom_path();
|
|
|
|
g_assert_false(qom_get_bool(path, "fpu"));
|
|
g_assert_false(qom_get_bool(path, "mce"));
|
|
g_assert_true(qom_get_bool(path, "cx8"));
|
|
|
|
/* Test both the original and the alias feature names: */
|
|
g_assert_true(qom_get_bool(path, "sse4-1"));
|
|
g_assert_true(qom_get_bool(path, "sse4.1"));
|
|
|
|
g_assert_true(qom_get_bool(path, "sse4-2"));
|
|
g_assert_true(qom_get_bool(path, "sse4.2"));
|
|
|
|
qtest_end();
|
|
g_free(path);
|
|
}
|
|
|
|
static void test_plus_minus(void)
|
|
{
|
|
g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0);
|
|
g_test_trap_assert_passed();
|
|
g_test_trap_assert_stderr("*Ambiguous CPU model string. "
|
|
"Don't mix both \"-mce\" and \"mce=on\"*");
|
|
g_test_trap_assert_stderr("*Ambiguous CPU model string. "
|
|
"Don't mix both \"+cx8\" and \"cx8=off\"*");
|
|
g_test_trap_assert_stdout("");
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess",
|
|
test_plus_minus_subprocess);
|
|
g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus);
|
|
|
|
/* Original level values for CPU models: */
|
|
add_cpuid_test("x86/cpuid/phenom/level",
|
|
"-cpu phenom", "level", 5);
|
|
add_cpuid_test("x86/cpuid/Conroe/level",
|
|
"-cpu Conroe", "level", 10);
|
|
add_cpuid_test("x86/cpuid/SandyBridge/level",
|
|
"-cpu SandyBridge", "level", 0xd);
|
|
add_cpuid_test("x86/cpuid/486/xlevel",
|
|
"-cpu 486", "xlevel", 0);
|
|
add_cpuid_test("x86/cpuid/core2duo/xlevel",
|
|
"-cpu core2duo", "xlevel", 0x80000008);
|
|
add_cpuid_test("x86/cpuid/phenom/xlevel",
|
|
"-cpu phenom", "xlevel", 0x8000001A);
|
|
add_cpuid_test("x86/cpuid/athlon/xlevel",
|
|
"-cpu athlon", "xlevel", 0x80000008);
|
|
|
|
/* If level is not large enough, it should increase automatically: */
|
|
/* CPUID[6].EAX: */
|
|
add_cpuid_test("x86/cpuid/auto-level/phenom/arat",
|
|
"-cpu 486,arat=on", "level", 6);
|
|
/* CPUID[EAX=7,ECX=0].EBX: */
|
|
add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase",
|
|
"-cpu phenom,fsgsbase=on", "level", 7);
|
|
/* CPUID[EAX=7,ECX=0].ECX: */
|
|
add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi",
|
|
"-cpu phenom,avx512vbmi=on", "level", 7);
|
|
/* CPUID[EAX=0xd,ECX=1].EAX: */
|
|
add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt",
|
|
"-cpu phenom,xsaveopt=on", "level", 0xd);
|
|
/* CPUID[8000_0001].EDX: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow",
|
|
"-cpu 486,3dnow=on", "xlevel", 0x80000001);
|
|
/* CPUID[8000_0001].ECX: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a",
|
|
"-cpu 486,sse4a=on", "xlevel", 0x80000001);
|
|
/* CPUID[8000_0007].EDX: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc",
|
|
"-cpu 486,invtsc=on", "xlevel", 0x80000007);
|
|
/* CPUID[8000_000A].EDX: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/486/npt",
|
|
"-cpu 486,svm=on,npt=on", "xlevel", 0x8000000A);
|
|
/* CPUID[C000_0001].EDX: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore",
|
|
"-cpu phenom,xstore=on", "xlevel2", 0xC0000001);
|
|
/* SVM needs CPUID[0x8000000A] */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm",
|
|
"-cpu athlon,svm=on", "xlevel", 0x8000000A);
|
|
|
|
|
|
/* If level is already large enough, it shouldn't change: */
|
|
add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple",
|
|
"-cpu SandyBridge,arat=on,fsgsbase=on,avx512vbmi=on",
|
|
"level", 0xd);
|
|
/* If level is explicitly set, it shouldn't change: */
|
|
add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF",
|
|
"-cpu 486,level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
|
|
"level", 0xF);
|
|
add_cpuid_test("x86/cpuid/auto-level/486/fixed/2",
|
|
"-cpu 486,level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
|
|
"level", 2);
|
|
add_cpuid_test("x86/cpuid/auto-level/486/fixed/0",
|
|
"-cpu 486,level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on",
|
|
"level", 0);
|
|
|
|
/* if xlevel is already large enough, it shouldn't change: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow",
|
|
"-cpu phenom,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
|
|
"xlevel", 0x8000001A);
|
|
/* If xlevel is explicitly set, it shouldn't change: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002",
|
|
"-cpu 486,xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
|
|
"xlevel", 0x80000002);
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A",
|
|
"-cpu 486,xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
|
|
"xlevel", 0x8000001A);
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0",
|
|
"-cpu 486,xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
|
|
"xlevel", 0);
|
|
|
|
/* if xlevel2 is already large enough, it shouldn't change: */
|
|
add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed",
|
|
"-cpu 486,xlevel2=0xC0000002,xstore=on",
|
|
"xlevel2", 0xC0000002);
|
|
|
|
/* Check compatibility of old machine-types that didn't
|
|
* auto-increase level/xlevel/xlevel2: */
|
|
if (qtest_has_machine("pc-i440fx-2.7")) {
|
|
add_cpuid_test("x86/cpuid/auto-level/pc-2.7",
|
|
"-machine pc-i440fx-2.7 -cpu 486,arat=on,avx512vbmi=on,xsaveopt=on",
|
|
"level", 1);
|
|
add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7",
|
|
"-machine pc-i440fx-2.7 -cpu 486,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on",
|
|
"xlevel", 0);
|
|
add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7",
|
|
"-machine pc-i440fx-2.7 -cpu 486,xstore=on",
|
|
"xlevel2", 0);
|
|
}
|
|
/*
|
|
* QEMU 2.3.0 had auto-level enabled for CPUID[7], already,
|
|
* and the compat code that sets default level shouldn't
|
|
* disable the auto-level=7 code:
|
|
*/
|
|
if (qtest_has_machine("pc-i440fx-2.3")) {
|
|
add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off",
|
|
"-machine pc-i440fx-2.3 -cpu Penryn",
|
|
"level", 4);
|
|
add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on",
|
|
"-machine pc-i440fx-2.3 -cpu Penryn,erms=on",
|
|
"level", 7);
|
|
}
|
|
if (qtest_has_machine("pc-i440fx-2.9")) {
|
|
add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off",
|
|
"-machine pc-i440fx-2.9 -cpu Conroe",
|
|
"level", 10);
|
|
add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on",
|
|
"-machine pc-i440fx-2.9 -cpu Conroe,erms=on",
|
|
"level", 10);
|
|
}
|
|
|
|
/*
|
|
* xlevel doesn't have any feature that triggers auto-level
|
|
* code on old machine-types. Just check that the compat code
|
|
* is working correctly:
|
|
*/
|
|
if (qtest_has_machine("pc-i440fx-2.3")) {
|
|
add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3",
|
|
"-machine pc-i440fx-2.3 -cpu SandyBridge",
|
|
"xlevel", 0x8000000a);
|
|
}
|
|
if (qtest_has_machine("pc-i440fx-2.4")) {
|
|
add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off",
|
|
"-machine pc-i440fx-2.4 -cpu SandyBridge,",
|
|
"xlevel", 0x80000008);
|
|
add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on",
|
|
"-machine pc-i440fx-2.4 -cpu SandyBridge,svm=on,npt=on",
|
|
"xlevel", 0x80000008);
|
|
}
|
|
|
|
/* Test feature parsing */
|
|
add_feature_test("x86/cpuid/features/plus",
|
|
"-cpu 486,+arat",
|
|
6, 0, "EAX", 2, true);
|
|
add_feature_test("x86/cpuid/features/minus",
|
|
"-cpu pentium,-mmx",
|
|
1, 0, "EDX", 23, false);
|
|
add_feature_test("x86/cpuid/features/on",
|
|
"-cpu 486,arat=on",
|
|
6, 0, "EAX", 2, true);
|
|
add_feature_test("x86/cpuid/features/off",
|
|
"-cpu pentium,mmx=off",
|
|
1, 0, "EDX", 23, false);
|
|
add_feature_test("x86/cpuid/features/max-plus-invtsc",
|
|
"-cpu max,+invtsc",
|
|
0x80000007, 0, "EDX", 8, true);
|
|
add_feature_test("x86/cpuid/features/max-invtsc-on",
|
|
"-cpu max,invtsc=on",
|
|
0x80000007, 0, "EDX", 8, true);
|
|
add_feature_test("x86/cpuid/features/max-minus-mmx",
|
|
"-cpu max,-mmx",
|
|
1, 0, "EDX", 23, false);
|
|
add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off",
|
|
"-cpu max,mmx=off",
|
|
1, 0, "EDX", 23, false);
|
|
|
|
return g_test_run();
|
|
}
|