1 /*
2  * Copyright (C) 2019-2022 Intel Corporation.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 #include <types.h>
7 #include <asm/vm_config.h>
8 #include <asm/guest/vm.h>
9 #include <vacpi.h>
10 #include <logmsg.h>
11 #include <util.h>
12 #include <asm/mmu.h>
13 #include <asm/pgtable.h>
14 #include <asm/guest/ept.h>
15 #include <acrn_common.h>
16 #include <quirks/smbios.h>
17 #include <boot.h>
18 
get_acpi_mod_entry(const char * signature,void * acpi)19 static void *get_acpi_mod_entry(const char *signature, void *acpi)
20 {
21 	struct acpi_table_xsdt *xsdt;
22 	uint32_t i, entry_cnt = 0U;
23 	struct acpi_table_header *header = NULL, *find = NULL;
24 
25 	xsdt = acpi + VIRT_XSDT_ADDR - VIRT_ACPI_DATA_ADDR;
26 	entry_cnt = (xsdt->header.length - sizeof(xsdt->header)) / (sizeof(xsdt->table_offset_entry[0]));
27 
28 	for (i = 0; i < entry_cnt; i++) {
29 		header = acpi + xsdt->table_offset_entry[i] - VIRT_ACPI_DATA_ADDR;
30 		if (strncmp(header->signature, signature, ACPI_NAME_SIZE) == 0) {
31 			find = header;
32 			break;
33 		}
34 	}
35 
36 	return find;
37 }
38 
tpm2_fixup(uint16_t vm_id)39 static void tpm2_fixup(uint16_t vm_id)
40 {
41 	struct acpi_table_tpm2 *vtpm2 = NULL, *tpm2 = NULL;
42 	struct acrn_vm_config *config = get_vm_config(vm_id);
43 	uint8_t checksum, i;
44 	struct acrn_boot_info *abi = get_acrn_boot_info();
45 	struct acrn_mmiodev *dev = NULL;
46 
47 	struct abi_module *mod = get_mod_by_tag(abi, config->acpi_config.acpi_mod_tag);
48 	if (mod != NULL) {
49 		vtpm2 = get_acpi_mod_entry(ACPI_SIG_TPM2, mod->start);
50 		tpm2 = get_acpi_tbl(ACPI_SIG_TPM2);
51 
52 		if (config->pt_tpm2 && (vtpm2 != NULL) && (tpm2 != NULL)) {
53 			for (i = 0U; i < MAX_MMIO_DEV_NUM; i++) {
54 				if (strncmp(config->mmiodevs[i].name, "tpm2", 4) == 0) {
55 					dev = &config->mmiodevs[i];
56 					break;
57 				}
58 			}
59 
60 			if (dev != NULL) {
61 				vtpm2->start_method = tpm2->start_method;
62 				memcpy_s(&vtpm2->start_method_spec_para, sizeof(tpm2->start_method_spec_para),
63 					 &tpm2->start_method_spec_para, sizeof(tpm2->start_method_spec_para));
64 
65 				/* tpm2 has event log */
66 				if (tpm2->laml != 0U) {
67 					vtpm2->header.length = tpm2->header.length;
68 					vtpm2->header.revision = tpm2->header.revision;
69 					vtpm2->laml = tpm2->laml;
70 					vtpm2->lasa = dev->res[1].user_vm_pa;
71 
72 					/* update log buffer length/HPA in vm_config */
73 					dev->res[1].size = tpm2->laml;
74 					dev->res[1].host_pa = tpm2->lasa;
75 				}
76 
77 				/* update checksum */
78 				vtpm2->header.checksum = 0;
79 				checksum = calculate_checksum8(vtpm2, sizeof(struct acpi_table_tpm2));
80 				vtpm2->header.checksum = checksum;
81 			} else {
82 				pr_err("%s, no TPM2 in acrn_vm_config", __func__);
83 			}
84 		}
85 	}
86 }
87 
security_vm_fixup(uint16_t vm_id)88 void security_vm_fixup(uint16_t vm_id)
89 {
90 	struct acrn_vm_config *vm_config = get_vm_config(vm_id);
91 
92 	if ((vm_config->guest_flags & GUEST_FLAG_SECURITY_VM) != 0UL) {
93 		stac();
94 		tpm2_fixup(vm_id);
95 		clac();
96 	}
97 }
98 
99 /* Below are code for SMBIOS passthrough */
100 
101 /* The region after the first 64kb is currently not used. On some platforms,
102  * there might be an TPM2 event log region starting from VIRT_ACPI_NVS_ADDR + 0xB0000.
103  * This table is usually a few KB in size so we have plenty of room here.
104  */
105 #define VIRT_SMBIOS_TABLE_ADDR (VIRT_ACPI_NVS_ADDR + 64 * MEM_1K)
106 /* We hardcode the eps addr to 0xf1000. The ACPI RSDP addr is hardcoded to 0xf2400
107  * so we're safe here. This header is at most 31 bytes.
108  */
109 #define VIRT_SMBIOS_EPS_ADDR    0xf1000UL
110 #define SMBIOS_EPS_SEARCH_START 0xf0000UL
111 #define SMBIOS_EPS_SEARCH_END   0xfffffUL
112 
113 /* For subsequent code, "smbios2" will be specifically refering to 32bit SMBIOS (major version 2),
114  * and "smbios3" will refer to 64-bit SMBIOS (major version 3). "smbios" will be a generic
115  * reference to SMBIOS structure.
116  */
117 struct smbios_info {
118     union {
119         struct smbios2_entry_point eps2;
120         struct smbios3_entry_point eps3;
121     } smbios_eps;
122     size_t smbios_eps_size;
123     void *smbios_table;
124     size_t smbios_table_size;
125 };
126 
efi_search_guid(EFI_SYSTEM_TABLE * tab,EFI_GUID * guid)127 static void *efi_search_guid(EFI_SYSTEM_TABLE *tab, EFI_GUID *guid)
128 {
129 	uint64_t i;
130 	void *pVendortable = NULL;
131 
132 	if (tab != NULL) {
133 		for (i = 0; i < tab->NumberOfTableEntries; i++) {
134 			EFI_CONFIGURATION_TABLE *conf_tab = &tab->ConfigurationTable[i];
135 
136 			if (uuid_is_equal((uint8_t *)&conf_tab->VendorGuid, (uint8_t *)guid)) {
137 				pVendortable = hpa2hva((uint64_t)conf_tab->VendorTable);
138 				break;
139 			}
140 		}
141 	}
142 
143 	return pVendortable;
144 }
145 
get_smbios3_info(struct smbios3_entry_point * eps3,struct smbios_info * si)146 static inline void get_smbios3_info(struct smbios3_entry_point *eps3, struct smbios_info *si)
147 {
148     si->smbios_eps_size = eps3->length;
149     memcpy_s(&si->smbios_eps, si->smbios_eps_size, eps3, si->smbios_eps_size);
150     si->smbios_table = hpa2hva(eps3->st_addr);
151     si->smbios_table_size = eps3->max_st_size;
152 }
153 
get_smbios2_info(struct smbios2_entry_point * eps2,struct smbios_info * si)154 static inline void get_smbios2_info(struct smbios2_entry_point *eps2, struct smbios_info *si)
155 {
156     si->smbios_eps_size = eps2->length;
157     memcpy_s(&si->smbios_eps, si->smbios_eps_size, eps2, si->smbios_eps_size);
158     si->smbios_table = hpa2hva(eps2->st_addr);
159     si->smbios_table_size = eps2->st_length;
160 }
161 
generate_checksum(uint8_t * byte_start,int nbytes,uint8_t * checksum_pos)162 static void generate_checksum(uint8_t *byte_start, int nbytes, uint8_t *checksum_pos)
163 {
164     *checksum_pos = 0;
165     *checksum_pos = -calculate_sum8(byte_start, nbytes);
166 }
167 
efi_search_smbios_eps(EFI_SYSTEM_TABLE * efi_system_table,struct smbios_info * si)168 static int efi_search_smbios_eps(EFI_SYSTEM_TABLE *efi_system_table, struct smbios_info *si)
169 {
170     void *p = NULL;
171     EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
172     EFI_GUID smbios2_guid = SMBIOS2_TABLE_GUID;
173 
174     /* If both are present, SMBIOS3 takes precedence over SMBIOS */
175     stac();
176     p = efi_search_guid(efi_system_table, &smbios3_guid);
177     if (p != NULL) {
178         get_smbios3_info((struct smbios3_entry_point *)p, si);
179     } else {
180         p = efi_search_guid(efi_system_table, &smbios2_guid);
181         if (p != NULL) {
182             get_smbios2_info((struct smbios2_entry_point *)p, si);
183         }
184     }
185     clac();
186 
187     return (p != NULL);
188 }
189 
copy_smbios_to_guest(struct acrn_vm * vm,struct smbios_info * si)190 static int copy_smbios_to_guest(struct acrn_vm *vm, struct smbios_info *si)
191 {
192     int ret = 0;
193     uint64_t gpa;
194 
195     gpa = VIRT_SMBIOS_TABLE_ADDR;
196     ret = copy_to_gpa(vm, si->smbios_table, gpa, si->smbios_table_size);
197     if (ret == 0) {
198         if (strncmp("_SM_", si->smbios_eps.eps2.anchor, 4) == 0) {
199             struct smbios2_entry_point *eps2 = &si->smbios_eps.eps2;
200             eps2->st_addr = (uint32_t)gpa;
201             /* If we wrote generate_checksum(eps->int_anchor, ...), the code scanning tool will
202              * emit warnings about array bound overflow. So use offsetof instead.
203              */
204             generate_checksum((uint8_t *)eps2 + offsetof(struct smbios2_entry_point, int_anchor),
205                 0xf, &eps2->int_checksum);
206             generate_checksum((uint8_t *)eps2, eps2->length, &eps2->checksum);
207         } else if (strncmp("_SM3_", si->smbios_eps.eps3.anchor, 5) == 0) {
208             struct smbios3_entry_point *eps3 = &si->smbios_eps.eps3;
209             eps3->st_addr = (uint32_t)gpa;
210             generate_checksum((uint8_t *)eps3, eps3->length, &eps3->checksum);
211         }
212 
213         gpa = VIRT_SMBIOS_EPS_ADDR;
214         ret = copy_to_gpa(vm, &si->smbios_eps, gpa, si->smbios_eps_size);
215     }
216 
217     return ret;
218 }
219 
is_smbios3_present(uint8_t * p)220 static int is_smbios3_present(uint8_t *p)
221 {
222     return (strncmp((const char *)p, "_SM3_", 5) == 0 &&
223         (calculate_sum8(p, ((struct smbios3_entry_point *)p)->length)) == 0);
224 }
225 
is_smbios2_present(uint8_t * p)226 static int is_smbios2_present(uint8_t *p)
227 {
228     return (strncmp((const char *)p, "_SM_", 4) == 0 &&
229         (calculate_sum8(p, ((struct smbios2_entry_point *)p)->length)) == 0);
230 }
231 
mem_search_smbios_eps(struct smbios_info * si)232 static int mem_search_smbios_eps(struct smbios_info *si)
233 {
234     uint8_t *start = (uint8_t *)hpa2hva(SMBIOS_EPS_SEARCH_START);
235     uint8_t *end = (uint8_t *)hpa2hva(SMBIOS_EPS_SEARCH_END);
236     uint8_t *p;
237 
238     /* per SMBIOS spec 3.2.0, 32-bit (SMBIOS) and 64-bit (SMBIOS3) EPS can be found by searching
239      * for the anchor string on paragraph (16-byte) boundaries within the physical address
240      * 0xf0000-0xfffff.
241      */
242     stac();
243     for (p = start; p < end; p += 16) {
244         if (is_smbios3_present(p)) {
245             get_smbios3_info((struct smbios3_entry_point *)p, si);
246             break;
247         } else if (is_smbios2_present(p)) {
248             get_smbios2_info((struct smbios2_entry_point *)p, si);
249             break;
250         }
251     }
252     clac();
253 
254     return (p < end);
255 }
256 
probe_smbios_table(struct acrn_boot_info * abi,struct smbios_info * si)257 static int probe_smbios_table(struct acrn_boot_info *abi, struct smbios_info *si)
258 {
259     int found = 0;
260 
261     if (boot_from_uefi(abi)) {
262         /* Get from EFI system table */
263         uint64_t efi_system_tab_paddr = ((uint64_t)abi->uefi_info.systab_hi << 32) | ((uint64_t)abi->uefi_info.systab);
264         EFI_SYSTEM_TABLE *efi_system_tab = (EFI_SYSTEM_TABLE *)hpa2hva(efi_system_tab_paddr);
265         found = efi_search_smbios_eps(efi_system_tab, si);
266     } else {
267         /* Search eps in 0xf0000-0xfffff */
268         found = mem_search_smbios_eps(si);
269     }
270     /* Note: Multiboot2 spec specifies an SMBIOS tag where the bootloader can pass an "SMBIOS table" to OS.
271      * As of today GRUB does not support this feature, and if they were to support it, they will do the same
272      * thing we did here, i.e.: either reading from EFI system table or scan 0xf0000~0xfffff. So we will skip
273      * trying to get SMBIOS table from Multiboot2 tag.
274      */
275 
276 #ifdef VM0_TPM_EVENTLOG_BASE_ADDR
277     if (found && (si->smbios_table_size > (VM0_TPM_EVENTLOG_BASE_ADDR - VIRT_SMBIOS_TABLE_ADDR))) {
278         /* Unlikely but we check this anyway */
279         pr_err("Error: SMBIOS table too large. Stop copying SMBIOS info to guest.");
280         found = 0;
281     }
282 #endif
283 
284     return found;
285 }
286 
passthrough_smbios(struct acrn_vm * vm,struct acrn_boot_info * abi)287 void passthrough_smbios(struct acrn_vm *vm, struct acrn_boot_info *abi)
288 {
289     struct smbios_info si;
290 	struct acrn_vm_config *vm_config = get_vm_config(vm->vm_id);
291 
292     if (is_prelaunched_vm(vm) && ((vm_config->guest_flags & GUEST_FLAG_SECURITY_VM) != 0)) {
293         memset(&si, 0, sizeof(struct smbios_info));
294 
295         if (probe_smbios_table(abi, &si)) {
296             if (copy_smbios_to_guest(vm, &si)) {
297                 pr_err("Failed to copy SMBIOS info to vm%d", vm->vm_id);
298             }
299         }
300     }
301 }
302