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