blob: 64a4877ed4b22556cfd87b797a20ba5d76d0346c [file] [log] [blame]
Patrick Georgi593124d2020-05-10 19:44:08 +02001/* SPDX-License-Identifier: BSD-2-Clause */
Eugene Myersae438be2020-01-21 17:01:47 -05002
3#include <console/console.h>
4#include <cpu/x86/cr.h>
5#include <cpu/x86/mp.h>
6#include <cpu/x86/msr.h>
7#include <cpu/x86/cache.h>
8#include <security/intel/stm/SmmStm.h>
Angel Pons002e5e02020-07-20 23:10:21 +02009#include <stdbool.h>
Eugene Myersae438be2020-01-21 17:01:47 -050010#include <string.h>
11
12#define TXT_EVTYPE_BASE 0x400
13#define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14)
14
15#define RDWR_ACCS 3
16#define FULL_ACCS 7
17
18#define SIZE_4KB 0x00001000
19#define SIZE_4MB 0x00400000
20
21#define PTP_SIZE SIZE_4KB
22
23#define IA32_PG_P (1 << 0)
24#define IA32_PG_RW (1 << 1)
25#define IA32_PG_PS (1 << 7)
26
27#define STM_PAGE_SHIFT 12
28#define STM_PAGE_MASK 0xFFF
29#define STM_SIZE_TO_PAGES(a) \
30 (((a) >> STM_PAGE_SHIFT) + (((a)&STM_PAGE_MASK) ? 1 : 0))
31#define STM_PAGES_TO_SIZE(a) ((a) << STM_PAGE_SHIFT)
32
33#define STM_ACCESS_DENIED 15
34#define STM_UNSUPPORTED 3
35
36#define STM_BUFFER_TOO_SMALL 1
37
38#define STM_SM_MONITOR_STATE_ENABLED 1
39
40typedef struct {
Eugene Myersae438be2020-01-21 17:01:47 -050041 uint64_t vmcs_revision_id : 31;
42 uint64_t always_zero : 1;
43 uint64_t vmcs_size : 13;
44 uint64_t reserved1 : 3;
45 uint64_t vmxon_add_width : 1;
46 uint64_t stm_supported : 1;
47 uint64_t vmcs_memory_type : 4;
48 uint64_t in_out_reporting : 1;
49 uint64_t may_clear_defaults : 1;
50 uint64_t reserved2 : 8;
51} VMX_BASIC_MSR_BITS;
52
53typedef union {
54 VMX_BASIC_MSR_BITS bits;
55 uint64_t uint64;
56 msr_t msr;
57} VMX_BASIC_MSR;
58
59typedef struct {
60 uint64_t valid : 1;
61 uint64_t reserved1 : 1;
62 uint64_t vmx_off_blockSmi : 1;
63 uint64_t reserved2 : 9;
64 uint64_t mseg_address : 20;
65 uint64_t reserved3 : 32;
66} SMM_MONITOR_CTL_MSR_BITS;
67
68extern struct mp_state {
69 struct mp_ops ops;
70 int cpu_count;
71 uintptr_t perm_smbase;
72 size_t perm_smsize;
73 size_t smm_save_state_size;
Felix Held207225c2022-07-12 01:02:48 +020074 bool do_smm;
Eugene Myersae438be2020-01-21 17:01:47 -050075} mp_state;
76
77typedef union {
78 SMM_MONITOR_CTL_MSR_BITS bits;
79 uint64_t uint64;
80 msr_t msr;
81} SMM_MONITOR_CTL_MSR;
82
83// Template of STM_RSC_END structure for copying.
84
85STM_RSC_END m_rsc_end_node = {
86 {END_OF_RESOURCES, sizeof(STM_RSC_END)},
87};
88
89uint8_t *m_stm_resources_ptr = NULL;
90uint32_t m_stm_resource_total_size = 0x0;
91uint32_t m_stm_resource_size_used = 0x0;
92uint32_t m_stm_resource_size_available = 0x0;
93
94uint8_t *stm_resource_heap = NULL;
95
96uint32_t m_stm_state = 0;
97
98/*
99 * Handle single Resource to see if it can be merged into Record.
100 *
101 * @param resource A pointer to resource node to be added
102 * @param record A pointer to record node to be merged
103 *
104 * @retval true resource handled
105 * @retval false resource is not handled
106 */
107
108static bool handle_single_resource(STM_RSC *resource, STM_RSC *record)
109{
110 uint64_t resource_lo = 0;
111 uint64_t resource_hi = 0;
112 uint64_t record_lo = 0;
113 uint64_t record_hi = 0;
114
115 // Calling code is responsible for making sure that
116 // Resource->Header.RscType == (*Record)->Header.RscType
117 // thus we use just one of them as switch variable.
118
119 switch (resource->header.rsc_type) {
120 case MEM_RANGE:
121 case MMIO_RANGE:
122 resource_lo = resource->mem.base;
123 resource_hi = resource->mem.base + resource->mem.length;
124 record_lo = record->mem.base;
125 record_hi = record->mem.base + record->mem.length;
126 if (resource->mem.rwx_attributes
127 != record->mem.rwx_attributes) {
128 if ((resource_lo == record_lo)
129 && (resource_hi == record_hi)) {
130 record->mem.rwx_attributes =
131 resource->mem.rwx_attributes
132 | record->mem.rwx_attributes;
133 return true;
134 } else {
135 return false;
136 }
137 }
138 break;
139 case IO_RANGE:
140 case TRAPPED_IO_RANGE:
141 resource_lo = (uint64_t)resource->io.base;
142 resource_hi = (uint64_t)resource->io.base
143 + (uint64_t)resource->io.length;
144 record_lo = (uint64_t)record->io.base;
145 record_hi =
146 (uint64_t)record->io.base + (uint64_t)record->io.length;
147 break;
148 case PCI_CFG_RANGE:
149 if ((resource->pci_cfg.originating_bus_number
150 != record->pci_cfg.originating_bus_number)
151 || (resource->pci_cfg.last_node_index
152 != record->pci_cfg.last_node_index))
153 return false;
154
155 if (memcmp(resource->pci_cfg.pci_device_path,
156 record->pci_cfg.pci_device_path,
157 sizeof(STM_PCI_DEVICE_PATH_NODE)
158 * (resource->pci_cfg.last_node_index + 1))
159 != 0) {
160 return false;
161 }
162 resource_lo = (uint64_t)resource->pci_cfg.base;
163 resource_hi = (uint64_t)resource->pci_cfg.base
164 + (uint64_t)resource->pci_cfg.length;
165 record_lo = (uint64_t)record->pci_cfg.base;
166 record_hi = (uint64_t)record->pci_cfg.base
167 + (uint64_t)record->pci_cfg.length;
168 if (resource->pci_cfg.rw_attributes
169 != record->pci_cfg.rw_attributes) {
170 if ((resource_lo == record_lo)
171 && (resource_hi == record_hi)) {
172 record->pci_cfg.rw_attributes =
173 resource->pci_cfg.rw_attributes
174 | record->pci_cfg.rw_attributes;
175 return true;
176 } else {
177 return false;
178 }
179 }
180 break;
181 case MACHINE_SPECIFIC_REG:
182
183 // Special case - merge MSR masks in place.
184 if (resource->msr.msr_index != record->msr.msr_index)
185 return false;
186 record->msr.read_mask |= resource->msr.read_mask;
187 record->msr.write_mask |= resource->msr.write_mask;
188 return true;
189 default:
190 return false;
191 }
192
193 // If resources are disjoint
194 if ((resource_hi < record_lo) || (resource_lo > record_hi))
195 return false;
196
197 // If resource is consumed by record.
198 if ((resource_lo >= record_lo) && (resource_hi <= record_hi))
199 return true;
200
201 // Resources are overlapping.
202 // Resource and record are merged.
203 resource_lo = (resource_lo < record_lo) ? resource_lo : record_lo;
204 resource_hi = (resource_hi > record_hi) ? resource_hi : record_hi;
205
206 switch (resource->header.rsc_type) {
207 case MEM_RANGE:
208 case MMIO_RANGE:
209 record->mem.base = resource_lo;
210 record->mem.length = resource_hi - resource_lo;
211 break;
212 case IO_RANGE:
213 case TRAPPED_IO_RANGE:
214 record->io.base = (uint64_t)resource_lo;
215 record->io.length = (uint64_t)(resource_hi - resource_lo);
216 break;
217 case PCI_CFG_RANGE:
218 record->pci_cfg.base = (uint64_t)resource_lo;
219 record->pci_cfg.length = (uint64_t)(resource_hi - resource_lo);
220 break;
221 default:
222 return false;
223 }
224
225 return true;
226}
227
228/*
229 * Add resource node.
230 *
231 * @param Resource A pointer to resource node to be added
232 */
233static void add_single_resource(STM_RSC *resource)
234{
235 STM_RSC *record;
236
237 record = (STM_RSC *)m_stm_resources_ptr;
238
239 while (true) {
240 if (record->header.rsc_type == END_OF_RESOURCES)
241 break;
242
243 // Go to next record if resource and record types don't match.
244 if (resource->header.rsc_type != record->header.rsc_type) {
245 record = (STM_RSC *)((void *)record
246 + record->header.length);
247 continue;
248 }
249
250 // Record is handled inside of procedure - don't adjust.
251 if (handle_single_resource(resource, record))
252 return;
253 record = (STM_RSC *)((void *)record + record->header.length);
254 }
255
256 // Add resource to the end of area.
257 memcpy(m_stm_resources_ptr + m_stm_resource_size_used
258 - sizeof(m_rsc_end_node),
259 resource, resource->header.length);
260 memcpy(m_stm_resources_ptr + m_stm_resource_size_used
261 - sizeof(m_rsc_end_node) + resource->header.length,
262 &m_rsc_end_node, sizeof(m_rsc_end_node));
263 m_stm_resource_size_used += resource->header.length;
264 m_stm_resource_size_available =
265 m_stm_resource_total_size - m_stm_resource_size_used;
266}
267
268/*
269 * Add resource list.
270 *
271 * @param resource_list A pointer to resource list to be added
272 * @param num_entries Optional number of entries.
273 * If 0, list must be terminated by END_OF_RESOURCES.
274 */
275static void add_resource(STM_RSC *resource_list, uint32_t num_entries)
276{
277 uint32_t count;
278 uint32_t index;
279 STM_RSC *resource;
280
281 if (num_entries == 0)
282 count = 0xFFFFFFFF;
283 else
284 count = num_entries;
285
286 resource = resource_list;
287
288 for (index = 0; index < count; index++) {
289 if (resource->header.rsc_type == END_OF_RESOURCES)
290 return;
291 add_single_resource(resource);
292 resource =
293 (STM_RSC *)((void *)resource + resource->header.length);
294 }
295}
296
297/*
298 * Validate resource list.
299 *
300 * @param resource_list A pointer to resource list to be added
301 * @param num_entries Optional number of entries.
302 * If 0, list must be terminated by END_OF_RESOURCES.
303 *
304 * @retval true resource valid
305 * @retval false resource invalid
306 */
307static bool validate_resource(STM_RSC *resource_list, uint32_t num_entries)
308{
309 uint32_t count;
310 uint32_t index;
311 STM_RSC *resource;
312 uint32_t sub_index;
313
314 // If NumEntries == 0 make it very big. Scan will be terminated by
315 // END_OF_RESOURCES.
316 if (num_entries == 0)
317 count = 0xFFFFFFFF;
318 else
319 count = num_entries;
320
321 // Start from beginning of resource list.
322 resource = resource_list;
323
324 for (index = 0; index < count; index++) {
325 printk(BIOS_DEBUG, "STM: %s (%u) - RscType(%x) length(0x%x)\n",
326 __func__,
327 index,
328 resource->header.rsc_type,
329 resource->header.length);
330 // Validate resource.
331 switch (resource->header.rsc_type) {
332 case END_OF_RESOURCES:
333 if (resource->header.length != sizeof(STM_RSC_END))
334 return false;
335
336 // If we are passed actual number of resources to add,
337 // END_OF_RESOURCES structure between them is considered
338 // an error. If NumEntries == 0 END_OF_RESOURCES is a
339 // termination.
340 if (num_entries != 0)
341 return false;
342
343 // If NumEntries == 0 and list reached end - return
344 // success.
345 return true;
346
347 case MEM_RANGE:
348 case MMIO_RANGE:
349 printk(BIOS_DEBUG,
350 "STM: %s - MEM (0x%0llx, 0x%0llx)\n",
351 __func__,
352 resource->mem.base,
353 resource->mem.length);
354
355 if (resource->header.length != sizeof(STM_RSC_MEM_DESC))
356 return false;
Eugene Myersae438be2020-01-21 17:01:47 -0500357 break;
358
359 case IO_RANGE:
360 case TRAPPED_IO_RANGE:
361 if (resource->header.length != sizeof(STM_RSC_IO_DESC))
362 return false;
363
364 if ((resource->io.base + resource->io.length) > 0xFFFF)
365 return false;
366 break;
367
368 case PCI_CFG_RANGE:
369 printk(BIOS_DEBUG,
370 "STM: %s - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n",
371 __func__,
372 resource->pci_cfg.originating_bus_number,
373 resource->pci_cfg.last_node_index,
374 resource->pci_cfg.pci_device_path[0].pci_device,
375 resource->pci_cfg.pci_device_path[0]
376 .pci_function);
377 if (resource->header.length
378 != sizeof(STM_RSC_PCI_CFG_DESC)
379 + (sizeof(STM_PCI_DEVICE_PATH_NODE)
380 * resource->pci_cfg.last_node_index))
381 return false;
382 for (sub_index = 0;
383 sub_index <= resource->pci_cfg.last_node_index;
384 sub_index++) {
385 if ((resource->pci_cfg
386 .pci_device_path[sub_index]
387 .pci_device
388 > 0x1F)
389 || (resource->pci_cfg
390 .pci_device_path[sub_index]
391 .pci_function
392 > 7))
393 return false;
394 }
395 if ((resource->pci_cfg.base + resource->pci_cfg.length)
396 > 0x1000)
397 return false;
398 break;
399
400 case MACHINE_SPECIFIC_REG:
401 if (resource->header.length != sizeof(STM_RSC_MSR_DESC))
402 return false;
403 break;
404
405 default:
406 printk(BIOS_DEBUG, "STM: %s - Unknown RscType(%x)\n",
407 __func__, resource->header.rsc_type);
408 return false;
409 }
410 resource =
411 (STM_RSC *)((void *)resource + resource->header.length);
412 }
413 return true;
414}
415
416/*
417 * Get resource list.
418 * EndResource is excluded.
419 *
420 * @param resou rce_list A pointer to resource list to be added
421 * @param num_entries Optional number of entries.
422 * If 0, list must be terminated by END_OF_RESOURCES.
423 *
424 * @retval true resource valid
425 * @retval false resource invalid
426 */
427static uint32_t get_resource_size(STM_RSC *resource_list, uint32_t num_entries)
428{
429 uint32_t count;
430 uint32_t index;
431 STM_RSC *resource;
432
433 resource = resource_list;
434
435 // If NumEntries == 0 make it very big. Scan will be terminated by
436 // END_OF_RESOURCES.
437 if (num_entries == 0)
438 count = 0xFFFFFFFF;
439 else
440 count = num_entries;
441
442 // Start from beginning of resource list.
443 resource = resource_list;
444
445 for (index = 0; index < count; index++) {
446 if (resource->header.rsc_type == END_OF_RESOURCES)
447 break;
448 resource =
449 (STM_RSC *)((void *)resource + resource->header.length);
450 }
451 return (uint32_t)((uint32_t)resource - (uint32_t)resource_list);
452}
453
454/*
455 * Add resources in list to database. Allocate new memory areas as needed.
456 *
457 * @param resource_list A pointer to resource list to be added
458 * @param num_entries Optional number of entries.
459 * If 0, list must be terminated by END_OF_RESOURCES.
460 *
461 * @retval SUCCESS If resources are added
462 * @retval INVALID_PARAMETER If nested procedure detected resource failure
463 * @retval OUT_OF_RESOURCES If nested procedure returned it and we cannot
464 * allocate more areas.
465 */
466int add_pi_resource(STM_RSC *resource_list, uint32_t num_entries)
467{
468 size_t resource_size;
469
470 printk(BIOS_DEBUG, "STM: %s - Enter\n", __func__);
471
472 if (!validate_resource(resource_list, num_entries))
473 return -1; // INVALID_PARAMETER;
474
475 resource_size = get_resource_size(resource_list, num_entries);
Felix Heldbbbdba12020-09-30 19:22:17 +0200476 printk(BIOS_DEBUG, "STM: ResourceSize - 0x%08zx\n", resource_size);
Eugene Myersae438be2020-01-21 17:01:47 -0500477 if (resource_size == 0)
478 return -1; // INVALID_PARAMETER;
479
Elyes Haouasaebccac2022-09-13 09:56:22 +0200480 if (!m_stm_resources_ptr) {
Eugene Myersae438be2020-01-21 17:01:47 -0500481 // Copy EndResource for initialization
482 m_stm_resources_ptr = stm_resource_heap;
483 m_stm_resource_total_size = CONFIG_BIOS_RESOURCE_LIST_SIZE;
484 memset(m_stm_resources_ptr, 0, CONFIG_BIOS_RESOURCE_LIST_SIZE);
485
486 memcpy(m_stm_resources_ptr, &m_rsc_end_node,
487 sizeof(m_rsc_end_node));
488 m_stm_resource_size_used = sizeof(m_rsc_end_node);
489 m_stm_resource_size_available =
490 m_stm_resource_total_size - sizeof(m_rsc_end_node);
491 wbinvd(); // force to memory
492
493 } else {
494 if (m_stm_resource_size_available < resource_size) {
495 printk(BIOS_DEBUG,
496 "STM: ERROR - not enough space for SMM resource list\n");
497 return -1; // OUT_OF_RESOURCES
498 }
499 }
500
501 // Check duplication
502 add_resource(resource_list, num_entries);
503
504 return 0; // SUCCESS;
505}
506
507/*
508 * Delete resources in list to database.
509 *
510 * @param resource_list A pointer to resource list to be deleted
511 * NULL means delete all resources.
512 * @param num_entries Optional number of entries.
513 * If 0, list must be terminated by END_OF_RESOURCES.
514 *
515 * @retval SUCCESS If resources are deleted
516 * @retval INVALID_PARAMETER If nested procedure detected resource failure
517 */
518int32_t delete_pi_resource(STM_RSC *resource_list, uint32_t num_entries)
519{
Elyes Haouasaebccac2022-09-13 09:56:22 +0200520 if (resource_list) {
Eugene Myersae438be2020-01-21 17:01:47 -0500521 // ASSERT (false);
522 return -1; // UNSUPPORTED;
523 }
524
525 // Delete all
526 memcpy(m_stm_resources_ptr, &m_rsc_end_node, sizeof(m_rsc_end_node));
527 m_stm_resource_size_used = sizeof(m_rsc_end_node);
528 m_stm_resource_size_available =
529 m_stm_resource_total_size - sizeof(m_rsc_end_node);
530 return 0; // SUCCESS;
531}
532
533/*
534 * Get BIOS resources.
535 *
536 * @param resource_list A pointer to resource list to be filled
537 * @param resource_size On input it means size of resource list input.
538 * On output it means size of resource list filled,
539 * or the size of resource list to be filled if size is
540 * too small.
541 *
542 * @retval SUCCESS If resources are returned.
543 * @retval BUFFER_TOO_SMALL If resource list buffer is too small to hold
544 * the whole resource list.
545 */
546int32_t get_pi_resource(STM_RSC *resource_list, uint32_t *resource_size)
547{
548 if (*resource_size < m_stm_resource_size_used) {
549 *resource_size = (uint32_t)m_stm_resource_size_used;
550 return -1; // BUFFER_TOO_SMALL;
551 }
552
553 memcpy(resource_list, m_stm_resources_ptr, m_stm_resource_size_used);
554 *resource_size = (uint32_t)m_stm_resource_size_used;
555 return 0; // SUCCESS;
556}
557
558/*
559 * Get 4K page aligned VMCS size.
560 * @return 4K page aligned VMCS size
561 */
562static uint32_t get_vmcs_size(void)
563{
564 uint32_t this_vmcs_size;
565 VMX_BASIC_MSR msr_data64;
566 int stm_support;
567
568 msr_data64.msr = rdmsr(IA32_VMX_BASIC_MSR);
569
570 this_vmcs_size = msr_data64.bits.vmcs_size;
571 stm_support = msr_data64.bits.stm_supported;
572 printk(BIOS_DEBUG, "STM: %s: Size %d StmSupport %d\n", __func__,
573 this_vmcs_size, stm_support);
574
575 // VMCS require 0x1000 alignment
576 this_vmcs_size = STM_PAGES_TO_SIZE(STM_SIZE_TO_PAGES(this_vmcs_size));
577
578 return this_vmcs_size;
579}
580
581/*
582 * Create 4G page table for STM.
583 * 2M PTEs for x86_64 or 2M PTEs for x86_32.
584 *
585 * @param pageable_base The page table base in MSEG
586 */
587void stm_gen_4g_pagetable_x64(uint32_t pagetable_base)
588{
589 uint32_t index;
590 uint32_t sub_index;
591 uint64_t *pde;
592 uint64_t *pte;
593 uint64_t *pml4;
594
595 pml4 = (uint64_t *)(uint32_t)pagetable_base;
596 pagetable_base += PTP_SIZE;
597 *pml4 = pagetable_base | IA32_PG_RW | IA32_PG_P;
598
599 pde = (uint64_t *)(uint32_t)pagetable_base;
600 pagetable_base += PTP_SIZE;
601 pte = (uint64_t *)(uint32_t)pagetable_base;
602
603 for (index = 0; index < 4; index++) {
604 *pde = pagetable_base | IA32_PG_RW | IA32_PG_P;
605 pde++;
606 pagetable_base += PTP_SIZE;
607
608 for (sub_index = 0; sub_index < SIZE_4KB / sizeof(*pte);
609 sub_index++) {
610 *pte = (((index << 9) + sub_index) << 21) | IA32_PG_PS
611 | IA32_PG_RW | IA32_PG_P;
612 pte++;
613 }
614 }
615}
616
617/*
618 * Check STM image size.
619 *
620 * @param stm_image STM image
621 * @param stm_imageSize STM image size
622 *
623 * @retval true check pass
624 * @retval false check fail
625 */
626
627bool stm_check_stm_image(void *stm_image, uint32_t stm_imagesize)
628{
629 uint32_t min_mseg_size;
630 STM_HEADER *stm_header;
631
632 stm_header = (STM_HEADER *)stm_image;
633
634 // Get Minimal required Mseg size
635 min_mseg_size = (STM_PAGES_TO_SIZE(STM_SIZE_TO_PAGES(
636 stm_header->sw_stm_hdr.static_image_size))
637 + stm_header->sw_stm_hdr.additional_dynamic_memory_size
638 + (stm_header->sw_stm_hdr.per_proc_dynamic_memory_size
639 + get_vmcs_size() * 2)
640 * mp_state.cpu_count);
641 if (min_mseg_size < stm_imagesize)
642 min_mseg_size = stm_imagesize;
643
644 if (stm_header->hw_stm_hdr.cr3_offset
645 >= stm_header->sw_stm_hdr.static_image_size) {
Eugene Myersae438be2020-01-21 17:01:47 -0500646 // We will create page table, just in case that SINIT does not
647 // create it.
648 if (min_mseg_size < stm_header->hw_stm_hdr.cr3_offset
649 + STM_PAGES_TO_SIZE(6)) {
650 min_mseg_size = stm_header->hw_stm_hdr.cr3_offset
651 + STM_PAGES_TO_SIZE(6);
652 }
653 }
654
655 // Check if it exceeds MSEG size
656 if (min_mseg_size > CONFIG_MSEG_SIZE)
657 return false;
658
659 return true;
660}
661
662/*
663 * This function return BIOS STM resource.
664 * Produced by SmmStm.
Martin Roth50863da2021-10-01 14:37:30 -0600665 * Consumed by SmmMpService when Init.
Eugene Myersae438be2020-01-21 17:01:47 -0500666 *
667 * @return BIOS STM resource
668 */
669void *get_stm_resource(void)
670{
671 return m_stm_resources_ptr;
672}