diff mbox series

[1/3] selftests: kvm: s390: Add uc_map_unmap VM test case

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

Commit Message

Christoph Schlameuss Aug. 15, 2024, 3:45 p.m. UTC
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(-)

Comments

Janosch Frank Aug. 16, 2024, 2:29 p.m. UTC | #1
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;
Christoph Schlameuss Aug. 19, 2024, 4:03 p.m. UTC | #2
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.
Janosch Frank Aug. 23, 2024, 8:02 a.m. UTC | #3
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?
Christoph Schlameuss Aug. 23, 2024, 1:03 p.m. UTC | #4
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
+}
Janosch Frank Aug. 23, 2024, 1:20 p.m. UTC | #5
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 mbox series

Patch

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;