blob: e3e2f5c469529c6fbfaa8ba612b8d78f97067b39 [file] [log] [blame]
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <arch/mmio.h>
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +01004#include <console/console.h>
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +01005#include <cbfs.h>
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +01006#include <cpu/x86/cr.h>
Angel Pons52082be2020-10-05 12:34:29 +02007#include <cpu/x86/lapic.h>
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +01008#include <cpu/x86/mp.h>
Felix Held7b6a3972021-07-09 23:08:07 +02009#include <cpu/x86/msr.h>
Angel Pons038cef92020-10-14 17:58:36 +020010#include <cpu/x86/mtrr.h>
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +010011#include <lib.h>
12#include <smp/node.h>
Angel Pons52082be2020-10-05 12:34:29 +020013#include <string.h>
14#include <types.h>
Angel Pons1fc43aa2020-08-04 17:54:01 +020015
16#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA)
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +010017#include <soc/intel/common/reset.h>
Angel Pons1fc43aa2020-08-04 17:54:01 +020018#else
19#include <cf9_reset.h>
20#endif
21
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +010022#include "txt.h"
23#include "txt_register.h"
24#include "txt_getsec.h"
25
Angel Pons1fc43aa2020-08-04 17:54:01 +020026/* Usual security practice: if an unexpected error happens, reboot */
27static void __noreturn txt_reset_platform(void)
28{
29#if CONFIG(SOC_INTEL_COMMON_BLOCK_SA)
30 global_reset();
31#else
32 full_reset();
33#endif
34}
35
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +010036/**
37 * Dump the ACM error status bits.
38 *
39 * @param acm_error The status register to dump
40 * @return -1 on error (register is not valid)
41 * 0 on error (Class > 0 and Major > 0)
42 * 1 on success (Class == 0 and Major == 0 and progress > 0)
43 */
44int intel_txt_log_acm_error(const uint32_t acm_error)
45{
46 if (!(acm_error & ACMERROR_TXT_VALID))
47 return -1;
48
49 const uint8_t type = (acm_error & ACMERROR_TXT_TYPE_CODE)
50 >> ACMERROR_TXT_TYPE_SHIFT;
51
52 switch (type) {
53 case ACMERROR_TXT_AC_MODULE_TYPE_BIOS:
54 printk(BIOS_ERR, "BIOSACM");
55 break;
56 case ACMERROR_TXT_AC_MODULE_TYPE_SINIT:
57 printk(BIOS_ERR, "SINIT");
58 break;
59 default:
60 printk(BIOS_ERR, "ACM");
61 break;
62 }
63 printk(BIOS_ERR, ": Error code valid\n");
64
65 if (acm_error & ACMERROR_TXT_EXTERNAL)
66 printk(BIOS_ERR, " Caused by: External\n");
67 else
68 printk(BIOS_ERR, " Caused by: Processor\n");
69
70 const uint32_t class = (acm_error & ACMERROR_TXT_CLASS_CODE)
71 >> ACMERROR_TXT_CLASS_SHIFT;
72 const uint32_t major = (acm_error & ACMERROR_TXT_MAJOR_CODE)
73 >> ACMERROR_TXT_MAJOR_SHIFT;
74 const uint32_t minor = (acm_error & ACMERROR_TXT_MINOR_CODE)
75 >> ACMERROR_TXT_MINOR_SHIFT;
76 const uint32_t progress = (acm_error & ACMERROR_TXT_PROGRESS_CODE)
77 >> ACMERROR_TXT_PROGRESS_SHIFT;
78
79 if (!minor) {
80 if (class == 0 && major == 0 && progress > 0) {
81 printk(BIOS_ERR, " Execution successful\n");
82 printk(BIOS_ERR, " Progress code 0x%x\n", progress);
83 } else {
84 printk(BIOS_ERR, " Error Class: %x\n", class);
85 printk(BIOS_ERR, " Error: %x.%x\n", major, progress);
86 }
87 } else {
88 printk(BIOS_ERR, " ACM didn't start\n");
89 printk(BIOS_ERR, " Error Type: 0x%x\n", acm_error & 0xffffff);
90 return -1;
91 }
92
93 return (acm_error & ACMERROR_TXT_EXTERNAL) && class == 0 && major == 0 && progress > 0;
94}
95
96void intel_txt_log_spad(void)
97{
98 const uint64_t acm_status = read64((void *)TXT_SPAD);
99
100 printk(BIOS_INFO, "TXT-STS: ACM verification ");
101
102 if (acm_status & ACMSTS_VERIFICATION_ERROR)
103 printk(BIOS_INFO, "error\n");
104 else
105 printk(BIOS_INFO, "successful\n");
106
107 printk(BIOS_INFO, "TXT-STS: IBB ");
108
109 if (acm_status & ACMSTS_IBB_MEASURED)
110 printk(BIOS_INFO, "measured\n");
111 else
112 printk(BIOS_INFO, "not measured\n");
113
114 printk(BIOS_INFO, "TXT-STS: TXT is ");
115
116 if (acm_status & ACMSTS_TXT_DISABLED)
117 printk(BIOS_INFO, "disabled\n");
118 else
119 printk(BIOS_INFO, "not disabled\n");
120
121 printk(BIOS_INFO, "TXT-STS: BIOS is ");
122
123 if (acm_status & ACMSTS_BIOS_TRUSTED)
124 printk(BIOS_INFO, "trusted\n");
125 else
126 printk(BIOS_INFO, "not trusted\n");
127}
128
129/* Returns true if secrets might be in memory */
130bool intel_txt_memory_has_secrets(void)
131{
132 bool ret;
133 if (!CONFIG(INTEL_TXT))
134 return false;
135
136 ret = (read8((void *)TXT_ESTS) & TXT_ESTS_WAKE_ERROR_STS) ||
137 (read64((void *)TXT_E2STS) & TXT_E2STS_SECRET_STS);
138
139 if (ret)
140 printk(BIOS_CRIT, "TXT-STS: Secrets in memory!\n");
141 return ret;
142}
143
144static struct acm_info_table *find_info_table(const void *ptr)
145{
146 const struct acm_header_v0 *acm_header = (struct acm_header_v0 *)ptr;
147
148 return (struct acm_info_table *)(ptr +
149 (acm_header->header_len + acm_header->scratch_size) * sizeof(uint32_t));
150}
151
152/**
Martin Roth50863da2021-10-01 14:37:30 -0600153 * Validate that the provided ACM is usable on this platform.
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100154 */
155static int validate_acm(const void *ptr)
156{
157 const struct acm_header_v0 *acm_header = (struct acm_header_v0 *)ptr;
158 uint32_t max_size_acm_area = 0;
159
160 if (acm_header->module_type != CHIPSET_ACM)
161 return ACM_E_TYPE_NOT_MATCH;
162
163 /* Seems inconsistent across generations. */
164 if (acm_header->module_sub_type != 0 && acm_header->module_sub_type != 1)
165 return ACM_E_MODULE_SUB_TYPE_WRONG;
166
167 if (acm_header->module_vendor != INTEL_ACM_VENDOR)
168 return ACM_E_MODULE_VENDOR_NOT_INTEL;
169
John Zhao536e9652020-08-04 11:29:08 -0700170 if (acm_header->size == 0)
171 return ACM_E_SIZE_INCORRECT;
172
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100173 if (((acm_header->header_len + acm_header->scratch_size) * sizeof(uint32_t) +
174 sizeof(struct acm_info_table)) > (acm_header->size & 0xffffff) * sizeof(uint32_t)) {
175 return ACM_E_SIZE_INCORRECT;
176 }
177
178 if (!getsec_parameter(NULL, NULL, &max_size_acm_area, NULL, NULL, NULL))
179 return ACM_E_CANT_CALL_GETSEC;
180
181 /*
182 * Causes #GP if acm_header->size > processor internal authenticated
183 * code area capacity.
184 * SAFER MODE EXTENSIONS REFERENCE.
185 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
186 */
187 const size_t acm_len = 1UL << log2_ceil((acm_header->size & 0xffffff) << 2);
188 if (max_size_acm_area < acm_len) {
189 printk(BIOS_ERR, "TEE-TXT: BIOS ACM doesn't fit into AC execution region\n");
190 return ACM_E_NOT_FIT_INTO_CPU_ACM_MEM;
191 }
192
193 struct acm_info_table *info = find_info_table(ptr);
194 if (!info)
195 return ACM_E_NO_INFO_TABLE;
196 if (info->chipset_acm_type != BIOS)
197 return ACM_E_NOT_BIOS_ACM;
198
199 static const u8 acm_uuid[] = {
200 0xaa, 0x3a, 0xc0, 0x7f, 0xa7, 0x46, 0xdb, 0x18,
201 0x2e, 0xac, 0x69, 0x8f, 0x8d, 0x41, 0x7f, 0x5a,
202 };
203 if (memcmp(acm_uuid, info->uuid, sizeof(acm_uuid)) != 0)
204 return ACM_E_UUID_NOT_MATCH;
205
206 if ((acm_header->flags & ACM_FORMAT_FLAGS_DEBUG) ==
207 (read64((void *)TXT_VER_FSBIF) & TXT_VER_PRODUCTION_FUSED))
208 return ACM_E_PLATFORM_IS_NOT_PROD;
209
210 return 0;
211}
212
213/*
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200214 * Prepare to run the BIOS ACM: mmap it from the CBFS and verify that it
215 * can be launched. Returns pointer to ACM on success, NULL on failure.
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100216 */
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200217static void *intel_txt_prepare_bios_acm(struct region_device *acm, size_t *acm_len)
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100218{
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200219 void *acm_data = NULL;
220
221 if (!acm || !acm_len)
222 return NULL;
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100223
Julius Werner77639e42021-02-05 16:51:25 -0800224 acm_data = cbfs_map(CONFIG_INTEL_TXT_CBFS_BIOS_ACM, acm_len);
225 if (!acm_data) {
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100226 printk(BIOS_ERR, "TEE-TXT: Couldn't locate BIOS ACM in CBFS.\n");
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200227 return NULL;
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100228 }
229
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100230 /*
231 * CPU enforces only 4KiB alignment.
232 * Chapter A.1.1
233 * Intel TXT Software Development Guide (Document: 315168-015)
234 */
235 if (!IS_ALIGNED((uintptr_t)acm_data, 4096)) {
236 printk(BIOS_ERR, "TEE-TXT: BIOS ACM isn't mapped at page boundary.\n");
Julius Werner77639e42021-02-05 16:51:25 -0800237 cbfs_unmap(acm_data);
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200238 return NULL;
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100239 }
240
241 /*
242 * Causes #GP if not multiple of 64.
243 * SAFER MODE EXTENSIONS REFERENCE.
244 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
245 */
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200246 if (!IS_ALIGNED(*acm_len, 64)) {
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100247 printk(BIOS_ERR, "TEE-TXT: BIOS ACM size isn't multiple of 64.\n");
Julius Werner77639e42021-02-05 16:51:25 -0800248 cbfs_unmap(acm_data);
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200249 return NULL;
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100250 }
251
252 /*
253 * The ACM should be aligned to it's size, but that's not possible, as
254 * some ACMs are not power of two. Use the next power of two for verification.
255 */
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200256 if (!IS_ALIGNED((uintptr_t)acm_data, (1UL << log2_ceil(*acm_len)))) {
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100257 printk(BIOS_ERR, "TEE-TXT: BIOS ACM isn't aligned to its size.\n");
Julius Werner77639e42021-02-05 16:51:25 -0800258 cbfs_unmap(acm_data);
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200259 return NULL;
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100260 }
261
Angel Pons038cef92020-10-14 17:58:36 +0200262 /*
263 * When setting up the MTRRs to cache the BIOS ACM, one must cache less than
264 * a page (4 KiB) of unused memory after the BIOS ACM. On Haswell, failure
265 * to do so will cause a TXT reset with Class Code 5, Major Error Code 2.
266 */
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200267 if (popcnt(ALIGN_UP(*acm_len, 4096)) > get_var_mtrr_count()) {
Angel Pons038cef92020-10-14 17:58:36 +0200268 printk(BIOS_ERR, "TEE-TXT: Not enough MTRRs to cache this BIOS ACM's size.\n");
Julius Werner77639e42021-02-05 16:51:25 -0800269 cbfs_unmap(acm_data);
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200270 return NULL;
Angel Pons038cef92020-10-14 17:58:36 +0200271 }
272
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100273 if (CONFIG(INTEL_TXT_LOGGING))
274 txt_dump_acm_info(acm_data);
275
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200276 const int ret = validate_acm(acm_data);
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100277 if (ret < 0) {
278 printk(BIOS_ERR, "TEE-TXT: Validation of ACM failed with: %d\n", ret);
Julius Werner77639e42021-02-05 16:51:25 -0800279 cbfs_unmap(acm_data);
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200280 return NULL;
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100281 }
282
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200283 return acm_data;
284}
285
Angel Pons6c49f402020-08-28 02:02:00 +0200286#define MCU_BASE_ADDR (TXT_BASE + 0x278)
287#define BIOACM_ADDR (TXT_BASE + 0x27c)
288#define APINIT_ADDR (TXT_BASE + 0x290)
289#define SEMAPHORE (TXT_BASE + 0x294)
290
291/* Returns on failure, resets the computer on success */
292void intel_txt_run_sclean(void)
293{
294 struct region_device acm;
295 size_t acm_len;
296
297 void *acm_data = intel_txt_prepare_bios_acm(&acm, &acm_len);
298
299 if (!acm_data)
300 return;
301
302 /* FIXME: Do we need to program these two? */
303 //write32((void *)MCU_BASE_ADDR, 0xffe1a990);
304 //write32((void *)APINIT_ADDR, 0xfffffff0);
305
306 write32((void *)BIOACM_ADDR, (uintptr_t)acm_data);
307 write32((void *)SEMAPHORE, 0);
308
309 /*
310 * The time SCLEAN will take depends on the installed RAM size.
311 * On Haswell with 8 GiB of DDR3, it takes five or ten minutes. (rough estimate)
312 */
313 printk(BIOS_ALERT, "TEE-TXT: Invoking SCLEAN. This can take several minutes.\n");
314
315 /*
316 * Invoke the BIOS ACM. If successful, the system will reset with memory unlocked.
317 */
318 getsec_sclean((uintptr_t)acm_data, acm_len);
319
320 /*
321 * However, if this function returns, the BIOS ACM could not be invoked. This is bad.
322 */
323 printk(BIOS_CRIT, "TEE-TXT: getsec_sclean could not launch the BIOS ACM.\n");
324
325 rdev_munmap(&acm, acm_data);
326}
327
Angel Pons7b4d67cf2020-10-20 14:17:42 +0200328/*
329 * Test all bits for TXT execution.
330 *
331 * @return 0 on success
332 */
333int intel_txt_run_bios_acm(const u8 input_params)
334{
335 struct region_device acm;
336 size_t acm_len;
337
338 void *acm_data = intel_txt_prepare_bios_acm(&acm, &acm_len);
339
340 if (!acm_data)
341 return -1;
342
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100343 /* Call into assembly which invokes the referenced ACM */
344 getsec_enteraccs(input_params, (uintptr_t)acm_data, acm_len);
345
346 rdev_munmap(&acm, acm_data);
347
348 const uint64_t acm_status = read64((void *)TXT_SPAD);
349 if (acm_status & ACMERROR_TXT_VALID) {
350 printk(BIOS_ERR, "TEE-TXT: FATAL ACM launch error !\n");
351 /*
352 * WARNING !
353 * To clear TXT.BIOSACM.ERRORCODE you must issue a cold reboot!
354 */
355 intel_txt_log_acm_error(read32((void *)TXT_BIOSACM_ERRORCODE));
356 return -1;
357 }
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100358
359 return 0;
360}
361
362 /* Returns true if cond is not met */
363static bool check_precondition(const int cond)
364{
365 printk(BIOS_DEBUG, "%s\n", cond ? "true" : "false");
366 return !cond;
367}
368
369/*
370 * Test all bits that are required for Intel TXT.
371 * Enable SMX if available.
372 *
373 * @return 0 on success
374 */
375bool intel_txt_prepare_txt_env(void)
376{
377 bool failure = false;
378 uint32_t txt_feature_flags = 0;
379
380 unsigned int ecx = cpuid_ecx(1);
381
382 printk(BIOS_DEBUG, "TEE-TXT: CPU supports SMX: ");
383 failure |= check_precondition(ecx & CPUID_SMX);
384
385 printk(BIOS_DEBUG, "TEE-TXT: CPU supports VMX: ");
386 failure |= check_precondition(ecx & CPUID_VMX);
387
388 msr_t msr = rdmsr(IA32_FEATURE_CONTROL);
389 if (!(msr.lo & BIT(0))) {
390 printk(BIOS_ERR, "TEE-TXT: IA32_FEATURE_CONTROL is not locked\n");
Angel Pons1fc43aa2020-08-04 17:54:01 +0200391 txt_reset_platform();
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100392 }
393
394 printk(BIOS_DEBUG, "TEE-TXT: IA32_FEATURE_CONTROL\n");
395 printk(BIOS_DEBUG, " VMXON in SMX enable: ");
396 failure |= check_precondition(msr.lo & BIT(1));
397
398 printk(BIOS_DEBUG, " VMXON outside SMX enable: ");
399 failure |= check_precondition(msr.lo & FEATURE_ENABLE_VMX);
400
401 printk(BIOS_DEBUG, " register is locked: ");
402 failure |= check_precondition(msr.lo & BIT(0));
403
404 /* IA32_FEATURE_CONTROL enables getsec instructions */
405 printk(BIOS_DEBUG, " GETSEC (all instructions) is enabled: ");
406 failure |= check_precondition((msr.lo & 0xff00) == 0xff00);
407
408 /* Prevent crash and opt out early */
409 if (failure)
410 return true;
411
412 uint32_t eax = 0;
413 /*
414 * GetSec[CAPABILITIES]
415 * SAFER MODE EXTENSIONS REFERENCE.
416 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
417 * Must check BIT0 of TXT chipset has been detected by CPU.
418 */
419 if (!getsec_capabilities(&eax))
420 return true;
421
422 printk(BIOS_DEBUG, "TEE-TXT: GETSEC[CAPABILITIES] returned:\n");
423 printk(BIOS_DEBUG, " TXT capable chipset: %s\n", (eax & BIT(0)) ? "true" : "false");
424
425 printk(BIOS_DEBUG, " ENTERACCS available: %s\n", (eax & BIT(2)) ? "true" : "false");
426 printk(BIOS_DEBUG, " EXITAC available: %s\n", (eax & BIT(3)) ? "true" : "false");
427 printk(BIOS_DEBUG, " SENTER available: %s\n", (eax & BIT(4)) ? "true" : "false");
428 printk(BIOS_DEBUG, " SEXIT available: %s\n", (eax & BIT(5)) ? "true" : "false");
429 printk(BIOS_DEBUG, " PARAMETERS available: %s\n", (eax & BIT(6)) ? "true" : "false");
430
431 /*
432 * Causes #GP if function is not supported by getsec.
433 * SAFER MODE EXTENSIONS REFERENCE.
434 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
435 * Order Number: 325383-060US
436 */
437 if ((eax & 0x7d) != 0x7d)
438 failure = true;
439
440 const uint64_t status = read64((void *)TXT_SPAD);
441
442 if (status & ACMSTS_TXT_DISABLED) {
443 printk(BIOS_INFO, "TEE-TXT: TXT disabled by BIOS policy in FIT.\n");
444 failure = true;
445 }
446
447 /*
448 * Only the BSP must call getsec[ENTERACCS].
449 * SAFER MODE EXTENSIONS REFERENCE.
450 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
451 * Order Number: 325383-060US
452 */
453 if (!boot_cpu()) {
454 printk(BIOS_ERR, "TEE-TXT: BSP flag not set in APICBASE_MSR.\n");
455 failure = true;
456 }
457
458 /*
459 * There must be no MCEs pending.
460 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
461 * Order Number: 325383-060US
462 */
463 msr = rdmsr(IA32_MCG_STATUS);
464 if (msr.lo & 0x4) {
465 printk(BIOS_ERR, "TEE-TXT: IA32_MCG_STATUS.MCIP is set.\n");
466 failure = true;
467 }
468
469 if (!getsec_parameter(NULL, NULL, NULL, NULL, NULL, &txt_feature_flags)) {
470 return true;
471 } else {
472 printk(BIOS_DEBUG, "TEE-TXT: Machine Check Register: ");
473 if (txt_feature_flags & GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK)
474 printk(BIOS_DEBUG, "preserved\n");
475 else
476 printk(BIOS_DEBUG, "must be clear\n");
477 }
478
479 if (!(txt_feature_flags & GETSEC_PARAMS_TXT_EXT_MACHINE_CHECK)) {
480 /*
481 * Make sure there are no uncorrectable MCE errors.
482 * Intel 64 and IA-32 Architectures Software Developer Manuals Vol 2D
483 */
Felix Held7cf37872021-07-09 23:05:21 +0200484 size_t max_mc_msr = mca_get_bank_count();
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100485 for (size_t i = 0; i < max_mc_msr; i++) {
Felix Held1b46e762021-07-13 00:54:32 +0200486 msr = rdmsr(IA32_MC_STATUS(i));
Philipp Deppenwiese5f9f7762018-11-20 14:22:15 +0100487 if (!(msr.hi & MCA_STATUS_HI_UC))
488 continue;
489
490 printk(BIOS_ERR, "TEE-TXT: IA32_MC%zd_STATUS.UC is set.\n", i);
491 failure = true;
492 break;
493 }
494 }
495
496 /* Need to park all APs. */
497 if (CONFIG(PARALLEL_MP_AP_WORK))
498 mp_park_aps();
499
500 return failure;
501}