Core x86 updates for v6.16:
Boot code changes:
- A large series of changes to reorganize the x86 boot code into a better isolated
and easier to maintain base of PIC early startup code in arch/x86/boot/startup/,
by Ard Biesheuvel.
Motivation & background:
| Since commit
|
| c88d71508e ("x86/boot/64: Rewrite startup_64() in C")
|
| dated Jun 6 2017, we have been using C code on the boot path in a way
| that is not supported by the toolchain, i.e., to execute non-PIC C
| code from a mapping of memory that is different from the one provided
| to the linker. It should have been obvious at the time that this was a
| bad idea, given the need to sprinkle fixup_pointer() calls left and
| right to manipulate global variables (including non-pointer variables)
| without crashing.
|
| This C startup code has been expanding, and in particular, the SEV-SNP
| startup code has been expanding over the past couple of years, and
| grown many of these warts, where the C code needs to use special
| annotations or helpers to access global objects.
This tree includes the first phase of this work-in-progress x86 boot code
reorganization.
Scalability enhancements and micro-optimizations:
- Improve code-patching scalability (Eric Dumazet)
- Remove MFENCEs for X86_BUG_CLFLUSH_MONITOR (Andrew Cooper)
CPU features enumeration updates:
- Thorough reorganization and cleanup of CPUID parsing APIs (Ahmed S. Darwish)
- Fix, refactor and clean up the cacheinfo code (Ahmed S. Darwish, Thomas Gleixner)
- Update CPUID bitfields to x86-cpuid-db v2.3 (Ahmed S. Darwish)
Memory management changes:
- Allow temporary MMs when IRQs are on (Andy Lutomirski)
- Opt-in to IRQs-off activate_mm() (Andy Lutomirski)
- Simplify choose_new_asid() and generate better code (Borislav Petkov)
- Simplify 32-bit PAE page table handling (Dave Hansen)
- Always use dynamic memory layout (Kirill A. Shutemov)
- Make SPARSEMEM_VMEMMAP the only memory model (Kirill A. Shutemov)
- Make 5-level paging support unconditional (Kirill A. Shutemov)
- Stop prefetching current->mm->mmap_lock on page faults (Mateusz Guzik)
- Predict valid_user_address() returning true (Mateusz Guzik)
- Consolidate initmem_init() (Mike Rapoport)
FPU support and vector computing:
- Enable Intel APX support (Chang S. Bae)
- Reorgnize and clean up the xstate code (Chang S. Bae)
- Make task_struct::thread constant size (Ingo Molnar)
- Restore fpu_thread_struct_whitelist() to fix CONFIG_HARDENED_USERCOPY=y
(Kees Cook)
- Simplify the switch_fpu_prepare() + switch_fpu_finish() logic (Oleg Nesterov)
- Always preserve non-user xfeatures/flags in __state_perm (Sean Christopherson)
Microcode loader changes:
- Help users notice when running old Intel microcode (Dave Hansen)
- AMD: Do not return error when microcode update is not necessary (Annie Li)
- AMD: Clean the cache if update did not load microcode (Boris Ostrovsky)
Code patching (alternatives) changes:
- Simplify, reorganize and clean up the x86 text-patching code (Ingo Molnar)
- Make smp_text_poke_batch_process() subsume smp_text_poke_batch_finish()
(Nikolay Borisov)
- Refactor the {,un}use_temporary_mm() code (Peter Zijlstra)
Debugging support:
- Add early IDT and GDT loading to debug relocate_kernel() bugs (David Woodhouse)
- Print the reason for the last reset on modern AMD CPUs (Yazen Ghannam)
- Add AMD Zen debugging document (Mario Limonciello)
- Fix opcode map (!REX2) superscript tags (Masami Hiramatsu)
- Stop decoding i64 instructions in x86-64 mode at opcode (Masami Hiramatsu)
CPU bugs and bug mitigations:
- Remove X86_BUG_MMIO_UNKNOWN (Borislav Petkov)
- Fix SRSO reporting on Zen1/2 with SMT disabled (Borislav Petkov)
- Restructure and harmonize the various CPU bug mitigation methods
(David Kaplan)
- Fix spectre_v2 mitigation default on Intel (Pawan Gupta)
MSR API:
- Large MSR code and API cleanup (Xin Li)
- In-kernel MSR API type cleanups and renames (Ingo Molnar)
PKEYS:
- Simplify PKRU update in signal frame (Chang S. Bae)
NMI handling code:
- Clean up, refactor and simplify the NMI handling code (Sohil Mehta)
- Improve NMI duration console printouts (Sohil Mehta)
Paravirt guests interface:
- Restrict PARAVIRT_XXL to 64-bit only (Kirill A. Shutemov)
SEV support:
- Share the sev_secrets_pa value again (Tom Lendacky)
x86 platform changes:
- Introduce the <asm/amd/> header namespace (Ingo Molnar)
- i2c: piix4, x86/platform: Move the SB800 PIIX4 FCH definitions to <asm/amd/fch.h>
(Mario Limonciello)
Fixes and cleanups:
- x86 assembly code cleanups and fixes (Uros Bizjak)
- Misc fixes and cleanups (Andi Kleen, Andy Lutomirski, Andy Shevchenko,
Ard Biesheuvel, Bagas Sanjaya, Baoquan He, Borislav Petkov, Chang S. Bae,
Chao Gao, Dan Williams, Dave Hansen, David Kaplan, David Woodhouse,
Eric Biggers, Ingo Molnar, Josh Poimboeuf, Juergen Gross, Malaya Kumar Rout,
Mario Limonciello, Nathan Chancellor, Oleg Nesterov, Pawan Gupta,
Peter Zijlstra, Shivank Garg, Sohil Mehta, Thomas Gleixner, Uros Bizjak,
Xin Li)
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-----BEGIN PGP SIGNATURE-----
iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmgy9WARHG1pbmdvQGtl
cm5lbC5vcmcACgkQEnMQ0APhK1jJSw/+OW2zvAx602doujBIE17vFLU7R10Xwj5H
lVgomkWCoTNscUZPhdT/iI+/kQF1fG8PtN9oZKUsTAUswldKJsqu7KevobviesiW
qI+FqH/fhHaIk7GVh9VP65Dgrdki8zsgd7BFxD8pLRBlbZTxTxXNNkuNJrs6LxJh
SxWp/FVtKo6Wd57qlUcsdo0tilAfcuhlEweFUarX55X2ouhdeHjcGNpxj9dHKOh8
M7R5yMYFrpfdpSms+WaCnKKahWHaIQtQTsPAyKwoVdtfl1kK+7NgaCF55Gbo3ogp
r59JwC/CGruDa5QnnDizCwFIwpZw9M52Q1NhP/eLEZbDGB4Yya3b5NW+Ya+6rPvO
ZZC3e1uUmlxW3lrYflUHurnwrVb2GjkQZOdf0gfnly/7LljIicIS2dk4qIQF9NBd
sQPpW5hjmIz9CsfeL8QaJW38pQyMsQWznFuz4YVuHcLHvleb3hR+n4fNfV5Lx9bw
oirVETSIT5hy/msAgShPqTqFUEiVCgp16ow20YstxxzFu/FQ+VG987tkeUyFkPMe
q1v5yF1hty+TkM4naKendIZ/MJnsrv0AxaegFz9YQrKGL1UPiOajQbSyKbzbto7+
ozmtN0W80E8n4oQq008j8htpgIhDV91UjF5m33qB82uSqKihHPPTsVcbeg5nZwh2
ti5g/a1jk94=
=JgQo
-----END PGP SIGNATURE-----
Merge tag 'x86-core-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core x86 updates from Ingo Molnar:
"Boot code changes:
- A large series of changes to reorganize the x86 boot code into a
better isolated and easier to maintain base of PIC early startup
code in arch/x86/boot/startup/, by Ard Biesheuvel.
Motivation & background:
| Since commit
|
| c88d71508e ("x86/boot/64: Rewrite startup_64() in C")
|
| dated Jun 6 2017, we have been using C code on the boot path in a way
| that is not supported by the toolchain, i.e., to execute non-PIC C
| code from a mapping of memory that is different from the one provided
| to the linker. It should have been obvious at the time that this was a
| bad idea, given the need to sprinkle fixup_pointer() calls left and
| right to manipulate global variables (including non-pointer variables)
| without crashing.
|
| This C startup code has been expanding, and in particular, the SEV-SNP
| startup code has been expanding over the past couple of years, and
| grown many of these warts, where the C code needs to use special
| annotations or helpers to access global objects.
This tree includes the first phase of this work-in-progress x86
boot code reorganization.
Scalability enhancements and micro-optimizations:
- Improve code-patching scalability (Eric Dumazet)
- Remove MFENCEs for X86_BUG_CLFLUSH_MONITOR (Andrew Cooper)
CPU features enumeration updates:
- Thorough reorganization and cleanup of CPUID parsing APIs (Ahmed S.
Darwish)
- Fix, refactor and clean up the cacheinfo code (Ahmed S. Darwish,
Thomas Gleixner)
- Update CPUID bitfields to x86-cpuid-db v2.3 (Ahmed S. Darwish)
Memory management changes:
- Allow temporary MMs when IRQs are on (Andy Lutomirski)
- Opt-in to IRQs-off activate_mm() (Andy Lutomirski)
- Simplify choose_new_asid() and generate better code (Borislav
Petkov)
- Simplify 32-bit PAE page table handling (Dave Hansen)
- Always use dynamic memory layout (Kirill A. Shutemov)
- Make SPARSEMEM_VMEMMAP the only memory model (Kirill A. Shutemov)
- Make 5-level paging support unconditional (Kirill A. Shutemov)
- Stop prefetching current->mm->mmap_lock on page faults (Mateusz
Guzik)
- Predict valid_user_address() returning true (Mateusz Guzik)
- Consolidate initmem_init() (Mike Rapoport)
FPU support and vector computing:
- Enable Intel APX support (Chang S. Bae)
- Reorgnize and clean up the xstate code (Chang S. Bae)
- Make task_struct::thread constant size (Ingo Molnar)
- Restore fpu_thread_struct_whitelist() to fix
CONFIG_HARDENED_USERCOPY=y (Kees Cook)
- Simplify the switch_fpu_prepare() + switch_fpu_finish() logic (Oleg
Nesterov)
- Always preserve non-user xfeatures/flags in __state_perm (Sean
Christopherson)
Microcode loader changes:
- Help users notice when running old Intel microcode (Dave Hansen)
- AMD: Do not return error when microcode update is not necessary
(Annie Li)
- AMD: Clean the cache if update did not load microcode (Boris
Ostrovsky)
Code patching (alternatives) changes:
- Simplify, reorganize and clean up the x86 text-patching code (Ingo
Molnar)
- Make smp_text_poke_batch_process() subsume
smp_text_poke_batch_finish() (Nikolay Borisov)
- Refactor the {,un}use_temporary_mm() code (Peter Zijlstra)
Debugging support:
- Add early IDT and GDT loading to debug relocate_kernel() bugs
(David Woodhouse)
- Print the reason for the last reset on modern AMD CPUs (Yazen
Ghannam)
- Add AMD Zen debugging document (Mario Limonciello)
- Fix opcode map (!REX2) superscript tags (Masami Hiramatsu)
- Stop decoding i64 instructions in x86-64 mode at opcode (Masami
Hiramatsu)
CPU bugs and bug mitigations:
- Remove X86_BUG_MMIO_UNKNOWN (Borislav Petkov)
- Fix SRSO reporting on Zen1/2 with SMT disabled (Borislav Petkov)
- Restructure and harmonize the various CPU bug mitigation methods
(David Kaplan)
- Fix spectre_v2 mitigation default on Intel (Pawan Gupta)
MSR API:
- Large MSR code and API cleanup (Xin Li)
- In-kernel MSR API type cleanups and renames (Ingo Molnar)
PKEYS:
- Simplify PKRU update in signal frame (Chang S. Bae)
NMI handling code:
- Clean up, refactor and simplify the NMI handling code (Sohil Mehta)
- Improve NMI duration console printouts (Sohil Mehta)
Paravirt guests interface:
- Restrict PARAVIRT_XXL to 64-bit only (Kirill A. Shutemov)
SEV support:
- Share the sev_secrets_pa value again (Tom Lendacky)
x86 platform changes:
- Introduce the <asm/amd/> header namespace (Ingo Molnar)
- i2c: piix4, x86/platform: Move the SB800 PIIX4 FCH definitions to
<asm/amd/fch.h> (Mario Limonciello)
Fixes and cleanups:
- x86 assembly code cleanups and fixes (Uros Bizjak)
- Misc fixes and cleanups (Andi Kleen, Andy Lutomirski, Andy
Shevchenko, Ard Biesheuvel, Bagas Sanjaya, Baoquan He, Borislav
Petkov, Chang S. Bae, Chao Gao, Dan Williams, Dave Hansen, David
Kaplan, David Woodhouse, Eric Biggers, Ingo Molnar, Josh Poimboeuf,
Juergen Gross, Malaya Kumar Rout, Mario Limonciello, Nathan
Chancellor, Oleg Nesterov, Pawan Gupta, Peter Zijlstra, Shivank
Garg, Sohil Mehta, Thomas Gleixner, Uros Bizjak, Xin Li)"
* tag 'x86-core-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (331 commits)
x86/bugs: Fix spectre_v2 mitigation default on Intel
x86/bugs: Restructure ITS mitigation
x86/xen/msr: Fix uninitialized variable 'err'
x86/msr: Remove a superfluous inclusion of <asm/asm.h>
x86/paravirt: Restrict PARAVIRT_XXL to 64-bit only
x86/mm/64: Make 5-level paging support unconditional
x86/mm/64: Make SPARSEMEM_VMEMMAP the only memory model
x86/mm/64: Always use dynamic memory layout
x86/bugs: Fix indentation due to ITS merge
x86/cpuid: Rename hypervisor_cpuid_base()/for_each_possible_hypervisor_cpuid_base() to cpuid_base_hypervisor()/for_each_possible_cpuid_base_hypervisor()
x86/cpu/intel: Rename CPUID(0x2) descriptors iterator parameter
x86/cacheinfo: Rename CPUID(0x2) descriptors iterator parameter
x86/cpuid: Rename cpuid_get_leaf_0x2_regs() to cpuid_leaf_0x2()
x86/cpuid: Rename have_cpuid_p() to cpuid_feature()
x86/cpuid: Set <asm/cpuid/api.h> as the main CPUID header
x86/cpuid: Move CPUID(0x2) APIs into <cpuid/api.h>
x86/msr: Add rdmsrl_on_cpu() compatibility wrapper
x86/mm: Fix kernel-doc descriptions of various pgtable methods
x86/asm-offsets: Export certain 'struct cpuinfo_x86' fields for 64-bit asm use too
x86/boot: Defer initialization of VM space related global variables
...
pull/1250/head
commit
785cdec46e
|
|
@ -517,6 +517,7 @@ What: /sys/devices/system/cpu/vulnerabilities
|
||||||
/sys/devices/system/cpu/vulnerabilities/mds
|
/sys/devices/system/cpu/vulnerabilities/mds
|
||||||
/sys/devices/system/cpu/vulnerabilities/meltdown
|
/sys/devices/system/cpu/vulnerabilities/meltdown
|
||||||
/sys/devices/system/cpu/vulnerabilities/mmio_stale_data
|
/sys/devices/system/cpu/vulnerabilities/mmio_stale_data
|
||||||
|
/sys/devices/system/cpu/vulnerabilities/old_microcode
|
||||||
/sys/devices/system/cpu/vulnerabilities/reg_file_data_sampling
|
/sys/devices/system/cpu/vulnerabilities/reg_file_data_sampling
|
||||||
/sys/devices/system/cpu/vulnerabilities/retbleed
|
/sys/devices/system/cpu/vulnerabilities/retbleed
|
||||||
/sys/devices/system/cpu/vulnerabilities/spec_store_bypass
|
/sys/devices/system/cpu/vulnerabilities/spec_store_bypass
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,5 @@ are configurable at compile, boot or run time.
|
||||||
gather_data_sampling
|
gather_data_sampling
|
||||||
reg-file-data-sampling
|
reg-file-data-sampling
|
||||||
rsb
|
rsb
|
||||||
|
old_microcode
|
||||||
indirect-target-selection
|
indirect-target-selection
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
=============
|
||||||
|
Old Microcode
|
||||||
|
=============
|
||||||
|
|
||||||
|
The kernel keeps a table of released microcode. Systems that had
|
||||||
|
microcode older than this at boot will say "Vulnerable". This means
|
||||||
|
that the system was vulnerable to some known CPU issue. It could be
|
||||||
|
security or functional, the kernel does not know or care.
|
||||||
|
|
||||||
|
You should update the CPU microcode to mitigate any exposure. This is
|
||||||
|
usually accomplished by updating the files in
|
||||||
|
/lib/firmware/intel-ucode/ via normal distribution updates. Intel also
|
||||||
|
distributes these files in a github repo:
|
||||||
|
|
||||||
|
https://github.com/intel/Intel-Linux-Processor-Microcode-Data-Files.git
|
||||||
|
|
||||||
|
Just like all the other hardware vulnerabilities, exposure is
|
||||||
|
determined at boot. Runtime microcode updates do not change the status
|
||||||
|
of this vulnerability.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -173,10 +173,10 @@ For example, when an old kernel is running on new hardware.
|
||||||
The kernel disabled support for it at compile-time
|
The kernel disabled support for it at compile-time
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
For example, if 5-level-paging is not enabled when building (i.e.,
|
For example, if Linear Address Masking (LAM) is not enabled when building (i.e.,
|
||||||
CONFIG_X86_5LEVEL is not selected) the flag "la57" will not show up [#f1]_.
|
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
|
Even though the feature will still be detected via CPUID, the kernel disables
|
||||||
it by clearing via setup_clear_cpu_cap(X86_FEATURE_LA57).
|
it by clearing via setup_clear_cpu_cap(X86_FEATURE_LAM).
|
||||||
|
|
||||||
The feature is disabled at boot-time
|
The feature is disabled at boot-time
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
@ -200,5 +200,3 @@ 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
|
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
|
CPUs and them missing microcode patches. Due to that, the kernel decides not to
|
||||||
enable a feature.
|
enable a feature.
|
||||||
|
|
||||||
.. [#f1] 5-level paging uses linear address of 57 bits.
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ x86-specific Documentation
|
||||||
shstk
|
shstk
|
||||||
iommu
|
iommu
|
||||||
intel_txt
|
intel_txt
|
||||||
|
amd-debugging
|
||||||
amd-memory-encryption
|
amd-memory-encryption
|
||||||
amd_hsmp
|
amd_hsmp
|
||||||
tdx
|
tdx
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 350 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 318 KiB |
|
|
@ -22,15 +22,6 @@ QEMU 2.9 and later support 5-level paging.
|
||||||
Virtual memory layout for 5-level paging is described in
|
Virtual memory layout for 5-level paging is described in
|
||||||
Documentation/arch/x86/x86_64/mm.rst
|
Documentation/arch/x86/x86_64/mm.rst
|
||||||
|
|
||||||
|
|
||||||
Enabling 5-level paging
|
|
||||||
=======================
|
|
||||||
CONFIG_X86_5LEVEL=y enables the feature.
|
|
||||||
|
|
||||||
Kernel with CONFIG_X86_5LEVEL=y still able to boot on 4-level hardware.
|
|
||||||
In this case additional page table level -- p4d -- will be folded at
|
|
||||||
runtime.
|
|
||||||
|
|
||||||
User-space and large virtual address space
|
User-space and large virtual address space
|
||||||
==========================================
|
==========================================
|
||||||
On x86, 5-level paging enables 56-bit userspace virtual address space.
|
On x86, 5-level paging enables 56-bit userspace virtual address space.
|
||||||
|
|
|
||||||
|
|
@ -1097,7 +1097,7 @@ R: Carlos Bilbao <carlos.bilbao@kernel.org>
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: Documentation/arch/x86/amd_hsmp.rst
|
F: Documentation/arch/x86/amd_hsmp.rst
|
||||||
F: arch/x86/include/asm/amd_hsmp.h
|
F: arch/x86/include/asm/amd/hsmp.h
|
||||||
F: arch/x86/include/uapi/asm/amd_hsmp.h
|
F: arch/x86/include/uapi/asm/amd_hsmp.h
|
||||||
F: drivers/platform/x86/amd/hsmp/
|
F: drivers/platform/x86/amd/hsmp/
|
||||||
|
|
||||||
|
|
@ -1142,7 +1142,7 @@ M: Mario Limonciello <mario.limonciello@amd.com>
|
||||||
M: Yazen Ghannam <yazen.ghannam@amd.com>
|
M: Yazen Ghannam <yazen.ghannam@amd.com>
|
||||||
L: linux-kernel@vger.kernel.org
|
L: linux-kernel@vger.kernel.org
|
||||||
S: Supported
|
S: Supported
|
||||||
F: arch/x86/include/asm/amd_node.h
|
F: arch/x86/include/asm/amd/node.h
|
||||||
F: arch/x86/kernel/amd_node.c
|
F: arch/x86/kernel/amd_node.c
|
||||||
|
|
||||||
AMD PDS CORE DRIVER
|
AMD PDS CORE DRIVER
|
||||||
|
|
@ -26368,7 +26368,7 @@ R: Ahmed S. Darwish <darwi@linutronix.de>
|
||||||
L: x86-cpuid@lists.linux.dev
|
L: x86-cpuid@lists.linux.dev
|
||||||
S: Maintained
|
S: Maintained
|
||||||
W: https://x86-cpuid.org
|
W: https://x86-cpuid.org
|
||||||
F: tools/arch/x86/kcpuid/cpuid.csv
|
F: tools/arch/x86/kcpuid/
|
||||||
|
|
||||||
X86 ENTRY CODE
|
X86 ENTRY CODE
|
||||||
M: Andy Lutomirski <luto@kernel.org>
|
M: Andy Lutomirski <luto@kernel.org>
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@ void *text_poke_copy(void *addr, const void *opcode, size_t len)
|
||||||
return text_poke(addr, opcode, len);
|
return text_poke(addr, opcode, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void text_poke_sync(void)
|
void smp_text_poke_sync_each_cpu(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,7 @@ config X86
|
||||||
select ARCH_WANT_HUGETLB_VMEMMAP_PREINIT if X86_64
|
select ARCH_WANT_HUGETLB_VMEMMAP_PREINIT if X86_64
|
||||||
select ARCH_WANTS_THP_SWAP if X86_64
|
select ARCH_WANTS_THP_SWAP if X86_64
|
||||||
select ARCH_HAS_PARANOID_L1D_FLUSH
|
select ARCH_HAS_PARANOID_L1D_FLUSH
|
||||||
|
select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
|
||||||
select BUILDTIME_TABLE_SORT
|
select BUILDTIME_TABLE_SORT
|
||||||
select CLKEVT_I8253
|
select CLKEVT_I8253
|
||||||
select CLOCKSOURCE_WATCHDOG
|
select CLOCKSOURCE_WATCHDOG
|
||||||
|
|
@ -426,8 +427,7 @@ config DYNAMIC_PHYSICAL_MASK
|
||||||
|
|
||||||
config PGTABLE_LEVELS
|
config PGTABLE_LEVELS
|
||||||
int
|
int
|
||||||
default 5 if X86_5LEVEL
|
default 5 if X86_64
|
||||||
default 4 if X86_64
|
|
||||||
default 3 if X86_PAE
|
default 3 if X86_PAE
|
||||||
default 2
|
default 2
|
||||||
|
|
||||||
|
|
@ -799,6 +799,7 @@ config PARAVIRT
|
||||||
|
|
||||||
config PARAVIRT_XXL
|
config PARAVIRT_XXL
|
||||||
bool
|
bool
|
||||||
|
depends on X86_64
|
||||||
|
|
||||||
config PARAVIRT_DEBUG
|
config PARAVIRT_DEBUG
|
||||||
bool "paravirt-ops debugging"
|
bool "paravirt-ops debugging"
|
||||||
|
|
@ -1463,27 +1464,6 @@ config X86_PAE
|
||||||
has the cost of more pagetable lookup overhead, and also
|
has the cost of more pagetable lookup overhead, and also
|
||||||
consumes more pagetable space per process.
|
consumes more pagetable space per process.
|
||||||
|
|
||||||
config X86_5LEVEL
|
|
||||||
bool "Enable 5-level page tables support"
|
|
||||||
default y
|
|
||||||
select DYNAMIC_MEMORY_LAYOUT
|
|
||||||
select SPARSEMEM_VMEMMAP
|
|
||||||
depends on X86_64
|
|
||||||
help
|
|
||||||
5-level paging enables access to larger address space:
|
|
||||||
up to 128 PiB of virtual address space and 4 PiB of
|
|
||||||
physical address space.
|
|
||||||
|
|
||||||
It will be supported by future Intel CPUs.
|
|
||||||
|
|
||||||
A kernel with the option enabled can be booted on machines that
|
|
||||||
support 4- or 5-level paging.
|
|
||||||
|
|
||||||
See Documentation/arch/x86/x86_64/5level-paging.rst for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
Say N if unsure.
|
|
||||||
|
|
||||||
config X86_DIRECT_GBPAGES
|
config X86_DIRECT_GBPAGES
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on X86_64
|
depends on X86_64
|
||||||
|
|
@ -1579,6 +1559,7 @@ config ARCH_SPARSEMEM_ENABLE
|
||||||
def_bool y
|
def_bool y
|
||||||
select SPARSEMEM_STATIC if X86_32
|
select SPARSEMEM_STATIC if X86_32
|
||||||
select SPARSEMEM_VMEMMAP_ENABLE if X86_64
|
select SPARSEMEM_VMEMMAP_ENABLE if X86_64
|
||||||
|
select SPARSEMEM_VMEMMAP if X86_64
|
||||||
|
|
||||||
config ARCH_SPARSEMEM_DEFAULT
|
config ARCH_SPARSEMEM_DEFAULT
|
||||||
def_bool X86_64 || (NUMA && X86_32)
|
def_bool X86_64 || (NUMA && X86_32)
|
||||||
|
|
@ -2167,17 +2148,10 @@ config PHYSICAL_ALIGN
|
||||||
|
|
||||||
Don't change this unless you know what you are doing.
|
Don't change this unless you know what you are doing.
|
||||||
|
|
||||||
config DYNAMIC_MEMORY_LAYOUT
|
|
||||||
bool
|
|
||||||
help
|
|
||||||
This option makes base addresses of vmalloc and vmemmap as well as
|
|
||||||
__PAGE_OFFSET movable during boot.
|
|
||||||
|
|
||||||
config RANDOMIZE_MEMORY
|
config RANDOMIZE_MEMORY
|
||||||
bool "Randomize the kernel memory sections"
|
bool "Randomize the kernel memory sections"
|
||||||
depends on X86_64
|
depends on X86_64
|
||||||
depends on RANDOMIZE_BASE
|
depends on RANDOMIZE_BASE
|
||||||
select DYNAMIC_MEMORY_LAYOUT
|
|
||||||
default RANDOMIZE_BASE
|
default RANDOMIZE_BASE
|
||||||
help
|
help
|
||||||
Randomizes the base virtual address of kernel memory sections
|
Randomizes the base virtual address of kernel memory sections
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,6 @@ config AS_AVX512
|
||||||
help
|
help
|
||||||
Supported by binutils >= 2.25 and LLVM integrated assembler
|
Supported by binutils >= 2.25 and LLVM integrated assembler
|
||||||
|
|
||||||
config AS_TPAUSE
|
|
||||||
def_bool $(as-instr,tpause %ecx)
|
|
||||||
help
|
|
||||||
Supported by binutils >= 2.31.1 and LLVM integrated assembler >= V7
|
|
||||||
|
|
||||||
config AS_GFNI
|
config AS_GFNI
|
||||||
def_bool $(as-instr,vgf2p8mulb %xmm0$(comma)%xmm1$(comma)%xmm2)
|
def_bool $(as-instr,vgf2p8mulb %xmm0$(comma)%xmm1$(comma)%xmm2)
|
||||||
help
|
help
|
||||||
|
|
|
||||||
|
|
@ -132,10 +132,6 @@ config X86_DISABLED_FEATURE_OSPKE
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on !X86_INTEL_MEMORY_PROTECTION_KEYS
|
depends on !X86_INTEL_MEMORY_PROTECTION_KEYS
|
||||||
|
|
||||||
config X86_DISABLED_FEATURE_LA57
|
|
||||||
def_bool y
|
|
||||||
depends on !X86_5LEVEL
|
|
||||||
|
|
||||||
config X86_DISABLED_FEATURE_PTI
|
config X86_DISABLED_FEATURE_PTI
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on !MITIGATION_PAGE_TABLE_ISOLATION
|
depends on !MITIGATION_PAGE_TABLE_ISOLATION
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,7 @@ archprepare: $(cpufeaturemasks.hdr)
|
||||||
###
|
###
|
||||||
# Kernel objects
|
# Kernel objects
|
||||||
|
|
||||||
|
core-y += arch/x86/boot/startup/
|
||||||
libs-y += arch/x86/lib/
|
libs-y += arch/x86/lib/
|
||||||
|
|
||||||
# drivers-y are linked after core-y
|
# drivers-y are linked after core-y
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ intcall:
|
||||||
movw %dx, %si
|
movw %dx, %si
|
||||||
movw %sp, %di
|
movw %sp, %di
|
||||||
movw $11, %cx
|
movw $11, %cx
|
||||||
rep; movsl
|
rep movsl
|
||||||
|
|
||||||
/* Pop full state from the stack */
|
/* Pop full state from the stack */
|
||||||
popal
|
popal
|
||||||
|
|
@ -67,7 +67,7 @@ intcall:
|
||||||
jz 4f
|
jz 4f
|
||||||
movw %sp, %si
|
movw %sp, %si
|
||||||
movw $11, %cx
|
movw $11, %cx
|
||||||
rep; movsl
|
rep movsl
|
||||||
4: addw $44, %sp
|
4: addw $44, %sp
|
||||||
|
|
||||||
/* Restore state and return */
|
/* Restore state and return */
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
extern struct setup_header hdr;
|
extern struct setup_header hdr;
|
||||||
extern struct boot_params boot_params;
|
extern struct boot_params boot_params;
|
||||||
|
|
||||||
#define cpu_relax() asm volatile("rep; nop")
|
#define cpu_relax() asm volatile("pause")
|
||||||
|
|
||||||
static inline void io_delay(void)
|
static inline void io_delay(void)
|
||||||
{
|
{
|
||||||
|
|
@ -155,14 +155,14 @@ static inline void wrgs32(u32 v, addr_t addr)
|
||||||
static inline bool memcmp_fs(const void *s1, addr_t s2, size_t len)
|
static inline bool memcmp_fs(const void *s1, addr_t s2, size_t len)
|
||||||
{
|
{
|
||||||
bool diff;
|
bool diff;
|
||||||
asm volatile("fs; repe; cmpsb" CC_SET(nz)
|
asm volatile("fs repe cmpsb" CC_SET(nz)
|
||||||
: CC_OUT(nz) (diff), "+D" (s1), "+S" (s2), "+c" (len));
|
: CC_OUT(nz) (diff), "+D" (s1), "+S" (s2), "+c" (len));
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
static inline bool memcmp_gs(const void *s1, addr_t s2, size_t len)
|
static inline bool memcmp_gs(const void *s1, addr_t s2, size_t len)
|
||||||
{
|
{
|
||||||
bool diff;
|
bool diff;
|
||||||
asm volatile("gs; repe; cmpsb" CC_SET(nz)
|
asm volatile("gs repe cmpsb" CC_SET(nz)
|
||||||
: CC_OUT(nz) (diff), "+D" (s1), "+S" (s2), "+c" (len));
|
: CC_OUT(nz) (diff), "+D" (s1), "+S" (s2), "+c" (len));
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,10 @@ KBUILD_CFLAGS += -D__DISABLE_EXPORTS
|
||||||
KBUILD_CFLAGS += $(call cc-option,-Wa$(comma)-mrelax-relocations=no)
|
KBUILD_CFLAGS += $(call cc-option,-Wa$(comma)-mrelax-relocations=no)
|
||||||
KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h
|
KBUILD_CFLAGS += -include $(srctree)/include/linux/hidden.h
|
||||||
|
|
||||||
# sev.c indirectly includes inat-table.h which is generated during
|
# sev-decode-insn.c indirectly includes inat-table.c which is generated during
|
||||||
# compilation and stored in $(objtree). Add the directory to the includes so
|
# compilation and stored in $(objtree). Add the directory to the includes so
|
||||||
# that the compiler finds it even with out-of-tree builds (make O=/some/path).
|
# that the compiler finds it even with out-of-tree builds (make O=/some/path).
|
||||||
CFLAGS_sev.o += -I$(objtree)/arch/x86/lib/
|
CFLAGS_sev-handle-vc.o += -I$(objtree)/arch/x86/lib/
|
||||||
|
|
||||||
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
|
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ LDFLAGS_vmlinux += -T
|
||||||
hostprogs := mkpiggy
|
hostprogs := mkpiggy
|
||||||
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
|
HOST_EXTRACFLAGS += -I$(srctree)/tools/include
|
||||||
|
|
||||||
sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(_text\|__start_rodata\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
|
sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABbCDGRSTtVW] \(_text\|__start_rodata\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
|
||||||
|
|
||||||
quiet_cmd_voffset = VOFFSET $@
|
quiet_cmd_voffset = VOFFSET $@
|
||||||
cmd_voffset = $(NM) $< | sed -n $(sed-voffset) > $@
|
cmd_voffset = $(NM) $< | sed -n $(sed-voffset) > $@
|
||||||
|
|
@ -96,8 +96,7 @@ ifdef CONFIG_X86_64
|
||||||
vmlinux-objs-y += $(obj)/idt_64.o $(obj)/idt_handlers_64.o
|
vmlinux-objs-y += $(obj)/idt_64.o $(obj)/idt_handlers_64.o
|
||||||
vmlinux-objs-$(CONFIG_AMD_MEM_ENCRYPT) += $(obj)/mem_encrypt.o
|
vmlinux-objs-$(CONFIG_AMD_MEM_ENCRYPT) += $(obj)/mem_encrypt.o
|
||||||
vmlinux-objs-y += $(obj)/pgtable_64.o
|
vmlinux-objs-y += $(obj)/pgtable_64.o
|
||||||
vmlinux-objs-$(CONFIG_AMD_MEM_ENCRYPT) += $(obj)/sev.o
|
vmlinux-objs-$(CONFIG_AMD_MEM_ENCRYPT) += $(obj)/sev.o $(obj)/sev-handle-vc.o
|
||||||
vmlinux-objs-y += $(obj)/la57toggle.o
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
|
vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
|
||||||
|
|
@ -106,6 +105,7 @@ vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o
|
||||||
|
|
||||||
vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
|
vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
|
||||||
vmlinux-libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
|
vmlinux-libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
|
||||||
|
vmlinux-libs-$(CONFIG_X86_64) += $(objtree)/arch/x86/boot/startup/lib.a
|
||||||
|
|
||||||
$(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE
|
$(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE
|
||||||
$(call if_changed,ld)
|
$(call if_changed,ld)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@
|
||||||
#include <asm/bootparam.h>
|
#include <asm/bootparam.h>
|
||||||
#include <asm/desc_defs.h>
|
#include <asm/desc_defs.h>
|
||||||
#include <asm/trapnr.h>
|
#include <asm/trapnr.h>
|
||||||
#include "pgtable.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fix alignment at 16 bytes. Following CONFIG_FUNCTION_ALIGNMENT will result
|
* Fix alignment at 16 bytes. Following CONFIG_FUNCTION_ALIGNMENT will result
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "pgtable.h"
|
|
||||||
#include "../string.h"
|
#include "../string.h"
|
||||||
#include "../voffset.h"
|
#include "../voffset.h"
|
||||||
#include <asm/bootparam_utils.h>
|
#include <asm/bootparam_utils.h>
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,9 @@ static inline void console_init(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
||||||
|
struct es_em_ctxt;
|
||||||
|
struct insn;
|
||||||
|
|
||||||
void sev_enable(struct boot_params *bp);
|
void sev_enable(struct boot_params *bp);
|
||||||
void snp_check_features(void);
|
void snp_check_features(void);
|
||||||
void sev_es_shutdown_ghcb(void);
|
void sev_es_shutdown_ghcb(void);
|
||||||
|
|
@ -143,6 +146,11 @@ extern bool sev_es_check_ghcb_fault(unsigned long address);
|
||||||
void snp_set_page_private(unsigned long paddr);
|
void snp_set_page_private(unsigned long paddr);
|
||||||
void snp_set_page_shared(unsigned long paddr);
|
void snp_set_page_shared(unsigned long paddr);
|
||||||
void sev_prep_identity_maps(unsigned long top_level_pgt);
|
void sev_prep_identity_maps(unsigned long top_level_pgt);
|
||||||
|
|
||||||
|
enum es_result vc_decode_insn(struct es_em_ctxt *ctxt);
|
||||||
|
bool insn_has_rep_prefix(struct insn *insn);
|
||||||
|
void sev_insn_decode_init(void);
|
||||||
|
bool early_setup_ghcb(void);
|
||||||
#else
|
#else
|
||||||
static inline void sev_enable(struct boot_params *bp)
|
static inline void sev_enable(struct boot_params *bp)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#ifndef BOOT_COMPRESSED_PAGETABLE_H
|
|
||||||
#define BOOT_COMPRESSED_PAGETABLE_H
|
|
||||||
|
|
||||||
#define TRAMPOLINE_32BIT_SIZE (2 * PAGE_SIZE)
|
|
||||||
|
|
||||||
#define TRAMPOLINE_32BIT_CODE_OFFSET PAGE_SIZE
|
|
||||||
#define TRAMPOLINE_32BIT_CODE_SIZE 0xA0
|
|
||||||
|
|
||||||
#ifndef __ASSEMBLER__
|
|
||||||
|
|
||||||
extern unsigned long *trampoline_32bit;
|
|
||||||
|
|
||||||
extern void trampoline_32bit_src(void *trampoline, bool enable_5lvl);
|
|
||||||
|
|
||||||
extern const u16 trampoline_ljmp_imm_offset;
|
|
||||||
|
|
||||||
#endif /* __ASSEMBLER__ */
|
|
||||||
#endif /* BOOT_COMPRESSED_PAGETABLE_H */
|
|
||||||
|
|
@ -4,19 +4,16 @@
|
||||||
#include <asm/bootparam_utils.h>
|
#include <asm/bootparam_utils.h>
|
||||||
#include <asm/e820/types.h>
|
#include <asm/e820/types.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include "pgtable.h"
|
|
||||||
#include "../string.h"
|
#include "../string.h"
|
||||||
#include "efi.h"
|
#include "efi.h"
|
||||||
|
|
||||||
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
|
#define BIOS_START_MIN 0x20000U /* 128K, less than this is insane */
|
||||||
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
|
#define BIOS_START_MAX 0x9f000U /* 640K, absolute maximum */
|
||||||
|
|
||||||
#ifdef CONFIG_X86_5LEVEL
|
|
||||||
/* __pgtable_l5_enabled needs to be in .data to avoid being cleared along with .bss */
|
/* __pgtable_l5_enabled needs to be in .data to avoid being cleared along with .bss */
|
||||||
unsigned int __section(".data") __pgtable_l5_enabled;
|
unsigned int __section(".data") __pgtable_l5_enabled;
|
||||||
unsigned int __section(".data") pgdir_shift = 39;
|
unsigned int __section(".data") pgdir_shift = 39;
|
||||||
unsigned int __section(".data") ptrs_per_p4d = 1;
|
unsigned int __section(".data") ptrs_per_p4d = 1;
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Buffer to preserve trampoline memory */
|
/* Buffer to preserve trampoline memory */
|
||||||
static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
|
static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
|
||||||
|
|
@ -115,18 +112,13 @@ asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
|
||||||
* Check if LA57 is desired and supported.
|
* Check if LA57 is desired and supported.
|
||||||
*
|
*
|
||||||
* There are several parts to the check:
|
* There are several parts to the check:
|
||||||
* - if the kernel supports 5-level paging: CONFIG_X86_5LEVEL=y
|
|
||||||
* - if user asked to disable 5-level paging: no5lvl in cmdline
|
* - if user asked to disable 5-level paging: no5lvl in cmdline
|
||||||
* - if the machine supports 5-level paging:
|
* - if the machine supports 5-level paging:
|
||||||
* + CPUID leaf 7 is supported
|
* + CPUID leaf 7 is supported
|
||||||
* + the leaf has the feature bit set
|
* + the leaf has the feature bit set
|
||||||
*
|
|
||||||
* That's substitute for boot_cpu_has() in early boot code.
|
|
||||||
*/
|
*/
|
||||||
if (IS_ENABLED(CONFIG_X86_5LEVEL) &&
|
if (!cmdline_find_option_bool("no5lvl") &&
|
||||||
!cmdline_find_option_bool("no5lvl") &&
|
native_cpuid_eax(0) >= 7 && (native_cpuid_ecx(7) & BIT(16))) {
|
||||||
native_cpuid_eax(0) >= 7 &&
|
|
||||||
(native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31)))) {
|
|
||||||
l5_required = true;
|
l5_required = true;
|
||||||
|
|
||||||
/* Initialize variables for 5-level paging */
|
/* Initialize variables for 5-level paging */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
#include "sev.h"
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <asm/insn.h>
|
||||||
|
#include <asm/pgtable_types.h>
|
||||||
|
#include <asm/ptrace.h>
|
||||||
|
#include <asm/sev.h>
|
||||||
|
#include <asm/trapnr.h>
|
||||||
|
#include <asm/trap_pf.h>
|
||||||
|
#include <asm/fpu/xcr.h>
|
||||||
|
|
||||||
|
#define __BOOT_COMPRESSED
|
||||||
|
|
||||||
|
/* Basic instruction decoding support needed */
|
||||||
|
#include "../../lib/inat.c"
|
||||||
|
#include "../../lib/insn.c"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy a version of this function here - insn-eval.c can't be used in
|
||||||
|
* pre-decompression code.
|
||||||
|
*/
|
||||||
|
bool insn_has_rep_prefix(struct insn *insn)
|
||||||
|
{
|
||||||
|
insn_byte_t p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn_get_prefixes(insn);
|
||||||
|
|
||||||
|
for_each_insn_prefix(insn, i, p) {
|
||||||
|
if (p == 0xf2 || p == 0xf3)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
|
||||||
|
{
|
||||||
|
char buffer[MAX_INSN_SIZE];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memcpy(buffer, (unsigned char *)ctxt->regs->ip, MAX_INSN_SIZE);
|
||||||
|
|
||||||
|
ret = insn_decode(&ctxt->insn, buffer, MAX_INSN_SIZE, INSN_MODE_64);
|
||||||
|
if (ret < 0)
|
||||||
|
return ES_DECODE_FAILED;
|
||||||
|
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void sev_insn_decode_init(void) __alias(inat_init_tables);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only a dummy for insn_get_seg_base() - Early boot-code is 64bit only and
|
||||||
|
* doesn't use segments.
|
||||||
|
*/
|
||||||
|
static unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
|
||||||
|
{
|
||||||
|
return 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
|
||||||
|
void *dst, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
memcpy(dst, buf, size);
|
||||||
|
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
|
||||||
|
void *src, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
memcpy(buf, src, size);
|
||||||
|
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
|
||||||
|
{
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fault_in_kernel_space(unsigned long address)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define sev_printk(fmt, ...)
|
||||||
|
|
||||||
|
#include "../../coco/sev/vc-shared.c"
|
||||||
|
|
||||||
|
void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
|
||||||
|
{
|
||||||
|
struct es_em_ctxt ctxt;
|
||||||
|
enum es_result result;
|
||||||
|
|
||||||
|
if (!boot_ghcb && !early_setup_ghcb())
|
||||||
|
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
|
||||||
|
|
||||||
|
vc_ghcb_invalidate(boot_ghcb);
|
||||||
|
result = vc_init_em_ctxt(&ctxt, regs, exit_code);
|
||||||
|
if (result != ES_OK)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
result = vc_check_opcode_bytes(&ctxt, exit_code);
|
||||||
|
if (result != ES_OK)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
switch (exit_code) {
|
||||||
|
case SVM_EXIT_RDTSC:
|
||||||
|
case SVM_EXIT_RDTSCP:
|
||||||
|
result = vc_handle_rdtsc(boot_ghcb, &ctxt, exit_code);
|
||||||
|
break;
|
||||||
|
case SVM_EXIT_IOIO:
|
||||||
|
result = vc_handle_ioio(boot_ghcb, &ctxt);
|
||||||
|
break;
|
||||||
|
case SVM_EXIT_CPUID:
|
||||||
|
result = vc_handle_cpuid(boot_ghcb, &ctxt);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
result = ES_UNSUPPORTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish:
|
||||||
|
if (result == ES_OK)
|
||||||
|
vc_finish_insn(&ctxt);
|
||||||
|
else if (result != ES_RETRY)
|
||||||
|
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
|
||||||
|
}
|
||||||
|
|
@ -21,99 +21,14 @@
|
||||||
#include <asm/fpu/xcr.h>
|
#include <asm/fpu/xcr.h>
|
||||||
#include <asm/ptrace.h>
|
#include <asm/ptrace.h>
|
||||||
#include <asm/svm.h>
|
#include <asm/svm.h>
|
||||||
#include <asm/cpuid.h>
|
#include <asm/cpuid/api.h>
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "../msr.h"
|
#include "sev.h"
|
||||||
|
|
||||||
static struct ghcb boot_ghcb_page __aligned(PAGE_SIZE);
|
static struct ghcb boot_ghcb_page __aligned(PAGE_SIZE);
|
||||||
struct ghcb *boot_ghcb;
|
struct ghcb *boot_ghcb;
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy a version of this function here - insn-eval.c can't be used in
|
|
||||||
* pre-decompression code.
|
|
||||||
*/
|
|
||||||
static bool insn_has_rep_prefix(struct insn *insn)
|
|
||||||
{
|
|
||||||
insn_byte_t p;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
insn_get_prefixes(insn);
|
|
||||||
|
|
||||||
for_each_insn_prefix(insn, i, p) {
|
|
||||||
if (p == 0xf2 || p == 0xf3)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only a dummy for insn_get_seg_base() - Early boot-code is 64bit only and
|
|
||||||
* doesn't use segments.
|
|
||||||
*/
|
|
||||||
static unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
|
|
||||||
{
|
|
||||||
return 0UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u64 sev_es_rd_ghcb_msr(void)
|
|
||||||
{
|
|
||||||
struct msr m;
|
|
||||||
|
|
||||||
boot_rdmsr(MSR_AMD64_SEV_ES_GHCB, &m);
|
|
||||||
|
|
||||||
return m.q;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void sev_es_wr_ghcb_msr(u64 val)
|
|
||||||
{
|
|
||||||
struct msr m;
|
|
||||||
|
|
||||||
m.q = val;
|
|
||||||
boot_wrmsr(MSR_AMD64_SEV_ES_GHCB, &m);
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum es_result vc_decode_insn(struct es_em_ctxt *ctxt)
|
|
||||||
{
|
|
||||||
char buffer[MAX_INSN_SIZE];
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
memcpy(buffer, (unsigned char *)ctxt->regs->ip, MAX_INSN_SIZE);
|
|
||||||
|
|
||||||
ret = insn_decode(&ctxt->insn, buffer, MAX_INSN_SIZE, INSN_MODE_64);
|
|
||||||
if (ret < 0)
|
|
||||||
return ES_DECODE_FAILED;
|
|
||||||
|
|
||||||
return ES_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
|
|
||||||
void *dst, char *buf, size_t size)
|
|
||||||
{
|
|
||||||
memcpy(dst, buf, size);
|
|
||||||
|
|
||||||
return ES_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
|
|
||||||
void *src, char *buf, size_t size)
|
|
||||||
{
|
|
||||||
memcpy(buf, src, size);
|
|
||||||
|
|
||||||
return ES_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
|
|
||||||
{
|
|
||||||
return ES_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool fault_in_kernel_space(unsigned long address)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef __init
|
#undef __init
|
||||||
#define __init
|
#define __init
|
||||||
|
|
||||||
|
|
@ -122,24 +37,27 @@ static bool fault_in_kernel_space(unsigned long address)
|
||||||
|
|
||||||
#define __BOOT_COMPRESSED
|
#define __BOOT_COMPRESSED
|
||||||
|
|
||||||
/* Basic instruction decoding support needed */
|
extern struct svsm_ca *boot_svsm_caa;
|
||||||
#include "../../lib/inat.c"
|
extern u64 boot_svsm_caa_pa;
|
||||||
#include "../../lib/insn.c"
|
|
||||||
|
|
||||||
/* Include code for early handlers */
|
struct svsm_ca *svsm_get_caa(void)
|
||||||
#include "../../coco/sev/shared.c"
|
|
||||||
|
|
||||||
static struct svsm_ca *svsm_get_caa(void)
|
|
||||||
{
|
{
|
||||||
return boot_svsm_caa;
|
return boot_svsm_caa;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u64 svsm_get_caa_pa(void)
|
u64 svsm_get_caa_pa(void)
|
||||||
{
|
{
|
||||||
return boot_svsm_caa_pa;
|
return boot_svsm_caa_pa;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int svsm_perform_call_protocol(struct svsm_call *call)
|
int svsm_perform_call_protocol(struct svsm_call *call);
|
||||||
|
|
||||||
|
u8 snp_vmpl;
|
||||||
|
|
||||||
|
/* Include code for early handlers */
|
||||||
|
#include "../../boot/startup/sev-shared.c"
|
||||||
|
|
||||||
|
int svsm_perform_call_protocol(struct svsm_call *call)
|
||||||
{
|
{
|
||||||
struct ghcb *ghcb;
|
struct ghcb *ghcb;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
@ -157,7 +75,7 @@ static int svsm_perform_call_protocol(struct svsm_call *call)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sev_snp_enabled(void)
|
static bool sev_snp_enabled(void)
|
||||||
{
|
{
|
||||||
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
|
return sev_status & MSR_AMD64_SEV_SNP_ENABLED;
|
||||||
}
|
}
|
||||||
|
|
@ -212,7 +130,7 @@ void snp_set_page_shared(unsigned long paddr)
|
||||||
__page_state_change(paddr, SNP_PAGE_STATE_SHARED);
|
__page_state_change(paddr, SNP_PAGE_STATE_SHARED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool early_setup_ghcb(void)
|
bool early_setup_ghcb(void)
|
||||||
{
|
{
|
||||||
if (set_page_decrypted((unsigned long)&boot_ghcb_page))
|
if (set_page_decrypted((unsigned long)&boot_ghcb_page))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -223,7 +141,7 @@ static bool early_setup_ghcb(void)
|
||||||
boot_ghcb = &boot_ghcb_page;
|
boot_ghcb = &boot_ghcb_page;
|
||||||
|
|
||||||
/* Initialize lookup tables for the instruction decoder */
|
/* Initialize lookup tables for the instruction decoder */
|
||||||
inat_init_tables();
|
sev_insn_decode_init();
|
||||||
|
|
||||||
/* SNP guest requires the GHCB GPA must be registered */
|
/* SNP guest requires the GHCB GPA must be registered */
|
||||||
if (sev_snp_enabled())
|
if (sev_snp_enabled())
|
||||||
|
|
@ -296,46 +214,6 @@ bool sev_es_check_ghcb_fault(unsigned long address)
|
||||||
return ((address & PAGE_MASK) == (unsigned long)&boot_ghcb_page);
|
return ((address & PAGE_MASK) == (unsigned long)&boot_ghcb_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
|
|
||||||
{
|
|
||||||
struct es_em_ctxt ctxt;
|
|
||||||
enum es_result result;
|
|
||||||
|
|
||||||
if (!boot_ghcb && !early_setup_ghcb())
|
|
||||||
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
|
|
||||||
|
|
||||||
vc_ghcb_invalidate(boot_ghcb);
|
|
||||||
result = vc_init_em_ctxt(&ctxt, regs, exit_code);
|
|
||||||
if (result != ES_OK)
|
|
||||||
goto finish;
|
|
||||||
|
|
||||||
result = vc_check_opcode_bytes(&ctxt, exit_code);
|
|
||||||
if (result != ES_OK)
|
|
||||||
goto finish;
|
|
||||||
|
|
||||||
switch (exit_code) {
|
|
||||||
case SVM_EXIT_RDTSC:
|
|
||||||
case SVM_EXIT_RDTSCP:
|
|
||||||
result = vc_handle_rdtsc(boot_ghcb, &ctxt, exit_code);
|
|
||||||
break;
|
|
||||||
case SVM_EXIT_IOIO:
|
|
||||||
result = vc_handle_ioio(boot_ghcb, &ctxt);
|
|
||||||
break;
|
|
||||||
case SVM_EXIT_CPUID:
|
|
||||||
result = vc_handle_cpuid(boot_ghcb, &ctxt);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
result = ES_UNSUPPORTED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish:
|
|
||||||
if (result == ES_OK)
|
|
||||||
vc_finish_insn(&ctxt);
|
|
||||||
else if (result != ES_RETRY)
|
|
||||||
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SNP_FEATURES_IMPL_REQ is the mask of SNP features that will need
|
* SNP_FEATURES_IMPL_REQ is the mask of SNP features that will need
|
||||||
* guest side implementation for proper functioning of the guest. If any
|
* guest side implementation for proper functioning of the guest. If any
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,31 @@
|
||||||
|
|
||||||
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
||||||
|
|
||||||
bool sev_snp_enabled(void);
|
#include "../msr.h"
|
||||||
|
|
||||||
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
|
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
|
||||||
u64 sev_get_status(void);
|
u64 sev_get_status(void);
|
||||||
bool early_is_sevsnp_guest(void);
|
bool early_is_sevsnp_guest(void);
|
||||||
|
|
||||||
|
static inline u64 sev_es_rd_ghcb_msr(void)
|
||||||
|
{
|
||||||
|
struct msr m;
|
||||||
|
|
||||||
|
boot_rdmsr(MSR_AMD64_SEV_ES_GHCB, &m);
|
||||||
|
|
||||||
|
return m.q;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void sev_es_wr_ghcb_msr(u64 val)
|
||||||
|
{
|
||||||
|
struct msr m;
|
||||||
|
|
||||||
|
m.q = val;
|
||||||
|
boot_wrmsr(MSR_AMD64_SEV_ES_GHCB, &m);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline bool sev_snp_enabled(void) { return false; }
|
|
||||||
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
|
static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { }
|
||||||
static inline u64 sev_get_status(void) { return 0; }
|
static inline u64 sev_get_status(void) { return 0; }
|
||||||
static inline bool early_is_sevsnp_guest(void) { return false; }
|
static inline bool early_is_sevsnp_guest(void) { return false; }
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ static void *____memcpy(void *dest, const void *src, size_t n)
|
||||||
{
|
{
|
||||||
int d0, d1, d2;
|
int d0, d1, d2;
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"rep ; movsl\n\t"
|
"rep movsl\n\t"
|
||||||
"movl %4,%%ecx\n\t"
|
"movl %4,%%ecx\n\t"
|
||||||
"rep ; movsb\n\t"
|
"rep movsb"
|
||||||
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
|
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
|
||||||
: "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src)
|
: "0" (n >> 2), "g" (n & 3), "1" (dest), "2" (src)
|
||||||
: "memory");
|
: "memory");
|
||||||
|
|
@ -29,9 +29,9 @@ static void *____memcpy(void *dest, const void *src, size_t n)
|
||||||
{
|
{
|
||||||
long d0, d1, d2;
|
long d0, d1, d2;
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"rep ; movsq\n\t"
|
"rep movsq\n\t"
|
||||||
"movq %4,%%rcx\n\t"
|
"movq %4,%%rcx\n\t"
|
||||||
"rep ; movsb\n\t"
|
"rep movsb"
|
||||||
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
|
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
|
||||||
: "0" (n >> 3), "g" (n & 7), "1" (dest), "2" (src)
|
: "0" (n >> 3), "g" (n & 7), "1" (dest), "2" (src)
|
||||||
: "memory");
|
: "memory");
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,10 @@ SYM_FUNC_START_NOALIGN(memcpy)
|
||||||
movw %dx, %si
|
movw %dx, %si
|
||||||
pushw %cx
|
pushw %cx
|
||||||
shrw $2, %cx
|
shrw $2, %cx
|
||||||
rep; movsl
|
rep movsl
|
||||||
popw %cx
|
popw %cx
|
||||||
andw $3, %cx
|
andw $3, %cx
|
||||||
rep; movsb
|
rep movsb
|
||||||
popw %di
|
popw %di
|
||||||
popw %si
|
popw %si
|
||||||
retl
|
retl
|
||||||
|
|
@ -38,10 +38,10 @@ SYM_FUNC_START_NOALIGN(memset)
|
||||||
imull $0x01010101,%eax
|
imull $0x01010101,%eax
|
||||||
pushw %cx
|
pushw %cx
|
||||||
shrw $2, %cx
|
shrw $2, %cx
|
||||||
rep; stosl
|
rep stosl
|
||||||
popw %cx
|
popw %cx
|
||||||
andw $3, %cx
|
andw $3, %cx
|
||||||
rep; stosb
|
rep stosb
|
||||||
popw %di
|
popw %di
|
||||||
retl
|
retl
|
||||||
SYM_FUNC_END(memset)
|
SYM_FUNC_END(memset)
|
||||||
|
|
|
||||||
|
|
@ -361,12 +361,8 @@ xloadflags:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
#ifdef CONFIG_X86_5LEVEL
|
|
||||||
#define XLF56 (XLF_5LEVEL|XLF_5LEVEL_ENABLED)
|
#define XLF56 (XLF_5LEVEL|XLF_5LEVEL_ENABLED)
|
||||||
#else
|
#else
|
||||||
#define XLF56 XLF_5LEVEL
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define XLF56 0
|
#define XLF56 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -585,7 +581,7 @@ start_of_setup:
|
||||||
xorl %eax, %eax
|
xorl %eax, %eax
|
||||||
subw %di, %cx
|
subw %di, %cx
|
||||||
shrw $2, %cx
|
shrw $2, %cx
|
||||||
rep; stosl
|
rep stosl
|
||||||
|
|
||||||
# Jump to C code (should not return)
|
# Jump to C code (should not return)
|
||||||
calll main
|
calll main
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
KBUILD_AFLAGS += -D__DISABLE_EXPORTS
|
||||||
|
KBUILD_CFLAGS += -D__DISABLE_EXPORTS -mcmodel=small -fPIC \
|
||||||
|
-Os -DDISABLE_BRANCH_PROFILING \
|
||||||
|
$(DISABLE_STACKLEAK_PLUGIN) \
|
||||||
|
-fno-stack-protector -D__NO_FORTIFY \
|
||||||
|
-fno-jump-tables \
|
||||||
|
-include $(srctree)/include/linux/hidden.h
|
||||||
|
|
||||||
|
# disable ftrace hooks and LTO
|
||||||
|
KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS))
|
||||||
|
KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS))
|
||||||
|
KASAN_SANITIZE := n
|
||||||
|
KCSAN_SANITIZE := n
|
||||||
|
KMSAN_SANITIZE := n
|
||||||
|
UBSAN_SANITIZE := n
|
||||||
|
KCOV_INSTRUMENT := n
|
||||||
|
|
||||||
|
obj-$(CONFIG_X86_64) += gdt_idt.o map_kernel.o
|
||||||
|
obj-$(CONFIG_AMD_MEM_ENCRYPT) += sme.o sev-startup.o
|
||||||
|
|
||||||
|
lib-$(CONFIG_X86_64) += la57toggle.o
|
||||||
|
lib-$(CONFIG_EFI_MIXED) += efi-mixed.o
|
||||||
|
|
||||||
|
#
|
||||||
|
# Disable objtool validation for all library code, which is intended
|
||||||
|
# to be linked into the decompressor or the EFI stub but not vmlinux
|
||||||
|
#
|
||||||
|
$(patsubst %.o,$(obj)/%.o,$(lib-y)): OBJECT_FILES_NON_STANDARD := y
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include <asm/desc.h>
|
||||||
|
#include <asm/init.h>
|
||||||
|
#include <asm/setup.h>
|
||||||
|
#include <asm/sev.h>
|
||||||
|
#include <asm/trapnr.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data structures and code used for IDT setup in head_64.S. The bringup-IDT is
|
||||||
|
* used until the idt_table takes over. On the boot CPU this happens in
|
||||||
|
* x86_64_start_kernel(), on secondary CPUs in start_secondary(). In both cases
|
||||||
|
* this happens in the functions called from head_64.S.
|
||||||
|
*
|
||||||
|
* The idt_table can't be used that early because all the code modifying it is
|
||||||
|
* in idt.c and can be instrumented by tracing or KASAN, which both don't work
|
||||||
|
* during early CPU bringup. Also the idt_table has the runtime vectors
|
||||||
|
* configured which require certain CPU state to be setup already (like TSS),
|
||||||
|
* which also hasn't happened yet in early CPU bringup.
|
||||||
|
*/
|
||||||
|
static gate_desc bringup_idt_table[NUM_EXCEPTION_VECTORS] __page_aligned_data;
|
||||||
|
|
||||||
|
/* This may run while still in the direct mapping */
|
||||||
|
void __head startup_64_load_idt(void *vc_handler)
|
||||||
|
{
|
||||||
|
struct desc_ptr desc = {
|
||||||
|
.address = (unsigned long)rip_rel_ptr(bringup_idt_table),
|
||||||
|
.size = sizeof(bringup_idt_table) - 1,
|
||||||
|
};
|
||||||
|
struct idt_data data;
|
||||||
|
gate_desc idt_desc;
|
||||||
|
|
||||||
|
/* @vc_handler is set only for a VMM Communication Exception */
|
||||||
|
if (vc_handler) {
|
||||||
|
init_idt_data(&data, X86_TRAP_VC, vc_handler);
|
||||||
|
idt_init_desc(&idt_desc, &data);
|
||||||
|
native_write_idt_entry((gate_desc *)desc.address, X86_TRAP_VC, &idt_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
native_load_idt(&desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup boot CPU state needed before kernel switches to virtual addresses.
|
||||||
|
*/
|
||||||
|
void __head startup_64_setup_gdt_idt(void)
|
||||||
|
{
|
||||||
|
struct gdt_page *gp = rip_rel_ptr((void *)(__force unsigned long)&gdt_page);
|
||||||
|
void *handler = NULL;
|
||||||
|
|
||||||
|
struct desc_ptr startup_gdt_descr = {
|
||||||
|
.address = (unsigned long)gp->gdt,
|
||||||
|
.size = GDT_SIZE - 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Load GDT */
|
||||||
|
native_load_gdt(&startup_gdt_descr);
|
||||||
|
|
||||||
|
/* New GDT is live - reload data segment registers */
|
||||||
|
asm volatile("movl %%eax, %%ds\n"
|
||||||
|
"movl %%eax, %%ss\n"
|
||||||
|
"movl %%eax, %%es\n" : : "a"(__KERNEL_DS) : "memory");
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT))
|
||||||
|
handler = rip_rel_ptr(vc_no_ghcb);
|
||||||
|
|
||||||
|
startup_64_load_idt(handler);
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
#include <asm/boot.h>
|
#include <asm/boot.h>
|
||||||
#include <asm/msr.h>
|
#include <asm/msr.h>
|
||||||
#include <asm/processor-flags.h>
|
#include <asm/processor-flags.h>
|
||||||
#include "pgtable.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the 32-bit trampoline that will be copied over to low memory. It
|
* This is the 32-bit trampoline that will be copied over to low memory. It
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/pgtable.h>
|
||||||
|
|
||||||
|
#include <asm/init.h>
|
||||||
|
#include <asm/sections.h>
|
||||||
|
#include <asm/setup.h>
|
||||||
|
#include <asm/sev.h>
|
||||||
|
|
||||||
|
extern pmd_t early_dynamic_pgts[EARLY_DYNAMIC_PAGE_TABLES][PTRS_PER_PMD];
|
||||||
|
extern unsigned int next_early_pgt;
|
||||||
|
|
||||||
|
static inline bool check_la57_support(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 5-level paging is detected and enabled at kernel decompression
|
||||||
|
* stage. Only check if it has been enabled there.
|
||||||
|
*/
|
||||||
|
if (!(native_read_cr4() & X86_CR4_LA57))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
__pgtable_l5_enabled = 1;
|
||||||
|
pgdir_shift = 48;
|
||||||
|
ptrs_per_p4d = 512;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long __head sme_postprocess_startup(struct boot_params *bp,
|
||||||
|
pmdval_t *pmd,
|
||||||
|
unsigned long p2v_offset)
|
||||||
|
{
|
||||||
|
unsigned long paddr, paddr_end;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Encrypt the kernel and related (if SME is active) */
|
||||||
|
sme_encrypt_kernel(bp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the memory encryption mask from the .bss..decrypted section.
|
||||||
|
* The bss section will be memset to zero later in the initialization so
|
||||||
|
* there is no need to zero it after changing the memory encryption
|
||||||
|
* attribute.
|
||||||
|
*/
|
||||||
|
if (sme_get_me_mask()) {
|
||||||
|
paddr = (unsigned long)rip_rel_ptr(__start_bss_decrypted);
|
||||||
|
paddr_end = (unsigned long)rip_rel_ptr(__end_bss_decrypted);
|
||||||
|
|
||||||
|
for (; paddr < paddr_end; paddr += PMD_SIZE) {
|
||||||
|
/*
|
||||||
|
* On SNP, transition the page to shared in the RMP table so that
|
||||||
|
* it is consistent with the page table attribute change.
|
||||||
|
*
|
||||||
|
* __start_bss_decrypted has a virtual address in the high range
|
||||||
|
* mapping (kernel .text). PVALIDATE, by way of
|
||||||
|
* early_snp_set_memory_shared(), requires a valid virtual
|
||||||
|
* address but the kernel is currently running off of the identity
|
||||||
|
* mapping so use the PA to get a *currently* valid virtual address.
|
||||||
|
*/
|
||||||
|
early_snp_set_memory_shared(paddr, paddr, PTRS_PER_PMD);
|
||||||
|
|
||||||
|
i = pmd_index(paddr - p2v_offset);
|
||||||
|
pmd[i] -= sme_get_me_mask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the SME encryption mask (if SME is active) to be used as a
|
||||||
|
* modifier for the initial pgdir entry programmed into CR3.
|
||||||
|
*/
|
||||||
|
return sme_get_me_mask();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code is compiled using PIC codegen because it will execute from the
|
||||||
|
* early 1:1 mapping of memory, which deviates from the mapping expected by the
|
||||||
|
* linker. Due to this deviation, taking the address of a global variable will
|
||||||
|
* produce an ambiguous result when using the plain & operator. Instead,
|
||||||
|
* rip_rel_ptr() must be used, which will return the RIP-relative address in
|
||||||
|
* the 1:1 mapping of memory. Kernel virtual addresses can be determined by
|
||||||
|
* subtracting p2v_offset from the RIP-relative address.
|
||||||
|
*/
|
||||||
|
unsigned long __head __startup_64(unsigned long p2v_offset,
|
||||||
|
struct boot_params *bp)
|
||||||
|
{
|
||||||
|
pmd_t (*early_pgts)[PTRS_PER_PMD] = rip_rel_ptr(early_dynamic_pgts);
|
||||||
|
unsigned long physaddr = (unsigned long)rip_rel_ptr(_text);
|
||||||
|
unsigned long va_text, va_end;
|
||||||
|
unsigned long pgtable_flags;
|
||||||
|
unsigned long load_delta;
|
||||||
|
pgdval_t *pgd;
|
||||||
|
p4dval_t *p4d;
|
||||||
|
pudval_t *pud;
|
||||||
|
pmdval_t *pmd, pmd_entry;
|
||||||
|
bool la57;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
la57 = check_la57_support();
|
||||||
|
|
||||||
|
/* Is the address too large? */
|
||||||
|
if (physaddr >> MAX_PHYSMEM_BITS)
|
||||||
|
for (;;);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the delta between the address I am compiled to run at
|
||||||
|
* and the address I am actually running at.
|
||||||
|
*/
|
||||||
|
phys_base = load_delta = __START_KERNEL_map + p2v_offset;
|
||||||
|
|
||||||
|
/* Is the address not 2M aligned? */
|
||||||
|
if (load_delta & ~PMD_MASK)
|
||||||
|
for (;;);
|
||||||
|
|
||||||
|
va_text = physaddr - p2v_offset;
|
||||||
|
va_end = (unsigned long)rip_rel_ptr(_end) - p2v_offset;
|
||||||
|
|
||||||
|
/* Include the SME encryption mask in the fixup value */
|
||||||
|
load_delta += sme_get_me_mask();
|
||||||
|
|
||||||
|
/* Fixup the physical addresses in the page table */
|
||||||
|
|
||||||
|
pgd = rip_rel_ptr(early_top_pgt);
|
||||||
|
pgd[pgd_index(__START_KERNEL_map)] += load_delta;
|
||||||
|
|
||||||
|
if (la57) {
|
||||||
|
p4d = (p4dval_t *)rip_rel_ptr(level4_kernel_pgt);
|
||||||
|
p4d[MAX_PTRS_PER_P4D - 1] += load_delta;
|
||||||
|
|
||||||
|
pgd[pgd_index(__START_KERNEL_map)] = (pgdval_t)p4d | _PAGE_TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
level3_kernel_pgt[PTRS_PER_PUD - 2].pud += load_delta;
|
||||||
|
level3_kernel_pgt[PTRS_PER_PUD - 1].pud += load_delta;
|
||||||
|
|
||||||
|
for (i = FIXMAP_PMD_TOP; i > FIXMAP_PMD_TOP - FIXMAP_PMD_NUM; i--)
|
||||||
|
level2_fixmap_pgt[i].pmd += load_delta;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the identity mapping for the switchover. These
|
||||||
|
* entries should *NOT* have the global bit set! This also
|
||||||
|
* creates a bunch of nonsense entries but that is fine --
|
||||||
|
* it avoids problems around wraparound.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pud = &early_pgts[0]->pmd;
|
||||||
|
pmd = &early_pgts[1]->pmd;
|
||||||
|
next_early_pgt = 2;
|
||||||
|
|
||||||
|
pgtable_flags = _KERNPG_TABLE_NOENC + sme_get_me_mask();
|
||||||
|
|
||||||
|
if (la57) {
|
||||||
|
p4d = &early_pgts[next_early_pgt++]->pmd;
|
||||||
|
|
||||||
|
i = (physaddr >> PGDIR_SHIFT) % PTRS_PER_PGD;
|
||||||
|
pgd[i + 0] = (pgdval_t)p4d + pgtable_flags;
|
||||||
|
pgd[i + 1] = (pgdval_t)p4d + pgtable_flags;
|
||||||
|
|
||||||
|
i = physaddr >> P4D_SHIFT;
|
||||||
|
p4d[(i + 0) % PTRS_PER_P4D] = (pgdval_t)pud + pgtable_flags;
|
||||||
|
p4d[(i + 1) % PTRS_PER_P4D] = (pgdval_t)pud + pgtable_flags;
|
||||||
|
} else {
|
||||||
|
i = (physaddr >> PGDIR_SHIFT) % PTRS_PER_PGD;
|
||||||
|
pgd[i + 0] = (pgdval_t)pud + pgtable_flags;
|
||||||
|
pgd[i + 1] = (pgdval_t)pud + pgtable_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = physaddr >> PUD_SHIFT;
|
||||||
|
pud[(i + 0) % PTRS_PER_PUD] = (pudval_t)pmd + pgtable_flags;
|
||||||
|
pud[(i + 1) % PTRS_PER_PUD] = (pudval_t)pmd + pgtable_flags;
|
||||||
|
|
||||||
|
pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL;
|
||||||
|
pmd_entry += sme_get_me_mask();
|
||||||
|
pmd_entry += physaddr;
|
||||||
|
|
||||||
|
for (i = 0; i < DIV_ROUND_UP(va_end - va_text, PMD_SIZE); i++) {
|
||||||
|
int idx = i + (physaddr >> PMD_SHIFT);
|
||||||
|
|
||||||
|
pmd[idx % PTRS_PER_PMD] = pmd_entry + i * PMD_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fixup the kernel text+data virtual addresses. Note that
|
||||||
|
* we might write invalid pmds, when the kernel is relocated
|
||||||
|
* cleanup_highmap() fixes this up along with the mappings
|
||||||
|
* beyond _end.
|
||||||
|
*
|
||||||
|
* Only the region occupied by the kernel image has so far
|
||||||
|
* been checked against the table of usable memory regions
|
||||||
|
* provided by the firmware, so invalidate pages outside that
|
||||||
|
* region. A page table entry that maps to a reserved area of
|
||||||
|
* memory would allow processor speculation into that area,
|
||||||
|
* and on some hardware (particularly the UV platform) even
|
||||||
|
* speculative access to some reserved areas is caught as an
|
||||||
|
* error, causing the BIOS to halt the system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pmd = rip_rel_ptr(level2_kernel_pgt);
|
||||||
|
|
||||||
|
/* invalidate pages before the kernel image */
|
||||||
|
for (i = 0; i < pmd_index(va_text); i++)
|
||||||
|
pmd[i] &= ~_PAGE_PRESENT;
|
||||||
|
|
||||||
|
/* fixup pages that are part of the kernel image */
|
||||||
|
for (; i <= pmd_index(va_end); i++)
|
||||||
|
if (pmd[i] & _PAGE_PRESENT)
|
||||||
|
pmd[i] += load_delta;
|
||||||
|
|
||||||
|
/* invalidate pages after the kernel image */
|
||||||
|
for (; i < PTRS_PER_PMD; i++)
|
||||||
|
pmd[i] &= ~_PAGE_PRESENT;
|
||||||
|
|
||||||
|
return sme_postprocess_startup(bp, pmd, p2v_offset);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,368 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* AMD Memory Encryption Support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 SUSE
|
||||||
|
*
|
||||||
|
* Author: Joerg Roedel <jroedel@suse.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "SEV: " fmt
|
||||||
|
|
||||||
|
#include <linux/percpu-defs.h>
|
||||||
|
#include <linux/cc_platform.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/mm_types.h>
|
||||||
|
#include <linux/set_memory.h>
|
||||||
|
#include <linux/memblock.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/efi.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/psp-sev.h>
|
||||||
|
#include <uapi/linux/sev-guest.h>
|
||||||
|
|
||||||
|
#include <asm/init.h>
|
||||||
|
#include <asm/cpu_entry_area.h>
|
||||||
|
#include <asm/stacktrace.h>
|
||||||
|
#include <asm/sev.h>
|
||||||
|
#include <asm/sev-internal.h>
|
||||||
|
#include <asm/insn-eval.h>
|
||||||
|
#include <asm/fpu/xcr.h>
|
||||||
|
#include <asm/processor.h>
|
||||||
|
#include <asm/realmode.h>
|
||||||
|
#include <asm/setup.h>
|
||||||
|
#include <asm/traps.h>
|
||||||
|
#include <asm/svm.h>
|
||||||
|
#include <asm/smp.h>
|
||||||
|
#include <asm/cpu.h>
|
||||||
|
#include <asm/apic.h>
|
||||||
|
#include <asm/cpuid/api.h>
|
||||||
|
#include <asm/cmdline.h>
|
||||||
|
|
||||||
|
/* For early boot hypervisor communication in SEV-ES enabled guests */
|
||||||
|
struct ghcb boot_ghcb_page __bss_decrypted __aligned(PAGE_SIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Needs to be in the .data section because we need it NULL before bss is
|
||||||
|
* cleared
|
||||||
|
*/
|
||||||
|
struct ghcb *boot_ghcb __section(".data");
|
||||||
|
|
||||||
|
/* Bitmap of SEV features supported by the hypervisor */
|
||||||
|
u64 sev_hv_features __ro_after_init;
|
||||||
|
|
||||||
|
/* Secrets page physical address from the CC blob */
|
||||||
|
u64 sev_secrets_pa __ro_after_init;
|
||||||
|
|
||||||
|
/* For early boot SVSM communication */
|
||||||
|
struct svsm_ca boot_svsm_ca_page __aligned(PAGE_SIZE);
|
||||||
|
|
||||||
|
DEFINE_PER_CPU(struct svsm_ca *, svsm_caa);
|
||||||
|
DEFINE_PER_CPU(u64, svsm_caa_pa);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nothing shall interrupt this code path while holding the per-CPU
|
||||||
|
* GHCB. The backup GHCB is only for NMIs interrupting this path.
|
||||||
|
*
|
||||||
|
* Callers must disable local interrupts around it.
|
||||||
|
*/
|
||||||
|
noinstr struct ghcb *__sev_get_ghcb(struct ghcb_state *state)
|
||||||
|
{
|
||||||
|
struct sev_es_runtime_data *data;
|
||||||
|
struct ghcb *ghcb;
|
||||||
|
|
||||||
|
WARN_ON(!irqs_disabled());
|
||||||
|
|
||||||
|
data = this_cpu_read(runtime_data);
|
||||||
|
ghcb = &data->ghcb_page;
|
||||||
|
|
||||||
|
if (unlikely(data->ghcb_active)) {
|
||||||
|
/* GHCB is already in use - save its contents */
|
||||||
|
|
||||||
|
if (unlikely(data->backup_ghcb_active)) {
|
||||||
|
/*
|
||||||
|
* Backup-GHCB is also already in use. There is no way
|
||||||
|
* to continue here so just kill the machine. To make
|
||||||
|
* panic() work, mark GHCBs inactive so that messages
|
||||||
|
* can be printed out.
|
||||||
|
*/
|
||||||
|
data->ghcb_active = false;
|
||||||
|
data->backup_ghcb_active = false;
|
||||||
|
|
||||||
|
instrumentation_begin();
|
||||||
|
panic("Unable to handle #VC exception! GHCB and Backup GHCB are already in use");
|
||||||
|
instrumentation_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark backup_ghcb active before writing to it */
|
||||||
|
data->backup_ghcb_active = true;
|
||||||
|
|
||||||
|
state->ghcb = &data->backup_ghcb;
|
||||||
|
|
||||||
|
/* Backup GHCB content */
|
||||||
|
*state->ghcb = *ghcb;
|
||||||
|
} else {
|
||||||
|
state->ghcb = NULL;
|
||||||
|
data->ghcb_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ghcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Include code shared with pre-decompression boot stage */
|
||||||
|
#include "sev-shared.c"
|
||||||
|
|
||||||
|
noinstr void __sev_put_ghcb(struct ghcb_state *state)
|
||||||
|
{
|
||||||
|
struct sev_es_runtime_data *data;
|
||||||
|
struct ghcb *ghcb;
|
||||||
|
|
||||||
|
WARN_ON(!irqs_disabled());
|
||||||
|
|
||||||
|
data = this_cpu_read(runtime_data);
|
||||||
|
ghcb = &data->ghcb_page;
|
||||||
|
|
||||||
|
if (state->ghcb) {
|
||||||
|
/* Restore GHCB from Backup */
|
||||||
|
*ghcb = *state->ghcb;
|
||||||
|
data->backup_ghcb_active = false;
|
||||||
|
state->ghcb = NULL;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Invalidate the GHCB so a VMGEXIT instruction issued
|
||||||
|
* from userspace won't appear to be valid.
|
||||||
|
*/
|
||||||
|
vc_ghcb_invalidate(ghcb);
|
||||||
|
data->ghcb_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int svsm_perform_call_protocol(struct svsm_call *call)
|
||||||
|
{
|
||||||
|
struct ghcb_state state;
|
||||||
|
unsigned long flags;
|
||||||
|
struct ghcb *ghcb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This can be called very early in the boot, use native functions in
|
||||||
|
* order to avoid paravirt issues.
|
||||||
|
*/
|
||||||
|
flags = native_local_irq_save();
|
||||||
|
|
||||||
|
if (sev_cfg.ghcbs_initialized)
|
||||||
|
ghcb = __sev_get_ghcb(&state);
|
||||||
|
else if (boot_ghcb)
|
||||||
|
ghcb = boot_ghcb;
|
||||||
|
else
|
||||||
|
ghcb = NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = ghcb ? svsm_perform_ghcb_protocol(ghcb, call)
|
||||||
|
: svsm_perform_msr_protocol(call);
|
||||||
|
} while (ret == -EAGAIN);
|
||||||
|
|
||||||
|
if (sev_cfg.ghcbs_initialized)
|
||||||
|
__sev_put_ghcb(&state);
|
||||||
|
|
||||||
|
native_local_irq_restore(flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __head
|
||||||
|
early_set_pages_state(unsigned long vaddr, unsigned long paddr,
|
||||||
|
unsigned long npages, enum psc_op op)
|
||||||
|
{
|
||||||
|
unsigned long paddr_end;
|
||||||
|
u64 val;
|
||||||
|
|
||||||
|
vaddr = vaddr & PAGE_MASK;
|
||||||
|
|
||||||
|
paddr = paddr & PAGE_MASK;
|
||||||
|
paddr_end = paddr + (npages << PAGE_SHIFT);
|
||||||
|
|
||||||
|
while (paddr < paddr_end) {
|
||||||
|
/* Page validation must be rescinded before changing to shared */
|
||||||
|
if (op == SNP_PAGE_STATE_SHARED)
|
||||||
|
pvalidate_4k_page(vaddr, paddr, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the MSR protocol because this function can be called before
|
||||||
|
* the GHCB is established.
|
||||||
|
*/
|
||||||
|
sev_es_wr_ghcb_msr(GHCB_MSR_PSC_REQ_GFN(paddr >> PAGE_SHIFT, op));
|
||||||
|
VMGEXIT();
|
||||||
|
|
||||||
|
val = sev_es_rd_ghcb_msr();
|
||||||
|
|
||||||
|
if (GHCB_RESP_CODE(val) != GHCB_MSR_PSC_RESP)
|
||||||
|
goto e_term;
|
||||||
|
|
||||||
|
if (GHCB_MSR_PSC_RESP_VAL(val))
|
||||||
|
goto e_term;
|
||||||
|
|
||||||
|
/* Page validation must be performed after changing to private */
|
||||||
|
if (op == SNP_PAGE_STATE_PRIVATE)
|
||||||
|
pvalidate_4k_page(vaddr, paddr, true);
|
||||||
|
|
||||||
|
vaddr += PAGE_SIZE;
|
||||||
|
paddr += PAGE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
e_term:
|
||||||
|
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_PSC);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __head early_snp_set_memory_private(unsigned long vaddr, unsigned long paddr,
|
||||||
|
unsigned long npages)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This can be invoked in early boot while running identity mapped, so
|
||||||
|
* use an open coded check for SNP instead of using cc_platform_has().
|
||||||
|
* This eliminates worries about jump tables or checking boot_cpu_data
|
||||||
|
* in the cc_platform_has() function.
|
||||||
|
*/
|
||||||
|
if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ask the hypervisor to mark the memory pages as private in the RMP
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
early_set_pages_state(vaddr, paddr, npages, SNP_PAGE_STATE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __head early_snp_set_memory_shared(unsigned long vaddr, unsigned long paddr,
|
||||||
|
unsigned long npages)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This can be invoked in early boot while running identity mapped, so
|
||||||
|
* use an open coded check for SNP instead of using cc_platform_has().
|
||||||
|
* This eliminates worries about jump tables or checking boot_cpu_data
|
||||||
|
* in the cc_platform_has() function.
|
||||||
|
*/
|
||||||
|
if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Ask hypervisor to mark the memory pages shared in the RMP table. */
|
||||||
|
early_set_pages_state(vaddr, paddr, npages, SNP_PAGE_STATE_SHARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initial set up of SNP relies on information provided by the
|
||||||
|
* Confidential Computing blob, which can be passed to the kernel
|
||||||
|
* in the following ways, depending on how it is booted:
|
||||||
|
*
|
||||||
|
* - when booted via the boot/decompress kernel:
|
||||||
|
* - via boot_params
|
||||||
|
*
|
||||||
|
* - when booted directly by firmware/bootloader (e.g. CONFIG_PVH):
|
||||||
|
* - via a setup_data entry, as defined by the Linux Boot Protocol
|
||||||
|
*
|
||||||
|
* Scan for the blob in that order.
|
||||||
|
*/
|
||||||
|
static __head struct cc_blob_sev_info *find_cc_blob(struct boot_params *bp)
|
||||||
|
{
|
||||||
|
struct cc_blob_sev_info *cc_info;
|
||||||
|
|
||||||
|
/* Boot kernel would have passed the CC blob via boot_params. */
|
||||||
|
if (bp->cc_blob_address) {
|
||||||
|
cc_info = (struct cc_blob_sev_info *)(unsigned long)bp->cc_blob_address;
|
||||||
|
goto found_cc_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If kernel was booted directly, without the use of the
|
||||||
|
* boot/decompression kernel, the CC blob may have been passed via
|
||||||
|
* setup_data instead.
|
||||||
|
*/
|
||||||
|
cc_info = find_cc_blob_setup_data(bp);
|
||||||
|
if (!cc_info)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
found_cc_info:
|
||||||
|
if (cc_info->magic != CC_BLOB_SEV_HDR_MAGIC)
|
||||||
|
snp_abort();
|
||||||
|
|
||||||
|
return cc_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __head void svsm_setup(struct cc_blob_sev_info *cc_info)
|
||||||
|
{
|
||||||
|
struct svsm_call call = {};
|
||||||
|
int ret;
|
||||||
|
u64 pa;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Record the SVSM Calling Area address (CAA) if the guest is not
|
||||||
|
* running at VMPL0. The CA will be used to communicate with the
|
||||||
|
* SVSM to perform the SVSM services.
|
||||||
|
*/
|
||||||
|
if (!svsm_setup_ca(cc_info))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is very early in the boot and the kernel is running identity
|
||||||
|
* mapped but without having adjusted the pagetables to where the
|
||||||
|
* kernel was loaded (physbase), so the get the CA address using
|
||||||
|
* RIP-relative addressing.
|
||||||
|
*/
|
||||||
|
pa = (u64)rip_rel_ptr(&boot_svsm_ca_page);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch over to the boot SVSM CA while the current CA is still
|
||||||
|
* addressable. There is no GHCB at this point so use the MSR protocol.
|
||||||
|
*
|
||||||
|
* SVSM_CORE_REMAP_CA call:
|
||||||
|
* RAX = 0 (Protocol=0, CallID=0)
|
||||||
|
* RCX = New CA GPA
|
||||||
|
*/
|
||||||
|
call.caa = svsm_get_caa();
|
||||||
|
call.rax = SVSM_CORE_CALL(SVSM_CORE_REMAP_CA);
|
||||||
|
call.rcx = pa;
|
||||||
|
ret = svsm_perform_call_protocol(&call);
|
||||||
|
if (ret)
|
||||||
|
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SVSM_CA_REMAP_FAIL);
|
||||||
|
|
||||||
|
boot_svsm_caa = (struct svsm_ca *)pa;
|
||||||
|
boot_svsm_caa_pa = pa;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __head snp_init(struct boot_params *bp)
|
||||||
|
{
|
||||||
|
struct cc_blob_sev_info *cc_info;
|
||||||
|
|
||||||
|
if (!bp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cc_info = find_cc_blob(bp);
|
||||||
|
if (!cc_info)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (cc_info->secrets_phys && cc_info->secrets_len == PAGE_SIZE)
|
||||||
|
sev_secrets_pa = cc_info->secrets_phys;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setup_cpuid_table(cc_info);
|
||||||
|
|
||||||
|
svsm_setup(cc_info);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The CC blob will be used later to access the secrets page. Cache
|
||||||
|
* it here like the boot kernel does.
|
||||||
|
*/
|
||||||
|
bp->cc_blob_address = (u32)(unsigned long)cc_info;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __head __noreturn snp_abort(void)
|
||||||
|
{
|
||||||
|
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
|
||||||
|
}
|
||||||
|
|
@ -45,8 +45,6 @@
|
||||||
#include <asm/coco.h>
|
#include <asm/coco.h>
|
||||||
#include <asm/sev.h>
|
#include <asm/sev.h>
|
||||||
|
|
||||||
#include "mm_internal.h"
|
|
||||||
|
|
||||||
#define PGD_FLAGS _KERNPG_TABLE_NOENC
|
#define PGD_FLAGS _KERNPG_TABLE_NOENC
|
||||||
#define P4D_FLAGS _KERNPG_TABLE_NOENC
|
#define P4D_FLAGS _KERNPG_TABLE_NOENC
|
||||||
#define PUD_FLAGS _KERNPG_TABLE_NOENC
|
#define PUD_FLAGS _KERNPG_TABLE_NOENC
|
||||||
|
|
@ -299,8 +297,7 @@ void __head sme_encrypt_kernel(struct boot_params *bp)
|
||||||
* instrumentation or checking boot_cpu_data in the cc_platform_has()
|
* instrumentation or checking boot_cpu_data in the cc_platform_has()
|
||||||
* function.
|
* function.
|
||||||
*/
|
*/
|
||||||
if (!sme_get_me_mask() ||
|
if (!sme_get_me_mask() || sev_status & MSR_AMD64_SEV_ENABLED)
|
||||||
RIP_REL_REF(sev_status) & MSR_AMD64_SEV_ENABLED)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -318,8 +315,8 @@ void __head sme_encrypt_kernel(struct boot_params *bp)
|
||||||
* memory from being cached.
|
* memory from being cached.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
kernel_start = (unsigned long)RIP_REL_REF(_text);
|
kernel_start = (unsigned long)rip_rel_ptr(_text);
|
||||||
kernel_end = ALIGN((unsigned long)RIP_REL_REF(_end), PMD_SIZE);
|
kernel_end = ALIGN((unsigned long)rip_rel_ptr(_end), PMD_SIZE);
|
||||||
kernel_len = kernel_end - kernel_start;
|
kernel_len = kernel_end - kernel_start;
|
||||||
|
|
||||||
initrd_start = 0;
|
initrd_start = 0;
|
||||||
|
|
@ -345,7 +342,7 @@ void __head sme_encrypt_kernel(struct boot_params *bp)
|
||||||
* pagetable structures for the encryption of the kernel
|
* pagetable structures for the encryption of the kernel
|
||||||
* pagetable structures for workarea (in case not currently mapped)
|
* pagetable structures for workarea (in case not currently mapped)
|
||||||
*/
|
*/
|
||||||
execute_start = workarea_start = (unsigned long)RIP_REL_REF(sme_workarea);
|
execute_start = workarea_start = (unsigned long)rip_rel_ptr(sme_workarea);
|
||||||
execute_end = execute_start + (PAGE_SIZE * 2) + PMD_SIZE;
|
execute_end = execute_start + (PAGE_SIZE * 2) + PMD_SIZE;
|
||||||
execute_len = execute_end - execute_start;
|
execute_len = execute_end - execute_start;
|
||||||
|
|
||||||
|
|
@ -526,7 +523,7 @@ void __head sme_enable(struct boot_params *bp)
|
||||||
me_mask = 1UL << (ebx & 0x3f);
|
me_mask = 1UL << (ebx & 0x3f);
|
||||||
|
|
||||||
/* Check the SEV MSR whether SEV or SME is enabled */
|
/* Check the SEV MSR whether SEV or SME is enabled */
|
||||||
RIP_REL_REF(sev_status) = msr = __rdmsr(MSR_AMD64_SEV);
|
sev_status = msr = native_rdmsrq(MSR_AMD64_SEV);
|
||||||
feature_mask = (msr & MSR_AMD64_SEV_ENABLED) ? AMD_SEV_BIT : AMD_SME_BIT;
|
feature_mask = (msr & MSR_AMD64_SEV_ENABLED) ? AMD_SEV_BIT : AMD_SME_BIT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -557,13 +554,22 @@ void __head sme_enable(struct boot_params *bp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* For SME, check the SYSCFG MSR */
|
/* For SME, check the SYSCFG MSR */
|
||||||
msr = __rdmsr(MSR_AMD64_SYSCFG);
|
msr = native_rdmsrq(MSR_AMD64_SYSCFG);
|
||||||
if (!(msr & MSR_AMD64_SYSCFG_MEM_ENCRYPT))
|
if (!(msr & MSR_AMD64_SYSCFG_MEM_ENCRYPT))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RIP_REL_REF(sme_me_mask) = me_mask;
|
sme_me_mask = me_mask;
|
||||||
RIP_REL_REF(physical_mask) &= ~me_mask;
|
physical_mask &= ~me_mask;
|
||||||
RIP_REL_REF(cc_vendor) = CC_VENDOR_AMD;
|
cc_vendor = CC_VENDOR_AMD;
|
||||||
cc_set_mask(me_mask);
|
cc_set_mask(me_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_MITIGATION_PAGE_TABLE_ISOLATION
|
||||||
|
/* Local version for startup code, which never operates on user page tables */
|
||||||
|
__weak
|
||||||
|
pgd_t __pti_set_user_pgtbl(pgd_t *pgdp, pgd_t pgd)
|
||||||
|
{
|
||||||
|
return pgd;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
int memcmp(const void *s1, const void *s2, size_t len)
|
int memcmp(const void *s1, const void *s2, size_t len)
|
||||||
{
|
{
|
||||||
bool diff;
|
bool diff;
|
||||||
asm("repe; cmpsb" CC_SET(nz)
|
asm("repe cmpsb" CC_SET(nz)
|
||||||
: CC_OUT(nz) (diff), "+D" (s1), "+S" (s2), "+c" (len));
|
: CC_OUT(nz) (diff), "+D" (s1), "+S" (s2), "+c" (len));
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ static void restore_screen(void)
|
||||||
"shrw %%cx ; "
|
"shrw %%cx ; "
|
||||||
"jnc 1f ; "
|
"jnc 1f ; "
|
||||||
"stosw \n\t"
|
"stosw \n\t"
|
||||||
"1: rep;stosl ; "
|
"1: rep stosl ; "
|
||||||
"popw %%es"
|
"popw %%es"
|
||||||
: "+D" (dst), "+c" (npad)
|
: "+D" (dst), "+c" (npad)
|
||||||
: "bdS" (video_segment),
|
: "bdS" (video_segment),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
|
|
||||||
enum cc_vendor cc_vendor __ro_after_init = CC_VENDOR_NONE;
|
enum cc_vendor cc_vendor __ro_after_init = CC_VENDOR_NONE;
|
||||||
|
SYM_PIC_ALIAS(cc_vendor);
|
||||||
u64 cc_mask __ro_after_init;
|
u64 cc_mask __ro_after_init;
|
||||||
|
SYM_PIC_ALIAS(cc_mask);
|
||||||
|
|
||||||
static struct cc_attr_flags {
|
static struct cc_attr_flags {
|
||||||
__u64 host_sev_snp : 1,
|
__u64 host_sev_snp : 1,
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,9 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
obj-y += core.o
|
obj-y += core.o sev-nmi.o vc-handle.o
|
||||||
|
|
||||||
# jump tables are emitted using absolute references in non-PIC code
|
|
||||||
# so they cannot be used in the early SEV startup code
|
|
||||||
CFLAGS_core.o += -fno-jump-tables
|
|
||||||
|
|
||||||
ifdef CONFIG_FUNCTION_TRACER
|
|
||||||
CFLAGS_REMOVE_core.o = -pg
|
|
||||||
endif
|
|
||||||
|
|
||||||
KASAN_SANITIZE_core.o := n
|
|
||||||
KMSAN_SANITIZE_core.o := n
|
|
||||||
KCOV_INSTRUMENT_core.o := n
|
|
||||||
|
|
||||||
# With some compiler versions the generated code results in boot hangs, caused
|
|
||||||
# by several compilation units. To be safe, disable all instrumentation.
|
|
||||||
KCSAN_SANITIZE := n
|
|
||||||
|
|
||||||
# Clang 14 and older may fail to respect __no_sanitize_undefined when inlining
|
# Clang 14 and older may fail to respect __no_sanitize_undefined when inlining
|
||||||
UBSAN_SANITIZE := n
|
UBSAN_SANITIZE_sev-nmi.o := n
|
||||||
|
|
||||||
|
# GCC may fail to respect __no_sanitize_address when inlining
|
||||||
|
KASAN_SANITIZE_sev-nmi.o := n
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,108 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* AMD Memory Encryption Support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 SUSE
|
||||||
|
*
|
||||||
|
* Author: Joerg Roedel <jroedel@suse.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "SEV: " fmt
|
||||||
|
|
||||||
|
#include <linux/bug.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
#include <asm/cpu_entry_area.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
#include <asm/ptrace.h>
|
||||||
|
#include <asm/sev.h>
|
||||||
|
#include <asm/sev-internal.h>
|
||||||
|
|
||||||
|
static __always_inline bool on_vc_stack(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long sp = regs->sp;
|
||||||
|
|
||||||
|
/* User-mode RSP is not trusted */
|
||||||
|
if (user_mode(regs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* SYSCALL gap still has user-mode RSP */
|
||||||
|
if (ip_within_syscall_gap(regs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function handles the case when an NMI is raised in the #VC
|
||||||
|
* exception handler entry code, before the #VC handler has switched off
|
||||||
|
* its IST stack. In this case, the IST entry for #VC must be adjusted,
|
||||||
|
* so that any nested #VC exception will not overwrite the stack
|
||||||
|
* contents of the interrupted #VC handler.
|
||||||
|
*
|
||||||
|
* The IST entry is adjusted unconditionally so that it can be also be
|
||||||
|
* unconditionally adjusted back in __sev_es_ist_exit(). Otherwise a
|
||||||
|
* nested sev_es_ist_exit() call may adjust back the IST entry too
|
||||||
|
* early.
|
||||||
|
*
|
||||||
|
* The __sev_es_ist_enter() and __sev_es_ist_exit() functions always run
|
||||||
|
* on the NMI IST stack, as they are only called from NMI handling code
|
||||||
|
* right now.
|
||||||
|
*/
|
||||||
|
void noinstr __sev_es_ist_enter(struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long old_ist, new_ist;
|
||||||
|
|
||||||
|
/* Read old IST entry */
|
||||||
|
new_ist = old_ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If NMI happened while on the #VC IST stack, set the new IST
|
||||||
|
* value below regs->sp, so that the interrupted stack frame is
|
||||||
|
* not overwritten by subsequent #VC exceptions.
|
||||||
|
*/
|
||||||
|
if (on_vc_stack(regs))
|
||||||
|
new_ist = regs->sp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reserve additional 8 bytes and store old IST value so this
|
||||||
|
* adjustment can be unrolled in __sev_es_ist_exit().
|
||||||
|
*/
|
||||||
|
new_ist -= sizeof(old_ist);
|
||||||
|
*(unsigned long *)new_ist = old_ist;
|
||||||
|
|
||||||
|
/* Set new IST entry */
|
||||||
|
this_cpu_write(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC], new_ist);
|
||||||
|
}
|
||||||
|
|
||||||
|
void noinstr __sev_es_ist_exit(void)
|
||||||
|
{
|
||||||
|
unsigned long ist;
|
||||||
|
|
||||||
|
/* Read IST entry */
|
||||||
|
ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]);
|
||||||
|
|
||||||
|
if (WARN_ON(ist == __this_cpu_ist_top_va(VC)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Read back old IST entry and write it to the TSS */
|
||||||
|
this_cpu_write(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC], *(unsigned long *)ist);
|
||||||
|
}
|
||||||
|
|
||||||
|
void noinstr __sev_es_nmi_complete(void)
|
||||||
|
{
|
||||||
|
struct ghcb_state state;
|
||||||
|
struct ghcb *ghcb;
|
||||||
|
|
||||||
|
ghcb = __sev_get_ghcb(&state);
|
||||||
|
|
||||||
|
vc_ghcb_invalidate(ghcb);
|
||||||
|
ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_NMI_COMPLETE);
|
||||||
|
ghcb_set_sw_exit_info_1(ghcb, 0);
|
||||||
|
ghcb_set_sw_exit_info_2(ghcb, 0);
|
||||||
|
|
||||||
|
sev_es_wr_ghcb_msr(__pa_nodebug(ghcb));
|
||||||
|
VMGEXIT();
|
||||||
|
|
||||||
|
__sev_put_ghcb(&state);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,504 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
static enum es_result vc_check_opcode_bytes(struct es_em_ctxt *ctxt,
|
||||||
|
unsigned long exit_code)
|
||||||
|
{
|
||||||
|
unsigned int opcode = (unsigned int)ctxt->insn.opcode.value;
|
||||||
|
u8 modrm = ctxt->insn.modrm.value;
|
||||||
|
|
||||||
|
switch (exit_code) {
|
||||||
|
|
||||||
|
case SVM_EXIT_IOIO:
|
||||||
|
case SVM_EXIT_NPF:
|
||||||
|
/* handled separately */
|
||||||
|
return ES_OK;
|
||||||
|
|
||||||
|
case SVM_EXIT_CPUID:
|
||||||
|
if (opcode == 0xa20f)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_INVD:
|
||||||
|
if (opcode == 0x080f)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_MONITOR:
|
||||||
|
/* MONITOR and MONITORX instructions generate the same error code */
|
||||||
|
if (opcode == 0x010f && (modrm == 0xc8 || modrm == 0xfa))
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_MWAIT:
|
||||||
|
/* MWAIT and MWAITX instructions generate the same error code */
|
||||||
|
if (opcode == 0x010f && (modrm == 0xc9 || modrm == 0xfb))
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_MSR:
|
||||||
|
/* RDMSR */
|
||||||
|
if (opcode == 0x320f ||
|
||||||
|
/* WRMSR */
|
||||||
|
opcode == 0x300f)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_RDPMC:
|
||||||
|
if (opcode == 0x330f)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_RDTSC:
|
||||||
|
if (opcode == 0x310f)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_RDTSCP:
|
||||||
|
if (opcode == 0x010f && modrm == 0xf9)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_READ_DR7:
|
||||||
|
if (opcode == 0x210f &&
|
||||||
|
X86_MODRM_REG(ctxt->insn.modrm.value) == 7)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_VMMCALL:
|
||||||
|
if (opcode == 0x010f && modrm == 0xd9)
|
||||||
|
return ES_OK;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_WRITE_DR7:
|
||||||
|
if (opcode == 0x230f &&
|
||||||
|
X86_MODRM_REG(ctxt->insn.modrm.value) == 7)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SVM_EXIT_WBINVD:
|
||||||
|
if (opcode == 0x90f)
|
||||||
|
return ES_OK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sev_printk(KERN_ERR "Wrong/unhandled opcode bytes: 0x%x, exit_code: 0x%lx, rIP: 0x%lx\n",
|
||||||
|
opcode, exit_code, ctxt->regs->ip);
|
||||||
|
|
||||||
|
return ES_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool vc_decoding_needed(unsigned long exit_code)
|
||||||
|
{
|
||||||
|
/* Exceptions don't require to decode the instruction */
|
||||||
|
return !(exit_code >= SVM_EXIT_EXCP_BASE &&
|
||||||
|
exit_code <= SVM_EXIT_LAST_EXCP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_init_em_ctxt(struct es_em_ctxt *ctxt,
|
||||||
|
struct pt_regs *regs,
|
||||||
|
unsigned long exit_code)
|
||||||
|
{
|
||||||
|
enum es_result ret = ES_OK;
|
||||||
|
|
||||||
|
memset(ctxt, 0, sizeof(*ctxt));
|
||||||
|
ctxt->regs = regs;
|
||||||
|
|
||||||
|
if (vc_decoding_needed(exit_code))
|
||||||
|
ret = vc_decode_insn(ctxt);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vc_finish_insn(struct es_em_ctxt *ctxt)
|
||||||
|
{
|
||||||
|
ctxt->regs->ip += ctxt->insn.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_insn_string_check(struct es_em_ctxt *ctxt,
|
||||||
|
unsigned long address,
|
||||||
|
bool write)
|
||||||
|
{
|
||||||
|
if (user_mode(ctxt->regs) && fault_in_kernel_space(address)) {
|
||||||
|
ctxt->fi.vector = X86_TRAP_PF;
|
||||||
|
ctxt->fi.error_code = X86_PF_USER;
|
||||||
|
ctxt->fi.cr2 = address;
|
||||||
|
if (write)
|
||||||
|
ctxt->fi.error_code |= X86_PF_WRITE;
|
||||||
|
|
||||||
|
return ES_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt,
|
||||||
|
void *src, char *buf,
|
||||||
|
unsigned int data_size,
|
||||||
|
unsigned int count,
|
||||||
|
bool backwards)
|
||||||
|
{
|
||||||
|
int i, b = backwards ? -1 : 1;
|
||||||
|
unsigned long address = (unsigned long)src;
|
||||||
|
enum es_result ret;
|
||||||
|
|
||||||
|
ret = vc_insn_string_check(ctxt, address, false);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
void *s = src + (i * data_size * b);
|
||||||
|
char *d = buf + (i * data_size);
|
||||||
|
|
||||||
|
ret = vc_read_mem(ctxt, s, d, data_size);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
|
||||||
|
void *dst, char *buf,
|
||||||
|
unsigned int data_size,
|
||||||
|
unsigned int count,
|
||||||
|
bool backwards)
|
||||||
|
{
|
||||||
|
int i, s = backwards ? -1 : 1;
|
||||||
|
unsigned long address = (unsigned long)dst;
|
||||||
|
enum es_result ret;
|
||||||
|
|
||||||
|
ret = vc_insn_string_check(ctxt, address, true);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
void *d = dst + (i * data_size * s);
|
||||||
|
char *b = buf + (i * data_size);
|
||||||
|
|
||||||
|
ret = vc_write_mem(ctxt, d, b, data_size);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IOIO_TYPE_STR BIT(2)
|
||||||
|
#define IOIO_TYPE_IN 1
|
||||||
|
#define IOIO_TYPE_INS (IOIO_TYPE_IN | IOIO_TYPE_STR)
|
||||||
|
#define IOIO_TYPE_OUT 0
|
||||||
|
#define IOIO_TYPE_OUTS (IOIO_TYPE_OUT | IOIO_TYPE_STR)
|
||||||
|
|
||||||
|
#define IOIO_REP BIT(3)
|
||||||
|
|
||||||
|
#define IOIO_ADDR_64 BIT(9)
|
||||||
|
#define IOIO_ADDR_32 BIT(8)
|
||||||
|
#define IOIO_ADDR_16 BIT(7)
|
||||||
|
|
||||||
|
#define IOIO_DATA_32 BIT(6)
|
||||||
|
#define IOIO_DATA_16 BIT(5)
|
||||||
|
#define IOIO_DATA_8 BIT(4)
|
||||||
|
|
||||||
|
#define IOIO_SEG_ES (0 << 10)
|
||||||
|
#define IOIO_SEG_DS (3 << 10)
|
||||||
|
|
||||||
|
static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
|
||||||
|
{
|
||||||
|
struct insn *insn = &ctxt->insn;
|
||||||
|
size_t size;
|
||||||
|
u64 port;
|
||||||
|
|
||||||
|
*exitinfo = 0;
|
||||||
|
|
||||||
|
switch (insn->opcode.bytes[0]) {
|
||||||
|
/* INS opcodes */
|
||||||
|
case 0x6c:
|
||||||
|
case 0x6d:
|
||||||
|
*exitinfo |= IOIO_TYPE_INS;
|
||||||
|
*exitinfo |= IOIO_SEG_ES;
|
||||||
|
port = ctxt->regs->dx & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* OUTS opcodes */
|
||||||
|
case 0x6e:
|
||||||
|
case 0x6f:
|
||||||
|
*exitinfo |= IOIO_TYPE_OUTS;
|
||||||
|
*exitinfo |= IOIO_SEG_DS;
|
||||||
|
port = ctxt->regs->dx & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* IN immediate opcodes */
|
||||||
|
case 0xe4:
|
||||||
|
case 0xe5:
|
||||||
|
*exitinfo |= IOIO_TYPE_IN;
|
||||||
|
port = (u8)insn->immediate.value & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* OUT immediate opcodes */
|
||||||
|
case 0xe6:
|
||||||
|
case 0xe7:
|
||||||
|
*exitinfo |= IOIO_TYPE_OUT;
|
||||||
|
port = (u8)insn->immediate.value & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* IN register opcodes */
|
||||||
|
case 0xec:
|
||||||
|
case 0xed:
|
||||||
|
*exitinfo |= IOIO_TYPE_IN;
|
||||||
|
port = ctxt->regs->dx & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* OUT register opcodes */
|
||||||
|
case 0xee:
|
||||||
|
case 0xef:
|
||||||
|
*exitinfo |= IOIO_TYPE_OUT;
|
||||||
|
port = ctxt->regs->dx & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ES_DECODE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
*exitinfo |= port << 16;
|
||||||
|
|
||||||
|
switch (insn->opcode.bytes[0]) {
|
||||||
|
case 0x6c:
|
||||||
|
case 0x6e:
|
||||||
|
case 0xe4:
|
||||||
|
case 0xe6:
|
||||||
|
case 0xec:
|
||||||
|
case 0xee:
|
||||||
|
/* Single byte opcodes */
|
||||||
|
*exitinfo |= IOIO_DATA_8;
|
||||||
|
size = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Length determined by instruction parsing */
|
||||||
|
*exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
|
||||||
|
: IOIO_DATA_32;
|
||||||
|
size = (insn->opnd_bytes == 2) ? 2 : 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (insn->addr_bytes) {
|
||||||
|
case 2:
|
||||||
|
*exitinfo |= IOIO_ADDR_16;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
*exitinfo |= IOIO_ADDR_32;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
*exitinfo |= IOIO_ADDR_64;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insn_has_rep_prefix(insn))
|
||||||
|
*exitinfo |= IOIO_REP;
|
||||||
|
|
||||||
|
return vc_ioio_check(ctxt, (u16)port, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs = ctxt->regs;
|
||||||
|
u64 exit_info_1, exit_info_2;
|
||||||
|
enum es_result ret;
|
||||||
|
|
||||||
|
ret = vc_ioio_exitinfo(ctxt, &exit_info_1);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (exit_info_1 & IOIO_TYPE_STR) {
|
||||||
|
|
||||||
|
/* (REP) INS/OUTS */
|
||||||
|
|
||||||
|
bool df = ((regs->flags & X86_EFLAGS_DF) == X86_EFLAGS_DF);
|
||||||
|
unsigned int io_bytes, exit_bytes;
|
||||||
|
unsigned int ghcb_count, op_count;
|
||||||
|
unsigned long es_base;
|
||||||
|
u64 sw_scratch;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the string variants with rep prefix the amount of in/out
|
||||||
|
* operations per #VC exception is limited so that the kernel
|
||||||
|
* has a chance to take interrupts and re-schedule while the
|
||||||
|
* instruction is emulated.
|
||||||
|
*/
|
||||||
|
io_bytes = (exit_info_1 >> 4) & 0x7;
|
||||||
|
ghcb_count = sizeof(ghcb->shared_buffer) / io_bytes;
|
||||||
|
|
||||||
|
op_count = (exit_info_1 & IOIO_REP) ? regs->cx : 1;
|
||||||
|
exit_info_2 = min(op_count, ghcb_count);
|
||||||
|
exit_bytes = exit_info_2 * io_bytes;
|
||||||
|
|
||||||
|
es_base = insn_get_seg_base(ctxt->regs, INAT_SEG_REG_ES);
|
||||||
|
|
||||||
|
/* Read bytes of OUTS into the shared buffer */
|
||||||
|
if (!(exit_info_1 & IOIO_TYPE_IN)) {
|
||||||
|
ret = vc_insn_string_read(ctxt,
|
||||||
|
(void *)(es_base + regs->si),
|
||||||
|
ghcb->shared_buffer, io_bytes,
|
||||||
|
exit_info_2, df);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Issue an VMGEXIT to the HV to consume the bytes from the
|
||||||
|
* shared buffer or to have it write them into the shared buffer
|
||||||
|
* depending on the instruction: OUTS or INS.
|
||||||
|
*/
|
||||||
|
sw_scratch = __pa(ghcb) + offsetof(struct ghcb, shared_buffer);
|
||||||
|
ghcb_set_sw_scratch(ghcb, sw_scratch);
|
||||||
|
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO,
|
||||||
|
exit_info_1, exit_info_2);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Read bytes from shared buffer into the guest's destination. */
|
||||||
|
if (exit_info_1 & IOIO_TYPE_IN) {
|
||||||
|
ret = vc_insn_string_write(ctxt,
|
||||||
|
(void *)(es_base + regs->di),
|
||||||
|
ghcb->shared_buffer, io_bytes,
|
||||||
|
exit_info_2, df);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (df)
|
||||||
|
regs->di -= exit_bytes;
|
||||||
|
else
|
||||||
|
regs->di += exit_bytes;
|
||||||
|
} else {
|
||||||
|
if (df)
|
||||||
|
regs->si -= exit_bytes;
|
||||||
|
else
|
||||||
|
regs->si += exit_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exit_info_1 & IOIO_REP)
|
||||||
|
regs->cx -= exit_info_2;
|
||||||
|
|
||||||
|
ret = regs->cx ? ES_RETRY : ES_OK;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* IN/OUT into/from rAX */
|
||||||
|
|
||||||
|
int bits = (exit_info_1 & 0x70) >> 1;
|
||||||
|
u64 rax = 0;
|
||||||
|
|
||||||
|
if (!(exit_info_1 & IOIO_TYPE_IN))
|
||||||
|
rax = lower_bits(regs->ax, bits);
|
||||||
|
|
||||||
|
ghcb_set_rax(ghcb, rax);
|
||||||
|
|
||||||
|
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO, exit_info_1, 0);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (exit_info_1 & IOIO_TYPE_IN) {
|
||||||
|
if (!ghcb_rax_is_valid(ghcb))
|
||||||
|
return ES_VMM_ERROR;
|
||||||
|
regs->ax = lower_bits(ghcb->save.rax, bits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vc_handle_cpuid_snp(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs = ctxt->regs;
|
||||||
|
struct cpuid_leaf leaf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
leaf.fn = regs->ax;
|
||||||
|
leaf.subfn = regs->cx;
|
||||||
|
ret = snp_cpuid(ghcb, ctxt, &leaf);
|
||||||
|
if (!ret) {
|
||||||
|
regs->ax = leaf.eax;
|
||||||
|
regs->bx = leaf.ebx;
|
||||||
|
regs->cx = leaf.ecx;
|
||||||
|
regs->dx = leaf.edx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_handle_cpuid(struct ghcb *ghcb,
|
||||||
|
struct es_em_ctxt *ctxt)
|
||||||
|
{
|
||||||
|
struct pt_regs *regs = ctxt->regs;
|
||||||
|
u32 cr4 = native_read_cr4();
|
||||||
|
enum es_result ret;
|
||||||
|
int snp_cpuid_ret;
|
||||||
|
|
||||||
|
snp_cpuid_ret = vc_handle_cpuid_snp(ghcb, ctxt);
|
||||||
|
if (!snp_cpuid_ret)
|
||||||
|
return ES_OK;
|
||||||
|
if (snp_cpuid_ret != -EOPNOTSUPP)
|
||||||
|
return ES_VMM_ERROR;
|
||||||
|
|
||||||
|
ghcb_set_rax(ghcb, regs->ax);
|
||||||
|
ghcb_set_rcx(ghcb, regs->cx);
|
||||||
|
|
||||||
|
if (cr4 & X86_CR4_OSXSAVE)
|
||||||
|
/* Safe to read xcr0 */
|
||||||
|
ghcb_set_xcr0(ghcb, xgetbv(XCR_XFEATURE_ENABLED_MASK));
|
||||||
|
else
|
||||||
|
/* xgetbv will cause #GP - use reset value for xcr0 */
|
||||||
|
ghcb_set_xcr0(ghcb, 1);
|
||||||
|
|
||||||
|
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_CPUID, 0, 0);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!(ghcb_rax_is_valid(ghcb) &&
|
||||||
|
ghcb_rbx_is_valid(ghcb) &&
|
||||||
|
ghcb_rcx_is_valid(ghcb) &&
|
||||||
|
ghcb_rdx_is_valid(ghcb)))
|
||||||
|
return ES_VMM_ERROR;
|
||||||
|
|
||||||
|
regs->ax = ghcb->save.rax;
|
||||||
|
regs->bx = ghcb->save.rbx;
|
||||||
|
regs->cx = ghcb->save.rcx;
|
||||||
|
regs->dx = ghcb->save.rdx;
|
||||||
|
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum es_result vc_handle_rdtsc(struct ghcb *ghcb,
|
||||||
|
struct es_em_ctxt *ctxt,
|
||||||
|
unsigned long exit_code)
|
||||||
|
{
|
||||||
|
bool rdtscp = (exit_code == SVM_EXIT_RDTSCP);
|
||||||
|
enum es_result ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The hypervisor should not be intercepting RDTSC/RDTSCP when Secure
|
||||||
|
* TSC is enabled. A #VC exception will be generated if the RDTSC/RDTSCP
|
||||||
|
* instructions are being intercepted. If this should occur and Secure
|
||||||
|
* TSC is enabled, guest execution should be terminated as the guest
|
||||||
|
* cannot rely on the TSC value provided by the hypervisor.
|
||||||
|
*/
|
||||||
|
if (sev_status & MSR_AMD64_SNP_SECURE_TSC)
|
||||||
|
return ES_VMM_ERROR;
|
||||||
|
|
||||||
|
ret = sev_es_ghcb_hv_call(ghcb, ctxt, exit_code, 0, 0);
|
||||||
|
if (ret != ES_OK)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!(ghcb_rax_is_valid(ghcb) && ghcb_rdx_is_valid(ghcb) &&
|
||||||
|
(!rdtscp || ghcb_rcx_is_valid(ghcb))))
|
||||||
|
return ES_VMM_ERROR;
|
||||||
|
|
||||||
|
ctxt->regs->ax = ghcb->save.rax;
|
||||||
|
ctxt->regs->dx = ghcb->save.rdx;
|
||||||
|
if (rdtscp)
|
||||||
|
ctxt->regs->cx = ghcb->save.rcx;
|
||||||
|
|
||||||
|
return ES_OK;
|
||||||
|
}
|
||||||
|
|
@ -341,9 +341,7 @@ void __init set_vsyscall_pgtable_user_bits(pgd_t *root)
|
||||||
pgd = pgd_offset_pgd(root, VSYSCALL_ADDR);
|
pgd = pgd_offset_pgd(root, VSYSCALL_ADDR);
|
||||||
set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
|
set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
|
||||||
p4d = p4d_offset(pgd, VSYSCALL_ADDR);
|
p4d = p4d_offset(pgd, VSYSCALL_ADDR);
|
||||||
#if CONFIG_PGTABLE_LEVELS >= 5
|
|
||||||
set_p4d(p4d, __p4d(p4d_val(*p4d) | _PAGE_USER));
|
set_p4d(p4d, __p4d(p4d_val(*p4d) | _PAGE_USER));
|
||||||
#endif
|
|
||||||
pud = pud_offset(p4d, VSYSCALL_ADDR);
|
pud = pud_offset(p4d, VSYSCALL_ADDR);
|
||||||
set_pud(pud, __pud(pud_val(*pud) | _PAGE_USER));
|
set_pud(pud, __pud(pud_val(*pud) | _PAGE_USER));
|
||||||
pmd = pmd_offset(pud, VSYSCALL_ADDR);
|
pmd = pmd_offset(pud, VSYSCALL_ADDR);
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ static inline unsigned int brs_to(int idx)
|
||||||
static __always_inline void set_debug_extn_cfg(u64 val)
|
static __always_inline void set_debug_extn_cfg(u64 val)
|
||||||
{
|
{
|
||||||
/* bits[4:3] must always be set to 11b */
|
/* bits[4:3] must always be set to 11b */
|
||||||
__wrmsr(MSR_AMD_DBG_EXTN_CFG, val | 3ULL << 3, val >> 32);
|
native_wrmsrq(MSR_AMD_DBG_EXTN_CFG, val | 3ULL << 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline u64 get_debug_extn_cfg(void)
|
static __always_inline u64 get_debug_extn_cfg(void)
|
||||||
{
|
{
|
||||||
return __rdmsr(MSR_AMD_DBG_EXTN_CFG);
|
return native_rdmsrq(MSR_AMD_DBG_EXTN_CFG);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool __init amd_brs_detect(void)
|
static bool __init amd_brs_detect(void)
|
||||||
|
|
@ -187,7 +187,7 @@ void amd_brs_reset(void)
|
||||||
/*
|
/*
|
||||||
* Mark first entry as poisoned
|
* Mark first entry as poisoned
|
||||||
*/
|
*/
|
||||||
wrmsrl(brs_to(0), BRS_POISON);
|
wrmsrq(brs_to(0), BRS_POISON);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __init amd_brs_init(void)
|
int __init amd_brs_init(void)
|
||||||
|
|
@ -325,7 +325,7 @@ void amd_brs_drain(void)
|
||||||
u32 brs_idx = tos - i;
|
u32 brs_idx = tos - i;
|
||||||
u64 from, to;
|
u64 from, to;
|
||||||
|
|
||||||
rdmsrl(brs_to(brs_idx), to);
|
rdmsrq(brs_to(brs_idx), to);
|
||||||
|
|
||||||
/* Entry does not belong to us (as marked by kernel) */
|
/* Entry does not belong to us (as marked by kernel) */
|
||||||
if (to == BRS_POISON)
|
if (to == BRS_POISON)
|
||||||
|
|
@ -341,7 +341,7 @@ void amd_brs_drain(void)
|
||||||
if (!amd_brs_match_plm(event, to))
|
if (!amd_brs_match_plm(event, to))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
rdmsrl(brs_from(brs_idx), from);
|
rdmsrq(brs_from(brs_idx), from);
|
||||||
|
|
||||||
perf_clear_branch_entry_bitfields(br+nr);
|
perf_clear_branch_entry_bitfields(br+nr);
|
||||||
|
|
||||||
|
|
@ -371,7 +371,7 @@ static void amd_brs_poison_buffer(void)
|
||||||
idx = amd_brs_get_tos(&cfg);
|
idx = amd_brs_get_tos(&cfg);
|
||||||
|
|
||||||
/* Poison target of entry */
|
/* Poison target of entry */
|
||||||
wrmsrl(brs_to(idx), BRS_POISON);
|
wrmsrq(brs_to(idx), BRS_POISON);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <asm/apicdef.h>
|
#include <asm/apicdef.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
@ -563,13 +564,13 @@ static void amd_pmu_cpu_reset(int cpu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Clear enable bits i.e. PerfCntrGlobalCtl.PerfCntrEn */
|
/* Clear enable bits i.e. PerfCntrGlobalCtl.PerfCntrEn */
|
||||||
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0);
|
wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clear freeze and overflow bits i.e. PerfCntrGLobalStatus.LbrFreeze
|
* Clear freeze and overflow bits i.e. PerfCntrGLobalStatus.LbrFreeze
|
||||||
* and PerfCntrGLobalStatus.PerfCntrOvfl
|
* and PerfCntrGLobalStatus.PerfCntrOvfl
|
||||||
*/
|
*/
|
||||||
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR,
|
wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR,
|
||||||
GLOBAL_STATUS_LBRS_FROZEN | amd_pmu_global_cntr_mask);
|
GLOBAL_STATUS_LBRS_FROZEN | amd_pmu_global_cntr_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,7 +652,7 @@ static void amd_pmu_cpu_dead(int cpu)
|
||||||
|
|
||||||
static __always_inline void amd_pmu_set_global_ctl(u64 ctl)
|
static __always_inline void amd_pmu_set_global_ctl(u64 ctl)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, ctl);
|
wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, ctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u64 amd_pmu_get_global_status(void)
|
static inline u64 amd_pmu_get_global_status(void)
|
||||||
|
|
@ -659,7 +660,7 @@ static inline u64 amd_pmu_get_global_status(void)
|
||||||
u64 status;
|
u64 status;
|
||||||
|
|
||||||
/* PerfCntrGlobalStatus is read-only */
|
/* PerfCntrGlobalStatus is read-only */
|
||||||
rdmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, status);
|
rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, status);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
@ -672,14 +673,14 @@ static inline void amd_pmu_ack_global_status(u64 status)
|
||||||
* clears the same bit in PerfCntrGlobalStatus
|
* clears the same bit in PerfCntrGlobalStatus
|
||||||
*/
|
*/
|
||||||
|
|
||||||
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, status);
|
wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool amd_pmu_test_overflow_topbit(int idx)
|
static bool amd_pmu_test_overflow_topbit(int idx)
|
||||||
{
|
{
|
||||||
u64 counter;
|
u64 counter;
|
||||||
|
|
||||||
rdmsrl(x86_pmu_event_addr(idx), counter);
|
rdmsrq(x86_pmu_event_addr(idx), counter);
|
||||||
|
|
||||||
return !(counter & BIT_ULL(x86_pmu.cntval_bits - 1));
|
return !(counter & BIT_ULL(x86_pmu.cntval_bits - 1));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <linux/sched/clock.h>
|
#include <linux/sched/clock.h>
|
||||||
|
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
@ -26,7 +27,7 @@ static u32 ibs_caps;
|
||||||
#include <linux/hardirq.h>
|
#include <linux/hardirq.h>
|
||||||
|
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/amd-ibs.h>
|
#include <asm/amd/ibs.h>
|
||||||
|
|
||||||
/* attr.config2 */
|
/* attr.config2 */
|
||||||
#define IBS_SW_FILTER_MASK 1
|
#define IBS_SW_FILTER_MASK 1
|
||||||
|
|
@ -424,7 +425,7 @@ perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event,
|
||||||
* prev count manually on overflow.
|
* prev count manually on overflow.
|
||||||
*/
|
*/
|
||||||
while (!perf_event_try_update(event, count, 64)) {
|
while (!perf_event_try_update(event, count, 64)) {
|
||||||
rdmsrl(event->hw.config_base, *config);
|
rdmsrq(event->hw.config_base, *config);
|
||||||
count = perf_ibs->get_count(*config);
|
count = perf_ibs->get_count(*config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -435,9 +436,9 @@ static inline void perf_ibs_enable_event(struct perf_ibs *perf_ibs,
|
||||||
u64 tmp = hwc->config | config;
|
u64 tmp = hwc->config | config;
|
||||||
|
|
||||||
if (perf_ibs->fetch_count_reset_broken)
|
if (perf_ibs->fetch_count_reset_broken)
|
||||||
wrmsrl(hwc->config_base, tmp & ~perf_ibs->enable_mask);
|
wrmsrq(hwc->config_base, tmp & ~perf_ibs->enable_mask);
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, tmp | perf_ibs->enable_mask);
|
wrmsrq(hwc->config_base, tmp | perf_ibs->enable_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -452,9 +453,9 @@ static inline void perf_ibs_disable_event(struct perf_ibs *perf_ibs,
|
||||||
{
|
{
|
||||||
config &= ~perf_ibs->cnt_mask;
|
config &= ~perf_ibs->cnt_mask;
|
||||||
if (boot_cpu_data.x86 == 0x10)
|
if (boot_cpu_data.x86 == 0x10)
|
||||||
wrmsrl(hwc->config_base, config);
|
wrmsrq(hwc->config_base, config);
|
||||||
config &= ~perf_ibs->enable_mask;
|
config &= ~perf_ibs->enable_mask;
|
||||||
wrmsrl(hwc->config_base, config);
|
wrmsrq(hwc->config_base, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -513,7 +514,7 @@ static void perf_ibs_stop(struct perf_event *event, int flags)
|
||||||
if (!stopping && (hwc->state & PERF_HES_UPTODATE))
|
if (!stopping && (hwc->state & PERF_HES_UPTODATE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rdmsrl(hwc->config_base, config);
|
rdmsrq(hwc->config_base, config);
|
||||||
|
|
||||||
if (stopping) {
|
if (stopping) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -1256,7 +1257,7 @@ fail:
|
||||||
hwc = &event->hw;
|
hwc = &event->hw;
|
||||||
msr = hwc->config_base;
|
msr = hwc->config_base;
|
||||||
buf = ibs_data.regs;
|
buf = ibs_data.regs;
|
||||||
rdmsrl(msr, *buf);
|
rdmsrq(msr, *buf);
|
||||||
if (!(*buf++ & perf_ibs->valid_mask))
|
if (!(*buf++ & perf_ibs->valid_mask))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|
@ -1274,7 +1275,7 @@ fail:
|
||||||
offset_max = perf_ibs_get_offset_max(perf_ibs, event, check_rip);
|
offset_max = perf_ibs_get_offset_max(perf_ibs, event, check_rip);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
rdmsrl(msr + offset, *buf++);
|
rdmsrq(msr + offset, *buf++);
|
||||||
size++;
|
size++;
|
||||||
offset = find_next_bit(perf_ibs->offset_mask,
|
offset = find_next_bit(perf_ibs->offset_mask,
|
||||||
perf_ibs->offset_max,
|
perf_ibs->offset_max,
|
||||||
|
|
@ -1304,17 +1305,17 @@ fail:
|
||||||
if (event->attr.sample_type & PERF_SAMPLE_RAW) {
|
if (event->attr.sample_type & PERF_SAMPLE_RAW) {
|
||||||
if (perf_ibs == &perf_ibs_op) {
|
if (perf_ibs == &perf_ibs_op) {
|
||||||
if (ibs_caps & IBS_CAPS_BRNTRGT) {
|
if (ibs_caps & IBS_CAPS_BRNTRGT) {
|
||||||
rdmsrl(MSR_AMD64_IBSBRTARGET, *buf++);
|
rdmsrq(MSR_AMD64_IBSBRTARGET, *buf++);
|
||||||
br_target_idx = size;
|
br_target_idx = size;
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
if (ibs_caps & IBS_CAPS_OPDATA4) {
|
if (ibs_caps & IBS_CAPS_OPDATA4) {
|
||||||
rdmsrl(MSR_AMD64_IBSOPDATA4, *buf++);
|
rdmsrq(MSR_AMD64_IBSOPDATA4, *buf++);
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (perf_ibs == &perf_ibs_fetch && (ibs_caps & IBS_CAPS_FETCHCTLEXTD)) {
|
if (perf_ibs == &perf_ibs_fetch && (ibs_caps & IBS_CAPS_FETCHCTLEXTD)) {
|
||||||
rdmsrl(MSR_AMD64_ICIBSEXTDCTL, *buf++);
|
rdmsrq(MSR_AMD64_ICIBSEXTDCTL, *buf++);
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1563,7 +1564,7 @@ static inline int ibs_eilvt_valid(void)
|
||||||
|
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
|
|
||||||
rdmsrl(MSR_AMD64_IBSCTL, val);
|
rdmsrq(MSR_AMD64_IBSCTL, val);
|
||||||
offset = val & IBSCTL_LVT_OFFSET_MASK;
|
offset = val & IBSCTL_LVT_OFFSET_MASK;
|
||||||
|
|
||||||
if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
|
if (!(val & IBSCTL_LVT_OFFSET_VALID)) {
|
||||||
|
|
@ -1678,7 +1679,7 @@ static inline int get_ibs_lvt_offset(void)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
rdmsrl(MSR_AMD64_IBSCTL, val);
|
rdmsrq(MSR_AMD64_IBSCTL, val);
|
||||||
if (!(val & IBSCTL_LVT_OFFSET_VALID))
|
if (!(val & IBSCTL_LVT_OFFSET_VALID))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/amd-iommu.h>
|
#include <linux/amd-iommu.h>
|
||||||
|
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
#include "iommu.h"
|
#include "iommu.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <asm/perf_event.h>
|
#include <asm/perf_event.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
@ -61,19 +62,19 @@ struct branch_entry {
|
||||||
|
|
||||||
static __always_inline void amd_pmu_lbr_set_from(unsigned int idx, u64 val)
|
static __always_inline void amd_pmu_lbr_set_from(unsigned int idx, u64 val)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2, val);
|
wrmsrq(MSR_AMD_SAMP_BR_FROM + idx * 2, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void amd_pmu_lbr_set_to(unsigned int idx, u64 val)
|
static __always_inline void amd_pmu_lbr_set_to(unsigned int idx, u64 val)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val);
|
wrmsrq(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline u64 amd_pmu_lbr_get_from(unsigned int idx)
|
static __always_inline u64 amd_pmu_lbr_get_from(unsigned int idx)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
rdmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2, val);
|
rdmsrq(MSR_AMD_SAMP_BR_FROM + idx * 2, val);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +83,7 @@ static __always_inline u64 amd_pmu_lbr_get_to(unsigned int idx)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
rdmsrl(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val);
|
rdmsrq(MSR_AMD_SAMP_BR_FROM + idx * 2 + 1, val);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
@ -333,7 +334,7 @@ void amd_pmu_lbr_reset(void)
|
||||||
|
|
||||||
cpuc->last_task_ctx = NULL;
|
cpuc->last_task_ctx = NULL;
|
||||||
cpuc->last_log_id = 0;
|
cpuc->last_log_id = 0;
|
||||||
wrmsrl(MSR_AMD64_LBR_SELECT, 0);
|
wrmsrq(MSR_AMD64_LBR_SELECT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void amd_pmu_lbr_add(struct perf_event *event)
|
void amd_pmu_lbr_add(struct perf_event *event)
|
||||||
|
|
@ -396,16 +397,16 @@ void amd_pmu_lbr_enable_all(void)
|
||||||
/* Set hardware branch filter */
|
/* Set hardware branch filter */
|
||||||
if (cpuc->lbr_select) {
|
if (cpuc->lbr_select) {
|
||||||
lbr_select = cpuc->lbr_sel->config & LBR_SELECT_MASK;
|
lbr_select = cpuc->lbr_sel->config & LBR_SELECT_MASK;
|
||||||
wrmsrl(MSR_AMD64_LBR_SELECT, lbr_select);
|
wrmsrq(MSR_AMD64_LBR_SELECT, lbr_select);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpu_feature_enabled(X86_FEATURE_AMD_LBR_PMC_FREEZE)) {
|
if (cpu_feature_enabled(X86_FEATURE_AMD_LBR_PMC_FREEZE)) {
|
||||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl);
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, dbg_ctl);
|
||||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
wrmsrq(MSR_IA32_DEBUGCTLMSR, dbg_ctl | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
||||||
}
|
}
|
||||||
|
|
||||||
rdmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg);
|
rdmsrq(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg);
|
||||||
wrmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg | DBG_EXTN_CFG_LBRV2EN);
|
wrmsrq(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg | DBG_EXTN_CFG_LBRV2EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void amd_pmu_lbr_disable_all(void)
|
void amd_pmu_lbr_disable_all(void)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
/* Event code: LSB 8 bits, passed in attr->config any other bit is reserved. */
|
/* Event code: LSB 8 bits, passed in attr->config any other bit is reserved. */
|
||||||
|
|
@ -48,8 +49,8 @@ static void event_update(struct perf_event *event)
|
||||||
|
|
||||||
prev_pwr_acc = hwc->pwr_acc;
|
prev_pwr_acc = hwc->pwr_acc;
|
||||||
prev_ptsc = hwc->ptsc;
|
prev_ptsc = hwc->ptsc;
|
||||||
rdmsrl(MSR_F15H_CU_PWR_ACCUMULATOR, new_pwr_acc);
|
rdmsrq(MSR_F15H_CU_PWR_ACCUMULATOR, new_pwr_acc);
|
||||||
rdmsrl(MSR_F15H_PTSC, new_ptsc);
|
rdmsrq(MSR_F15H_PTSC, new_ptsc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the CU power consumption over a time period, the unit of
|
* Calculate the CU power consumption over a time period, the unit of
|
||||||
|
|
@ -75,8 +76,8 @@ static void __pmu_event_start(struct perf_event *event)
|
||||||
|
|
||||||
event->hw.state = 0;
|
event->hw.state = 0;
|
||||||
|
|
||||||
rdmsrl(MSR_F15H_PTSC, event->hw.ptsc);
|
rdmsrq(MSR_F15H_PTSC, event->hw.ptsc);
|
||||||
rdmsrl(MSR_F15H_CU_PWR_ACCUMULATOR, event->hw.pwr_acc);
|
rdmsrq(MSR_F15H_CU_PWR_ACCUMULATOR, event->hw.pwr_acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pmu_event_start(struct perf_event *event, int mode)
|
static void pmu_event_start(struct perf_event *event, int mode)
|
||||||
|
|
@ -272,7 +273,7 @@ static int __init amd_power_pmu_init(void)
|
||||||
|
|
||||||
cpu_pwr_sample_ratio = cpuid_ecx(0x80000007);
|
cpu_pwr_sample_ratio = cpuid_ecx(0x80000007);
|
||||||
|
|
||||||
if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &max_cu_acc_power)) {
|
if (rdmsrq_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &max_cu_acc_power)) {
|
||||||
pr_err("Failed to read max compute unit power accumulator MSR\n");
|
pr_err("Failed to read max compute unit power accumulator MSR\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,9 +150,9 @@ static void amd_uncore_read(struct perf_event *event)
|
||||||
* read counts directly from the corresponding PERF_CTR.
|
* read counts directly from the corresponding PERF_CTR.
|
||||||
*/
|
*/
|
||||||
if (hwc->event_base_rdpmc < 0)
|
if (hwc->event_base_rdpmc < 0)
|
||||||
rdmsrl(hwc->event_base, new);
|
rdmsrq(hwc->event_base, new);
|
||||||
else
|
else
|
||||||
rdpmcl(hwc->event_base_rdpmc, new);
|
new = rdpmc(hwc->event_base_rdpmc);
|
||||||
|
|
||||||
local64_set(&hwc->prev_count, new);
|
local64_set(&hwc->prev_count, new);
|
||||||
delta = (new << COUNTER_SHIFT) - (prev << COUNTER_SHIFT);
|
delta = (new << COUNTER_SHIFT) - (prev << COUNTER_SHIFT);
|
||||||
|
|
@ -170,11 +170,11 @@ static void amd_uncore_start(struct perf_event *event, int flags)
|
||||||
amd_uncore_start_hrtimer(ctx);
|
amd_uncore_start_hrtimer(ctx);
|
||||||
|
|
||||||
if (flags & PERF_EF_RELOAD)
|
if (flags & PERF_EF_RELOAD)
|
||||||
wrmsrl(hwc->event_base, (u64)local64_read(&hwc->prev_count));
|
wrmsrq(hwc->event_base, (u64)local64_read(&hwc->prev_count));
|
||||||
|
|
||||||
hwc->state = 0;
|
hwc->state = 0;
|
||||||
__set_bit(hwc->idx, ctx->active_mask);
|
__set_bit(hwc->idx, ctx->active_mask);
|
||||||
wrmsrl(hwc->config_base, (hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE));
|
wrmsrq(hwc->config_base, (hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE));
|
||||||
perf_event_update_userpage(event);
|
perf_event_update_userpage(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +184,7 @@ static void amd_uncore_stop(struct perf_event *event, int flags)
|
||||||
struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
|
struct amd_uncore_ctx *ctx = *per_cpu_ptr(pmu->ctx, event->cpu);
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config);
|
wrmsrq(hwc->config_base, hwc->config);
|
||||||
hwc->state |= PERF_HES_STOPPED;
|
hwc->state |= PERF_HES_STOPPED;
|
||||||
|
|
||||||
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
|
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
|
||||||
|
|
@ -948,11 +948,11 @@ static void amd_uncore_umc_start(struct perf_event *event, int flags)
|
||||||
amd_uncore_start_hrtimer(ctx);
|
amd_uncore_start_hrtimer(ctx);
|
||||||
|
|
||||||
if (flags & PERF_EF_RELOAD)
|
if (flags & PERF_EF_RELOAD)
|
||||||
wrmsrl(hwc->event_base, (u64)local64_read(&hwc->prev_count));
|
wrmsrq(hwc->event_base, (u64)local64_read(&hwc->prev_count));
|
||||||
|
|
||||||
hwc->state = 0;
|
hwc->state = 0;
|
||||||
__set_bit(hwc->idx, ctx->active_mask);
|
__set_bit(hwc->idx, ctx->active_mask);
|
||||||
wrmsrl(hwc->config_base, (hwc->config | AMD64_PERFMON_V2_ENABLE_UMC));
|
wrmsrq(hwc->config_base, (hwc->config | AMD64_PERFMON_V2_ENABLE_UMC));
|
||||||
perf_event_update_userpage(event);
|
perf_event_update_userpage(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
#include <asm/stacktrace.h>
|
#include <asm/stacktrace.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
#include <asm/smp.h>
|
#include <asm/smp.h>
|
||||||
#include <asm/alternative.h>
|
#include <asm/alternative.h>
|
||||||
|
|
@ -139,7 +140,7 @@ u64 x86_perf_event_update(struct perf_event *event)
|
||||||
*/
|
*/
|
||||||
prev_raw_count = local64_read(&hwc->prev_count);
|
prev_raw_count = local64_read(&hwc->prev_count);
|
||||||
do {
|
do {
|
||||||
rdpmcl(hwc->event_base_rdpmc, new_raw_count);
|
new_raw_count = rdpmc(hwc->event_base_rdpmc);
|
||||||
} while (!local64_try_cmpxchg(&hwc->prev_count,
|
} while (!local64_try_cmpxchg(&hwc->prev_count,
|
||||||
&prev_raw_count, new_raw_count));
|
&prev_raw_count, new_raw_count));
|
||||||
|
|
||||||
|
|
@ -274,7 +275,7 @@ bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
|
||||||
*/
|
*/
|
||||||
for_each_set_bit(i, cntr_mask, X86_PMC_IDX_MAX) {
|
for_each_set_bit(i, cntr_mask, X86_PMC_IDX_MAX) {
|
||||||
reg = x86_pmu_config_addr(i);
|
reg = x86_pmu_config_addr(i);
|
||||||
ret = rdmsrl_safe(reg, &val);
|
ret = rdmsrq_safe(reg, &val);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto msr_fail;
|
goto msr_fail;
|
||||||
if (val & ARCH_PERFMON_EVENTSEL_ENABLE) {
|
if (val & ARCH_PERFMON_EVENTSEL_ENABLE) {
|
||||||
|
|
@ -288,7 +289,7 @@ bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
|
||||||
|
|
||||||
if (*(u64 *)fixed_cntr_mask) {
|
if (*(u64 *)fixed_cntr_mask) {
|
||||||
reg = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
|
reg = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
|
||||||
ret = rdmsrl_safe(reg, &val);
|
ret = rdmsrq_safe(reg, &val);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto msr_fail;
|
goto msr_fail;
|
||||||
for_each_set_bit(i, fixed_cntr_mask, X86_PMC_IDX_MAX) {
|
for_each_set_bit(i, fixed_cntr_mask, X86_PMC_IDX_MAX) {
|
||||||
|
|
@ -319,11 +320,11 @@ bool check_hw_exists(struct pmu *pmu, unsigned long *cntr_mask,
|
||||||
* (qemu/kvm) that don't trap on the MSR access and always return 0s.
|
* (qemu/kvm) that don't trap on the MSR access and always return 0s.
|
||||||
*/
|
*/
|
||||||
reg = x86_pmu_event_addr(reg_safe);
|
reg = x86_pmu_event_addr(reg_safe);
|
||||||
if (rdmsrl_safe(reg, &val))
|
if (rdmsrq_safe(reg, &val))
|
||||||
goto msr_fail;
|
goto msr_fail;
|
||||||
val ^= 0xffffUL;
|
val ^= 0xffffUL;
|
||||||
ret = wrmsrl_safe(reg, val);
|
ret = wrmsrq_safe(reg, val);
|
||||||
ret |= rdmsrl_safe(reg, &val_new);
|
ret |= rdmsrq_safe(reg, &val_new);
|
||||||
if (ret || val != val_new)
|
if (ret || val != val_new)
|
||||||
goto msr_fail;
|
goto msr_fail;
|
||||||
|
|
||||||
|
|
@ -699,13 +700,13 @@ void x86_pmu_disable_all(void)
|
||||||
|
|
||||||
if (!test_bit(idx, cpuc->active_mask))
|
if (!test_bit(idx, cpuc->active_mask))
|
||||||
continue;
|
continue;
|
||||||
rdmsrl(x86_pmu_config_addr(idx), val);
|
rdmsrq(x86_pmu_config_addr(idx), val);
|
||||||
if (!(val & ARCH_PERFMON_EVENTSEL_ENABLE))
|
if (!(val & ARCH_PERFMON_EVENTSEL_ENABLE))
|
||||||
continue;
|
continue;
|
||||||
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
||||||
wrmsrl(x86_pmu_config_addr(idx), val);
|
wrmsrq(x86_pmu_config_addr(idx), val);
|
||||||
if (is_counter_pair(hwc))
|
if (is_counter_pair(hwc))
|
||||||
wrmsrl(x86_pmu_config_addr(idx + 1), 0);
|
wrmsrq(x86_pmu_config_addr(idx + 1), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1427,14 +1428,14 @@ int x86_perf_event_set_period(struct perf_event *event)
|
||||||
*/
|
*/
|
||||||
local64_set(&hwc->prev_count, (u64)-left);
|
local64_set(&hwc->prev_count, (u64)-left);
|
||||||
|
|
||||||
wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
wrmsrq(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sign extend the Merge event counter's upper 16 bits since
|
* Sign extend the Merge event counter's upper 16 bits since
|
||||||
* we currently declare a 48-bit counter width
|
* we currently declare a 48-bit counter width
|
||||||
*/
|
*/
|
||||||
if (is_counter_pair(hwc))
|
if (is_counter_pair(hwc))
|
||||||
wrmsrl(x86_pmu_event_addr(idx + 1), 0xffff);
|
wrmsrq(x86_pmu_event_addr(idx + 1), 0xffff);
|
||||||
|
|
||||||
perf_event_update_userpage(event);
|
perf_event_update_userpage(event);
|
||||||
|
|
||||||
|
|
@ -1557,10 +1558,10 @@ void perf_event_print_debug(void)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (x86_pmu.version >= 2) {
|
if (x86_pmu.version >= 2) {
|
||||||
rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl);
|
rdmsrq(MSR_CORE_PERF_GLOBAL_CTRL, ctrl);
|
||||||
rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
||||||
rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow);
|
rdmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow);
|
||||||
rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed);
|
rdmsrq(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed);
|
||||||
|
|
||||||
pr_info("\n");
|
pr_info("\n");
|
||||||
pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl);
|
pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl);
|
||||||
|
|
@ -1568,19 +1569,19 @@ void perf_event_print_debug(void)
|
||||||
pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow);
|
pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow);
|
||||||
pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed);
|
pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed);
|
||||||
if (pebs_constraints) {
|
if (pebs_constraints) {
|
||||||
rdmsrl(MSR_IA32_PEBS_ENABLE, pebs);
|
rdmsrq(MSR_IA32_PEBS_ENABLE, pebs);
|
||||||
pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs);
|
pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs);
|
||||||
}
|
}
|
||||||
if (x86_pmu.lbr_nr) {
|
if (x86_pmu.lbr_nr) {
|
||||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||||
pr_info("CPU#%d: debugctl: %016llx\n", cpu, debugctl);
|
pr_info("CPU#%d: debugctl: %016llx\n", cpu, debugctl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask);
|
pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask);
|
||||||
|
|
||||||
for_each_set_bit(idx, cntr_mask, X86_PMC_IDX_MAX) {
|
for_each_set_bit(idx, cntr_mask, X86_PMC_IDX_MAX) {
|
||||||
rdmsrl(x86_pmu_config_addr(idx), pmc_ctrl);
|
rdmsrq(x86_pmu_config_addr(idx), pmc_ctrl);
|
||||||
rdmsrl(x86_pmu_event_addr(idx), pmc_count);
|
rdmsrq(x86_pmu_event_addr(idx), pmc_count);
|
||||||
|
|
||||||
prev_left = per_cpu(pmc_prev_left[idx], cpu);
|
prev_left = per_cpu(pmc_prev_left[idx], cpu);
|
||||||
|
|
||||||
|
|
@ -1594,7 +1595,7 @@ void perf_event_print_debug(void)
|
||||||
for_each_set_bit(idx, fixed_cntr_mask, X86_PMC_IDX_MAX) {
|
for_each_set_bit(idx, fixed_cntr_mask, X86_PMC_IDX_MAX) {
|
||||||
if (fixed_counter_disabled(idx, cpuc->pmu))
|
if (fixed_counter_disabled(idx, cpuc->pmu))
|
||||||
continue;
|
continue;
|
||||||
rdmsrl(x86_pmu_fixed_ctr_addr(idx), pmc_count);
|
rdmsrq(x86_pmu_fixed_ctr_addr(idx), pmc_count);
|
||||||
|
|
||||||
pr_info("CPU#%d: fixed-PMC%d count: %016llx\n",
|
pr_info("CPU#%d: fixed-PMC%d count: %016llx\n",
|
||||||
cpu, idx, pmc_count);
|
cpu, idx, pmc_count);
|
||||||
|
|
@ -2509,9 +2510,9 @@ void perf_clear_dirty_counters(void)
|
||||||
if (!test_bit(i - INTEL_PMC_IDX_FIXED, hybrid(cpuc->pmu, fixed_cntr_mask)))
|
if (!test_bit(i - INTEL_PMC_IDX_FIXED, hybrid(cpuc->pmu, fixed_cntr_mask)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
wrmsrl(x86_pmu_fixed_ctr_addr(i - INTEL_PMC_IDX_FIXED), 0);
|
wrmsrq(x86_pmu_fixed_ctr_addr(i - INTEL_PMC_IDX_FIXED), 0);
|
||||||
} else {
|
} else {
|
||||||
wrmsrl(x86_pmu_event_addr(i), 0);
|
wrmsrq(x86_pmu_event_addr(i), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2816,8 +2817,15 @@ static unsigned long get_segment_base(unsigned int segment)
|
||||||
#ifdef CONFIG_MODIFY_LDT_SYSCALL
|
#ifdef CONFIG_MODIFY_LDT_SYSCALL
|
||||||
struct ldt_struct *ldt;
|
struct ldt_struct *ldt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're not in a valid context with a real (not just lazy)
|
||||||
|
* user mm, then don't even try.
|
||||||
|
*/
|
||||||
|
if (!nmi_uaccess_okay())
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* IRQs are off, so this synchronizes with smp_store_release */
|
/* IRQs are off, so this synchronizes with smp_store_release */
|
||||||
ldt = READ_ONCE(current->active_mm->context.ldt);
|
ldt = smp_load_acquire(¤t->mm->context.ldt);
|
||||||
if (!ldt || idx >= ldt->nr_entries)
|
if (!ldt || idx >= ldt->nr_entries)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include <asm/perf_event.h>
|
#include <asm/perf_event.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include <asm/intel_pt.h>
|
#include <asm/intel_pt.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
@ -2297,7 +2298,7 @@ static __always_inline void __intel_pmu_disable_all(bool bts)
|
||||||
{
|
{
|
||||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||||
|
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
||||||
|
|
||||||
if (bts && test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask))
|
if (bts && test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask))
|
||||||
intel_pmu_disable_bts();
|
intel_pmu_disable_bts();
|
||||||
|
|
@ -2318,11 +2319,11 @@ static void __intel_pmu_enable_all(int added, bool pmi)
|
||||||
intel_pmu_lbr_enable_all(pmi);
|
intel_pmu_lbr_enable_all(pmi);
|
||||||
|
|
||||||
if (cpuc->fixed_ctrl_val != cpuc->active_fixed_ctrl_val) {
|
if (cpuc->fixed_ctrl_val != cpuc->active_fixed_ctrl_val) {
|
||||||
wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, cpuc->fixed_ctrl_val);
|
wrmsrq(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, cpuc->fixed_ctrl_val);
|
||||||
cpuc->active_fixed_ctrl_val = cpuc->fixed_ctrl_val;
|
cpuc->active_fixed_ctrl_val = cpuc->fixed_ctrl_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL,
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL,
|
||||||
intel_ctrl & ~cpuc->intel_ctrl_guest_mask);
|
intel_ctrl & ~cpuc->intel_ctrl_guest_mask);
|
||||||
|
|
||||||
if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) {
|
if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) {
|
||||||
|
|
@ -2438,12 +2439,12 @@ static void intel_pmu_nhm_workaround(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + i, nhm_magic[i]);
|
wrmsrq(MSR_ARCH_PERFMON_EVENTSEL0 + i, nhm_magic[i]);
|
||||||
wrmsrl(MSR_ARCH_PERFMON_PERFCTR0 + i, 0x0);
|
wrmsrq(MSR_ARCH_PERFMON_PERFCTR0 + i, 0x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0xf);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0xf);
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x0);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0x0);
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
event = cpuc->events[i];
|
event = cpuc->events[i];
|
||||||
|
|
@ -2453,7 +2454,7 @@ static void intel_pmu_nhm_workaround(void)
|
||||||
__x86_pmu_enable_event(&event->hw,
|
__x86_pmu_enable_event(&event->hw,
|
||||||
ARCH_PERFMON_EVENTSEL_ENABLE);
|
ARCH_PERFMON_EVENTSEL_ENABLE);
|
||||||
} else
|
} else
|
||||||
wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0x0);
|
wrmsrq(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0x0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2470,7 +2471,7 @@ static void intel_set_tfa(struct cpu_hw_events *cpuc, bool on)
|
||||||
|
|
||||||
if (cpuc->tfa_shadow != val) {
|
if (cpuc->tfa_shadow != val) {
|
||||||
cpuc->tfa_shadow = val;
|
cpuc->tfa_shadow = val;
|
||||||
wrmsrl(MSR_TSX_FORCE_ABORT, val);
|
wrmsrq(MSR_TSX_FORCE_ABORT, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2501,14 +2502,14 @@ static inline u64 intel_pmu_get_status(void)
|
||||||
{
|
{
|
||||||
u64 status;
|
u64 status;
|
||||||
|
|
||||||
rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void intel_pmu_ack_status(u64 ack)
|
static inline void intel_pmu_ack_status(u64 ack)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool event_is_checkpointed(struct perf_event *event)
|
static inline bool event_is_checkpointed(struct perf_event *event)
|
||||||
|
|
@ -2634,15 +2635,15 @@ static int icl_set_topdown_event_period(struct perf_event *event)
|
||||||
* Don't need to clear them again.
|
* Don't need to clear them again.
|
||||||
*/
|
*/
|
||||||
if (left == x86_pmu.max_period) {
|
if (left == x86_pmu.max_period) {
|
||||||
wrmsrl(MSR_CORE_PERF_FIXED_CTR3, 0);
|
wrmsrq(MSR_CORE_PERF_FIXED_CTR3, 0);
|
||||||
wrmsrl(MSR_PERF_METRICS, 0);
|
wrmsrq(MSR_PERF_METRICS, 0);
|
||||||
hwc->saved_slots = 0;
|
hwc->saved_slots = 0;
|
||||||
hwc->saved_metric = 0;
|
hwc->saved_metric = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hwc->saved_slots) && is_slots_event(event)) {
|
if ((hwc->saved_slots) && is_slots_event(event)) {
|
||||||
wrmsrl(MSR_CORE_PERF_FIXED_CTR3, hwc->saved_slots);
|
wrmsrq(MSR_CORE_PERF_FIXED_CTR3, hwc->saved_slots);
|
||||||
wrmsrl(MSR_PERF_METRICS, hwc->saved_metric);
|
wrmsrq(MSR_PERF_METRICS, hwc->saved_metric);
|
||||||
}
|
}
|
||||||
|
|
||||||
perf_event_update_userpage(event);
|
perf_event_update_userpage(event);
|
||||||
|
|
@ -2739,12 +2740,12 @@ static u64 intel_update_topdown_event(struct perf_event *event, int metric_end,
|
||||||
|
|
||||||
if (!val) {
|
if (!val) {
|
||||||
/* read Fixed counter 3 */
|
/* read Fixed counter 3 */
|
||||||
rdpmcl((3 | INTEL_PMC_FIXED_RDPMC_BASE), slots);
|
slots = rdpmc(3 | INTEL_PMC_FIXED_RDPMC_BASE);
|
||||||
if (!slots)
|
if (!slots)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* read PERF_METRICS */
|
/* read PERF_METRICS */
|
||||||
rdpmcl(INTEL_PMC_FIXED_RDPMC_METRICS, metrics);
|
metrics = rdpmc(INTEL_PMC_FIXED_RDPMC_METRICS);
|
||||||
} else {
|
} else {
|
||||||
slots = val[0];
|
slots = val[0];
|
||||||
metrics = val[1];
|
metrics = val[1];
|
||||||
|
|
@ -2788,8 +2789,8 @@ static u64 intel_update_topdown_event(struct perf_event *event, int metric_end,
|
||||||
|
|
||||||
if (reset) {
|
if (reset) {
|
||||||
/* The fixed counter 3 has to be written before the PERF_METRICS. */
|
/* The fixed counter 3 has to be written before the PERF_METRICS. */
|
||||||
wrmsrl(MSR_CORE_PERF_FIXED_CTR3, 0);
|
wrmsrq(MSR_CORE_PERF_FIXED_CTR3, 0);
|
||||||
wrmsrl(MSR_PERF_METRICS, 0);
|
wrmsrq(MSR_PERF_METRICS, 0);
|
||||||
if (event)
|
if (event)
|
||||||
update_saved_topdown_regs(event, 0, 0, metric_end);
|
update_saved_topdown_regs(event, 0, 0, metric_end);
|
||||||
}
|
}
|
||||||
|
|
@ -3040,7 +3041,7 @@ int intel_pmu_save_and_restart(struct perf_event *event)
|
||||||
*/
|
*/
|
||||||
if (unlikely(event_is_checkpointed(event))) {
|
if (unlikely(event_is_checkpointed(event))) {
|
||||||
/* No race with NMIs because the counter should not be armed */
|
/* No race with NMIs because the counter should not be armed */
|
||||||
wrmsrl(event->hw.event_base, 0);
|
wrmsrq(event->hw.event_base, 0);
|
||||||
local64_set(&event->hw.prev_count, 0);
|
local64_set(&event->hw.prev_count, 0);
|
||||||
}
|
}
|
||||||
return static_call(x86_pmu_set_period)(event);
|
return static_call(x86_pmu_set_period)(event);
|
||||||
|
|
@ -3079,13 +3080,13 @@ static void intel_pmu_reset(void)
|
||||||
pr_info("clearing PMU state on CPU#%d\n", smp_processor_id());
|
pr_info("clearing PMU state on CPU#%d\n", smp_processor_id());
|
||||||
|
|
||||||
for_each_set_bit(idx, cntr_mask, INTEL_PMC_MAX_GENERIC) {
|
for_each_set_bit(idx, cntr_mask, INTEL_PMC_MAX_GENERIC) {
|
||||||
wrmsrl_safe(x86_pmu_config_addr(idx), 0ull);
|
wrmsrq_safe(x86_pmu_config_addr(idx), 0ull);
|
||||||
wrmsrl_safe(x86_pmu_event_addr(idx), 0ull);
|
wrmsrq_safe(x86_pmu_event_addr(idx), 0ull);
|
||||||
}
|
}
|
||||||
for_each_set_bit(idx, fixed_cntr_mask, INTEL_PMC_MAX_FIXED) {
|
for_each_set_bit(idx, fixed_cntr_mask, INTEL_PMC_MAX_FIXED) {
|
||||||
if (fixed_counter_disabled(idx, cpuc->pmu))
|
if (fixed_counter_disabled(idx, cpuc->pmu))
|
||||||
continue;
|
continue;
|
||||||
wrmsrl_safe(x86_pmu_fixed_ctr_addr(idx), 0ull);
|
wrmsrq_safe(x86_pmu_fixed_ctr_addr(idx), 0ull);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ds)
|
if (ds)
|
||||||
|
|
@ -3094,7 +3095,7 @@ static void intel_pmu_reset(void)
|
||||||
/* Ack all overflows and disable fixed counters */
|
/* Ack all overflows and disable fixed counters */
|
||||||
if (x86_pmu.version >= 2) {
|
if (x86_pmu.version >= 2) {
|
||||||
intel_pmu_ack_status(intel_pmu_get_status());
|
intel_pmu_ack_status(intel_pmu_get_status());
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset LBRs and LBR freezing */
|
/* Reset LBRs and LBR freezing */
|
||||||
|
|
@ -3203,7 +3204,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
|
||||||
* Update the MSR if pebs_enabled is changed.
|
* Update the MSR if pebs_enabled is changed.
|
||||||
*/
|
*/
|
||||||
if (pebs_enabled != cpuc->pebs_enabled)
|
if (pebs_enabled != cpuc->pebs_enabled)
|
||||||
wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
wrmsrq(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Above PEBS handler (PEBS counters snapshotting) has updated fixed
|
* Above PEBS handler (PEBS counters snapshotting) has updated fixed
|
||||||
|
|
@ -5301,7 +5302,7 @@ static void update_pmu_cap(struct pmu *pmu)
|
||||||
|
|
||||||
if (!intel_pmu_broken_perf_cap()) {
|
if (!intel_pmu_broken_perf_cap()) {
|
||||||
/* Perf Metric (Bit 15) and PEBS via PT (Bit 16) are hybrid enumeration */
|
/* Perf Metric (Bit 15) and PEBS via PT (Bit 16) are hybrid enumeration */
|
||||||
rdmsrl(MSR_IA32_PERF_CAPABILITIES, hybrid(pmu, intel_cap).capabilities);
|
rdmsrq(MSR_IA32_PERF_CAPABILITIES, hybrid(pmu, intel_cap).capabilities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5449,7 +5450,7 @@ static void intel_pmu_cpu_starting(int cpu)
|
||||||
if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics) {
|
if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics) {
|
||||||
union perf_capabilities perf_cap;
|
union perf_capabilities perf_cap;
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_cap.capabilities);
|
rdmsrq(MSR_IA32_PERF_CAPABILITIES, perf_cap.capabilities);
|
||||||
if (!perf_cap.perf_metrics) {
|
if (!perf_cap.perf_metrics) {
|
||||||
x86_pmu.intel_cap.perf_metrics = 0;
|
x86_pmu.intel_cap.perf_metrics = 0;
|
||||||
x86_pmu.intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS);
|
x86_pmu.intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS);
|
||||||
|
|
@ -5857,24 +5858,24 @@ static bool check_msr(unsigned long msr, u64 mask)
|
||||||
* matches, this is needed to detect certain hardware emulators
|
* matches, this is needed to detect certain hardware emulators
|
||||||
* (qemu/kvm) that don't trap on the MSR access and always return 0s.
|
* (qemu/kvm) that don't trap on the MSR access and always return 0s.
|
||||||
*/
|
*/
|
||||||
if (rdmsrl_safe(msr, &val_old))
|
if (rdmsrq_safe(msr, &val_old))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only change the bits which can be updated by wrmsrl.
|
* Only change the bits which can be updated by wrmsrq.
|
||||||
*/
|
*/
|
||||||
val_tmp = val_old ^ mask;
|
val_tmp = val_old ^ mask;
|
||||||
|
|
||||||
if (is_lbr_from(msr))
|
if (is_lbr_from(msr))
|
||||||
val_tmp = lbr_from_signext_quirk_wr(val_tmp);
|
val_tmp = lbr_from_signext_quirk_wr(val_tmp);
|
||||||
|
|
||||||
if (wrmsrl_safe(msr, val_tmp) ||
|
if (wrmsrq_safe(msr, val_tmp) ||
|
||||||
rdmsrl_safe(msr, &val_new))
|
rdmsrq_safe(msr, &val_new))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Quirk only affects validation in wrmsr(), so wrmsrl()'s value
|
* Quirk only affects validation in wrmsr(), so wrmsrq()'s value
|
||||||
* should equal rdmsrl()'s even with the quirk.
|
* should equal rdmsrq()'s even with the quirk.
|
||||||
*/
|
*/
|
||||||
if (val_new != val_tmp)
|
if (val_new != val_tmp)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -5885,7 +5886,7 @@ static bool check_msr(unsigned long msr, u64 mask)
|
||||||
/* Here it's sure that the MSR can be safely accessed.
|
/* Here it's sure that the MSR can be safely accessed.
|
||||||
* Restore the old value and return.
|
* Restore the old value and return.
|
||||||
*/
|
*/
|
||||||
wrmsrl(msr, val_old);
|
wrmsrq(msr, val_old);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -6914,7 +6915,7 @@ __init int intel_pmu_init(void)
|
||||||
if (boot_cpu_has(X86_FEATURE_PDCM)) {
|
if (boot_cpu_has(X86_FEATURE_PDCM)) {
|
||||||
u64 capabilities;
|
u64 capabilities;
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities);
|
rdmsrq(MSR_IA32_PERF_CAPABILITIES, capabilities);
|
||||||
x86_pmu.intel_cap.capabilities = capabilities;
|
x86_pmu.intel_cap.capabilities = capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@
|
||||||
#include <linux/nospec.h>
|
#include <linux/nospec.h>
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
#include <asm/intel-family.h>
|
#include <asm/intel-family.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
#include "../probe.h"
|
#include "../probe.h"
|
||||||
|
|
||||||
|
|
@ -320,7 +321,7 @@ static inline u64 cstate_pmu_read_counter(struct perf_event *event)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
rdmsrl(event->hw.event_base, val);
|
rdmsrq(event->hw.event_base, val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <asm/timer.h>
|
#include <asm/timer.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
@ -1520,7 +1521,7 @@ static void intel_pmu_pebs_via_pt_enable(struct perf_event *event)
|
||||||
else
|
else
|
||||||
value = ds->pebs_event_reset[MAX_PEBS_EVENTS + idx];
|
value = ds->pebs_event_reset[MAX_PEBS_EVENTS + idx];
|
||||||
}
|
}
|
||||||
wrmsrl(base + idx, value);
|
wrmsrq(base + idx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc)
|
static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc)
|
||||||
|
|
@ -1557,7 +1558,7 @@ void intel_pmu_pebs_enable(struct perf_event *event)
|
||||||
*/
|
*/
|
||||||
intel_pmu_drain_pebs_buffer();
|
intel_pmu_drain_pebs_buffer();
|
||||||
adaptive_pebs_record_size_update();
|
adaptive_pebs_record_size_update();
|
||||||
wrmsrl(MSR_PEBS_DATA_CFG, pebs_data_cfg);
|
wrmsrq(MSR_PEBS_DATA_CFG, pebs_data_cfg);
|
||||||
cpuc->active_pebs_data_cfg = pebs_data_cfg;
|
cpuc->active_pebs_data_cfg = pebs_data_cfg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1620,7 +1621,7 @@ void intel_pmu_pebs_disable(struct perf_event *event)
|
||||||
intel_pmu_pebs_via_pt_disable(event);
|
intel_pmu_pebs_via_pt_disable(event);
|
||||||
|
|
||||||
if (cpuc->enabled)
|
if (cpuc->enabled)
|
||||||
wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
wrmsrq(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
||||||
|
|
||||||
hwc->config |= ARCH_PERFMON_EVENTSEL_INT;
|
hwc->config |= ARCH_PERFMON_EVENTSEL_INT;
|
||||||
}
|
}
|
||||||
|
|
@ -1630,7 +1631,7 @@ void intel_pmu_pebs_enable_all(void)
|
||||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||||
|
|
||||||
if (cpuc->pebs_enabled)
|
if (cpuc->pebs_enabled)
|
||||||
wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
wrmsrq(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_pmu_pebs_disable_all(void)
|
void intel_pmu_pebs_disable_all(void)
|
||||||
|
|
@ -2276,7 +2277,7 @@ intel_pmu_save_and_restart_reload(struct perf_event *event, int count)
|
||||||
WARN_ON(this_cpu_read(cpu_hw_events.enabled));
|
WARN_ON(this_cpu_read(cpu_hw_events.enabled));
|
||||||
|
|
||||||
prev_raw_count = local64_read(&hwc->prev_count);
|
prev_raw_count = local64_read(&hwc->prev_count);
|
||||||
rdpmcl(hwc->event_base_rdpmc, new_raw_count);
|
new_raw_count = rdpmc(hwc->event_base_rdpmc);
|
||||||
local64_set(&hwc->prev_count, new_raw_count);
|
local64_set(&hwc->prev_count, new_raw_count);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -2794,5 +2795,5 @@ void perf_restore_debug_store(void)
|
||||||
if (!x86_pmu.bts && !x86_pmu.ds_pebs)
|
if (!x86_pmu.bts && !x86_pmu.ds_pebs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wrmsrl(MSR_IA32_DS_AREA, (unsigned long)ds);
|
wrmsrq(MSR_IA32_DS_AREA, (unsigned long)ds);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <asm/hardirq.h>
|
#include <asm/hardirq.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
@ -159,18 +160,18 @@ static void knc_pmu_disable_all(void)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
rdmsrq(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
||||||
val &= ~(KNC_ENABLE_COUNTER0|KNC_ENABLE_COUNTER1);
|
val &= ~(KNC_ENABLE_COUNTER0|KNC_ENABLE_COUNTER1);
|
||||||
wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
wrmsrq(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void knc_pmu_enable_all(int added)
|
static void knc_pmu_enable_all(int added)
|
||||||
{
|
{
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
rdmsrq(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
||||||
val |= (KNC_ENABLE_COUNTER0|KNC_ENABLE_COUNTER1);
|
val |= (KNC_ENABLE_COUNTER0|KNC_ENABLE_COUNTER1);
|
||||||
wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
wrmsrq(MSR_KNC_IA32_PERF_GLOBAL_CTRL, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
|
@ -182,7 +183,7 @@ knc_pmu_disable_event(struct perf_event *event)
|
||||||
val = hwc->config;
|
val = hwc->config;
|
||||||
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
||||||
|
|
||||||
(void)wrmsrl_safe(hwc->config_base + hwc->idx, val);
|
(void)wrmsrq_safe(hwc->config_base + hwc->idx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void knc_pmu_enable_event(struct perf_event *event)
|
static void knc_pmu_enable_event(struct perf_event *event)
|
||||||
|
|
@ -193,21 +194,21 @@ static void knc_pmu_enable_event(struct perf_event *event)
|
||||||
val = hwc->config;
|
val = hwc->config;
|
||||||
val |= ARCH_PERFMON_EVENTSEL_ENABLE;
|
val |= ARCH_PERFMON_EVENTSEL_ENABLE;
|
||||||
|
|
||||||
(void)wrmsrl_safe(hwc->config_base + hwc->idx, val);
|
(void)wrmsrq_safe(hwc->config_base + hwc->idx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u64 knc_pmu_get_status(void)
|
static inline u64 knc_pmu_get_status(void)
|
||||||
{
|
{
|
||||||
u64 status;
|
u64 status;
|
||||||
|
|
||||||
rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_STATUS, status);
|
rdmsrq(MSR_KNC_IA32_PERF_GLOBAL_STATUS, status);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void knc_pmu_ack_status(u64 ack)
|
static inline void knc_pmu_ack_status(u64 ack)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_OVF_CONTROL, ack);
|
wrmsrq(MSR_KNC_IA32_PERF_GLOBAL_OVF_CONTROL, ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int knc_pmu_handle_irq(struct pt_regs *regs)
|
static int knc_pmu_handle_irq(struct pt_regs *regs)
|
||||||
|
|
|
||||||
|
|
@ -137,9 +137,9 @@ static void __intel_pmu_lbr_enable(bool pmi)
|
||||||
if (cpuc->lbr_sel)
|
if (cpuc->lbr_sel)
|
||||||
lbr_select = cpuc->lbr_sel->config & x86_pmu.lbr_sel_mask;
|
lbr_select = cpuc->lbr_sel->config & x86_pmu.lbr_sel_mask;
|
||||||
if (!static_cpu_has(X86_FEATURE_ARCH_LBR) && !pmi && cpuc->lbr_sel)
|
if (!static_cpu_has(X86_FEATURE_ARCH_LBR) && !pmi && cpuc->lbr_sel)
|
||||||
wrmsrl(MSR_LBR_SELECT, lbr_select);
|
wrmsrq(MSR_LBR_SELECT, lbr_select);
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||||
orig_debugctl = debugctl;
|
orig_debugctl = debugctl;
|
||||||
|
|
||||||
if (!static_cpu_has(X86_FEATURE_ARCH_LBR))
|
if (!static_cpu_has(X86_FEATURE_ARCH_LBR))
|
||||||
|
|
@ -155,10 +155,10 @@ static void __intel_pmu_lbr_enable(bool pmi)
|
||||||
debugctl |= DEBUGCTLMSR_FREEZE_LBRS_ON_PMI;
|
debugctl |= DEBUGCTLMSR_FREEZE_LBRS_ON_PMI;
|
||||||
|
|
||||||
if (orig_debugctl != debugctl)
|
if (orig_debugctl != debugctl)
|
||||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||||
|
|
||||||
if (static_cpu_has(X86_FEATURE_ARCH_LBR))
|
if (static_cpu_has(X86_FEATURE_ARCH_LBR))
|
||||||
wrmsrl(MSR_ARCH_LBR_CTL, lbr_select | ARCH_LBR_CTL_LBREN);
|
wrmsrq(MSR_ARCH_LBR_CTL, lbr_select | ARCH_LBR_CTL_LBREN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_pmu_lbr_reset_32(void)
|
void intel_pmu_lbr_reset_32(void)
|
||||||
|
|
@ -166,7 +166,7 @@ void intel_pmu_lbr_reset_32(void)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < x86_pmu.lbr_nr; i++)
|
for (i = 0; i < x86_pmu.lbr_nr; i++)
|
||||||
wrmsrl(x86_pmu.lbr_from + i, 0);
|
wrmsrq(x86_pmu.lbr_from + i, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_pmu_lbr_reset_64(void)
|
void intel_pmu_lbr_reset_64(void)
|
||||||
|
|
@ -174,17 +174,17 @@ void intel_pmu_lbr_reset_64(void)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||||
wrmsrl(x86_pmu.lbr_from + i, 0);
|
wrmsrq(x86_pmu.lbr_from + i, 0);
|
||||||
wrmsrl(x86_pmu.lbr_to + i, 0);
|
wrmsrq(x86_pmu.lbr_to + i, 0);
|
||||||
if (x86_pmu.lbr_has_info)
|
if (x86_pmu.lbr_has_info)
|
||||||
wrmsrl(x86_pmu.lbr_info + i, 0);
|
wrmsrq(x86_pmu.lbr_info + i, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_pmu_arch_lbr_reset(void)
|
static void intel_pmu_arch_lbr_reset(void)
|
||||||
{
|
{
|
||||||
/* Write to ARCH_LBR_DEPTH MSR, all LBR entries are reset to 0 */
|
/* Write to ARCH_LBR_DEPTH MSR, all LBR entries are reset to 0 */
|
||||||
wrmsrl(MSR_ARCH_LBR_DEPTH, x86_pmu.lbr_nr);
|
wrmsrq(MSR_ARCH_LBR_DEPTH, x86_pmu.lbr_nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_pmu_lbr_reset(void)
|
void intel_pmu_lbr_reset(void)
|
||||||
|
|
@ -199,7 +199,7 @@ void intel_pmu_lbr_reset(void)
|
||||||
cpuc->last_task_ctx = NULL;
|
cpuc->last_task_ctx = NULL;
|
||||||
cpuc->last_log_id = 0;
|
cpuc->last_log_id = 0;
|
||||||
if (!static_cpu_has(X86_FEATURE_ARCH_LBR) && cpuc->lbr_select)
|
if (!static_cpu_has(X86_FEATURE_ARCH_LBR) && cpuc->lbr_select)
|
||||||
wrmsrl(MSR_LBR_SELECT, 0);
|
wrmsrq(MSR_LBR_SELECT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -209,7 +209,7 @@ static inline u64 intel_pmu_lbr_tos(void)
|
||||||
{
|
{
|
||||||
u64 tos;
|
u64 tos;
|
||||||
|
|
||||||
rdmsrl(x86_pmu.lbr_tos, tos);
|
rdmsrq(x86_pmu.lbr_tos, tos);
|
||||||
return tos;
|
return tos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,17 +282,17 @@ static u64 lbr_from_signext_quirk_rd(u64 val)
|
||||||
static __always_inline void wrlbr_from(unsigned int idx, u64 val)
|
static __always_inline void wrlbr_from(unsigned int idx, u64 val)
|
||||||
{
|
{
|
||||||
val = lbr_from_signext_quirk_wr(val);
|
val = lbr_from_signext_quirk_wr(val);
|
||||||
wrmsrl(x86_pmu.lbr_from + idx, val);
|
wrmsrq(x86_pmu.lbr_from + idx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void wrlbr_to(unsigned int idx, u64 val)
|
static __always_inline void wrlbr_to(unsigned int idx, u64 val)
|
||||||
{
|
{
|
||||||
wrmsrl(x86_pmu.lbr_to + idx, val);
|
wrmsrq(x86_pmu.lbr_to + idx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void wrlbr_info(unsigned int idx, u64 val)
|
static __always_inline void wrlbr_info(unsigned int idx, u64 val)
|
||||||
{
|
{
|
||||||
wrmsrl(x86_pmu.lbr_info + idx, val);
|
wrmsrq(x86_pmu.lbr_info + idx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline u64 rdlbr_from(unsigned int idx, struct lbr_entry *lbr)
|
static __always_inline u64 rdlbr_from(unsigned int idx, struct lbr_entry *lbr)
|
||||||
|
|
@ -302,7 +302,7 @@ static __always_inline u64 rdlbr_from(unsigned int idx, struct lbr_entry *lbr)
|
||||||
if (lbr)
|
if (lbr)
|
||||||
return lbr->from;
|
return lbr->from;
|
||||||
|
|
||||||
rdmsrl(x86_pmu.lbr_from + idx, val);
|
rdmsrq(x86_pmu.lbr_from + idx, val);
|
||||||
|
|
||||||
return lbr_from_signext_quirk_rd(val);
|
return lbr_from_signext_quirk_rd(val);
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +314,7 @@ static __always_inline u64 rdlbr_to(unsigned int idx, struct lbr_entry *lbr)
|
||||||
if (lbr)
|
if (lbr)
|
||||||
return lbr->to;
|
return lbr->to;
|
||||||
|
|
||||||
rdmsrl(x86_pmu.lbr_to + idx, val);
|
rdmsrq(x86_pmu.lbr_to + idx, val);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +326,7 @@ static __always_inline u64 rdlbr_info(unsigned int idx, struct lbr_entry *lbr)
|
||||||
if (lbr)
|
if (lbr)
|
||||||
return lbr->info;
|
return lbr->info;
|
||||||
|
|
||||||
rdmsrl(x86_pmu.lbr_info + idx, val);
|
rdmsrq(x86_pmu.lbr_info + idx, val);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
@ -380,10 +380,10 @@ void intel_pmu_lbr_restore(void *ctx)
|
||||||
wrlbr_info(lbr_idx, 0);
|
wrlbr_info(lbr_idx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(x86_pmu.lbr_tos, tos);
|
wrmsrq(x86_pmu.lbr_tos, tos);
|
||||||
|
|
||||||
if (cpuc->lbr_select)
|
if (cpuc->lbr_select)
|
||||||
wrmsrl(MSR_LBR_SELECT, task_ctx->lbr_sel);
|
wrmsrq(MSR_LBR_SELECT, task_ctx->lbr_sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_pmu_arch_lbr_restore(void *ctx)
|
static void intel_pmu_arch_lbr_restore(void *ctx)
|
||||||
|
|
@ -475,7 +475,7 @@ void intel_pmu_lbr_save(void *ctx)
|
||||||
task_ctx->tos = tos;
|
task_ctx->tos = tos;
|
||||||
|
|
||||||
if (cpuc->lbr_select)
|
if (cpuc->lbr_select)
|
||||||
rdmsrl(MSR_LBR_SELECT, task_ctx->lbr_sel);
|
rdmsrq(MSR_LBR_SELECT, task_ctx->lbr_sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_pmu_arch_lbr_save(void *ctx)
|
static void intel_pmu_arch_lbr_save(void *ctx)
|
||||||
|
|
@ -752,7 +752,7 @@ void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
|
||||||
u64 lbr;
|
u64 lbr;
|
||||||
} msr_lastbranch;
|
} msr_lastbranch;
|
||||||
|
|
||||||
rdmsrl(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr);
|
rdmsrq(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr);
|
||||||
|
|
||||||
perf_clear_branch_entry_bitfields(br);
|
perf_clear_branch_entry_bitfields(br);
|
||||||
|
|
||||||
|
|
@ -1602,7 +1602,7 @@ void __init intel_pmu_arch_lbr_init(void)
|
||||||
goto clear_arch_lbr;
|
goto clear_arch_lbr;
|
||||||
|
|
||||||
/* Apply the max depth of Arch LBR */
|
/* Apply the max depth of Arch LBR */
|
||||||
if (wrmsrl_safe(MSR_ARCH_LBR_DEPTH, lbr_nr))
|
if (wrmsrq_safe(MSR_ARCH_LBR_DEPTH, lbr_nr))
|
||||||
goto clear_arch_lbr;
|
goto clear_arch_lbr;
|
||||||
|
|
||||||
x86_pmu.lbr_depth_mask = eax.split.lbr_depth_mask;
|
x86_pmu.lbr_depth_mask = eax.split.lbr_depth_mask;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
#include <asm/hardirq.h>
|
#include <asm/hardirq.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
@ -859,9 +860,9 @@ static inline int p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc)
|
||||||
u64 v;
|
u64 v;
|
||||||
|
|
||||||
/* an official way for overflow indication */
|
/* an official way for overflow indication */
|
||||||
rdmsrl(hwc->config_base, v);
|
rdmsrq(hwc->config_base, v);
|
||||||
if (v & P4_CCCR_OVF) {
|
if (v & P4_CCCR_OVF) {
|
||||||
wrmsrl(hwc->config_base, v & ~P4_CCCR_OVF);
|
wrmsrq(hwc->config_base, v & ~P4_CCCR_OVF);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -872,7 +873,7 @@ static inline int p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc)
|
||||||
* the counter has reached zero value and continued counting before
|
* the counter has reached zero value and continued counting before
|
||||||
* real NMI signal was received:
|
* real NMI signal was received:
|
||||||
*/
|
*/
|
||||||
rdmsrl(hwc->event_base, v);
|
rdmsrq(hwc->event_base, v);
|
||||||
if (!(v & ARCH_P4_UNFLAGGED_BIT))
|
if (!(v & ARCH_P4_UNFLAGGED_BIT))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
@ -897,8 +898,8 @@ static void p4_pmu_disable_pebs(void)
|
||||||
* So at moment let leave metrics turned on forever -- it's
|
* So at moment let leave metrics turned on forever -- it's
|
||||||
* ok for now but need to be revisited!
|
* ok for now but need to be revisited!
|
||||||
*
|
*
|
||||||
* (void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, 0);
|
* (void)wrmsrq_safe(MSR_IA32_PEBS_ENABLE, 0);
|
||||||
* (void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, 0);
|
* (void)wrmsrq_safe(MSR_P4_PEBS_MATRIX_VERT, 0);
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -911,7 +912,7 @@ static inline void p4_pmu_disable_event(struct perf_event *event)
|
||||||
* state we need to clear P4_CCCR_OVF, otherwise interrupt get
|
* state we need to clear P4_CCCR_OVF, otherwise interrupt get
|
||||||
* asserted again and again
|
* asserted again and again
|
||||||
*/
|
*/
|
||||||
(void)wrmsrl_safe(hwc->config_base,
|
(void)wrmsrq_safe(hwc->config_base,
|
||||||
p4_config_unpack_cccr(hwc->config) & ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED);
|
p4_config_unpack_cccr(hwc->config) & ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -944,8 +945,8 @@ static void p4_pmu_enable_pebs(u64 config)
|
||||||
|
|
||||||
bind = &p4_pebs_bind_map[idx];
|
bind = &p4_pebs_bind_map[idx];
|
||||||
|
|
||||||
(void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, (u64)bind->metric_pebs);
|
(void)wrmsrq_safe(MSR_IA32_PEBS_ENABLE, (u64)bind->metric_pebs);
|
||||||
(void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, (u64)bind->metric_vert);
|
(void)wrmsrq_safe(MSR_P4_PEBS_MATRIX_VERT, (u64)bind->metric_vert);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __p4_pmu_enable_event(struct perf_event *event)
|
static void __p4_pmu_enable_event(struct perf_event *event)
|
||||||
|
|
@ -979,8 +980,8 @@ static void __p4_pmu_enable_event(struct perf_event *event)
|
||||||
*/
|
*/
|
||||||
p4_pmu_enable_pebs(hwc->config);
|
p4_pmu_enable_pebs(hwc->config);
|
||||||
|
|
||||||
(void)wrmsrl_safe(escr_addr, escr_conf);
|
(void)wrmsrq_safe(escr_addr, escr_conf);
|
||||||
(void)wrmsrl_safe(hwc->config_base,
|
(void)wrmsrq_safe(hwc->config_base,
|
||||||
(cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE);
|
(cccr & ~P4_CCCR_RESERVED) | P4_CCCR_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1024,7 +1025,7 @@ static int p4_pmu_set_period(struct perf_event *event)
|
||||||
*
|
*
|
||||||
* the former idea is taken from OProfile code
|
* the former idea is taken from OProfile code
|
||||||
*/
|
*/
|
||||||
wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
wrmsrq(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -1397,7 +1398,7 @@ __init int p4_pmu_init(void)
|
||||||
*/
|
*/
|
||||||
for_each_set_bit(i, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) {
|
for_each_set_bit(i, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) {
|
||||||
reg = x86_pmu_config_addr(i);
|
reg = x86_pmu_config_addr(i);
|
||||||
wrmsrl_safe(reg, 0ULL);
|
wrmsrq_safe(reg, 0ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
@ -142,9 +143,9 @@ static void p6_pmu_disable_all(void)
|
||||||
u64 val;
|
u64 val;
|
||||||
|
|
||||||
/* p6 only has one enable register */
|
/* p6 only has one enable register */
|
||||||
rdmsrl(MSR_P6_EVNTSEL0, val);
|
rdmsrq(MSR_P6_EVNTSEL0, val);
|
||||||
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
||||||
wrmsrl(MSR_P6_EVNTSEL0, val);
|
wrmsrq(MSR_P6_EVNTSEL0, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void p6_pmu_enable_all(int added)
|
static void p6_pmu_enable_all(int added)
|
||||||
|
|
@ -152,9 +153,9 @@ static void p6_pmu_enable_all(int added)
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
|
|
||||||
/* p6 only has one enable register */
|
/* p6 only has one enable register */
|
||||||
rdmsrl(MSR_P6_EVNTSEL0, val);
|
rdmsrq(MSR_P6_EVNTSEL0, val);
|
||||||
val |= ARCH_PERFMON_EVENTSEL_ENABLE;
|
val |= ARCH_PERFMON_EVENTSEL_ENABLE;
|
||||||
wrmsrl(MSR_P6_EVNTSEL0, val);
|
wrmsrq(MSR_P6_EVNTSEL0, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
|
@ -163,7 +164,7 @@ p6_pmu_disable_event(struct perf_event *event)
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
u64 val = P6_NOP_EVENT;
|
u64 val = P6_NOP_EVENT;
|
||||||
|
|
||||||
(void)wrmsrl_safe(hwc->config_base, val);
|
(void)wrmsrq_safe(hwc->config_base, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void p6_pmu_enable_event(struct perf_event *event)
|
static void p6_pmu_enable_event(struct perf_event *event)
|
||||||
|
|
@ -180,7 +181,7 @@ static void p6_pmu_enable_event(struct perf_event *event)
|
||||||
* to actually enable the events.
|
* to actually enable the events.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(void)wrmsrl_safe(hwc->config_base, val);
|
(void)wrmsrq_safe(hwc->config_base, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
PMU_FORMAT_ATTR(event, "config:0-7" );
|
PMU_FORMAT_ATTR(event, "config:0-7" );
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,13 @@
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
|
||||||
#include <asm/cpuid.h>
|
#include <asm/cpuid/api.h>
|
||||||
#include <asm/perf_event.h>
|
#include <asm/perf_event.h>
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/intel_pt.h>
|
#include <asm/intel_pt.h>
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
#include "pt.h"
|
#include "pt.h"
|
||||||
|
|
@ -194,7 +195,7 @@ static int __init pt_pmu_hw_init(void)
|
||||||
int ret;
|
int ret;
|
||||||
long i;
|
long i;
|
||||||
|
|
||||||
rdmsrl(MSR_PLATFORM_INFO, reg);
|
rdmsrq(MSR_PLATFORM_INFO, reg);
|
||||||
pt_pmu.max_nonturbo_ratio = (reg & 0xff00) >> 8;
|
pt_pmu.max_nonturbo_ratio = (reg & 0xff00) >> 8;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -230,7 +231,7 @@ static int __init pt_pmu_hw_init(void)
|
||||||
* "IA32_VMX_MISC[bit 14]" being 1 means PT can trace
|
* "IA32_VMX_MISC[bit 14]" being 1 means PT can trace
|
||||||
* post-VMXON.
|
* post-VMXON.
|
||||||
*/
|
*/
|
||||||
rdmsrl(MSR_IA32_VMX_MISC, reg);
|
rdmsrq(MSR_IA32_VMX_MISC, reg);
|
||||||
if (reg & BIT(14))
|
if (reg & BIT(14))
|
||||||
pt_pmu.vmx = true;
|
pt_pmu.vmx = true;
|
||||||
}
|
}
|
||||||
|
|
@ -426,7 +427,7 @@ static void pt_config_start(struct perf_event *event)
|
||||||
if (READ_ONCE(pt->vmx_on))
|
if (READ_ONCE(pt->vmx_on))
|
||||||
perf_aux_output_flag(&pt->handle, PERF_AUX_FLAG_PARTIAL);
|
perf_aux_output_flag(&pt->handle, PERF_AUX_FLAG_PARTIAL);
|
||||||
else
|
else
|
||||||
wrmsrl(MSR_IA32_RTIT_CTL, ctl);
|
wrmsrq(MSR_IA32_RTIT_CTL, ctl);
|
||||||
|
|
||||||
WRITE_ONCE(event->hw.aux_config, ctl);
|
WRITE_ONCE(event->hw.aux_config, ctl);
|
||||||
}
|
}
|
||||||
|
|
@ -485,12 +486,12 @@ static u64 pt_config_filters(struct perf_event *event)
|
||||||
|
|
||||||
/* avoid redundant msr writes */
|
/* avoid redundant msr writes */
|
||||||
if (pt->filters.filter[range].msr_a != filter->msr_a) {
|
if (pt->filters.filter[range].msr_a != filter->msr_a) {
|
||||||
wrmsrl(pt_address_ranges[range].msr_a, filter->msr_a);
|
wrmsrq(pt_address_ranges[range].msr_a, filter->msr_a);
|
||||||
pt->filters.filter[range].msr_a = filter->msr_a;
|
pt->filters.filter[range].msr_a = filter->msr_a;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pt->filters.filter[range].msr_b != filter->msr_b) {
|
if (pt->filters.filter[range].msr_b != filter->msr_b) {
|
||||||
wrmsrl(pt_address_ranges[range].msr_b, filter->msr_b);
|
wrmsrq(pt_address_ranges[range].msr_b, filter->msr_b);
|
||||||
pt->filters.filter[range].msr_b = filter->msr_b;
|
pt->filters.filter[range].msr_b = filter->msr_b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,7 +510,7 @@ static void pt_config(struct perf_event *event)
|
||||||
/* First round: clear STATUS, in particular the PSB byte counter. */
|
/* First round: clear STATUS, in particular the PSB byte counter. */
|
||||||
if (!event->hw.aux_config) {
|
if (!event->hw.aux_config) {
|
||||||
perf_event_itrace_started(event);
|
perf_event_itrace_started(event);
|
||||||
wrmsrl(MSR_IA32_RTIT_STATUS, 0);
|
wrmsrq(MSR_IA32_RTIT_STATUS, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = pt_config_filters(event);
|
reg = pt_config_filters(event);
|
||||||
|
|
@ -569,7 +570,7 @@ static void pt_config_stop(struct perf_event *event)
|
||||||
|
|
||||||
ctl &= ~RTIT_CTL_TRACEEN;
|
ctl &= ~RTIT_CTL_TRACEEN;
|
||||||
if (!READ_ONCE(pt->vmx_on))
|
if (!READ_ONCE(pt->vmx_on))
|
||||||
wrmsrl(MSR_IA32_RTIT_CTL, ctl);
|
wrmsrq(MSR_IA32_RTIT_CTL, ctl);
|
||||||
|
|
||||||
WRITE_ONCE(event->hw.aux_config, ctl);
|
WRITE_ONCE(event->hw.aux_config, ctl);
|
||||||
|
|
||||||
|
|
@ -658,13 +659,13 @@ static void pt_config_buffer(struct pt_buffer *buf)
|
||||||
reg = virt_to_phys(base);
|
reg = virt_to_phys(base);
|
||||||
if (pt->output_base != reg) {
|
if (pt->output_base != reg) {
|
||||||
pt->output_base = reg;
|
pt->output_base = reg;
|
||||||
wrmsrl(MSR_IA32_RTIT_OUTPUT_BASE, reg);
|
wrmsrq(MSR_IA32_RTIT_OUTPUT_BASE, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
reg = 0x7f | (mask << 7) | ((u64)buf->output_off << 32);
|
reg = 0x7f | (mask << 7) | ((u64)buf->output_off << 32);
|
||||||
if (pt->output_mask != reg) {
|
if (pt->output_mask != reg) {
|
||||||
pt->output_mask = reg;
|
pt->output_mask = reg;
|
||||||
wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, reg);
|
wrmsrq(MSR_IA32_RTIT_OUTPUT_MASK, reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -926,7 +927,7 @@ static void pt_handle_status(struct pt *pt)
|
||||||
int advance = 0;
|
int advance = 0;
|
||||||
u64 status;
|
u64 status;
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_RTIT_STATUS, status);
|
rdmsrq(MSR_IA32_RTIT_STATUS, status);
|
||||||
|
|
||||||
if (status & RTIT_STATUS_ERROR) {
|
if (status & RTIT_STATUS_ERROR) {
|
||||||
pr_err_ratelimited("ToPA ERROR encountered, trying to recover\n");
|
pr_err_ratelimited("ToPA ERROR encountered, trying to recover\n");
|
||||||
|
|
@ -970,7 +971,7 @@ static void pt_handle_status(struct pt *pt)
|
||||||
if (advance)
|
if (advance)
|
||||||
pt_buffer_advance(buf);
|
pt_buffer_advance(buf);
|
||||||
|
|
||||||
wrmsrl(MSR_IA32_RTIT_STATUS, status);
|
wrmsrq(MSR_IA32_RTIT_STATUS, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -985,12 +986,12 @@ static void pt_read_offset(struct pt_buffer *buf)
|
||||||
struct topa_page *tp;
|
struct topa_page *tp;
|
||||||
|
|
||||||
if (!buf->single) {
|
if (!buf->single) {
|
||||||
rdmsrl(MSR_IA32_RTIT_OUTPUT_BASE, pt->output_base);
|
rdmsrq(MSR_IA32_RTIT_OUTPUT_BASE, pt->output_base);
|
||||||
tp = phys_to_virt(pt->output_base);
|
tp = phys_to_virt(pt->output_base);
|
||||||
buf->cur = &tp->topa;
|
buf->cur = &tp->topa;
|
||||||
}
|
}
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_RTIT_OUTPUT_MASK, pt->output_mask);
|
rdmsrq(MSR_IA32_RTIT_OUTPUT_MASK, pt->output_mask);
|
||||||
/* offset within current output region */
|
/* offset within current output region */
|
||||||
buf->output_off = pt->output_mask >> 32;
|
buf->output_off = pt->output_mask >> 32;
|
||||||
/* index of current output region within this table */
|
/* index of current output region within this table */
|
||||||
|
|
@ -1585,7 +1586,7 @@ void intel_pt_handle_vmx(int on)
|
||||||
|
|
||||||
/* Turn PTs back on */
|
/* Turn PTs back on */
|
||||||
if (!on && event)
|
if (!on && event)
|
||||||
wrmsrl(MSR_IA32_RTIT_CTL, event->hw.aux_config);
|
wrmsrq(MSR_IA32_RTIT_CTL, event->hw.aux_config);
|
||||||
|
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
@ -1611,7 +1612,7 @@ static void pt_event_start(struct perf_event *event, int mode)
|
||||||
* PMI might have just cleared these, so resume_allowed
|
* PMI might have just cleared these, so resume_allowed
|
||||||
* must be checked again also.
|
* must be checked again also.
|
||||||
*/
|
*/
|
||||||
rdmsrl(MSR_IA32_RTIT_STATUS, status);
|
rdmsrq(MSR_IA32_RTIT_STATUS, status);
|
||||||
if (!(status & (RTIT_STATUS_TRIGGEREN |
|
if (!(status & (RTIT_STATUS_TRIGGEREN |
|
||||||
RTIT_STATUS_ERROR |
|
RTIT_STATUS_ERROR |
|
||||||
RTIT_STATUS_STOPPED)) &&
|
RTIT_STATUS_STOPPED)) &&
|
||||||
|
|
@ -1839,7 +1840,7 @@ static __init int pt_init(void)
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
u64 ctl;
|
u64 ctl;
|
||||||
|
|
||||||
ret = rdmsrl_safe_on_cpu(cpu, MSR_IA32_RTIT_CTL, &ctl);
|
ret = rdmsrq_safe_on_cpu(cpu, MSR_IA32_RTIT_CTL, &ctl);
|
||||||
if (!ret && (ctl & RTIT_CTL_TRACEEN))
|
if (!ret && (ctl & RTIT_CTL_TRACEEN))
|
||||||
prior_warn++;
|
prior_warn++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
#include <asm/intel-family.h>
|
#include <asm/intel-family.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "uncore.h"
|
#include "uncore.h"
|
||||||
#include "uncore_discovery.h"
|
#include "uncore_discovery.h"
|
||||||
|
|
||||||
|
|
@ -150,7 +151,7 @@ u64 uncore_msr_read_counter(struct intel_uncore_box *box, struct perf_event *eve
|
||||||
{
|
{
|
||||||
u64 count;
|
u64 count;
|
||||||
|
|
||||||
rdmsrl(event->hw.event_base, count);
|
rdmsrq(event->hw.event_base, count);
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "uncore.h"
|
#include "uncore.h"
|
||||||
#include "uncore_discovery.h"
|
#include "uncore_discovery.h"
|
||||||
|
|
||||||
|
|
@ -441,17 +442,17 @@ static u64 intel_generic_uncore_box_ctl(struct intel_uncore_box *box)
|
||||||
|
|
||||||
void intel_generic_uncore_msr_init_box(struct intel_uncore_box *box)
|
void intel_generic_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(intel_generic_uncore_box_ctl(box), GENERIC_PMON_BOX_CTL_INT);
|
wrmsrq(intel_generic_uncore_box_ctl(box), GENERIC_PMON_BOX_CTL_INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_generic_uncore_msr_disable_box(struct intel_uncore_box *box)
|
void intel_generic_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(intel_generic_uncore_box_ctl(box), GENERIC_PMON_BOX_CTL_FRZ);
|
wrmsrq(intel_generic_uncore_box_ctl(box), GENERIC_PMON_BOX_CTL_FRZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_generic_uncore_msr_enable_box(struct intel_uncore_box *box)
|
void intel_generic_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(intel_generic_uncore_box_ctl(box), 0);
|
wrmsrq(intel_generic_uncore_box_ctl(box), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_generic_uncore_msr_enable_event(struct intel_uncore_box *box,
|
static void intel_generic_uncore_msr_enable_event(struct intel_uncore_box *box,
|
||||||
|
|
@ -459,7 +460,7 @@ static void intel_generic_uncore_msr_enable_event(struct intel_uncore_box *box,
|
||||||
{
|
{
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config);
|
wrmsrq(hwc->config_base, hwc->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_generic_uncore_msr_disable_event(struct intel_uncore_box *box,
|
static void intel_generic_uncore_msr_disable_event(struct intel_uncore_box *box,
|
||||||
|
|
@ -467,7 +468,7 @@ static void intel_generic_uncore_msr_disable_event(struct intel_uncore_box *box,
|
||||||
{
|
{
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, 0);
|
wrmsrq(hwc->config_base, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops generic_uncore_msr_ops = {
|
static struct intel_uncore_ops generic_uncore_msr_ops = {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* Nehalem-EX/Westmere-EX uncore support */
|
/* Nehalem-EX/Westmere-EX uncore support */
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "uncore.h"
|
#include "uncore.h"
|
||||||
|
|
||||||
/* NHM-EX event control */
|
/* NHM-EX event control */
|
||||||
|
|
@ -200,12 +201,12 @@ DEFINE_UNCORE_FORMAT_ATTR(mask, mask, "config2:0-63");
|
||||||
|
|
||||||
static void nhmex_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void nhmex_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(NHMEX_U_MSR_PMON_GLOBAL_CTL, NHMEX_U_PMON_GLOBAL_EN_ALL);
|
wrmsrq(NHMEX_U_MSR_PMON_GLOBAL_CTL, NHMEX_U_PMON_GLOBAL_EN_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nhmex_uncore_msr_exit_box(struct intel_uncore_box *box)
|
static void nhmex_uncore_msr_exit_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(NHMEX_U_MSR_PMON_GLOBAL_CTL, 0);
|
wrmsrq(NHMEX_U_MSR_PMON_GLOBAL_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nhmex_uncore_msr_disable_box(struct intel_uncore_box *box)
|
static void nhmex_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||||
|
|
@ -214,12 +215,12 @@ static void nhmex_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||||
u64 config;
|
u64 config;
|
||||||
|
|
||||||
if (msr) {
|
if (msr) {
|
||||||
rdmsrl(msr, config);
|
rdmsrq(msr, config);
|
||||||
config &= ~((1ULL << uncore_num_counters(box)) - 1);
|
config &= ~((1ULL << uncore_num_counters(box)) - 1);
|
||||||
/* WBox has a fixed counter */
|
/* WBox has a fixed counter */
|
||||||
if (uncore_msr_fixed_ctl(box))
|
if (uncore_msr_fixed_ctl(box))
|
||||||
config &= ~NHMEX_W_PMON_GLOBAL_FIXED_EN;
|
config &= ~NHMEX_W_PMON_GLOBAL_FIXED_EN;
|
||||||
wrmsrl(msr, config);
|
wrmsrq(msr, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,18 +230,18 @@ static void nhmex_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
u64 config;
|
u64 config;
|
||||||
|
|
||||||
if (msr) {
|
if (msr) {
|
||||||
rdmsrl(msr, config);
|
rdmsrq(msr, config);
|
||||||
config |= (1ULL << uncore_num_counters(box)) - 1;
|
config |= (1ULL << uncore_num_counters(box)) - 1;
|
||||||
/* WBox has a fixed counter */
|
/* WBox has a fixed counter */
|
||||||
if (uncore_msr_fixed_ctl(box))
|
if (uncore_msr_fixed_ctl(box))
|
||||||
config |= NHMEX_W_PMON_GLOBAL_FIXED_EN;
|
config |= NHMEX_W_PMON_GLOBAL_FIXED_EN;
|
||||||
wrmsrl(msr, config);
|
wrmsrq(msr, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nhmex_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
|
static void nhmex_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||||
{
|
{
|
||||||
wrmsrl(event->hw.config_base, 0);
|
wrmsrq(event->hw.config_base, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nhmex_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
static void nhmex_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||||
|
|
@ -248,11 +249,11 @@ static void nhmex_uncore_msr_enable_event(struct intel_uncore_box *box, struct p
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
if (hwc->idx == UNCORE_PMC_IDX_FIXED)
|
if (hwc->idx == UNCORE_PMC_IDX_FIXED)
|
||||||
wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0);
|
wrmsrq(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0);
|
||||||
else if (box->pmu->type->event_mask & NHMEX_PMON_CTL_EN_BIT0)
|
else if (box->pmu->type->event_mask & NHMEX_PMON_CTL_EN_BIT0)
|
||||||
wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22);
|
wrmsrq(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22);
|
||||||
else
|
else
|
||||||
wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
|
wrmsrq(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NHMEX_UNCORE_OPS_COMMON_INIT() \
|
#define NHMEX_UNCORE_OPS_COMMON_INIT() \
|
||||||
|
|
@ -382,10 +383,10 @@ static void nhmex_bbox_msr_enable_event(struct intel_uncore_box *box, struct per
|
||||||
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
|
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE) {
|
if (reg1->idx != EXTRA_REG_NONE) {
|
||||||
wrmsrl(reg1->reg, reg1->config);
|
wrmsrq(reg1->reg, reg1->config);
|
||||||
wrmsrl(reg1->reg + 1, reg2->config);
|
wrmsrq(reg1->reg + 1, reg2->config);
|
||||||
}
|
}
|
||||||
wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 |
|
wrmsrq(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 |
|
||||||
(hwc->config & NHMEX_B_PMON_CTL_EV_SEL_MASK));
|
(hwc->config & NHMEX_B_PMON_CTL_EV_SEL_MASK));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,12 +468,12 @@ static void nhmex_sbox_msr_enable_event(struct intel_uncore_box *box, struct per
|
||||||
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
|
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE) {
|
if (reg1->idx != EXTRA_REG_NONE) {
|
||||||
wrmsrl(reg1->reg, 0);
|
wrmsrq(reg1->reg, 0);
|
||||||
wrmsrl(reg1->reg + 1, reg1->config);
|
wrmsrq(reg1->reg + 1, reg1->config);
|
||||||
wrmsrl(reg1->reg + 2, reg2->config);
|
wrmsrq(reg1->reg + 2, reg2->config);
|
||||||
wrmsrl(reg1->reg, NHMEX_S_PMON_MM_CFG_EN);
|
wrmsrq(reg1->reg, NHMEX_S_PMON_MM_CFG_EN);
|
||||||
}
|
}
|
||||||
wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22);
|
wrmsrq(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT22);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute *nhmex_uncore_sbox_formats_attr[] = {
|
static struct attribute *nhmex_uncore_sbox_formats_attr[] = {
|
||||||
|
|
@ -842,25 +843,25 @@ static void nhmex_mbox_msr_enable_event(struct intel_uncore_box *box, struct per
|
||||||
|
|
||||||
idx = __BITS_VALUE(reg1->idx, 0, 8);
|
idx = __BITS_VALUE(reg1->idx, 0, 8);
|
||||||
if (idx != 0xff)
|
if (idx != 0xff)
|
||||||
wrmsrl(__BITS_VALUE(reg1->reg, 0, 16),
|
wrmsrq(__BITS_VALUE(reg1->reg, 0, 16),
|
||||||
nhmex_mbox_shared_reg_config(box, idx));
|
nhmex_mbox_shared_reg_config(box, idx));
|
||||||
idx = __BITS_VALUE(reg1->idx, 1, 8);
|
idx = __BITS_VALUE(reg1->idx, 1, 8);
|
||||||
if (idx != 0xff)
|
if (idx != 0xff)
|
||||||
wrmsrl(__BITS_VALUE(reg1->reg, 1, 16),
|
wrmsrq(__BITS_VALUE(reg1->reg, 1, 16),
|
||||||
nhmex_mbox_shared_reg_config(box, idx));
|
nhmex_mbox_shared_reg_config(box, idx));
|
||||||
|
|
||||||
if (reg2->idx != EXTRA_REG_NONE) {
|
if (reg2->idx != EXTRA_REG_NONE) {
|
||||||
wrmsrl(reg2->reg, 0);
|
wrmsrq(reg2->reg, 0);
|
||||||
if (reg2->config != ~0ULL) {
|
if (reg2->config != ~0ULL) {
|
||||||
wrmsrl(reg2->reg + 1,
|
wrmsrq(reg2->reg + 1,
|
||||||
reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
|
reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
|
||||||
wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
|
wrmsrq(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
|
||||||
(reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
|
(reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
|
||||||
wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
|
wrmsrq(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
|
wrmsrq(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3");
|
DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3");
|
||||||
|
|
@ -1121,31 +1122,31 @@ static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct per
|
||||||
|
|
||||||
switch (idx % 6) {
|
switch (idx % 6) {
|
||||||
case 0:
|
case 0:
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_IPERF_CFG0(port), reg1->config);
|
wrmsrq(NHMEX_R_MSR_PORTN_IPERF_CFG0(port), reg1->config);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_IPERF_CFG1(port), reg1->config);
|
wrmsrq(NHMEX_R_MSR_PORTN_IPERF_CFG1(port), reg1->config);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_QLX_CFG(port),
|
wrmsrq(NHMEX_R_MSR_PORTN_QLX_CFG(port),
|
||||||
uncore_shared_reg_config(box, 2 + (idx / 6) * 5));
|
uncore_shared_reg_config(box, 2 + (idx / 6) * 5));
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port),
|
wrmsrq(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port),
|
||||||
hwc->config >> 32);
|
hwc->config >> 32);
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(port), reg1->config);
|
wrmsrq(NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(port), reg1->config);
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MASK(port), reg2->config);
|
wrmsrq(NHMEX_R_MSR_PORTN_XBR_SET1_MASK(port), reg2->config);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port),
|
wrmsrq(NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port),
|
||||||
hwc->config >> 32);
|
hwc->config >> 32);
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(port), reg1->config);
|
wrmsrq(NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(port), reg1->config);
|
||||||
wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MASK(port), reg2->config);
|
wrmsrq(NHMEX_R_MSR_PORTN_XBR_SET2_MASK(port), reg2->config);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 |
|
wrmsrq(hwc->config_base, NHMEX_PMON_CTL_EN_BIT0 |
|
||||||
(hwc->config & NHMEX_R_PMON_CTL_EV_SEL_MASK));
|
(hwc->config & NHMEX_R_PMON_CTL_EV_SEL_MASK));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* Nehalem/SandBridge/Haswell/Broadwell/Skylake uncore support */
|
/* Nehalem/SandBridge/Haswell/Broadwell/Skylake uncore support */
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "uncore.h"
|
#include "uncore.h"
|
||||||
#include "uncore_discovery.h"
|
#include "uncore_discovery.h"
|
||||||
|
|
||||||
|
|
@ -260,34 +261,34 @@ static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct per
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
if (hwc->idx < UNCORE_PMC_IDX_FIXED)
|
if (hwc->idx < UNCORE_PMC_IDX_FIXED)
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
|
||||||
else
|
else
|
||||||
wrmsrl(hwc->config_base, SNB_UNC_CTL_EN);
|
wrmsrq(hwc->config_base, SNB_UNC_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snb_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
|
static void snb_uncore_msr_disable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||||
{
|
{
|
||||||
wrmsrl(event->hw.config_base, 0);
|
wrmsrq(event->hw.config_base, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0) {
|
if (box->pmu->pmu_idx == 0) {
|
||||||
wrmsrl(SNB_UNC_PERF_GLOBAL_CTL,
|
wrmsrq(SNB_UNC_PERF_GLOBAL_CTL,
|
||||||
SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
|
SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snb_uncore_msr_enable_box(struct intel_uncore_box *box)
|
static void snb_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(SNB_UNC_PERF_GLOBAL_CTL,
|
wrmsrq(SNB_UNC_PERF_GLOBAL_CTL,
|
||||||
SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
|
SNB_UNC_GLOBAL_CTL_EN | SNB_UNC_GLOBAL_CTL_CORE_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snb_uncore_msr_exit_box(struct intel_uncore_box *box)
|
static void snb_uncore_msr_exit_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(SNB_UNC_PERF_GLOBAL_CTL, 0);
|
wrmsrq(SNB_UNC_PERF_GLOBAL_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct uncore_event_desc snb_uncore_events[] = {
|
static struct uncore_event_desc snb_uncore_events[] = {
|
||||||
|
|
@ -372,7 +373,7 @@ void snb_uncore_cpu_init(void)
|
||||||
static void skl_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void skl_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0) {
|
if (box->pmu->pmu_idx == 0) {
|
||||||
wrmsrl(SKL_UNC_PERF_GLOBAL_CTL,
|
wrmsrq(SKL_UNC_PERF_GLOBAL_CTL,
|
||||||
SNB_UNC_GLOBAL_CTL_EN | SKL_UNC_GLOBAL_CTL_CORE_ALL);
|
SNB_UNC_GLOBAL_CTL_EN | SKL_UNC_GLOBAL_CTL_CORE_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,14 +384,14 @@ static void skl_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
|
|
||||||
static void skl_uncore_msr_enable_box(struct intel_uncore_box *box)
|
static void skl_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(SKL_UNC_PERF_GLOBAL_CTL,
|
wrmsrq(SKL_UNC_PERF_GLOBAL_CTL,
|
||||||
SNB_UNC_GLOBAL_CTL_EN | SKL_UNC_GLOBAL_CTL_CORE_ALL);
|
SNB_UNC_GLOBAL_CTL_EN | SKL_UNC_GLOBAL_CTL_CORE_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void skl_uncore_msr_exit_box(struct intel_uncore_box *box)
|
static void skl_uncore_msr_exit_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(SKL_UNC_PERF_GLOBAL_CTL, 0);
|
wrmsrq(SKL_UNC_PERF_GLOBAL_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops skl_uncore_msr_ops = {
|
static struct intel_uncore_ops skl_uncore_msr_ops = {
|
||||||
|
|
@ -504,7 +505,7 @@ static int icl_get_cbox_num(void)
|
||||||
{
|
{
|
||||||
u64 num_boxes;
|
u64 num_boxes;
|
||||||
|
|
||||||
rdmsrl(ICL_UNC_CBO_CONFIG, num_boxes);
|
rdmsrq(ICL_UNC_CBO_CONFIG, num_boxes);
|
||||||
|
|
||||||
return num_boxes & ICL_UNC_NUM_CBO_MASK;
|
return num_boxes & ICL_UNC_NUM_CBO_MASK;
|
||||||
}
|
}
|
||||||
|
|
@ -525,7 +526,7 @@ static struct intel_uncore_type *tgl_msr_uncores[] = {
|
||||||
static void rkl_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void rkl_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(SKL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
wrmsrq(SKL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tgl_uncore_cpu_init(void)
|
void tgl_uncore_cpu_init(void)
|
||||||
|
|
@ -541,24 +542,24 @@ void tgl_uncore_cpu_init(void)
|
||||||
static void adl_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void adl_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adl_uncore_msr_enable_box(struct intel_uncore_box *box)
|
static void adl_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adl_uncore_msr_disable_box(struct intel_uncore_box *box)
|
static void adl_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, 0);
|
wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adl_uncore_msr_exit_box(struct intel_uncore_box *box)
|
static void adl_uncore_msr_exit_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, 0);
|
wrmsrq(ADL_UNC_PERF_GLOBAL_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops adl_uncore_msr_ops = {
|
static struct intel_uncore_ops adl_uncore_msr_ops = {
|
||||||
|
|
@ -691,7 +692,7 @@ static struct intel_uncore_type mtl_uncore_hac_cbox = {
|
||||||
|
|
||||||
static void mtl_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void mtl_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(uncore_msr_box_ctl(box), SNB_UNC_GLOBAL_CTL_EN);
|
wrmsrq(uncore_msr_box_ctl(box), SNB_UNC_GLOBAL_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops mtl_uncore_msr_ops = {
|
static struct intel_uncore_ops mtl_uncore_msr_ops = {
|
||||||
|
|
@ -758,7 +759,7 @@ static struct intel_uncore_type *lnl_msr_uncores[] = {
|
||||||
static void lnl_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void lnl_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
if (box->pmu->pmu_idx == 0)
|
if (box->pmu->pmu_idx == 0)
|
||||||
wrmsrl(LNL_UNC_MSR_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
wrmsrq(LNL_UNC_MSR_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops lnl_uncore_msr_ops = {
|
static struct intel_uncore_ops lnl_uncore_msr_ops = {
|
||||||
|
|
@ -1306,12 +1307,12 @@ int skl_uncore_pci_init(void)
|
||||||
/* Nehalem uncore support */
|
/* Nehalem uncore support */
|
||||||
static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box)
|
static void nhm_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, 0);
|
wrmsrq(NHM_UNC_PERF_GLOBAL_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box)
|
static void nhm_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
wrmsrl(NHM_UNC_PERF_GLOBAL_CTL, NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC);
|
wrmsrq(NHM_UNC_PERF_GLOBAL_CTL, NHM_UNC_GLOBAL_CTL_EN_PC_ALL | NHM_UNC_GLOBAL_CTL_EN_FC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event)
|
||||||
|
|
@ -1319,9 +1320,9 @@ static void nhm_uncore_msr_enable_event(struct intel_uncore_box *box, struct per
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
if (hwc->idx < UNCORE_PMC_IDX_FIXED)
|
if (hwc->idx < UNCORE_PMC_IDX_FIXED)
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNB_UNC_CTL_EN);
|
||||||
else
|
else
|
||||||
wrmsrl(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN);
|
wrmsrq(hwc->config_base, NHM_UNC_FIXED_CTR_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute *nhm_uncore_formats_attr[] = {
|
static struct attribute *nhm_uncore_formats_attr[] = {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/* SandyBridge-EP/IvyTown uncore support */
|
/* SandyBridge-EP/IvyTown uncore support */
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "uncore.h"
|
#include "uncore.h"
|
||||||
#include "uncore_discovery.h"
|
#include "uncore_discovery.h"
|
||||||
|
|
||||||
|
|
@ -618,9 +619,9 @@ static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box)
|
||||||
|
|
||||||
msr = uncore_msr_box_ctl(box);
|
msr = uncore_msr_box_ctl(box);
|
||||||
if (msr) {
|
if (msr) {
|
||||||
rdmsrl(msr, config);
|
rdmsrq(msr, config);
|
||||||
config |= SNBEP_PMON_BOX_CTL_FRZ;
|
config |= SNBEP_PMON_BOX_CTL_FRZ;
|
||||||
wrmsrl(msr, config);
|
wrmsrq(msr, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,9 +632,9 @@ static void snbep_uncore_msr_enable_box(struct intel_uncore_box *box)
|
||||||
|
|
||||||
msr = uncore_msr_box_ctl(box);
|
msr = uncore_msr_box_ctl(box);
|
||||||
if (msr) {
|
if (msr) {
|
||||||
rdmsrl(msr, config);
|
rdmsrq(msr, config);
|
||||||
config &= ~SNBEP_PMON_BOX_CTL_FRZ;
|
config &= ~SNBEP_PMON_BOX_CTL_FRZ;
|
||||||
wrmsrl(msr, config);
|
wrmsrq(msr, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -643,9 +644,9 @@ static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box, struct p
|
||||||
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE)
|
if (reg1->idx != EXTRA_REG_NONE)
|
||||||
wrmsrl(reg1->reg, uncore_shared_reg_config(box, 0));
|
wrmsrq(reg1->reg, uncore_shared_reg_config(box, 0));
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snbep_uncore_msr_disable_event(struct intel_uncore_box *box,
|
static void snbep_uncore_msr_disable_event(struct intel_uncore_box *box,
|
||||||
|
|
@ -653,7 +654,7 @@ static void snbep_uncore_msr_disable_event(struct intel_uncore_box *box,
|
||||||
{
|
{
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config);
|
wrmsrq(hwc->config_base, hwc->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snbep_uncore_msr_init_box(struct intel_uncore_box *box)
|
static void snbep_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
|
|
@ -661,7 +662,7 @@ static void snbep_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
unsigned msr = uncore_msr_box_ctl(box);
|
unsigned msr = uncore_msr_box_ctl(box);
|
||||||
|
|
||||||
if (msr)
|
if (msr)
|
||||||
wrmsrl(msr, SNBEP_PMON_BOX_CTL_INT);
|
wrmsrq(msr, SNBEP_PMON_BOX_CTL_INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct attribute *snbep_uncore_formats_attr[] = {
|
static struct attribute *snbep_uncore_formats_attr[] = {
|
||||||
|
|
@ -1532,7 +1533,7 @@ static void ivbep_uncore_msr_init_box(struct intel_uncore_box *box)
|
||||||
{
|
{
|
||||||
unsigned msr = uncore_msr_box_ctl(box);
|
unsigned msr = uncore_msr_box_ctl(box);
|
||||||
if (msr)
|
if (msr)
|
||||||
wrmsrl(msr, IVBEP_PMON_BOX_CTL_INT);
|
wrmsrq(msr, IVBEP_PMON_BOX_CTL_INT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ivbep_uncore_pci_init_box(struct intel_uncore_box *box)
|
static void ivbep_uncore_pci_init_box(struct intel_uncore_box *box)
|
||||||
|
|
@ -1783,11 +1784,11 @@ static void ivbep_cbox_enable_event(struct intel_uncore_box *box, struct perf_ev
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE) {
|
if (reg1->idx != EXTRA_REG_NONE) {
|
||||||
u64 filter = uncore_shared_reg_config(box, 0);
|
u64 filter = uncore_shared_reg_config(box, 0);
|
||||||
wrmsrl(reg1->reg, filter & 0xffffffff);
|
wrmsrq(reg1->reg, filter & 0xffffffff);
|
||||||
wrmsrl(reg1->reg + 6, filter >> 32);
|
wrmsrq(reg1->reg + 6, filter >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops ivbep_uncore_cbox_ops = {
|
static struct intel_uncore_ops ivbep_uncore_cbox_ops = {
|
||||||
|
|
@ -2767,11 +2768,11 @@ static void hswep_cbox_enable_event(struct intel_uncore_box *box,
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE) {
|
if (reg1->idx != EXTRA_REG_NONE) {
|
||||||
u64 filter = uncore_shared_reg_config(box, 0);
|
u64 filter = uncore_shared_reg_config(box, 0);
|
||||||
wrmsrl(reg1->reg, filter & 0xffffffff);
|
wrmsrq(reg1->reg, filter & 0xffffffff);
|
||||||
wrmsrl(reg1->reg + 1, filter >> 32);
|
wrmsrq(reg1->reg + 1, filter >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops hswep_uncore_cbox_ops = {
|
static struct intel_uncore_ops hswep_uncore_cbox_ops = {
|
||||||
|
|
@ -2816,7 +2817,7 @@ static void hswep_uncore_sbox_msr_init_box(struct intel_uncore_box *box)
|
||||||
|
|
||||||
for_each_set_bit(i, (unsigned long *)&init, 64) {
|
for_each_set_bit(i, (unsigned long *)&init, 64) {
|
||||||
flags |= (1ULL << i);
|
flags |= (1ULL << i);
|
||||||
wrmsrl(msr, flags);
|
wrmsrq(msr, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3708,7 +3709,7 @@ static void skx_iio_enable_event(struct intel_uncore_box *box,
|
||||||
{
|
{
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops skx_uncore_iio_ops = {
|
static struct intel_uncore_ops skx_uncore_iio_ops = {
|
||||||
|
|
@ -3765,7 +3766,7 @@ static int skx_msr_cpu_bus_read(int cpu, u64 *topology)
|
||||||
{
|
{
|
||||||
u64 msr_value;
|
u64 msr_value;
|
||||||
|
|
||||||
if (rdmsrl_on_cpu(cpu, SKX_MSR_CPU_BUS_NUMBER, &msr_value) ||
|
if (rdmsrq_on_cpu(cpu, SKX_MSR_CPU_BUS_NUMBER, &msr_value) ||
|
||||||
!(msr_value & SKX_MSR_CPU_BUS_VALID_BIT))
|
!(msr_value & SKX_MSR_CPU_BUS_VALID_BIT))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
|
|
@ -4655,9 +4656,9 @@ static void snr_cha_enable_event(struct intel_uncore_box *box,
|
||||||
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE)
|
if (reg1->idx != EXTRA_REG_NONE)
|
||||||
wrmsrl(reg1->reg, reg1->config);
|
wrmsrq(reg1->reg, reg1->config);
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
wrmsrq(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct intel_uncore_ops snr_uncore_chabox_ops = {
|
static struct intel_uncore_ops snr_uncore_chabox_ops = {
|
||||||
|
|
@ -5882,9 +5883,9 @@ static void spr_uncore_msr_enable_event(struct intel_uncore_box *box,
|
||||||
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE)
|
if (reg1->idx != EXTRA_REG_NONE)
|
||||||
wrmsrl(reg1->reg, reg1->config);
|
wrmsrq(reg1->reg, reg1->config);
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config);
|
wrmsrq(hwc->config_base, hwc->config);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void spr_uncore_msr_disable_event(struct intel_uncore_box *box,
|
static void spr_uncore_msr_disable_event(struct intel_uncore_box *box,
|
||||||
|
|
@ -5894,9 +5895,9 @@ static void spr_uncore_msr_disable_event(struct intel_uncore_box *box,
|
||||||
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
|
||||||
|
|
||||||
if (reg1->idx != EXTRA_REG_NONE)
|
if (reg1->idx != EXTRA_REG_NONE)
|
||||||
wrmsrl(reg1->reg, 0);
|
wrmsrq(reg1->reg, 0);
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, 0);
|
wrmsrq(hwc->config_base, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int spr_cha_hw_config(struct intel_uncore_box *box, struct perf_event *event)
|
static int spr_cha_hw_config(struct intel_uncore_box *box, struct perf_event *event)
|
||||||
|
|
@ -6485,7 +6486,7 @@ void spr_uncore_cpu_init(void)
|
||||||
* of UNCORE_SPR_CHA) is incorrect on some SPR variants because of a
|
* of UNCORE_SPR_CHA) is incorrect on some SPR variants because of a
|
||||||
* firmware bug. Using the value from SPR_MSR_UNC_CBO_CONFIG to replace it.
|
* firmware bug. Using the value from SPR_MSR_UNC_CBO_CONFIG to replace it.
|
||||||
*/
|
*/
|
||||||
rdmsrl(SPR_MSR_UNC_CBO_CONFIG, num_cbo);
|
rdmsrq(SPR_MSR_UNC_CBO_CONFIG, num_cbo);
|
||||||
/*
|
/*
|
||||||
* The MSR doesn't work on the EMR XCC, but the firmware bug doesn't impact
|
* The MSR doesn't work on the EMR XCC, but the firmware bug doesn't impact
|
||||||
* the EMR XCC. Don't let the value from the MSR replace the existing value.
|
* the EMR XCC. Don't let the value from the MSR replace the existing value.
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
#include <linux/nospec.h>
|
#include <linux/nospec.h>
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "probe.h"
|
#include "probe.h"
|
||||||
|
|
||||||
enum perf_msr_id {
|
enum perf_msr_id {
|
||||||
|
|
@ -231,7 +233,7 @@ static inline u64 msr_read_counter(struct perf_event *event)
|
||||||
u64 now;
|
u64 now;
|
||||||
|
|
||||||
if (event->hw.event_base)
|
if (event->hw.event_base)
|
||||||
rdmsrl(event->hw.event_base, now);
|
rdmsrq(event->hw.event_base, now);
|
||||||
else
|
else
|
||||||
now = rdtsc_ordered();
|
now = rdtsc_ordered();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include <asm/fpu/xstate.h>
|
#include <asm/fpu/xstate.h>
|
||||||
#include <asm/intel_ds.h>
|
#include <asm/intel_ds.h>
|
||||||
#include <asm/cpu.h>
|
#include <asm/cpu.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
/* To enable MSR tracing please use the generic trace points. */
|
/* To enable MSR tracing please use the generic trace points. */
|
||||||
|
|
||||||
|
|
@ -1242,16 +1243,16 @@ static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc,
|
||||||
u64 disable_mask = __this_cpu_read(cpu_hw_events.perf_ctr_virt_mask);
|
u64 disable_mask = __this_cpu_read(cpu_hw_events.perf_ctr_virt_mask);
|
||||||
|
|
||||||
if (hwc->extra_reg.reg)
|
if (hwc->extra_reg.reg)
|
||||||
wrmsrl(hwc->extra_reg.reg, hwc->extra_reg.config);
|
wrmsrq(hwc->extra_reg.reg, hwc->extra_reg.config);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add enabled Merge event on next counter
|
* Add enabled Merge event on next counter
|
||||||
* if large increment event being enabled on this counter
|
* if large increment event being enabled on this counter
|
||||||
*/
|
*/
|
||||||
if (is_counter_pair(hwc))
|
if (is_counter_pair(hwc))
|
||||||
wrmsrl(x86_pmu_config_addr(hwc->idx + 1), x86_pmu.perf_ctr_pair_en);
|
wrmsrq(x86_pmu_config_addr(hwc->idx + 1), x86_pmu.perf_ctr_pair_en);
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, (hwc->config | enable_mask) & ~disable_mask);
|
wrmsrq(hwc->config_base, (hwc->config | enable_mask) & ~disable_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void x86_pmu_enable_all(int added);
|
void x86_pmu_enable_all(int added);
|
||||||
|
|
@ -1267,10 +1268,10 @@ static inline void x86_pmu_disable_event(struct perf_event *event)
|
||||||
u64 disable_mask = __this_cpu_read(cpu_hw_events.perf_ctr_virt_mask);
|
u64 disable_mask = __this_cpu_read(cpu_hw_events.perf_ctr_virt_mask);
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
|
|
||||||
wrmsrl(hwc->config_base, hwc->config & ~disable_mask);
|
wrmsrq(hwc->config_base, hwc->config & ~disable_mask);
|
||||||
|
|
||||||
if (is_counter_pair(hwc))
|
if (is_counter_pair(hwc))
|
||||||
wrmsrl(x86_pmu_config_addr(hwc->idx + 1), 0);
|
wrmsrq(x86_pmu_config_addr(hwc->idx + 1), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void x86_pmu_enable_event(struct perf_event *event);
|
void x86_pmu_enable_event(struct perf_event *event);
|
||||||
|
|
@ -1438,12 +1439,12 @@ static __always_inline void __amd_pmu_lbr_disable(void)
|
||||||
{
|
{
|
||||||
u64 dbg_ctl, dbg_extn_cfg;
|
u64 dbg_ctl, dbg_extn_cfg;
|
||||||
|
|
||||||
rdmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg);
|
rdmsrq(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg);
|
||||||
wrmsrl(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg & ~DBG_EXTN_CFG_LBRV2EN);
|
wrmsrq(MSR_AMD_DBG_EXTN_CFG, dbg_extn_cfg & ~DBG_EXTN_CFG_LBRV2EN);
|
||||||
|
|
||||||
if (cpu_feature_enabled(X86_FEATURE_AMD_LBR_PMC_FREEZE)) {
|
if (cpu_feature_enabled(X86_FEATURE_AMD_LBR_PMC_FREEZE)) {
|
||||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl);
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, dbg_ctl);
|
||||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, dbg_ctl & ~DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
wrmsrq(MSR_IA32_DEBUGCTLMSR, dbg_ctl & ~DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1575,21 +1576,21 @@ static inline bool intel_pmu_has_bts(struct perf_event *event)
|
||||||
|
|
||||||
static __always_inline void __intel_pmu_pebs_disable_all(void)
|
static __always_inline void __intel_pmu_pebs_disable_all(void)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_IA32_PEBS_ENABLE, 0);
|
wrmsrq(MSR_IA32_PEBS_ENABLE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void __intel_pmu_arch_lbr_disable(void)
|
static __always_inline void __intel_pmu_arch_lbr_disable(void)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_ARCH_LBR_CTL, 0);
|
wrmsrq(MSR_ARCH_LBR_CTL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline void __intel_pmu_lbr_disable(void)
|
static __always_inline void __intel_pmu_lbr_disable(void)
|
||||||
{
|
{
|
||||||
u64 debugctl;
|
u64 debugctl;
|
||||||
|
|
||||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||||
debugctl &= ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
debugctl &= ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
||||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
int intel_pmu_save_and_restart(struct perf_event *event);
|
int intel_pmu_save_and_restart(struct perf_event *event);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "probe.h"
|
#include "probe.h"
|
||||||
|
|
||||||
static umode_t
|
static umode_t
|
||||||
|
|
@ -43,7 +45,7 @@ perf_msr_probe(struct perf_msr *msr, int cnt, bool zero, void *data)
|
||||||
if (msr[bit].test && !msr[bit].test(bit, data))
|
if (msr[bit].test && !msr[bit].test(bit, data))
|
||||||
continue;
|
continue;
|
||||||
/* Virt sucks; you cannot tell if a R/O MSR is present :/ */
|
/* Virt sucks; you cannot tell if a R/O MSR is present :/ */
|
||||||
if (rdmsrl_safe(msr[bit].msr, &val))
|
if (rdmsrq_safe(msr[bit].msr, &val))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mask = msr[bit].mask;
|
mask = msr[bit].mask;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
#include <linux/nospec.h>
|
#include <linux/nospec.h>
|
||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
#include <asm/intel-family.h>
|
#include <asm/intel-family.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "perf_event.h"
|
#include "perf_event.h"
|
||||||
#include "probe.h"
|
#include "probe.h"
|
||||||
|
|
||||||
|
|
@ -192,7 +193,7 @@ static inline unsigned int get_rapl_pmu_idx(int cpu, int scope)
|
||||||
static inline u64 rapl_read_counter(struct perf_event *event)
|
static inline u64 rapl_read_counter(struct perf_event *event)
|
||||||
{
|
{
|
||||||
u64 raw;
|
u64 raw;
|
||||||
rdmsrl(event->hw.event_base, raw);
|
rdmsrq(event->hw.event_base, raw);
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,7 +222,7 @@ static u64 rapl_event_update(struct perf_event *event)
|
||||||
|
|
||||||
prev_raw_count = local64_read(&hwc->prev_count);
|
prev_raw_count = local64_read(&hwc->prev_count);
|
||||||
do {
|
do {
|
||||||
rdmsrl(event->hw.event_base, new_raw_count);
|
rdmsrq(event->hw.event_base, new_raw_count);
|
||||||
} while (!local64_try_cmpxchg(&hwc->prev_count,
|
} while (!local64_try_cmpxchg(&hwc->prev_count,
|
||||||
&prev_raw_count, new_raw_count));
|
&prev_raw_count, new_raw_count));
|
||||||
|
|
||||||
|
|
@ -610,8 +611,8 @@ static int rapl_check_hw_unit(void)
|
||||||
u64 msr_rapl_power_unit_bits;
|
u64 msr_rapl_power_unit_bits;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* protect rdmsrl() to handle virtualization */
|
/* protect rdmsrq() to handle virtualization */
|
||||||
if (rdmsrl_safe(rapl_model->msr_power_unit, &msr_rapl_power_unit_bits))
|
if (rdmsrq_safe(rapl_model->msr_power_unit, &msr_rapl_power_unit_bits))
|
||||||
return -1;
|
return -1;
|
||||||
for (i = 0; i < NR_RAPL_PKG_DOMAINS; i++)
|
for (i = 0; i < NR_RAPL_PKG_DOMAINS; i++)
|
||||||
rapl_pkg_hw_unit[i] = (msr_rapl_power_unit_bits >> 8) & 0x1FULL;
|
rapl_pkg_hw_unit[i] = (msr_rapl_power_unit_bits >> 8) & 0x1FULL;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <asm/insn.h>
|
#include <asm/insn.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
|
|
||||||
|
#include <asm/msr.h>
|
||||||
#include "perf_event.h"
|
#include "perf_event.h"
|
||||||
|
|
||||||
static int decode_branch_type(struct insn *insn)
|
static int decode_branch_type(struct insn *insn)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
#include <asm/hardirq.h>
|
#include <asm/hardirq.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include "../perf_event.h"
|
#include "../perf_event.h"
|
||||||
|
|
||||||
|
|
@ -254,26 +255,26 @@ static __initconst const u64 zxe_hw_cache_event_ids
|
||||||
|
|
||||||
static void zhaoxin_pmu_disable_all(void)
|
static void zhaoxin_pmu_disable_all(void)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zhaoxin_pmu_enable_all(int added)
|
static void zhaoxin_pmu_enable_all(int added)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u64 zhaoxin_pmu_get_status(void)
|
static inline u64 zhaoxin_pmu_get_status(void)
|
||||||
{
|
{
|
||||||
u64 status;
|
u64 status;
|
||||||
|
|
||||||
rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void zhaoxin_pmu_ack_status(u64 ack)
|
static inline void zhaoxin_pmu_ack_status(u64 ack)
|
||||||
{
|
{
|
||||||
wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack);
|
wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void zxc_pmu_ack_status(u64 ack)
|
static inline void zxc_pmu_ack_status(u64 ack)
|
||||||
|
|
@ -293,9 +294,9 @@ static void zhaoxin_pmu_disable_fixed(struct hw_perf_event *hwc)
|
||||||
|
|
||||||
mask = 0xfULL << (idx * 4);
|
mask = 0xfULL << (idx * 4);
|
||||||
|
|
||||||
rdmsrl(hwc->config_base, ctrl_val);
|
rdmsrq(hwc->config_base, ctrl_val);
|
||||||
ctrl_val &= ~mask;
|
ctrl_val &= ~mask;
|
||||||
wrmsrl(hwc->config_base, ctrl_val);
|
wrmsrq(hwc->config_base, ctrl_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zhaoxin_pmu_disable_event(struct perf_event *event)
|
static void zhaoxin_pmu_disable_event(struct perf_event *event)
|
||||||
|
|
@ -329,10 +330,10 @@ static void zhaoxin_pmu_enable_fixed(struct hw_perf_event *hwc)
|
||||||
bits <<= (idx * 4);
|
bits <<= (idx * 4);
|
||||||
mask = 0xfULL << (idx * 4);
|
mask = 0xfULL << (idx * 4);
|
||||||
|
|
||||||
rdmsrl(hwc->config_base, ctrl_val);
|
rdmsrq(hwc->config_base, ctrl_val);
|
||||||
ctrl_val &= ~mask;
|
ctrl_val &= ~mask;
|
||||||
ctrl_val |= bits;
|
ctrl_val |= bits;
|
||||||
wrmsrl(hwc->config_base, ctrl_val);
|
wrmsrq(hwc->config_base, ctrl_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zhaoxin_pmu_enable_event(struct perf_event *event)
|
static void zhaoxin_pmu_enable_event(struct perf_event *event)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include <asm/hypervisor.h>
|
#include <asm/hypervisor.h>
|
||||||
#include <asm/mshyperv.h>
|
#include <asm/mshyperv.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
#include <asm/trace/hyperv.h>
|
#include <asm/trace/hyperv.h>
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ static u64 hv_apic_icr_read(void)
|
||||||
{
|
{
|
||||||
u64 reg_val;
|
u64 reg_val;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_ICR, reg_val);
|
rdmsrq(HV_X64_MSR_ICR, reg_val);
|
||||||
return reg_val;
|
return reg_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ static void hv_apic_icr_write(u32 low, u32 id)
|
||||||
reg_val = reg_val << 32;
|
reg_val = reg_val << 32;
|
||||||
reg_val |= low;
|
reg_val |= low;
|
||||||
|
|
||||||
wrmsrl(HV_X64_MSR_ICR, reg_val);
|
wrmsrq(HV_X64_MSR_ICR, reg_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 hv_apic_read(u32 reg)
|
static u32 hv_apic_read(u32 reg)
|
||||||
|
|
@ -75,10 +76,10 @@ static void hv_apic_write(u32 reg, u32 val)
|
||||||
{
|
{
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case APIC_EOI:
|
case APIC_EOI:
|
||||||
wrmsr(HV_X64_MSR_EOI, val, 0);
|
wrmsrq(HV_X64_MSR_EOI, val);
|
||||||
break;
|
break;
|
||||||
case APIC_TASKPRI:
|
case APIC_TASKPRI:
|
||||||
wrmsr(HV_X64_MSR_TPR, val, 0);
|
wrmsrq(HV_X64_MSR_TPR, val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
native_apic_mem_write(reg, val);
|
native_apic_mem_write(reg, val);
|
||||||
|
|
@ -92,7 +93,7 @@ static void hv_apic_eoi_write(void)
|
||||||
if (hvp && (xchg(&hvp->apic_assist, 0) & 0x1))
|
if (hvp && (xchg(&hvp->apic_assist, 0) & 0x1))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wrmsr(HV_X64_MSR_EOI, APIC_EOI_ACK, 0);
|
wrmsrq(HV_X64_MSR_EOI, APIC_EOI_ACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool cpu_is_self(int cpu)
|
static bool cpu_is_self(int cpu)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include <asm/hypervisor.h>
|
#include <asm/hypervisor.h>
|
||||||
#include <hyperv/hvhdk.h>
|
#include <hyperv/hvhdk.h>
|
||||||
#include <asm/mshyperv.h>
|
#include <asm/mshyperv.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <asm/idtentry.h>
|
#include <asm/idtentry.h>
|
||||||
#include <asm/set_memory.h>
|
#include <asm/set_memory.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
|
|
@ -62,7 +63,7 @@ static int hyperv_init_ghcb(void)
|
||||||
* returned by MSR_AMD64_SEV_ES_GHCB is above shared
|
* returned by MSR_AMD64_SEV_ES_GHCB is above shared
|
||||||
* memory boundary and map it here.
|
* memory boundary and map it here.
|
||||||
*/
|
*/
|
||||||
rdmsrl(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
|
rdmsrq(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
|
||||||
|
|
||||||
/* Mask out vTOM bit. ioremap_cache() maps decrypted */
|
/* Mask out vTOM bit. ioremap_cache() maps decrypted */
|
||||||
ghcb_gpa &= ~ms_hyperv.shared_gpa_boundary;
|
ghcb_gpa &= ~ms_hyperv.shared_gpa_boundary;
|
||||||
|
|
@ -95,7 +96,7 @@ static int hv_cpu_init(unsigned int cpu)
|
||||||
* For root partition we get the hypervisor provided VP assist
|
* For root partition we get the hypervisor provided VP assist
|
||||||
* page, instead of allocating a new page.
|
* page, instead of allocating a new page.
|
||||||
*/
|
*/
|
||||||
rdmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
rdmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
||||||
*hvp = memremap(msr.pfn << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT,
|
*hvp = memremap(msr.pfn << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT,
|
||||||
PAGE_SIZE, MEMREMAP_WB);
|
PAGE_SIZE, MEMREMAP_WB);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -128,7 +129,7 @@ static int hv_cpu_init(unsigned int cpu)
|
||||||
}
|
}
|
||||||
if (!WARN_ON(!(*hvp))) {
|
if (!WARN_ON(!(*hvp))) {
|
||||||
msr.enable = 1;
|
msr.enable = 1;
|
||||||
wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
wrmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hyperv_init_ghcb();
|
return hyperv_init_ghcb();
|
||||||
|
|
@ -140,7 +141,7 @@ static void hv_reenlightenment_notify(struct work_struct *dummy)
|
||||||
{
|
{
|
||||||
struct hv_tsc_emulation_status emu_status;
|
struct hv_tsc_emulation_status emu_status;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
|
rdmsrq(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
|
||||||
|
|
||||||
/* Don't issue the callback if TSC accesses are not emulated */
|
/* Don't issue the callback if TSC accesses are not emulated */
|
||||||
if (hv_reenlightenment_cb && emu_status.inprogress)
|
if (hv_reenlightenment_cb && emu_status.inprogress)
|
||||||
|
|
@ -153,11 +154,11 @@ void hyperv_stop_tsc_emulation(void)
|
||||||
u64 freq;
|
u64 freq;
|
||||||
struct hv_tsc_emulation_status emu_status;
|
struct hv_tsc_emulation_status emu_status;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
|
rdmsrq(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
|
||||||
emu_status.inprogress = 0;
|
emu_status.inprogress = 0;
|
||||||
wrmsrl(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
|
wrmsrq(HV_X64_MSR_TSC_EMULATION_STATUS, *(u64 *)&emu_status);
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_TSC_FREQUENCY, freq);
|
rdmsrq(HV_X64_MSR_TSC_FREQUENCY, freq);
|
||||||
tsc_khz = div64_u64(freq, 1000);
|
tsc_khz = div64_u64(freq, 1000);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hyperv_stop_tsc_emulation);
|
EXPORT_SYMBOL_GPL(hyperv_stop_tsc_emulation);
|
||||||
|
|
@ -203,8 +204,8 @@ void set_hv_tscchange_cb(void (*cb)(void))
|
||||||
|
|
||||||
re_ctrl.target_vp = hv_vp_index[get_cpu()];
|
re_ctrl.target_vp = hv_vp_index[get_cpu()];
|
||||||
|
|
||||||
wrmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
|
wrmsrq(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
|
||||||
wrmsrl(HV_X64_MSR_TSC_EMULATION_CONTROL, *((u64 *)&emu_ctrl));
|
wrmsrq(HV_X64_MSR_TSC_EMULATION_CONTROL, *((u64 *)&emu_ctrl));
|
||||||
|
|
||||||
put_cpu();
|
put_cpu();
|
||||||
}
|
}
|
||||||
|
|
@ -217,9 +218,9 @@ void clear_hv_tscchange_cb(void)
|
||||||
if (!hv_reenlightenment_available())
|
if (!hv_reenlightenment_available())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *(u64 *)&re_ctrl);
|
rdmsrq(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *(u64 *)&re_ctrl);
|
||||||
re_ctrl.enabled = 0;
|
re_ctrl.enabled = 0;
|
||||||
wrmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *(u64 *)&re_ctrl);
|
wrmsrq(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *(u64 *)&re_ctrl);
|
||||||
|
|
||||||
hv_reenlightenment_cb = NULL;
|
hv_reenlightenment_cb = NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -251,16 +252,16 @@ static int hv_cpu_die(unsigned int cpu)
|
||||||
*/
|
*/
|
||||||
memunmap(hv_vp_assist_page[cpu]);
|
memunmap(hv_vp_assist_page[cpu]);
|
||||||
hv_vp_assist_page[cpu] = NULL;
|
hv_vp_assist_page[cpu] = NULL;
|
||||||
rdmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
rdmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
||||||
msr.enable = 0;
|
msr.enable = 0;
|
||||||
}
|
}
|
||||||
wrmsrl(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
wrmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hv_reenlightenment_cb == NULL)
|
if (hv_reenlightenment_cb == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
|
rdmsrq(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
|
||||||
if (re_ctrl.target_vp == hv_vp_index[cpu]) {
|
if (re_ctrl.target_vp == hv_vp_index[cpu]) {
|
||||||
/*
|
/*
|
||||||
* Reassign reenlightenment notifications to some other online
|
* Reassign reenlightenment notifications to some other online
|
||||||
|
|
@ -274,7 +275,7 @@ static int hv_cpu_die(unsigned int cpu)
|
||||||
else
|
else
|
||||||
re_ctrl.enabled = 0;
|
re_ctrl.enabled = 0;
|
||||||
|
|
||||||
wrmsrl(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
|
wrmsrq(HV_X64_MSR_REENLIGHTENMENT_CONTROL, *((u64 *)&re_ctrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -331,9 +332,9 @@ static int hv_suspend(void)
|
||||||
hv_hypercall_pg = NULL;
|
hv_hypercall_pg = NULL;
|
||||||
|
|
||||||
/* Disable the hypercall page in the hypervisor */
|
/* Disable the hypercall page in the hypervisor */
|
||||||
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
rdmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
hypercall_msr.enable = 0;
|
hypercall_msr.enable = 0;
|
||||||
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
|
|
||||||
ret = hv_cpu_die(0);
|
ret = hv_cpu_die(0);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -348,11 +349,11 @@ static void hv_resume(void)
|
||||||
WARN_ON(ret);
|
WARN_ON(ret);
|
||||||
|
|
||||||
/* Re-enable the hypercall page */
|
/* Re-enable the hypercall page */
|
||||||
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
rdmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
hypercall_msr.enable = 1;
|
hypercall_msr.enable = 1;
|
||||||
hypercall_msr.guest_physical_address =
|
hypercall_msr.guest_physical_address =
|
||||||
vmalloc_to_pfn(hv_hypercall_pg_saved);
|
vmalloc_to_pfn(hv_hypercall_pg_saved);
|
||||||
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
|
|
||||||
hv_hypercall_pg = hv_hypercall_pg_saved;
|
hv_hypercall_pg = hv_hypercall_pg_saved;
|
||||||
hv_hypercall_pg_saved = NULL;
|
hv_hypercall_pg_saved = NULL;
|
||||||
|
|
@ -499,7 +500,7 @@ void __init hyperv_init(void)
|
||||||
* in such a VM and is only used in such a VM.
|
* in such a VM and is only used in such a VM.
|
||||||
*/
|
*/
|
||||||
guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
|
guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
|
||||||
wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
|
wrmsrq(HV_X64_MSR_GUEST_OS_ID, guest_id);
|
||||||
|
|
||||||
/* With the paravisor, the VM must also write the ID via GHCB/GHCI */
|
/* With the paravisor, the VM must also write the ID via GHCB/GHCI */
|
||||||
hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
|
hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id);
|
||||||
|
|
@ -515,7 +516,7 @@ void __init hyperv_init(void)
|
||||||
if (hv_hypercall_pg == NULL)
|
if (hv_hypercall_pg == NULL)
|
||||||
goto clean_guest_os_id;
|
goto clean_guest_os_id;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
rdmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
hypercall_msr.enable = 1;
|
hypercall_msr.enable = 1;
|
||||||
|
|
||||||
if (hv_root_partition()) {
|
if (hv_root_partition()) {
|
||||||
|
|
@ -532,7 +533,7 @@ void __init hyperv_init(void)
|
||||||
* so it is populated with code, then copy the code to an
|
* so it is populated with code, then copy the code to an
|
||||||
* executable page.
|
* executable page.
|
||||||
*/
|
*/
|
||||||
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
|
|
||||||
pg = vmalloc_to_page(hv_hypercall_pg);
|
pg = vmalloc_to_page(hv_hypercall_pg);
|
||||||
src = memremap(hypercall_msr.guest_physical_address << PAGE_SHIFT, PAGE_SIZE,
|
src = memremap(hypercall_msr.guest_physical_address << PAGE_SHIFT, PAGE_SIZE,
|
||||||
|
|
@ -544,7 +545,7 @@ void __init hyperv_init(void)
|
||||||
hv_remap_tsc_clocksource();
|
hv_remap_tsc_clocksource();
|
||||||
} else {
|
} else {
|
||||||
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
|
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hv_hypercall_pg);
|
||||||
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
wrmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_hypercall_pg_init:
|
skip_hypercall_pg_init:
|
||||||
|
|
@ -608,7 +609,7 @@ skip_hypercall_pg_init:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clean_guest_os_id:
|
clean_guest_os_id:
|
||||||
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
|
wrmsrq(HV_X64_MSR_GUEST_OS_ID, 0);
|
||||||
hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
|
hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
|
||||||
cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
|
cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
|
||||||
free_ghcb_page:
|
free_ghcb_page:
|
||||||
|
|
@ -629,7 +630,7 @@ void hyperv_cleanup(void)
|
||||||
union hv_reference_tsc_msr tsc_msr;
|
union hv_reference_tsc_msr tsc_msr;
|
||||||
|
|
||||||
/* Reset our OS id */
|
/* Reset our OS id */
|
||||||
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
|
wrmsrq(HV_X64_MSR_GUEST_OS_ID, 0);
|
||||||
hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
|
hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -667,18 +668,18 @@ void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die)
|
||||||
return;
|
return;
|
||||||
panic_reported = true;
|
panic_reported = true;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
|
rdmsrq(HV_X64_MSR_GUEST_OS_ID, guest_id);
|
||||||
|
|
||||||
wrmsrl(HV_X64_MSR_CRASH_P0, err);
|
wrmsrq(HV_X64_MSR_CRASH_P0, err);
|
||||||
wrmsrl(HV_X64_MSR_CRASH_P1, guest_id);
|
wrmsrq(HV_X64_MSR_CRASH_P1, guest_id);
|
||||||
wrmsrl(HV_X64_MSR_CRASH_P2, regs->ip);
|
wrmsrq(HV_X64_MSR_CRASH_P2, regs->ip);
|
||||||
wrmsrl(HV_X64_MSR_CRASH_P3, regs->ax);
|
wrmsrq(HV_X64_MSR_CRASH_P3, regs->ax);
|
||||||
wrmsrl(HV_X64_MSR_CRASH_P4, regs->sp);
|
wrmsrq(HV_X64_MSR_CRASH_P4, regs->sp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Let Hyper-V know there is crash data available
|
* Let Hyper-V know there is crash data available
|
||||||
*/
|
*/
|
||||||
wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
|
wrmsrq(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hyperv_report_panic);
|
EXPORT_SYMBOL_GPL(hyperv_report_panic);
|
||||||
|
|
||||||
|
|
@ -701,7 +702,7 @@ bool hv_is_hyperv_initialized(void)
|
||||||
* that the hypercall page is setup
|
* that the hypercall page is setup
|
||||||
*/
|
*/
|
||||||
hypercall_msr.as_uint64 = 0;
|
hypercall_msr.as_uint64 = 0;
|
||||||
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
rdmsrq(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
|
||||||
|
|
||||||
return hypercall_msr.enable;
|
return hypercall_msr.enable;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <asm/mshyperv.h>
|
#include <asm/mshyperv.h>
|
||||||
#include <asm/paravirt.h>
|
#include <asm/paravirt.h>
|
||||||
#include <asm/apic.h>
|
#include <asm/apic.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
|
|
||||||
static bool hv_pvspin __initdata = true;
|
static bool hv_pvspin __initdata = true;
|
||||||
|
|
||||||
|
|
@ -39,18 +40,18 @@ static void hv_qlock_wait(u8 *byte, u8 val)
|
||||||
* To prevent a race against the unlock path it is required to
|
* To prevent a race against the unlock path it is required to
|
||||||
* disable interrupts before accessing the HV_X64_MSR_GUEST_IDLE
|
* disable interrupts before accessing the HV_X64_MSR_GUEST_IDLE
|
||||||
* MSR. Otherwise, if the IPI from hv_qlock_kick() arrives between
|
* MSR. Otherwise, if the IPI from hv_qlock_kick() arrives between
|
||||||
* the lock value check and the rdmsrl() then the vCPU might be put
|
* the lock value check and the rdmsrq() then the vCPU might be put
|
||||||
* into 'idle' state by the hypervisor and kept in that state for
|
* into 'idle' state by the hypervisor and kept in that state for
|
||||||
* an unspecified amount of time.
|
* an unspecified amount of time.
|
||||||
*/
|
*/
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
/*
|
/*
|
||||||
* Only issue the rdmsrl() when the lock state has not changed.
|
* Only issue the rdmsrq() when the lock state has not changed.
|
||||||
*/
|
*/
|
||||||
if (READ_ONCE(*byte) == val) {
|
if (READ_ONCE(*byte) == val) {
|
||||||
unsigned long msr_val;
|
unsigned long msr_val;
|
||||||
|
|
||||||
rdmsrl(HV_X64_MSR_GUEST_IDLE, msr_val);
|
rdmsrq(HV_X64_MSR_GUEST_IDLE, msr_val);
|
||||||
|
|
||||||
(void)msr_val;
|
(void)msr_val;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <asm/desc.h>
|
#include <asm/desc.h>
|
||||||
#include <asm/i8259.h>
|
#include <asm/i8259.h>
|
||||||
#include <asm/mshyperv.h>
|
#include <asm/mshyperv.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <asm/realmode.h>
|
#include <asm/realmode.h>
|
||||||
#include <asm/reboot.h>
|
#include <asm/reboot.h>
|
||||||
#include <../kernel/smpboot.h>
|
#include <../kernel/smpboot.h>
|
||||||
|
|
@ -149,11 +150,11 @@ static int hv_vtl_bringup_vcpu(u32 target_vp_index, int cpu, u64 eip_ignored)
|
||||||
input->vp_context.rip = rip;
|
input->vp_context.rip = rip;
|
||||||
input->vp_context.rsp = rsp;
|
input->vp_context.rsp = rsp;
|
||||||
input->vp_context.rflags = 0x0000000000000002;
|
input->vp_context.rflags = 0x0000000000000002;
|
||||||
input->vp_context.efer = __rdmsr(MSR_EFER);
|
input->vp_context.efer = native_rdmsrq(MSR_EFER);
|
||||||
input->vp_context.cr0 = native_read_cr0();
|
input->vp_context.cr0 = native_read_cr0();
|
||||||
input->vp_context.cr3 = __native_read_cr3();
|
input->vp_context.cr3 = __native_read_cr3();
|
||||||
input->vp_context.cr4 = native_read_cr4();
|
input->vp_context.cr4 = native_read_cr4();
|
||||||
input->vp_context.msr_cr_pat = __rdmsr(MSR_IA32_CR_PAT);
|
input->vp_context.msr_cr_pat = native_rdmsrq(MSR_IA32_CR_PAT);
|
||||||
input->vp_context.idtr.limit = idt_ptr.size;
|
input->vp_context.idtr.limit = idt_ptr.size;
|
||||||
input->vp_context.idtr.base = idt_ptr.address;
|
input->vp_context.idtr.base = idt_ptr.address;
|
||||||
input->vp_context.gdtr.limit = gdt_ptr.size;
|
input->vp_context.gdtr.limit = gdt_ptr.size;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
#include <asm/realmode.h>
|
#include <asm/realmode.h>
|
||||||
#include <asm/e820/api.h>
|
#include <asm/e820/api.h>
|
||||||
#include <asm/desc.h>
|
#include <asm/desc.h>
|
||||||
|
#include <asm/msr.h>
|
||||||
#include <uapi/asm/vmx.h>
|
#include <uapi/asm/vmx.h>
|
||||||
|
|
||||||
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
#ifdef CONFIG_AMD_MEM_ENCRYPT
|
||||||
|
|
@ -110,12 +111,12 @@ u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size)
|
||||||
|
|
||||||
static inline u64 rd_ghcb_msr(void)
|
static inline u64 rd_ghcb_msr(void)
|
||||||
{
|
{
|
||||||
return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
|
return native_rdmsrq(MSR_AMD64_SEV_ES_GHCB);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void wr_ghcb_msr(u64 val)
|
static inline void wr_ghcb_msr(u64 val)
|
||||||
{
|
{
|
||||||
native_wrmsrl(MSR_AMD64_SEV_ES_GHCB, val);
|
native_wrmsrq(MSR_AMD64_SEV_ES_GHCB, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum es_result hv_ghcb_hv_call(struct ghcb *ghcb, u64 exit_code,
|
static enum es_result hv_ghcb_hv_call(struct ghcb *ghcb, u64 exit_code,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ void acrn_remove_intr_handler(void);
|
||||||
static inline u32 acrn_cpuid_base(void)
|
static inline u32 acrn_cpuid_base(void)
|
||||||
{
|
{
|
||||||
if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
|
if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
|
||||||
return hypervisor_cpuid_base("ACRNACRNACRN", 0);
|
return cpuid_base_hypervisor("ACRNACRNACRN", 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,12 @@ struct alt_instr {
|
||||||
|
|
||||||
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
|
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
|
||||||
|
|
||||||
|
extern s32 __retpoline_sites[], __retpoline_sites_end[];
|
||||||
|
extern s32 __return_sites[], __return_sites_end[];
|
||||||
|
extern s32 __cfi_sites[], __cfi_sites_end[];
|
||||||
|
extern s32 __ibt_endbr_seal[], __ibt_endbr_seal_end[];
|
||||||
|
extern s32 __smp_locks[], __smp_locks_end[];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Debug flag that can be tested to see whether alternative
|
* Debug flag that can be tested to see whether alternative
|
||||||
* instructions were patched in already:
|
* instructions were patched in already:
|
||||||
|
|
@ -367,11 +373,6 @@ void nop_func(void);
|
||||||
__ALTERNATIVE(\oldinstr, \newinstr, \ft_flags)
|
__ALTERNATIVE(\oldinstr, \newinstr, \ft_flags)
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
#define old_len 141b-140b
|
|
||||||
#define new_len1 144f-143f
|
|
||||||
#define new_len2 145f-144f
|
|
||||||
#define new_len3 146f-145f
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Same as ALTERNATIVE macro above but for two alternatives. If CPU
|
* Same as ALTERNATIVE macro above but for two alternatives. If CPU
|
||||||
* has @feature1, it replaces @oldinstr with @newinstr1. If CPU has
|
* has @feature1, it replaces @oldinstr with @newinstr1. If CPU has
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _ASM_X86_AMD_FCH_H_
|
||||||
|
#define _ASM_X86_AMD_FCH_H_
|
||||||
|
|
||||||
|
#define FCH_PM_BASE 0xFED80300
|
||||||
|
|
||||||
|
/* Register offsets from PM base: */
|
||||||
|
#define FCH_PM_DECODEEN 0x00
|
||||||
|
#define FCH_PM_DECODEEN_SMBUS0SEL GENMASK(20, 19)
|
||||||
|
#define FCH_PM_SCRATCH 0x80
|
||||||
|
#define FCH_PM_S5_RESET_STATUS 0xC0
|
||||||
|
|
||||||
|
#endif /* _ASM_X86_AMD_FCH_H_ */
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
|
|
||||||
#ifndef _ASM_X86_AMD_HSMP_H_
|
#ifndef _ASM_X86_AMD_HSMP_H_
|
||||||
#define _ASM_X86_AMD_HSMP_H_
|
#define _ASM_X86_AMD_HSMP_H_
|
||||||
|
|
||||||
|
|
@ -13,4 +12,5 @@ static inline int hsmp_send_message(struct hsmp_message *msg)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /*_ASM_X86_AMD_HSMP_H_*/
|
#endif /*_ASM_X86_AMD_HSMP_H_*/
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
#ifndef _ASM_X86_AMD_IBS_H
|
||||||
|
#define _ASM_X86_AMD_IBS_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From PPR Vol 1 for AMD Family 19h Model 01h B1
|
* From PPR Vol 1 for AMD Family 19h Model 01h B1
|
||||||
* 55898 Rev 0.35 - Feb 5, 2021
|
* 55898 Rev 0.35 - Feb 5, 2021
|
||||||
|
|
@ -151,3 +154,5 @@ struct perf_ibs_data {
|
||||||
};
|
};
|
||||||
u64 regs[MSR_AMD64_IBS_REG_COUNT_MAX];
|
u64 regs[MSR_AMD64_IBS_REG_COUNT_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif /* _ASM_X86_AMD_IBS_H */
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <asm/amd_node.h>
|
#include <asm/amd/node.h>
|
||||||
|
|
||||||
struct amd_nb_bus_dev_range {
|
struct amd_nb_bus_dev_range {
|
||||||
u8 bus;
|
u8 bus;
|
||||||
|
|
@ -120,7 +120,7 @@ static inline bool apic_is_x2apic_enabled(void)
|
||||||
{
|
{
|
||||||
u64 msr;
|
u64 msr;
|
||||||
|
|
||||||
if (rdmsrl_safe(MSR_IA32_APICBASE, &msr))
|
if (rdmsrq_safe(MSR_IA32_APICBASE, &msr))
|
||||||
return false;
|
return false;
|
||||||
return msr & X2APIC_ENABLE;
|
return msr & X2APIC_ENABLE;
|
||||||
}
|
}
|
||||||
|
|
@ -209,12 +209,12 @@ static inline void native_apic_msr_write(u32 reg, u32 v)
|
||||||
reg == APIC_LVR)
|
reg == APIC_LVR)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wrmsr(APIC_BASE_MSR + (reg >> 4), v, 0);
|
wrmsrq(APIC_BASE_MSR + (reg >> 4), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void native_apic_msr_eoi(void)
|
static inline void native_apic_msr_eoi(void)
|
||||||
{
|
{
|
||||||
__wrmsr(APIC_BASE_MSR + (APIC_EOI >> 4), APIC_EOI_ACK, 0);
|
native_wrmsrq(APIC_BASE_MSR + (APIC_EOI >> 4), APIC_EOI_ACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 native_apic_msr_read(u32 reg)
|
static inline u32 native_apic_msr_read(u32 reg)
|
||||||
|
|
@ -224,20 +224,20 @@ static inline u32 native_apic_msr_read(u32 reg)
|
||||||
if (reg == APIC_DFR)
|
if (reg == APIC_DFR)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
rdmsrl(APIC_BASE_MSR + (reg >> 4), msr);
|
rdmsrq(APIC_BASE_MSR + (reg >> 4), msr);
|
||||||
return (u32)msr;
|
return (u32)msr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void native_x2apic_icr_write(u32 low, u32 id)
|
static inline void native_x2apic_icr_write(u32 low, u32 id)
|
||||||
{
|
{
|
||||||
wrmsrl(APIC_BASE_MSR + (APIC_ICR >> 4), ((__u64) id) << 32 | low);
|
wrmsrq(APIC_BASE_MSR + (APIC_ICR >> 4), ((__u64) id) << 32 | low);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u64 native_x2apic_icr_read(void)
|
static inline u64 native_x2apic_icr_read(void)
|
||||||
{
|
{
|
||||||
unsigned long val;
|
unsigned long val;
|
||||||
|
|
||||||
rdmsrl(APIC_BASE_MSR + (APIC_ICR >> 4), val);
|
rdmsrq(APIC_BASE_MSR + (APIC_ICR >> 4), val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@ static __always_inline unsigned int __arch_hweight32(unsigned int w)
|
||||||
{
|
{
|
||||||
unsigned int res;
|
unsigned int res;
|
||||||
|
|
||||||
asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE
|
asm_inline (ALTERNATIVE("call __sw_hweight32",
|
||||||
"call __sw_hweight32",
|
|
||||||
"popcntl %[val], %[cnt]", X86_FEATURE_POPCNT)
|
"popcntl %[val], %[cnt]", X86_FEATURE_POPCNT)
|
||||||
: [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT
|
: [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT
|
||||||
: [val] REG_IN (w));
|
: [val] REG_IN (w));
|
||||||
|
|
@ -46,8 +45,7 @@ static __always_inline unsigned long __arch_hweight64(__u64 w)
|
||||||
{
|
{
|
||||||
unsigned long res;
|
unsigned long res;
|
||||||
|
|
||||||
asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE
|
asm_inline (ALTERNATIVE("call __sw_hweight64",
|
||||||
"call __sw_hweight64",
|
|
||||||
"popcntq %[val], %[cnt]", X86_FEATURE_POPCNT)
|
"popcntq %[val], %[cnt]", X86_FEATURE_POPCNT)
|
||||||
: [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT
|
: [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT
|
||||||
: [val] REG_IN (w));
|
: [val] REG_IN (w));
|
||||||
|
|
|
||||||
|
|
@ -114,17 +114,12 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef __ASSEMBLER__
|
#ifndef __ASSEMBLER__
|
||||||
#ifndef __pic__
|
|
||||||
static __always_inline __pure void *rip_rel_ptr(void *p)
|
static __always_inline __pure void *rip_rel_ptr(void *p)
|
||||||
{
|
{
|
||||||
asm("leaq %c1(%%rip), %0" : "=r"(p) : "i"(p));
|
asm("leaq %c1(%%rip), %0" : "=r"(p) : "i"(p));
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
#define RIP_REL_REF(var) (*(typeof(&(var)))rip_rel_ptr(&(var)))
|
|
||||||
#else
|
|
||||||
#define RIP_REL_REF(var) (var)
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -243,5 +238,24 @@ register unsigned long current_stack_pointer asm(_ASM_SP);
|
||||||
#define _ASM_EXTABLE_FAULT(from, to) \
|
#define _ASM_EXTABLE_FAULT(from, to) \
|
||||||
_ASM_EXTABLE_TYPE(from, to, EX_TYPE_FAULT)
|
_ASM_EXTABLE_TYPE(from, to, EX_TYPE_FAULT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Both i386 and x86_64 returns 64-bit values in edx:eax for certain
|
||||||
|
* instructions, but GCC's "A" constraint has different meanings.
|
||||||
|
* For i386, "A" means exactly edx:eax, while for x86_64 it
|
||||||
|
* means rax *or* rdx.
|
||||||
|
*
|
||||||
|
* These helpers wrapping these semantic differences save one instruction
|
||||||
|
* clearing the high half of 'low':
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_X86_64
|
||||||
|
# define EAX_EDX_DECLARE_ARGS(val, low, high) unsigned long low, high
|
||||||
|
# define EAX_EDX_VAL(val, low, high) ((low) | (high) << 32)
|
||||||
|
# define EAX_EDX_RET(val, low, high) "=a" (low), "=d" (high)
|
||||||
|
#else
|
||||||
|
# define EAX_EDX_DECLARE_ARGS(val, low, high) u64 val
|
||||||
|
# define EAX_EDX_VAL(val, low, high) (val)
|
||||||
|
# define EAX_EDX_RET(val, low, high) "=A" (val)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
#endif /* _ASM_X86_ASM_H */
|
#endif /* _ASM_X86_ASM_H */
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ arch_test_bit_acquire(unsigned long nr, const volatile unsigned long *addr)
|
||||||
|
|
||||||
static __always_inline unsigned long variable__ffs(unsigned long word)
|
static __always_inline unsigned long variable__ffs(unsigned long word)
|
||||||
{
|
{
|
||||||
asm("rep; bsf %1,%0"
|
asm("tzcnt %1,%0"
|
||||||
: "=r" (word)
|
: "=r" (word)
|
||||||
: ASM_INPUT_RM (word));
|
: ASM_INPUT_RM (word));
|
||||||
return word;
|
return word;
|
||||||
|
|
@ -267,10 +267,7 @@ static __always_inline unsigned long variable__ffs(unsigned long word)
|
||||||
|
|
||||||
static __always_inline unsigned long variable_ffz(unsigned long word)
|
static __always_inline unsigned long variable_ffz(unsigned long word)
|
||||||
{
|
{
|
||||||
asm("rep; bsf %1,%0"
|
return variable__ffs(~word);
|
||||||
: "=r" (word)
|
|
||||||
: "r" (~word));
|
|
||||||
return word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,11 @@
|
||||||
# define BOOT_STACK_SIZE 0x1000
|
# define BOOT_STACK_SIZE 0x1000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define TRAMPOLINE_32BIT_SIZE (2 * PAGE_SIZE)
|
||||||
|
|
||||||
|
#define TRAMPOLINE_32BIT_CODE_OFFSET PAGE_SIZE
|
||||||
|
#define TRAMPOLINE_32BIT_CODE_SIZE 0xA0
|
||||||
|
|
||||||
#ifndef __ASSEMBLER__
|
#ifndef __ASSEMBLER__
|
||||||
extern unsigned int output_len;
|
extern unsigned int output_len;
|
||||||
extern const unsigned long kernel_text_size;
|
extern const unsigned long kernel_text_size;
|
||||||
|
|
@ -83,6 +88,11 @@ unsigned long decompress_kernel(unsigned char *outbuf, unsigned long virt_addr,
|
||||||
void (*error)(char *x));
|
void (*error)(char *x));
|
||||||
|
|
||||||
extern struct boot_params *boot_params_ptr;
|
extern struct boot_params *boot_params_ptr;
|
||||||
|
extern unsigned long *trampoline_32bit;
|
||||||
|
extern const u16 trampoline_ljmp_imm_offset;
|
||||||
|
|
||||||
|
void trampoline_32bit_src(void *trampoline, bool enable_5lvl);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* _ASM_X86_BOOT_H */
|
#endif /* _ASM_X86_BOOT_H */
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ static inline u64 cc_get_mask(void)
|
||||||
|
|
||||||
static inline void cc_set_mask(u64 mask)
|
static inline void cc_set_mask(u64 mask)
|
||||||
{
|
{
|
||||||
RIP_REL_REF(cc_mask) = mask;
|
cc_mask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 cc_mkenc(u64 val);
|
u64 cc_mkenc(u64 val);
|
||||||
|
|
|
||||||
|
|
@ -476,12 +476,13 @@
|
||||||
#define X86_FEATURE_CLEAR_BHB_LOOP (21*32+ 1) /* Clear branch history at syscall entry using SW loop */
|
#define X86_FEATURE_CLEAR_BHB_LOOP (21*32+ 1) /* Clear branch history at syscall entry using SW loop */
|
||||||
#define X86_FEATURE_BHI_CTRL (21*32+ 2) /* BHI_DIS_S HW control available */
|
#define X86_FEATURE_BHI_CTRL (21*32+ 2) /* BHI_DIS_S HW control available */
|
||||||
#define X86_FEATURE_CLEAR_BHB_HW (21*32+ 3) /* BHI_DIS_S HW control enabled */
|
#define X86_FEATURE_CLEAR_BHB_HW (21*32+ 3) /* BHI_DIS_S HW control enabled */
|
||||||
#define X86_FEATURE_CLEAR_BHB_LOOP_ON_VMEXIT (21*32+ 4) /* Clear branch history at vmexit using SW loop */
|
#define X86_FEATURE_CLEAR_BHB_VMEXIT (21*32+ 4) /* Clear branch history at vmexit using SW loop */
|
||||||
#define X86_FEATURE_AMD_FAST_CPPC (21*32 + 5) /* Fast CPPC */
|
#define X86_FEATURE_AMD_FAST_CPPC (21*32+ 5) /* Fast CPPC */
|
||||||
#define X86_FEATURE_AMD_HETEROGENEOUS_CORES (21*32 + 6) /* Heterogeneous Core Topology */
|
#define X86_FEATURE_AMD_HTR_CORES (21*32+ 6) /* Heterogeneous Core Topology */
|
||||||
#define X86_FEATURE_AMD_WORKLOAD_CLASS (21*32 + 7) /* Workload Classification */
|
#define X86_FEATURE_AMD_WORKLOAD_CLASS (21*32+ 7) /* Workload Classification */
|
||||||
#define X86_FEATURE_PREFER_YMM (21*32 + 8) /* Avoid ZMM registers due to downclocking */
|
#define X86_FEATURE_PREFER_YMM (21*32+ 8) /* Avoid ZMM registers due to downclocking */
|
||||||
#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32 + 9) /* Use thunk for indirect branches in lower half of cacheline */
|
#define X86_FEATURE_APX (21*32+ 9) /* Advanced Performance Extensions */
|
||||||
|
#define X86_FEATURE_INDIRECT_THUNK_ITS (21*32+10) /* Use thunk for indirect branches in lower half of cacheline */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BUG word(s)
|
* BUG word(s)
|
||||||
|
|
@ -520,7 +521,7 @@
|
||||||
#define X86_BUG_ITLB_MULTIHIT X86_BUG(23) /* "itlb_multihit" CPU may incur MCE during certain page attribute changes */
|
#define X86_BUG_ITLB_MULTIHIT X86_BUG(23) /* "itlb_multihit" CPU may incur MCE during certain page attribute changes */
|
||||||
#define X86_BUG_SRBDS X86_BUG(24) /* "srbds" CPU may leak RNG bits if not mitigated */
|
#define X86_BUG_SRBDS X86_BUG(24) /* "srbds" CPU may leak RNG bits if not mitigated */
|
||||||
#define X86_BUG_MMIO_STALE_DATA X86_BUG(25) /* "mmio_stale_data" CPU is affected by Processor MMIO Stale Data vulnerabilities */
|
#define X86_BUG_MMIO_STALE_DATA X86_BUG(25) /* "mmio_stale_data" CPU is affected by Processor MMIO Stale Data vulnerabilities */
|
||||||
#define X86_BUG_MMIO_UNKNOWN X86_BUG(26) /* "mmio_unknown" CPU is too old and its MMIO Stale Data status is unknown */
|
/* unused, was #define X86_BUG_MMIO_UNKNOWN X86_BUG(26) "mmio_unknown" CPU is too old and its MMIO Stale Data status is unknown */
|
||||||
#define X86_BUG_RETBLEED X86_BUG(27) /* "retbleed" CPU is affected by RETBleed */
|
#define X86_BUG_RETBLEED X86_BUG(27) /* "retbleed" CPU is affected by RETBleed */
|
||||||
#define X86_BUG_EIBRS_PBRSB X86_BUG(28) /* "eibrs_pbrsb" EIBRS is vulnerable to Post Barrier RSB Predictions */
|
#define X86_BUG_EIBRS_PBRSB X86_BUG(28) /* "eibrs_pbrsb" EIBRS is vulnerable to Post Barrier RSB Predictions */
|
||||||
#define X86_BUG_SMT_RSB X86_BUG(29) /* "smt_rsb" CPU is vulnerable to Cross-Thread Return Address Predictions */
|
#define X86_BUG_SMT_RSB X86_BUG(29) /* "smt_rsb" CPU is vulnerable to Cross-Thread Return Address Predictions */
|
||||||
|
|
@ -528,12 +529,14 @@
|
||||||
#define X86_BUG_TDX_PW_MCE X86_BUG(31) /* "tdx_pw_mce" CPU may incur #MC if non-TD software does partial write to TDX private memory */
|
#define X86_BUG_TDX_PW_MCE X86_BUG(31) /* "tdx_pw_mce" CPU may incur #MC if non-TD software does partial write to TDX private memory */
|
||||||
|
|
||||||
/* BUG word 2 */
|
/* BUG word 2 */
|
||||||
#define X86_BUG_SRSO X86_BUG(1*32 + 0) /* "srso" AMD SRSO bug */
|
#define X86_BUG_SRSO X86_BUG( 1*32+ 0) /* "srso" AMD SRSO bug */
|
||||||
#define X86_BUG_DIV0 X86_BUG(1*32 + 1) /* "div0" AMD DIV0 speculation bug */
|
#define X86_BUG_DIV0 X86_BUG( 1*32+ 1) /* "div0" AMD DIV0 speculation bug */
|
||||||
#define X86_BUG_RFDS X86_BUG(1*32 + 2) /* "rfds" CPU is vulnerable to Register File Data Sampling */
|
#define X86_BUG_RFDS X86_BUG( 1*32+ 2) /* "rfds" CPU is vulnerable to Register File Data Sampling */
|
||||||
#define X86_BUG_BHI X86_BUG(1*32 + 3) /* "bhi" CPU is affected by Branch History Injection */
|
#define X86_BUG_BHI X86_BUG( 1*32+ 3) /* "bhi" CPU is affected by Branch History Injection */
|
||||||
#define X86_BUG_IBPB_NO_RET X86_BUG(1*32 + 4) /* "ibpb_no_ret" IBPB omits return target predictions */
|
#define X86_BUG_IBPB_NO_RET X86_BUG( 1*32+ 4) /* "ibpb_no_ret" IBPB omits return target predictions */
|
||||||
#define X86_BUG_SPECTRE_V2_USER X86_BUG(1*32 + 5) /* "spectre_v2_user" CPU is affected by Spectre variant 2 attack between user processes */
|
#define X86_BUG_SPECTRE_V2_USER X86_BUG( 1*32+ 5) /* "spectre_v2_user" CPU is affected by Spectre variant 2 attack between user processes */
|
||||||
#define X86_BUG_ITS X86_BUG(1*32 + 6) /* "its" CPU is affected by Indirect Target Selection */
|
#define X86_BUG_OLD_MICROCODE X86_BUG( 1*32+ 6) /* "old_microcode" CPU has old microcode, it is surely vulnerable to something */
|
||||||
#define X86_BUG_ITS_NATIVE_ONLY X86_BUG(1*32 + 7) /* "its_native_only" CPU is affected by ITS, VMX is not affected */
|
#define X86_BUG_ITS X86_BUG( 1*32+ 7) /* "its" CPU is affected by Indirect Target Selection */
|
||||||
|
#define X86_BUG_ITS_NATIVE_ONLY X86_BUG( 1*32+ 8) /* "its_native_only" CPU is affected by ITS, VMX is not affected */
|
||||||
|
|
||||||
#endif /* _ASM_X86_CPUFEATURES_H */
|
#endif /* _ASM_X86_CPUFEATURES_H */
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
bool have_cpuid_p(void);
|
bool cpuid_feature(void);
|
||||||
#else
|
#else
|
||||||
static inline bool have_cpuid_p(void)
|
static inline bool cpuid_feature(void)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +160,10 @@ static inline void __cpuid_read_reg(u32 leaf, u32 subleaf,
|
||||||
__cpuid_read_reg(leaf, 0, regidx, (u32 *)(reg)); \
|
__cpuid_read_reg(leaf, 0, regidx, (u32 *)(reg)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hypervisor-related APIs:
|
||||||
|
*/
|
||||||
|
|
||||||
static __always_inline bool cpuid_function_is_indexed(u32 function)
|
static __always_inline bool cpuid_function_is_indexed(u32 function)
|
||||||
{
|
{
|
||||||
switch (function) {
|
switch (function) {
|
||||||
|
|
@ -184,14 +188,14 @@ static __always_inline bool cpuid_function_is_indexed(u32 function)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define for_each_possible_hypervisor_cpuid_base(function) \
|
#define for_each_possible_cpuid_base_hypervisor(function) \
|
||||||
for (function = 0x40000000; function < 0x40010000; function += 0x100)
|
for (function = 0x40000000; function < 0x40010000; function += 0x100)
|
||||||
|
|
||||||
static inline u32 hypervisor_cpuid_base(const char *sig, u32 leaves)
|
static inline u32 cpuid_base_hypervisor(const char *sig, u32 leaves)
|
||||||
{
|
{
|
||||||
u32 base, eax, signature[3];
|
u32 base, eax, signature[3];
|
||||||
|
|
||||||
for_each_possible_hypervisor_cpuid_base(base) {
|
for_each_possible_cpuid_base_hypervisor(base) {
|
||||||
cpuid(base, &eax, &signature[0], &signature[1], &signature[2]);
|
cpuid(base, &eax, &signature[0], &signature[1], &signature[2]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -207,4 +211,82 @@ static inline u32 hypervisor_cpuid_base(const char *sig, u32 leaves)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPUID(0x2) parsing:
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cpuid_leaf_0x2() - Return sanitized CPUID(0x2) register output
|
||||||
|
* @regs: Output parameter
|
||||||
|
*
|
||||||
|
* Query CPUID(0x2) and store its output in @regs. Force set any
|
||||||
|
* invalid 1-byte descriptor returned by the hardware to zero (the NULL
|
||||||
|
* cache/TLB descriptor) before returning it to the caller.
|
||||||
|
*
|
||||||
|
* Use for_each_cpuid_0x2_desc() to iterate over the register output in
|
||||||
|
* parsed form.
|
||||||
|
*/
|
||||||
|
static inline void cpuid_leaf_0x2(union leaf_0x2_regs *regs)
|
||||||
|
{
|
||||||
|
cpuid_leaf(0x2, regs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All Intel CPUs must report an iteration count of 1. In case
|
||||||
|
* of bogus hardware, treat all returned descriptors as NULL.
|
||||||
|
*/
|
||||||
|
if (regs->desc[0] != 0x01) {
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
regs->regv[i] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The most significant bit (MSB) of each register must be clear.
|
||||||
|
* If a register is invalid, replace its descriptors with NULL.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (regs->reg[i].invalid)
|
||||||
|
regs->regv[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for_each_cpuid_0x2_desc() - Iterator for parsed CPUID(0x2) descriptors
|
||||||
|
* @_regs: CPUID(0x2) register output, as returned by cpuid_leaf_0x2()
|
||||||
|
* @_ptr: u8 pointer, for macro internal use only
|
||||||
|
* @_desc: Pointer to the parsed CPUID(0x2) descriptor at each iteration
|
||||||
|
*
|
||||||
|
* Loop over the 1-byte descriptors in the passed CPUID(0x2) output registers
|
||||||
|
* @_regs. Provide the parsed information for each descriptor through @_desc.
|
||||||
|
*
|
||||||
|
* To handle cache-specific descriptors, switch on @_desc->c_type. For TLB
|
||||||
|
* descriptors, switch on @_desc->t_type.
|
||||||
|
*
|
||||||
|
* Example usage for cache descriptors::
|
||||||
|
*
|
||||||
|
* const struct leaf_0x2_table *desc;
|
||||||
|
* union leaf_0x2_regs regs;
|
||||||
|
* u8 *ptr;
|
||||||
|
*
|
||||||
|
* cpuid_leaf_0x2(®s);
|
||||||
|
* for_each_cpuid_0x2_desc(regs, ptr, desc) {
|
||||||
|
* switch (desc->c_type) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
#define for_each_cpuid_0x2_desc(_regs, _ptr, _desc) \
|
||||||
|
for (_ptr = &(_regs).desc[1]; \
|
||||||
|
_ptr < &(_regs).desc[16] && (_desc = &cpuid_0x2_table[*_ptr]); \
|
||||||
|
_ptr++)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPUID(0x80000006) parsing:
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline bool cpuid_amd_hygon_has_l3_cache(void)
|
||||||
|
{
|
||||||
|
return cpuid_edx(0x80000006);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _ASM_X86_CPUID_API_H */
|
#endif /* _ASM_X86_CPUID_API_H */
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#ifndef _ASM_X86_CPUID_TYPES_H
|
#ifndef _ASM_X86_CPUID_TYPES_H
|
||||||
#define _ASM_X86_CPUID_TYPES_H
|
#define _ASM_X86_CPUID_TYPES_H
|
||||||
|
|
||||||
|
#include <linux/build_bug.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -29,4 +30,98 @@ enum cpuid_regs_idx {
|
||||||
#define CPUID_LEAF_FREQ 0x16
|
#define CPUID_LEAF_FREQ 0x16
|
||||||
#define CPUID_LEAF_TILE 0x1d
|
#define CPUID_LEAF_TILE 0x1d
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types for CPUID(0x2) parsing:
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct leaf_0x2_reg {
|
||||||
|
u32 : 31,
|
||||||
|
invalid : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
union leaf_0x2_regs {
|
||||||
|
struct leaf_0x2_reg reg[4];
|
||||||
|
u32 regv[4];
|
||||||
|
u8 desc[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Leaf 0x2 1-byte descriptors' cache types
|
||||||
|
* To be used for their mappings at cpuid_0x2_table[]
|
||||||
|
*
|
||||||
|
* Start at 1 since type 0 is reserved for HW byte descriptors which are
|
||||||
|
* not recognized by the kernel; i.e., those without an explicit mapping.
|
||||||
|
*/
|
||||||
|
enum _cache_table_type {
|
||||||
|
CACHE_L1_INST = 1,
|
||||||
|
CACHE_L1_DATA,
|
||||||
|
CACHE_L2,
|
||||||
|
CACHE_L3
|
||||||
|
/* Adjust __TLB_TABLE_TYPE_BEGIN before adding more types */
|
||||||
|
} __packed;
|
||||||
|
#ifndef __CHECKER__
|
||||||
|
static_assert(sizeof(enum _cache_table_type) == 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that leaf 0x2 cache and TLB type values do not intersect,
|
||||||
|
* since they share the same type field at struct cpuid_0x2_table.
|
||||||
|
*/
|
||||||
|
#define __TLB_TABLE_TYPE_BEGIN (CACHE_L3 + 1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Leaf 0x2 1-byte descriptors' TLB types
|
||||||
|
* To be used for their mappings at cpuid_0x2_table[]
|
||||||
|
*/
|
||||||
|
enum _tlb_table_type {
|
||||||
|
TLB_INST_4K = __TLB_TABLE_TYPE_BEGIN,
|
||||||
|
TLB_INST_4M,
|
||||||
|
TLB_INST_2M_4M,
|
||||||
|
TLB_INST_ALL,
|
||||||
|
|
||||||
|
TLB_DATA_4K,
|
||||||
|
TLB_DATA_4M,
|
||||||
|
TLB_DATA_2M_4M,
|
||||||
|
TLB_DATA_4K_4M,
|
||||||
|
TLB_DATA_1G,
|
||||||
|
TLB_DATA_1G_2M_4M,
|
||||||
|
|
||||||
|
TLB_DATA0_4K,
|
||||||
|
TLB_DATA0_4M,
|
||||||
|
TLB_DATA0_2M_4M,
|
||||||
|
|
||||||
|
STLB_4K,
|
||||||
|
STLB_4K_2M,
|
||||||
|
} __packed;
|
||||||
|
#ifndef __CHECKER__
|
||||||
|
static_assert(sizeof(enum _tlb_table_type) == 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Combined parsing table for leaf 0x2 cache and TLB descriptors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct leaf_0x2_table {
|
||||||
|
union {
|
||||||
|
enum _cache_table_type c_type;
|
||||||
|
enum _tlb_table_type t_type;
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
short c_size;
|
||||||
|
short entries;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct leaf_0x2_table cpuid_0x2_table[256];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All of leaf 0x2's one-byte TLB descriptors implies the same number of entries
|
||||||
|
* for their respective TLB types. TLB descriptor 0x63 is an exception: it
|
||||||
|
* implies 4 dTLB entries for 1GB pages and 32 dTLB entries for 2MB or 4MB pages.
|
||||||
|
*
|
||||||
|
* Encode that descriptor's dTLB entry count for 2MB/4MB pages here, as the entry
|
||||||
|
* count for dTLB 1GB pages is already encoded at the cpuid_0x2_table[]'s mapping.
|
||||||
|
*/
|
||||||
|
#define TLB_0x63_2M_4M_ENTRIES 32
|
||||||
|
|
||||||
#endif /* _ASM_X86_CPUID_TYPES_H */
|
#endif /* _ASM_X86_CPUID_TYPES_H */
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ DECLARE_PER_CPU(unsigned long, cpu_dr7);
|
||||||
|
|
||||||
static __always_inline unsigned long native_get_debugreg(int regno)
|
static __always_inline unsigned long native_get_debugreg(int regno)
|
||||||
{
|
{
|
||||||
unsigned long val = 0; /* Damn you, gcc! */
|
unsigned long val;
|
||||||
|
|
||||||
switch (regno) {
|
switch (regno) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -43,7 +43,7 @@ static __always_inline unsigned long native_get_debugreg(int regno)
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
/*
|
/*
|
||||||
* Apply __FORCE_ORDER to DR7 reads to forbid re-ordering them
|
* Use "asm volatile" for DR7 reads to forbid re-ordering them
|
||||||
* with other code.
|
* with other code.
|
||||||
*
|
*
|
||||||
* This is needed because a DR7 access can cause a #VC exception
|
* This is needed because a DR7 access can cause a #VC exception
|
||||||
|
|
@ -55,7 +55,7 @@ static __always_inline unsigned long native_get_debugreg(int regno)
|
||||||
* re-ordered to happen before the call to sev_es_ist_enter(),
|
* re-ordered to happen before the call to sev_es_ist_enter(),
|
||||||
* causing stack recursion.
|
* causing stack recursion.
|
||||||
*/
|
*/
|
||||||
asm volatile("mov %%db7, %0" : "=r" (val) : __FORCE_ORDER);
|
asm volatile("mov %%db7, %0" : "=r" (val));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
|
|
@ -83,15 +83,15 @@ static __always_inline void native_set_debugreg(int regno, unsigned long value)
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
/*
|
/*
|
||||||
* Apply __FORCE_ORDER to DR7 writes to forbid re-ordering them
|
* Use "asm volatile" for DR7 writes to forbid re-ordering them
|
||||||
* with other code.
|
* with other code.
|
||||||
*
|
*
|
||||||
* While is didn't happen with a DR7 write (see the DR7 read
|
* While is didn't happen with a DR7 write (see the DR7 read
|
||||||
* comment above which explains where it happened), add the
|
* comment above which explains where it happened), add the
|
||||||
* __FORCE_ORDER here too to avoid similar problems in the
|
* "asm volatile" here too to avoid similar problems in the
|
||||||
* future.
|
* future.
|
||||||
*/
|
*/
|
||||||
asm volatile("mov %0, %%db7" ::"r" (value), __FORCE_ORDER);
|
asm volatile("mov %0, %%db7" ::"r" (value));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
|
|
@ -169,7 +169,7 @@ static inline unsigned long get_debugctlmsr(void)
|
||||||
if (boot_cpu_data.x86 < 6)
|
if (boot_cpu_data.x86 < 6)
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
rdmsrq(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
||||||
|
|
||||||
return debugctlmsr;
|
return debugctlmsr;
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +180,7 @@ static inline void update_debugctlmsr(unsigned long debugctlmsr)
|
||||||
if (boot_cpu_data.x86 < 6)
|
if (boot_cpu_data.x86 < 6)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
wrmsrq(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _ASM_X86_DEBUGREG_H */
|
#endif /* _ASM_X86_DEBUGREG_H */
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ static inline void arch_exit_work(unsigned long ti_work)
|
||||||
if (unlikely(ti_work & _TIF_IO_BITMAP))
|
if (unlikely(ti_work & _TIF_IO_BITMAP))
|
||||||
tss_update_io_bitmap();
|
tss_update_io_bitmap();
|
||||||
|
|
||||||
fpregs_assert_state_consistent();
|
|
||||||
if (unlikely(ti_work & _TIF_NEED_FPU_LOAD))
|
if (unlikely(ti_work & _TIF_NEED_FPU_LOAD))
|
||||||
switch_fpu_return();
|
switch_fpu_return();
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +60,9 @@ static inline void arch_exit_work(unsigned long ti_work)
|
||||||
static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs,
|
static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs,
|
||||||
unsigned long ti_work)
|
unsigned long ti_work)
|
||||||
{
|
{
|
||||||
if (IS_ENABLED(CONFIG_X86_DEBUG_FPU) || unlikely(ti_work))
|
fpregs_assert_state_consistent();
|
||||||
|
|
||||||
|
if (unlikely(ti_work))
|
||||||
arch_exit_work(ti_work);
|
arch_exit_work(ti_work);
|
||||||
|
|
||||||
fred_update_rsp0();
|
fred_update_rsp0();
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ static inline void fpstate_free(struct fpu *fpu) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* fpstate-related functions which are exported to KVM */
|
/* fpstate-related functions which are exported to KVM */
|
||||||
extern void fpstate_clear_xstate_component(struct fpstate *fps, unsigned int xfeature);
|
extern void fpstate_clear_xstate_component(struct fpstate *fpstate, unsigned int xfeature);
|
||||||
|
|
||||||
extern u64 xstate_get_guest_group_perm(void);
|
extern u64 xstate_get_guest_group_perm(void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
#include <asm/trace/fpu.h>
|
#include <asm/trace/fpu.h>
|
||||||
|
|
||||||
extern void save_fpregs_to_fpstate(struct fpu *fpu);
|
extern void save_fpregs_to_fpstate(struct fpu *fpu);
|
||||||
extern void fpu__drop(struct fpu *fpu);
|
extern void fpu__drop(struct task_struct *tsk);
|
||||||
extern int fpu_clone(struct task_struct *dst, unsigned long clone_flags, bool minimal,
|
extern int fpu_clone(struct task_struct *dst, unsigned long clone_flags, bool minimal,
|
||||||
unsigned long shstk_addr);
|
unsigned long shstk_addr);
|
||||||
extern void fpu_flush_thread(void);
|
extern void fpu_flush_thread(void);
|
||||||
|
|
@ -18,31 +18,25 @@ extern void fpu_flush_thread(void);
|
||||||
/*
|
/*
|
||||||
* FPU state switching for scheduling.
|
* FPU state switching for scheduling.
|
||||||
*
|
*
|
||||||
* This is a two-stage process:
|
* switch_fpu() saves the old state and sets TIF_NEED_FPU_LOAD if
|
||||||
|
* TIF_NEED_FPU_LOAD is not set. This is done within the context
|
||||||
|
* of the old process.
|
||||||
*
|
*
|
||||||
* - switch_fpu_prepare() saves the old state.
|
* Once TIF_NEED_FPU_LOAD is set, it is required to load the
|
||||||
* This is done within the context of the old process.
|
|
||||||
*
|
|
||||||
* - switch_fpu_finish() sets TIF_NEED_FPU_LOAD; the floating point state
|
|
||||||
* will get loaded on return to userspace, or when the kernel needs it.
|
|
||||||
*
|
|
||||||
* If TIF_NEED_FPU_LOAD is cleared then the CPU's FPU registers
|
|
||||||
* are saved in the current thread's FPU register state.
|
|
||||||
*
|
|
||||||
* If TIF_NEED_FPU_LOAD is set then CPU's FPU registers may not
|
|
||||||
* hold current()'s FPU registers. It is required to load the
|
|
||||||
* registers before returning to userland or using the content
|
* registers before returning to userland or using the content
|
||||||
* otherwise.
|
* otherwise.
|
||||||
*
|
*
|
||||||
* The FPU context is only stored/restored for a user task and
|
* The FPU context is only stored/restored for a user task and
|
||||||
* PF_KTHREAD is used to distinguish between kernel and user threads.
|
* PF_KTHREAD is used to distinguish between kernel and user threads.
|
||||||
*/
|
*/
|
||||||
static inline void switch_fpu_prepare(struct task_struct *old, int cpu)
|
static inline void switch_fpu(struct task_struct *old, int cpu)
|
||||||
{
|
{
|
||||||
if (cpu_feature_enabled(X86_FEATURE_FPU) &&
|
if (!test_tsk_thread_flag(old, TIF_NEED_FPU_LOAD) &&
|
||||||
|
cpu_feature_enabled(X86_FEATURE_FPU) &&
|
||||||
!(old->flags & (PF_KTHREAD | PF_USER_WORKER))) {
|
!(old->flags & (PF_KTHREAD | PF_USER_WORKER))) {
|
||||||
struct fpu *old_fpu = &old->thread.fpu;
|
struct fpu *old_fpu = x86_task_fpu(old);
|
||||||
|
|
||||||
|
set_tsk_thread_flag(old, TIF_NEED_FPU_LOAD);
|
||||||
save_fpregs_to_fpstate(old_fpu);
|
save_fpregs_to_fpstate(old_fpu);
|
||||||
/*
|
/*
|
||||||
* The save operation preserved register state, so the
|
* The save operation preserved register state, so the
|
||||||
|
|
@ -50,7 +44,7 @@ static inline void switch_fpu_prepare(struct task_struct *old, int cpu)
|
||||||
* current CPU number in @old_fpu, so the next return
|
* current CPU number in @old_fpu, so the next return
|
||||||
* to user space can avoid the FPU register restore
|
* to user space can avoid the FPU register restore
|
||||||
* when is returns on the same CPU and still owns the
|
* when is returns on the same CPU and still owns the
|
||||||
* context.
|
* context. See fpregs_restore_userregs().
|
||||||
*/
|
*/
|
||||||
old_fpu->last_cpu = cpu;
|
old_fpu->last_cpu = cpu;
|
||||||
|
|
||||||
|
|
@ -58,14 +52,4 @@ static inline void switch_fpu_prepare(struct task_struct *old, int cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Delay loading of the complete FPU state until the return to userland.
|
|
||||||
* PKRU is handled separately.
|
|
||||||
*/
|
|
||||||
static inline void switch_fpu_finish(struct task_struct *new)
|
|
||||||
{
|
|
||||||
if (cpu_feature_enabled(X86_FEATURE_FPU))
|
|
||||||
set_tsk_thread_flag(new, TIF_NEED_FPU_LOAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _ASM_X86_FPU_SCHED_H */
|
#endif /* _ASM_X86_FPU_SCHED_H */
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ enum xfeature {
|
||||||
XFEATURE_RSRVD_COMP_16,
|
XFEATURE_RSRVD_COMP_16,
|
||||||
XFEATURE_XTILE_CFG,
|
XFEATURE_XTILE_CFG,
|
||||||
XFEATURE_XTILE_DATA,
|
XFEATURE_XTILE_DATA,
|
||||||
|
XFEATURE_APX,
|
||||||
|
|
||||||
XFEATURE_MAX,
|
XFEATURE_MAX,
|
||||||
};
|
};
|
||||||
|
|
@ -145,6 +146,7 @@ enum xfeature {
|
||||||
#define XFEATURE_MASK_LBR (1 << XFEATURE_LBR)
|
#define XFEATURE_MASK_LBR (1 << XFEATURE_LBR)
|
||||||
#define XFEATURE_MASK_XTILE_CFG (1 << XFEATURE_XTILE_CFG)
|
#define XFEATURE_MASK_XTILE_CFG (1 << XFEATURE_XTILE_CFG)
|
||||||
#define XFEATURE_MASK_XTILE_DATA (1 << XFEATURE_XTILE_DATA)
|
#define XFEATURE_MASK_XTILE_DATA (1 << XFEATURE_XTILE_DATA)
|
||||||
|
#define XFEATURE_MASK_APX (1 << XFEATURE_APX)
|
||||||
|
|
||||||
#define XFEATURE_MASK_FPSSE (XFEATURE_MASK_FP | XFEATURE_MASK_SSE)
|
#define XFEATURE_MASK_FPSSE (XFEATURE_MASK_FP | XFEATURE_MASK_SSE)
|
||||||
#define XFEATURE_MASK_AVX512 (XFEATURE_MASK_OPMASK \
|
#define XFEATURE_MASK_AVX512 (XFEATURE_MASK_OPMASK \
|
||||||
|
|
@ -303,6 +305,13 @@ struct xtile_data {
|
||||||
struct reg_1024_byte tmm;
|
struct reg_1024_byte tmm;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* State component 19: 8B extended general purpose register.
|
||||||
|
*/
|
||||||
|
struct apx_state {
|
||||||
|
u64 egpr[16];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* State component 10 is supervisor state used for context-switching the
|
* State component 10 is supervisor state used for context-switching the
|
||||||
* PASID state.
|
* PASID state.
|
||||||
|
|
@ -407,9 +416,11 @@ struct fpu_state_perm {
|
||||||
/*
|
/*
|
||||||
* @__state_perm:
|
* @__state_perm:
|
||||||
*
|
*
|
||||||
* This bitmap indicates the permission for state components, which
|
* This bitmap indicates the permission for state components
|
||||||
* are available to a thread group. The permission prctl() sets the
|
* available to a thread group, including both user and supervisor
|
||||||
* enabled state bits in thread_group_leader()->thread.fpu.
|
* components and software-defined bits like FPU_GUEST_PERM_LOCKED.
|
||||||
|
* The permission prctl() sets the enabled state bits in
|
||||||
|
* thread_group_leader()->thread.fpu.
|
||||||
*
|
*
|
||||||
* All run time operations use the per thread information in the
|
* All run time operations use the per thread information in the
|
||||||
* currently active fpu.fpstate which contains the xfeature masks
|
* currently active fpu.fpstate which contains the xfeature masks
|
||||||
|
|
@ -524,13 +535,6 @@ struct fpu_guest {
|
||||||
*/
|
*/
|
||||||
u64 xfeatures;
|
u64 xfeatures;
|
||||||
|
|
||||||
/*
|
|
||||||
* @perm: xfeature bitmap of features which are
|
|
||||||
* permitted to be enabled for the guest
|
|
||||||
* vCPU.
|
|
||||||
*/
|
|
||||||
u64 perm;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @xfd_err: Save the guest value.
|
* @xfd_err: Save the guest value.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue