Message ID | 20240815154529.628087-2-schlameuss@linux.ibm.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | selftests: kvm: s390: Add ucontrol memory selftests | expand |
On 8/15/24 5:45 PM, Christoph Schlameuss wrote: > Add a test case verifying basic running and interaction of ucontrol VMs. > Fill the segment and page tables for allocated memory and map memory on > first access. > > * uc_map_unmap > Store and load data to mapped and unmapped memory and use pic segment > translation handling to map memory on access. > > Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com> > --- > .../selftests/kvm/s390x/ucontrol_test.c | 120 +++++++++++++++++- > 1 file changed, 119 insertions(+), 1 deletion(-) > > +static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) * self) > +{ > + struct kvm_run *run = self->run; > + > + TEST_ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); > + switch (run->s390_ucontrol.pgm_code) { > + case PGM_SEGMENT_TRANSLATION: > + pr_info("ucontrol pic segment translation 0x%llx\n", > + run->s390_ucontrol.trans_exc_code); > + /* map / make additional memory available */ > + struct kvm_s390_ucas_mapping map2 = { > + .user_addr = (u64)gpa2hva(self, run->s390_ucontrol.trans_exc_code), > + .vcpu_addr = run->s390_ucontrol.trans_exc_code, > + .length = VM_MEM_EXT_SIZE, > + }; > + pr_info("ucas map %p %p 0x%llx\n", > + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); > + TEST_ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2)); > + break; Why is this necessary if you fix up the mapping in the test? [...] > > +TEST_F(uc_kvm, uc_map_unmap) > +{ > + struct kvm_sync_regs *sync_regs = &self->run->s.regs; > + struct kvm_run *run = self->run; > + int rc; > + > + /* copy test_mem_asm to code_hva / code_gpa */ > + TH_LOG("copy code %p to vm mapped memory %p / %p", > + &test_mem_asm, (void *)self->code_hva, (void *)self->code_gpa); > + memcpy((void *)self->code_hva, &test_mem_asm, PAGE_SIZE); > + > + /* DAT disabled + 64 bit mode */ > + run->psw_mask = 0x0000000180000000ULL; > + run->psw_addr = self->code_gpa; > + > + /* set register content for test_mem_asm to access not mapped memory*/ > + sync_regs->gprs[1] = 0x55; > + sync_regs->gprs[5] = self->base_gpa; > + sync_regs->gprs[6] = VM_MEM_SIZE; > + run->kvm_dirty_regs |= KVM_SYNC_GPRS; > + > + /* run and expect to fail witch ucontrol pic segment translation */ s/witch/with/ > + ASSERT_EQ(0, uc_run_once(self)); > + ASSERT_EQ(1, sync_regs->gprs[0]); > + ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); > + > + ASSERT_EQ(PGM_SEGMENT_TRANSLATION, run->s390_ucontrol.pgm_code); > + ASSERT_EQ(self->base_gpa + VM_MEM_SIZE, run->s390_ucontrol.trans_exc_code); > + /* map / make additional memory available */ > + struct kvm_s390_ucas_mapping map2 = { > + .user_addr = (u64)gpa2hva(self, self->base_gpa + VM_MEM_SIZE), > + .vcpu_addr = self->base_gpa + VM_MEM_SIZE, > + .length = VM_MEM_EXT_SIZE, > + }; > + TH_LOG("ucas map %p %p 0x%llx", > + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); > + rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2); > + ASSERT_EQ(0, rc) > + TH_LOG("ucas map result %d not expected, %s", rc, strerror(errno)); > + ASSERT_EQ(0, uc_run_once(self)); > + ASSERT_EQ(false, uc_handle_exit(self)); > + uc_assert_diag44(self); > + > + /* assert registers and memory are in expected state */ > + ASSERT_EQ(2, sync_regs->gprs[0]); > + ASSERT_EQ(0x55, sync_regs->gprs[1]); > + ASSERT_EQ(0x55, *(u32 *)gpa2hva(self, self->base_gpa + VM_MEM_SIZE)); > + > + /* unmap and run loop again */ > + TH_LOG("ucas unmap %p %p 0x%llx", > + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); > + rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_UNMAP, &map2); > + ASSERT_EQ(0, rc) > + TH_LOG("ucas map result %d not expected, %s", rc, strerror(errno)); s/map/unmap/ > + ASSERT_EQ(0, uc_run_once(self)); > + ASSERT_EQ(3, sync_regs->gprs[0]); > + ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); > + ASSERT_EQ(true, uc_handle_exit(self)); > +} > + > TEST_F(uc_kvm, uc_gprs) > { > struct kvm_sync_regs *sync_regs = &self->run->s.regs;
On Fri Aug 16, 2024 at 4:29 PM CEST, Janosch Frank wrote: > On 8/15/24 5:45 PM, Christoph Schlameuss wrote: > > Add a test case verifying basic running and interaction of ucontrol VMs. > > Fill the segment and page tables for allocated memory and map memory on > > first access. > > > > * uc_map_unmap > > Store and load data to mapped and unmapped memory and use pic segment > > translation handling to map memory on access. > > > > Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com> > > --- > > .../selftests/kvm/s390x/ucontrol_test.c | 120 +++++++++++++++++- > > 1 file changed, 119 insertions(+), 1 deletion(-) > > > > > +static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) * self) > > +{ > > + struct kvm_run *run = self->run; > > + > > + TEST_ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); > > + switch (run->s390_ucontrol.pgm_code) { > > + case PGM_SEGMENT_TRANSLATION: > > + pr_info("ucontrol pic segment translation 0x%llx\n", > > + run->s390_ucontrol.trans_exc_code); > > + /* map / make additional memory available */ > > + struct kvm_s390_ucas_mapping map2 = { > > + .user_addr = (u64)gpa2hva(self, run->s390_ucontrol.trans_exc_code), > > + .vcpu_addr = run->s390_ucontrol.trans_exc_code, > > + .length = VM_MEM_EXT_SIZE, > > + }; > > + pr_info("ucas map %p %p 0x%llx\n", > > + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); > > + TEST_ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2)); > > + break; > > Why is this necessary if you fix up the mapping in the test? > I did split this out here to have an automatic and clean way to do the map for other test cases as well. Other test cases would likely not bother with the unmap. This is also used within the uc_skey test to make sure the remap does work after the unmap. I could change this to use uc_handle_exit_ucontrol for both, the map and the remap. But looking at the test code I felt that was more confusing than this within the uc_skey test. > [...] > Thanks for finding the typos.
On 8/19/24 6:03 PM, Christoph Schlameuss wrote: > On Fri Aug 16, 2024 at 4:29 PM CEST, Janosch Frank wrote: >> On 8/15/24 5:45 PM, Christoph Schlameuss wrote: >>> Add a test case verifying basic running and interaction of ucontrol VMs. >>> Fill the segment and page tables for allocated memory and map memory on >>> first access. >>> >>> * uc_map_unmap >>> Store and load data to mapped and unmapped memory and use pic segment >>> translation handling to map memory on access. >>> >>> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com> >>> --- >>> .../selftests/kvm/s390x/ucontrol_test.c | 120 +++++++++++++++++- >>> 1 file changed, 119 insertions(+), 1 deletion(-) >>> >> >>> +static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) * self) >>> +{ >>> + struct kvm_run *run = self->run; >>> + >>> + TEST_ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); >>> + switch (run->s390_ucontrol.pgm_code) { >>> + case PGM_SEGMENT_TRANSLATION: >>> + pr_info("ucontrol pic segment translation 0x%llx\n", >>> + run->s390_ucontrol.trans_exc_code); >>> + /* map / make additional memory available */ >>> + struct kvm_s390_ucas_mapping map2 = { >>> + .user_addr = (u64)gpa2hva(self, run->s390_ucontrol.trans_exc_code), >>> + .vcpu_addr = run->s390_ucontrol.trans_exc_code, >>> + .length = VM_MEM_EXT_SIZE, >>> + }; >>> + pr_info("ucas map %p %p 0x%llx\n", >>> + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); >>> + TEST_ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2)); >>> + break; >> >> Why is this necessary if you fix up the mapping in the test? >> > > This is also used within the uc_skey test to make sure the remap does > work after the unmap. Maybe I'm blind because I'm still recovering but where exactly?
On Fri Aug 23, 2024 at 10:02 AM CEST, Janosch Frank wrote: > On 8/19/24 6:03 PM, Christoph Schlameuss wrote: > > On Fri Aug 16, 2024 at 4:29 PM CEST, Janosch Frank wrote: > >> On 8/15/24 5:45 PM, Christoph Schlameuss wrote: > >>> Add a test case verifying basic running and interaction of ucontrol VMs. > >>> Fill the segment and page tables for allocated memory and map memory on > >>> first access. > >>> > >>> * uc_map_unmap > >>> Store and load data to mapped and unmapped memory and use pic segment > >>> translation handling to map memory on access. > >>> > >>> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com> > >>> --- > >>> .../selftests/kvm/s390x/ucontrol_test.c | 120 +++++++++++++++++- > >>> 1 file changed, 119 insertions(+), 1 deletion(-) > >>> > >> > >>> +static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) * self) > >>> +{ > >>> + struct kvm_run *run = self->run; > >>> + > >>> + TEST_ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); > >>> + switch (run->s390_ucontrol.pgm_code) { > >>> + case PGM_SEGMENT_TRANSLATION: > >>> + pr_info("ucontrol pic segment translation 0x%llx\n", > >>> + run->s390_ucontrol.trans_exc_code); > >>> + /* map / make additional memory available */ > >>> + struct kvm_s390_ucas_mapping map2 = { > >>> + .user_addr = (u64)gpa2hva(self, run->s390_ucontrol.trans_exc_code), > >>> + .vcpu_addr = run->s390_ucontrol.trans_exc_code, > >>> + .length = VM_MEM_EXT_SIZE, > >>> + }; > >>> + pr_info("ucas map %p %p 0x%llx\n", > >>> + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); > >>> + TEST_ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2)); > >>> + break; > >> > >> Why is this necessary if you fix up the mapping in the test? > >> > > > > This is also used within the uc_skey test to make sure the remap does > > work after the unmap. > > Maybe I'm blind because I'm still recovering but where exactly? It is literally used in the last line of the test case. Calling uc_handle_exit() again re-maps previously unmapped memory. I can try to make that a little bit more obvious. + /* unmap and run loop again */ + TH_LOG("ucas unmap %p %p 0x%llx", + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); + rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_UNMAP, &map2); + ASSERT_EQ(0, rc) + TH_LOG("ucas map result %d not expected, %s", rc, strerror(errno)); + ASSERT_EQ(0, uc_run_once(self)); + ASSERT_EQ(3, sync_regs->gprs[0]); + ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); + ASSERT_EQ(true, uc_handle_exit(self)); // <--- HERE +}
On 8/23/24 3:03 PM, Christoph Schlameuss wrote: > On Fri Aug 23, 2024 at 10:02 AM CEST, Janosch Frank wrote: >> On 8/19/24 6:03 PM, Christoph Schlameuss wrote: >>> On Fri Aug 16, 2024 at 4:29 PM CEST, Janosch Frank wrote: >>>> On 8/15/24 5:45 PM, Christoph Schlameuss wrote: >>>>> Add a test case verifying basic running and interaction of ucontrol VMs. >>>>> Fill the segment and page tables for allocated memory and map memory on >>>>> first access. >>>>> >>>>> * uc_map_unmap >>>>> Store and load data to mapped and unmapped memory and use pic segment >>>>> translation handling to map memory on access. >>>>> >>>>> Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com> >>>>> --- >>>>> .../selftests/kvm/s390x/ucontrol_test.c | 120 +++++++++++++++++- >>>>> 1 file changed, 119 insertions(+), 1 deletion(-) >>>>> >>>> >>>>> +static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) * self) >>>>> +{ >>>>> + struct kvm_run *run = self->run; >>>>> + >>>>> + TEST_ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); >>>>> + switch (run->s390_ucontrol.pgm_code) { >>>>> + case PGM_SEGMENT_TRANSLATION: >>>>> + pr_info("ucontrol pic segment translation 0x%llx\n", >>>>> + run->s390_ucontrol.trans_exc_code); >>>>> + /* map / make additional memory available */ >>>>> + struct kvm_s390_ucas_mapping map2 = { >>>>> + .user_addr = (u64)gpa2hva(self, run->s390_ucontrol.trans_exc_code), >>>>> + .vcpu_addr = run->s390_ucontrol.trans_exc_code, >>>>> + .length = VM_MEM_EXT_SIZE, >>>>> + }; >>>>> + pr_info("ucas map %p %p 0x%llx\n", >>>>> + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); >>>>> + TEST_ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2)); >>>>> + break; >>>> >>>> Why is this necessary if you fix up the mapping in the test? >>>> >>> >>> This is also used within the uc_skey test to make sure the remap does >>> work after the unmap. >> >> Maybe I'm blind because I'm still recovering but where exactly? > > It is literally used in the last line of the test case. Calling uc_handle_exit() > again re-maps previously unmapped memory. > > I can try to make that a little bit more obvious. > > + /* unmap and run loop again */ > + TH_LOG("ucas unmap %p %p 0x%llx", > + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); > + rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_UNMAP, &map2); > + ASSERT_EQ(0, rc) > + TH_LOG("ucas map result %d not expected, %s", rc, strerror(errno)); > + ASSERT_EQ(0, uc_run_once(self)); > + ASSERT_EQ(3, sync_regs->gprs[0]); > + ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); > + ASSERT_EQ(true, uc_handle_exit(self)); // <--- HERE > +} > > Seems like I'm blind then :)
diff --git a/tools/testing/selftests/kvm/s390x/ucontrol_test.c b/tools/testing/selftests/kvm/s390x/ucontrol_test.c index 030c59010fe1..41306bb52f29 100644 --- a/tools/testing/selftests/kvm/s390x/ucontrol_test.c +++ b/tools/testing/selftests/kvm/s390x/ucontrol_test.c @@ -16,7 +16,11 @@ #include <linux/capability.h> #include <linux/sizes.h> +#define PGM_SEGMENT_TRANSLATION 0x10 + #define VM_MEM_SIZE (4 * SZ_1M) +#define VM_MEM_EXT_SIZE (2 * SZ_1M) +#define VM_MEM_MAX_M ((VM_MEM_SIZE + VM_MEM_EXT_SIZE) / SZ_1M) /* so directly declare capget to check caps without libcap */ int capget(cap_user_header_t header, cap_user_data_t data); @@ -58,6 +62,23 @@ asm("test_gprs_asm:\n" " j 0b\n" ); +/* Test program manipulating memory */ +extern char test_mem_asm[]; +asm("test_mem_asm:\n" + "xgr %r0, %r0\n" + + "0:\n" + " ahi %r0,1\n" + " st %r1,0(%r5,%r6)\n" + + " xgr %r1, %r1\n" + " l %r1,0(%r5,%r6)\n" + " ahi %r0,1\n" + " diag 0,0,0x44\n" + + " j 0b\n" +); + FIXTURE(uc_kvm) { struct kvm_s390_sie_block *sie_block; @@ -67,6 +88,7 @@ FIXTURE(uc_kvm) uintptr_t base_hva; uintptr_t code_hva; int kvm_run_size; + vm_paddr_t pgd; void *vm_mem; int vcpu_fd; int kvm_fd; @@ -116,7 +138,7 @@ FIXTURE_SETUP(uc_kvm) self->base_gpa = 0; self->code_gpa = self->base_gpa + (3 * SZ_1M); - self->vm_mem = aligned_alloc(SZ_1M, VM_MEM_SIZE); + self->vm_mem = aligned_alloc(SZ_1M, VM_MEM_MAX_M * SZ_1M); ASSERT_NE(NULL, self->vm_mem) TH_LOG("malloc failed %u", errno); self->base_hva = (uintptr_t)self->vm_mem; self->code_hva = self->base_hva - self->base_gpa + self->code_gpa; @@ -222,6 +244,36 @@ TEST(uc_cap_hpage) close(kvm_fd); } +/* calculate host virtual addr from guest physical addr */ +static void *gpa2hva(FIXTURE_DATA(uc_kvm) * self, u64 gpa) +{ + return (void *)(self->base_hva - self->base_gpa + gpa); +} + +static void uc_handle_exit_ucontrol(FIXTURE_DATA(uc_kvm) * self) +{ + struct kvm_run *run = self->run; + + TEST_ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); + switch (run->s390_ucontrol.pgm_code) { + case PGM_SEGMENT_TRANSLATION: + pr_info("ucontrol pic segment translation 0x%llx\n", + run->s390_ucontrol.trans_exc_code); + /* map / make additional memory available */ + struct kvm_s390_ucas_mapping map2 = { + .user_addr = (u64)gpa2hva(self, run->s390_ucontrol.trans_exc_code), + .vcpu_addr = run->s390_ucontrol.trans_exc_code, + .length = VM_MEM_EXT_SIZE, + }; + pr_info("ucas map %p %p 0x%llx\n", + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); + TEST_ASSERT_EQ(0, ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2)); + break; + default: + TEST_FAIL("UNEXPECTED PGM CODE %d", run->s390_ucontrol.pgm_code); + } +} + /* verify SIEIC exit * * reset stop requests * * fail on codes not expected in the test cases @@ -256,6 +308,12 @@ static bool uc_handle_exit(FIXTURE_DATA(uc_kvm) * self) struct kvm_run *run = self->run; switch (run->exit_reason) { + case KVM_EXIT_S390_UCONTROL: + /** check program interruption code + * handle page fault --> ucas map + */ + uc_handle_exit_ucontrol(self); + break; case KVM_EXIT_S390_SIEIC: return uc_handle_sieic(self); default: @@ -287,6 +345,66 @@ static void uc_assert_diag44(FIXTURE_DATA(uc_kvm) * self) TEST_ASSERT_EQ(0x440000, sie_block->ipb); } +TEST_F(uc_kvm, uc_map_unmap) +{ + struct kvm_sync_regs *sync_regs = &self->run->s.regs; + struct kvm_run *run = self->run; + int rc; + + /* copy test_mem_asm to code_hva / code_gpa */ + TH_LOG("copy code %p to vm mapped memory %p / %p", + &test_mem_asm, (void *)self->code_hva, (void *)self->code_gpa); + memcpy((void *)self->code_hva, &test_mem_asm, PAGE_SIZE); + + /* DAT disabled + 64 bit mode */ + run->psw_mask = 0x0000000180000000ULL; + run->psw_addr = self->code_gpa; + + /* set register content for test_mem_asm to access not mapped memory*/ + sync_regs->gprs[1] = 0x55; + sync_regs->gprs[5] = self->base_gpa; + sync_regs->gprs[6] = VM_MEM_SIZE; + run->kvm_dirty_regs |= KVM_SYNC_GPRS; + + /* run and expect to fail witch ucontrol pic segment translation */ + ASSERT_EQ(0, uc_run_once(self)); + ASSERT_EQ(1, sync_regs->gprs[0]); + ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); + + ASSERT_EQ(PGM_SEGMENT_TRANSLATION, run->s390_ucontrol.pgm_code); + ASSERT_EQ(self->base_gpa + VM_MEM_SIZE, run->s390_ucontrol.trans_exc_code); + /* map / make additional memory available */ + struct kvm_s390_ucas_mapping map2 = { + .user_addr = (u64)gpa2hva(self, self->base_gpa + VM_MEM_SIZE), + .vcpu_addr = self->base_gpa + VM_MEM_SIZE, + .length = VM_MEM_EXT_SIZE, + }; + TH_LOG("ucas map %p %p 0x%llx", + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); + rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_MAP, &map2); + ASSERT_EQ(0, rc) + TH_LOG("ucas map result %d not expected, %s", rc, strerror(errno)); + ASSERT_EQ(0, uc_run_once(self)); + ASSERT_EQ(false, uc_handle_exit(self)); + uc_assert_diag44(self); + + /* assert registers and memory are in expected state */ + ASSERT_EQ(2, sync_regs->gprs[0]); + ASSERT_EQ(0x55, sync_regs->gprs[1]); + ASSERT_EQ(0x55, *(u32 *)gpa2hva(self, self->base_gpa + VM_MEM_SIZE)); + + /* unmap and run loop again */ + TH_LOG("ucas unmap %p %p 0x%llx", + (void *)map2.user_addr, (void *)map2.vcpu_addr, map2.length); + rc = ioctl(self->vcpu_fd, KVM_S390_UCAS_UNMAP, &map2); + ASSERT_EQ(0, rc) + TH_LOG("ucas map result %d not expected, %s", rc, strerror(errno)); + ASSERT_EQ(0, uc_run_once(self)); + ASSERT_EQ(3, sync_regs->gprs[0]); + ASSERT_EQ(KVM_EXIT_S390_UCONTROL, run->exit_reason); + ASSERT_EQ(true, uc_handle_exit(self)); +} + TEST_F(uc_kvm, uc_gprs) { struct kvm_sync_regs *sync_regs = &self->run->s.regs;
Add a test case verifying basic running and interaction of ucontrol VMs. Fill the segment and page tables for allocated memory and map memory on first access. * uc_map_unmap Store and load data to mapped and unmapped memory and use pic segment translation handling to map memory on access. Signed-off-by: Christoph Schlameuss <schlameuss@linux.ibm.com> --- .../selftests/kvm/s390x/ucontrol_test.c | 120 +++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-)