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)) )
+ 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 */
--
2.17.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 */
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,
--
Julien Grall
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.
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,
--
Julien Grall
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>
© 2016 - 2026 Red Hat, Inc.