All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
@ 2016-07-28 14:51 Julien Grall
  2016-07-28 14:51 ` [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch Julien Grall
                   ` (24 more replies)
  0 siblings, 25 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel
  Cc: Edgar E. Iglesias, sstabellini, Razvan Cojocaru, steve.capper,
	proskurin, Dirk Behme, Julien Grall, Tamas K Lengyel,
	Shanker Donthineni, wei.chen

Hello all,

The ARM architecture mandates the use of a break-before-make sequence when
changing translation entries if the page table is shared between multiple
CPUs whenever a valid entry is replaced by another valid entry (see D4.7.1
in ARM DDI 0487A.j for more details).

The current P2M code does not respect this sequence and may result to
break coherency on some processors.

Adapting the current implementation to use break-before-make sequence would
imply some code duplication and more TLBs invalidations than necessary.
For instance, if we are replacing a 4KB page and the current mapping in
the P2M is using a 1GB superpage, the following steps will happen:
    1) Shatter the 1GB superpage into a series of 2MB superpages
    2) Shatter the 2MB superpage into a series of 4KB superpages
    3) Replace the 4KB page

As the current implementation is shattering while descending and install
the mapping before continuing to the next level, Xen would need to issue 3
TLB invalidation instructions which is clearly inefficient.

Furthermore, all the operations which modify the page table are using the
same skeleton. It is more complicated to maintain different code paths than
having a generic function that set an entry and take care of the break-before-
make sequence.

The new implementation is based on the x86 EPT one which, I think, fits
quite well for the break-before-make sequence whilst keeping the code
simple.

I sent this patch series as an RFC because there are still some TODOs
in the code (mostly sanity check and possible optimization) and I have
done limited testing. However, I think it is a good shape to start reviewing,
get more feedback and have wider testing on different board.

Also, I need to figure out the impact on ARM32 because the domheap is not
always mapped.

This series has dependencies on some rework sent separately ([1] and [2]).
I have provided a branch with all the dependencies and this series applied:

git://xenbits.xen.org/people/julieng/xen-unstable.git branch p2m-rfc

Comments are welcome.

Yours sincerely,

Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
Cc: Tamas K Lengyel <tamas@tklengyel.com>
Cc: Shanker Donthineni <shankerd@codeaurora.org>
Cc: Dirk Behme <dirk.behme@de.bosch.com>
Cc: Edgar E. Iglesias <edgar.iglesias@xilinx.com>

[1] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02936.html
[2] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02830.html

Julien Grall (22):
  xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of
    the switch
  xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
  xen/arm: p2m: Rename parameter in p2m_{remove,write}_pte...
  xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set
  xen/arm: traps: Move MMIO emulation code in a separate helper
  xen/arm: traps: Check the P2M before injecting a data/instruction
    abort
  xen/arm: p2m: Rework p2m_put_l3_page
  xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m
  xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned
    int
  xen/arm: p2m: Move the lookup helpers at the top of the file
  xen/arm: p2m: Introduce p2m_get_root_pointer and use it in
    __p2m_lookup
  xen/arm: p2m: Introduce p2m_get_entry and use it to implement
    __p2m_lookup
  xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
  xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
  xen/arm: p2m: Make p2m_{valid,table,mapping} helpers inline
  xen/arm: p2m: Introduce a helper to check if an entry is a superpage
  xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry
  xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
  xen/arm: p2m: Re-implement p2m_set_mem_access using
    p2m_{set,get}_entry
  xen/arm: p2m: Do not handle shattering in p2m_create_table

 xen/arch/arm/domain.c      |    8 +-
 xen/arch/arm/p2m.c         | 1274 ++++++++++++++++++++++----------------------
 xen/arch/arm/traps.c       |  126 +++--
 xen/include/asm-arm/p2m.h  |   14 +
 xen/include/asm-arm/page.h |    4 +
 5 files changed, 742 insertions(+), 684 deletions(-)

-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-16  0:21   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry Julien Grall
                   ` (23 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

A follow-up patch will add more case to the switch that will require the
IPA. So move the computation out of the switch.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/traps.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 683bcb2..46e0663 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -2403,35 +2403,35 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
     int rc;
     register_t gva = READ_SYSREG(FAR_EL2);
     uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
+    paddr_t gpa;
+
+    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
+        gpa = get_faulting_ipa(gva);
+    else
+    {
+        /*
+         * Flush the TLB to make sure the DTLB is clear before
+         * doing GVA->IPA translation. If we got here because of
+         * an entry only present in the ITLB, this translation may
+         * still be inaccurate.
+         */
+        flush_tlb_local();
+
+        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
+        if ( rc == -EFAULT )
+            return; /* Try again */
+    }
 
     switch ( fsc )
     {
     case FSC_FLT_PERM:
     {
-        paddr_t gpa;
         const struct npfec npfec = {
             .insn_fetch = 1,
             .gla_valid = 1,
             .kind = hsr.iabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
         };
 
-        if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
-            gpa = get_faulting_ipa(gva);
-        else
-        {
-            /*
-             * Flush the TLB to make sure the DTLB is clear before
-             * doing GVA->IPA translation. If we got here because of
-             * an entry only present in the ITLB, this translation may
-             * still be inaccurate.
-             */
-            flush_tlb_local();
-
-            rc = gva_to_ipa(gva, &gpa, GV2M_READ);
-            if ( rc == -EFAULT )
-                return; /* Try again */
-        }
-
         rc = p2m_mem_access_check(gpa, gva, npfec);
 
         /* Trap was triggered by mem_access, work here is done */
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
  2016-07-28 14:51 ` [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-16  0:35   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 03/22] xen/arm: p2m: Rename parameter in p2m_{remove, write}_pte Julien Grall
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Each entry in the page table has to table clean when the IOMMU does not
support coherent walk. Rather than querying every time the page table is
updated, it is possible to do it only once when the p2m is initialized.

This is because this value can never change, Xen would be in big trouble
otherwise.

With this change, the initialize of the IOMMU for a given domain has to
be done earlier in order to know whether the page table entries need to
be clean. It is fine to move the call earlier because it has no
dependency.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/domain.c     |  8 +++++---
 xen/arch/arm/p2m.c        | 47 ++++++++++++++++++++++-------------------------
 xen/include/asm-arm/p2m.h |  3 +++
 3 files changed, 30 insertions(+), 28 deletions(-)

diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
index 20bb2ba..48f04c8 100644
--- a/xen/arch/arm/domain.c
+++ b/xen/arch/arm/domain.c
@@ -555,6 +555,11 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags,
         return 0;
 
     ASSERT(config != NULL);
+
+    /* p2m_init relies on some value initialized by the IOMMU subsystem */
+    if ( (rc = iommu_domain_init(d)) != 0 )
+        goto fail;
+
     if ( (rc = p2m_init(d)) != 0 )
         goto fail;
 
@@ -637,9 +642,6 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags,
     if ( is_hardware_domain(d) && (rc = domain_vuart_init(d)) )
         goto fail;
 
-    if ( (rc = iommu_domain_init(d)) != 0 )
-        goto fail;
-
     return 0;
 
 fail:
diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 40a0b80..d389f2b 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -416,7 +416,7 @@ static inline void p2m_remove_pte(lpae_t *p, bool_t flush_cache)
  * level_shift is the number of bits at the level we want to create.
  */
 static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
-                            int level_shift, bool_t flush_cache)
+                            int level_shift)
 {
     struct page_info *page;
     lpae_t *p;
@@ -462,7 +462,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
     else
         clear_page(p);
 
-    if ( flush_cache )
+    if ( p2m->clean_pte )
         clean_dcache_va_range(p, PAGE_SIZE);
 
     unmap_domain_page(p);
@@ -470,7 +470,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
     pte = mfn_to_p2m_entry(_mfn(page_to_mfn(page)), p2m_invalid,
                            p2m->default_access);
 
-    p2m_write_pte(entry, pte, flush_cache);
+    p2m_write_pte(entry, pte, p2m->clean_pte);
 
     return 0;
 }
@@ -653,12 +653,10 @@ static const paddr_t level_shifts[] =
 
 static int p2m_shatter_page(struct p2m_domain *p2m,
                             lpae_t *entry,
-                            unsigned int level,
-                            bool_t flush_cache)
+                            unsigned int level)
 {
     const paddr_t level_shift = level_shifts[level];
-    int rc = p2m_create_table(p2m, entry,
-                              level_shift - PAGE_SHIFT, flush_cache);
+    int rc = p2m_create_table(p2m, entry, level_shift - PAGE_SHIFT);
 
     if ( !rc )
     {
@@ -680,7 +678,6 @@ static int p2m_shatter_page(struct p2m_domain *p2m,
 static int apply_one_level(struct domain *d,
                            lpae_t *entry,
                            unsigned int level,
-                           bool_t flush_cache,
                            enum p2m_operation op,
                            paddr_t start_gpaddr,
                            paddr_t end_gpaddr,
@@ -719,7 +716,7 @@ static int apply_one_level(struct domain *d,
             if ( level < 3 )
                 pte.p2m.table = 0; /* Superpage entry */
 
-            p2m_write_pte(entry, pte, flush_cache);
+            p2m_write_pte(entry, pte, p2m->clean_pte);
 
             *flush |= p2m_valid(orig_pte);
 
@@ -754,7 +751,7 @@ static int apply_one_level(struct domain *d,
             /* Not present -> create table entry and descend */
             if ( !p2m_valid(orig_pte) )
             {
-                rc = p2m_create_table(p2m, entry, 0, flush_cache);
+                rc = p2m_create_table(p2m, entry, 0);
                 if ( rc < 0 )
                     return rc;
                 return P2M_ONE_DESCEND;
@@ -764,7 +761,7 @@ static int apply_one_level(struct domain *d,
             if ( p2m_mapping(orig_pte) )
             {
                 *flush = true;
-                rc = p2m_shatter_page(p2m, entry, level, flush_cache);
+                rc = p2m_shatter_page(p2m, entry, level);
                 if ( rc < 0 )
                     return rc;
             } /* else: an existing table mapping -> descend */
@@ -801,7 +798,7 @@ static int apply_one_level(struct domain *d,
                  * and descend.
                  */
                 *flush = true;
-                rc = p2m_shatter_page(p2m, entry, level, flush_cache);
+                rc = p2m_shatter_page(p2m, entry, level);
                 if ( rc < 0 )
                     return rc;
 
@@ -827,7 +824,7 @@ static int apply_one_level(struct domain *d,
 
         *flush = true;
 
-        p2m_remove_pte(entry, flush_cache);
+        p2m_remove_pte(entry, p2m->clean_pte);
         p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), p2m_access_rwx);
 
         *addr += level_size;
@@ -886,7 +883,7 @@ static int apply_one_level(struct domain *d,
             /* Shatter large pages as we descend */
             if ( p2m_mapping(orig_pte) )
             {
-                rc = p2m_shatter_page(p2m, entry, level, flush_cache);
+                rc = p2m_shatter_page(p2m, entry, level);
                 if ( rc < 0 )
                     return rc;
             } /* else: an existing table mapping -> descend */
@@ -904,7 +901,7 @@ static int apply_one_level(struct domain *d,
                     return rc;
 
                 p2m_set_permission(&pte, pte.p2m.type, a);
-                p2m_write_pte(entry, pte, flush_cache);
+                p2m_write_pte(entry, pte, p2m->clean_pte);
             }
 
             *addr += level_size;
@@ -954,17 +951,9 @@ static int apply_p2m_changes(struct domain *d,
     const unsigned int preempt_count_limit = (op == MEMACCESS) ? 1 : 0x2000;
     const bool_t preempt = !is_idle_vcpu(current);
     bool_t flush = false;
-    bool_t flush_pt;
     PAGE_LIST_HEAD(free_pages);
     struct page_info *pg;
 
-    /*
-     * Some IOMMU don't support coherent PT walk. When the p2m is
-     * shared with the CPU, Xen has to make sure that the PT changes have
-     * reached the memory
-     */
-    flush_pt = iommu_enabled && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK);
-
     p2m_write_lock(p2m);
 
     /* Static mapping. P2M_ROOT_PAGES > 1 are handled below */
@@ -1070,7 +1059,7 @@ static int apply_p2m_changes(struct domain *d,
             lpae_t old_entry = *entry;
 
             ret = apply_one_level(d, entry,
-                                  level, flush_pt, op,
+                                  level, op,
                                   start_gpaddr, end_gpaddr,
                                   &addr, &maddr, &flush,
                                   t, a);
@@ -1127,7 +1116,7 @@ static int apply_p2m_changes(struct domain *d,
 
                 page_list_del(pg, &p2m->pages);
 
-                p2m_remove_pte(entry, flush_pt);
+                p2m_remove_pte(entry, p2m->clean_pte);
 
                 p2m->stats.mappings[level - 1]--;
                 update_reference_mapping(pages[level - 1], old_entry, *entry);
@@ -1399,6 +1388,14 @@ int p2m_init(struct domain *d)
     p2m->mem_access_enabled = false;
     radix_tree_init(&p2m->mem_access_settings);
 
+    /*
+     * Some IOMMUs don't support coherent PT walk. When the p2m is
+     * shared with the CPU, Xen has to make sure that the PT changes have
+     * reached the memory
+     */
+    p2m->clean_pte = iommu_enabled &&
+        !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK);
+
     rc = p2m_alloc_table(d);
 
     return rc;
diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
index 53c4d78..03bfd5e 100644
--- a/xen/include/asm-arm/p2m.h
+++ b/xen/include/asm-arm/p2m.h
@@ -48,6 +48,9 @@ struct p2m_domain {
      * decrease. */
     gfn_t lowest_mapped_gfn;
 
+    /* Indicate if it is required to clean the cache when writing an entry */
+    bool_t clean_pte;
+
     /* Gather some statistics for information purposes only */
     struct {
         /* Number of mappings at each p2m tree level */
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 03/22] xen/arm: p2m: Rename parameter in p2m_{remove, write}_pte...
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
  2016-07-28 14:51 ` [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch Julien Grall
  2016-07-28 14:51 ` [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-16  0:36   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 04/22] xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set Julien Grall
                   ` (21 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

to make clear of the usage. I.e it is used to inform whether Xen needs
to clean the entry after writing in the page table.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index d389f2b..ff82f12 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -390,19 +390,19 @@ static lpae_t mfn_to_p2m_entry(mfn_t mfn, p2m_type_t t, p2m_access_t a)
     return e;
 }
 
-static inline void p2m_write_pte(lpae_t *p, lpae_t pte, bool_t flush_cache)
+static inline void p2m_write_pte(lpae_t *p, lpae_t pte, bool clean_pte)
 {
     write_pte(p, pte);
-    if ( flush_cache )
+    if ( clean_pte )
         clean_dcache(*p);
 }
 
-static inline void p2m_remove_pte(lpae_t *p, bool_t flush_cache)
+static inline void p2m_remove_pte(lpae_t *p, bool clean_pte)
 {
     lpae_t pte;
 
     memset(&pte, 0x00, sizeof(pte));
-    p2m_write_pte(p, pte, flush_cache);
+    p2m_write_pte(p, pte, clean_pte);
 }
 
 /*
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 04/22] xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (2 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 03/22] xen/arm: p2m: Rename parameter in p2m_{remove, write}_pte Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-16  0:39   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper Julien Grall
                   ` (20 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

p2m_mem_access_radix_set is expecting a gfn in a parameter. Rename the
parameter 'pfn' to 'gfn' to match its content and use the typesafe gfn
to avoid possible misusage.

Also rename the parameter to gfn to match its content.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index ff82f12..aecdd1e 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -542,7 +542,7 @@ static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
     return 0;
 }
 
-static int p2m_mem_access_radix_set(struct p2m_domain *p2m, unsigned long pfn,
+static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
                                     p2m_access_t a)
 {
     int rc;
@@ -552,18 +552,18 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, unsigned long pfn,
 
     if ( p2m_access_rwx == a )
     {
-        radix_tree_delete(&p2m->mem_access_settings, pfn);
+        radix_tree_delete(&p2m->mem_access_settings, gfn_x(gfn));
         return 0;
     }
 
-    rc = radix_tree_insert(&p2m->mem_access_settings, pfn,
+    rc = radix_tree_insert(&p2m->mem_access_settings, gfn_x(gfn),
                            radix_tree_int_to_ptr(a));
     if ( rc == -EEXIST )
     {
         /* If a setting already exists, change it to the new one */
         radix_tree_replace_slot(
             radix_tree_lookup_slot(
-                &p2m->mem_access_settings, pfn),
+                &p2m->mem_access_settings, gfn_x(gfn)),
             radix_tree_int_to_ptr(a));
         rc = 0;
     }
@@ -707,7 +707,7 @@ static int apply_one_level(struct domain *d,
             */
              (level == 3 || (!p2m_table(orig_pte) && !p2m->mem_access_enabled)) )
         {
-            rc = p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), a);
+            rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)), a);
             if ( rc < 0 )
                 return rc;
 
@@ -825,7 +825,8 @@ static int apply_one_level(struct domain *d,
         *flush = true;
 
         p2m_remove_pte(entry, p2m->clean_pte);
-        p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), p2m_access_rwx);
+        p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
+                                 p2m_access_rwx);
 
         *addr += level_size;
         *maddr += level_size;
@@ -896,7 +897,8 @@ static int apply_one_level(struct domain *d,
 
             if ( p2m_valid(pte) )
             {
-                rc = p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), a);
+                rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
+                                              a);
                 if ( rc < 0 )
                     return rc;
 
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (3 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 04/22] xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-16  0:49   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort Julien Grall
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Currently, a stage-2 fault translation will likely access an emulated
region. All the checks are pre-sanitity check for MMIO emulation.

A follow-up patch will handle a new case that could lead to a stage-2
translation. To improve the clarity of the code and the changes, the
current implementation is move in a separate helper.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/traps.c | 58 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 33 insertions(+), 25 deletions(-)

diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index 46e0663..b46284c 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -2444,6 +2444,38 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
     inject_iabt_exception(regs, gva, hsr.len);
 }
 
+static bool_t try_handle_mmio(struct cpu_user_regs *regs,
+                              mmio_info_t *info)
+{
+    const struct hsr_dabt dabt = info->dabt;
+    int rc;
+
+    /* stage-1 page table should never live in an emulated MMIO region */
+    if ( dabt.s1ptw )
+        return 0;
+
+    /* All the instructions used on emulated MMIO region should be valid */
+    if ( !dabt.valid )
+        return 0;
+
+    /*
+     * Erratum 766422: Thumb store translation fault to Hypervisor may
+     * not have correct HSR Rt value.
+     */
+    if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
+         dabt.write )
+    {
+        rc = decode_instruction(regs, &info->dabt);
+        if ( rc )
+        {
+            gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
+            return 0;
+        }
+    }
+
+    return !!handle_mmio(info);
+}
+
 static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
                                      const union hsr hsr)
 {
@@ -2487,40 +2519,16 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
         break;
     }
     case FSC_FLT_TRANS:
-        if ( dabt.s1ptw )
-            goto bad_data_abort;
-
-        /* XXX: Decode the instruction if ISS is not valid */
-        if ( !dabt.valid )
-            goto bad_data_abort;
-
-        /*
-         * Erratum 766422: Thumb store translation fault to Hypervisor may
-         * not have correct HSR Rt value.
-         */
-        if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
-             dabt.write )
-        {
-            rc = decode_instruction(regs, &info.dabt);
-            if ( rc )
-            {
-                gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
-                goto bad_data_abort;
-            }
-        }
-
-        if ( handle_mmio(&info) )
+        if ( try_handle_mmio(regs, &info) )
         {
             advance_pc(regs, hsr);
             return;
         }
-        break;
     default:
         gprintk(XENLOG_WARNING, "Unsupported DFSC: HSR=%#x DFSC=%#x\n",
                 hsr.bits, dabt.dfsc);
     }
 
-bad_data_abort:
     gdprintk(XENLOG_DEBUG, "HSR=0x%x pc=%#"PRIregister" gva=%#"PRIvaddr
              " gpa=%#"PRIpaddr"\n", hsr.bits, regs->pc, info.gva, info.gpa);
     inject_dabt_exception(regs, info.gva, hsr.len);
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (4 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-23  1:05   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 07/22] xen/arm: p2m: Rework p2m_put_l3_page Julien Grall
                   ` (18 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

A data/instruction abort may have occurred if another CPU was playing
with the stage-2 page table when following the break-before-make
sequence (see D4.7.1 in ARM DDI 0487A.j). Rather than injecting directly
the fault to the guest, we need to check whether the mapping exists. If
it exists, return to the guest to replay the instruction.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/traps.c | 40 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
index b46284c..da56cc0 100644
--- a/xen/arch/arm/traps.c
+++ b/xen/arch/arm/traps.c
@@ -2404,6 +2404,7 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
     register_t gva = READ_SYSREG(FAR_EL2);
     uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
     paddr_t gpa;
+    mfn_t mfn;
 
     if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
         gpa = get_faulting_ipa(gva);
@@ -2417,6 +2418,11 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
          */
         flush_tlb_local();
 
+        /*
+         * We may not be able to translate because someone is
+         * playing with the Stage-2 page table of the domain.
+         * Return to the guest.
+         */
         rc = gva_to_ipa(gva, &gpa, GV2M_READ);
         if ( rc == -EFAULT )
             return; /* Try again */
@@ -2437,8 +2443,17 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
         /* Trap was triggered by mem_access, work here is done */
         if ( !rc )
             return;
+        break;
     }
-    break;
+    case FSC_FLT_TRANS:
+        /*
+         * The PT walk may have failed because someone was playing
+         * with the Stage-2 page table. Walk the Stage-2 PT to check
+         * if the entry exists. If it's the case, return to the guest
+         */
+        mfn = p2m_lookup(current->domain, _gfn(paddr_to_pfn(gpa)), NULL);
+        if ( !mfn_eq(mfn, INVALID_MFN) )
+            return;
     }
 
     inject_iabt_exception(regs, gva, hsr.len);
@@ -2455,7 +2470,7 @@ static bool_t try_handle_mmio(struct cpu_user_regs *regs,
         return 0;
 
     /* All the instructions used on emulated MMIO region should be valid */
-    if ( !dabt.valid )
+    if ( !info->dabt.valid )
         return 0;
 
     /*
@@ -2483,6 +2498,7 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
     int rc;
     mmio_info_t info;
     uint8_t fsc = hsr.dabt.dfsc & ~FSC_LL_MASK;
+    mfn_t mfn;
 
     info.dabt = dabt;
 #ifdef CONFIG_ARM_32
@@ -2496,6 +2512,11 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
     else
     {
         rc = gva_to_ipa(info.gva, &info.gpa, GV2M_READ);
+        /*
+         * We may not be able to translate because someone is
+         * playing with the Stage-2 page table of the domain.
+         * Return to the guest.
+         */
         if ( rc == -EFAULT )
             return; /* Try again */
     }
@@ -2519,11 +2540,26 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
         break;
     }
     case FSC_FLT_TRANS:
+        /*
+         * Attempt first to emulate the MMIO has the data abort will
+         * likely happen an emulated region.
+         */
         if ( try_handle_mmio(regs, &info) )
         {
             advance_pc(regs, hsr);
             return;
         }
+
+        /*
+         * The PT walk may have failed because someone was playing
+         * with the Stage-2 page table. Walk the Stage-2 PT to check
+         * if the entry exists. If it's the case, return to the guest
+         */
+        mfn = p2m_lookup(current->domain, _gfn(paddr_to_pfn(info.gpa)), NULL);
+        if ( !mfn_eq(mfn, INVALID_MFN) )
+            return;
+
+        break;
     default:
         gprintk(XENLOG_WARNING, "Unsupported DFSC: HSR=%#x DFSC=%#x\n",
                 hsr.bits, dabt.dfsc);
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 07/22] xen/arm: p2m: Rework p2m_put_l3_page
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (5 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-23  1:10   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 08/22] xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m Julien Grall
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Modify the prototype to directly pass the mfn and the type in
parameters. This will be useful later when we do not have the entry in
hand.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index aecdd1e..6b29cf0 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -584,10 +584,8 @@ enum p2m_operation {
  * TODO: Handle superpages, for now we only take special references for leaf
  * pages (specifically foreign ones, which can't be super mapped today).
  */
-static void p2m_put_l3_page(const lpae_t pte)
+static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
 {
-    ASSERT(p2m_valid(pte));
-
     /*
      * TODO: Handle other p2m types
      *
@@ -595,12 +593,10 @@ static void p2m_put_l3_page(const lpae_t pte)
      * flush the TLBs if the page is reallocated before the end of
      * this loop.
      */
-    if ( p2m_is_foreign(pte.p2m.type) )
+    if ( p2m_is_foreign(type) )
     {
-        unsigned long mfn = pte.p2m.base;
-
-        ASSERT(mfn_valid(mfn));
-        put_page(mfn_to_page(mfn));
+        ASSERT(mfn_valid(mfn_x(mfn)));
+        put_page(mfn_to_page(mfn_x(mfn)));
     }
 }
 
@@ -734,7 +730,8 @@ static int apply_one_level(struct domain *d,
                  */
                 BUG_ON(level < 3 && p2m_table(orig_pte));
                 if ( level == 3 )
-                    p2m_put_l3_page(orig_pte);
+                    p2m_put_l3_page(_mfn(orig_pte.p2m.base),
+                                    orig_pte.p2m.type);
             }
             else /* New mapping */
                 p2m->stats.mappings[level]++;
@@ -834,7 +831,7 @@ static int apply_one_level(struct domain *d,
         p2m->stats.mappings[level]--;
 
         if ( level == 3 )
-            p2m_put_l3_page(orig_pte);
+            p2m_put_l3_page(_mfn(orig_pte.p2m.base), orig_pte.p2m.type);
 
         /*
          * This is still a single pte write, no matter the level, so no need to
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 08/22] xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (6 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 07/22] xen/arm: p2m: Rework p2m_put_l3_page Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-23  1:18   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int Julien Grall
                   ` (16 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Sometimes the invalidation of the TLBs can be deferred until the p2m is
unlocked. This is for instance the case when multiple mappings are
removed. In other case, such as shattering a superpage, an immediate
flush is required.

Keep track whether a flush is needed directly in the p2m_domain structure
to allow serializing multiple changes. The TLBs will be invalidated when
write unlocking the p2m if necessary.

Also a new helper, p2m_flush_sync, has been introduced to force a
synchronous TLB invalidation.

Finally, replace the call to p2m_flush_tlb by p2m_flush_tlb_sync in
apply_p2m_changes.

Note this patch is not useful today, however follow-up patches will make
advantage of it.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c        | 33 ++++++++++++++++++++++++++++++++-
 xen/include/asm-arm/p2m.h | 11 +++++++++++
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 6b29cf0..a6dce0c 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -52,8 +52,21 @@ static inline void p2m_write_lock(struct p2m_domain *p2m)
     write_lock(&p2m->lock);
 }
 
+static void p2m_flush_tlb(struct p2m_domain *p2m);
+
 static inline void p2m_write_unlock(struct p2m_domain *p2m)
 {
+    if ( p2m->need_flush )
+    {
+        p2m->need_flush = false;
+        /*
+         * The final flush is done with the P2M write lock taken to
+         * to avoid someone else modify the P2M before the TLB
+         * invalidation has completed.
+         */
+        p2m_flush_tlb(p2m);
+    }
+
     write_unlock(&p2m->lock);
 }
 
@@ -72,6 +85,11 @@ static inline int p2m_is_locked(struct p2m_domain *p2m)
     return rw_is_locked(&p2m->lock);
 }
 
+static inline int p2m_is_write_locked(struct p2m_domain *p2m)
+{
+    return rw_is_write_locked(&p2m->lock);
+}
+
 void p2m_dump_info(struct domain *d)
 {
     struct p2m_domain *p2m = &d->arch.p2m;
@@ -165,6 +183,19 @@ static void p2m_flush_tlb(struct p2m_domain *p2m)
 }
 
 /*
+ * Force a synchronous P2M TLB flush.
+ *
+ * Must be called with the p2m lock held.
+ */
+static void p2m_flush_tlb_sync(struct p2m_domain *p2m)
+{
+    ASSERT(p2m_is_write_locked(p2m));
+
+    p2m_flush_tlb(p2m);
+    p2m->need_flush = false;
+}
+
+/*
  * Lookup the MFN corresponding to a domain's GFN.
  *
  * There are no processor functions to do a stage 2 only lookup therefore we
@@ -1142,7 +1173,7 @@ static int apply_p2m_changes(struct domain *d,
 out:
     if ( flush )
     {
-        p2m_flush_tlb(&d->arch.p2m);
+        p2m_flush_tlb_sync(&d->arch.p2m);
         ret = iommu_iotlb_flush(d, gfn_x(sgfn), nr);
         if ( !rc )
             rc = ret;
diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
index 03bfd5e..e6be3ea 100644
--- a/xen/include/asm-arm/p2m.h
+++ b/xen/include/asm-arm/p2m.h
@@ -51,6 +51,17 @@ struct p2m_domain {
     /* Indicate if it is required to clean the cache when writing an entry */
     bool_t clean_pte;
 
+    /*
+     * P2M updates may required TLBs to be flushed (invalidated).
+     *
+     * Flushes may be deferred by setting 'need_flush' and then flushing
+     * when the p2m write lock is released.
+     *
+     * If an immediate flush is required (e.g, if a super page is
+     * shattered), call p2m_tlb_flush_sync().
+     */
+    bool need_flush;
+
     /* Gather some statistics for information purposes only */
     struct {
         /* Number of mappings at each p2m tree level */
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (7 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 08/22] xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-23  1:20   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 10/22] xen/arm: p2m: Move the lookup helpers at the top of the file Julien Grall
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The level shift can be encoded with 32-bit. So it is not necessary to
use paddr_t (i.e 64-bit).

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index a6dce0c..798faa8 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -675,7 +675,7 @@ static const paddr_t level_sizes[] =
     { ZEROETH_SIZE, FIRST_SIZE, SECOND_SIZE, THIRD_SIZE };
 static const paddr_t level_masks[] =
     { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
-static const paddr_t level_shifts[] =
+static const unsigned int level_shifts[] =
     { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
 
 static int p2m_shatter_page(struct p2m_domain *p2m,
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 10/22] xen/arm: p2m: Move the lookup helpers at the top of the file
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (8 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-07-28 14:51 ` [RFC 11/22] xen/arm: p2m: Introduce p2m_get_root_pointer and use it in __p2m_lookup Julien Grall
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

This will be used later in functions that will be defined earlier in the
file.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 798faa8..ea582c8 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -29,6 +29,14 @@ static unsigned int __read_mostly p2m_root_level;
 
 unsigned int __read_mostly p2m_ipa_bits;
 
+/* Helpers to lookup the properties of each level */
+static const paddr_t level_sizes[] =
+    { ZEROETH_SIZE, FIRST_SIZE, SECOND_SIZE, THIRD_SIZE };
+static const paddr_t level_masks[] =
+    { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
+static const unsigned int level_shifts[] =
+    { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
+
 static bool_t p2m_valid(lpae_t pte)
 {
     return pte.p2m.valid;
@@ -670,14 +678,6 @@ static bool_t is_mapping_aligned(const paddr_t start_gpaddr,
 #define P2M_ONE_PROGRESS_NOP   0x1
 #define P2M_ONE_PROGRESS       0x10
 
-/* Helpers to lookup the properties of each level */
-static const paddr_t level_sizes[] =
-    { ZEROETH_SIZE, FIRST_SIZE, SECOND_SIZE, THIRD_SIZE };
-static const paddr_t level_masks[] =
-    { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
-static const unsigned int level_shifts[] =
-    { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
-
 static int p2m_shatter_page(struct p2m_domain *p2m,
                             lpae_t *entry,
                             unsigned int level)
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 11/22] xen/arm: p2m: Introduce p2m_get_root_pointer and use it in __p2m_lookup
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (9 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 10/22] xen/arm: p2m: Move the lookup helpers at the top of the file Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-08-23  1:34   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup Julien Grall
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Mapping the root table is always done the same way. To avoid duplicating
the code in a later patch, move the code in a separate helper.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 53 +++++++++++++++++++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 18 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index ea582c8..d4a4b62 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -204,6 +204,37 @@ static void p2m_flush_tlb_sync(struct p2m_domain *p2m)
 }
 
 /*
+ * Find and map the root page table. The caller is responsible for
+ * unmapping the table.
+ *
+ * The function will return NULL if the offset of the root table is
+ * invalid.
+ */
+static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m,
+                                    gfn_t gfn)
+{
+    unsigned int root_table;
+
+    if ( P2M_ROOT_PAGES == 1 )
+        return __map_domain_page(p2m->root);
+
+    /*
+     * Concatenated root-level tables. The table number will be the
+     * offset at the previous level. It is not possible to
+     * concatenate a level-0 root.
+     */
+    ASSERT(P2M_ROOT_LEVEL > 0);
+
+    root_table = gfn_x(gfn) >>  (level_shifts[P2M_ROOT_LEVEL - 1] - PAGE_SHIFT);
+    root_table &= LPAE_ENTRY_MASK;
+
+    if ( root_table >= P2M_ROOT_PAGES )
+        return NULL;
+
+    return __map_domain_page(p2m->root + root_table);
+}
+
+/*
  * Lookup the MFN corresponding to a domain's GFN.
  *
  * There are no processor functions to do a stage 2 only lookup therefore we
@@ -226,7 +257,7 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
     mfn_t mfn = INVALID_MFN;
     paddr_t mask = 0;
     p2m_type_t _t;
-    unsigned int level, root_table;
+    unsigned int level;
 
     ASSERT(p2m_is_locked(p2m));
     BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
@@ -236,22 +267,9 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
 
     *t = p2m_invalid;
 
-    if ( P2M_ROOT_PAGES > 1 )
-    {
-        /*
-         * Concatenated root-level tables. The table number will be
-         * the offset at the previous level. It is not possible to
-         * concatenate a level-0 root.
-         */
-        ASSERT(P2M_ROOT_LEVEL > 0);
-        root_table = offsets[P2M_ROOT_LEVEL - 1];
-        if ( root_table >= P2M_ROOT_PAGES )
-            goto err;
-    }
-    else
-        root_table = 0;
-
-    map = __map_domain_page(p2m->root + root_table);
+    map = p2m_get_root_pointer(p2m, gfn);
+    if ( !map )
+        return INVALID_MFN;
 
     ASSERT(P2M_ROOT_LEVEL < 4);
 
@@ -286,7 +304,6 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
         *t = pte.p2m.type;
     }
 
-err:
     return mfn;
 }
 
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (10 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 11/22] xen/arm: p2m: Introduce p2m_get_root_pointer and use it in __p2m_lookup Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-07-30 18:37   ` Tamas K Lengyel
  2016-08-31  0:30   ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo Stefano Stabellini
  2016-07-28 14:51 ` [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry Julien Grall
                   ` (12 subsequent siblings)
  24 siblings, 2 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Currently, for a given GFN, the function __p2m_lookup will only return
the associated MFN and the p2m type of the mapping.

In some case we need the order of the mapping and the memaccess
permission. Rather than providing separate function for this purpose,
it is better to implement a generic function to return all the
information.

To avoid passing dummy parameter, a caller that does need a specific
information can use NULL instead.

The list of the informations retrieved is based on the x86 version. All
of them will be used in follow-up patches.

It might have been possible to extend __p2m_lookup, however I choose to
reimplement it from scratch to allow sharing some helpers with the
function that will update the P2M (will be added in a follow-up patch).

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c         | 188 ++++++++++++++++++++++++++++++++++-----------
 xen/include/asm-arm/page.h |   4 +
 2 files changed, 149 insertions(+), 43 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index d4a4b62..8676b9d 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -36,6 +36,8 @@ static const paddr_t level_masks[] =
     { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
 static const unsigned int level_shifts[] =
     { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
+static const unsigned int level_orders[] =
+    { ZEROETH_ORDER, FIRST_ORDER, SECOND_ORDER, THIRD_ORDER };
 
 static bool_t p2m_valid(lpae_t pte)
 {
@@ -236,28 +238,99 @@ static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m,
 
 /*
  * Lookup the MFN corresponding to a domain's GFN.
+ * Lookup mem access in the ratrix tree.
+ * The entries associated to the GFN is considered valid.
+ */
+static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m, gfn_t gfn)
+{
+    void *ptr;
+
+    if ( !p2m->mem_access_enabled )
+        return p2m_access_rwx;
+
+    ptr = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
+    if ( !ptr )
+        return p2m_access_rwx;
+    else
+        return radix_tree_ptr_to_int(ptr);
+}
+
+#define GUEST_TABLE_MAP_FAILED 0
+#define GUEST_TABLE_SUPER_PAGE 1
+#define GUEST_TABLE_NORMAL_PAGE 2
+
+static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
+                            int level_shift);
+
+/*
+ * Take the currently mapped table, find the corresponding GFN entry,
+ * and map the next table, if available.
  *
- * There are no processor functions to do a stage 2 only lookup therefore we
- * do a a software walk.
+ * Return values:
+ *  GUEST_TABLE_MAP_FAILED: Either read_only was set and the entry
+ *  was empty, or allocating a new page failed.
+ *  GUEST_TABLE_NORMAL_PAGE: next level mapped normally
+ *  GUEST_TABLE_SUPER_PAGE: The next entry points to a superpage.
  */
-static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
+static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
+                          lpae_t **table, unsigned int offset)
 {
-    struct p2m_domain *p2m = &d->arch.p2m;
-    const paddr_t paddr = pfn_to_paddr(gfn_x(gfn));
-    const unsigned int offsets[4] = {
-        zeroeth_table_offset(paddr),
-        first_table_offset(paddr),
-        second_table_offset(paddr),
-        third_table_offset(paddr)
-    };
-    const paddr_t masks[4] = {
-        ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK
-    };
-    lpae_t pte, *map;
+    lpae_t *entry;
+    int ret;
+    mfn_t mfn;
+
+    entry = *table + offset;
+
+    if ( !p2m_valid(*entry) )
+    {
+        if ( read_only )
+            return GUEST_TABLE_MAP_FAILED;
+
+        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
+        if ( ret )
+            return GUEST_TABLE_MAP_FAILED;
+    }
+
+    /* The function p2m_next_level is never called at the 3rd level */
+    if ( p2m_mapping(*entry) )
+        return GUEST_TABLE_SUPER_PAGE;
+
+    mfn = _mfn(entry->p2m.base);
+
+    unmap_domain_page(*table);
+    *table = map_domain_page(mfn);
+
+    return GUEST_TABLE_NORMAL_PAGE;
+}
+
+/*
+ * Get the details of a given gfn.
+ *
+ * If the entry is present, the associated MFN will be returned and the
+ * access and type filled up. The page_order will correspond to the
+ * order of the mapping in the page table (i.e it could be a superpage).
+ *
+ * If the entry is not present, INVALID_MFN will be returned and the
+ * page_order will be set according to the order of the invalid range.
+ */
+static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
+                           p2m_type_t *t, p2m_access_t *a,
+                           unsigned int *page_order)
+{
+    paddr_t addr = pfn_to_paddr(gfn_x(gfn));
+    unsigned int level = 0;
+    lpae_t entry, *table;
+    int rc;
     mfn_t mfn = INVALID_MFN;
-    paddr_t mask = 0;
     p2m_type_t _t;
-    unsigned int level;
+
+    /* Convenience aliases */
+    const unsigned int offsets[4] = {
+        zeroeth_table_offset(addr),
+        first_table_offset(addr),
+        second_table_offset(addr),
+        third_table_offset(addr)
+    };
 
     ASSERT(p2m_is_locked(p2m));
     BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
@@ -267,46 +340,75 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
 
     *t = p2m_invalid;
 
-    map = p2m_get_root_pointer(p2m, gfn);
-    if ( !map )
-        return INVALID_MFN;
+    /* XXX: Check if the mapping is lower than the mapped gfn */
 
-    ASSERT(P2M_ROOT_LEVEL < 4);
-
-    for ( level = P2M_ROOT_LEVEL ; level < 4 ; level++ )
+    /* This gfn is higher than the highest the p2m map currently holds */
+    if ( gfn_x(gfn) > gfn_x(p2m->max_mapped_gfn) )
     {
-        mask = masks[level];
-
-        pte = map[offsets[level]];
+        for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
+        {
+            if ( (gfn_x(gfn) & (level_masks[level] >> PAGE_SHIFT)) >
+                 gfn_x(p2m->max_mapped_gfn) )
+                break;
+            goto out;
+        }
+    }
 
-        if ( level == 3 && !p2m_table(pte) )
-            /* Invalid, clobber the pte */
-            pte.bits = 0;
-        if ( level == 3 || !p2m_table(pte) )
-            /* Done */
-            break;
+    table = p2m_get_root_pointer(p2m, gfn);
 
-        ASSERT(level < 3);
+    /*
+     * the table should always be non-NULL because the gfn is below
+     * p2m->max_mapped_gfn and the root table pages are always present.
+     */
+    BUG_ON(table == NULL);
 
-        /* Map for next level */
-        unmap_domain_page(map);
-        map = map_domain_page(_mfn(pte.p2m.base));
+    for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
+    {
+        rc = p2m_next_level(p2m, true, &table, offsets[level]);
+        if ( rc == GUEST_TABLE_MAP_FAILED )
+            goto out_unmap;
+        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
+            break;
     }
 
-    unmap_domain_page(map);
+    entry = table[offsets[level]];
 
-    if ( p2m_valid(pte) )
+    if ( p2m_valid(entry) )
     {
-        ASSERT(mask);
-        ASSERT(pte.p2m.type != p2m_invalid);
-        mfn = _mfn(paddr_to_pfn((pte.bits & PADDR_MASK & mask) |
-                                (paddr & ~mask)));
-        *t = pte.p2m.type;
+        *t = entry.p2m.type;
+
+        if ( a )
+            *a = p2m_mem_access_radix_get(p2m, gfn);
+
+        mfn = _mfn(entry.p2m.base);
+        /*
+         * The entry may point to a superpage. Find the MFN associated
+         * to the GFN.
+         */
+        mfn = mfn_add(mfn, gfn_x(gfn) & ((1UL << level_orders[level]) - 1));
     }
 
+out_unmap:
+    unmap_domain_page(table);
+
+out:
+    if ( page_order )
+        *page_order = level_shifts[level] - PAGE_SHIFT;
+
     return mfn;
 }
 
+/*
+ * Lookup the MFN corresponding to a domain's GFN.
+ *
+ * There are no processor functions to do a stage 2 only lookup therefore we
+ * do a a software walk.
+ */
+static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
+{
+    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
+}
+
 mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
 {
     mfn_t ret;
diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h
index 05d9f82..1c5bd8b 100644
--- a/xen/include/asm-arm/page.h
+++ b/xen/include/asm-arm/page.h
@@ -457,15 +457,19 @@ static inline int gva_to_ipa(vaddr_t va, paddr_t *paddr, unsigned int flags)
 #define LPAE_ENTRY_MASK (LPAE_ENTRIES - 1)
 
 #define THIRD_SHIFT    (PAGE_SHIFT)
+#define THIRD_ORDER    0
 #define THIRD_SIZE     ((paddr_t)1 << THIRD_SHIFT)
 #define THIRD_MASK     (~(THIRD_SIZE - 1))
 #define SECOND_SHIFT   (THIRD_SHIFT + LPAE_SHIFT)
+#define SECOND_ORDER   (THIRD_ORDER + LPAE_SHIFT)
 #define SECOND_SIZE    ((paddr_t)1 << SECOND_SHIFT)
 #define SECOND_MASK    (~(SECOND_SIZE - 1))
 #define FIRST_SHIFT    (SECOND_SHIFT + LPAE_SHIFT)
+#define FIRST_ORDER    (SECOND_ORDER + LPAE_SHIFT)
 #define FIRST_SIZE     ((paddr_t)1 << FIRST_SHIFT)
 #define FIRST_MASK     (~(FIRST_SIZE - 1))
 #define ZEROETH_SHIFT  (FIRST_SHIFT + LPAE_SHIFT)
+#define ZEROETH_ORDER  (FIRST_ORDER + LPAE_SHIFT)
 #define ZEROETH_SIZE   ((paddr_t)1 << ZEROETH_SHIFT)
 #define ZEROETH_MASK   (~(ZEROETH_SIZE - 1))
 
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (11 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-07-28 17:29   ` Tamas K Lengyel
  2016-09-05 20:45   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry Julien Grall
                   ` (11 subsequent siblings)
  24 siblings, 2 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin,
	Julien Grall, Tamas K Lengyel, wei.chen

__p2m_lookup is just a wrapper to p2m_get_entry.

Signed-off-by: Julien Grall <julien.grall@arm.com>
Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
Cc: Tamas K Lengyel <tamas@tklengyel.com>

---
    It might be possible to rework the memaccess code to take advantage
    of all the parameters. I will defer this to the memaccess folks.
---
 xen/arch/arm/p2m.c | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 8676b9d..9a9c85c 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -398,24 +398,13 @@ out:
     return mfn;
 }
 
-/*
- * Lookup the MFN corresponding to a domain's GFN.
- *
- * There are no processor functions to do a stage 2 only lookup therefore we
- * do a a software walk.
- */
-static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
-{
-    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
-}
-
 mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
 {
     mfn_t ret;
     struct p2m_domain *p2m = &d->arch.p2m;
 
     p2m_read_lock(p2m);
-    ret = __p2m_lookup(d, gfn, t);
+    ret = p2m_get_entry(p2m, gfn, t, NULL, NULL);
     p2m_read_unlock(p2m);
 
     return ret;
@@ -679,7 +668,7 @@ static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
          * No setting was found in the Radix tree. Check if the
          * entry exists in the page-tables.
          */
-        mfn_t mfn = __p2m_lookup(d, gfn, NULL);
+        mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
 
         if ( mfn_eq(mfn, INVALID_MFN) )
             return -ESRCH;
@@ -1595,6 +1584,7 @@ p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag)
     xenmem_access_t xma;
     p2m_type_t t;
     struct page_info *page = NULL;
+    struct p2m_domain *p2m = &current->domain->arch.p2m;
 
     rc = gva_to_ipa(gva, &ipa, flag);
     if ( rc < 0 )
@@ -1655,7 +1645,7 @@ p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag)
      * We had a mem_access permission limiting the access, but the page type
      * could also be limiting, so we need to check that as well.
      */
-    mfn = __p2m_lookup(current->domain, gfn, &t);
+    mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL);
     if ( mfn_eq(mfn, INVALID_MFN) )
         goto err;
 
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (12 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-05 21:13   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping " Julien Grall
                   ` (10 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The function p2m_cache_flush can be re-implemented using the generic
function p2m_get_entry by iterating over the range and using the mapping
order given by the callee.

As the current implementation, no preemption is implemented, although
the comment in the current code claimed it. As the function is called by
a DOMCTL with a region of 1GB maximum, I think the preemption can be
left unimplemented for now.

Finally drop the operation CACHEFLUSH in apply_one_level as nobody is
using it anymore. Note that the function could have been dropped in one
go at the end, however I find easier to drop the operations one by one
avoiding a big deletion in the patch that convert the last operation.

Signed-off-by: Julien Grall <julien.grall@arm.com>

---
    The loop pattern will be very for the reliquish function. It might
    be possible to extract it in a separate function.
---
 xen/arch/arm/p2m.c | 67 +++++++++++++++++++++++++++---------------------------
 1 file changed, 34 insertions(+), 33 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 9a9c85c..e7697bb 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -722,7 +722,6 @@ enum p2m_operation {
     INSERT,
     REMOVE,
     RELINQUISH,
-    CACHEFLUSH,
     MEMACCESS,
 };
 
@@ -978,36 +977,6 @@ static int apply_one_level(struct domain *d,
          */
         return P2M_ONE_PROGRESS;
 
-    case CACHEFLUSH:
-        if ( !p2m_valid(orig_pte) )
-        {
-            *addr = (*addr + level_size) & level_mask;
-            return P2M_ONE_PROGRESS_NOP;
-        }
-
-        if ( level < 3 && p2m_table(orig_pte) )
-            return P2M_ONE_DESCEND;
-
-        /*
-         * could flush up to the next superpage boundary, but would
-         * need to be careful about preemption, so just do one 4K page
-         * now and return P2M_ONE_PROGRESS{,_NOP} so that the caller will
-         * continue to loop over the rest of the range.
-         */
-        if ( p2m_is_ram(orig_pte.p2m.type) )
-        {
-            unsigned long offset = paddr_to_pfn(*addr & ~level_mask);
-            flush_page_to_ram(orig_pte.p2m.base + offset);
-
-            *addr += PAGE_SIZE;
-            return P2M_ONE_PROGRESS;
-        }
-        else
-        {
-            *addr += PAGE_SIZE;
-            return P2M_ONE_PROGRESS_NOP;
-        }
-
     case MEMACCESS:
         if ( level < 3 )
         {
@@ -1555,12 +1524,44 @@ int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr)
 {
     struct p2m_domain *p2m = &d->arch.p2m;
     gfn_t end = gfn_add(start, nr);
+    p2m_type_t t;
+    unsigned int order;
 
     start = gfn_max(start, p2m->lowest_mapped_gfn);
     end = gfn_min(end, p2m->max_mapped_gfn);
 
-    return apply_p2m_changes(d, CACHEFLUSH, start, nr, INVALID_MFN,
-                             0, p2m_invalid, d->arch.p2m.default_access);
+    /* XXX: Should we use write lock here? */
+    p2m_read_lock(p2m);
+
+    for ( ; gfn_x(start) < gfn_x(end); start = gfn_add(start, 1UL << order) )
+    {
+        mfn_t mfn = p2m_get_entry(p2m, start, &t, NULL, &order);
+
+        /* Skip hole and non-RAM page */
+        if ( mfn_eq(mfn, INVALID_MFN) || !p2m_is_ram(t) )
+        {
+            /*
+             * the order corresponds to the order of the mapping in the
+             * page table. so we need to align the gfn before
+             * incrementing.
+             */
+            start = _gfn(gfn_x(start) & ~((1UL << order) - 1));
+            continue;
+        }
+
+        /*
+         * Could flush up to the next superpage boundary, but we would
+         * need to be careful about preemption, so just do one 4K page
+         * now.
+         * XXX: Implement preemption.
+         */
+        flush_page_to_ram(mfn_x(mfn));
+        order = 0;
+    }
+
+    p2m_read_unlock(p2m);
+
+    return 0;
 }
 
 mfn_t gfn_to_mfn(struct domain *d, gfn_t gfn)
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (13 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-05 21:58   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 16/22] xen/arm: p2m: Make p2m_{valid, table, mapping} helpers inline Julien Grall
                   ` (9 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The current implementation of relinquish_p2m_mapping is modifying the
page table to erase the entry one by one. However, this is not necessary
because the domain is not running anymore and therefore will speed up
the domain destruction.

The function relinquish_p2m_mapping can be re-implemented using
p2m_get_entry by iterating over the range mapped and using the mapping
order given by the callee.

Given that the preemption was chosen arbitrarily, it is no done on every
512 iterations. Meaning that Xen may check more often if the function is
preempted when there are no mappings.

Finally drop the operation RELINQUISH in apply_* as nobody is using it
anymore. Note that the functions could have been dropped in one go at
the end, however I find easier to drop the operations one by one
avoiding a big deletion in the patch that remove the last operation.

Signed-off-by: Julien Grall <julien.grall@arm.com>

---
    Further investigation needs to be done before applying this patch to
    check if someone could take advantage of this change (such
    modifying an entry which was relinquished).
---
 xen/arch/arm/p2m.c | 70 ++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 52 insertions(+), 18 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index e7697bb..d0aba5b 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -721,7 +721,6 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
 enum p2m_operation {
     INSERT,
     REMOVE,
-    RELINQUISH,
     MEMACCESS,
 };
 
@@ -908,7 +907,6 @@ static int apply_one_level(struct domain *d,
 
         break;
 
-    case RELINQUISH:
     case REMOVE:
         if ( !p2m_valid(orig_pte) )
         {
@@ -1092,17 +1090,6 @@ static int apply_p2m_changes(struct domain *d,
         {
             switch ( op )
             {
-            case RELINQUISH:
-                /*
-                 * Arbitrarily, preempt every 512 operations or 8192 nops.
-                 * 512*P2M_ONE_PROGRESS == 8192*P2M_ONE_PROGRESS_NOP == 0x2000
-                 * This is set in preempt_count_limit.
-                 *
-                 */
-                p2m->lowest_mapped_gfn = _gfn(addr >> PAGE_SHIFT);
-                rc = -ERESTART;
-                goto out;
-
             case MEMACCESS:
             {
                 /*
@@ -1508,16 +1495,63 @@ int p2m_init(struct domain *d)
     return rc;
 }
 
+/*
+ * The function will go through the p2m and remove page reference when it
+ * is required.
+ * The mapping are left intact in the p2m. This is fine because the
+ * domain will never run at that point.
+ *
+ * XXX: Check what does it mean for other part (such as lookup)
+ */
 int relinquish_p2m_mapping(struct domain *d)
 {
     struct p2m_domain *p2m = &d->arch.p2m;
-    unsigned long nr;
+    unsigned long count = 0;
+    p2m_type_t t;
+    int rc = 0;
+    unsigned int order;
+
+    /* Convenience alias */
+    gfn_t start = p2m->lowest_mapped_gfn;
+    gfn_t end = p2m->max_mapped_gfn;
 
-    nr = gfn_x(p2m->max_mapped_gfn) - gfn_x(p2m->lowest_mapped_gfn);
+    p2m_write_lock(p2m);
 
-    return apply_p2m_changes(d, RELINQUISH, p2m->lowest_mapped_gfn, nr,
-                             INVALID_MFN, 0, p2m_invalid,
-                             d->arch.p2m.default_access);
+    for ( ; gfn_x(start) < gfn_x(end); start = gfn_add(start, 1UL << order) )
+    {
+        mfn_t mfn = p2m_get_entry(p2m, start, &t, NULL, &order);
+
+        count++;
+        /*
+         * Arbitrarily preempt every 512 iterations.
+         */
+        if ( !(count % 512) && hypercall_preempt_check() )
+        {
+            rc = -ERESTART;
+            break;
+        }
+
+        /* Skip hole and any superpage */
+        if ( mfn_eq(mfn, INVALID_MFN) || order != 0 )
+            /*
+             * The order corresponds to the order of the mapping in the
+             * page table. So we need to align the GFN before
+             * incrementing.
+             */
+            start = _gfn(gfn_x(start) & ~((1UL << order) - 1));
+        else
+            p2m_put_l3_page(mfn, t);
+    }
+
+    /*
+     * Update lowest_mapped_gfn so on the next call we still start where
+     * we stopped.
+     */
+    p2m->lowest_mapped_gfn = start;
+
+    p2m_write_unlock(p2m);
+
+    return rc;
 }
 
 int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr)
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 16/22] xen/arm: p2m: Make p2m_{valid, table, mapping} helpers inline
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (14 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping " Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-05 22:00   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 17/22] xen/arm: p2m: Introduce a helper to check if an entry is a superpage Julien Grall
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Those helpers are very small and often used. Let know the compiler they
can be inlined.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index d0aba5b..ca2f1b0 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -39,7 +39,7 @@ static const unsigned int level_shifts[] =
 static const unsigned int level_orders[] =
     { ZEROETH_ORDER, FIRST_ORDER, SECOND_ORDER, THIRD_ORDER };
 
-static bool_t p2m_valid(lpae_t pte)
+static inline bool_t p2m_valid(lpae_t pte)
 {
     return pte.p2m.valid;
 }
@@ -48,11 +48,11 @@ static bool_t p2m_valid(lpae_t pte)
  * the table bit and therefore these would return the opposite to what
  * you would expect.
  */
-static bool_t p2m_table(lpae_t pte)
+static inline bool_t p2m_table(lpae_t pte)
 {
     return p2m_valid(pte) && pte.p2m.table;
 }
-static bool_t p2m_mapping(lpae_t pte)
+static inline bool_t p2m_mapping(lpae_t pte)
 {
     return p2m_valid(pte) && !pte.p2m.table;
 }
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 17/22] xen/arm: p2m: Introduce a helper to check if an entry is a superpage
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (15 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 16/22] xen/arm: p2m: Make p2m_{valid, table, mapping} helpers inline Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-05 22:03   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry Julien Grall
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

Use the level and the entry to know whether an entry is a superpage.
A superpage can only happen below level 3.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index ca2f1b0..c93e554 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -57,6 +57,11 @@ static inline bool_t p2m_mapping(lpae_t pte)
     return p2m_valid(pte) && !pte.p2m.table;
 }
 
+static inline bool_t p2m_is_superpage(lpae_t pte, unsigned int level)
+{
+    return (level < 3) && p2m_mapping(pte);
+}
+
 static inline void p2m_write_lock(struct p2m_domain *p2m)
 {
     write_lock(&p2m->lock);
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (16 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 17/22] xen/arm: p2m: Introduce a helper to check if an entry is a superpage Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-07-30 18:40   ` Tamas K Lengyel
                     ` (2 more replies)
  2016-07-28 14:51 ` [RFC 19/22] xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry Julien Grall
                   ` (6 subsequent siblings)
  24 siblings, 3 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The ARM architecture mandates to use of a break-before-make sequence
when changing translation entries if the page table is shared between
multiple CPUs whenever a valid entry is replaced by another valid entry
(see D4.7.1 in ARM DDI 0487A.j for more details).

The break-before-make sequence can be divided in the following steps:
    1) Invalidate the old entry in the page table
    2) Issue a TLB invalidation instruction for the address associated
    to this entry
    3) Write the new entry

The current P2M code implemented in apply_one_level does not respect
this sequence and may result to break coherency on some processors.

Adapting the current implementation to use the break-before-make
sequence would imply some code duplication and more TLBs invalidation
than necessary. For instance, if we are replacing a 4KB page and the
current mapping in the P2M is using a 1GB superpage, the following steps
will happen:
    1) Shatter the 1GB superpage into a series of 2MB superpages
    2) Shatter the 2MB superpage into a series of 4KB pages
    3) Replace the 4KB page

As the current implementation is shattering while descending and install
the mapping, Xen would need to issue 3 TLB invalidation instructions
which is clearly inefficient.

Furthermore, all the operations which modify the page table are using
the same skeleton. It is more complicated to maintain different code paths
than having a generic function that set an entry and take care of the
break-before-make sequence.

The new implementation is based on the x86 EPT one which, I think,
fits quite well for the break-before-make sequence whilst keeping
the code simple.

The main function of the new implementation is __p2m_get_entry. It will
only work on mapping that are aligned to a block entry in the page table
(i.e 1GB, 2MB, 4KB when using a 4KB granularity).

Another function, p2m_get_entry, is provided to break down is region
into mapping that is aligned to a block entry.

Note that to keep this patch "small", there are no caller of those
functions added in this patch (they will be added in follow-up patches).

Signed-off-by: Julien Grall <julien.grall@arm.com>

---
    I need to find the impact of this new implementation on ARM32 because
    the domheap is not always mapped. This means that Xen needs to map/unmap
    everytime the page associated to the page table. It might be possible
    to re-use some caching structure as the current implementation does
    or rework the way a domheap is mapped/unmapped.

    Also, this code still contain few TODOs mostly to add sanity
    checks and few optimization. The IOMMU is not yet supported.
---
 xen/arch/arm/p2m.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 335 insertions(+)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index c93e554..297b176 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -750,6 +750,341 @@ static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
     }
 }
 
+#if 0
+/* Free lpae sub-tree behind an entry */
+static void p2m_free_entry(struct p2m_domain *p2m,
+                           lpae_t entry, unsigned int level)
+{
+    unsigned int i;
+    lpae_t *table;
+    mfn_t mfn;
+
+    /* Nothing to do if the entry is invalid or a super-page */
+    if ( !p2m_valid(entry) || p2m_is_superpage(entry, level) )
+        return;
+
+    if ( level == 3 )
+    {
+        p2m_put_l3_page(_mfn(entry.p2m.base), entry.p2m.type);
+        return;
+    }
+
+    table = map_domain_page(_mfn(entry.p2m.base));
+    for ( i = 0; i < LPAE_ENTRIES; i++ )
+        p2m_free_entry(p2m, *(table + i), level + 1);
+
+    unmap_domain_page(table);
+
+    /*
+     * Make sure all the references in the TLB have been removed before
+     * freing the intermediate page table.
+     * XXX: Should we defer the free of the page table to avoid the
+     * flush?
+     */
+    if ( p2m->need_flush )
+        p2m_flush_tlb_sync(p2m);
+
+    mfn = _mfn(entry.p2m.base);
+    ASSERT(mfn_valid(mfn_x(mfn)));
+
+    free_domheap_page(mfn_to_page(mfn_x(mfn)));
+}
+
+static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry,
+                                unsigned int level, unsigned int target,
+                                const unsigned int *offsets)
+{
+    struct page_info *page;
+    unsigned int i;
+    lpae_t pte, *table;
+    bool rv = true;
+
+    /* Convenience aliases */
+    p2m_type_t t = entry->p2m.type;
+    mfn_t mfn = _mfn(entry->p2m.base);
+
+    /* Convenience aliases */
+    unsigned int next_level = level + 1;
+    unsigned int level_order = level_orders[next_level];
+
+    /*
+     * This should only be called with target != level and the entry is
+     * a superpage.
+     */
+    ASSERT(level < target);
+    ASSERT(p2m_is_superpage(*entry, level));
+
+    page = alloc_domheap_page(NULL, 0);
+    if ( !page )
+        return false;
+
+    page_list_add(page, &p2m->pages);
+    table = __map_domain_page(page);
+
+    /*
+     * We are either splitting a first level 1G page into 512 second level
+     * 2M pages, or a second level 2M page into 512 third level 4K pages.
+     */
+    for ( i = 0; i < LPAE_ENTRIES; i++ )
+    {
+        lpae_t *new_entry = table + i;
+
+        pte = mfn_to_p2m_entry(mfn, t, p2m->default_access);
+
+        mfn = mfn_add(mfn, (1UL << level_order));
+
+        /*
+         * First and second level pages set p2m.table = 0, but third
+         * level entries set p2m.table = 1.
+         */
+        if ( next_level < 3 )
+            pte.p2m.table = 0;
+
+        write_pte(new_entry, pte);
+    }
+
+    /*
+     * Shatter superpage in the page to the level we want to make the
+     * changes.
+     * This is done outside the loop to avoid checking the offset to
+     * know whether the entry should be shattered for every entry.
+     */
+    if ( next_level != target )
+        rv = p2m_split_superpage(p2m, table + offsets[next_level],
+                                 level + 1, target, offsets);
+
+    if ( p2m->clean_pte )
+        clean_dcache_va_range(table, PAGE_SIZE);
+
+    unmap_domain_page(table);
+
+    pte = mfn_to_p2m_entry(_mfn(page_to_mfn(page)), p2m_invalid,
+                           p2m->default_access);
+
+    p2m_write_pte(entry, pte, p2m->clean_pte);
+
+    /*
+     * Even if we failed, we should install the newly allocated LPAE
+     * entry. The caller will be in charge to free the sub-tree.
+     * XXX: See if we can free entry here.
+     */
+    *entry = pte;
+
+    return rv;
+}
+
+/*
+ * Insert an entry in the p2m. This should be called with a mapping
+ * equal to a page/superpage (4K, 2M, 1G).
+ */
+static int __p2m_set_entry(struct p2m_domain *p2m,
+                           gfn_t sgfn,
+                           unsigned int page_order,
+                           mfn_t smfn,
+                           p2m_type_t t,
+                           p2m_access_t a)
+{
+    paddr_t addr = pfn_to_paddr(gfn_x(sgfn));
+    unsigned int level = 0;
+    unsigned int target = 3 - (page_order / LPAE_SHIFT);
+    lpae_t *entry, *table, orig_pte;
+    int rc;
+
+    /* Convenience aliases */
+    const unsigned int offsets[4] = {
+        zeroeth_table_offset(addr),
+        first_table_offset(addr),
+        second_table_offset(addr),
+        third_table_offset(addr)
+    };
+
+    /* TODO: Check the validity for the address */
+
+    ASSERT(p2m_is_write_locked(p2m));
+
+    /*
+     * Check if the level target is valid: we only support
+     * 4K - 2M - 1G mapping.
+     */
+    ASSERT(target > 0 && target <= 3);
+
+    table = p2m_get_root_pointer(p2m, sgfn);
+    if ( !table )
+        return -EINVAL;
+
+    for ( level = P2M_ROOT_LEVEL; level < target; level++ )
+    {
+        rc = p2m_next_level(p2m, false, &table, offsets[level]);
+        if ( rc == GUEST_TABLE_MAP_FAILED )
+        {
+            rc = -ENOENT;
+            goto out;
+        }
+        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
+            break;
+    }
+
+    entry = table + offsets[level];
+
+    /*
+     * If we are here with level < target, we must be at a leaf node,
+     * and we need to break up the superpage.
+     */
+    if ( level < target )
+    {
+        /* We need to split the original page. */
+        lpae_t split_pte = *entry;
+
+        ASSERT(p2m_is_superpage(*entry, level));
+
+        if ( !p2m_split_superpage(p2m, &split_pte, level, target, offsets) )
+        {
+            p2m_free_entry(p2m, split_pte, level);
+            rc = -ENOMEM;
+            goto out;
+        }
+
+        /*
+         * Follow the break-before-sequence to update the entry.
+         * For more details see (D4.7.1 in ARM DDI 0487A.j).
+         * XXX: Can we flush by address?
+         */
+        p2m_remove_pte(entry, p2m->clean_pte);
+        p2m_flush_tlb_sync(p2m);
+
+        p2m_write_pte(entry, split_pte, p2m->clean_pte);
+
+        /* then move to the level we want to make real changes */
+        for ( ; level < target; level++ )
+        {
+            rc = p2m_next_level(p2m, true, &table, offsets[level]);
+
+            /*
+             * The entry should be found and either be a table
+             * or a superpage if level 3 is not targeted
+             */
+            ASSERT(rc == GUEST_TABLE_NORMAL_PAGE ||
+                   (rc == GUEST_TABLE_SUPER_PAGE && target < 3));
+        }
+
+        entry = table + offsets[level];
+    }
+
+    /*
+     * We should always be there with the correct level because
+     * all the intermediate tables have been installed if necessary.
+     */
+    ASSERT(level == target);
+
+    orig_pte = *entry;
+
+    /*
+     * The radix-tree can only work on 4KB. This is only used when
+     * memaccess is enabled.
+     */
+    ASSERT(!p2m->mem_access_enabled || page_order == 0);
+    /*
+     * The access type should always be p2m_access_rwx when the mapping
+     * is removed.
+     */
+    ASSERT(!mfn_eq(INVALID_MFN, smfn) || (a == p2m_access_rwx));
+    /*
+     * Update the mem access permission before update the P2M. So we
+     * don't have to revert the mapping if it has failed.
+     */
+    rc = p2m_mem_access_radix_set(p2m, sgfn, a);
+    if ( rc )
+        goto out;
+
+    /*
+     * Always remove the entry in order to follow the break-before-make
+     * sequence when updating the translation table (D4.7.1 in ARM DDI
+     * 0487A.j).
+     */
+    if ( p2m_valid(orig_pte) )
+        p2m_remove_pte(entry, p2m->clean_pte);
+
+    if ( mfn_eq(smfn, INVALID_MFN) )
+        /* Flush can be deferred if the entry is removed */
+        p2m->need_flush |= !!p2m_valid(orig_pte);
+    else
+    {
+        lpae_t pte;
+
+        /*
+         * Flush the TLB before write the new one to keep coherency.
+         * XXX: Can we flush by address?
+         */
+        if ( p2m_valid(orig_pte) )
+            p2m_flush_tlb_sync(p2m);
+
+        pte = mfn_to_p2m_entry(smfn, t, a);
+        if ( level < 3 )
+            pte.p2m.table = 0; /* Superpage entry */
+
+        p2m_write_pte(entry, pte, p2m->clean_pte);
+
+        p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn,
+                                      gfn_add(sgfn, 1 << page_order));
+        p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn);
+    }
+
+    /*
+     * Free the entry only if the original pte was valid and the base
+     * is different (to avoid freeing when permission is changed).
+     */
+    if ( p2m_valid(orig_pte) && entry->p2m.base != orig_pte.p2m.base )
+        p2m_free_entry(p2m, orig_pte, level);
+
+    /* XXX: Flush iommu */
+
+    rc = 0;
+
+out:
+    unmap_domain_page(table);
+
+    return rc;
+}
+
+static int p2m_set_entry(struct p2m_domain *p2m,
+                         gfn_t sgfn,
+                         unsigned long todo,
+                         mfn_t smfn,
+                         p2m_type_t t,
+                         p2m_access_t a)
+{
+    int rc = 0;
+
+    while ( todo )
+    {
+        unsigned long mask = gfn_x(sgfn) | mfn_x(smfn) | todo;
+        unsigned long order;
+
+        /* Always map 4k by 4k when memaccess is enabled */
+        if ( unlikely(p2m->mem_access_enabled) )
+            order = THIRD_ORDER;
+        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
+            order = FIRST_ORDER;
+        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
+            order = SECOND_ORDER;
+        else
+            order = THIRD_ORDER;
+
+        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
+        if ( rc )
+            break;
+
+        sgfn = gfn_add(sgfn, (1 << order));
+        if ( !mfn_eq(smfn, INVALID_MFN) )
+           smfn = mfn_add(smfn, (1 << order));
+
+        todo -= (1 << order);
+    }
+
+    return rc;
+}
+#endif
+
 /*
  * Returns true if start_gpaddr..end_gpaddr contains at least one
  * suitably aligned level_size mappping of maddr.
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 19/22] xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (17 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-06 18:53   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping " Julien Grall
                   ` (5 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The function p2m_insert_mapping can be re-implemented using the generic
function p2m_set_entry.

Also drop the operation REMOVE in apply_* as nobody is using it anymore.
Note that the functions could have been dropped in one go at the end,
however I find easier to drop the operations one by one avoiding a big
deletion in the patch that converts the last operation.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 127 ++++++-----------------------------------------------
 1 file changed, 13 insertions(+), 114 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 297b176..0920222 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -725,7 +725,6 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
 
 enum p2m_operation {
     INSERT,
-    REMOVE,
     MEMACCESS,
 };
 
@@ -750,7 +749,6 @@ static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
     }
 }
 
-#if 0
 /* Free lpae sub-tree behind an entry */
 static void p2m_free_entry(struct p2m_domain *p2m,
                            lpae_t entry, unsigned int level)
@@ -1083,7 +1081,6 @@ static int p2m_set_entry(struct p2m_domain *p2m,
 
     return rc;
 }
-#endif
 
 /*
  * Returns true if start_gpaddr..end_gpaddr contains at least one
@@ -1161,7 +1158,6 @@ static int apply_one_level(struct domain *d,
                            p2m_access_t a)
 {
     const paddr_t level_size = level_sizes[level];
-    const paddr_t level_mask = level_masks[level];
 
     struct p2m_domain *p2m = &d->arch.p2m;
     lpae_t pte;
@@ -1247,74 +1243,6 @@ static int apply_one_level(struct domain *d,
 
         break;
 
-    case REMOVE:
-        if ( !p2m_valid(orig_pte) )
-        {
-            /* Progress up to next boundary */
-            *addr = (*addr + level_size) & level_mask;
-            *maddr = (*maddr + level_size) & level_mask;
-            return P2M_ONE_PROGRESS_NOP;
-        }
-
-        if ( level < 3 )
-        {
-            if ( p2m_table(orig_pte) )
-                return P2M_ONE_DESCEND;
-
-            if ( op == REMOVE &&
-                 !is_mapping_aligned(*addr, end_gpaddr,
-                                     0, /* maddr doesn't matter for remove */
-                                     level_size) )
-            {
-                /*
-                 * Removing a mapping from the middle of a superpage. Shatter
-                 * and descend.
-                 */
-                *flush = true;
-                rc = p2m_shatter_page(p2m, entry, level);
-                if ( rc < 0 )
-                    return rc;
-
-                return P2M_ONE_DESCEND;
-            }
-        }
-
-        /*
-         * Ensure that the guest address addr currently being
-         * handled (that is in the range given as argument to
-         * this function) is actually mapped to the corresponding
-         * machine address in the specified range. maddr here is
-         * the machine address given to the function, while
-         * orig_pte.p2m.base is the machine frame number actually
-         * mapped to the guest address: check if the two correspond.
-         */
-         if ( op == REMOVE &&
-              pfn_to_paddr(orig_pte.p2m.base) != *maddr )
-             printk(XENLOG_G_WARNING
-                    "p2m_remove dom%d: mapping at %"PRIpaddr" is of maddr %"PRIpaddr" not %"PRIpaddr" as expected\n",
-                    d->domain_id, *addr, pfn_to_paddr(orig_pte.p2m.base),
-                    *maddr);
-
-        *flush = true;
-
-        p2m_remove_pte(entry, p2m->clean_pte);
-        p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
-                                 p2m_access_rwx);
-
-        *addr += level_size;
-        *maddr += level_size;
-
-        p2m->stats.mappings[level]--;
-
-        if ( level == 3 )
-            p2m_put_l3_page(_mfn(orig_pte.p2m.base), orig_pte.p2m.type);
-
-        /*
-         * This is still a single pte write, no matter the level, so no need to
-         * scale.
-         */
-        return P2M_ONE_PROGRESS;
-
     case MEMACCESS:
         if ( level < 3 )
         {
@@ -1526,43 +1454,6 @@ static int apply_p2m_changes(struct domain *d,
         }
 
         BUG_ON(level > 3);
-
-        if ( op == REMOVE )
-        {
-            for ( ; level > P2M_ROOT_LEVEL; level-- )
-            {
-                lpae_t old_entry;
-                lpae_t *entry;
-                unsigned int offset;
-
-                pg = pages[level];
-
-                /*
-                 * No need to try the previous level if the current one
-                 * still contains some mappings.
-                 */
-                if ( pg->u.inuse.p2m_refcount )
-                    break;
-
-                offset = offsets[level - 1];
-                entry = &mappings[level - 1][offset];
-                old_entry = *entry;
-
-                page_list_del(pg, &p2m->pages);
-
-                p2m_remove_pte(entry, p2m->clean_pte);
-
-                p2m->stats.mappings[level - 1]--;
-                update_reference_mapping(pages[level - 1], old_entry, *entry);
-
-                /*
-                 * We can't free the page now because it may be present
-                 * in the guest TLB. Queue it and free it after the TLB
-                 * has been flushed.
-                 */
-                page_list_add(pg, &free_pages);
-            }
-        }
     }
 
     if ( op == INSERT )
@@ -1604,8 +1495,10 @@ out:
          * addr keeps the address of the end of the last successfully-inserted
          * mapping.
          */
-        apply_p2m_changes(d, REMOVE, sgfn, gfn - gfn_x(sgfn), smfn,
-                          0, p2m_invalid, d->arch.p2m.default_access);
+        p2m_write_lock(p2m);
+        p2m_set_entry(p2m, sgfn, gfn - gfn_x(sgfn), INVALID_MFN,
+                      p2m_invalid, p2m_access_rwx);
+        p2m_write_unlock(p2m);
     }
 
     return rc;
@@ -1626,9 +1519,15 @@ static inline int p2m_remove_mapping(struct domain *d,
                                      unsigned long nr,
                                      mfn_t mfn)
 {
-    return apply_p2m_changes(d, REMOVE, start_gfn, nr, mfn,
-                             /* arguments below not used when removing mapping */
-                             0, p2m_invalid, d->arch.p2m.default_access);
+    struct p2m_domain *p2m = &d->arch.p2m;
+    int rc;
+
+    p2m_write_lock(p2m);
+    rc = p2m_set_entry(p2m, start_gfn, nr, INVALID_MFN,
+                       p2m_invalid, p2m_access_rwx);
+    p2m_write_unlock(p2m);
+
+    return rc;
 }
 
 int map_regions_rw_cache(struct domain *d,
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (18 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 19/22] xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-06 18:57   ` Stefano Stabellini
  2016-07-28 14:51 ` [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry Julien Grall
                   ` (4 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The function p2m_insert_mapping can be re-implemented using the generic
function p2m_set_entry.

Note that the mapping is not reverted anymore if Xen fails to insert a
mapping. This was added to ensure the MMIO are not kept half-mapped
in case of failure and to follow the x86 counterpart. This was removed
on the x86 part by commit c3c756bd "x86/p2m: use large pages for MMIO
mappings" and I think we should let the caller taking care of it.

Finally drop the operation INSERT in apply_* as nobody is using it
anymore. Note that the functios could have been dropped in one go at the
end, however I find easier to drop the operations one by one avoiding a
big deletion in the patch that convert the last operation.

Signed-off-by: Julien Grall <julien.grall@arm.com>

---
    Whilst there is no safety checks on what is replaced in the P2M
    (such as foreign/grant mapping as x86 does), we may want to add it
    ensuring the guest is not doing something dumb. Any opinions?
---
 xen/arch/arm/p2m.c | 148 +++++------------------------------------------------
 1 file changed, 12 insertions(+), 136 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 0920222..707c7be 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -724,7 +724,6 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
 }
 
 enum p2m_operation {
-    INSERT,
     MEMACCESS,
 };
 
@@ -1082,41 +1081,6 @@ static int p2m_set_entry(struct p2m_domain *p2m,
     return rc;
 }
 
-/*
- * Returns true if start_gpaddr..end_gpaddr contains at least one
- * suitably aligned level_size mappping of maddr.
- *
- * So long as the range is large enough the end_gpaddr need not be
- * aligned (callers should create one superpage mapping based on this
- * result and then call this again on the new range, eventually the
- * slop at the end will cause this function to return false).
- */
-static bool_t is_mapping_aligned(const paddr_t start_gpaddr,
-                                 const paddr_t end_gpaddr,
-                                 const paddr_t maddr,
-                                 const paddr_t level_size)
-{
-    const paddr_t level_mask = level_size - 1;
-
-    /* No hardware superpages at level 0 */
-    if ( level_size == ZEROETH_SIZE )
-        return false;
-
-    /*
-     * A range smaller than the size of a superpage at this level
-     * cannot be superpage aligned.
-     */
-    if ( ( end_gpaddr - start_gpaddr ) < level_size - 1 )
-        return false;
-
-    /* Both the gpaddr and maddr must be aligned */
-    if ( start_gpaddr & level_mask )
-        return false;
-    if ( maddr & level_mask )
-        return false;
-    return true;
-}
-
 #define P2M_ONE_DESCEND        0
 #define P2M_ONE_PROGRESS_NOP   0x1
 #define P2M_ONE_PROGRESS       0x10
@@ -1168,81 +1132,6 @@ static int apply_one_level(struct domain *d,
 
     switch ( op )
     {
-    case INSERT:
-        if ( is_mapping_aligned(*addr, end_gpaddr, *maddr, level_size) &&
-           /*
-            * We do not handle replacing an existing table with a superpage
-            * or when mem_access is in use.
-            */
-             (level == 3 || (!p2m_table(orig_pte) && !p2m->mem_access_enabled)) )
-        {
-            rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)), a);
-            if ( rc < 0 )
-                return rc;
-
-            /* New mapping is superpage aligned, make it */
-            pte = mfn_to_p2m_entry(_mfn(*maddr >> PAGE_SHIFT), t, a);
-            if ( level < 3 )
-                pte.p2m.table = 0; /* Superpage entry */
-
-            p2m_write_pte(entry, pte, p2m->clean_pte);
-
-            *flush |= p2m_valid(orig_pte);
-
-            *addr += level_size;
-            *maddr += level_size;
-
-            if ( p2m_valid(orig_pte) )
-            {
-                /*
-                 * We can't currently get here for an existing table
-                 * mapping, since we don't handle replacing an
-                 * existing table with a superpage. If we did we would
-                 * need to handle freeing (and accounting) for the bit
-                 * of the p2m tree which we would be about to lop off.
-                 */
-                BUG_ON(level < 3 && p2m_table(orig_pte));
-                if ( level == 3 )
-                    p2m_put_l3_page(_mfn(orig_pte.p2m.base),
-                                    orig_pte.p2m.type);
-            }
-            else /* New mapping */
-                p2m->stats.mappings[level]++;
-
-            return P2M_ONE_PROGRESS;
-        }
-        else
-        {
-            /* New mapping is not superpage aligned, create a new table entry */
-
-            /* L3 is always suitably aligned for mapping (handled, above) */
-            BUG_ON(level == 3);
-
-            /* Not present -> create table entry and descend */
-            if ( !p2m_valid(orig_pte) )
-            {
-                rc = p2m_create_table(p2m, entry, 0);
-                if ( rc < 0 )
-                    return rc;
-                return P2M_ONE_DESCEND;
-            }
-
-            /* Existing superpage mapping -> shatter and descend */
-            if ( p2m_mapping(orig_pte) )
-            {
-                *flush = true;
-                rc = p2m_shatter_page(p2m, entry, level);
-                if ( rc < 0 )
-                    return rc;
-            } /* else: an existing table mapping -> descend */
-
-            BUG_ON(!p2m_table(*entry));
-
-            return P2M_ONE_DESCEND;
-        }
-
-        break;
-
     case MEMACCESS:
         if ( level < 3 )
         {
@@ -1456,13 +1345,6 @@ static int apply_p2m_changes(struct domain *d,
         BUG_ON(level > 3);
     }
 
-    if ( op == INSERT )
-    {
-        p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn,
-                                      gfn_add(sgfn, nr));
-        p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn);
-    }
-
     rc = 0;
 
 out:
@@ -1485,22 +1367,6 @@ out:
 
     p2m_write_unlock(p2m);
 
-    if ( rc < 0 && ( op == INSERT ) &&
-         addr != start_gpaddr )
-    {
-        unsigned long gfn = paddr_to_pfn(addr);
-
-        BUG_ON(addr == end_gpaddr);
-        /*
-         * addr keeps the address of the end of the last successfully-inserted
-         * mapping.
-         */
-        p2m_write_lock(p2m);
-        p2m_set_entry(p2m, sgfn, gfn - gfn_x(sgfn), INVALID_MFN,
-                      p2m_invalid, p2m_access_rwx);
-        p2m_write_unlock(p2m);
-    }
-
     return rc;
 }
 
@@ -1510,8 +1376,18 @@ static inline int p2m_insert_mapping(struct domain *d,
                                      mfn_t mfn,
                                      p2m_type_t t)
 {
-    return apply_p2m_changes(d, INSERT, start_gfn, nr, mfn,
-                             0, t, d->arch.p2m.default_access);
+    struct p2m_domain *p2m = &d->arch.p2m;
+    int rc;
+
+    p2m_write_lock(p2m);
+    /*
+     * XXX: Do we want to do safety check on what is replaced?
+     * See what x86 is doing.
+     */
+    rc = p2m_set_entry(p2m, start_gfn, nr, mfn, t, p2m->default_access);
+    p2m_write_unlock(p2m);
+
+    return rc;
 }
 
 static inline int p2m_remove_mapping(struct domain *d,
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (19 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping " Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-07-28 15:04   ` Razvan Cojocaru
                     ` (2 more replies)
  2016-07-28 14:51 ` [RFC 22/22] xen/arm: p2m: Do not handle shattering in p2m_create_table Julien Grall
                   ` (3 subsequent siblings)
  24 siblings, 3 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin,
	Julien Grall, Tamas K Lengyel, wei.chen

The function p2m_set_mem_access can be re-implemented using the generic
functions p2m_get_entry and __p2m_set_entry.

Note that because of the implementation of p2m_get_entry, a TLB
invalidation instruction will be issued for each 4KB page. Therefore the
performance of memaccess will be impacted, however the function is now
safe on all the processors.

Also the function apply_p2m_changes is dropped completely as it is not
unused anymore.

Signed-off-by: Julien Grall <julien.grall@arm.com>
Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
Cc: Tamas K Lengyel <tamas@tklengyel.com>

---
    I have not ran any performance test with memaccess for now, but I
    expect an important and unavoidable impact because of how memaccess
    has been designed to workaround hardware limitation. Note that might
    be possible to re-work memaccess work on superpage but this should
    be done in a separate patch.
---
 xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
 1 file changed, 38 insertions(+), 291 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 707c7be..16ed393 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -1081,295 +1081,6 @@ static int p2m_set_entry(struct p2m_domain *p2m,
     return rc;
 }
 
-#define P2M_ONE_DESCEND        0
-#define P2M_ONE_PROGRESS_NOP   0x1
-#define P2M_ONE_PROGRESS       0x10
-
-static int p2m_shatter_page(struct p2m_domain *p2m,
-                            lpae_t *entry,
-                            unsigned int level)
-{
-    const paddr_t level_shift = level_shifts[level];
-    int rc = p2m_create_table(p2m, entry, level_shift - PAGE_SHIFT);
-
-    if ( !rc )
-    {
-        p2m->stats.shattered[level]++;
-        p2m->stats.mappings[level]--;
-        p2m->stats.mappings[level+1] += LPAE_ENTRIES;
-    }
-
-    return rc;
-}
-
-/*
- * 0   == (P2M_ONE_DESCEND) continue to descend the tree
- * +ve == (P2M_ONE_PROGRESS_*) handled at this level, continue, flush,
- *        entry, addr and maddr updated.  Return value is an
- *        indication of the amount of work done (for preemption).
- * -ve == (-Exxx) error.
- */
-static int apply_one_level(struct domain *d,
-                           lpae_t *entry,
-                           unsigned int level,
-                           enum p2m_operation op,
-                           paddr_t start_gpaddr,
-                           paddr_t end_gpaddr,
-                           paddr_t *addr,
-                           paddr_t *maddr,
-                           bool_t *flush,
-                           p2m_type_t t,
-                           p2m_access_t a)
-{
-    const paddr_t level_size = level_sizes[level];
-
-    struct p2m_domain *p2m = &d->arch.p2m;
-    lpae_t pte;
-    const lpae_t orig_pte = *entry;
-    int rc;
-
-    BUG_ON(level > 3);
-
-    switch ( op )
-    {
-    case MEMACCESS:
-        if ( level < 3 )
-        {
-            if ( !p2m_valid(orig_pte) )
-            {
-                *addr += level_size;
-                return P2M_ONE_PROGRESS_NOP;
-            }
-
-            /* Shatter large pages as we descend */
-            if ( p2m_mapping(orig_pte) )
-            {
-                rc = p2m_shatter_page(p2m, entry, level);
-                if ( rc < 0 )
-                    return rc;
-            } /* else: an existing table mapping -> descend */
-
-            return P2M_ONE_DESCEND;
-        }
-        else
-        {
-            pte = orig_pte;
-
-            if ( p2m_valid(pte) )
-            {
-                rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
-                                              a);
-                if ( rc < 0 )
-                    return rc;
-
-                p2m_set_permission(&pte, pte.p2m.type, a);
-                p2m_write_pte(entry, pte, p2m->clean_pte);
-            }
-
-            *addr += level_size;
-            *flush = true;
-            return P2M_ONE_PROGRESS;
-        }
-    }
-
-    BUG(); /* Should never get here */
-}
-
-/*
- * The page is only used by the P2M code which is protected by the p2m->lock.
- * So we can avoid to use atomic helpers.
- */
-static void update_reference_mapping(struct page_info *page,
-                                     lpae_t old_entry,
-                                     lpae_t new_entry)
-{
-    if ( p2m_valid(old_entry) && !p2m_valid(new_entry) )
-        page->u.inuse.p2m_refcount--;
-    else if ( !p2m_valid(old_entry) && p2m_valid(new_entry) )
-        page->u.inuse.p2m_refcount++;
-}
-
-static int apply_p2m_changes(struct domain *d,
-                     enum p2m_operation op,
-                     gfn_t sgfn,
-                     unsigned long nr,
-                     mfn_t smfn,
-                     uint32_t mask,
-                     p2m_type_t t,
-                     p2m_access_t a)
-{
-    paddr_t start_gpaddr = pfn_to_paddr(gfn_x(sgfn));
-    paddr_t end_gpaddr = pfn_to_paddr(gfn_x(sgfn) + nr);
-    paddr_t maddr = pfn_to_paddr(mfn_x(smfn));
-    int rc, ret;
-    struct p2m_domain *p2m = &d->arch.p2m;
-    lpae_t *mappings[4] = { NULL, NULL, NULL, NULL };
-    struct page_info *pages[4] = { NULL, NULL, NULL, NULL };
-    paddr_t addr;
-    unsigned int level = 0;
-    unsigned int cur_root_table = ~0;
-    unsigned int cur_offset[4] = { ~0, ~0, ~0, ~0 };
-    unsigned int count = 0;
-    const unsigned int preempt_count_limit = (op == MEMACCESS) ? 1 : 0x2000;
-    const bool_t preempt = !is_idle_vcpu(current);
-    bool_t flush = false;
-    PAGE_LIST_HEAD(free_pages);
-    struct page_info *pg;
-
-    p2m_write_lock(p2m);
-
-    /* Static mapping. P2M_ROOT_PAGES > 1 are handled below */
-    if ( P2M_ROOT_PAGES == 1 )
-    {
-        mappings[P2M_ROOT_LEVEL] = __map_domain_page(p2m->root);
-        pages[P2M_ROOT_LEVEL] = p2m->root;
-    }
-
-    addr = start_gpaddr;
-    while ( addr < end_gpaddr )
-    {
-        int root_table;
-        const unsigned int offsets[4] = {
-            zeroeth_table_offset(addr),
-            first_table_offset(addr),
-            second_table_offset(addr),
-            third_table_offset(addr)
-        };
-
-        /*
-         * Check if current iteration should be possibly preempted.
-         * Since count is initialised to 0 above we are guaranteed to
-         * always make at least one pass as long as preempt_count_limit is
-         * initialized with a value >= 1.
-         */
-        if ( preempt && count >= preempt_count_limit
-             && hypercall_preempt_check() )
-        {
-            switch ( op )
-            {
-            case MEMACCESS:
-            {
-                /*
-                 * Preempt setting mem_access permissions as required by XSA-89,
-                 * if it's not the last iteration.
-                 */
-                uint32_t progress = paddr_to_pfn(addr) - gfn_x(sgfn) + 1;
-
-                if ( nr > progress && !(progress & mask) )
-                {
-                    rc = progress;
-                    goto out;
-                }
-                break;
-            }
-
-            default:
-                break;
-            };
-
-            /*
-             * Reset current iteration counter.
-             */
-            count = 0;
-        }
-
-        if ( P2M_ROOT_PAGES > 1 )
-        {
-            int i;
-            /*
-             * Concatenated root-level tables. The table number will be the
-             * offset at the previous level. It is not possible to concatenate
-             * a level-0 root.
-             */
-            ASSERT(P2M_ROOT_LEVEL > 0);
-            root_table = offsets[P2M_ROOT_LEVEL - 1];
-            if ( root_table >= P2M_ROOT_PAGES )
-            {
-                rc = -EINVAL;
-                goto out;
-            }
-
-            if ( cur_root_table != root_table )
-            {
-                if ( mappings[P2M_ROOT_LEVEL] )
-                    unmap_domain_page(mappings[P2M_ROOT_LEVEL]);
-                mappings[P2M_ROOT_LEVEL] =
-                    __map_domain_page(p2m->root + root_table);
-                pages[P2M_ROOT_LEVEL] = p2m->root + root_table;
-                cur_root_table = root_table;
-                /* Any mapping further down is now invalid */
-                for ( i = P2M_ROOT_LEVEL; i < 4; i++ )
-                    cur_offset[i] = ~0;
-            }
-        }
-
-        for ( level = P2M_ROOT_LEVEL; level < 4; level++ )
-        {
-            unsigned offset = offsets[level];
-            lpae_t *entry = &mappings[level][offset];
-            lpae_t old_entry = *entry;
-
-            ret = apply_one_level(d, entry,
-                                  level, op,
-                                  start_gpaddr, end_gpaddr,
-                                  &addr, &maddr, &flush,
-                                  t, a);
-            if ( ret < 0 ) { rc = ret ; goto out; }
-            count += ret;
-
-            if ( ret != P2M_ONE_PROGRESS_NOP )
-                update_reference_mapping(pages[level], old_entry, *entry);
-
-            /* L3 had better have done something! We cannot descend any further */
-            BUG_ON(level == 3 && ret == P2M_ONE_DESCEND);
-            if ( ret != P2M_ONE_DESCEND ) break;
-
-            BUG_ON(!p2m_valid(*entry));
-
-            if ( cur_offset[level] != offset )
-            {
-                /* Update mapping for next level */
-                int i;
-                if ( mappings[level+1] )
-                    unmap_domain_page(mappings[level+1]);
-                mappings[level+1] = map_domain_page(_mfn(entry->p2m.base));
-                pages[level+1] = mfn_to_page(entry->p2m.base);
-                cur_offset[level] = offset;
-                /* Any mapping further down is now invalid */
-                for ( i = level+1; i < 4; i++ )
-                    cur_offset[i] = ~0;
-            }
-            /* else: next level already valid */
-        }
-
-        BUG_ON(level > 3);
-    }
-
-    rc = 0;
-
-out:
-    if ( flush )
-    {
-        p2m_flush_tlb_sync(&d->arch.p2m);
-        ret = iommu_iotlb_flush(d, gfn_x(sgfn), nr);
-        if ( !rc )
-            rc = ret;
-    }
-
-    while ( (pg = page_list_remove_head(&free_pages)) )
-        free_domheap_page(pg);
-
-    for ( level = P2M_ROOT_LEVEL; level < 4; level ++ )
-    {
-        if ( mappings[level] )
-            unmap_domain_page(mappings[level]);
-    }
-
-    p2m_write_unlock(p2m);
-
-    return rc;
-}
-
 static inline int p2m_insert_mapping(struct domain *d,
                                      gfn_t start_gfn,
                                      unsigned long nr,
@@ -2069,6 +1780,7 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
 {
     struct p2m_domain *p2m = p2m_get_hostp2m(d);
     p2m_access_t a;
+    unsigned int order;
     long rc = 0;
 
     static const p2m_access_t memaccess[] = {
@@ -2111,8 +1823,43 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
         return 0;
     }
 
-    rc = apply_p2m_changes(d, MEMACCESS, gfn_add(gfn, start),
-                           (nr - start), INVALID_MFN, mask, 0, a);
+    p2m_write_lock(p2m);
+
+    for ( gfn = gfn_add(gfn, start); nr > start; gfn = gfn_add(gfn, 1UL << order) )
+    {
+        p2m_type_t t;
+        mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order);
+
+        /* Skip hole */
+        if ( mfn_eq(mfn, INVALID_MFN) )
+        {
+            /*
+             * the order corresponds to the order of the mapping in the
+             * page table. so we need to align the gfn before
+             * incrementing.
+             */
+            gfn = _gfn(gfn_x(gfn) & ~((1UL << order) - 1));
+            continue;
+        }
+        else
+        {
+            order = 0;
+            rc = __p2m_set_entry(p2m, gfn, 0, mfn, t, a);
+            if ( rc )
+                break;
+        }
+
+        start += (1UL << order);
+        /* Check for continuation if it is not the last iteration */
+        if ( nr > start && !(start & mask) && hypercall_preempt_check() )
+        {
+            rc = start;
+            break;
+        }
+    }
+
+    p2m_write_unlock(p2m);
+
     if ( rc < 0 )
         return rc;
     else if ( rc > 0 )
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* [RFC 22/22] xen/arm: p2m: Do not handle shattering in p2m_create_table
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (20 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry Julien Grall
@ 2016-07-28 14:51 ` Julien Grall
  2016-09-06 18:59   ` Stefano Stabellini
  2016-07-28 17:46 ` [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Tamas K Lengyel
                   ` (2 subsequent siblings)
  24 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-28 14:51 UTC (permalink / raw)
  To: xen-devel; +Cc: proskurin, Julien Grall, sstabellini, steve.capper, wei.chen

The helper p2m_create_table is only called to create a brand new table.

Signed-off-by: Julien Grall <julien.grall@arm.com>
---
 xen/arch/arm/p2m.c | 51 ++++++---------------------------------------------
 1 file changed, 6 insertions(+), 45 deletions(-)

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 16ed393..4aaa96f 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -264,8 +264,7 @@ static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m, gfn_t gfn)
 #define GUEST_TABLE_SUPER_PAGE 1
 #define GUEST_TABLE_NORMAL_PAGE 2
 
-static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
-                            int level_shift);
+static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry);
 
 /*
  * Take the currently mapped table, find the corresponding GFN entry,
@@ -291,7 +290,7 @@ static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
         if ( read_only )
             return GUEST_TABLE_MAP_FAILED;
 
-        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
+        ret = p2m_create_table(p2m, entry);
         if ( ret )
             return GUEST_TABLE_MAP_FAILED;
     }
@@ -557,25 +556,14 @@ static inline void p2m_remove_pte(lpae_t *p, bool clean_pte)
     p2m_write_pte(p, pte, clean_pte);
 }
 
-/*
- * Allocate a new page table page and hook it in via the given entry.
- * apply_one_level relies on this returning 0 on success
- * and -ve on failure.
- *
- * If the existing entry is present then it must be a mapping and not
- * a table and it will be shattered into the next level down.
- *
- * level_shift is the number of bits at the level we want to create.
- */
-static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
-                            int level_shift)
+/* Allocate a new page table page and hook it in via the given entry. */
+static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry)
 {
     struct page_info *page;
     lpae_t *p;
     lpae_t pte;
-    int splitting = p2m_valid(*entry);
 
-    BUG_ON(p2m_table(*entry));
+    ASSERT(!p2m_valid(*entry));
 
     page = alloc_domheap_page(NULL, 0);
     if ( page == NULL )
@@ -584,35 +572,8 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
     page_list_add(page, &p2m->pages);
 
     p = __map_domain_page(page);
-    if ( splitting )
-    {
-        p2m_type_t t = entry->p2m.type;
-        mfn_t mfn = _mfn(entry->p2m.base);
-        int i;
 
-        /*
-         * We are either splitting a first level 1G page into 512 second level
-         * 2M pages, or a second level 2M page into 512 third level 4K pages.
-         */
-         for ( i=0 ; i < LPAE_ENTRIES; i++ )
-         {
-             pte = mfn_to_p2m_entry(mfn_add(mfn, i << (level_shift - LPAE_SHIFT)),
-                                    t, p2m->default_access);
-
-             /*
-              * First and second level super pages set p2m.table = 0, but
-              * third level entries set table = 1.
-              */
-             if ( level_shift - LPAE_SHIFT )
-                 pte.p2m.table = 0;
-
-             write_pte(&p[i], pte);
-         }
-
-         page->u.inuse.p2m_refcount = LPAE_ENTRIES;
-    }
-    else
-        clear_page(p);
+    clear_page(p);
 
     if ( p2m->clean_pte )
         clean_dcache_va_range(p, PAGE_SIZE);
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply related	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-07-28 14:51 ` [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry Julien Grall
@ 2016-07-28 15:04   ` Razvan Cojocaru
  2016-07-28 15:16     ` Julien Grall
  2016-08-01 15:40     ` Julien Grall
  2016-07-28 17:21   ` Tamas K Lengyel
  2016-09-06 19:06   ` Stefano Stabellini
  2 siblings, 2 replies; 98+ messages in thread
From: Razvan Cojocaru @ 2016-07-28 15:04 UTC (permalink / raw)
  To: Julien Grall, xen-devel
  Cc: proskurin, sstabellini, Tamas K Lengyel, steve.capper, wei.chen

On 07/28/2016 05:51 PM, Julien Grall wrote:
> The function p2m_set_mem_access can be re-implemented using the generic
> functions p2m_get_entry and __p2m_set_entry.
> 
> Note that because of the implementation of p2m_get_entry, a TLB
> invalidation instruction will be issued for each 4KB page. Therefore the
> performance of memaccess will be impacted, however the function is now
> safe on all the processors.
> 
> Also the function apply_p2m_changes is dropped completely as it is not
> unused anymore.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> Cc: Tamas K Lengyel <tamas@tklengyel.com>
> 
> ---
>     I have not ran any performance test with memaccess for now, but I
>     expect an important and unavoidable impact because of how memaccess
>     has been designed to workaround hardware limitation. Note that might
>     be possible to re-work memaccess work on superpage but this should
>     be done in a separate patch.
> ---
>  xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
>  1 file changed, 38 insertions(+), 291 deletions(-)

Thanks for the CC!

This seems to only impact ARM, are there any planned changes for x86
along these lines as well?


Thanks,
Razvan

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-07-28 15:04   ` Razvan Cojocaru
@ 2016-07-28 15:16     ` Julien Grall
  2016-08-01 15:40     ` Julien Grall
  1 sibling, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 15:16 UTC (permalink / raw)
  To: Razvan Cojocaru, xen-devel
  Cc: proskurin, sstabellini, Tamas K Lengyel, steve.capper, wei.chen



On 28/07/16 16:04, Razvan Cojocaru wrote:
> On 07/28/2016 05:51 PM, Julien Grall wrote:
>> The function p2m_set_mem_access can be re-implemented using the generic
>> functions p2m_get_entry and __p2m_set_entry.
>>
>> Note that because of the implementation of p2m_get_entry, a TLB
>> invalidation instruction will be issued for each 4KB page. Therefore the
>> performance of memaccess will be impacted, however the function is now
>> safe on all the processors.
>>
>> Also the function apply_p2m_changes is dropped completely as it is not
>> unused anymore.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>
>> ---
>>     I have not ran any performance test with memaccess for now, but I
>>     expect an important and unavoidable impact because of how memaccess
>>     has been designed to workaround hardware limitation. Note that might
>>     be possible to re-work memaccess work on superpage but this should
>>     be done in a separate patch.
>> ---
>>  xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
>>  1 file changed, 38 insertions(+), 291 deletions(-)
>
> Thanks for the CC!

Hi Razvan,

> This seems to only impact ARM, are there any planned changes for x86
> along these lines as well?

The break-before-make sequence is required by the ARM architecture. I 
don't know the x86 architecture, you can ask the x86 maintainers if this 
is something necessary.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-07-28 14:51 ` [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry Julien Grall
  2016-07-28 15:04   ` Razvan Cojocaru
@ 2016-07-28 17:21   ` Tamas K Lengyel
  2016-09-06 19:06   ` Stefano Stabellini
  2 siblings, 0 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-28 17:21 UTC (permalink / raw)
  To: Julien Grall
  Cc: Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com> wrote:
> The function p2m_set_mem_access can be re-implemented using the generic
> functions p2m_get_entry and __p2m_set_entry.
>
> Note that because of the implementation of p2m_get_entry, a TLB
> invalidation instruction will be issued for each 4KB page. Therefore the
> performance of memaccess will be impacted, however the function is now
> safe on all the processors.
>
> Also the function apply_p2m_changes is dropped completely as it is not
> unused anymore.

Typo, (not *used*).

[...]

> @@ -2069,6 +1780,7 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
>  {
>      struct p2m_domain *p2m = p2m_get_hostp2m(d);
>      p2m_access_t a;
> +    unsigned int order;
>      long rc = 0;
>
>      static const p2m_access_t memaccess[] = {
> @@ -2111,8 +1823,43 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
>          return 0;
>      }
>
> -    rc = apply_p2m_changes(d, MEMACCESS, gfn_add(gfn, start),
> -                           (nr - start), INVALID_MFN, mask, 0, a);
> +    p2m_write_lock(p2m);
> +
> +    for ( gfn = gfn_add(gfn, start); nr > start; gfn = gfn_add(gfn, 1UL << order) )

Long line here (84 width).

Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-28 14:51 ` [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry Julien Grall
@ 2016-07-28 17:29   ` Tamas K Lengyel
  2016-07-28 17:36     ` Tamas K Lengyel
  2016-07-28 17:51     ` Julien Grall
  2016-09-05 20:45   ` Stefano Stabellini
  1 sibling, 2 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-28 17:29 UTC (permalink / raw)
  To: Julien Grall
  Cc: Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com> wrote:
> __p2m_lookup is just a wrapper to p2m_get_entry.
>
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>
> ---
>     It might be possible to rework the memaccess code to take advantage
>     of all the parameters. I will defer this to the memaccess folks.

Could you elaborate on what you mean?

> ---
>  xen/arch/arm/p2m.c | 18 ++++--------------
>  1 file changed, 4 insertions(+), 14 deletions(-)
>
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 8676b9d..9a9c85c 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -398,24 +398,13 @@ out:
>      return mfn;
>  }
>
> -/*
> - * Lookup the MFN corresponding to a domain's GFN.
> - *
> - * There are no processor functions to do a stage 2 only lookup therefore we
> - * do a a software walk.
> - */
> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> -{
> -    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
> -}
> -
>  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>  {
>      mfn_t ret;
>      struct p2m_domain *p2m = &d->arch.p2m;
>
>      p2m_read_lock(p2m);
> -    ret = __p2m_lookup(d, gfn, t);
> +    ret = p2m_get_entry(p2m, gfn, t, NULL, NULL);
>      p2m_read_unlock(p2m);
>
>      return ret;
> @@ -679,7 +668,7 @@ static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
>           * No setting was found in the Radix tree. Check if the
>           * entry exists in the page-tables.
>           */
> -        mfn_t mfn = __p2m_lookup(d, gfn, NULL);
> +        mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
>
>          if ( mfn_eq(mfn, INVALID_MFN) )
>              return -ESRCH;
> @@ -1595,6 +1584,7 @@ p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag)
>      xenmem_access_t xma;
>      p2m_type_t t;
>      struct page_info *page = NULL;
> +    struct p2m_domain *p2m = &current->domain->arch.p2m;

I think this would be a good time to change this and pass p2m as an
input to p2m_mem_access_check_and_get_page. This would help with our
altp2m series as well.

Thanks,
Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-28 17:29   ` Tamas K Lengyel
@ 2016-07-28 17:36     ` Tamas K Lengyel
  2016-07-29 15:06       ` Julien Grall
  2016-07-28 17:51     ` Julien Grall
  1 sibling, 1 reply; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-28 17:36 UTC (permalink / raw)
  To: Julien Grall
  Cc: Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Thu, Jul 28, 2016 at 11:29 AM, Tamas K Lengyel <tamas@tklengyel.com> wrote:
> On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com> wrote:
>> __p2m_lookup is just a wrapper to p2m_get_entry.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>
>> ---
>>     It might be possible to rework the memaccess code to take advantage
>>     of all the parameters. I will defer this to the memaccess folks.
>
> Could you elaborate on what you mean?
>

Never mind, I see it. Yes, doing __p2m_get_mem_access and then
p2m_get_entry later duplicates work. I would suggest just replacing
__p2m_get_mem_access with a single call to p2m_get_entry to get both
the type and the mem_access setting on the page in a single run.

Thanks,
Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (21 preceding siblings ...)
  2016-07-28 14:51 ` [RFC 22/22] xen/arm: p2m: Do not handle shattering in p2m_create_table Julien Grall
@ 2016-07-28 17:46 ` Tamas K Lengyel
  2016-07-29 16:23   ` Julien Grall
  2016-08-15 10:24 ` Julien Grall
  2016-08-15 15:06 ` Edgar E. Iglesias
  24 siblings, 1 reply; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-28 17:46 UTC (permalink / raw)
  To: Julien Grall
  Cc: Edgar E. Iglesias, Stefano Stabellini, Razvan Cojocaru,
	Steve Capper, Sergej Proskurin, Xen-devel, Dirk Behme,
	Shanker Donthineni, wei.chen

Hi Julien,

> I sent this patch series as an RFC because there are still some TODOs
> in the code (mostly sanity check and possible optimization) and I have
> done limited testing. However, I think it is a good shape to start reviewing,
> get more feedback and have wider testing on different board.

I've tested this series on my Cubietruck but when I try to enable
xen-access on a domain I get the following errors:

~/xen/tools/tests/xen-access# ./xen-access 1 write
xenaccess init
max_gpfn = 48000
starting write 1
(XEN) traps.c:2569:d1v0 HSR=0x9000004f pc=0xc029eb10 gva=0xc0e013c0
gpa=0x00000040e013c0
Error -1 setting all memory to access type 5

The same thing works fine on the latest staging build, so this series
introduces some regression along the way.

Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-28 17:29   ` Tamas K Lengyel
  2016-07-28 17:36     ` Tamas K Lengyel
@ 2016-07-28 17:51     ` Julien Grall
  1 sibling, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-28 17:51 UTC (permalink / raw)
  To: Tamas K Lengyel
  Cc: Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, 2Xen-devel, wei.chen

Hello Tamas,

On 28/07/2016 18:29, Tamas K Lengyel wrote:
> On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com> wrote:

[...]

>> ---
>>  xen/arch/arm/p2m.c | 18 ++++--------------
>>  1 file changed, 4 insertions(+), 14 deletions(-)
>>
>> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
>> index 8676b9d..9a9c85c 100644
>> --- a/xen/arch/arm/p2m.c
>> +++ b/xen/arch/arm/p2m.c
>> @@ -398,24 +398,13 @@ out:
>>      return mfn;
>>  }
>>
>> -/*
>> - * Lookup the MFN corresponding to a domain's GFN.
>> - *
>> - * There are no processor functions to do a stage 2 only lookup therefore we
>> - * do a a software walk.
>> - */
>> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>> -{
>> -    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
>> -}
>> -
>>  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>>  {
>>      mfn_t ret;
>>      struct p2m_domain *p2m = &d->arch.p2m;
>>
>>      p2m_read_lock(p2m);
>> -    ret = __p2m_lookup(d, gfn, t);
>> +    ret = p2m_get_entry(p2m, gfn, t, NULL, NULL);
>>      p2m_read_unlock(p2m);
>>
>>      return ret;
>> @@ -679,7 +668,7 @@ static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
>>           * No setting was found in the Radix tree. Check if the
>>           * entry exists in the page-tables.
>>           */
>> -        mfn_t mfn = __p2m_lookup(d, gfn, NULL);
>> +        mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
>>
>>          if ( mfn_eq(mfn, INVALID_MFN) )
>>              return -ESRCH;
>> @@ -1595,6 +1584,7 @@ p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag)
>>      xenmem_access_t xma;
>>      p2m_type_t t;
>>      struct page_info *page = NULL;
>> +    struct p2m_domain *p2m = &current->domain->arch.p2m;
>
> I think this would be a good time to change this and pass p2m as an
> input to p2m_mem_access_check_and_get_page. This would help with our
> altp2m series as well.

This function can only work with the current p2m because of the call to 
gva_to_ipa. So I don't think it is a good idea to pass the p2m in parameter.

If you want to pass the p2m in parameter, you have to context switch 
properly all the registers (i.e VTTBR_EL2, TTBR{0,1}_EL1 and SCTLR_EL1).

Actually, this function is buggy if memaccess has changed the permission 
on memory holding the stage-1 page-table. Because we are using the 
hardware to translate the VA -> PA, the translate may fail due to memaccess.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-28 17:36     ` Tamas K Lengyel
@ 2016-07-29 15:06       ` Julien Grall
  2016-07-29 22:36         ` Tamas K Lengyel
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-29 15:06 UTC (permalink / raw)
  To: Tamas K Lengyel
  Cc: Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen



On 28/07/16 18:36, Tamas K Lengyel wrote:
> On Thu, Jul 28, 2016 at 11:29 AM, Tamas K Lengyel <tamas@tklengyel.com> wrote:
>> On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com> wrote:
>>> __p2m_lookup is just a wrapper to p2m_get_entry.
>>>
>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>>
>>> ---
>>>     It might be possible to rework the memaccess code to take advantage
>>>     of all the parameters. I will defer this to the memaccess folks.
>>
>> Could you elaborate on what you mean?
>>
>
> Never mind, I see it. Yes, doing __p2m_get_mem_access and then
> p2m_get_entry later duplicates work. I would suggest just replacing
> __p2m_get_mem_access with a single call to p2m_get_entry to get both
> the type and the mem_access setting on the page in a single run.

I am not planning to clean-up the memaccess code. Feel free to send a 
follow-up patch for that.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
  2016-07-28 17:46 ` [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Tamas K Lengyel
@ 2016-07-29 16:23   ` Julien Grall
  2016-07-29 19:05     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-07-29 16:23 UTC (permalink / raw)
  To: Tamas K Lengyel
  Cc: Edgar E. Iglesias, Stefano Stabellini, Razvan Cojocaru,
	Steve Capper, Sergej Proskurin, Xen-devel, Dirk Behme,
	Shanker Donthineni, wei.chen



On 28/07/16 18:46, Tamas K Lengyel wrote:
> Hi Julien,

Hello,

>> I sent this patch series as an RFC because there are still some TODOs
>> in the code (mostly sanity check and possible optimization) and I have
>> done limited testing. However, I think it is a good shape to start reviewing,
>> get more feedback and have wider testing on different board.
> 
> I've tested this series on my Cubietruck but when I try to enable
> xen-access on a domain I get the following errors:
> 
> ~/xen/tools/tests/xen-access# ./xen-access 1 write
> xenaccess init
> max_gpfn = 48000
> starting write 1
> (XEN) traps.c:2569:d1v0 HSR=0x9000004f pc=0xc029eb10 gva=0xc0e013c0
> gpa=0x00000040e013c0
> Error -1 setting all memory to access type 5
> 
> The same thing works fine on the latest staging build, so this series
> introduces some regression along the way.

Well, memaccess is not working on staging. As soon as it is enabled, the console
is flooded with:

traps.c:2503:d1v0 HSR=0x9200004f pc=0xffffff8008579b90 gva=0xffffffc0011bf000 gpa=0x000000411bf000

And the guest is crashing soon after because it received a data abort.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
  2016-07-29 16:23   ` Julien Grall
@ 2016-07-29 19:05     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-07-29 19:05 UTC (permalink / raw)
  To: Tamas K Lengyel
  Cc: Edgar E. Iglesias, Stefano Stabellini, Razvan Cojocaru,
	Steve Capper, Sergej Proskurin, Xen-devel, Dirk Behme,
	Shanker Donthineni, wei.chen



On 29/07/16 17:23, Julien Grall wrote:
>
>
> On 28/07/16 18:46, Tamas K Lengyel wrote:
>> Hi Julien,
>
> Hello,
>
>>> I sent this patch series as an RFC because there are still some TODOs
>>> in the code (mostly sanity check and possible optimization) and I have
>>> done limited testing. However, I think it is a good shape to start reviewing,
>>> get more feedback and have wider testing on different board.
>>
>> I've tested this series on my Cubietruck but when I try to enable
>> xen-access on a domain I get the following errors:
>>
>> ~/xen/tools/tests/xen-access# ./xen-access 1 write
>> xenaccess init
>> max_gpfn = 48000
>> starting write 1
>> (XEN) traps.c:2569:d1v0 HSR=0x9000004f pc=0xc029eb10 gva=0xc0e013c0
>> gpa=0x00000040e013c0
>> Error -1 setting all memory to access type 5
>>
>> The same thing works fine on the latest staging build, so this series
>> introduces some regression along the way.
>
> Well, memaccess is not working on staging. As soon as it is enabled, the console
> is flooded with:
>
> traps.c:2503:d1v0 HSR=0x9200004f pc=0xffffff8008579b90 gva=0xffffffc0011bf000 gpa=0x000000411bf000
>
> And the guest is crashing soon after because it received a data abort.

I found the problem and sent a patch for unstable (see [1]). I can hit 
easily on all the platform I tested. So I am not sure why you don't see 
the issue on the cubietruck.

I will update patch #18 to apply a similar fix in p2m_split_superpage.

Regards,

[1] 
https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg03133.html

>
> Regards,
>

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-29 15:06       ` Julien Grall
@ 2016-07-29 22:36         ` Tamas K Lengyel
  0 siblings, 0 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-29 22:36 UTC (permalink / raw)
  To: Julien Grall
  Cc: Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Fri, Jul 29, 2016 at 9:06 AM, Julien Grall <julien.grall@arm.com> wrote:
>
>
> On 28/07/16 18:36, Tamas K Lengyel wrote:
>>
>> On Thu, Jul 28, 2016 at 11:29 AM, Tamas K Lengyel <tamas@tklengyel.com>
>> wrote:
>>>
>>> On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com>
>>> wrote:
>>>>
>>>> __p2m_lookup is just a wrapper to p2m_get_entry.
>>>>
>>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>>>
>>>> ---
>>>>     It might be possible to rework the memaccess code to take advantage
>>>>     of all the parameters. I will defer this to the memaccess folks.
>>>
>>>
>>> Could you elaborate on what you mean?
>>>
>>
>> Never mind, I see it. Yes, doing __p2m_get_mem_access and then
>> p2m_get_entry later duplicates work. I would suggest just replacing
>> __p2m_get_mem_access with a single call to p2m_get_entry to get both
>> the type and the mem_access setting on the page in a single run.
>
>
> I am not planning to clean-up the memaccess code. Feel free to send a
> follow-up patch for that.
>

Since you are already touching this code you might as well but
whatever, up to you.

Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup
  2016-07-28 14:51 ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup Julien Grall
@ 2016-07-30 18:37   ` Tamas K Lengyel
  2016-08-31  0:30   ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo Stefano Stabellini
  1 sibling, 0 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-30 18:37 UTC (permalink / raw)
  To: Julien Grall
  Cc: Sergej Proskurin, Stefano Stabellini, Steve Capper, wei.chen, Xen-devel

On Thu, Jul 28, 2016 at 8:51 AM, Julien Grall <julien.grall@arm.com> wrote:
> Currently, for a given GFN, the function __p2m_lookup will only return
> the associated MFN and the p2m type of the mapping.
>
> In some case we need the order of the mapping and the memaccess
> permission. Rather than providing separate function for this purpose,
> it is better to implement a generic function to return all the
> information.
>
> To avoid passing dummy parameter, a caller that does need a specific
> information can use NULL instead.
>
> The list of the informations retrieved is based on the x86 version. All
> of them will be used in follow-up patches.
>
> It might have been possible to extend __p2m_lookup, however I choose to
> reimplement it from scratch to allow sharing some helpers with the
> function that will update the P2M (will be added in a follow-up patch).
>
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/p2m.c         | 188 ++++++++++++++++++++++++++++++++++-----------
>  xen/include/asm-arm/page.h |   4 +
>  2 files changed, 149 insertions(+), 43 deletions(-)
>
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index d4a4b62..8676b9d 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -36,6 +36,8 @@ static const paddr_t level_masks[] =
>      { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
>  static const unsigned int level_shifts[] =
>      { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
> +static const unsigned int level_orders[] =
> +    { ZEROETH_ORDER, FIRST_ORDER, SECOND_ORDER, THIRD_ORDER };
>
>  static bool_t p2m_valid(lpae_t pte)
>  {
> @@ -236,28 +238,99 @@ static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m,
>
>  /*
>   * Lookup the MFN corresponding to a domain's GFN.
> + * Lookup mem access in the ratrix tree.
> + * The entries associated to the GFN is considered valid.
> + */
> +static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m, gfn_t gfn)
> +{
> +    void *ptr;
> +
> +    if ( !p2m->mem_access_enabled )
> +        return p2m_access_rwx;
> +
> +    ptr = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> +    if ( !ptr )
> +        return p2m_access_rwx;
> +    else
> +        return radix_tree_ptr_to_int(ptr);
> +}

This looks fine from the mem_access side.

> +
> +#define GUEST_TABLE_MAP_FAILED 0
> +#define GUEST_TABLE_SUPER_PAGE 1
> +#define GUEST_TABLE_NORMAL_PAGE 2
> +
> +static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
> +                            int level_shift);
> +
> +/*
> + * Take the currently mapped table, find the corresponding GFN entry,
> + * and map the next table, if available.
>   *
> - * There are no processor functions to do a stage 2 only lookup therefore we
> - * do a a software walk.
> + * Return values:
> + *  GUEST_TABLE_MAP_FAILED: Either read_only was set and the entry
> + *  was empty, or allocating a new page failed.
> + *  GUEST_TABLE_NORMAL_PAGE: next level mapped normally
> + *  GUEST_TABLE_SUPER_PAGE: The next entry points to a superpage.
>   */
> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> +static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
> +                          lpae_t **table, unsigned int offset)
>  {
> -    struct p2m_domain *p2m = &d->arch.p2m;
> -    const paddr_t paddr = pfn_to_paddr(gfn_x(gfn));
> -    const unsigned int offsets[4] = {
> -        zeroeth_table_offset(paddr),
> -        first_table_offset(paddr),
> -        second_table_offset(paddr),
> -        third_table_offset(paddr)
> -    };
> -    const paddr_t masks[4] = {
> -        ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK
> -    };
> -    lpae_t pte, *map;
> +    lpae_t *entry;
> +    int ret;
> +    mfn_t mfn;
> +
> +    entry = *table + offset;
> +
> +    if ( !p2m_valid(*entry) )
> +    {
> +        if ( read_only )
> +            return GUEST_TABLE_MAP_FAILED;
> +
> +        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
> +        if ( ret )
> +            return GUEST_TABLE_MAP_FAILED;
> +    }
> +
> +    /* The function p2m_next_level is never called at the 3rd level */
> +    if ( p2m_mapping(*entry) )
> +        return GUEST_TABLE_SUPER_PAGE;
> +
> +    mfn = _mfn(entry->p2m.base);
> +
> +    unmap_domain_page(*table);
> +    *table = map_domain_page(mfn);
> +
> +    return GUEST_TABLE_NORMAL_PAGE;
> +}
> +
> +/*
> + * Get the details of a given gfn.
> + *
> + * If the entry is present, the associated MFN will be returned and the
> + * access and type filled up. The page_order will correspond to the
> + * order of the mapping in the page table (i.e it could be a superpage).
> + *
> + * If the entry is not present, INVALID_MFN will be returned and the
> + * page_order will be set according to the order of the invalid range.
> + */
> +static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
> +                           p2m_type_t *t, p2m_access_t *a,
> +                           unsigned int *page_order)

It is going to be necessary to make this function accessible outside
p2m.c to allow us to split mem_access components out of p2m. If you
could declare it as a non-static function and expose it in p2m.h that
would be great (x86 already has it accessible outside p2m.c).

Thanks,
Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-07-28 14:51 ` [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry Julien Grall
@ 2016-07-30 18:40   ` Tamas K Lengyel
  2016-08-15 10:22   ` Sergej Proskurin
  2016-09-06  1:08   ` Stefano Stabellini
  2 siblings, 0 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-07-30 18:40 UTC (permalink / raw)
  To: Julien Grall
  Cc: Sergej Proskurin, Stefano Stabellini, Steve Capper, wei.chen, Xen-devel

> +static int p2m_set_entry(struct p2m_domain *p2m,
> +                         gfn_t sgfn,
> +                         unsigned long todo,
> +                         mfn_t smfn,
> +                         p2m_type_t t,
> +                         p2m_access_t a)

It is going to be necessary to make this function accessible outside
p2m.c to allow us to split mem_access components out of p2m. If you
could declare it as a non-static function and expose it in p2m.h that
would be great (x86 already has it accessible outside p2m.c).

Thanks,
Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-07-28 15:04   ` Razvan Cojocaru
  2016-07-28 15:16     ` Julien Grall
@ 2016-08-01 15:40     ` Julien Grall
  2016-08-01 15:59       ` Tamas K Lengyel
  2016-08-01 16:34       ` Mark Rutland
  1 sibling, 2 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-01 15:40 UTC (permalink / raw)
  To: Razvan Cojocaru, xen-devel
  Cc: Mark Rutland, sstabellini, steve.capper, proskurin,
	Tamas K Lengyel, wei.chen

Hi Razvan,

On 28/07/16 16:04, Razvan Cojocaru wrote:
> On 07/28/2016 05:51 PM, Julien Grall wrote:
>> The function p2m_set_mem_access can be re-implemented using the generic
>> functions p2m_get_entry and __p2m_set_entry.
>>
>> Note that because of the implementation of p2m_get_entry, a TLB
>> invalidation instruction will be issued for each 4KB page. Therefore the
>> performance of memaccess will be impacted, however the function is now
>> safe on all the processors.
>>
>> Also the function apply_p2m_changes is dropped completely as it is not
>> unused anymore.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>
>> ---
>>     I have not ran any performance test with memaccess for now, but I
>>     expect an important and unavoidable impact because of how memaccess
>>     has been designed to workaround hardware limitation. Note that might
>>     be possible to re-work memaccess work on superpage but this should
>>     be done in a separate patch.
>> ---
>>  xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
>>  1 file changed, 38 insertions(+), 291 deletions(-)
>
> Thanks for the CC!
>
> This seems to only impact ARM, are there any planned changes for x86
> along these lines as well?

Actually, it might be possible to remove the TLB for each 4KB entry in 
the memaccess case.

After I read again multiple time the ARM ARM (D4-1732 in ARM DDI 
0487A.i) and spoke with various ARM folks, changing the permission (i.e 
read, write, execute) does not require the break-before-make sequence.

However, I noticed a latent bug in the memaccess code when the 
permission restriction are removed/changed.

In the current implementation (i.e without this series), the TLB 
invalidation is deferred until the p2m is released. Until that time, a 
vCPU may still run with the previous permission and trap into the 
hypervisor the permission fault. However, as the permission as changed, 
p2m_memaccess_check may fail and we will inject an abort to the guest.

The problem is very similar to [1]. This will still be true with this 
series applied if I relaxed the use of the break-before-make sequence. 
The two ways I can see to fix this are either try again the instruction 
(we would trap again if the permission was not correct) or keep the 
break-before-make.

The former will be cleaner given than stage-2 permission fault can only 
happen because of memaccess for now. Any opinions?

Regards,

[1] 
https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg03133.html

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 15:40     ` Julien Grall
@ 2016-08-01 15:59       ` Tamas K Lengyel
  2016-08-01 16:15         ` Julien Grall
  2016-08-01 16:34       ` Mark Rutland
  1 sibling, 1 reply; 98+ messages in thread
From: Tamas K Lengyel @ 2016-08-01 15:59 UTC (permalink / raw)
  To: Julien Grall
  Cc: Mark Rutland, Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Mon, Aug 1, 2016 at 9:40 AM, Julien Grall <julien.grall@arm.com> wrote:
> Hi Razvan,
>
> On 28/07/16 16:04, Razvan Cojocaru wrote:
>>
>> On 07/28/2016 05:51 PM, Julien Grall wrote:
>>>
>>> The function p2m_set_mem_access can be re-implemented using the generic
>>> functions p2m_get_entry and __p2m_set_entry.
>>>
>>> Note that because of the implementation of p2m_get_entry, a TLB
>>> invalidation instruction will be issued for each 4KB page. Therefore the
>>> performance of memaccess will be impacted, however the function is now
>>> safe on all the processors.
>>>
>>> Also the function apply_p2m_changes is dropped completely as it is not
>>> unused anymore.
>>>
>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>>
>>> ---
>>>     I have not ran any performance test with memaccess for now, but I
>>>     expect an important and unavoidable impact because of how memaccess
>>>     has been designed to workaround hardware limitation. Note that might
>>>     be possible to re-work memaccess work on superpage but this should
>>>     be done in a separate patch.
>>> ---
>>>  xen/arch/arm/p2m.c | 329
>>> +++++++----------------------------------------------
>>>  1 file changed, 38 insertions(+), 291 deletions(-)
>>
>>
>> Thanks for the CC!
>>
>> This seems to only impact ARM, are there any planned changes for x86
>> along these lines as well?
>
>
> Actually, it might be possible to remove the TLB for each 4KB entry in the
> memaccess case.
>
> After I read again multiple time the ARM ARM (D4-1732 in ARM DDI 0487A.i)
> and spoke with various ARM folks, changing the permission (i.e read, write,
> execute) does not require the break-before-make sequence.
>
> However, I noticed a latent bug in the memaccess code when the permission
> restriction are removed/changed.
>
> In the current implementation (i.e without this series), the TLB
> invalidation is deferred until the p2m is released. Until that time, a vCPU
> may still run with the previous permission and trap into the hypervisor the
> permission fault. However, as the permission as changed, p2m_memaccess_check
> may fail and we will inject an abort to the guest.
>
> The problem is very similar to [1]. This will still be true with this series
> applied if I relaxed the use of the break-before-make sequence. The two ways
> I can see to fix this are either try again the instruction (we would trap
> again if the permission was not correct) or keep the break-before-make.
>
> The former will be cleaner given than stage-2 permission fault can only
> happen because of memaccess for now. Any opinions?
>

IMHO we should just pause the domain while mem_access permissions are
being changed. On x86 it is not necessary as the mem_access
bookkeeping is kept in the ept pte entries but since on ARM the two
things are disjoint it is required. Even on x86 though - while it's
not strictly necessary - I think it would be a good idea to just pause
the domain as from a user perspective it would be more intuitive then
keeping the vCPUs running.

Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 15:59       ` Tamas K Lengyel
@ 2016-08-01 16:15         ` Julien Grall
  2016-08-01 16:27           ` Tamas K Lengyel
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-08-01 16:15 UTC (permalink / raw)
  To: Tamas K Lengyel
  Cc: Mark Rutland, Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen



On 01/08/16 16:59, Tamas K Lengyel wrote:
> On Mon, Aug 1, 2016 at 9:40 AM, Julien Grall <julien.grall@arm.com> wrote:
> IMHO we should just pause the domain while mem_access permissions are
> being changed. On x86 it is not necessary as the mem_access
> bookkeeping is kept in the ept pte entries but since on ARM the two
> things are disjoint it is required. Even on x86 though - while it's
> not strictly necessary - I think it would be a good idea to just pause
> the domain as from a user perspective it would be more intuitive then
> keeping the vCPUs running.

The problem is not because of the bookkeeping, but how the TLBs work. I 
never mentioned the radix tree in my previous mail because storing the 
access in the entry will not help here. The entry may have been updated 
but the TLBs not yet invalidated.

x86 does not seem to be affected because if the mem access check fail, 
the instruction is replayed (see hvm_hap_nested_page_fault). This is 
exactly the solution I suggested, which is IHMO the best.

I don't think pausing all the vCPUs of a domain is a good solution, the 
overhead will be too high for modifying only one page (see 
p2m_mem_access_check for instance).

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 16:15         ` Julien Grall
@ 2016-08-01 16:27           ` Tamas K Lengyel
  2016-08-01 16:33             ` Julien Grall
  2016-08-02  6:07             ` Razvan Cojocaru
  0 siblings, 2 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-08-01 16:27 UTC (permalink / raw)
  To: Julien Grall
  Cc: Mark Rutland, Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Mon, Aug 1, 2016 at 10:15 AM, Julien Grall <julien.grall@arm.com> wrote:
>
>
> On 01/08/16 16:59, Tamas K Lengyel wrote:
>>
>> On Mon, Aug 1, 2016 at 9:40 AM, Julien Grall <julien.grall@arm.com> wrote:
>> IMHO we should just pause the domain while mem_access permissions are
>> being changed. On x86 it is not necessary as the mem_access
>> bookkeeping is kept in the ept pte entries but since on ARM the two
>> things are disjoint it is required. Even on x86 though - while it's
>> not strictly necessary - I think it would be a good idea to just pause
>> the domain as from a user perspective it would be more intuitive then
>> keeping the vCPUs running.
>
>
> The problem is not because of the bookkeeping, but how the TLBs work. I
> never mentioned the radix tree in my previous mail because storing the
> access in the entry will not help here. The entry may have been updated but
> the TLBs not yet invalidated.
>
> x86 does not seem to be affected because if the mem access check fail, the
> instruction is replayed (see hvm_hap_nested_page_fault). This is exactly the
> solution I suggested, which is IHMO the best.

I see. Yes, indeed that sounds like the route to take here.

>
> I don't think pausing all the vCPUs of a domain is a good solution, the
> overhead will be too high for modifying only one page (see
> p2m_mem_access_check for instance).
>

IMHO the overhead of pausing the domain for setting mem_access
permissions is not critical as it is done only on occasion. We can
certainly leave the default behavior like this but I'll probably also
introduce a new XENMEM_access_op_set_access_sync op that will pause
the domain for the duration of the op.

Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 16:27           ` Tamas K Lengyel
@ 2016-08-01 16:33             ` Julien Grall
  2016-08-01 16:41               ` Tamas K Lengyel
  2016-08-02  6:07             ` Razvan Cojocaru
  1 sibling, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-08-01 16:33 UTC (permalink / raw)
  To: Tamas K Lengyel
  Cc: Mark Rutland, Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen



On 01/08/16 17:27, Tamas K Lengyel wrote:
> On Mon, Aug 1, 2016 at 10:15 AM, Julien Grall <julien.grall@arm.com> wrote:
>>
>>
>> On 01/08/16 16:59, Tamas K Lengyel wrote:
>>>
>>> On Mon, Aug 1, 2016 at 9:40 AM, Julien Grall <julien.grall@arm.com> wrote:
>>> IMHO we should just pause the domain while mem_access permissions are
>>> being changed. On x86 it is not necessary as the mem_access
>>> bookkeeping is kept in the ept pte entries but since on ARM the two
>>> things are disjoint it is required. Even on x86 though - while it's
>>> not strictly necessary - I think it would be a good idea to just pause
>>> the domain as from a user perspective it would be more intuitive then
>>> keeping the vCPUs running.
>>
>>
>> The problem is not because of the bookkeeping, but how the TLBs work. I
>> never mentioned the radix tree in my previous mail because storing the
>> access in the entry will not help here. The entry may have been updated but
>> the TLBs not yet invalidated.
>>
>> x86 does not seem to be affected because if the mem access check fail, the
>> instruction is replayed (see hvm_hap_nested_page_fault). This is exactly the
>> solution I suggested, which is IHMO the best.
>
> I see. Yes, indeed that sounds like the route to take here.

I am looking forward to see a patch.

>>
>> I don't think pausing all the vCPUs of a domain is a good solution, the
>> overhead will be too high for modifying only one page (see
>> p2m_mem_access_check for instance).
>>
>
> IMHO the overhead of pausing the domain for setting mem_access
> permissions is not critical as it is done only on occasion. We can
> certainly leave the default behavior like this but I'll probably also
> introduce a new XENMEM_access_op_set_access_sync op that will pause
> the domain for the duration of the op.

Oh, you meant for a user app to set the memaccess. I guess it is fine. I 
mentioned p2m_mem_access_check because this function is supposed to be 
called often in the data/instruction abort handlers, so pausing a domain 
here would be a big overhead.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 15:40     ` Julien Grall
  2016-08-01 15:59       ` Tamas K Lengyel
@ 2016-08-01 16:34       ` Mark Rutland
  2016-08-01 16:57         ` Julien Grall
  1 sibling, 1 reply; 98+ messages in thread
From: Mark Rutland @ 2016-08-01 16:34 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin, xen-devel,
	Tamas K Lengyel, wei.chen

Hi Julien, 

On Mon, Aug 01, 2016 at 04:40:50PM +0100, Julien Grall wrote:
> Hi Razvan,
> 
> On 28/07/16 16:04, Razvan Cojocaru wrote:
> >On 07/28/2016 05:51 PM, Julien Grall wrote:
> >>The function p2m_set_mem_access can be re-implemented using the generic
> >>functions p2m_get_entry and __p2m_set_entry.
> >>
> >>Note that because of the implementation of p2m_get_entry, a TLB
> >>invalidation instruction will be issued for each 4KB page. Therefore the
> >>performance of memaccess will be impacted, however the function is now
> >>safe on all the processors.
> >>
> >>Also the function apply_p2m_changes is dropped completely as it is not
> >>unused anymore.
> >>
> >>Signed-off-by: Julien Grall <julien.grall@arm.com>
> >>Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> >>Cc: Tamas K Lengyel <tamas@tklengyel.com>
> >>
> >>---
> >>    I have not ran any performance test with memaccess for now, but I
> >>    expect an important and unavoidable impact because of how memaccess
> >>    has been designed to workaround hardware limitation. Note that might
> >>    be possible to re-work memaccess work on superpage but this should
> >>    be done in a separate patch.
> >>---
> >> xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
> >> 1 file changed, 38 insertions(+), 291 deletions(-)
> >
> >Thanks for the CC!
> >
> >This seems to only impact ARM, are there any planned changes for x86
> >along these lines as well?
> 
> Actually, it might be possible to remove the TLB for each 4KB entry
> in the memaccess case.

Could you clarify what you mean by "remove the TLB"? Do you mean a TLBI
instruction?

> After I read again multiple time the ARM ARM (D4-1732 in ARM DDI
> 0487A.i) and spoke with various ARM folks, changing the permission
> (i.e read, write, execute) does not require the break-before-make
> sequence.

Regardless of whether Break-Before-Make (BBM) is required, TLB
invalidation is still required to ensure the new permissions have taken
effect after *any* modification to page tables, unless:

* The prior entry was not permitted to be held in a TLB, and:
* No TLB held an entry for the address range.

> However, I noticed a latent bug in the memaccess code when the
> permission restriction are removed/changed.
> 
> In the current implementation (i.e without this series), the TLB
> invalidation is deferred until the p2m is released. Until that time,
> a vCPU may still run with the previous permission and trap into the
> hypervisor the permission fault. However, as the permission as
> changed, p2m_memaccess_check may fail and we will inject an abort to
> the guest.
> 
> The problem is very similar to [1]. This will still be true with
> this series applied if I relaxed the use of the break-before-make
> sequence. The two ways I can see to fix this are either try again
> the instruction (we would trap again if the permission was not
> correct) or keep the break-before-make.

Why can you not have TLB invalidation without the full BBM sequence?

Thanks,
Mark.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 16:33             ` Julien Grall
@ 2016-08-01 16:41               ` Tamas K Lengyel
  0 siblings, 0 replies; 98+ messages in thread
From: Tamas K Lengyel @ 2016-08-01 16:41 UTC (permalink / raw)
  To: Julien Grall
  Cc: Mark Rutland, Stefano Stabellini, Razvan Cojocaru, Steve Capper,
	Sergej Proskurin, Xen-devel, wei.chen

On Mon, Aug 1, 2016 at 10:33 AM, Julien Grall <julien.grall@arm.com> wrote:
>
>
> On 01/08/16 17:27, Tamas K Lengyel wrote:
>>
>> On Mon, Aug 1, 2016 at 10:15 AM, Julien Grall <julien.grall@arm.com>
>> wrote:
>>>
>>>
>>>
>>> On 01/08/16 16:59, Tamas K Lengyel wrote:
>>>>
>>>>
>>>> On Mon, Aug 1, 2016 at 9:40 AM, Julien Grall <julien.grall@arm.com>
>>>> wrote:
>>>> IMHO we should just pause the domain while mem_access permissions are
>>>> being changed. On x86 it is not necessary as the mem_access
>>>> bookkeeping is kept in the ept pte entries but since on ARM the two
>>>> things are disjoint it is required. Even on x86 though - while it's
>>>> not strictly necessary - I think it would be a good idea to just pause
>>>> the domain as from a user perspective it would be more intuitive then
>>>> keeping the vCPUs running.
>>>
>>>
>>>
>>> The problem is not because of the bookkeeping, but how the TLBs work. I
>>> never mentioned the radix tree in my previous mail because storing the
>>> access in the entry will not help here. The entry may have been updated
>>> but
>>> the TLBs not yet invalidated.
>>>
>>> x86 does not seem to be affected because if the mem access check fail,
>>> the
>>> instruction is replayed (see hvm_hap_nested_page_fault). This is exactly
>>> the
>>> solution I suggested, which is IHMO the best.
>>
>>
>> I see. Yes, indeed that sounds like the route to take here.
>
>
> I am looking forward to see a patch.
>
>>>
>>> I don't think pausing all the vCPUs of a domain is a good solution, the
>>> overhead will be too high for modifying only one page (see
>>> p2m_mem_access_check for instance).
>>>
>>
>> IMHO the overhead of pausing the domain for setting mem_access
>> permissions is not critical as it is done only on occasion. We can
>> certainly leave the default behavior like this but I'll probably also
>> introduce a new XENMEM_access_op_set_access_sync op that will pause
>> the domain for the duration of the op.
>
>
> Oh, you meant for a user app to set the memaccess. I guess it is fine. I
> mentioned p2m_mem_access_check because this function is supposed to be
> called often in the data/instruction abort handlers, so pausing a domain
> here would be a big overhead.

Right, that would be in most cases. We have a couple mem_access types
though that automatically change to a different one on the first
violation (rx2rw, n2rwx) which could also benefit from the domain
being paused for the duration of the change. All the other permissions
are fine as they are and should only require a domain pause when the
user is setting them.

Tamas

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 16:34       ` Mark Rutland
@ 2016-08-01 16:57         ` Julien Grall
  2016-08-01 17:26           ` Mark Rutland
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-08-01 16:57 UTC (permalink / raw)
  To: Mark Rutland
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin, xen-devel,
	Tamas K Lengyel, wei.chen



On 01/08/16 17:34, Mark Rutland wrote:
> Hi Julien,

Hi Mark,

> On Mon, Aug 01, 2016 at 04:40:50PM +0100, Julien Grall wrote:
>> Hi Razvan,
>>
>> On 28/07/16 16:04, Razvan Cojocaru wrote:
>>> On 07/28/2016 05:51 PM, Julien Grall wrote:
>>>> The function p2m_set_mem_access can be re-implemented using the generic
>>>> functions p2m_get_entry and __p2m_set_entry.
>>>>
>>>> Note that because of the implementation of p2m_get_entry, a TLB
>>>> invalidation instruction will be issued for each 4KB page. Therefore the
>>>> performance of memaccess will be impacted, however the function is now
>>>> safe on all the processors.
>>>>
>>>> Also the function apply_p2m_changes is dropped completely as it is not
>>>> unused anymore.
>>>>
>>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>>>
>>>> ---
>>>>    I have not ran any performance test with memaccess for now, but I
>>>>    expect an important and unavoidable impact because of how memaccess
>>>>    has been designed to workaround hardware limitation. Note that might
>>>>    be possible to re-work memaccess work on superpage but this should
>>>>    be done in a separate patch.
>>>> ---
>>>> xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
>>>> 1 file changed, 38 insertions(+), 291 deletions(-)
>>>
>>> Thanks for the CC!
>>>
>>> This seems to only impact ARM, are there any planned changes for x86
>>> along these lines as well?
>>
>> Actually, it might be possible to remove the TLB for each 4KB entry
>> in the memaccess case.
>
> Could you clarify what you mean by "remove the TLB"? Do you mean a TLBI
> instruction?

I meant TLBI instruction sorry for the confusion.

>> After I read again multiple time the ARM ARM (D4-1732 in ARM DDI
>> 0487A.i) and spoke with various ARM folks, changing the permission
>> (i.e read, write, execute) does not require the break-before-make
>> sequence.
>
> Regardless of whether Break-Before-Make (BBM) is required, TLB
> invalidation is still required to ensure the new permissions have taken
> effect after *any* modification to page tables, unless:
>
> * The prior entry was not permitted to be held in a TLB, and:
> * No TLB held an entry for the address range.

Agreed, however we only need one TLBI instruction (assuming there is no 
superpage shattering) per-batch rather than one per-entry in this case.

>
>> However, I noticed a latent bug in the memaccess code when the
>> permission restriction are removed/changed.
>>
>> In the current implementation (i.e without this series), the TLB
>> invalidation is deferred until the p2m is released. Until that time,
>> a vCPU may still run with the previous permission and trap into the
>> hypervisor the permission fault. However, as the permission as
>> changed, p2m_memaccess_check may fail and we will inject an abort to
>> the guest.
>>
>> The problem is very similar to [1]. This will still be true with
>> this series applied if I relaxed the use of the break-before-make
>> sequence. The two ways I can see to fix this are either try again
>> the instruction (we would trap again if the permission was not
>> correct) or keep the break-before-make.
>
> Why can you not have TLB invalidation without the full BBM sequence?

I agree that in general TLBI instruction does not require the full BBM 
sequence. If we ever need the TLB invalidation per entry, it will be 
better to keep BBM to keep the code streamlined.

However, in this case I think it will be better to return to the guest 
and replay the instruction if a data/instruction abort has been received.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 16:57         ` Julien Grall
@ 2016-08-01 17:26           ` Mark Rutland
  2016-08-01 18:22             ` Mark Rutland
  0 siblings, 1 reply; 98+ messages in thread
From: Mark Rutland @ 2016-08-01 17:26 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, marc.zyngier,
	proskurin, xen-devel, Tamas K Lengyel, wei.chen

On Mon, Aug 01, 2016 at 05:57:50PM +0100, Julien Grall wrote:
> On 01/08/16 17:34, Mark Rutland wrote:
> >On Mon, Aug 01, 2016 at 04:40:50PM +0100, Julien Grall wrote:
> >>After I read again multiple time the ARM ARM (D4-1732 in ARM DDI
> >>0487A.i) and spoke with various ARM folks, changing the permission
> >>(i.e read, write, execute) does not require the break-before-make
> >>sequence.
> >
> >Regardless of whether Break-Before-Make (BBM) is required, TLB
> >invalidation is still required to ensure the new permissions have taken
> >effect after *any* modification to page tables, unless:
> >
> >* The prior entry was not permitted to be held in a TLB, and:
> >* No TLB held an entry for the address range.
> 
> Agreed, however we only need one TLBI instruction (assuming there is
> no superpage shattering) per-batch rather than one per-entry in this
> case.

I got Cc'd to a reply without the original patch context, so I'm not
sure what the case is here. I'm not exactly sure what you mean by
"per-batch".

Assuming that you've (only) changed the permissions (i.e. the AP bits
and XN bits) of a number of stage-2 leaf entries, you need either:

* A single TLBI VMALLE1IS

  Note that this removes *all* stage-2 or combined stage-1&2 entries
  from TLBs, and thus is stronger than necessary.

* Per entry, a TLBI IPAS2LE1IS

  e.g. 

    for_each_entry(x)
      modify_ap_bits(x);
    dsb(ishst);
    tlbi(ipas2le1is);
    dsb(ish);

> >>In the current implementation (i.e without this series), the TLB
> >>invalidation is deferred until the p2m is released. Until that time,
> >>a vCPU may still run with the previous permission and trap into the
> >>hypervisor the permission fault. However, as the permission as
> >>changed, p2m_memaccess_check may fail and we will inject an abort to
> >>the guest.
> >>
> >>The problem is very similar to [1]. This will still be true with
> >>this series applied if I relaxed the use of the break-before-make
> >>sequence. The two ways I can see to fix this are either try again
> >>the instruction (we would trap again if the permission was not
> >>correct) or keep the break-before-make.
> >
> >Why can you not have TLB invalidation without the full BBM sequence?
> 
> I agree that in general TLBI instruction does not require the full
> BBM sequence. If we ever need the TLB invalidation per entry, it
> will be better to keep BBM to keep the code streamlined.

If this is not performance-critical, this sounds fine.

This does unnecessarily penalise some cases, though.

e.g. a guest only performing reads if write permission is removed from
pages. 

> However, in this case I think it will be better to return to the
> guest and replay the instruction if a data/instruction abort has
> been received.

That sounds like what we do in Linux.

On a fault, if the page tables are such that the fault should not occur,
we assume we raced with concurrent modification, and return to the
faulting instruction. Eventually (once the TLB maintenance is
completed), the guest will make forward progress.

We have locking around page table manipulation, but only have to take
them in the (hopefully) unlikely case of a race on the affected memory
area.

Thanks,
Mark.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 17:26           ` Mark Rutland
@ 2016-08-01 18:22             ` Mark Rutland
  2016-08-02  9:58               ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Mark Rutland @ 2016-08-01 18:22 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, marc.zyngier,
	proskurin, xen-devel, Tamas K Lengyel, wei.chen

Hi,

I realised I made a confusing mistake in my last reply; clarification below.

On Mon, Aug 01, 2016 at 06:26:54PM +0100, Mark Rutland wrote:
> On Mon, Aug 01, 2016 at 05:57:50PM +0100, Julien Grall wrote:
> > however we only need one TLBI instruction (assuming there is
> > no superpage shattering) per-batch rather than one per-entry in this
> > case.
> 
> I got Cc'd to a reply without the original patch context, so I'm not
> sure what the case is here. I'm not exactly sure what you mean by
> "per-batch".
> 
> Assuming that you've (only) changed the permissions (i.e. the AP bits
> and XN bits) of a number of stage-2 leaf entries, you need either:

[...]

> * Per entry, a TLBI IPAS2LE1IS
> 
>   e.g. 
> 
>     for_each_entry(x)
>       modify_ap_bits(x);
>     dsb(ishst);
>     tlbi(ipas2le1is);
>     dsb(ish);

Here I was trying to have the bare minimum barriers necessary, but in focussing
on that I failed to add the required loop to have a TLBI per entry.

The above should have been:

  for_each_entry(x)
    modify_ap_bits(x);
  dsb(ishst);
  for_each_entry(x)
    tlbi(ipas2le1is, x);
  dsb(ish);

Assuming the necessary bit fiddling for the TLBI to affect the IPA of x, with
the right VMID, etc.

Thanks,
Mark.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 16:27           ` Tamas K Lengyel
  2016-08-01 16:33             ` Julien Grall
@ 2016-08-02  6:07             ` Razvan Cojocaru
  1 sibling, 0 replies; 98+ messages in thread
From: Razvan Cojocaru @ 2016-08-02  6:07 UTC (permalink / raw)
  To: Tamas K Lengyel, Julien Grall
  Cc: Mark Rutland, Stefano Stabellini, Steve Capper, Sergej Proskurin,
	Xen-devel, wei.chen

On 08/01/16 19:27, Tamas K Lengyel wrote:
> On Mon, Aug 1, 2016 at 10:15 AM, Julien Grall <julien.grall@arm.com> wrote:
>>
>>
>> On 01/08/16 16:59, Tamas K Lengyel wrote:
>>>
>>> On Mon, Aug 1, 2016 at 9:40 AM, Julien Grall <julien.grall@arm.com> wrote:
>>> IMHO we should just pause the domain while mem_access permissions are
>>> being changed. On x86 it is not necessary as the mem_access
>>> bookkeeping is kept in the ept pte entries but since on ARM the two
>>> things are disjoint it is required. Even on x86 though - while it's
>>> not strictly necessary - I think it would be a good idea to just pause
>>> the domain as from a user perspective it would be more intuitive then
>>> keeping the vCPUs running.
>>
>>
>> The problem is not because of the bookkeeping, but how the TLBs work. I
>> never mentioned the radix tree in my previous mail because storing the
>> access in the entry will not help here. The entry may have been updated but
>> the TLBs not yet invalidated.
>>
>> x86 does not seem to be affected because if the mem access check fail, the
>> instruction is replayed (see hvm_hap_nested_page_fault). This is exactly the
>> solution I suggested, which is IHMO the best.
> 
> I see. Yes, indeed that sounds like the route to take here.
> 
>>
>> I don't think pausing all the vCPUs of a domain is a good solution, the
>> overhead will be too high for modifying only one page (see
>> p2m_mem_access_check for instance).
>>
> 
> IMHO the overhead of pausing the domain for setting mem_access
> permissions is not critical as it is done only on occasion. We can
> certainly leave the default behavior like this but I'll probably also
> introduce a new XENMEM_access_op_set_access_sync op that will pause
> the domain for the duration of the op.

Well, not really - we do set / remove access restrictions dynamically,
while the guest is running, according to context. It's not the
application's main business to just set access permissions, so in that
respect I suppose we could get away with pausing the whole domain, but I
bet the impact would be measurable with tools.

Adding a new XENMEM_access_op is fine with me.


Thanks,
Razvan


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-01 18:22             ` Mark Rutland
@ 2016-08-02  9:58               ` Julien Grall
  2016-08-02 10:26                 ` Mark Rutland
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-08-02  9:58 UTC (permalink / raw)
  To: Mark Rutland
  Cc: sstabellini, Razvan Cojocaru, steve.capper, marc.zyngier,
	proskurin, xen-devel, Tamas K Lengyel, wei.chen



On 01/08/2016 19:22, Mark Rutland wrote:
> Hi,

Hi Mark,

> I realised I made a confusing mistake in my last reply; clarification below.
>
> On Mon, Aug 01, 2016 at 06:26:54PM +0100, Mark Rutland wrote:
>> On Mon, Aug 01, 2016 at 05:57:50PM +0100, Julien Grall wrote:
>>> however we only need one TLBI instruction (assuming there is
>>> no superpage shattering) per-batch rather than one per-entry in this
>>> case.
>>
>> I got Cc'd to a reply without the original patch context, so I'm not
>> sure what the case is here. I'm not exactly sure what you mean by
>> "per-batch".

Sorry for that. I CCed in case I did not summarize correctly the 
conversation we had.

The page table handling code can be found in patch #18 [1].

>>
>> Assuming that you've (only) changed the permissions (i.e. the AP bits
>> and XN bits) of a number of stage-2 leaf entries, you need either:
>
> [...]
>
>> * Per entry, a TLBI IPAS2LE1IS
>>
>>   e.g.
>>
>>     for_each_entry(x)
>>       modify_ap_bits(x);
>>     dsb(ishst);
>>     tlbi(ipas2le1is);
>>     dsb(ish);
>
> Here I was trying to have the bare minimum barriers necessary, but in focussing
> on that I failed to add the required loop to have a TLBI per entry.
>
> The above should have been:
>
>   for_each_entry(x)
>     modify_ap_bits(x);
>   dsb(ishst);
>   for_each_entry(x)
>     tlbi(ipas2le1is, x);
>   dsb(ish);

I have a question related to this example. Is there a threshold where 
invalidate all the TLB entry for a given VMID/ASID is worth?

>
> Assuming the necessary bit fiddling for the TLBI to affect the IPA of x, with
> the right VMID, etc.


Regards,


[1] https://lists.xen.org/archives/html/xen-devel/2016-07/msg02966.html

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-08-02  9:58               ` Julien Grall
@ 2016-08-02 10:26                 ` Mark Rutland
  0 siblings, 0 replies; 98+ messages in thread
From: Mark Rutland @ 2016-08-02 10:26 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, marc.zyngier,
	proskurin, xen-devel, Tamas K Lengyel, wei.chen

On Tue, Aug 02, 2016 at 10:58:00AM +0100, Julien Grall wrote:
> On 01/08/2016 19:22, Mark Rutland wrote:
> >On Mon, Aug 01, 2016 at 06:26:54PM +0100, Mark Rutland wrote:
> >>On Mon, Aug 01, 2016 at 05:57:50PM +0100, Julien Grall wrote:
> >>>however we only need one TLBI instruction (assuming there is
> >>>no superpage shattering) per-batch rather than one per-entry in this
> >>>case.
> >>
> >>I got Cc'd to a reply without the original patch context, so I'm not
> >>sure what the case is here. I'm not exactly sure what you mean by
> >>"per-batch".
> 
> Sorry for that. I CCed in case I did not summarize correctly the
> conversation we had.
> 
> The page table handling code can be found in patch #18 [1].

If I've understood, you're asking if you can do a TLBI VMALLE1IS per
batch of leaf entry updates in p2m_set_entry?

As below, if only the AP and/or XN bits are changing, that should be
safe. If any other fields are being altered (inc. the output address,
even for intermediate entries), that may not be safe.

> >>Assuming that you've (only) changed the permissions (i.e. the AP bits
> >>and XN bits) of a number of stage-2 leaf entries, you need either:

> >>* Per entry, a TLBI IPAS2LE1IS
> >>
> >>  e.g.

> >  for_each_entry(x)
> >    modify_ap_bits(x);
> >  dsb(ishst);
> >  for_each_entry(x)
> >    tlbi(ipas2le1is, x);
> >  dsb(ish);
> 
> I have a question related to this example. Is there a threshold
> where invalidate all the TLB entry for a given VMID/ASID is worth?

Strictly speaking, "yes", but the value is going to depend on
implementation and workload, so there's no "good" value as such provided
by the architecture.

In Linux, we end up doing so in some cases to avoid softlockups. Look
for MAX_TLB_RANGE in arch/arm64/include/asm/tlbflush.h. There are some
more details in commit 05ac65305437e8ef ("arm64: fix soft lockup due to
large tlb flush range").

Thanks,
Mark.

> [1] https://lists.xen.org/archives/html/xen-devel/2016-07/msg02966.html

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-07-28 14:51 ` [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry Julien Grall
  2016-07-30 18:40   ` Tamas K Lengyel
@ 2016-08-15 10:22   ` Sergej Proskurin
  2016-09-06  1:08   ` Stefano Stabellini
  2 siblings, 0 replies; 98+ messages in thread
From: Sergej Proskurin @ 2016-08-15 10:22 UTC (permalink / raw)
  To: Julien Grall, xen-devel; +Cc: sstabellini, steve.capper, wei.chen

Hi Julien,

[...]

> +static int p2m_set_entry(struct p2m_domain *p2m,
> +                         gfn_t sgfn,
> +                         unsigned long todo,
> +                         mfn_t smfn,
> +                         p2m_type_t t,
> +                         p2m_access_t a)
> +{
> +    int rc = 0;
> +
> +    while ( todo )
> +    {
> +        unsigned long mask = gfn_x(sgfn) | mfn_x(smfn) | todo;
> +        unsigned long order;
> +
> +        /* Always map 4k by 4k when memaccess is enabled */
> +        if ( unlikely(p2m->mem_access_enabled) )
> +            order = THIRD_ORDER;
> +        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
> +            order = FIRST_ORDER;

The 2nd outer if statement does not consider p2m->mem_access_enabled.
That is, even if p2m->mem_access_enabled is set, the 2nd if statement
would set an order, which is potentially not equal to the THIRD_ORDER
(please use the THIRD_ORDER define in the ASSERT -- see ASSERT beyond).

As far as I understand, p2m->mem_access_enabled should prevent mapping
of superpages. With this implementation, however, this would not be the
case. Even worse: the ASSERT in __p2m_set_entry would crash the system,
if p2m->mem_access_enabled was set and order was not equal to the
THIRD_ORDER:

+    /*
+     * The radix-tree can only work on 4KB. This is only used when
+     * memaccess is enabled.
+     */
+    ASSERT(!p2m->mem_access_enabled || page_order == 0);

This can be fixed, by simply unifying both outer if statements:

+        /* Always map 4k by 4k when memaccess is enabled */
+        if ( unlikely(p2m->mem_access_enabled) )
+            order = THIRD_ORDER;
+        else if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
+            order = FIRST_ORDER;
+        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
+            order = SECOND_ORDER;
+        else
+            order = THIRD_ORDER;


> +        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
> +            order = SECOND_ORDER;
> +        else
> +            order = THIRD_ORDER;
> +
> +        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
> +        if ( rc )
> +            break;
> +
> +        sgfn = gfn_add(sgfn, (1 << order));
> +        if ( !mfn_eq(smfn, INVALID_MFN) )
> +           smfn = mfn_add(smfn, (1 << order));
> +
> +        todo -= (1 << order);
> +    }
> +
> +    return rc;
> +}
> +#endif
> +
>  /*
>   * Returns true if start_gpaddr..end_gpaddr contains at least one
>   * suitably aligned level_size mappping of maddr.
> 

Best regards,
~Sergej

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (22 preceding siblings ...)
  2016-07-28 17:46 ` [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Tamas K Lengyel
@ 2016-08-15 10:24 ` Julien Grall
  2016-08-15 15:06 ` Edgar E. Iglesias
  24 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-15 10:24 UTC (permalink / raw)
  To: sstabellini
  Cc: Edgar E. Iglesias, Tamas K Lengyel, Razvan Cojocaru,
	steve.capper, proskurin, xen-devel, Dirk Behme,
	Shanker Donthineni, wei.chen

Hi Stefano,

Do you have any comments on this series?

Cheers,

On 28/07/2016 16:51, Julien Grall wrote:
> Hello all,
>
> The ARM architecture mandates the use of a break-before-make sequence when
> changing translation entries if the page table is shared between multiple
> CPUs whenever a valid entry is replaced by another valid entry (see D4.7.1
> in ARM DDI 0487A.j for more details).
>
> The current P2M code does not respect this sequence and may result to
> break coherency on some processors.
>
> Adapting the current implementation to use break-before-make sequence would
> imply some code duplication and more TLBs invalidations than necessary.
> For instance, if we are replacing a 4KB page and the current mapping in
> the P2M is using a 1GB superpage, the following steps will happen:
>     1) Shatter the 1GB superpage into a series of 2MB superpages
>     2) Shatter the 2MB superpage into a series of 4KB superpages
>     3) Replace the 4KB page
>
> As the current implementation is shattering while descending and install
> the mapping before continuing to the next level, Xen would need to issue 3
> TLB invalidation instructions which is clearly inefficient.
>
> Furthermore, all the operations which modify the page table are using the
> same skeleton. It is more complicated to maintain different code paths than
> having a generic function that set an entry and take care of the break-before-
> make sequence.
>
> The new implementation is based on the x86 EPT one which, I think, fits
> quite well for the break-before-make sequence whilst keeping the code
> simple.
>
> I sent this patch series as an RFC because there are still some TODOs
> in the code (mostly sanity check and possible optimization) and I have
> done limited testing. However, I think it is a good shape to start reviewing,
> get more feedback and have wider testing on different board.
>
> Also, I need to figure out the impact on ARM32 because the domheap is not
> always mapped.
>
> This series has dependencies on some rework sent separately ([1] and [2]).
> I have provided a branch with all the dependencies and this series applied:
>
> git://xenbits.xen.org/people/julieng/xen-unstable.git branch p2m-rfc
>
> Comments are welcome.
>
> Yours sincerely,
>
> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> Cc: Tamas K Lengyel <tamas@tklengyel.com>
> Cc: Shanker Donthineni <shankerd@codeaurora.org>
> Cc: Dirk Behme <dirk.behme@de.bosch.com>
> Cc: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
>
> [1] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02936.html
> [2] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02830.html
>
> Julien Grall (22):
>   xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of
>     the switch
>   xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
>   xen/arm: p2m: Rename parameter in p2m_{remove,write}_pte...
>   xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set
>   xen/arm: traps: Move MMIO emulation code in a separate helper
>   xen/arm: traps: Check the P2M before injecting a data/instruction
>     abort
>   xen/arm: p2m: Rework p2m_put_l3_page
>   xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m
>   xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned
>     int
>   xen/arm: p2m: Move the lookup helpers at the top of the file
>   xen/arm: p2m: Introduce p2m_get_root_pointer and use it in
>     __p2m_lookup
>   xen/arm: p2m: Introduce p2m_get_entry and use it to implement
>     __p2m_lookup
>   xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
>   xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
>   xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
>   xen/arm: p2m: Make p2m_{valid,table,mapping} helpers inline
>   xen/arm: p2m: Introduce a helper to check if an entry is a superpage
>   xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
>   xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry
>   xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
>   xen/arm: p2m: Re-implement p2m_set_mem_access using
>     p2m_{set,get}_entry
>   xen/arm: p2m: Do not handle shattering in p2m_create_table
>
>  xen/arch/arm/domain.c      |    8 +-
>  xen/arch/arm/p2m.c         | 1274 ++++++++++++++++++++++----------------------
>  xen/arch/arm/traps.c       |  126 +++--
>  xen/include/asm-arm/p2m.h  |   14 +
>  xen/include/asm-arm/page.h |    4 +
>  5 files changed, 742 insertions(+), 684 deletions(-)
>

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
  2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
                   ` (23 preceding siblings ...)
  2016-08-15 10:24 ` Julien Grall
@ 2016-08-15 15:06 ` Edgar E. Iglesias
  2016-08-17  2:28   ` Shanker Donthineni
  24 siblings, 1 reply; 98+ messages in thread
From: Edgar E. Iglesias @ 2016-08-15 15:06 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin, xen-devel,
	Dirk Behme, Tamas K Lengyel, Shanker Donthineni, wei.chen

On Thu, Jul 28, 2016 at 03:51:23PM +0100, Julien Grall wrote:
> Hello all,
> 
> The ARM architecture mandates the use of a break-before-make sequence when
> changing translation entries if the page table is shared between multiple
> CPUs whenever a valid entry is replaced by another valid entry (see D4.7.1
> in ARM DDI 0487A.j for more details).
> 
> The current P2M code does not respect this sequence and may result to
> break coherency on some processors.
> 
> Adapting the current implementation to use break-before-make sequence would
> imply some code duplication and more TLBs invalidations than necessary.
> For instance, if we are replacing a 4KB page and the current mapping in
> the P2M is using a 1GB superpage, the following steps will happen:
>     1) Shatter the 1GB superpage into a series of 2MB superpages
>     2) Shatter the 2MB superpage into a series of 4KB superpages
>     3) Replace the 4KB page
> 
> As the current implementation is shattering while descending and install
> the mapping before continuing to the next level, Xen would need to issue 3
> TLB invalidation instructions which is clearly inefficient.
> 
> Furthermore, all the operations which modify the page table are using the
> same skeleton. It is more complicated to maintain different code paths than
> having a generic function that set an entry and take care of the break-before-
> make sequence.
> 
> The new implementation is based on the x86 EPT one which, I think, fits
> quite well for the break-before-make sequence whilst keeping the code
> simple.
> 
> I sent this patch series as an RFC because there are still some TODOs
> in the code (mostly sanity check and possible optimization) and I have
> done limited testing. However, I think it is a good shape to start reviewing,
> get more feedback and have wider testing on different board.
> 
> Also, I need to figure out the impact on ARM32 because the domheap is not
> always mapped.
> 
> This series has dependencies on some rework sent separately ([1] and [2]).
> I have provided a branch with all the dependencies and this series applied:
> 
> git://xenbits.xen.org/people/julieng/xen-unstable.git branch p2m-rfc


Hi Julien,

FWIW, I gave this a spin on the ZynqMP and it seems to be working fine.
I tried dom0 and starting a few additional guests. All looks good.

Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>

Cheers,
Edgar


> 
> Comments are welcome.
> 
> Yours sincerely,
> 
> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> Cc: Tamas K Lengyel <tamas@tklengyel.com>
> Cc: Shanker Donthineni <shankerd@codeaurora.org>
> Cc: Dirk Behme <dirk.behme@de.bosch.com>
> Cc: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> 
> [1] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02936.html
> [2] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02830.html
> 
> Julien Grall (22):
>   xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of
>     the switch
>   xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
>   xen/arm: p2m: Rename parameter in p2m_{remove,write}_pte...
>   xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set
>   xen/arm: traps: Move MMIO emulation code in a separate helper
>   xen/arm: traps: Check the P2M before injecting a data/instruction
>     abort
>   xen/arm: p2m: Rework p2m_put_l3_page
>   xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m
>   xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned
>     int
>   xen/arm: p2m: Move the lookup helpers at the top of the file
>   xen/arm: p2m: Introduce p2m_get_root_pointer and use it in
>     __p2m_lookup
>   xen/arm: p2m: Introduce p2m_get_entry and use it to implement
>     __p2m_lookup
>   xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
>   xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
>   xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
>   xen/arm: p2m: Make p2m_{valid,table,mapping} helpers inline
>   xen/arm: p2m: Introduce a helper to check if an entry is a superpage
>   xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
>   xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry
>   xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
>   xen/arm: p2m: Re-implement p2m_set_mem_access using
>     p2m_{set,get}_entry
>   xen/arm: p2m: Do not handle shattering in p2m_create_table
> 
>  xen/arch/arm/domain.c      |    8 +-
>  xen/arch/arm/p2m.c         | 1274 ++++++++++++++++++++++----------------------
>  xen/arch/arm/traps.c       |  126 +++--
>  xen/include/asm-arm/p2m.h  |   14 +
>  xen/include/asm-arm/page.h |    4 +
>  5 files changed, 742 insertions(+), 684 deletions(-)
> 
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch
  2016-07-28 14:51 ` [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch Julien Grall
@ 2016-08-16  0:21   ` Stefano Stabellini
  2016-08-16 16:20     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-16  0:21 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> A follow-up patch will add more case to the switch that will require the
> IPA. So move the computation out of the switch.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/traps.c | 36 ++++++++++++++++++------------------
>  1 file changed, 18 insertions(+), 18 deletions(-)
> 
> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> index 683bcb2..46e0663 100644
> --- a/xen/arch/arm/traps.c
> +++ b/xen/arch/arm/traps.c
> @@ -2403,35 +2403,35 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>      int rc;
>      register_t gva = READ_SYSREG(FAR_EL2);
>      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
> +    paddr_t gpa;
> +
> +    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
> +        gpa = get_faulting_ipa(gva);
> +    else
> +    {
> +        /*
> +         * Flush the TLB to make sure the DTLB is clear before
> +         * doing GVA->IPA translation. If we got here because of
> +         * an entry only present in the ITLB, this translation may
> +         * still be inaccurate.
> +         */
> +        flush_tlb_local();
> +
> +        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
> +        if ( rc == -EFAULT )
> +            return; /* Try again */

The issue with this is that now for any cases that don't require a gpa
if gva_to_ipa fails we wrongly return -EFAULT.

I suggest having two switches or falling through from the first case to
the second.


> +    }
>  
>      switch ( fsc )
>      {
>      case FSC_FLT_PERM:
>      {
> -        paddr_t gpa;
>          const struct npfec npfec = {
>              .insn_fetch = 1,
>              .gla_valid = 1,
>              .kind = hsr.iabt.s1ptw ? npfec_kind_in_gpt : npfec_kind_with_gla
>          };
>  
> -        if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
> -            gpa = get_faulting_ipa(gva);
> -        else
> -        {
> -            /*
> -             * Flush the TLB to make sure the DTLB is clear before
> -             * doing GVA->IPA translation. If we got here because of
> -             * an entry only present in the ITLB, this translation may
> -             * still be inaccurate.
> -             */
> -            flush_tlb_local();
> -
> -            rc = gva_to_ipa(gva, &gpa, GV2M_READ);
> -            if ( rc == -EFAULT )
> -                return; /* Try again */
> -        }
> -
>          rc = p2m_mem_access_check(gpa, gva, npfec);
>  
>          /* Trap was triggered by mem_access, work here is done */
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
  2016-07-28 14:51 ` [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry Julien Grall
@ 2016-08-16  0:35   ` Stefano Stabellini
  2016-08-31 10:29     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-16  0:35 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Each entry in the page table has to table clean when the IOMMU does not

What does it mean to "table clean" ?


> support coherent walk. Rather than querying every time the page table is
> updated, it is possible to do it only once when the p2m is initialized.
> 
> This is because this value can never change, Xen would be in big trouble
> otherwise.
> 
> With this change, the initialize of the IOMMU for a given domain has to

"the initialization"


> be done earlier in order to know whether the page table entries need to
> be clean. It is fine to move the call earlier because it has no

"be cleaned"


Aside from the small imperfections in the commit message:

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


> dependency.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/domain.c     |  8 +++++---
>  xen/arch/arm/p2m.c        | 47 ++++++++++++++++++++++-------------------------
>  xen/include/asm-arm/p2m.h |  3 +++
>  3 files changed, 30 insertions(+), 28 deletions(-)
> 
> diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c
> index 20bb2ba..48f04c8 100644
> --- a/xen/arch/arm/domain.c
> +++ b/xen/arch/arm/domain.c
> @@ -555,6 +555,11 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags,
>          return 0;
>  
>      ASSERT(config != NULL);
> +
> +    /* p2m_init relies on some value initialized by the IOMMU subsystem */
> +    if ( (rc = iommu_domain_init(d)) != 0 )
> +        goto fail;
> +
>      if ( (rc = p2m_init(d)) != 0 )
>          goto fail;
>  
> @@ -637,9 +642,6 @@ int arch_domain_create(struct domain *d, unsigned int domcr_flags,
>      if ( is_hardware_domain(d) && (rc = domain_vuart_init(d)) )
>          goto fail;
>  
> -    if ( (rc = iommu_domain_init(d)) != 0 )
> -        goto fail;
> -
>      return 0;
>  
>  fail:
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 40a0b80..d389f2b 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -416,7 +416,7 @@ static inline void p2m_remove_pte(lpae_t *p, bool_t flush_cache)
>   * level_shift is the number of bits at the level we want to create.
>   */
>  static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
> -                            int level_shift, bool_t flush_cache)
> +                            int level_shift)
>  {
>      struct page_info *page;
>      lpae_t *p;
> @@ -462,7 +462,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
>      else
>          clear_page(p);
>  
> -    if ( flush_cache )
> +    if ( p2m->clean_pte )
>          clean_dcache_va_range(p, PAGE_SIZE);
>  
>      unmap_domain_page(p);
> @@ -470,7 +470,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
>      pte = mfn_to_p2m_entry(_mfn(page_to_mfn(page)), p2m_invalid,
>                             p2m->default_access);
>  
> -    p2m_write_pte(entry, pte, flush_cache);
> +    p2m_write_pte(entry, pte, p2m->clean_pte);
>  
>      return 0;
>  }
> @@ -653,12 +653,10 @@ static const paddr_t level_shifts[] =
>  
>  static int p2m_shatter_page(struct p2m_domain *p2m,
>                              lpae_t *entry,
> -                            unsigned int level,
> -                            bool_t flush_cache)
> +                            unsigned int level)
>  {
>      const paddr_t level_shift = level_shifts[level];
> -    int rc = p2m_create_table(p2m, entry,
> -                              level_shift - PAGE_SHIFT, flush_cache);
> +    int rc = p2m_create_table(p2m, entry, level_shift - PAGE_SHIFT);
>  
>      if ( !rc )
>      {
> @@ -680,7 +678,6 @@ static int p2m_shatter_page(struct p2m_domain *p2m,
>  static int apply_one_level(struct domain *d,
>                             lpae_t *entry,
>                             unsigned int level,
> -                           bool_t flush_cache,
>                             enum p2m_operation op,
>                             paddr_t start_gpaddr,
>                             paddr_t end_gpaddr,
> @@ -719,7 +716,7 @@ static int apply_one_level(struct domain *d,
>              if ( level < 3 )
>                  pte.p2m.table = 0; /* Superpage entry */
>  
> -            p2m_write_pte(entry, pte, flush_cache);
> +            p2m_write_pte(entry, pte, p2m->clean_pte);
>  
>              *flush |= p2m_valid(orig_pte);
>  
> @@ -754,7 +751,7 @@ static int apply_one_level(struct domain *d,
>              /* Not present -> create table entry and descend */
>              if ( !p2m_valid(orig_pte) )
>              {
> -                rc = p2m_create_table(p2m, entry, 0, flush_cache);
> +                rc = p2m_create_table(p2m, entry, 0);
>                  if ( rc < 0 )
>                      return rc;
>                  return P2M_ONE_DESCEND;
> @@ -764,7 +761,7 @@ static int apply_one_level(struct domain *d,
>              if ( p2m_mapping(orig_pte) )
>              {
>                  *flush = true;
> -                rc = p2m_shatter_page(p2m, entry, level, flush_cache);
> +                rc = p2m_shatter_page(p2m, entry, level);
>                  if ( rc < 0 )
>                      return rc;
>              } /* else: an existing table mapping -> descend */
> @@ -801,7 +798,7 @@ static int apply_one_level(struct domain *d,
>                   * and descend.
>                   */
>                  *flush = true;
> -                rc = p2m_shatter_page(p2m, entry, level, flush_cache);
> +                rc = p2m_shatter_page(p2m, entry, level);
>                  if ( rc < 0 )
>                      return rc;
>  
> @@ -827,7 +824,7 @@ static int apply_one_level(struct domain *d,
>  
>          *flush = true;
>  
> -        p2m_remove_pte(entry, flush_cache);
> +        p2m_remove_pte(entry, p2m->clean_pte);
>          p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), p2m_access_rwx);
>  
>          *addr += level_size;
> @@ -886,7 +883,7 @@ static int apply_one_level(struct domain *d,
>              /* Shatter large pages as we descend */
>              if ( p2m_mapping(orig_pte) )
>              {
> -                rc = p2m_shatter_page(p2m, entry, level, flush_cache);
> +                rc = p2m_shatter_page(p2m, entry, level);
>                  if ( rc < 0 )
>                      return rc;
>              } /* else: an existing table mapping -> descend */
> @@ -904,7 +901,7 @@ static int apply_one_level(struct domain *d,
>                      return rc;
>  
>                  p2m_set_permission(&pte, pte.p2m.type, a);
> -                p2m_write_pte(entry, pte, flush_cache);
> +                p2m_write_pte(entry, pte, p2m->clean_pte);
>              }
>  
>              *addr += level_size;
> @@ -954,17 +951,9 @@ static int apply_p2m_changes(struct domain *d,
>      const unsigned int preempt_count_limit = (op == MEMACCESS) ? 1 : 0x2000;
>      const bool_t preempt = !is_idle_vcpu(current);
>      bool_t flush = false;
> -    bool_t flush_pt;
>      PAGE_LIST_HEAD(free_pages);
>      struct page_info *pg;
>  
> -    /*
> -     * Some IOMMU don't support coherent PT walk. When the p2m is
> -     * shared with the CPU, Xen has to make sure that the PT changes have
> -     * reached the memory
> -     */
> -    flush_pt = iommu_enabled && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK);
> -
>      p2m_write_lock(p2m);
>  
>      /* Static mapping. P2M_ROOT_PAGES > 1 are handled below */
> @@ -1070,7 +1059,7 @@ static int apply_p2m_changes(struct domain *d,
>              lpae_t old_entry = *entry;
>  
>              ret = apply_one_level(d, entry,
> -                                  level, flush_pt, op,
> +                                  level, op,
>                                    start_gpaddr, end_gpaddr,
>                                    &addr, &maddr, &flush,
>                                    t, a);
> @@ -1127,7 +1116,7 @@ static int apply_p2m_changes(struct domain *d,
>  
>                  page_list_del(pg, &p2m->pages);
>  
> -                p2m_remove_pte(entry, flush_pt);
> +                p2m_remove_pte(entry, p2m->clean_pte);
>  
>                  p2m->stats.mappings[level - 1]--;
>                  update_reference_mapping(pages[level - 1], old_entry, *entry);
> @@ -1399,6 +1388,14 @@ int p2m_init(struct domain *d)
>      p2m->mem_access_enabled = false;
>      radix_tree_init(&p2m->mem_access_settings);
>  
> +    /*
> +     * Some IOMMUs don't support coherent PT walk. When the p2m is
> +     * shared with the CPU, Xen has to make sure that the PT changes have
> +     * reached the memory
> +     */
> +    p2m->clean_pte = iommu_enabled &&
> +        !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK);
> +
>      rc = p2m_alloc_table(d);
>  
>      return rc;
> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> index 53c4d78..03bfd5e 100644
> --- a/xen/include/asm-arm/p2m.h
> +++ b/xen/include/asm-arm/p2m.h
> @@ -48,6 +48,9 @@ struct p2m_domain {
>       * decrease. */
>      gfn_t lowest_mapped_gfn;
>  
> +    /* Indicate if it is required to clean the cache when writing an entry */
> +    bool_t clean_pte;
> +
>      /* Gather some statistics for information purposes only */
>      struct {
>          /* Number of mappings at each p2m tree level */
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 03/22] xen/arm: p2m: Rename parameter in p2m_{remove, write}_pte...
  2016-07-28 14:51 ` [RFC 03/22] xen/arm: p2m: Rename parameter in p2m_{remove, write}_pte Julien Grall
@ 2016-08-16  0:36   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-16  0:36 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> to make clear of the usage. I.e it is used to inform whether Xen needs
> to clean the entry after writing in the page table.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Acked-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index d389f2b..ff82f12 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -390,19 +390,19 @@ static lpae_t mfn_to_p2m_entry(mfn_t mfn, p2m_type_t t, p2m_access_t a)
>      return e;
>  }
>  
> -static inline void p2m_write_pte(lpae_t *p, lpae_t pte, bool_t flush_cache)
> +static inline void p2m_write_pte(lpae_t *p, lpae_t pte, bool clean_pte)
>  {
>      write_pte(p, pte);
> -    if ( flush_cache )
> +    if ( clean_pte )
>          clean_dcache(*p);
>  }
>  
> -static inline void p2m_remove_pte(lpae_t *p, bool_t flush_cache)
> +static inline void p2m_remove_pte(lpae_t *p, bool clean_pte)
>  {
>      lpae_t pte;
>  
>      memset(&pte, 0x00, sizeof(pte));
> -    p2m_write_pte(p, pte, flush_cache);
> +    p2m_write_pte(p, pte, clean_pte);
>  }
>  
>  /*
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 04/22] xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set
  2016-07-28 14:51 ` [RFC 04/22] xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set Julien Grall
@ 2016-08-16  0:39   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-16  0:39 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> p2m_mem_access_radix_set is expecting a gfn in a parameter. Rename the
> parameter 'pfn' to 'gfn' to match its content and use the typesafe gfn
> to avoid possible misusage.
> 
> Also rename the parameter to gfn to match its content.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 16 +++++++++-------
>  1 file changed, 9 insertions(+), 7 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index ff82f12..aecdd1e 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -542,7 +542,7 @@ static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
>      return 0;
>  }
>  
> -static int p2m_mem_access_radix_set(struct p2m_domain *p2m, unsigned long pfn,
> +static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
>                                      p2m_access_t a)
>  {
>      int rc;
> @@ -552,18 +552,18 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, unsigned long pfn,
>  
>      if ( p2m_access_rwx == a )
>      {
> -        radix_tree_delete(&p2m->mem_access_settings, pfn);
> +        radix_tree_delete(&p2m->mem_access_settings, gfn_x(gfn));
>          return 0;
>      }
>  
> -    rc = radix_tree_insert(&p2m->mem_access_settings, pfn,
> +    rc = radix_tree_insert(&p2m->mem_access_settings, gfn_x(gfn),
>                             radix_tree_int_to_ptr(a));
>      if ( rc == -EEXIST )
>      {
>          /* If a setting already exists, change it to the new one */
>          radix_tree_replace_slot(
>              radix_tree_lookup_slot(
> -                &p2m->mem_access_settings, pfn),
> +                &p2m->mem_access_settings, gfn_x(gfn)),
>              radix_tree_int_to_ptr(a));
>          rc = 0;
>      }
> @@ -707,7 +707,7 @@ static int apply_one_level(struct domain *d,
>              */
>               (level == 3 || (!p2m_table(orig_pte) && !p2m->mem_access_enabled)) )
>          {
> -            rc = p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), a);
> +            rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)), a);
>              if ( rc < 0 )
>                  return rc;
>  
> @@ -825,7 +825,8 @@ static int apply_one_level(struct domain *d,
>          *flush = true;
>  
>          p2m_remove_pte(entry, p2m->clean_pte);
> -        p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), p2m_access_rwx);
> +        p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
> +                                 p2m_access_rwx);
>  
>          *addr += level_size;
>          *maddr += level_size;
> @@ -896,7 +897,8 @@ static int apply_one_level(struct domain *d,
>  
>              if ( p2m_valid(pte) )
>              {
> -                rc = p2m_mem_access_radix_set(p2m, paddr_to_pfn(*addr), a);
> +                rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
> +                                              a);
>                  if ( rc < 0 )
>                      return rc;
>  
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper
  2016-07-28 14:51 ` [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper Julien Grall
@ 2016-08-16  0:49   ` Stefano Stabellini
  2016-08-31 10:36     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-16  0:49 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Currently, a stage-2 fault translation will likely access an emulated
> region. All the checks are pre-sanitity check for MMIO emulation.
> 
> A follow-up patch will handle a new case that could lead to a stage-2
> translation. To improve the clarity of the code and the changes, the
> current implementation is move in a separate helper.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/traps.c | 58 ++++++++++++++++++++++++++++++----------------------
>  1 file changed, 33 insertions(+), 25 deletions(-)
> 
> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> index 46e0663..b46284c 100644
> --- a/xen/arch/arm/traps.c
> +++ b/xen/arch/arm/traps.c
> @@ -2444,6 +2444,38 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>      inject_iabt_exception(regs, gva, hsr.len);
>  }
>  
> +static bool_t try_handle_mmio(struct cpu_user_regs *regs,
> +                              mmio_info_t *info)
> +{
> +    const struct hsr_dabt dabt = info->dabt;
> +    int rc;
> +
> +    /* stage-1 page table should never live in an emulated MMIO region */
> +    if ( dabt.s1ptw )
> +        return 0;
> +
> +    /* All the instructions used on emulated MMIO region should be valid */
> +    if ( !dabt.valid )
> +        return 0;
> +
> +    /*
> +     * Erratum 766422: Thumb store translation fault to Hypervisor may
> +     * not have correct HSR Rt value.
> +     */
> +    if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
> +         dabt.write )
> +    {
> +        rc = decode_instruction(regs, &info->dabt);
> +        if ( rc )
> +        {
> +            gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
> +            return 0;
> +        }
> +    }
> +
> +    return !!handle_mmio(info);
> +}
> +
>  static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>                                       const union hsr hsr)
>  {
> @@ -2487,40 +2519,16 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>          break;
>      }
>      case FSC_FLT_TRANS:
> -        if ( dabt.s1ptw )
> -            goto bad_data_abort;
> -
> -        /* XXX: Decode the instruction if ISS is not valid */
> -        if ( !dabt.valid )
> -            goto bad_data_abort;
> -
> -        /*
> -         * Erratum 766422: Thumb store translation fault to Hypervisor may
> -         * not have correct HSR Rt value.
> -         */
> -        if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
> -             dabt.write )
> -        {
> -            rc = decode_instruction(regs, &info.dabt);
> -            if ( rc )
> -            {
> -                gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
> -                goto bad_data_abort;
> -            }
> -        }
> -
> -        if ( handle_mmio(&info) )
> +        if ( try_handle_mmio(regs, &info) )
>          {
>              advance_pc(regs, hsr);
>              return;
>          }
> -        break;

I would keep this break, because we don't want to print the warning
below in all error cases, such as !dabt.valid.

Aside from the break removal:

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>      default:
>          gprintk(XENLOG_WARNING, "Unsupported DFSC: HSR=%#x DFSC=%#x\n",
>                  hsr.bits, dabt.dfsc);
>      }
>  
> -bad_data_abort:
>      gdprintk(XENLOG_DEBUG, "HSR=0x%x pc=%#"PRIregister" gva=%#"PRIvaddr
>               " gpa=%#"PRIpaddr"\n", hsr.bits, regs->pc, info.gva, info.gpa);
>      inject_dabt_exception(regs, info.gva, hsr.len);
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch
  2016-08-16  0:21   ` Stefano Stabellini
@ 2016-08-16 16:20     ` Julien Grall
  2016-08-31 10:01       ` Julien Grall
  2016-08-31 19:43       ` Stefano Stabellini
  0 siblings, 2 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-16 16:20 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 16/08/2016 01:21, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> A follow-up patch will add more case to the switch that will require the
>> IPA. So move the computation out of the switch.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>> ---
>>  xen/arch/arm/traps.c | 36 ++++++++++++++++++------------------
>>  1 file changed, 18 insertions(+), 18 deletions(-)
>>
>> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
>> index 683bcb2..46e0663 100644
>> --- a/xen/arch/arm/traps.c
>> +++ b/xen/arch/arm/traps.c
>> @@ -2403,35 +2403,35 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>>      int rc;
>>      register_t gva = READ_SYSREG(FAR_EL2);
>>      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
>> +    paddr_t gpa;
>> +
>> +    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
>> +        gpa = get_faulting_ipa(gva);
>> +    else
>> +    {
>> +        /*
>> +         * Flush the TLB to make sure the DTLB is clear before
>> +         * doing GVA->IPA translation. If we got here because of
>> +         * an entry only present in the ITLB, this translation may
>> +         * still be inaccurate.
>> +         */
>> +        flush_tlb_local();
>> +
>> +        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
>> +        if ( rc == -EFAULT )
>> +            return; /* Try again */
>
> The issue with this is that now for any cases that don't require a gpa
> if gva_to_ipa fails we wrongly return -EFAULT.

Well, stage-1 fault is prioritized over stage-2 fault (see B3.12.3 in 
ARM DDI 0406C.b), so gva_to_ipa should never fail unless someone is 
playing with the stage-1 page table at the same time or because of an 
erratum (see 834220). In both case, we should replay the instruction to 
let the processor injecting the correct fault.

FWIW, this is already what we do for the data abort handler.

>
> I suggest having two switches or falling through from the first case to
> the second.

I am not sure to understand your suggestion. Could you detail it?

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence
  2016-08-15 15:06 ` Edgar E. Iglesias
@ 2016-08-17  2:28   ` Shanker Donthineni
  0 siblings, 0 replies; 98+ messages in thread
From: Shanker Donthineni @ 2016-08-17  2:28 UTC (permalink / raw)
  To: Edgar E. Iglesias, Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin, xen-devel,
	Dirk Behme, Tamas K Lengyel, wei.chen

Hi Julien,

I have verified this patch series on Qualcomm Server platform QDF2XXX 
without any issue.

Tested-by: Shanker Donthineni <shankerd@codeaurora.org>

On 08/15/2016 10:06 AM, Edgar E. Iglesias wrote:
> On Thu, Jul 28, 2016 at 03:51:23PM +0100, Julien Grall wrote:
>> Hello all,
>>
>> The ARM architecture mandates the use of a break-before-make sequence when
>> changing translation entries if the page table is shared between multiple
>> CPUs whenever a valid entry is replaced by another valid entry (see D4.7.1
>> in ARM DDI 0487A.j for more details).
>>
>> The current P2M code does not respect this sequence and may result to
>> break coherency on some processors.
>>
>> Adapting the current implementation to use break-before-make sequence would
>> imply some code duplication and more TLBs invalidations than necessary.
>> For instance, if we are replacing a 4KB page and the current mapping in
>> the P2M is using a 1GB superpage, the following steps will happen:
>>      1) Shatter the 1GB superpage into a series of 2MB superpages
>>      2) Shatter the 2MB superpage into a series of 4KB superpages
>>      3) Replace the 4KB page
>>
>> As the current implementation is shattering while descending and install
>> the mapping before continuing to the next level, Xen would need to issue 3
>> TLB invalidation instructions which is clearly inefficient.
>>
>> Furthermore, all the operations which modify the page table are using the
>> same skeleton. It is more complicated to maintain different code paths than
>> having a generic function that set an entry and take care of the break-before-
>> make sequence.
>>
>> The new implementation is based on the x86 EPT one which, I think, fits
>> quite well for the break-before-make sequence whilst keeping the code
>> simple.
>>
>> I sent this patch series as an RFC because there are still some TODOs
>> in the code (mostly sanity check and possible optimization) and I have
>> done limited testing. However, I think it is a good shape to start reviewing,
>> get more feedback and have wider testing on different board.
>>
>> Also, I need to figure out the impact on ARM32 because the domheap is not
>> always mapped.
>>
>> This series has dependencies on some rework sent separately ([1] and [2]).
>> I have provided a branch with all the dependencies and this series applied:
>>
>> git://xenbits.xen.org/people/julieng/xen-unstable.git branch p2m-rfc
>
> Hi Julien,
>
> FWIW, I gave this a spin on the ZynqMP and it seems to be working fine.
> I tried dom0 and starting a few additional guests. All looks good.
>
> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
>
> Cheers,
> Edgar
>
>
>> Comments are welcome.
>>
>> Yours sincerely,
>>
>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>> Cc: Shanker Donthineni <shankerd@codeaurora.org>
>> Cc: Dirk Behme <dirk.behme@de.bosch.com>
>> Cc: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
>>
>> [1] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02936.html
>> [2] https://lists.xenproject.org/archives/html/xen-devel/2016-07/msg02830.html
>>
>> Julien Grall (22):
>>    xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of
>>      the switch
>>    xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
>>    xen/arm: p2m: Rename parameter in p2m_{remove,write}_pte...
>>    xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set
>>    xen/arm: traps: Move MMIO emulation code in a separate helper
>>    xen/arm: traps: Check the P2M before injecting a data/instruction
>>      abort
>>    xen/arm: p2m: Rework p2m_put_l3_page
>>    xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m
>>    xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned
>>      int
>>    xen/arm: p2m: Move the lookup helpers at the top of the file
>>    xen/arm: p2m: Introduce p2m_get_root_pointer and use it in
>>      __p2m_lookup
>>    xen/arm: p2m: Introduce p2m_get_entry and use it to implement
>>      __p2m_lookup
>>    xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
>>    xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
>>    xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
>>    xen/arm: p2m: Make p2m_{valid,table,mapping} helpers inline
>>    xen/arm: p2m: Introduce a helper to check if an entry is a superpage
>>    xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
>>    xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry
>>    xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
>>    xen/arm: p2m: Re-implement p2m_set_mem_access using
>>      p2m_{set,get}_entry
>>    xen/arm: p2m: Do not handle shattering in p2m_create_table
>>
>>   xen/arch/arm/domain.c      |    8 +-
>>   xen/arch/arm/p2m.c         | 1274 ++++++++++++++++++++++----------------------
>>   xen/arch/arm/traps.c       |  126 +++--
>>   xen/include/asm-arm/p2m.h  |   14 +
>>   xen/include/asm-arm/page.h |    4 +
>>   5 files changed, 742 insertions(+), 684 deletions(-)
>>
>> -- 
>> 1.9.1
>>

-- 
Shanker Donthineni
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort
  2016-07-28 14:51 ` [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort Julien Grall
@ 2016-08-23  1:05   ` Stefano Stabellini
  2016-08-31 10:58     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-23  1:05 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> A data/instruction abort may have occurred if another CPU was playing
> with the stage-2 page table when following the break-before-make
> sequence (see D4.7.1 in ARM DDI 0487A.j). Rather than injecting directly
> the fault to the guest, we need to check whether the mapping exists. If
> it exists, return to the guest to replay the instruction.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/traps.c | 40 ++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 38 insertions(+), 2 deletions(-)
> 
> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> index b46284c..da56cc0 100644
> --- a/xen/arch/arm/traps.c
> +++ b/xen/arch/arm/traps.c
> @@ -2404,6 +2404,7 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>      register_t gva = READ_SYSREG(FAR_EL2);
>      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
>      paddr_t gpa;
> +    mfn_t mfn;
>  
>      if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
>          gpa = get_faulting_ipa(gva);
> @@ -2417,6 +2418,11 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>           */
>          flush_tlb_local();
>  
> +        /*
> +         * We may not be able to translate because someone is
> +         * playing with the Stage-2 page table of the domain.
> +         * Return to the guest.
> +         */
>          rc = gva_to_ipa(gva, &gpa, GV2M_READ);
>          if ( rc == -EFAULT )
>              return; /* Try again */
> @@ -2437,8 +2443,17 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>          /* Trap was triggered by mem_access, work here is done */
>          if ( !rc )
>              return;
> +        break;
>      }
> -    break;
> +    case FSC_FLT_TRANS:

Please add brackets under the case statement for code style


> +        /*
> +         * The PT walk may have failed because someone was playing
> +         * with the Stage-2 page table. Walk the Stage-2 PT to check
> +         * if the entry exists. If it's the case, return to the guest
> +         */
> +        mfn = p2m_lookup(current->domain, _gfn(paddr_to_pfn(gpa)), NULL);
> +        if ( !mfn_eq(mfn, INVALID_MFN) )
> +            return;

Just checking, but isn't it possible to get a genuine translation abort
with an mfn != invalid_mfn?


>      }
>  
>      inject_iabt_exception(regs, gva, hsr.len);
> @@ -2455,7 +2470,7 @@ static bool_t try_handle_mmio(struct cpu_user_regs *regs,
>          return 0;
>  
>      /* All the instructions used on emulated MMIO region should be valid */
> -    if ( !dabt.valid )
> +    if ( !info->dabt.valid )
>          return 0;

Spurious change?


>      /*
> @@ -2483,6 +2498,7 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>      int rc;
>      mmio_info_t info;
>      uint8_t fsc = hsr.dabt.dfsc & ~FSC_LL_MASK;
> +    mfn_t mfn;
>  
>      info.dabt = dabt;
>  #ifdef CONFIG_ARM_32
> @@ -2496,6 +2512,11 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>      else
>      {
>          rc = gva_to_ipa(info.gva, &info.gpa, GV2M_READ);
> +        /*
> +         * We may not be able to translate because someone is
> +         * playing with the Stage-2 page table of the domain.
> +         * Return to the guest.
> +         */
>          if ( rc == -EFAULT )
>              return; /* Try again */
>      }
> @@ -2519,11 +2540,26 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>          break;
>      }
>      case FSC_FLT_TRANS:
> +        /*
> +         * Attempt first to emulate the MMIO has the data abort will
                                                ^ as

> +         * likely happen an emulated region.
                           ^ on/in

> +         */
>          if ( try_handle_mmio(regs, &info) )
>          {
>              advance_pc(regs, hsr);
>              return;
>          }
> +
> +        /*
> +         * The PT walk may have failed because someone was playing
> +         * with the Stage-2 page table. Walk the Stage-2 PT to check
> +         * if the entry exists. If it's the case, return to the guest
> +         */
> +        mfn = p2m_lookup(current->domain, _gfn(paddr_to_pfn(info.gpa)), NULL);
> +        if ( !mfn_eq(mfn, INVALID_MFN) )
> +            return;
> +
> +        break;
>      default:
>          gprintk(XENLOG_WARNING, "Unsupported DFSC: HSR=%#x DFSC=%#x\n",
>                  hsr.bits, dabt.dfsc);
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 07/22] xen/arm: p2m: Rework p2m_put_l3_page
  2016-07-28 14:51 ` [RFC 07/22] xen/arm: p2m: Rework p2m_put_l3_page Julien Grall
@ 2016-08-23  1:10   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-23  1:10 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Modify the prototype to directly pass the mfn and the type in
> parameters. This will be useful later when we do not have the entry in
> hand.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 17 +++++++----------
>  1 file changed, 7 insertions(+), 10 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index aecdd1e..6b29cf0 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -584,10 +584,8 @@ enum p2m_operation {
>   * TODO: Handle superpages, for now we only take special references for leaf
>   * pages (specifically foreign ones, which can't be super mapped today).
>   */
> -static void p2m_put_l3_page(const lpae_t pte)
> +static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
>  {
> -    ASSERT(p2m_valid(pte));
> -
>      /*
>       * TODO: Handle other p2m types
>       *
> @@ -595,12 +593,10 @@ static void p2m_put_l3_page(const lpae_t pte)
>       * flush the TLBs if the page is reallocated before the end of
>       * this loop.
>       */
> -    if ( p2m_is_foreign(pte.p2m.type) )
> +    if ( p2m_is_foreign(type) )
>      {
> -        unsigned long mfn = pte.p2m.base;
> -
> -        ASSERT(mfn_valid(mfn));
> -        put_page(mfn_to_page(mfn));
> +        ASSERT(mfn_valid(mfn_x(mfn)));
> +        put_page(mfn_to_page(mfn_x(mfn)));
>      }
>  }
>  
> @@ -734,7 +730,8 @@ static int apply_one_level(struct domain *d,
>                   */
>                  BUG_ON(level < 3 && p2m_table(orig_pte));
>                  if ( level == 3 )
> -                    p2m_put_l3_page(orig_pte);
> +                    p2m_put_l3_page(_mfn(orig_pte.p2m.base),
> +                                    orig_pte.p2m.type);
>              }
>              else /* New mapping */
>                  p2m->stats.mappings[level]++;
> @@ -834,7 +831,7 @@ static int apply_one_level(struct domain *d,
>          p2m->stats.mappings[level]--;
>  
>          if ( level == 3 )
> -            p2m_put_l3_page(orig_pte);
> +            p2m_put_l3_page(_mfn(orig_pte.p2m.base), orig_pte.p2m.type);
>  
>          /*
>           * This is still a single pte write, no matter the level, so no need to
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 08/22] xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m
  2016-07-28 14:51 ` [RFC 08/22] xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m Julien Grall
@ 2016-08-23  1:18   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-23  1:18 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Sometimes the invalidation of the TLBs can be deferred until the p2m is
> unlocked. This is for instance the case when multiple mappings are
> removed. In other case, such as shattering a superpage, an immediate
> flush is required.
> 
> Keep track whether a flush is needed directly in the p2m_domain structure
> to allow serializing multiple changes. The TLBs will be invalidated when
> write unlocking the p2m if necessary.
> 
> Also a new helper, p2m_flush_sync, has been introduced to force a
> synchronous TLB invalidation.
> 
> Finally, replace the call to p2m_flush_tlb by p2m_flush_tlb_sync in
> apply_p2m_changes.
> 
> Note this patch is not useful today, however follow-up patches will make
> advantage of it.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c        | 33 ++++++++++++++++++++++++++++++++-
>  xen/include/asm-arm/p2m.h | 11 +++++++++++
>  2 files changed, 43 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 6b29cf0..a6dce0c 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -52,8 +52,21 @@ static inline void p2m_write_lock(struct p2m_domain *p2m)
>      write_lock(&p2m->lock);
>  }
>  
> +static void p2m_flush_tlb(struct p2m_domain *p2m);
> +
>  static inline void p2m_write_unlock(struct p2m_domain *p2m)
>  {
> +    if ( p2m->need_flush )
> +    {
> +        p2m->need_flush = false;
> +        /*
> +         * The final flush is done with the P2M write lock taken to
> +         * to avoid someone else modify the P2M before the TLB
> +         * invalidation has completed.
> +         */
> +        p2m_flush_tlb(p2m);
> +    }
> +
>      write_unlock(&p2m->lock);
>  }
>  
> @@ -72,6 +85,11 @@ static inline int p2m_is_locked(struct p2m_domain *p2m)
>      return rw_is_locked(&p2m->lock);
>  }
>  
> +static inline int p2m_is_write_locked(struct p2m_domain *p2m)
> +{
> +    return rw_is_write_locked(&p2m->lock);
> +}
> +
>  void p2m_dump_info(struct domain *d)
>  {
>      struct p2m_domain *p2m = &d->arch.p2m;
> @@ -165,6 +183,19 @@ static void p2m_flush_tlb(struct p2m_domain *p2m)
>  }
>  
>  /*
> + * Force a synchronous P2M TLB flush.
> + *
> + * Must be called with the p2m lock held.
> + */
> +static void p2m_flush_tlb_sync(struct p2m_domain *p2m)
> +{
> +    ASSERT(p2m_is_write_locked(p2m));
> +
> +    p2m_flush_tlb(p2m);
> +    p2m->need_flush = false;
> +}
> +
> +/*
>   * Lookup the MFN corresponding to a domain's GFN.
>   *
>   * There are no processor functions to do a stage 2 only lookup therefore we
> @@ -1142,7 +1173,7 @@ static int apply_p2m_changes(struct domain *d,
>  out:
>      if ( flush )
>      {
> -        p2m_flush_tlb(&d->arch.p2m);
> +        p2m_flush_tlb_sync(&d->arch.p2m);
>          ret = iommu_iotlb_flush(d, gfn_x(sgfn), nr);
>          if ( !rc )
>              rc = ret;
> diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h
> index 03bfd5e..e6be3ea 100644
> --- a/xen/include/asm-arm/p2m.h
> +++ b/xen/include/asm-arm/p2m.h
> @@ -51,6 +51,17 @@ struct p2m_domain {
>      /* Indicate if it is required to clean the cache when writing an entry */
>      bool_t clean_pte;
>  
> +    /*
> +     * P2M updates may required TLBs to be flushed (invalidated).
> +     *
> +     * Flushes may be deferred by setting 'need_flush' and then flushing
> +     * when the p2m write lock is released.
> +     *
> +     * If an immediate flush is required (e.g, if a super page is
> +     * shattered), call p2m_tlb_flush_sync().
> +     */
> +    bool need_flush;
> +
>      /* Gather some statistics for information purposes only */
>      struct {
>          /* Number of mappings at each p2m tree level */
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int
  2016-07-28 14:51 ` [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int Julien Grall
@ 2016-08-23  1:20   ` Stefano Stabellini
  2016-08-31 11:04     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-23  1:20 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The level shift can be encoded with 32-bit. So it is not necessary to
> use paddr_t (i.e 64-bit).

You might as well use 8 bit. 


> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/p2m.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index a6dce0c..798faa8 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -675,7 +675,7 @@ static const paddr_t level_sizes[] =
>      { ZEROETH_SIZE, FIRST_SIZE, SECOND_SIZE, THIRD_SIZE };
>  static const paddr_t level_masks[] =
>      { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
> -static const paddr_t level_shifts[] =
> +static const unsigned int level_shifts[] =
>      { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
>  
>  static int p2m_shatter_page(struct p2m_domain *p2m,
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 11/22] xen/arm: p2m: Introduce p2m_get_root_pointer and use it in __p2m_lookup
  2016-07-28 14:51 ` [RFC 11/22] xen/arm: p2m: Introduce p2m_get_root_pointer and use it in __p2m_lookup Julien Grall
@ 2016-08-23  1:34   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-23  1:34 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Mapping the root table is always done the same way. To avoid duplicating
> the code in a later patch, move the code in a separate helper.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 53 +++++++++++++++++++++++++++++++++++------------------
>  1 file changed, 35 insertions(+), 18 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index ea582c8..d4a4b62 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -204,6 +204,37 @@ static void p2m_flush_tlb_sync(struct p2m_domain *p2m)
>  }
>  
>  /*
> + * Find and map the root page table. The caller is responsible for
> + * unmapping the table.
> + *
> + * The function will return NULL if the offset of the root table is
> + * invalid.
> + */
> +static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m,
> +                                    gfn_t gfn)
> +{
> +    unsigned int root_table;
> +
> +    if ( P2M_ROOT_PAGES == 1 )
> +        return __map_domain_page(p2m->root);
> +
> +    /*
> +     * Concatenated root-level tables. The table number will be the
> +     * offset at the previous level. It is not possible to
> +     * concatenate a level-0 root.
> +     */
> +    ASSERT(P2M_ROOT_LEVEL > 0);
> +
> +    root_table = gfn_x(gfn) >>  (level_shifts[P2M_ROOT_LEVEL - 1] - PAGE_SHIFT);
> +    root_table &= LPAE_ENTRY_MASK;
> +
> +    if ( root_table >= P2M_ROOT_PAGES )
> +        return NULL;
> +
> +    return __map_domain_page(p2m->root + root_table);
> +}
> +
> +/*
>   * Lookup the MFN corresponding to a domain's GFN.
>   *
>   * There are no processor functions to do a stage 2 only lookup therefore we
> @@ -226,7 +257,7 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>      mfn_t mfn = INVALID_MFN;
>      paddr_t mask = 0;
>      p2m_type_t _t;
> -    unsigned int level, root_table;
> +    unsigned int level;
>  
>      ASSERT(p2m_is_locked(p2m));
>      BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
> @@ -236,22 +267,9 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>  
>      *t = p2m_invalid;
>  
> -    if ( P2M_ROOT_PAGES > 1 )
> -    {
> -        /*
> -         * Concatenated root-level tables. The table number will be
> -         * the offset at the previous level. It is not possible to
> -         * concatenate a level-0 root.
> -         */
> -        ASSERT(P2M_ROOT_LEVEL > 0);
> -        root_table = offsets[P2M_ROOT_LEVEL - 1];
> -        if ( root_table >= P2M_ROOT_PAGES )
> -            goto err;
> -    }
> -    else
> -        root_table = 0;
> -
> -    map = __map_domain_page(p2m->root + root_table);
> +    map = p2m_get_root_pointer(p2m, gfn);
> +    if ( !map )
> +        return INVALID_MFN;
>  
>      ASSERT(P2M_ROOT_LEVEL < 4);
>  
> @@ -286,7 +304,6 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>          *t = pte.p2m.type;
>      }
>  
> -err:
>      return mfn;
>  }
>  
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo
  2016-07-28 14:51 ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup Julien Grall
  2016-07-30 18:37   ` Tamas K Lengyel
@ 2016-08-31  0:30   ` Stefano Stabellini
  2016-08-31 12:25     ` Julien Grall
  1 sibling, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-31  0:30 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Currently, for a given GFN, the function __p2m_lookup will only return
> the associated MFN and the p2m type of the mapping.
> 
> In some case we need the order of the mapping and the memaccess
> permission. Rather than providing separate function for this purpose,
                                    ^ a separate

> it is better to implement a generic function to return all the
> information.
> 
> To avoid passing dummy parameter, a caller that does need a specific
                                                      ^ not need?


> information can use NULL instead.
> 
> The list of the informations retrieved is based on the x86 version. All
> of them will be used in follow-up patches.
> 
> It might have been possible to extend __p2m_lookup, however I choose to
> reimplement it from scratch to allow sharing some helpers with the
> function that will update the P2M (will be added in a follow-up patch).
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> ---
>  xen/arch/arm/p2m.c         | 188 ++++++++++++++++++++++++++++++++++-----------
>  xen/include/asm-arm/page.h |   4 +
>  2 files changed, 149 insertions(+), 43 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index d4a4b62..8676b9d 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -36,6 +36,8 @@ static const paddr_t level_masks[] =
>      { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
>  static const unsigned int level_shifts[] =
>      { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
> +static const unsigned int level_orders[] =
> +    { ZEROETH_ORDER, FIRST_ORDER, SECOND_ORDER, THIRD_ORDER };
>  
>  static bool_t p2m_valid(lpae_t pte)
>  {
> @@ -236,28 +238,99 @@ static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m,
>  
>  /*
>   * Lookup the MFN corresponding to a domain's GFN.
> + * Lookup mem access in the ratrix tree.
> + * The entries associated to the GFN is considered valid.
> + */
> +static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m, gfn_t gfn)
> +{
> +    void *ptr;
> +
> +    if ( !p2m->mem_access_enabled )
> +        return p2m_access_rwx;

Shouldn't this be p2m->default_access?


> +    ptr = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> +    if ( !ptr )
> +        return p2m_access_rwx;

Same here?


> +    else
> +        return radix_tree_ptr_to_int(ptr);
> +}
> +
> +#define GUEST_TABLE_MAP_FAILED 0
> +#define GUEST_TABLE_SUPER_PAGE 1
> +#define GUEST_TABLE_NORMAL_PAGE 2
> +
> +static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
> +                            int level_shift);
> +
> +/*
> + * Take the currently mapped table, find the corresponding GFN entry,
> + * and map the next table, if available.

It is important to write down that the function also unmaps the previous
table.


>   *
> - * There are no processor functions to do a stage 2 only lookup therefore we
> - * do a a software walk.
> + * Return values:
> + *  GUEST_TABLE_MAP_FAILED: Either read_only was set and the entry
> + *  was empty, or allocating a new page failed.
> + *  GUEST_TABLE_NORMAL_PAGE: next level mapped normally
> + *  GUEST_TABLE_SUPER_PAGE: The next entry points to a superpage.
>   */
> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> +static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
> +                          lpae_t **table, unsigned int offset)
>  {
> -    struct p2m_domain *p2m = &d->arch.p2m;
> -    const paddr_t paddr = pfn_to_paddr(gfn_x(gfn));
> -    const unsigned int offsets[4] = {
> -        zeroeth_table_offset(paddr),
> -        first_table_offset(paddr),
> -        second_table_offset(paddr),
> -        third_table_offset(paddr)
> -    };
> -    const paddr_t masks[4] = {
> -        ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK
> -    };
> -    lpae_t pte, *map;
> +    lpae_t *entry;
> +    int ret;
> +    mfn_t mfn;
> +
> +    entry = *table + offset;
> +
> +    if ( !p2m_valid(*entry) )
> +    {
> +        if ( read_only )
> +            return GUEST_TABLE_MAP_FAILED;
> +
> +        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
> +        if ( ret )
> +            return GUEST_TABLE_MAP_FAILED;
> +    }
> +
> +    /* The function p2m_next_level is never called at the 3rd level */
> +    if ( p2m_mapping(*entry) )
> +        return GUEST_TABLE_SUPER_PAGE;
> +
> +    mfn = _mfn(entry->p2m.base);
> +
> +    unmap_domain_page(*table);
> +    *table = map_domain_page(mfn);
> +
> +    return GUEST_TABLE_NORMAL_PAGE;

I am a bit worried about having the same function doing the lookup and
creating new tables, especially given it doesn't tell you whether the
entry was already there or it was created: the return value is the same
in both cases. At the very least the return values should be different.


> +}
> +
> +/*
> + * Get the details of a given gfn.
> + *
> + * If the entry is present, the associated MFN will be returned and the
> + * access and type filled up. The page_order will correspond to the
> + * order of the mapping in the page table (i.e it could be a superpage).
> + *
> + * If the entry is not present, INVALID_MFN will be returned and the
> + * page_order will be set according to the order of the invalid range.
> + */
> +static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
> +                           p2m_type_t *t, p2m_access_t *a,
> +                           unsigned int *page_order)
> +{
> +    paddr_t addr = pfn_to_paddr(gfn_x(gfn));
> +    unsigned int level = 0;
> +    lpae_t entry, *table;
> +    int rc;
>      mfn_t mfn = INVALID_MFN;
> -    paddr_t mask = 0;
>      p2m_type_t _t;
> -    unsigned int level;
> +
> +    /* Convenience aliases */
> +    const unsigned int offsets[4] = {
> +        zeroeth_table_offset(addr),
> +        first_table_offset(addr),
> +        second_table_offset(addr),
> +        third_table_offset(addr)
> +    };
>  
>      ASSERT(p2m_is_locked(p2m));
>      BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
> @@ -267,46 +340,75 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>  
>      *t = p2m_invalid;
>  
> -    map = p2m_get_root_pointer(p2m, gfn);
> -    if ( !map )
> -        return INVALID_MFN;
> +    /* XXX: Check if the mapping is lower than the mapped gfn */
>  
> -    ASSERT(P2M_ROOT_LEVEL < 4);
> -
> -    for ( level = P2M_ROOT_LEVEL ; level < 4 ; level++ )
> +    /* This gfn is higher than the highest the p2m map currently holds */
> +    if ( gfn_x(gfn) > gfn_x(p2m->max_mapped_gfn) )
>      {
> -        mask = masks[level];
> -
> -        pte = map[offsets[level]];
> +        for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
> +        {
> +            if ( (gfn_x(gfn) & (level_masks[level] >> PAGE_SHIFT)) >
> +                 gfn_x(p2m->max_mapped_gfn) )
> +                break;
> +            goto out;

I am not sure what this loop is for, but it looks wrong.


> +        }
> +    }
>  
> -        if ( level == 3 && !p2m_table(pte) )
> -            /* Invalid, clobber the pte */
> -            pte.bits = 0;
> -        if ( level == 3 || !p2m_table(pte) )
> -            /* Done */
> -            break;
> +    table = p2m_get_root_pointer(p2m, gfn);
>  
> -        ASSERT(level < 3);
> +    /*
> +     * the table should always be non-NULL because the gfn is below
> +     * p2m->max_mapped_gfn and the root table pages are always present.
> +     */
> +    BUG_ON(table == NULL);
>  
> -        /* Map for next level */
> -        unmap_domain_page(map);
> -        map = map_domain_page(_mfn(pte.p2m.base));
> +    for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
> +    {
> +        rc = p2m_next_level(p2m, true, &table, offsets[level]);
> +        if ( rc == GUEST_TABLE_MAP_FAILED )
> +            goto out_unmap;
> +        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
> +            break;
>      }
>  
> -    unmap_domain_page(map);
> +    entry = table[offsets[level]];
>  
> -    if ( p2m_valid(pte) )
> +    if ( p2m_valid(entry) )
>      {
> -        ASSERT(mask);
> -        ASSERT(pte.p2m.type != p2m_invalid);
> -        mfn = _mfn(paddr_to_pfn((pte.bits & PADDR_MASK & mask) |
> -                                (paddr & ~mask)));
> -        *t = pte.p2m.type;
> +        *t = entry.p2m.type;

Why don't you have a check for ( t ) like you have done in the case of
( a )? It would be more consistent.


> +
> +        if ( a )
> +            *a = p2m_mem_access_radix_get(p2m, gfn);
> +
> +        mfn = _mfn(entry.p2m.base);
> +        /*
> +         * The entry may point to a superpage. Find the MFN associated
> +         * to the GFN.
> +         */
> +        mfn = mfn_add(mfn, gfn_x(gfn) & ((1UL << level_orders[level]) - 1));
>      }
>  
> +out_unmap:
> +    unmap_domain_page(table);
> +
> +out:
> +    if ( page_order )
> +        *page_order = level_shifts[level] - PAGE_SHIFT;
> +
>      return mfn;
>  }
>  
> +/*
> + * Lookup the MFN corresponding to a domain's GFN.
> + *
> + * There are no processor functions to do a stage 2 only lookup therefore we
> + * do a a software walk.
> + */
> +static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> +{
> +    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
> +}
> +
>  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>  {
>      mfn_t ret;
> diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h
> index 05d9f82..1c5bd8b 100644
> --- a/xen/include/asm-arm/page.h
> +++ b/xen/include/asm-arm/page.h
> @@ -457,15 +457,19 @@ static inline int gva_to_ipa(vaddr_t va, paddr_t *paddr, unsigned int flags)
>  #define LPAE_ENTRY_MASK (LPAE_ENTRIES - 1)
>  
>  #define THIRD_SHIFT    (PAGE_SHIFT)
> +#define THIRD_ORDER    0
>  #define THIRD_SIZE     ((paddr_t)1 << THIRD_SHIFT)
>  #define THIRD_MASK     (~(THIRD_SIZE - 1))
>  #define SECOND_SHIFT   (THIRD_SHIFT + LPAE_SHIFT)
> +#define SECOND_ORDER   (THIRD_ORDER + LPAE_SHIFT)
>  #define SECOND_SIZE    ((paddr_t)1 << SECOND_SHIFT)
>  #define SECOND_MASK    (~(SECOND_SIZE - 1))
>  #define FIRST_SHIFT    (SECOND_SHIFT + LPAE_SHIFT)
> +#define FIRST_ORDER    (SECOND_ORDER + LPAE_SHIFT)
>  #define FIRST_SIZE     ((paddr_t)1 << FIRST_SHIFT)
>  #define FIRST_MASK     (~(FIRST_SIZE - 1))
>  #define ZEROETH_SHIFT  (FIRST_SHIFT + LPAE_SHIFT)
> +#define ZEROETH_ORDER  (FIRST_ORDER + LPAE_SHIFT)
>  #define ZEROETH_SIZE   ((paddr_t)1 << ZEROETH_SHIFT)
>  #define ZEROETH_MASK   (~(ZEROETH_SIZE - 1))

It might be clearer to define them by SHIFT:

    #define THIRD_ORDER     (THIRD_SHIFT - PAGE_SHIFT)
    #define SECOND_ORDER    (SECOND_SHIFT - PAGE_SHIFT)
    #define FIRST_ORDER     (FIRST_SHIFT - PAGE_SHIFT)
    #define ZEROETH_ORDER   (ZEROETH_SHIFT - PAGE_SHIFT)

or avoid them and just use level_shifts which is already defined.  I
don't think they add much value.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch
  2016-08-16 16:20     ` Julien Grall
@ 2016-08-31 10:01       ` Julien Grall
  2016-08-31 19:43       ` Stefano Stabellini
  1 sibling, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-31 10:01 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 16/08/16 17:20, Julien Grall wrote:
> On 16/08/2016 01:21, Stefano Stabellini wrote:
>> On Thu, 28 Jul 2016, Julien Grall wrote:
>>> A follow-up patch will add more case to the switch that will require the
>>> IPA. So move the computation out of the switch.
>>>
>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>> ---
>>>  xen/arch/arm/traps.c | 36 ++++++++++++++++++------------------
>>>  1 file changed, 18 insertions(+), 18 deletions(-)
>>>
>>> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
>>> index 683bcb2..46e0663 100644
>>> --- a/xen/arch/arm/traps.c
>>> +++ b/xen/arch/arm/traps.c
>>> @@ -2403,35 +2403,35 @@ static void do_trap_instr_abort_guest(struct
>>> cpu_user_regs *regs,
>>>      int rc;
>>>      register_t gva = READ_SYSREG(FAR_EL2);
>>>      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
>>> +    paddr_t gpa;
>>> +
>>> +    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
>>> +        gpa = get_faulting_ipa(gva);
>>> +    else
>>> +    {
>>> +        /*
>>> +         * Flush the TLB to make sure the DTLB is clear before
>>> +         * doing GVA->IPA translation. If we got here because of
>>> +         * an entry only present in the ITLB, this translation may
>>> +         * still be inaccurate.
>>> +         */
>>> +        flush_tlb_local();
>>> +
>>> +        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
>>> +        if ( rc == -EFAULT )
>>> +            return; /* Try again */
>>
>> The issue with this is that now for any cases that don't require a gpa
>> if gva_to_ipa fails we wrongly return -EFAULT.
>
> Well, stage-1 fault is prioritized over stage-2 fault (see B3.12.3 in
> ARM DDI 0406C.b), so gva_to_ipa should never fail unless someone is
> playing with the stage-1 page table at the same time or because of an
> erratum (see 834220). In both case, we should replay the instruction to
> let the processor injecting the correct fault.
>
> FWIW, this is already what we do for the data abort handler.
>
>>
>> I suggest having two switches or falling through from the first case to
>> the second.
>
> I am not sure to understand your suggestion. Could you detail it?

Do you have any thoughts here or are you fine with the current approach?

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry
  2016-08-16  0:35   ` Stefano Stabellini
@ 2016-08-31 10:29     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-31 10:29 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 16/08/16 01:35, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> Each entry in the page table has to table clean when the IOMMU does not
>
> What does it mean to "table clean" ?

I meant "has to be cleaned".

>
>
>> support coherent walk. Rather than querying every time the page table is
>> updated, it is possible to do it only once when the p2m is initialized.
>>
>> This is because this value can never change, Xen would be in big trouble
>> otherwise.
>>
>> With this change, the initialize of the IOMMU for a given domain has to
>
> "the initialization"
>
>
>> be done earlier in order to know whether the page table entries need to
>> be clean. It is fine to move the call earlier because it has no
>
> "be cleaned"
>
>
> Aside from the small imperfections in the commit message:
>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

Thank you!

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper
  2016-08-16  0:49   ` Stefano Stabellini
@ 2016-08-31 10:36     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-31 10:36 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 16/08/16 01:49, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>>  static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>>                                       const union hsr hsr)
>>  {
>> @@ -2487,40 +2519,16 @@ static void do_trap_data_abort_guest(struct cpu_user_regs *regs,
>>          break;
>>      }
>>      case FSC_FLT_TRANS:
>> -        if ( dabt.s1ptw )
>> -            goto bad_data_abort;
>> -
>> -        /* XXX: Decode the instruction if ISS is not valid */
>> -        if ( !dabt.valid )
>> -            goto bad_data_abort;
>> -
>> -        /*
>> -         * Erratum 766422: Thumb store translation fault to Hypervisor may
>> -         * not have correct HSR Rt value.
>> -         */
>> -        if ( check_workaround_766422() && (regs->cpsr & PSR_THUMB) &&
>> -             dabt.write )
>> -        {
>> -            rc = decode_instruction(regs, &info.dabt);
>> -            if ( rc )
>> -            {
>> -                gprintk(XENLOG_DEBUG, "Unable to decode instruction\n");
>> -                goto bad_data_abort;
>> -            }
>> -        }
>> -
>> -        if ( handle_mmio(&info) )
>> +        if ( try_handle_mmio(regs, &info) )
>>          {
>>              advance_pc(regs, hsr);
>>              return;
>>          }
>> -        break;
>
> I would keep this break, because we don't want to print the warning
> below in all error cases, such as !dabt.valid.

It was meant to be kept, I removed it by error.

> Aside from the break removal:
>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

Thank you!

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort
  2016-08-23  1:05   ` Stefano Stabellini
@ 2016-08-31 10:58     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-31 10:58 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 23/08/16 02:05, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> A data/instruction abort may have occurred if another CPU was playing
>> with the stage-2 page table when following the break-before-make
>> sequence (see D4.7.1 in ARM DDI 0487A.j). Rather than injecting directly
>> the fault to the guest, we need to check whether the mapping exists. If
>> it exists, return to the guest to replay the instruction.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>> ---
>>  xen/arch/arm/traps.c | 40 ++++++++++++++++++++++++++++++++++++++--
>>  1 file changed, 38 insertions(+), 2 deletions(-)
>>
>> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
>> index b46284c..da56cc0 100644
>> --- a/xen/arch/arm/traps.c
>> +++ b/xen/arch/arm/traps.c
>> @@ -2404,6 +2404,7 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>>      register_t gva = READ_SYSREG(FAR_EL2);
>>      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
>>      paddr_t gpa;
>> +    mfn_t mfn;
>>
>>      if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
>>          gpa = get_faulting_ipa(gva);
>> @@ -2417,6 +2418,11 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>>           */
>>          flush_tlb_local();
>>
>> +        /*
>> +         * We may not be able to translate because someone is
>> +         * playing with the Stage-2 page table of the domain.
>> +         * Return to the guest.
>> +         */
>>          rc = gva_to_ipa(gva, &gpa, GV2M_READ);
>>          if ( rc == -EFAULT )
>>              return; /* Try again */
>> @@ -2437,8 +2443,17 @@ static void do_trap_instr_abort_guest(struct cpu_user_regs *regs,
>>          /* Trap was triggered by mem_access, work here is done */
>>          if ( !rc )
>>              return;
>> +        break;
>>      }
>> -    break;
>> +    case FSC_FLT_TRANS:
>
> Please add brackets under the case statement for code style

This is not part of the coding style. We only use the brackets when 
local variable is defined for a specific case. See vgic-{v2,v3}.c for 
instance.

Note that I am a bit surprised you complain here about the missing 
brackets but you did not on commit def4273 "xen/arm: traps: MMIO should 
only be emulated for fault translation" at the beginning of august.

>
>
>> +        /*
>> +         * The PT walk may have failed because someone was playing
>> +         * with the Stage-2 page table. Walk the Stage-2 PT to check
>> +         * if the entry exists. If it's the case, return to the guest
>> +         */
>> +        mfn = p2m_lookup(current->domain, _gfn(paddr_to_pfn(gpa)), NULL);
>> +        if ( !mfn_eq(mfn, INVALID_MFN) )
>> +            return;
>
> Just checking, but isn't it possible to get a genuine translation abort
> with an mfn != invalid_mfn?

A translation fault means the entry is not present in the page table at 
the time the processor did the page table walk.

This may happen because the hypervisor is modifying the stage-2 page 
table on another processor (for instance with the break-before-make 
sequence).

p2m_lookup will do a software page walk to get entry. As the function is 
using a read lock, it will wait until the other processor finishes to 
update the page tables before doing the lookup. So if the mfn returned 
is valid, then we know that the translation fault is spurious and should 
return to the guest to retry the execution of the faulting instruction.

>
>
>>      }
>>
>>      inject_iabt_exception(regs, gva, hsr.len);
>> @@ -2455,7 +2470,7 @@ static bool_t try_handle_mmio(struct cpu_user_regs *regs,
>>          return 0;
>>
>>      /* All the instructions used on emulated MMIO region should be valid */
>> -    if ( !dabt.valid )
>> +    if ( !info->dabt.valid )
>>          return 0;
>
> Spurious change?

Yes. I will drop it.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int
  2016-08-23  1:20   ` Stefano Stabellini
@ 2016-08-31 11:04     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-08-31 11:04 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 23/08/16 02:20, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> The level shift can be encoded with 32-bit. So it is not necessary to
>> use paddr_t (i.e 64-bit).
>
> You might as well use 8 bit.

Good point. I will change it in the next version.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo
  2016-08-31  0:30   ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo Stefano Stabellini
@ 2016-08-31 12:25     ` Julien Grall
  2016-08-31 19:33       ` Stefano Stabellini
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-08-31 12:25 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 31/08/16 01:30, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> Currently, for a given GFN, the function __p2m_lookup will only return
>> the associated MFN and the p2m type of the mapping.
>>
>> In some case we need the order of the mapping and the memaccess
>> permission. Rather than providing separate function for this purpose,
>                                     ^ a separate
>
>> it is better to implement a generic function to return all the
>> information.
>>
>> To avoid passing dummy parameter, a caller that does need a specific
>                                                       ^ not need?

Yes, I will fix it in the next version.

>
>
>> information can use NULL instead.
>>
>> The list of the informations retrieved is based on the x86 version. All
>> of them will be used in follow-up patches.
>>
>> It might have been possible to extend __p2m_lookup, however I choose to
>> reimplement it from scratch to allow sharing some helpers with the
>> function that will update the P2M (will be added in a follow-up patch).
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>> ---
>>  xen/arch/arm/p2m.c         | 188 ++++++++++++++++++++++++++++++++++-----------
>>  xen/include/asm-arm/page.h |   4 +
>>  2 files changed, 149 insertions(+), 43 deletions(-)
>>
>> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
>> index d4a4b62..8676b9d 100644
>> --- a/xen/arch/arm/p2m.c
>> +++ b/xen/arch/arm/p2m.c
>> @@ -36,6 +36,8 @@ static const paddr_t level_masks[] =
>>      { ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK };
>>  static const unsigned int level_shifts[] =
>>      { ZEROETH_SHIFT, FIRST_SHIFT, SECOND_SHIFT, THIRD_SHIFT };
>> +static const unsigned int level_orders[] =
>> +    { ZEROETH_ORDER, FIRST_ORDER, SECOND_ORDER, THIRD_ORDER };
>>
>>  static bool_t p2m_valid(lpae_t pte)
>>  {
>> @@ -236,28 +238,99 @@ static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m,
>>
>>  /*
>>   * Lookup the MFN corresponding to a domain's GFN.
>> + * Lookup mem access in the ratrix tree.
>> + * The entries associated to the GFN is considered valid.
>> + */
>> +static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m, gfn_t gfn)
>> +{
>> +    void *ptr;
>> +
>> +    if ( !p2m->mem_access_enabled )
>> +        return p2m_access_rwx;
>
> Shouldn't this be p2m->default_access?

default_access will always be p2m_access_rwx when memaccess is disabled. 
It will lead to crash a if you try to restrict permission without memaccess.

Note that, this is matching the behavior of __p2m_get_mem_access.

>
>
>> +    ptr = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
>> +    if ( !ptr )
>> +        return p2m_access_rwx;
>
> Same here?

The radix tree will contain all the permission restriction but 
p2m_access_rwx. This is because you may change the default_access whilst 
memaccess is enabled and you don't know what page was restricted with 
the default access.

Note that this is matching the behavior of p2m_mem_access_radix_set.

>
>
>> +    else
>> +        return radix_tree_ptr_to_int(ptr);
>> +}
>> +
>> +#define GUEST_TABLE_MAP_FAILED 0
>> +#define GUEST_TABLE_SUPER_PAGE 1
>> +#define GUEST_TABLE_NORMAL_PAGE 2
>> +
>> +static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
>> +                            int level_shift);
>> +
>> +/*
>> + * Take the currently mapped table, find the corresponding GFN entry,
>> + * and map the next table, if available.
>
> It is important to write down that the function also unmaps the previous
> table.

Will do.

>
>>   *
>> - * There are no processor functions to do a stage 2 only lookup therefore we
>> - * do a a software walk.
>> + * Return values:
>> + *  GUEST_TABLE_MAP_FAILED: Either read_only was set and the entry
>> + *  was empty, or allocating a new page failed.
>> + *  GUEST_TABLE_NORMAL_PAGE: next level mapped normally
>> + *  GUEST_TABLE_SUPER_PAGE: The next entry points to a superpage.
>>   */
>> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>> +static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
>> +                          lpae_t **table, unsigned int offset)
>>  {
>> -    struct p2m_domain *p2m = &d->arch.p2m;
>> -    const paddr_t paddr = pfn_to_paddr(gfn_x(gfn));
>> -    const unsigned int offsets[4] = {
>> -        zeroeth_table_offset(paddr),
>> -        first_table_offset(paddr),
>> -        second_table_offset(paddr),
>> -        third_table_offset(paddr)
>> -    };
>> -    const paddr_t masks[4] = {
>> -        ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK
>> -    };
>> -    lpae_t pte, *map;
>> +    lpae_t *entry;
>> +    int ret;
>> +    mfn_t mfn;
>> +
>> +    entry = *table + offset;
>> +
>> +    if ( !p2m_valid(*entry) )
>> +    {
>> +        if ( read_only )
>> +            return GUEST_TABLE_MAP_FAILED;
>> +
>> +        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
>> +        if ( ret )
>> +            return GUEST_TABLE_MAP_FAILED;
>> +    }
>> +
>> +    /* The function p2m_next_level is never called at the 3rd level */
>> +    if ( p2m_mapping(*entry) )
>> +        return GUEST_TABLE_SUPER_PAGE;
>> +
>> +    mfn = _mfn(entry->p2m.base);
>> +
>> +    unmap_domain_page(*table);
>> +    *table = map_domain_page(mfn);
>> +
>> +    return GUEST_TABLE_NORMAL_PAGE;
>
> I am a bit worried about having the same function doing the lookup and
> creating new tables, especially given it doesn't tell you whether the
> entry was already there or it was created: the return value is the same
> in both cases. At the very least the return values should be different.

I don't understand your worry here. Why would you care that the table 
has been allocated or was already existing?

If the caller does not want to allocate table, it can request to browse 
the entry in a read-only mode (see read_only).

>
>
>> +}
>> +
>> +/*
>> + * Get the details of a given gfn.
>> + *
>> + * If the entry is present, the associated MFN will be returned and the
>> + * access and type filled up. The page_order will correspond to the
>> + * order of the mapping in the page table (i.e it could be a superpage).
>> + *
>> + * If the entry is not present, INVALID_MFN will be returned and the
>> + * page_order will be set according to the order of the invalid range.
>> + */
>> +static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
>> +                           p2m_type_t *t, p2m_access_t *a,
>> +                           unsigned int *page_order)
>> +{
>> +    paddr_t addr = pfn_to_paddr(gfn_x(gfn));
>> +    unsigned int level = 0;
>> +    lpae_t entry, *table;
>> +    int rc;
>>      mfn_t mfn = INVALID_MFN;
>> -    paddr_t mask = 0;
>>      p2m_type_t _t;
>> -    unsigned int level;
>> +
>> +    /* Convenience aliases */
>> +    const unsigned int offsets[4] = {
>> +        zeroeth_table_offset(addr),
>> +        first_table_offset(addr),
>> +        second_table_offset(addr),
>> +        third_table_offset(addr)
>> +    };
>>
>>      ASSERT(p2m_is_locked(p2m));
>>      BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
>> @@ -267,46 +340,75 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>>
>>      *t = p2m_invalid;
>>
>> -    map = p2m_get_root_pointer(p2m, gfn);
>> -    if ( !map )
>> -        return INVALID_MFN;
>> +    /* XXX: Check if the mapping is lower than the mapped gfn */
>>
>> -    ASSERT(P2M_ROOT_LEVEL < 4);
>> -
>> -    for ( level = P2M_ROOT_LEVEL ; level < 4 ; level++ )
>> +    /* This gfn is higher than the highest the p2m map currently holds */
>> +    if ( gfn_x(gfn) > gfn_x(p2m->max_mapped_gfn) )
>>      {
>> -        mask = masks[level];
>> -
>> -        pte = map[offsets[level]];
>> +        for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
>> +        {
>> +            if ( (gfn_x(gfn) & (level_masks[level] >> PAGE_SHIFT)) >
>> +                 gfn_x(p2m->max_mapped_gfn) )
>> +                break;
>> +            goto out;
>
> I am not sure what this loop is for, but it looks wrong.

As mentioned in the description of the function, if the entry is not 
present, the function will return the order of the invalid range.

The loop will find the highest possible order by checking the base of 
the block mapping is greater than the max mapped gfn.

>
>
>> +        }
>> +    }
>>
>> -        if ( level == 3 && !p2m_table(pte) )
>> -            /* Invalid, clobber the pte */
>> -            pte.bits = 0;
>> -        if ( level == 3 || !p2m_table(pte) )
>> -            /* Done */
>> -            break;
>> +    table = p2m_get_root_pointer(p2m, gfn);
>>
>> -        ASSERT(level < 3);
>> +    /*
>> +     * the table should always be non-NULL because the gfn is below
>> +     * p2m->max_mapped_gfn and the root table pages are always present.
>> +     */
>> +    BUG_ON(table == NULL);
>>
>> -        /* Map for next level */
>> -        unmap_domain_page(map);
>> -        map = map_domain_page(_mfn(pte.p2m.base));
>> +    for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
>> +    {
>> +        rc = p2m_next_level(p2m, true, &table, offsets[level]);
>> +        if ( rc == GUEST_TABLE_MAP_FAILED )
>> +            goto out_unmap;
>> +        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
>> +            break;
>>      }
>>
>> -    unmap_domain_page(map);
>> +    entry = table[offsets[level]];
>>
>> -    if ( p2m_valid(pte) )
>> +    if ( p2m_valid(entry) )
>>      {
>> -        ASSERT(mask);
>> -        ASSERT(pte.p2m.type != p2m_invalid);
>> -        mfn = _mfn(paddr_to_pfn((pte.bits & PADDR_MASK & mask) |
>> -                                (paddr & ~mask)));
>> -        *t = pte.p2m.type;
>> +        *t = entry.p2m.type;
>
> Why don't you have a check for ( t ) like you have done in the case of
> ( a )? It would be more consistent.

This is done at the beginning of the function:

/* Allow t to be NULL */
t = t ?: &_t;

*t = p2m_invalid;

This was kept from the implementation of __p2m_lookup because some 
callers check the type before checking if the MFN is invalid (e.g 
get_page_from_gfn).

I didn't follow the same principle for the access because going through 
the radix tree is expensive and most of the hot path does not care about 
the access.

>
>
>> +
>> +        if ( a )
>> +            *a = p2m_mem_access_radix_get(p2m, gfn);
>> +
>> +        mfn = _mfn(entry.p2m.base);
>> +        /*
>> +         * The entry may point to a superpage. Find the MFN associated
>> +         * to the GFN.
>> +         */
>> +        mfn = mfn_add(mfn, gfn_x(gfn) & ((1UL << level_orders[level]) - 1));
>>      }
>>
>> +out_unmap:
>> +    unmap_domain_page(table);
>> +
>> +out:
>> +    if ( page_order )
>> +        *page_order = level_shifts[level] - PAGE_SHIFT;
>> +
>>      return mfn;
>>  }
>>
>> +/*
>> + * Lookup the MFN corresponding to a domain's GFN.
>> + *
>> + * There are no processor functions to do a stage 2 only lookup therefore we
>> + * do a a software walk.
>> + */
>> +static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>> +{
>> +    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
>> +}
>> +
>>  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>>  {
>>      mfn_t ret;
>> diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h
>> index 05d9f82..1c5bd8b 100644
>> --- a/xen/include/asm-arm/page.h
>> +++ b/xen/include/asm-arm/page.h
>> @@ -457,15 +457,19 @@ static inline int gva_to_ipa(vaddr_t va, paddr_t *paddr, unsigned int flags)
>>  #define LPAE_ENTRY_MASK (LPAE_ENTRIES - 1)
>>
>>  #define THIRD_SHIFT    (PAGE_SHIFT)
>> +#define THIRD_ORDER    0
>>  #define THIRD_SIZE     ((paddr_t)1 << THIRD_SHIFT)
>>  #define THIRD_MASK     (~(THIRD_SIZE - 1))
>>  #define SECOND_SHIFT   (THIRD_SHIFT + LPAE_SHIFT)
>> +#define SECOND_ORDER   (THIRD_ORDER + LPAE_SHIFT)
>>  #define SECOND_SIZE    ((paddr_t)1 << SECOND_SHIFT)
>>  #define SECOND_MASK    (~(SECOND_SIZE - 1))
>>  #define FIRST_SHIFT    (SECOND_SHIFT + LPAE_SHIFT)
>> +#define FIRST_ORDER    (SECOND_ORDER + LPAE_SHIFT)
>>  #define FIRST_SIZE     ((paddr_t)1 << FIRST_SHIFT)
>>  #define FIRST_MASK     (~(FIRST_SIZE - 1))
>>  #define ZEROETH_SHIFT  (FIRST_SHIFT + LPAE_SHIFT)
>> +#define ZEROETH_ORDER  (FIRST_ORDER + LPAE_SHIFT)
>>  #define ZEROETH_SIZE   ((paddr_t)1 << ZEROETH_SHIFT)
>>  #define ZEROETH_MASK   (~(ZEROETH_SIZE - 1))
>
> It might be clearer to define them by SHIFT:
>
>     #define THIRD_ORDER     (THIRD_SHIFT - PAGE_SHIFT)
>     #define SECOND_ORDER    (SECOND_SHIFT - PAGE_SHIFT)
>     #define FIRST_ORDER     (FIRST_SHIFT - PAGE_SHIFT)
>     #define ZEROETH_ORDER   (ZEROETH_SHIFT - PAGE_SHIFT)

I was following the way the other constant has been defined. The order 
of the 2nd level can be expressed in term of the order of the 3rd level...

I don't think this is clearer, but I don't mind to use them.

>
> or avoid them and just use level_shifts which is already defined.  I
> don't think they add much value.

Really? It avoids to spread - {PAGE,LPAE}_SHIFT everywhere in the code 
so the code will be cleaner.

Note that I have noticed that there are still few places where 
(level_shifts - PAGE_SHIFT) is still in use. However, we can replace by 
level_orders.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo
  2016-08-31 12:25     ` Julien Grall
@ 2016-08-31 19:33       ` Stefano Stabellini
  2016-09-01 11:37         ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-31 19:33 UTC (permalink / raw)
  To: Julien Grall
  Cc: proskurin, Stefano Stabellini, steve.capper, wei.chen, xen-devel

On Wed, 31 Aug 2016, Julien Grall wrote:
> > > @@ -236,28 +238,99 @@ static lpae_t *p2m_get_root_pointer(struct
> > > p2m_domain *p2m,
> > > 
> > >  /*
> > >   * Lookup the MFN corresponding to a domain's GFN.
> > > + * Lookup mem access in the ratrix tree.
> > > + * The entries associated to the GFN is considered valid.
> > > + */
> > > +static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m,
> > > gfn_t gfn)
> > > +{
> > > +    void *ptr;
> > > +
> > > +    if ( !p2m->mem_access_enabled )
> > > +        return p2m_access_rwx;
> > 
> > Shouldn't this be p2m->default_access?
> 
> default_access will always be p2m_access_rwx when memaccess is disabled. It
> will lead to crash a if you try to restrict permission without memaccess.
> 
> Note that, this is matching the behavior of __p2m_get_mem_access.

I would like to avoid some places to use p2m_access_rwx and others to
use p2m->default_access. But it is true that in this context both are
fine. This was just a suggestion.


> > > +    ptr = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
> > > +    if ( !ptr )
> > > +        return p2m_access_rwx;
> > 
> > Same here?
> 
> The radix tree will contain all the permission restriction but p2m_access_rwx.
> This is because you may change the default_access whilst memaccess is enabled
> and you don't know what page was restricted with the default access.
> 
> Note that this is matching the behavior of p2m_mem_access_radix_set.
> 
> > 
> > 
> > > +    else
> > > +        return radix_tree_ptr_to_int(ptr);
> > > +}
> > > +
> > > +#define GUEST_TABLE_MAP_FAILED 0
> > > +#define GUEST_TABLE_SUPER_PAGE 1
> > > +#define GUEST_TABLE_NORMAL_PAGE 2
> > > +
> > > +static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
> > > +                            int level_shift);
> > > +
> > > +/*
> > > + * Take the currently mapped table, find the corresponding GFN entry,
> > > + * and map the next table, if available.
> > 
> > It is important to write down that the function also unmaps the previous
> > table.
> 
> Will do.
> 
> > 
> > >   *
> > > - * There are no processor functions to do a stage 2 only lookup therefore
> > > we
> > > - * do a a software walk.
> > > + * Return values:
> > > + *  GUEST_TABLE_MAP_FAILED: Either read_only was set and the entry
> > > + *  was empty, or allocating a new page failed.
> > > + *  GUEST_TABLE_NORMAL_PAGE: next level mapped normally
> > > + *  GUEST_TABLE_SUPER_PAGE: The next entry points to a superpage.
> > >   */
> > > -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> > > +static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
> > > +                          lpae_t **table, unsigned int offset)
> > >  {
> > > -    struct p2m_domain *p2m = &d->arch.p2m;
> > > -    const paddr_t paddr = pfn_to_paddr(gfn_x(gfn));
> > > -    const unsigned int offsets[4] = {
> > > -        zeroeth_table_offset(paddr),
> > > -        first_table_offset(paddr),
> > > -        second_table_offset(paddr),
> > > -        third_table_offset(paddr)
> > > -    };
> > > -    const paddr_t masks[4] = {
> > > -        ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK
> > > -    };
> > > -    lpae_t pte, *map;
> > > +    lpae_t *entry;
> > > +    int ret;
> > > +    mfn_t mfn;
> > > +
> > > +    entry = *table + offset;
> > > +
> > > +    if ( !p2m_valid(*entry) )
> > > +    {
> > > +        if ( read_only )
> > > +            return GUEST_TABLE_MAP_FAILED;
> > > +
> > > +        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
> > > +        if ( ret )
> > > +            return GUEST_TABLE_MAP_FAILED;
> > > +    }
> > > +
> > > +    /* The function p2m_next_level is never called at the 3rd level */
> > > +    if ( p2m_mapping(*entry) )
> > > +        return GUEST_TABLE_SUPER_PAGE;
> > > +
> > > +    mfn = _mfn(entry->p2m.base);
> > > +
> > > +    unmap_domain_page(*table);
> > > +    *table = map_domain_page(mfn);
> > > +
> > > +    return GUEST_TABLE_NORMAL_PAGE;
> > 
> > I am a bit worried about having the same function doing the lookup and
> > creating new tables, especially given it doesn't tell you whether the
> > entry was already there or it was created: the return value is the same
> > in both cases. At the very least the return values should be different.
> 
> I don't understand your worry here. Why would you care that the table has been
> allocated or was already existing?
>
> If the caller does not want to allocate table, it can request to browse the
> entry in a read-only mode (see read_only).

To avoid unintentional side effects, such as calling this function for a
lookup but passing read-only as false by mistake. But maybe we just need
to be careful enough in the code reviews, after all this function won't
be called that many times.



> > > +}
> > > +
> > > +/*
> > > + * Get the details of a given gfn.
> > > + *
> > > + * If the entry is present, the associated MFN will be returned and the
> > > + * access and type filled up. The page_order will correspond to the
> > > + * order of the mapping in the page table (i.e it could be a superpage).
> > > + *
> > > + * If the entry is not present, INVALID_MFN will be returned and the
> > > + * page_order will be set according to the order of the invalid range.
> > > + */
> > > +static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
> > > +                           p2m_type_t *t, p2m_access_t *a,
> > > +                           unsigned int *page_order)
> > > +{
> > > +    paddr_t addr = pfn_to_paddr(gfn_x(gfn));
> > > +    unsigned int level = 0;
> > > +    lpae_t entry, *table;
> > > +    int rc;
> > >      mfn_t mfn = INVALID_MFN;
> > > -    paddr_t mask = 0;
> > >      p2m_type_t _t;
> > > -    unsigned int level;
> > > +
> > > +    /* Convenience aliases */
> > > +    const unsigned int offsets[4] = {
> > > +        zeroeth_table_offset(addr),
> > > +        first_table_offset(addr),
> > > +        second_table_offset(addr),
> > > +        third_table_offset(addr)
> > > +    };
> > > 
> > >      ASSERT(p2m_is_locked(p2m));
> > >      BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
> > > @@ -267,46 +340,75 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t
> > > gfn, p2m_type_t *t)
> > > 
> > >      *t = p2m_invalid;
> > > 
> > > -    map = p2m_get_root_pointer(p2m, gfn);
> > > -    if ( !map )
> > > -        return INVALID_MFN;
> > > +    /* XXX: Check if the mapping is lower than the mapped gfn */
> > > 
> > > -    ASSERT(P2M_ROOT_LEVEL < 4);
> > > -
> > > -    for ( level = P2M_ROOT_LEVEL ; level < 4 ; level++ )
> > > +    /* This gfn is higher than the highest the p2m map currently holds */
> > > +    if ( gfn_x(gfn) > gfn_x(p2m->max_mapped_gfn) )
> > >      {
> > > -        mask = masks[level];
> > > -
> > > -        pte = map[offsets[level]];
> > > +        for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
> > > +        {
> > > +            if ( (gfn_x(gfn) & (level_masks[level] >> PAGE_SHIFT)) >
> > > +                 gfn_x(p2m->max_mapped_gfn) )
> > > +                break;
> > > +            goto out;
> > 
> > I am not sure what this loop is for, but it looks wrong.
> 
> As mentioned in the description of the function, if the entry is not present,
> the function will return the order of the invalid range.
> 
> The loop will find the highest possible order by checking the base of the
> block mapping is greater than the max mapped gfn.

All right, but shouldn't the `goto out` be right after the loop?
Otherwise it is not really a loop :-)


> > > +        }
> > > +    }
> > > 
> > > -        if ( level == 3 && !p2m_table(pte) )
> > > -            /* Invalid, clobber the pte */
> > > -            pte.bits = 0;
> > > -        if ( level == 3 || !p2m_table(pte) )
> > > -            /* Done */
> > > -            break;
> > > +    table = p2m_get_root_pointer(p2m, gfn);
> > > 
> > > -        ASSERT(level < 3);
> > > +    /*
> > > +     * the table should always be non-NULL because the gfn is below
> > > +     * p2m->max_mapped_gfn and the root table pages are always present.
> > > +     */
> > > +    BUG_ON(table == NULL);
> > > 
> > > -        /* Map for next level */
> > > -        unmap_domain_page(map);
> > > -        map = map_domain_page(_mfn(pte.p2m.base));
> > > +    for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
> > > +    {
> > > +        rc = p2m_next_level(p2m, true, &table, offsets[level]);
> > > +        if ( rc == GUEST_TABLE_MAP_FAILED )
> > > +            goto out_unmap;
> > > +        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
> > > +            break;
> > >      }
> > > 
> > > -    unmap_domain_page(map);
> > > +    entry = table[offsets[level]];
> > > 
> > > -    if ( p2m_valid(pte) )
> > > +    if ( p2m_valid(entry) )
> > >      {
> > > -        ASSERT(mask);
> > > -        ASSERT(pte.p2m.type != p2m_invalid);
> > > -        mfn = _mfn(paddr_to_pfn((pte.bits & PADDR_MASK & mask) |
> > > -                                (paddr & ~mask)));
> > > -        *t = pte.p2m.type;
> > > +        *t = entry.p2m.type;
> > 
> > Why don't you have a check for ( t ) like you have done in the case of
> > ( a )? It would be more consistent.
> 
> This is done at the beginning of the function:
> 
> /* Allow t to be NULL */
> t = t ?: &_t;
> 
> *t = p2m_invalid;
> 
> This was kept from the implementation of __p2m_lookup because some callers
> check the type before checking if the MFN is invalid (e.g get_page_from_gfn).
> 
> I didn't follow the same principle for the access because going through the
> radix tree is expensive and most of the hot path does not care about the
> access.

Makes sense.


> > > +
> > > +        if ( a )
> > > +            *a = p2m_mem_access_radix_get(p2m, gfn);
> > > +
> > > +        mfn = _mfn(entry.p2m.base);
> > > +        /*
> > > +         * The entry may point to a superpage. Find the MFN associated
> > > +         * to the GFN.
> > > +         */
> > > +        mfn = mfn_add(mfn, gfn_x(gfn) & ((1UL << level_orders[level]) -
> > > 1));
> > >      }
> > > 
> > > +out_unmap:
> > > +    unmap_domain_page(table);
> > > +
> > > +out:
> > > +    if ( page_order )
> > > +        *page_order = level_shifts[level] - PAGE_SHIFT;
> > > +
> > >      return mfn;
> > >  }
> > > 
> > > +/*
> > > + * Lookup the MFN corresponding to a domain's GFN.
> > > + *
> > > + * There are no processor functions to do a stage 2 only lookup therefore
> > > we
> > > + * do a a software walk.
> > > + */
> > > +static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> > > +{
> > > +    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
> > > +}
> > > +
> > >  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> > >  {
> > >      mfn_t ret;
> > > diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h
> > > index 05d9f82..1c5bd8b 100644
> > > --- a/xen/include/asm-arm/page.h
> > > +++ b/xen/include/asm-arm/page.h
> > > @@ -457,15 +457,19 @@ static inline int gva_to_ipa(vaddr_t va, paddr_t
> > > *paddr, unsigned int flags)
> > >  #define LPAE_ENTRY_MASK (LPAE_ENTRIES - 1)
> > > 
> > >  #define THIRD_SHIFT    (PAGE_SHIFT)
> > > +#define THIRD_ORDER    0
> > >  #define THIRD_SIZE     ((paddr_t)1 << THIRD_SHIFT)
> > >  #define THIRD_MASK     (~(THIRD_SIZE - 1))
> > >  #define SECOND_SHIFT   (THIRD_SHIFT + LPAE_SHIFT)
> > > +#define SECOND_ORDER   (THIRD_ORDER + LPAE_SHIFT)
> > >  #define SECOND_SIZE    ((paddr_t)1 << SECOND_SHIFT)
> > >  #define SECOND_MASK    (~(SECOND_SIZE - 1))
> > >  #define FIRST_SHIFT    (SECOND_SHIFT + LPAE_SHIFT)
> > > +#define FIRST_ORDER    (SECOND_ORDER + LPAE_SHIFT)
> > >  #define FIRST_SIZE     ((paddr_t)1 << FIRST_SHIFT)
> > >  #define FIRST_MASK     (~(FIRST_SIZE - 1))
> > >  #define ZEROETH_SHIFT  (FIRST_SHIFT + LPAE_SHIFT)
> > > +#define ZEROETH_ORDER  (FIRST_ORDER + LPAE_SHIFT)
> > >  #define ZEROETH_SIZE   ((paddr_t)1 << ZEROETH_SHIFT)
> > >  #define ZEROETH_MASK   (~(ZEROETH_SIZE - 1))
> > 
> > It might be clearer to define them by SHIFT:
> > 
> >     #define THIRD_ORDER     (THIRD_SHIFT - PAGE_SHIFT)
> >     #define SECOND_ORDER    (SECOND_SHIFT - PAGE_SHIFT)
> >     #define FIRST_ORDER     (FIRST_SHIFT - PAGE_SHIFT)
> >     #define ZEROETH_ORDER   (ZEROETH_SHIFT - PAGE_SHIFT)
> 
> I was following the way the other constant has been defined. The order of the
> 2nd level can be expressed in term of the order of the 3rd level...
> 
> I don't think this is clearer, but I don't mind to use them.
>
> > or avoid them and just use level_shifts which is already defined.  I
> > don't think they add much value.
> 
> Really? It avoids to spread - {PAGE,LPAE}_SHIFT everywhere in the code so the
> code will be cleaner.
> 
> Note that I have noticed that there are still few places where (level_shifts -
> PAGE_SHIFT) is still in use. However, we can replace by level_orders.

The reason why I suggested the alternative implementation is that
"order" is not commonly used when dealing with pagetable levels (while
"mask" and "shift" are). I had a guess about what it meant, but wasn't
sure. To be sure I had to read the implementation. I think this version
is more obvious about what it does. That said, this is just a
suggestion, not a requirement.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch
  2016-08-16 16:20     ` Julien Grall
  2016-08-31 10:01       ` Julien Grall
@ 2016-08-31 19:43       ` Stefano Stabellini
  2016-09-06 14:54         ` Julien Grall
  1 sibling, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-08-31 19:43 UTC (permalink / raw)
  To: Julien Grall
  Cc: proskurin, Stefano Stabellini, steve.capper, wei.chen, xen-devel

On Tue, 16 Aug 2016, Julien Grall wrote:
> Hi Stefano,
> 
> On 16/08/2016 01:21, Stefano Stabellini wrote:
> > On Thu, 28 Jul 2016, Julien Grall wrote:
> > > A follow-up patch will add more case to the switch that will require the
> > > IPA. So move the computation out of the switch.
> > > 
> > > Signed-off-by: Julien Grall <julien.grall@arm.com>
> > > ---
> > >  xen/arch/arm/traps.c | 36 ++++++++++++++++++------------------
> > >  1 file changed, 18 insertions(+), 18 deletions(-)
> > > 
> > > diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> > > index 683bcb2..46e0663 100644
> > > --- a/xen/arch/arm/traps.c
> > > +++ b/xen/arch/arm/traps.c
> > > @@ -2403,35 +2403,35 @@ static void do_trap_instr_abort_guest(struct
> > > cpu_user_regs *regs,
> > >      int rc;
> > >      register_t gva = READ_SYSREG(FAR_EL2);
> > >      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
> > > +    paddr_t gpa;
> > > +
> > > +    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
> > > +        gpa = get_faulting_ipa(gva);
> > > +    else
> > > +    {
> > > +        /*
> > > +         * Flush the TLB to make sure the DTLB is clear before
> > > +         * doing GVA->IPA translation. If we got here because of
> > > +         * an entry only present in the ITLB, this translation may
> > > +         * still be inaccurate.
> > > +         */
> > > +        flush_tlb_local();
> > > +
> > > +        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
> > > +        if ( rc == -EFAULT )
> > > +            return; /* Try again */
> > 
> > The issue with this is that now for any cases that don't require a gpa
> > if gva_to_ipa fails we wrongly return -EFAULT.
> 
> Well, stage-1 fault is prioritized over stage-2 fault (see B3.12.3 in ARM DDI
> 0406C.b), so gva_to_ipa should never fail unless someone is playing with the
> stage-1 page table at the same time or because of an erratum (see 834220). In
> both case, we should replay the instruction to let the processor injecting the
> correct fault.
> 
> FWIW, this is already what we do for the data abort handler.
>
> > 
> > I suggest having two switches or falling through from the first case to
> > the second.
> 
> I am not sure to understand your suggestion. Could you detail it?

I was merely suggesting to add another switch like:

  +   switch ( fsc )
  +   {
  +   case FSC_FLT_PERM:
  +   case BLA:
  +       find gpa;
  +   default:
  +   }

     switch ( fsc )
     {

but given that we are already relying on the gva_to_ipa translation in
the data abort handler, your patch is fine too.

Acked-by: Stefano Stabellini <sstabellini@kernel.org>

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo
  2016-08-31 19:33       ` Stefano Stabellini
@ 2016-09-01 11:37         ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-09-01 11:37 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 31/08/16 20:33, Stefano Stabellini wrote:
> On Wed, 31 Aug 2016, Julien Grall wrote:
>>>> @@ -236,28 +238,99 @@ static lpae_t *p2m_get_root_pointer(struct
>>>> p2m_domain *p2m,
>>>>
>>>>  /*
>>>>   * Lookup the MFN corresponding to a domain's GFN.
>>>> + * Lookup mem access in the ratrix tree.
>>>> + * The entries associated to the GFN is considered valid.
>>>> + */
>>>> +static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m,
>>>> gfn_t gfn)
>>>> +{
>>>> +    void *ptr;
>>>> +
>>>> +    if ( !p2m->mem_access_enabled )
>>>> +        return p2m_access_rwx;
>>>
>>> Shouldn't this be p2m->default_access?
>>
>> default_access will always be p2m_access_rwx when memaccess is disabled. It
>> will lead to crash a if you try to restrict permission without memaccess.
>>
>> Note that, this is matching the behavior of __p2m_get_mem_access.
>
> I would like to avoid some places to use p2m_access_rwx and others to
> use p2m->default_access. But it is true that in this context both are
> fine. This was just a suggestion.

Thinking a bit more, this will allow us to catch any error where 
mem_access is not enabled but default_access is not p2m_access_rwx.

I will use p2m->default_access here for this case (the one below should 
stay p2m_access_rwx).

>
>>>> +    ptr = radix_tree_lookup(&p2m->mem_access_settings, gfn_x(gfn));
>>>> +    if ( !ptr )
>>>> +        return p2m_access_rwx;
>>>
>>> Same here?
>>
>> The radix tree will contain all the permission restriction but p2m_access_rwx.
>> This is because you may change the default_access whilst memaccess is enabled
>> and you don't know what page was restricted with the default access.
>>
>> Note that this is matching the behavior of p2m_mem_access_radix_set.

[...]

>>>
>>>>   *
>>>> - * There are no processor functions to do a stage 2 only lookup therefore
>>>> we
>>>> - * do a a software walk.
>>>> + * Return values:
>>>> + *  GUEST_TABLE_MAP_FAILED: Either read_only was set and the entry
>>>> + *  was empty, or allocating a new page failed.
>>>> + *  GUEST_TABLE_NORMAL_PAGE: next level mapped normally
>>>> + *  GUEST_TABLE_SUPER_PAGE: The next entry points to a superpage.
>>>>   */
>>>> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>>>> +static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
>>>> +                          lpae_t **table, unsigned int offset)
>>>>  {
>>>> -    struct p2m_domain *p2m = &d->arch.p2m;
>>>> -    const paddr_t paddr = pfn_to_paddr(gfn_x(gfn));
>>>> -    const unsigned int offsets[4] = {
>>>> -        zeroeth_table_offset(paddr),
>>>> -        first_table_offset(paddr),
>>>> -        second_table_offset(paddr),
>>>> -        third_table_offset(paddr)
>>>> -    };
>>>> -    const paddr_t masks[4] = {
>>>> -        ZEROETH_MASK, FIRST_MASK, SECOND_MASK, THIRD_MASK
>>>> -    };
>>>> -    lpae_t pte, *map;
>>>> +    lpae_t *entry;
>>>> +    int ret;
>>>> +    mfn_t mfn;
>>>> +
>>>> +    entry = *table + offset;
>>>> +
>>>> +    if ( !p2m_valid(*entry) )
>>>> +    {
>>>> +        if ( read_only )
>>>> +            return GUEST_TABLE_MAP_FAILED;
>>>> +
>>>> +        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
>>>> +        if ( ret )
>>>> +            return GUEST_TABLE_MAP_FAILED;
>>>> +    }
>>>> +
>>>> +    /* The function p2m_next_level is never called at the 3rd level */
>>>> +    if ( p2m_mapping(*entry) )
>>>> +        return GUEST_TABLE_SUPER_PAGE;
>>>> +
>>>> +    mfn = _mfn(entry->p2m.base);
>>>> +
>>>> +    unmap_domain_page(*table);
>>>> +    *table = map_domain_page(mfn);
>>>> +
>>>> +    return GUEST_TABLE_NORMAL_PAGE;
>>>
>>> I am a bit worried about having the same function doing the lookup and
>>> creating new tables, especially given it doesn't tell you whether the
>>> entry was already there or it was created: the return value is the same
>>> in both cases. At the very least the return values should be different.
>>
>> I don't understand your worry here. Why would you care that the table has been
>> allocated or was already existing?
>>
>> If the caller does not want to allocate table, it can request to browse the
>> entry in a read-only mode (see read_only).
>
> To avoid unintentional side effects, such as calling this function for a
> lookup but passing read-only as false by mistake. But maybe we just need
> to be careful enough in the code reviews, after all this function won't
> be called that many times.

This would be the same if the caller does not check the return value 
properly, thinking page table will not be allocated.

I prefer to keep the interface like that for now and rely on the review. 
I will also document the behavior of read_only bit in the next version.

>
>
>
>>>> +}
>>>> +
>>>> +/*
>>>> + * Get the details of a given gfn.
>>>> + *
>>>> + * If the entry is present, the associated MFN will be returned and the
>>>> + * access and type filled up. The page_order will correspond to the
>>>> + * order of the mapping in the page table (i.e it could be a superpage).
>>>> + *
>>>> + * If the entry is not present, INVALID_MFN will be returned and the
>>>> + * page_order will be set according to the order of the invalid range.
>>>> + */
>>>> +static mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn,
>>>> +                           p2m_type_t *t, p2m_access_t *a,
>>>> +                           unsigned int *page_order)
>>>> +{
>>>> +    paddr_t addr = pfn_to_paddr(gfn_x(gfn));
>>>> +    unsigned int level = 0;
>>>> +    lpae_t entry, *table;
>>>> +    int rc;
>>>>      mfn_t mfn = INVALID_MFN;
>>>> -    paddr_t mask = 0;
>>>>      p2m_type_t _t;
>>>> -    unsigned int level;
>>>> +
>>>> +    /* Convenience aliases */
>>>> +    const unsigned int offsets[4] = {
>>>> +        zeroeth_table_offset(addr),
>>>> +        first_table_offset(addr),
>>>> +        second_table_offset(addr),
>>>> +        third_table_offset(addr)
>>>> +    };
>>>>
>>>>      ASSERT(p2m_is_locked(p2m));
>>>>      BUILD_BUG_ON(THIRD_MASK != PAGE_MASK);
>>>> @@ -267,46 +340,75 @@ static mfn_t __p2m_lookup(struct domain *d, gfn_t
>>>> gfn, p2m_type_t *t)
>>>>
>>>>      *t = p2m_invalid;
>>>>
>>>> -    map = p2m_get_root_pointer(p2m, gfn);
>>>> -    if ( !map )
>>>> -        return INVALID_MFN;
>>>> +    /* XXX: Check if the mapping is lower than the mapped gfn */
>>>>
>>>> -    ASSERT(P2M_ROOT_LEVEL < 4);
>>>> -
>>>> -    for ( level = P2M_ROOT_LEVEL ; level < 4 ; level++ )
>>>> +    /* This gfn is higher than the highest the p2m map currently holds */
>>>> +    if ( gfn_x(gfn) > gfn_x(p2m->max_mapped_gfn) )
>>>>      {
>>>> -        mask = masks[level];
>>>> -
>>>> -        pte = map[offsets[level]];
>>>> +        for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
>>>> +        {
>>>> +            if ( (gfn_x(gfn) & (level_masks[level] >> PAGE_SHIFT)) >
>>>> +                 gfn_x(p2m->max_mapped_gfn) )
>>>> +                break;
>>>> +            goto out;
>>>
>>> I am not sure what this loop is for, but it looks wrong.
>>
>> As mentioned in the description of the function, if the entry is not present,
>> the function will return the order of the invalid range.
>>
>> The loop will find the highest possible order by checking the base of the
>> block mapping is greater than the max mapped gfn.
>
> All right, but shouldn't the `goto out` be right after the loop?
> Otherwise it is not really a loop :-)

Oh, right. Wei Chen pointed me out this issue a couple of weeks ago and 
I fixed it in my development branch:

         for ( level = P2M_ROOT_LEVEL; level < 3; level++ )
             if ( (gfn_x(gfn) & (level_masks[level] >> PAGE_SHIFT)) >
                  gfn_x(p2m->max_mapped_gfn) )
                 break;

         goto out;

>
>
>>>> +        }
>>>> +    }

[..]

>>>> +
>>>> +        if ( a )
>>>> +            *a = p2m_mem_access_radix_get(p2m, gfn);
>>>> +
>>>> +        mfn = _mfn(entry.p2m.base);
>>>> +        /*
>>>> +         * The entry may point to a superpage. Find the MFN associated
>>>> +         * to the GFN.
>>>> +         */
>>>> +        mfn = mfn_add(mfn, gfn_x(gfn) & ((1UL << level_orders[level]) -
>>>> 1));
>>>>      }
>>>>
>>>> +out_unmap:
>>>> +    unmap_domain_page(table);
>>>> +
>>>> +out:
>>>> +    if ( page_order )
>>>> +        *page_order = level_shifts[level] - PAGE_SHIFT;
>>>> +
>>>>      return mfn;
>>>>  }
>>>>
>>>> +/*
>>>> + * Lookup the MFN corresponding to a domain's GFN.
>>>> + *
>>>> + * There are no processor functions to do a stage 2 only lookup therefore
>>>> we
>>>> + * do a a software walk.
>>>> + */
>>>> +static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>>>> +{
>>>> +    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
>>>> +}
>>>> +
>>>>  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>>>>  {
>>>>      mfn_t ret;
>>>> diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h
>>>> index 05d9f82..1c5bd8b 100644
>>>> --- a/xen/include/asm-arm/page.h
>>>> +++ b/xen/include/asm-arm/page.h
>>>> @@ -457,15 +457,19 @@ static inline int gva_to_ipa(vaddr_t va, paddr_t
>>>> *paddr, unsigned int flags)
>>>>  #define LPAE_ENTRY_MASK (LPAE_ENTRIES - 1)
>>>>
>>>>  #define THIRD_SHIFT    (PAGE_SHIFT)
>>>> +#define THIRD_ORDER    0
>>>>  #define THIRD_SIZE     ((paddr_t)1 << THIRD_SHIFT)
>>>>  #define THIRD_MASK     (~(THIRD_SIZE - 1))
>>>>  #define SECOND_SHIFT   (THIRD_SHIFT + LPAE_SHIFT)
>>>> +#define SECOND_ORDER   (THIRD_ORDER + LPAE_SHIFT)
>>>>  #define SECOND_SIZE    ((paddr_t)1 << SECOND_SHIFT)
>>>>  #define SECOND_MASK    (~(SECOND_SIZE - 1))
>>>>  #define FIRST_SHIFT    (SECOND_SHIFT + LPAE_SHIFT)
>>>> +#define FIRST_ORDER    (SECOND_ORDER + LPAE_SHIFT)
>>>>  #define FIRST_SIZE     ((paddr_t)1 << FIRST_SHIFT)
>>>>  #define FIRST_MASK     (~(FIRST_SIZE - 1))
>>>>  #define ZEROETH_SHIFT  (FIRST_SHIFT + LPAE_SHIFT)
>>>> +#define ZEROETH_ORDER  (FIRST_ORDER + LPAE_SHIFT)
>>>>  #define ZEROETH_SIZE   ((paddr_t)1 << ZEROETH_SHIFT)
>>>>  #define ZEROETH_MASK   (~(ZEROETH_SIZE - 1))
>>>
>>> It might be clearer to define them by SHIFT:
>>>
>>>     #define THIRD_ORDER     (THIRD_SHIFT - PAGE_SHIFT)
>>>     #define SECOND_ORDER    (SECOND_SHIFT - PAGE_SHIFT)
>>>     #define FIRST_ORDER     (FIRST_SHIFT - PAGE_SHIFT)
>>>     #define ZEROETH_ORDER   (ZEROETH_SHIFT - PAGE_SHIFT)
>>
>> I was following the way the other constant has been defined. The order of the
>> 2nd level can be expressed in term of the order of the 3rd level...
>>
>> I don't think this is clearer, but I don't mind to use them.
>>
>>> or avoid them and just use level_shifts which is already defined.  I
>>> don't think they add much value.
>>
>> Really? It avoids to spread - {PAGE,LPAE}_SHIFT everywhere in the code so the
>> code will be cleaner.
>>
>> Note that I have noticed that there are still few places where (level_shifts -
>> PAGE_SHIFT) is still in use. However, we can replace by level_orders.
>
> The reason why I suggested the alternative implementation is that
> "order" is not commonly used when dealing with pagetable levels (while
> "mask" and "shift" are). I had a guess about what it meant, but wasn't
> sure. To be sure I had to read the implementation. I think this version
> is more obvious about what it does. That said, this is just a
> suggestion, not a requirement.

Most of the generic Xen function are dealing with order and frame number 
(see guest_physmap_*). Those defines avoid to have to move back and 
forth between address and frame number. This helps to keep the typesafe 
gfn/mfn as far as possible.

I am happy to use the definition you suggested and document a bit more 
the code (though I am not sure what to say here).

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry
  2016-07-28 14:51 ` [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry Julien Grall
  2016-07-28 17:29   ` Tamas K Lengyel
@ 2016-09-05 20:45   ` Stefano Stabellini
  1 sibling, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-05 20:45 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin, xen-devel,
	Tamas K Lengyel, wei.chen

On Thu, 28 Jul 2016, Julien Grall wrote:
> __p2m_lookup is just a wrapper to p2m_get_entry.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> Cc: Tamas K Lengyel <tamas@tklengyel.com>

Acked-by: Stefano Stabellini <sstabellini@kernel.org>


> ---
>     It might be possible to rework the memaccess code to take advantage
>     of all the parameters. I will defer this to the memaccess folks.
> ---
>  xen/arch/arm/p2m.c | 18 ++++--------------
>  1 file changed, 4 insertions(+), 14 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 8676b9d..9a9c85c 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -398,24 +398,13 @@ out:
>      return mfn;
>  }
>  
> -/*
> - * Lookup the MFN corresponding to a domain's GFN.
> - *
> - * There are no processor functions to do a stage 2 only lookup therefore we
> - * do a a software walk.
> - */
> -static mfn_t __p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
> -{
> -    return p2m_get_entry(&d->arch.p2m, gfn, t, NULL, NULL);
> -}
> -
>  mfn_t p2m_lookup(struct domain *d, gfn_t gfn, p2m_type_t *t)
>  {
>      mfn_t ret;
>      struct p2m_domain *p2m = &d->arch.p2m;
>  
>      p2m_read_lock(p2m);
> -    ret = __p2m_lookup(d, gfn, t);
> +    ret = p2m_get_entry(p2m, gfn, t, NULL, NULL);
>      p2m_read_unlock(p2m);
>  
>      return ret;
> @@ -679,7 +668,7 @@ static int __p2m_get_mem_access(struct domain *d, gfn_t gfn,
>           * No setting was found in the Radix tree. Check if the
>           * entry exists in the page-tables.
>           */
> -        mfn_t mfn = __p2m_lookup(d, gfn, NULL);
> +        mfn_t mfn = p2m_get_entry(p2m, gfn, NULL, NULL, NULL);
>  
>          if ( mfn_eq(mfn, INVALID_MFN) )
>              return -ESRCH;
> @@ -1595,6 +1584,7 @@ p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag)
>      xenmem_access_t xma;
>      p2m_type_t t;
>      struct page_info *page = NULL;
> +    struct p2m_domain *p2m = &current->domain->arch.p2m;
>  
>      rc = gva_to_ipa(gva, &ipa, flag);
>      if ( rc < 0 )
> @@ -1655,7 +1645,7 @@ p2m_mem_access_check_and_get_page(vaddr_t gva, unsigned long flag)
>       * We had a mem_access permission limiting the access, but the page type
>       * could also be limiting, so we need to check that as well.
>       */
> -    mfn = __p2m_lookup(current->domain, gfn, &t);
> +    mfn = p2m_get_entry(p2m, gfn, &t, NULL, NULL);
>      if ( mfn_eq(mfn, INVALID_MFN) )
>          goto err;
>  
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
  2016-07-28 14:51 ` [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry Julien Grall
@ 2016-09-05 21:13   ` Stefano Stabellini
  2016-09-06 14:56     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-05 21:13 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The function p2m_cache_flush can be re-implemented using the generic
> function p2m_get_entry by iterating over the range and using the mapping
> order given by the callee.
> 
> As the current implementation, no preemption is implemented, although
> the comment in the current code claimed it. As the function is called by
> a DOMCTL with a region of 1GB maximum, I think the preemption can be
> left unimplemented for now.
> 
> Finally drop the operation CACHEFLUSH in apply_one_level as nobody is
> using it anymore. Note that the function could have been dropped in one
> go at the end, however I find easier to drop the operations one by one
> avoiding a big deletion in the patch that convert the last operation.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> 
> ---
>     The loop pattern will be very for the reliquish function. It might
>     be possible to extract it in a separate function.
> ---
>  xen/arch/arm/p2m.c | 67 +++++++++++++++++++++++++++---------------------------
>  1 file changed, 34 insertions(+), 33 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 9a9c85c..e7697bb 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -722,7 +722,6 @@ enum p2m_operation {
>      INSERT,
>      REMOVE,
>      RELINQUISH,
> -    CACHEFLUSH,
>      MEMACCESS,
>  };
>  
> @@ -978,36 +977,6 @@ static int apply_one_level(struct domain *d,
>           */
>          return P2M_ONE_PROGRESS;
>  
> -    case CACHEFLUSH:
> -        if ( !p2m_valid(orig_pte) )
> -        {
> -            *addr = (*addr + level_size) & level_mask;
> -            return P2M_ONE_PROGRESS_NOP;
> -        }
> -
> -        if ( level < 3 && p2m_table(orig_pte) )
> -            return P2M_ONE_DESCEND;
> -
> -        /*
> -         * could flush up to the next superpage boundary, but would
> -         * need to be careful about preemption, so just do one 4K page
> -         * now and return P2M_ONE_PROGRESS{,_NOP} so that the caller will
> -         * continue to loop over the rest of the range.
> -         */
> -        if ( p2m_is_ram(orig_pte.p2m.type) )
> -        {
> -            unsigned long offset = paddr_to_pfn(*addr & ~level_mask);
> -            flush_page_to_ram(orig_pte.p2m.base + offset);
> -
> -            *addr += PAGE_SIZE;
> -            return P2M_ONE_PROGRESS;
> -        }
> -        else
> -        {
> -            *addr += PAGE_SIZE;
> -            return P2M_ONE_PROGRESS_NOP;
> -        }
> -
>      case MEMACCESS:
>          if ( level < 3 )
>          {
> @@ -1555,12 +1524,44 @@ int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr)
>  {
>      struct p2m_domain *p2m = &d->arch.p2m;
>      gfn_t end = gfn_add(start, nr);
> +    p2m_type_t t;
> +    unsigned int order;
>  
>      start = gfn_max(start, p2m->lowest_mapped_gfn);
>      end = gfn_min(end, p2m->max_mapped_gfn);
>  
> -    return apply_p2m_changes(d, CACHEFLUSH, start, nr, INVALID_MFN,
> -                             0, p2m_invalid, d->arch.p2m.default_access);
> +    /* XXX: Should we use write lock here? */

Good question. As the p2m is left unchanged by this function, I think
that the read lock is sufficient.


> +    p2m_read_lock(p2m);
> +
> +    for ( ; gfn_x(start) < gfn_x(end); start = gfn_add(start, 1UL << order) )
> +    {
> +        mfn_t mfn = p2m_get_entry(p2m, start, &t, NULL, &order);
> +
> +        /* Skip hole and non-RAM page */
> +        if ( mfn_eq(mfn, INVALID_MFN) || !p2m_is_ram(t) )
> +        {
> +            /*
> +             * the order corresponds to the order of the mapping in the
> +             * page table. so we need to align the gfn before
> +             * incrementing.
> +             */
> +            start = _gfn(gfn_x(start) & ~((1UL << order) - 1));
> +            continue;
> +        }
> +
> +        /*
> +         * Could flush up to the next superpage boundary, but we would
> +         * need to be careful about preemption, so just do one 4K page
> +         * now.

I think that even without preemption you should implement flushing up to
the next superpage boundary (but not beyond "end"). You can still do it
4K at a time, but only call p2m_get_entry once per "order". Could be a
decent performance improvement as cacheflush is a performance critical
hypercall.


> +         * XXX: Implement preemption.
> +         */
> +        flush_page_to_ram(mfn_x(mfn));
> +        order = 0;
> +    }
> +
> +    p2m_read_unlock(p2m);
> +
> +    return 0;
>  }
>  
>  mfn_t gfn_to_mfn(struct domain *d, gfn_t gfn)
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
  2016-07-28 14:51 ` [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping " Julien Grall
@ 2016-09-05 21:58   ` Stefano Stabellini
  2016-09-06 15:05     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-05 21:58 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The current implementation of relinquish_p2m_mapping is modifying the
> page table to erase the entry one by one. However, this is not necessary
> because the domain is not running anymore and therefore will speed up
> the domain destruction.

Could you please elaborate on this? Who is going to remove the p2m
entries if not this function?


> The function relinquish_p2m_mapping can be re-implemented using
> p2m_get_entry by iterating over the range mapped and using the mapping
> order given by the callee.
> 
> Given that the preemption was chosen arbitrarily, it is no done on every
                                                          ^ now?

> 512 iterations. Meaning that Xen may check more often if the function is
> preempted when there are no mappings.
> 
> Finally drop the operation RELINQUISH in apply_* as nobody is using it
> anymore. Note that the functions could have been dropped in one go at
> the end, however I find easier to drop the operations one by one
> avoiding a big deletion in the patch that remove the last operation.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> 
> ---
>     Further investigation needs to be done before applying this patch to
>     check if someone could take advantage of this change (such
>     modifying an entry which was relinquished).
> ---
>  xen/arch/arm/p2m.c | 70 ++++++++++++++++++++++++++++++++++++++++--------------
>  1 file changed, 52 insertions(+), 18 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index e7697bb..d0aba5b 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -721,7 +721,6 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
>  enum p2m_operation {
>      INSERT,
>      REMOVE,
> -    RELINQUISH,
>      MEMACCESS,
>  };
>  
> @@ -908,7 +907,6 @@ static int apply_one_level(struct domain *d,
>  
>          break;
>  
> -    case RELINQUISH:
>      case REMOVE:
>          if ( !p2m_valid(orig_pte) )
>          {
> @@ -1092,17 +1090,6 @@ static int apply_p2m_changes(struct domain *d,
>          {
>              switch ( op )
>              {
> -            case RELINQUISH:
> -                /*
> -                 * Arbitrarily, preempt every 512 operations or 8192 nops.
> -                 * 512*P2M_ONE_PROGRESS == 8192*P2M_ONE_PROGRESS_NOP == 0x2000
> -                 * This is set in preempt_count_limit.
> -                 *
> -                 */
> -                p2m->lowest_mapped_gfn = _gfn(addr >> PAGE_SHIFT);
> -                rc = -ERESTART;
> -                goto out;
> -
>              case MEMACCESS:
>              {
>                  /*
> @@ -1508,16 +1495,63 @@ int p2m_init(struct domain *d)
>      return rc;
>  }
>  
> +/*
> + * The function will go through the p2m and remove page reference when it
> + * is required.
> + * The mapping are left intact in the p2m. This is fine because the
> + * domain will never run at that point.
> + *
> + * XXX: Check what does it mean for other part (such as lookup)
> + */
>  int relinquish_p2m_mapping(struct domain *d)
>  {
>      struct p2m_domain *p2m = &d->arch.p2m;
> -    unsigned long nr;
> +    unsigned long count = 0;
> +    p2m_type_t t;
> +    int rc = 0;
> +    unsigned int order;
> +
> +    /* Convenience alias */
> +    gfn_t start = p2m->lowest_mapped_gfn;
> +    gfn_t end = p2m->max_mapped_gfn;
>  
> -    nr = gfn_x(p2m->max_mapped_gfn) - gfn_x(p2m->lowest_mapped_gfn);
> +    p2m_write_lock(p2m);
>  
> -    return apply_p2m_changes(d, RELINQUISH, p2m->lowest_mapped_gfn, nr,
> -                             INVALID_MFN, 0, p2m_invalid,
> -                             d->arch.p2m.default_access);
> +    for ( ; gfn_x(start) < gfn_x(end); start = gfn_add(start, 1UL << order) )
> +    {
> +        mfn_t mfn = p2m_get_entry(p2m, start, &t, NULL, &order);
> +
> +        count++;
> +        /*
> +         * Arbitrarily preempt every 512 iterations.
> +         */
> +        if ( !(count % 512) && hypercall_preempt_check() )
> +        {
> +            rc = -ERESTART;
> +            break;
> +        }
> +
> +        /* Skip hole and any superpage */
> +        if ( mfn_eq(mfn, INVALID_MFN) || order != 0 )
> +            /*
> +             * The order corresponds to the order of the mapping in the
> +             * page table. So we need to align the GFN before
> +             * incrementing.
> +             */
> +            start = _gfn(gfn_x(start) & ~((1UL << order) - 1));
> +        else
> +            p2m_put_l3_page(mfn, t);
> +    }
> +
> +    /*
> +     * Update lowest_mapped_gfn so on the next call we still start where
> +     * we stopped.
> +     */
> +    p2m->lowest_mapped_gfn = start;
> +
> +    p2m_write_unlock(p2m);
> +
> +    return rc;
>  }
>  
>  int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr)

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 16/22] xen/arm: p2m: Make p2m_{valid, table, mapping} helpers inline
  2016-07-28 14:51 ` [RFC 16/22] xen/arm: p2m: Make p2m_{valid, table, mapping} helpers inline Julien Grall
@ 2016-09-05 22:00   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-05 22:00 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Those helpers are very small and often used. Let know the compiler they
> can be inlined.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index d0aba5b..ca2f1b0 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -39,7 +39,7 @@ static const unsigned int level_shifts[] =
>  static const unsigned int level_orders[] =
>      { ZEROETH_ORDER, FIRST_ORDER, SECOND_ORDER, THIRD_ORDER };
>  
> -static bool_t p2m_valid(lpae_t pte)
> +static inline bool_t p2m_valid(lpae_t pte)
>  {
>      return pte.p2m.valid;
>  }
> @@ -48,11 +48,11 @@ static bool_t p2m_valid(lpae_t pte)
>   * the table bit and therefore these would return the opposite to what
>   * you would expect.
>   */
> -static bool_t p2m_table(lpae_t pte)
> +static inline bool_t p2m_table(lpae_t pte)
>  {
>      return p2m_valid(pte) && pte.p2m.table;
>  }
> -static bool_t p2m_mapping(lpae_t pte)
> +static inline bool_t p2m_mapping(lpae_t pte)
>  {
>      return p2m_valid(pte) && !pte.p2m.table;
>  }
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 17/22] xen/arm: p2m: Introduce a helper to check if an entry is a superpage
  2016-07-28 14:51 ` [RFC 17/22] xen/arm: p2m: Introduce a helper to check if an entry is a superpage Julien Grall
@ 2016-09-05 22:03   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-05 22:03 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> Use the level and the entry to know whether an entry is a superpage.
> A superpage can only happen below level 3.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index ca2f1b0..c93e554 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -57,6 +57,11 @@ static inline bool_t p2m_mapping(lpae_t pte)
>      return p2m_valid(pte) && !pte.p2m.table;
>  }
>  
> +static inline bool_t p2m_is_superpage(lpae_t pte, unsigned int level)
> +{
> +    return (level < 3) && p2m_mapping(pte);
> +}
>
>  static inline void p2m_write_lock(struct p2m_domain *p2m)
>  {
>      write_lock(&p2m->lock);
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-07-28 14:51 ` [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry Julien Grall
  2016-07-30 18:40   ` Tamas K Lengyel
  2016-08-15 10:22   ` Sergej Proskurin
@ 2016-09-06  1:08   ` Stefano Stabellini
  2016-09-06 17:12     ` Julien Grall
  2 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06  1:08 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The ARM architecture mandates to use of a break-before-make sequence
> when changing translation entries if the page table is shared between
> multiple CPUs whenever a valid entry is replaced by another valid entry
> (see D4.7.1 in ARM DDI 0487A.j for more details).
> 
> The break-before-make sequence can be divided in the following steps:
>     1) Invalidate the old entry in the page table
>     2) Issue a TLB invalidation instruction for the address associated
>     to this entry
>     3) Write the new entry
> 
> The current P2M code implemented in apply_one_level does not respect
> this sequence and may result to break coherency on some processors.
> 
> Adapting the current implementation to use the break-before-make
> sequence would imply some code duplication and more TLBs invalidation
> than necessary. For instance, if we are replacing a 4KB page and the
> current mapping in the P2M is using a 1GB superpage, the following steps
> will happen:
>     1) Shatter the 1GB superpage into a series of 2MB superpages
>     2) Shatter the 2MB superpage into a series of 4KB pages
>     3) Replace the 4KB page
> 
> As the current implementation is shattering while descending and install
> the mapping, Xen would need to issue 3 TLB invalidation instructions
> which is clearly inefficient.
> 
> Furthermore, all the operations which modify the page table are using
> the same skeleton. It is more complicated to maintain different code paths
> than having a generic function that set an entry and take care of the
> break-before-make sequence.
> 
> The new implementation is based on the x86 EPT one which, I think,
> fits quite well for the break-before-make sequence whilst keeping
> the code simple.
> 
> The main function of the new implementation is __p2m_get_entry. It will
> only work on mapping that are aligned to a block entry in the page table
> (i.e 1GB, 2MB, 4KB when using a 4KB granularity).
> 
> Another function, p2m_get_entry, is provided to break down is region
> into mapping that is aligned to a block entry.
> 
> Note that to keep this patch "small", there are no caller of those
> functions added in this patch (they will be added in follow-up patches).
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> 
> ---
>     I need to find the impact of this new implementation on ARM32 because
>     the domheap is not always mapped. This means that Xen needs to map/unmap
>     everytime the page associated to the page table. It might be possible
>     to re-use some caching structure as the current implementation does
>     or rework the way a domheap is mapped/unmapped.
> 
>     Also, this code still contain few TODOs mostly to add sanity
>     checks and few optimization. The IOMMU is not yet supported.
> ---
>  xen/arch/arm/p2m.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 335 insertions(+)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index c93e554..297b176 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -750,6 +750,341 @@ static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
>      }
>  }
>  
> +#if 0
> +/* Free lpae sub-tree behind an entry */
> +static void p2m_free_entry(struct p2m_domain *p2m,
> +                           lpae_t entry, unsigned int level)
> +{
> +    unsigned int i;
> +    lpae_t *table;
> +    mfn_t mfn;
> +
> +    /* Nothing to do if the entry is invalid or a super-page */

Why are we not freeing superpages? It looks like this function could be
correctly called on a superpage (with level == target).


> +    if ( !p2m_valid(entry) || p2m_is_superpage(entry, level) )
> +        return;
> +
> +    if ( level == 3 )
> +    {
> +        p2m_put_l3_page(_mfn(entry.p2m.base), entry.p2m.type);
> +        return;
> +    }
> +
> +    table = map_domain_page(_mfn(entry.p2m.base));
> +    for ( i = 0; i < LPAE_ENTRIES; i++ )
> +        p2m_free_entry(p2m, *(table + i), level + 1);
> +
> +    unmap_domain_page(table);
> +
> +    /*
> +     * Make sure all the references in the TLB have been removed before
> +     * freing the intermediate page table.
> +     * XXX: Should we defer the free of the page table to avoid the
> +     * flush?

Yes, I would probably move the p2m flush out of this function.


> +     */
> +    if ( p2m->need_flush )
> +        p2m_flush_tlb_sync(p2m);
> +
> +    mfn = _mfn(entry.p2m.base);
> +    ASSERT(mfn_valid(mfn_x(mfn)));
> +
> +    free_domheap_page(mfn_to_page(mfn_x(mfn)));
> +}
> +
> +static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry,
> +                                unsigned int level, unsigned int target,
> +                                const unsigned int *offsets)
> +{
> +    struct page_info *page;
> +    unsigned int i;
> +    lpae_t pte, *table;
> +    bool rv = true;
> +
> +    /* Convenience aliases */
> +    p2m_type_t t = entry->p2m.type;
> +    mfn_t mfn = _mfn(entry->p2m.base);
> +
> +    /* Convenience aliases */
> +    unsigned int next_level = level + 1;
> +    unsigned int level_order = level_orders[next_level];
> +
> +    /*
> +     * This should only be called with target != level and the entry is
> +     * a superpage.
> +     */
> +    ASSERT(level < target);
> +    ASSERT(p2m_is_superpage(*entry, level));
> +
> +    page = alloc_domheap_page(NULL, 0);
> +    if ( !page )
> +        return false;
> +
> +    page_list_add(page, &p2m->pages);
> +    table = __map_domain_page(page);
> +
> +    /*
> +     * We are either splitting a first level 1G page into 512 second level
> +     * 2M pages, or a second level 2M page into 512 third level 4K pages.
> +     */
> +    for ( i = 0; i < LPAE_ENTRIES; i++ )
> +    {
> +        lpae_t *new_entry = table + i;
> +
> +        pte = mfn_to_p2m_entry(mfn, t, p2m->default_access);
> +
> +        mfn = mfn_add(mfn, (1UL << level_order));
> +
> +        /*
> +         * First and second level pages set p2m.table = 0, but third
> +         * level entries set p2m.table = 1.
> +         */
> +        if ( next_level < 3 )
> +            pte.p2m.table = 0;
> +
> +        write_pte(new_entry, pte);
> +    }
> +
> +    /*
> +     * Shatter superpage in the page to the level we want to make the
> +     * changes.
> +     * This is done outside the loop to avoid checking the offset to
> +     * know whether the entry should be shattered for every entry.
> +     */
> +    if ( next_level != target )
> +        rv = p2m_split_superpage(p2m, table + offsets[next_level],
> +                                 level + 1, target, offsets);
> +
> +    if ( p2m->clean_pte )
> +        clean_dcache_va_range(table, PAGE_SIZE);
> +
> +    unmap_domain_page(table);
> +
> +    pte = mfn_to_p2m_entry(_mfn(page_to_mfn(page)), p2m_invalid,
> +                           p2m->default_access);
> +
> +    p2m_write_pte(entry, pte, p2m->clean_pte);
> +
> +    /*
> +     * Even if we failed, we should install the newly allocated LPAE
> +     * entry. The caller will be in charge to free the sub-tree.
> +     * XXX: See if we can free entry here.
> +     */
> +    *entry = pte;

Isn't the call to p2m_write_pte just above enough?


> +
> +    return rv;
> +}
> +
> +/*
> + * Insert an entry in the p2m. This should be called with a mapping
> + * equal to a page/superpage (4K, 2M, 1G).
> + */
> +static int __p2m_set_entry(struct p2m_domain *p2m,
> +                           gfn_t sgfn,
> +                           unsigned int page_order,
> +                           mfn_t smfn,
> +                           p2m_type_t t,
> +                           p2m_access_t a)
> +{
> +    paddr_t addr = pfn_to_paddr(gfn_x(sgfn));
> +    unsigned int level = 0;
> +    unsigned int target = 3 - (page_order / LPAE_SHIFT);
> +    lpae_t *entry, *table, orig_pte;
> +    int rc;
> +
> +    /* Convenience aliases */
> +    const unsigned int offsets[4] = {
> +        zeroeth_table_offset(addr),
> +        first_table_offset(addr),
> +        second_table_offset(addr),
> +        third_table_offset(addr)
> +    };
> +
> +    /* TODO: Check the validity for the address */
> +
> +    ASSERT(p2m_is_write_locked(p2m));
> +
> +    /*
> +     * Check if the level target is valid: we only support
> +     * 4K - 2M - 1G mapping.
> +     */
> +    ASSERT(target > 0 && target <= 3);
> +
> +    table = p2m_get_root_pointer(p2m, sgfn);
> +    if ( !table )
> +        return -EINVAL;
> +
> +    for ( level = P2M_ROOT_LEVEL; level < target; level++ )
> +    {
> +        rc = p2m_next_level(p2m, false, &table, offsets[level]);

If smfn is INVALID_MFN, does it make sense to create intermediate table
entries?


> +        if ( rc == GUEST_TABLE_MAP_FAILED )
> +        {
> +            rc = -ENOENT;
> +            goto out;
> +        }
> +        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
> +            break;
> +    }
> +
> +    entry = table + offsets[level];
> +
> +    /*
> +     * If we are here with level < target, we must be at a leaf node,
> +     * and we need to break up the superpage.
> +     */
> +    if ( level < target )
> +    {
> +        /* We need to split the original page. */
> +        lpae_t split_pte = *entry;
> +
> +        ASSERT(p2m_is_superpage(*entry, level));
> +
> +        if ( !p2m_split_superpage(p2m, &split_pte, level, target, offsets) )
> +        {
> +            p2m_free_entry(p2m, split_pte, level);
> +            rc = -ENOMEM;
> +            goto out;
> +        }
> +
> +        /*
> +         * Follow the break-before-sequence to update the entry.
> +         * For more details see (D4.7.1 in ARM DDI 0487A.j).
> +         * XXX: Can we flush by address?
> +         */
> +        p2m_remove_pte(entry, p2m->clean_pte);
> +        p2m_flush_tlb_sync(p2m);
> +
> +        p2m_write_pte(entry, split_pte, p2m->clean_pte);
> +
> +        /* then move to the level we want to make real changes */
> +        for ( ; level < target; level++ )
> +        {
> +            rc = p2m_next_level(p2m, true, &table, offsets[level]);
> +
> +            /*
> +             * The entry should be found and either be a table
> +             * or a superpage if level 3 is not targeted
> +             */
> +            ASSERT(rc == GUEST_TABLE_NORMAL_PAGE ||
> +                   (rc == GUEST_TABLE_SUPER_PAGE && target < 3));
> +        }
> +        entry = table + offsets[level];
> +    }
> +
> +    /*
> +     * We should always be there with the correct level because
> +     * all the intermediate tables have been installed if necessary.
> +     */
> +    ASSERT(level == target);
> +
> +    orig_pte = *entry;
> +
> +    /*
> +     * The radix-tree can only work on 4KB. This is only used when
> +     * memaccess is enabled.

"This" what? This function? The radix-tree?


> +     */
> +    ASSERT(!p2m->mem_access_enabled || page_order == 0);
> +    /*
> +     * The access type should always be p2m_access_rwx when the mapping
> +     * is removed.
> +     */
> +    ASSERT(!mfn_eq(INVALID_MFN, smfn) || (a == p2m_access_rwx));
> +    /*
> +     * Update the mem access permission before update the P2M. So we
> +     * don't have to revert the mapping if it has failed.
> +     */
> +    rc = p2m_mem_access_radix_set(p2m, sgfn, a);
> +    if ( rc )
> +        goto out;
> +
> +    /*
> +     * Always remove the entry in order to follow the break-before-make
> +     * sequence when updating the translation table (D4.7.1 in ARM DDI
> +     * 0487A.j).
> +     */
> +    if ( p2m_valid(orig_pte) )
> +        p2m_remove_pte(entry, p2m->clean_pte);
> +
> +    if ( mfn_eq(smfn, INVALID_MFN) )
> +        /* Flush can be deferred if the entry is removed */
> +        p2m->need_flush |= !!p2m_valid(orig_pte);
> +    else
> +    {
> +        lpae_t pte;
> +
> +        /*
> +         * Flush the TLB before write the new one to keep coherency.
> +         * XXX: Can we flush by address?

I was asking myself the same question


> +         */
> +        if ( p2m_valid(orig_pte) )
> +            p2m_flush_tlb_sync(p2m);
> +
> +        pte = mfn_to_p2m_entry(smfn, t, a);
> +        if ( level < 3 )
> +            pte.p2m.table = 0; /* Superpage entry */
> +
> +        p2m_write_pte(entry, pte, p2m->clean_pte);
> +
> +        p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn,
> +                                      gfn_add(sgfn, 1 << page_order));
> +        p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn);
> +    }
> +
> +    /*
> +     * Free the entry only if the original pte was valid and the base
> +     * is different (to avoid freeing when permission is changed).
> +     */
> +    if ( p2m_valid(orig_pte) && entry->p2m.base != orig_pte.p2m.base )
> +        p2m_free_entry(p2m, orig_pte, level);
> +
> +    /* XXX: Flush iommu */

Please do, it would be best if we had iommu flushing in this patch
before committing it. 


> +    rc = 0;
> +
> +out:
> +    unmap_domain_page(table);
> +
> +    return rc;
> +}
> +
> +static int p2m_set_entry(struct p2m_domain *p2m,
> +                         gfn_t sgfn,
> +                         unsigned long todo,
> +                         mfn_t smfn,
> +                         p2m_type_t t,
> +                         p2m_access_t a)
> +{
> +    int rc = 0;
> +
> +    while ( todo )
> +    {
> +        unsigned long mask = gfn_x(sgfn) | mfn_x(smfn) | todo;
> +        unsigned long order;
> +
> +        /* Always map 4k by 4k when memaccess is enabled */
> +        if ( unlikely(p2m->mem_access_enabled) )
> +            order = THIRD_ORDER;

Missing an "else" here


> +        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
> +            order = FIRST_ORDER;
> +        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
> +            order = SECOND_ORDER;
> +        else
> +            order = THIRD_ORDER;
> +
> +        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
> +        if ( rc )
> +            break;

Shouldn't we be checking that "(1<<order)" doesn't exceed "todo" before
calling __p2m_set_entry? Otherwise we risk creating a mapping bigger
than requested.


> +        sgfn = gfn_add(sgfn, (1 << order));
> +        if ( !mfn_eq(smfn, INVALID_MFN) )
> +           smfn = mfn_add(smfn, (1 << order));
> +
> +        todo -= (1 << order);
> +    }
> +
> +    return rc;
> +}
> +#endif

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch
  2016-08-31 19:43       ` Stefano Stabellini
@ 2016-09-06 14:54         ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-09-06 14:54 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel



On 31/08/16 20:43, Stefano Stabellini wrote:
> On Tue, 16 Aug 2016, Julien Grall wrote:
>> Hi Stefano,
>>
>> On 16/08/2016 01:21, Stefano Stabellini wrote:
>>> On Thu, 28 Jul 2016, Julien Grall wrote:
>>>> A follow-up patch will add more case to the switch that will require the
>>>> IPA. So move the computation out of the switch.
>>>>
>>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>>> ---
>>>>  xen/arch/arm/traps.c | 36 ++++++++++++++++++------------------
>>>>  1 file changed, 18 insertions(+), 18 deletions(-)
>>>>
>>>> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
>>>> index 683bcb2..46e0663 100644
>>>> --- a/xen/arch/arm/traps.c
>>>> +++ b/xen/arch/arm/traps.c
>>>> @@ -2403,35 +2403,35 @@ static void do_trap_instr_abort_guest(struct
>>>> cpu_user_regs *regs,
>>>>      int rc;
>>>>      register_t gva = READ_SYSREG(FAR_EL2);
>>>>      uint8_t fsc = hsr.iabt.ifsc & ~FSC_LL_MASK;
>>>> +    paddr_t gpa;
>>>> +
>>>> +    if ( hpfar_is_valid(hsr.iabt.s1ptw, fsc) )
>>>> +        gpa = get_faulting_ipa(gva);
>>>> +    else
>>>> +    {
>>>> +        /*
>>>> +         * Flush the TLB to make sure the DTLB is clear before
>>>> +         * doing GVA->IPA translation. If we got here because of
>>>> +         * an entry only present in the ITLB, this translation may
>>>> +         * still be inaccurate.
>>>> +         */
>>>> +        flush_tlb_local();
>>>> +
>>>> +        rc = gva_to_ipa(gva, &gpa, GV2M_READ);
>>>> +        if ( rc == -EFAULT )
>>>> +            return; /* Try again */
>>>
>>> The issue with this is that now for any cases that don't require a gpa
>>> if gva_to_ipa fails we wrongly return -EFAULT.
>>
>> Well, stage-1 fault is prioritized over stage-2 fault (see B3.12.3 in ARM DDI
>> 0406C.b), so gva_to_ipa should never fail unless someone is playing with the
>> stage-1 page table at the same time or because of an erratum (see 834220). In
>> both case, we should replay the instruction to let the processor injecting the
>> correct fault.
>>
>> FWIW, this is already what we do for the data abort handler.
>>
>>>
>>> I suggest having two switches or falling through from the first case to
>>> the second.
>>
>> I am not sure to understand your suggestion. Could you detail it?
>
> I was merely suggesting to add another switch like:
>
>   +   switch ( fsc )
>   +   {
>   +   case FSC_FLT_PERM:
>   +   case BLA:
>   +       find gpa;
>   +   default:
>   +   }
>
>      switch ( fsc )
>      {
>
> but given that we are already relying on the gva_to_ipa translation in
> the data abort handler, your patch is fine too.
>
> Acked-by: Stefano Stabellini <sstabellini@kernel.org>

Thank you! FWIW, I am planning to merge the instruction and data abort 
paths after Xen 4.8. I will avoid to diverge between the two 
implementations.

Cheers,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry
  2016-09-05 21:13   ` Stefano Stabellini
@ 2016-09-06 14:56     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-09-06 14:56 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 05/09/16 22:13, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> The function p2m_cache_flush can be re-implemented using the generic
>> function p2m_get_entry by iterating over the range and using the mapping
>> order given by the callee.
>>
>> As the current implementation, no preemption is implemented, although
>> the comment in the current code claimed it. As the function is called by
>> a DOMCTL with a region of 1GB maximum, I think the preemption can be
>> left unimplemented for now.
>>
>> Finally drop the operation CACHEFLUSH in apply_one_level as nobody is
>> using it anymore. Note that the function could have been dropped in one
>> go at the end, however I find easier to drop the operations one by one
>> avoiding a big deletion in the patch that convert the last operation.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>
>> ---
>>     The loop pattern will be very for the reliquish function. It might
>>     be possible to extract it in a separate function.
>> ---
>>  xen/arch/arm/p2m.c | 67 +++++++++++++++++++++++++++---------------------------
>>  1 file changed, 34 insertions(+), 33 deletions(-)
>>
>> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
>> index 9a9c85c..e7697bb 100644
>> --- a/xen/arch/arm/p2m.c
>> +++ b/xen/arch/arm/p2m.c
>> @@ -722,7 +722,6 @@ enum p2m_operation {
>>      INSERT,
>>      REMOVE,
>>      RELINQUISH,
>> -    CACHEFLUSH,
>>      MEMACCESS,
>>  };
>>
>> @@ -978,36 +977,6 @@ static int apply_one_level(struct domain *d,
>>           */
>>          return P2M_ONE_PROGRESS;
>>
>> -    case CACHEFLUSH:
>> -        if ( !p2m_valid(orig_pte) )
>> -        {
>> -            *addr = (*addr + level_size) & level_mask;
>> -            return P2M_ONE_PROGRESS_NOP;
>> -        }
>> -
>> -        if ( level < 3 && p2m_table(orig_pte) )
>> -            return P2M_ONE_DESCEND;
>> -
>> -        /*
>> -         * could flush up to the next superpage boundary, but would
>> -         * need to be careful about preemption, so just do one 4K page
>> -         * now and return P2M_ONE_PROGRESS{,_NOP} so that the caller will
>> -         * continue to loop over the rest of the range.
>> -         */
>> -        if ( p2m_is_ram(orig_pte.p2m.type) )
>> -        {
>> -            unsigned long offset = paddr_to_pfn(*addr & ~level_mask);
>> -            flush_page_to_ram(orig_pte.p2m.base + offset);
>> -
>> -            *addr += PAGE_SIZE;
>> -            return P2M_ONE_PROGRESS;
>> -        }
>> -        else
>> -        {
>> -            *addr += PAGE_SIZE;
>> -            return P2M_ONE_PROGRESS_NOP;
>> -        }
>> -
>>      case MEMACCESS:
>>          if ( level < 3 )
>>          {
>> @@ -1555,12 +1524,44 @@ int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr)
>>  {
>>      struct p2m_domain *p2m = &d->arch.p2m;
>>      gfn_t end = gfn_add(start, nr);
>> +    p2m_type_t t;
>> +    unsigned int order;
>>
>>      start = gfn_max(start, p2m->lowest_mapped_gfn);
>>      end = gfn_min(end, p2m->max_mapped_gfn);
>>
>> -    return apply_p2m_changes(d, CACHEFLUSH, start, nr, INVALID_MFN,
>> -                             0, p2m_invalid, d->arch.p2m.default_access);
>> +    /* XXX: Should we use write lock here? */
>
> Good question. As the p2m is left unchanged by this function, I think
> that the read lock is sufficient.

It is what I thought. I will replace the todo by a comment explain why 
read-lock is used here.

>
>
>> +    p2m_read_lock(p2m);
>> +
>> +    for ( ; gfn_x(start) < gfn_x(end); start = gfn_add(start, 1UL << order) )
>> +    {
>> +        mfn_t mfn = p2m_get_entry(p2m, start, &t, NULL, &order);
>> +
>> +        /* Skip hole and non-RAM page */
>> +        if ( mfn_eq(mfn, INVALID_MFN) || !p2m_is_ram(t) )
>> +        {
>> +            /*
>> +             * the order corresponds to the order of the mapping in the
>> +             * page table. so we need to align the gfn before
>> +             * incrementing.
>> +             */
>> +            start = _gfn(gfn_x(start) & ~((1UL << order) - 1));
>> +            continue;
>> +        }
>> +
>> +        /*
>> +         * Could flush up to the next superpage boundary, but we would
>> +         * need to be careful about preemption, so just do one 4K page
>> +         * now.
>
> I think that even without preemption you should implement flushing up to
> the next superpage boundary (but not beyond "end"). You can still do it
> 4K at a time, but only call p2m_get_entry once per "order". Could be a
> decent performance improvement as cacheflush is a performance critical
> hypercall.

Good point. I will give a look for the next version.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
  2016-09-05 21:58   ` Stefano Stabellini
@ 2016-09-06 15:05     ` Julien Grall
  2016-09-06 18:21       ` Stefano Stabellini
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-09-06 15:05 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 05/09/16 22:58, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> The current implementation of relinquish_p2m_mapping is modifying the
>> page table to erase the entry one by one. However, this is not necessary
>> because the domain is not running anymore and therefore will speed up
>> the domain destruction.
>
> Could you please elaborate on this? Who is going to remove the p2m
> entries if not this function?

The current version of relinquish is removing the reference on the page 
and then invalidate the entry (which may involve a cache flush).

As the page tables are not used anymore by the hardware, the latter 
action is not necessary. This is an optimization because flushing the 
cache can be expensive. However as mentioned later in the commit 
message, we need to have a think on how the other helpers interact with 
the page table to avoid return wrong entry.

I am thinking to defer this optimization for the next release (i.e Xen 
4.9) to avoid rushing on it.

>
>
>> The function relinquish_p2m_mapping can be re-implemented using
>> p2m_get_entry by iterating over the range mapped and using the mapping
>> order given by the callee.
>>
>> Given that the preemption was chosen arbitrarily, it is no done on every
>                                                           ^ now?

Yes, will fix it in the next version.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-09-06  1:08   ` Stefano Stabellini
@ 2016-09-06 17:12     ` Julien Grall
  2016-09-06 18:51       ` Stefano Stabellini
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-09-06 17:12 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 06/09/16 02:08, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> The ARM architecture mandates to use of a break-before-make sequence
>> when changing translation entries if the page table is shared between
>> multiple CPUs whenever a valid entry is replaced by another valid entry
>> (see D4.7.1 in ARM DDI 0487A.j for more details).
>>
>> The break-before-make sequence can be divided in the following steps:
>>     1) Invalidate the old entry in the page table
>>     2) Issue a TLB invalidation instruction for the address associated
>>     to this entry
>>     3) Write the new entry
>>
>> The current P2M code implemented in apply_one_level does not respect
>> this sequence and may result to break coherency on some processors.
>>
>> Adapting the current implementation to use the break-before-make
>> sequence would imply some code duplication and more TLBs invalidation
>> than necessary. For instance, if we are replacing a 4KB page and the
>> current mapping in the P2M is using a 1GB superpage, the following steps
>> will happen:
>>     1) Shatter the 1GB superpage into a series of 2MB superpages
>>     2) Shatter the 2MB superpage into a series of 4KB pages
>>     3) Replace the 4KB page
>>
>> As the current implementation is shattering while descending and install
>> the mapping, Xen would need to issue 3 TLB invalidation instructions
>> which is clearly inefficient.
>>
>> Furthermore, all the operations which modify the page table are using
>> the same skeleton. It is more complicated to maintain different code paths
>> than having a generic function that set an entry and take care of the
>> break-before-make sequence.
>>
>> The new implementation is based on the x86 EPT one which, I think,
>> fits quite well for the break-before-make sequence whilst keeping
>> the code simple.
>>
>> The main function of the new implementation is __p2m_get_entry. It will
>> only work on mapping that are aligned to a block entry in the page table
>> (i.e 1GB, 2MB, 4KB when using a 4KB granularity).
>>
>> Another function, p2m_get_entry, is provided to break down is region
>> into mapping that is aligned to a block entry.
>>
>> Note that to keep this patch "small", there are no caller of those
>> functions added in this patch (they will be added in follow-up patches).
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>
>> ---
>>     I need to find the impact of this new implementation on ARM32 because
>>     the domheap is not always mapped. This means that Xen needs to map/unmap
>>     everytime the page associated to the page table. It might be possible
>>     to re-use some caching structure as the current implementation does
>>     or rework the way a domheap is mapped/unmapped.
>>
>>     Also, this code still contain few TODOs mostly to add sanity
>>     checks and few optimization. The IOMMU is not yet supported.
>> ---
>>  xen/arch/arm/p2m.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 335 insertions(+)
>>
>> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
>> index c93e554..297b176 100644
>> --- a/xen/arch/arm/p2m.c
>> +++ b/xen/arch/arm/p2m.c
>> @@ -750,6 +750,341 @@ static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
>>      }
>>  }
>>
>> +#if 0
>> +/* Free lpae sub-tree behind an entry */
>> +static void p2m_free_entry(struct p2m_domain *p2m,
>> +                           lpae_t entry, unsigned int level)
>> +{
>> +    unsigned int i;
>> +    lpae_t *table;
>> +    mfn_t mfn;
>> +
>> +    /* Nothing to do if the entry is invalid or a super-page */
>
> Why are we not freeing superpages? It looks like this function could be
> correctly called on a superpage (with level == target).

The p2m code on ARM does not take any reference on mapping except for 
foreign one. So there is nothing to do in the case of superpage (it is 
not possible to have foreign superpage).

>
>
>> +    if ( !p2m_valid(entry) || p2m_is_superpage(entry, level) )
>> +        return;
>> +
>> +    if ( level == 3 )
>> +    {
>> +        p2m_put_l3_page(_mfn(entry.p2m.base), entry.p2m.type);
>> +        return;
>> +    }
>> +
>> +    table = map_domain_page(_mfn(entry.p2m.base));
>> +    for ( i = 0; i < LPAE_ENTRIES; i++ )
>> +        p2m_free_entry(p2m, *(table + i), level + 1);
>> +
>> +    unmap_domain_page(table);
>> +
>> +    /*
>> +     * Make sure all the references in the TLB have been removed before
>> +     * freing the intermediate page table.
>> +     * XXX: Should we defer the free of the page table to avoid the
>> +     * flush?
>
> Yes, I would probably move the p2m flush out of this function.

We would need to also defer the free of the intermediate table (see 
free_domheap_page below) that means keeping track of the pages to free 
later.

The flush here will only happen once and iff a sub-tree is removed (i.e 
unlikely in most of the cases).

So I am not sure it is worth to keep a list of page to free later. Any 
opinions?

>
>
>> +     */
>> +    if ( p2m->need_flush )
>> +        p2m_flush_tlb_sync(p2m);
>> +
>> +    mfn = _mfn(entry.p2m.base);
>> +    ASSERT(mfn_valid(mfn_x(mfn)));
>> +
>> +    free_domheap_page(mfn_to_page(mfn_x(mfn)));
>> +}
>> +
>> +static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry,
>> +                                unsigned int level, unsigned int target,
>> +                                const unsigned int *offsets)
>> +{
>> +    struct page_info *page;
>> +    unsigned int i;
>> +    lpae_t pte, *table;
>> +    bool rv = true;
>> +
>> +    /* Convenience aliases */
>> +    p2m_type_t t = entry->p2m.type;
>> +    mfn_t mfn = _mfn(entry->p2m.base);
>> +
>> +    /* Convenience aliases */
>> +    unsigned int next_level = level + 1;
>> +    unsigned int level_order = level_orders[next_level];
>> +
>> +    /*
>> +     * This should only be called with target != level and the entry is
>> +     * a superpage.
>> +     */
>> +    ASSERT(level < target);
>> +    ASSERT(p2m_is_superpage(*entry, level));
>> +
>> +    page = alloc_domheap_page(NULL, 0);
>> +    if ( !page )
>> +        return false;
>> +
>> +    page_list_add(page, &p2m->pages);
>> +    table = __map_domain_page(page);
>> +
>> +    /*
>> +     * We are either splitting a first level 1G page into 512 second level
>> +     * 2M pages, or a second level 2M page into 512 third level 4K pages.
>> +     */
>> +    for ( i = 0; i < LPAE_ENTRIES; i++ )
>> +    {
>> +        lpae_t *new_entry = table + i;
>> +
>> +        pte = mfn_to_p2m_entry(mfn, t, p2m->default_access);
>> +
>> +        mfn = mfn_add(mfn, (1UL << level_order));
>> +
>> +        /*
>> +         * First and second level pages set p2m.table = 0, but third
>> +         * level entries set p2m.table = 1.
>> +         */
>> +        if ( next_level < 3 )
>> +            pte.p2m.table = 0;
>> +
>> +        write_pte(new_entry, pte);
>> +    }
>> +
>> +    /*
>> +     * Shatter superpage in the page to the level we want to make the
>> +     * changes.
>> +     * This is done outside the loop to avoid checking the offset to
>> +     * know whether the entry should be shattered for every entry.
>> +     */
>> +    if ( next_level != target )
>> +        rv = p2m_split_superpage(p2m, table + offsets[next_level],
>> +                                 level + 1, target, offsets);
>> +
>> +    if ( p2m->clean_pte )
>> +        clean_dcache_va_range(table, PAGE_SIZE);
>> +
>> +    unmap_domain_page(table);
>> +
>> +    pte = mfn_to_p2m_entry(_mfn(page_to_mfn(page)), p2m_invalid,
>> +                           p2m->default_access);
>> +
>> +    p2m_write_pte(entry, pte, p2m->clean_pte);
>> +
>> +    /*
>> +     * Even if we failed, we should install the newly allocated LPAE
>> +     * entry. The caller will be in charge to free the sub-tree.
>> +     * XXX: See if we can free entry here.
>> +     */
>> +    *entry = pte;
>
> Isn't the call to p2m_write_pte just above enough?

Hmmm yes. And it is wrong because not safe. I will drop it.

>
>
>> +
>> +    return rv;
>> +}
>> +
>> +/*
>> + * Insert an entry in the p2m. This should be called with a mapping
>> + * equal to a page/superpage (4K, 2M, 1G).
>> + */
>> +static int __p2m_set_entry(struct p2m_domain *p2m,
>> +                           gfn_t sgfn,
>> +                           unsigned int page_order,
>> +                           mfn_t smfn,
>> +                           p2m_type_t t,
>> +                           p2m_access_t a)
>> +{
>> +    paddr_t addr = pfn_to_paddr(gfn_x(sgfn));
>> +    unsigned int level = 0;
>> +    unsigned int target = 3 - (page_order / LPAE_SHIFT);
>> +    lpae_t *entry, *table, orig_pte;
>> +    int rc;
>> +
>> +    /* Convenience aliases */
>> +    const unsigned int offsets[4] = {
>> +        zeroeth_table_offset(addr),
>> +        first_table_offset(addr),
>> +        second_table_offset(addr),
>> +        third_table_offset(addr)
>> +    };
>> +
>> +    /* TODO: Check the validity for the address */
>> +
>> +    ASSERT(p2m_is_write_locked(p2m));
>> +
>> +    /*
>> +     * Check if the level target is valid: we only support
>> +     * 4K - 2M - 1G mapping.
>> +     */
>> +    ASSERT(target > 0 && target <= 3);
>> +
>> +    table = p2m_get_root_pointer(p2m, sgfn);
>> +    if ( !table )
>> +        return -EINVAL;
>> +
>> +    for ( level = P2M_ROOT_LEVEL; level < target; level++ )
>> +    {
>> +        rc = p2m_next_level(p2m, false, &table, offsets[level]);
>
> If smfn is INVALID_MFN, does it make sense to create intermediate table
> entries?

I asked myself the same question at the beginning and decided to avoid 
caring about it. The caller is supposed to remove a page that has a 
entry in the p2m, so for me it is a caller mistake.

But I agree, that we allocate intermediate table entries. I will give a 
look if I can find an non-intrusive way to do it.

>
>
>> +        if ( rc == GUEST_TABLE_MAP_FAILED )
>> +        {
>> +            rc = -ENOENT;
>> +            goto out;
>> +        }
>> +        else if ( rc != GUEST_TABLE_NORMAL_PAGE )
>> +            break;
>> +    }
>> +
>> +    entry = table + offsets[level];
>> +
>> +    /*
>> +     * If we are here with level < target, we must be at a leaf node,
>> +     * and we need to break up the superpage.
>> +     */
>> +    if ( level < target )
>> +    {
>> +        /* We need to split the original page. */
>> +        lpae_t split_pte = *entry;
>> +
>> +        ASSERT(p2m_is_superpage(*entry, level));
>> +
>> +        if ( !p2m_split_superpage(p2m, &split_pte, level, target, offsets) )
>> +        {
>> +            p2m_free_entry(p2m, split_pte, level);
>> +            rc = -ENOMEM;
>> +            goto out;
>> +        }
>> +
>> +        /*
>> +         * Follow the break-before-sequence to update the entry.
>> +         * For more details see (D4.7.1 in ARM DDI 0487A.j).
>> +         * XXX: Can we flush by address?
>> +         */
>> +        p2m_remove_pte(entry, p2m->clean_pte);
>> +        p2m_flush_tlb_sync(p2m);
>> +
>> +        p2m_write_pte(entry, split_pte, p2m->clean_pte);
>> +
>> +        /* then move to the level we want to make real changes */
>> +        for ( ; level < target; level++ )
>> +        {
>> +            rc = p2m_next_level(p2m, true, &table, offsets[level]);
>> +
>> +            /*
>> +             * The entry should be found and either be a table
>> +             * or a superpage if level 3 is not targeted
>> +             */
>> +            ASSERT(rc == GUEST_TABLE_NORMAL_PAGE ||
>> +                   (rc == GUEST_TABLE_SUPER_PAGE && target < 3));
>> +        }
>> +        entry = table + offsets[level];
>> +    }
>> +
>> +    /*
>> +     * We should always be there with the correct level because
>> +     * all the intermediate tables have been installed if necessary.
>> +     */
>> +    ASSERT(level == target);
>> +
>> +    orig_pte = *entry;
>> +
>> +    /*
>> +     * The radix-tree can only work on 4KB. This is only used when
>> +     * memaccess is enabled.
>
> "This" what? This function? The radix-tree?

The radix-tree. I didn't want to repeat the word and though "this" would 
make sense.

>> +     */
>> +    ASSERT(!p2m->mem_access_enabled || page_order == 0);
>> +    /*
>> +     * The access type should always be p2m_access_rwx when the mapping
>> +     * is removed.
>> +     */
>> +    ASSERT(!mfn_eq(INVALID_MFN, smfn) || (a == p2m_access_rwx));
>> +    /*
>> +     * Update the mem access permission before update the P2M. So we
>> +     * don't have to revert the mapping if it has failed.
>> +     */
>> +    rc = p2m_mem_access_radix_set(p2m, sgfn, a);
>> +    if ( rc )
>> +        goto out;
>> +
>> +    /*
>> +     * Always remove the entry in order to follow the break-before-make
>> +     * sequence when updating the translation table (D4.7.1 in ARM DDI
>> +     * 0487A.j).
>> +     */
>> +    if ( p2m_valid(orig_pte) )
>> +        p2m_remove_pte(entry, p2m->clean_pte);
>> +
>> +    if ( mfn_eq(smfn, INVALID_MFN) )
>> +        /* Flush can be deferred if the entry is removed */
>> +        p2m->need_flush |= !!p2m_valid(orig_pte);
>> +    else
>> +    {
>> +        lpae_t pte;
>> +
>> +        /*
>> +         * Flush the TLB before write the new one to keep coherency.
>> +         * XXX: Can we flush by address?
>
> I was asking myself the same question

It is not trivial. On ARMv7, there is no way to invalidate by IPA, so we 
still need to do a full flush.

In the case of ARMv8, it is possible to do a flush by IPA with the 
following sequence (see D4-1739 in ARM DDI 0487A.i):
  tlbi ipa2e1, xt
  dsb
  tlbi vmalle1

So I was wondering if we could leave that for a future optimization.

>
>
>> +         */
>> +        if ( p2m_valid(orig_pte) )
>> +            p2m_flush_tlb_sync(p2m);
>> +
>> +        pte = mfn_to_p2m_entry(smfn, t, a);
>> +        if ( level < 3 )
>> +            pte.p2m.table = 0; /* Superpage entry */
>> +
>> +        p2m_write_pte(entry, pte, p2m->clean_pte);
>> +
>> +        p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn,
>> +                                      gfn_add(sgfn, 1 << page_order));
>> +        p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn);
>> +    }
>> +
>> +    /*
>> +     * Free the entry only if the original pte was valid and the base
>> +     * is different (to avoid freeing when permission is changed).
>> +     */
>> +    if ( p2m_valid(orig_pte) && entry->p2m.base != orig_pte.p2m.base )
>> +        p2m_free_entry(p2m, orig_pte, level);
>> +
>> +    /* XXX: Flush iommu */
>
> Please do, it would be best if we had iommu flushing in this patch
> before committing it.

It was my plan, hence the "RFC". I just wanted to send a first version 
to get feedback before refining the code.

>
>
>> +    rc = 0;
>> +
>> +out:
>> +    unmap_domain_page(table);
>> +
>> +    return rc;
>> +}
>> +
>> +static int p2m_set_entry(struct p2m_domain *p2m,
>> +                         gfn_t sgfn,
>> +                         unsigned long todo,
>> +                         mfn_t smfn,
>> +                         p2m_type_t t,
>> +                         p2m_access_t a)
>> +{
>> +    int rc = 0;
>> +
>> +    while ( todo )
>> +    {
>> +        unsigned long mask = gfn_x(sgfn) | mfn_x(smfn) | todo;
>> +        unsigned long order;
>> +
>> +        /* Always map 4k by 4k when memaccess is enabled */
>> +        if ( unlikely(p2m->mem_access_enabled) )
>> +            order = THIRD_ORDER;
>
> Missing an "else" here

I will fix it in the next version.

>
>
>> +        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
>> +            order = FIRST_ORDER;
>> +        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
>> +            order = SECOND_ORDER;
>> +        else
>> +            order = THIRD_ORDER;
>> +
>> +        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
>> +        if ( rc )
>> +            break;
>
> Shouldn't we be checking that "(1<<order)" doesn't exceed "todo" before
> calling __p2m_set_entry? Otherwise we risk creating a mapping bigger
> than requested.

The mask is defined as gfn_x(sgfn) | mfn_x(smfn) | todo

So we will never have the order exceeding "todo".

>
>
>> +        sgfn = gfn_add(sgfn, (1 << order));
>> +        if ( !mfn_eq(smfn, INVALID_MFN) )
>> +           smfn = mfn_add(smfn, (1 << order));
>> +
>> +        todo -= (1 << order);
>> +    }
>> +
>> +    return rc;
>> +}
>> +#endif
>

Regards,

-- 
Julien Grall

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
  2016-09-06 15:05     ` Julien Grall
@ 2016-09-06 18:21       ` Stefano Stabellini
  2016-09-07  7:37         ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 18:21 UTC (permalink / raw)
  To: Julien Grall
  Cc: proskurin, Stefano Stabellini, steve.capper, wei.chen, xen-devel

On Tue, 6 Sep 2016, Julien Grall wrote:
> Hi Stefano,
> 
> On 05/09/16 22:58, Stefano Stabellini wrote:
> > On Thu, 28 Jul 2016, Julien Grall wrote:
> > > The current implementation of relinquish_p2m_mapping is modifying the
> > > page table to erase the entry one by one. However, this is not necessary
> > > because the domain is not running anymore and therefore will speed up
> > > the domain destruction.
> > 
> > Could you please elaborate on this? Who is going to remove the p2m
> > entries if not this function?
> 
> The current version of relinquish is removing the reference on the page and
> then invalidate the entry (which may involve a cache flush).
> 
> As the page tables are not used anymore by the hardware, the latter action is
> not necessary. This is an optimization because flushing the cache can be
> expensive. However as mentioned later in the commit message, we need to have a
> think on how the other helpers interact with the page table to avoid return
> wrong entry.

The idea is that nobody will remove the p2m entries, until the whole p2m
is torn down (p2m_teardown)?


> I am thinking to defer this optimization for the next release (i.e Xen 4.9) to
> avoid rushing on it.

If we are sure that there are no interactions with the p2m between the
domain_relinquish_resources and the p2m_teardown call, then this is
acceptable. Otherwise delaying this optimization is wiser.


> > > The function relinquish_p2m_mapping can be re-implemented using
> > > p2m_get_entry by iterating over the range mapped and using the mapping
> > > order given by the callee.
> > > 
> > > Given that the preemption was chosen arbitrarily, it is no done on every
> >                                                           ^ now?
> 
> Yes, will fix it in the next version.
> 
> Regards,
> 
> -- 
> Julien Grall
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-09-06 17:12     ` Julien Grall
@ 2016-09-06 18:51       ` Stefano Stabellini
  2016-09-07  8:18         ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 18:51 UTC (permalink / raw)
  To: Julien Grall
  Cc: proskurin, Stefano Stabellini, steve.capper, wei.chen, xen-devel

On Tue, 6 Sep 2016, Julien Grall wrote:
> > > +    if ( !p2m_valid(entry) || p2m_is_superpage(entry, level) )
> > > +        return;
> > > +
> > > +    if ( level == 3 )
> > > +    {
> > > +        p2m_put_l3_page(_mfn(entry.p2m.base), entry.p2m.type);
> > > +        return;
> > > +    }
> > > +
> > > +    table = map_domain_page(_mfn(entry.p2m.base));
> > > +    for ( i = 0; i < LPAE_ENTRIES; i++ )
> > > +        p2m_free_entry(p2m, *(table + i), level + 1);
> > > +
> > > +    unmap_domain_page(table);
> > > +
> > > +    /*
> > > +     * Make sure all the references in the TLB have been removed before
> > > +     * freing the intermediate page table.
> > > +     * XXX: Should we defer the free of the page table to avoid the
> > > +     * flush?
> > 
> > Yes, I would probably move the p2m flush out of this function.
> 
> We would need to also defer the free of the intermediate table (see
> free_domheap_page below) that means keeping track of the pages to free later.
> 
> The flush here will only happen once and iff a sub-tree is removed (i.e
> unlikely in most of the cases).
> 
> So I am not sure it is worth to keep a list of page to free later. Any
> opinions?

Leave it for now. In addition flushing the p2m by address would probably
be enough -- it would reduce the benefits of deferring the flush here.


> > > +     */
> > > +    ASSERT(!p2m->mem_access_enabled || page_order == 0);
> > > +    /*
> > > +     * The access type should always be p2m_access_rwx when the mapping
> > > +     * is removed.
> > > +     */
> > > +    ASSERT(!mfn_eq(INVALID_MFN, smfn) || (a == p2m_access_rwx));
> > > +    /*
> > > +     * Update the mem access permission before update the P2M. So we
> > > +     * don't have to revert the mapping if it has failed.
> > > +     */
> > > +    rc = p2m_mem_access_radix_set(p2m, sgfn, a);
> > > +    if ( rc )
> > > +        goto out;
> > > +
> > > +    /*
> > > +     * Always remove the entry in order to follow the break-before-make
> > > +     * sequence when updating the translation table (D4.7.1 in ARM DDI
> > > +     * 0487A.j).
> > > +     */
> > > +    if ( p2m_valid(orig_pte) )
> > > +        p2m_remove_pte(entry, p2m->clean_pte);
> > > +
> > > +    if ( mfn_eq(smfn, INVALID_MFN) )
> > > +        /* Flush can be deferred if the entry is removed */
> > > +        p2m->need_flush |= !!p2m_valid(orig_pte);
> > > +    else
> > > +    {
> > > +        lpae_t pte;
> > > +
> > > +        /*
> > > +         * Flush the TLB before write the new one to keep coherency.
> > > +         * XXX: Can we flush by address?
> > 
> > I was asking myself the same question
> 
> It is not trivial. On ARMv7, there is no way to invalidate by IPA, so we still
> need to do a full flush.
> 
> In the case of ARMv8, it is possible to do a flush by IPA with the following
> sequence (see D4-1739 in ARM DDI 0487A.i):
>  tlbi ipa2e1, xt
>  dsb
>  tlbi vmalle1
> 
> So I was wondering if we could leave that for a future optimization.

We can leave it for now but I have an hunch that it is going to have a
pretty significant impact.

 
> > > +        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
> > > +            order = FIRST_ORDER;
> > > +        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
> > > +            order = SECOND_ORDER;
> > > +        else
> > > +            order = THIRD_ORDER;
> > > +
> > > +        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
> > > +        if ( rc )
> > > +            break;
> > 
> > Shouldn't we be checking that "(1<<order)" doesn't exceed "todo" before
> > calling __p2m_set_entry? Otherwise we risk creating a mapping bigger
> > than requested.
> 
> The mask is defined as gfn_x(sgfn) | mfn_x(smfn) | todo
> 
> So we will never have the order exceeding "todo".

Ah, I see. But this way we are not able to do superpage mappings for
regions larger than 2MB but not multiple of 2MB (3MB for example). But I
don't think we were able to do it before either so it is not a
requirement for the patch.


> > 
> > > +        sgfn = gfn_add(sgfn, (1 << order));
> > > +        if ( !mfn_eq(smfn, INVALID_MFN) )
> > > +           smfn = mfn_add(smfn, (1 << order));
> > > +
> > > +        todo -= (1 << order);
> > > +    }
> > > +
> > > +    return rc;
> > > +}
> > > +#endif

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 19/22] xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry
  2016-07-28 14:51 ` [RFC 19/22] xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry Julien Grall
@ 2016-09-06 18:53   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 18:53 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The function p2m_insert_mapping can be re-implemented using the generic
> function p2m_set_entry.
> 
> Also drop the operation REMOVE in apply_* as nobody is using it anymore.
> Note that the functions could have been dropped in one go at the end,
> however I find easier to drop the operations one by one avoiding a big
> deletion in the patch that converts the last operation.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 127 ++++++-----------------------------------------------
>  1 file changed, 13 insertions(+), 114 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 297b176..0920222 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -725,7 +725,6 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
>  
>  enum p2m_operation {
>      INSERT,
> -    REMOVE,
>      MEMACCESS,
>  };
>  
> @@ -750,7 +749,6 @@ static void p2m_put_l3_page(mfn_t mfn, p2m_type_t type)
>      }
>  }
>  
> -#if 0
>  /* Free lpae sub-tree behind an entry */
>  static void p2m_free_entry(struct p2m_domain *p2m,
>                             lpae_t entry, unsigned int level)
> @@ -1083,7 +1081,6 @@ static int p2m_set_entry(struct p2m_domain *p2m,
>  
>      return rc;
>  }
> -#endif
>  
>  /*
>   * Returns true if start_gpaddr..end_gpaddr contains at least one
> @@ -1161,7 +1158,6 @@ static int apply_one_level(struct domain *d,
>                             p2m_access_t a)
>  {
>      const paddr_t level_size = level_sizes[level];
> -    const paddr_t level_mask = level_masks[level];
>  
>      struct p2m_domain *p2m = &d->arch.p2m;
>      lpae_t pte;
> @@ -1247,74 +1243,6 @@ static int apply_one_level(struct domain *d,
>  
>          break;
>  
> -    case REMOVE:
> -        if ( !p2m_valid(orig_pte) )
> -        {
> -            /* Progress up to next boundary */
> -            *addr = (*addr + level_size) & level_mask;
> -            *maddr = (*maddr + level_size) & level_mask;
> -            return P2M_ONE_PROGRESS_NOP;
> -        }
> -
> -        if ( level < 3 )
> -        {
> -            if ( p2m_table(orig_pte) )
> -                return P2M_ONE_DESCEND;
> -
> -            if ( op == REMOVE &&
> -                 !is_mapping_aligned(*addr, end_gpaddr,
> -                                     0, /* maddr doesn't matter for remove */
> -                                     level_size) )
> -            {
> -                /*
> -                 * Removing a mapping from the middle of a superpage. Shatter
> -                 * and descend.
> -                 */
> -                *flush = true;
> -                rc = p2m_shatter_page(p2m, entry, level);
> -                if ( rc < 0 )
> -                    return rc;
> -
> -                return P2M_ONE_DESCEND;
> -            }
> -        }
> -
> -        /*
> -         * Ensure that the guest address addr currently being
> -         * handled (that is in the range given as argument to
> -         * this function) is actually mapped to the corresponding
> -         * machine address in the specified range. maddr here is
> -         * the machine address given to the function, while
> -         * orig_pte.p2m.base is the machine frame number actually
> -         * mapped to the guest address: check if the two correspond.
> -         */
> -         if ( op == REMOVE &&
> -              pfn_to_paddr(orig_pte.p2m.base) != *maddr )
> -             printk(XENLOG_G_WARNING
> -                    "p2m_remove dom%d: mapping at %"PRIpaddr" is of maddr %"PRIpaddr" not %"PRIpaddr" as expected\n",
> -                    d->domain_id, *addr, pfn_to_paddr(orig_pte.p2m.base),
> -                    *maddr);
> -
> -        *flush = true;
> -
> -        p2m_remove_pte(entry, p2m->clean_pte);
> -        p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
> -                                 p2m_access_rwx);
> -
> -        *addr += level_size;
> -        *maddr += level_size;
> -
> -        p2m->stats.mappings[level]--;
> -
> -        if ( level == 3 )
> -            p2m_put_l3_page(_mfn(orig_pte.p2m.base), orig_pte.p2m.type);
> -
> -        /*
> -         * This is still a single pte write, no matter the level, so no need to
> -         * scale.
> -         */
> -        return P2M_ONE_PROGRESS;
> -
>      case MEMACCESS:
>          if ( level < 3 )
>          {
> @@ -1526,43 +1454,6 @@ static int apply_p2m_changes(struct domain *d,
>          }
>  
>          BUG_ON(level > 3);
> -
> -        if ( op == REMOVE )
> -        {
> -            for ( ; level > P2M_ROOT_LEVEL; level-- )
> -            {
> -                lpae_t old_entry;
> -                lpae_t *entry;
> -                unsigned int offset;
> -
> -                pg = pages[level];
> -
> -                /*
> -                 * No need to try the previous level if the current one
> -                 * still contains some mappings.
> -                 */
> -                if ( pg->u.inuse.p2m_refcount )
> -                    break;
> -
> -                offset = offsets[level - 1];
> -                entry = &mappings[level - 1][offset];
> -                old_entry = *entry;
> -
> -                page_list_del(pg, &p2m->pages);
> -
> -                p2m_remove_pte(entry, p2m->clean_pte);
> -
> -                p2m->stats.mappings[level - 1]--;
> -                update_reference_mapping(pages[level - 1], old_entry, *entry);
> -
> -                /*
> -                 * We can't free the page now because it may be present
> -                 * in the guest TLB. Queue it and free it after the TLB
> -                 * has been flushed.
> -                 */
> -                page_list_add(pg, &free_pages);
> -            }
> -        }
>      }
>  
>      if ( op == INSERT )
> @@ -1604,8 +1495,10 @@ out:
>           * addr keeps the address of the end of the last successfully-inserted
>           * mapping.
>           */
> -        apply_p2m_changes(d, REMOVE, sgfn, gfn - gfn_x(sgfn), smfn,
> -                          0, p2m_invalid, d->arch.p2m.default_access);
> +        p2m_write_lock(p2m);
> +        p2m_set_entry(p2m, sgfn, gfn - gfn_x(sgfn), INVALID_MFN,
> +                      p2m_invalid, p2m_access_rwx);
> +        p2m_write_unlock(p2m);
>      }
>  
>      return rc;
> @@ -1626,9 +1519,15 @@ static inline int p2m_remove_mapping(struct domain *d,
>                                       unsigned long nr,
>                                       mfn_t mfn)
>  {
> -    return apply_p2m_changes(d, REMOVE, start_gfn, nr, mfn,
> -                             /* arguments below not used when removing mapping */
> -                             0, p2m_invalid, d->arch.p2m.default_access);
> +    struct p2m_domain *p2m = &d->arch.p2m;
> +    int rc;
> +
> +    p2m_write_lock(p2m);
> +    rc = p2m_set_entry(p2m, start_gfn, nr, INVALID_MFN,
> +                       p2m_invalid, p2m_access_rwx);
> +    p2m_write_unlock(p2m);
> +
> +    return rc;
>  }
>  
>  int map_regions_rw_cache(struct domain *d,
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
  2016-07-28 14:51 ` [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping " Julien Grall
@ 2016-09-06 18:57   ` Stefano Stabellini
  2016-09-15 10:38     ` Julien Grall
  0 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 18:57 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The function p2m_insert_mapping can be re-implemented using the generic
> function p2m_set_entry.
> 
> Note that the mapping is not reverted anymore if Xen fails to insert a
> mapping. This was added to ensure the MMIO are not kept half-mapped
> in case of failure and to follow the x86 counterpart. This was removed
> on the x86 part by commit c3c756bd "x86/p2m: use large pages for MMIO
> mappings" and I think we should let the caller taking care of it.
> 
> Finally drop the operation INSERT in apply_* as nobody is using it
> anymore. Note that the functios could have been dropped in one go at the
                           ^ functions

> end, however I find easier to drop the operations one by one avoiding a
> big deletion in the patch that convert the last operation.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> 
> ---
>     Whilst there is no safety checks on what is replaced in the P2M
>     (such as foreign/grant mapping as x86 does), we may want to add it
>     ensuring the guest is not doing something dumb. Any opinions?

We don't necessarily need to protect a guest from its own dumbness, as
long as we can correctly deal with it (account for grant and foreign
mappings replaced this way).

Given that the old code doesn't do it anyway:

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>



> ---
>  xen/arch/arm/p2m.c | 148 +++++------------------------------------------------
>  1 file changed, 12 insertions(+), 136 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 0920222..707c7be 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -724,7 +724,6 @@ static int p2m_mem_access_radix_set(struct p2m_domain *p2m, gfn_t gfn,
>  }
>  
>  enum p2m_operation {
> -    INSERT,
>      MEMACCESS,
>  };
>  
> @@ -1082,41 +1081,6 @@ static int p2m_set_entry(struct p2m_domain *p2m,
>      return rc;
>  }
>  
> -/*
> - * Returns true if start_gpaddr..end_gpaddr contains at least one
> - * suitably aligned level_size mappping of maddr.
> - *
> - * So long as the range is large enough the end_gpaddr need not be
> - * aligned (callers should create one superpage mapping based on this
> - * result and then call this again on the new range, eventually the
> - * slop at the end will cause this function to return false).
> - */
> -static bool_t is_mapping_aligned(const paddr_t start_gpaddr,
> -                                 const paddr_t end_gpaddr,
> -                                 const paddr_t maddr,
> -                                 const paddr_t level_size)
> -{
> -    const paddr_t level_mask = level_size - 1;
> -
> -    /* No hardware superpages at level 0 */
> -    if ( level_size == ZEROETH_SIZE )
> -        return false;
> -
> -    /*
> -     * A range smaller than the size of a superpage at this level
> -     * cannot be superpage aligned.
> -     */
> -    if ( ( end_gpaddr - start_gpaddr ) < level_size - 1 )
> -        return false;
> -
> -    /* Both the gpaddr and maddr must be aligned */
> -    if ( start_gpaddr & level_mask )
> -        return false;
> -    if ( maddr & level_mask )
> -        return false;
> -    return true;
> -}
> -
>  #define P2M_ONE_DESCEND        0
>  #define P2M_ONE_PROGRESS_NOP   0x1
>  #define P2M_ONE_PROGRESS       0x10
> @@ -1168,81 +1132,6 @@ static int apply_one_level(struct domain *d,
>  
>      switch ( op )
>      {
> -    case INSERT:
> -        if ( is_mapping_aligned(*addr, end_gpaddr, *maddr, level_size) &&
> -           /*
> -            * We do not handle replacing an existing table with a superpage
> -            * or when mem_access is in use.
> -            */
> -             (level == 3 || (!p2m_table(orig_pte) && !p2m->mem_access_enabled)) )
> -        {
> -            rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)), a);
> -            if ( rc < 0 )
> -                return rc;
> -
> -            /* New mapping is superpage aligned, make it */
> -            pte = mfn_to_p2m_entry(_mfn(*maddr >> PAGE_SHIFT), t, a);
> -            if ( level < 3 )
> -                pte.p2m.table = 0; /* Superpage entry */
> -
> -            p2m_write_pte(entry, pte, p2m->clean_pte);
> -
> -            *flush |= p2m_valid(orig_pte);
> -
> -            *addr += level_size;
> -            *maddr += level_size;
> -
> -            if ( p2m_valid(orig_pte) )
> -            {
> -                /*
> -                 * We can't currently get here for an existing table
> -                 * mapping, since we don't handle replacing an
> -                 * existing table with a superpage. If we did we would
> -                 * need to handle freeing (and accounting) for the bit
> -                 * of the p2m tree which we would be about to lop off.
> -                 */
> -                BUG_ON(level < 3 && p2m_table(orig_pte));
> -                if ( level == 3 )
> -                    p2m_put_l3_page(_mfn(orig_pte.p2m.base),
> -                                    orig_pte.p2m.type);
> -            }
> -            else /* New mapping */
> -                p2m->stats.mappings[level]++;
> -
> -            return P2M_ONE_PROGRESS;
> -        }
> -        else
> -        {
> -            /* New mapping is not superpage aligned, create a new table entry */
> -
> -            /* L3 is always suitably aligned for mapping (handled, above) */
> -            BUG_ON(level == 3);
> -
> -            /* Not present -> create table entry and descend */
> -            if ( !p2m_valid(orig_pte) )
> -            {
> -                rc = p2m_create_table(p2m, entry, 0);
> -                if ( rc < 0 )
> -                    return rc;
> -                return P2M_ONE_DESCEND;
> -            }
> -
> -            /* Existing superpage mapping -> shatter and descend */
> -            if ( p2m_mapping(orig_pte) )
> -            {
> -                *flush = true;
> -                rc = p2m_shatter_page(p2m, entry, level);
> -                if ( rc < 0 )
> -                    return rc;
> -            } /* else: an existing table mapping -> descend */
> -
> -            BUG_ON(!p2m_table(*entry));
> -
> -            return P2M_ONE_DESCEND;
> -        }
> -
> -        break;
> -
>      case MEMACCESS:
>          if ( level < 3 )
>          {
> @@ -1456,13 +1345,6 @@ static int apply_p2m_changes(struct domain *d,
>          BUG_ON(level > 3);
>      }
>  
> -    if ( op == INSERT )
> -    {
> -        p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn,
> -                                      gfn_add(sgfn, nr));
> -        p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn);
> -    }
> -
>      rc = 0;
>  
>  out:
> @@ -1485,22 +1367,6 @@ out:
>  
>      p2m_write_unlock(p2m);
>  
> -    if ( rc < 0 && ( op == INSERT ) &&
> -         addr != start_gpaddr )
> -    {
> -        unsigned long gfn = paddr_to_pfn(addr);
> -
> -        BUG_ON(addr == end_gpaddr);
> -        /*
> -         * addr keeps the address of the end of the last successfully-inserted
> -         * mapping.
> -         */
> -        p2m_write_lock(p2m);
> -        p2m_set_entry(p2m, sgfn, gfn - gfn_x(sgfn), INVALID_MFN,
> -                      p2m_invalid, p2m_access_rwx);
> -        p2m_write_unlock(p2m);
> -    }
> -
>      return rc;
>  }
>  
> @@ -1510,8 +1376,18 @@ static inline int p2m_insert_mapping(struct domain *d,
>                                       mfn_t mfn,
>                                       p2m_type_t t)
>  {
> -    return apply_p2m_changes(d, INSERT, start_gfn, nr, mfn,
> -                             0, t, d->arch.p2m.default_access);
> +    struct p2m_domain *p2m = &d->arch.p2m;
> +    int rc;
> +
> +    p2m_write_lock(p2m);
> +    /*
> +     * XXX: Do we want to do safety check on what is replaced?
> +     * See what x86 is doing.
> +     */
> +    rc = p2m_set_entry(p2m, start_gfn, nr, mfn, t, p2m->default_access);
> +    p2m_write_unlock(p2m);
> +
> +    return rc;
>  }
>  
>  static inline int p2m_remove_mapping(struct domain *d,
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 22/22] xen/arm: p2m: Do not handle shattering in p2m_create_table
  2016-07-28 14:51 ` [RFC 22/22] xen/arm: p2m: Do not handle shattering in p2m_create_table Julien Grall
@ 2016-09-06 18:59   ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 18:59 UTC (permalink / raw)
  To: Julien Grall; +Cc: proskurin, sstabellini, steve.capper, wei.chen, xen-devel

On Thu, 28 Jul 2016, Julien Grall wrote:
> The helper p2m_create_table is only called to create a brand new table.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>  xen/arch/arm/p2m.c | 51 ++++++---------------------------------------------
>  1 file changed, 6 insertions(+), 45 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 16ed393..4aaa96f 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -264,8 +264,7 @@ static p2m_access_t p2m_mem_access_radix_get(struct p2m_domain *p2m, gfn_t gfn)
>  #define GUEST_TABLE_SUPER_PAGE 1
>  #define GUEST_TABLE_NORMAL_PAGE 2
>  
> -static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
> -                            int level_shift);
> +static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry);
>  
>  /*
>   * Take the currently mapped table, find the corresponding GFN entry,
> @@ -291,7 +290,7 @@ static int p2m_next_level(struct p2m_domain *p2m, bool read_only,
>          if ( read_only )
>              return GUEST_TABLE_MAP_FAILED;
>  
> -        ret = p2m_create_table(p2m, entry, /* not used */ ~0);
> +        ret = p2m_create_table(p2m, entry);
>          if ( ret )
>              return GUEST_TABLE_MAP_FAILED;
>      }
> @@ -557,25 +556,14 @@ static inline void p2m_remove_pte(lpae_t *p, bool clean_pte)
>      p2m_write_pte(p, pte, clean_pte);
>  }
>  
> -/*
> - * Allocate a new page table page and hook it in via the given entry.
> - * apply_one_level relies on this returning 0 on success
> - * and -ve on failure.
> - *
> - * If the existing entry is present then it must be a mapping and not
> - * a table and it will be shattered into the next level down.
> - *
> - * level_shift is the number of bits at the level we want to create.
> - */
> -static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
> -                            int level_shift)
> +/* Allocate a new page table page and hook it in via the given entry. */
> +static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry)
>  {
>      struct page_info *page;
>      lpae_t *p;
>      lpae_t pte;
> -    int splitting = p2m_valid(*entry);
>  
> -    BUG_ON(p2m_table(*entry));
> +    ASSERT(!p2m_valid(*entry));
>  
>      page = alloc_domheap_page(NULL, 0);
>      if ( page == NULL )
> @@ -584,35 +572,8 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry,
>      page_list_add(page, &p2m->pages);
>  
>      p = __map_domain_page(page);
> -    if ( splitting )
> -    {
> -        p2m_type_t t = entry->p2m.type;
> -        mfn_t mfn = _mfn(entry->p2m.base);
> -        int i;
>  
> -        /*
> -         * We are either splitting a first level 1G page into 512 second level
> -         * 2M pages, or a second level 2M page into 512 third level 4K pages.
> -         */
> -         for ( i=0 ; i < LPAE_ENTRIES; i++ )
> -         {
> -             pte = mfn_to_p2m_entry(mfn_add(mfn, i << (level_shift - LPAE_SHIFT)),
> -                                    t, p2m->default_access);
> -
> -             /*
> -              * First and second level super pages set p2m.table = 0, but
> -              * third level entries set table = 1.
> -              */
> -             if ( level_shift - LPAE_SHIFT )
> -                 pte.p2m.table = 0;
> -
> -             write_pte(&p[i], pte);
> -         }
> -
> -         page->u.inuse.p2m_refcount = LPAE_ENTRIES;
> -    }
> -    else
> -        clear_page(p);
> +    clear_page(p);
>  
>      if ( p2m->clean_pte )
>          clean_dcache_va_range(p, PAGE_SIZE);
> -- 
> 1.9.1
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-07-28 14:51 ` [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry Julien Grall
  2016-07-28 15:04   ` Razvan Cojocaru
  2016-07-28 17:21   ` Tamas K Lengyel
@ 2016-09-06 19:06   ` Stefano Stabellini
  2016-09-06 19:16     ` Razvan Cojocaru
  2 siblings, 1 reply; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 19:06 UTC (permalink / raw)
  To: Julien Grall
  Cc: sstabellini, Razvan Cojocaru, steve.capper, proskurin, xen-devel,
	Tamas K Lengyel, wei.chen

On Thu, 28 Jul 2016, Julien Grall wrote:
> The function p2m_set_mem_access can be re-implemented using the generic
> functions p2m_get_entry and __p2m_set_entry.
> 
> Note that because of the implementation of p2m_get_entry, a TLB
> invalidation instruction will be issued for each 4KB page. Therefore the
> performance of memaccess will be impacted, however the function is now
> safe on all the processors.
> 
> Also the function apply_p2m_changes is dropped completely as it is not
> unused anymore.
> 
> Signed-off-by: Julien Grall <julien.grall@arm.com>
> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> Cc: Tamas K Lengyel <tamas@tklengyel.com>

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>


>     I have not ran any performance test with memaccess for now, but I
>     expect an important and unavoidable impact because of how memaccess
>     has been designed to workaround hardware limitation. Note that might
>     be possible to re-work memaccess work on superpage but this should
>     be done in a separate patch.
> ---
>  xen/arch/arm/p2m.c | 329 +++++++----------------------------------------------
>  1 file changed, 38 insertions(+), 291 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 707c7be..16ed393 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -1081,295 +1081,6 @@ static int p2m_set_entry(struct p2m_domain *p2m,
>      return rc;
>  }
>  
> -#define P2M_ONE_DESCEND        0
> -#define P2M_ONE_PROGRESS_NOP   0x1
> -#define P2M_ONE_PROGRESS       0x10
> -
> -static int p2m_shatter_page(struct p2m_domain *p2m,
> -                            lpae_t *entry,
> -                            unsigned int level)
> -{
> -    const paddr_t level_shift = level_shifts[level];
> -    int rc = p2m_create_table(p2m, entry, level_shift - PAGE_SHIFT);
> -
> -    if ( !rc )
> -    {
> -        p2m->stats.shattered[level]++;
> -        p2m->stats.mappings[level]--;
> -        p2m->stats.mappings[level+1] += LPAE_ENTRIES;
> -    }
> -
> -    return rc;
> -}
> -
> -/*
> - * 0   == (P2M_ONE_DESCEND) continue to descend the tree
> - * +ve == (P2M_ONE_PROGRESS_*) handled at this level, continue, flush,
> - *        entry, addr and maddr updated.  Return value is an
> - *        indication of the amount of work done (for preemption).
> - * -ve == (-Exxx) error.
> - */
> -static int apply_one_level(struct domain *d,
> -                           lpae_t *entry,
> -                           unsigned int level,
> -                           enum p2m_operation op,
> -                           paddr_t start_gpaddr,
> -                           paddr_t end_gpaddr,
> -                           paddr_t *addr,
> -                           paddr_t *maddr,
> -                           bool_t *flush,
> -                           p2m_type_t t,
> -                           p2m_access_t a)
> -{
> -    const paddr_t level_size = level_sizes[level];
> -
> -    struct p2m_domain *p2m = &d->arch.p2m;
> -    lpae_t pte;
> -    const lpae_t orig_pte = *entry;
> -    int rc;
> -
> -    BUG_ON(level > 3);
> -
> -    switch ( op )
> -    {
> -    case MEMACCESS:
> -        if ( level < 3 )
> -        {
> -            if ( !p2m_valid(orig_pte) )
> -            {
> -                *addr += level_size;
> -                return P2M_ONE_PROGRESS_NOP;
> -            }
> -
> -            /* Shatter large pages as we descend */
> -            if ( p2m_mapping(orig_pte) )
> -            {
> -                rc = p2m_shatter_page(p2m, entry, level);
> -                if ( rc < 0 )
> -                    return rc;
> -            } /* else: an existing table mapping -> descend */
> -
> -            return P2M_ONE_DESCEND;
> -        }
> -        else
> -        {
> -            pte = orig_pte;
> -
> -            if ( p2m_valid(pte) )
> -            {
> -                rc = p2m_mem_access_radix_set(p2m, _gfn(paddr_to_pfn(*addr)),
> -                                              a);
> -                if ( rc < 0 )
> -                    return rc;
> -
> -                p2m_set_permission(&pte, pte.p2m.type, a);
> -                p2m_write_pte(entry, pte, p2m->clean_pte);
> -            }
> -
> -            *addr += level_size;
> -            *flush = true;
> -            return P2M_ONE_PROGRESS;
> -        }
> -    }
> -
> -    BUG(); /* Should never get here */
> -}
> -
> -/*
> - * The page is only used by the P2M code which is protected by the p2m->lock.
> - * So we can avoid to use atomic helpers.
> - */
> -static void update_reference_mapping(struct page_info *page,
> -                                     lpae_t old_entry,
> -                                     lpae_t new_entry)
> -{
> -    if ( p2m_valid(old_entry) && !p2m_valid(new_entry) )
> -        page->u.inuse.p2m_refcount--;
> -    else if ( !p2m_valid(old_entry) && p2m_valid(new_entry) )
> -        page->u.inuse.p2m_refcount++;
> -}
> -
> -static int apply_p2m_changes(struct domain *d,
> -                     enum p2m_operation op,
> -                     gfn_t sgfn,
> -                     unsigned long nr,
> -                     mfn_t smfn,
> -                     uint32_t mask,
> -                     p2m_type_t t,
> -                     p2m_access_t a)
> -{
> -    paddr_t start_gpaddr = pfn_to_paddr(gfn_x(sgfn));
> -    paddr_t end_gpaddr = pfn_to_paddr(gfn_x(sgfn) + nr);
> -    paddr_t maddr = pfn_to_paddr(mfn_x(smfn));
> -    int rc, ret;
> -    struct p2m_domain *p2m = &d->arch.p2m;
> -    lpae_t *mappings[4] = { NULL, NULL, NULL, NULL };
> -    struct page_info *pages[4] = { NULL, NULL, NULL, NULL };
> -    paddr_t addr;
> -    unsigned int level = 0;
> -    unsigned int cur_root_table = ~0;
> -    unsigned int cur_offset[4] = { ~0, ~0, ~0, ~0 };
> -    unsigned int count = 0;
> -    const unsigned int preempt_count_limit = (op == MEMACCESS) ? 1 : 0x2000;
> -    const bool_t preempt = !is_idle_vcpu(current);
> -    bool_t flush = false;
> -    PAGE_LIST_HEAD(free_pages);
> -    struct page_info *pg;
> -
> -    p2m_write_lock(p2m);
> -
> -    /* Static mapping. P2M_ROOT_PAGES > 1 are handled below */
> -    if ( P2M_ROOT_PAGES == 1 )
> -    {
> -        mappings[P2M_ROOT_LEVEL] = __map_domain_page(p2m->root);
> -        pages[P2M_ROOT_LEVEL] = p2m->root;
> -    }
> -
> -    addr = start_gpaddr;
> -    while ( addr < end_gpaddr )
> -    {
> -        int root_table;
> -        const unsigned int offsets[4] = {
> -            zeroeth_table_offset(addr),
> -            first_table_offset(addr),
> -            second_table_offset(addr),
> -            third_table_offset(addr)
> -        };
> -
> -        /*
> -         * Check if current iteration should be possibly preempted.
> -         * Since count is initialised to 0 above we are guaranteed to
> -         * always make at least one pass as long as preempt_count_limit is
> -         * initialized with a value >= 1.
> -         */
> -        if ( preempt && count >= preempt_count_limit
> -             && hypercall_preempt_check() )
> -        {
> -            switch ( op )
> -            {
> -            case MEMACCESS:
> -            {
> -                /*
> -                 * Preempt setting mem_access permissions as required by XSA-89,
> -                 * if it's not the last iteration.
> -                 */
> -                uint32_t progress = paddr_to_pfn(addr) - gfn_x(sgfn) + 1;
> -
> -                if ( nr > progress && !(progress & mask) )
> -                {
> -                    rc = progress;
> -                    goto out;
> -                }
> -                break;
> -            }
> -
> -            default:
> -                break;
> -            };
> -
> -            /*
> -             * Reset current iteration counter.
> -             */
> -            count = 0;
> -        }
> -
> -        if ( P2M_ROOT_PAGES > 1 )
> -        {
> -            int i;
> -            /*
> -             * Concatenated root-level tables. The table number will be the
> -             * offset at the previous level. It is not possible to concatenate
> -             * a level-0 root.
> -             */
> -            ASSERT(P2M_ROOT_LEVEL > 0);
> -            root_table = offsets[P2M_ROOT_LEVEL - 1];
> -            if ( root_table >= P2M_ROOT_PAGES )
> -            {
> -                rc = -EINVAL;
> -                goto out;
> -            }
> -
> -            if ( cur_root_table != root_table )
> -            {
> -                if ( mappings[P2M_ROOT_LEVEL] )
> -                    unmap_domain_page(mappings[P2M_ROOT_LEVEL]);
> -                mappings[P2M_ROOT_LEVEL] =
> -                    __map_domain_page(p2m->root + root_table);
> -                pages[P2M_ROOT_LEVEL] = p2m->root + root_table;
> -                cur_root_table = root_table;
> -                /* Any mapping further down is now invalid */
> -                for ( i = P2M_ROOT_LEVEL; i < 4; i++ )
> -                    cur_offset[i] = ~0;
> -            }
> -        }
> -
> -        for ( level = P2M_ROOT_LEVEL; level < 4; level++ )
> -        {
> -            unsigned offset = offsets[level];
> -            lpae_t *entry = &mappings[level][offset];
> -            lpae_t old_entry = *entry;
> -
> -            ret = apply_one_level(d, entry,
> -                                  level, op,
> -                                  start_gpaddr, end_gpaddr,
> -                                  &addr, &maddr, &flush,
> -                                  t, a);
> -            if ( ret < 0 ) { rc = ret ; goto out; }
> -            count += ret;
> -
> -            if ( ret != P2M_ONE_PROGRESS_NOP )
> -                update_reference_mapping(pages[level], old_entry, *entry);
> -
> -            /* L3 had better have done something! We cannot descend any further */
> -            BUG_ON(level == 3 && ret == P2M_ONE_DESCEND);
> -            if ( ret != P2M_ONE_DESCEND ) break;
> -
> -            BUG_ON(!p2m_valid(*entry));
> -
> -            if ( cur_offset[level] != offset )
> -            {
> -                /* Update mapping for next level */
> -                int i;
> -                if ( mappings[level+1] )
> -                    unmap_domain_page(mappings[level+1]);
> -                mappings[level+1] = map_domain_page(_mfn(entry->p2m.base));
> -                pages[level+1] = mfn_to_page(entry->p2m.base);
> -                cur_offset[level] = offset;
> -                /* Any mapping further down is now invalid */
> -                for ( i = level+1; i < 4; i++ )
> -                    cur_offset[i] = ~0;
> -            }
> -            /* else: next level already valid */
> -        }
> -
> -        BUG_ON(level > 3);
> -    }
> -
> -    rc = 0;
> -
> -out:
> -    if ( flush )
> -    {
> -        p2m_flush_tlb_sync(&d->arch.p2m);
> -        ret = iommu_iotlb_flush(d, gfn_x(sgfn), nr);
> -        if ( !rc )
> -            rc = ret;
> -    }
> -
> -    while ( (pg = page_list_remove_head(&free_pages)) )
> -        free_domheap_page(pg);
> -
> -    for ( level = P2M_ROOT_LEVEL; level < 4; level ++ )
> -    {
> -        if ( mappings[level] )
> -            unmap_domain_page(mappings[level]);
> -    }
> -
> -    p2m_write_unlock(p2m);
> -
> -    return rc;
> -}
> -
>  static inline int p2m_insert_mapping(struct domain *d,
>                                       gfn_t start_gfn,
>                                       unsigned long nr,
> @@ -2069,6 +1780,7 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
>  {
>      struct p2m_domain *p2m = p2m_get_hostp2m(d);
>      p2m_access_t a;
> +    unsigned int order;
>      long rc = 0;
>  
>      static const p2m_access_t memaccess[] = {
> @@ -2111,8 +1823,43 @@ long p2m_set_mem_access(struct domain *d, gfn_t gfn, uint32_t nr,
>          return 0;
>      }
>  
> -    rc = apply_p2m_changes(d, MEMACCESS, gfn_add(gfn, start),
> -                           (nr - start), INVALID_MFN, mask, 0, a);
> +    p2m_write_lock(p2m);
> +
> +    for ( gfn = gfn_add(gfn, start); nr > start; gfn = gfn_add(gfn, 1UL << order) )
> +    {
> +        p2m_type_t t;
> +        mfn_t mfn = p2m_get_entry(p2m, gfn, &t, NULL, &order);
> +
> +        /* Skip hole */
> +        if ( mfn_eq(mfn, INVALID_MFN) )
> +        {
> +            /*
> +             * the order corresponds to the order of the mapping in the
> +             * page table. so we need to align the gfn before
> +             * incrementing.
> +             */
> +            gfn = _gfn(gfn_x(gfn) & ~((1UL << order) - 1));
> +            continue;
> +        }
> +        else
> +        {
> +            order = 0;
> +            rc = __p2m_set_entry(p2m, gfn, 0, mfn, t, a);
> +            if ( rc )
> +                break;
> +        }
> +
> +        start += (1UL << order);
> +        /* Check for continuation if it is not the last iteration */
> +        if ( nr > start && !(start & mask) && hypercall_preempt_check() )
> +        {
> +            rc = start;
> +            break;
> +        }
> +    }
> +
> +    p2m_write_unlock(p2m);
> +
>      if ( rc < 0 )
>          return rc;
>      else if ( rc > 0 )
.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-09-06 19:06   ` Stefano Stabellini
@ 2016-09-06 19:16     ` Razvan Cojocaru
  2016-09-06 19:17       ` Stefano Stabellini
  2016-09-07  6:56       ` Julien Grall
  0 siblings, 2 replies; 98+ messages in thread
From: Razvan Cojocaru @ 2016-09-06 19:16 UTC (permalink / raw)
  To: Stefano Stabellini, Julien Grall
  Cc: proskurin, xen-devel, Tamas K Lengyel, wei.chen, steve.capper

On 09/06/16 22:06, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> > The function p2m_set_mem_access can be re-implemented using the generic
>> > functions p2m_get_entry and __p2m_set_entry.
>> > 
>> > Note that because of the implementation of p2m_get_entry, a TLB
>> > invalidation instruction will be issued for each 4KB page. Therefore the
>> > performance of memaccess will be impacted, however the function is now
>> > safe on all the processors.
>> > 
>> > Also the function apply_p2m_changes is dropped completely as it is not
>> > unused anymore.
>> > 
>> > Signed-off-by: Julien Grall <julien.grall@arm.com>
>> > Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>> > Cc: Tamas K Lengyel <tamas@tklengyel.com>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

How far is this patch from landing into staging? Considering the recent
discussion about the patch I'm working on, this would certainly impact
the upcoming ARM part of it.


Thanks,
Razvan

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-09-06 19:16     ` Razvan Cojocaru
@ 2016-09-06 19:17       ` Stefano Stabellini
  2016-09-07  6:56       ` Julien Grall
  1 sibling, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-06 19:17 UTC (permalink / raw)
  To: Razvan Cojocaru
  Cc: Stefano Stabellini, steve.capper, proskurin, xen-devel,
	Julien Grall, Tamas K Lengyel, wei.chen

On Tue, 6 Sep 2016, Razvan Cojocaru wrote:
> On 09/06/16 22:06, Stefano Stabellini wrote:
> > On Thu, 28 Jul 2016, Julien Grall wrote:
> >> > The function p2m_set_mem_access can be re-implemented using the generic
> >> > functions p2m_get_entry and __p2m_set_entry.
> >> > 
> >> > Note that because of the implementation of p2m_get_entry, a TLB
> >> > invalidation instruction will be issued for each 4KB page. Therefore the
> >> > performance of memaccess will be impacted, however the function is now
> >> > safe on all the processors.
> >> > 
> >> > Also the function apply_p2m_changes is dropped completely as it is not
> >> > unused anymore.
> >> > 
> >> > Signed-off-by: Julien Grall <julien.grall@arm.com>
> >> > Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
> >> > Cc: Tamas K Lengyel <tamas@tklengyel.com>
> > Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
> 
> How far is this patch from landing into staging? Considering the recent
> discussion about the patch I'm working on, this would certainly impact
> the upcoming ARM part of it.

The patch depends on previous patches on this series. Some of them are
acked but not all.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-09-06 19:16     ` Razvan Cojocaru
  2016-09-06 19:17       ` Stefano Stabellini
@ 2016-09-07  6:56       ` Julien Grall
  2016-09-07  7:03         ` Razvan Cojocaru
  1 sibling, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-09-07  6:56 UTC (permalink / raw)
  To: Razvan Cojocaru, Stefano Stabellini
  Cc: proskurin, xen-devel, Tamas K Lengyel, wei.chen, steve.capper

Hi Ravzan,

On 06/09/2016 20:16, Razvan Cojocaru wrote:
> On 09/06/16 22:06, Stefano Stabellini wrote:
>> On Thu, 28 Jul 2016, Julien Grall wrote:
>>>> The function p2m_set_mem_access can be re-implemented using the generic
>>>> functions p2m_get_entry and __p2m_set_entry.
>>>>
>>>> Note that because of the implementation of p2m_get_entry, a TLB
>>>> invalidation instruction will be issued for each 4KB page. Therefore the
>>>> performance of memaccess will be impacted, however the function is now
>>>> safe on all the processors.
>>>>
>>>> Also the function apply_p2m_changes is dropped completely as it is not
>>>> unused anymore.
>>>>
>>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
>
> How far is this patch from landing into staging? Considering the recent
> discussion about the patch I'm working on, this would certainly impact
> the upcoming ARM part of it.

I expect this to be in Xen 4.8. I also realized that without this patch 
it will be harder to implement the ARM side.

Given that I would be fine to write the ARM side once this series is out 
and your patch ready.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry
  2016-09-07  6:56       ` Julien Grall
@ 2016-09-07  7:03         ` Razvan Cojocaru
  0 siblings, 0 replies; 98+ messages in thread
From: Razvan Cojocaru @ 2016-09-07  7:03 UTC (permalink / raw)
  To: Julien Grall, Stefano Stabellini
  Cc: proskurin, xen-devel, Tamas K Lengyel, wei.chen, steve.capper

On 09/07/16 09:56, Julien Grall wrote:
> Hi Ravzan,
> 
> On 06/09/2016 20:16, Razvan Cojocaru wrote:
>> On 09/06/16 22:06, Stefano Stabellini wrote:
>>> On Thu, 28 Jul 2016, Julien Grall wrote:
>>>>> The function p2m_set_mem_access can be re-implemented using the
>>>>> generic
>>>>> functions p2m_get_entry and __p2m_set_entry.
>>>>>
>>>>> Note that because of the implementation of p2m_get_entry, a TLB
>>>>> invalidation instruction will be issued for each 4KB page.
>>>>> Therefore the
>>>>> performance of memaccess will be impacted, however the function is now
>>>>> safe on all the processors.
>>>>>
>>>>> Also the function apply_p2m_changes is dropped completely as it is not
>>>>> unused anymore.
>>>>>
>>>>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>>>> Cc: Razvan Cojocaru <rcojocaru@bitdefender.com>
>>>>> Cc: Tamas K Lengyel <tamas@tklengyel.com>
>>> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
>>
>> How far is this patch from landing into staging? Considering the recent
>> discussion about the patch I'm working on, this would certainly impact
>> the upcoming ARM part of it.
> 
> I expect this to be in Xen 4.8. I also realized that without this patch
> it will be harder to implement the ARM side.
> 
> Given that I would be fine to write the ARM side once this series is out
> and your patch ready.

Thank you Julien, I appreciate your help!


Thanks,
Razvan

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping using p2m_get_entry
  2016-09-06 18:21       ` Stefano Stabellini
@ 2016-09-07  7:37         ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-09-07  7:37 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 06/09/2016 19:21, Stefano Stabellini wrote:
> On Tue, 6 Sep 2016, Julien Grall wrote:
>> Hi Stefano,
>>
>> On 05/09/16 22:58, Stefano Stabellini wrote:
>>> On Thu, 28 Jul 2016, Julien Grall wrote:
>>>> The current implementation of relinquish_p2m_mapping is modifying the
>>>> page table to erase the entry one by one. However, this is not necessary
>>>> because the domain is not running anymore and therefore will speed up
>>>> the domain destruction.
>>>
>>> Could you please elaborate on this? Who is going to remove the p2m
>>> entries if not this function?
>>
>> The current version of relinquish is removing the reference on the page and
>> then invalidate the entry (which may involve a cache flush).
>>
>> As the page tables are not used anymore by the hardware, the latter action is
>> not necessary. This is an optimization because flushing the cache can be
>> expensive. However as mentioned later in the commit message, we need to have a
>> think on how the other helpers interact with the page table to avoid return
>> wrong entry.
>
> The idea is that nobody will remove the p2m entries, until the whole p2m
> is torn down (p2m_teardown)?

That the idea but I hadn't had time to confirm it was the case.

>> I am thinking to defer this optimization for the next release (i.e Xen 4.9) to
>> avoid rushing on it.
>
> If we are sure that there are no interactions with the p2m between the
> domain_relinquish_resources and the p2m_teardown call, then this is
> acceptable. Otherwise delaying this optimization is wiser.

I will delay the optimization.

Regards,


-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-09-06 18:51       ` Stefano Stabellini
@ 2016-09-07  8:18         ` Julien Grall
  2016-09-09 23:14           ` Stefano Stabellini
  0 siblings, 1 reply; 98+ messages in thread
From: Julien Grall @ 2016-09-07  8:18 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 06/09/2016 19:51, Stefano Stabellini wrote:
> On Tue, 6 Sep 2016, Julien Grall wrote:
>>> I was asking myself the same question
>>
>> It is not trivial. On ARMv7, there is no way to invalidate by IPA, so we still
>> need to do a full flush.
>>
>> In the case of ARMv8, it is possible to do a flush by IPA with the following
>> sequence (see D4-1739 in ARM DDI 0487A.i):
>>  tlbi ipa2e1, xt
>>  dsb
>>  tlbi vmalle1
>>
>> So I was wondering if we could leave that for a future optimization.
>
> We can leave it for now but I have an hunch that it is going to have a
> pretty significant impact.

In theory, the current approach will have an impact on platform where 
the TLBs are caching separately stage-1 and stage-2 translation.

This will not be the case if the TLBs are caching stage-1 and stage-2 in 
a single entry.

However, on ARMv7 it will not be possible to minimize the impact.

But to be honest, most of the time, the p2m will be modified via 
guest_physmap_add_entry and guest_physmap_remove_page. Both are often 
called with order = 0 or order = 6 (for domain use 64KB page granularity).

Taken aside domains using 64KB page granularity, the number of TLB 
flushs will be either 1 or 2 depending whether you need to shatter a 
superpage. Which is the same as today.

In the case of 64KB domain, the number of TLB flush will be higher if 
the domain is replacing existing mapping (1 TLB flush per 4KB-entry).
But this is already a programming issue given that in this case the 
underlying memory (if RAM) will not be freed until the domain is 
destroyed. In general a domain should remove a mapping before creating a 
new one at the same address.

I have done some testing and noticed that DOMU p2m will not be often 
modified. In the case of DOM0, this will mostly happen when a domain is 
created (you have to map the new domain memory). Although, the number of 
flushes should be the same given dom0 will use balloon page (i.e the 
stage-2 mapping does not exist).

I have some ideas on how to optimize a bit more the code, but they are 
heavy and will be hard to maintain. I would prefer to defer it until 
user come with use case where the performance hit is too much.

>
>
>>>> +        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
>>>> +            order = FIRST_ORDER;
>>>> +        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
>>>> +            order = SECOND_ORDER;
>>>> +        else
>>>> +            order = THIRD_ORDER;
>>>> +
>>>> +        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
>>>> +        if ( rc )
>>>> +            break;
>>>
>>> Shouldn't we be checking that "(1<<order)" doesn't exceed "todo" before
>>> calling __p2m_set_entry? Otherwise we risk creating a mapping bigger
>>> than requested.
>>
>> The mask is defined as gfn_x(sgfn) | mfn_x(smfn) | todo
>>
>> So we will never have the order exceeding "todo".
>
> Ah, I see. But this way we are not able to do superpage mappings for
> regions larger than 2MB but not multiple of 2MB (3MB for example). But I
> don't think we were able to do it before either so it is not a
> requirement for the patch.

 From my understanding of is_mapping_aligned, the case you mentioned is 
handled.

The function p2m_set_entry is using the number of page because some 
caller (such as MMIO ones) are passing a number of page. However, the 
main callers are guest_physmap_remove_page and guest_physmap_add_entry. 
They take an order in parameter.

So it would be a nice feature to have, but I don't think this is 
strictly necessary.

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry
  2016-09-07  8:18         ` Julien Grall
@ 2016-09-09 23:14           ` Stefano Stabellini
  0 siblings, 0 replies; 98+ messages in thread
From: Stefano Stabellini @ 2016-09-09 23:14 UTC (permalink / raw)
  To: Julien Grall
  Cc: proskurin, Stefano Stabellini, steve.capper, wei.chen, xen-devel

On Wed, 7 Sep 2016, Julien Grall wrote:
> Hi Stefano,
> 
> On 06/09/2016 19:51, Stefano Stabellini wrote:
> > On Tue, 6 Sep 2016, Julien Grall wrote:
> > > > I was asking myself the same question
> > > 
> > > It is not trivial. On ARMv7, there is no way to invalidate by IPA, so we
> > > still
> > > need to do a full flush.
> > > 
> > > In the case of ARMv8, it is possible to do a flush by IPA with the
> > > following
> > > sequence (see D4-1739 in ARM DDI 0487A.i):
> > >  tlbi ipa2e1, xt
> > >  dsb
> > >  tlbi vmalle1
> > > 
> > > So I was wondering if we could leave that for a future optimization.
> > 
> > We can leave it for now but I have an hunch that it is going to have a
> > pretty significant impact.
> 
> In theory, the current approach will have an impact on platform where the TLBs
> are caching separately stage-1 and stage-2 translation.
> 
> This will not be the case if the TLBs are caching stage-1 and stage-2 in a
> single entry.
> 
> However, on ARMv7 it will not be possible to minimize the impact.
> 
> But to be honest, most of the time, the p2m will be modified via
> guest_physmap_add_entry and guest_physmap_remove_page. Both are often called
> with order = 0 or order = 6 (for domain use 64KB page granularity).
> 
> Taken aside domains using 64KB page granularity, the number of TLB flushs will
> be either 1 or 2 depending whether you need to shatter a superpage. Which is
> the same as today.
> 
> In the case of 64KB domain, the number of TLB flush will be higher if the
> domain is replacing existing mapping (1 TLB flush per 4KB-entry).
> But this is already a programming issue given that in this case the underlying
> memory (if RAM) will not be freed until the domain is destroyed. In general a
> domain should remove a mapping before creating a new one at the same address.
> 
> I have done some testing and noticed that DOMU p2m will not be often modified.
> In the case of DOM0, this will mostly happen when a domain is created (you
> have to map the new domain memory). Although, the number of flushes should be
> the same given dom0 will use balloon page (i.e the stage-2 mapping does not
> exist).
> 
> I have some ideas on how to optimize a bit more the code, but they are heavy
> and will be hard to maintain. I would prefer to defer it until user come with
> use case where the performance hit is too much.

All right.


> > > > > +        if ( !(mask & ((1UL << FIRST_ORDER) - 1)) )
> > > > > +            order = FIRST_ORDER;
> > > > > +        else if ( !(mask & ((1UL << SECOND_ORDER) - 1)) )
> > > > > +            order = SECOND_ORDER;
> > > > > +        else
> > > > > +            order = THIRD_ORDER;
> > > > > +
> > > > > +        rc = __p2m_set_entry(p2m, sgfn, order, smfn, t, a);
> > > > > +        if ( rc )
> > > > > +            break;
> > > > 
> > > > Shouldn't we be checking that "(1<<order)" doesn't exceed "todo" before
> > > > calling __p2m_set_entry? Otherwise we risk creating a mapping bigger
> > > > than requested.
> > > 
> > > The mask is defined as gfn_x(sgfn) | mfn_x(smfn) | todo
> > > 
> > > So we will never have the order exceeding "todo".
> > 
> > Ah, I see. But this way we are not able to do superpage mappings for
> > regions larger than 2MB but not multiple of 2MB (3MB for example). But I
> > don't think we were able to do it before either so it is not a
> > requirement for the patch.
> 
> From my understanding of is_mapping_aligned, the case you mentioned is
> handled.
> 
> The function p2m_set_entry is using the number of page because some caller
> (such as MMIO ones) are passing a number of page. However, the main callers
> are guest_physmap_remove_page and guest_physmap_add_entry. They take an order
> in parameter.
> 
> So it would be a nice feature to have, but I don't think this is strictly
> necessary.
 
I think it shouldn't take much to implement it, but it's up to you.

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping using p2m_set_entry
  2016-09-06 18:57   ` Stefano Stabellini
@ 2016-09-15 10:38     ` Julien Grall
  0 siblings, 0 replies; 98+ messages in thread
From: Julien Grall @ 2016-09-15 10:38 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: proskurin, wei.chen, steve.capper, xen-devel

Hi Stefano,

On 06/09/2016 19:57, Stefano Stabellini wrote:
> On Thu, 28 Jul 2016, Julien Grall wrote:
>> The function p2m_insert_mapping can be re-implemented using the generic
>> function p2m_set_entry.
>>
>> Note that the mapping is not reverted anymore if Xen fails to insert a
>> mapping. This was added to ensure the MMIO are not kept half-mapped
>> in case of failure and to follow the x86 counterpart. This was removed
>> on the x86 part by commit c3c756bd "x86/p2m: use large pages for MMIO
>> mappings" and I think we should let the caller taking care of it.
>>
>> Finally drop the operation INSERT in apply_* as nobody is using it
>> anymore. Note that the functios could have been dropped in one go at the
>                            ^ functions
>
>> end, however I find easier to drop the operations one by one avoiding a
>> big deletion in the patch that convert the last operation.
>>
>> Signed-off-by: Julien Grall <julien.grall@arm.com>
>>
>> ---
>>     Whilst there is no safety checks on what is replaced in the P2M
>>     (such as foreign/grant mapping as x86 does), we may want to add it
>>     ensuring the guest is not doing something dumb. Any opinions?
>
> We don't necessarily need to protect a guest from its own dumbness, as
> long as we can correctly deal with it (account for grant and foreign
> mappings replaced this way).

I think this could be a good improvement for the future and help 
debugging potential bug in the guest.

Anyway, it is not strictly necessary today given that all the reference 
counting should be done properly. I will add it in my todo list.

>
> Given that the old code doesn't do it anyway:
>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>

Thank you!

Regards,

-- 
Julien Grall

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel

^ permalink raw reply	[flat|nested] 98+ messages in thread

end of thread, other threads:[~2016-09-15 10:38 UTC | newest]

Thread overview: 98+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-28 14:51 [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Julien Grall
2016-07-28 14:51 ` [RFC 01/22] xen/arm: do_trap_instr_abort_guest: Move the IPA computation out of the switch Julien Grall
2016-08-16  0:21   ` Stefano Stabellini
2016-08-16 16:20     ` Julien Grall
2016-08-31 10:01       ` Julien Grall
2016-08-31 19:43       ` Stefano Stabellini
2016-09-06 14:54         ` Julien Grall
2016-07-28 14:51 ` [RFC 02/22] xen/arm: p2m: Store in p2m_domain whether we need to clean the entry Julien Grall
2016-08-16  0:35   ` Stefano Stabellini
2016-08-31 10:29     ` Julien Grall
2016-07-28 14:51 ` [RFC 03/22] xen/arm: p2m: Rename parameter in p2m_{remove, write}_pte Julien Grall
2016-08-16  0:36   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 04/22] xen/arm: p2m: Use typesafe gfn in p2m_mem_access_radix_set Julien Grall
2016-08-16  0:39   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 05/22] xen/arm: traps: Move MMIO emulation code in a separate helper Julien Grall
2016-08-16  0:49   ` Stefano Stabellini
2016-08-31 10:36     ` Julien Grall
2016-07-28 14:51 ` [RFC 06/22] xen/arm: traps: Check the P2M before injecting a data/instruction abort Julien Grall
2016-08-23  1:05   ` Stefano Stabellini
2016-08-31 10:58     ` Julien Grall
2016-07-28 14:51 ` [RFC 07/22] xen/arm: p2m: Rework p2m_put_l3_page Julien Grall
2016-08-23  1:10   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 08/22] xen/arm: p2m: Invalidate the TLBs when write unlocking the p2m Julien Grall
2016-08-23  1:18   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 09/22] xen/arm: p2m: Change the type of level_shifts from paddr_t to unsigned int Julien Grall
2016-08-23  1:20   ` Stefano Stabellini
2016-08-31 11:04     ` Julien Grall
2016-07-28 14:51 ` [RFC 10/22] xen/arm: p2m: Move the lookup helpers at the top of the file Julien Grall
2016-07-28 14:51 ` [RFC 11/22] xen/arm: p2m: Introduce p2m_get_root_pointer and use it in __p2m_lookup Julien Grall
2016-08-23  1:34   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookup Julien Grall
2016-07-30 18:37   ` Tamas K Lengyel
2016-08-31  0:30   ` [RFC 12/22] xen/arm: p2m: Introduce p2m_get_entry and use it to implement __p2m_lookupo Stefano Stabellini
2016-08-31 12:25     ` Julien Grall
2016-08-31 19:33       ` Stefano Stabellini
2016-09-01 11:37         ` Julien Grall
2016-07-28 14:51 ` [RFC 13/22] xen/arm: p2m: Replace all usage of __p2m_lookup with p2m_get_entry Julien Grall
2016-07-28 17:29   ` Tamas K Lengyel
2016-07-28 17:36     ` Tamas K Lengyel
2016-07-29 15:06       ` Julien Grall
2016-07-29 22:36         ` Tamas K Lengyel
2016-07-28 17:51     ` Julien Grall
2016-09-05 20:45   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 14/22] xen/arm: p2m: Re-implement p2m_cache_flush using p2m_get_entry Julien Grall
2016-09-05 21:13   ` Stefano Stabellini
2016-09-06 14:56     ` Julien Grall
2016-07-28 14:51 ` [RFC 15/22] xen/arm: p2m: Re-implement relinquish_p2m_mapping " Julien Grall
2016-09-05 21:58   ` Stefano Stabellini
2016-09-06 15:05     ` Julien Grall
2016-09-06 18:21       ` Stefano Stabellini
2016-09-07  7:37         ` Julien Grall
2016-07-28 14:51 ` [RFC 16/22] xen/arm: p2m: Make p2m_{valid, table, mapping} helpers inline Julien Grall
2016-09-05 22:00   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 17/22] xen/arm: p2m: Introduce a helper to check if an entry is a superpage Julien Grall
2016-09-05 22:03   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 18/22] xen/arm: p2m: Introduce p2m_set_entry and __p2m_set_entry Julien Grall
2016-07-30 18:40   ` Tamas K Lengyel
2016-08-15 10:22   ` Sergej Proskurin
2016-09-06  1:08   ` Stefano Stabellini
2016-09-06 17:12     ` Julien Grall
2016-09-06 18:51       ` Stefano Stabellini
2016-09-07  8:18         ` Julien Grall
2016-09-09 23:14           ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 19/22] xen/arm: p2m: Re-implement p2m_remove_using using p2m_set_entry Julien Grall
2016-09-06 18:53   ` Stefano Stabellini
2016-07-28 14:51 ` [RFC 20/22] xen/arm: p2m: Re-implement p2m_insert_mapping " Julien Grall
2016-09-06 18:57   ` Stefano Stabellini
2016-09-15 10:38     ` Julien Grall
2016-07-28 14:51 ` [RFC 21/22] xen/arm: p2m: Re-implement p2m_set_mem_access using p2m_{set, get}_entry Julien Grall
2016-07-28 15:04   ` Razvan Cojocaru
2016-07-28 15:16     ` Julien Grall
2016-08-01 15:40     ` Julien Grall
2016-08-01 15:59       ` Tamas K Lengyel
2016-08-01 16:15         ` Julien Grall
2016-08-01 16:27           ` Tamas K Lengyel
2016-08-01 16:33             ` Julien Grall
2016-08-01 16:41               ` Tamas K Lengyel
2016-08-02  6:07             ` Razvan Cojocaru
2016-08-01 16:34       ` Mark Rutland
2016-08-01 16:57         ` Julien Grall
2016-08-01 17:26           ` Mark Rutland
2016-08-01 18:22             ` Mark Rutland
2016-08-02  9:58               ` Julien Grall
2016-08-02 10:26                 ` Mark Rutland
2016-07-28 17:21   ` Tamas K Lengyel
2016-09-06 19:06   ` Stefano Stabellini
2016-09-06 19:16     ` Razvan Cojocaru
2016-09-06 19:17       ` Stefano Stabellini
2016-09-07  6:56       ` Julien Grall
2016-09-07  7:03         ` Razvan Cojocaru
2016-07-28 14:51 ` [RFC 22/22] xen/arm: p2m: Do not handle shattering in p2m_create_table Julien Grall
2016-09-06 18:59   ` Stefano Stabellini
2016-07-28 17:46 ` [RFC 00/22] xen/arm: Rework the P2M code to follow break-before-make sequence Tamas K Lengyel
2016-07-29 16:23   ` Julien Grall
2016-07-29 19:05     ` Julien Grall
2016-08-15 10:24 ` Julien Grall
2016-08-15 15:06 ` Edgar E. Iglesias
2016-08-17  2:28   ` Shanker Donthineni

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.