drivers/wifi: Remove unnecessary data structure copy
[coreboot2.git] / src / security / intel / stm / SmmStm.c
blob64a4877ed4b22556cfd87b797a20ba5d76d0346c
1 /* SPDX-License-Identifier: BSD-2-Clause */
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>
9 #include <stdbool.h>
10 #include <string.h>
12 #define TXT_EVTYPE_BASE 0x400
13 #define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14)
15 #define RDWR_ACCS 3
16 #define FULL_ACCS 7
18 #define SIZE_4KB 0x00001000
19 #define SIZE_4MB 0x00400000
21 #define PTP_SIZE SIZE_4KB
23 #define IA32_PG_P (1 << 0)
24 #define IA32_PG_RW (1 << 1)
25 #define IA32_PG_PS (1 << 7)
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)
33 #define STM_ACCESS_DENIED 15
34 #define STM_UNSUPPORTED 3
36 #define STM_BUFFER_TOO_SMALL 1
38 #define STM_SM_MONITOR_STATE_ENABLED 1
40 typedef struct {
41 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;
53 typedef union {
54 VMX_BASIC_MSR_BITS bits;
55 uint64_t uint64;
56 msr_t msr;
57 } VMX_BASIC_MSR;
59 typedef 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;
68 extern 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;
74 bool do_smm;
75 } mp_state;
77 typedef union {
78 SMM_MONITOR_CTL_MSR_BITS bits;
79 uint64_t uint64;
80 msr_t msr;
81 } SMM_MONITOR_CTL_MSR;
83 // Template of STM_RSC_END structure for copying.
85 STM_RSC_END m_rsc_end_node = {
86 {END_OF_RESOURCES, sizeof(STM_RSC_END)},
89 uint8_t *m_stm_resources_ptr = NULL;
90 uint32_t m_stm_resource_total_size = 0x0;
91 uint32_t m_stm_resource_size_used = 0x0;
92 uint32_t m_stm_resource_size_available = 0x0;
94 uint8_t *stm_resource_heap = NULL;
96 uint32_t m_stm_state = 0;
99 * Handle single Resource to see if it can be merged into Record.
101 * @param resource A pointer to resource node to be added
102 * @param record A pointer to record node to be merged
104 * @retval true resource handled
105 * @retval false resource is not handled
108 static bool handle_single_resource(STM_RSC *resource, STM_RSC *record)
110 uint64_t resource_lo = 0;
111 uint64_t resource_hi = 0;
112 uint64_t record_lo = 0;
113 uint64_t record_hi = 0;
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.
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;
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;
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;
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;
180 break;
181 case MACHINE_SPECIFIC_REG:
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;
193 // If resources are disjoint
194 if ((resource_hi < record_lo) || (resource_lo > record_hi))
195 return false;
197 // If resource is consumed by record.
198 if ((resource_lo >= record_lo) && (resource_hi <= record_hi))
199 return true;
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;
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;
225 return true;
229 * Add resource node.
231 * @param Resource A pointer to resource node to be added
233 static void add_single_resource(STM_RSC *resource)
235 STM_RSC *record;
237 record = (STM_RSC *)m_stm_resources_ptr;
239 while (true) {
240 if (record->header.rsc_type == END_OF_RESOURCES)
241 break;
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;
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);
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;
269 * Add resource list.
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.
275 static void add_resource(STM_RSC *resource_list, uint32_t num_entries)
277 uint32_t count;
278 uint32_t index;
279 STM_RSC *resource;
281 if (num_entries == 0)
282 count = 0xFFFFFFFF;
283 else
284 count = num_entries;
286 resource = resource_list;
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);
298 * Validate resource list.
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.
304 * @retval true resource valid
305 * @retval false resource invalid
307 static bool validate_resource(STM_RSC *resource_list, uint32_t num_entries)
309 uint32_t count;
310 uint32_t index;
311 STM_RSC *resource;
312 uint32_t sub_index;
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;
321 // Start from beginning of resource list.
322 resource = resource_list;
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;
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;
343 // If NumEntries == 0 and list reached end - return
344 // success.
345 return true;
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);
355 if (resource->header.length != sizeof(STM_RSC_MEM_DESC))
356 return false;
357 break;
359 case IO_RANGE:
360 case TRAPPED_IO_RANGE:
361 if (resource->header.length != sizeof(STM_RSC_IO_DESC))
362 return false;
364 if ((resource->io.base + resource->io.length) > 0xFFFF)
365 return false;
366 break;
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;
395 if ((resource->pci_cfg.base + resource->pci_cfg.length)
396 > 0x1000)
397 return false;
398 break;
400 case MACHINE_SPECIFIC_REG:
401 if (resource->header.length != sizeof(STM_RSC_MSR_DESC))
402 return false;
403 break;
405 default:
406 printk(BIOS_DEBUG, "STM: %s - Unknown RscType(%x)\n",
407 __func__, resource->header.rsc_type);
408 return false;
410 resource =
411 (STM_RSC *)((void *)resource + resource->header.length);
413 return true;
417 * Get resource list.
418 * EndResource is excluded.
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.
424 * @retval true resource valid
425 * @retval false resource invalid
427 static uint32_t get_resource_size(STM_RSC *resource_list, uint32_t num_entries)
429 uint32_t count;
430 uint32_t index;
431 STM_RSC *resource;
433 resource = resource_list;
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;
442 // Start from beginning of resource list.
443 resource = resource_list;
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);
451 return (uint32_t)((uint32_t)resource - (uint32_t)resource_list);
455 * Add resources in list to database. Allocate new memory areas as needed.
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.
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.
466 int add_pi_resource(STM_RSC *resource_list, uint32_t num_entries)
468 size_t resource_size;
470 printk(BIOS_DEBUG, "STM: %s - Enter\n", __func__);
472 if (!validate_resource(resource_list, num_entries))
473 return -1; // INVALID_PARAMETER;
475 resource_size = get_resource_size(resource_list, num_entries);
476 printk(BIOS_DEBUG, "STM: ResourceSize - 0x%08zx\n", resource_size);
477 if (resource_size == 0)
478 return -1; // INVALID_PARAMETER;
480 if (!m_stm_resources_ptr) {
481 // 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);
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
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
501 // Check duplication
502 add_resource(resource_list, num_entries);
504 return 0; // SUCCESS;
508 * Delete resources in list to database.
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.
515 * @retval SUCCESS If resources are deleted
516 * @retval INVALID_PARAMETER If nested procedure detected resource failure
518 int32_t delete_pi_resource(STM_RSC *resource_list, uint32_t num_entries)
520 if (resource_list) {
521 // ASSERT (false);
522 return -1; // UNSUPPORTED;
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;
534 * Get BIOS resources.
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.
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.
546 int32_t get_pi_resource(STM_RSC *resource_list, uint32_t *resource_size)
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;
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;
559 * Get 4K page aligned VMCS size.
560 * @return 4K page aligned VMCS size
562 static uint32_t get_vmcs_size(void)
564 uint32_t this_vmcs_size;
565 VMX_BASIC_MSR msr_data64;
566 int stm_support;
568 msr_data64.msr = rdmsr(IA32_VMX_BASIC_MSR);
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);
575 // VMCS require 0x1000 alignment
576 this_vmcs_size = STM_PAGES_TO_SIZE(STM_SIZE_TO_PAGES(this_vmcs_size));
578 return this_vmcs_size;
582 * Create 4G page table for STM.
583 * 2M PTEs for x86_64 or 2M PTEs for x86_32.
585 * @param pageable_base The page table base in MSEG
587 void stm_gen_4g_pagetable_x64(uint32_t pagetable_base)
589 uint32_t index;
590 uint32_t sub_index;
591 uint64_t *pde;
592 uint64_t *pte;
593 uint64_t *pml4;
595 pml4 = (uint64_t *)(uint32_t)pagetable_base;
596 pagetable_base += PTP_SIZE;
597 *pml4 = pagetable_base | IA32_PG_RW | IA32_PG_P;
599 pde = (uint64_t *)(uint32_t)pagetable_base;
600 pagetable_base += PTP_SIZE;
601 pte = (uint64_t *)(uint32_t)pagetable_base;
603 for (index = 0; index < 4; index++) {
604 *pde = pagetable_base | IA32_PG_RW | IA32_PG_P;
605 pde++;
606 pagetable_base += PTP_SIZE;
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++;
618 * Check STM image size.
620 * @param stm_image STM image
621 * @param stm_imageSize STM image size
623 * @retval true check pass
624 * @retval false check fail
627 bool stm_check_stm_image(void *stm_image, uint32_t stm_imagesize)
629 uint32_t min_mseg_size;
630 STM_HEADER *stm_header;
632 stm_header = (STM_HEADER *)stm_image;
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;
644 if (stm_header->hw_stm_hdr.cr3_offset
645 >= stm_header->sw_stm_hdr.static_image_size) {
646 // 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);
655 // Check if it exceeds MSEG size
656 if (min_mseg_size > CONFIG_MSEG_SIZE)
657 return false;
659 return true;
663 * This function return BIOS STM resource.
664 * Produced by SmmStm.
665 * Consumed by SmmMpService when Init.
667 * @return BIOS STM resource
669 void *get_stm_resource(void)
671 return m_stm_resources_ptr;