From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247864; cv=none; d=zohomail.com; s=zohoarc; b=QT91S/hmNAy3FvhiCKbW0xjNSwMB82GPeBd5GOMS0uPB7RMyXJBFgPk7jyykC8PNUEKsedOcdau5br38W4Z2yzh+tOM08PPCIfSUfujikT3wPamVXglJL0ztpctPVwf1EROiYgxlk7ZVvPwCBcOmwtyQk59TqLCBADPUpSBMEE0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247864; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=cEDD9mG2oPJvt6c9y//9kSMyh8V/fAJEbBVdy3IMrMU=; b=PXukPtPNY9Z+YKPCni4gkZHMh79Qvc7Y5K/1VECLz+v8AzwS40IQM3vkks3uZuus3Ik4X+eZmeWMUF1eeUJdibbDxOijlE9/WUNbkbkp7MgmBdoKnV0ZJx5NO32dZCbxlztnC1ZMTU7sOOWwB9Z/mJQ7lJdMhAW1ag+vDBxcI88= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1787247864138234.66463151692847; Thu, 20 Aug 2026 10:44:24 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396846.1634490 (Exim 4.92) (envelope-from ) id 1wx6o8-0000vG-HR; Thu, 20 Aug 2026 17:44:04 +0000 Received: by outflank-mailman (output) from mailman id 1396846.1634490; Thu, 20 Aug 2026 17:44:04 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6o8-0000v4-D0; Thu, 20 Aug 2026 17:44:04 +0000 Received: by outflank-mailman (input) for mailman id 1396846; Thu, 20 Aug 2026 17:44:03 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6o7-0000sP-3b for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:03 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6o6-00E23K-1p; Thu, 20 Aug 2026 17:44:02 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6o6-00DjFg-07; Thu, 20 Aug 2026 17:44:02 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=cEDD9mG2oPJvt6c9y//9kSMyh8V/fAJEbBVdy3IMrMU=; b=JOC6YU5ZVo9GVFtmytXAbvbrsj CTSBtdd+DAtuDSjpPO4AaLsztgfgGZfoEfav+R65oU3ro7b1ZuO2cUD0RHmNLub1yJjN11xyEn6F7 OjYj5G5+4bQMeiGcFAw0bykcl3C1x2zGYYJmL/BgR/jzwE2kLEckZ5S+YznQh8PDAoLk=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 1/7] x86/mm: allocate the per-domain page-tables from the xenheap Date: Thu, 20 Aug 2026 18:43:41 +0100 Message-ID: <20260820-asi-part1-1-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247865298158500 Content-Type: text/plain; charset="utf-8" The per-domain area's L3, L2 and L1 page-tables are currently domain-heap pages with no owner, and accessed through map_domain_page() whenever they need editing. That has two costs. First, every edit not going through the linear mappings of the loaded page-tables goes through map_domain_page() per level. Even now that is sometimes a mapcache round trip; once the direct map becomes on-demand it always will be. Second, the mapcache is not usable everywhere; specifically, it cannot be called in the context switch before the incoming vCPU's page-tables are loaded. Currently the Xen slot of a PV vcpu's full GDT is written during context switch; that works by special-casing the GDT/LDT L1 tables into the xenheap and stashing their addresses in d->arch.pv.gdt_ldt_l1tab. Later in the series the mappings of the guest's root page-table (for the XPTI root sync) and of the CPU's own stack (for per-CPU stack isolation) need writing at context switch too, and per-vCPU roots add writes on the PV kernel/user switch and new-CR3 paths. Instead of adding more ad-hoc pointers, or arranging to be able to call map_domain_page() from within a context switch, allocate *all* of the per-domain page-tables from the xenheap. Keep a pointer directly to the L3 page within the xenheap, and when walking the page-tables, use the MFN in the entry to reconstruct the virtual address of the xenheap page directly. Xenheap pages are mapped in every context, so the tables can be edited from anywhere through their always-mapped alias: no mapcache, no special case for the context switch. Under the planned on-demand direct map, xenheap pages remain mapped at that alias for their lifetime, so this stays true. It may seem strange for a series whose end goal is to move things out of global mappings to start by requiring a further class of pages to stay globally mapped. The series is about protecting *guest* data. The pages in question here hold page-table entries only -- MFNs and flags, reachable through the linear mappings whenever the tables are loaded anyway -- not guest data; XPTI's per-CPU root page-tables are xenheap pages by the same reasoning. The pages mapped *by* these tables (GDT/LDT frames, the mapcache's targets, the compat argument-translation area) are unaffected and stay domain-heap pages. The "capture" mode of create_perdomain_mapping() no longer needs to take its L1s from a different heap than the others; it now only records the pointers. The is_xen_heap_page() check in free_perdomain_mappings() goes away with it. No change for callers. Two side effects to note. First, the tables are subject to the xenheap allocation limit (within the PV-visible direct map), as the stashed L1s, the GDTs and XPTI's root page-tables already are -- a few pages per vcpu at most. Second, the xenheap allocator takes no domain, so there is no round-robin over the domain's node affinity: so we place the tables with MEMF_node(domain_to_node(d)), on the node of the domain's first vcpu (or the allocating CPU's node before it exists), as the stashed L1s already were. Assisted-by: Claude Code:claude-fable-5 Signed-off-by: George Dunlap --- xen/arch/x86/domain.c | 4 +- xen/arch/x86/include/asm/domain.h | 3 +- xen/arch/x86/mm.c | 121 ++++++++++-------------------- 3 files changed, 45 insertions(+), 83 deletions(-) diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 996b50af7a..163a2c97ae 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -2015,8 +2015,8 @@ void cf_check paravirt_ctxt_switch_to(struct vcpu *v) =20 if ( root_pgt ) root_pgt[root_table_offset(PERDOMAIN_VIRT_START)] =3D - l4e_from_page(v->domain->arch.perdomain_l3_pg, - __PAGE_HYPERVISOR_RW); + l4e_from_paddr(__pa(v->domain->arch.perdomain_l3), + __PAGE_HYPERVISOR_RW); =20 if ( unlikely(v->arch.dr7 & DR7_ACTIVE_MASK) ) activate_debugregs(v); diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/d= omain.h index 2d0a915410..5275bb10ea 100644 --- a/xen/arch/x86/include/asm/domain.h +++ b/xen/arch/x86/include/asm/domain.h @@ -332,7 +332,8 @@ struct monitor_write_data { =20 struct arch_domain { - struct page_info *perdomain_l3_pg; + /* Xenheap page: the per-domain page-tables are always mapped. */ + l3_pgentry_t *perdomain_l3; =20 /* I/O-port admin-specified access capabilities. */ struct rangeset *ioport_caps; diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index b158742408..38a0f984fc 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -1679,7 +1679,7 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, =20 /* Slot 260: Per-domain mappings. */ l4t[l4_table_offset(PERDOMAIN_VIRT_START)] =3D - l4e_from_page(d->arch.perdomain_l3_pg, __PAGE_HYPERVISOR_RW); + l4e_from_mfn(virt_to_mfn(d->arch.perdomain_l3), __PAGE_HYPERVISOR_= RW); =20 /* Slot 4: Per-domain mappings mirror. */ BUILD_BUG_ON(IS_ENABLED(CONFIG_PV32) && @@ -6219,54 +6219,47 @@ int create_perdomain_mapping(struct domain *d, unsi= gned long va, l3_pgentry_t *l3tab; l2_pgentry_t *l2tab; l1_pgentry_t *l1tab; + unsigned int memflags =3D MEMF_node(domain_to_node(d)); int rc =3D 0; =20 ASSERT(va >=3D PERDOMAIN_VIRT_START && va < PERDOMAIN_VIRT_SLOT(PERDOMAIN_SLOTS)); =20 - if ( !d->arch.perdomain_l3_pg ) + /* + * The per-domain page-tables come from the xenheap, so that they can = be + * edited from any context through their always-mapped alias, without + * going through the mapcache -- in particular from the context switch, + * which writes the incoming vcpu's tables before loading them. + */ + l3tab =3D d->arch.perdomain_l3; + if ( !l3tab ) { - pg =3D alloc_domheap_page(d, MEMF_no_owner); - if ( !pg ) + l3tab =3D alloc_xenheap_pages(0, memflags); + if ( !l3tab ) return -ENOMEM; - l3tab =3D __map_domain_page(pg); clear_page(l3tab); - d->arch.perdomain_l3_pg =3D pg; - if ( !nr ) - { - unmap_domain_page(l3tab); - return 0; - } + d->arch.perdomain_l3 =3D l3tab; } - else if ( !nr ) + + if ( !nr ) return 0; - else - l3tab =3D __map_domain_page(d->arch.perdomain_l3_pg); =20 ASSERT(!l3_table_offset(va ^ (va + nr * PAGE_SIZE - 1))); =20 if ( !(l3e_get_flags(l3tab[l3_table_offset(va)]) & _PAGE_PRESENT) ) { - pg =3D alloc_domheap_page(d, MEMF_no_owner); - if ( !pg ) - { - unmap_domain_page(l3tab); + l2tab =3D alloc_xenheap_pages(0, memflags); + if ( !l2tab ) return -ENOMEM; - } - l2tab =3D __map_domain_page(pg); clear_page(l2tab); - l3tab[l3_table_offset(va)] =3D l3e_from_page(pg, __PAGE_HYPERVISOR= _RW); + l3tab[l3_table_offset(va)] =3D l3e_from_mfn(virt_to_mfn(l2tab), + __PAGE_HYPERVISOR_RW); } else - l2tab =3D map_l2t_from_l3e(l3tab[l3_table_offset(va)]); - - unmap_domain_page(l3tab); + l2tab =3D maddr_to_virt(l3e_get_paddr(l3tab[l3_table_offset(va)])); =20 if ( !pl1tab && !ppg ) - { - unmap_domain_page(l2tab); return 0; - } =20 for ( l1tab =3D NULL; !rc && nr--; ) { @@ -6274,33 +6267,22 @@ int create_perdomain_mapping(struct domain *d, unsi= gned long va, =20 if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) ) { + l1tab =3D alloc_xenheap_pages(0, memflags); + if ( !l1tab ) + { + rc =3D -ENOMEM; + break; + } if ( pl1tab && !IS_NIL(pl1tab) ) { - l1tab =3D alloc_xenheap_pages(0, MEMF_node(domain_to_node(= d))); - if ( !l1tab ) - { - rc =3D -ENOMEM; - break; - } ASSERT(!pl1tab[l2_table_offset(va)]); pl1tab[l2_table_offset(va)] =3D l1tab; - pg =3D virt_to_page(l1tab); - } - else - { - pg =3D alloc_domheap_page(d, MEMF_no_owner); - if ( !pg ) - { - rc =3D -ENOMEM; - break; - } - l1tab =3D __map_domain_page(pg); } clear_page(l1tab); - *pl2e =3D l2e_from_page(pg, __PAGE_HYPERVISOR_RW); + *pl2e =3D l2e_from_mfn(virt_to_mfn(l1tab), __PAGE_HYPERVISOR_R= W); } else if ( !l1tab ) - l1tab =3D map_l1t_from_l2e(*pl2e); + l1tab =3D maddr_to_virt(l2e_get_paddr(*pl2e)); =20 if ( ppg && !(l1e_get_flags(l1tab[l1_table_offset(va)]) & _PAGE_PRESENT) ) @@ -6321,15 +6303,10 @@ int create_perdomain_mapping(struct domain *d, unsi= gned long va, =20 va +=3D PAGE_SIZE; if ( rc || !nr || !l1_table_offset(va) ) - { - /* Note that this is a no-op for the alloc_xenheap_page() case= . */ - unmap_domain_page(l1tab); l1tab =3D NULL; - } } =20 ASSERT(!l1tab); - unmap_domain_page(l2tab); =20 return rc; } @@ -6343,15 +6320,15 @@ void destroy_perdomain_mapping(struct domain *d, un= signed long va, va < PERDOMAIN_VIRT_SLOT(PERDOMAIN_SLOTS)); ASSERT(!nr || !l3_table_offset(va ^ (va + nr * PAGE_SIZE - 1))); =20 - if ( !d->arch.perdomain_l3_pg ) + l3tab =3D d->arch.perdomain_l3; + if ( !l3tab ) return; =20 - l3tab =3D __map_domain_page(d->arch.perdomain_l3_pg); pl3e =3D l3tab + l3_table_offset(va); =20 if ( l3e_get_flags(*pl3e) & _PAGE_PRESENT ) { - const l2_pgentry_t *l2tab =3D map_l2t_from_l3e(*pl3e); + const l2_pgentry_t *l2tab =3D maddr_to_virt(l3e_get_paddr(*pl3e)); const l2_pgentry_t *pl2e =3D l2tab + l2_table_offset(va); unsigned int i =3D l1_table_offset(va); =20 @@ -6359,7 +6336,7 @@ void destroy_perdomain_mapping(struct domain *d, unsi= gned long va, { if ( l2e_get_flags(*pl2e) & _PAGE_PRESENT ) { - l1_pgentry_t *l1tab =3D map_l1t_from_l2e(*pl2e); + l1_pgentry_t *l1tab =3D maddr_to_virt(l2e_get_paddr(*pl2e)= ); =20 for ( ; nr && i < L1_PAGETABLE_ENTRIES; --nr, ++i ) { @@ -6367,8 +6344,6 @@ void destroy_perdomain_mapping(struct domain *d, unsi= gned long va, free_domheap_page(l1e_get_page(l1tab[i])); l1tab[i] =3D l1e_empty(); } - - unmap_domain_page(l1tab); } else if ( nr + i < L1_PAGETABLE_ENTRIES ) break; @@ -6378,60 +6353,46 @@ void destroy_perdomain_mapping(struct domain *d, un= signed long va, ++pl2e; i =3D 0; } - - unmap_domain_page(l2tab); } - - unmap_domain_page(l3tab); } =20 void free_perdomain_mappings(struct domain *d) { - l3_pgentry_t *l3tab; + l3_pgentry_t *l3tab =3D d->arch.perdomain_l3; unsigned int i; =20 - if ( !d->arch.perdomain_l3_pg ) + if ( !l3tab ) return; =20 - l3tab =3D __map_domain_page(d->arch.perdomain_l3_pg); - for ( i =3D 0; i < PERDOMAIN_SLOTS; ++i) if ( l3e_get_flags(l3tab[i]) & _PAGE_PRESENT ) { - struct page_info *l2pg =3D l3e_get_page(l3tab[i]); - l2_pgentry_t *l2tab =3D __map_domain_page(l2pg); + l2_pgentry_t *l2tab =3D maddr_to_virt(l3e_get_paddr(l3tab[i])); unsigned int j; =20 for ( j =3D 0; j < L2_PAGETABLE_ENTRIES; ++j ) if ( l2e_get_flags(l2tab[j]) & _PAGE_PRESENT ) { - struct page_info *l1pg =3D l2e_get_page(l2tab[j]); + l1_pgentry_t *l1tab =3D + maddr_to_virt(l2e_get_paddr(l2tab[j])); =20 if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) { - l1_pgentry_t *l1tab =3D __map_domain_page(l1pg); unsigned int k; =20 for ( k =3D 0; k < L1_PAGETABLE_ENTRIES; ++k ) if ( perdomain_l1e_needs_freeing(l1tab[k]) ) free_domheap_page(l1e_get_page(l1tab[k])); - - unmap_domain_page(l1tab); } =20 - if ( is_xen_heap_page(l1pg) ) - free_xenheap_page(page_to_virt(l1pg)); - else - free_domheap_page(l1pg); + free_xenheap_page(l1tab); } =20 - unmap_domain_page(l2tab); - free_domheap_page(l2pg); + free_xenheap_page(l2tab); } =20 - unmap_domain_page(l3tab); - free_domheap_page(d->arch.perdomain_l3_pg); - d->arch.perdomain_l3_pg =3D NULL; + free_xenheap_page(l3tab); + d->arch.perdomain_l3 =3D NULL; } =20 static void write_sss_token(unsigned long *ptr) --=20 2.55.0 From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247871; cv=none; d=zohomail.com; s=zohoarc; b=exTodIcatcvnscY0EfQ5KvaTP7XogInoS18xMU6Oq/wWQPimWkLieCKxZ73IcPX9doFGhVnfezL3wuiH1qealhxC1EJmjMywUDAo/vAfFYDr0eCtWeTSFsZjGuhDO8LwkRDQxxBiXGphpTbn9StsbOkzS6k6hKlFvMOV13dQYP4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247871; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=uIVVXYNBp/ouS5mM6fbgcFwcP3APHMH5u1xUU28Itjw=; b=GW9ggrG7dlahTNH6QVVXvilHhWGlu0ECeCLF1nFGk4abBqZ00zNN3mRW2WZrdIH+4eqKlkw0xj2UwYR+oKpasRn5YUAsCJ4z2FI1EMrRIUotKKHFnAp32tXiTl/jlo11JQuXZLuRLFtS+NLFnzK8pzyEdcaaXEVCByI9cy5Jdjw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1787247871024647.3942766295802; Thu, 20 Aug 2026 10:44:31 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396847.1634502 (Exim 4.92) (envelope-from ) id 1wx6o9-0001Hq-RB; Thu, 20 Aug 2026 17:44:05 +0000 Received: by outflank-mailman (output) from mailman id 1396847.1634502; Thu, 20 Aug 2026 17:44:05 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6o9-0001Hj-O1; Thu, 20 Aug 2026 17:44:05 +0000 Received: by outflank-mailman (input) for mailman id 1396847; Thu, 20 Aug 2026 17:44:04 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6o8-00015l-R5 for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:04 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6o8-00E23U-0B; Thu, 20 Aug 2026 17:44:03 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6o7-00DjFg-1e; Thu, 20 Aug 2026 17:44:03 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=uIVVXYNBp/ouS5mM6fbgcFwcP3APHMH5u1xUU28Itjw=; b=vzAZ3DGb7CsElFQ64hD05qH3Vw Rx4tQ/GJZg+MWIcm2oDqeGutDxIXPhpXVHDda7QlhU0JOn7+rSQNzYbdyMTyyJt5l6gauzFbFGRQT ekAUEi3k/u+wwTHYRSkN+WkLQYO4rrgSFR5mshKUrMSjILW97fYLAVfSyv2V9hCRRAns=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 2/7] x86/mm: introduce populate_perdomain_mapping() Date: Thu, 20 Aug 2026 18:43:42 +0100 Message-ID: <20260820-asi-part1-2-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247872822158500 From: Roger Pau Monn=C3=A9 The per-domain area already has central machinery for building its page-tables and for managing the backing pages it owns itself: create_perdomain_mapping() / destroy_perdomain_mapping(), used by the mapcache bitmaps, the compat argument-translation area, and the GDT/LDT slots alike. What the interface lacks is a way to install a caller's own pages at a chosen address. The PV GDT and LDT code open-codes its own modifications to the per-domain area, capturing aliases of its L1 tables at creation time (create_perdomain_mapping()'s pl1tab argument) and stashing them in d->arch.pv.gdt_ldt_l1tab. Introduce populate_perdomain_mapping(v, va, mfn, nr, flags) to close this gap, giving the perdomain area's rules a single place to live. populate_perdomain_mapping writes the given MFNs, with the given page-table flags, into v's view of the per-domain area by walking its per-domain page-tables. Those are xenheap pages, reached through their always-mapped alias, so the walk involves no mapping and is usable from any context -- including the context switch, before the incoming vcpu's page-tables are loaded. Callers don't need to know where the page-tables live, how the area is structured, or whether it is per-domain or per-vcpu. We require the range to already have been populated down to the L1 tables by create_perdomain_mapping(). TLB flushing is left to the caller. A present entry not owned by the area (!_PAGE_AVAIL0) is replaced. A present entry owned by the area (_PAGE_AVAIL0, installed by create_perdomain_mapping() itself) is freed and replaced: such a page is referenced only by the mapping, so displacing it without freeing it would leak it. Nothing in this series replaces area-owned backing, so the free is marked ASSERT_UNREACHABLE(); note that freeing requires a context where the allocator may be entered -- IRQs enabled, not in interrupt context (see ASSERT_ALLOC_CONTEXT()) -- so any future caller replacing area-owned backing must not do so from the context switch path. Missing page-table structure is a hypervisor bug and BUG_ON(): there is no safe continuation, least of all from the context switch, where the next descriptor fetch through an unmapped GDT slot would be fatal. Subsequent patches convert the users of the stashed L1 tables to this interface, starting with the Xen slots of the full GDT; the stash -- which could in any case not represent per-vcpu mappings without being replicated for every vcpu and slot -- is then removed, leaving create_perdomain_mapping() to manage only the page-table structure and the pages the area owns itself. Later parts of the series use the new interface for their own mappings rather than adding further mechanisms. Signed-off-by: Roger Pau Monn=C3=A9 Assisted-by: Claude Code:claude-fable-5, Claude Code:claude-opus-4-8 Signed-off-by: George Dunlap --- Changes since the previously posted version: - Split the introduction of populate_perdomain_mapping() from its first user (previously one patch: "x86/pv: introduce function to populate perdomain area and use it to map Xen GDT"). - Drop the linear-map fast path and the sync_local_execstate() call: with the per-domain page-tables in the xenheap (previous patch) the walk needs no mapping, so a single path serves all callers and contexts. - Keep the ASSERT_UNREACHABLE() + free_domheap_page() handling of a replaced area-owned entry, and document the allocation-context requirement it places on callers replacing such entries. BUG_ON() missing page-table structure, instead of domain_crash(). - Take the page-table flags as a parameter (the Xen GDT and guest GDT slots want RW mappings; the zero page backing torn-down GDT slots is mapped read-only, as today). - Document the contract in a header comment. - Make the mfn parameter const and nr unsigned int, matching {create,destroy}_perdomain_mapping(). - Drop the unused cr3_mfn() helper. Considered, but not done to limit churn against the previously posted version: splitting the interface into a "populate" variant (any present entry is a bug) and an "update" variant (replacement expected), so that call sites declare their intent and unexpected collisions become detectable. Of the eventual call sites in the wider series, roughly half are of each kind. Could be done as a follow-up if there is interest. --- xen/arch/x86/include/asm/mm.h | 3 ++ xen/arch/x86/mm.c | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h index 2254a7e3fe..1888807394 100644 --- a/xen/arch/x86/include/asm/mm.h +++ b/xen/arch/x86/include/asm/mm.h @@ -606,6 +606,9 @@ int compat_arch_memory_op(unsigned long cmd, XEN_GUEST_= HANDLE_PARAM(void) arg); int create_perdomain_mapping(struct domain *d, unsigned long va, unsigned int nr, l1_pgentry_t **pl1tab, struct page_info **ppg); +void populate_perdomain_mapping(const struct vcpu *v, unsigned long va, + const mfn_t *mfn, unsigned int nr, + unsigned int flags); void destroy_perdomain_mapping(struct domain *d, unsigned long va, unsigned int nr); void free_perdomain_mappings(struct domain *d); diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 38a0f984fc..1810971677 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -6311,6 +6311,74 @@ int create_perdomain_mapping(struct domain *d, unsig= ned long va, return rc; } =20 +/* + * Map @nr pages, @mfn[0..nr-1], at consecutive pages from @va in v's view= of + * the per-domain area, with page-table @flags. The range must lie within= a + * single per-domain slot, and must already have been plumbed down to the = L1 + * tables by create_perdomain_mapping(): missing structure is a bug. A + * present entry not owned by the area (no _PAGE_AVAIL0) is silently + * replaced, as that is how callers update their mappings; a present + * area-owned entry is freed and replaced, which constrains the calling + * context (see the comment in the body). No TLB flushing is done: the + * caller decides whether the old translations can still be cached + * anywhere. + * + * The walk goes through the always-mapped xenheap alias of the per-domain + * page-tables, so it needs nothing from the current address space and is + * usable from any context -- including the context switch, before the + * incoming vcpu's page-tables are loaded. + */ +void populate_perdomain_mapping(const struct vcpu *v, unsigned long va, + const mfn_t *mfn, unsigned int nr, + unsigned int flags) +{ + l1_pgentry_t *l1tab =3D NULL, *pl1e; + const l3_pgentry_t *l3tab; + const l2_pgentry_t *l2tab; + + ASSERT(va >=3D PERDOMAIN_VIRT_START && + va < PERDOMAIN_VIRT_SLOT(PERDOMAIN_SLOTS)); + ASSERT(!nr || !l3_table_offset(va ^ (va + nr * PAGE_SIZE - 1))); + /* Area-owned pages are installed by create_perdomain_mapping() only. = */ + ASSERT(!(flags & _PAGE_AVAIL0)); + + l3tab =3D v->domain->arch.perdomain_l3; + BUG_ON(!l3tab); + BUG_ON(!(l3e_get_flags(l3tab[l3_table_offset(va)]) & _PAGE_PRESENT)); + + l2tab =3D maddr_to_virt(l3e_get_paddr(l3tab[l3_table_offset(va)])); + + for ( ; nr--; va +=3D PAGE_SIZE, mfn++ ) + { + if ( !l1tab || !l1_table_offset(va) ) + { + const l2_pgentry_t *pl2e =3D l2tab + l2_table_offset(va); + + BUG_ON(!(l2e_get_flags(*pl2e) & _PAGE_PRESENT)); + l1tab =3D maddr_to_virt(l2e_get_paddr(*pl2e)); + } + + pl1e =3D &l1tab[l1_table_offset(va)]; + + /* + * An area-owned entry (installed by create_perdomain_mapping(), + * marked _PAGE_AVAIL0) holds the only reference to its page, so + * displacing it means freeing it. Nothing in this series replaces + * area-owned backing, hence the ASSERT_UNREACHABLE(); any future + * caller doing so must run where freeing is permitted -- IRQs + * enabled, not in interrupt context (see ASSERT_ALLOC_CONTEXT()) + * -- which the context switch path is not. + */ + if ( unlikely(perdomain_l1e_needs_freeing(*pl1e)) ) + { + ASSERT_UNREACHABLE(); + free_domheap_page(l1e_get_page(*pl1e)); + } + + l1e_write(pl1e, l1e_from_mfn(*mfn, flags)); + } +} + void destroy_perdomain_mapping(struct domain *d, unsigned long va, unsigned int nr) { --=20 2.55.0 From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247865; cv=none; d=zohomail.com; s=zohoarc; b=XXs9+tul7T2LDYCS/zPR+LfWO0LJ5Y3h4o/1O466I+0QUUNzx8BoK2iHKPMDApyD13uw8ZnTIE6t8cHOtNZEWqTSBFrFRhncrykVXJ2DDjjED39UNKmDLFfyont6IOOBP4OaUnmwBMkPCqu1/L1neIKeAfC4NTCVD+X4Za3RP1I= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247865; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=KyUzHauc47Dbzb+CzrzMBc+T+6WI7kFJLHChZreYt2Y=; b=CeAOdpgpqzL9FYtuBe1Y2ENGY6dEIOckss2sWStdeqa0WbGVC74sTXd8KRIsmkWOnRzPlenBpKY0WMeikO5SOh0MO3txH5syufPA7sHolq+w8DutMcQbJFcKfpojyOffkDJ451EgmK3/Wf+pLL9SoKrBxUAEIYf2cLc/e8+VE0w= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1787247865428776.6371729080745; Thu, 20 Aug 2026 10:44:25 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396848.1634510 (Exim 4.92) (envelope-from ) id 1wx6oB-0001V5-1m; Thu, 20 Aug 2026 17:44:07 +0000 Received: by outflank-mailman (output) from mailman id 1396848.1634510; Thu, 20 Aug 2026 17:44:07 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oA-0001Uy-VT; Thu, 20 Aug 2026 17:44:06 +0000 Received: by outflank-mailman (input) for mailman id 1396848; Thu, 20 Aug 2026 17:44:06 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oA-0001My-3L for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:06 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6o9-00E23e-1a; Thu, 20 Aug 2026 17:44:05 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6o8-00DjFg-3D; Thu, 20 Aug 2026 17:44:05 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=KyUzHauc47Dbzb+CzrzMBc+T+6WI7kFJLHChZreYt2Y=; b=WW9phl8g1HvvMFoIU0fHSEPlwm Q47FtqfSVIV4WP5EZyUC2aONk9rsUMyxNacn7zzrw+bwnNs8YzqNmpRXUJzp6snu1J8fbwRHdU7YK pI6Mo9L2eEmogfFaBCZaw5lgp5imqJIbA/W0v1UyGJDDSTgMDT1NYyNIOODvowJtFeG4=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 3/7] x86/pv: use populate_perdomain_mapping() to map the Xen GDT Date: Thu, 20 Aug 2026 18:43:43 +0100 Message-ID: <20260820-asi-part1-3-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247866747158500 From: Roger Pau Monn=C3=A9 Currently, update_xen_slot_in_full_gdt() uses the stashed pointer in d->arch.pv.gdt_ldt_l1tab to update the incoming vcpu's page-tables with Xen's GDT, by writing a stashed per-cpu copy of a pre-baked L1 entry (either 64-bit or compat version). Now that all perdomain pagetables are allocated from the xenheap and their root L3 stashed in d->arch.perdomain_l3, d->arch.pv.gdt_ldt_l1tab is redundant. Remove one user by switching update_xen_slot_in_full_gdt() to using populate_perdomain_mapping(). Since populate_perdomain_mapping() takes an mfn rather than an l1e, cache the mfn of the per-cpu page instead. As a side effect, this consolidates the setting of the flags into a single place. Continue to check that the per-cpu value we're using has been initialized: per-CPU data starts out zeroed, and no GDT can live at MFN 0. What was one store through a cached pointer is now an out-of-line walk (L3 pointer, L3e, L2e, then the L1e) on every PV context switch: three dependent loads, negligible next to the CR3 write that follows. Signed-off-by: Roger Pau Monn=C3=A9 Assisted-by: Claude Code:claude-fable-5, Claude Code:claude-opus-4-8 Signed-off-by: George Dunlap --- Changes since the previously posted version: - populate_perdomain_mapping() introduction split into the previous patch; this patch is now just the Xen GDT conversion. - Retain the "GDT MFN cached" check as ASSERT(mfn_x(mfn)). --- xen/arch/x86/domain.c | 13 +++++++++---- xen/arch/x86/include/asm/desc.h | 6 ++++-- xen/arch/x86/smpboot.c | 14 +++++--------- xen/arch/x86/traps.c | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 163a2c97ae..f5b2eef95a 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -2062,11 +2062,16 @@ static always_inline bool need_full_gdt(const struc= t domain *d) =20 static void update_xen_slot_in_full_gdt(const struct vcpu *v, unsigned int= cpu) { - ASSERT(per_cpu(gdt_l1e, cpu).l1); /* Confirm these have been cached. */ + mfn_t mfn =3D !is_pv_32bit_vcpu(v) ? per_cpu(gdt_mfn, cpu) + : per_cpu(compat_gdt_mfn, cpu); =20 - l1e_write(pv_gdt_ptes(v) + FIRST_RESERVED_GDT_PAGE, - !is_pv_32bit_vcpu(v) ? per_cpu(gdt_l1e, cpu) - : per_cpu(compat_gdt_l1e, cpu)); + /* Confirm the GDT MFNs have been cached (MFN 0 is never a GDT). */ + ASSERT(mfn_x(mfn)); + + populate_perdomain_mapping(v, + GDT_VIRT_START(v) + + (FIRST_RESERVED_GDT_PAGE << PAGE_SHIFT), + &mfn, 1, __PAGE_HYPERVISOR_RW); } =20 static void load_full_gdt(const struct vcpu *v, unsigned int cpu) diff --git a/xen/arch/x86/include/asm/desc.h b/xen/arch/x86/include/asm/des= c.h index dcbdac3ff7..a26df9fbe9 100644 --- a/xen/arch/x86/include/asm/desc.h +++ b/xen/arch/x86/include/asm/desc.h @@ -44,6 +44,8 @@ =20 #ifndef __ASSEMBLER__ =20 +#include + #define GUEST_KERNEL_RPL(d) (is_pv_32bit_domain(d) ? 1 : 3) =20 /* Fix up the RPL of a guest segment selector. */ @@ -136,10 +138,10 @@ struct __packed desc_ptr { =20 extern seg_desc_t boot_gdt[]; DECLARE_PER_CPU(seg_desc_t *, gdt); -DECLARE_PER_CPU(l1_pgentry_t, gdt_l1e); +DECLARE_PER_CPU(mfn_t, gdt_mfn); extern seg_desc_t boot_compat_gdt[]; DECLARE_PER_CPU(seg_desc_t *, compat_gdt); -DECLARE_PER_CPU(l1_pgentry_t, compat_gdt_l1e); +DECLARE_PER_CPU(mfn_t, compat_gdt_mfn); DECLARE_PER_CPU(bool, full_gdt_loaded); =20 static inline void lgdt(const struct desc_ptr *gdtr) diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c index 84e9e4beed..9246945506 100644 --- a/xen/arch/x86/smpboot.c +++ b/xen/arch/x86/smpboot.c @@ -1085,8 +1085,7 @@ static int cpu_smpboot_alloc(unsigned int cpu) if ( gdt =3D=3D NULL ) goto out; per_cpu(gdt, cpu) =3D gdt; - per_cpu(gdt_l1e, cpu) =3D - l1e_from_pfn(virt_to_mfn(gdt), __PAGE_HYPERVISOR_RW); + per_cpu(gdt_mfn, cpu) =3D _mfn(virt_to_mfn(gdt)); memcpy(gdt, boot_gdt, NR_RESERVED_GDT_PAGES * PAGE_SIZE); BUILD_BUG_ON(NR_CPUS > 0x10000); gdt[PER_CPU_GDT_ENTRY - FIRST_RESERVED_GDT_ENTRY].a =3D cpu; @@ -1095,8 +1094,7 @@ static int cpu_smpboot_alloc(unsigned int cpu) per_cpu(compat_gdt, cpu) =3D gdt =3D alloc_xenheap_pages(0, memflags); if ( gdt =3D=3D NULL ) goto out; - per_cpu(compat_gdt_l1e, cpu) =3D - l1e_from_pfn(virt_to_mfn(gdt), __PAGE_HYPERVISOR_RW); + per_cpu(compat_gdt_mfn, cpu) =3D _mfn(virt_to_mfn(gdt)); memcpy(gdt, boot_compat_gdt, NR_RESERVED_GDT_PAGES * PAGE_SIZE); gdt[PER_CPU_GDT_ENTRY - FIRST_RESERVED_GDT_ENTRY].a =3D cpu; #endif @@ -1174,15 +1172,13 @@ void __init smp_prepare_cpus(void) print_cpu_info(0); =20 /* - * Cache {,compat_}gdt_l1e for the BSP now that physically relocation = is + * Cache {,compat_}gdt_mfn for the BSP now that physically relocation = is * done. It must be after physical relocation of Xen, and before the * first context_switch(). */ - this_cpu(gdt_l1e) =3D - l1e_from_pfn(virt_to_mfn(boot_gdt), __PAGE_HYPERVISOR_RW); + this_cpu(gdt_mfn) =3D _mfn(virt_to_mfn(boot_gdt)); if ( IS_ENABLED(CONFIG_PV32) ) - this_cpu(compat_gdt_l1e) =3D - l1e_from_pfn(virt_to_mfn(boot_compat_gdt), __PAGE_HYPERVISOR_R= W); + this_cpu(compat_gdt_mfn) =3D _mfn(virt_to_mfn(boot_compat_gdt)); =20 boot_cpu_physical_apicid =3D get_apic_id(); x86_cpu_to_apicid[0] =3D boot_cpu_physical_apicid; diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 1774966305..2fcaa413f7 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -71,10 +71,10 @@ DEFINE_PER_CPU(uint64_t, efer); static DEFINE_PER_CPU(unsigned long, last_extable_addr); =20 DEFINE_PER_CPU_READ_MOSTLY(seg_desc_t *, gdt); -DEFINE_PER_CPU_READ_MOSTLY(l1_pgentry_t, gdt_l1e); +DEFINE_PER_CPU_READ_MOSTLY(mfn_t, gdt_mfn); #ifdef CONFIG_PV32 DEFINE_PER_CPU_READ_MOSTLY(seg_desc_t *, compat_gdt); -DEFINE_PER_CPU_READ_MOSTLY(l1_pgentry_t, compat_gdt_l1e); +DEFINE_PER_CPU_READ_MOSTLY(mfn_t, compat_gdt_mfn); #endif =20 /* --=20 2.55.0 From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247872; cv=none; d=zohomail.com; s=zohoarc; b=YLnl6gx2QAU5FP7+m5pGxw+q9z9l4hzlYIAIjNNPP7hYSVaTCo9tXpPS/y0T1w0g+hqbtb3L7qxXp8b9ByCEj+cGp88rGny5P2tNzSz6iQdkwt+9CV0IGnLnz9TrJHCZg5FSh2Y+1Auh63wHG7kkWDRCPiSV422qSDBYDYkM9D0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247872; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=OfHOX4KyRE8Utb0GZ4BSRadT0zqgRSmp6GLhoRcTwLQ=; b=jMAbKNQsjKkEVpxYk+6A/x/DtoprLQMuYihrWeJXdeNraRE5XVDhgNcPxF6FRZlmV3nNtxWFFZu25Xpfncak/i9CRrh7WIl1nbQM90Cr8B6fL+huNuAUE4rtStQD5v/O5AQwvaXqNzmafaLnLBOU3SzfMn3NzTCdS7b3xS9Wf8Q= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 178724787257970.20083533496654; Thu, 20 Aug 2026 10:44:32 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396849.1634520 (Exim 4.92) (envelope-from ) id 1wx6oD-0001kZ-8z; Thu, 20 Aug 2026 17:44:09 +0000 Received: by outflank-mailman (output) from mailman id 1396849.1634520; Thu, 20 Aug 2026 17:44:09 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oD-0001kQ-5b; Thu, 20 Aug 2026 17:44:09 +0000 Received: by outflank-mailman (input) for mailman id 1396849; Thu, 20 Aug 2026 17:44:07 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oB-0001bY-FY for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:07 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6oA-00E23p-36; Thu, 20 Aug 2026 17:44:06 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6oA-00DjFg-1U; Thu, 20 Aug 2026 17:44:06 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=OfHOX4KyRE8Utb0GZ4BSRadT0zqgRSmp6GLhoRcTwLQ=; b=4y8MUZHlsbdrNBj6nQMCbGKCqH MJjbkHibEvm8XvbREkZQ2NncE0+meWnXz2bWyFrtcq009390MduLx7ClFKeeLzMKh77AN9y08MFfU xiV4wJsHh3aWu70+F9BJwXexJZBd+Rahe46P3k31wITsF9aStyysvGRjdlaOBL/XT+UY=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 4/7] x86/pv: set/clear guest GDT mappings using populate_perdomain_mapping() Date: Thu, 20 Aug 2026 18:43:44 +0100 Message-ID: <20260820-asi-part1-4-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247872825158500 From: Roger Pau Monn=C3=A9 Until the previous patch, update_xen_slot_in_full_gdt() used the stashed pointer in d->arch.pv.gdt_ldt_l1tab to update the incoming vCPU's page tables with Xen's GDT; this was necessary because perdomain pagetables were mapped from the domheap by default, and map_domain_page() couldn't be called in a context switch. Having a handy pointer to an always-mapped version of the GDT/LDT L1 table, other sites which modify the table started using it for convenience, even if they weren't called from within a context switch. One example is pv_{set,destroy}_gdt. Now that all perdomain pagetables are allocated from the xenheap and their root L3 stashed in d->arch.perdomain_l3, d->arch.pv.gdt_ldt_l1tab is redundant. The previous patch removed one user by modifying update_xen_slot_in_full_gdt() to call populate_perdomain_mapping(). Continue that process by switching both pv_{set,destroy}_gdt() to it as well. pv_destroy_gdt() currently loops over the L1 entries directly, extracting the MFN from each, dropping the type and reference unless it was the zero page, and replacing the entry with a read-only mapping of the zero page. Since we no longer have the L1 to hand, drop the references using v->arch.pv.gdt_frames[] instead, and install the zero-page mappings with a single populate_perdomain_mapping() call. This makes gdt_frames[] consistently the source of truth for MFNs. Behaviour is unchanged: torn-down slots map the zero page read-only, as they have since cf6d39f819 ("x86/PV: properly populate descriptor tables"), so that LAR/LSL/VERR/VERW on a selector beyond the guest's limit clear ZF as on native rather than taking a #PF-converted #GP -- and as every PV vCPU's unused slots do from the start, pv_set_gdt() tearing down the old GDT (zero page included) before installing the new one. In the case of pv_set_gdt, we have a slightly awkward situation with types. The ABI with the guest uses unsigned long[], but populate_perdomain_mapping wants an array of mfn_t. v->arch.pv.gdt_frames being unsigned long means we can just copy from it across the guest ABI with no conversions. We could in theory convert it to mfn_t[] instead, and then pass v->arch.pv.gdt_frames into populate_perdomain_mapping; but then we'd need to add a conversion on all the places where frames are copied out. We choose instead to copy frames into a temporary mfn_t array on the stack to pass into populate_perdomain_mapping. Signed-off-by: Roger Pau Monn=C3=A9 Assisted-by: Claude Code:claude-fable-5, Claude Code:claude-opus-4-8 Signed-off-by: George Dunlap --- Changes since the previously posted version: - Retain the gdt_ents zeroing when tearing down the GDT (its removal was queried by Jan). - Map torn-down slots read-only to the zero page (via the populate_perdomain_mapping() flags parameter) rather than removing the mappings with destroy_perdomain_mapping(): empty slots would be a guest-visible partial revert of cf6d39f819 (see the commit message). With the destroy call gone, its v->arch.cr3 guard -- also queried by Jan -- goes too: the zero-page rewrite runs unconditionally. - Keep gdt_frames[] as unsigned long[] rather than switching it to mfn_t[] as Jan suggested; the commit message explains the trade-off. - Retitle: destroy_perdomain_mapping() is no longer used here. --- xen/arch/x86/pv/descriptor-tables.c | 37 ++++++++++++++++++----------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/xen/arch/x86/pv/descriptor-tables.c b/xen/arch/x86/pv/descript= or-tables.c index 8a32b9ae5c..5dda5bffe3 100644 --- a/xen/arch/x86/pv/descriptor-tables.c +++ b/xen/arch/x86/pv/descriptor-tables.c @@ -49,33 +49,42 @@ bool pv_destroy_ldt(struct vcpu *v) =20 void pv_destroy_gdt(struct vcpu *v) { - l1_pgentry_t *pl1e =3D pv_gdt_ptes(v); - mfn_t zero_mfn =3D _mfn(virt_to_mfn(zero_page)); - l1_pgentry_t zero_l1e =3D l1e_from_mfn(zero_mfn, __PAGE_HYPERVISOR_RO); + const mfn_t zero_mfn =3D _mfn(virt_to_mfn(zero_page)); + mfn_t zero_mfns[ARRAY_SIZE(v->arch.pv.gdt_frames)]; unsigned int i; =20 ASSERT(v =3D=3D current || !vcpu_cpu_dirty(v)); =20 v->arch.pv.gdt_ents =3D 0; - for ( i =3D 0; i < FIRST_RESERVED_GDT_PAGE; i++ ) + + for ( i =3D 0; i < ARRAY_SIZE(zero_mfns); i++ ) { - mfn_t mfn =3D l1e_get_mfn(pl1e[i]); + zero_mfns[i] =3D zero_mfn; =20 - if ( (l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) && - !mfn_eq(mfn, zero_mfn) ) - put_page_and_type(mfn_to_page(mfn)); + /* MFN 0 can never pass get_page_and_type(), so 0 marks unused slo= ts. */ + if ( !v->arch.pv.gdt_frames[i] ) + continue; =20 - l1e_write(&pl1e[i], zero_l1e); + put_page_and_type(mfn_to_page(_mfn(v->arch.pv.gdt_frames[i]))); v->arch.pv.gdt_frames[i] =3D 0; } + + /* + * Point every slot at the zero page, read-only: a descriptor fetch fr= om + * the unused part of the GDT then finds a not-present descriptor rath= er + * than a missing mapping, so LAR/LSL/VERR/VERW on a selector beyond t= he + * guest's limit clear ZF as they do on native, instead of faulting. + */ + populate_perdomain_mapping(v, GDT_VIRT_START(v), zero_mfns, + ARRAY_SIZE(zero_mfns), __PAGE_HYPERVISOR_RO= ); } =20 int pv_set_gdt(struct vcpu *v, const unsigned long frames[], unsigned int entries) { struct domain *d =3D v->domain; - l1_pgentry_t *pl1e; unsigned int i, nr_frames =3D DIV_ROUND_UP(entries, 512); + mfn_t mfns[ARRAY_SIZE(v->arch.pv.gdt_frames)]; =20 ASSERT(v =3D=3D current || !vcpu_cpu_dirty(v)); =20 @@ -90,6 +99,8 @@ int pv_set_gdt(struct vcpu *v, const unsigned long frames= [], if ( !mfn_valid(mfn) || !get_page_and_type(mfn_to_page(mfn), d, PGT_seg_desc_page) ) goto fail; + + mfns[i] =3D mfn; } =20 /* Tear down the old GDT. */ @@ -97,12 +108,10 @@ int pv_set_gdt(struct vcpu *v, const unsigned long fra= mes[], =20 /* Install the new GDT. */ v->arch.pv.gdt_ents =3D entries; - pl1e =3D pv_gdt_ptes(v); for ( i =3D 0; i < nr_frames; i++ ) - { v->arch.pv.gdt_frames[i] =3D frames[i]; - l1e_write(&pl1e[i], l1e_from_pfn(frames[i], __PAGE_HYPERVISOR_RW)); - } + populate_perdomain_mapping(v, GDT_VIRT_START(v), mfns, nr_frames, + __PAGE_HYPERVISOR_RW); =20 return 0; =20 --=20 2.55.0 From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247870; cv=none; d=zohomail.com; s=zohoarc; b=nFCL42VoQhCeSqc8ICJgsesSGATnT1+PZyu6pWryi50Waeiz90hlT64LOGDgQOgrGZrA/uugDasoT0f/Gy6llZs7HnuN5bv4smur69vfBEMeuIUjV4ETk0RYM5x9LALhfMQ+wqcQaDhn1iAPiSNetsvLuGjWLqKoU32Iby3/xDU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247870; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=R1q6Hy5ynYIAwwjvHwGLb/Kb5YMqyXOjLpgHFR8Cx8w=; b=FMj5V4b8jBIl+xc/+Xp8TOEhQ1jsXcrnIUBv2YYbiIg0U/jxr6LlnOUMrSlMCDIpJFsx1joHOmgKw1temWCc3RCJrVTiJPYGEKc61mR0fOZX6p4Hc/EutqkVaQjLh0iO9jcc3oUtqt8ovc9LBGuya/A37WcvzOAkK0WP3OBEBxw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1787247870805188.3808201764008; Thu, 20 Aug 2026 10:44:30 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396850.1634530 (Exim 4.92) (envelope-from ) id 1wx6oE-0001yd-HM; Thu, 20 Aug 2026 17:44:10 +0000 Received: by outflank-mailman (output) from mailman id 1396850.1634530; Thu, 20 Aug 2026 17:44:10 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oE-0001yR-Ck; Thu, 20 Aug 2026 17:44:10 +0000 Received: by outflank-mailman (input) for mailman id 1396850; Thu, 20 Aug 2026 17:44:09 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oC-0001kB-VQ for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:08 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6oC-00E247-1M; Thu, 20 Aug 2026 17:44:08 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6oB-00DjFg-2z; Thu, 20 Aug 2026 17:44:08 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=R1q6Hy5ynYIAwwjvHwGLb/Kb5YMqyXOjLpgHFR8Cx8w=; b=pdYwYHiyc31hVa8zA+xb6TO8bJ loPp6CKyaWscSnC9EfUqCb4GVETAO7ezbJOlK0dPnM9b5nNZ69wzKl2uUvM/00AYg4VvwyWmVgq9I Vyu9oSD9IpdbKnKv1GoCJyOqMsClNuJQ36cpm5m3dASBwytVbPXKOqHkBY61dtRHpN54=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 5/7] x86/pv: update guest LDT mappings using {populate,destroy}_perdomain_mapping() Date: Thu, 20 Aug 2026 18:43:45 +0100 Message-ID: <20260820-asi-part1-5-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247872826158500 From: Roger Pau Monn=C3=A9 Until two patches ago, update_xen_slot_in_full_gdt() used the stashed pointer in d->arch.pv.gdt_ldt_l1tab to update the incoming vCPU's page tables with Xen's GDT; this was necessary because perdomain pagetables were mapped from the domheap by default, and map_domain_page() couldn't be called in a context switch. Having a handy pointer to an always-mapped version of the GDT/LDT L1 table, other sites which modify the table started using it for convenience, even if they weren't called from within a context switch. These include pv_map_ldt_shadow_page() and pv_destroy_ldt(). Now that all perdomain pagetables are allocated from the xenheap and their root L3 stashed in d->arch.perdomain_l3, d->arch.pv.gdt_ldt_l1tab is redundant. The previous two patches removed the GDT users; continue that process by refactoring the LDT sites as well. pv_map_ldt_shadow_page() is, by definition, always modifying the currently-running vCPU: it runs from the #PF handler for a guest-mode descriptor fetch, and running the guest implies its page tables are loaded. So it could simply write the linear recursive mappings directly. Go through populate_perdomain_mapping() anyway, to keep a single writer for the per-domain area. For pv_destroy_ldt(), use destroy_perdomain_mapping(). Previously, pv_destroy_ldt() used the L1 LDT entries themselves to determine which MFNs to drop type and count references to. Since we don't have the L1 handy, we must now keep the MFNs corresponding to L1 slots in an array in the vCPU structure, as we do in the GDT case. Note that mappings_dropped (the return value of pv_destroy_ldt()) now reflects the *number of valid MFNs in this array*, not *the number of non-empty L1 entries*. This introduces an invariant we must maintain: pv_map_ldt_shadow_page() writes both the array entry and the mapping, and pv_destroy_ldt() clears both, so the two stay in lockstep. Also note that, unlike pv_destroy_gdt() from the previous patch, pv_destroy_ldt() doesn't fill in the values with zero_l1e (see 61031e64d3), so there's no change here. Signed-off-by: Roger Pau Monn=C3=A9 Assisted-by: Claude Code:claude-fable-5, Claude Code:claude-opus-4-8 Signed-off-by: George Dunlap --- Changes since the previously posted version: - Initialise ldt_frames[] ahead of the first fail-able initialisation step. - Call destroy_perdomain_mapping() with its existing domain parameter; the switch to a vCPU parameter moves to a future patch. - Use populate_perdomain_mapping() in pv_map_ldt_shadow_page() rather than open-coding the linear-map write; retitle accordingly. - Comment the INVALID_MFN skip in pv_destroy_ldt(): the LDT is demand-faulted, so its pages may be sparsely mapped (Alejandro's question on v2). - Rewrite commit message (including describing the ldt_frames[] array's role directly, as Jan asked). --- xen/arch/x86/include/asm/domain.h | 2 ++ xen/arch/x86/pv/descriptor-tables.c | 20 +++++++++++--------- xen/arch/x86/pv/domain.c | 4 ++++ xen/arch/x86/pv/mm.c | 16 ++++++++++++---- 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/d= omain.h index 5275bb10ea..2c9efd59bf 100644 --- a/xen/arch/x86/include/asm/domain.h +++ b/xen/arch/x86/include/asm/domain.h @@ -542,6 +542,8 @@ struct pv_vcpu struct trap_info *trap_ctxt; =20 unsigned long gdt_frames[FIRST_RESERVED_GDT_PAGE]; + /* Max LDT entries is 8192, so 8192 * 8 =3D 64KiB (16 pages). */ + mfn_t ldt_frames[16]; unsigned long ldt_base; unsigned int gdt_ents, ldt_ents; =20 diff --git a/xen/arch/x86/pv/descriptor-tables.c b/xen/arch/x86/pv/descript= or-tables.c index 5dda5bffe3..261bf29c90 100644 --- a/xen/arch/x86/pv/descriptor-tables.c +++ b/xen/arch/x86/pv/descriptor-tables.c @@ -20,28 +20,30 @@ */ bool pv_destroy_ldt(struct vcpu *v) { - l1_pgentry_t *pl1e; + const unsigned int nr_frames =3D ARRAY_SIZE(v->arch.pv.ldt_frames); unsigned int i, mappings_dropped =3D 0; - struct page_info *page; =20 ASSERT(!in_irq()); =20 ASSERT(v =3D=3D current || !vcpu_cpu_dirty(v)); =20 - pl1e =3D pv_ldt_ptes(v); + destroy_perdomain_mapping(v->domain, LDT_VIRT_START(v), nr_frames); =20 - for ( i =3D 0; i < 16; i++ ) + for ( i =3D 0; i < nr_frames; i++ ) { - if ( !(l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) ) - continue; + mfn_t mfn =3D v->arch.pv.ldt_frames[i]; + struct page_info *page; =20 - page =3D l1e_get_page(pl1e[i]); - l1e_write(&pl1e[i], l1e_empty()); - mappings_dropped++; + /* The LDT is demand-faulted, so its pages may be sparsely mapped.= */ + if ( mfn_eq(mfn, INVALID_MFN) ) + continue; =20 + v->arch.pv.ldt_frames[i] =3D INVALID_MFN; + page =3D mfn_to_page(mfn); ASSERT_PAGE_IS_TYPE(page, PGT_seg_desc_page); ASSERT_PAGE_IS_DOMAIN(page, v->domain); put_page_and_type(page); + mappings_dropped++; } =20 return mappings_dropped; diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c index 0c42ae58aa..7ddab1949f 100644 --- a/xen/arch/x86/pv/domain.c +++ b/xen/arch/x86/pv/domain.c @@ -340,10 +340,14 @@ void pv_vcpu_destroy(struct vcpu *v) int pv_vcpu_initialise(struct vcpu *v) { struct domain *d =3D v->domain; + unsigned int i; int rc; =20 ASSERT(!is_idle_domain(d)); =20 + for ( i =3D 0; i < ARRAY_SIZE(v->arch.pv.ldt_frames); i++ ) + v->arch.pv.ldt_frames[i] =3D INVALID_MFN; + rc =3D pv_create_gdt_ldt_l1tab(v); if ( rc ) return rc; diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c index 5378299b8c..da280d7757 100644 --- a/xen/arch/x86/pv/mm.c +++ b/xen/arch/x86/pv/mm.c @@ -53,7 +53,8 @@ bool pv_map_ldt_shadow_page(unsigned int offset) struct vcpu *curr =3D current; struct domain *currd =3D curr->domain; struct page_info *page; - l1_pgentry_t gl1e, *pl1e, nl1e; + l1_pgentry_t gl1e; + mfn_t mfn; unsigned long linear =3D curr->arch.pv.ldt_base + offset; =20 BUG_ON(in_irq()); @@ -87,10 +88,17 @@ bool pv_map_ldt_shadow_page(unsigned int offset) return false; } =20 - pl1e =3D &pv_ldt_ptes(curr)[offset >> PAGE_SHIFT]; - nl1e =3D l1e_from_pfn(l1e_get_pfn(gl1e), __PAGE_HYPERVISOR_RW); + mfn =3D page_to_mfn(page); + curr->arch.pv.ldt_frames[offset >> PAGE_SHIFT] =3D mfn; =20 - l1e_write(pl1e, nl1e); + /* + * Running the guest implies its page-tables are loaded, so the linear + * mappings would do; go through the interface anyway to keep a single + * writer for the per-domain area. + */ + populate_perdomain_mapping(curr, + LDT_VIRT_START(curr) + (offset & PAGE_MASK), + &mfn, 1, __PAGE_HYPERVISOR_RW); =20 return true; } --=20 2.55.0 From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247876; cv=none; d=zohomail.com; s=zohoarc; b=aVn21khYzrY2HxWV5zYbI+kKzbqo85+aPwzwGEO5yKaV6quTMD/R3VNmQxD/C3MRvPPmaiTVkOdvI/aIMgFMimz2TRSNSTDYZJAphF9RIIoP0Bt/+InsDOwXX9G2QVr4g96yuEBQxytJ1a2NOzcha/CKD4TqPwFU9FuJAE+CbNo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247876; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=oJNvo8JLdTW3Dqid1mmBEIeehjw6UcTmR3AblTiyPqc=; b=fixZ/T3PDG17n5jX7R99Gtez9LyvsO04ZFOHyFYti9RNqgbIvD4adH3SIDUJctIjwIoVBGPdq5yl+sSzhPhf/JNm+f+MRCR41QKogHgRDtlAE5sHB8fypPF2YarKSZdicemAtnF1tw5iCpEFX6ABAToiGu6DS9UJ3hqlGZGU3AI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1787247876017356.3615602094309; Thu, 20 Aug 2026 10:44:36 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396851.1634538 (Exim 4.92) (envelope-from ) id 1wx6oF-0002Ei-UW; Thu, 20 Aug 2026 17:44:11 +0000 Received: by outflank-mailman (output) from mailman id 1396851.1634538; Thu, 20 Aug 2026 17:44:11 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oF-0002EZ-PI; Thu, 20 Aug 2026 17:44:11 +0000 Received: by outflank-mailman (input) for mailman id 1396851; Thu, 20 Aug 2026 17:44:10 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oE-0001yp-G0 for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:10 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6oD-00E24I-2r; Thu, 20 Aug 2026 17:44:09 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6oD-00DjFg-1F; Thu, 20 Aug 2026 17:44:09 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=oJNvo8JLdTW3Dqid1mmBEIeehjw6UcTmR3AblTiyPqc=; b=YbMXUi63sZKy5I28G0wUZLoKxQ y7swpzEgPhHbQxJ5BItN+sT9hbK/KHHwbKYrYM4gFA/AFnebVE8RqPxOxAX3feR+behZhCI/6xig8 /oYIzm68ix8KF2TLDHF2xgENXKiwUJsugl1DFqPvlttX3zzhl1FXMtLrQBHxkOAdzZbs=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 6/7] x86/pv: remove stashing of GDT/LDT L1 page-tables Date: Thu, 20 Aug 2026 18:43:46 +0100 Message-ID: <20260820-asi-part1-6-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247876937158500 From: Roger Pau Monn=C3=A9 There are no remaining users of the stashed L1 page-tables in d->arch.pv.gdt_ldt_l1tab. Remove it, and all helpers. pv_create_gdt_ldt_l1tab() now passes NIL() rather than the stash array. create_perdomain_mapping() still eagerly allocates the L1 tables covering the GDT/LDT range, but their addresses are no longer handed back. Doing this is necessary because populate_perdomain_mapping() only fills existing tables, and treats missing structure as a bug. Signed-off-by: Roger Pau Monn=C3=A9 Assisted-by: Claude Code:claude-fable-5 Signed-off-by: George Dunlap --- Changes since the previously posted version: - Note the implications of changing from pointer to NIL() in pv_create_gdt_ldt_l1tab(). In v2 this also changed where new GDT/LDT L1 tables were allocated from: upstream's capture mode takes them from the xenheap (the stashed pointer has to stay usable), the NIL() mode from the domheap. Here they come from the xenheap in all modes ("x86/mm: allocate the per-domain page-tables from the xenheap"), so the switch only stops the addresses being handed back. --- xen/arch/x86/include/asm/domain.h | 9 --------- xen/arch/x86/pv/domain.c | 10 +--------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/d= omain.h index 2c9efd59bf..7eab2ff597 100644 --- a/xen/arch/x86/include/asm/domain.h +++ b/xen/arch/x86/include/asm/domain.h @@ -287,8 +287,6 @@ struct time_scale { =20 struct pv_domain { - l1_pgentry_t **gdt_ldt_l1tab; - atomic_t nr_l4_pages; =20 /* Is a 32-bit PV guest? */ @@ -525,13 +523,6 @@ struct arch_domain #define has_pirq(d) (!!((d)->arch.emulation_flags & X86_EMU_USE_PIR= Q)) #define has_vpci(d) (!!((d)->arch.emulation_flags & X86_EMU_VPCI)) =20 -#define gdt_ldt_pt_idx(v) \ - ((v)->vcpu_id >> (PAGETABLE_ORDER - GDT_LDT_VCPU_SHIFT)) -#define pv_gdt_ptes(v) \ - ((v)->domain->arch.pv.gdt_ldt_l1tab[gdt_ldt_pt_idx(v)] + \ - (((v)->vcpu_id << GDT_LDT_VCPU_SHIFT) & (L1_PAGETABLE_ENTRIES - 1))) -#define pv_ldt_ptes(v) (pv_gdt_ptes(v) + 16) - struct pv_vcpu { /* map_domain_page() mapping cache. */ diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c index 7ddab1949f..35d1761c9c 100644 --- a/xen/arch/x86/pv/domain.c +++ b/xen/arch/x86/pv/domain.c @@ -315,7 +315,7 @@ static int pv_create_gdt_ldt_l1tab(struct vcpu *v) { return create_perdomain_mapping(v->domain, GDT_VIRT_START(v), 1U << GDT_LDT_VCPU_SHIFT, - v->domain->arch.pv.gdt_ldt_l1tab, + NIL(l1_pgentry_t *), NULL); } =20 @@ -389,8 +389,6 @@ void pv_domain_destroy(struct domain *d) GDT_LDT_MBYTES << (20 - PAGE_SHIFT)); =20 XFREE(d->arch.pv.cpuidmasks); - - FREE_XENHEAP_PAGE(d->arch.pv.gdt_ldt_l1tab); } =20 void noreturn cf_check continue_pv_domain(void); @@ -406,12 +404,6 @@ int pv_domain_initialise(struct domain *d) =20 pv_l1tf_domain_init(d); =20 - d->arch.pv.gdt_ldt_l1tab =3D - alloc_xenheap_pages(0, MEMF_node(domain_to_node(d))); - if ( !d->arch.pv.gdt_ldt_l1tab ) - goto fail; - clear_page(d->arch.pv.gdt_ldt_l1tab); - if ( levelling_caps & ~LCAP_faulting && (d->arch.pv.cpuidmasks =3D xmemdup(&cpuidmask_defaults)) =3D=3D N= ULL ) goto fail; --=20 2.55.0 From nobody Thu Sep 24 20:24:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=xenproject.org ARC-Seal: i=1; a=rsa-sha256; t=1787247871; cv=none; d=zohomail.com; s=zohoarc; b=AEG7mAScaB+WWpitYHqFEHi2QAkS2Wj2RUbfITO1Drpu5bC2qerPnUZ6R76W/9GBJgWDYtV/C6iqm7CBw+FlQP4TJPjfPC+Uc8a3fGqJt0a0vOw3M0u4tFjS0/e9FQSCtN7jWPZhrn2a4py2n9UdX9uJAB2cUaUMKlSMw5b1l+g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1787247871; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=/Kcw94vkaHCxYDdapb519eUOueMrlstSp7fpRrLXQ38=; b=X0QDu092A1JEVSm1uk1jLxevwjT9xIotF0a/MKuAdqIH4m/YBtYXMkvRS1Dwth9cVMvJX2dM2QhaX+YNKkavgZK3kRv1JNeqqQncuXJaOXGfZ9PFYttaOx1en8tIzbv/JI+ss9MjnWlnLF2yzIx+YUvGSY0PLg20MDXveLutx/U= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1787247871121579.3836474793202; Thu, 20 Aug 2026 10:44:31 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1396852.1634547 (Exim 4.92) (envelope-from ) id 1wx6oH-0002Uh-8y; Thu, 20 Aug 2026 17:44:13 +0000 Received: by outflank-mailman (output) from mailman id 1396852.1634547; Thu, 20 Aug 2026 17:44:13 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oH-0002U3-4W; Thu, 20 Aug 2026 17:44:13 +0000 Received: by outflank-mailman (input) for mailman id 1396852; Thu, 20 Aug 2026 17:44:12 +0000 Received: from mail.xenproject.org ([104.130.215.37]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wx6oG-0002Hb-2t for xen-devel@lists.xenproject.org; Thu, 20 Aug 2026 17:44:12 +0000 Received: from xenbits.xenproject.org ([104.239.192.120]) by mail.xenproject.org with esmtp (Exim 4.96) (envelope-from ) id 1wx6oF-00E24S-17; Thu, 20 Aug 2026 17:44:11 +0000 Received: from [217.155.165.12] (helo=Georges-MacBook-Pro-2.tail87ea19.ts.net) by xenbits.xenproject.org with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (Exim 4.96) (envelope-from ) id 1wx6oE-00DjFg-2j; Thu, 20 Aug 2026 17:44:11 +0000 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=xenproject.org; s=20200302mail; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From; bh=/Kcw94vkaHCxYDdapb519eUOueMrlstSp7fpRrLXQ38=; b=ljM+ma/aVDVEQo/PRvbC3bv2Zd rJmyXQwNQTKn/Ou5LBOPke8rER0kGMa3abJV/NVi9hyp4nHoaPD1jDxFTVOXjnsvZq8XJGzoRMlc9 OEKN4lQE6XKS1AE6YG99iqB0HR8R2I9SAH4RT4B0rc1DFV68ZgKSTUIR7bFMfwLigPeE=; From: George Dunlap To: xen-devel@lists.xenproject.org Cc: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Jan Beulich , Andrew Cooper , =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= , Teddy Astie , Anthony PERARD , Michal Orzel , Julien Grall , Stefano Stabellini Subject: [PATCH 7/7] x86/mm: simplify create_perdomain_mapping() interface Date: Thu, 20 Aug 2026 18:43:47 +0100 Message-ID: <20260820-asi-part1-7-f2dbd92b8459@xenproject.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> References: <20260820-asi-part1-0-f2dbd92b8459@xenproject.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @xenproject.org) X-ZM-MESSAGEID: 1787247872811158500 From: Roger Pau Monn=C3=A9 create_perdomain_mapping()'s interface is richer than any caller needs. The paging structure for the requested range is built to a depth selected by the pl1tab and ppg arguments, each of which distinguishes NULL from NIL() from a real pointer: - nr =3D=3D 0: only ensure the per-domain L3 exists; nothing else is allocated, and the other arguments are ignored. - pl1tab =3D=3D a pointer: allocate the L1 tables covering the range, and return their addresses in the array -- the mode that existed to build the GDT/LDT stash. - pl1tab =3D=3D NIL(): allocate the L1 tables, and return nothing. - pl1tab =3D=3D NULL: do not plumb L1 tables for their own sake (they are still allocated on demand if data-page population requires them). - ppg =3D=3D a pointer: allocate and install zeroed data pages across the range, and return their struct page_info pointers in the array. - ppg =3D=3D NIL(): allocate and install the zeroed data pages, but hand nothing back; the pages are reachable only through the mapping. - ppg =3D=3D NULL: do not allocate data pages. - both NULL, nr > 0: stop after the slot's L2; do not plumb L1 tables at all. Very few of these modes have users now. The last user of the pl1tab capture mode was removed when we removed the GDT/LDT stash. The ppg capture mode never had any users. Nothing uses the both-NULL L2-only mode with nr !=3D 0. What remains is exactly one bit of information: whether the caller wants the range populated with zeroed, area-owned data pages, or merely plumbed down to the L1 tables, ready for populate_perdomain_mapping() to install caller-owned pages. Replace the two arguments with a boolean expressing that bit. With the stashing mode gone the NIL()/IS_NIL() macros lose their last user, so drop them as well; and document the resulting interface. No caller changes behaviour: every existing call maps onto the boolean exactly. Signed-off-by: Roger Pau Monn=C3=A9 Assisted-by: Claude Code:claude-fable-5 Signed-off-by: George Dunlap --- Changes since the previously posted version: - Drop the now-unused NIL()/IS_NIL() macros as requested during review - Describe the prior interface in the commit message and add a doc comment for the simplified one. --- xen/arch/x86/domain_page.c | 10 ++++----- xen/arch/x86/hvm/hvm.c | 2 +- xen/arch/x86/include/asm/mm.h | 6 +----- xen/arch/x86/mm.c | 40 +++++++++++++++++++++++------------ xen/arch/x86/pv/domain.c | 4 +--- xen/arch/x86/x86_64/mm.c | 3 +-- 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c index 72c00194f3..1c1deeeebc 100644 --- a/xen/arch/x86/domain_page.c +++ b/xen/arch/x86/domain_page.c @@ -246,8 +246,7 @@ int mapcache_domain_init(struct domain *d) spin_lock_init(&dcache->lock); =20 return create_perdomain_mapping(d, (unsigned long)dcache->inuse, - 2 * bitmap_pages + 1, - NIL(l1_pgentry_t *), NULL); + 2 * bitmap_pages + 1, false); } =20 int mapcache_vcpu_init(struct vcpu *v) @@ -264,16 +263,15 @@ int mapcache_vcpu_init(struct vcpu *v) if ( ents > dcache->entries ) { /* Populate page tables. */ - int rc =3D create_perdomain_mapping(d, MAPCACHE_VIRT_START, ents, - NIL(l1_pgentry_t *), NULL); + int rc =3D create_perdomain_mapping(d, MAPCACHE_VIRT_START, ents, = false); =20 /* Populate bit maps. */ if ( !rc ) rc =3D create_perdomain_mapping(d, (unsigned long)dcache->inus= e, - nr, NULL, NIL(struct page_info *= )); + nr, true); if ( !rc ) rc =3D create_perdomain_mapping(d, (unsigned long)dcache->garb= age, - nr, NULL, NIL(struct page_info *= )); + nr, true); =20 if ( rc ) return rc; diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 955fc062a5..e8fbe7ba27 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -620,7 +620,7 @@ int hvm_domain_initialise(struct domain *d, INIT_LIST_HEAD(&d->arch.hvm.mmcfg_regions); INIT_LIST_HEAD(&d->arch.hvm.msix_tables); =20 - rc =3D create_perdomain_mapping(d, PERDOMAIN_VIRT_START, 0, NULL, NULL= ); + rc =3D create_perdomain_mapping(d, PERDOMAIN_VIRT_START, 0, false); if ( rc ) goto fail; =20 diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h index 1888807394..30eaec9179 100644 --- a/xen/arch/x86/include/asm/mm.h +++ b/xen/arch/x86/include/asm/mm.h @@ -600,12 +600,8 @@ long arch_memory_op(unsigned long cmd, XEN_GUEST_HANDL= E_PARAM(void) arg); long subarch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) arg= ); int compat_arch_memory_op(unsigned long cmd, XEN_GUEST_HANDLE_PARAM(void) = arg); =20 -#define NIL(type) ((type *)-sizeof(type)) -#define IS_NIL(ptr) (!((uintptr_t)(ptr) + sizeof(*(ptr)))) - int create_perdomain_mapping(struct domain *d, unsigned long va, - unsigned int nr, l1_pgentry_t **pl1tab, - struct page_info **ppg); + unsigned int nr, bool populate); void populate_perdomain_mapping(const struct vcpu *v, unsigned long va, const mfn_t *mfn, unsigned int nr, unsigned int flags); diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 1810971677..aa26d12ac2 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -6211,9 +6211,33 @@ static bool perdomain_l1e_needs_freeing(l1_pgentry_t= l1e) (_PAGE_PRESENT | _PAGE_AVAIL0); } =20 +/* + * Ensure the paging structure for [va, va + nr * PAGE_SIZE) of d's + * per-domain area is in place, allocating whichever levels are missing: + * the (domain-wide) L3 root, the slot's L2, and all L1 tables covering + * the range. The page-tables come from the xenheap, so that they stay + * reachable through their always-mapped alias; populated pages come from + * the domain heap. The range must lie within a single per-domain slot + * (one L3 entry), and already-present levels and entries are left + * untouched, so calls are idempotent over existing ranges. + * + * nr =3D=3D 0: only ensure the per-domain L3 itself exists; populate is + * ignored. Used to set the area up before any sub-range is known. + * + * populate =3D=3D false: stop once the L1 tables are in place. The range= is + * then ready for caller-owned pages to be mapped and unmapped via + * populate_perdomain_mapping() / destroy_perdomain_mapping(), which only + * fill (or clear) existing tables; populate treats missing structure as a + * bug, destroy skips it. + * + * populate =3D=3D true: additionally install a freshly allocated, zeroed = page + * at every not-yet-present entry in the range. Such pages are marked + * _PAGE_AVAIL0, "owned by the per-domain area": teardown frees them (see + * perdomain_l1e_needs_freeing()), whereas caller-owned mappings are only + * ever unmapped. + */ int create_perdomain_mapping(struct domain *d, unsigned long va, - unsigned int nr, l1_pgentry_t **pl1tab, - struct page_info **ppg) + unsigned int nr, bool populate) { struct page_info *pg; l3_pgentry_t *l3tab; @@ -6258,9 +6282,6 @@ int create_perdomain_mapping(struct domain *d, unsign= ed long va, else l2tab =3D maddr_to_virt(l3e_get_paddr(l3tab[l3_table_offset(va)])); =20 - if ( !pl1tab && !ppg ) - return 0; - for ( l1tab =3D NULL; !rc && nr--; ) { l2_pgentry_t *pl2e =3D l2tab + l2_table_offset(va); @@ -6273,26 +6294,19 @@ int create_perdomain_mapping(struct domain *d, unsi= gned long va, rc =3D -ENOMEM; break; } - if ( pl1tab && !IS_NIL(pl1tab) ) - { - ASSERT(!pl1tab[l2_table_offset(va)]); - pl1tab[l2_table_offset(va)] =3D l1tab; - } clear_page(l1tab); *pl2e =3D l2e_from_mfn(virt_to_mfn(l1tab), __PAGE_HYPERVISOR_R= W); } else if ( !l1tab ) l1tab =3D maddr_to_virt(l2e_get_paddr(*pl2e)); =20 - if ( ppg && + if ( populate && !(l1e_get_flags(l1tab[l1_table_offset(va)]) & _PAGE_PRESENT) ) { pg =3D alloc_domheap_page(d, MEMF_no_owner); if ( pg ) { clear_domain_page(page_to_mfn(pg)); - if ( !IS_NIL(ppg) ) - *ppg++ =3D pg; l1tab[l1_table_offset(va)] =3D l1e_from_page(pg, __PAGE_HYPERVISOR_RW | _PAGE_AVAIL0); l2e_add_flags(*pl2e, _PAGE_AVAIL0); diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c index 35d1761c9c..15a8238aff 100644 --- a/xen/arch/x86/pv/domain.c +++ b/xen/arch/x86/pv/domain.c @@ -314,9 +314,7 @@ int switch_compat(struct domain *d) static int pv_create_gdt_ldt_l1tab(struct vcpu *v) { return create_perdomain_mapping(v->domain, GDT_VIRT_START(v), - 1U << GDT_LDT_VCPU_SHIFT, - NIL(l1_pgentry_t *), - NULL); + 1U << GDT_LDT_VCPU_SHIFT, false); } =20 static void pv_destroy_gdt_ldt_l1tab(struct vcpu *v) diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c index 8eadab7933..ffeda06e08 100644 --- a/xen/arch/x86/x86_64/mm.c +++ b/xen/arch/x86/x86_64/mm.c @@ -733,8 +733,7 @@ void __init zap_low_mappings(void) int setup_compat_arg_xlat(struct vcpu *v) { return create_perdomain_mapping(v->domain, ARG_XLAT_START(v), - PFN_UP(COMPAT_ARG_XLAT_SIZE), - NULL, NIL(struct page_info *)); + PFN_UP(COMPAT_ARG_XLAT_SIZE), true); } =20 void free_compat_arg_xlat(struct vcpu *v) --=20 2.55.0