cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

userspace_io_test.c (2870B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <fcntl.h>
      3#include <stdio.h>
      4#include <stdlib.h>
      5#include <string.h>
      6#include <sys/ioctl.h>
      7
      8#include "test_util.h"
      9
     10#include "kvm_util.h"
     11#include "processor.h"
     12
     13#define VCPU_ID			1
     14
     15static void guest_ins_port80(uint8_t *buffer, unsigned int count)
     16{
     17	unsigned long end;
     18
     19	if (count == 2)
     20		end = (unsigned long)buffer + 1;
     21	else
     22		end = (unsigned long)buffer + 8192;
     23
     24	asm volatile("cld; rep; insb" : "+D"(buffer), "+c"(count) : "d"(0x80) : "memory");
     25	GUEST_ASSERT_1(count == 0, count);
     26	GUEST_ASSERT_2((unsigned long)buffer == end, buffer, end);
     27}
     28
     29static void guest_code(void)
     30{
     31	uint8_t buffer[8192];
     32	int i;
     33
     34	/*
     35	 * Special case tests.  main() will adjust RCX 2 => 1 and 3 => 8192 to
     36	 * test that KVM doesn't explode when userspace modifies the "count" on
     37	 * a userspace I/O exit.  KVM isn't required to play nice with the I/O
     38	 * itself as KVM doesn't support manipulating the count, it just needs
     39	 * to not explode or overflow a buffer.
     40	 */
     41	guest_ins_port80(buffer, 2);
     42	guest_ins_port80(buffer, 3);
     43
     44	/* Verify KVM fills the buffer correctly when not stuffing RCX. */
     45	memset(buffer, 0, sizeof(buffer));
     46	guest_ins_port80(buffer, 8192);
     47	for (i = 0; i < 8192; i++)
     48		GUEST_ASSERT_2(buffer[i] == 0xaa, i, buffer[i]);
     49
     50	GUEST_DONE();
     51}
     52
     53int main(int argc, char *argv[])
     54{
     55	struct kvm_regs regs;
     56	struct kvm_run *run;
     57	struct kvm_vm *vm;
     58	struct ucall uc;
     59	int rc;
     60
     61	/* Tell stdout not to buffer its content */
     62	setbuf(stdout, NULL);
     63
     64	/* Create VM */
     65	vm = vm_create_default(VCPU_ID, 0, guest_code);
     66	run = vcpu_state(vm, VCPU_ID);
     67
     68	memset(&regs, 0, sizeof(regs));
     69
     70	while (1) {
     71		rc = _vcpu_run(vm, VCPU_ID);
     72
     73		TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc);
     74		TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
     75			    "Unexpected exit reason: %u (%s),\n",
     76			    run->exit_reason,
     77			    exit_reason_str(run->exit_reason));
     78
     79		if (get_ucall(vm, VCPU_ID, &uc))
     80			break;
     81
     82		TEST_ASSERT(run->io.port == 0x80,
     83			    "Expected I/O at port 0x80, got port 0x%x\n", run->io.port);
     84
     85		/*
     86		 * Modify the rep string count in RCX: 2 => 1 and 3 => 8192.
     87		 * Note, this abuses KVM's batching of rep string I/O to avoid
     88		 * getting stuck in an infinite loop.  That behavior isn't in
     89		 * scope from a testing perspective as it's not ABI in any way,
     90		 * i.e. it really is abusing internal KVM knowledge.
     91		 */
     92		vcpu_regs_get(vm, VCPU_ID, &regs);
     93		if (regs.rcx == 2)
     94			regs.rcx = 1;
     95		if (regs.rcx == 3)
     96			regs.rcx = 8192;
     97		memset((void *)run + run->io.data_offset, 0xaa, 4096);
     98		vcpu_regs_set(vm, VCPU_ID, &regs);
     99	}
    100
    101	switch (uc.cmd) {
    102	case UCALL_DONE:
    103		break;
    104	case UCALL_ABORT:
    105		TEST_FAIL("%s at %s:%ld : argN+1 = 0x%lx, argN+2 = 0x%lx",
    106			  (const char *)uc.args[0], __FILE__, uc.args[1],
    107			  uc.args[2], uc.args[3]);
    108	default:
    109		TEST_FAIL("Unknown ucall %lu", uc.cmd);
    110	}
    111
    112	kvm_vm_free(vm);
    113	return 0;
    114}