Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
BACKPORT: ACPI / APEI: Switch estatus pool to use vmalloc memory
Browse files Browse the repository at this point in the history
The ghes code is careful to parse and round firmware's advertised
memory requirements for CPER records, up to a maximum of 64K.
However when ghes_estatus_pool_expand() does its work, it splits
the requested size into PAGE_SIZE granules.

This means if firmware generates 5K of CPER records, and correctly
describes this in the table, __process_error() will silently fail as it
is unable to allocate more than PAGE_SIZE.

Switch the estatus pool to vmalloc() memory. On x86 vmalloc() memory
may fault and be fixed up by vmalloc_fault(). To prevent this call
vmalloc_sync_all() before an NMI handler could discover the memory.

This patch is needed because Quicksilver firmware-first error handling
uses the SDEI notification type for communication between trusted
firmware and the OS. This adds needed NMI and SDEI functionality so
that the SDEI path in the kernel through APEI acts as an NMI and is
properly wired up to the APEI interfaces.

Backported from: torvalds/linux@0ac234b

Signed-off-by: James Morse <[email protected]>
Reviewed-by: Borislav Petkov <[email protected]>
Signed-off-by: Tyler Baicar <[email protected]>
  • Loading branch information
James Morse authored and tphan-ampere committed Apr 21, 2020
1 parent 460f959 commit a33b383
Showing 1 changed file with 15 additions and 15 deletions.
30 changes: 15 additions & 15 deletions drivers/acpi/apei/ghes.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,40 +170,40 @@ static int ghes_estatus_pool_init(void)
return 0;
}

static void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
static void ghes_estatus_pool_free_chunk(struct gen_pool *pool,
struct gen_pool_chunk *chunk,
void *data)
{
free_page(chunk->start_addr);
vfree((void *)chunk->start_addr);
}

static void ghes_estatus_pool_exit(void)
{
gen_pool_for_each_chunk(ghes_estatus_pool,
ghes_estatus_pool_free_chunk_page, NULL);
ghes_estatus_pool_free_chunk, NULL);
gen_pool_destroy(ghes_estatus_pool);
}

static int ghes_estatus_pool_expand(unsigned long len)
{
unsigned long i, pages, size, addr;
int ret;
unsigned long size, addr;

ghes_estatus_pool_size_request += PAGE_ALIGN(len);
size = gen_pool_size(ghes_estatus_pool);
if (size >= ghes_estatus_pool_size_request)
return 0;
pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
for (i = 0; i < pages; i++) {
addr = __get_free_page(GFP_KERNEL);
if (!addr)
return -ENOMEM;
ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
if (ret)
return ret;
}

return 0;
addr = (unsigned long)vmalloc(PAGE_ALIGN(len));
if (!addr)
return -ENOMEM;

/*
* New allocation must be visible in all pgd before it can be found by
* an NMI allocating from the pool.
*/
vmalloc_sync_all();

return gen_pool_add(ghes_estatus_pool, addr, PAGE_ALIGN(len), -1);
}

static int map_gen_v2(struct ghes *ghes)
Expand Down

0 comments on commit a33b383

Please sign in to comment.