From: Julien Grall Date: Sat, 26 Sep 2020 18:53:27 +0000 (+0100) Subject: xen/arm: acpi: The fixmap area should always be cleared during failure/unmap X-Git-Tag: archive/raspbian/4.14.3+32-g9de3671772-1_deb11u1+rpi1^2~14 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=e6554149628dda2855dbec1acb27709836293355;p=xen.git xen/arm: acpi: The fixmap area should always be cleared during failure/unmap 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 Signed-off-by: Julien Grall Reviewed-by: Stefano Stabellini (cherry picked from commit 4d625ff3c3a939dc270b03654337568c30c5ab6e) Gbp-Pq: Name 0031-xen-arm-acpi-The-fixmap-area-should-always-be-cleare.patch --- diff --git a/xen/arch/arm/acpi/lib.c b/xen/arch/arm/acpi/lib.c index fcc186b033..a59cc4074c 100644 --- a/xen/arch/arm/acpi/lib.c +++ b/xen/arch/arm/acpi/lib.c @@ -25,40 +25,79 @@ #include #include +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) + 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 */