/* * Write to the region while mprotect(PROT_READ) is underway. Keep * looping until the memory is guaranteed to be read-only and a fault * has occurred, otherwise vCPUs may complete their writes and advance * to the next stage prematurely. * * For architectures that support skipping the faulting instruction, * generate the store via inline assembly to ensure the exact length * of the instruction is known and stable (vcpu_arch_put_guest() on * fixed-length architectures should work, but the cost of paranoia * is low in this case). For x86, hand-code the exact opcode so that * there is no room for variability in the generated instruction.
*/ do { for (gpa = start_gpa; gpa < end_gpa; gpa += stride) #ifdef __x86_64__ asmvolatile(".byte 0x48,0x89,0x00" :: "a"(gpa) : "memory"); /* mov %rax, (%rax) */ #elifdefined(__aarch64__) asmvolatile("str %0, [%0]" :: "r" (gpa) : "memory"); #else
vcpu_arch_put_guest(*((volatile uint64_t *)gpa), gpa); #endif
} while (!READ_ONCE(mprotect_ro_done) || !READ_ONCE(all_vcpus_hit_ro_fault));
/* * Only architectures that write the entire range can explicitly sync, * as other architectures will be stuck on the write fault.
*/ #ifdefined(__x86_64__) || defined(__aarch64__)
GUEST_SYNC(3); #endif
/* Stage 0, write all of guest memory. */
run_vcpu(vcpu, 0);
rendezvous_with_boss(); #ifdef __x86_64__
vcpu_sregs_get(vcpu, &sregs); /* Toggle CR0.WP to trigger a MMU context reset. */
sregs.cr0 ^= X86_CR0_WP;
vcpu_sregs_set(vcpu, &sregs); #endif
rendezvous_with_boss();
/* Stage 1, re-write all of guest memory. */
run_vcpu(vcpu, 1);
rendezvous_with_boss();
/* Stage 2, read all of guest memory, which is now read-only. */
run_vcpu(vcpu, 2);
/* * Stage 3, write guest memory and verify KVM returns -EFAULT for once * the mprotect(PROT_READ) lands. Only architectures that support * validating *all* of guest memory sync for this stage, as vCPUs will * be stuck on the faulting instruction for other architectures. Go to * stage 3 without a rendezvous
*/
r = _vcpu_run(vcpu);
TEST_ASSERT(r == -1 && errno == EFAULT, "Expected EFAULT on write to RO memory, got r = %d, errno = %d", r, errno);
#ifdefined(__x86_64__) || defined(__aarch64__) /* * Verify *all* writes from the guest hit EFAULT due to the VMA now * being read-only. x86 and arm64 only at this time as skipping the * instruction that hits the EFAULT requires advancing the program * counter, which is arch specific and relies on inline assembly.
*/ #ifdef __x86_64__
vcpu->run->kvm_valid_regs = KVM_SYNC_X86_REGS; #endif for (;;) {
r = _vcpu_run(vcpu); if (!r) break;
TEST_ASSERT_EQ(errno, EFAULT); #ifdefined(__x86_64__)
WRITE_ONCE(vcpu->run->kvm_dirty_regs, KVM_SYNC_X86_REGS);
vcpu->run->s.regs.regs.rip += 3; #elifdefined(__aarch64__)
vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc),
vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc)) + 4); #endif
/* * Stage 4. Run to completion, waiting for mprotect(PROT_WRITE) to * make the memory writable again.
*/ do {
r = _vcpu_run(vcpu);
} while (r && errno == EFAULT);
TEST_ASSERT_EQ(r, 0);
assert_sync_stage(vcpu, 4);
rendezvous_with_boss();
staticvoid rendezvous_with_vcpus(struct timespec *time, constchar *name)
{ int i, rendezvoused;
pr_info("Waiting for vCPUs to finish %s...\n", name);
rendezvoused = atomic_read(&rendezvous); for (i = 0; abs(rendezvoused) != 1; i++) {
usleep(100); if (!(i & 0x3f))
pr_info("\r%d vCPUs haven't rendezvoused...",
abs(rendezvoused) - 1);
rendezvoused = atomic_read(&rendezvous);
}
clock_gettime(CLOCK_MONOTONIC, time);
/* Release the vCPUs after getting the time of the previous action. */
pr_info("\rAll vCPUs finished %s, releasing...\n", name); if (rendezvoused > 0)
atomic_set(&rendezvous, -nr_vcpus - 1); else
atomic_set(&rendezvous, nr_vcpus + 1);
}
staticvoid calc_default_nr_vcpus(void)
{
cpu_set_t possible_mask; int r;
int main(int argc, char *argv[])
{ /* * Skip the first 4gb and slot0. slot0 maps <1gb and is used to back * the guest's code, stack, and page tables. Because selftests creates * an IRQCHIP, a.k.a. a local APIC, KVM creates an internal memslot * just below the 4gb boundary. This test could create memory at * 1gb-3gb,but it's simpler to skip straight to 4gb.
*/ const uint64_t start_gpa = SZ_4G; constint first_slot = 1;
/* * Default to 2gb so that maxing out systems with MAXPHADDR=46, which * are quite common for x86, requires changing only max_mem (KVM allows * 32k memslots, 32k * 2gb == ~64tb of guest memory).
*/
slot_size = SZ_2G;
max_slots = kvm_check_cap(KVM_CAP_NR_MEMSLOTS);
TEST_ASSERT(max_slots > first_slot, "KVM is broken");
/* All KVM MMUs should be able to survive a 128gb guest. */
max_mem = 128ull * SZ_1G;
#ifdef __x86_64__ /* Identity map memory in the guest using 1gb pages. */ for (i = 0; i < slot_size; i += SZ_1G)
__virt_pg_map(vm, gpa + i, gpa + i, PG_LEVEL_1G); #else for (i = 0; i < slot_size; i += vm->page_size)
virt_pg_map(vm, gpa + i, gpa + i); #endif
}
/* * Delete even numbered slots (arbitrary) and unmap the first half of * the backing (also arbitrary) to verify KVM correctly drops all * references to the removed regions.
*/ for (slot = (slot - 1) & ~1ull; slot >= first_slot; slot -= 2)
vm_set_user_memory_region(vm, slot, 0, 0, 0, NULL);
munmap(mem, slot_size / 2);
/* Sanity check that the vCPUs actually ran. */ for (i = 0; i < nr_vcpus; i++)
pthread_join(threads[i], NULL);
/* * Deliberately exit without deleting the remaining memslots or closing * kvm_fd to test cleanup via mmu_notifier.release.
*/
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.