目录
2.1 ACPI Bus Init - acpi_bus_init()
2.1.1 bus_register(&acpi_bus_type);
2.2 ACPI Bus Scan - acpi_scan_init().Part1
2.2.1.1. acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
2.2.3 acpi_memory_hotplug_init();
2.3 ACPI Bus Scan - acpi_scan_init().Part2
2.3.1.1 acpi_add_single_object()
2.3.2.1 acpi_scan_attach_handler()
2.3.3.1 PCI RC Attach - acpi_pci_root_add()
2.3.3.2 Processor Attach - acpi_processor_add()
3. ACPI Devices Scanning Example Log
1. ACPI Namespace Overview
ACPI Spec 现在已经演进到 6.x 版本了, Windows/Linux 也都遵循该标准, 这样对于OS 上层来说, 只需要考虑FW 上传的ACPI 接口(包括各种 FADT/DSDT etc), 就可以对底层进行操作。
对于ACPI OSPM 来说, 整个系统结构都是通过虚拟的ACPI bus 相连, 包括 Processor, Memory, PCIe devices etc. 所以UEFI BIOS 汇报ACPI table 给OS,就是符合 ACPI namespace 规则
1.1 Example Computer System
截图摘自 UEFI Spec.
1.2 ACPI NameSpace
截图摘自 ACPI Spec
1.3 Linux ACPI Device Tree
ACPI Device Tree - Representation of ACPI Namespace — The Linux Kernel documentation +--------------+---+-----------------+ | LNXSYSTEM:00 | \ | acpi:LNXSYSTEM: | +--------------+---+-----------------+ | | +-------------+-----+----------------+ +-| LNXPWRBN:00 | N/A | acpi:LNXPWRBN: | | +-------------+-----+----------------+ | | +-------------+-----+----------------+ +-| LNXSLPBN:00 | N/A | acpi:LNXSLPBN: | | +-------------+-----+----------------+ | | +-----------+------------+--------------+ +-| LNXCPU:00 | \_PR_.CPU0 | acpi:LNXCPU: | | +-----------+------------+--------------+ | | +-------------+-------+----------------+ +-| LNXSYBUS:00 | \_SB_ | acpi:LNXSYBUS: | | +-------------+-------+----------------+ | | | | +- - - - - - - +- - - - - - +- - - - - - - -+ | +-| PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: | | | +- - - - - - - +- - - - - - +- - - - - - - -+ | | | | +------------+------------+-----------------------+ | +-| PNP0A08:00 | \_SB_.PCI0 | acpi:PNP0A08:PNP0A03: | | +------------+------------+-----------------------+ | | | | +-----------+-----------------+-----+ | +-| device:00 | \_SB_.PCI0.RP03 | N/A | | | +-----------+-----------------+-----+ | | | | | | +-------------+----------------------+----------------+ | | +-| LNXPOWER:00 | \_SB_.PCI0.RP03.PXP3 | acpi:LNXPOWER: | | | +-------------+----------------------+----------------+ | | | | +-------------+-----------------+----------------+ | +-| LNXVIDEO:00 | \_SB_.PCI0.GFX0 | acpi:LNXVIDEO: | | +-------------+-----------------+----------------+ | | | | +-----------+-----------------+-----+ | +-| device:01 | \_SB_.PCI0.DD01 | N/A | | +-----------+-----------------+-----+ | | +-------------+-------+----------------+ +-| LNXSYBUS:01 | \_TZ_ | acpi:LNXSYBUS: | +-------------+-------+----------------+ | | +-------------+------------+----------------+ +-| LNXPOWER:0a | \_TZ_.FN00 | acpi:LNXPOWER: | | +-------------+------------+----------------+ | | +------------+------------+---------------+ +-| PNP0C0B:00 | \_TZ_.FAN0 | acpi:PNP0C0B: | | +------------+------------+---------------+ | | +-------------+------------+----------------+ +-| LNXTHERM:00 | \_TZ_.TZ00 | acpi:LNXTHERM: | +-------------+------------+----------------+ Figure 3. Example Linux ACPI Device Tree
2. ACPI Bus Scan
static int __init acpi_init(void)
{
int result;
if (acpi_disabled) {
pr_info("Interpreter disabled.\n");
return -ENODEV;
}
acpi_kobj = kobject_create_and_add("acpi", firmware_kobj);
if (!acpi_kobj)
pr_debug("%s: kset create error\n", __func__);
init_prmt();
acpi_init_pcc();
result = acpi_bus_init();
acpi_init_ffh();
pci_mmcfg_late_init();
acpi_iort_init();
acpi_viot_early_init();
acpi_hest_init();
acpi_ghes_init();
acpi_scan_init();
acpi_ec_init();
acpi_debugfs_init();
acpi_sleep_proc_init();
acpi_wakeup_device_init();
acpi_debugger_init();
acpi_setup_sb_notify_handler();
acpi_viot_init();
acpi_agdi_init();
acpi_apmt_init();
return 0;
}
subsys_initcall(acpi_init);
主要是俩大步骤:
1. acpi_bus_init(); 初始化
2. acpi_scan_init(); 扫描总线
2.1 ACPI Bus Init - acpi_bus_init()
主要是Load ACPI table, 以及注册 ACPI bus, 其他先跳过。
struct bus_type acpi_bus_type = {
.name = "acpi",
.match = acpi_bus_match,
.probe = acpi_device_probe,
.remove = acpi_device_remove,
.uevent = acpi_device_uevent,
};
acpi_init()
acpi_bus_init()
bus_register(&acpi_bus_type);
2.1.1 bus_register(&acpi_bus_type);
2.2 ACPI Bus Scan - acpi_scan_init().Part1
上面已经讲过, ACPI 总线是一条virtual的系统高级电源管理总线, 上面挂载着Processor/Memory/PCIE devices以及一些辅助系统级设备。
所以,如何扫描各种大类的设备 Processor/Memory/PCIE devices 是关键, Part1 以初始化为主。
void __init acpi_scan_init(void)
{
acpi_status status;
struct acpi_table_stao *stao_ptr;
acpi_pci_root_init();
acpi_pci_link_init();
acpi_processor_init();
acpi_platform_init();
acpi_lpss_init();
acpi_apd_init();
acpi_cmos_rtc_init();
acpi_container_init();
acpi_memory_hotplug_init();
acpi_watchdog_init();
acpi_pnp_init();
acpi_int340x_thermal_init();
acpi_amba_init();
acpi_init_lpit();
…
/*
* Enumerate devices in the ACPI namespace.
*/
if (acpi_bus_scan(ACPI_ROOT_OBJECT))
goto unlock;
acpi_root = acpi_fetch_acpi_dev(ACPI_ROOT_OBJECT);
..}
2.2.1 acpi_pci_root_init();
挂载 acpi_scan_handler pci_root_handler{} -> acpi_scan_handlers_list[]
static struct acpi_scan_handler pci_root_handler = {
.ids = root_device_ids,
.attach = acpi_pci_root_add,
.detach = acpi_pci_root_remove,
.hotplug = {
.enabled = true,
.scan_dependent = acpi_pci_root_scan_dependent,
},
};
void __init acpi_pci_root_init(void)
{
pci_acpi_crs_quirks();
acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
}
2.2.1.1. acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
const char *hotplug_profile_name)
{
error = acpi_scan_add_handler(handler);
acpi_sysfs_add_hotplug_profile(&handler->hotplug, hotplug_profile_name);
return 0;
}
int acpi_scan_add_handler(struct acpi_scan_handler *handler)
{
list_add_tail(&handler->list_node, &acpi_scan_handlers_list);
return 0;
}
2.2.2 acpi_processor_init();
挂载 acpi_scan_handler processor_handler{} -> acpi_scan_handlers_list[]
static struct acpi_scan_handler processor_handler = {
.ids = processor_device_ids,
.attach = acpi_processor_add,
#ifdef CONFIG_ACPI_HOTPLUG_CPU
.detach = acpi_processor_remove,
#endif
.hotplug = {
.enabled = true,
},
};
void __init acpi_processor_init(void)
{
acpi_processor_check_duplicates();
acpi_scan_add_handler_with_hotplug(&processor_handler, "processor");
acpi_scan_add_handler(&processor_container_handler);
}
2.2.3 acpi_memory_hotplug_init();
static struct acpi_scan_handler memory_device_handler = {
.ids = memory_device_ids,
.attach = acpi_memory_device_add,
.detach = acpi_memory_device_remove,
.hotplug = {
.enabled = true,
},
};
void __init acpi_memory_hotplug_init(void)
{
if (acpi_no_memhotplug) {
memory_device_handler.attach = NULL;
acpi_scan_add_handler(&memory_device_handler);
return;
}
acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
}
2.3 ACPI Bus Scan - acpi_scan_init().Part2
Part2 以 ACPI Bus scan 为主
acpi_init()
acpi_bus_init();
acpi_scan_init()
acpi_pci_root_init();
acpi_bus_scan()
acpi_bus_check_add(); //1. 添加所有的ACPI Devices
acpi_add_single_object()
acpi_bus_attach(device, (void *)true);
acpi_scan_attach_handler() //2. 匹配注册的ACPI attach driver.
acpi_scan_match_handler()
handler->attach(device, devid);
//acpi_pci_root_add() //3. 扫描 RC port 下游设备
//....
int acpi_bus_scan(acpi_handle handle)
{
struct acpi_device *device = NULL;
acpi_bus_scan_second_pass = false;
/* Pass 1: Avoid enumerating devices with missing dependencies. */
if (ACPI_SUCCESS(acpi_bus_check_add(handle, true, &device)))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add_1, NULL, NULL, (void **)&device);
if (!device) return -ENODEV;
acpi_bus_attach(device, (void *)true);
if (!acpi_bus_scan_second_pass)
return 0;
/* Pass 2: Enumerate all of the remaining devices. */
device = NULL;
if (ACPI_SUCCESS(acpi_bus_check_add(handle, false, &device)))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add_2, NULL, NULL, (void **)&device);
acpi_bus_attach(device, NULL);
return 0;
}
2.3.1 acpi_bus_check_add()
2.3.1.1 acpi_add_single_object()
2.3.2 acpi_bus_attach()
2.3.2.1 acpi_scan_attach_handler()
static int acpi_scan_attach_handler(struct acpi_device *device)
{
struct acpi_hardware_id *hwid;
int ret = 0;
list_for_each_entry(hwid, &device->pnp.ids, list) {
const struct acpi_device_id *devid;
struct acpi_scan_handler *handler;
handler = acpi_scan_match_handler(hwid->id, &devid);
if (handler) {
if (!handler->attach) {
device->pnp.type.platform_id = 0;
continue;
}
device->handler = handler;
ret = handler->attach(device, devid);
if (ret > 0) break;
device->handler = NULL;
if (ret < 0) break;
}
}
return ret;
}
static struct acpi_scan_handler *acpi_scan_match_handler(const char *idstr,
const struct acpi_device_id **matchid)
{
struct acpi_scan_handler *handler;
list_for_each_entry(handler, &acpi_scan_handlers_list, list_node)
if (acpi_scan_handler_matching(handler, idstr, matchid))
return handler;
return NULL;
}
2.3.3 Attach Examples
2.3.3.1 PCI RC Attach - acpi_pci_root_add()
acpi_init()
acpi_bus_init();
acpi_scan_init()
acpi_pci_root_init();
acpi_bus_scan()
acpi_bus_check_add(); //1. 添加所有的ACPI Devices
acpi_add_single_object()
acpi_bus_attach(device, (void *)true);
acpi_scan_attach_handler() //2. 匹配注册的ACPI attach driver.
acpi_scan_match_handler()
handler->attach(device, devid);
//acpi_pci_root_add() //3. 扫描 RC port 下游设备
static struct acpi_scan_handler pci_root_handler = {
.ids = root_device_ids,
.attach = acpi_pci_root_add,
.detach = acpi_pci_root_remove,
.hotplug = {
.enabled = true,
.scan_dependent = acpi_pci_root_scan_dependent,
},
};
Log
[ 46.678136] ACPI: Device [PCI0] status [0000000f]
[ 46.682830] ACPI: acpi_scan_attach_handler ():attach..
[ 46.688668] ACPI: acpi_pci_root_add(): PCI Root Bridge
[ 46.694314] ACPI: PCI Root Bridge [PCI0] (domain 0007 [bus 00-ff])
[ 46.700591] acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig ASPM ClockPM Segments MSI EDR HPX-Type3]
[ 46.710141] acpi PNP0A08:00: _OSC: platform does not support [LTR]
[ 46.716630] acpi PNP0A08:00: _OSC: OS now controls [PCIeHotplug PME AER PCIeCapability DPC]
[ 46.728931] acpi PNP0A08:00: ECAM area [mem 0x73fff0000000-0x73ffffffffff] reserved by PNP0C02:00
[ 46.737801] acpi PNP0A08:00: ECAM at [mem 0x73fff0000000-0x73ffffffffff] for [bus 00-ff]
[ 46.745997] ACPI: acpi_pci_root_create(): PCI Bus 0007:00
[ 46.751903] pci_acpi_root_prepare_resources():
[ 46.775368] pci_register_host_bridge():
[ 46.779916] device: 'pci0007:00': device_add
[ 46.784182] PM: Adding info for No Bus:pci0007:00
[ 46.788887] device: '0007:00': device_add
[ 46.792894] PM: Adding info for No Bus:0007:00
[ 46.797327] pci_slot: Checking slot on path: \_SB_.SOC0.PCI0.P2P1
[ 46.803414] pci_slot: _SUN returned 5 on \_SB_.SOC0.PCI0.P2P1
[ 46.809148] pci_slot: Checking slot on path: \_SB_.SOC0.PCI0.P2P3
[ 46.815234] pci_slot: _SUN returned 5 on \_SB_.SOC0.PCI0.P2P3
[ 46.820968] pci_slot: Checking slot on path: \_SB_.SOC0.PCI0.P2P5
[ 46.827054] pci_slot: _SUN returned 5 on \_SB_.SOC0.PCI0.P2P5
[ 46.832788] pci_slot: Checking slot on path: \_SB_.SOC0.PCI0.P2P7
[ 46.838882] pci_slot: _SUN returned 5 on \_SB_.SOC0.PCI0.P2P7
[ 46.844684] PCI host bridge to bus 0007:00
[ 46.848768] pci_bus 0007:00: root bus resource [mem 0x700004000000-0x700013ffffff window] (bus address [0x04000000-0x13ffffff])
[ 46.860232] pci_bus 0007:00: root bus resource [mem 0x700100000000-0x73ffdfffffff window]
[ 46.868395] pci_bus 0007:00: root bus resource [bus 00-ff]
2.3.3.2 Processor Attach - acpi_processor_add()
acpi_init()
acpi_bus_init();
acpi_scan_init()
acpi_pci_root_init();
acpi_bus_scan()
acpi_bus_check_add(); //1. 添加所有的ACPI Devices
acpi_add_single_object()
acpi_bus_attach(device, (void *)true);
acpi_scan_attach_handler() //2. 匹配注册的ACPI attach driver.
acpi_scan_match_handler()
handler->attach(device, devid);
//acpi_processor_add() //3. 扫描 RC port 下游设备
static struct acpi_scan_handler processor_handler = {
.ids = processor_device_ids,
.attach = acpi_processor_add,
#ifdef CONFIG_ACPI_HOTPLUG_CPU
.detach = acpi_processor_remove,
#endif
.hotplug = {
.enabled = true,
},
};
3. ACPI Devices Scanning Example Log
...
[ 0.482926] ACPI: Device [ACPI] status [0000000f]
[ 0.482931] device: 'LNXSYSTM:00': device_add
[ 0.482934] bus: 'acpi': add device LNXSYSTM:00
[ 0.482939] PM: Adding info for acpi:LNXSYSTM:00
[ 0.482946] ACPI: acpi_add_single_object(): acpi_device_add LNXSYSTM:00
[ 0.482985] ACPI: Device [_SB] status [0000000f]
[ 0.482989] device: 'LNXSYBUS:00': device_add
[ 0.482992] bus: 'acpi': add device LNXSYBUS:00
[ 0.482996] PM: Adding info for acpi:LNXSYBUS:00
[ 0.483002] ACPI: acpi_add_single_object(): acpi_device_add LNXSYBUS:00
[ 0.483052] ACPI: Device [GED1] status [0000000f]
[ 0.483056] device: 'ACPI0013:00': device_add
[ 0.483059] bus: 'acpi': add device ACPI0013:00
[ 0.483065] PM: Adding info for acpi:ACPI0013:00
[ 0.483072] ACPI: acpi_add_single_object(): acpi_device_add ACPI0013:00
[ 0.483113] ACPI: Device [HED0] status [0000000f]
[ 0.483117] device: 'PNP0C33:00': device_add
[ 0.483120] bus: 'acpi': add device PNP0C33:00
[ 0.483124] PM: Adding info for acpi:PNP0C33:00
[ 0.483131] ACPI: acpi_add_single_object(): acpi_device_add PNP0C33:00
[ 0.483203] ACPI: Device [TPM0] status [00000000]
[ 0.483207] device: 'NNNN0000:00': device_add
[ 0.483210] bus: 'acpi': add device NNNN0000:00
[ 0.483214] PM: Adding info for acpi:NNNN0000:00
[ 0.483229] ACPI: acpi_add_single_object(): acpi_device_add NNNN0000:00
[ 0.483304] ACPI: Device [GED0] status [0000000f]
[ 0.483307] device: 'ACPI0013:01': device_add
[ 0.483310] bus: 'acpi': add device ACPI0013:01
[ 0.483315] PM: Adding info for acpi:ACPI0013:01
[ 0.483320] ACPI: acpi_add_single_object(): acpi_device_add ACPI0013:01
[ 0.483395] ACPI: Device [PWRB] status [0000000f]
[ 0.483399] device: 'PNP0C0C:00': device_add
[ 0.483402] bus: 'acpi': add device PNP0C0C:00
[ 0.483406] PM: Adding info for acpi:PNP0C0C:00
[ 0.483412] ACPI: acpi_add_single_object(): acpi_device_add PNP0C0C:00
[ 0.483455] ACPI: Device [SOC0] status [0000000f]
[ 0.483459] device: 'ACPI0004:00': device_add
[ 0.483463] bus: 'acpi': add device ACPI0004:00
[ 0.483467] PM: Adding info for acpi:ACPI0004:00
[ 0.483477] ACPI: acpi_add_single_object(): acpi_device_add ACPI0004:00
[ 0.484782] ACPI: Device [PCI0] status [0000000f] //Root Complex 0
[ 0.484786] device: 'PNP0A08:00': device_add
[ 0.484789] bus: 'acpi': add device PNP0A08:00
[ 0.484793] PM: Adding info for acpi:PNP0A08:00
[ 0.484804] ACPI: acpi_add_single_object(): acpi_device_add PNP0A08:00
[ 0.485498] ACPI: Device [PCI1] status [0000000f] //RC 1
[ 0.485502] device: 'PNP0A08:01': device_add
[ 0.485505] bus: 'acpi': add device PNP0A08:01
[ 0.485509] PM: Adding info for acpi:PNP0A08:01
[ 0.485519] ACPI: acpi_add_single_object(): acpi_device_add PNP0A08:01
...
...
[ 0.489986] ACPI: Device [PCI7] status [0000000f] //RC7
[ 0.489990] device: 'PNP0A08:07': device_add
[ 0.489993] bus: 'acpi': add device PNP0A08:07
[ 0.489997] PM: Adding info for acpi:PNP0A08:07
[ 0.490007] ACPI: acpi_add_single_object(): acpi_device_add PNP0A08:07
...
[ 0.496144] ACPI: Device [C000] status [0000000f]
[ 0.496148] device: 'ACPI0007:00': device_add
[ 0.496151] bus: 'acpi': add device ACPI0007:00
[ 0.496156] PM: Adding info for acpi:ACPI0007:00
[ 0.496162] ACPI: acpi_add_single_object(): acpi_device_add ACPI0007:00
[ 0.496258] ACPI: Device [C001] status [0000000f]
[ 0.496262] device: 'ACPI0007:01': device_add
[ 0.496265] bus: 'acpi': add device ACPI0007:01
[ 0.496269] PM: Adding info for acpi:ACPI0007:01
[ 0.496278] ACPI: acpi_add_single_object(): acpi_device_add ACPI0007:01
[ 0.496374] ACPI: Device [C002] status [0000000f]
[ 0.496378] device: 'ACPI0007:02': device_add
[ 0.496381] bus: 'acpi': add device ACPI0007:02
[ 0.496385] PM: Adding info for acpi:ACPI0007:02
[ 0.496392] ACPI: acpi_add_single_object(): acpi_device_add ACPI0007:02
...