요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
===================
ACPI on Arm systems
===================
ACPI can be used for Armv8 and Armv9 systems designed to follow
the BSA (Arm Base System Architecture) [0] and BBR (Arm
Base Boot Requirements) [1] specifications. Both BSA and BBR are publicly
accessible documents.
Arm Servers, in addition to being BSA compliant, comply with a set
of rules defined in SBSA (Server Base System Architecture) [2].
The Arm kernel implements the reduced hardware model of ACPI version
5.1 or later. Links to the specification and all external documents
it refers to are managed by the UEFI Forum. The specification is
available at http://www.uefi.org/specifications and documents referenced
by the specification can be found via http://www.uefi.org/acpi.
If an Arm system does not meet the requirements of the BSA and BBR,
or cannot be described using the mechanisms defined in the required ACPI
specifications, then ACPI may not be a good fit for the hardware.
While the documents mentioned above set out the requirements for building
industry-standard Arm systems, they also apply to more than one operating
system. The purpose of this document is to describe the interaction between
ACPI and Linux only, on an Arm system -- that is, what Linux expects of
ACPI and what ACPI can expect of Linux.
Why ACPI on Arm?
----------------
Before examining the details of the interface between ACPI and Linux, it is
useful to understand why ACPI is being used. Several technologies already
exist in Linux for describing non-enumerable hardware, after all. In this
section we summarize a blog post [3] from Grant Likely that outlines the
reasoning behind ACPI on Arm systems. Actually, we snitch a good portion
of the summary text almost directly, to be honest.
The short form of the rationale for ACPI on Arm is:
- ACPI’s byte code (AML) allows the platform to encode hardware behavior,
while DT explicitly does not support this. For hardware vendors, being
able to encode behavior is a key tool used in supporting operating
system releases on new hardware.
- ACPI’s OSPM defines a power management model that constrains what the
platform is allowed to do into a specific model, while still providing
flexibility in hardware design.
- In the enterprise server environment, ACPI has established bindings (such
as for RAS) which are currently used in production systems. DT does not.
Such bindings could be defined in DT at some point, but doing so means Arm
and x86 would end up using completely different code paths in both firmware
and the kernel.
- Choosing a single interface to describe the abstraction between a platform
and an OS is important. Hardware vendors would not be required to implement
both DT and ACPI if they want to support multiple operating systems. And,
agreeing on a single interface instead of being fragmented into per OS
interfaces makes for better interoperability overall.
- The new ACPI governance process works well and Linux is now at the same
table as hardware vendors and other OS vendors. In fact, there is no
longer any reason to feel that ACPI only belongs to Windows or that
Linux is in any way secondary to Microsoft in this arena. The move of
ACPI governance into the UEFI forum has significantly opened up the
specification development process, and currently, a large portion of the
changes being made to ACPI are being driven by Linux.
Key to the use of ACPI is the support model. For servers in general, the
responsibility for hardware behaviour cannot solely be the domain of the
kernel, but rather must be split between the platform and the kernel, in
order to allow for orderly change over time. ACPI frees the OS from needing
to understand all the minute details of the hardware so that the OS doesn’t
need to be ported to each and every device individually. It allows the
hardware vendors to take responsibility for power management behaviour without
depending on an OS release cycle which is not under their control.
ACPI is also important because hardware and OS vendors have already worked
out the mechanisms for supporting a general purpose computing ecosystem. The
infrastructure is in place, the bindings are in place, and the processes are
in place. DT does exactly what Linux needs it to when working with vertically
integrated devices, but there are no good processes for supporting what the
server vendors need. Linux could potentially get there with DT, but doing so
really just duplicates something that already works. ACPI already does what
the hardware vendors need, Microsoft won’t collaborate on DT, and hardware
vendors would still end up providing two completely separate firmware
interfaces -- one for Linux and one for Windows.
Kernel Compatibility
--------------------
One of the primary motivations for ACPI is standardization, and using that
to provide backward compatibility for Linux kernels. In the server market,
software and hardware are often used for long periods. ACPI allows the
kernel and firmware to agree on a consistent abstraction that can be
maintained over time, even as hardware or software change. As long as the
abstraction is supported, systems can be updated without necessarily having
to replace the kernel.
When a Linux driver or subsystem is first implemented using ACPI, it by
definition ends up requiring a specific version of the ACPI specification
-- its baseline. ACPI firmware must continue to work, even though it may
not be optimal, with the earliest kernel version that first provides support
for that baseline version of ACPI. There may be a need for additional drivers,
but adding new functionality (e.g., CPU power management) should not break
older kernel versions. Further, ACPI firmware must also work with the most
recent version of the kernel.
Relationship with Device Tree
-----------------------------
ACPI support in drivers and subsystems for Arm should never be mutually
exclusive with DT support at compile time.
At boot time the kernel will only use one description method depending on
parameters passed from the boot loader (including kernel bootargs).
Regardless of whether DT or ACPI is used, the kernel must always be capable
of booting with either scheme (in kernels with both schemes enabled at compile
time).
Booting using ACPI tables
-------------------------
The only defined method for passing ACPI tables to the kernel on Arm
is via the UEFI system configuration table. Just so it is explicit, this
means that ACPI is only supported on platforms that boot via UEFI.
When an Arm system boots, it can either have DT information, ACPI tables,
or in some very unusual cases, both. If no command line parameters are used,
the kernel will try to use DT for device enumeration; if there is no DT
present, the kernel will try to use ACPI tables, but only if they are present.
If neither is available, the kernel will not boot. If acpi=force is used
on the command line, the kernel will attempt to use ACPI tables first, but
fall back to DT if there are no ACPI tables present. The basic idea is that
the kernel will not fail to boot unless it absolutely has no other choice.
Processing of ACPI tables may be disabled by passing acpi=off on the kernel
command line; this is the default behavior.
In order for the kernel to load and use ACPI tables, the UEFI implementation
MUST set the ACPI_20_TABLE_GUID to point to the RSDP table (the table with
the ACPI signature "RSD PTR "). If this pointer is incorrect and acpi=force
is used, the kernel will disable ACPI and try to use DT to boot instead; the
kernel has, in effect, determined that ACPI tables are not present at that
point.
If the pointer to the RSDP table is correct, the table will be mapped into
the kernel by the ACPI core, using the address provided by UEFI.
The ACPI core will then locate and map in all other ACPI tables provided by
using the addresses in the RSDP table to find the XSDT (eXtended System
Description Table). The XSDT in turn provides the addresses to all other
ACPI tables provided by the system firmware; the ACPI core will then traverse
this table and map in the tables listed.
The ACPI core will ignore any provided RSDT (Root System Description Table).
RSDTs have been deprecated and are ignored on arm64 since they only allow
for 32-bit addresses.
Further, the ACPI core will only use the 64-bit address fields in the FADT
(Fixed ACPI Description Table). Any 32-bit address fields in the FADT will
be ignored on arm64.
Hardware reduced mode (see Section 4.1 of the ACPI 6.1 specification) will
be enforced by the ACPI core on arm64. Doing so allows the ACPI core to
run less complex code since it no longer has to provide support for legacy
hardware from other architectures. Any fields that are not to be used for
hardware reduced mode must be set to zero.
For the ACPI core to operate properly, and in turn provide the information
the kernel needs to configure devices, it expects to find the following
tables (all section numbers refer to the ACPI 6.5 specification):
- RSDP (Root System Description Pointer), section 5.2.5
- XSDT (eXtended System Description Table), section 5.2.8
- FADT (Fixed ACPI Description Table), section 5.2.9
- DSDT (Differentiated System Description Table), section
5.2.11.1
- MADT (Multiple APIC Description Table), section 5.2.12
- GTDT (Generic Timer Description Table), section 5.2.24
- PPTT (Processor Properties Topology Table), section 5.2.30
- DBG2 (DeBuG port table 2), section 5.2.6, specifically Table 5-6.
- APMT (Arm Performance Monitoring unit Table), section 5.2.6, specifically Table 5-6.
- AGDI (Arm Generic diagnostic Dump and Reset Device Interface Table), section 5.2.6, specifically Table 5-6.
- If PCI is supported, the MCFG (Memory mapped ConFiGuration
Table), section 5.2.6, specifically Table 5-6.
- If booting without a console=<device> kernel parameter is
supported, the SPCR (Serial Port Console Redirection table),
section 5.2.6, specifically Table 5-6.
- If necessary to describe the I/O topology, SMMUs and GIC ITSs,
the IORT (Input Output Remapping Table, section 5.2.6, specifically
Table 5-6).
- If NUMA is supported, the following tables are required:
- SRAT (System Resource Affinity Table), section 5.2.16
- SLIT (System Locality distance Information Table), section 5.2.17
- If NUMA is supported, and the system contains heterogeneous memory,
the HMAT (Heterogeneous Memory Attribute Table), section 5.2.28.
- If the ACPI Platform Error Interfaces are required, the following
tables are conditionally required:
- BERT (Boot Error Record Table, section 18.3.1)
- EINJ (Error INJection table, section 18.6.1)
- ERST (Error Record Serialization Table, section 18.5)
- HEST (Hardware Error Source Table, section 18.3.2)
- SDEI (Software Delegated Exception Interface table, section 5.2.6,
specifically Table 5-6)
- AEST (Arm Error Source Table, section 5.2.6,
specifically Table 5-6)
- RAS2 (ACPI RAS2 feature table, section 5.2.21)
- If the system contains controllers using PCC channel, the
PCCT (Platform Communications Channel Table), section 14.1
- If the system contains a controller to capture board-level system state,
and communicates with the host via PCC, the PDTT (Platform Debug Trigger
Table), section 5.2.29.
- If NVDIMM is supported, the NFIT (NVDIMM Firmware Interface Table), section 5.2.26
- If video framebuffer is present, the BGRT (Boot Graphics Resource Table), section 5.2.23
- If IPMI is implemented, the SPMI (Server Platform Management Interface),
section 5.2.6, specifically Table 5-6.
- If the system contains a CXL Host Bridge, the CEDT (CXL Early Discovery
Table), section 5.2.6, specifically Table 5-6.
- If the system supports MPAM, the MPAM (Memory Partitioning And Monitoring table), section 5.2.6,
specifically Table 5-6.
- If the system lacks persistent storage, the IBFT (ISCSI Boot Firmware
Table), section 5.2.6, specifically Table 5-6.
If the above tables are not all present, the kernel may or may not be
able to boot properly since it may not be able to configure all of the
devices available. This list of tables is not meant to be all inclusive;
in some environments other tables may be needed (e.g., any of the APEI
tables from section 18) to support specific functionality.
ACPI Detection
--------------
Drivers should determine their probe() type by checking for a null
value for ACPI_HANDLE, or checking .of_node, or other information in
the device structure. This is detailed further in the "Driver
Recommendations" section.
In non-driver code, if the presence of ACPI needs to be detected at
run time, then check the value of acpi_disabled. If CONFIG_ACPI is not
set, acpi_disabled will always be 1.
Device Enumeration
------------------
Device descriptions in ACPI should use standard recognized ACPI interfaces.
These may contain less information than is typically provided via a Device
Tree description for the same device. This is also one of the reasons that
ACPI can be useful -- the driver takes into account that it may have less
detailed information about the device and uses sensible defaults instead.
If done properly in the driver, the hardware can change and improve over
time without the driver having to change at all.
Clocks provide an excellent example. In DT, clocks need to be specified
and the drivers need to take them into account. In ACPI, the assumption
is that UEFI will leave the device in a reasonable default state, including
any clock settings. If for some reason the driver needs to change a clock
value, this can be done in an ACPI method; all the driver needs to do is
invoke the method and not concern itself with what the method needs to do
to change the clock. Changing the hardware can then take place over time
by changing what the ACPI method does, and not the driver.
In DT, the parameters needed by the driver to set up clocks as in the example
above are known as "bindings"; in ACPI, these are known as "Device Properties"
and provided to a driver via the _DSD object.
ACPI tables are described with a formal language called ASL, the ACPI
Source Language (section 19 of the specification). This means that there
are always multiple ways to describe the same thing -- including device
properties. For example, device properties could use an ASL construct
that looks like this: Name(KEY0, "value0"). An ACPI device driver would
then retrieve the value of the property by evaluating the KEY0 object.
However, using Name() this way has multiple problems: (1) ACPI limits
names ("KEY0") to four characters unlike DT; (2) there is no industry
wide registry that maintains a list of names, minimizing re-use; (3)
there is also no registry for the definition of property values ("value0"),
again making re-use difficult; and (4) how does one maintain backward
compatibility as new hardware comes out? The _DSD method was created
to solve precisely these sorts of problems; Linux drivers should ALWAYS
use the _DSD method for device properties and nothing else.
The _DSM object (ACPI Section 9.14.1) could also be used for conveying
device properties to a driver. Linux drivers should only expect it to
be used if _DSD cannot represent the data required, and there is no way
to create a new UUID for the _DSD object. Note that there is even less
regulation of the use of _DSM than there is of _DSD. Drivers that depend
on the contents of _DSM objects will be more difficult to maintain over
time because of this; as of this writing, the use of _DSM is the cause
of quite a few firmware problems and is not recommended.
Drivers should look for device properties in the _DSD object ONLY; the _DSD
object is described in the ACPI specification section 6.2.5, but this only
describes how to define the structure of an object returned via _DSD, and
how specific data structures are defined by specific UUIDs. Linux should
only use the _DSD Device Properties UUID [4]:
- UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301
Common device properties can be registered by creating a pull request to [4] so
that they may be used across all operating systems supporting ACPI.
Device properties that have not been registered with the UEFI Forum can be used
but not as "uefi-" common properties.
Before creating new device properties, check to be sure that they have not
been defined before and either registered in the Linux kernel documentation
as DT bindings, or the UEFI Forum as device properties. While we do not want
to simply move all DT bindings into ACPI device properties, we can learn from
what has been previously defined.
If it is necessary to define a new device property, or if it makes sense to
synthesize the definition of a binding so it can be used in any firmware,
both DT bindings and ACPI device properties for device drivers have review
processes. Use them both. When the driver itself is submitted for review
to the Linux mailing lists, the device property definitions needed must be
submitted at the same time. A driver that supports ACPI and uses device
properties will not be considered complete without their definitions. Once
the device property has been accepted by the Linux community, it must be
registered with the UEFI Forum [4], which will review it again for consistency
within the registry. This may require iteration. The UEFI Forum, though,
will always be the canonical site for device property definitions.
It may make sense to provide notice to the UEFI Forum that there is the
intent to register a previously unused device property name as a means of
reserving the name for later use. Other operating system vendors will
also be submitting registration requests and this may help smooth the
process.
Once registration and review have been completed, the kernel provides an
interface for looking up device properties in a manner independent of
whether DT or ACPI is being used. This API should be used [5]; it can
eliminate some duplication of code paths in driver probing functions and
discourage divergence between DT bindings and ACPI device properties.
Programmable Power Control Resources
------------------------------------
Programmable power control resources include such resources as voltage/current
providers (regulators) and clock sources.
With ACPI, the kernel clock and regulator framework is not expected to be used
at all.
The kernel assumes that power control of these resources is represented with
Power Resource Objects (ACPI section 7.1). The ACPI core will then handle
correctly enabling and disabling resources as they are needed. In order to
get that to work, ACPI assumes each device has defined D-states and that these
can be controlled through the optional ACPI methods _PS0, _PS1, _PS2, and _PS3;
in ACPI, _PS0 is the method to invoke to turn a device full on, and _PS3 is for
turning a device full off.
There are two options for using those Power Resources. They can:
- be managed in a _PSx method which gets called on entry to power
state Dx.
- be declared separately as power resources with their own _ON and _OFF
methods. They are then tied back to D-states for a particular device
via _PRx which specifies which power resources a device needs to be on
while in Dx. Kernel then tracks number of devices using a power resource
and calls _ON/_OFF as needed.
The kernel ACPI code will also assume that the _PSx methods follow the normal
ACPI rules for such methods:
- If either _PS0 or _PS3 is implemented, then the other method must also
be implemented.
- If a device requires usage or setup of a power resource when on, the ASL
should organize that it is allocated/enabled using the _PS0 method.
- Resources allocated or enabled in the _PS0 method should be disabled
or de-allocated in the _PS3 method.
- Firmware will leave the resources in a reasonable state before handing
over control to the kernel.
Such code in _PSx methods will of course be very platform specific. But,
this allows the driver to abstract out the interface for operating the device
and avoid having to read special non-standard values from ACPI tables. Further,
abstracting the use of these resources allows the hardware to change over time
without requiring updates to the driver.
Clocks
------
ACPI makes the assumption that clocks are initialized by the firmware --
UEFI, in this case -- to some working value before control is handed over
to the kernel. This has implications for devices such as UARTs, or SoC-driven
LCD displays, for example.
When the kernel boots, the clocks are assumed to be set to reasonable
working values. If for some reason the frequency needs to change -- e.g.,
throttling for power management -- the device driver should expect that
process to be abstracted out into some ACPI method that can be invoked
(please see the ACPI specification for further recommendations on standard
methods to be expected). The only exceptions to this are CPU clocks where
CPPC provides a much richer interface than ACPI methods. If the clocks
are not set, there is no direct way for Linux to control them.
If an SoC vendor wants to provide fine-grained control of the system clocks,
they could do so by providing ACPI methods that could be invoked by Linux
drivers. However, this is NOT recommended and Linux drivers should NOT use
such methods, even if they are provided. Such methods are not currently
standardized in the ACPI specification, and using them could tie a kernel
to a very specific SoC, or tie an SoC to a very specific version of the
kernel, both of which we are trying to avoid.
Driver Recommendations
----------------------
DO NOT remove any DT handling when adding ACPI support for a driver. The
same device may be used on many different systems.
DO try to structure the driver so that it is data-driven. That is, set up
a struct containing internal per-device state based on defaults and whatever
else must be discovered by the driver probe function. Then, have the rest
of the driver operate off of the contents of that struct. Doing so should
allow most divergence between ACPI and DT functionality to be kept local to
the probe function instead of being scattered throughout the driver. For
example::
static int device_probe_dt(struct platform_device *pdev)
{
/* DT specific functionality */
...
}
static int device_probe_acpi(struct platform_device *pdev)
{
/* ACPI specific functionality */
...
}
static int device_probe(struct platform_device *pdev)
{
...
struct device_node node = pdev->dev.of_node;
...
if (node)
ret = device_probe_dt(pdev);
else if (ACPI_HANDLE(&pdev->dev))
ret = device_probe_acpi(pdev);
else
/* other initialization */
...
/* Continue with any generic probe operations */
...
}
DO keep the MODULE_DEVICE_TABLE entries together in the driver to make it
clear the different names the driver is probed for, both from DT and from
ACPI::
static struct of_device_id virtio_mmio_match[] = {
{ .compatible = "virtio,mmio", },
{ }
};
MODULE_DEVICE_TABLE(of, virtio_mmio_match);
static const struct acpi_device_id virtio_mmio_acpi_match[] = {
{ "LNRO0005", },
{ }
};
MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
ASWG
----
The ACPI specification changes regularly. During the year 2014, for instance,
version 5.1 was released and version 6.0 substantially completed, with most of
the changes being driven by Arm-specific requirements. Proposed changes are
presented and discussed in the ASWG (ACPI Specification Working Group) which
is a part of the UEFI Forum. The current version of the ACPI specification
is 6.5 release in August 2022.
Participation in this group is open to all UEFI members. Please see
http://www.uefi.org/workinggroup for details on group membership.
It is the intent of the Arm ACPI kernel code to follow the ACPI specification
as closely as possible, and to only implement functionality that complies with
the released standards from UEFI ASWG. As a practical matter, there will be
vendors that provide bad ACPI tables or violate the standards in some way.
If this is because of errors, quirks and fix-ups may be necessary, but will
be avoided if possible. If there are features missing from ACPI that preclude
it from being used on a platform, ECRs (Engineering Change Requests) should be
submitted to ASWG and go through the normal approval process; for those that
are not UEFI members, many other members of the Linux community are and would
likely be willing to assist in submitting ECRs.
Linux Code
----------
Individual items specific to Linux on Arm, contained in the Linux
source code, are in the list that follows:
ACPI_OS_NAME
This macro defines the string to be returned when
an ACPI method invokes the _OS method. On Arm
systems, this macro will be "Linux" by default.
The command line parameter acpi_os=<string>
can be used to set it to some other value. The
default value for other architectures is "Microsoft
Windows NT", for example.
ACPI Objects
------------
Detailed expectations for ACPI tables and object are listed in the file
Documentation/arch/arm64/acpi_object_usage.rst.
References
----------
[0] https://developer.arm.com/documentation/den0094/latest
document Arm-DEN-0094: "Arm Base System Architecture", version 1.0C, dated 6 Oct 2022
[1] https://developer.arm.com/documentation/den0044/latest
Document Arm-DEN-0044: "Arm Base Boot Requirements", version 2.0G, dated 15 Apr 2022
[2] https://developer.arm.com/documentation/den0029/latest
Document Arm-DEN-0029: "Arm Server Base System Architecture", version 7.1, dated 06 Oct 2022
[3] http://www.secretlab.ca/archives/151,
10 Jan 2015, Copyright (c) 2015,
Linaro Ltd., written by Grant Likely.
[4] _DSD (Device Specific Data) Implementation Guide
https://github.com/UEFI/DSD-Guide/blob/main/dsd-guide.pdf
[5] Kernel code for the unified device
property interface can be found in
include/linux/property.h and drivers/base/property.c.
Authors
-------
- Al Stone <[email protected]>
- Graeme Gregory <[email protected]>
- Hanjun Guo <[email protected]>
- Grant Likely <[email protected]>, for the "Why ACPI on ARM?" section
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
적용 범위와 기준 사양
1-28ACPI는 BSA(Arm Base System Architecture) [0]와 BBR(Arm Base Boot Requirements) [1] 사양을 따르도록 설계한 Armv8 및 Armv9 시스템에 사용할 수 있습니다. 두 문서는 공개되어 있습니다. Arm server는 BSA를 준수하는 것에 더해 SBSA(Server Base System Architecture) [2]가 정한 규칙도 준수합니다.
Arm kernel은 ACPI version 5.1 이상에서 정의한 reduced hardware model을 구현합니다. 사양과 사양이 참조하는 외부 문서의 링크는 UEFI Forum이 관리합니다.
- http://www.uefi.org/specifications
http://www.uefi.org/specifications - http://www.uefi.org/acpi
http://www.uefi.org/acpi
Arm 시스템이 BSA와 BBR 요구사항을 충족하지 않거나 필수 ACPI 사양의 메커니즘으로 설명할 수 없다면 ACPI가 그 hardware에 적합하지 않을 수 있습니다.
앞의 문서들은 여러 OS에 적용되는 industry-standard Arm system 요구사항을 정합니다. 이 문서의 목적은 그중 Arm 시스템에서 ACPI와 Linux가 상호 작용하는 방식, 즉 Linux가 ACPI에 기대하는 것과 ACPI가 Linux에 기대할 수 있는 것만 설명하는 데 있습니다.
Arm에서 ACPI를 사용하는 이유
29-89ACPI와 Linux의 세부 interface를 살펴보기 전에 이미 Linux에 non-enumerable hardware를 설명하는 기술이 여러 개 있는데도 ACPI를 쓰는 이유를 이해할 필요가 있습니다. 이 절은 Grant Likely의 blog 글 [3]을 상당 부분 직접 요약합니다.
Arm에서 ACPI를 사용하는 핵심 근거는 다음과 같습니다.
- ACPI byte code인 AML은 platform이 hardware behavior를 encode할 수 있지만 DT는 이를 명시적으로 지원하지 않습니다. Hardware vendor에게 behavior encoding은 새 hardware에서 OS release를 지원하는 핵심 도구입니다.
- ACPI OSPM은 platform이 수행할 수 있는 power management 동작을 특정 model로 제한하면서도 hardware 설계의 유연성을 남깁니다.
- Enterprise server 환경에서는 RAS 같은 ACPI binding이 이미 production system에서 쓰이지만 DT에는 없습니다. DT에 새로 정의하면 Arm과 x86의 firmware와 kernel 양쪽에 완전히 다른 code path가 생깁니다.
- Platform과 OS 사이의 abstraction을 설명할 단일 interface를 선택하는 것이 중요합니다. 여러 OS를 지원하려는 hardware vendor가 DT와 ACPI를 모두 구현할 필요가 없어지고, OS별 interface로 분열되는 것보다 상호 운용성이 좋아집니다.
- 새 ACPI governance process에서는 Linux가 hardware vendor 및 다른 OS vendor와 동등하게 참여합니다. ACPI가 Windows 전용이거나 Linux가 Microsoft보다 부차적이라고 볼 이유가 없습니다. Governance를 UEFI Forum으로 옮기면서 사양 개발 과정이 크게 개방되었고 현재 ACPI 변경의 상당 부분을 Linux 요구가 이끕니다.
ACPI 사용의 핵심은 support model입니다. Server hardware behavior의 책임을 kernel에만 둘 수 없으며, 시간이 지나면서 질서 있게 변경할 수 있도록 platform과 kernel이 나눠야 합니다. ACPI는 OS가 hardware의 모든 세부를 이해하고 장치마다 별도로 port할 필요를 없애며, hardware vendor가 통제할 수 없는 OS release cycle에 의존하지 않고 power management 동작을 책임지게 합니다.
ACPI가 중요한 또 다른 이유는 hardware와 OS vendor가 general-purpose computing ecosystem을 지원할 infrastructure, binding, process를 이미 마련했기 때문입니다. DT는 vertically integrated device에서 Linux에 필요한 일을 정확히 하지만 server vendor 요구를 지원할 좋은 process가 없습니다. DT로 같은 수준에 도달할 수는 있어도 이미 동작하는 것을 중복 구현하게 됩니다. ACPI는 vendor 요구를 충족하고 Microsoft는 DT에 협력하지 않으므로, 그렇지 않으면 vendor는 Linux와 Windows용 firmware interface를 각각 제공해야 합니다.
Kernel 호환성
90-109ACPI의 주된 동기 중 하나는 표준화를 통해 Linux kernel의 backward compatibility를 제공하는 것입니다. Server 시장에서는 software와 hardware를 오랫동안 사용합니다. ACPI는 hardware나 software가 바뀌어도 kernel과 firmware가 장기간 유지할 수 있는 일관된 abstraction에 합의하게 합니다. Abstraction이 지원되는 한 반드시 kernel을 교체하지 않고도 시스템을 갱신할 수 있습니다.
Linux driver나 subsystem이 처음 ACPI를 사용해 구현되면 그 정의상 특정 ACPI 사양 version을 baseline으로 요구합니다. ACPI firmware는 최적이 아니더라도 해당 baseline을 처음 지원한 가장 이른 kernel version에서 계속 동작해야 합니다. Driver 추가가 필요할 수는 있지만 CPU power management 같은 새 기능이 오래된 kernel version을 망가뜨려서는 안 됩니다. 동시에 가장 최신 kernel version에서도 동작해야 합니다.
Device Tree와의 관계
110-122Arm driver와 subsystem의 ACPI 지원은 compile time에 DT 지원과 상호 배타적이어서는 안 됩니다.
Boot할 때 kernel은 boot loader가 전달한 parameter와 kernel bootargs에 따라 설명 방식 하나만 사용합니다. Compile time에 두 방식을 모두 활성화한 kernel은 실제 boot에서 DT와 ACPI 어느 쪽으로도 부팅할 수 있어야 합니다.
ACPI 테이블로 부팅하는 절차
123-170Arm에서 ACPI 테이블을 kernel에 전달하는 유일하게 정의된 방법은 UEFI system configuration table입니다. 즉, ACPI는 UEFI로 boot하는 platform에서만 지원됩니다.
Arm 시스템은 DT 정보나 ACPI 테이블 중 하나를 갖고 boot하며 아주 드물게 둘 다 가질 수 있습니다. Command line parameter가 없으면 kernel은 먼저 DT로 장치를 열거하고, DT가 없을 때만 ACPI 테이블이 있으면 이를 시도합니다. 둘 다 없으면 boot하지 못합니다. `acpi=force`를 쓰면 ACPI를 먼저 시도하되 ACPI 테이블이 없으면 DT로 fallback합니다. 다른 선택지가 전혀 없을 때만 boot 실패를 허용하는 방식입니다.
Kernel command line에 `acpi=off`를 전달하면 ACPI 테이블 처리를 비활성화할 수 있으며 이것이 기본 동작입니다.
Kernel이 ACPI 테이블을 load해 사용하려면 UEFI 구현은 반드시 `ACPI_20_TABLE_GUID`가 RSDP 테이블, 즉 ACPI signature가 `RSD PTR `인 테이블을 가리키게 해야 합니다. Pointer가 잘못되었는데 `acpi=force`를 사용하면 kernel은 ACPI를 끄고 DT boot를 시도합니다. 이 시점에서 kernel은 ACPI 테이블이 없다고 판단한 셈입니다.
RSDP pointer가 올바르면 ACPI core가 UEFI 제공 address를 사용해 테이블을 kernel에 map합니다. 이어 RSDP의 address로 XSDT(eXtended System Description Table)를 찾고 map합니다. XSDT는 system firmware가 제공한 나머지 ACPI 테이블의 address를 담고 있으며 ACPI core가 이를 순회해 목록의 테이블을 map합니다.
ACPI core는 제공된 RSDT를 무시합니다. RSDT는 32-bit address만 허용하므로 arm64에서 폐기되었습니다. FADT에서도 64-bit address field만 사용하고 모든 32-bit address field를 무시합니다.
Arm64의 ACPI core는 ACPI 6.1 사양 4.1절의 hardware reduced mode를 강제합니다. 다른 architecture의 legacy hardware 지원이 필요 없어 core code를 단순화할 수 있습니다. Hardware reduced mode에서 사용하지 않는 모든 field는 0으로 설정해야 합니다.
필수 및 조건부 ACPI 테이블
171-258ACPI core가 정상 동작해 kernel의 장치 구성 정보를 제공하려면 다음 테이블을 기대합니다. 절 번호는 모두 ACPI 6.5 사양을 기준으로 합니다.
| 테이블 | 필요 조건 | 사양 절 | 역할 |
|---|---|---|---|
| `RSDP` | 기본 필수 | 5.2.5 | Root System Description Pointer |
| `XSDT` | 기본 필수 | 5.2.8 | eXtended System Description Table |
| `FADT` | 기본 필수 | 5.2.9 | Fixed ACPI Description Table |
| `DSDT` | 기본 필수 | 5.2.11.1 | Differentiated System Description Table |
| `MADT` | 기본 필수 | 5.2.12 | Multiple APIC Description Table |
| `GTDT` | 기본 필수 | 5.2.24 | Generic Timer Description Table |
| `PPTT` | 기본 필수 | 5.2.30 | Processor Properties Topology Table |
| `DBG2` | 기본 필수 | 5.2.6 표 5-6 | DeBuG port table 2 |
| `APMT` | 기본 필수 | 5.2.6 표 5-6 | Arm Performance Monitoring unit Table |
| `AGDI` | 기본 필수 | 5.2.6 표 5-6 | Arm Generic diagnostic Dump and Reset Device Interface Table |
| `MCFG` | PCI 지원 시 | 5.2.6 표 5-6 | Memory mapped ConFiGuration Table |
| `SPCR` | `console=<device>` 없이 boot 지원 시 | 5.2.6 표 5-6 | Serial Port Console Redirection table |
| `IORT` | I/O topology, SMMU, GIC ITS 설명이 필요할 때 | 5.2.6 표 5-6 | Input Output Remapping Table |
| `SRAT` | NUMA 지원 시 | 5.2.16 | System Resource Affinity Table |
| `SLIT` | NUMA 지원 시 | 5.2.17 | System Locality distance Information Table |
| `HMAT` | NUMA와 heterogeneous memory가 있을 때 | 5.2.28 | Heterogeneous Memory Attribute Table |
| `BERT` | APEI가 필요할 때 | 18.3.1 | Boot Error Record Table |
| `EINJ` | APEI가 필요할 때 | 18.6.1 | Error INJection table |
| `ERST` | APEI가 필요할 때 | 18.5 | Error Record Serialization Table |
| `HEST` | APEI가 필요할 때 | 18.3.2 | Hardware Error Source Table |
| `SDEI` | APEI가 필요할 때 | 5.2.6 표 5-6 | Software Delegated Exception Interface table |
| `AEST` | APEI가 필요할 때 | 5.2.6 표 5-6 | Arm Error Source Table |
| `RAS2` | APEI가 필요할 때 | 5.2.21 | ACPI RAS2 feature table |
| `PCCT` | PCC channel을 쓰는 controller가 있을 때 | 14.1 | Platform Communications Channel Table |
| `PDTT` | Board-level system state controller가 PCC로 host와 통신할 때 | 5.2.29 | Platform Debug Trigger Table |
| `NFIT` | NVDIMM 지원 시 | 5.2.26 | NVDIMM Firmware Interface Table |
| `BGRT` | Video framebuffer가 있을 때 | 5.2.23 | Boot Graphics Resource Table |
| `SPMI` | IPMI 구현 시 | 5.2.6 표 5-6 | Server Platform Management Interface |
| `CEDT` | CXL Host Bridge가 있을 때 | 5.2.6 표 5-6 | CXL Early Discovery Table |
| `MPAM` | MPAM 지원 시 | 5.2.6 표 5-6 | Memory Partitioning And Monitoring table |
| `IBFT` | Persistent storage가 없을 때 | 5.2.6 표 5-6 | ISCSI Boot Firmware Table |
완전성 확인과 ACPI 감지
259-277앞의 테이블이 모두 없으면 kernel이 사용 가능한 모든 장치를 구성하지 못해 정상 boot할 수도 있고 실패할 수도 있습니다. 이 목록은 모든 경우를 포함하지 않으며, 특정 기능을 지원하려면 18절의 APEI 테이블처럼 다른 테이블이 필요한 환경도 있습니다.
Driver는 `ACPI_HANDLE`이 null인지, `.of_node`가 있는지 또는 device structure의 다른 정보를 확인해 `probe()` 유형을 결정해야 합니다. 자세한 내용은 뒤의 Driver Recommendations 절에 있습니다.
Driver가 아닌 code에서 runtime에 ACPI 존재 여부를 감지해야 하면 `acpi_disabled` 값을 확인합니다. `CONFIG_ACPI`가 설정되지 않았으면 `acpi_disabled`는 항상 1입니다.
장치 열거와 Device Properties
278-367ACPI의 장치 설명은 표준으로 인정된 ACPI interface를 사용해야 합니다. 같은 장치의 Device Tree 설명보다 정보가 적을 수 있지만 이것이 ACPI가 유용한 이유이기도 합니다. Driver는 상세 정보가 적을 수 있음을 감안해 합리적인 기본값을 사용합니다. 올바르게 구현하면 hardware가 바뀌고 개선되어도 driver를 바꿀 필요가 없습니다.
Clock이 좋은 예입니다. DT에서는 clock을 명시하고 driver가 이를 고려해야 합니다. ACPI에서는 UEFI가 clock 설정을 포함해 장치를 합리적인 기본 상태로 남긴다고 가정합니다. Driver가 clock 값을 바꿔야 하면 ACPI method 안에서 수행할 수 있습니다. Driver는 method만 호출하고 clock 변경의 구체적인 방법은 알 필요가 없습니다. 시간이 지나 hardware가 바뀌어도 driver가 아니라 ACPI method 구현을 바꾸면 됩니다.
앞 예처럼 driver가 clock을 설정하는 데 필요한 parameter를 DT에서는 binding이라 부르고, ACPI에서는 Device Property라 부르며 `_DSD` 객체를 통해 driver에 제공합니다.
ACPI 테이블은 ACPI Source Language인 ASL이라는 formal language로 기술합니다(사양 19절). 같은 대상을 device property까지 포함해 여러 방식으로 표현할 수 있습니다. 예를 들어 `Name(KEY0, "value0")`처럼 ASL construct를 만들고 ACPI driver가 `KEY0` 객체를 평가해 값을 가져올 수 있습니다. 그러나 `Name()`을 이런 식으로 쓰면 네 가지 문제가 있습니다.
- ACPI name인 `"KEY0"`은 DT와 달리 네 문자로 제한됩니다.
- Name 재사용을 돕는 industry-wide registry가 없습니다.
- Property value인 `"value0"` 정의를 위한 registry도 없어 값 재사용이 어렵습니다.
- 새 hardware가 나올 때 backward compatibility를 어떻게 유지할지 정하기 어렵습니다.
`_DSD` method는 바로 이런 문제를 해결하기 위해 만들어졌습니다. Linux driver는 device property에 항상 `_DSD`만 사용해야 합니다.
`_DSM` 객체(ACPI 9.14.1절)도 device property를 전달할 수 있지만, `_DSD`로 필요한 data를 표현할 수 없고 `_DSD`용 새 UUID도 만들 수 없을 때만 Linux driver가 이를 기대해야 합니다. `_DSM` 사용은 `_DSD`보다 규제가 더 약해 유지보수가 어렵고, 이 문서 작성 당시 상당수 firmware 문제의 원인이므로 권장하지 않습니다.
Driver는 `_DSD` 객체에서만 device property를 찾아야 합니다. ACPI 사양 6.2.5절은 `_DSD` 반환 객체의 구조와 특정 UUID가 data structure를 정의하는 방법을 설명합니다. Linux는 다음 `_DSD` Device Properties UUID [4]만 사용해야 합니다.
UUID: `daffd814-6eba-4d8c-8a91-bc9bbf4aa301`
Common device property는 [4]에 pull request를 만들어 등록하면 ACPI를 지원하는 모든 OS에서 사용할 수 있습니다. UEFI Forum에 등록하지 않은 device property도 사용할 수 있지만 `uefi-` common property로는 사용할 수 없습니다.
새 device property를 만들기 전에 Linux kernel 문서의 DT binding이나 UEFI Forum의 device property로 이미 정의·등록되었는지 확인하십시오. 모든 DT binding을 ACPI property로 그대로 옮기려는 것은 아니지만 기존 정의에서 배울 수 있습니다.
새 device property가 필요하거나 어떤 binding 정의를 합성해 모든 firmware에서 쓰는 것이 합리적이면 DT binding과 ACPI device property 양쪽 review process를 모두 사용하십시오. Driver를 Linux mailing list에 제출할 때 필요한 property 정의도 동시에 제출해야 합니다. ACPI를 지원하며 device property를 쓰는 driver는 정의가 없으면 완전한 것으로 간주되지 않습니다. Linux community가 property를 승인하면 UEFI Forum [4]에 등록해 registry 일관성 review를 다시 받아야 하며 반복 수정이 필요할 수 있습니다. UEFI Forum이 device property 정의의 canonical site입니다.
나중에 등록할 미사용 property name을 예약하려면 등록 의도를 UEFI Forum에 미리 알리는 것이 합리적일 수 있습니다. 다른 OS vendor도 등록을 요청하므로 절차가 원활해질 수 있습니다.
등록과 review가 끝나면 kernel은 DT와 ACPI 중 무엇을 사용하는지와 무관하게 device property를 조회하는 interface를 제공합니다. 이 API [5]를 사용하면 driver probe function의 code path 중복을 줄이고 DT binding과 ACPI property의 분화를 억제할 수 있습니다.
프로그램 가능한 전력 제어 자원
368-417프로그램 가능한 power control resource에는 voltage/current provider인 regulator와 clock source 등이 포함됩니다. ACPI에서는 kernel clock framework와 regulator framework를 전혀 사용하지 않는다고 가정합니다.
Kernel은 이런 자원의 power control이 Power Resource Object(ACPI 7.1절)로 표현된다고 가정합니다. 그러면 ACPI core가 필요에 따라 자원을 올바르게 활성화하고 비활성화합니다. 이를 위해 ACPI는 각 장치에 D-state가 정의되고 선택적 ACPI method인 `_PS0`, `_PS1`, `_PS2`, `_PS3`로 제어할 수 있다고 가정합니다. `_PS0`은 장치를 완전히 켜고 `_PS3`은 완전히 끕니다.
Power Resource 사용 방법은 두 가지입니다.
- Power state `Dx`에 들어갈 때 호출되는 `_PSx` method 안에서 관리합니다.
- 각각 `_ON`과 `_OFF` method를 가진 별도 power resource로 선언합니다. 특정 장치의 D-state와 `_PRx`로 연결해 장치가 `Dx` 상태에서 켜져 있기 위해 필요한 자원을 지정합니다. Kernel은 해당 power resource를 사용하는 장치 수를 추적하고 필요에 따라 `_ON`/`_OFF`를 호출합니다.
Kernel ACPI code는 `_PSx` method가 일반 ACPI 규칙도 따른다고 가정합니다.
- `_PS0` 또는 `_PS3` 중 하나를 구현하면 다른 하나도 구현해야 합니다.
- 장치가 켜져 있을 때 power resource 사용이나 설정이 필요하면 ASL이 `_PS0` method에서 할당·활성화하도록 구성해야 합니다.
- `_PS0`에서 할당하거나 활성화한 자원은 `_PS3`에서 비활성화하거나 deallocate해야 합니다.
- Firmware는 kernel에 제어권을 넘기기 전에 자원을 합리적인 상태로 남깁니다.
`_PSx` method code는 platform에 매우 구체적일 수밖에 없습니다. 하지만 이 방식은 driver가 장치 조작 interface를 추상화하고 ACPI 테이블에서 비표준 특수 값을 읽지 않게 합니다. 자원 사용을 추상화하면 hardware가 바뀌어도 driver를 갱신할 필요가 없습니다.
Clock
418-442ACPI는 firmware, 이 경우 UEFI가 kernel에 제어권을 넘기기 전에 clock을 동작 가능한 값으로 초기화한다고 가정합니다. 이는 UART나 SoC가 구동하는 LCD display 같은 장치에 영향을 줍니다.
Kernel boot 시 clock은 합리적으로 동작하는 값으로 설정되어 있다고 가정합니다. Power management throttling처럼 frequency를 바꿔야 하면 device driver는 호출 가능한 ACPI method로 그 절차가 추상화되어 있기를 기대해야 합니다. 표준 method에 관한 추가 권고는 ACPI 사양을 참조하십시오. 예외는 ACPI method보다 훨씬 풍부한 interface를 CPPC가 제공하는 CPU clock입니다. Clock이 설정되어 있지 않으면 Linux가 직접 제어할 방법이 없습니다.
SoC vendor가 system clock의 세밀한 제어를 제공하려면 Linux driver가 호출할 ACPI method를 만들 수는 있습니다. 그러나 이를 권장하지 않으며 Linux driver도 제공된 method를 사용해서는 안 됩니다. 현재 ACPI 사양에 표준화되지 않았으므로 kernel을 특정 SoC에 묶거나 SoC를 특정 kernel version에 묶을 수 있고, 이는 모두 피하려는 결과입니다.
Driver 권고사항
443-500Driver에 ACPI 지원을 추가할 때 기존 DT 처리를 절대로 제거하지 마십시오. 같은 장치를 서로 다른 여러 시스템에서 사용할 수 있습니다.
Driver를 data-driven 방식으로 구성하십시오. 기본값과 `probe()` function이 발견한 정보로 장치별 내부 상태를 담는 struct를 설정하고, 나머지 driver가 그 struct 내용만 사용하게 합니다. 그러면 ACPI와 DT 기능 차이 대부분을 driver 전체에 흩뜨리지 않고 probe function 안에 국한할 수 있습니다. 예시는 다음과 같습니다.
static int device_probe_dt(struct platform_device *pdev)
{
/* DT specific functionality */
...
}
static int device_probe_acpi(struct platform_device *pdev)
{
/* ACPI specific functionality */
...
}
static int device_probe(struct platform_device *pdev)
{
...
struct device_node node = pdev->dev.of_node;
...
if (node)
ret = device_probe_dt(pdev);
else if (ACPI_HANDLE(&pdev->dev))
ret = device_probe_acpi(pdev);
else
/* other initialization */
...
/* Continue with any generic probe operations */
...
}
Driver를 probe할 수 있는 DT와 ACPI 이름이 한눈에 보이도록 `MODULE_DEVICE_TABLE` entry를 driver 안에서 함께 배치하십시오.
static struct of_device_id virtio_mmio_match[] = {
{ .compatible = "virtio,mmio", },
{ }
};
MODULE_DEVICE_TABLE(of, virtio_mmio_match);
static const struct acpi_device_id virtio_mmio_acpi_match[] = {
{ "LNRO0005", },
{ }
};
MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
ASWG와 사양 변경
501-525ACPI 사양은 정기적으로 바뀝니다. 예를 들어 2014년에는 version 5.1이 release되고 version 6.0이 대부분 완성되었으며, 변경의 상당 부분을 Arm 전용 요구가 이끌었습니다. 제안 변경은 UEFI Forum 산하 ASWG(ACPI Specification Working Group)에 제출해 논의합니다. 현재 사양은 2022년 8월에 release된 ACPI 6.5입니다.
이 그룹에는 모든 UEFI member가 참여할 수 있습니다.
Arm ACPI kernel code는 ACPI 사양을 최대한 가깝게 따르고 UEFI ASWG가 release한 표준을 준수하는 기능만 구현하려 합니다. 실제로 vendor가 잘못된 ACPI 테이블을 제공하거나 표준을 위반할 수 있습니다. 오류 때문이면 quirk와 fix-up이 필요할 수 있지만 가능한 한 피합니다.
ACPI에 빠진 기능 때문에 어떤 platform에서 ACPI를 사용할 수 없다면 ECR(Engineering Change Request)을 ASWG에 제출해 정상 승인 절차를 거쳐야 합니다. UEFI member가 아닌 사람도 Linux community의 여러 member에게 ECR 제출 지원을 요청할 수 있습니다.
Arm Linux 전용 코드 항목과 객체 지침
526-545Linux source code에 포함된 Arm Linux 전용 항목은 `ACPI_OS_NAME`입니다. ACPI method가 `_OS`를 호출할 때 반환할 문자열을 정의합니다. Arm 시스템의 기본값은 `Linux`이며 command line parameter `acpi_os=<string>`으로 다른 값을 설정할 수 있습니다. 다른 architecture의 기본값은 예를 들어 `Microsoft Windows NT`입니다.
ACPI 테이블과 객체에 대한 자세한 기대사항은 `Documentation/arch/arm64/acpi_object_usage.rst`에 정리되어 있습니다.
참고 자료
546-568[0] Arm-DEN-0094, `Arm Base System Architecture`, version 1.0C, 2022-10-06.
[1] Arm-DEN-0044, `Arm Base Boot Requirements`, version 2.0G, 2022-04-15.
[2] Arm-DEN-0029, `Arm Server Base System Architecture`, version 7.1, 2022-10-06.
[3] Grant Likely가 작성한 Linaro Ltd. 저작권의 blog 글, 2015-01-10.
[4] `_DSD (Device Specific Data) Implementation Guide`.
- https://developer.arm.com/documentation/den0094/latest
https://developer.arm.com/documentation/den0094/latest - https://developer.arm.com/documentation/den0044/latest
https://developer.arm.com/documentation/den0044/latest - https://developer.arm.com/documentation/den0029/latest
https://developer.arm.com/documentation/den0029/latest - http://www.secretlab.ca/archives/151
http://www.secretlab.ca/archives/151 - https://github.com/UEFI/DSD-Guide/blob/main/dsd-guide.pdf
https://github.com/UEFI/DSD-Guide/blob/main/dsd-guide.pdf
[5] 통합 device property interface의 kernel code는 `include/linux/property.h`와 `drivers/base/property.c`에 있습니다.
작성자
569-575- Al Stone `<[email protected]>`
- Graeme Gregory `<[email protected]>`
- Hanjun Guo `<[email protected]>`
- Grant Likely `<[email protected]>`, `Why ACPI on ARM?` 절
요약과 해설
arm-acpi.rst:1-575Arm ACPI는 BSA·BBR와 hardware reduced model을 전제로 하며 UEFI configuration table을 통해서만 boot 정보를 받습니다. Firmware는 RSDP와 XSDT에서 시작하는 일관된 table graph를 제공하고, driver는 ACPI와 DT를 함께 지원하면서 차이를 probe 단계에 가둬야 합니다.
UEFI가 RSDP pointer를 넘기고 ACPI core가 64-bit table graph를 따라 장치 정보를 map합니다.
ACPI는 hardware 세부를 firmware method로 추상화하고 driver는 표준 interface만 소비합니다.