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>
12 #define TXT_EVTYPE_BASE 0x400
13 #define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14)
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
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;
54 VMX_BASIC_MSR_BITS bits
;
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
{
71 uintptr_t perm_smbase
;
73 size_t smm_save_state_size
;
78 SMM_MONITOR_CTL_MSR_BITS bits
;
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
) {
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
;
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
;
146 (uint64_t)record
->io
.base
+ (uint64_t)record
->io
.length
;
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
))
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))
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
;
181 case MACHINE_SPECIFIC_REG
:
183 // Special case - merge MSR masks in place.
184 if (resource
->msr
.msr_index
!= record
->msr
.msr_index
)
186 record
->msr
.read_mask
|= resource
->msr
.read_mask
;
187 record
->msr
.write_mask
|= resource
->msr
.write_mask
;
193 // If resources are disjoint
194 if ((resource_hi
< record_lo
) || (resource_lo
> record_hi
))
197 // If resource is consumed by record.
198 if ((resource_lo
>= record_lo
) && (resource_hi
<= record_hi
))
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
) {
209 record
->mem
.base
= resource_lo
;
210 record
->mem
.length
= resource_hi
- resource_lo
;
213 case TRAPPED_IO_RANGE
:
214 record
->io
.base
= (uint64_t)resource_lo
;
215 record
->io
.length
= (uint64_t)(resource_hi
- resource_lo
);
218 record
->pci_cfg
.base
= (uint64_t)resource_lo
;
219 record
->pci_cfg
.length
= (uint64_t)(resource_hi
- resource_lo
);
231 * @param Resource A pointer to resource node to be added
233 static void add_single_resource(STM_RSC
*resource
)
237 record
= (STM_RSC
*)m_stm_resources_ptr
;
240 if (record
->header
.rsc_type
== END_OF_RESOURCES
)
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
);
250 // Record is handled inside of procedure - don't adjust.
251 if (handle_single_resource(resource
, record
))
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
;
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
)
281 if (num_entries
== 0)
286 resource
= resource_list
;
288 for (index
= 0; index
< count
; index
++) {
289 if (resource
->header
.rsc_type
== END_OF_RESOURCES
)
291 add_single_resource(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
)
314 // If NumEntries == 0 make it very big. Scan will be terminated by
316 if (num_entries
== 0)
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",
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
))
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
340 if (num_entries
!= 0)
343 // If NumEntries == 0 and list reached end - return
350 "STM: %s - MEM (0x%0llx, 0x%0llx)\n",
353 resource
->mem
.length
);
355 if (resource
->header
.length
!= sizeof(STM_RSC_MEM_DESC
))
360 case TRAPPED_IO_RANGE
:
361 if (resource
->header
.length
!= sizeof(STM_RSC_IO_DESC
))
364 if ((resource
->io
.base
+ resource
->io
.length
) > 0xFFFF)
370 "STM: %s - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n",
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]
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
))
383 sub_index
<= resource
->pci_cfg
.last_node_index
;
385 if ((resource
->pci_cfg
386 .pci_device_path
[sub_index
]
389 || (resource
->pci_cfg
390 .pci_device_path
[sub_index
]
395 if ((resource
->pci_cfg
.base
+ resource
->pci_cfg
.length
)
400 case MACHINE_SPECIFIC_REG
:
401 if (resource
->header
.length
!= sizeof(STM_RSC_MSR_DESC
))
406 printk(BIOS_DEBUG
, "STM: %s - Unknown RscType(%x)\n",
407 __func__
, resource
->header
.rsc_type
);
411 (STM_RSC
*)((void *)resource
+ resource
->header
.length
);
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
)
433 resource
= resource_list
;
435 // If NumEntries == 0 make it very big. Scan will be terminated by
437 if (num_entries
== 0)
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
)
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
494 if (m_stm_resource_size_available
< resource_size
) {
496 "STM: ERROR - not enough space for SMM resource list\n");
497 return -1; // OUT_OF_RESOURCES
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
)
522 return -1; // UNSUPPORTED;
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
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
;
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
)
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
;
606 pagetable_base
+= PTP_SIZE
;
608 for (sub_index
= 0; sub_index
< SIZE_4KB
/ sizeof(*pte
);
610 *pte
= (((index
<< 9) + sub_index
) << 21) | IA32_PG_PS
611 | IA32_PG_RW
| IA32_PG_P
;
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
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
)
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
;