diff mbox series

[v2,2/7] xen/arm: acpi: The fixmap area should always be cleared during failure/unmap

Message ID 20201023154156.6593-3-julien@xen.org (mailing list archive)
State New, archived
Headers show
Series xen/arm: Unbreak ACPI | expand

Commit Message

Julien Grall Oct. 23, 2020, 3:41 p.m. UTC
From: Julien Grall <jgrall@amazon.com>

Commit 022387ee1ad3 "xen/arm: mm: Don't open-code Xen PT update in
{set, clear}_fixmap()" enforced that each set_fixmap() should be
paired with a clear_fixmap(). Any failure to follow the model would
result to a platform crash.

Unfortunately, the use of fixmap in the ACPI code was overlooked as it
is calling set_fixmap() but not clear_fixmap().

The function __acpi_os_map_table() is reworked so:
    - We know before the mapping whether the fixmap region is big
    enough for the mapping.
    - It will fail if the fixmap is already in use. This is not a
    change of behavior but clarifying the current expectation to avoid
    hitting a BUG().

The function __acpi_os_unmap_table() will now call clear_fixmap().

Reported-by: Wei Xu <xuwei5@hisilicon.com>
Signed-off-by: Julien Grall <jgrall@amazon.com>

---

The discussion on the original thread [1] suggested to also zap it on
x86. This is technically not necessary today, so it is left alone for
now.

I looked at making the fixmap code common but the index are inverted
between Arm and x86.

    Changes in v2:
        - Clarify the commit message
        - Fix the size computation in __acpi_unmap_table()

[1] https://lore.kernel.org/xen-devel/5E26C935.9080107@hisilicon.com/
---
 xen/arch/arm/acpi/lib.c | 73 +++++++++++++++++++++++++++++++----------
 1 file changed, 56 insertions(+), 17 deletions(-)

Comments

Stefano Stabellini Oct. 24, 2020, 12:16 a.m. UTC | #1
On Fri, 23 Oct 2020, Julien Grall wrote:
> From: Julien Grall <jgrall@amazon.com>
> 
> Commit 022387ee1ad3 "xen/arm: mm: Don't open-code Xen PT update in
> {set, clear}_fixmap()" enforced that each set_fixmap() should be
> paired with a clear_fixmap(). Any failure to follow the model would
> result to a platform crash.
> 
> Unfortunately, the use of fixmap in the ACPI code was overlooked as it
> is calling set_fixmap() but not clear_fixmap().
> 
> The function __acpi_os_map_table() is reworked so:
>     - We know before the mapping whether the fixmap region is big
>     enough for the mapping.
>     - It will fail if the fixmap is already in use. This is not a
>     change of behavior but clarifying the current expectation to avoid
>     hitting a BUG().
> 
> The function __acpi_os_unmap_table() will now call clear_fixmap().
> 
> Reported-by: Wei Xu <xuwei5@hisilicon.com>
> Signed-off-by: Julien Grall <jgrall@amazon.com>
> 
> ---
> 
> The discussion on the original thread [1] suggested to also zap it on
> x86. This is technically not necessary today, so it is left alone for
> now.
> 
> I looked at making the fixmap code common but the index are inverted
> between Arm and x86.
> 
>     Changes in v2:
>         - Clarify the commit message
>         - Fix the size computation in __acpi_unmap_table()
> 
> [1] https://lore.kernel.org/xen-devel/5E26C935.9080107@hisilicon.com/
> ---
>  xen/arch/arm/acpi/lib.c | 73 +++++++++++++++++++++++++++++++----------
>  1 file changed, 56 insertions(+), 17 deletions(-)
> 
> diff --git a/xen/arch/arm/acpi/lib.c b/xen/arch/arm/acpi/lib.c
> index fcc186b03399..b755620e67b5 100644
> --- a/xen/arch/arm/acpi/lib.c
> +++ b/xen/arch/arm/acpi/lib.c
> @@ -25,40 +25,79 @@
>  #include <xen/init.h>
>  #include <xen/mm.h>
>  
> +static bool fixmap_inuse;
> +
>  char *__acpi_map_table(paddr_t phys, unsigned long size)
>  {
> -    unsigned long base, offset, mapped_size;
> -    int idx;
> +    unsigned long base, offset;
> +    mfn_t mfn;
> +    unsigned int idx;
>  
>      /* No arch specific implementation after early boot */
>      if ( system_state >= SYS_STATE_boot )
>          return NULL;
>  
>      offset = phys & (PAGE_SIZE - 1);
> -    mapped_size = PAGE_SIZE - offset;
> -    set_fixmap(FIXMAP_ACPI_BEGIN, maddr_to_mfn(phys), PAGE_HYPERVISOR);
> -    base = FIXMAP_ADDR(FIXMAP_ACPI_BEGIN);
> +    base = FIXMAP_ADDR(FIXMAP_ACPI_BEGIN) + offset;
> +
> +    /* Check the fixmap is big enough to map the region */
> +    if ( (FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE - base) < size )
> +        return NULL;
> +
> +    /* With the fixmap, we can only map one region at the time */
> +    if ( fixmap_inuse )
> +        return NULL;
>  
> -    /* Most cases can be covered by the below. */
> +    fixmap_inuse = true;
> +
> +    size += offset;
> +    mfn = maddr_to_mfn(phys);
>      idx = FIXMAP_ACPI_BEGIN;
> -    while ( mapped_size < size )
> -    {
> -        if ( ++idx > FIXMAP_ACPI_END )
> -            return NULL;    /* cannot handle this */
> -        phys += PAGE_SIZE;
> -        set_fixmap(idx, maddr_to_mfn(phys), PAGE_HYPERVISOR);
> -        mapped_size += PAGE_SIZE;
> -    }
>  
> -    return ((char *) base + offset);
> +    do {
> +        set_fixmap(idx, mfn, PAGE_HYPERVISOR);
> +        size -= min(size, (unsigned long)PAGE_SIZE);
> +        mfn = mfn_add(mfn, 1);
> +        idx++;
> +    } while ( size > 0 );
> +
> +    return (char *)base;
>  }
>  
>  bool __acpi_unmap_table(const void *ptr, unsigned long size)
>  {
>      vaddr_t vaddr = (vaddr_t)ptr;
> +    unsigned int idx;
> +
> +    /* We are only handling fixmap address in the arch code */
> +    if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
> +         (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END)) )

Is it missing "+ PAGE_SIZE"?

   if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
        (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE) )


> +        return false;
> +
> +    /*
> +     * __acpi_map_table() will always return a pointer in the first page
> +     * for the ACPI fixmap region. The caller is expected to free with
> +     * the same address.
> +     */
> +    ASSERT((vaddr & PAGE_MASK) == FIXMAP_ADDR(FIXMAP_ACPI_BEGIN));
> +
> +    /* The region allocated fit in the ACPI fixmap region. */
> +    ASSERT(size < (FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE - vaddr));
> +    ASSERT(fixmap_inuse);
> +
> +    fixmap_inuse = false;
> +
> +    size += vaddr - FIXMAP_ADDR(FIXMAP_ACPI_BEGIN);
> +    idx = FIXMAP_ACPI_BEGIN;
> +
> +    do
> +    {
> +        clear_fixmap(idx);
> +        size -= min(size, (unsigned long)PAGE_SIZE);
> +        idx++;
> +    } while ( size > 0 );
>  
> -    return ((vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) &&
> -            (vaddr < (FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE)));
> +    return true;
>  }
>  
>  /* True to indicate PSCI 0.2+ is implemented */
Julien Grall Oct. 30, 2020, 6:21 p.m. UTC | #2
Hi Stefano,

On 24/10/2020 01:16, Stefano Stabellini wrote:
> On Fri, 23 Oct 2020, Julien Grall wrote:
>>   bool __acpi_unmap_table(const void *ptr, unsigned long size)
>>   {
>>       vaddr_t vaddr = (vaddr_t)ptr;
>> +    unsigned int idx;
>> +
>> +    /* We are only handling fixmap address in the arch code */
>> +    if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
>> +         (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END)) )
> 
> Is it missing "+ PAGE_SIZE"?
> 
>     if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
>          (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE) )

Yes it should be + PAGE_SIZE. Do you mind if I fix it on commit?

Cheers,
Stefano Stabellini Oct. 30, 2020, 6:28 p.m. UTC | #3
On Fri, 30 Oct 2020, Julien Grall wrote:
> Hi Stefano,
> 
> On 24/10/2020 01:16, Stefano Stabellini wrote:
> > On Fri, 23 Oct 2020, Julien Grall wrote:
> > >   bool __acpi_unmap_table(const void *ptr, unsigned long size)
> > >   {
> > >       vaddr_t vaddr = (vaddr_t)ptr;
> > > +    unsigned int idx;
> > > +
> > > +    /* We are only handling fixmap address in the arch code */
> > > +    if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
> > > +         (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END)) )
> > 
> > Is it missing "+ PAGE_SIZE"?
> > 
> >     if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
> >          (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE) )
> 
> Yes it should be + PAGE_SIZE. Do you mind if I fix it on commit?

No, I don't mind. Go ahead.
Julien Grall Oct. 30, 2020, 6:29 p.m. UTC | #4
Hi,

On 30/10/2020 18:28, Stefano Stabellini wrote:
> On Fri, 30 Oct 2020, Julien Grall wrote:
>> Hi Stefano,
>>
>> On 24/10/2020 01:16, Stefano Stabellini wrote:
>>> On Fri, 23 Oct 2020, Julien Grall wrote:
>>>>    bool __acpi_unmap_table(const void *ptr, unsigned long size)
>>>>    {
>>>>        vaddr_t vaddr = (vaddr_t)ptr;
>>>> +    unsigned int idx;
>>>> +
>>>> +    /* We are only handling fixmap address in the arch code */
>>>> +    if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
>>>> +         (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END)) )
>>>
>>> Is it missing "+ PAGE_SIZE"?
>>>
>>>      if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
>>>           (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE) )
>>
>> Yes it should be + PAGE_SIZE. Do you mind if I fix it on commit?
> 
> No, I don't mind. Go ahead.

I technically don't have a ack for this patch. Can I consider this as a 
Acked-by? :)

Cheers,
Stefano Stabellini Oct. 30, 2020, 6:34 p.m. UTC | #5
On Fri, 30 Oct 2020, Julien Grall wrote:
> Hi,
> 
> On 30/10/2020 18:28, Stefano Stabellini wrote:
> > On Fri, 30 Oct 2020, Julien Grall wrote:
> > > Hi Stefano,
> > > 
> > > On 24/10/2020 01:16, Stefano Stabellini wrote:
> > > > On Fri, 23 Oct 2020, Julien Grall wrote:
> > > > >    bool __acpi_unmap_table(const void *ptr, unsigned long size)
> > > > >    {
> > > > >        vaddr_t vaddr = (vaddr_t)ptr;
> > > > > +    unsigned int idx;
> > > > > +
> > > > > +    /* We are only handling fixmap address in the arch code */
> > > > > +    if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
> > > > > +         (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END)) )
> > > > 
> > > > Is it missing "+ PAGE_SIZE"?
> > > > 
> > > >      if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
> > > >           (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE) )
> > > 
> > > Yes it should be + PAGE_SIZE. Do you mind if I fix it on commit?
> > 
> > No, I don't mind. Go ahead.
> 
> I technically don't have a ack for this patch. Can I consider this as a
> Acked-by? :)

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

Patch

diff --git a/xen/arch/arm/acpi/lib.c b/xen/arch/arm/acpi/lib.c
index fcc186b03399..b755620e67b5 100644
--- a/xen/arch/arm/acpi/lib.c
+++ b/xen/arch/arm/acpi/lib.c
@@ -25,40 +25,79 @@ 
 #include <xen/init.h>
 #include <xen/mm.h>
 
+static bool fixmap_inuse;
+
 char *__acpi_map_table(paddr_t phys, unsigned long size)
 {
-    unsigned long base, offset, mapped_size;
-    int idx;
+    unsigned long base, offset;
+    mfn_t mfn;
+    unsigned int idx;
 
     /* No arch specific implementation after early boot */
     if ( system_state >= SYS_STATE_boot )
         return NULL;
 
     offset = phys & (PAGE_SIZE - 1);
-    mapped_size = PAGE_SIZE - offset;
-    set_fixmap(FIXMAP_ACPI_BEGIN, maddr_to_mfn(phys), PAGE_HYPERVISOR);
-    base = FIXMAP_ADDR(FIXMAP_ACPI_BEGIN);
+    base = FIXMAP_ADDR(FIXMAP_ACPI_BEGIN) + offset;
+
+    /* Check the fixmap is big enough to map the region */
+    if ( (FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE - base) < size )
+        return NULL;
+
+    /* With the fixmap, we can only map one region at the time */
+    if ( fixmap_inuse )
+        return NULL;
 
-    /* Most cases can be covered by the below. */
+    fixmap_inuse = true;
+
+    size += offset;
+    mfn = maddr_to_mfn(phys);
     idx = FIXMAP_ACPI_BEGIN;
-    while ( mapped_size < size )
-    {
-        if ( ++idx > FIXMAP_ACPI_END )
-            return NULL;    /* cannot handle this */
-        phys += PAGE_SIZE;
-        set_fixmap(idx, maddr_to_mfn(phys), PAGE_HYPERVISOR);
-        mapped_size += PAGE_SIZE;
-    }
 
-    return ((char *) base + offset);
+    do {
+        set_fixmap(idx, mfn, PAGE_HYPERVISOR);
+        size -= min(size, (unsigned long)PAGE_SIZE);
+        mfn = mfn_add(mfn, 1);
+        idx++;
+    } while ( size > 0 );
+
+    return (char *)base;
 }
 
 bool __acpi_unmap_table(const void *ptr, unsigned long size)
 {
     vaddr_t vaddr = (vaddr_t)ptr;
+    unsigned int idx;
+
+    /* We are only handling fixmap address in the arch code */
+    if ( (vaddr < FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) ||
+         (vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_END)) )
+        return false;
+
+    /*
+     * __acpi_map_table() will always return a pointer in the first page
+     * for the ACPI fixmap region. The caller is expected to free with
+     * the same address.
+     */
+    ASSERT((vaddr & PAGE_MASK) == FIXMAP_ADDR(FIXMAP_ACPI_BEGIN));
+
+    /* The region allocated fit in the ACPI fixmap region. */
+    ASSERT(size < (FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE - vaddr));
+    ASSERT(fixmap_inuse);
+
+    fixmap_inuse = false;
+
+    size += vaddr - FIXMAP_ADDR(FIXMAP_ACPI_BEGIN);
+    idx = FIXMAP_ACPI_BEGIN;
+
+    do
+    {
+        clear_fixmap(idx);
+        size -= min(size, (unsigned long)PAGE_SIZE);
+        idx++;
+    } while ( size > 0 );
 
-    return ((vaddr >= FIXMAP_ADDR(FIXMAP_ACPI_BEGIN)) &&
-            (vaddr < (FIXMAP_ADDR(FIXMAP_ACPI_END) + PAGE_SIZE)));
+    return true;
 }
 
 /* True to indicate PSCI 0.2+ is implemented */