Re: [kvm-unit-tests PATCH v2] s390x: IEP tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 11.06.2018 11:01, Janosch Frank wrote:
> From: Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx>
> 
> Tests no-execute (Instruction Execution Protection) DAT protection.
> 
> Signed-off-by: Janosch Frank <frankja@xxxxxxxxxxxxxxxxxx>
> ---
>  lib/s390x/interrupt.c |  6 +++++
>  lib/s390x/mmu.c       | 46 ++++++++++++++++++++++++++++++++++++--
>  lib/s390x/mmu.h       | 20 +++++++++++++++++
>  s390x/Makefile        |  1 +
>  s390x/iep.c           | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  s390x/unittests.cfg   |  4 ++++
>  6 files changed, 137 insertions(+), 2 deletions(-)
>  create mode 100644 lib/s390x/mmu.h
>  create mode 100644 s390x/iep.c
> 
> diff --git a/lib/s390x/interrupt.c b/lib/s390x/interrupt.c
> index 56c7603..bc44e3a 100644
> --- a/lib/s390x/interrupt.c
> +++ b/lib/s390x/interrupt.c
> @@ -51,6 +51,12 @@ static void fixup_pgm_int(void)
>  		 */
>  		lc->pgm_old_psw.mask &= ~PSW_MASK_PSTATE;
>  		break;
> +	case PGM_INT_CODE_PROTECTION:
> +		/* Handling for iep.c test case. */
> +		if (lc->trans_exc_id & 0x80UL && lc->trans_exc_id & 0x04UL &&
> +		    !(lc->trans_exc_id & 0x08UL))
> +			lc->pgm_old_psw.addr = lc->sw_int_grs[14];
> +		break;
>  	case PGM_INT_CODE_SEGMENT_TRANSLATION:
>  	case PGM_INT_CODE_PAGE_TRANSLATION:
>  	case PGM_INT_CODE_TRACE_TABLE:
> diff --git a/lib/s390x/mmu.c b/lib/s390x/mmu.c
> index 288f835..70753c3 100644
> --- a/lib/s390x/mmu.c
> +++ b/lib/s390x/mmu.c
> @@ -16,6 +16,8 @@
>  #include <asm/barrier.h>
>  #include <vmalloc.h>
>  
> +static pgd_t *table_root;
> +
>  void configure_dat(int enable)
>  {
>  	uint64_t mask;
> @@ -62,7 +64,7 @@ phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *vaddr)
>  	       ((unsigned long)vaddr & ~PAGE_MASK);
>  }
>  
> -pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr)
> +static pteval_t *set_pte(pgd_t *pgtable, pteval_t val, void *vaddr)
>  {
>  	pteval_t *p_pte = get_pte(pgtable, (uintptr_t)vaddr);
>  
> @@ -70,10 +72,49 @@ pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr)
>  	if (!(*p_pte & PAGE_ENTRY_I))
>  		ipte((uintptr_t)vaddr, p_pte);
>  
> -	*p_pte = __pa(phys);
> +	*p_pte = val;
>  	return p_pte;
>  }
>  
> +pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *vaddr)
> +{
> +	return set_pte(pgtable, __pa(phys), vaddr);
> +}
> +
> +void protect_page(void *vaddr, unsigned long prot)
> +{
> +	pteval_t *p_pte = get_pte(table_root, (uintptr_t)vaddr);
> +	pteval_t n_pte = *p_pte | prot;
> +
> +	set_pte(table_root, n_pte, vaddr);
> +}
> +
> +void unprotect_page(void *vaddr, unsigned long prot)
> +{
> +	pteval_t *p_pte = get_pte(table_root, (uintptr_t)vaddr);
> +	pteval_t n_pte = *p_pte & ~prot;
> +
> +	set_pte(table_root, n_pte, vaddr);
> +}
> +
> +void protect_range(void *start, unsigned long len, unsigned long prot)
> +{
> +	uintptr_t curr = (uintptr_t)start & PAGE_MASK;
> +
> +	len &= PAGE_MASK;
> +	for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE)
> +		protect_page((void *)curr, prot);
> +}
> +
> +void unprotect_range(void *start, unsigned long len, unsigned long prot)
> +{
> +	uintptr_t curr = (uintptr_t)start & PAGE_MASK;
> +
> +	len &= PAGE_MASK;
> +	for (; len; len -= PAGE_SIZE, curr += PAGE_SIZE)
> +		unprotect_page((void *)curr, prot);
> +}
> +
>  static void setup_identity(pgd_t *pgtable, phys_addr_t start_addr,
>  			   phys_addr_t end_addr)
>  {
> @@ -104,5 +145,6 @@ void *setup_mmu(phys_addr_t phys_end){
>  
>  	/* finally enable DAT with the new table */
>  	mmu_enable(page_root);
> +	table_root = page_root;
>  	return page_root;
>  }
> diff --git a/lib/s390x/mmu.h b/lib/s390x/mmu.h
> new file mode 100644
> index 0000000..f5095fa
> --- /dev/null
> +++ b/lib/s390x/mmu.h
> @@ -0,0 +1,20 @@
> +/*
> + * s390x mmu functions
> + *
> + * Copyright (c) 2018 IBM Corp
> + *
> + * Authors:
> + *	Janosch Frank <frankja@xxxxxxxxxx>
> + *
> + * This code is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Library General Public License version 2.
> + */
> +#ifndef _ASMS390X_MMU_H_
> +#define _ASMS390X_MMU_H_
> +
> +void protect_page(void *vaddr, unsigned long prot);
> +void protect_range(void *start, unsigned long len, unsigned long prot);
> +void unprotect_page(void *vaddr, unsigned long prot);
> +void unprotect_range(void *start, unsigned long len, unsigned long prot);
> +
> +#endif /* _ASMS390X_MMU_H_ */
> diff --git a/s390x/Makefile b/s390x/Makefile
> index abc3242..d4275a1 100644
> --- a/s390x/Makefile
> +++ b/s390x/Makefile
> @@ -9,6 +9,7 @@ tests += $(TEST_DIR)/pfmf.elf
>  tests += $(TEST_DIR)/cmm.elf
>  tests += $(TEST_DIR)/vector.elf
>  tests += $(TEST_DIR)/gs.elf
> +tests += $(TEST_DIR)/iep.elf
>  
>  all: directories test_cases
>  
> diff --git a/s390x/iep.c b/s390x/iep.c
> new file mode 100644
> index 0000000..e4abc72
> --- /dev/null
> +++ b/s390x/iep.c
> @@ -0,0 +1,62 @@
> +/*
> + * Instruction Execution Prevention (IEP) DAT test.
> + *
> + * Copyright (c) 2018 IBM Corp
> + *
> + * Authors:
> + *	Janosch Frank <frankja@xxxxxxxxxx>
> + *
> + * This code is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Library General Public License version 2.
> + */
> +#include <libcflat.h>
> +#include <vmalloc.h>
> +#include <asm/facility.h>
> +#include <asm/interrupt.h>
> +#include <mmu.h>
> +#include <asm/pgtable.h>
> +#include <asm-generic/barrier.h>
> +
> +static void test_iep(void)
> +{
> +	uint16_t *code;
> +	uint8_t *iepbuf = NULL;
> +	void (*fn)(void);
> +
> +	/* Enable IEP */
> +	ctl_set_bit(0, 20);
> +
> +	/* Get and protect a page with the IEP bit */
> +	iepbuf = alloc_page();
> +	protect_page(iepbuf, PAGE_ENTRY_IEP);
> +
> +	/* Code branches into r14 which contains the return address. */
> +	code = (uint16_t *)iepbuf;
> +	*code = 0x07fe;
> +	fn = (void *)code;
> +
> +	expect_pgm_int();
> +	/* Jump into protected page */
> +	fn();
> +	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
> +	unprotect_page(iepbuf, PAGE_ENTRY_IEP);
> +	ctl_clear_bit(0, 20);
> +}
> +
> +int main(void)
> +{
> +	bool has_iep = test_facility(130);
> +
> +	report_prefix_push("iep");
> +	report_xfail("DAT IEP available", !has_iep, has_iep);
> +	if (!has_iep)
> +		goto done;
> +
> +	/* Setup DAT 1:1 mapping and memory management */
> +	setup_vm();
> +	test_iep();
> +
> +done:
> +	report_prefix_pop();
> +	return report_summary();
> +}
> diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
> index ff7eea1..760402e 100644
> --- a/s390x/unittests.cfg
> +++ b/s390x/unittests.cfg
> @@ -55,3 +55,7 @@ file = vector.elf
>  
>  [gs]
>  file = gs.elf
> +
> +[iep]
> +file = iep.elf
> +
> 

Acked-by: David Hildenbrand <david@xxxxxxxxxx>

We might hold the range protection functions back and include once we're
actually using them.

Are you planning to extend the IEP test case? (e.g. not a call into
protected memory but let code run into a protected page)

Instead of using a hand crafted function, you could use a real function,
make sure it sits on a single page (isolated from other code), and call
it before protection, after protection and after unprotection.

noinline __attribute__((aligned(4096)))
static void testf(void) {...}

noinline __attribute__((aligned(4096)))
static void test_iep(void)

I used something similar once to make sure my target code resides on a
separate page, maybe it could work here too.

-- 

Thanks,

David / dhildenb



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux