Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,368 @@
.. SPDX-License-Identifier: GPL-2.0
Debugging AMD Zen systems
+++++++++++++++++++++++++
Introduction
============
This document describes techniques that are useful for debugging issues with
AMD Zen systems. It is intended for use by developers and technical users
to help identify and resolve issues.
S3 vs s2idle
============
On AMD systems, it's not possible to simultaneously support suspend-to-RAM (S3)
and suspend-to-idle (s2idle). To confirm which mode your system supports you
can look at ``cat /sys/power/mem_sleep``. If it shows ``s2idle [deep]`` then
*S3* is supported. If it shows ``[s2idle]`` then *s2idle* is
supported.
On systems that support *S3*, the firmware will be utilized to put all hardware into
the appropriate low power state.
On systems that support *s2idle*, the kernel will be responsible for transitioning devices
into the appropriate low power state. When all devices are in the appropriate low
power state, the hardware will transition into a hardware sleep state.
After a suspend cycle you can tell how much time was spent in a hardware sleep
state by looking at ``cat /sys/power/suspend_stats/last_hw_sleep``.
This flowchart explains how the AMD s2idle suspend flow works.
.. kernel-figure:: suspend.svg
This flowchart explains how the amd s2idle resume flow works.
.. kernel-figure:: resume.svg
s2idle debugging tool
=====================
As there are a lot of places that problems can occur, a debugging tool has been
created at
`amd-debug-tools <https://git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git/about/>`_
that can help test for common problems and offer suggestions.
If you have an s2idle issue, it's best to start with this and follow instructions
from its findings. If you continue to have an issue, raise a bug with the
report generated from this script to
`drm/amd gitlab <https://gitlab.freedesktop.org/drm/amd/-/issues/new?issuable_template=s2idle_BUG_TEMPLATE>`_.
Spurious s2idle wakeups from an IRQ
===================================
Spurious wakeups will generally have an IRQ set to ``/sys/power/pm_wakeup_irq``.
This can be matched to ``/proc/interrupts`` to determine what device woke the system.
If this isn't enough to debug the problem, then the following sysfs files
can be set to add more verbosity to the wakeup process: ::
# echo 1 | sudo tee /sys/power/pm_debug_messages
# echo 1 | sudo tee /sys/power/pm_print_times
After making those changes, the kernel will display messages that can
be traced back to kernel s2idle loop code as well as display any active
GPIO sources while waking up.
If the wakeup is caused by the ACPI SCI, additional ACPI debugging may be
needed. These commands can enable additional trace data: ::
# echo enable | sudo tee /sys/module/acpi/parameters/trace_state
# echo 1 | sudo tee /sys/module/acpi/parameters/aml_debug_output
# echo 0x0800000f | sudo tee /sys/module/acpi/parameters/debug_level
# echo 0xffff0000 | sudo tee /sys/module/acpi/parameters/debug_layer
Spurious s2idle wakeups from a GPIO
===================================
If a GPIO is active when waking up the system ideally you would look at the
schematic to determine what device it is associated with. If the schematic
is not available, another tactic is to look at the ACPI _EVT() entry
to determine what device is notified when that GPIO is active.
For a hypothetical example, say that GPIO 59 woke up the system. You can
look at the SSDT to determine what device is notified when GPIO 59 is active.
First convert the GPIO number into hex. ::
$ python3 -c "print(hex(59))"
0x3b
Next determine which ACPI table has the ``_EVT`` entry. For example: ::
$ sudo grep EVT /sys/firmware/acpi/tables/SSDT*
grep: /sys/firmware/acpi/tables/SSDT27: binary file matches
Decode this table::
$ sudo cp /sys/firmware/acpi/tables/SSDT27 .
$ sudo iasl -d SSDT27
Then look at the table and find the matching entry for GPIO 0x3b. ::
Case (0x3B)
{
M000 (0x393B)
M460 (" Notify (\\_SB.PCI0.GP17.XHC1, 0x02)\n", Zero, Zero, Zero, Zero, Zero, Zero)
Notify (\_SB.PCI0.GP17.XHC1, 0x02) // Device Wake
}
You can see in this case that the device ``\_SB.PCI0.GP17.XHC1`` is notified
when GPIO 59 is active. It's obvious this is an XHCI controller, but to go a
step further you can figure out which XHCI controller it is by matching it to
ACPI.::
$ grep "PCI0.GP17.XHC1" /sys/bus/acpi/devices/*/path
/sys/bus/acpi/devices/device:2d/path:\_SB_.PCI0.GP17.XHC1
/sys/bus/acpi/devices/device:2e/path:\_SB_.PCI0.GP17.XHC1.RHUB
/sys/bus/acpi/devices/device:2f/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT1
/sys/bus/acpi/devices/device:30/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT1.CAM0
/sys/bus/acpi/devices/device:31/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT1.CAM1
/sys/bus/acpi/devices/device:32/path:\_SB_.PCI0.GP17.XHC1.RHUB.PRT2
/sys/bus/acpi/devices/LNXPOWER:0d/path:\_SB_.PCI0.GP17.XHC1.PWRS
Here you can see it matches to ``device:2d``. Look at the ``physical_node``
to determine what PCI device that actually is. ::
$ ls -l /sys/bus/acpi/devices/device:2d/physical_node
lrwxrwxrwx 1 root root 0 Feb 12 13:22 /sys/bus/acpi/devices/device:2d/physical_node -> ../../../../../pci0000:00/0000:00:08.1/0000:c2:00.4
So there you have it: the PCI device associated with this GPIO wakeup was ``0000:c2:00.4``.
The ``amd_s2idle.py`` script will capture most of these artifacts for you.
s2idle PM debug messages
========================
During the s2idle flow on AMD systems, the ACPI LPS0 driver is responsible
to check all uPEP constraints. Failing uPEP constraints does not prevent
s0i3 entry. This means that if some constraints are not met, it is possible
the kernel may attempt to enter s2idle even if there are some known issues.
To activate PM debugging, either specify ``pm_debug_messagess`` kernel
command-line option at boot or write to ``/sys/power/pm_debug_messages``.
Unmet constraints will be displayed in the kernel log and can be
viewed by logging tools that process kernel ring buffer like ``dmesg`` or
``journalctl``."
If the system freezes on entry/exit before these messages are flushed, a
useful debugging tactic is to unbind the ``amd_pmc`` driver to prevent
notification to the platform to start s0i3 entry. This will stop the
system from freezing on entry or exit and let you view all the failed
constraints. ::
cd /sys/bus/platform/drivers/amd_pmc
ls | grep AMD | sudo tee unbind
After doing this, run the suspend cycle and look specifically for errors around: ::
ACPI: LPI: Constraint not met; min power state:%s current power state:%s
Historical examples of s2idle issues
====================================
To help understand the types of issues that can occur and how to debug them,
here are some historical examples of s2idle issues that have been resolved.
Core offlining
--------------
An end user had reported that taking a core offline would prevent the system
from properly entering s0i3. This was debugged using internal AMD tools
to capture and display a stream of metrics from the hardware showing what changed
when a core was offlined. It was determined that the hardware didn't get
notification the offline cores were in the deepest state, and so it prevented
CPU from going into the deepest state. The issue was debugged to a missing
command to put cores into C3 upon offline.
`commit d6b88ce2eb9d2 ("ACPI: processor idle: Allow playing dead in C3 state") <https://git.kernel.org/torvalds/c/d6b88ce2eb9d2>`_
Corruption after resume
-----------------------
A big problem that occurred with Rembrandt was that there was graphical
corruption after resume. This happened because of a misalignment of PSP
and driver responsibility. The PSP will save and restore DMCUB, but the
driver assumed it needed to reset DMCUB on resume.
This actually was a misalignment for earlier silicon as well, but was not
observed.
`commit 79d6b9351f086 ("drm/amd/display: Don't reinitialize DMCUB on s0ix resume") <https://git.kernel.org/torvalds/c/79d6b9351f086>`_
Back to Back suspends fail
--------------------------
When using a wakeup source that triggers the IRQ to wakeup, a bug in the
pinctrl-amd driver may capture the wrong state of the IRQ and prevent the
system going back to sleep properly.
`commit b8c824a869f22 ("pinctrl: amd: Don't save/restore interrupt status and wake status bits") <https://git.kernel.org/torvalds/c/b8c824a869f22>`_
Spurious timer based wakeup after 5 minutes
-------------------------------------------
The HPET was being used to program the wakeup source for the system, however
this was causing a spurious wakeup after 5 minutes. The correct alarm to use
was the ACPI alarm.
`commit 3d762e21d5637 ("rtc: cmos: Use ACPI alarm for non-Intel x86 systems too") <https://git.kernel.org/torvalds/c/3d762e21d5637>`_
Disk disappears after resume
----------------------------
After resuming from s2idle, the NVME disk would disappear. This was due to the
BIOS not specifying the _DSD StorageD3Enable property. This caused the NVME
driver not to put the disk into the expected state at suspend and to fail
on resume.
`commit e79a10652bbd3 ("ACPI: x86: Force StorageD3Enable on more products") <https://git.kernel.org/torvalds/c/e79a10652bbd3>`_
Spurious IRQ1
-------------
A number of Renoir, Lucienne, Cezanne, & Barcelo platforms have a
platform firmware bug where IRQ1 is triggered during s0i3 resume.
This was fixed in the platform firmware, but a number of systems didn't
receive any more platform firmware updates.
`commit 8e60615e89321 ("platform/x86/amd: pmc: Disable IRQ1 wakeup for RN/CZN") <https://git.kernel.org/torvalds/c/8e60615e89321>`_
Hardware timeout
----------------
The hardware performs many actions besides accepting the values from
amd-pmc driver. As the communication path with the hardware is a mailbox,
it's possible that it might not respond quickly enough.
This issue manifested as a failure to suspend: ::
PM: dpm_run_callback(): acpi_subsys_suspend_noirq+0x0/0x50 returns -110
amd_pmc AMDI0005:00: PM: failed to suspend noirq: error -110
The timing problem was identified by comparing the values of the idle mask.
`commit 3c3c8e88c8712 ("platform/x86: amd-pmc: Increase the response register timeout") <https://git.kernel.org/torvalds/c/3c3c8e88c8712>`_
Failed to reach hardware sleep state with panel on
--------------------------------------------------
On some Strix systems certain panels were observed to block the system from
entering a hardware sleep state if the internal panel was on during the sequence.
Even though the panel got turned off during suspend it exposed a timing problem
where an interrupt caused the display hardware to wake up and block low power
state entry.
`commit 40b8c14936bd2 ("drm/amd/display: Disable unneeded hpd interrupts during dm_init") <https://git.kernel.org/torvalds/c/40b8c14936bd2>`_
Runtime power consumption issues
================================
Runtime power consumption is influenced by many factors, including but not
limited to the configuration of the PCIe Active State Power Management (ASPM),
the display brightness, the EPP policy of the CPU, and the power management
of the devices.
ASPM
----
For the best runtime power consumption, ASPM should be programmed as intended
by the BIOS from the hardware vendor. To accomplish this the Linux kernel
should be compiled with ``CONFIG_PCIEASPM_DEFAULT`` set to ``y`` and the
sysfs file ``/sys/module/pcie_aspm/parameters/policy`` should not be modified.
Most notably, if L1.2 is not configured properly for any devices, the SoC
will not be able to enter the deepest idle state.
EPP Policy
----------
The ``energy_performance_preference`` sysfs file can be used to set a bias
of efficiency or performance for a CPU. This has a direct relationship on
the battery life when more heavily biased towards performance.
BIOS debug messages
===================
Most OEM machines don't have a serial UART for outputting kernel or BIOS
debug messages. However BIOS debug messages are useful for understanding
both BIOS bugs and bugs with the Linux kernel drivers that call BIOS AML.
As the BIOS on most OEM AMD systems are based off an AMD reference BIOS,
the infrastructure used for exporting debugging messages is often the same
as AMD reference BIOS.
Manually Parsing
----------------
There is generally an ACPI method ``\M460`` that different paths of the AML
will call to emit a message to the BIOS serial log. This method takes
7 arguments, with the first being a string and the rest being optional
integers::
Method (M460, 7, Serialized)
Here is an example of a string that BIOS AML may call out using ``\M460``::
M460 (" OEM-ASL-PCIe Address (0x%X)._REG (%d %d) PCSA = %d\n", DADR, Arg0, Arg1, PCSA, Zero, Zero)
Normally when executed, the ``\M460`` method would populate the additional
arguments into the string. In order to get these messages from the Linux
kernel a hook has been added into ACPICA that can capture the *arguments*
sent to ``\M460`` and print them to the kernel ring buffer.
For example the following message could be emitted into kernel ring buffer::
extrace-0174 ex_trace_args : " OEM-ASL-PCIe Address (0x%X)._REG (%d %d) PCSA = %d\n", ec106000, 2, 1, 1, 0, 0
In order to get these messages, you need to compile with ``CONFIG_ACPI_DEBUG``
and then turn on the following ACPICA tracing parameters.
This can be done either on the kernel command line or at runtime:
* ``acpi.trace_method_name=\M460``
* ``acpi.trace_state=method``
NOTE: These can be very noisy at bootup. If you turn these parameters on
the kernel command, please also consider turning up ``CONFIG_LOG_BUF_SHIFT``
to a larger size such as 17 to avoid losing early boot messages.
Tool assisted Parsing
---------------------
As mentioned above, parsing by hand can be tedious, especially with a lot of
messages. To help with this, a tool has been created at
`amd-debug-tools <https://git.kernel.org/pub/scm/linux/kernel/git/superm1/amd-debug-tools.git/about/>`_
to help parse the messages.
Random reboot issues
====================
When a random reboot occurs, the high-level reason for the reboot is stored
in a register that will persist onto the next boot.
There are 6 classes of reasons for the reboot:
* Software induced
* Power state transition
* Pin induced
* Hardware induced
* Remote reset
* Internal CPU event
.. csv-table::
:header: "Bit", "Type", "Reason"
:align: left
"0", "Pin", "thermal pin BP_THERMTRIP_L was tripped"
"1", "Pin", "power button was pressed for 4 seconds"
"2", "Pin", "shutdown pin was tripped"
"4", "Remote", "remote ASF power off command was received"
"9", "Internal", "internal CPU thermal limit was tripped"
"16", "Pin", "system reset pin BP_SYS_RST_L was tripped"
"17", "Software", "software issued PCI reset"
"18", "Software", "software wrote 0x4 to reset control register 0xCF9"
"19", "Software", "software wrote 0x6 to reset control register 0xCF9"
"20", "Software", "software wrote 0xE to reset control register 0xCF9"
"21", "ACPI-state", "ACPI power state transition occurred"
"22", "Pin", "keyboard reset pin KB_RST_L was tripped"
"23", "Internal", "internal CPU shutdown event occurred"
"24", "Hardware", "system failed to boot before failed boot timer expired"
"25", "Hardware", "hardware watchdog timer expired"
"26", "Remote", "remote ASF reset command was received"
"27", "Internal", "an uncorrected error caused a data fabric sync flood event"
"29", "Internal", "FCH and MP1 failed warm reset handshake"
"30", "Internal", "a parity error occurred"
"31", "Internal", "a software sync flood event occurred"
This information is read by the kernel at bootup and printed into
the syslog. When a random reboot occurs this message can be helpful
to determine the next component to debug.
@@ -0,0 +1,133 @@
.. SPDX-License-Identifier: GPL-2.0
======================================================================
Hardware Feedback Interface For Hetero Core Scheduling On AMD Platform
======================================================================
:Copyright: 2025 Advanced Micro Devices, Inc. All Rights Reserved.
:Author: Perry Yuan <perry.yuan@amd.com>
:Author: Mario Limonciello <mario.limonciello@amd.com>
Overview
--------
AMD Heterogeneous Core implementations are comprised of more than one
architectural class and CPUs are comprised of cores of various efficiency and
power capabilities: performance-oriented *classic cores* and power-efficient
*dense cores*. As such, power management strategies must be designed to
accommodate the complexities introduced by incorporating different core types.
Heterogeneous systems can also extend to more than two architectural classes
as well. The purpose of the scheduling feedback mechanism is to provide
information to the operating system scheduler in real time such that the
scheduler can direct threads to the optimal core.
The goal of AMD's heterogeneous architecture is to attain power benefit by
sending background threads to the dense cores while sending high priority
threads to the classic cores. From a performance perspective, sending
background threads to dense cores can free up power headroom and allow the
classic cores to optimally service demanding threads. Furthermore, the area
optimized nature of the dense cores allows for an increasing number of
physical cores. This improved core density will have positive multithreaded
performance impact.
AMD Heterogeneous Core Driver
-----------------------------
The ``amd_hfi`` driver delivers the operating system a performance and energy
efficiency capability data for each CPU in the system. The scheduler can use
the ranking data from the HFI driver to make task placement decisions.
Thread Classification and Ranking Table Interaction
----------------------------------------------------
The thread classification is used to select into a ranking table that
describes an efficiency and performance ranking for each classification.
Threads are classified during runtime into enumerated classes. The classes
represent thread performance/power characteristics that may benefit from
special scheduling behaviors. The below table depicts an example of thread
classification and a preference where a given thread should be scheduled
based on its thread class. The real time thread classification is consumed
by the operating system and is used to inform the scheduler of where the
thread should be placed.
Thread Classification Example Table
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+----------+----------------+-------------------------------+---------------------+---------+
| class ID | Classification | Preferred scheduling behavior | Preemption priority | Counter |
+----------+----------------+-------------------------------+---------------------+---------+
| 0 | Default | Performant | Highest | |
+----------+----------------+-------------------------------+---------------------+---------+
| 1 | Non-scalable | Efficient | Lowest | PMCx1A1 |
+----------+----------------+-------------------------------+---------------------+---------+
| 2 | I/O bound | Efficient | Lowest | PMCx044 |
+----------+----------------+-------------------------------+---------------------+---------+
Thread classification is performed by the hardware each time that the thread is switched out.
Threads that don't meet any hardware specified criteria are classified as "default".
AMD Hardware Feedback Interface
--------------------------------
The Hardware Feedback Interface provides to the operating system information
about the performance and energy efficiency of each CPU in the system. Each
capability is given as a unit-less quantity in the range [0-255]. A higher
performance value indicates higher performance capability, and a higher
efficiency value indicates more efficiency. Energy efficiency and performance
are reported in separate capabilities in the shared memory based ranking table.
These capabilities may change at runtime as a result of changes in the
operating conditions of the system or the action of external factors.
Power Management firmware is responsible for detecting events that require
a reordering of the performance and efficiency ranking. Table updates happen
relatively infrequently and occur on the time scale of seconds or more.
The following events trigger a table update:
* Thermal Stress Events
* Silent Compute
* Extreme Low Battery Scenarios
The kernel or a userspace policy daemon can use these capabilities to modify
task placement decisions. For instance, if either the performance or energy
capabilities of a given logical processor becomes zero, it is an indication
that the hardware recommends to the operating system to not schedule any tasks
on that processor for performance or energy efficiency reasons, respectively.
Implementation details for Linux
--------------------------------
The implementation of threads scheduling consists of the following steps:
1. A thread is spawned and scheduled to the ideal core using the default
heterogeneous scheduling policy.
2. The processor profiles thread execution and assigns an enumerated
classification ID.
This classification is communicated to the OS via logical processor
scope MSR.
3. During the thread context switch out the operating system consumes the
workload (WL) classification which resides in a logical processor scope MSR.
4. The OS triggers the hardware to clear its history by writing to an MSR,
after consuming the WL classification and before switching in the new thread.
5. If due to the classification, ranking table, and processor availability,
the thread is not on its ideal processor, the OS will then consider
scheduling the thread on its ideal processor (if available).
Ranking Table
-------------
The ranking table is a shared memory region that is used to communicate the
performance and energy efficiency capabilities of each CPU in the system.
The ranking table design includes rankings for each APIC ID in the system and
rankings both for performance and efficiency for each workload classification.
.. kernel-doc:: drivers/platform/x86/amd/hfi/hfi.c
:doc: amd_shmem_info
Ranking Table update
---------------------------
The power management firmware issues an platform interrupt after updating the
ranking table and is ready for the operating system to consume it. CPUs receive
such interrupt and read new ranking table from shared memory which PCCT table
has provided, then ``amd_hfi`` driver parses the new table to provide new
consume data for scheduling decisions.
@@ -0,0 +1,278 @@
.. SPDX-License-Identifier: GPL-2.0
=====================
AMD Memory Encryption
=====================
Secure Memory Encryption (SME) and Secure Encrypted Virtualization (SEV) are
features found on AMD processors.
SME provides the ability to mark individual pages of memory as encrypted using
the standard x86 page tables. A page that is marked encrypted will be
automatically decrypted when read from DRAM and encrypted when written to
DRAM. SME can therefore be used to protect the contents of DRAM from physical
attacks on the system.
SEV enables running encrypted virtual machines (VMs) in which the code and data
of the guest VM are secured so that a decrypted version is available only
within the VM itself. SEV guest VMs have the concept of private and shared
memory. Private memory is encrypted with the guest-specific key, while shared
memory may be encrypted with hypervisor key. When SME is enabled, the hypervisor
key is the same key which is used in SME.
A page is encrypted when a page table entry has the encryption bit set (see
below on how to determine its position). The encryption bit can also be
specified in the cr3 register, allowing the PGD table to be encrypted. Each
successive level of page tables can also be encrypted by setting the encryption
bit in the page table entry that points to the next table. This allows the full
page table hierarchy to be encrypted. Note, this means that just because the
encryption bit is set in cr3, doesn't imply the full hierarchy is encrypted.
Each page table entry in the hierarchy needs to have the encryption bit set to
achieve that. So, theoretically, you could have the encryption bit set in cr3
so that the PGD is encrypted, but not set the encryption bit in the PGD entry
for a PUD which results in the PUD pointed to by that entry to not be
encrypted.
When SEV is enabled, instruction pages and guest page tables are always treated
as private. All the DMA operations inside the guest must be performed on shared
memory. Since the memory encryption bit is controlled by the guest OS when it
is operating in 64-bit or 32-bit PAE mode, in all other modes the SEV hardware
forces the memory encryption bit to 1.
Support for SME and SEV can be determined through the CPUID instruction. The
CPUID function 0x8000001f reports information related to SME::
0x8000001f[eax]:
Bit[0] indicates support for SME
Bit[1] indicates support for SEV
0x8000001f[ebx]:
Bits[5:0] pagetable bit number used to activate memory
encryption
Bits[11:6] reduction in physical address space, in bits, when
memory encryption is enabled (this only affects
system physical addresses, not guest physical
addresses)
If support for SME is present, MSR 0xc00100010 (MSR_AMD64_SYSCFG) can be used to
determine if SME is enabled and/or to enable memory encryption::
0xc0010010:
Bit[23] 0 = memory encryption features are disabled
1 = memory encryption features are enabled
If SEV is supported, MSR 0xc0010131 (MSR_AMD64_SEV) can be used to determine if
SEV is active::
0xc0010131:
Bit[0] 0 = memory encryption is not active
1 = memory encryption is active
Linux relies on BIOS to set this bit if BIOS has determined that the reduction
in the physical address space as a result of enabling memory encryption (see
CPUID information above) will not conflict with the address space resource
requirements for the system. If this bit is not set upon Linux startup then
Linux itself will not set it and memory encryption will not be possible.
The state of SME in the Linux kernel can be documented as follows:
- Supported:
The CPU supports SME (determined through CPUID instruction).
- Enabled:
Supported and bit 23 of MSR_AMD64_SYSCFG is set.
- Active:
Supported, Enabled and the Linux kernel is actively applying
the encryption bit to page table entries (the SME mask in the
kernel is non-zero).
SME can also be enabled and activated in the BIOS. If SME is enabled and
activated in the BIOS, then all memory accesses will be encrypted and it
will not be necessary to activate the Linux memory encryption support.
If the BIOS merely enables SME (sets bit 23 of the MSR_AMD64_SYSCFG),
then memory encryption can be enabled by supplying mem_encrypt=on on the
kernel command line. However, if BIOS does not enable SME, then Linux
will not be able to activate memory encryption, even if configured to do
so by default or the mem_encrypt=on command line parameter is specified.
Secure Nested Paging (SNP)
==========================
SEV-SNP introduces new features (SEV_FEATURES[1:63]) which can be enabled
by the hypervisor for security enhancements. Some of these features need
guest side implementation to function correctly. The below table lists the
expected guest behavior with various possible scenarios of guest/hypervisor
SNP feature support.
+-----------------+---------------+---------------+------------------+
| Feature Enabled | Guest needs | Guest has | Guest boot |
| by the HV | implementation| implementation| behaviour |
+=================+===============+===============+==================+
| No | No | No | Boot |
| | | | |
+-----------------+---------------+---------------+------------------+
| No | Yes | No | Boot |
| | | | |
+-----------------+---------------+---------------+------------------+
| No | Yes | Yes | Boot |
| | | | |
+-----------------+---------------+---------------+------------------+
| Yes | No | No | Boot with |
| | | | feature enabled |
+-----------------+---------------+---------------+------------------+
| Yes | Yes | No | Graceful boot |
| | | | failure |
+-----------------+---------------+---------------+------------------+
| Yes | Yes | Yes | Boot with |
| | | | feature enabled |
+-----------------+---------------+---------------+------------------+
More details in AMD64 APM[1] Vol 2: 15.34.10 SEV_STATUS MSR
Reverse Map Table (RMP)
=======================
The RMP is a structure in system memory that is used to ensure a one-to-one
mapping between system physical addresses and guest physical addresses. Each
page of memory that is potentially assignable to guests has one entry within
the RMP.
The RMP table can be either contiguous in memory or a collection of segments
in memory.
Contiguous RMP
--------------
Support for this form of the RMP is present when support for SEV-SNP is
present, which can be determined using the CPUID instruction::
0x8000001f[eax]:
Bit[4] indicates support for SEV-SNP
The location of the RMP is identified to the hardware through two MSRs::
0xc0010132 (RMP_BASE):
System physical address of the first byte of the RMP
0xc0010133 (RMP_END):
System physical address of the last byte of the RMP
Hardware requires that RMP_BASE and (RPM_END + 1) be 8KB aligned, but SEV
firmware increases the alignment requirement to require a 1MB alignment.
The RMP consists of a 16KB region used for processor bookkeeping followed
by the RMP entries, which are 16 bytes in size. The size of the RMP
determines the range of physical memory that the hypervisor can assign to
SEV-SNP guests. The RMP covers the system physical address from::
0 to ((RMP_END + 1 - RMP_BASE - 16KB) / 16B) x 4KB.
The current Linux support relies on BIOS to allocate/reserve the memory for
the RMP and to set RMP_BASE and RMP_END appropriately. Linux uses the MSR
values to locate the RMP and determine the size of the RMP. The RMP must
cover all of system memory in order for Linux to enable SEV-SNP.
Segmented RMP
-------------
Segmented RMP support is a new way of representing the layout of an RMP.
Initial RMP support required the RMP table to be contiguous in memory.
RMP accesses from a NUMA node on which the RMP doesn't reside
can take longer than accesses from a NUMA node on which the RMP resides.
Segmented RMP support allows the RMP entries to be located on the same
node as the memory the RMP is covering, potentially reducing latency
associated with accessing an RMP entry associated with the memory. Each
RMP segment covers a specific range of system physical addresses.
Support for this form of the RMP can be determined using the CPUID
instruction::
0x8000001f[eax]:
Bit[23] indicates support for segmented RMP
If supported, segmented RMP attributes can be found using the CPUID
instruction::
0x80000025[eax]:
Bits[5:0] minimum supported RMP segment size
Bits[11:6] maximum supported RMP segment size
0x80000025[ebx]:
Bits[9:0] number of cacheable RMP segment definitions
Bit[10] indicates if the number of cacheable RMP segments
is a hard limit
To enable a segmented RMP, a new MSR is available::
0xc0010136 (RMP_CFG):
Bit[0] indicates if segmented RMP is enabled
Bits[13:8] contains the size of memory covered by an RMP
segment (expressed as a power of 2)
The RMP segment size defined in the RMP_CFG MSR applies to all segments
of the RMP. Therefore each RMP segment covers a specific range of system
physical addresses. For example, if the RMP_CFG MSR value is 0x2401, then
the RMP segment coverage value is 0x24 => 36, meaning the size of memory
covered by an RMP segment is 64GB (1 << 36). So the first RMP segment
covers physical addresses from 0 to 0xF_FFFF_FFFF, the second RMP segment
covers physical addresses from 0x10_0000_0000 to 0x1F_FFFF_FFFF, etc.
When a segmented RMP is enabled, RMP_BASE points to the RMP bookkeeping
area as it does today (16K in size). However, instead of RMP entries
beginning immediately after the bookkeeping area, there is a 4K RMP
segment table (RST). Each entry in the RST is 8-bytes in size and represents
an RMP segment::
Bits[19:0] mapped size (in GB)
The mapped size can be less than the defined segment size.
A value of zero, indicates that no RMP exists for the range
of system physical addresses associated with this segment.
Bits[51:20] segment physical address
This address is left shift 20-bits (or just masked when
read) to form the physical address of the segment (1MB
alignment).
The RST can hold 512 segment entries but can be limited in size to the number
of cacheable RMP segments (CPUID 0x80000025_EBX[9:0]) if the number of cacheable
RMP segments is a hard limit (CPUID 0x80000025_EBX[10]).
The current Linux support relies on BIOS to allocate/reserve the memory for
the segmented RMP (the bookkeeping area, RST, and all segments), build the RST
and to set RMP_BASE, RMP_END, and RMP_CFG appropriately. Linux uses the MSR
values to locate the RMP and determine the size and location of the RMP
segments. The RMP must cover all of system memory in order for Linux to enable
SEV-SNP.
More details in the AMD64 APM Vol 2, section "15.36.3 Reverse Map Table",
docID: 24593.
Secure VM Service Module (SVSM)
===============================
SNP provides a feature called Virtual Machine Privilege Levels (VMPL) which
defines four privilege levels at which guest software can run. The most
privileged level is 0 and numerically higher numbers have lesser privileges.
More details in the AMD64 APM Vol 2, section "15.35.7 Virtual Machine
Privilege Levels", docID: 24593.
When using that feature, different services can run at different protection
levels, apart from the guest OS but still within the secure SNP environment.
They can provide services to the guest, like a vTPM, for example.
When a guest is not running at VMPL0, it needs to communicate with the software
running at VMPL0 to perform privileged operations or to interact with secure
services. An example fur such a privileged operation is PVALIDATE which is
*required* to be executed at VMPL0.
In this scenario, the software running at VMPL0 is usually called a Secure VM
Service Module (SVSM). Discovery of an SVSM and the API used to communicate
with it is documented in "Secure VM Service Module for SEV-SNP Guests", docID:
58019.
(Latest versions of the above-mentioned documents can be found by using
a search engine like duckduckgo.com and typing in:
site:amd.com "Secure VM Service Module for SEV-SNP Guests", docID: 58019
for example.)
@@ -0,0 +1,192 @@
.. SPDX-License-Identifier: GPL-2.0
============================================
AMD HSMP interface
============================================
Newer Fam19h(model 0x00-0x1f, 0x30-0x3f, 0x90-0x9f, 0xa0-0xaf),
Fam1Ah(model 0x00-0x1f) EPYC server line of processors from AMD support
system management functionality via HSMP (Host System Management Port).
The Host System Management Port (HSMP) is an interface to provide
OS-level software with access to system management functions via a
set of mailbox registers.
More details on the interface can be found in chapter
"7 Host System Management Port (HSMP)" of the family/model PPR
Eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50
HSMP interface is supported on EPYC line of server CPUs and MI300A (APU).
HSMP device
============================================
amd_hsmp driver under drivers/platforms/x86/amd/hsmp/ has separate driver files
for ACPI object based probing, platform device based probing and for the common
code for these two drivers.
Kconfig option CONFIG_AMD_HSMP_PLAT compiles plat.c and creates amd_hsmp.ko.
Kconfig option CONFIG_AMD_HSMP_ACPI compiles acpi.c and creates hsmp_acpi.ko.
Selecting any of these two configs automatically selects CONFIG_AMD_HSMP. This
compiles common code hsmp.c and creates hsmp_common.ko module.
Both the ACPI and plat drivers create the miscdevice /dev/hsmp to let
user space programs run hsmp mailbox commands.
The ACPI object format supported by the driver is defined below.
$ ls -al /dev/hsmp
crw-r--r-- 1 root root 10, 123 Jan 21 21:41 /dev/hsmp
Characteristics of the dev node:
* Write mode is used for running set/configure commands
* Read mode is used for running get/status monitor commands
Access restrictions:
* Only root user is allowed to open the file in write mode.
* The file can be opened in read mode by all the users.
In-kernel integration:
* Other subsystems in the kernel can use the exported transport
function hsmp_send_message().
* Locking across callers is taken care by the driver.
HSMP sysfs interface
====================
1. Metrics table binary sysfs
AMD MI300A MCM provides GET_METRICS_TABLE message to retrieve
most of the system management information from SMU in one go.
The metrics table is made available as hexadecimal sysfs binary file
under per socket sysfs directory created at
/sys/devices/platform/amd_hsmp/socket%d/metrics_bin
Note: lseek() is not supported as entire metrics table is read.
Metrics table definitions will be documented as part of Public PPR.
The same is defined in the amd_hsmp.h header.
2. HSMP telemetry sysfs files
Following sysfs files are available at /sys/devices/platform/AMDI0097:0X/.
* c0_residency_input: Percentage of cores in C0 state.
* prochot_status: Reports 1 if the processor is at thermal threshold value,
0 otherwise.
* smu_fw_version: SMU firmware version.
* protocol_version: HSMP interface version.
* ddr_max_bw: Theoretical maximum DDR bandwidth in GB/s.
* ddr_utilised_bw_input: Current utilized DDR bandwidth in GB/s.
* ddr_utilised_bw_perc_input(%): Percentage of current utilized DDR bandwidth.
* mclk_input: Memory clock in MHz.
* fclk_input: Fabric clock in MHz.
* clk_fmax: Maximum frequency of socket in MHz.
* clk_fmin: Minimum frequency of socket in MHz.
* cclk_freq_limit_input: Core clock frequency limit per socket in MHz.
* pwr_current_active_freq_limit: Current active frequency limit of socket
in MHz.
* pwr_current_active_freq_limit_source: Source of current active frequency
limit.
ACPI device object format
=========================
The ACPI object format expected from the amd_hsmp driver
for socket with ID00 is given below::
Device(HSMP)
{
Name(_HID, "AMDI0097")
Name(_UID, "ID00")
Name(HSE0, 0x00000001)
Name(RBF0, ResourceTemplate()
{
Memory32Fixed(ReadWrite, 0xxxxxxx, 0x00100000)
})
Method(_CRS, 0, NotSerialized)
{
Return(RBF0)
}
Method(_STA, 0, NotSerialized)
{
If(LEqual(HSE0, One))
{
Return(0x0F)
}
Else
{
Return(Zero)
}
}
Name(_DSD, Package(2)
{
Buffer(0x10)
{
0x9D, 0x61, 0x4D, 0xB7, 0x07, 0x57, 0xBD, 0x48,
0xA6, 0x9F, 0x4E, 0xA2, 0x87, 0x1F, 0xC2, 0xF6
},
Package(3)
{
Package(2) {"MsgIdOffset", 0x00010934},
Package(2) {"MsgRspOffset", 0x00010980},
Package(2) {"MsgArgOffset", 0x000109E0}
}
})
}
HSMP HWMON interface
====================
HSMP power sensors are registered with the hwmon interface. A separate hwmon
directory is created for each socket and the following files are generated
within the hwmon directory.
- power1_input (read only)
- power1_cap_max (read only)
- power1_cap (read, write)
An example
==========
To access hsmp device from a C program.
First, you need to include the headers::
#include <linux/amd_hsmp.h>
Which defines the supported messages/message IDs.
Next thing, open the device file, as follows::
int file;
file = open("/dev/hsmp", O_RDWR);
if (file < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
exit(1);
}
The following IOCTL is defined:
``ioctl(file, HSMP_IOCTL_CMD, struct hsmp_message *msg)``
The argument is a pointer to a::
struct hsmp_message {
__u32 msg_id; /* Message ID */
__u16 num_args; /* Number of input argument words in message */
__u16 response_sz; /* Number of expected output/response words */
__u32 args[HSMP_MAX_MSG_LEN]; /* argument/response buffer */
__u16 sock_ind; /* socket number */
};
The ioctl would return a non-zero on failure; you can read errno to see
what happened. The transaction returns 0 on success.
More details on the interface and message definitions can be found in chapter
"7 Host System Management Port (HSMP)" of the respective family/model PPR
eg: https://docs.amd.com/v/u/en-US/55898_B1_pub_0_50
User space C-APIs are made available by linking against the esmi library,
which is provided by the E-SMS project https://www.amd.com/en/developer/e-sms.html.
See: https://github.com/amd/esmi_ib_library
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
.. SPDX-License-Identifier: GPL-2.0
DeviceTree Booting
------------------
There is one single 32bit entry point to the kernel at code32_start,
the decompressor (the real mode entry point goes to the same 32bit
entry point once it switched into protected mode). That entry point
supports one calling convention which is documented in
Documentation/arch/x86/boot.rst
The physical pointer to the device-tree block is passed via setup_data
which requires at least boot protocol 2.09.
The type filed is defined as
#define SETUP_DTB 2
This device-tree is used as an extension to the "boot page". As such it
does not parse / consider data which is already covered by the boot
page. This includes memory size, reserved ranges, command line arguments
or initrd address. It simply holds information which can not be retrieved
otherwise like interrupt routing or a list of devices behind an I2C bus.
@@ -0,0 +1,133 @@
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
===============================
Bus lock detection and handling
===============================
:Copyright: |copy| 2021 Intel Corporation
:Authors: - Fenghua Yu <fenghua.yu@intel.com>
- Tony Luck <tony.luck@intel.com>
Problem
=======
A split lock is any atomic operation whose operand crosses two cache lines.
Since the operand spans two cache lines and the operation must be atomic,
the system locks the bus while the CPU accesses the two cache lines.
A bus lock is acquired through either split locked access to writeback (WB)
memory or any locked access to non-WB memory. This is typically thousands of
cycles slower than an atomic operation within a cache line. It also disrupts
performance on other cores and brings the whole system to its knees.
Detection
=========
Intel processors may support either or both of the following hardware
mechanisms to detect split locks and bus locks. Some AMD processors also
support bus lock detect.
#AC exception for split lock detection
--------------------------------------
Beginning with the Tremont Atom CPU split lock operations may raise an
Alignment Check (#AC) exception when a split lock operation is attempted.
#DB exception for bus lock detection
------------------------------------
Some CPUs have the ability to notify the kernel by an #DB trap after a user
instruction acquires a bus lock and is executed. This allows the kernel to
terminate the application or to enforce throttling.
Software handling
=================
The kernel #AC and #DB handlers handle bus lock based on the kernel
parameter "split_lock_detect". Here is a summary of different options:
+------------------+----------------------------+-----------------------+
|split_lock_detect=|#AC for split lock |#DB for bus lock |
+------------------+----------------------------+-----------------------+
|off |Do nothing |Do nothing |
+------------------+----------------------------+-----------------------+
|warn |Kernel OOPs |Warn once per task and |
|(default) |Warn once per task, add a |and continues to run. |
| |delay, add synchronization | |
| |to prevent more than one | |
| |core from executing a | |
| |split lock in parallel. | |
| |sysctl split_lock_mitigate | |
| |can be used to avoid the | |
| |delay and synchronization | |
| |When both features are | |
| |supported, warn in #AC | |
+------------------+----------------------------+-----------------------+
|fatal |Kernel OOPs |Send SIGBUS to user. |
| |Send SIGBUS to user | |
| |When both features are | |
| |supported, fatal in #AC | |
+------------------+----------------------------+-----------------------+
|ratelimit:N |Do nothing |Limit bus lock rate to |
|(0 < N <= 1000) | |N bus locks per second |
| | |system wide and warn on|
| | |bus locks. |
+------------------+----------------------------+-----------------------+
Usages
======
Detecting and handling bus lock may find usages in various areas:
It is critical for real time system designers who build consolidated real
time systems. These systems run hard real time code on some cores and run
"untrusted" user processes on other cores. The hard real time cannot afford
to have any bus lock from the untrusted processes to hurt real time
performance. To date the designers have been unable to deploy these
solutions as they have no way to prevent the "untrusted" user code from
generating split lock and bus lock to block the hard real time code to
access memory during bus locking.
It's also useful for general computing to prevent guests or user
applications from slowing down the overall system by executing instructions
with bus lock.
Guidance
========
off
---
Disable checking for split lock and bus lock. This option can be useful if
there are legacy applications that trigger these events at a low rate so
that mitigation is not needed.
warn
----
A warning is emitted when a bus lock is detected which allows to identify
the offending application. This is the default behavior.
fatal
-----
In this case, the bus lock is not tolerated and the process is killed.
ratelimit
---------
A system wide bus lock rate limit N is specified where 0 < N <= 1000. This
allows a bus lock rate up to N bus locks per second. When the bus lock rate
is exceeded then any task which is caught via the buslock #DB exception is
throttled by enforced sleeps until the rate goes under the limit again.
This is an effective mitigation in cases where a minimal impact can be
tolerated, but an eventual Denial of Service attack has to be prevented. It
allows to identify the offending processes and analyze whether they are
malicious or just badly written.
Selecting a rate limit of 1000 allows the bus to be locked for up to about
seven million cycles each second (assuming 7000 cycles for each bus
lock). On a 2 GHz processor that would be about 0.35% system slowdown.
@@ -0,0 +1,206 @@
.. SPDX-License-Identifier: GPL-2.0
=================
x86 Feature Flags
=================
Introduction
============
The list of feature flags in /proc/cpuinfo is not complete and
represents an ill-fated attempt from long time ago to put feature flags
in an easy to find place for userspace.
However, the number of feature flags is growing with each CPU generation,
leading to unparseable and unwieldy /proc/cpuinfo.
What is more, those feature flags do not even need to be in that file
because userspace doesn't care about them - glibc et al already use
CPUID to find out what the target machine supports and what not.
And even if it doesn't show a particular feature flag - although the CPU
still does have support for the respective hardware functionality and
said CPU supports CPUID faulting - userspace can simply probe for the
feature and figure out if it is supported or not, regardless of whether
it is being advertised somewhere.
Furthermore, those flag strings become an ABI the moment they appear
there and maintaining them forever when nothing even uses them is a lot
of wasted effort.
So, the current use of /proc/cpuinfo is to show features which the
kernel has *enabled* and *supports*. As in: the CPUID feature flag is
there, there's an additional setup which the kernel has done while
booting and the functionality is ready to use. A perfect example for
that is "user_shstk" where additional code enablement is present in the
kernel to support shadow stack for user programs.
So, if users want to know if a feature is available on a given system,
they try to find the flag in /proc/cpuinfo. If a given flag is present,
it means that
* the kernel knows about the feature enough to have an X86_FEATURE bit
* the kernel supports it and is currently making it available either to
userspace or some other part of the kernel
* if the flag represents a hardware feature the hardware supports it.
The absence of a flag in /proc/cpuinfo by itself means almost nothing to
an end user.
On the one hand, a feature like "vaes" might be fully available to user
applications on a kernel that has not defined X86_FEATURE_VAES and thus
there is no "vaes" in /proc/cpuinfo.
On the other hand, a new kernel running on non-VAES hardware would also
have no "vaes" in /proc/cpuinfo. There's no way for an application or
user to tell the difference.
The end result is that the flags field in /proc/cpuinfo is marginally
useful for kernel debugging, but not really for anything else.
Applications should instead use things like the glibc facilities for
querying CPU support. Users should rely on tools like
tools/arch/x86/kcpuid and cpuid(1).
Regarding implementation, flags appearing in /proc/cpuinfo have an
X86_FEATURE definition in arch/x86/include/asm/cpufeatures.h. These flags
represent hardware features as well as software features.
If the kernel cares about a feature or KVM want to expose the feature to
a KVM guest, it should only then expose it to the guest when the guest
needs to parse /proc/cpuinfo. Which, as mentioned above, is highly
unlikely. KVM can synthesize the CPUID bit and the KVM guest can simply
query CPUID and figure out what the hypervisor supports and what not. As
already stated, /proc/cpuinfo is not a dumping ground for useless
feature flags.
How are feature flags created?
==============================
Feature flags can be derived from the contents of CPUID leaves
--------------------------------------------------------------
These feature definitions are organized mirroring the layout of CPUID
leaves and grouped in words with offsets as mapped in enum cpuid_leafs
in cpufeatures.h (see arch/x86/include/asm/cpufeatures.h for details).
If a feature is defined with a X86_FEATURE_<name> definition in
cpufeatures.h, and if it is detected at run time, the flags will be
displayed accordingly in /proc/cpuinfo. For example, the flag "avx2"
comes from X86_FEATURE_AVX2 in cpufeatures.h.
Flags can be from scattered CPUID-based features
------------------------------------------------
Hardware features enumerated in sparsely populated CPUID leaves get
software-defined values. Still, CPUID needs to be queried to determine
if a given feature is present. This is done in init_scattered_cpuid_features().
For instance, X86_FEATURE_CQM_LLC is defined as 11*32 + 0 and its presence is
checked at runtime in the respective CPUID leaf [EAX=f, ECX=0] bit EDX[1].
The intent of scattering CPUID leaves is to not bloat struct
cpuinfo_x86.x86_capability[] unnecessarily. For instance, the CPUID leaf
[EAX=7, ECX=0] has 30 features and is dense, but the CPUID leaf [EAX=7, EAX=1]
has only one feature and would waste 31 bits of space in the x86_capability[]
array. Since there is a struct cpuinfo_x86 for each possible CPU, the wasted
memory is not trivial.
Flags can be created synthetically under certain conditions for hardware features
---------------------------------------------------------------------------------
Examples of conditions include whether certain features are present in
MSR_IA32_CORE_CAPS or specific CPU models are identified. If the needed
conditions are met, the features are enabled by the set_cpu_cap or
setup_force_cpu_cap macros. For example, if bit 5 is set in MSR_IA32_CORE_CAPS,
the feature X86_FEATURE_SPLIT_LOCK_DETECT will be enabled and
"split_lock_detect" will be displayed. The flag "ring3mwait" will be
displayed only when running on INTEL_XEON_PHI_[KNL|KNM] processors.
Flags can represent purely software features
--------------------------------------------
These flags do not represent hardware features. Instead, they represent a
software feature implemented in the kernel. For example, Kernel Page Table
Isolation is purely software feature and its feature flag X86_FEATURE_PTI is
also defined in cpufeatures.h.
Naming of Flags
===============
The script arch/x86/kernel/cpu/mkcapflags.sh processes the
#define X86_FEATURE_<name> from cpufeatures.h and generates the
x86_cap/bug_flags[] arrays in kernel/cpu/capflags.c. The names in the
resulting x86_cap/bug_flags[] are used to populate /proc/cpuinfo. The naming
of flags in the x86_cap/bug_flags[] are as follows:
Flags do not appear by default in /proc/cpuinfo
-----------------------------------------------
Feature flags are omitted by default from /proc/cpuinfo as it does not make
sense for the feature to be exposed to userspace in most cases. For example,
X86_FEATURE_ALWAYS is defined in cpufeatures.h but that flag is an internal
kernel feature used in the alternative runtime patching functionality. So the
flag does not appear in /proc/cpuinfo.
Specify a flag name if absolutely needed
----------------------------------------
If the comment on the line for the #define X86_FEATURE_* starts with a
double-quote character (""), the string inside the double-quote characters
will be the name of the flags. For example, the flag "sse4_1" comes from
the comment "sse4_1" following the X86_FEATURE_XMM4_1 definition.
There are situations in which overriding the displayed name of the flag is
needed. For instance, /proc/cpuinfo is a userspace interface and must remain
constant. If, for some reason, the naming of X86_FEATURE_<name> changes, one
shall override the new naming with the name already used in /proc/cpuinfo.
Flags are missing when one or more of these happen
==================================================
The hardware does not enumerate support for it
----------------------------------------------
For example, when a new kernel is running on old hardware or the feature is
not enabled by boot firmware. Even if the hardware is new, there might be a
problem enabling the feature at run time, the flag will not be displayed.
The kernel does not know about the flag
---------------------------------------
For example, when an old kernel is running on new hardware.
The kernel disabled support for it at compile-time
--------------------------------------------------
For example, if Linear Address Masking (LAM) is not enabled when building (i.e.,
CONFIG_ADDRESS_MASKING is not selected) the flag "lam" will not show up.
Even though the feature will still be detected via CPUID, the kernel disables
it by clearing via setup_clear_cpu_cap(X86_FEATURE_LAM).
The feature is disabled at boot-time
------------------------------------
A feature can be disabled either using a command-line parameter or because
it failed to be enabled. The command-line parameter clearcpuid= can be used
to disable features using the feature number as defined in
/arch/x86/include/asm/cpufeatures.h. For instance, User Mode Instruction
Protection can be disabled using clearcpuid=514. The number 514 is calculated
from #define X86_FEATURE_UMIP (16*32 + 2).
DO NOT USE this cmdline option in production - it is meant to be used only as
a quick'n'dirty debugging aid to rule out a feature-enabling code is the
culprit. If you use it, it'll taint the kernel.
In addition, there exists a variety of custom command-line parameters that
disable specific features. The list of parameters includes, but is not limited
to, nofsgsbase, nosgx, noxsave, etc. 5-level paging can also be disabled using
"no5lvl".
The feature was known to be non-functional
------------------------------------------
The feature was known to be non-functional because a dependency was
missing at runtime. For example, AVX flags will not show up if XSAVE feature
is disabled since they depend on XSAVE feature. Another example would be broken
CPUs and them missing microcode patches. Due to that, the kernel decides not to
enable a feature.
@@ -0,0 +1,151 @@
.. SPDX-License-Identifier: GPL-2.0
============
Early Printk
============
Mini-HOWTO for using the earlyprintk=dbgp boot option with a
USB2 Debug port key and a debug cable, on x86 systems.
You need two computers, the 'USB debug key' special gadget and
two USB cables, connected like this::
[host/target] <-------> [USB debug key] <-------> [client/console]
Hardware requirements
=====================
a) Host/target system needs to have USB debug port capability.
You can check this capability by looking at a 'Debug port' bit in
the lspci -vvv output::
# lspci -vvv
...
00:1d.7 USB Controller: Intel Corporation 82801H (ICH8 Family) USB2 EHCI Controller #1 (rev 03) (prog-if 20 [EHCI])
Subsystem: Lenovo ThinkPad T61
Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Latency: 0
Interrupt: pin D routed to IRQ 19
Region 0: Memory at fe227000 (32-bit, non-prefetchable) [size=1K]
Capabilities: [50] Power Management version 2
Flags: PMEClk- DSI- D1- D2- AuxCurrent=375mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
Status: D0 PME-Enable- DSel=0 DScale=0 PME+
Capabilities: [58] Debug port: BAR=1 offset=00a0
^^^^^^^^^^^ <==================== [ HERE ]
Kernel driver in use: ehci_hcd
Kernel modules: ehci-hcd
...
.. note::
If your system does not list a debug port capability then you probably
won't be able to use the USB debug key.
b) You also need a NetChip USB debug cable/key:
http://www.plxtech.com/products/NET2000/NET20DC/default.asp
This is a small blue plastic connector with two USB connections;
it draws power from its USB connections.
c) You need a second client/console system with a high speed USB 2.0 port.
d) The NetChip device must be plugged directly into the physical
debug port on the "host/target" system. You cannot use a USB hub in
between the physical debug port and the "host/target" system.
The EHCI debug controller is bound to a specific physical USB
port and the NetChip device will only work as an early printk
device in this port. The EHCI host controllers are electrically
wired such that the EHCI debug controller is hooked up to the
first physical port and there is no way to change this via software.
You can find the physical port through experimentation by trying
each physical port on the system and rebooting. Or you can try
and use lsusb or look at the kernel info messages emitted by the
usb stack when you plug a usb device into various ports on the
"host/target" system.
Some hardware vendors do not expose the usb debug port with a
physical connector and if you find such a device send a complaint
to the hardware vendor, because there is no reason not to wire
this port into one of the physically accessible ports.
e) It is also important to note, that many versions of the NetChip
device require the "client/console" system to be plugged into the
right hand side of the device (with the product logo facing up and
readable left to right). The reason being is that the 5 volt
power supply is taken from only one side of the device and it
must be the side that does not get rebooted.
Software requirements
=====================
a) On the host/target system:
You need to enable the following kernel config option::
CONFIG_EARLY_PRINTK_DBGP=y
And you need to add the boot command line: "earlyprintk=dbgp".
.. note::
If you are using Grub, append it to the 'kernel' line in
/etc/grub.conf. If you are using Grub2 on a BIOS firmware system,
append it to the 'linux' line in /boot/grub2/grub.cfg. If you are
using Grub2 on an EFI firmware system, append it to the 'linux'
or 'linuxefi' line in /boot/grub2/grub.cfg or
/boot/efi/EFI/<distro>/grub.cfg.
On systems with more than one EHCI debug controller you must
specify the correct EHCI debug controller number. The ordering
comes from the PCI bus enumeration of the EHCI controllers. The
default with no number argument is "0" or the first EHCI debug
controller. To use the second EHCI debug controller, you would
use the command line: "earlyprintk=dbgp1"
.. note::
normally earlyprintk console gets turned off once the
regular console is alive - use "earlyprintk=dbgp,keep" to keep
this channel open beyond early bootup. This can be useful for
debugging crashes under Xorg, etc.
b) On the client/console system:
You should enable the following kernel config option::
CONFIG_USB_SERIAL_DEBUG=y
On the next bootup with the modified kernel you should
get a /dev/ttyUSBx device(s).
Now this channel of kernel messages is ready to be used: start
your favorite terminal emulator (minicom, etc.) and set
it up to use /dev/ttyUSB0 - or use a raw 'cat /dev/ttyUSBx' to
see the raw output.
c) On Nvidia Southbridge based systems: the kernel will try to probe
and find out which port has a debug device connected.
Testing
=======
You can test the output by using earlyprintk=dbgp,keep and provoking
kernel messages on the host/target system. You can provoke a harmless
kernel message by for example doing::
echo h > /proc/sysrq-trigger
On the host/target system you should see this help line in "dmesg" output::
SysRq : HELP : loglevel(0-9) reBoot Crashdump terminate-all-tasks(E) memory-full-oom-kill(F) kill-all-tasks(I) saK show-backtrace-all-active-cpus(L) show-memory-usage(M) nice-all-RT-tasks(N) powerOff show-registers(P) show-all-timers(Q) unRaw Sync show-task-states(T) Unmount show-blocked-tasks(W) dump-ftrace-buffer(Z)
On the client/console system do::
cat /dev/ttyUSB0
And you should see the help line above displayed shortly after you've
provoked it on the host system.
If it does not work then please ask about it on the linux-kernel@vger.kernel.org
mailing list or contact the x86 maintainers.
@@ -0,0 +1,53 @@
.. SPDX-License-Identifier: GPL-2.0
==================================
x86-specific ELF Auxiliary Vectors
==================================
This document describes the semantics of the x86 auxiliary vectors.
Introduction
============
ELF Auxiliary vectors enable the kernel to efficiently provide
configuration-specific parameters to userspace. In this example, a program
allocates an alternate stack based on the kernel-provided size::
#include <sys/auxv.h>
#include <elf.h>
#include <signal.h>
#include <stdlib.h>
#include <assert.h>
#include <err.h>
#ifndef AT_MINSIGSTKSZ
#define AT_MINSIGSTKSZ 51
#endif
....
stack_t ss;
ss.ss_sp = malloc(ss.ss_size);
assert(ss.ss_sp);
ss.ss_size = getauxval(AT_MINSIGSTKSZ) + SIGSTKSZ;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL))
err(1, "sigaltstack");
The exposed auxiliary vectors
=============================
AT_SYSINFO is used for locating the vsyscall entry point. It is not
exported on 64-bit mode.
AT_SYSINFO_EHDR is the start address of the page containing the vDSO.
AT_MINSIGSTKSZ denotes the minimum stack size required by the kernel to
deliver a signal to user-space. AT_MINSIGSTKSZ comprehends the space
consumed by the kernel to accommodate the user context for the current
hardware configuration. It does not comprehend subsequent user-space stack
consumption, which must be added by the user. (e.g. Above, user-space adds
SIGSTKSZ to AT_MINSIGSTKSZ.)
@@ -0,0 +1,110 @@
.. SPDX-License-Identifier: GPL-2.0
==============
Kernel Entries
==============
This file documents some of the kernel entries in
arch/x86/entry/entry_64.S. A lot of this explanation is adapted from
an email from Ingo Molnar:
https://lore.kernel.org/r/20110529191055.GC9835%40elte.hu
The x86 architecture has quite a few different ways to jump into
kernel code. Most of these entry points are registered in
arch/x86/kernel/traps.c and implemented in arch/x86/entry/entry_64.S
for 64-bit, arch/x86/entry/entry_32.S for 32-bit and finally
arch/x86/entry/entry_64_compat.S which implements the 32-bit compatibility
syscall entry points and thus provides for 32-bit processes the
ability to execute syscalls when running on 64-bit kernels.
The IDT vector assignments are listed in arch/x86/include/asm/irq_vectors.h.
Some of these entries are:
- system_call: syscall instruction from 64-bit code.
- entry_INT80_compat: int 0x80 from 32-bit or 64-bit code; compat syscall
either way.
- entry_INT80_compat, ia32_sysenter: syscall and sysenter from 32-bit
code
- interrupt: An array of entries. Every IDT vector that doesn't
explicitly point somewhere else gets set to the corresponding
value in interrupts. These point to a whole array of
magically-generated functions that make their way to common_interrupt()
with the interrupt number as a parameter.
- APIC interrupts: Various special-purpose interrupts for things
like TLB shootdown.
- Architecturally-defined exceptions like divide_error.
There are a few complexities here. The different x86-64 entries
have different calling conventions. The syscall and sysenter
instructions have their own peculiar calling conventions. Some of
the IDT entries push an error code onto the stack; others don't.
IDT entries using the IST alternative stack mechanism need their own
magic to get the stack frames right. (You can find some
documentation in the AMD APM, Volume 2, Chapter 8 and the Intel SDM,
Volume 3, Chapter 6.)
Dealing with the swapgs instruction is especially tricky. Swapgs
toggles whether gs is the kernel gs or the user gs. The swapgs
instruction is rather fragile: it must nest perfectly and only in
single depth, it should only be used if entering from user mode to
kernel mode and then when returning to user-space, and precisely
so. If we mess that up even slightly, we crash.
So when we have a secondary entry, already in kernel mode, we *must
not* use SWAPGS blindly - nor must we forget doing a SWAPGS when it's
not switched/swapped yet.
Now, there's a secondary complication: there's a cheap way to test
which mode the CPU is in and an expensive way.
The cheap way is to pick this info off the entry frame on the kernel
stack, from the CS of the ptregs area of the kernel stack::
xorl %ebx,%ebx
testl $3,CS+8(%rsp)
je error_kernelspace
SWAPGS
The expensive (paranoid) way is to read back the MSR_GS_BASE value
(which is what SWAPGS modifies)::
movl $1,%ebx
movl $MSR_GS_BASE,%ecx
rdmsr
testl %edx,%edx
js 1f /* negative -> in kernel */
SWAPGS
xorl %ebx,%ebx
1: ret
If we are at an interrupt or user-trap/gate-alike boundary then we can
use the faster check: the stack will be a reliable indicator of
whether SWAPGS was already done: if we see that we are a secondary
entry interrupting kernel mode execution, then we know that the GS
base has already been switched. If it says that we interrupted
user-space execution then we must do the SWAPGS.
But if we are in an NMI/MCE/DEBUG/whatever super-atomic entry context,
which might have triggered right after a normal entry wrote CS to the
stack but before we executed SWAPGS, then the only safe way to check
for GS is the slower method: the RDMSR.
Therefore, super-atomic entries (except NMI, which is handled separately)
must use idtentry with paranoid=1 to handle gsbase correctly. This
triggers three main behavior changes:
- Interrupt entry will use the slower gsbase check.
- Interrupt entry from user mode will switch off the IST stack.
- Interrupt exit to kernel mode will not attempt to reschedule.
We try to only use IST entries and the paranoid entry code for vectors
that absolutely need the more expensive check for the GS base - and we
generate all 'normal' entry points with the regular (faster) paranoid=0
variant.
@@ -0,0 +1,357 @@
.. SPDX-License-Identifier: GPL-2.0
===============================
Kernel level exception handling
===============================
Commentary by Joerg Pommnitz <joerg@raleigh.ibm.com>
When a process runs in kernel mode, it often has to access user
mode memory whose address has been passed by an untrusted program.
To protect itself the kernel has to verify this address.
In older versions of Linux this was done with the
int verify_area(int type, const void * addr, unsigned long size)
function (which has since been replaced by access_ok()).
This function verified that the memory area starting at address
'addr' and of size 'size' was accessible for the operation specified
in type (read or write). To do this, verify_read had to look up the
virtual memory area (vma) that contained the address addr. In the
normal case (correctly working program), this test was successful.
It only failed for a few buggy programs. In some kernel profiling
tests, this normally unneeded verification used up a considerable
amount of time.
To overcome this situation, Linus decided to let the virtual memory
hardware present in every Linux-capable CPU handle this test.
How does this work?
Whenever the kernel tries to access an address that is currently not
accessible, the CPU generates a page fault exception and calls the
page fault handler::
void exc_page_fault(struct pt_regs *regs, unsigned long error_code)
in arch/x86/mm/fault.c. The parameters on the stack are set up by
the low level assembly glue in arch/x86/entry/entry_32.S. The parameter
regs is a pointer to the saved registers on the stack, error_code
contains a reason code for the exception.
exc_page_fault() first obtains the inaccessible address from the CPU
control register CR2. If the address is within the virtual address
space of the process, the fault probably occurred, because the page
was not swapped in, write protected or something similar. However,
we are interested in the other case: the address is not valid, there
is no vma that contains this address. In this case, the kernel jumps
to the bad_area label.
There it uses the address of the instruction that caused the exception
(i.e. regs->eip) to find an address where the execution can continue
(fixup). If this search is successful, the fault handler modifies the
return address (again regs->eip) and returns. The execution will
continue at the address in fixup.
Where does fixup point to?
Since we jump to the contents of fixup, fixup obviously points
to executable code. This code is hidden inside the user access macros.
I have picked the get_user() macro defined in arch/x86/include/asm/uaccess.h
as an example. The definition is somewhat hard to follow, so let's peek at
the code generated by the preprocessor and the compiler. I selected
the get_user() call in drivers/char/sysrq.c for a detailed examination.
The original code in sysrq.c line 587::
get_user(c, buf);
The preprocessor output (edited to become somewhat readable)::
(
{
long __gu_err = - 14 , __gu_val = 0;
const __typeof__(*( ( buf ) )) *__gu_addr = ((buf));
if (((((0 + current_set[0])->tss.segment) == 0x18 ) ||
(((sizeof(*(buf))) <= 0xC0000000UL) &&
((unsigned long)(__gu_addr ) <= 0xC0000000UL - (sizeof(*(buf)))))))
do {
__gu_err = 0;
switch ((sizeof(*(buf)))) {
case 1:
__asm__ __volatile__(
"1: mov" "b" " %2,%" "b" "1\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3: movl %3,%0\n"
" xor" "b" " %" "b" "1,%" "b" "1\n"
" jmp 2b\n"
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 1b,3b\n"
".text" : "=r"(__gu_err), "=q" (__gu_val): "m"((*(struct __large_struct *)
( __gu_addr )) ), "i"(- 14 ), "0"( __gu_err )) ;
break;
case 2:
__asm__ __volatile__(
"1: mov" "w" " %2,%" "w" "1\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3: movl %3,%0\n"
" xor" "w" " %" "w" "1,%" "w" "1\n"
" jmp 2b\n"
".section __ex_table,\"a\"\n"
" .align 4\n"
" .long 1b,3b\n"
".text" : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
( __gu_addr )) ), "i"(- 14 ), "0"( __gu_err ));
break;
case 4:
__asm__ __volatile__(
"1: mov" "l" " %2,%" "" "1\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3: movl %3,%0\n"
" xor" "l" " %" "" "1,%" "" "1\n"
" jmp 2b\n"
".section __ex_table,\"a\"\n"
" .align 4\n" " .long 1b,3b\n"
".text" : "=r"(__gu_err), "=r" (__gu_val) : "m"((*(struct __large_struct *)
( __gu_addr )) ), "i"(- 14 ), "0"(__gu_err));
break;
default:
(__gu_val) = __get_user_bad();
}
} while (0) ;
((c)) = (__typeof__(*((buf))))__gu_val;
__gu_err;
}
);
WOW! Black GCC/assembly magic. This is impossible to follow, so let's
see what code gcc generates::
> xorl %edx,%edx
> movl current_set,%eax
> cmpl $24,788(%eax)
> je .L1424
> cmpl $-1073741825,64(%esp)
> ja .L1423
> .L1424:
> movl %edx,%eax
> movl 64(%esp),%ebx
> #APP
> 1: movb (%ebx),%dl /* this is the actual user access */
> 2:
> .section .fixup,"ax"
> 3: movl $-14,%eax
> xorb %dl,%dl
> jmp 2b
> .section __ex_table,"a"
> .align 4
> .long 1b,3b
> .text
> #NO_APP
> .L1423:
> movzbl %dl,%esi
The optimizer does a good job and gives us something we can actually
understand. Can we? The actual user access is quite obvious. Thanks
to the unified address space we can just access the address in user
memory. But what does the .section stuff do?????
To understand this we have to look at the final kernel::
> objdump --section-headers vmlinux
>
> vmlinux: file format elf32-i386
>
> Sections:
> Idx Name Size VMA LMA File off Algn
> 0 .text 00098f40 c0100000 c0100000 00001000 2**4
> CONTENTS, ALLOC, LOAD, READONLY, CODE
> 1 .fixup 000016bc c0198f40 c0198f40 00099f40 2**0
> CONTENTS, ALLOC, LOAD, READONLY, CODE
> 2 .rodata 0000f127 c019a5fc c019a5fc 0009b5fc 2**2
> CONTENTS, ALLOC, LOAD, READONLY, DATA
> 3 __ex_table 000015c0 c01a9724 c01a9724 000aa724 2**2
> CONTENTS, ALLOC, LOAD, READONLY, DATA
> 4 .data 0000ea58 c01abcf0 c01abcf0 000abcf0 2**4
> CONTENTS, ALLOC, LOAD, DATA
> 5 .bss 00018e21 c01ba748 c01ba748 000ba748 2**2
> ALLOC
> 6 .comment 00000ec4 00000000 00000000 000ba748 2**0
> CONTENTS, READONLY
> 7 .note 00001068 00000ec4 00000ec4 000bb60c 2**0
> CONTENTS, READONLY
There are obviously 2 non standard ELF sections in the generated object
file. But first we want to find out what happened to our code in the
final kernel executable::
> objdump --disassemble --section=.text vmlinux
>
> c017e785 <do_con_write+c1> xorl %edx,%edx
> c017e787 <do_con_write+c3> movl 0xc01c7bec,%eax
> c017e78c <do_con_write+c8> cmpl $0x18,0x314(%eax)
> c017e793 <do_con_write+cf> je c017e79f <do_con_write+db>
> c017e795 <do_con_write+d1> cmpl $0xbfffffff,0x40(%esp,1)
> c017e79d <do_con_write+d9> ja c017e7a7 <do_con_write+e3>
> c017e79f <do_con_write+db> movl %edx,%eax
> c017e7a1 <do_con_write+dd> movl 0x40(%esp,1),%ebx
> c017e7a5 <do_con_write+e1> movb (%ebx),%dl
> c017e7a7 <do_con_write+e3> movzbl %dl,%esi
The whole user memory access is reduced to 10 x86 machine instructions.
The instructions bracketed in the .section directives are no longer
in the normal execution path. They are located in a different section
of the executable file::
> objdump --disassemble --section=.fixup vmlinux
>
> c0199ff5 <.fixup+10b5> movl $0xfffffff2,%eax
> c0199ffa <.fixup+10ba> xorb %dl,%dl
> c0199ffc <.fixup+10bc> jmp c017e7a7 <do_con_write+e3>
And finally::
> objdump --full-contents --section=__ex_table vmlinux
>
> c01aa7c4 93c017c0 e09f19c0 97c017c0 99c017c0 ................
> c01aa7d4 f6c217c0 e99f19c0 a5e717c0 f59f19c0 ................
> c01aa7e4 080a18c0 01a019c0 0a0a18c0 04a019c0 ................
or in human readable byte order::
> c01aa7c4 c017c093 c0199fe0 c017c097 c017c099 ................
> c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5 ................
^^^^^^^^^^^^^^^^^
this is the interesting part!
> c01aa7e4 c0180a08 c019a001 c0180a0a c019a004 ................
What happened? The assembly directives::
.section .fixup,"ax"
.section __ex_table,"a"
told the assembler to move the following code to the specified
sections in the ELF object file. So the instructions::
3: movl $-14,%eax
xorb %dl,%dl
jmp 2b
ended up in the .fixup section of the object file and the addresses::
.long 1b,3b
ended up in the __ex_table section of the object file. 1b and 3b
are local labels. The local label 1b (1b stands for next label 1
backward) is the address of the instruction that might fault, i.e.
in our case the address of the label 1 is c017e7a5:
the original assembly code: > 1: movb (%ebx),%dl
and linked in vmlinux : > c017e7a5 <do_con_write+e1> movb (%ebx),%dl
The local label 3 (backwards again) is the address of the code to handle
the fault, in our case the actual value is c0199ff5:
the original assembly code: > 3: movl $-14,%eax
and linked in vmlinux : > c0199ff5 <.fixup+10b5> movl $0xfffffff2,%eax
If the fixup was able to handle the exception, control flow may be returned
to the instruction after the one that triggered the fault, ie. local label 2b.
The assembly code::
> .section __ex_table,"a"
> .align 4
> .long 1b,3b
becomes the value pair::
> c01aa7d4 c017c2f6 c0199fe9 c017e7a5 c0199ff5 ................
^this is ^this is
1b 3b
c017e7a5,c0199ff5 in the exception table of the kernel.
So, what actually happens if a fault from kernel mode with no suitable
vma occurs?
#. access to invalid address::
> c017e7a5 <do_con_write+e1> movb (%ebx),%dl
#. MMU generates exception
#. CPU calls exc_page_fault()
#. exc_page_fault() calls do_user_addr_fault()
#. do_user_addr_fault() calls kernelmode_fixup_or_oops()
#. kernelmode_fixup_or_oops() calls fixup_exception() (regs->eip == c017e7a5);
#. fixup_exception() calls search_exception_tables()
#. search_exception_tables() looks up the address c017e7a5 in the
exception table (i.e. the contents of the ELF section __ex_table)
and returns the address of the associated fault handle code c0199ff5.
#. fixup_exception() modifies its own return address to point to the fault
handle code and returns.
#. execution continues in the fault handling code.
#. a) EAX becomes -EFAULT (== -14)
b) DL becomes zero (the value we "read" from user space)
c) execution continues at local label 2 (address of the
instruction immediately after the faulting user access).
The steps a to c above in a certain way emulate the faulting instruction.
That's it, mostly. If you look at our example, you might ask why
we set EAX to -EFAULT in the exception handler code. Well, the
get_user() macro actually returns a value: 0, if the user access was
successful, -EFAULT on failure. Our original code did not test this
return value, however the inline assembly code in get_user() tries to
return -EFAULT. GCC selected EAX to return this value.
NOTE:
Due to the way that the exception table is built and needs to be ordered,
only use exceptions for code in the .text section. Any other section
will cause the exception table to not be sorted correctly, and the
exceptions will fail.
Things changed when 64-bit support was added to x86 Linux. Rather than
double the size of the exception table by expanding the two entries
from 32-bits to 64 bits, a clever trick was used to store addresses
as relative offsets from the table itself. The assembly code changed
from::
.long 1b,3b
to:
.long (from) - .
.long (to) - .
and the C-code that uses these values converts back to absolute addresses
like this::
ex_insn_addr(const struct exception_table_entry *x)
{
return (unsigned long)&x->insn + x->insn;
}
In v4.6 the exception table entry was expanded with a new field "handler".
This is also 32-bits wide and contains a third relative function
pointer which points to one of:
1) ``int ex_handler_default(const struct exception_table_entry *fixup)``
This is legacy case that just jumps to the fixup code
2) ``int ex_handler_fault(const struct exception_table_entry *fixup)``
This case provides the fault number of the trap that occurred at
entry->insn. It is used to distinguish page faults from machine
check.
More functions can easily be added.
CONFIG_BUILDTIME_TABLE_SORT allows the __ex_table section to be sorted post
link of the kernel image, via a host utility scripts/sorttable. It will set the
symbol main_extable_sort_needed to 0, avoiding sorting the __ex_table section
at boot time. With the exception table sorted, at runtime when an exception
occurs we can quickly lookup the __ex_table entry via binary search.
This is not just a boot time optimization, some architectures require this
table to be sorted in order to handle exceptions relatively early in the boot
process. For example, i386 makes use of this form of exception handling before
paging support is even enabled!
@@ -0,0 +1,3 @@
.. SPDX-License-Identifier: GPL-2.0
.. kernel-feat:: features x86
@@ -0,0 +1,123 @@
.. SPDX-License-Identifier: GPL-2.0
=======
IO-APIC
=======
:Author: Ingo Molnar <mingo@kernel.org>
Most (all) Intel-MP compliant SMP boards have the so-called 'IO-APIC',
which is an enhanced interrupt controller. It enables us to route
hardware interrupts to multiple CPUs, or to CPU groups. Without an
IO-APIC, interrupts from hardware will be delivered only to the
CPU which boots the operating system (usually CPU#0).
Linux supports all variants of compliant SMP boards, including ones with
multiple IO-APICs. Multiple IO-APICs are used in high-end servers to
distribute IRQ load further.
There are (a few) known breakages in certain older boards, such bugs are
usually worked around by the kernel. If your MP-compliant SMP board does
not boot Linux, then consult the linux-smp mailing list archives first.
If your box boots fine with enabled IO-APIC IRQs, then your
/proc/interrupts will look like this one::
hell:~> cat /proc/interrupts
CPU0
0: 1360293 IO-APIC-edge timer
1: 4 IO-APIC-edge keyboard
2: 0 XT-PIC cascade
13: 1 XT-PIC fpu
14: 1448 IO-APIC-edge ide0
16: 28232 IO-APIC-level Intel EtherExpress Pro 10/100 Ethernet
17: 51304 IO-APIC-level eth0
NMI: 0
ERR: 0
hell:~>
Some interrupts are still listed as 'XT PIC', but this is not a problem;
none of those IRQ sources is performance-critical.
In the unlikely case that your board does not create a working mp-table,
you can use the pirq= boot parameter to 'hand-construct' IRQ entries. This
is non-trivial though and cannot be automated. One sample /etc/lilo.conf
entry::
append="pirq=15,11,10"
The actual numbers depend on your system, on your PCI cards and on their
PCI slot position. Usually PCI slots are 'daisy chained' before they are
connected to the PCI chipset IRQ routing facility (the incoming PIRQ1-4
lines)::
,-. ,-. ,-. ,-. ,-.
PIRQ4 ----| |-. ,-| |-. ,-| |-. ,-| |--------| |
|S| \ / |S| \ / |S| \ / |S| |S|
PIRQ3 ----|l|-. `/---|l|-. `/---|l|-. `/---|l|--------|l|
|o| \/ |o| \/ |o| \/ |o| |o|
PIRQ2 ----|t|-./`----|t|-./`----|t|-./`----|t|--------|t|
|1| /\ |2| /\ |3| /\ |4| |5|
PIRQ1 ----| |- `----| |- `----| |- `----| |--------| |
`-' `-' `-' `-' `-'
Every PCI card emits a PCI IRQ, which can be INTA, INTB, INTC or INTD::
,-.
INTD--| |
|S|
INTC--|l|
|o|
INTB--|t|
|x|
INTA--| |
`-'
These INTA-D PCI IRQs are always 'local to the card', their real meaning
depends on which slot they are in. If you look at the daisy chaining diagram,
a card in slot4, issuing INTA IRQ, it will end up as a signal on PIRQ4 of
the PCI chipset. Most cards issue INTA, this creates optimal distribution
between the PIRQ lines. (distributing IRQ sources properly is not a
necessity, PCI IRQs can be shared at will, but it's a good for performance
to have non shared interrupts). Slot5 should be used for videocards, they
do not use interrupts normally, thus they are not daisy chained either.
so if you have your SCSI card (IRQ11) in Slot1, Tulip card (IRQ9) in
Slot2, then you'll have to specify this pirq= line::
append="pirq=11,9"
the following script tries to figure out such a default pirq= line from
your PCI configuration::
echo -n pirq=; echo `scanpci | grep T_L | cut -c56-` | sed 's/ /,/g'
note that this script won't work if you have skipped a few slots or if your
board does not do default daisy-chaining. (or the IO-APIC has the PIRQ pins
connected in some strange way). E.g. if in the above case you have your SCSI
card (IRQ11) in Slot3, and have Slot1 empty::
append="pirq=0,9,11"
[value '0' is a generic 'placeholder', reserved for empty (or non-IRQ emitting)
slots.]
Generally, it's always possible to find out the correct pirq= settings, just
permute all IRQ numbers properly ... it will take some time though. An
'incorrect' pirq line will cause the booting process to hang, or a device
won't function properly (e.g. if it's inserted as a module).
If you have 2 PCI buses, then you can use up to 8 pirq values, although such
boards tend to have a good configuration.
Be prepared that it might happen that you need some strange pirq line::
append="pirq=0,0,0,0,0,0,9,11"
Use smart trial-and-error techniques to find out the correct pirq line ...
Good luck and mail to linux-smp@vger.kernel.org or
linux-kernel@vger.kernel.org if you have any problems that are not covered
by this document.
@@ -0,0 +1,10 @@
.. SPDX-License-Identifier: GPL-2.0
============
i386 Support
============
.. toctree::
:maxdepth: 2
IO-APIC
@@ -0,0 +1,2 @@
.. SPDX-License-Identifier: GPL-2.0
.. kernel-doc:: drivers/platform/x86/intel/ifs/ifs.h
@@ -0,0 +1,46 @@
.. SPDX-License-Identifier: GPL-2.0
==========================
x86-specific Documentation
==========================
.. toctree::
:maxdepth: 2
:numbered:
boot
booting-dt
cpuinfo
topology
exception-tables
kernel-stacks
entry_64
earlyprintk
orc-unwinder
zero-page
tlb
mtrr
pat
intel-hfi
shstk
iommu
intel_txt
amd-debugging
amd-memory-encryption
amd_hsmp
amd-hfi
tdx
pti
mds
microcode
tsx_async_abort
buslock
usb-legacy-support
i386/index
x86_64/index
ifs
sva
sgx
features
elf_auxvec
xstate
@@ -0,0 +1,72 @@
.. SPDX-License-Identifier: GPL-2.0
============================================================
Hardware-Feedback Interface for scheduling on Intel Hardware
============================================================
Overview
--------
Intel has described the Hardware Feedback Interface (HFI) in the Intel 64 and
IA-32 Architectures Software Developer's Manual (Intel SDM) Volume 3 Section
14.6 [1]_.
The HFI gives the operating system a performance and energy efficiency
capability data for each CPU in the system. Linux can use the information from
the HFI to influence task placement decisions.
The Hardware Feedback Interface
-------------------------------
The Hardware Feedback Interface provides to the operating system information
about the performance and energy efficiency of each CPU in the system. Each
capability is given as a unit-less quantity in the range [0-255]. Higher values
indicate higher capability. Energy efficiency and performance are reported in
separate capabilities. Even though on some systems these two metrics may be
related, they are specified as independent capabilities in the Intel SDM.
These capabilities may change at runtime as a result of changes in the
operating conditions of the system or the action of external factors. The rate
at which these capabilities are updated is specific to each processor model. On
some models, capabilities are set at boot time and never change. On others,
capabilities may change every tens of milliseconds. For instance, a remote
mechanism may be used to lower Thermal Design Power. Such change can be
reflected in the HFI. Likewise, if the system needs to be throttled due to
excessive heat, the HFI may reflect reduced performance on specific CPUs.
The kernel or a userspace policy daemon can use these capabilities to modify
task placement decisions. For instance, if either the performance or energy
capabilities of a given logical processor becomes zero, it is an indication that
the hardware recommends to the operating system to not schedule any tasks on
that processor for performance or energy efficiency reasons, respectively.
Implementation details for Linux
--------------------------------
The infrastructure to handle thermal event interrupts has two parts. In the
Local Vector Table of a CPU's local APIC, there exists a register for the
Thermal Monitor Register. This register controls how interrupts are delivered
to a CPU when the thermal monitor generates and interrupt. Further details
can be found in the Intel SDM Vol. 3 Section 10.5 [1]_.
The thermal monitor may generate interrupts per CPU or per package. The HFI
generates package-level interrupts. This monitor is configured and initialized
via a set of machine-specific registers. Specifically, the HFI interrupt and
status are controlled via designated bits in the IA32_PACKAGE_THERM_INTERRUPT
and IA32_PACKAGE_THERM_STATUS registers, respectively. There exists one HFI
table per package. Further details can be found in the Intel SDM Vol. 3
Section 14.9 [1]_.
The hardware issues an HFI interrupt after updating the HFI table and is ready
for the operating system to consume it. CPUs receive such interrupt via the
thermal entry in the Local APIC's Local Vector Table.
When servicing such interrupt, the HFI driver parses the updated table and
relays the update to userspace using the thermal notification framework. Given
that there may be many HFI updates every second, the updates relayed to
userspace are throttled at a rate of CONFIG_HZ jiffies.
References
----------
.. [1] https://www.intel.com/sdm
@@ -0,0 +1,227 @@
=====================
Intel(R) TXT Overview
=====================
Intel's technology for safer computing, Intel(R) Trusted Execution
Technology (Intel(R) TXT), defines platform-level enhancements that
provide the building blocks for creating trusted platforms.
Intel TXT was formerly known by the code name LaGrande Technology (LT).
Intel TXT in Brief:
- Provides dynamic root of trust for measurement (DRTM)
- Data protection in case of improper shutdown
- Measurement and verification of launched environment
Intel TXT is part of the vPro(TM) brand and is also available some
non-vPro systems. It is currently available on desktop systems
based on the Q35, X38, Q45, and Q43 Express chipsets (e.g. Dell
Optiplex 755, HP dc7800, etc.) and mobile systems based on the GM45,
PM45, and GS45 Express chipsets.
For more information, see http://www.intel.com/technology/security/.
This site also has a link to the Intel TXT MLE Developers Manual,
which has been updated for the new released platforms.
Intel TXT has been presented at various events over the past few
years, some of which are:
- LinuxTAG 2008:
http://www.linuxtag.org/2008/en/conf/events/vp-donnerstag.html
- TRUST2008:
http://www.trust-conference.eu/downloads/Keynote-Speakers/
3_David-Grawrock_The-Front-Door-of-Trusted-Computing.pdf
- IDF, Shanghai:
http://www.prcidf.com.cn/index_en.html
- IDFs 2006, 2007
(I'm not sure if/where they are online)
Trusted Boot Project Overview
=============================
Trusted Boot (tboot) is an open source, pre-kernel/VMM module that
uses Intel TXT to perform a measured and verified launch of an OS
kernel/VMM.
It is hosted on SourceForge at http://sourceforge.net/projects/tboot.
The mercurial source repo is available at http://www.bughost.org/
repos.hg/tboot.hg.
Tboot currently supports launching Xen (open source VMM/hypervisor
w/ TXT support since v3.2), and now Linux kernels.
Value Proposition for Linux or "Why should you care?"
=====================================================
While there are many products and technologies that attempt to
measure or protect the integrity of a running kernel, they all
assume the kernel is "good" to begin with. The Integrity
Measurement Architecture (IMA) and Linux Integrity Module interface
are examples of such solutions.
To get trust in the initial kernel without using Intel TXT, a
static root of trust must be used. This bases trust in BIOS
starting at system reset and requires measurement of all code
executed between system reset through the completion of the kernel
boot as well as data objects used by that code. In the case of a
Linux kernel, this means all of BIOS, any option ROMs, the
bootloader and the boot config. In practice, this is a lot of
code/data, much of which is subject to change from boot to boot
(e.g. changing NICs may change option ROMs). Without reference
hashes, these measurement changes are difficult to assess or
confirm as benign. This process also does not provide DMA
protection, memory configuration/alias checks and locks, crash
protection, or policy support.
By using the hardware-based root of trust that Intel TXT provides,
many of these issues can be mitigated. Specifically: many
pre-launch components can be removed from the trust chain, DMA
protection is provided to all launched components, a large number
of platform configuration checks are performed and values locked,
protection is provided for any data in the event of an improper
shutdown, and there is support for policy-based execution/verification.
This provides a more stable measurement and a higher assurance of
system configuration and initial state than would be otherwise
possible. Since the tboot project is open source, source code for
almost all parts of the trust chain is available (excepting SMM and
Intel-provided firmware).
How Does it Work?
=================
- Tboot is an executable that is launched by the bootloader as
the "kernel" (the binary the bootloader executes).
- It performs all of the work necessary to determine if the
platform supports Intel TXT and, if so, executes the GETSEC[SENTER]
processor instruction that initiates the dynamic root of trust.
- If tboot determines that the system does not support Intel TXT
or is not configured correctly (e.g. the SINIT AC Module was
incorrect), it will directly launch the kernel with no changes
to any state.
- Tboot will output various information about its progress to the
terminal, serial port, and/or an in-memory log; the output
locations can be configured with a command line switch.
- The GETSEC[SENTER] instruction will return control to tboot and
tboot then verifies certain aspects of the environment (e.g. TPM NV
lock, e820 table does not have invalid entries, etc.).
- It will wake the APs from the special sleep state the GETSEC[SENTER]
instruction had put them in and place them into a wait-for-SIPI
state.
- Because the processors will not respond to an INIT or SIPI when
in the TXT environment, it is necessary to create a small VT-x
guest for the APs. When they run in this guest, they will
simply wait for the INIT-SIPI-SIPI sequence, which will cause
VMEXITs, and then disable VT and jump to the SIPI vector. This
approach seemed like a better choice than having to insert
special code into the kernel's MP wakeup sequence.
- Tboot then applies an (optional) user-defined launch policy to
verify the kernel and initrd.
- This policy is rooted in TPM NV and is described in the tboot
project. The tboot project also contains code for tools to
create and provision the policy.
- Policies are completely under user control and if not present
then any kernel will be launched.
- Policy action is flexible and can include halting on failures
or simply logging them and continuing.
- Tboot adjusts the e820 table provided by the bootloader to reserve
its own location in memory as well as to reserve certain other
TXT-related regions.
- As part of its launch, tboot DMA protects all of RAM (using the
VT-d PMRs). Thus, the kernel must be booted with 'intel_iommu=on'
in order to remove this blanket protection and use VT-d's
page-level protection.
- Tboot will populate a shared page with some data about itself and
pass this to the Linux kernel as it transfers control.
- The location of the shared page is passed via the boot_params
struct as a physical address.
- The kernel will look for the tboot shared page address and, if it
exists, map it.
- As one of the checks/protections provided by TXT, it makes a copy
of the VT-d DMARs in a DMA-protected region of memory and verifies
them for correctness. The VT-d code will detect if the kernel was
launched with tboot and use this copy instead of the one in the
ACPI table.
- At this point, tboot and TXT are out of the picture until a
shutdown (S<n>)
- In order to put a system into any of the sleep states after a TXT
launch, TXT must first be exited. This is to prevent attacks that
attempt to crash the system to gain control on reboot and steal
data left in memory.
- The kernel will perform all of its sleep preparation and
populate the shared page with the ACPI data needed to put the
platform in the desired sleep state.
- Then the kernel jumps into tboot via the vector specified in the
shared page.
- Tboot will clean up the environment and disable TXT, then use the
kernel-provided ACPI information to actually place the platform
into the desired sleep state.
- In the case of S3, tboot will also register itself as the resume
vector. This is necessary because it must re-establish the
measured environment upon resume. Once the TXT environment
has been restored, it will restore the TPM PCRs and then
transfer control back to the kernel's S3 resume vector.
In order to preserve system integrity across S3, the kernel
provides tboot with a set of memory ranges (RAM and RESERVED_KERN
in the e820 table, but not any memory that BIOS might alter over
the S3 transition) that tboot will calculate a MAC (message
authentication code) over and then seal with the TPM. On resume
and once the measured environment has been re-established, tboot
will re-calculate the MAC and verify it against the sealed value.
Tboot's policy determines what happens if the verification fails.
Note that the c/s 194 of tboot which has the new MAC code supports
this.
That's pretty much it for TXT support.
Configuring the System
======================
This code works with 32bit, 32bit PAE, and 64bit (x86_64) kernels.
In BIOS, the user must enable: TPM, TXT, VT-x, VT-d. Not all BIOSes
allow these to be individually enabled/disabled and the screens in
which to find them are BIOS-specific.
grub.conf needs to be modified as follows::
title Linux 2.6.29-tip w/ tboot
root (hd0,0)
kernel /tboot.gz logging=serial,vga,memory
module /vmlinuz-2.6.29-tip intel_iommu=on ro
root=LABEL=/ rhgb console=ttyS0,115200 3
module /initrd-2.6.29-tip.img
module /Q35_SINIT_17.BIN
The kernel option for enabling Intel TXT support is found under the
Security top-level menu and is called "Enable Intel(R) Trusted
Execution Technology (TXT)". It is considered EXPERIMENTAL and
depends on the generic x86 support (to allow maximum flexibility in
kernel build options), since the tboot code will detect whether the
platform actually supports Intel TXT and thus whether any of the
kernel code is executed.
The Q35_SINIT_17.BIN file is what Intel TXT refers to as an
Authenticated Code Module. It is specific to the chipset in the
system and can also be found on the Trusted Boot site. It is an
(unencrypted) module signed by Intel that is used as part of the
DRTM process to verify and configure the system. It is signed
because it operates at a higher privilege level in the system than
any other macrocode and its correct operation is critical to the
establishment of the DRTM. The process for determining the correct
SINIT ACM for a system is documented in the SINIT-guide.txt file
that is on the tboot SourceForge site under the SINIT ACM downloads.
@@ -0,0 +1,152 @@
=================
x86 IOMMU Support
=================
The architecture specs can be obtained from the vendor websites.
Search for the following documents to obtain the latest versions:
- Intel: Intel Virtualization Technology for Directed I/O Architecture Specification (ID: D51397)
- AMD: AMD I/O Virtualization Technology (IOMMU) Specification (ID: 48882)
This guide gives a quick cheat sheet for some basic understanding.
Basic stuff
-----------
ACPI enumerates and lists the different IOMMUs on the platform, and
device scope relationships between devices and which IOMMU controls
them.
Some ACPI Keywords:
- DMAR - Intel DMA Remapping table
- DRHD - Intel DMA Remapping Hardware Unit Definition
- RMRR - Intel Reserved Memory Region Reporting Structure
- IVRS - AMD I/O Virtualization Reporting Structure
- IVDB - AMD I/O Virtualization Definition Block
- IVHD - AMD I/O Virtualization Hardware Definition
What is Intel RMRR?
^^^^^^^^^^^^^^^^^^^
There are some devices the BIOS controls, for e.g USB devices to perform
PS2 emulation. The regions of memory used for these devices are marked
reserved in the e820 map. When we turn on DMA translation, DMA to those
regions will fail. Hence BIOS uses RMRR to specify these regions along with
devices that need to access these regions. OS is expected to setup
unity mappings for these regions for these devices to access these regions.
What is AMD IVRS?
^^^^^^^^^^^^^^^^^
The architecture defines an ACPI-compatible data structure called an I/O
Virtualization Reporting Structure (IVRS) that is used to convey information
related to I/O virtualization to system software. The IVRS describes the
configuration and capabilities of the IOMMUs contained in the platform as
well as information about the devices that each IOMMU virtualizes.
The IVRS provides information about the following:
- IOMMUs present in the platform including their capabilities and proper configuration
- System I/O topology relevant to each IOMMU
- Peripheral devices that cannot be otherwise enumerated
- Memory regions used by SMI/SMM, platform firmware, and platform hardware. These are generally exclusion ranges to be configured by system software.
How is an I/O Virtual Address (IOVA) generated?
-----------------------------------------------
Well behaved drivers call dma_map_*() calls before sending command to device
that needs to perform DMA. Once DMA is completed and mapping is no longer
required, driver performs dma_unmap_*() calls to unmap the region.
Intel Specific Notes
--------------------
Graphics Problems?
^^^^^^^^^^^^^^^^^^
If you encounter issues with graphics devices, you can try adding
option intel_iommu=igfx_off to turn off the integrated graphics engine.
If this fixes anything, please ensure you file a bug reporting the problem.
Some exceptions to IOVA
^^^^^^^^^^^^^^^^^^^^^^^
Interrupt ranges are not address translated, (0xfee00000 - 0xfeefffff).
The same is true for peer to peer transactions. Hence we reserve the
address from PCI MMIO ranges so they are not allocated for IOVA addresses.
AMD Specific Notes
------------------
Graphics Problems?
^^^^^^^^^^^^^^^^^^
If you encounter issues with integrated graphics devices, you can try adding
option iommu=pt to the kernel command line use a 1:1 mapping for the IOMMU. If
this fixes anything, please ensure you file a bug reporting the problem.
Fault reporting
---------------
When errors are reported, the IOMMU signals via an interrupt. The fault
reason and device that caused it is printed on the console.
Kernel Log Samples
------------------
Intel Boot Messages
^^^^^^^^^^^^^^^^^^^
Something like this gets printed indicating presence of DMAR tables
in ACPI:
::
ACPI: DMAR (v001 A M I OEMDMAR 0x00000001 MSFT 0x00000097) @ 0x000000007f5b5ef0
When DMAR is being processed and initialized by ACPI, prints DMAR locations
and any RMRR's processed:
::
ACPI DMAR:Host address width 36
ACPI DMAR:DRHD (flags: 0x00000000)base: 0x00000000fed90000
ACPI DMAR:DRHD (flags: 0x00000000)base: 0x00000000fed91000
ACPI DMAR:DRHD (flags: 0x00000001)base: 0x00000000fed93000
ACPI DMAR:RMRR base: 0x00000000000ed000 end: 0x00000000000effff
ACPI DMAR:RMRR base: 0x000000007f600000 end: 0x000000007fffffff
When DMAR is enabled for use, you will notice:
::
PCI-DMA: Using DMAR IOMMU
Intel Fault reporting
^^^^^^^^^^^^^^^^^^^^^
::
DMAR:[DMA Write] Request device [00:02.0] fault addr 6df084000
DMAR:[fault reason 05] PTE Write access is not set
DMAR:[DMA Write] Request device [00:02.0] fault addr 6df084000
DMAR:[fault reason 05] PTE Write access is not set
AMD Boot Messages
^^^^^^^^^^^^^^^^^
Something like this gets printed indicating presence of the IOMMU:
::
iommu: Default domain type: Translated
iommu: DMA domain TLB invalidation policy: lazy mode
AMD Fault reporting
^^^^^^^^^^^^^^^^^^^
::
AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x0007 address=0xffffc02000 flags=0x0000]
AMD-Vi: Event logged [IO_PAGE_FAULT device=07:00.0 domain=0x0007 address=0xffffc02000 flags=0x0000]
@@ -0,0 +1,152 @@
.. SPDX-License-Identifier: GPL-2.0
=============
Kernel Stacks
=============
Kernel stacks on x86-64 bit
===========================
Most of the text from Keith Owens, hacked by AK
x86_64 page size (PAGE_SIZE) is 4K.
Like all other architectures, x86_64 has a kernel stack for every
active thread. These thread stacks are THREAD_SIZE (4*PAGE_SIZE) big.
These stacks contain useful data as long as a thread is alive or a
zombie. While the thread is in user space the kernel stack is empty
except for the thread_info structure at the bottom.
In addition to the per thread stacks, there are specialized stacks
associated with each CPU. These stacks are only used while the kernel
is in control on that CPU; when a CPU returns to user space the
specialized stacks contain no useful data. The main CPU stacks are:
* Interrupt stack. IRQ_STACK_SIZE
Used for external hardware interrupts. If this is the first external
hardware interrupt (i.e. not a nested hardware interrupt) then the
kernel switches from the current task to the interrupt stack. Like
the split thread and interrupt stacks on i386, this gives more room
for kernel interrupt processing without having to increase the size
of every per thread stack.
The interrupt stack is also used when processing a softirq.
Switching to the kernel interrupt stack is done by software based on a
per CPU interrupt nest counter. This is needed because x86-64 "IST"
hardware stacks cannot nest without races.
x86_64 also has a feature which is not available on i386, the ability
to automatically switch to a new stack for designated events such as
double fault or NMI, which makes it easier to handle these unusual
events on x86_64. This feature is called the Interrupt Stack Table
(IST). There can be up to 7 IST entries per CPU. The IST code is an
index into the Task State Segment (TSS). The IST entries in the TSS
point to dedicated stacks; each stack can be a different size.
An IST is selected by a non-zero value in the IST field of an
interrupt-gate descriptor. When an interrupt occurs and the hardware
loads such a descriptor, the hardware automatically sets the new stack
pointer based on the IST value, then invokes the interrupt handler. If
the interrupt came from user mode, then the interrupt handler prologue
will switch back to the per-thread stack. If software wants to allow
nested IST interrupts then the handler must adjust the IST values on
entry to and exit from the interrupt handler. (This is occasionally
done, e.g. for debug exceptions.)
Events with different IST codes (i.e. with different stacks) can be
nested. For example, a debug interrupt can safely be interrupted by an
NMI. arch/x86_64/kernel/entry.S::paranoidentry adjusts the stack
pointers on entry to and exit from all IST events, in theory allowing
IST events with the same code to be nested. However in most cases, the
stack size allocated to an IST assumes no nesting for the same code.
If that assumption is ever broken then the stacks will become corrupt.
The currently assigned IST stacks are:
* ESTACK_DF. EXCEPTION_STKSZ (PAGE_SIZE).
Used for interrupt 8 - Double Fault Exception (#DF).
Invoked when handling one exception causes another exception. Happens
when the kernel is very confused (e.g. kernel stack pointer corrupt).
Using a separate stack allows the kernel to recover from it well enough
in many cases to still output an oops.
* ESTACK_NMI. EXCEPTION_STKSZ (PAGE_SIZE).
Used for non-maskable interrupts (NMI).
NMI can be delivered at any time, including when the kernel is in the
middle of switching stacks. Using IST for NMI events avoids making
assumptions about the previous state of the kernel stack.
* ESTACK_DB. EXCEPTION_STKSZ (PAGE_SIZE).
Used for hardware debug interrupts (interrupt 1) and for software
debug interrupts (INT3).
When debugging a kernel, debug interrupts (both hardware and
software) can occur at any time. Using IST for these interrupts
avoids making assumptions about the previous state of the kernel
stack.
To handle nested #DB correctly there exist two instances of DB stacks. On
#DB entry the IST stackpointer for #DB is switched to the second instance
so a nested #DB starts from a clean stack. The nested #DB switches
the IST stackpointer to a guard hole to catch triple nesting.
* ESTACK_MCE. EXCEPTION_STKSZ (PAGE_SIZE).
Used for interrupt 18 - Machine Check Exception (#MC).
MCE can be delivered at any time, including when the kernel is in the
middle of switching stacks. Using IST for MCE events avoids making
assumptions about the previous state of the kernel stack.
For more details see the Intel IA32 or AMD AMD64 architecture manuals.
Printing backtraces on x86
==========================
The question about the '?' preceding function names in an x86 stacktrace
keeps popping up, here's an indepth explanation. It helps if the reader
stares at print_context_stack() and the whole machinery in and around
arch/x86/kernel/dumpstack.c.
Adapted from Ingo's mail, Message-ID: <20150521101614.GA10889@gmail.com>:
We always scan the full kernel stack for return addresses stored on
the kernel stack(s) [1]_, from stack top to stack bottom, and print out
anything that 'looks like' a kernel text address.
If it fits into the frame pointer chain, we print it without a question
mark, knowing that it's part of the real backtrace.
If the address does not fit into our expected frame pointer chain we
still print it, but we print a '?'. It can mean two things:
- either the address is not part of the call chain: it's just stale
values on the kernel stack, from earlier function calls. This is
the common case.
- or it is part of the call chain, but the frame pointer was not set
up properly within the function, so we don't recognize it.
This way we will always print out the real call chain (plus a few more
entries), regardless of whether the frame pointer was set up correctly
or not - but in most cases we'll get the call chain right as well. The
entries printed are strictly in stack order, so you can deduce more
information from that as well.
The most important property of this method is that we _never_ lose
information: we always strive to print _all_ addresses on the stack(s)
that look like kernel text addresses, so if debug information is wrong,
we still print out the real call chain as well - just with more question
marks than ideal.
.. [1] For things like IRQ and IST stacks, we also scan those stacks, in
the right order, and try to cross from one stack into another
reconstructing the call chain. This works most of the time.
+209
View File
@@ -0,0 +1,209 @@
Microarchitectural Data Sampling (MDS) mitigation
=================================================
.. _mds:
Overview
--------
Microarchitectural Data Sampling (MDS) is a family of side channel attacks
on internal buffers in Intel CPUs. The variants are:
- Microarchitectural Store Buffer Data Sampling (MSBDS) (CVE-2018-12126)
- Microarchitectural Fill Buffer Data Sampling (MFBDS) (CVE-2018-12130)
- Microarchitectural Load Port Data Sampling (MLPDS) (CVE-2018-12127)
- Microarchitectural Data Sampling Uncacheable Memory (MDSUM) (CVE-2019-11091)
MSBDS leaks Store Buffer Entries which can be speculatively forwarded to a
dependent load (store-to-load forwarding) as an optimization. The forward
can also happen to a faulting or assisting load operation for a different
memory address, which can be exploited under certain conditions. Store
buffers are partitioned between Hyper-Threads so cross thread forwarding is
not possible. But if a thread enters or exits a sleep state the store
buffer is repartitioned which can expose data from one thread to the other.
MFBDS leaks Fill Buffer Entries. Fill buffers are used internally to manage
L1 miss situations and to hold data which is returned or sent in response
to a memory or I/O operation. Fill buffers can forward data to a load
operation and also write data to the cache. When the fill buffer is
deallocated it can retain the stale data of the preceding operations which
can then be forwarded to a faulting or assisting load operation, which can
be exploited under certain conditions. Fill buffers are shared between
Hyper-Threads so cross thread leakage is possible.
MLPDS leaks Load Port Data. Load ports are used to perform load operations
from memory or I/O. The received data is then forwarded to the register
file or a subsequent operation. In some implementations the Load Port can
contain stale data from a previous operation which can be forwarded to
faulting or assisting loads under certain conditions, which again can be
exploited eventually. Load ports are shared between Hyper-Threads so cross
thread leakage is possible.
MDSUM is a special case of MSBDS, MFBDS and MLPDS. An uncacheable load from
memory that takes a fault or assist can leave data in a microarchitectural
structure that may later be observed using one of the same methods used by
MSBDS, MFBDS or MLPDS.
Exposure assumptions
--------------------
It is assumed that attack code resides in user space or in a guest with one
exception. The rationale behind this assumption is that the code construct
needed for exploiting MDS requires:
- to control the load to trigger a fault or assist
- to have a disclosure gadget which exposes the speculatively accessed
data for consumption through a side channel.
- to control the pointer through which the disclosure gadget exposes the
data
The existence of such a construct in the kernel cannot be excluded with
100% certainty, but the complexity involved makes it extremely unlikely.
There is one exception, which is untrusted BPF. The functionality of
untrusted BPF is limited, but it needs to be thoroughly investigated
whether it can be used to create such a construct.
Mitigation strategy
-------------------
All variants have the same mitigation strategy at least for the single CPU
thread case (SMT off): Force the CPU to clear the affected buffers.
This is achieved by using the otherwise unused and obsolete VERW
instruction in combination with a microcode update. The microcode clears
the affected CPU buffers when the VERW instruction is executed.
For virtualization there are two ways to achieve CPU buffer
clearing. Either the modified VERW instruction or via the L1D Flush
command. The latter is issued when L1TF mitigation is enabled so the extra
VERW can be avoided. If the CPU is not affected by L1TF then VERW needs to
be issued.
If the VERW instruction with the supplied segment selector argument is
executed on a CPU without the microcode update there is no side effect
other than a small number of pointlessly wasted CPU cycles.
This does not protect against cross Hyper-Thread attacks except for MSBDS
which is only exploitable cross Hyper-thread when one of the Hyper-Threads
enters a C-state.
The kernel provides a function to invoke the buffer clearing:
x86_clear_cpu_buffers()
Also macro CLEAR_CPU_BUFFERS can be used in ASM late in exit-to-user path.
Other than CFLAGS.ZF, this macro doesn't clobber any registers.
The mitigation is invoked on kernel/userspace, hypervisor/guest and C-state
(idle) transitions.
As a special quirk to address virtualization scenarios where the host has
the microcode updated, but the hypervisor does not (yet) expose the
MD_CLEAR CPUID bit to guests, the kernel issues the VERW instruction in the
hope that it might actually clear the buffers. The state is reflected
accordingly.
According to current knowledge additional mitigations inside the kernel
itself are not required because the necessary gadgets to expose the leaked
data cannot be controlled in a way which allows exploitation from malicious
user space or VM guests.
Kernel internal mitigation modes
--------------------------------
======= ============================================================
off Mitigation is disabled. Either the CPU is not affected or
mds=off is supplied on the kernel command line
full Mitigation is enabled. CPU is affected and MD_CLEAR is
advertised in CPUID.
vmwerv Mitigation is enabled. CPU is affected and MD_CLEAR is not
advertised in CPUID. That is mainly for virtualization
scenarios where the host has the updated microcode but the
hypervisor does not expose MD_CLEAR in CPUID. It's a best
effort approach without guarantee.
======= ============================================================
If the CPU is affected and mds=off is not supplied on the kernel command
line then the kernel selects the appropriate mitigation mode depending on
the availability of the MD_CLEAR CPUID bit.
Mitigation points
-----------------
1. Return to user space
^^^^^^^^^^^^^^^^^^^^^^^
When transitioning from kernel to user space the CPU buffers are flushed
on affected CPUs when the mitigation is not disabled on the kernel
command line. The mitigation is enabled through the feature flag
X86_FEATURE_CLEAR_CPU_BUF.
The mitigation is invoked just before transitioning to userspace after
user registers are restored. This is done to minimize the window in
which kernel data could be accessed after VERW e.g. via an NMI after
VERW.
**Corner case not handled**
Interrupts returning to kernel don't clear CPUs buffers since the
exit-to-user path is expected to do that anyways. But, there could be
a case when an NMI is generated in kernel after the exit-to-user path
has cleared the buffers. This case is not handled and NMI returning to
kernel don't clear CPU buffers because:
1. It is rare to get an NMI after VERW, but before returning to userspace.
2. For an unprivileged user, there is no known way to make that NMI
less rare or target it.
3. It would take a large number of these precisely-timed NMIs to mount
an actual attack. There's presumably not enough bandwidth.
4. The NMI in question occurs after a VERW, i.e. when user state is
restored and most interesting data is already scrubbed. What's left
is only the data that NMI touches, and that may or may not be of
any interest.
2. C-State transition
^^^^^^^^^^^^^^^^^^^^^
When a CPU goes idle and enters a C-State the CPU buffers need to be
cleared on affected CPUs when SMT is active. This addresses the
repartitioning of the store buffer when one of the Hyper-Threads enters
a C-State.
When SMT is inactive, i.e. either the CPU does not support it or all
sibling threads are offline CPU buffer clearing is not required.
The idle clearing is enabled on CPUs which are only affected by MSBDS
and not by any other MDS variant. The other MDS variants cannot be
protected against cross Hyper-Thread attacks because the Fill Buffer and
the Load Ports are shared. So on CPUs affected by other variants, the
idle clearing would be a window dressing exercise and is therefore not
activated.
The invocation is controlled by the static key cpu_buf_idle_clear which is
switched depending on the chosen mitigation mode and the SMT state of the
system.
The buffer clear is only invoked before entering the C-State to prevent
that stale data from the idling CPU from spilling to the Hyper-Thread
sibling after the store buffer got repartitioned and all entries are
available to the non idle sibling.
When coming out of idle the store buffer is partitioned again so each
sibling has half of it available. The back from idle CPU could be then
speculatively exposed to contents of the sibling. The buffers are
flushed either on exit to user space or on VMENTER so malicious code
in user space or the guest cannot speculatively access them.
The mitigation is hooked into all variants of halt()/mwait(), but does
not cover the legacy ACPI IO-Port mechanism because the ACPI idle driver
has been superseded by the intel_idle driver around 2010 and is
preferred on all affected CPUs which are expected to gain the MD_CLEAR
functionality in microcode. Aside of that the IO-Port mechanism is a
legacy interface which is only used on older systems which are either
not affected or do not receive microcode updates anymore.
@@ -0,0 +1,240 @@
.. SPDX-License-Identifier: GPL-2.0
==========================
The Linux Microcode Loader
==========================
:Authors: - Fenghua Yu <fenghua.yu@intel.com>
- Borislav Petkov <bp@suse.de>
- Ashok Raj <ashok.raj@intel.com>
The kernel has a x86 microcode loading facility which is supposed to
provide microcode loading methods in the OS. Potential use cases are
updating the microcode on platforms beyond the OEM End-Of-Life support,
and updating the microcode on long-running systems without rebooting.
The loader supports three loading methods:
Early load microcode
====================
The kernel can update microcode very early during boot. Loading
microcode early can fix CPU issues before they are observed during
kernel boot time.
The microcode is stored in an initrd file. During boot, it is read from
it and loaded into the CPU cores.
The format of the combined initrd image is microcode in (uncompressed)
cpio format followed by the (possibly compressed) initrd image. The
loader parses the combined initrd image during boot.
The microcode files in cpio name space are:
on Intel:
kernel/x86/microcode/GenuineIntel.bin
on AMD :
kernel/x86/microcode/AuthenticAMD.bin
During BSP (BootStrapping Processor) boot (pre-SMP), the kernel
scans the microcode file in the initrd. If microcode matching the
CPU is found, it will be applied in the BSP and later on in all APs
(Application Processors).
The loader also saves the matching microcode for the CPU in memory.
Thus, the cached microcode patch is applied when CPUs resume from a
sleep state.
Here's a crude example how to prepare an initrd with microcode (this is
normally done automatically by the distribution, when recreating the
initrd, so you don't really have to do it yourself. It is documented
here for future reference only).
::
#!/bin/bash
if [ -z "$1" ]; then
echo "You need to supply an initrd file"
exit 1
fi
INITRD="$1"
DSTDIR=kernel/x86/microcode
TMPDIR=/tmp/initrd
rm -rf $TMPDIR
mkdir $TMPDIR
cd $TMPDIR
mkdir -p $DSTDIR
if [ -d /lib/firmware/amd-ucode ]; then
cat /lib/firmware/amd-ucode/microcode_amd*.bin > $DSTDIR/AuthenticAMD.bin
fi
if [ -d /lib/firmware/intel-ucode ]; then
cat /lib/firmware/intel-ucode/* > $DSTDIR/GenuineIntel.bin
fi
find . | cpio -o -H newc >../ucode.cpio
cd ..
mv $INITRD $INITRD.orig
cat ucode.cpio $INITRD.orig > $INITRD
rm -rf $TMPDIR
The system needs to have the microcode packages installed into
/lib/firmware or you need to fixup the paths above if yours are
somewhere else and/or you've downloaded them directly from the processor
vendor's site.
Late loading
============
You simply install the microcode packages your distro supplies and
run::
# echo 1 > /sys/devices/system/cpu/microcode/reload
as root.
The loading mechanism looks for microcode blobs in
/lib/firmware/{intel-ucode,amd-ucode}. The default distro installation
packages already put them there.
Since kernel 5.19, late loading is not enabled by default.
The /dev/cpu/microcode method has been removed in 5.19.
Why is late loading dangerous?
==============================
Synchronizing all CPUs
----------------------
The microcode engine which receives the microcode update is shared
between the two logical threads in a SMT system. Therefore, when
the update is executed on one SMT thread of the core, the sibling
"automatically" gets the update.
Since the microcode can "simulate" MSRs too, while the microcode update
is in progress, those simulated MSRs transiently cease to exist. This
can result in unpredictable results if the SMT sibling thread happens to
be in the middle of an access to such an MSR. The usual observation is
that such MSR accesses cause #GPs to be raised to signal that former are
not present.
The disappearing MSRs are just one common issue which is being observed.
Any other instruction that's being patched and gets concurrently
executed by the other SMT sibling, can also result in similar,
unpredictable behavior.
To eliminate this case, a stop_machine()-based CPU synchronization was
introduced as a way to guarantee that all logical CPUs will not execute
any code but just wait in a spin loop, polling an atomic variable.
While this took care of device or external interrupts, IPIs including
LVT ones, such as CMCI etc, it cannot address other special interrupts
that can't be shut off. Those are Machine Check (#MC), System Management
(#SMI) and Non-Maskable interrupts (#NMI).
Machine Checks
--------------
Machine Checks (#MC) are non-maskable. There are two kinds of MCEs.
Fatal un-recoverable MCEs and recoverable MCEs. While un-recoverable
errors are fatal, recoverable errors can also happen in kernel context
are also treated as fatal by the kernel.
On certain Intel machines, MCEs are also broadcast to all threads in a
system. If one thread is in the middle of executing WRMSR, a MCE will be
taken at the end of the flow. Either way, they will wait for the thread
performing the wrmsr(0x79) to rendezvous in the MCE handler and shutdown
eventually if any of the threads in the system fail to check in to the
MCE rendezvous.
To be paranoid and get predictable behavior, the OS can choose to set
MCG_STATUS.MCIP. Since MCEs can be at most one in a system, if an
MCE was signaled, the above condition will promote to a system reset
automatically. OS can turn off MCIP at the end of the update for that
core.
System Management Interrupt
---------------------------
SMIs are also broadcast to all CPUs in the platform. Microcode update
requests exclusive access to the core before writing to MSR 0x79. So if
it does happen such that, one thread is in WRMSR flow, and the 2nd got
an SMI, that thread will be stopped in the first instruction in the SMI
handler.
Since the secondary thread is stopped in the first instruction in SMI,
there is very little chance that it would be in the middle of executing
an instruction being patched. Plus OS has no way to stop SMIs from
happening.
Non-Maskable Interrupts
-----------------------
When thread0 of a core is doing the microcode update, if thread1 is
pulled into NMI, that can cause unpredictable behavior due to the
reasons above.
OS can choose a variety of methods to avoid running into this situation.
Is the microcode suitable for late loading?
-------------------------------------------
Late loading is done when the system is fully operational and running
real workloads. Late loading behavior depends on what the base patch on
the CPU is before upgrading to the new patch.
This is true for Intel CPUs.
Consider, for example, a CPU has patch level 1 and the update is to
patch level 3.
Between patch1 and patch3, patch2 might have deprecated a software-visible
feature.
This is unacceptable if software is even potentially using that feature.
For instance, say MSR_X is no longer available after an update,
accessing that MSR will cause a #GP fault.
Basically there is no way to declare a new microcode update suitable
for late-loading. This is another one of the problems that caused late
loading to be not enabled by default.
Builtin microcode
=================
The loader supports also loading of a builtin microcode supplied through
the regular builtin firmware method CONFIG_EXTRA_FIRMWARE. Only 64-bit is
currently supported.
Here's an example::
CONFIG_EXTRA_FIRMWARE="intel-ucode/06-3a-09 amd-ucode/microcode_amd_fam15h.bin"
CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware"
This basically means, you have the following tree structure locally::
/lib/firmware/
|-- amd-ucode
...
| |-- microcode_amd_fam15h.bin
...
|-- intel-ucode
...
| |-- 06-3a-09
...
so that the build system can find those files and integrate them into
the final kernel image. The early loader finds them and applies them.
Needless to say, this method is not the most flexible one because it
requires rebuilding the kernel each time updated microcode from the CPU
vendor is available.
+354
View File
@@ -0,0 +1,354 @@
.. SPDX-License-Identifier: GPL-2.0
=========================================
MTRR (Memory Type Range Register) control
=========================================
:Authors: - Richard Gooch <rgooch@atnf.csiro.au> - 3 Jun 1999
- Luis R. Rodriguez <mcgrof@do-not-panic.com> - April 9, 2015
Phasing out MTRR use
====================
MTRR use is replaced on modern x86 hardware with PAT. Direct MTRR use by
drivers on Linux is now completely phased out, device drivers should use
arch_phys_wc_add() in combination with ioremap_wc() to make MTRR effective on
non-PAT systems while a no-op but equally effective on PAT enabled systems.
Even if Linux does not use MTRRs directly, some x86 platform firmware may still
set up MTRRs early before booting the OS. They do this as some platform
firmware may still have implemented access to MTRRs which would be controlled
and handled by the platform firmware directly. An example of platform use of
MTRRs is through the use of SMI handlers, one case could be for fan control,
the platform code would need uncachable access to some of its fan control
registers. Such platform access does not need any Operating System MTRR code in
place other than mtrr_type_lookup() to ensure any OS specific mapping requests
are aligned with platform MTRR setup. If MTRRs are only set up by the platform
firmware code though and the OS does not make any specific MTRR mapping
requests mtrr_type_lookup() should always return MTRR_TYPE_INVALID.
For details refer to Documentation/arch/x86/pat.rst.
.. tip::
On Intel P6 family processors (Pentium Pro, Pentium II and later)
the Memory Type Range Registers (MTRRs) may be used to control
processor access to memory ranges. This is most useful when you have
a video (VGA) card on a PCI or AGP bus. Enabling write-combining
allows bus write transfers to be combined into a larger transfer
before bursting over the PCI/AGP bus. This can increase performance
of image write operations 2.5 times or more.
The Cyrix 6x86, 6x86MX and M II processors have Address Range
Registers (ARRs) which provide a similar functionality to MTRRs. For
these, the ARRs are used to emulate the MTRRs.
The AMD K6-2 (stepping 8 and above) and K6-3 processors have two
MTRRs. These are supported. The AMD Athlon family provide 8 Intel
style MTRRs.
The Centaur C6 (WinChip) has 8 MCRs, allowing write-combining. These
are supported.
The VIA Cyrix III and VIA C3 CPUs offer 8 Intel style MTRRs.
The CONFIG_MTRR option creates a /proc/mtrr file which may be used
to manipulate your MTRRs. Typically the X server should use
this. This should have a reasonably generic interface so that
similar control registers on other processors can be easily
supported.
There are two interfaces to /proc/mtrr: one is an ASCII interface
which allows you to read and write. The other is an ioctl()
interface. The ASCII interface is meant for administration. The
ioctl() interface is meant for C programs (i.e. the X server). The
interfaces are described below, with sample commands and C code.
Reading MTRRs from the shell
============================
::
% cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size= 128MB: write-back, count=1
reg01: base=0x08000000 ( 128MB), size= 64MB: write-back, count=1
Creating MTRRs from the C-shell::
# echo "base=0xf8000000 size=0x400000 type=write-combining" >! /proc/mtrr
or if you use bash::
# echo "base=0xf8000000 size=0x400000 type=write-combining" >| /proc/mtrr
And the result thereof::
% cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size= 128MB: write-back, count=1
reg01: base=0x08000000 ( 128MB), size= 64MB: write-back, count=1
reg02: base=0xf8000000 (3968MB), size= 4MB: write-combining, count=1
This is for video RAM at base address 0xf8000000 and size 4 megabytes. To
find out your base address, you need to look at the output of your X
server, which tells you where the linear framebuffer address is. A
typical line that you may get is::
(--) S3: PCI: 968 rev 0, Linear FB @ 0xf8000000
Note that you should only use the value from the X server, as it may
move the framebuffer base address, so the only value you can trust is
that reported by the X server.
To find out the size of your framebuffer (what, you don't actually
know?), the following line will tell you::
(--) S3: videoram: 4096k
That's 4 megabytes, which is 0x400000 bytes (in hexadecimal).
A patch is being written for XFree86 which will make this automatic:
in other words the X server will manipulate /proc/mtrr using the
ioctl() interface, so users won't have to do anything. If you use a
commercial X server, lobby your vendor to add support for MTRRs.
Creating overlapping MTRRs
==========================
::
%echo "base=0xfb000000 size=0x1000000 type=write-combining" >/proc/mtrr
%echo "base=0xfb000000 size=0x1000 type=uncachable" >/proc/mtrr
And the results::
% cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size= 64MB: write-back, count=1
reg01: base=0xfb000000 (4016MB), size= 16MB: write-combining, count=1
reg02: base=0xfb000000 (4016MB), size= 4kB: uncachable, count=1
Some cards (especially Voodoo Graphics boards) need this 4 kB area
excluded from the beginning of the region because it is used for
registers.
NOTE: You can only create type=uncachable region, if the first
region that you created is type=write-combining.
Removing MTRRs from the C-shel
==============================
::
% echo "disable=2" >! /proc/mtrr
or using bash::
% echo "disable=2" >| /proc/mtrr
Reading MTRRs from a C program using ioctl()'s
==============================================
::
/* mtrr-show.c
Source file for mtrr-show (example program to show MTRRs using ioctl()'s)
Copyright (C) 1997-1998 Richard Gooch
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Richard Gooch may be reached by email at rgooch@atnf.csiro.au
The postal address is:
Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/
/*
This program will use an ioctl() on /proc/mtrr to show the current MTRR
settings. This is an alternative to reading /proc/mtrr.
Written by Richard Gooch 17-DEC-1997
Last updated by Richard Gooch 2-MAY-1998
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <asm/mtrr.h>
#define TRUE 1
#define FALSE 0
#define ERRSTRING strerror (errno)
static char *mtrr_strings[MTRR_NUM_TYPES] =
{
"uncachable", /* 0 */
"write-combining", /* 1 */
"?", /* 2 */
"?", /* 3 */
"write-through", /* 4 */
"write-protect", /* 5 */
"write-back", /* 6 */
};
int main ()
{
int fd;
struct mtrr_gentry gentry;
if ( ( fd = open ("/proc/mtrr", O_RDONLY, 0) ) == -1 )
{
if (errno == ENOENT)
{
fputs ("/proc/mtrr not found: not supported or you don't have a PPro?\n",
stderr);
exit (1);
}
fprintf (stderr, "Error opening /proc/mtrr\t%s\n", ERRSTRING);
exit (2);
}
for (gentry.regnum = 0; ioctl (fd, MTRRIOC_GET_ENTRY, &gentry) == 0;
++gentry.regnum)
{
if (gentry.size < 1)
{
fprintf (stderr, "Register: %u disabled\n", gentry.regnum);
continue;
}
fprintf (stderr, "Register: %u base: 0x%lx size: 0x%lx type: %s\n",
gentry.regnum, gentry.base, gentry.size,
mtrr_strings[gentry.type]);
}
if (errno == EINVAL) exit (0);
fprintf (stderr, "Error doing ioctl(2) on /dev/mtrr\t%s\n", ERRSTRING);
exit (3);
} /* End Function main */
Creating MTRRs from a C programme using ioctl()'s
=================================================
::
/* mtrr-add.c
Source file for mtrr-add (example programme to add an MTRRs using ioctl())
Copyright (C) 1997-1998 Richard Gooch
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Richard Gooch may be reached by email at rgooch@atnf.csiro.au
The postal address is:
Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/
/*
This programme will use an ioctl() on /proc/mtrr to add an entry. The first
available mtrr is used. This is an alternative to writing /proc/mtrr.
Written by Richard Gooch 17-DEC-1997
Last updated by Richard Gooch 2-MAY-1998
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <asm/mtrr.h>
#define TRUE 1
#define FALSE 0
#define ERRSTRING strerror (errno)
static char *mtrr_strings[MTRR_NUM_TYPES] =
{
"uncachable", /* 0 */
"write-combining", /* 1 */
"?", /* 2 */
"?", /* 3 */
"write-through", /* 4 */
"write-protect", /* 5 */
"write-back", /* 6 */
};
int main (int argc, char **argv)
{
int fd;
struct mtrr_sentry sentry;
if (argc != 4)
{
fprintf (stderr, "Usage:\tmtrr-add base size type\n");
exit (1);
}
sentry.base = strtoul (argv[1], NULL, 0);
sentry.size = strtoul (argv[2], NULL, 0);
for (sentry.type = 0; sentry.type < MTRR_NUM_TYPES; ++sentry.type)
{
if (strcmp (argv[3], mtrr_strings[sentry.type]) == 0) break;
}
if (sentry.type >= MTRR_NUM_TYPES)
{
fprintf (stderr, "Illegal type: \"%s\"\n", argv[3]);
exit (2);
}
if ( ( fd = open ("/proc/mtrr", O_WRONLY, 0) ) == -1 )
{
if (errno == ENOENT)
{
fputs ("/proc/mtrr not found: not supported or you don't have a PPro?\n",
stderr);
exit (3);
}
fprintf (stderr, "Error opening /proc/mtrr\t%s\n", ERRSTRING);
exit (4);
}
if (ioctl (fd, MTRRIOC_ADD_ENTRY, &sentry) == -1)
{
fprintf (stderr, "Error doing ioctl(2) on /dev/mtrr\t%s\n", ERRSTRING);
exit (5);
}
fprintf (stderr, "Sleeping for 5 seconds so you can see the new entry\n");
sleep (5);
close (fd);
fputs ("I've just closed /proc/mtrr so now the new entry should be gone\n",
stderr);
} /* End Function main */
@@ -0,0 +1,182 @@
.. SPDX-License-Identifier: GPL-2.0
============
ORC unwinder
============
Overview
========
The kernel CONFIG_UNWINDER_ORC option enables the ORC unwinder, which is
similar in concept to a DWARF unwinder. The difference is that the
format of the ORC data is much simpler than DWARF, which in turn allows
the ORC unwinder to be much simpler and faster.
The ORC data consists of unwind tables which are generated by objtool.
They contain out-of-band data which is used by the in-kernel ORC
unwinder. Objtool generates the ORC data by first doing compile-time
stack metadata validation (CONFIG_STACK_VALIDATION). After analyzing
all the code paths of a .o file, it determines information about the
stack state at each instruction address in the file and outputs that
information to the .orc_unwind and .orc_unwind_ip sections.
The per-object ORC sections are combined at link time and are sorted and
post-processed at boot time. The unwinder uses the resulting data to
correlate instruction addresses with their stack states at run time.
ORC vs frame pointers
=====================
With frame pointers enabled, GCC adds instrumentation code to every
function in the kernel. The kernel's .text size increases by about
3.2%, resulting in a broad kernel-wide slowdown. Measurements by Mel
Gorman [1]_ have shown a slowdown of 5-10% for some workloads.
In contrast, the ORC unwinder has no effect on text size or runtime
performance, because the debuginfo is out of band. So if you disable
frame pointers and enable the ORC unwinder, you get a nice performance
improvement across the board, and still have reliable stack traces.
Ingo Molnar says:
"Note that it's not just a performance improvement, but also an
instruction cache locality improvement: 3.2% .text savings almost
directly transform into a similarly sized reduction in cache
footprint. That can transform to even higher speedups for workloads
whose cache locality is borderline."
Another benefit of ORC compared to frame pointers is that it can
reliably unwind across interrupts and exceptions. Frame pointer based
unwinds can sometimes skip the caller of the interrupted function, if it
was a leaf function or if the interrupt hit before the frame pointer was
saved.
The main disadvantage of the ORC unwinder compared to frame pointers is
that it needs more memory to store the ORC unwind tables: roughly 2-4MB
depending on the kernel config.
ORC vs DWARF
============
ORC debuginfo's advantage over DWARF itself is that it's much simpler.
It gets rid of the complex DWARF CFI state machine and also gets rid of
the tracking of unnecessary registers. This allows the unwinder to be
much simpler, meaning fewer bugs, which is especially important for
mission critical oops code.
The simpler debuginfo format also enables the unwinder to be much faster
than DWARF, which is important for perf and lockdep. In a basic
performance test by Jiri Slaby [2]_, the ORC unwinder was about 20x
faster than an out-of-tree DWARF unwinder. (Note: That measurement was
taken before some performance tweaks were added, which doubled
performance, so the speedup over DWARF may be closer to 40x.)
The ORC data format does have a few downsides compared to DWARF. ORC
unwind tables take up ~50% more RAM (+1.3MB on an x86 defconfig kernel)
than DWARF-based eh_frame tables.
Another potential downside is that, as GCC evolves, it's conceivable
that the ORC data may end up being *too* simple to describe the state of
the stack for certain optimizations. But IMO this is unlikely because
GCC saves the frame pointer for any unusual stack adjustments it does,
so I suspect we'll really only ever need to keep track of the stack
pointer and the frame pointer between call frames. But even if we do
end up having to track all the registers DWARF tracks, at least we will
still be able to control the format, e.g. no complex state machines.
ORC unwind table generation
===========================
The ORC data is generated by objtool. With the existing compile-time
stack metadata validation feature, objtool already follows all code
paths, and so it already has all the information it needs to be able to
generate ORC data from scratch. So it's an easy step to go from stack
validation to ORC data generation.
It should be possible to instead generate the ORC data with a simple
tool which converts DWARF to ORC data. However, such a solution would
be incomplete due to the kernel's extensive use of asm, inline asm, and
special sections like exception tables.
That could be rectified by manually annotating those special code paths
using GNU assembler .cfi annotations in .S files, and homegrown
annotations for inline asm in .c files. But asm annotations were tried
in the past and were found to be unmaintainable. They were often
incorrect/incomplete and made the code harder to read and keep updated.
And based on looking at glibc code, annotating inline asm in .c files
might be even worse.
Objtool still needs a few annotations, but only in code which does
unusual things to the stack like entry code. And even then, far fewer
annotations are needed than what DWARF would need, so they're much more
maintainable than DWARF CFI annotations.
So the advantages of using objtool to generate ORC data are that it
gives more accurate debuginfo, with very few annotations. It also
insulates the kernel from toolchain bugs which can be very painful to
deal with in the kernel since we often have to workaround issues in
older versions of the toolchain for years.
The downside is that the unwinder now becomes dependent on objtool's
ability to reverse engineer GCC code flow. If GCC optimizations become
too complicated for objtool to follow, the ORC data generation might
stop working or become incomplete. (It's worth noting that livepatch
already has such a dependency on objtool's ability to follow GCC code
flow.)
If newer versions of GCC come up with some optimizations which break
objtool, we may need to revisit the current implementation. Some
possible solutions would be asking GCC to make the optimizations more
palatable, or having objtool use DWARF as an additional input, or
creating a GCC plugin to assist objtool with its analysis. But for now,
objtool follows GCC code quite well.
Unwinder implementation details
===============================
Objtool generates the ORC data by integrating with the compile-time
stack metadata validation feature, which is described in detail in
tools/objtool/Documentation/objtool.txt. After analyzing all
the code paths of a .o file, it creates an array of orc_entry structs,
and a parallel array of instruction addresses associated with those
structs, and writes them to the .orc_unwind and .orc_unwind_ip sections
respectively.
The ORC data is split into the two arrays for performance reasons, to
make the searchable part of the data (.orc_unwind_ip) more compact. The
arrays are sorted in parallel at boot time.
Performance is further improved by the use of a fast lookup table which
is created at runtime. The fast lookup table associates a given address
with a range of indices for the .orc_unwind table, so that only a small
subset of the table needs to be searched.
Etymology
=========
Orcs, fearsome creatures of medieval folklore, are the Dwarves' natural
enemies. Similarly, the ORC unwinder was created in opposition to the
complexity and slowness of DWARF.
"Although Orcs rarely consider multiple solutions to a problem, they do
excel at getting things done because they are creatures of action, not
thought." [3]_ Similarly, unlike the esoteric DWARF unwinder, the
veracious ORC unwinder wastes no time or siloconic effort decoding
variable-length zero-extended unsigned-integer byte-coded
state-machine-based debug information entries.
Similar to how Orcs frequently unravel the well-intentioned plans of
their adversaries, the ORC unwinder frequently unravels stacks with
brutal, unyielding efficiency.
ORC stands for Oops Rewind Capability.
.. [1] https://lore.kernel.org/r/20170602104048.jkkzssljsompjdwy@suse.de
.. [2] https://lore.kernel.org/r/d2ca5435-6386-29b8-db87-7f227c2b713a@suse.cz
.. [3] http://dustin.wikidot.com/half-orcs-and-orcs
+240
View File
@@ -0,0 +1,240 @@
.. SPDX-License-Identifier: GPL-2.0
==========================
PAT (Page Attribute Table)
==========================
x86 Page Attribute Table (PAT) allows for setting the memory attribute at the
page level granularity. PAT is complementary to the MTRR settings which allows
for setting of memory types over physical address ranges. However, PAT is
more flexible than MTRR due to its capability to set attributes at page level
and also due to the fact that there are no hardware limitations on number of
such attribute settings allowed. Added flexibility comes with guidelines for
not having memory type aliasing for the same physical memory with multiple
virtual addresses.
PAT allows for different types of memory attributes. The most commonly used
ones that will be supported at this time are:
=== ==============
WB Write-back
UC Uncached
WC Write-combined
WT Write-through
UC- Uncached Minus
=== ==============
PAT APIs
========
There are many different APIs in the kernel that allows setting of memory
attributes at the page level. In order to avoid aliasing, these interfaces
should be used thoughtfully. Below is a table of interfaces available,
their intended usage and their memory attribute relationships. Internally,
these APIs use a reserve_memtype()/free_memtype() interface on the physical
address range to avoid any aliasing.
+------------------------+----------+--------------+------------------+
| API | RAM | ACPI,... | Reserved/Holes |
+------------------------+----------+--------------+------------------+
| ioremap | -- | UC- | UC- |
+------------------------+----------+--------------+------------------+
| ioremap_cache | -- | WB | WB |
+------------------------+----------+--------------+------------------+
| ioremap_uc | -- | UC | UC |
+------------------------+----------+--------------+------------------+
| ioremap_wc | -- | -- | WC |
+------------------------+----------+--------------+------------------+
| ioremap_wt | -- | -- | WT |
+------------------------+----------+--------------+------------------+
| set_memory_uc, | UC- | -- | -- |
| set_memory_wb | | | |
+------------------------+----------+--------------+------------------+
| set_memory_wc, | WC | -- | -- |
| set_memory_wb | | | |
+------------------------+----------+--------------+------------------+
| set_memory_wt, | WT | -- | -- |
| set_memory_wb | | | |
+------------------------+----------+--------------+------------------+
| pci sysfs resource | -- | -- | UC- |
+------------------------+----------+--------------+------------------+
| pci sysfs resource_wc | -- | -- | WC |
| is IORESOURCE_PREFETCH | | | |
+------------------------+----------+--------------+------------------+
| pci proc | -- | -- | UC- |
| !PCIIOC_WRITE_COMBINE | | | |
+------------------------+----------+--------------+------------------+
| pci proc | -- | -- | WC |
| PCIIOC_WRITE_COMBINE | | | |
+------------------------+----------+--------------+------------------+
| /dev/mem | -- | WB/WC/UC- | WB/WC/UC- |
| read-write | | | |
+------------------------+----------+--------------+------------------+
| /dev/mem | -- | UC- | UC- |
| mmap SYNC flag | | | |
+------------------------+----------+--------------+------------------+
| /dev/mem | -- | WB/WC/UC- | WB/WC/UC- |
| mmap !SYNC flag | | | |
| and | |(from existing| (from existing |
| any alias to this area | |alias) | alias) |
+------------------------+----------+--------------+------------------+
| /dev/mem | -- | WB | WB |
| mmap !SYNC flag | | | |
| no alias to this area | | | |
| and | | | |
| MTRR says WB | | | |
+------------------------+----------+--------------+------------------+
| /dev/mem | -- | -- | UC- |
| mmap !SYNC flag | | | |
| no alias to this area | | | |
| and | | | |
| MTRR says !WB | | | |
+------------------------+----------+--------------+------------------+
Advanced APIs for drivers
=========================
A. Exporting pages to users with remap_pfn_range, io_remap_pfn_range,
vmf_insert_pfn.
Drivers wanting to export some pages to userspace do it by using mmap
interface and a combination of:
1) pgprot_noncached()
2) io_remap_pfn_range() or remap_pfn_range() or vmf_insert_pfn()
With PAT support, a new API pgprot_writecombine is being added. So, drivers can
continue to use the above sequence, with either pgprot_noncached() or
pgprot_writecombine() in step 1, followed by step 2.
In addition, step 2 internally tracks the region as UC or WC in memtype
list in order to ensure no conflicting mapping.
Note that this set of APIs only works with IO (non RAM) regions. If driver
wants to export a RAM region, it has to do set_memory_uc() or set_memory_wc()
as step 0 above and also track the usage of those pages and use set_memory_wb()
before the page is freed to free pool.
MTRR effects on PAT / non-PAT systems
=====================================
The following table provides the effects of using write-combining MTRRs when
using ioremap*() calls on x86 for both non-PAT and PAT systems. Ideally
mtrr_add() usage will be phased out in favor of arch_phys_wc_add() which will
be a no-op on PAT enabled systems. The region over which a arch_phys_wc_add()
is made, should already have been ioremapped with WC attributes or PAT entries,
this can be done by using ioremap_wc() / set_memory_wc(). Devices which
combine areas of IO memory desired to remain uncacheable with areas where
write-combining is desirable should consider use of ioremap_uc() followed by
set_memory_wc() to white-list effective write-combined areas. Such use is
nevertheless discouraged as the effective memory type is considered
implementation defined, yet this strategy can be used as last resort on devices
with size-constrained regions where otherwise MTRR write-combining would
otherwise not be effective.
::
==== ======= === ========================= =====================
MTRR Non-PAT PAT Linux ioremap value Effective memory type
==== ======= === ========================= =====================
PAT Non-PAT | PAT
|PCD |
||PWT |
||| |
WC 000 WB _PAGE_CACHE_MODE_WB WC | WC
WC 001 WC _PAGE_CACHE_MODE_WC WC* | WC
WC 010 UC- _PAGE_CACHE_MODE_UC_MINUS WC* | UC
WC 011 UC _PAGE_CACHE_MODE_UC UC | UC
==== ======= === ========================= =====================
(*) denotes implementation defined and is discouraged
.. note:: -- in the above table mean "Not suggested usage for the API". Some
of the --'s are strictly enforced by the kernel. Some others are not really
enforced today, but may be enforced in future.
For ioremap and pci access through /sys or /proc - The actual type returned
can be more restrictive, in case of any existing aliasing for that address.
For example: If there is an existing uncached mapping, a new ioremap_wc can
return uncached mapping in place of write-combine requested.
set_memory_[uc|wc|wt] and set_memory_wb should be used in pairs, where driver
will first make a region uc, wc or wt and switch it back to wb after use.
Over time writes to /proc/mtrr will be deprecated in favor of using PAT based
interfaces. Users writing to /proc/mtrr are suggested to use above interfaces.
Drivers should use ioremap_[uc|wc] to access PCI BARs with [uc|wc] access
types.
Drivers should use set_memory_[uc|wc|wt] to set access type for RAM ranges.
PAT debugging
=============
With CONFIG_DEBUG_FS enabled, PAT memtype list can be examined by::
# mount -t debugfs debugfs /sys/kernel/debug
# cat /sys/kernel/debug/x86/pat_memtype_list
PAT memtype list:
uncached-minus @ 0x7fadf000-0x7fae0000
uncached-minus @ 0x7fb19000-0x7fb1a000
uncached-minus @ 0x7fb1a000-0x7fb1b000
uncached-minus @ 0x7fb1b000-0x7fb1c000
uncached-minus @ 0x7fb1c000-0x7fb1d000
uncached-minus @ 0x7fb1d000-0x7fb1e000
uncached-minus @ 0x7fb1e000-0x7fb25000
uncached-minus @ 0x7fb25000-0x7fb26000
uncached-minus @ 0x7fb26000-0x7fb27000
uncached-minus @ 0x7fb27000-0x7fb28000
uncached-minus @ 0x7fb28000-0x7fb2e000
uncached-minus @ 0x7fb2e000-0x7fb2f000
uncached-minus @ 0x7fb2f000-0x7fb30000
uncached-minus @ 0x7fb31000-0x7fb32000
uncached-minus @ 0x80000000-0x90000000
This list shows physical address ranges and various PAT settings used to
access those physical address ranges.
Another, more verbose way of getting PAT related debug messages is with
"debugpat" boot parameter. With this parameter, various debug messages are
printed to dmesg log.
PAT Initialization
==================
The following table describes how PAT is initialized under various
configurations. The PAT MSR must be updated by Linux in order to support WC
and WT attributes. Otherwise, the PAT MSR has the value programmed in it
by the firmware. Note, Xen enables WC attribute in the PAT MSR for guests.
==== ===== ========================== ========= =======
MTRR PAT Call Sequence PAT State PAT MSR
==== ===== ========================== ========= =======
E E MTRR -> PAT init Enabled OS
E D MTRR -> PAT init Disabled -
D E MTRR -> PAT disable Disabled BIOS
D D MTRR -> PAT disable Disabled -
- np/E PAT -> PAT disable Disabled BIOS
- np/D PAT -> PAT disable Disabled -
E !P/E MTRR -> PAT init Disabled BIOS
D !P/E MTRR -> PAT disable Disabled BIOS
!M !P/E MTRR stub -> PAT disable Disabled BIOS
==== ===== ========================== ========= =======
Legend
========= =======================================
E Feature enabled in CPU
D Feature disabled/unsupported in CPU
np "nopat" boot option specified
!P CONFIG_X86_PAT option unset
!M CONFIG_MTRR option unset
Enabled PAT state set to enabled
Disabled PAT state set to disabled
OS PAT initializes PAT MSR with OS setting
BIOS PAT keeps PAT MSR with BIOS setting
========= =======================================
+193
View File
@@ -0,0 +1,193 @@
.. SPDX-License-Identifier: GPL-2.0
==========================
Page Table Isolation (PTI)
==========================
Overview
========
Page Table Isolation (pti, previously known as KAISER [1]_) is a
countermeasure against attacks on the shared user/kernel address
space such as the "Meltdown" approach [2]_.
To mitigate this class of attacks, we create an independent set of
page tables for use only when running userspace applications. When
the kernel is entered via syscalls, interrupts or exceptions, the
page tables are switched to the full "kernel" copy. When the system
switches back to user mode, the user copy is used again.
The userspace page tables contain only a minimal amount of kernel
data: only what is needed to enter/exit the kernel such as the
entry/exit functions themselves and the interrupt descriptor table
(IDT). There are a few strictly unnecessary things that get mapped
such as the first C function when entering an interrupt (see
comments in pti.c).
This approach helps to ensure that side-channel attacks leveraging
the paging structures do not function when PTI is enabled. It can be
enabled by setting CONFIG_MITIGATION_PAGE_TABLE_ISOLATION=y at compile
time. Once enabled at compile-time, it can be disabled at boot with
the 'nopti' or 'pti=' kernel parameters (see kernel-parameters.txt).
Page Table Management
=====================
When PTI is enabled, the kernel manages two sets of page tables.
The first set is very similar to the single set which is present in
kernels without PTI. This includes a complete mapping of userspace
that the kernel can use for things like copy_to_user().
Although _complete_, the user portion of the kernel page tables is
crippled by setting the NX bit in the top level. This ensures
that any missed kernel->user CR3 switch will immediately crash
userspace upon executing its first instruction.
The userspace page tables map only the kernel data needed to enter
and exit the kernel. This data is entirely contained in the 'struct
cpu_entry_area' structure which is placed in the fixmap which gives
each CPU's copy of the area a compile-time-fixed virtual address.
For new userspace mappings, the kernel makes the entries in its
page tables like normal. The only difference is when the kernel
makes entries in the top (PGD) level. In addition to setting the
entry in the main kernel PGD, a copy of the entry is made in the
userspace page tables' PGD.
This sharing at the PGD level also inherently shares all the lower
layers of the page tables. This leaves a single, shared set of
userspace page tables to manage. One PTE to lock, one set of
accessed bits, dirty bits, etc...
Overhead
========
Protection against side-channel attacks is important. But,
this protection comes at a cost:
1. Increased Memory Use
a. Each process now needs an order-1 PGD instead of order-0.
(Consumes an additional 4k per process).
b. The 'cpu_entry_area' structure must be 2MB in size and 2MB
aligned so that it can be mapped by setting a single PMD
entry. This consumes nearly 2MB of RAM once the kernel
is decompressed, but no space in the kernel image itself.
2. Runtime Cost
a. CR3 manipulation to switch between the page table copies
must be done at interrupt, syscall, and exception entry
and exit (it can be skipped when the kernel is interrupted,
though.) Moves to CR3 are on the order of a hundred
cycles, and are required at every entry and exit.
b. Percpu TSS is mapped into the user page tables to allow SYSCALL64 path
to work under PTI. This doesn't have a direct runtime cost but it can
be argued it opens certain timing attack scenarios.
c. Global pages are disabled for all kernel structures not
mapped into both kernel and userspace page tables. This
feature of the MMU allows different processes to share TLB
entries mapping the kernel. Losing the feature means more
TLB misses after a context switch. The actual loss of
performance is very small, however, never exceeding 1%.
d. Process Context IDentifiers (PCID) is a CPU feature that
allows us to skip flushing the entire TLB when switching page
tables by setting a special bit in CR3 when the page tables
are changed. This makes switching the page tables (at context
switch, or kernel entry/exit) cheaper. But, on systems with
PCID support, the context switch code must flush both the user
and kernel entries out of the TLB. The user PCID TLB flush is
deferred until the exit to userspace, minimizing the cost.
See intel.com/sdm for the gory PCID/INVPCID details.
e. The userspace page tables must be populated for each new
process. Even without PTI, the shared kernel mappings
are created by copying top-level (PGD) entries into each
new process. But, with PTI, there are now *two* kernel
mappings: one in the kernel page tables that maps everything
and one for the entry/exit structures. At fork(), we need to
copy both.
f. In addition to the fork()-time copying, there must also
be an update to the userspace PGD any time a set_pgd() is done
on a PGD used to map userspace. This ensures that the kernel
and userspace copies always map the same userspace
memory.
g. On systems without PCID support, each CR3 write flushes
the entire TLB. That means that each syscall, interrupt
or exception flushes the TLB.
h. INVPCID is a TLB-flushing instruction which allows flushing
of TLB entries for non-current PCIDs. Some systems support
PCIDs, but do not support INVPCID. On these systems, addresses
can only be flushed from the TLB for the current PCID. When
flushing a kernel address, we need to flush all PCIDs, so a
single kernel address flush will require a TLB-flushing CR3
write upon the next use of every PCID.
Possible Future Work
====================
1. We can be more careful about not actually writing to CR3
unless its value is actually changed.
2. Allow PTI to be enabled/disabled at runtime in addition to the
boot-time switching.
Testing
========
To test stability of PTI, the following test procedure is recommended,
ideally doing all of these in parallel:
1. Set CONFIG_DEBUG_ENTRY=y
2. Run several copies of all of the tools/testing/selftests/x86/ tests
(excluding MPX and protection_keys) in a loop on multiple CPUs for
several minutes. These tests frequently uncover corner cases in the
kernel entry code. In general, old kernels might cause these tests
themselves to crash, but they should never crash the kernel.
3. Run the 'perf' tool in a mode (top or record) that generates many
frequent performance monitoring non-maskable interrupts (see "NMI"
in /proc/interrupts). This exercises the NMI entry/exit code which
is known to trigger bugs in code paths that did not expect to be
interrupted, including nested NMIs. Using "-c" boosts the rate of
NMIs, and using two -c with separate counters encourages nested NMIs
and less deterministic behavior.
::
while true; do perf record -c 10000 -e instructions,cycles -a sleep 10; done
4. Launch a KVM virtual machine.
5. Run 32-bit binaries on systems supporting the SYSCALL instruction.
This has been a lightly-tested code path and needs extra scrutiny.
Debugging
=========
Bugs in PTI cause a few different signatures of crashes
that are worth noting here.
* Failures of the selftests/x86 code. Usually a bug in one of the
more obscure corners of entry_64.S
* Crashes in early boot, especially around CPU bringup. Bugs
in the mappings cause these.
* Crashes at the first interrupt. Caused by bugs in entry_64.S,
like screwing up a page table switch. Also caused by
incorrectly mapping the IRQ handler entry code.
* Crashes at the first NMI. The NMI code is separate from main
interrupt handlers and can have bugs that do not affect
normal interrupts. Also caused by incorrectly mapping NMI
code. NMIs that interrupt the entry code must be very
careful and can be the cause of crashes that show up when
running perf.
* Kernel crashes at the first exit to userspace. entry_64.S
bugs, or failing to map some of the exit code.
* Crashes at first interrupt that interrupts userspace. The paths
in entry_64.S that return to userspace are sometimes separate
from the ones that return to the kernel.
* Double faults: overflowing the kernel stack because of page
faults upon page faults. Caused by touching non-pti-mapped
data in the entry code, or forgetting to switch to kernel
CR3 before calling into C functions which are not pti-mapped.
* Userspace segfaults early in boot, sometimes manifesting
as mount(8) failing to mount the rootfs. These have
tended to be TLB invalidation issues. Usually invalidating
the wrong PCID, or otherwise missing an invalidation.
.. [1] https://gruss.cc/files/kaiser.pdf
.. [2] https://meltdownattack.com/meltdown.pdf
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 350 KiB

+302
View File
@@ -0,0 +1,302 @@
.. SPDX-License-Identifier: GPL-2.0
===============================
Software Guard eXtensions (SGX)
===============================
Overview
========
Software Guard eXtensions (SGX) hardware enables for user space applications
to set aside private memory regions of code and data:
* Privileged (ring-0) ENCLS functions orchestrate the construction of the
regions.
* Unprivileged (ring-3) ENCLU functions allow an application to enter and
execute inside the regions.
These memory regions are called enclaves. An enclave can be only entered at a
fixed set of entry points. Each entry point can hold a single hardware thread
at a time. While the enclave is loaded from a regular binary file by using
ENCLS functions, only the threads inside the enclave can access its memory. The
region is denied from outside access by the CPU, and encrypted before it leaves
from LLC.
The support can be determined by
``grep sgx /proc/cpuinfo``
SGX must both be supported in the processor and enabled by the BIOS. If SGX
appears to be unsupported on a system which has hardware support, ensure
support is enabled in the BIOS. If a BIOS presents a choice between "Enabled"
and "Software Enabled" modes for SGX, choose "Enabled".
Enclave Page Cache
==================
SGX utilizes an *Enclave Page Cache (EPC)* to store pages that are associated
with an enclave. It is contained in a BIOS-reserved region of physical memory.
Unlike pages used for regular memory, pages can only be accessed from outside of
the enclave during enclave construction with special, limited SGX instructions.
Only a CPU executing inside an enclave can directly access enclave memory.
However, a CPU executing inside an enclave may access normal memory outside the
enclave.
The kernel manages enclave memory similar to how it treats device memory.
Enclave Page Types
------------------
**SGX Enclave Control Structure (SECS)**
Enclave's address range, attributes and other global data are defined
by this structure.
**Regular (REG)**
Regular EPC pages contain the code and data of an enclave.
**Thread Control Structure (TCS)**
Thread Control Structure pages define the entry points to an enclave and
track the execution state of an enclave thread.
**Version Array (VA)**
Version Array pages contain 512 slots, each of which can contain a version
number for a page evicted from the EPC.
Enclave Page Cache Map
----------------------
The processor tracks EPC pages in a hardware metadata structure called the
*Enclave Page Cache Map (EPCM)*. The EPCM contains an entry for each EPC page
which describes the owning enclave, access rights and page type among the other
things.
EPCM permissions are separate from the normal page tables. This prevents the
kernel from, for instance, allowing writes to data which an enclave wishes to
remain read-only. EPCM permissions may only impose additional restrictions on
top of normal x86 page permissions.
For all intents and purposes, the SGX architecture allows the processor to
invalidate all EPCM entries at will. This requires that software be prepared to
handle an EPCM fault at any time. In practice, this can happen on events like
power transitions when the ephemeral key that encrypts enclave memory is lost.
Application interface
=====================
Enclave build functions
-----------------------
In addition to the traditional compiler and linker build process, SGX has a
separate enclave “build” process. Enclaves must be built before they can be
executed (entered). The first step in building an enclave is opening the
**/dev/sgx_enclave** device. Since enclave memory is protected from direct
access, special privileged instructions are then used to copy data into enclave
pages and establish enclave page permissions.
.. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c
:functions: sgx_ioc_enclave_create
sgx_ioc_enclave_add_pages
sgx_ioc_enclave_init
sgx_ioc_enclave_provision
Enclave runtime management
--------------------------
Systems supporting SGX2 additionally support changes to initialized
enclaves: modifying enclave page permissions and type, and dynamically
adding and removing of enclave pages. When an enclave accesses an address
within its address range that does not have a backing page then a new
regular page will be dynamically added to the enclave. The enclave is
still required to run EACCEPT on the new page before it can be used.
.. kernel-doc:: arch/x86/kernel/cpu/sgx/ioctl.c
:functions: sgx_ioc_enclave_restrict_permissions
sgx_ioc_enclave_modify_types
sgx_ioc_enclave_remove_pages
Enclave vDSO
------------
Entering an enclave can only be done through SGX-specific EENTER and ERESUME
functions, and is a non-trivial process. Because of the complexity of
transitioning to and from an enclave, enclaves typically utilize a library to
handle the actual transitions. This is roughly analogous to how glibc
implementations are used by most applications to wrap system calls.
Another crucial characteristic of enclaves is that they can generate exceptions
as part of their normal operation that need to be handled in the enclave or are
unique to SGX.
Instead of the traditional signal mechanism to handle these exceptions, SGX
can leverage special exception fixup provided by the vDSO. The kernel-provided
vDSO function wraps low-level transitions to/from the enclave like EENTER and
ERESUME. The vDSO function intercepts exceptions that would otherwise generate
a signal and return the fault information directly to its caller. This avoids
the need to juggle signal handlers.
.. kernel-doc:: arch/x86/include/uapi/asm/sgx.h
:functions: vdso_sgx_enter_enclave_t
ksgxd
=====
SGX support includes a kernel thread called *ksgxd*.
EPC sanitization
----------------
ksgxd is started when SGX initializes. Enclave memory is typically ready
for use when the processor powers on or resets. However, if SGX has been in
use since the reset, enclave pages may be in an inconsistent state. This might
occur after a crash and kexec() cycle, for instance. At boot, ksgxd
reinitializes all enclave pages so that they can be allocated and re-used.
The sanitization is done by going through EPC address space and applying the
EREMOVE function to each physical page. Some enclave pages like SECS pages have
hardware dependencies on other pages which prevents EREMOVE from functioning.
Executing two EREMOVE passes removes the dependencies.
Page reclaimer
--------------
Similar to the core kswapd, ksgxd, is responsible for managing the
overcommitment of enclave memory. If the system runs out of enclave memory,
*ksgxd* “swaps” enclave memory to normal memory.
Launch Control
==============
SGX provides a launch control mechanism. After all enclave pages have been
copied, kernel executes EINIT function, which initializes the enclave. Only after
this the CPU can execute inside the enclave.
EINIT function takes an RSA-3072 signature of the enclave measurement. The function
checks that the measurement is correct and signature is signed with the key
hashed to the four **IA32_SGXLEPUBKEYHASH{0, 1, 2, 3}** MSRs representing the
SHA256 of a public key.
Those MSRs can be configured by the BIOS to be either readable or writable.
Linux supports only writable configuration in order to give full control to the
kernel on launch control policy. Before calling EINIT function, the driver sets
the MSRs to match the enclave's signing key.
Encryption engines
==================
In order to conceal the enclave data while it is out of the CPU package, the
memory controller has an encryption engine to transparently encrypt and decrypt
enclave memory.
In CPUs prior to Ice Lake, the Memory Encryption Engine (MEE) is used to
encrypt pages leaving the CPU caches. MEE uses a n-ary Merkle tree with root in
SRAM to maintain integrity of the encrypted data. This provides integrity and
anti-replay protection but does not scale to large memory sizes because the time
required to update the Merkle tree grows logarithmically in relation to the
memory size.
CPUs starting from Icelake use Total Memory Encryption (TME) in the place of
MEE. TME-based SGX implementations do not have an integrity Merkle tree, which
means integrity and replay-attacks are not mitigated. B, it includes
additional changes to prevent cipher text from being returned and SW memory
aliases from being created.
DMA to enclave memory is blocked by range registers on both MEE and TME systems
(SDM section 41.10).
Usage Models
============
Shared Library
--------------
Sensitive data and the code that acts on it is partitioned from the application
into a separate library. The library is then linked as a DSO which can be loaded
into an enclave. The application can then make individual function calls into
the enclave through special SGX instructions. A run-time within the enclave is
configured to marshal function parameters into and out of the enclave and to
call the correct library function.
Application Container
---------------------
An application may be loaded into a container enclave which is specially
configured with a library OS and run-time which permits the application to run.
The enclave run-time and library OS work together to execute the application
when a thread enters the enclave.
Impact of Potential Kernel SGX Bugs
===================================
EPC leaks
---------
When EPC page leaks happen, a WARNING like this is shown in dmesg:
"EREMOVE returned ... and an EPC page was leaked. SGX may become unusable..."
This is effectively a kernel use-after-free of an EPC page, and due
to the way SGX works, the bug is detected at freeing. Rather than
adding the page back to the pool of available EPC pages, the kernel
intentionally leaks the page to avoid additional errors in the future.
When this happens, the kernel will likely soon leak more EPC pages, and
SGX will likely become unusable because the memory available to SGX is
limited. However, while this may be fatal to SGX, the rest of the kernel
is unlikely to be impacted and should continue to work.
As a result, when this happens, user should stop running any new
SGX workloads, (or just any new workloads), and migrate all valuable
workloads. Although a machine reboot can recover all EPC memory, the bug
should be reported to Linux developers.
Virtual EPC
===========
The implementation has also a virtual EPC driver to support SGX enclaves
in guests. Unlike the SGX driver, an EPC page allocated by the virtual
EPC driver doesn't have a specific enclave associated with it. This is
because KVM doesn't track how a guest uses EPC pages.
As a result, the SGX core page reclaimer doesn't support reclaiming EPC
pages allocated to KVM guests through the virtual EPC driver. If the
user wants to deploy SGX applications both on the host and in guests
on the same machine, the user should reserve enough EPC (by taking out
total virtual EPC size of all SGX VMs from the physical EPC size) for
host SGX applications so they can run with acceptable performance.
Architectural behavior is to restore all EPC pages to an uninitialized
state also after a guest reboot. Because this state can be reached only
through the privileged ``ENCLS[EREMOVE]`` instruction, ``/dev/sgx_vepc``
provides the ``SGX_IOC_VEPC_REMOVE_ALL`` ioctl to execute the instruction
on all pages in the virtual EPC.
``EREMOVE`` can fail for three reasons. Userspace must pay attention
to expected failures and handle them as follows:
1. Page removal will always fail when any thread is running in the
enclave to which the page belongs. In this case the ioctl will
return ``EBUSY`` independent of whether it has successfully removed
some pages; userspace can avoid these failures by preventing execution
of any vcpu which maps the virtual EPC.
2. Page removal will cause a general protection fault if two calls to
``EREMOVE`` happen concurrently for pages that refer to the same
"SECS" metadata pages. This can happen if there are concurrent
invocations to ``SGX_IOC_VEPC_REMOVE_ALL``, or if a ``/dev/sgx_vepc``
file descriptor in the guest is closed at the same time as
``SGX_IOC_VEPC_REMOVE_ALL``; it will also be reported as ``EBUSY``.
This can be avoided in userspace by serializing calls to the ioctl()
and to close(), but in general it should not be a problem.
3. Finally, page removal will fail for SECS metadata pages which still
have child pages. Child pages can be removed by executing
``SGX_IOC_VEPC_REMOVE_ALL`` on all ``/dev/sgx_vepc`` file descriptors
mapped into the guest. This means that the ioctl() must be called
twice: an initial set of calls to remove child pages and a subsequent
set of calls to remove SECS pages. The second set of calls is only
required for those mappings that returned a nonzero value from the
first call. It indicates a bug in the kernel or the userspace client
if any of the second round of ``SGX_IOC_VEPC_REMOVE_ALL`` calls has
a return code other than 0.
@@ -0,0 +1,179 @@
.. SPDX-License-Identifier: GPL-2.0
======================================================
Control-flow Enforcement Technology (CET) Shadow Stack
======================================================
CET Background
==============
Control-flow Enforcement Technology (CET) covers several related x86 processor
features that provide protection against control flow hijacking attacks. CET
can protect both applications and the kernel.
CET introduces shadow stack and indirect branch tracking (IBT). A shadow stack
is a secondary stack allocated from memory which cannot be directly modified by
applications. When executing a CALL instruction, the processor pushes the
return address to both the normal stack and the shadow stack. Upon
function return, the processor pops the shadow stack copy and compares it
to the normal stack copy. If the two differ, the processor raises a
control-protection fault. IBT verifies indirect CALL/JMP targets are intended
as marked by the compiler with 'ENDBR' opcodes. Not all CPU's have both Shadow
Stack and Indirect Branch Tracking. Today in the 64-bit kernel, only userspace
shadow stack and kernel IBT are supported.
Requirements to use Shadow Stack
================================
To use userspace shadow stack you need HW that supports it, a kernel
configured with it and userspace libraries compiled with it.
The kernel Kconfig option is X86_USER_SHADOW_STACK. When compiled in, shadow
stacks can be disabled at runtime with the kernel parameter: nousershstk.
To build a user shadow stack enabled kernel, Binutils v2.29 or LLVM v6 or later
are required.
At run time, /proc/cpuinfo shows CET features if the processor supports
CET. "user_shstk" means that userspace shadow stack is supported on the current
kernel and HW.
Application Enabling
====================
An application's CET capability is marked in its ELF note and can be verified
from readelf/llvm-readelf output::
readelf -n <application> | grep -a SHSTK
properties: x86 feature: SHSTK
The kernel does not process these applications markers directly. Applications
or loaders must enable CET features using the interface described in section 4.
Typically this would be done in dynamic loader or static runtime objects, as is
the case in GLIBC.
Enabling arch_prctl()'s
=======================
Elf features should be enabled by the loader using the below arch_prctl's. They
are only supported in 64 bit user applications. These operate on the features
on a per-thread basis. The enablement status is inherited on clone, so if the
feature is enabled on the first thread, it will propagate to all the thread's
in an app.
arch_prctl(ARCH_SHSTK_ENABLE, unsigned long feature)
Enable a single feature specified in 'feature'. Can only operate on
one feature at a time.
arch_prctl(ARCH_SHSTK_DISABLE, unsigned long feature)
Disable a single feature specified in 'feature'. Can only operate on
one feature at a time.
arch_prctl(ARCH_SHSTK_LOCK, unsigned long features)
Lock in features at their current enabled or disabled status. 'features'
is a mask of all features to lock. All bits set are processed, unset bits
are ignored. The mask is ORed with the existing value. So any feature bits
set here cannot be enabled or disabled afterwards.
arch_prctl(ARCH_SHSTK_UNLOCK, unsigned long features)
Unlock features. 'features' is a mask of all features to unlock. All
bits set are processed, unset bits are ignored. Only works via ptrace.
arch_prctl(ARCH_SHSTK_STATUS, unsigned long addr)
Copy the currently enabled features to the address passed in addr. The
features are described using the bits passed into the others in
'features'.
The return values are as follows. On success, return 0. On error, errno can
be::
-EPERM if any of the passed feature are locked.
-ENOTSUPP if the feature is not supported by the hardware or
kernel.
-EINVAL arguments (non existing feature, etc)
-EFAULT if could not copy information back to userspace
The feature's bits supported are::
ARCH_SHSTK_SHSTK - Shadow stack
ARCH_SHSTK_WRSS - WRSS
Currently shadow stack and WRSS are supported via this interface. WRSS
can only be enabled with shadow stack, and is automatically disabled
if shadow stack is disabled.
Proc Status
===========
To check if an application is actually running with shadow stack, the
user can read the /proc/$PID/status. It will report "wrss" or "shstk"
depending on what is enabled. The lines look like this::
x86_Thread_features: shstk wrss
x86_Thread_features_locked: shstk wrss
Implementation of the Shadow Stack
==================================
Shadow Stack Size
-----------------
A task's shadow stack is allocated from memory to a fixed size of
MIN(RLIMIT_STACK, 4 GB). In other words, the shadow stack is allocated to
the maximum size of the normal stack, but capped to 4 GB. In the case
of the clone3 syscall, there is a stack size passed in and shadow stack
uses this instead of the rlimit.
Signal
------
The main program and its signal handlers use the same shadow stack. Because
the shadow stack stores only return addresses, a large shadow stack covers
the condition that both the program stack and the signal alternate stack run
out.
When a signal happens, the old pre-signal state is pushed on the stack. When
shadow stack is enabled, the shadow stack specific state is pushed onto the
shadow stack. Today this is only the old SSP (shadow stack pointer), pushed
in a special format with bit 63 set. On sigreturn this old SSP token is
verified and restored by the kernel. The kernel will also push the normal
restorer address to the shadow stack to help userspace avoid a shadow stack
violation on the sigreturn path that goes through the restorer.
So the shadow stack signal frame format is as follows::
|1...old SSP| - Pointer to old pre-signal ssp in sigframe token format
(bit 63 set to 1)
| ...| - Other state may be added in the future
32 bit ABI signals are not supported in shadow stack processes. Linux prevents
32 bit execution while shadow stack is enabled by the allocating shadow stacks
outside of the 32 bit address space. When execution enters 32 bit mode, either
via far call or returning to userspace, a #GP is generated by the hardware
which, will be delivered to the process as a segfault. When transitioning to
userspace the register's state will be as if the userspace ip being returned to
caused the segfault.
Fork
----
The shadow stack's vma has VM_SHADOW_STACK flag set; its PTEs are required
to be read-only and dirty. When a shadow stack PTE is not RO and dirty, a
shadow access triggers a page fault with the shadow stack access bit set
in the page fault error code.
When a task forks a child, its shadow stack PTEs are copied and both the
parent's and the child's shadow stack PTEs are cleared of the dirty bit.
Upon the next shadow stack access, the resulting shadow stack page fault
is handled by page copy/reuse.
When a pthread child is created, the kernel allocates a new shadow stack
for the new thread. New shadow stack creation behaves like mmap() with respect
to ASLR behavior. Similarly, on thread exit the thread's shadow stack is
disabled.
Exec
----
On exec, shadow stack features are disabled by the kernel. At which point,
userspace can choose to re-enable, or lock them.
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 318 KiB

+286
View File
@@ -0,0 +1,286 @@
.. SPDX-License-Identifier: GPL-2.0
===========================================
Shared Virtual Addressing (SVA) with ENQCMD
===========================================
Background
==========
Shared Virtual Addressing (SVA) allows the processor and device to use the
same virtual addresses avoiding the need for software to translate virtual
addresses to physical addresses. SVA is what PCIe calls Shared Virtual
Memory (SVM).
In addition to the convenience of using application virtual addresses
by the device, it also doesn't require pinning pages for DMA.
PCIe Address Translation Services (ATS) along with Page Request Interface
(PRI) allow devices to function much the same way as the CPU handling
application page-faults. For more information please refer to the PCIe
specification Chapter 10: ATS Specification.
Use of SVA requires IOMMU support in the platform. IOMMU is also
required to support the PCIe features ATS and PRI. ATS allows devices
to cache translations for virtual addresses. The IOMMU driver uses the
mmu_notifier() support to keep the device TLB cache and the CPU cache in
sync. When an ATS lookup fails for a virtual address, the device should
use the PRI in order to request the virtual address to be paged into the
CPU page tables. The device must use ATS again in order to fetch the
translation before use.
Shared Hardware Workqueues
==========================
Unlike Single Root I/O Virtualization (SR-IOV), Scalable IOV (SIOV) permits
the use of Shared Work Queues (SWQ) by both applications and Virtual
Machines (VM's). This allows better hardware utilization vs. hard
partitioning resources that could result in under utilization. In order to
allow the hardware to distinguish the context for which work is being
executed in the hardware by SWQ interface, SIOV uses Process Address Space
ID (PASID), which is a 20-bit number defined by the PCIe SIG.
PASID value is encoded in all transactions from the device. This allows the
IOMMU to track I/O on a per-PASID granularity in addition to using the PCIe
Resource Identifier (RID) which is the Bus/Device/Function.
ENQCMD
======
ENQCMD is a new instruction on Intel platforms that atomically submits a
work descriptor to a device. The descriptor includes the operation to be
performed, virtual addresses of all parameters, virtual address of a completion
record, and the PASID (process address space ID) of the current process.
ENQCMD works with non-posted semantics and carries a status back if the
command was accepted by hardware. This allows the submitter to know if the
submission needs to be retried or other device specific mechanisms to
implement fairness or ensure forward progress should be provided.
ENQCMD is the glue that ensures applications can directly submit commands
to the hardware and also permits hardware to be aware of application context
to perform I/O operations via use of PASID.
Process Address Space Tagging
=============================
A new thread-scoped MSR (IA32_PASID) provides the connection between
user processes and the rest of the hardware. When an application first
accesses an SVA-capable device, this MSR is initialized with a newly
allocated PASID. The driver for the device calls an IOMMU-specific API
that sets up the routing for DMA and page-requests.
For example, the Intel Data Streaming Accelerator (DSA) uses
iommu_sva_bind_device(), which will do the following:
- Allocate the PASID, and program the process page-table (%cr3 register) in the
PASID context entries.
- Register for mmu_notifier() to track any page-table invalidations to keep
the device TLB in sync. For example, when a page-table entry is invalidated,
the IOMMU propagates the invalidation to the device TLB. This will force any
future access by the device to this virtual address to participate in
ATS. If the IOMMU responds with proper response that a page is not
present, the device would request the page to be paged in via the PCIe PRI
protocol before performing I/O.
This MSR is managed with the XSAVE feature set as "supervisor state" to
ensure the MSR is updated during context switch.
PASID Management
================
The kernel must allocate a PASID on behalf of each process which will use
ENQCMD and program it into the new MSR to communicate the process identity to
platform hardware. ENQCMD uses the PASID stored in this MSR to tag requests
from this process. When a user submits a work descriptor to a device using the
ENQCMD instruction, the PASID field in the descriptor is auto-filled with the
value from MSR_IA32_PASID. Requests for DMA from the device are also tagged
with the same PASID. The platform IOMMU uses the PASID in the transaction to
perform address translation. The IOMMU APIs setup the corresponding PASID
entry in IOMMU with the process address used by the CPU (e.g. %cr3 register in
x86).
The MSR must be configured on each logical CPU before any application
thread can interact with a device. Threads that belong to the same
process share the same page tables, thus the same MSR value.
PASID Life Cycle Management
===========================
PASID is initialized as IOMMU_PASID_INVALID (-1) when a process is created.
Only processes that access SVA-capable devices need to have a PASID
allocated. This allocation happens when a process opens/binds an SVA-capable
device but finds no PASID for this process. Subsequent binds of the same, or
other devices will share the same PASID.
Although the PASID is allocated to the process by opening a device,
it is not active in any of the threads of that process. It's loaded to the
IA32_PASID MSR lazily when a thread tries to submit a work descriptor
to a device using the ENQCMD.
That first access will trigger a #GP fault because the IA32_PASID MSR
has not been initialized with the PASID value assigned to the process
when the device was opened. The Linux #GP handler notes that a PASID has
been allocated for the process, and so initializes the IA32_PASID MSR
and returns so that the ENQCMD instruction is re-executed.
On fork(2) or exec(2) the PASID is removed from the process as it no
longer has the same address space that it had when the device was opened.
On clone(2) the new task shares the same address space, so will be
able to use the PASID allocated to the process. The IA32_PASID is not
preemptively initialized as the PASID value might not be allocated yet or
the kernel does not know whether this thread is going to access the device
and the cleared IA32_PASID MSR reduces context switch overhead by xstate
init optimization. Since #GP faults have to be handled on any threads that
were created before the PASID was assigned to the mm of the process, newly
created threads might as well be treated in a consistent way.
Due to complexity of freeing the PASID and clearing all IA32_PASID MSRs in
all threads in unbind, free the PASID lazily only on mm exit.
If a process does a close(2) of the device file descriptor and munmap(2)
of the device MMIO portal, then the driver will unbind the device. The
PASID is still marked VALID in the PASID_MSR for any threads in the
process that accessed the device. But this is harmless as without the
MMIO portal they cannot submit new work to the device.
Relationships
=============
* Each process has many threads, but only one PASID.
* Devices have a limited number (~10's to 1000's) of hardware workqueues.
The device driver manages allocating hardware workqueues.
* A single mmap() maps a single hardware workqueue as a "portal" and
each portal maps down to a single workqueue.
* For each device with which a process interacts, there must be
one or more mmap()'d portals.
* Many threads within a process can share a single portal to access
a single device.
* Multiple processes can separately mmap() the same portal, in
which case they still share one device hardware workqueue.
* The single process-wide PASID is used by all threads to interact
with all devices. There is not, for instance, a PASID for each
thread or each thread<->device pair.
FAQ
===
* What is SVA/SVM?
Shared Virtual Addressing (SVA) permits I/O hardware and the processor to
work in the same address space, i.e., to share it. Some call it Shared
Virtual Memory (SVM), but Linux community wanted to avoid confusing it with
POSIX Shared Memory and Secure Virtual Machines which were terms already in
circulation.
* What is a PASID?
A Process Address Space ID (PASID) is a PCIe-defined Transaction Layer Packet
(TLP) prefix. A PASID is a 20-bit number allocated and managed by the OS.
PASID is included in all transactions between the platform and the device.
* How are shared workqueues different?
Traditionally, in order for userspace applications to interact with hardware,
there is a separate hardware instance required per process. For example,
consider doorbells as a mechanism of informing hardware about work to process.
Each doorbell is required to be spaced 4k (or page-size) apart for process
isolation. This requires hardware to provision that space and reserve it in
MMIO. This doesn't scale as the number of threads becomes quite large. The
hardware also manages the queue depth for Shared Work Queues (SWQ), and
consumers don't need to track queue depth. If there is no space to accept
a command, the device will return an error indicating retry.
A user should check Deferrable Memory Write (DMWr) capability on the device
and only submits ENQCMD when the device supports it. In the new DMWr PCIe
terminology, devices need to support DMWr completer capability. In addition,
it requires all switch ports to support DMWr routing and must be enabled by
the PCIe subsystem, much like how PCIe atomic operations are managed for
instance.
SWQ allows hardware to provision just a single address in the device. When
used with ENQCMD to submit work, the device can distinguish the process
submitting the work since it will include the PASID assigned to that
process. This helps the device scale to a large number of processes.
* Is this the same as a user space device driver?
Communicating with the device via the shared workqueue is much simpler
than a full blown user space driver. The kernel driver does all the
initialization of the hardware. User space only needs to worry about
submitting work and processing completions.
* Is this the same as SR-IOV?
Single Root I/O Virtualization (SR-IOV) focuses on providing independent
hardware interfaces for virtualizing hardware. Hence, it's required to be
an almost fully functional interface to software supporting the traditional
BARs, space for interrupts via MSI-X, its own register layout.
Virtual Functions (VFs) are assisted by the Physical Function (PF)
driver.
Scalable I/O Virtualization builds on the PASID concept to create device
instances for virtualization. SIOV requires host software to assist in
creating virtual devices; each virtual device is represented by a PASID
along with the bus/device/function of the device. This allows device
hardware to optimize device resource creation and can grow dynamically on
demand. SR-IOV creation and management is very static in nature. Consult
references below for more details.
* Why not just create a virtual function for each app?
Creating PCIe SR-IOV type Virtual Functions (VF) is expensive. VFs require
duplicated hardware for PCI config space and interrupts such as MSI-X.
Resources such as interrupts have to be hard partitioned between VFs at
creation time, and cannot scale dynamically on demand. The VFs are not
completely independent from the Physical Function (PF). Most VFs require
some communication and assistance from the PF driver. SIOV, in contrast,
creates a software-defined device where all the configuration and control
aspects are mediated via the slow path. The work submission and completion
happen without any mediation.
* Does this support virtualization?
ENQCMD can be used from within a guest VM. In these cases, the VMM helps
with setting up a translation table to translate from Guest PASID to Host
PASID. Please consult the ENQCMD instruction set reference for more
details.
* Does memory need to be pinned?
When devices support SVA along with platform hardware such as IOMMU
supporting such devices, there is no need to pin memory for DMA purposes.
Devices that support SVA also support other PCIe features that remove the
pinning requirement for memory.
Device TLB support - Device requests the IOMMU to lookup an address before
use via Address Translation Service (ATS) requests. If the mapping exists
but there is no page allocated by the OS, IOMMU hardware returns that no
mapping exists.
Device requests the virtual address to be mapped via Page Request
Interface (PRI). Once the OS has successfully completed the mapping, it
returns the response back to the device. The device requests again for
a translation and continues.
IOMMU works with the OS in managing consistency of page-tables with the
device. When removing pages, it interacts with the device to remove any
device TLB entry that might have been cached before removing the mappings from
the OS.
References
==========
VT-D:
https://01.org/blogs/ashokraj/2018/recent-enhancements-intel-virtualization-technology-directed-i/o-intel-vt-d
SIOV:
https://01.org/blogs/2019/assignable-interfaces-intel-scalable-i/o-virtualization-linux
ENQCMD in ISE:
https://software.intel.com/sites/default/files/managed/c5/15/architecture-instruction-set-extensions-programming-reference.pdf
DSA spec:
https://software.intel.com/sites/default/files/341204-intel-data-streaming-accelerator-spec.pdf
+540
View File
@@ -0,0 +1,540 @@
.. SPDX-License-Identifier: GPL-2.0
=====================================
Intel Trust Domain Extensions (TDX)
=====================================
Intel's Trust Domain Extensions (TDX) protect confidential guest VMs from
the host and physical attacks by isolating the guest register state and by
encrypting the guest memory. In TDX, a special module running in a special
mode sits between the host and the guest and manages the guest/host
separation.
TDX Host Kernel Support
=======================
TDX introduces a new CPU mode called Secure Arbitration Mode (SEAM) and
a new isolated range pointed by the SEAM Ranger Register (SEAMRR). A
CPU-attested software module called 'the TDX module' runs inside the new
isolated range to provide the functionalities to manage and run protected
VMs.
TDX also leverages Intel Multi-Key Total Memory Encryption (MKTME) to
provide crypto-protection to the VMs. TDX reserves part of MKTME KeyIDs
as TDX private KeyIDs, which are only accessible within the SEAM mode.
BIOS is responsible for partitioning legacy MKTME KeyIDs and TDX KeyIDs.
Before the TDX module can be used to create and run protected VMs, it
must be loaded into the isolated range and properly initialized. The TDX
architecture doesn't require the BIOS to load the TDX module, but the
kernel assumes it is loaded by the BIOS.
TDX boot-time detection
-----------------------
The kernel detects TDX by detecting TDX private KeyIDs during kernel
boot. Below dmesg shows when TDX is enabled by BIOS::
[..] virt/tdx: BIOS enabled: private KeyID range: [16, 64)
TDX module initialization
---------------------------------------
The kernel talks to the TDX module via the new SEAMCALL instruction. The
TDX module implements SEAMCALL leaf functions to allow the kernel to
initialize it.
If the TDX module isn't loaded, the SEAMCALL instruction fails with a
special error. In this case the kernel fails the module initialization
and reports the module isn't loaded::
[..] virt/tdx: module not loaded
Initializing the TDX module consumes roughly ~1/256th system RAM size to
use it as 'metadata' for the TDX memory. It also takes additional CPU
time to initialize those metadata along with the TDX module itself. Both
are not trivial. The kernel initializes the TDX module at runtime on
demand.
Besides initializing the TDX module, a per-cpu initialization SEAMCALL
must be done on one cpu before any other SEAMCALLs can be made on that
cpu.
User can consult dmesg to see whether the TDX module has been initialized.
If the TDX module is initialized successfully, dmesg shows something
like below::
[..] virt/tdx: 262668 KBs allocated for PAMT
[..] virt/tdx: TDX-Module initialized
If the TDX module failed to initialize, dmesg also shows it failed to
initialize::
[..] virt/tdx: TDX-Module initialization failed ...
TDX module Runtime Update
-------------------------
Similar to microcode, the BIOS generally has a copy of the TDX module
in flash. It loads this module image in to RAM at boot. However, just
like microcode, the BIOS-loaded TDX module might be out of date either
because the BIOS is old or the system has been up a long time. The
kernel can replace the BIOS version in RAM and load a different TDX
module. Kernel-loaded TDX modules do not affect the BIOS flash and do
not survive reboots.
The TDX module is normally the only piece of software running in SEAM
mode with which the kernel interacts. But there is a second piece of
software which is used to load or update the TDX module: a persistent
SEAM loader (P-SEAMLDR). It runs in SEAM mode separately from the TDX
module. The kernel communicates with the P-SEAMLDR to perform TDX
module runtime updates.
How to update the TDX module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Updating the TDX module is a complex process. Much of the logic and
policy is left to userspace. End users should use existing update
infrastructure provided by their distro. The Intel TDX Module Binaries
repository has a reference implementation of this logic:
https://github.com/intel/confidential-computing.tdx.tdx-module.binaries/blob/main/version_select_and_load.py
This section will now lay out roughly what is needed to implement a
userspace-driven TDX module update. Detailed documentation on the
tdx_host ABIs is available here::
Documentation/ABI/testing/sysfs-devices-faux-tdx-host
and is not duplicated in this document.
1. Check whether runtime update is supported at all
Verify that the TDX module firmware upload interface is available::
/sys/class/firmware/tdx_module
Note that this is the generic kernel firmware update ABI. It is
separate from the "tdx_host" device ABI itself.
2. Check whether additional updates are possible. Verify that::
/sys/devices/faux/tdx_host/num_remaining_updates
has a value greater than 0. If it is 0, the TDX update log might be
full. Reboot to reset this to a nonzero value.
3. Choose a compatible TDX module image
Choosing a compatible TDX module image is not trivial. There are both
hard compatibility requirements and policy choices to make.
Hard compatibility requirements:
- The update must be compatible with the kernel.
The update must not change any TDX ABIs in any non-backward-compatible
way. It can introduce new features but must not require that the kernel
use new ABIs for existing features. It must ensure that the rest of the
system is not affected in any way. Software on the system must never
notice any behavioral changes. Attestation results should be identical
except for version changes.
- The update must be compatible with the CPU.
The set of supported CPU FMS values (family, model, stepping) is
encoded in the module image itself. In practice, module version series
are platform-specific. For example, the 1.5.x series runs on Sapphire
Rapids but not Granite Rapids, which needs 2.0.x.
- The update must be compatible with the P-SEAMLDR.
This information is provided in a metadata file, typically
mapping_file.json, released with the module image. Each module image
specifies the minimum required P-SEAMLDR version, and the update is
compatible only if the running P-SEAMLDR meets that requirement.
The current version of the P-SEAMLDR can be read here::
/sys/devices/faux/tdx_host/seamldr_version
- The update must be compatible with the running TDX module.
Like P-SEAMLDR, each module image also specifies a minimum required
TDX module version. The running module must satisfy that requirement.
The update software can read the current TDX module version here::
/sys/devices/faux/tdx_host/version
Policy choices:
- The update software chooses how to optimize its update. For instance,
it can optimize for fewer updates or for smaller version steps,
for example, 1.2.3 => 1.2.5 versus 1.2.3 => 1.2.4 => 1.2.5.
4. Perform the update
Run::
echo 1 > /sys/class/firmware/tdx_module/loading
cat <path_to_module_image> > /sys/class/firmware/tdx_module/data
echo 0 > /sys/class/firmware/tdx_module/loading
The files /sys/class/firmware/tdx_module/status and
/sys/class/firmware/tdx_module/error report update progress and error
information.
After the update completes, the new module version is visible in
/sys/devices/faux/tdx_host/version.
Impact on running TDs
~~~~~~~~~~~~~~~~~~~~~
TDX module runtime updates must have virtually no visible impact on running
TDs. Any TD visible impact is a TDX module bug.
The main exception is the TEE_TCB_SVN_2 field in TD quotes, which
reflects the TCB of the currently running TDX module and therefore
changes after an update. By contrast, TEE_TCB_SVN reflects the TCB at TD
launch time and is not affected.
TDX Interaction to Other Kernel Components
------------------------------------------
TDX Memory Policy
~~~~~~~~~~~~~~~~~
TDX reports a list of "Convertible Memory Region" (CMR) to tell the
kernel which memory is TDX compatible. The kernel needs to build a list
of memory regions (out of CMRs) as "TDX-usable" memory and pass those
regions to the TDX module. Once this is done, those "TDX-usable" memory
regions are fixed during module's lifetime.
To keep things simple, currently the kernel simply guarantees all pages
in the page allocator are TDX memory. Specifically, the kernel uses all
system memory in the core-mm "at the time of TDX module initialization"
as TDX memory, and in the meantime, refuses to online any non-TDX-memory
in the memory hotplug.
Physical Memory Hotplug
~~~~~~~~~~~~~~~~~~~~~~~
Note TDX assumes convertible memory is always physically present during
machine's runtime. A non-buggy BIOS should never support hot-removal of
any convertible memory. This implementation doesn't handle ACPI memory
removal but depends on the BIOS to behave correctly.
CPU Hotplug
~~~~~~~~~~~
TDX module requires the per-cpu initialization SEAMCALL must be done on
one cpu before any other SEAMCALLs can be made on that cpu. The kernel,
via the CPU hotplug framework, performs the necessary initialization when
a CPU is first brought online.
TDX doesn't support physical (ACPI) CPU hotplug. During machine boot,
TDX verifies all boot-time present logical CPUs are TDX compatible before
enabling TDX. A non-buggy BIOS should never support hot-add/removal of
physical CPU. Currently the kernel doesn't handle physical CPU hotplug,
but depends on the BIOS to behave correctly.
Note TDX works with CPU logical online/offline, thus the kernel still
allows to offline logical CPU and online it again.
Erratum
~~~~~~~
The first few generations of TDX hardware have an erratum. A partial
write to a TDX private memory cacheline will silently "poison" the
line. Subsequent reads will consume the poison and generate a machine
check.
A partial write is a memory write where a write transaction of less than
cacheline lands at the memory controller. The CPU does these via
non-temporal write instructions (like MOVNTI), or through UC/WC memory
mappings. Devices can also do partial writes via DMA.
Theoretically, a kernel bug could do partial write to TDX private memory
and trigger unexpected machine check. What's more, the machine check
code will present these as "Hardware error" when they were, in fact, a
software-triggered issue. But in the end, this issue is hard to trigger.
If the platform has such erratum, the kernel prints additional message in
machine check handler to tell user the machine check may be caused by
kernel bug on TDX private memory.
Interaction vs S3 and deeper states
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TDX cannot survive from S3 and deeper states. The hardware resets and
disables TDX completely when platform goes to S3 and deeper. Both TDX
guests and the TDX module get destroyed permanently.
The kernel uses S3 for suspend-to-ram, and use S4 and deeper states for
hibernation. Currently, for simplicity, the kernel chooses to make TDX
mutually exclusive with S3 and hibernation.
The kernel disables TDX during early boot when hibernation support is
available::
[..] virt/tdx: initialization failed: Hibernation support is enabled
Add 'nohibernate' kernel command line to disable hibernation in order to
use TDX.
ACPI S3 is disabled during kernel early boot if TDX is enabled. The user
needs to turn off TDX in the BIOS in order to use S3.
TDX Guest Support
=================
Since the host cannot directly access guest registers or memory, much
normal functionality of a hypervisor must be moved into the guest. This is
implemented using a Virtualization Exception (#VE) that is handled by the
guest kernel. A #VE is handled entirely inside the guest kernel, but some
require the hypervisor to be consulted.
TDX includes new hypercall-like mechanisms for communicating from the
guest to the hypervisor or the TDX module.
New TDX Exceptions
------------------
TDX guests behave differently from bare-metal and traditional VMX guests.
In TDX guests, otherwise normal instructions or memory accesses can cause
#VE or #GP exceptions.
Instructions marked with an '*' conditionally cause exceptions. The
details for these instructions are discussed below.
Instruction-based #VE
~~~~~~~~~~~~~~~~~~~~~
- Port I/O (INS, OUTS, IN, OUT)
- HLT
- MONITOR, MWAIT
- WBINVD, INVD
- VMCALL
- RDMSR*,WRMSR*
- CPUID*
Instruction-based #GP
~~~~~~~~~~~~~~~~~~~~~
- All VMX instructions: INVEPT, INVVPID, VMCLEAR, VMFUNC, VMLAUNCH,
VMPTRLD, VMPTRST, VMREAD, VMRESUME, VMWRITE, VMXOFF, VMXON
- ENCLS, ENCLU
- GETSEC
- RSM
- ENQCMD
- RDMSR*,WRMSR*
RDMSR/WRMSR Behavior
~~~~~~~~~~~~~~~~~~~~
MSR access behavior falls into three categories:
- #GP generated
- #VE generated
- "Just works"
In general, the #GP MSRs should not be used in guests. Their use likely
indicates a bug in the guest. The guest may try to handle the #GP with a
hypercall but it is unlikely to succeed.
The #VE MSRs are typically able to be handled by the hypervisor. Guests
can make a hypercall to the hypervisor to handle the #VE.
The "just works" MSRs do not need any special guest handling. They might
be implemented by directly passing through the MSR to the hardware or by
trapping and handling in the TDX module. Other than possibly being slow,
these MSRs appear to function just as they would on bare metal.
CPUID Behavior
~~~~~~~~~~~~~~
For some CPUID leaves and sub-leaves, the virtualized bit fields of CPUID
return values (in guest EAX/EBX/ECX/EDX) are configurable by the
hypervisor. For such cases, the Intel TDX module architecture defines two
virtualization types:
- Bit fields for which the hypervisor controls the value seen by the guest
TD.
- Bit fields for which the hypervisor configures the value such that the
guest TD either sees their native value or a value of 0. For these bit
fields, the hypervisor can mask off the native values, but it can not
turn *on* values.
A #VE is generated for CPUID leaves and sub-leaves that the TDX module does
not know how to handle. The guest kernel may ask the hypervisor for the
value with a hypercall.
#VE on Memory Accesses
----------------------
There are essentially two classes of TDX memory: private and shared.
Private memory receives full TDX protections. Its content is protected
against access from the hypervisor. Shared memory is expected to be
shared between guest and hypervisor and does not receive full TDX
protections.
A TD guest is in control of whether its memory accesses are treated as
private or shared. It selects the behavior with a bit in its page table
entries. This helps ensure that a guest does not place sensitive
information in shared memory, exposing it to the untrusted hypervisor.
#VE on Shared Memory
~~~~~~~~~~~~~~~~~~~~
Access to shared mappings can cause a #VE. The hypervisor ultimately
controls whether a shared memory access causes a #VE, so the guest must be
careful to only reference shared pages it can safely handle a #VE. For
instance, the guest should be careful not to access shared memory in the
#VE handler before it reads the #VE info structure (TDG.VP.VEINFO.GET).
Shared mapping content is entirely controlled by the hypervisor. The guest
should only use shared mappings for communicating with the hypervisor.
Shared mappings must never be used for sensitive memory content like kernel
stacks. A good rule of thumb is that hypervisor-shared memory should be
treated the same as memory mapped to userspace. Both the hypervisor and
userspace are completely untrusted.
MMIO for virtual devices is implemented as shared memory. The guest must
be careful not to access device MMIO regions unless it is also prepared to
handle a #VE.
#VE on Private Pages
~~~~~~~~~~~~~~~~~~~~
An access to private mappings can also cause a #VE. Since all kernel
memory is also private memory, the kernel might theoretically need to
handle a #VE on arbitrary kernel memory accesses. This is not feasible, so
TDX guests ensure that all guest memory has been "accepted" before memory
is used by the kernel.
A modest amount of memory (typically 512M) is pre-accepted by the firmware
before the kernel runs to ensure that the kernel can start up without
being subjected to a #VE.
The hypervisor is permitted to unilaterally move accepted pages to a
"blocked" state. However, if it does this, page access will not generate a
#VE. It will, instead, cause a "TD Exit" where the hypervisor is required
to handle the exception.
Linux #VE handler
-----------------
Just like page faults or #GP's, #VE exceptions can be either handled or be
fatal. Typically, an unhandled userspace #VE results in a SIGSEGV.
An unhandled kernel #VE results in an oops.
Handling nested exceptions on x86 is typically nasty business. A #VE
could be interrupted by an NMI which triggers another #VE and hilarity
ensues. The TDX #VE architecture anticipated this scenario and includes a
feature to make it slightly less nasty.
During #VE handling, the TDX module ensures that all interrupts (including
NMIs) are blocked. The block remains in place until the guest makes a
TDG.VP.VEINFO.GET TDCALL. This allows the guest to control when interrupts
or a new #VE can be delivered.
However, the guest kernel must still be careful to avoid potential
#VE-triggering actions (discussed above) while this block is in place.
While the block is in place, any #VE is elevated to a double fault (#DF)
which is not recoverable.
MMIO handling
-------------
In non-TDX VMs, MMIO is usually implemented by giving a guest access to a
mapping which will cause a VMEXIT on access, and then the hypervisor
emulates the access. That is not possible in TDX guests because VMEXIT
will expose the register state to the host. TDX guests don't trust the host
and can't have their state exposed to the host.
In TDX, MMIO regions typically trigger a #VE exception in the guest. The
guest #VE handler then emulates the MMIO instruction inside the guest and
converts it into a controlled TDCALL to the host, rather than exposing
guest state to the host.
MMIO addresses on x86 are just special physical addresses. They can
theoretically be accessed with any instruction that accesses memory.
However, the kernel instruction decoding method is limited. It is only
designed to decode instructions like those generated by io.h macros.
MMIO access via other means (like structure overlays) may result in an
oops.
Shared Memory Conversions
-------------------------
All TDX guest memory starts out as private at boot. This memory can not
be accessed by the hypervisor. However, some kernel users like device
drivers might have a need to share data with the hypervisor. To do this,
memory must be converted between shared and private. This can be
accomplished using some existing memory encryption helpers:
* set_memory_decrypted() converts a range of pages to shared.
* set_memory_encrypted() converts memory back to private.
Device drivers are the primary user of shared memory, but there's no need
to touch every driver. DMA buffers and ioremap() do the conversions
automatically.
TDX uses SWIOTLB for most DMA allocations. The SWIOTLB buffer is
converted to shared on boot.
For coherent DMA allocation, the DMA buffer gets converted on the
allocation. Check force_dma_unencrypted() for details.
Attestation
===========
Attestation is used to verify the TDX guest trustworthiness to other
entities before provisioning secrets to the guest. For example, a key
server may want to use attestation to verify that the guest is the
desired one before releasing the encryption keys to mount the encrypted
rootfs or a secondary drive.
The TDX module records the state of the TDX guest in various stages of
the guest boot process using the build time measurement register (MRTD)
and runtime measurement registers (RTMR). Measurements related to the
guest initial configuration and firmware image are recorded in the MRTD
register. Measurements related to initial state, kernel image, firmware
image, command line options, initrd, ACPI tables, etc are recorded in
RTMR registers. For more details, as an example, please refer to TDX
Virtual Firmware design specification, section titled "TD Measurement".
At TDX guest runtime, the attestation process is used to attest to these
measurements.
The attestation process consists of two steps: TDREPORT generation and
Quote generation.
TDX guest uses TDCALL[TDG.MR.REPORT] to get the TDREPORT (TDREPORT_STRUCT)
from the TDX module. TDREPORT is a fixed-size data structure generated by
the TDX module which contains guest-specific information (such as build
and boot measurements), platform security version, and the MAC to protect
the integrity of the TDREPORT. A user-provided 64-Byte REPORTDATA is used
as input and included in the TDREPORT. Typically it can be some nonce
provided by attestation service so the TDREPORT can be verified uniquely.
More details about the TDREPORT can be found in Intel TDX Module
specification, section titled "TDG.MR.REPORT Leaf".
After getting the TDREPORT, the second step of the attestation process
is to send it to the Quoting Enclave (QE) to generate the Quote. TDREPORT
by design can only be verified on the local platform as the MAC key is
bound to the platform. To support remote verification of the TDREPORT,
TDX leverages Intel SGX Quoting Enclave to verify the TDREPORT locally
and convert it to a remotely verifiable Quote. Method of sending TDREPORT
to QE is implementation specific. Attestation software can choose
whatever communication channel available (i.e. vsock or TCP/IP) to
send the TDREPORT to QE and receive the Quote.
References
==========
TDX reference material is collected here:
https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html
@@ -0,0 +1,83 @@
.. SPDX-License-Identifier: GPL-2.0
=======
The TLB
=======
When the kernel unmaps or modified the attributes of a range of
memory, it has two choices:
1. Flush the entire TLB with a two-instruction sequence. This is
a quick operation, but it causes collateral damage: TLB entries
from areas other than the one we are trying to flush will be
destroyed and must be refilled later, at some cost.
2. Use the invlpg instruction to invalidate a single page at a
time. This could potentially cost many more instructions, but
it is a much more precise operation, causing no collateral
damage to other TLB entries.
Which method to do depends on a few things:
1. The size of the flush being performed. A flush of the entire
address space is obviously better performed by flushing the
entire TLB than doing 2^48/PAGE_SIZE individual flushes.
2. The contents of the TLB. If the TLB is empty, then there will
be no collateral damage caused by doing the global flush, and
all of the individual flush will have ended up being wasted
work.
3. The size of the TLB. The larger the TLB, the more collateral
damage we do with a full flush. So, the larger the TLB, the
more attractive an individual flush looks. Data and
instructions have separate TLBs, as do different page sizes.
4. The microarchitecture. The TLB has become a multi-level
cache on modern CPUs, and the global flushes have become more
expensive relative to single-page flushes.
There is obviously no way the kernel can know all these things,
especially the contents of the TLB during a given flush. The
sizes of the flush will vary greatly depending on the workload as
well. There is essentially no "right" point to choose.
You may be doing too many individual invalidations if you see the
invlpg instruction (or instructions _near_ it) show up high in
profiles. If you believe that individual invalidations being
called too often, you can lower the tunable::
/sys/kernel/debug/x86/tlb_single_page_flush_ceiling
This will cause us to do the global flush for more cases.
Lowering it to 0 will disable the use of the individual flushes.
Setting it to 1 is a very conservative setting and it should
never need to be 0 under normal circumstances.
Despite the fact that a single individual flush on x86 is
guaranteed to flush a full 2MB [1]_, hugetlbfs always uses the full
flushes. THP is treated exactly the same as normal memory.
You might see invlpg inside of flush_tlb_mm_range() show up in
profiles, or you can use the trace_tlb_flush() tracepoints. to
determine how long the flush operations are taking.
Essentially, you are balancing the cycles you spend doing invlpg
with the cycles that you spend refilling the TLB later.
You can measure how expensive TLB refills are by using
performance counters and 'perf stat', like this::
perf stat -e
cpu/event=0x8,umask=0x84,name=dtlb_load_misses_walk_duration/,
cpu/event=0x8,umask=0x82,name=dtlb_load_misses_walk_completed/,
cpu/event=0x49,umask=0x4,name=dtlb_store_misses_walk_duration/,
cpu/event=0x49,umask=0x2,name=dtlb_store_misses_walk_completed/,
cpu/event=0x85,umask=0x4,name=itlb_misses_walk_duration/,
cpu/event=0x85,umask=0x2,name=itlb_misses_walk_completed/
That works on an IvyBridge-era CPU (i5-3320M). Different CPUs
may have differently-named counters, but they should at least
be there in some form. You can use pmu-tools 'ocperf list'
(https://github.com/andikleen/pmu-tools) to find the right
counters for a given CPU.
.. [1] A footnote in Intel's SDM "4.10.4.2 Recommended Invalidation"
says: "One execution of INVLPG is sufficient even for a page
with size greater than 4 KBytes."
@@ -0,0 +1,421 @@
.. SPDX-License-Identifier: GPL-2.0
============
x86 Topology
============
This documents and clarifies the main aspects of x86 topology modelling and
representation in the kernel. Update/change when doing changes to the
respective code.
The architecture-agnostic topology definitions are in
Documentation/admin-guide/cputopology.rst. This file holds x86-specific
differences/specialities which must not necessarily apply to the generic
definitions. Thus, the way to read up on Linux topology on x86 is to start
with the generic one and look at this one in parallel for the x86 specifics.
Needless to say, code should use the generic functions - this file is *only*
here to *document* the inner workings of x86 topology.
Started by Thomas Gleixner <tglx@kernel.org> and Borislav Petkov <bp@alien8.de>.
The main aim of the topology facilities is to present adequate interfaces to
code which needs to know/query/use the structure of the running system wrt
threads, cores, packages, etc.
The kernel does not care about the concept of physical sockets because a
socket has no relevance to software. It's an electromechanical component. In
the past a socket always contained a single package (see below), but with the
advent of Multi Chip Modules (MCM) a socket can hold more than one package. So
there might be still references to sockets in the code, but they are of
historical nature and should be cleaned up.
The topology of a system is described in the units of:
- packages
- cores
- threads
Package
=======
Packages contain a number of cores plus shared resources, e.g. DRAM
controller, shared caches etc.
Modern systems may also use the term 'Die' for package.
AMD nomenclature for package is 'Node'.
Package-related topology information in the kernel:
- topology_num_threads_per_package()
The number of threads in a package.
- topology_num_cores_per_package()
The number of cores in a package.
- topology_max_dies_per_package()
The maximum number of dies in a package.
- cpuinfo_x86.topo.die_id:
The physical ID of the die.
- cpuinfo_x86.topo.pkg_id:
The physical ID of the package. This information is retrieved via CPUID
and deduced from the APIC IDs of the cores in the package.
Modern systems use this value for the socket. There may be multiple
packages within a socket. This value may differ from topo.die_id.
- cpuinfo_x86.topo.logical_pkg_id:
The logical ID of the package. As we do not trust BIOSes to enumerate the
packages in a consistent way, we introduced the concept of logical package
ID so we can sanely calculate the number of maximum possible packages in
the system and have the packages enumerated linearly.
- topology_max_packages():
The maximum possible number of packages in the system. Helpful for per
package facilities to preallocate per package information.
- cpuinfo_x86.topo.llc_id:
- On Intel, the first APIC ID of the list of CPUs sharing the Last Level
Cache
- On AMD, the Node ID or Core Complex ID containing the Last Level
Cache. In general, it is a number identifying an LLC uniquely on the
system.
Cores
=====
A core consists of 1 or more threads. It does not matter whether the threads
are SMT- or CMT-type threads.
AMDs nomenclature for a CMT core is "Compute Unit". The kernel always uses
"core".
Threads
=======
A thread is a single scheduling unit. It's the equivalent to a logical Linux
CPU.
AMDs nomenclature for CMT threads is "Compute Unit Core". The kernel always
uses "thread".
Thread-related topology information in the kernel:
- topology_core_cpumask():
The cpumask contains all online threads in the package to which a thread
belongs.
The number of online threads is also printed in /proc/cpuinfo "siblings."
- topology_sibling_cpumask():
The cpumask contains all online threads in the core to which a thread
belongs.
- topology_logical_package_id():
The logical package ID to which a thread belongs.
- topology_physical_package_id():
The physical package ID to which a thread belongs.
- topology_core_id();
The ID of the core to which a thread belongs. It is also printed in /proc/cpuinfo
"core_id."
- topology_logical_core_id();
The logical core ID to which a thread belongs.
System topology enumeration
===========================
The topology on x86 systems can be discovered using a combination of vendor
specific CPUID leaves which enumerate the processor topology and the cache
hierarchy.
The CPUID leaves in their preferred order of parsing for each x86 vendor is as
follows:
1) AMD
1) CPUID leaf 0x80000026 [Extended CPU Topology] (Core::X86::Cpuid::ExCpuTopology)
The extended CPUID leaf 0x80000026 is the extension of the CPUID leaf 0xB
and provides the topology information of Core, Complex, CCD (Die), and
Socket in each level.
Support for the leaf is discovered by checking if the maximum extended
CPUID level is >= 0x80000026 and then checking if `LogProcAtThisLevel`
in `EBX[15:0]` at a particular level (starting from 0) is non-zero.
The `LevelType` in `ECX[15:8]` at the level provides the topology domain
the level describes - Core, Complex, CCD(Die), or the Socket.
The kernel uses the `CoreMaskWidth` from `EAX[4:0]` to discover the
number of bits that need to be right-shifted from `ExtendedLocalApicId`
in `EDX[31:0]` in order to get a unique Topology ID for the topology
level. CPUs with the same Topology ID share the resources at that level.
CPUID leaf 0x80000026 also provides more information regarding the power
and efficiency rankings, and about the core type on AMD processors with
heterogeneous characteristics.
If CPUID leaf 0x80000026 is supported, further parsing is not required.
2) CPUID leaf 0x0000000B [Extended Topology Enumeration] (Core::X86::Cpuid::ExtTopEnum)
The extended CPUID leaf 0x0000000B is the predecessor on the extended
CPUID leaf 0x80000026 and only describes the core, and the socket domains
of the processor topology.
The support for the leaf is discovered by checking if the maximum supported
CPUID level is >= 0xB and then if `EBX[31:0]` at a particular level
(starting from 0) is non-zero.
The `LevelType` in `ECX[15:8]` at the level provides the topology domain
that the level describes - Thread, or Processor (Socket).
The kernel uses the `CoreMaskWidth` from `EAX[4:0]` to discover the
number of bits that need to be right-shifted from the `ExtendedLocalApicId`
in `EDX[31:0]` to get a unique Topology ID for that topology level. CPUs
sharing the Topology ID share the resources at that level.
If CPUID leaf 0xB is supported, further parsing is not required.
3) CPUID leaf 0x80000008 ECX [Size Identifiers] (Core::X86::Cpuid::SizeId)
If neither the CPUID leaf 0x80000026 nor 0xB is supported, the number of
CPUs on the package is detected using the Size Identifier leaf
0x80000008 ECX.
The support for the leaf is discovered by checking if the supported
extended CPUID level is >= 0x80000008.
The shifts from the APIC ID for the Socket ID is calculated from the
`ApicIdSize` field in `ECX[15:12]` if it is non-zero.
If `ApicIdSize` is reported to be zero, the shift is calculated as the
order of the `number of threads` calculated from `NC` field in
`ECX[7:0]` which describes the `number of threads - 1` on the package.
Unless Extended APIC ID is supported, the APIC ID used to find the
Socket ID is from the `LocalApicId` field of CPUID leaf 0x00000001
`EBX[31:24]`.
The topology parsing continues to detect if Extended APIC ID is
supported or not.
4) CPUID leaf 0x8000001E [Extended APIC ID, Core Identifiers, Node Identifiers]
(Core::X86::Cpuid::{ExtApicId,CoreId,NodeId})
The support for Extended APIC ID can be detected by checking for the
presence of `TopologyExtensions` in `ECX[22]` of CPUID leaf 0x80000001
[Feature Identifiers] (Core::X86::Cpuid::FeatureExtIdEcx).
If Topology Extensions is supported, the APIC ID from `ExtendedApicId`
from CPUID leaf 0x8000001E `EAX[31:0]` should be preferred over that from
`LocalApicId` field of CPUID leaf 0x00000001 `EBX[31:24]` for topology
enumeration.
On processors of Family 0x17 and above that do not support CPUID leaf
0x80000026 or CPUID leaf 0xB, the shifts from the APIC ID for the Core
ID is calculated using the order of `number of threads per core`
calculated using the `ThreadsPerCore` field in `EBX[15:8]` which
describes `number of threads per core - 1`.
On Processors of Family 0x15, the Core ID from `EBX[7:0]` is used as the
`cu_id` (Compute Unit ID) to detect CPUs that share the compute units.
All AMD processors that support the `TopologyExtensions` feature store the
`NodeId` from the `ECX[7:0]` of CPUID leaf 0x8000001E
(Core::X86::Cpuid::NodeId) as the per-CPU `node_id`. On older processors,
the `node_id` was discovered using MSR_FAM10H_NODE_ID MSR (MSR
0x0xc001_100c). The presence of the NODE_ID MSR was detected by checking
`ECX[19]` of CPUID leaf 0x80000001 [Feature Identifiers]
(Core::X86::Cpuid::FeatureExtIdEcx).
2) Intel
On Intel platforms, the CPUID leaves that enumerate the processor
topology are as follows:
1) CPUID leaf 0x1F (V2 Extended Topology Enumeration Leaf)
The CPUID leaf 0x1F is the extension of the CPUID leaf 0xB and provides
the topology information of Core, Module, Tile, Die, DieGrp, and Socket
in each level.
The support for the leaf is discovered by checking if the supported
CPUID level is >= 0x1F and then `EBX[31:0]` at a particular level
(starting from 0) is non-zero.
The `Domain Type` in `ECX[15:8]` of the sub-leaf provides the topology
domain that the level describes - Core, Module, Tile, Die, DieGrp, and
Socket.
The kernel uses the value from `EAX[4:0]` to discover the number of
bits that need to be right shifted from the `x2APIC ID` in `EDX[31:0]`
to get a unique Topology ID for the topology level. CPUs with the same
Topology ID share the resources at that level.
If CPUID leaf 0x1F is supported, further parsing is not required.
2) CPUID leaf 0x0000000B (Extended Topology Enumeration Leaf)
The extended CPUID leaf 0x0000000B is the predecessor of the V2 Extended
Topology Enumeration Leaf 0x1F and only describes the core, and the
socket domains of the processor topology.
The support for the leaf is iscovered by checking if the supported CPUID
level is >= 0xB and then checking if `EBX[31:0]` at a particular level
(starting from 0) is non-zero.
CPUID leaf 0x0000000B shares the same layout as CPUID leaf 0x1F and
should be enumerated in a similar manner.
If CPUID leaf 0xB is supported, further parsing is not required.
3) CPUID leaf 0x00000004 (Deterministic Cache Parameters Leaf)
On Intel processors that support neither CPUID leaf 0x1F, nor CPUID leaf
0xB, the shifts for the SMT domains is calculated using the number of
CPUs sharing the L1 cache.
Processors that feature Hyper-Threading is detected using `EDX[28]` of
CPUID leaf 0x1 (Basic CPUID Information).
The order of `Maximum number of addressable IDs for logical processors
sharing this cache` from `EAX[25:14]` of level-0 of CPUID 0x4 provides
the shifts from the APIC ID required to compute the Core ID.
The APIC ID and Package information is computed using the data from
CPUID leaf 0x1.
4) CPUID leaf 0x00000001 (Basic CPUID Information)
The mask and shifts to derive the Physical Package (socket) ID is
computed using the `Maximum number of addressable IDs for logical
processors in this physical package` from `EBX[23:16]` of CPUID leaf
0x1.
The APIC ID on the legacy platforms is derived from the `Initial APIC
ID` field from `EBX[31:24]` of CPUID leaf 0x1.
3) Centaur and Zhaoxin
Similar to Intel, Centaur and Zhaoxin use a combination of CPUID leaf
0x00000004 (Deterministic Cache Parameters Leaf) and CPUID leaf 0x00000001
(Basic CPUID Information) to derive the topology information.
System topology examples
========================
.. note::
The alternative Linux CPU enumeration depends on how the BIOS enumerates the
threads. Many BIOSes enumerate all threads 0 first and then all threads 1.
That has the "advantage" that the logical Linux CPU numbers of threads 0 stay
the same whether threads are enabled or not. That's merely an implementation
detail and has no practical impact.
1) Single Package, Single Core::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
2) Single Package, Dual Core
a) One thread per core::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
-> [core 1] -> [thread 0] -> Linux CPU 1
b) Two threads per core::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
-> [thread 1] -> Linux CPU 1
-> [core 1] -> [thread 0] -> Linux CPU 2
-> [thread 1] -> Linux CPU 3
Alternative enumeration::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
-> [thread 1] -> Linux CPU 2
-> [core 1] -> [thread 0] -> Linux CPU 1
-> [thread 1] -> Linux CPU 3
AMD nomenclature for CMT systems::
[node 0] -> [Compute Unit 0] -> [Compute Unit Core 0] -> Linux CPU 0
-> [Compute Unit Core 1] -> Linux CPU 1
-> [Compute Unit 1] -> [Compute Unit Core 0] -> Linux CPU 2
-> [Compute Unit Core 1] -> Linux CPU 3
4) Dual Package, Dual Core
a) One thread per core::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
-> [core 1] -> [thread 0] -> Linux CPU 1
[package 1] -> [core 0] -> [thread 0] -> Linux CPU 2
-> [core 1] -> [thread 0] -> Linux CPU 3
b) Two threads per core::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
-> [thread 1] -> Linux CPU 1
-> [core 1] -> [thread 0] -> Linux CPU 2
-> [thread 1] -> Linux CPU 3
[package 1] -> [core 0] -> [thread 0] -> Linux CPU 4
-> [thread 1] -> Linux CPU 5
-> [core 1] -> [thread 0] -> Linux CPU 6
-> [thread 1] -> Linux CPU 7
Alternative enumeration::
[package 0] -> [core 0] -> [thread 0] -> Linux CPU 0
-> [thread 1] -> Linux CPU 4
-> [core 1] -> [thread 0] -> Linux CPU 1
-> [thread 1] -> Linux CPU 5
[package 1] -> [core 0] -> [thread 0] -> Linux CPU 2
-> [thread 1] -> Linux CPU 6
-> [core 1] -> [thread 0] -> Linux CPU 3
-> [thread 1] -> Linux CPU 7
AMD nomenclature for CMT systems::
[node 0] -> [Compute Unit 0] -> [Compute Unit Core 0] -> Linux CPU 0
-> [Compute Unit Core 1] -> Linux CPU 1
-> [Compute Unit 1] -> [Compute Unit Core 0] -> Linux CPU 2
-> [Compute Unit Core 1] -> Linux CPU 3
[node 1] -> [Compute Unit 0] -> [Compute Unit Core 0] -> Linux CPU 4
-> [Compute Unit Core 1] -> Linux CPU 5
-> [Compute Unit 1] -> [Compute Unit Core 0] -> Linux CPU 6
-> [Compute Unit Core 1] -> Linux CPU 7
@@ -0,0 +1,117 @@
.. SPDX-License-Identifier: GPL-2.0
TSX Async Abort (TAA) mitigation
================================
.. _tsx_async_abort:
Overview
--------
TSX Async Abort (TAA) is a side channel attack on internal buffers in some
Intel processors similar to Microachitectural Data Sampling (MDS). In this
case certain loads may speculatively pass invalid data to dependent operations
when an asynchronous abort condition is pending in a Transactional
Synchronization Extensions (TSX) transaction. This includes loads with no
fault or assist condition. Such loads may speculatively expose stale data from
the same uarch data structures as in MDS, with same scope of exposure i.e.
same-thread and cross-thread. This issue affects all current processors that
support TSX.
Mitigation strategy
-------------------
a) TSX disable - one of the mitigations is to disable TSX. A new MSR
IA32_TSX_CTRL will be available in future and current processors after
microcode update which can be used to disable TSX. In addition, it
controls the enumeration of the TSX feature bits (RTM and HLE) in CPUID.
b) Clear CPU buffers - similar to MDS, clearing the CPU buffers mitigates this
vulnerability. More details on this approach can be found in
:ref:`Documentation/admin-guide/hw-vuln/mds.rst <mds>`.
Kernel internal mitigation modes
--------------------------------
============= ============================================================
off Mitigation is disabled. Either the CPU is not affected or
tsx_async_abort=off is supplied on the kernel command line.
tsx disabled Mitigation is enabled. TSX feature is disabled by default at
bootup on processors that support TSX control.
verw Mitigation is enabled. CPU is affected and MD_CLEAR is
advertised in CPUID.
ucode needed Mitigation is enabled. CPU is affected and MD_CLEAR is not
advertised in CPUID. That is mainly for virtualization
scenarios where the host has the updated microcode but the
hypervisor does not expose MD_CLEAR in CPUID. It's a best
effort approach without guarantee.
============= ============================================================
If the CPU is affected and the "tsx_async_abort" kernel command line parameter is
not provided then the kernel selects an appropriate mitigation depending on the
status of RTM and MD_CLEAR CPUID bits.
Below tables indicate the impact of tsx=on|off|auto cmdline options on state of
TAA mitigation, VERW behavior and TSX feature for various combinations of
MSR_IA32_ARCH_CAPABILITIES bits.
1. "tsx=off"
========= ========= ============ ============ ============== =================== ======================
MSR_IA32_ARCH_CAPABILITIES bits Result with cmdline tsx=off
---------------------------------- -------------------------------------------------------------------------
TAA_NO MDS_NO TSX_CTRL_MSR TSX state VERW can clear TAA mitigation TAA mitigation
after bootup CPU buffers tsx_async_abort=off tsx_async_abort=full
========= ========= ============ ============ ============== =================== ======================
0 0 0 HW default Yes Same as MDS Same as MDS
0 0 1 Invalid case Invalid case Invalid case Invalid case
0 1 0 HW default No Need ucode update Need ucode update
0 1 1 Disabled Yes TSX disabled TSX disabled
1 X 1 Disabled X None needed None needed
========= ========= ============ ============ ============== =================== ======================
2. "tsx=on"
========= ========= ============ ============ ============== =================== ======================
MSR_IA32_ARCH_CAPABILITIES bits Result with cmdline tsx=on
---------------------------------- -------------------------------------------------------------------------
TAA_NO MDS_NO TSX_CTRL_MSR TSX state VERW can clear TAA mitigation TAA mitigation
after bootup CPU buffers tsx_async_abort=off tsx_async_abort=full
========= ========= ============ ============ ============== =================== ======================
0 0 0 HW default Yes Same as MDS Same as MDS
0 0 1 Invalid case Invalid case Invalid case Invalid case
0 1 0 HW default No Need ucode update Need ucode update
0 1 1 Enabled Yes None Same as MDS
1 X 1 Enabled X None needed None needed
========= ========= ============ ============ ============== =================== ======================
3. "tsx=auto"
========= ========= ============ ============ ============== =================== ======================
MSR_IA32_ARCH_CAPABILITIES bits Result with cmdline tsx=auto
---------------------------------- -------------------------------------------------------------------------
TAA_NO MDS_NO TSX_CTRL_MSR TSX state VERW can clear TAA mitigation TAA mitigation
after bootup CPU buffers tsx_async_abort=off tsx_async_abort=full
========= ========= ============ ============ ============== =================== ======================
0 0 0 HW default Yes Same as MDS Same as MDS
0 0 1 Invalid case Invalid case Invalid case Invalid case
0 1 0 HW default No Need ucode update Need ucode update
0 1 1 Disabled Yes TSX disabled TSX disabled
1 X 1 Enabled X None needed None needed
========= ========= ============ ============ ============== =================== ======================
In the tables, TSX_CTRL_MSR is a new bit in MSR_IA32_ARCH_CAPABILITIES that
indicates whether MSR_IA32_TSX_CTRL is supported.
There are two control bits in IA32_TSX_CTRL MSR:
Bit 0: When set it disables the Restricted Transactional Memory (RTM)
sub-feature of TSX (will force all transactions to abort on the
XBEGIN instruction).
Bit 1: When set it disables the enumeration of the RTM and HLE feature
(i.e. it will make CPUID(EAX=7).EBX{bit4} and
CPUID(EAX=7).EBX{bit11} read as 0).
@@ -0,0 +1,41 @@
.. SPDX-License-Identifier: GPL-2.0
==================
USB Legacy support
==================
:Author: Vojtech Pavlik <vojtech@suse.cz>, January 2004
Also known as "USB Keyboard" or "USB Mouse support" in the BIOS Setup is a
feature that allows one to use the USB mouse and keyboard as if they were
their classic PS/2 counterparts. This means one can use an USB keyboard to
type in LILO for example.
It has several drawbacks, though:
1) On some machines, the emulated PS/2 mouse takes over even when no USB
mouse is present and a real PS/2 mouse is present. In that case the extra
features (wheel, extra buttons, touchpad mode) of the real PS/2 mouse may
not be available.
2) If AMD64 64-bit mode is enabled, again system crashes often happen,
because the SMM BIOS isn't expecting the CPU to be in 64-bit mode. The
BIOS manufacturers only test with Windows, and Windows doesn't do 64-bit
yet.
Solutions:
Problem 1)
can be solved by loading the USB drivers prior to loading the
PS/2 mouse driver. Since the PS/2 mouse driver is in 2.6 compiled into
the kernel unconditionally, this means the USB drivers need to be
compiled-in, too.
Problem 2)
is usually fixed by a BIOS update. Check the board
manufacturers web site. If an update is not available, disable USB
Legacy support in the BIOS. If this alone doesn't help, try also adding
idle=poll on the kernel command line. The BIOS may be entering the SMM
on the HLT instruction as well.
@@ -0,0 +1,58 @@
.. SPDX-License-Identifier: GPL-2.0
==============
5-level paging
==============
Overview
========
Original x86-64 was limited by 4-level paging to 256 TiB of virtual address
space and 64 TiB of physical address space. We are already bumping into
this limit: some vendors offer servers with 64 TiB of memory today.
To overcome the limitation upcoming hardware will introduce support for
5-level paging. It is a straight-forward extension of the current page
table structure adding one more layer of translation.
It bumps the limits to 128 PiB of virtual address space and 4 PiB of
physical address space. This "ought to be enough for anybody" ©.
QEMU 2.9 and later support 5-level paging.
Virtual memory layout for 5-level paging is described in
Documentation/arch/x86/x86_64/mm.rst
User-space and large virtual address space
==========================================
On x86, 5-level paging enables 56-bit userspace virtual address space.
Not all user space is ready to handle wide addresses. It's known that
at least some JIT compilers use higher bits in pointers to encode their
information. It collides with valid pointers with 5-level paging and
leads to crashes.
To mitigate this, we are not going to allocate virtual address space
above 47-bit by default.
But userspace can ask for allocation from full address space by
specifying hint address (with or without MAP_FIXED) above 47-bits.
If hint address set above 47-bit, but MAP_FIXED is not specified, we try
to look for unmapped area by specified address. If it's already
occupied, we look for unmapped area in *full* address space, rather than
from 47-bit window.
A high hint address would only affect the allocation in question, but not
any future mmap()s.
Specifying high hint address on older kernel or on machine without 5-level
paging support is safe. The hint will be ignored and kernel will fall back
to allocation from 47-bit address space.
This approach helps to easily make application's memory allocator aware
about large address space without manually tracking allocated virtual
address space.
One important case we need to handle here is interaction with MPX.
MPX (without MAWA extension) cannot handle addresses above 47-bit, so we
need to make sure that MPX cannot be enabled we already have VMA above
the boundary and forbid creating such VMAs once MPX is enabled.
@@ -0,0 +1,24 @@
.. SPDX-License-Identifier: GPL-2.0
===================================================
Firmware support for CPU hotplug under Linux/x86-64
===================================================
Linux/x86-64 supports CPU hotplug now. For various reasons Linux wants to
know in advance of boot time the maximum number of CPUs that could be plugged
into the system. ACPI 3.0 currently has no official way to supply
this information from the firmware to the operating system.
In ACPI each CPU needs an LAPIC object in the MADT table (5.2.11.5 in the
ACPI 3.0 specification). ACPI already has the concept of disabled LAPIC
objects by setting the Enabled bit in the LAPIC object to zero.
For CPU hotplug Linux/x86-64 expects now that any possible future hotpluggable
CPU is already available in the MADT. If the CPU is not available yet
it should have its LAPIC Enabled bit set to 0. Linux will use the number
of disabled LAPICs to compute the maximum number of future CPUs.
In the worst case the user can overwrite this choice using a command line
option (additional_cpus=...), but it is recommended to supply the correct
number (or a reasonable approximation of it, with erring towards more not less)
in the MADT to avoid manual configuration.
@@ -0,0 +1,78 @@
.. SPDX-License-Identifier: GPL-2.0
=====================
Fake NUMA For CPUSets
=====================
:Author: David Rientjes <rientjes@cs.washington.edu>
Using numa=fake and CPUSets for Resource Management
This document describes how the numa=fake x86_64 command-line option can be used
in conjunction with cpusets for coarse memory management. Using this feature,
you can create fake NUMA nodes that represent contiguous chunks of memory and
assign them to cpusets and their attached tasks. This is a way of limiting the
amount of system memory that are available to a certain class of tasks.
For more information on the features of cpusets, see
Documentation/admin-guide/cgroup-v1/cpusets.rst.
There are a number of different configurations you can use for your needs. For
more information on the numa=fake command line option and its various ways of
configuring fake nodes, see Documentation/admin-guide/kernel-parameters.txt
For the purposes of this introduction, we'll assume a very primitive NUMA
emulation setup of "numa=fake=4*512,". This will split our system memory into
four equal chunks of 512M each that we can now use to assign to cpusets. As
you become more familiar with using this combination for resource control,
you'll determine a better setup to minimize the number of nodes you have to deal
with.
A machine may be split as follows with "numa=fake=4*512," as reported by dmesg::
Faking node 0 at 0000000000000000-0000000020000000 (512MB)
Faking node 1 at 0000000020000000-0000000040000000 (512MB)
Faking node 2 at 0000000040000000-0000000060000000 (512MB)
Faking node 3 at 0000000060000000-0000000080000000 (512MB)
...
On node 0 totalpages: 130975
On node 1 totalpages: 131072
On node 2 totalpages: 131072
On node 3 totalpages: 131072
Now following the instructions for mounting the cpusets filesystem from
Documentation/admin-guide/cgroup-v1/cpusets.rst, you can assign fake nodes (i.e. contiguous memory
address spaces) to individual cpusets::
[root@xroads /]# mkdir exampleset
[root@xroads /]# mount -t cpuset none exampleset
[root@xroads /]# mkdir exampleset/ddset
[root@xroads /]# cd exampleset/ddset
[root@xroads /exampleset/ddset]# echo 0-1 > cpus
[root@xroads /exampleset/ddset]# echo 0-1 > mems
Now this cpuset, 'ddset', will only allowed access to fake nodes 0 and 1 for
memory allocations (1G).
You can now assign tasks to these cpusets to limit the memory resources
available to them according to the fake nodes assigned as mems::
[root@xroads /exampleset/ddset]# echo $$ > tasks
[root@xroads /exampleset/ddset]# dd if=/dev/zero of=tmp bs=1024 count=1G
[1] 13425
Notice the difference between the system memory usage as reported by
/proc/meminfo between the restricted cpuset case above and the unrestricted
case (i.e. running the same 'dd' command without assigning it to a fake NUMA
cpuset):
======== ============ ==========
Name Unrestricted Restricted
======== ============ ==========
MemTotal 3091900 kB 3091900 kB
MemFree 42113 kB 1513236 kB
======== ============ ==========
This allows for coarse memory management for the tasks you assign to particular
cpusets. Since cpusets can form a hierarchy, you can create some pretty
interesting combinations of use-cases for various classes of tasks for your
memory management needs.
@@ -0,0 +1,96 @@
.. SPDX-License-Identifier: GPL-2.0
=========================================
Flexible Return and Event Delivery (FRED)
=========================================
Overview
========
The FRED architecture defines simple new transitions that change
privilege level (ring transitions). The FRED architecture was
designed with the following goals:
1) Improve overall performance and response time by replacing event
delivery through the interrupt descriptor table (IDT event
delivery) and event return by the IRET instruction with lower
latency transitions.
2) Improve software robustness by ensuring that event delivery
establishes the full supervisor context and that event return
establishes the full user context.
The new transitions defined by the FRED architecture are FRED event
delivery and, for returning from events, two FRED return instructions.
FRED event delivery can effect a transition from ring 3 to ring 0, but
it is used also to deliver events incident to ring 0. One FRED
instruction (ERETU) effects a return from ring 0 to ring 3, while the
other (ERETS) returns while remaining in ring 0. Collectively, FRED
event delivery and the FRED return instructions are FRED transitions.
In addition to these transitions, the FRED architecture defines a new
instruction (LKGS) for managing the state of the GS segment register.
The LKGS instruction can be used by 64-bit operating systems that do
not use the new FRED transitions.
Furthermore, the FRED architecture is easy to extend for future CPU
architectures.
Software based event dispatching
================================
FRED operates differently from IDT in terms of event handling. Instead
of directly dispatching an event to its handler based on the event
vector, FRED requires the software to dispatch an event to its handler
based on both the event's type and vector. Therefore, an event dispatch
framework must be implemented to facilitate the event-to-handler
dispatch process. The FRED event dispatch framework takes control
once an event is delivered, and employs a two-level dispatch.
The first level dispatching is event type based, and the second level
dispatching is event vector based.
Full supervisor/user context
============================
FRED event delivery atomically save and restore full supervisor/user
context upon event delivery and return. Thus it avoids the problem of
transient states due to %cr2 and/or %dr6, and it is no longer needed
to handle all the ugly corner cases caused by half baked entry states.
FRED allows explicit unblock of NMI with new event return instructions
ERETS/ERETU, avoiding the mess caused by IRET which unconditionally
unblocks NMI, e.g., when an exception happens during NMI handling.
FRED always restores the full value of %rsp, thus ESPFIX is no longer
needed when FRED is enabled.
LKGS
====
LKGS behaves like the MOV to GS instruction except that it loads the
base address into the IA32_KERNEL_GS_BASE MSR instead of the GS
segment’s descriptor cache. With LKGS, it ends up with avoiding
mucking with kernel GS, i.e., an operating system can always operate
with its own GS base address.
Because FRED event delivery from ring 3 and ERETU both swap the value
of the GS base address and that of the IA32_KERNEL_GS_BASE MSR, plus
the introduction of LKGS instruction, the SWAPGS instruction is no
longer needed when FRED is enabled, thus is disallowed (#UD).
Stack levels
============
4 stack levels 0~3 are introduced to replace the nonreentrant IST for
event handling, and each stack level should be configured to use a
dedicated stack.
The current stack level could be unchanged or go higher upon FRED
event delivery. If unchanged, the CPU keeps using the current event
stack. If higher, the CPU switches to a new event stack specified by
the MSR of the new stack level, i.e., MSR_IA32_FRED_RSP[123].
Only execution of a FRED return instruction ERET[US], could lower the
current stack level, causing the CPU to switch back to the stack it was
on before a previous event delivery that promoted the stack level.
@@ -0,0 +1,199 @@
.. SPDX-License-Identifier: GPL-2.0
Using FS and GS segments in user space applications
===================================================
The x86 architecture supports segmentation. Instructions which access
memory can use segment register based addressing mode. The following
notation is used to address a byte within a segment:
Segment-register:Byte-address
The segment base address is added to the Byte-address to compute the
resulting virtual address which is accessed. This allows to access multiple
instances of data with the identical Byte-address, i.e. the same code. The
selection of a particular instance is purely based on the base-address in
the segment register.
In 32-bit mode the CPU provides 6 segments, which also support segment
limits. The limits can be used to enforce address space protections.
In 64-bit mode the CS/SS/DS/ES segments are ignored and the base address is
always 0 to provide a full 64bit address space. The FS and GS segments are
still functional in 64-bit mode.
Common FS and GS usage
------------------------------
The FS segment is commonly used to address Thread Local Storage (TLS). FS
is usually managed by runtime code or a threading library. Variables
declared with the '__thread' storage class specifier are instantiated per
thread and the compiler emits the FS: address prefix for accesses to these
variables. Each thread has its own FS base address so common code can be
used without complex address offset calculations to access the per thread
instances. Applications should not use FS for other purposes when they use
runtimes or threading libraries which manage the per thread FS.
The GS segment has no common use and can be used freely by
applications. GCC and Clang support GS based addressing via address space
identifiers.
Reading and writing the FS/GS base address
------------------------------------------
There exist two mechanisms to read and write the FS/GS base address:
- the arch_prctl() system call
- the FSGSBASE instruction family
Accessing FS/GS base with arch_prctl()
--------------------------------------
The arch_prctl(2) based mechanism is available on all 64-bit CPUs and all
kernel versions.
Reading the base:
arch_prctl(ARCH_GET_FS, &fsbase);
arch_prctl(ARCH_GET_GS, &gsbase);
Writing the base:
arch_prctl(ARCH_SET_FS, fsbase);
arch_prctl(ARCH_SET_GS, gsbase);
The ARCH_SET_GS prctl may be disabled depending on kernel configuration
and security settings.
Accessing FS/GS base with the FSGSBASE instructions
---------------------------------------------------
With the Ivy Bridge CPU generation Intel introduced a new set of
instructions to access the FS and GS base registers directly from user
space. These instructions are also supported on AMD Family 17H CPUs. The
following instructions are available:
=============== ===========================
RDFSBASE %reg Read the FS base register
RDGSBASE %reg Read the GS base register
WRFSBASE %reg Write the FS base register
WRGSBASE %reg Write the GS base register
=============== ===========================
The instructions avoid the overhead of the arch_prctl() syscall and allow
more flexible usage of the FS/GS addressing modes in user space
applications. This does not prevent conflicts between threading libraries
and runtimes which utilize FS and applications which want to use it for
their own purpose.
FSGSBASE instructions enablement
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The instructions are enumerated in CPUID leaf 7, bit 0 of EBX. If
available /proc/cpuinfo shows 'fsgsbase' in the flag entry of the CPUs.
The availability of the instructions does not enable them
automatically. The kernel has to enable them explicitly in CR4. The
reason for this is that older kernels make assumptions about the values in
the GS register and enforce them when GS base is set via
arch_prctl(). Allowing user space to write arbitrary values to GS base
would violate these assumptions and cause malfunction.
On kernels which do not enable FSGSBASE the execution of the FSGSBASE
instructions will fault with a #UD exception.
The kernel provides reliable information about the enabled state in the
ELF AUX vector. If the HWCAP2_FSGSBASE bit is set in the AUX vector, the
kernel has FSGSBASE instructions enabled and applications can use them.
The following code example shows how this detection works::
#include <sys/auxv.h>
#include <elf.h>
/* Will be eventually in asm/hwcap.h */
#ifndef HWCAP2_FSGSBASE
#define HWCAP2_FSGSBASE (1 << 1)
#endif
....
unsigned val = getauxval(AT_HWCAP2);
if (val & HWCAP2_FSGSBASE)
printf("FSGSBASE enabled\n");
FSGSBASE instructions compiler support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
GCC version 4.6.4 and newer provide intrinsics for the FSGSBASE
instructions. Clang 5 supports them as well.
=================== ===========================
_readfsbase_u64() Read the FS base register
_readgsbase_u64() Read the GS base register
_writefsbase_u64() Write the FS base register
_writegsbase_u64() Write the GS base register
=================== ===========================
To utilize these intrinsics <immintrin.h> must be included in the source
code and the compiler option -mfsgsbase has to be added.
Compiler support for FS/GS based addressing
-------------------------------------------
GCC version 6 and newer provide support for FS/GS based addressing via
Named Address Spaces. GCC implements the following address space
identifiers for x86:
========= ====================================
__seg_fs Variable is addressed relative to FS
__seg_gs Variable is addressed relative to GS
========= ====================================
The preprocessor symbols __SEG_FS and __SEG_GS are defined when these
address spaces are supported. Code which implements fallback modes should
check whether these symbols are defined. Usage example::
#ifdef __SEG_GS
long data0 = 0;
long data1 = 1;
long __seg_gs *ptr;
/* Check whether FSGSBASE is enabled by the kernel (HWCAP2_FSGSBASE) */
....
/* Set GS base to point to data0 */
_writegsbase_u64(&data0);
/* Access offset 0 of GS */
ptr = 0;
printf("data0 = %ld\n", *ptr);
/* Set GS base to point to data1 */
_writegsbase_u64(&data1);
/* ptr still addresses offset 0! */
printf("data1 = %ld\n", *ptr);
Clang does not provide the GCC address space identifiers, but it provides
address spaces via an attribute based mechanism in Clang 2.6 and newer
versions:
==================================== =====================================
__attribute__(address_space(256)) Variable is addressed relative to GS
__attribute__(address_space(257)) Variable is addressed relative to FS
==================================== =====================================
FS/GS based addressing with inline assembly
-------------------------------------------
In case the compiler does not support address spaces, inline assembly can
be used for FS/GS based addressing mode::
mov %fs:offset, %reg
mov %gs:offset, %reg
mov %reg, %fs:offset
mov %reg, %gs:offset
@@ -0,0 +1,17 @@
.. SPDX-License-Identifier: GPL-2.0
==============
x86_64 Support
==============
.. toctree::
:maxdepth: 2
uefi
mm
5level-paging
fake-numa-for-cpusets
cpu-hotplug-spec
machinecheck
fsgs
fred
@@ -0,0 +1,33 @@
.. SPDX-License-Identifier: GPL-2.0
===============================================================
Configurable sysfs parameters for the x86-64 machine check code
===============================================================
Machine checks report internal hardware error conditions detected
by the CPU. Uncorrected errors typically cause a machine check
(often with panic), corrected ones cause a machine check log entry.
Machine checks are organized in banks (normally associated with
a hardware subsystem) and subevents in a bank. The exact meaning
of the banks and subevent is CPU specific.
mcelog knows how to decode them.
When you see the "Machine check errors logged" message in the system
log then mcelog should run to collect and decode machine check entries
from /dev/mcelog. Normally mcelog should be run regularly from a cronjob.
Each CPU has a directory in /sys/devices/system/machinecheck/machinecheckN
(N = CPU number).
The directory contains some configurable entries. See
Documentation/ABI/testing/sysfs-mce for more details.
TBD document entries for AMD threshold interrupt configuration
For more details about the x86 machine check architecture
see the Intel and AMD architecture manuals from their developer websites.
For more details about the architecture
see http://one.firstfloor.org/~andi/mce.pdf
@@ -0,0 +1,180 @@
.. SPDX-License-Identifier: GPL-2.0
=================
Memory Management
=================
Complete virtual memory map with 4-level page tables
====================================================
.. note::
- Negative addresses such as "-23 TB" are absolute addresses in bytes, counted down
from the top of the 64-bit address space. It's easier to understand the layout
when seen both in absolute addresses and in distance-from-top notation.
For example 0xffffe90000000000 == -23 TB, it's 23 TB lower than the top of the
64-bit address space (ffffffffffffffff).
Note that as we get closer to the top of the address space, the notation changes
from TB to GB and then MB/KB.
- "16M TB" might look weird at first sight, but it's an easier way to visualize size
notation than "16 EB", which few will recognize at first sight as 16 exabytes.
It also shows it nicely how incredibly large 64-bit address space is.
::
========================================================================================================================
Start addr | Offset | End addr | Size | VM area description
========================================================================================================================
| | | |
0000000000000000 | 0 | 00007fffffffefff | ~128 TB | user-space virtual memory, different per mm
00007ffffffff000 | ~128 TB | 00007fffffffffff | 4 kB | ... guard hole
__________________|____________|__________________|_________|___________________________________________________________
| | | |
0000800000000000 | +128 TB | 7fffffffffffffff | ~8 EB | ... huge, almost 63 bits wide hole of non-canonical
| | | | virtual memory addresses up to the -8 EB
| | | | starting offset of kernel mappings.
| | | |
| | | | LAM relaxes canonicallity check allowing to create aliases
| | | | for userspace memory here.
__________________|____________|__________________|_________|___________________________________________________________
|
| Kernel-space virtual memory, shared between all processes:
__________________|____________|__________________|_________|___________________________________________________________
| | | |
8000000000000000 | -8 EB | ffff7fffffffffff | ~8 EB | ... huge, almost 63 bits wide hole of non-canonical
| | | | virtual memory addresses up to the -128 TB
| | | | starting offset of kernel mappings.
| | | |
| | | | LAM_SUP relaxes canonicallity check allowing to create
| | | | aliases for kernel memory here.
____________________________________________________________|___________________________________________________________
| | | |
ffff800000000000 | -128 TB | ffff87ffffffffff | 8 TB | ... guard hole, also reserved for hypervisor
ffff880000000000 | -120 TB | ffff887fffffffff | 0.5 TB | LDT remap for PTI
ffff888000000000 | -119.5 TB | ffffc87fffffffff | 64 TB | direct mapping of all physical memory (page_offset_base)
ffffc88000000000 | -55.5 TB | ffffc8ffffffffff | 0.5 TB | ... unused hole
ffffc90000000000 | -55 TB | ffffe8ffffffffff | 32 TB | vmalloc/ioremap space (vmalloc_base)
ffffe90000000000 | -23 TB | ffffe9ffffffffff | 1 TB | ... unused hole
ffffea0000000000 | -22 TB | ffffeaffffffffff | 1 TB | virtual memory map (vmemmap_base)
ffffeb0000000000 | -21 TB | ffffebffffffffff | 1 TB | ... unused hole
ffffec0000000000 | -20 TB | fffffbffffffffff | 16 TB | KASAN shadow memory
__________________|____________|__________________|_________|____________________________________________________________
|
| Identical layout to the 56-bit one from here on:
____________________________________________________________|____________________________________________________________
| | | |
fffffc0000000000 | -4 TB | fffffdffffffffff | 2 TB | ... unused hole
| | | | vaddr_end for KASLR
fffffe0000000000 | -2 TB | fffffe7fffffffff | 0.5 TB | cpu_entry_area mapping
fffffe8000000000 | -1.5 TB | fffffeffffffffff | 0.5 TB | ... unused hole
ffffff0000000000 | -1 TB | ffffff7fffffffff | 0.5 TB | %esp fixup stacks
ffffff8000000000 | -512 GB | ffffffeeffffffff | 444 GB | ... unused hole
ffffffef00000000 | -68 GB | fffffffeffffffff | 64 GB | EFI region mapping space
ffffffff00000000 | -4 GB | ffffffff7fffffff | 2 GB | ... unused hole
ffffffff80000000 | -2 GB | ffffffff9fffffff | 512 MB | kernel text mapping, mapped to physical address 0
ffffffff80000000 |-2048 MB | | |
ffffffffa0000000 |-1536 MB | fffffffffeffffff | 1520 MB | module mapping space
ffffffffff000000 | -16 MB | | |
FIXADDR_START | ~-11 MB | ffffffffff5fffff | ~0.5 MB | kernel-internal fixmap range, variable size and offset
ffffffffff600000 | -10 MB | ffffffffff600fff | 4 kB | legacy vsyscall ABI
ffffffffffe00000 | -2 MB | ffffffffffffffff | 2 MB | ... unused hole
__________________|____________|__________________|_________|___________________________________________________________
Complete virtual memory map with 5-level page tables
====================================================
.. note::
- With 56-bit addresses, user-space memory gets expanded by a factor of 512x,
from 0.125 PB to 64 PB. All kernel mappings shift down to the -64 PB starting
offset and many of the regions expand to support the much larger physical
memory supported.
::
========================================================================================================================
Start addr | Offset | End addr | Size | VM area description
========================================================================================================================
| | | |
0000000000000000 | 0 | 00fffffffffff000 | ~64 PB | user-space virtual memory, different per mm
00fffffffffff000 | ~64 PB | 00ffffffffffffff | 4 kB | ... guard hole
__________________|____________|__________________|_________|___________________________________________________________
| | | |
0100000000000000 | +64 PB | 7fffffffffffffff | ~8 EB | ... huge, almost 63 bits wide hole of non-canonical
| | | | virtual memory addresses up to the -8EB TB
| | | | starting offset of kernel mappings.
| | | |
| | | | LAM relaxes canonicallity check allowing to create aliases
| | | | for userspace memory here.
__________________|____________|__________________|_________|___________________________________________________________
|
| Kernel-space virtual memory, shared between all processes:
____________________________________________________________|___________________________________________________________
8000000000000000 | -8 EB | feffffffffffffff | ~8 EB | ... huge, almost 63 bits wide hole of non-canonical
| | | | virtual memory addresses up to the -64 PB
| | | | starting offset of kernel mappings.
| | | |
| | | | LAM_SUP relaxes canonicallity check allowing to create
| | | | aliases for kernel memory here.
____________________________________________________________|___________________________________________________________
| | | |
ff00000000000000 | -64 PB | ff0fffffffffffff | 4 PB | ... guard hole, also reserved for hypervisor
ff10000000000000 | -60 PB | ff10ffffffffffff | 0.25 PB | LDT remap for PTI
ff11000000000000 | -59.75 PB | ff90ffffffffffff | 32 PB | direct mapping of all physical memory (page_offset_base)
ff91000000000000 | -27.75 PB | ff9fffffffffffff | 3.75 PB | ... unused hole
ffa0000000000000 | -24 PB | ffd1ffffffffffff | 12.5 PB | vmalloc/ioremap space (vmalloc_base)
ffd2000000000000 | -11.5 PB | ffd3ffffffffffff | 0.5 PB | ... unused hole
ffd4000000000000 | -11 PB | ffd5ffffffffffff | 0.5 PB | virtual memory map (vmemmap_base)
ffd6000000000000 | -10.5 PB | ffdeffffffffffff | 2.25 PB | ... unused hole
ffdf000000000000 | -8.25 PB | fffffbffffffffff | ~8 PB | KASAN shadow memory
__________________|____________|__________________|_________|____________________________________________________________
|
| Identical layout to the 47-bit one from here on:
____________________________________________________________|____________________________________________________________
| | | |
fffffc0000000000 | -4 TB | fffffdffffffffff | 2 TB | ... unused hole
| | | | vaddr_end for KASLR
fffffe0000000000 | -2 TB | fffffe7fffffffff | 0.5 TB | cpu_entry_area mapping
fffffe8000000000 | -1.5 TB | fffffeffffffffff | 0.5 TB | ... unused hole
ffffff0000000000 | -1 TB | ffffff7fffffffff | 0.5 TB | %esp fixup stacks
ffffff8000000000 | -512 GB | ffffffeeffffffff | 444 GB | ... unused hole
ffffffef00000000 | -68 GB | fffffffeffffffff | 64 GB | EFI region mapping space
ffffffff00000000 | -4 GB | ffffffff7fffffff | 2 GB | ... unused hole
ffffffff80000000 | -2 GB | ffffffff9fffffff | 512 MB | kernel text mapping, mapped to physical address 0
ffffffff80000000 |-2048 MB | | |
ffffffffa0000000 |-1536 MB | fffffffffeffffff | 1520 MB | module mapping space
ffffffffff000000 | -16 MB | | |
FIXADDR_START | ~-11 MB | ffffffffff5fffff | ~0.5 MB | kernel-internal fixmap range, variable size and offset
ffffffffff600000 | -10 MB | ffffffffff600fff | 4 kB | legacy vsyscall ABI
ffffffffffe00000 | -2 MB | ffffffffffffffff | 2 MB | ... unused hole
__________________|____________|__________________|_________|___________________________________________________________
Architecture defines a 64-bit virtual address. Implementations can support
less. Currently supported are 48- and 57-bit virtual addresses. Bits 63
through to the most-significant implemented bit are sign extended.
This causes hole between user space and kernel addresses if you interpret them
as unsigned.
The direct mapping covers all memory in the system up to the highest
memory address (this means in some cases it can also include PCI memory
holes).
We map EFI runtime services in the 'efi_pgd' PGD in a 64GB large virtual
memory window (this size is arbitrary, it can be raised later if needed).
The mappings are not part of any other kernel PGD and are only available
during EFI runtime calls.
Note that if CONFIG_RANDOMIZE_MEMORY is enabled, the direct mapping of all
physical memory, vmalloc/ioremap space and virtual memory map are randomized.
Their order is preserved but their base will be offset early at boot time.
Be very careful vs. KASLR when changing anything here. The KASLR address
range must not overlap with anything except the KASAN shadow area, which is
correct as KASAN disables KASLR.
For both 4- and 5-level layouts, the KSTACK_ERASE_POISON value in the last 2MB
hole: ffffffffffff4111
@@ -0,0 +1,75 @@
.. SPDX-License-Identifier: GPL-2.0
=====================================
General note on [U]EFI x86_64 support
=====================================
The nomenclature EFI and UEFI are used interchangeably in this document.
Although the tools below are _not_ needed for building the kernel,
the needed bootloader support and associated tools for x86_64 platforms
with EFI firmware and specifications are listed below.
1. UEFI specification: http://www.uefi.org
2. Booting Linux kernel on UEFI x86_64 platform can either be
done using the <Documentation/admin-guide/efi-stub.rst> or using a
separate bootloader.
3. x86_64 platform with EFI/UEFI firmware.
Mechanics
---------
Refer to <Documentation/admin-guide/efi-stub.rst> to learn how to use the EFI stub.
Below are general EFI setup guidelines on the x86_64 platform,
regardless of whether you use the EFI stub or a separate bootloader.
- Build the kernel with the following configuration::
CONFIG_FB_EFI=y
CONFIG_FRAMEBUFFER_CONSOLE=y
If EFI runtime services are expected, the following configuration should
be selected::
CONFIG_EFI=y
CONFIG_EFIVAR_FS=y or m # optional
- Create a VFAT partition on the disk with the EFI System flag
You can do this with fdisk with the following commands:
1. g - initialize a GPT partition table
2. n - create a new partition
3. t - change the partition type to "EFI System" (number 1)
4. w - write and save the changes
Afterwards, initialize the VFAT filesystem by running mkfs::
mkfs.fat /dev/<your-partition>
- Copy the boot files to the VFAT partition:
If you use the EFI stub method, the kernel acts also as an EFI executable.
You can just copy the bzImage to the EFI/boot/bootx64.efi path on the partition
so that it will automatically get booted, see the <Documentation/admin-guide/efi-stub.rst> page
for additional instructions regarding passage of kernel parameters and initramfs.
If you use a custom bootloader, refer to the relevant documentation for help on this part.
- If some or all EFI runtime services don't work, you can try following
kernel command line parameters to turn off some or all EFI runtime
services.
noefi
turn off all EFI runtime services
reboot_type=k
turn off EFI reboot runtime service
- If the EFI memory map has additional entries not in the E820 map,
you can include those entries in the kernels memory map of available
physical RAM by using the following kernel command line parameter.
add_efi_memmap
include EFI memory map of available physical RAM
@@ -0,0 +1,174 @@
Using XSTATE features in user space applications
================================================
The x86 architecture supports floating-point extensions which are
enumerated via CPUID. Applications consult CPUID and use XGETBV to
evaluate which features have been enabled by the kernel XCR0.
Up to AVX-512 and PKRU states, these features are automatically enabled by
the kernel if available. Features like AMX TILE_DATA (XSTATE component 18)
are enabled by XCR0 as well, but the first use of related instruction is
trapped by the kernel because by default the required large XSTATE buffers
are not allocated automatically.
The purpose for dynamic features
--------------------------------
Legacy userspace libraries often have hard-coded, static sizes for
alternate signal stacks, often using MINSIGSTKSZ which is typically 2KB.
That stack must be able to store at *least* the signal frame that the
kernel sets up before jumping into the signal handler. That signal frame
must include an XSAVE buffer defined by the CPU.
However, that means that the size of signal stacks is dynamic, not static,
because different CPUs have differently-sized XSAVE buffers. A compiled-in
size of 2KB with existing applications is too small for new CPU features
like AMX. Instead of universally requiring larger stack, with the dynamic
enabling, the kernel can enforce userspace applications to have
properly-sized altstacks.
Using dynamically enabled XSTATE features in user space applications
--------------------------------------------------------------------
The kernel provides an arch_prctl(2) based mechanism for applications to
request the usage of such features. The arch_prctl(2) options related to
this are:
-ARCH_GET_XCOMP_SUPP
arch_prctl(ARCH_GET_XCOMP_SUPP, &features);
ARCH_GET_XCOMP_SUPP stores the supported features in userspace storage of
type uint64_t. The second argument is a pointer to that storage.
-ARCH_GET_XCOMP_PERM
arch_prctl(ARCH_GET_XCOMP_PERM, &features);
ARCH_GET_XCOMP_PERM stores the features for which the userspace process
has permission in userspace storage of type uint64_t. The second argument
is a pointer to that storage.
-ARCH_REQ_XCOMP_PERM
arch_prctl(ARCH_REQ_XCOMP_PERM, feature_nr);
ARCH_REQ_XCOMP_PERM allows to request permission for a dynamically enabled
feature or a feature set. A feature set can be mapped to a facility, e.g.
AMX, and can require one or more XSTATE components to be enabled.
The feature argument is the number of the highest XSTATE component which
is required for a facility to work.
When requesting permission for a feature, the kernel checks the
availability. The kernel ensures that sigaltstacks in the process's tasks
are large enough to accommodate the resulting large signal frame. It
enforces this both during ARCH_REQ_XCOMP_SUPP and during any subsequent
sigaltstack(2) calls. If an installed sigaltstack is smaller than the
resulting sigframe size, ARCH_REQ_XCOMP_SUPP results in -ENOSUPP. Also,
sigaltstack(2) results in -ENOMEM if the requested altstack is too small
for the permitted features.
Permission, when granted, is valid per process. Permissions are inherited
on fork(2) and cleared on exec(3).
The first use of an instruction related to a dynamically enabled feature is
trapped by the kernel. The trap handler checks whether the process has
permission to use the feature. If the process has no permission then the
kernel sends SIGILL to the application. If the process has permission then
the handler allocates a larger xstate buffer for the task so the large
state can be context switched. In the unlikely cases that the allocation
fails, the kernel sends SIGSEGV.
AMX TILE_DATA enabling example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Below is the example of how userspace applications enable
TILE_DATA dynamically:
1. The application first needs to query the kernel for AMX
support::
#include <asm/prctl.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <unistd.h>
#ifndef ARCH_GET_XCOMP_SUPP
#define ARCH_GET_XCOMP_SUPP 0x1021
#endif
#ifndef ARCH_XCOMP_TILECFG
#define ARCH_XCOMP_TILECFG 17
#endif
#ifndef ARCH_XCOMP_TILEDATA
#define ARCH_XCOMP_TILEDATA 18
#endif
#define MASK_XCOMP_TILE ((1 << ARCH_XCOMP_TILECFG) | \
(1 << ARCH_XCOMP_TILEDATA))
unsigned long features;
long rc;
...
rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_SUPP, &features);
if (!rc && (features & MASK_XCOMP_TILE) == MASK_XCOMP_TILE)
printf("AMX is available.\n");
2. After that, determining support for AMX, an application must
explicitly ask permission to use it::
#ifndef ARCH_REQ_XCOMP_PERM
#define ARCH_REQ_XCOMP_PERM 0x1023
#endif
...
rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, ARCH_XCOMP_TILEDATA);
if (!rc)
printf("AMX is ready for use.\n");
Note this example does not include the sigaltstack preparation.
Dynamic features in signal frames
---------------------------------
Dynamically enabled features are not written to the signal frame upon signal
entry if the feature is in its initial configuration. This differs from
non-dynamic features which are always written regardless of their
configuration. Signal handlers can examine the XSAVE buffer's XSTATE_BV
field to determine if a features was written.
Dynamic features for virtual machines
-------------------------------------
The permission for the guest state component needs to be managed separately
from the host, as they are exclusive to each other. A coupled of options
are extended to control the guest permission:
-ARCH_GET_XCOMP_GUEST_PERM
arch_prctl(ARCH_GET_XCOMP_GUEST_PERM, &features);
ARCH_GET_XCOMP_GUEST_PERM is a variant of ARCH_GET_XCOMP_PERM. So it
provides the same semantics and functionality but for the guest
components.
-ARCH_REQ_XCOMP_GUEST_PERM
arch_prctl(ARCH_REQ_XCOMP_GUEST_PERM, feature_nr);
ARCH_REQ_XCOMP_GUEST_PERM is a variant of ARCH_REQ_XCOMP_PERM. It has the
same semantics for the guest permission. While providing a similar
functionality, this comes with a constraint. Permission is frozen when the
first VCPU is created. Any attempt to change permission after that point
is going to be rejected. So, the permission has to be requested before the
first VCPU creation.
Note that some VMMs may have already established a set of supported state
components. These options are not presumed to support any particular VMM.
@@ -0,0 +1,47 @@
.. SPDX-License-Identifier: GPL-2.0
=========
Zero Page
=========
The additional fields in struct boot_params as a part of 32-bit boot
protocol of kernel. These should be filled by bootloader or 16-bit
real-mode setup code of the kernel. References/settings to it mainly
are in::
arch/x86/include/uapi/asm/bootparam.h
=========== ===== ======================= =================================================
Offset/Size Proto Name Meaning
000/040 ALL screen_info Text mode or frame buffer information
(struct screen_info)
040/014 ALL apm_bios_info APM BIOS information (struct apm_bios_info)
058/008 ALL tboot_addr Physical address of tboot shared page
060/010 ALL ist_info Intel SpeedStep (IST) BIOS support information
(struct ist_info)
070/008 ALL acpi_rsdp_addr Physical address of ACPI RSDP table
080/010 ALL hd0_info hd0 disk parameter, OBSOLETE!!
090/010 ALL hd1_info hd1 disk parameter, OBSOLETE!!
0A0/010 ALL sys_desc_table System description table (struct sys_desc_table),
OBSOLETE!!
0B0/010 ALL olpc_ofw_header OLPC's OpenFirmware CIF and friends
0C0/004 ALL ext_ramdisk_image ramdisk_image high 32bits
0C4/004 ALL ext_ramdisk_size ramdisk_size high 32bits
0C8/004 ALL ext_cmd_line_ptr cmd_line_ptr high 32bits
13C/004 ALL cc_blob_address Physical address of Confidential Computing blob
140/080 ALL edid_info Video mode setup (struct edid_info)
1C0/020 ALL efi_info EFI 32 information (struct efi_info)
1E0/004 ALL alt_mem_k Alternative mem check, in KB
1E4/004 ALL scratch Scratch field for the kernel setup code
1E8/001 ALL e820_entries Number of entries in e820_table (below)
1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below)
1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer
(below)
1EB/001 ALL kbd_status Numlock is enabled
1EC/001 ALL secure_boot Secure boot is enabled in the firmware
1EF/001 ALL sentinel Used to detect broken bootloaders
290/040 ALL edd_mbr_sig_buffer EDD MBR signatures
2D0/A00 ALL e820_table E820 memory map table
(array of struct e820_entry)
D00/1EC ALL eddbuf EDD data (array of struct edd_info)
=========== ===== ======================= =================================================