From 1bf21c92db6af5f8263977d8b99706f8ab9b3a27 Mon Sep 17 00:00:00 2001 From: Patrick Mooney Date: Wed, 2 Nov 2022 01:50:04 +0000 Subject: 15136 want unit test for bhyve dirty page tracking Reviewed by: Greg Colombo Approved by: Dan McDonald --- usr/src/pkg/manifests/system-bhyve-tests.p5m | 1 + usr/src/test/bhyve-tests/tests/inst_emul/Makefile | 3 +- .../test/bhyve-tests/tests/inst_emul/page_dirty.c | 162 +++++++++++++++++++++ .../tests/inst_emul/payload_page_dirty.s | 29 ++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c create mode 100644 usr/src/test/bhyve-tests/tests/inst_emul/payload_page_dirty.s diff --git a/usr/src/pkg/manifests/system-bhyve-tests.p5m b/usr/src/pkg/manifests/system-bhyve-tests.p5m index 9c98c34969..92985ee1cb 100644 --- a/usr/src/pkg/manifests/system-bhyve-tests.p5m +++ b/usr/src/pkg/manifests/system-bhyve-tests.p5m @@ -34,6 +34,7 @@ dir path=opt/bhyve-tests/tests/inst_emul file path=opt/bhyve-tests/tests/inst_emul/cpuid mode=0555 file path=opt/bhyve-tests/tests/inst_emul/exit_paging mode=0555 file path=opt/bhyve-tests/tests/inst_emul/imul mode=0555 +file path=opt/bhyve-tests/tests/inst_emul/page_dirty mode=0555 file path=opt/bhyve-tests/tests/inst_emul/rdmsr mode=0555 file path=opt/bhyve-tests/tests/inst_emul/triple_fault mode=0555 file path=opt/bhyve-tests/tests/inst_emul/wrmsr mode=0555 diff --git a/usr/src/test/bhyve-tests/tests/inst_emul/Makefile b/usr/src/test/bhyve-tests/tests/inst_emul/Makefile index 37a8b62b4f..f01a9c3bee 100644 --- a/usr/src/test/bhyve-tests/tests/inst_emul/Makefile +++ b/usr/src/test/bhyve-tests/tests/inst_emul/Makefile @@ -23,7 +23,8 @@ PROG = rdmsr \ # These should probably go in the `vmm` tests, but since they depend on # in-guest payloads, it is easier to build them here. PROG += triple_fault \ - exit_paging + exit_paging \ + page_dirty # C-based payloads need additional utils object CPAYLOADS = cpuid diff --git a/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c b/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c new file mode 100644 index 0000000000..2e3a06bf47 --- /dev/null +++ b/usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c @@ -0,0 +1,162 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 Oxide Computer Company + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "in_guest.h" + +#define PAGE_SZ 4096 + +#define DIRTY_BITMAP_SZ (MEM_TOTAL_SZ / (PAGE_SZ * 8)) + +static void +read_dirty_bitmap(struct vmctx *ctx, uint8_t *bitmap) +{ + struct vmm_dirty_tracker track = { + .vdt_start_gpa = 0, + .vdt_len = MEM_TOTAL_SZ, + .vdt_pfns = (void *)bitmap, + }; + int err = ioctl(vm_get_device_fd(ctx), VM_TRACK_DIRTY_PAGES, &track); + if (err != 0) { + test_fail_errno(errno, "Could not get dirty page bitmap"); + } +} + +static uint8_t +popc8(uint8_t val) +{ + uint8_t cnt; + + for (cnt = 0; val != 0; val &= (val - 1)) { + cnt++; + } + return (cnt); +} + +static uint_t +count_dirty_pages(const uint8_t *bitmap) +{ + uint_t count = 0; + for (uint_t i = 0; i < DIRTY_BITMAP_SZ; i++) { + count += popc8(bitmap[i]); + } + return (count); +} + +int +main(int argc, char *argv[]) +{ + const char *test_suite_name = basename(argv[0]); + struct vmctx *ctx = NULL; + int err; + + ctx = test_initialize(test_suite_name); + + /* Until #14251 is fixed, warn the user of the test requirement */ + (void) fprintf(stderr, + "Ensure that 'gpt_track_dirty' is set to 1 via mdb -kw\n" + "The reasoning is described in illumos #14251\n"); + + err = test_setup_vcpu(ctx, 0, MEM_LOC_PAYLOAD, MEM_LOC_STACK); + if (err != 0) { + test_fail_errno(err, "Could not initialize vcpu0"); + } + + uint8_t dirty_bitmap[DIRTY_BITMAP_SZ] = { 0 }; + + /* Clear pages which were dirtied as part of initialization */ + read_dirty_bitmap(ctx, dirty_bitmap); + if (count_dirty_pages(dirty_bitmap) == 0) { + test_fail_msg("no pages dirtied during setup\n"); + } + + /* + * With nothing running, and the old dirty bits cleared, the NPT should + * now be devoid of pages marked dirty. + */ + read_dirty_bitmap(ctx, dirty_bitmap); + if (count_dirty_pages(dirty_bitmap) != 0) { + test_fail_msg("pages still dirty after clear\n"); + } + + /* Dirty a page through the segvmm mapping. */ + uint8_t *dptr = vm_map_gpa(ctx, MEM_LOC_STACK, 1); + *dptr = 1; + + /* Check that it was marked as such */ + read_dirty_bitmap(ctx, dirty_bitmap); + if (count_dirty_pages(dirty_bitmap) != 1) { + test_fail_msg("direct access did not dirty page\n"); + } + if (dirty_bitmap[MEM_LOC_STACK / (PAGE_SZ * 8)] == 0) { + test_fail_msg("unexpected page dirtied\n"); + } + + + /* Dirty it again to check shootdown logic */ + *dptr = 2; + if (count_dirty_pages(dirty_bitmap) != 1) { + test_fail_msg("subsequent direct access did not dirty page\n"); + } + + struct vm_entry ventry = { 0 }; + struct vm_exit vexit = { 0 }; + do { + const enum vm_exit_kind kind = + test_run_vcpu(ctx, 0, &ventry, &vexit); + switch (kind) { + case VEK_REENTR: + break; + case VEK_TEST_PASS: + /* + * By now, the guest should have dirtied that page + * directly via hardware-accelerated path. + */ + read_dirty_bitmap(ctx, dirty_bitmap); + /* + * The guest will dirty more than the page it is + * explicitly writing to: it must mark its own page + * tables with accessed/dirty bits too. + */ + if (count_dirty_pages(dirty_bitmap) <= 1) { + test_fail_msg( + "in-guest access did not dirty page\n"); + } + if (dirty_bitmap[MEM_LOC_STACK / (PAGE_SZ * 8)] == 0) { + test_fail_msg("expected page not dirtied\n"); + } + test_pass(); + break; + default: + test_fail_vmexit(&vexit); + break; + } + } while (true); +} diff --git a/usr/src/test/bhyve-tests/tests/inst_emul/payload_page_dirty.s b/usr/src/test/bhyve-tests/tests/inst_emul/payload_page_dirty.s new file mode 100644 index 0000000000..4c1869ca9b --- /dev/null +++ b/usr/src/test/bhyve-tests/tests/inst_emul/payload_page_dirty.s @@ -0,0 +1,29 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2022 Oxide Computer Company + */ + +#include +#include "payload_common.h" + + +ENTRY(start) + /* Dirty the stack page once again */ + xorl %eax, %eax + movq %rax, MEM_LOC_STACK + + movw $IOP_TEST_RESULT, %dx + movb $TEST_RESULT_PASS, %al + outb (%dx) + hlt +SET_SIZE(start) -- cgit v1.2.3