1350bf64aSZixuan Wang /*
2350bf64aSZixuan Wang * AMD SEV support in kvm-unit-tests
3350bf64aSZixuan Wang *
4350bf64aSZixuan Wang * Copyright (c) 2021, Google Inc
5350bf64aSZixuan Wang *
6350bf64aSZixuan Wang * Authors:
7350bf64aSZixuan Wang * Zixuan Wang <zixuanwang@google.com>
8350bf64aSZixuan Wang *
9350bf64aSZixuan Wang * SPDX-License-Identifier: LGPL-2.0-or-later
10350bf64aSZixuan Wang */
11350bf64aSZixuan Wang
12350bf64aSZixuan Wang #include "amd_sev.h"
13350bf64aSZixuan Wang #include "x86/processor.h"
14b114aa57SZixuan Wang #include "x86/vm.h"
15350bf64aSZixuan Wang
16350bf64aSZixuan Wang static unsigned short amd_sev_c_bit_pos;
17350bf64aSZixuan Wang
amd_sev_enabled(void)18350bf64aSZixuan Wang bool amd_sev_enabled(void)
19350bf64aSZixuan Wang {
20350bf64aSZixuan Wang static bool sev_enabled;
21350bf64aSZixuan Wang static bool initialized = false;
22350bf64aSZixuan Wang
23350bf64aSZixuan Wang /* Check CPUID and MSR for SEV status and store it for future function calls. */
24350bf64aSZixuan Wang if (!initialized) {
25350bf64aSZixuan Wang initialized = true;
26350bf64aSZixuan Wang
27b643ae62SSean Christopherson sev_enabled = this_cpu_has(X86_FEATURE_SEV) &&
28*cebc6ef7SSean Christopherson rdmsr(MSR_SEV_STATUS) & SEV_STATUS_SEV_ENABLED;
29350bf64aSZixuan Wang }
30350bf64aSZixuan Wang
31350bf64aSZixuan Wang return sev_enabled;
32350bf64aSZixuan Wang }
33350bf64aSZixuan Wang
setup_amd_sev(void)34350bf64aSZixuan Wang efi_status_t setup_amd_sev(void)
35350bf64aSZixuan Wang {
36350bf64aSZixuan Wang if (!amd_sev_enabled()) {
37350bf64aSZixuan Wang return EFI_UNSUPPORTED;
38350bf64aSZixuan Wang }
39350bf64aSZixuan Wang
4038147316SSean Christopherson amd_sev_c_bit_pos = this_cpu_property(X86_PROPERTY_SEV_C_BIT);
41350bf64aSZixuan Wang
42350bf64aSZixuan Wang return EFI_SUCCESS;
43350bf64aSZixuan Wang }
44350bf64aSZixuan Wang
amd_sev_es_enabled(void)45bf812590SZixuan Wang bool amd_sev_es_enabled(void)
46bf812590SZixuan Wang {
47bf812590SZixuan Wang static bool sev_es_enabled;
48bf812590SZixuan Wang static bool initialized = false;
49bf812590SZixuan Wang
50bf812590SZixuan Wang if (!initialized) {
51bf812590SZixuan Wang initialized = true;
52bf812590SZixuan Wang
53b643ae62SSean Christopherson sev_es_enabled = amd_sev_enabled() &&
54b643ae62SSean Christopherson this_cpu_has(X86_FEATURE_SEV_ES) &&
55*cebc6ef7SSean Christopherson rdmsr(MSR_SEV_STATUS) & SEV_STATUS_SEV_ES_ENABLED;
56bf812590SZixuan Wang }
57bf812590SZixuan Wang
58bf812590SZixuan Wang return sev_es_enabled;
59bf812590SZixuan Wang }
60bf812590SZixuan Wang
setup_amd_sev_es(void)61706ede18SZixuan Wang efi_status_t setup_amd_sev_es(void)
62706ede18SZixuan Wang {
63706ede18SZixuan Wang struct descriptor_table_ptr idtr;
64706ede18SZixuan Wang idt_entry_t *idt;
65706ede18SZixuan Wang idt_entry_t vc_handler_idt;
66706ede18SZixuan Wang
67706ede18SZixuan Wang if (!amd_sev_es_enabled()) {
68706ede18SZixuan Wang return EFI_UNSUPPORTED;
69706ede18SZixuan Wang }
70706ede18SZixuan Wang
71706ede18SZixuan Wang /*
72706ede18SZixuan Wang * Copy UEFI's #VC IDT entry, so KVM-Unit-Tests can reuse it and does
73706ede18SZixuan Wang * not have to re-implement a #VC handler. Also update the #VC IDT code
74706ede18SZixuan Wang * segment to use KVM-Unit-Tests segments, KERNEL_CS, so that we do not
75706ede18SZixuan Wang * have to copy the UEFI GDT entries into KVM-Unit-Tests GDT.
76706ede18SZixuan Wang *
77706ede18SZixuan Wang * TODO: Reusing UEFI #VC handler is a temporary workaround to simplify
78706ede18SZixuan Wang * the boot up process, the long-term solution is to implement a #VC
79706ede18SZixuan Wang * handler in kvm-unit-tests and load it, so that kvm-unit-tests does
80706ede18SZixuan Wang * not depend on specific UEFI #VC handler implementation.
81706ede18SZixuan Wang */
82706ede18SZixuan Wang sidt(&idtr);
83706ede18SZixuan Wang idt = (idt_entry_t *)idtr.base;
845d80d64dSSean Christopherson vc_handler_idt = idt[VC_VECTOR];
85706ede18SZixuan Wang vc_handler_idt.selector = KERNEL_CS;
865d80d64dSSean Christopherson boot_idt[VC_VECTOR] = vc_handler_idt;
87706ede18SZixuan Wang
88706ede18SZixuan Wang return EFI_SUCCESS;
89706ede18SZixuan Wang }
90706ede18SZixuan Wang
setup_ghcb_pte(pgd_t * page_table)91b114aa57SZixuan Wang void setup_ghcb_pte(pgd_t *page_table)
92b114aa57SZixuan Wang {
93b114aa57SZixuan Wang /*
94b114aa57SZixuan Wang * SEV-ES guest uses GHCB page to communicate with the host. This page
95b114aa57SZixuan Wang * must be unencrypted, i.e. its c-bit should be unset. To do so, this
96b114aa57SZixuan Wang * function searches GHCB's L1 pte, creates corresponding L1 ptes if not
97b114aa57SZixuan Wang * found, and unsets the c-bit of GHCB's L1 pte.
98b114aa57SZixuan Wang */
99b114aa57SZixuan Wang phys_addr_t ghcb_addr, ghcb_base_addr;
100b114aa57SZixuan Wang pteval_t *pte;
101b114aa57SZixuan Wang
102b114aa57SZixuan Wang /* Read the current GHCB page addr */
103*cebc6ef7SSean Christopherson ghcb_addr = rdmsr(MSR_SEV_ES_GHCB);
104b114aa57SZixuan Wang
105b114aa57SZixuan Wang /* Search Level 1 page table entry for GHCB page */
106b114aa57SZixuan Wang pte = get_pte_level(page_table, (void *)ghcb_addr, 1);
107b114aa57SZixuan Wang
108b114aa57SZixuan Wang /* Create Level 1 pte for GHCB page if not found */
109b114aa57SZixuan Wang if (pte == NULL) {
110b114aa57SZixuan Wang /* Find Level 2 page base address */
111b114aa57SZixuan Wang ghcb_base_addr = ghcb_addr & ~(LARGE_PAGE_SIZE - 1);
112b114aa57SZixuan Wang /* Install Level 1 ptes */
113b114aa57SZixuan Wang install_pages(page_table, ghcb_base_addr, LARGE_PAGE_SIZE, (void *)ghcb_base_addr);
114b114aa57SZixuan Wang /* Find Level 2 pte, set as 4KB pages */
115b114aa57SZixuan Wang pte = get_pte_level(page_table, (void *)ghcb_addr, 2);
116b114aa57SZixuan Wang assert(pte);
117b114aa57SZixuan Wang *pte &= ~(PT_PAGE_SIZE_MASK);
118b114aa57SZixuan Wang /* Find Level 1 GHCB pte */
119b114aa57SZixuan Wang pte = get_pte_level(page_table, (void *)ghcb_addr, 1);
120b114aa57SZixuan Wang assert(pte);
121b114aa57SZixuan Wang }
122b114aa57SZixuan Wang
123b114aa57SZixuan Wang /* Unset c-bit in Level 1 GHCB pte */
124b114aa57SZixuan Wang *pte &= ~(get_amd_sev_c_bit_mask());
125b114aa57SZixuan Wang }
126b114aa57SZixuan Wang
get_amd_sev_c_bit_mask(void)127350bf64aSZixuan Wang unsigned long long get_amd_sev_c_bit_mask(void)
128350bf64aSZixuan Wang {
129350bf64aSZixuan Wang if (amd_sev_enabled()) {
130350bf64aSZixuan Wang return 1ull << amd_sev_c_bit_pos;
131350bf64aSZixuan Wang } else {
132350bf64aSZixuan Wang return 0;
133350bf64aSZixuan Wang }
134350bf64aSZixuan Wang }
13530203ea5SZixuan Wang
get_amd_sev_addr_upperbound(void)13630203ea5SZixuan Wang unsigned long long get_amd_sev_addr_upperbound(void)
13730203ea5SZixuan Wang {
13830203ea5SZixuan Wang if (amd_sev_enabled()) {
13930203ea5SZixuan Wang return amd_sev_c_bit_pos - 1;
14030203ea5SZixuan Wang } else {
14130203ea5SZixuan Wang /* Default memory upper bound */
14230203ea5SZixuan Wang return PT_ADDR_UPPER_BOUND_DEFAULT;
14330203ea5SZixuan Wang }
14430203ea5SZixuan Wang }
145