Path: blob/master/tools/testing/selftests/kvm/x86/amx_test.c
38236 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* amx tests3*4* Copyright (C) 2021, Intel, Inc.5*6* Tests for amx #NM exception and save/restore.7*/8#include <fcntl.h>9#include <stdio.h>10#include <stdlib.h>11#include <string.h>12#include <sys/ioctl.h>13#include <sys/syscall.h>1415#include "test_util.h"1617#include "kvm_util.h"18#include "processor.h"19#include "vmx.h"2021#ifndef __x86_64__22# error This test is 64-bit only23#endif2425#define NUM_TILES 826#define TILE_SIZE 102427#define XSAVE_SIZE ((NUM_TILES * TILE_SIZE) + PAGE_SIZE)2829/* Tile configuration associated: */30#define PALETTE_TABLE_INDEX 131#define MAX_TILES 1632#define RESERVED_BYTES 143334#define XSAVE_HDR_OFFSET 5123536struct tile_config {37u8 palette_id;38u8 start_row;39u8 reserved[RESERVED_BYTES];40u16 colsb[MAX_TILES];41u8 rows[MAX_TILES];42};4344struct tile_data {45u8 data[NUM_TILES * TILE_SIZE];46};4748struct xtile_info {49u16 bytes_per_tile;50u16 bytes_per_row;51u16 max_names;52u16 max_rows;53u32 xsave_offset;54u32 xsave_size;55};5657static struct xtile_info xtile;5859static inline void __ldtilecfg(void *cfg)60{61asm volatile(".byte 0xc4,0xe2,0x78,0x49,0x00"62: : "a"(cfg));63}6465static inline void __tileloadd(void *tile)66{67asm volatile(".byte 0xc4,0xe2,0x7b,0x4b,0x04,0x10"68: : "a"(tile), "d"(0));69}7071static inline void __tilerelease(void)72{73asm volatile(".byte 0xc4, 0xe2, 0x78, 0x49, 0xc0" ::);74}7576static inline void __xsavec(struct xstate *xstate, uint64_t rfbm)77{78uint32_t rfbm_lo = rfbm;79uint32_t rfbm_hi = rfbm >> 32;8081asm volatile("xsavec (%%rdi)"82: : "D" (xstate), "a" (rfbm_lo), "d" (rfbm_hi)83: "memory");84}8586static void check_xtile_info(void)87{88GUEST_ASSERT((xgetbv(0) & XFEATURE_MASK_XTILE) == XFEATURE_MASK_XTILE);8990GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_XSTATE_MAX_SIZE_XCR0));91GUEST_ASSERT(this_cpu_property(X86_PROPERTY_XSTATE_MAX_SIZE_XCR0) <= XSAVE_SIZE);9293xtile.xsave_offset = this_cpu_property(X86_PROPERTY_XSTATE_TILE_OFFSET);94GUEST_ASSERT(xtile.xsave_offset == 2816);95xtile.xsave_size = this_cpu_property(X86_PROPERTY_XSTATE_TILE_SIZE);96GUEST_ASSERT(xtile.xsave_size == 8192);97GUEST_ASSERT(sizeof(struct tile_data) >= xtile.xsave_size);9899GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_AMX_MAX_PALETTE_TABLES));100GUEST_ASSERT(this_cpu_property(X86_PROPERTY_AMX_MAX_PALETTE_TABLES) >=101PALETTE_TABLE_INDEX);102103GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_AMX_NR_TILE_REGS));104xtile.max_names = this_cpu_property(X86_PROPERTY_AMX_NR_TILE_REGS);105GUEST_ASSERT(xtile.max_names == 8);106xtile.bytes_per_tile = this_cpu_property(X86_PROPERTY_AMX_BYTES_PER_TILE);107GUEST_ASSERT(xtile.bytes_per_tile == 1024);108xtile.bytes_per_row = this_cpu_property(X86_PROPERTY_AMX_BYTES_PER_ROW);109GUEST_ASSERT(xtile.bytes_per_row == 64);110xtile.max_rows = this_cpu_property(X86_PROPERTY_AMX_MAX_ROWS);111GUEST_ASSERT(xtile.max_rows == 16);112}113114static void set_tilecfg(struct tile_config *cfg)115{116int i;117118/* Only palette id 1 */119cfg->palette_id = 1;120for (i = 0; i < xtile.max_names; i++) {121cfg->colsb[i] = xtile.bytes_per_row;122cfg->rows[i] = xtile.max_rows;123}124}125126static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,127struct tile_data *tiledata,128struct xstate *xstate)129{130GUEST_ASSERT(this_cpu_has(X86_FEATURE_XSAVE) &&131this_cpu_has(X86_FEATURE_OSXSAVE));132check_xtile_info();133GUEST_SYNC(1);134135/* xfd=0, enable amx */136wrmsr(MSR_IA32_XFD, 0);137GUEST_SYNC(2);138GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == 0);139set_tilecfg(amx_cfg);140__ldtilecfg(amx_cfg);141GUEST_SYNC(3);142/* Check save/restore when trap to userspace */143__tileloadd(tiledata);144GUEST_SYNC(4);145__tilerelease();146GUEST_SYNC(5);147/*148* After XSAVEC, XTILEDATA is cleared in the xstate_bv but is set in149* the xcomp_bv.150*/151xstate->header.xstate_bv = XFEATURE_MASK_XTILE_DATA;152__xsavec(xstate, XFEATURE_MASK_XTILE_DATA);153GUEST_ASSERT(!(xstate->header.xstate_bv & XFEATURE_MASK_XTILE_DATA));154GUEST_ASSERT(xstate->header.xcomp_bv & XFEATURE_MASK_XTILE_DATA);155156/* xfd=0x40000, disable amx tiledata */157wrmsr(MSR_IA32_XFD, XFEATURE_MASK_XTILE_DATA);158159/*160* XTILEDATA is cleared in xstate_bv but set in xcomp_bv, this property161* remains the same even when amx tiledata is disabled by IA32_XFD.162*/163xstate->header.xstate_bv = XFEATURE_MASK_XTILE_DATA;164__xsavec(xstate, XFEATURE_MASK_XTILE_DATA);165GUEST_ASSERT(!(xstate->header.xstate_bv & XFEATURE_MASK_XTILE_DATA));166GUEST_ASSERT((xstate->header.xcomp_bv & XFEATURE_MASK_XTILE_DATA));167168GUEST_SYNC(6);169GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);170set_tilecfg(amx_cfg);171__ldtilecfg(amx_cfg);172/* Trigger #NM exception */173__tileloadd(tiledata);174GUEST_SYNC(10);175176GUEST_DONE();177}178179void guest_nm_handler(struct ex_regs *regs)180{181/* Check if #NM is triggered by XFEATURE_MASK_XTILE_DATA */182GUEST_SYNC(7);183GUEST_ASSERT(!(get_cr0() & X86_CR0_TS));184GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILE_DATA);185GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);186GUEST_SYNC(8);187GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILE_DATA);188GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);189/* Clear xfd_err */190wrmsr(MSR_IA32_XFD_ERR, 0);191/* xfd=0, enable amx */192wrmsr(MSR_IA32_XFD, 0);193GUEST_SYNC(9);194}195196int main(int argc, char *argv[])197{198struct kvm_regs regs1, regs2;199struct kvm_vcpu *vcpu;200struct kvm_vm *vm;201struct kvm_x86_state *state;202int xsave_restore_size;203vm_vaddr_t amx_cfg, tiledata, xstate;204struct ucall uc;205u32 amx_offset;206int ret;207208/*209* Note, all off-by-default features must be enabled before anything210* caches KVM_GET_SUPPORTED_CPUID, e.g. before using kvm_cpu_has().211*/212vm_xsave_require_permission(XFEATURE_MASK_XTILE_DATA);213214TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XFD));215TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));216TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_AMX_TILE));217TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILECFG));218TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA));219TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA_XFD));220221/* Create VM */222vm = vm_create_with_one_vcpu(&vcpu, guest_code);223224TEST_ASSERT(kvm_cpu_has_p(X86_PROPERTY_XSTATE_MAX_SIZE),225"KVM should enumerate max XSAVE size when XSAVE is supported");226xsave_restore_size = kvm_cpu_property(X86_PROPERTY_XSTATE_MAX_SIZE);227228vcpu_regs_get(vcpu, ®s1);229230/* Register #NM handler */231vm_install_exception_handler(vm, NM_VECTOR, guest_nm_handler);232233/* amx cfg for guest_code */234amx_cfg = vm_vaddr_alloc_page(vm);235memset(addr_gva2hva(vm, amx_cfg), 0x0, getpagesize());236237/* amx tiledata for guest_code */238tiledata = vm_vaddr_alloc_pages(vm, 2);239memset(addr_gva2hva(vm, tiledata), rand() | 1, 2 * getpagesize());240241/* XSAVE state for guest_code */242xstate = vm_vaddr_alloc_pages(vm, DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE));243memset(addr_gva2hva(vm, xstate), 0, PAGE_SIZE * DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE));244vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xstate);245246for (;;) {247vcpu_run(vcpu);248TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);249250switch (get_ucall(vcpu, &uc)) {251case UCALL_ABORT:252REPORT_GUEST_ASSERT(uc);253/* NOT REACHED */254case UCALL_SYNC:255switch (uc.args[1]) {256case 1:257case 2:258case 3:259case 5:260case 6:261case 7:262case 8:263fprintf(stderr, "GUEST_SYNC(%ld)\n", uc.args[1]);264break;265case 4:266case 10:267fprintf(stderr,268"GUEST_SYNC(%ld), check save/restore status\n", uc.args[1]);269270/* Compacted mode, get amx offset by xsave area271* size subtract 8K amx size.272*/273amx_offset = xsave_restore_size - NUM_TILES*TILE_SIZE;274state = vcpu_save_state(vcpu);275void *amx_start = (void *)state->xsave + amx_offset;276void *tiles_data = (void *)addr_gva2hva(vm, tiledata);277/* Only check TMM0 register, 1 tile */278ret = memcmp(amx_start, tiles_data, TILE_SIZE);279TEST_ASSERT(ret == 0, "memcmp failed, ret=%d", ret);280kvm_x86_state_cleanup(state);281break;282case 9:283fprintf(stderr,284"GUEST_SYNC(%ld), #NM exception and enable amx\n", uc.args[1]);285break;286}287break;288case UCALL_DONE:289fprintf(stderr, "UCALL_DONE\n");290goto done;291default:292TEST_FAIL("Unknown ucall %lu", uc.cmd);293}294295state = vcpu_save_state(vcpu);296memset(®s1, 0, sizeof(regs1));297vcpu_regs_get(vcpu, ®s1);298299kvm_vm_release(vm);300301/* Restore state in a new VM. */302vcpu = vm_recreate_with_one_vcpu(vm);303vcpu_load_state(vcpu, state);304kvm_x86_state_cleanup(state);305306memset(®s2, 0, sizeof(regs2));307vcpu_regs_get(vcpu, ®s2);308TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)),309"Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx",310(ulong) regs2.rdi, (ulong) regs2.rsi);311}312done:313kvm_vm_free(vm);314}315316317