Commit 174cc718 authored by Lv Zheng's avatar Lv Zheng Committed by Rafael J. Wysocki

ACPICA: Tables: Back port acpi_get_table_with_size() and...

ACPICA: Tables: Back port acpi_get_table_with_size() and early_acpi_os_unmap_memory() from Linux kernel

ACPICA commit cac6790954d4d752a083e6122220b8a22febcd07

This patch back ports Linux acpi_get_table_with_size() and
early_acpi_os_unmap_memory() into ACPICA upstream to reduce divergences.

The 2 APIs are used by Linux as table management APIs for long time, it
contains a hidden logic that during the early stage, the mapped tables
should be unmapped before the early stage ends.

During the early stage, tables are handled by the following sequence:
 acpi_get_table_with_size();
 parse the table
 early_acpi_os_unmap_memory();
During the late stage, tables are handled by the following sequence:
 acpi_get_table();
 parse the table
Linux uses acpi_gbl_permanent_mmap to distinguish the early stage and the
late stage.

The reasoning of introducing acpi_get_table_with_size() is: ACPICA will
remember the early mapped pointer in acpi_get_table() and Linux isn't able to
prevent ACPICA from using the wrong early mapped pointer during the late
stage as there is no API provided from ACPICA to be an inverse of
acpi_get_table() to forget the early mapped pointer.

But how ACPICA can work with the early/late stage requirement? Inside of
ACPICA, tables are ensured to be remained in "INSTALLED" state during the
early stage, and they are carefully not transitioned to "VALIDATED" state
until the late stage. So the same logic is in fact implemented inside of
ACPICA in a different way. The gap is only that the feature is not provided
to the OSPMs in an accessible external API style.

It then is possible to fix the gap by providing an inverse of
acpi_get_table() from ACPICA, so that the two Linux sequences can be
combined:
 acpi_get_table();
 parse the table
 acpi_put_table();
In order to work easier with the current Linux code, acpi_get_table() and
acpi_put_table() is implemented in a usage counting based style:
 1. When the usage count of the table is increased from 0 to 1, table is
    mapped and .Pointer is set with the mapping address (VALIDATED);
 2. When the usage count of the table is decreased from 1 to 0, .Pointer
    is unset and the mapping address is unmapped (INVALIDATED).
So that we can deploy the new APIs to Linux with minimal effort by just
invoking acpi_get_table() in acpi_get_table_with_size() and invoking
acpi_put_table() in early_acpi_os_unmap_memory(). Lv Zheng.

Link: https://github.com/acpica/acpica/commit/cac67909Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 5a6e7ec3
...@@ -166,6 +166,12 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc, ...@@ -166,6 +166,12 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address); acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address);
acpi_status
acpi_tb_get_table(struct acpi_table_desc *table_desc,
struct acpi_table_header **out_table);
void acpi_tb_put_table(struct acpi_table_desc *table_desc);
/* /*
* tbxfload * tbxfload
*/ */
......
...@@ -381,3 +381,88 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address) ...@@ -381,3 +381,88 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
acpi_os_unmap_memory(table, length); acpi_os_unmap_memory(table, length);
return_ACPI_STATUS(AE_OK); return_ACPI_STATUS(AE_OK);
} }
/*******************************************************************************
*
* FUNCTION: acpi_tb_get_table
*
* PARAMETERS: table_desc - Table descriptor
* out_table - Where the pointer to the table is returned
*
* RETURN: Status and pointer to the requested table
*
* DESCRIPTION: Increase a reference to a table descriptor and return the
* validated table pointer.
* If the table descriptor is an entry of the root table list,
* this API must be invoked with ACPI_MTX_TABLES acquired.
*
******************************************************************************/
acpi_status
acpi_tb_get_table(struct acpi_table_desc *table_desc,
struct acpi_table_header **out_table)
{
acpi_status status;
ACPI_FUNCTION_TRACE(acpi_tb_get_table);
if (table_desc->validation_count == 0) {
/* Table need to be "VALIDATED" */
status = acpi_tb_validate_table(table_desc);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
}
table_desc->validation_count++;
if (table_desc->validation_count == 0) {
ACPI_ERROR((AE_INFO,
"Table %p, Validation count is zero after increment\n",
table_desc));
table_desc->validation_count--;
return_ACPI_STATUS(AE_LIMIT);
}
*out_table = table_desc->pointer;
return_ACPI_STATUS(AE_OK);
}
/*******************************************************************************
*
* FUNCTION: acpi_tb_put_table
*
* PARAMETERS: table_desc - Table descriptor
*
* RETURN: None
*
* DESCRIPTION: Decrease a reference to a table descriptor and release the
* validated table pointer if no references.
* If the table descriptor is an entry of the root table list,
* this API must be invoked with ACPI_MTX_TABLES acquired.
*
******************************************************************************/
void acpi_tb_put_table(struct acpi_table_desc *table_desc)
{
ACPI_FUNCTION_TRACE(acpi_tb_put_table);
if (table_desc->validation_count == 0) {
ACPI_WARNING((AE_INFO,
"Table %p, Validation count is zero before decrement\n",
table_desc));
return_VOID;
}
table_desc->validation_count--;
if (table_desc->validation_count == 0) {
/* Table need to be "INVALIDATED" */
acpi_tb_invalidate_table(table_desc);
}
return_VOID;
}
...@@ -282,7 +282,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header) ...@@ -282,7 +282,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_get_table_with_size * FUNCTION: acpi_get_table
* *
* PARAMETERS: signature - ACPI signature of needed table * PARAMETERS: signature - ACPI signature of needed table
* instance - Which instance (for SSDTs) * instance - Which instance (for SSDTs)
...@@ -292,16 +292,21 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header) ...@@ -292,16 +292,21 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header)
* *
* DESCRIPTION: Finds and verifies an ACPI table. Table must be in the * DESCRIPTION: Finds and verifies an ACPI table. Table must be in the
* RSDT/XSDT. * RSDT/XSDT.
* Note that an early stage acpi_get_table() call must be paired
* with an early stage acpi_put_table() call. otherwise the table
* pointer mapped by the early stage mapping implementation may be
* erroneously unmapped by the late stage unmapping implementation
* in an acpi_put_table() invoked during the late stage.
* *
******************************************************************************/ ******************************************************************************/
acpi_status acpi_status
acpi_get_table_with_size(char *signature, acpi_get_table(char *signature,
u32 instance, struct acpi_table_header **out_table, u32 instance, struct acpi_table_header ** out_table)
acpi_size *tbl_size)
{ {
u32 i; u32 i;
u32 j; u32 j;
acpi_status status; acpi_status status = AE_NOT_FOUND;
struct acpi_table_desc *table_desc;
/* Parameter validation */ /* Parameter validation */
...@@ -309,13 +314,22 @@ acpi_get_table_with_size(char *signature, ...@@ -309,13 +314,22 @@ acpi_get_table_with_size(char *signature,
return (AE_BAD_PARAMETER); return (AE_BAD_PARAMETER);
} }
/*
* Note that the following line is required by some OSPMs, they only
* check if the returned table is NULL instead of the returned status
* to determined if this function is succeeded.
*/
*out_table = NULL;
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
/* Walk the root table list */ /* Walk the root table list */
for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count; for (i = 0, j = 0; i < acpi_gbl_root_table_list.current_table_count;
i++) { i++) {
if (!ACPI_COMPARE_NAME table_desc = &acpi_gbl_root_table_list.tables[i];
(&(acpi_gbl_root_table_list.tables[i].signature),
signature)) { if (!ACPI_COMPARE_NAME(&table_desc->signature, signature)) {
continue; continue;
} }
...@@ -323,43 +337,65 @@ acpi_get_table_with_size(char *signature, ...@@ -323,43 +337,65 @@ acpi_get_table_with_size(char *signature,
continue; continue;
} }
status = status = acpi_tb_get_table(table_desc, out_table);
acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]); break;
if (ACPI_SUCCESS(status)) {
*out_table = acpi_gbl_root_table_list.tables[i].pointer;
*tbl_size = acpi_gbl_root_table_list.tables[i].length;
}
if (!acpi_gbl_permanent_mmap) {
acpi_gbl_root_table_list.tables[i].pointer = NULL;
} }
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return (status); return (status);
}
return (AE_NOT_FOUND);
} }
ACPI_EXPORT_SYMBOL(acpi_get_table_with_size) ACPI_EXPORT_SYMBOL(acpi_get_table)
acpi_status /*******************************************************************************
acpi_get_table(char *signature, *
u32 instance, struct acpi_table_header **out_table) * FUNCTION: acpi_put_table
*
* PARAMETERS: table - The pointer to the table
*
* RETURN: None
*
* DESCRIPTION: Release a table returned by acpi_get_table() and its clones.
* Note that it is not safe if this function was invoked after an
* uninstallation happened to the original table descriptor.
* Currently there is no OSPMs' requirement to handle such
* situations.
*
******************************************************************************/
void acpi_put_table(struct acpi_table_header *table)
{ {
acpi_size tbl_size; u32 i;
struct acpi_table_desc *table_desc;
return acpi_get_table_with_size(signature, ACPI_FUNCTION_TRACE(acpi_put_table);
instance, out_table, &tbl_size);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
/* Walk the root table list */
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; i++) {
table_desc = &acpi_gbl_root_table_list.tables[i];
if (table_desc->pointer != table) {
continue;
}
acpi_tb_put_table(table_desc);
break;
}
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_VOID;
} }
ACPI_EXPORT_SYMBOL(acpi_get_table) ACPI_EXPORT_SYMBOL(acpi_put_table)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_get_table_by_index * FUNCTION: acpi_get_table_by_index
* *
* PARAMETERS: table_index - Table index * PARAMETERS: table_index - Table index
* table - Where the pointer to the table is returned * out_table - Where the pointer to the table is returned
* *
* RETURN: Status and pointer to the requested table * RETURN: Status and pointer to the requested table
* *
...@@ -368,7 +404,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table) ...@@ -368,7 +404,7 @@ ACPI_EXPORT_SYMBOL(acpi_get_table)
* *
******************************************************************************/ ******************************************************************************/
acpi_status acpi_status
acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table) acpi_get_table_by_index(u32 table_index, struct acpi_table_header **out_table)
{ {
acpi_status status; acpi_status status;
...@@ -376,35 +412,33 @@ acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table) ...@@ -376,35 +412,33 @@ acpi_get_table_by_index(u32 table_index, struct acpi_table_header **table)
/* Parameter validation */ /* Parameter validation */
if (!table) { if (!out_table) {
return_ACPI_STATUS(AE_BAD_PARAMETER); return_ACPI_STATUS(AE_BAD_PARAMETER);
} }
/*
* Note that the following line is required by some OSPMs, they only
* check if the returned table is NULL instead of the returned status
* to determined if this function is succeeded.
*/
*out_table = NULL;
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
/* Validate index */ /* Validate index */
if (table_index >= acpi_gbl_root_table_list.current_table_count) { if (table_index >= acpi_gbl_root_table_list.current_table_count) {
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES); status = AE_BAD_PARAMETER;
return_ACPI_STATUS(AE_BAD_PARAMETER); goto unlock_and_exit;
} }
if (!acpi_gbl_root_table_list.tables[table_index].pointer) {
/* Table is not mapped, map it */
status = status =
acpi_tb_validate_table(&acpi_gbl_root_table_list. acpi_tb_get_table(&acpi_gbl_root_table_list.tables[table_index],
tables[table_index]); out_table);
if (ACPI_FAILURE(status)) {
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status);
}
}
*table = acpi_gbl_root_table_list.tables[table_index].pointer; unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(AE_OK); return_ACPI_STATUS(status);
} }
ACPI_EXPORT_SYMBOL(acpi_get_table_by_index) ACPI_EXPORT_SYMBOL(acpi_get_table_by_index)
......
...@@ -433,10 +433,45 @@ void __ref acpi_os_unmap_memory(void *virt, acpi_size size) ...@@ -433,10 +433,45 @@ void __ref acpi_os_unmap_memory(void *virt, acpi_size size)
} }
EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); EXPORT_SYMBOL_GPL(acpi_os_unmap_memory);
/*******************************************************************************
*
* acpi_get_table_with_size()/early_acpi_os_unmap_memory():
*
* These 2 functions are traditionally used by Linux to map/unmap physical
* addressed ACPI tables during the early stage.
* They are deprectated now. Do not use them in the new code, but use
* acpi_get_table()/acpi_put_table() instead.
*
******************************************************************************/
acpi_status
acpi_get_table_with_size(char *signature,
u32 instance, struct acpi_table_header **out_table,
acpi_size *tbl_size)
{
acpi_status status;
status = acpi_get_table(signature, instance, out_table);
if (ACPI_SUCCESS(status)) {
/*
* "tbl_size" is no longer used by
* early_acpi_os_unmap_memory(), but is still used by the
* ACPI table drivers. So sets it to the length of the
* table when the tbl_size is requested.
* "out_table" is not sanity checked as AE_BAD_PARAMETER
* is returned if it is NULL.
*/
if (tbl_size && *out_table)
*tbl_size = (*out_table)->length;
}
return (status);
}
ACPI_EXPORT_SYMBOL(acpi_get_table_with_size)
void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size)
{ {
if (!acpi_gbl_permanent_mmap) acpi_put_table(ACPI_CAST_PTR(struct acpi_table_header, virt));
__acpi_unmap_table(virt, size);
} }
int acpi_os_map_generic_address(struct acpi_generic_address *gas) int acpi_os_map_generic_address(struct acpi_generic_address *gas)
......
...@@ -513,6 +513,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status ...@@ -513,6 +513,8 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
acpi_get_table(acpi_string signature, u32 instance, acpi_get_table(acpi_string signature, u32 instance,
struct acpi_table_header struct acpi_table_header
**out_table)) **out_table))
ACPI_EXTERNAL_RETURN_VOID(void acpi_put_table(struct acpi_table_header *table))
ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_EXTERNAL_RETURN_STATUS(acpi_status
acpi_get_table_by_index(u32 table_index, acpi_get_table_by_index(u32 table_index,
struct acpi_table_header struct acpi_table_header
...@@ -974,6 +976,10 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status ...@@ -974,6 +976,10 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
**out_table, **out_table,
acpi_size *tbl_size)) acpi_size *tbl_size))
ACPI_EXTERNAL_RETURN_VOID(void
early_acpi_os_unmap_memory(void __iomem * virt,
acpi_size size))
ACPI_EXTERNAL_RETURN_STATUS(acpi_status ACPI_EXTERNAL_RETURN_STATUS(acpi_status
acpi_get_data_full(acpi_handle object, acpi_get_data_full(acpi_handle object,
acpi_object_handler handler, acpi_object_handler handler,
......
...@@ -371,6 +371,7 @@ struct acpi_table_desc { ...@@ -371,6 +371,7 @@ struct acpi_table_desc {
union acpi_name_union signature; union acpi_name_union signature;
acpi_owner_id owner_id; acpi_owner_id owner_id;
u8 flags; u8 flags;
u16 validation_count;
}; };
/* Masks for Flags field above */ /* Masks for Flags field above */
......
...@@ -142,7 +142,6 @@ static inline void acpi_os_terminate_command_signals(void) ...@@ -142,7 +142,6 @@ static inline void acpi_os_terminate_command_signals(void)
/* /*
* OSL interfaces added by Linux * OSL interfaces added by Linux
*/ */
void early_acpi_os_unmap_memory(void __iomem * virt, acpi_size size);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment