18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * KVM dirty page logging test 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018, Red Hat, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define _GNU_SOURCE /* for program_invocation_name */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <stdlib.h> 128c2ecf20Sopenharmony_ci#include <unistd.h> 138c2ecf20Sopenharmony_ci#include <time.h> 148c2ecf20Sopenharmony_ci#include <pthread.h> 158c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 168c2ecf20Sopenharmony_ci#include <linux/bitops.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "test_util.h" 198c2ecf20Sopenharmony_ci#include "kvm_util.h" 208c2ecf20Sopenharmony_ci#include "processor.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define VCPU_ID 1 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* The memory slot index to track dirty pages */ 258c2ecf20Sopenharmony_ci#define TEST_MEM_SLOT_INDEX 1 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Default guest test virtual memory offset */ 288c2ecf20Sopenharmony_ci#define DEFAULT_GUEST_TEST_MEM 0xc0000000 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* How many pages to dirty for each guest loop */ 318c2ecf20Sopenharmony_ci#define TEST_PAGES_PER_LOOP 1024 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* How many host loops to run (one KVM_GET_DIRTY_LOG for each loop) */ 348c2ecf20Sopenharmony_ci#define TEST_HOST_LOOP_N 32UL 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Interval for each host loop (ms) */ 378c2ecf20Sopenharmony_ci#define TEST_HOST_LOOP_INTERVAL 10UL 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Dirty bitmaps are always little endian, so we need to swap on big endian */ 408c2ecf20Sopenharmony_ci#if defined(__s390x__) 418c2ecf20Sopenharmony_ci# define BITOP_LE_SWIZZLE ((BITS_PER_LONG-1) & ~0x7) 428c2ecf20Sopenharmony_ci# define test_bit_le(nr, addr) \ 438c2ecf20Sopenharmony_ci test_bit((nr) ^ BITOP_LE_SWIZZLE, addr) 448c2ecf20Sopenharmony_ci# define set_bit_le(nr, addr) \ 458c2ecf20Sopenharmony_ci set_bit((nr) ^ BITOP_LE_SWIZZLE, addr) 468c2ecf20Sopenharmony_ci# define clear_bit_le(nr, addr) \ 478c2ecf20Sopenharmony_ci clear_bit((nr) ^ BITOP_LE_SWIZZLE, addr) 488c2ecf20Sopenharmony_ci# define test_and_set_bit_le(nr, addr) \ 498c2ecf20Sopenharmony_ci test_and_set_bit((nr) ^ BITOP_LE_SWIZZLE, addr) 508c2ecf20Sopenharmony_ci# define test_and_clear_bit_le(nr, addr) \ 518c2ecf20Sopenharmony_ci test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, addr) 528c2ecf20Sopenharmony_ci#else 538c2ecf20Sopenharmony_ci# define test_bit_le test_bit 548c2ecf20Sopenharmony_ci# define set_bit_le set_bit 558c2ecf20Sopenharmony_ci# define clear_bit_le clear_bit 568c2ecf20Sopenharmony_ci# define test_and_set_bit_le test_and_set_bit 578c2ecf20Sopenharmony_ci# define test_and_clear_bit_le test_and_clear_bit 588c2ecf20Sopenharmony_ci#endif 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Guest/Host shared variables. Ensure addr_gva2hva() and/or 628c2ecf20Sopenharmony_ci * sync_global_to/from_guest() are used when accessing from 638c2ecf20Sopenharmony_ci * the host. READ/WRITE_ONCE() should also be used with anything 648c2ecf20Sopenharmony_ci * that may change. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic uint64_t host_page_size; 678c2ecf20Sopenharmony_cistatic uint64_t guest_page_size; 688c2ecf20Sopenharmony_cistatic uint64_t guest_num_pages; 698c2ecf20Sopenharmony_cistatic uint64_t random_array[TEST_PAGES_PER_LOOP]; 708c2ecf20Sopenharmony_cistatic uint64_t iteration; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Guest physical memory offset of the testing memory slot. 748c2ecf20Sopenharmony_ci * This will be set to the topmost valid physical address minus 758c2ecf20Sopenharmony_ci * the test memory size. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic uint64_t guest_test_phys_mem; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * Guest virtual memory offset of the testing memory slot. 818c2ecf20Sopenharmony_ci * Must not conflict with identity mapped test code. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * Continuously write to the first 8 bytes of a random pages within 878c2ecf20Sopenharmony_ci * the testing memory region. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_cistatic void guest_code(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci uint64_t addr; 928c2ecf20Sopenharmony_ci int i; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * On s390x, all pages of a 1M segment are initially marked as dirty 968c2ecf20Sopenharmony_ci * when a page of the segment is written to for the very first time. 978c2ecf20Sopenharmony_ci * To compensate this specialty in this test, we need to touch all 988c2ecf20Sopenharmony_ci * pages during the first iteration. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci for (i = 0; i < guest_num_pages; i++) { 1018c2ecf20Sopenharmony_ci addr = guest_test_virt_mem + i * guest_page_size; 1028c2ecf20Sopenharmony_ci *(uint64_t *)addr = READ_ONCE(iteration); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci while (true) { 1068c2ecf20Sopenharmony_ci for (i = 0; i < TEST_PAGES_PER_LOOP; i++) { 1078c2ecf20Sopenharmony_ci addr = guest_test_virt_mem; 1088c2ecf20Sopenharmony_ci addr += (READ_ONCE(random_array[i]) % guest_num_pages) 1098c2ecf20Sopenharmony_ci * guest_page_size; 1108c2ecf20Sopenharmony_ci addr &= ~(host_page_size - 1); 1118c2ecf20Sopenharmony_ci *(uint64_t *)addr = READ_ONCE(iteration); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Tell the host that we need more random numbers */ 1158c2ecf20Sopenharmony_ci GUEST_SYNC(1); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* Host variables */ 1208c2ecf20Sopenharmony_cistatic bool host_quit; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* Points to the test VM memory region on which we track dirty logs */ 1238c2ecf20Sopenharmony_cistatic void *host_test_mem; 1248c2ecf20Sopenharmony_cistatic uint64_t host_num_pages; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* For statistics only */ 1278c2ecf20Sopenharmony_cistatic uint64_t host_dirty_count; 1288c2ecf20Sopenharmony_cistatic uint64_t host_clear_count; 1298c2ecf20Sopenharmony_cistatic uint64_t host_track_next_count; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cienum log_mode_t { 1328c2ecf20Sopenharmony_ci /* Only use KVM_GET_DIRTY_LOG for logging */ 1338c2ecf20Sopenharmony_ci LOG_MODE_DIRTY_LOG = 0, 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Use both KVM_[GET|CLEAR]_DIRTY_LOG for logging */ 1368c2ecf20Sopenharmony_ci LOG_MODE_CLEAR_LOG = 1, 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci LOG_MODE_NUM, 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Run all supported modes */ 1418c2ecf20Sopenharmony_ci LOG_MODE_ALL = LOG_MODE_NUM, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* Mode of logging to test. Default is to run all supported modes */ 1458c2ecf20Sopenharmony_cistatic enum log_mode_t host_log_mode_option = LOG_MODE_ALL; 1468c2ecf20Sopenharmony_ci/* Logging mode for current run */ 1478c2ecf20Sopenharmony_cistatic enum log_mode_t host_log_mode; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic bool clear_log_supported(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci return kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void clear_log_create_vm_done(struct kvm_vm *vm) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct kvm_enable_cap cap = {}; 1578c2ecf20Sopenharmony_ci u64 manual_caps; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci manual_caps = kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); 1608c2ecf20Sopenharmony_ci TEST_ASSERT(manual_caps, "MANUAL_CAPS is zero!"); 1618c2ecf20Sopenharmony_ci manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | 1628c2ecf20Sopenharmony_ci KVM_DIRTY_LOG_INITIALLY_SET); 1638c2ecf20Sopenharmony_ci cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2; 1648c2ecf20Sopenharmony_ci cap.args[0] = manual_caps; 1658c2ecf20Sopenharmony_ci vm_enable_cap(vm, &cap); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void dirty_log_collect_dirty_pages(struct kvm_vm *vm, int slot, 1698c2ecf20Sopenharmony_ci void *bitmap, uint32_t num_pages) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci kvm_vm_get_dirty_log(vm, slot, bitmap); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void clear_log_collect_dirty_pages(struct kvm_vm *vm, int slot, 1758c2ecf20Sopenharmony_ci void *bitmap, uint32_t num_pages) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci kvm_vm_get_dirty_log(vm, slot, bitmap); 1788c2ecf20Sopenharmony_ci kvm_vm_clear_dirty_log(vm, slot, bitmap, 0, num_pages); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistruct log_mode { 1828c2ecf20Sopenharmony_ci const char *name; 1838c2ecf20Sopenharmony_ci /* Return true if this mode is supported, otherwise false */ 1848c2ecf20Sopenharmony_ci bool (*supported)(void); 1858c2ecf20Sopenharmony_ci /* Hook when the vm creation is done (before vcpu creation) */ 1868c2ecf20Sopenharmony_ci void (*create_vm_done)(struct kvm_vm *vm); 1878c2ecf20Sopenharmony_ci /* Hook to collect the dirty pages into the bitmap provided */ 1888c2ecf20Sopenharmony_ci void (*collect_dirty_pages) (struct kvm_vm *vm, int slot, 1898c2ecf20Sopenharmony_ci void *bitmap, uint32_t num_pages); 1908c2ecf20Sopenharmony_ci} log_modes[LOG_MODE_NUM] = { 1918c2ecf20Sopenharmony_ci { 1928c2ecf20Sopenharmony_ci .name = "dirty-log", 1938c2ecf20Sopenharmony_ci .collect_dirty_pages = dirty_log_collect_dirty_pages, 1948c2ecf20Sopenharmony_ci }, 1958c2ecf20Sopenharmony_ci { 1968c2ecf20Sopenharmony_ci .name = "clear-log", 1978c2ecf20Sopenharmony_ci .supported = clear_log_supported, 1988c2ecf20Sopenharmony_ci .create_vm_done = clear_log_create_vm_done, 1998c2ecf20Sopenharmony_ci .collect_dirty_pages = clear_log_collect_dirty_pages, 2008c2ecf20Sopenharmony_ci }, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * We use this bitmap to track some pages that should have its dirty 2058c2ecf20Sopenharmony_ci * bit set in the _next_ iteration. For example, if we detected the 2068c2ecf20Sopenharmony_ci * page value changed to current iteration but at the same time the 2078c2ecf20Sopenharmony_ci * page bit is cleared in the latest bitmap, then the system must 2088c2ecf20Sopenharmony_ci * report that write in the next get dirty log call. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_cistatic unsigned long *host_bmap_track; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void log_modes_dump(void) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int i; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci printf("all"); 2178c2ecf20Sopenharmony_ci for (i = 0; i < LOG_MODE_NUM; i++) 2188c2ecf20Sopenharmony_ci printf(", %s", log_modes[i].name); 2198c2ecf20Sopenharmony_ci printf("\n"); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic bool log_mode_supported(void) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct log_mode *mode = &log_modes[host_log_mode]; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (mode->supported) 2278c2ecf20Sopenharmony_ci return mode->supported(); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return true; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void log_mode_create_vm_done(struct kvm_vm *vm) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct log_mode *mode = &log_modes[host_log_mode]; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (mode->create_vm_done) 2378c2ecf20Sopenharmony_ci mode->create_vm_done(vm); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void log_mode_collect_dirty_pages(struct kvm_vm *vm, int slot, 2418c2ecf20Sopenharmony_ci void *bitmap, uint32_t num_pages) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct log_mode *mode = &log_modes[host_log_mode]; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci TEST_ASSERT(mode->collect_dirty_pages != NULL, 2468c2ecf20Sopenharmony_ci "collect_dirty_pages() is required for any log mode!"); 2478c2ecf20Sopenharmony_ci mode->collect_dirty_pages(vm, slot, bitmap, num_pages); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void generate_random_array(uint64_t *guest_array, uint64_t size) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci uint64_t i; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 2558c2ecf20Sopenharmony_ci guest_array[i] = random(); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void *vcpu_worker(void *data) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int ret; 2618c2ecf20Sopenharmony_ci struct kvm_vm *vm = data; 2628c2ecf20Sopenharmony_ci uint64_t *guest_array; 2638c2ecf20Sopenharmony_ci uint64_t pages_count = 0; 2648c2ecf20Sopenharmony_ci struct kvm_run *run; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci run = vcpu_state(vm, VCPU_ID); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci guest_array = addr_gva2hva(vm, (vm_vaddr_t)random_array); 2698c2ecf20Sopenharmony_ci generate_random_array(guest_array, TEST_PAGES_PER_LOOP); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci while (!READ_ONCE(host_quit)) { 2728c2ecf20Sopenharmony_ci /* Let the guest dirty the random pages */ 2738c2ecf20Sopenharmony_ci ret = _vcpu_run(vm, VCPU_ID); 2748c2ecf20Sopenharmony_ci TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); 2758c2ecf20Sopenharmony_ci if (get_ucall(vm, VCPU_ID, NULL) == UCALL_SYNC) { 2768c2ecf20Sopenharmony_ci pages_count += TEST_PAGES_PER_LOOP; 2778c2ecf20Sopenharmony_ci generate_random_array(guest_array, TEST_PAGES_PER_LOOP); 2788c2ecf20Sopenharmony_ci } else { 2798c2ecf20Sopenharmony_ci TEST_FAIL("Invalid guest sync status: " 2808c2ecf20Sopenharmony_ci "exit_reason=%s\n", 2818c2ecf20Sopenharmony_ci exit_reason_str(run->exit_reason)); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci pr_info("Dirtied %"PRIu64" pages\n", pages_count); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return NULL; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void vm_dirty_log_verify(enum vm_guest_mode mode, unsigned long *bmap) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci uint64_t step = vm_num_host_pages(mode, 1); 2938c2ecf20Sopenharmony_ci uint64_t page; 2948c2ecf20Sopenharmony_ci uint64_t *value_ptr; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for (page = 0; page < host_num_pages; page += step) { 2978c2ecf20Sopenharmony_ci value_ptr = host_test_mem + page * host_page_size; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* If this is a special page that we were tracking... */ 3008c2ecf20Sopenharmony_ci if (test_and_clear_bit_le(page, host_bmap_track)) { 3018c2ecf20Sopenharmony_ci host_track_next_count++; 3028c2ecf20Sopenharmony_ci TEST_ASSERT(test_bit_le(page, bmap), 3038c2ecf20Sopenharmony_ci "Page %"PRIu64" should have its dirty bit " 3048c2ecf20Sopenharmony_ci "set in this iteration but it is missing", 3058c2ecf20Sopenharmony_ci page); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (test_and_clear_bit_le(page, bmap)) { 3098c2ecf20Sopenharmony_ci host_dirty_count++; 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * If the bit is set, the value written onto 3128c2ecf20Sopenharmony_ci * the corresponding page should be either the 3138c2ecf20Sopenharmony_ci * previous iteration number or the current one. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci TEST_ASSERT(*value_ptr == iteration || 3168c2ecf20Sopenharmony_ci *value_ptr == iteration - 1, 3178c2ecf20Sopenharmony_ci "Set page %"PRIu64" value %"PRIu64 3188c2ecf20Sopenharmony_ci " incorrect (iteration=%"PRIu64")", 3198c2ecf20Sopenharmony_ci page, *value_ptr, iteration); 3208c2ecf20Sopenharmony_ci } else { 3218c2ecf20Sopenharmony_ci host_clear_count++; 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * If cleared, the value written can be any 3248c2ecf20Sopenharmony_ci * value smaller or equals to the iteration 3258c2ecf20Sopenharmony_ci * number. Note that the value can be exactly 3268c2ecf20Sopenharmony_ci * (iteration-1) if that write can happen 3278c2ecf20Sopenharmony_ci * like this: 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * (1) increase loop count to "iteration-1" 3308c2ecf20Sopenharmony_ci * (2) write to page P happens (with value 3318c2ecf20Sopenharmony_ci * "iteration-1") 3328c2ecf20Sopenharmony_ci * (3) get dirty log for "iteration-1"; we'll 3338c2ecf20Sopenharmony_ci * see that page P bit is set (dirtied), 3348c2ecf20Sopenharmony_ci * and not set the bit in host_bmap_track 3358c2ecf20Sopenharmony_ci * (4) increase loop count to "iteration" 3368c2ecf20Sopenharmony_ci * (which is current iteration) 3378c2ecf20Sopenharmony_ci * (5) get dirty log for current iteration, 3388c2ecf20Sopenharmony_ci * we'll see that page P is cleared, with 3398c2ecf20Sopenharmony_ci * value "iteration-1". 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci TEST_ASSERT(*value_ptr <= iteration, 3428c2ecf20Sopenharmony_ci "Clear page %"PRIu64" value %"PRIu64 3438c2ecf20Sopenharmony_ci " incorrect (iteration=%"PRIu64")", 3448c2ecf20Sopenharmony_ci page, *value_ptr, iteration); 3458c2ecf20Sopenharmony_ci if (*value_ptr == iteration) { 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * This page is _just_ modified; it 3488c2ecf20Sopenharmony_ci * should report its dirtyness in the 3498c2ecf20Sopenharmony_ci * next run 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci set_bit_le(page, host_bmap_track); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid, 3588c2ecf20Sopenharmony_ci uint64_t extra_mem_pages, void *guest_code) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct kvm_vm *vm; 3618c2ecf20Sopenharmony_ci uint64_t extra_pg_pages = extra_mem_pages / 512 * 2; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode)); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci vm = vm_create(mode, DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR); 3668c2ecf20Sopenharmony_ci kvm_vm_elf_load(vm, program_invocation_name, 0, 0); 3678c2ecf20Sopenharmony_ci#ifdef __x86_64__ 3688c2ecf20Sopenharmony_ci vm_create_irqchip(vm); 3698c2ecf20Sopenharmony_ci#endif 3708c2ecf20Sopenharmony_ci log_mode_create_vm_done(vm); 3718c2ecf20Sopenharmony_ci vm_vcpu_add_default(vm, vcpuid, guest_code); 3728c2ecf20Sopenharmony_ci return vm; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci#define DIRTY_MEM_BITS 30 /* 1G */ 3768c2ecf20Sopenharmony_ci#define PAGE_SHIFT_4K 12 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic void run_test(enum vm_guest_mode mode, unsigned long iterations, 3798c2ecf20Sopenharmony_ci unsigned long interval, uint64_t phys_offset) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci pthread_t vcpu_thread; 3828c2ecf20Sopenharmony_ci struct kvm_vm *vm; 3838c2ecf20Sopenharmony_ci unsigned long *bmap; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (!log_mode_supported()) { 3868c2ecf20Sopenharmony_ci print_skip("Log mode '%s' not supported", 3878c2ecf20Sopenharmony_ci log_modes[host_log_mode].name); 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * We reserve page table for 2 times of extra dirty mem which 3938c2ecf20Sopenharmony_ci * will definitely cover the original (1G+) test range. Here 3948c2ecf20Sopenharmony_ci * we do the calculation with 4K page size which is the 3958c2ecf20Sopenharmony_ci * smallest so the page number will be enough for all archs 3968c2ecf20Sopenharmony_ci * (e.g., 64K page size guest will need even less memory for 3978c2ecf20Sopenharmony_ci * page tables). 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci vm = create_vm(mode, VCPU_ID, 4008c2ecf20Sopenharmony_ci 2ul << (DIRTY_MEM_BITS - PAGE_SHIFT_4K), 4018c2ecf20Sopenharmony_ci guest_code); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci guest_page_size = vm_get_page_size(vm); 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * A little more than 1G of guest page sized pages. Cover the 4068c2ecf20Sopenharmony_ci * case where the size is not aligned to 64 pages. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci guest_num_pages = (1ul << (DIRTY_MEM_BITS - 4098c2ecf20Sopenharmony_ci vm_get_page_shift(vm))) + 3; 4108c2ecf20Sopenharmony_ci guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci host_page_size = getpagesize(); 4138c2ecf20Sopenharmony_ci host_num_pages = vm_num_host_pages(mode, guest_num_pages); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (!phys_offset) { 4168c2ecf20Sopenharmony_ci guest_test_phys_mem = (vm_get_max_gfn(vm) - 4178c2ecf20Sopenharmony_ci guest_num_pages) * guest_page_size; 4188c2ecf20Sopenharmony_ci guest_test_phys_mem &= ~(host_page_size - 1); 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci guest_test_phys_mem = phys_offset; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci#ifdef __s390x__ 4248c2ecf20Sopenharmony_ci /* Align to 1M (segment size) */ 4258c2ecf20Sopenharmony_ci guest_test_phys_mem &= ~((1 << 20) - 1); 4268c2ecf20Sopenharmony_ci#endif 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci bmap = bitmap_alloc(host_num_pages); 4318c2ecf20Sopenharmony_ci host_bmap_track = bitmap_alloc(host_num_pages); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* Add an extra memory slot for testing dirty logging */ 4348c2ecf20Sopenharmony_ci vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 4358c2ecf20Sopenharmony_ci guest_test_phys_mem, 4368c2ecf20Sopenharmony_ci TEST_MEM_SLOT_INDEX, 4378c2ecf20Sopenharmony_ci guest_num_pages, 4388c2ecf20Sopenharmony_ci KVM_MEM_LOG_DIRTY_PAGES); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Do mapping for the dirty track memory slot */ 4418c2ecf20Sopenharmony_ci virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* Cache the HVA pointer of the region */ 4448c2ecf20Sopenharmony_ci host_test_mem = addr_gpa2hva(vm, (vm_paddr_t)guest_test_phys_mem); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci#ifdef __x86_64__ 4478c2ecf20Sopenharmony_ci vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); 4488c2ecf20Sopenharmony_ci#endif 4498c2ecf20Sopenharmony_ci ucall_init(vm, NULL); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Export the shared variables to the guest */ 4528c2ecf20Sopenharmony_ci sync_global_to_guest(vm, host_page_size); 4538c2ecf20Sopenharmony_ci sync_global_to_guest(vm, guest_page_size); 4548c2ecf20Sopenharmony_ci sync_global_to_guest(vm, guest_test_virt_mem); 4558c2ecf20Sopenharmony_ci sync_global_to_guest(vm, guest_num_pages); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Start the iterations */ 4588c2ecf20Sopenharmony_ci iteration = 1; 4598c2ecf20Sopenharmony_ci sync_global_to_guest(vm, iteration); 4608c2ecf20Sopenharmony_ci host_quit = false; 4618c2ecf20Sopenharmony_ci host_dirty_count = 0; 4628c2ecf20Sopenharmony_ci host_clear_count = 0; 4638c2ecf20Sopenharmony_ci host_track_next_count = 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci pthread_create(&vcpu_thread, NULL, vcpu_worker, vm); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci while (iteration < iterations) { 4688c2ecf20Sopenharmony_ci /* Give the vcpu thread some time to dirty some pages */ 4698c2ecf20Sopenharmony_ci usleep(interval * 1000); 4708c2ecf20Sopenharmony_ci log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX, 4718c2ecf20Sopenharmony_ci bmap, host_num_pages); 4728c2ecf20Sopenharmony_ci vm_dirty_log_verify(mode, bmap); 4738c2ecf20Sopenharmony_ci iteration++; 4748c2ecf20Sopenharmony_ci sync_global_to_guest(vm, iteration); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Tell the vcpu thread to quit */ 4788c2ecf20Sopenharmony_ci host_quit = true; 4798c2ecf20Sopenharmony_ci pthread_join(vcpu_thread, NULL); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci pr_info("Total bits checked: dirty (%"PRIu64"), clear (%"PRIu64"), " 4828c2ecf20Sopenharmony_ci "track_next (%"PRIu64")\n", host_dirty_count, host_clear_count, 4838c2ecf20Sopenharmony_ci host_track_next_count); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci free(bmap); 4868c2ecf20Sopenharmony_ci free(host_bmap_track); 4878c2ecf20Sopenharmony_ci ucall_uninit(vm); 4888c2ecf20Sopenharmony_ci kvm_vm_free(vm); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistruct guest_mode { 4928c2ecf20Sopenharmony_ci bool supported; 4938c2ecf20Sopenharmony_ci bool enabled; 4948c2ecf20Sopenharmony_ci}; 4958c2ecf20Sopenharmony_cistatic struct guest_mode guest_modes[NUM_VM_MODES]; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci#define guest_mode_init(mode, supported, enabled) ({ \ 4988c2ecf20Sopenharmony_ci guest_modes[mode] = (struct guest_mode){ supported, enabled }; \ 4998c2ecf20Sopenharmony_ci}) 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic void help(char *name) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci int i; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci puts(""); 5068c2ecf20Sopenharmony_ci printf("usage: %s [-h] [-i iterations] [-I interval] " 5078c2ecf20Sopenharmony_ci "[-p offset] [-m mode]\n", name); 5088c2ecf20Sopenharmony_ci puts(""); 5098c2ecf20Sopenharmony_ci printf(" -i: specify iteration counts (default: %"PRIu64")\n", 5108c2ecf20Sopenharmony_ci TEST_HOST_LOOP_N); 5118c2ecf20Sopenharmony_ci printf(" -I: specify interval in ms (default: %"PRIu64" ms)\n", 5128c2ecf20Sopenharmony_ci TEST_HOST_LOOP_INTERVAL); 5138c2ecf20Sopenharmony_ci printf(" -p: specify guest physical test memory offset\n" 5148c2ecf20Sopenharmony_ci " Warning: a low offset can conflict with the loaded test code.\n"); 5158c2ecf20Sopenharmony_ci printf(" -M: specify the host logging mode " 5168c2ecf20Sopenharmony_ci "(default: run all log modes). Supported modes: \n\t"); 5178c2ecf20Sopenharmony_ci log_modes_dump(); 5188c2ecf20Sopenharmony_ci printf(" -m: specify the guest mode ID to test " 5198c2ecf20Sopenharmony_ci "(default: test all supported modes)\n" 5208c2ecf20Sopenharmony_ci " This option may be used multiple times.\n" 5218c2ecf20Sopenharmony_ci " Guest mode IDs:\n"); 5228c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VM_MODES; ++i) { 5238c2ecf20Sopenharmony_ci printf(" %d: %s%s\n", i, vm_guest_mode_string(i), 5248c2ecf20Sopenharmony_ci guest_modes[i].supported ? " (supported)" : ""); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci puts(""); 5278c2ecf20Sopenharmony_ci exit(0); 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciint main(int argc, char *argv[]) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci unsigned long iterations = TEST_HOST_LOOP_N; 5338c2ecf20Sopenharmony_ci unsigned long interval = TEST_HOST_LOOP_INTERVAL; 5348c2ecf20Sopenharmony_ci bool mode_selected = false; 5358c2ecf20Sopenharmony_ci uint64_t phys_offset = 0; 5368c2ecf20Sopenharmony_ci unsigned int mode; 5378c2ecf20Sopenharmony_ci int opt, i, j; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci#ifdef __x86_64__ 5408c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_PXXV48_4K, true, true); 5418c2ecf20Sopenharmony_ci#endif 5428c2ecf20Sopenharmony_ci#ifdef __aarch64__ 5438c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P40V48_4K, true, true); 5448c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P40V48_64K, true, true); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci { 5478c2ecf20Sopenharmony_ci unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (limit >= 52) 5508c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P52V48_64K, true, true); 5518c2ecf20Sopenharmony_ci if (limit >= 48) { 5528c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P48V48_4K, true, true); 5538c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P48V48_64K, true, true); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci#endif 5578c2ecf20Sopenharmony_ci#ifdef __s390x__ 5588c2ecf20Sopenharmony_ci guest_mode_init(VM_MODE_P40V48_4K, true, true); 5598c2ecf20Sopenharmony_ci#endif 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci while ((opt = getopt(argc, argv, "hi:I:p:m:M:")) != -1) { 5628c2ecf20Sopenharmony_ci switch (opt) { 5638c2ecf20Sopenharmony_ci case 'i': 5648c2ecf20Sopenharmony_ci iterations = strtol(optarg, NULL, 10); 5658c2ecf20Sopenharmony_ci break; 5668c2ecf20Sopenharmony_ci case 'I': 5678c2ecf20Sopenharmony_ci interval = strtol(optarg, NULL, 10); 5688c2ecf20Sopenharmony_ci break; 5698c2ecf20Sopenharmony_ci case 'p': 5708c2ecf20Sopenharmony_ci phys_offset = strtoull(optarg, NULL, 0); 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci case 'm': 5738c2ecf20Sopenharmony_ci if (!mode_selected) { 5748c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VM_MODES; ++i) 5758c2ecf20Sopenharmony_ci guest_modes[i].enabled = false; 5768c2ecf20Sopenharmony_ci mode_selected = true; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci mode = strtoul(optarg, NULL, 10); 5798c2ecf20Sopenharmony_ci TEST_ASSERT(mode < NUM_VM_MODES, 5808c2ecf20Sopenharmony_ci "Guest mode ID %d too big", mode); 5818c2ecf20Sopenharmony_ci guest_modes[mode].enabled = true; 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case 'M': 5848c2ecf20Sopenharmony_ci if (!strcmp(optarg, "all")) { 5858c2ecf20Sopenharmony_ci host_log_mode_option = LOG_MODE_ALL; 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci for (i = 0; i < LOG_MODE_NUM; i++) { 5898c2ecf20Sopenharmony_ci if (!strcmp(optarg, log_modes[i].name)) { 5908c2ecf20Sopenharmony_ci pr_info("Setting log mode to: '%s'\n", 5918c2ecf20Sopenharmony_ci optarg); 5928c2ecf20Sopenharmony_ci host_log_mode_option = i; 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci if (i == LOG_MODE_NUM) { 5978c2ecf20Sopenharmony_ci printf("Log mode '%s' invalid. Please choose " 5988c2ecf20Sopenharmony_ci "from: ", optarg); 5998c2ecf20Sopenharmony_ci log_modes_dump(); 6008c2ecf20Sopenharmony_ci exit(1); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci case 'h': 6048c2ecf20Sopenharmony_ci default: 6058c2ecf20Sopenharmony_ci help(argv[0]); 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci TEST_ASSERT(iterations > 2, "Iterations must be greater than two"); 6118c2ecf20Sopenharmony_ci TEST_ASSERT(interval > 0, "Interval must be greater than zero"); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci pr_info("Test iterations: %"PRIu64", interval: %"PRIu64" (ms)\n", 6148c2ecf20Sopenharmony_ci iterations, interval); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci srandom(time(0)); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci for (i = 0; i < NUM_VM_MODES; ++i) { 6198c2ecf20Sopenharmony_ci if (!guest_modes[i].enabled) 6208c2ecf20Sopenharmony_ci continue; 6218c2ecf20Sopenharmony_ci TEST_ASSERT(guest_modes[i].supported, 6228c2ecf20Sopenharmony_ci "Guest mode ID %d (%s) not supported.", 6238c2ecf20Sopenharmony_ci i, vm_guest_mode_string(i)); 6248c2ecf20Sopenharmony_ci if (host_log_mode_option == LOG_MODE_ALL) { 6258c2ecf20Sopenharmony_ci /* Run each log mode */ 6268c2ecf20Sopenharmony_ci for (j = 0; j < LOG_MODE_NUM; j++) { 6278c2ecf20Sopenharmony_ci pr_info("Testing Log Mode '%s'\n", 6288c2ecf20Sopenharmony_ci log_modes[j].name); 6298c2ecf20Sopenharmony_ci host_log_mode = j; 6308c2ecf20Sopenharmony_ci run_test(i, iterations, interval, phys_offset); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci } else { 6338c2ecf20Sopenharmony_ci host_log_mode = host_log_mode_option; 6348c2ecf20Sopenharmony_ci run_test(i, iterations, interval, phys_offset); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 640