|
27 | 27 | #include <linux/log2.h> |
28 | 28 | #include <linux/acpi.h> |
29 | 29 | #include <linux/suspend.h> |
| 30 | +#include <linux/acpi.h> |
30 | 31 | #include <asm/page.h> |
31 | 32 | #include <asm/special_insns.h> |
32 | 33 | #include <asm/msr-index.h> |
|
35 | 36 | #include <asm/tdx.h> |
36 | 37 | #include <asm/intel-family.h> |
37 | 38 | #include <asm/processor.h> |
| 39 | +#include <asm/mce.h> |
38 | 40 | #include "tdx.h" |
39 | 41 |
|
40 | 42 | static u32 tdx_global_keyid __ro_after_init; |
@@ -942,6 +944,13 @@ static int construct_tdmrs(struct list_head *tmb_list, |
942 | 944 | if (ret) |
943 | 945 | tdmrs_free_pamt_all(tdmr_list); |
944 | 946 |
|
| 947 | + /* |
| 948 | + * The tdmr_info_list is read-only from here on out. |
| 949 | + * Ensure that these writes are seen by other CPUs. |
| 950 | + * Pairs with a smp_rmb() in is_pamt_page(). |
| 951 | + */ |
| 952 | + smp_wmb(); |
| 953 | + |
945 | 954 | return ret; |
946 | 955 | } |
947 | 956 |
|
@@ -1235,6 +1244,106 @@ int tdx_enable(void) |
1235 | 1244 | } |
1236 | 1245 | EXPORT_SYMBOL_GPL(tdx_enable); |
1237 | 1246 |
|
| 1247 | +static bool is_pamt_page(unsigned long phys) |
| 1248 | +{ |
| 1249 | + struct tdmr_info_list *tdmr_list = &tdx_tdmr_list; |
| 1250 | + int i; |
| 1251 | + |
| 1252 | + /* Ensure that all remote 'tdmr_list' writes are visible: */ |
| 1253 | + smp_rmb(); |
| 1254 | + |
| 1255 | + /* |
| 1256 | + * The TDX module is no longer returning TDX_SYS_NOT_READY and |
| 1257 | + * is initialized. The 'tdmr_list' was initialized long ago |
| 1258 | + * and is now read-only. |
| 1259 | + */ |
| 1260 | + for (i = 0; i < tdmr_list->nr_consumed_tdmrs; i++) { |
| 1261 | + unsigned long base, size; |
| 1262 | + |
| 1263 | + tdmr_get_pamt(tdmr_entry(tdmr_list, i), &base, &size); |
| 1264 | + |
| 1265 | + if (phys >= base && phys < (base + size)) |
| 1266 | + return true; |
| 1267 | + } |
| 1268 | + |
| 1269 | + return false; |
| 1270 | +} |
| 1271 | + |
| 1272 | +/* |
| 1273 | + * Return whether the memory page at the given physical address is TDX |
| 1274 | + * private memory or not. |
| 1275 | + * |
| 1276 | + * This can be imprecise for two known reasons: |
| 1277 | + * 1. PAMTs are private memory and exist before the TDX module is |
| 1278 | + * ready and TDH_PHYMEM_PAGE_RDMD works. This is a relatively |
| 1279 | + * short window that occurs once per boot. |
| 1280 | + * 2. TDH_PHYMEM_PAGE_RDMD reflects the TDX module's knowledge of the |
| 1281 | + * page. However, the page can still cause #MC until it has been |
| 1282 | + * fully converted to shared using 64-byte writes like MOVDIR64B. |
| 1283 | + * Buggy hosts might still leave #MC-causing memory in place which |
| 1284 | + * this function can not detect. |
| 1285 | + */ |
| 1286 | +static bool paddr_is_tdx_private(unsigned long phys) |
| 1287 | +{ |
| 1288 | + struct tdx_module_args args = { |
| 1289 | + .rcx = phys & PAGE_MASK, |
| 1290 | + }; |
| 1291 | + u64 sret; |
| 1292 | + |
| 1293 | + if (!boot_cpu_has(X86_FEATURE_TDX_HOST_PLATFORM)) |
| 1294 | + return false; |
| 1295 | + |
| 1296 | + /* Get page type from the TDX module */ |
| 1297 | + sret = __seamcall_ret(TDH_PHYMEM_PAGE_RDMD, &args); |
| 1298 | + |
| 1299 | + /* |
| 1300 | + * The SEAMCALL will not return success unless there is a |
| 1301 | + * working, "ready" TDX module. Assume an absence of TDX |
| 1302 | + * private pages until SEAMCALL is working. |
| 1303 | + */ |
| 1304 | + if (sret) |
| 1305 | + return false; |
| 1306 | + |
| 1307 | + /* |
| 1308 | + * SEAMCALL was successful -- read page type (via RCX): |
| 1309 | + * |
| 1310 | + * - PT_NDA: Page is not used by the TDX module |
| 1311 | + * - PT_RSVD: Reserved for Non-TDX use |
| 1312 | + * - Others: Page is used by the TDX module |
| 1313 | + * |
| 1314 | + * Note PAMT pages are marked as PT_RSVD but they are also TDX |
| 1315 | + * private memory. |
| 1316 | + */ |
| 1317 | + switch (args.rcx) { |
| 1318 | + case PT_NDA: |
| 1319 | + return false; |
| 1320 | + case PT_RSVD: |
| 1321 | + return is_pamt_page(phys); |
| 1322 | + default: |
| 1323 | + return true; |
| 1324 | + } |
| 1325 | +} |
| 1326 | + |
| 1327 | +/* |
| 1328 | + * Some TDX-capable CPUs have an erratum. A write to TDX private |
| 1329 | + * memory poisons that memory, and a subsequent read of that memory |
| 1330 | + * triggers #MC. |
| 1331 | + * |
| 1332 | + * Help distinguish erratum-triggered #MCs from a normal hardware one. |
| 1333 | + * Just print additional message to show such #MC may be result of the |
| 1334 | + * erratum. |
| 1335 | + */ |
| 1336 | +const char *tdx_dump_mce_info(struct mce *m) |
| 1337 | +{ |
| 1338 | + if (!m || !mce_is_memory_error(m) || !mce_usable_address(m)) |
| 1339 | + return NULL; |
| 1340 | + |
| 1341 | + if (!paddr_is_tdx_private(m->addr)) |
| 1342 | + return NULL; |
| 1343 | + |
| 1344 | + return "TDX private memory error. Possible kernel bug."; |
| 1345 | +} |
| 1346 | + |
1238 | 1347 | static __init int record_keyid_partitioning(u32 *tdx_keyid_start, |
1239 | 1348 | u32 *nr_tdx_keyids) |
1240 | 1349 | { |
|
0 commit comments