summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2022-11-02 01:50:04 +0000
committerPatrick Mooney <pmooney@oxide.computer>2022-11-08 17:34:56 +0000
commit1bf21c92db6af5f8263977d8b99706f8ab9b3a27 (patch)
tree26455be2eb912d826fbaf46c75f6ebc82f1f776a
parentab4969f8ab0fdee71bced8aced6cd4db6cbed1ca (diff)
downloadillumos-joyent-1bf21c92db6af5f8263977d8b99706f8ab9b3a27.tar.gz
15136 want unit test for bhyve dirty page tracking
Reviewed by: Greg Colombo <greg@oxidecomputer.com> Approved by: Dan McDonald <danmcd@mnx.io>
-rw-r--r--usr/src/pkg/manifests/system-bhyve-tests.p5m1
-rw-r--r--usr/src/test/bhyve-tests/tests/inst_emul/Makefile3
-rw-r--r--usr/src/test/bhyve-tests/tests/inst_emul/page_dirty.c162
-rw-r--r--usr/src/test/bhyve-tests/tests/inst_emul/payload_page_dirty.s29
4 files changed, 194 insertions, 1 deletions
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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <libgen.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/debug.h>
+#include <sys/mman.h>
+#include <sys/vmm.h>
+#include <sys/vmm_dev.h>
+#include <vmmapi.h>
+
+#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 <sys/asm_linkage.h>
+#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)