[PATCH v3 8/9] KVM: s390: vsie: Fix guest page tables protection

Claudio Imbrenda posted 9 patches 1 week, 4 days ago
There is a newer version of this series
[PATCH v3 8/9] KVM: s390: vsie: Fix guest page tables protection
Posted by Claudio Imbrenda 1 week, 4 days ago
When shadowing, the guest page tables are write-protected, in order to
trap changes and properly unshadow the shadow mapping for the nested
guest. Already shadowed levels are skipped, so that only the needed
levels are write protected.

Currently the levels that get write protected are exactly one level too
deep: the last level (nested guest memory) gets protected in the wrong
way, and will be protected again correctly a few lines afterwards; most
importantly, the highest non-shadowed level does *not* get write
protected.

Moreover, if the nested guest is running in a real address space, there
are no DAT tables to shadow.

Write protect the correct levels, so that all the levels that need to
be protected are protected, and avoid double protecting the last level;
skip attempting to shadow the DAT tables when the nested guest is
running in a real address space.

Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Tested-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Janosch Frank <frankja@linux.ibm.com>
---
 arch/s390/kvm/gaccess.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c
index 0ac2d775d4c0..93a757749a6e 100644
--- a/arch/s390/kvm/gaccess.c
+++ b/arch/s390/kvm/gaccess.c
@@ -1518,6 +1518,13 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
 	    (!ptep && crste_leaf(*table) && !table->h.i && table->h.p == w->p))
 		return 0;
 
+	/* In case of a real address space */
+	if (w->level <= LEVEL_MEM) {
+		l = TABLE_TYPE_PAGE_TABLE;
+		hl = TABLE_TYPE_REGION1;
+		goto real_address_space;
+	}
+
 	gl = get_level(table, ptep);
 
 	/*
@@ -1525,8 +1532,8 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
 	 * only the page containing the entry, not the whole table.
 	 */
 	for (i = gl ; i >= w->level; i--) {
-		rc = gmap_protect_rmap(mc, sg, entries[i - 1].gfn, gpa_to_gfn(saddr),
-				       entries[i - 1].pfn, i, entries[i - 1].writable);
+		rc = gmap_protect_rmap(mc, sg, entries[i].gfn, gpa_to_gfn(saddr),
+				       entries[i].pfn, i + 1, entries[i].writable);
 		if (rc)
 			return rc;
 		if (!sg->parent)
@@ -1542,6 +1549,7 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
 	/* Get the smallest granularity */
 	l = min3(gl, hl, w->level);
 
+real_address_space:
 	flags = DAT_WALK_SPLIT_ALLOC | (uses_skeys(sg->parent) ? DAT_WALK_USES_SKEYS : 0);
 	/* If necessary, create the shadow mapping */
 	if (l < gl) {
-- 
2.53.0
Re: [PATCH v3 8/9] KVM: s390: vsie: Fix guest page tables protection
Posted by kernel test robot 1 week, 4 days ago
Hi Claudio,

kernel test robot noticed the following build warnings:

[auto build test WARNING on kvm/queue]
[also build test WARNING on kvm/next linus/master v7.0-rc5 next-20260324]
[cannot apply to kvms390/next kvm/linux-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Claudio-Imbrenda/KVM-s390-vsie-Fix-dat_split_ste/20260325-092851
base:   https://git.kernel.org/pub/scm/virt/kvm/kvm.git queue
patch link:    https://lore.kernel.org/r/20260324174301.232921-9-imbrenda%40linux.ibm.com
patch subject: [PATCH v3 8/9] KVM: s390: vsie: Fix guest page tables protection
config: s390-defconfig (https://download.01.org/0day-ci/archive/20260325/202603252045.f3Juk9sU-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project 054e11d1a17e5ba88bb1a8ef32fad3346e80b186)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260325/202603252045.f3Juk9sU-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603252045.f3Juk9sU-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> arch/s390/kvm/gaccess.c:1522:6: warning: variable 'gl' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
    1522 |         if (w->level <= LEVEL_MEM) {
         |             ^~~~~~~~~~~~~~~~~~~~~
   arch/s390/kvm/gaccess.c:1555:10: note: uninitialized use occurs here
    1555 |         if (l < gl) {
         |                 ^~
   arch/s390/kvm/gaccess.c:1522:2: note: remove the 'if' if its condition is always false
    1522 |         if (w->level <= LEVEL_MEM) {
         |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
    1523 |                 l = TABLE_TYPE_PAGE_TABLE;
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~
    1524 |                 hl = TABLE_TYPE_REGION1;
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~
    1525 |                 goto real_address_space;
         |                 ~~~~~~~~~~~~~~~~~~~~~~~~
    1526 |         }
         |         ~
   arch/s390/kvm/gaccess.c:1500:22: note: initialize the variable 'gl' to silence this warning
    1500 |         int flags, i, hl, gl, l, rc;
         |                             ^
         |                              = 0
   1 warning generated.


vim +1522 arch/s390/kvm/gaccess.c

  1495	
  1496	static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
  1497				      unsigned long saddr, struct pgtwalk *w)
  1498	{
  1499		struct guest_fault *entries;
  1500		int flags, i, hl, gl, l, rc;
  1501		union crste *table, *host;
  1502		union pte *ptep, *ptep_h;
  1503	
  1504		lockdep_assert_held(&sg->kvm->mmu_lock);
  1505		lockdep_assert_held(&sg->parent->children_lock);
  1506	
  1507		entries = get_entries(w);
  1508		ptep_h = NULL;
  1509		ptep = NULL;
  1510	
  1511		rc = dat_entry_walk(NULL, gpa_to_gfn(saddr), sg->asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE,
  1512				    &table, &ptep);
  1513		if (rc)
  1514			return rc;
  1515	
  1516		/* A race occurred. The shadow mapping is already valid, nothing to do */
  1517		if ((ptep && !ptep->h.i && ptep->h.p == w->p) ||
  1518		    (!ptep && crste_leaf(*table) && !table->h.i && table->h.p == w->p))
  1519			return 0;
  1520	
  1521		/* In case of a real address space */
> 1522		if (w->level <= LEVEL_MEM) {
  1523			l = TABLE_TYPE_PAGE_TABLE;
  1524			hl = TABLE_TYPE_REGION1;
  1525			goto real_address_space;
  1526		}
  1527	
  1528		gl = get_level(table, ptep);
  1529	
  1530		/*
  1531		 * Skip levels that are already protected. For each level, protect
  1532		 * only the page containing the entry, not the whole table.
  1533		 */
  1534		for (i = gl ; i >= w->level; i--) {
  1535			rc = gmap_protect_rmap(mc, sg, entries[i].gfn, gpa_to_gfn(saddr),
  1536					       entries[i].pfn, i + 1, entries[i].writable);
  1537			if (rc)
  1538				return rc;
  1539			if (!sg->parent)
  1540				return -EAGAIN;
  1541		}
  1542	
  1543		rc = dat_entry_walk(NULL, entries[LEVEL_MEM].gfn, sg->parent->asce, DAT_WALK_LEAF,
  1544				    TABLE_TYPE_PAGE_TABLE, &host, &ptep_h);
  1545		if (rc)
  1546			return rc;
  1547	
  1548		hl = get_level(host, ptep_h);
  1549		/* Get the smallest granularity */
  1550		l = min3(gl, hl, w->level);
  1551	
  1552	real_address_space:
  1553		flags = DAT_WALK_SPLIT_ALLOC | (uses_skeys(sg->parent) ? DAT_WALK_USES_SKEYS : 0);
  1554		/* If necessary, create the shadow mapping */
  1555		if (l < gl) {
  1556			rc = dat_entry_walk(mc, gpa_to_gfn(saddr), sg->asce, flags, l, &table, &ptep);
  1557			if (rc)
  1558				return rc;
  1559		}
  1560		if (l < hl) {
  1561			rc = dat_entry_walk(mc, entries[LEVEL_MEM].gfn, sg->parent->asce,
  1562					    flags, l, &host, &ptep_h);
  1563			if (rc)
  1564				return rc;
  1565		}
  1566	
  1567		if (KVM_BUG_ON(l > TABLE_TYPE_REGION3, sg->kvm))
  1568			return -EFAULT;
  1569		if (l == TABLE_TYPE_PAGE_TABLE)
  1570			return _do_shadow_pte(sg, saddr, ptep_h, ptep, entries + LEVEL_MEM, w->p);
  1571		return _do_shadow_crste(sg, saddr, host, table, entries + LEVEL_MEM, w->p);
  1572	}
  1573	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki