acpi table解析过程分析

acpi表结构大概是这样的
在这里插入图片描述
知道了表的大概结构我问来分析的解析的过程
解析的入口函数为acpi_tb_parse_root_table 函数

/*******************************************************************************
 *
 * FUNCTION:    acpi_tb_parse_root_table
 *
 * PARAMETERS:  rsdp                    - Pointer to the RSDP
 *
 * RETURN:      Status
 *
 * DESCRIPTION: This function is called to parse the Root System Description
 *              Table (RSDT or XSDT)
 *
 * NOTE:        Tables are mapped (not copied) for efficiency. The FACS must
 *              be mapped and cannot be copied because it contains the actual
 *              memory location of the ACPI Global Lock.
 *
 ******************************************************************************/

acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
{
	struct acpi_table_rsdp *rsdp;
	u32 table_entry_size;
	u32 i;
	u32 table_count;
	struct acpi_table_header *table;
	acpi_physical_address address;
	u32 length;
	u8 *table_entry;
	acpi_status status;
	u32 table_index;

	ACPI_FUNCTION_TRACE(tb_parse_root_table);

	/* Map the entire RSDP and extract the address of the RSDT or XSDT */

	rsdp = acpi_os_map_memory(rsdp_address, sizeof(struct acpi_table_rsdp));
	if (!rsdp) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	acpi_tb_print_table_header(rsdp_address,
				   ACPI_CAST_PTR(struct acpi_table_header,
						 rsdp));

	/* Use XSDT if present and not overridden. Otherwise, use RSDT */

	if ((rsdp->revision > 1) &&
	    rsdp->xsdt_physical_address && !acpi_gbl_do_not_use_xsdt) {
		/*
		 * RSDP contains an XSDT (64-bit physical addresses). We must use
		 * the XSDT if the revision is > 1 and the XSDT pointer is present,
		 * as per the ACPI specification.
		 */
		address = (acpi_physical_address) rsdp->xsdt_physical_address;
		table_entry_size = ACPI_XSDT_ENTRY_SIZE;
	} else {
		/* Root table is an RSDT (32-bit physical addresses) */

		address = (acpi_physical_address) rsdp->rsdt_physical_address;
		table_entry_size = ACPI_RSDT_ENTRY_SIZE;
	}

	/*
	 * It is not possible to map more than one entry in some environments,
	 * so unmap the RSDP here before mapping other tables
	 */
	acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp));

	/* Map the RSDT/XSDT table header to get the full table length */

	table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
	if (!table) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	acpi_tb_print_table_header(address, table);

	/*
	 * Validate length of the table, and map entire table.
	 * Minimum length table must contain at least one entry.
	 */
	length = table->length;
	acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));

	if (length < (sizeof(struct acpi_table_header) + table_entry_size)) {
		ACPI_BIOS_ERROR((AE_INFO,
				 "Invalid table length 0x%X in RSDT/XSDT",
				 length));
		return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
	}

	table = acpi_os_map_memory(address, length);
	if (!table) {
		return_ACPI_STATUS(AE_NO_MEMORY);
	}

	/* Validate the root table checksum */

	status = acpi_tb_verify_checksum(table, length);
	if (ACPI_FAILURE(status)) {
		acpi_os_unmap_memory(table, length);
		return_ACPI_STATUS(status);
	}

	/* Get the number of entries and pointer to first entry */

	table_count = (u32)((table->length - sizeof(struct acpi_table_header)) /
			    table_entry_size);
	table_entry = ACPI_ADD_PTR(u8, table, sizeof(struct acpi_table_header));

	/*
	 * First three entries in the table array are reserved for the DSDT
	 * and 32bit/64bit FACS, which are not actually present in the
	 * RSDT/XSDT - they come from the FADT
	 */
	acpi_gbl_root_table_list.current_table_count = 3;

	/* Initialize the root table array from the RSDT/XSDT */

	for (i = 0; i < table_count; i++) {

		/* Get the table physical address (32-bit for RSDT, 64-bit for XSDT) */

		address =
		    acpi_tb_get_root_table_entry(table_entry, table_entry_size);

		/* Skip NULL entries in RSDT/XSDT */

		if (!address) {
			goto next_table;
		}

		status = acpi_tb_install_standard_table(address,
							ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
							FALSE, TRUE,
							&table_index);

		if (ACPI_SUCCESS(status) &&
		    ACPI_COMPARE_NAME(&acpi_gbl_root_table_list.
				      tables[table_index].signature,
				      ACPI_SIG_FADT)) {
			acpi_tb_parse_fadt(table_index);
		}

next_table:

		table_entry += table_entry_size;
	}

	acpi_os_unmap_memory(table, length);

	return_ACPI_STATUS(AE_OK);
}

首先注释上说acpi表为了高效使用map映射,而FACS必须使用映射因为这段内存中存在全局锁
1 rsdp = acpi_os_map_memory(rsdp_address, sizeof(struct acpi_table_rsdp));
映射到虚拟地址RSDP
2acpi_tb_print_table_header 打印表头
表头结构如下,注意表头是直接从rsdp转换得到的

/*******************************************************************************
 *
 * Master ACPI Table Header. This common header is used by all ACPI tables
 * except the RSDP and FACS.
 *
 ******************************************************************************/

struct acpi_table_header {
    char signature[ACPI_NAME_SIZE]; /* ASCII table signature */
    u32 length;     /* Length of table in bytes, including this header */
    u8 revision;        /* ACPI Specification minor version number */
    u8 checksum;        /* To make sum of entire table == 0 */
    char oem_id[ACPI_OEM_ID_SIZE];  /* ASCII OEM identification */
    char oem_table_id[ACPI_OEM_TABLE_ID_SIZE];  /* ASCII OEM table identification */
    u32 oem_revision;   /* OEM revision number */
    char asl_compiler_id[ACPI_NAME_SIZE];   /* ASCII ASL compiler vendor ID */
    u32 asl_compiler_revision;  /* ASL compiler version */
};

第一项signature是表的签名
length为表的长度
revision 版本
checksum 校验值
oem_id 厂商id
oem_table_id table的id
oem_revision厂商版本
asl_compiler_id 编译器id
asl_compiler_revision 编译器版本

编译是为解释acpi操作语言ASL的编译器

3 根据版本选择使用xsdt还是rsdt, revision>1并且存在xsdt的情况必须使用XSDT
XSDT表全称Extended Root System Description Table,它的作用于RSDT一样,区别在于两者包含的指针地址一个是32位的,一个是64位的。XSDT表结构如下


typedef struct {
 EFI_ACPI_DESCRIPTION_HEADER Header;
 UINT64                      Entry;

RSDT结构如下

typedef struct {
 EFI_ACPI_DESCRIPTION_HEADER Header;
 UINT32                      Entry;
} 

4 找到要具体使用的RSDT/XSDT就可以进行解析了,首先解除RSDP的映射,
然后映射RSDT/XSDT表头,通过表头验证表长度是否正确,如果正确再
映射整张表

5 验证表校验和

6 处表头后为entry,获取entry个数,able_count = (u32)((table->length - sizeof(struct acpi_table_header)) /table_entry_size)

7 acpi_gbl_root_table_list.current_table_count = 3 该值设置为3的理由是保留前三项用于存放DSDT 和 FACS,这两个表不是从RSDT和XSDT中获取的,而是从FADT中得到的

8 按照计算好的表项个数去遍历RSDT/XSDT
acpi_tb_get_root_table_entry函数用于获取一个entry
acpi_tb_install_standard_table用于安装标准table
如果table是FADT 则使用acpi_tb_parse_fadt函数去解析DSDT 和 FACS

扫描二维码关注公众号,回复: 8508279 查看本文章

下面具体分析下acpi_tb_get_root_table_entry 函数,acpi_tb_install_standard_table和acpi_tb_parse_fadt

acpi_tb_get_root_table_entry用于从RSDT/XSDT中解析子table
注意RSDT/XSDT的entry变量描述的就是子table的物理地址

/*******************************************************************************
 *
 * FUNCTION:    acpi_tb_get_root_table_entry
 *
 * PARAMETERS:  table_entry         - Pointer to the RSDT/XSDT table entry
 *              table_entry_size    - sizeof 32 or 64 (RSDT or XSDT)
 *
 * RETURN:      Physical address extracted from the root table
 *
 * DESCRIPTION: Get one root table entry. Handles 32-bit and 64-bit cases on
 *              both 32-bit and 64-bit platforms
 *
 * NOTE:        acpi_physical_address is 32-bit on 32-bit platforms, 64-bit on
 *              64-bit platforms.
 *
 ******************************************************************************/

static acpi_physical_address
acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
{
    u64 address64;

    /*
     * Get the table physical address (32-bit for RSDT, 64-bit for XSDT):
     * Note: Addresses are 32-bit aligned (not 64) in both RSDT and XSDT
     */
    if (table_entry_size == ACPI_RSDT_ENTRY_SIZE) {
        /*
         * 32-bit platform, RSDT: Return 32-bit table entry
         * 64-bit platform, RSDT: Expand 32-bit to 64-bit and return
         */
        return ((acpi_physical_address)
            (*ACPI_CAST_PTR(u32, table_entry)));
    } else {
        /*
         * 32-bit platform, XSDT: Truncate 64-bit to 32-bit and return
         * 64-bit platform, XSDT: Move (unaligned) 64-bit to local,
         *  return 64-bit
         */
        ACPI_MOVE_64_TO_64(&address64, table_entry);

   #if ACPI_MACHINE_WIDTH == 32
        if (address64 > ACPI_UINT32_MAX) {

            /* Will truncate 64-bit address to 32 bits, issue warning */

            ACPI_BIOS_WARNING((AE_INFO,
                       "64-bit Physical Address in XSDT is too large (0x%8.8X%8.8X),"
                       " truncating",
                       ACPI_FORMAT_UINT64(address64)));
        }
#endif
        return ((acpi_physical_address) (address64));
    }
}

其实很简单,就是按照RSDT(32位entry) 或者 XDDT(64位entry) 转换一下。

acpi_tb_install_standard_table函数用于安装前面解析到的table entry指向的table

/*******************************************************************************
 *
 * FUNCTION:    acpi_tb_install_standard_table
 *
 * PARAMETERS:  address             - Address of the table (might be a virtual
 *                                    address depending on the table_flags)
 *              flags               - Flags for the table
 *              reload              - Whether reload should be performed
 *              override            - Whether override should be performed
 *              table_index         - Where the table index is returned
 *
 * RETURN:      Status
 *
 * DESCRIPTION: This function is called to install an ACPI table that is
 *              neither DSDT nor FACS (a "standard" table.)
 *              When this function is called by "Load" or "LoadTable" opcodes,
 *              or by acpi_load_table() API, the "Reload" parameter is set.
 *              After sucessfully returning from this function, table is
 *              "INSTALLED" but not "VALIDATED".
 *
 ******************************************************************************/

acpi_status
acpi_tb_install_standard_table(acpi_physical_address address,
                   u8 flags,
                   u8 reload, u8 override, u32 *table_index)
{
    u32 i;
    acpi_status status = AE_OK;
    struct acpi_table_desc new_table_desc;

    ACPI_FUNCTION_TRACE(tb_install_standard_table);

    /* Acquire a temporary table descriptor for validation */

    status = acpi_tb_acquire_temp_table(&new_table_desc, address, flags);
    if (ACPI_FAILURE(status)) {
        ACPI_ERROR((AE_INFO,
                "Could not acquire table length at %8.8X%8.8X",
                ACPI_FORMAT_UINT64(address))); return_ACPI_STATUS(status);
    }

    /*
     * Optionally do not load any SSDTs from the RSDT/XSDT. This can
     * be useful for debugging ACPI problems on some machines.
     */
    if (!reload &&
        acpi_gbl_disable_ssdt_table_install &&
        ACPI_COMPARE_NAME(&new_table_desc.signature, ACPI_SIG_SSDT)) {
        ACPI_INFO((AE_INFO,
               "Ignoring installation of %4.4s at %8.8X%8.8X",
               new_table_desc.signature.ascii,
               ACPI_FORMAT_UINT64(address)));
        goto release_and_exit;
    }

    /* Validate and verify a table before installation */

    status = acpi_tb_verify_temp_table(&new_table_desc, NULL);
    if (ACPI_FAILURE(status)) {
        goto release_and_exit;
    }

    if (reload) {
        /*
         * Validate the incoming table signature.
         *
         * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
         * 2) We added support for OEMx tables, signature "OEM".
         * 3) Valid tables were encountered with a null signature, so we just
         *    gave up on validating the signature, (05/2008).
         * 4) We encountered non-AML tables such as the MADT, which caused
         *    interpreter errors and kernel faults. So now, we once again allow
         *    only "SSDT", "OEMx", and now, also a null signature. (05/2011).
         */
        if ((new_table_desc.signature.ascii[0] != 0x00) &&
            (!ACPI_COMPARE_NAME
             (&new_table_desc.signature, ACPI_SIG_SSDT))
            && (ACPI_STRNCMP(new_table_desc.signature.ascii, "OEM", 3)))
        {
            ACPI_BIOS_ERROR((AE_INFO,
                     "Table has invalid signature [%4.4s] (0x%8.8X), "
                     "must be SSDT or OEMx",
                     acpi_ut_valid_acpi_name(new_table_desc.
                           signature.
                                 ascii) ?
                     new_table_desc.signature.
                     ascii : "????",
                     new_table_desc.signature.integer));

            status = AE_BAD_SIGNATURE;
            goto release_and_exit;
        }

        /* Check if table is already registered */

        for (i = 0; i < acpi_gbl_root_table_list.current_table_count;
             ++i) {
            /*
             * Check for a table match on the entire table length,
             * not just the header.
             */
            if (!acpi_tb_compare_tables(&new_table_desc, i)) {
                continue;
            }

            /*
             * Note: the current mechanism does not unregister a table if it is
             * dynamically unloaded. The related namespace entries are deleted,
             * but the table remains in the root table list.
             *
             * The assumption here is that the number of different tables that
             * will be loaded is actually small, and there is minimal overhead
             * in just keeping the table in case it is needed again.
             *
             * If this assumption changes in the future (perhaps on large
             * machines with many table load/unload operations), tables will
             * need to be unregistered when they are unloaded, and slots in the
             * root table list should be reused when empty.
             */
            if (acpi_gbl_root_table_list.tables[i].
                flags & ACPI_TABLE_IS_LOADED) {

                /* Table is still loaded, this is an error */

                status = AE_ALREADY_EXISTS;
                goto release_and_exit;
            } else {
                /*
                 * Table was unloaded, allow it to be reloaded.
                 * As we are going to return AE_OK to the caller, we should
                 * take the responsibility of freeing the input descriptor.
                 * Refill the input descriptor to ensure
                 * acpi_tb_install_table_with_override() can be called again to
                 * indicate the re-installation.
                 */
                acpi_tb_uninstall_table(&new_table_desc);
                *table_index = i;
                return_ACPI_STATUS(AE_OK);
            }
        }
    }

    /* Add the table to the global root table list */

    status = acpi_tb_get_next_root_index(&i);
    if (ACPI_FAILURE(status)) {
        goto release_and_exit;
    }

    *table_index = i;
    acpi_tb_install_table_with_override(i, &new_table_desc, override);

release_and_exit:

    /* Release the temporary table descriptor */

    acpi_tb_release_temp_table(&new_table_desc);
    return_ACPI_STATUS(status);
}

这是一个比较凶恶的函数,代码很长

1 acpi_tb_acquire_temp_table 用于创建一个acpi_table_desc结构,acpi_table_desc定义如下

struct acpi_table_desc {
    acpi_physical_address address;
    struct acpi_table_header *pointer;
    u32 length;     /* Length fixed at 32 bits (fixed in table header) */
    union acpi_name_union signature;
    acpi_owner_id owner_id;
    u8 flags;
};

主要描述了table的物理地址, table头地址和table长度,签名以及标志

2 acpi_tb_verify_temp_table 验证table是否可用,主要验证包括签名,check sum,长度等信息

3 reload表示重新加载,要先卸载原来的再加载当前的

4 acpi_tb_install_table_with_override 安装

/*******************************************************************************
 *
 * FUNCTION:    acpi_tb_install_table_with_override
 *
 * PARAMETERS:  table_index             - Index into root table array
 *              new_table_desc          - New table descriptor to install
 *              override                - Whether override should be performed
 *
 * RETURN:      None
 *
 * DESCRIPTION: Install an ACPI table into the global data structure. The
 *              table override mechanism is called to allow the host
 *              OS to replace any table before it is installed in the root
 *              table array.
 *
 ******************************************************************************/

void
acpi_tb_install_table_with_override(u32 table_index,
                    struct acpi_table_desc *new_table_desc,
                    u8 override)
{

    if (table_index >= acpi_gbl_root_table_list.current_table_count) {
        return;
    }

    /*
     * ACPI Table Override:
     *
     * Before we install the table, let the host OS override it with a new
     * one if desired. Any table within the RSDT/XSDT can be replaced,
     * including the DSDT which is pointed to by the FADT.
     */
    if (override) {
        acpi_tb_override_table(new_table_desc);
    }

    acpi_tb_init_table_descriptor(&acpi_gbl_root_table_list.
                      tables[table_index],
                      new_table_desc->address,
                      new_table_desc->flags,
                      new_table_desc->pointer);
                      acpi_tb_print_table_header(new_table_desc->address,
                     new_table_desc->pointer);

    /* Set the global integer width (based upon revision of the DSDT) */

    if (table_index == ACPI_TABLE_INDEX_DSDT) {
        acpi_ut_set_integer_width(new_table_desc->pointer->revision);
    }
}

1 如果覆盖参数为真,则用新表覆盖老表acpi_tb_override_table

2 acpi_tb_init_table_descriptor 初始化表,注意这里的参数是&acpi_gbl_root_table_list.
tables[table_index], 也就是把table_descriptor 安装到acpi_gbl_root_table_list.tables数组就算安装完成了

acpi_tb_parse_fadt函数去解析DSDT 和 FACS

/*******************************************************************************
 *
 * FUNCTION:    acpi_tb_parse_fadt
 *
 * PARAMETERS:  table_index         - Index for the FADT
 *
 * RETURN:      None
 *
 * DESCRIPTION: Initialize the FADT, DSDT and FACS tables
 *              (FADT contains the addresses of the DSDT and FACS)
 *
 ******************************************************************************/

void acpi_tb_parse_fadt(u32 table_index)
{
    u32 length;
    struct acpi_table_header *table;

    /*
     * The FADT has multiple versions with different lengths,
     * and it contains pointers to both the DSDT and FACS tables.
     *
     * Get a local copy of the FADT and convert it to a common format
     * Map entire FADT, assumed to be smaller than one page.
     */
    length = acpi_gbl_root_table_list.tables[table_index].length;

    table =
        acpi_os_map_memory(acpi_gbl_root_table_list.tables[table_index].
                   address, length);
    if (!table) {
        return;
         }

    /*
     * Validate the FADT checksum before we copy the table. Ignore
     * checksum error as we want to try to get the DSDT and FACS.
     */
    (void)acpi_tb_verify_checksum(table, length);

    /* Create a local copy of the FADT in common ACPI 2.0+ format */

    acpi_tb_create_local_fadt(table, length);

    /* All done with the real FADT, unmap it */

    acpi_os_unmap_memory(table, length);

    /* Obtain the DSDT and FACS tables via their addresses within the FADT */

    acpi_tb_install_fixed_table((acpi_physical_address) acpi_gbl_FADT.Xdsdt,
                    ACPI_SIG_DSDT, ACPI_TABLE_INDEX_DSDT);

    /* If Hardware Reduced flag is set, there is no FACS */

    if (!acpi_gbl_reduced_hardware) {
        if (acpi_gbl_FADT.facs) {
            acpi_tb_install_fixed_table((acpi_physical_address)
                            acpi_gbl_FADT.facs,
                            ACPI_SIG_FACS,
                            ACPI_TABLE_INDEX_FACS);
        }
        if (acpi_gbl_FADT.Xfacs) {
            acpi_tb_install_fixed_table((acpi_physical_address)
                            acpi_gbl_FADT.Xfacs,
                            ACPI_SIG_FACS,
                            ACPI_TABLE_INDEX_X_FACS);
        }
    }
}

函数用于初始化FADT, DSDT and FACS , FADT 包含DSDT和FACS 。
这里FADT已经通过acpi_tb_install_standard_table 函数安装到了acpi_gbl_root_table_list.tables数组中
1 映射FADT,因为要从这个table中找到DSDT 和 FACS
2 校验该table
3 转换FDAT为标准格式,主要是设置全局变量 struct acpi_table_fadt acpi_gbl_FADT
4 安装 DSDT
5 安装facs和Xfacs

发布了113 篇原创文章 · 获赞 22 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/woai110120130/article/details/93318611