| 1 | |
| 2 | |
| 3 | * tboot.c: main implementation of helper functions used by kernel for
|
| 4 | * runtime support of Intel(R) Trusted Execution Technology
|
| 5 | *
|
| 6 | * Copyright (c) 2006-2009, Intel Corporation
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/init_task.h>
|
| 10 | #include <linux/spinlock.h>
|
| 11 | #include <linux/export.h>
|
| 12 | #include <linux/delay.h>
|
| 13 | #include <linux/sched.h>
|
| 14 | #include <linux/init.h>
|
| 15 | #include <linux/dmar.h>
|
| 16 | #include <linux/cpu.h>
|
| 17 | #include <linux/pfn.h>
|
| 18 | #include <linux/mm.h>
|
| 19 | #include <linux/tboot.h>
|
| 20 | #include <linux/debugfs.h>
|
| 21 |
|
| 22 | #include <asm/realmode.h>
|
| 23 | #include <asm/processor.h>
|
| 24 | #include <asm/bootparam.h>
|
| 25 | #include <asm/pgalloc.h>
|
| 26 | #include <asm/fixmap.h>
|
| 27 | #include <asm/proto.h>
|
| 28 | #include <asm/setup.h>
|
| 29 | #include <asm/e820/api.h>
|
| 30 | #include <asm/io.h>
|
| 31 |
|
| 32 | #include "../realmode/rm/wakeup.h"
|
| 33 |
|
| 34 |
|
| 35 | static struct tboot *tboot __read_mostly;
|
| 36 |
|
| 37 |
|
| 38 | #define AP_WAIT_TIMEOUT 1
|
| 39 |
|
| 40 | #undef pr_fmt
|
| 41 | #define pr_fmt(fmt) "tboot: " fmt
|
| 42 |
|
| 43 | static u8 tboot_uuid[16] __initdata = TBOOT_UUID;
|
| 44 |
|
| 45 | bool tboot_enabled(void)
|
| 46 | {
|
| 47 | return tboot != NULL;
|
| 48 | }
|
| 49 |
|
| 50 |
|
| 51 | static noinline __init bool check_tboot_version(void)
|
| 52 | {
|
| 53 | if (memcmp(&tboot_uuid, &tboot->uuid, sizeof(tboot->uuid))) {
|
| 54 | pr_warn("tboot at 0x%llx is invalid\n", boot_params.tboot_addr);
|
| 55 | return false;
|
| 56 | }
|
| 57 |
|
| 58 | if (tboot->version < 5) {
|
| 59 | pr_warn("tboot version is invalid: %u\n", tboot->version);
|
| 60 | return false;
|
| 61 | }
|
| 62 |
|
| 63 | pr_info("found shared page at phys addr 0x%llx:\n",
|
| 64 | boot_params.tboot_addr);
|
| 65 | pr_debug("version: %d\n", tboot->version);
|
| 66 | pr_debug("log_addr: 0x%08x\n", tboot->log_addr);
|
| 67 | pr_debug("shutdown_entry: 0x%x\n", tboot->shutdown_entry);
|
| 68 | pr_debug("tboot_base: 0x%08x\n", tboot->tboot_base);
|
| 69 | pr_debug("tboot_size: 0x%x\n", tboot->tboot_size);
|
| 70 |
|
| 71 | return true;
|
| 72 | }
|
| 73 |
|
| 74 | void __init tboot_probe(void)
|
| 75 | {
|
| 76 |
|
| 77 | if (!boot_params.tboot_addr)
|
| 78 | return;
|
| 79 | |
| 80 | * also verify that it is mapped as we expect it before calling
|
| 81 | * set_fixmap(), to reduce chance of garbage value causing crash
|
| 82 |
|
| 83 | if (!e820__mapped_any(boot_params.tboot_addr,
|
| 84 | boot_params.tboot_addr, E820_TYPE_RESERVED)) {
|
| 85 | pr_warn("non-0 tboot_addr but it is not of type E820_TYPE_RESERVED\n");
|
| 86 | return;
|
| 87 | }
|
| 88 |
|
| 89 |
|
| 90 | set_fixmap(FIX_TBOOT_BASE, boot_params.tboot_addr);
|
| 91 | tboot = (void *)fix_to_virt(FIX_TBOOT_BASE);
|
| 92 | if (!check_tboot_version())
|
| 93 | tboot = NULL;
|
| 94 | }
|
| 95 |
|
| 96 | static pgd_t *tboot_pg_dir;
|
| 97 | static struct mm_struct tboot_mm = {
|
| 98 | .mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, tboot_mm.mmap_lock),
|
| 99 | .pgd = swapper_pg_dir,
|
| 100 | .mm_users = ATOMIC_INIT(2),
|
| 101 | .mm_count = ATOMIC_INIT(1),
|
| 102 | .write_protect_seq = SEQCNT_ZERO(tboot_mm.write_protect_seq),
|
| 103 | MMAP_LOCK_INITIALIZER(init_mm)
|
| 104 | .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
|
| 105 | .mmlist = LIST_HEAD_INIT(init_mm.mmlist),
|
| 106 | };
|
| 107 |
|
| 108 | static inline void switch_to_tboot_pt(void)
|
| 109 | {
|
| 110 | write_cr3(virt_to_phys(tboot_pg_dir));
|
| 111 | }
|
| 112 |
|
| 113 | static int map_tboot_page(unsigned long vaddr, unsigned long pfn,
|
| 114 | pgprot_t prot)
|
| 115 | {
|
| 116 | pgd_t *pgd;
|
| 117 | p4d_t *p4d;
|
| 118 | pud_t *pud;
|
| 119 | pmd_t *pmd;
|
| 120 | pte_t *pte;
|
| 121 |
|
| 122 | pgd = pgd_offset(&tboot_mm, vaddr);
|
| 123 | p4d = p4d_alloc(&tboot_mm, pgd, vaddr);
|
| 124 | if (!p4d)
|
| 125 | return -1;
|
| 126 | pud = pud_alloc(&tboot_mm, p4d, vaddr);
|
| 127 | if (!pud)
|
| 128 | return -1;
|
| 129 | pmd = pmd_alloc(&tboot_mm, pud, vaddr);
|
| 130 | if (!pmd)
|
| 131 | return -1;
|
| 132 | pte = pte_alloc_map(&tboot_mm, pmd, vaddr);
|
| 133 | if (!pte)
|
| 134 | return -1;
|
| 135 | set_pte_at(&tboot_mm, vaddr, pte, pfn_pte(pfn, prot));
|
| 136 | pte_unmap(pte);
|
| 137 |
|
| 138 | |
| 139 | * PTI poisons low addresses in the kernel page tables in the
|
| 140 | * name of making them unusable for userspace. To execute
|
| 141 | * code at such a low address, the poison must be cleared.
|
| 142 | *
|
| 143 | * Note: 'pgd' actually gets set in p4d_alloc() _or_
|
| 144 | * pud_alloc() depending on 4/5-level paging.
|
| 145 |
|
| 146 | pgd->pgd &= ~_PAGE_NX;
|
| 147 |
|
| 148 | return 0;
|
| 149 | }
|
| 150 |
|
| 151 | static int map_tboot_pages(unsigned long vaddr, unsigned long start_pfn,
|
| 152 | unsigned long nr)
|
| 153 | {
|
| 154 |
|
| 155 | tboot_pg_dir = pgd_alloc(&tboot_mm);
|
| 156 | if (!tboot_pg_dir)
|
| 157 | return -1;
|
| 158 |
|
| 159 | for (; nr > 0; nr--, vaddr += PAGE_SIZE, start_pfn++) {
|
| 160 | if (map_tboot_page(vaddr, start_pfn, PAGE_KERNEL_EXEC))
|
| 161 | return -1;
|
| 162 | }
|
| 163 |
|
| 164 | return 0;
|
| 165 | }
|
| 166 |
|
| 167 | static void tboot_create_trampoline(void)
|
| 168 | {
|
| 169 | u32 map_base, map_size;
|
| 170 |
|
| 171 |
|
| 172 | map_base = PFN_DOWN(tboot->tboot_base);
|
| 173 | map_size = PFN_UP(tboot->tboot_size);
|
| 174 | if (map_tboot_pages(map_base << PAGE_SHIFT, map_base, map_size))
|
| 175 | panic("tboot: Error mapping tboot pages (mfns) @ 0x%x, 0x%x\n",
|
| 176 | map_base, map_size);
|
| 177 | }
|
| 178 |
|
| 179 | #ifdef CONFIG_ACPI_SLEEP
|
| 180 |
|
| 181 | static void add_mac_region(phys_addr_t start, unsigned long size)
|
| 182 | {
|
| 183 | struct tboot_mac_region *mr;
|
| 184 | phys_addr_t end = start + size;
|
| 185 |
|
| 186 | if (tboot->num_mac_regions >= MAX_TB_MAC_REGIONS)
|
| 187 | panic("tboot: Too many MAC regions\n");
|
| 188 |
|
| 189 | if (start && size) {
|
| 190 | mr = &tboot->mac_regions[tboot->num_mac_regions++];
|
| 191 | mr->start = round_down(start, PAGE_SIZE);
|
| 192 | mr->size = round_up(end, PAGE_SIZE) - mr->start;
|
| 193 | }
|
| 194 | }
|
| 195 |
|
| 196 | static int tboot_setup_sleep(void)
|
| 197 | {
|
| 198 | int i;
|
| 199 |
|
| 200 | tboot->num_mac_regions = 0;
|
| 201 |
|
| 202 | for (i = 0; i < e820_table->nr_entries; i++) {
|
| 203 | if (e820_table->entries[i].type != E820_TYPE_RAM)
|
| 204 | continue;
|
| 205 |
|
| 206 | add_mac_region(e820_table->entries[i].addr, e820_table->entries[i].size);
|
| 207 | }
|
| 208 |
|
| 209 | tboot->acpi_sinfo.kernel_s3_resume_vector =
|
| 210 | real_mode_header->wakeup_start;
|
| 211 |
|
| 212 | return 0;
|
| 213 | }
|
| 214 |
|
| 215 | #else
|
| 216 |
|
| 217 | static int tboot_setup_sleep(void)
|
| 218 | {
|
| 219 |
|
| 220 | BUG();
|
| 221 | return -1;
|
| 222 | }
|
| 223 |
|
| 224 | #endif
|
| 225 |
|
| 226 | void tboot_shutdown(u32 shutdown_type)
|
| 227 | {
|
| 228 | void (*shutdown)(void);
|
| 229 |
|
| 230 | if (!tboot_enabled())
|
| 231 | return;
|
| 232 |
|
| 233 | |
| 234 | * if we're being called before the 1:1 mapping is set up then just
|
| 235 | * return and let the normal shutdown happen; this should only be
|
| 236 | * due to very early panic()
|
| 237 |
|
| 238 | if (!tboot_pg_dir)
|
| 239 | return;
|
| 240 |
|
| 241 |
|
| 242 | if (shutdown_type == TB_SHUTDOWN_S3)
|
| 243 | if (tboot_setup_sleep())
|
| 244 | return;
|
| 245 |
|
| 246 | tboot->shutdown_type = shutdown_type;
|
| 247 |
|
| 248 | switch_to_tboot_pt();
|
| 249 |
|
| 250 | shutdown = (void(*)(void))(unsigned long)tboot->shutdown_entry;
|
| 251 | shutdown();
|
| 252 |
|
| 253 |
|
| 254 | while (1)
|
| 255 | halt();
|
| 256 | }
|
| 257 |
|
| 258 | static void tboot_copy_fadt(const struct acpi_table_fadt *fadt)
|
| 259 | {
|
| 260 | #define TB_COPY_GAS(tbg, g) \
|
| 261 | tbg.space_id = g.space_id; \
|
| 262 | tbg.bit_width = g.bit_width; \
|
| 263 | tbg.bit_offset = g.bit_offset; \
|
| 264 | tbg.access_width = g.access_width; \
|
| 265 | tbg.address = g.address;
|
| 266 |
|
| 267 | TB_COPY_GAS(tboot->acpi_sinfo.pm1a_cnt_blk, fadt->xpm1a_control_block);
|
| 268 | TB_COPY_GAS(tboot->acpi_sinfo.pm1b_cnt_blk, fadt->xpm1b_control_block);
|
| 269 | TB_COPY_GAS(tboot->acpi_sinfo.pm1a_evt_blk, fadt->xpm1a_event_block);
|
| 270 | TB_COPY_GAS(tboot->acpi_sinfo.pm1b_evt_blk, fadt->xpm1b_event_block);
|
| 271 |
|
| 272 | |
| 273 | * We need phys addr of waking vector, but can't use virt_to_phys() on
|
| 274 | * &acpi_gbl_FACS because it is ioremap'ed, so calc from FACS phys
|
| 275 | * addr.
|
| 276 |
|
| 277 | tboot->acpi_sinfo.wakeup_vector = fadt->facs +
|
| 278 | offsetof(struct acpi_table_facs, firmware_waking_vector);
|
| 279 | }
|
| 280 |
|
| 281 | static int tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
|
| 282 | {
|
| 283 | static u32 acpi_shutdown_map[ACPI_S_STATE_COUNT] = {
|
| 284 | -1, -1, -1,
|
| 285 | TB_SHUTDOWN_S3,
|
| 286 | TB_SHUTDOWN_S4,
|
| 287 | TB_SHUTDOWN_S5 };
|
| 288 |
|
| 289 | if (!tboot_enabled())
|
| 290 | return 0;
|
| 291 |
|
| 292 | tboot_copy_fadt(&acpi_gbl_FADT);
|
| 293 | tboot->acpi_sinfo.pm1a_cnt_val = pm1a_control;
|
| 294 | tboot->acpi_sinfo.pm1b_cnt_val = pm1b_control;
|
| 295 |
|
| 296 | tboot->acpi_sinfo.vector_width = 32;
|
| 297 |
|
| 298 | if (sleep_state >= ACPI_S_STATE_COUNT ||
|
| 299 | acpi_shutdown_map[sleep_state] == -1) {
|
| 300 | pr_warn("unsupported sleep state 0x%x\n", sleep_state);
|
| 301 | return -1;
|
| 302 | }
|
| 303 |
|
| 304 | tboot_shutdown(acpi_shutdown_map[sleep_state]);
|
| 305 | return 0;
|
| 306 | }
|
| 307 |
|
| 308 | static int tboot_extended_sleep(u8 sleep_state, u32 val_a, u32 val_b)
|
| 309 | {
|
| 310 | if (!tboot_enabled())
|
| 311 | return 0;
|
| 312 |
|
| 313 | pr_warn("tboot is not able to suspend on platforms with reduced hardware sleep (ACPIv5)");
|
| 314 | return -ENODEV;
|
| 315 | }
|
| 316 |
|
| 317 | static atomic_t ap_wfs_count;
|
| 318 |
|
| 319 | static int tboot_wait_for_aps(int num_aps)
|
| 320 | {
|
| 321 | unsigned long timeout;
|
| 322 |
|
| 323 | timeout = AP_WAIT_TIMEOUT*HZ;
|
| 324 | while (atomic_read((atomic_t *)&tboot->num_in_wfs) != num_aps &&
|
| 325 | timeout) {
|
| 326 | mdelay(1);
|
| 327 | timeout--;
|
| 328 | }
|
| 329 |
|
| 330 | if (timeout)
|
| 331 | pr_warn("tboot wait for APs timeout\n");
|
| 332 |
|
| 333 | return !(atomic_read((atomic_t *)&tboot->num_in_wfs) == num_aps);
|
| 334 | }
|
| 335 |
|
| 336 | static int tboot_dying_cpu(unsigned int cpu)
|
| 337 | {
|
| 338 | atomic_inc(&ap_wfs_count);
|
| 339 | if (num_online_cpus() == 1) {
|
| 340 | if (tboot_wait_for_aps(atomic_read(&ap_wfs_count)))
|
| 341 | return -EBUSY;
|
| 342 | }
|
| 343 | return 0;
|
| 344 | }
|
| 345 |
|
| 346 | #ifdef CONFIG_DEBUG_FS
|
| 347 |
|
| 348 | #define TBOOT_LOG_UUID { 0x26, 0x25, 0x19, 0xc0, 0x30, 0x6b, 0xb4, 0x4d, \
|
| 349 | 0x4c, 0x84, 0xa3, 0xe9, 0x53, 0xb8, 0x81, 0x74 }
|
| 350 |
|
| 351 | #define TBOOT_SERIAL_LOG_ADDR 0x60000
|
| 352 | #define TBOOT_SERIAL_LOG_SIZE 0x08000
|
| 353 | #define LOG_MAX_SIZE_OFF 16
|
| 354 | #define LOG_BUF_OFF 24
|
| 355 |
|
| 356 | static uint8_t tboot_log_uuid[16] = TBOOT_LOG_UUID;
|
| 357 |
|
| 358 | static ssize_t tboot_log_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
|
| 359 | {
|
| 360 | void __iomem *log_base;
|
| 361 | u8 log_uuid[16];
|
| 362 | u32 max_size;
|
| 363 | void *kbuf;
|
| 364 | int ret = -EFAULT;
|
| 365 |
|
| 366 | log_base = ioremap(TBOOT_SERIAL_LOG_ADDR, TBOOT_SERIAL_LOG_SIZE);
|
| 367 | if (!log_base)
|
| 368 | return ret;
|
| 369 |
|
| 370 | memcpy_fromio(log_uuid, log_base, sizeof(log_uuid));
|
| 371 | if (memcmp(&tboot_log_uuid, log_uuid, sizeof(log_uuid)))
|
| 372 | goto err_iounmap;
|
| 373 |
|
| 374 | max_size = readl(log_base + LOG_MAX_SIZE_OFF);
|
| 375 | if (*ppos >= max_size) {
|
| 376 | ret = 0;
|
| 377 | goto err_iounmap;
|
| 378 | }
|
| 379 |
|
| 380 | if (*ppos + count > max_size)
|
| 381 | count = max_size - *ppos;
|
| 382 |
|
| 383 | kbuf = kmalloc(count, GFP_KERNEL);
|
| 384 | if (!kbuf) {
|
| 385 | ret = -ENOMEM;
|
| 386 | goto err_iounmap;
|
| 387 | }
|
| 388 |
|
| 389 | memcpy_fromio(kbuf, log_base + LOG_BUF_OFF + *ppos, count);
|
| 390 | if (copy_to_user(user_buf, kbuf, count))
|
| 391 | goto err_kfree;
|
| 392 |
|
| 393 | *ppos += count;
|
| 394 |
|
| 395 | ret = count;
|
| 396 |
|
| 397 | err_kfree:
|
| 398 | kfree(kbuf);
|
| 399 |
|
| 400 | err_iounmap:
|
| 401 | iounmap(log_base);
|
| 402 |
|
| 403 | return ret;
|
| 404 | }
|
| 405 |
|
| 406 | static const struct file_operations tboot_log_fops = {
|
| 407 | .read = tboot_log_read,
|
| 408 | .llseek = default_llseek,
|
| 409 | };
|
| 410 |
|
| 411 | #endif
|
| 412 |
|
| 413 | static __init int tboot_late_init(void)
|
| 414 | {
|
| 415 | if (!tboot_enabled())
|
| 416 | return 0;
|
| 417 |
|
| 418 | tboot_create_trampoline();
|
| 419 |
|
| 420 | atomic_set(&ap_wfs_count, 0);
|
| 421 | cpuhp_setup_state(CPUHP_AP_X86_TBOOT_DYING, "x86/tboot:dying", NULL,
|
| 422 | tboot_dying_cpu);
|
| 423 | #ifdef CONFIG_DEBUG_FS
|
| 424 | debugfs_create_file("tboot_log", S_IRUSR,
|
| 425 | arch_debugfs_dir, NULL, &tboot_log_fops);
|
| 426 | #endif
|
| 427 |
|
| 428 | acpi_os_set_prepare_sleep(&tboot_sleep);
|
| 429 | acpi_os_set_prepare_extended_sleep(&tboot_extended_sleep);
|
| 430 | return 0;
|
| 431 | }
|
| 432 |
|
| 433 | late_initcall(tboot_late_init);
|
| 434 |
|
| 435 | |
| 436 | * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE)
|
| 437 |
|
| 438 |
|
| 439 | #define TXT_PUB_CONFIG_REGS_BASE 0xfed30000
|
| 440 | #define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000
|
| 441 |
|
| 442 |
|
| 443 | #define NR_TXT_CONFIG_PAGES ((TXT_PUB_CONFIG_REGS_BASE - \
|
| 444 | TXT_PRIV_CONFIG_REGS_BASE) >> PAGE_SHIFT)
|
| 445 |
|
| 446 |
|
| 447 | #define TXTCR_HEAP_BASE 0x0300
|
| 448 | #define TXTCR_HEAP_SIZE 0x0308
|
| 449 |
|
| 450 | #define SHA1_SIZE 20
|
| 451 |
|
| 452 | struct sha1_hash {
|
| 453 | u8 hash[SHA1_SIZE];
|
| 454 | };
|
| 455 |
|
| 456 | struct sinit_mle_data {
|
| 457 | u32 version;
|
| 458 | struct sha1_hash bios_acm_id;
|
| 459 | u32 edx_senter_flags;
|
| 460 | u64 mseg_valid;
|
| 461 | struct sha1_hash sinit_hash;
|
| 462 | struct sha1_hash mle_hash;
|
| 463 | struct sha1_hash stm_hash;
|
| 464 | struct sha1_hash lcp_policy_hash;
|
| 465 | u32 lcp_policy_control;
|
| 466 | u32 rlp_wakeup_addr;
|
| 467 | u32 reserved;
|
| 468 | u32 num_mdrs;
|
| 469 | u32 mdrs_off;
|
| 470 | u32 num_vtd_dmars;
|
| 471 | u32 vtd_dmars_off;
|
| 472 | } __packed;
|
| 473 |
|
| 474 | struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
|
| 475 | {
|
| 476 | void *heap_base, *heap_ptr, *config;
|
| 477 |
|
| 478 | if (!tboot_enabled())
|
| 479 | return dmar_tbl;
|
| 480 |
|
| 481 | |
| 482 | * ACPI tables may not be DMA protected by tboot, so use DMAR copy
|
| 483 | * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
|
| 484 |
|
| 485 |
|
| 486 |
|
| 487 | config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
|
| 488 | PAGE_SIZE);
|
| 489 | if (!config)
|
| 490 | return NULL;
|
| 491 |
|
| 492 |
|
| 493 | heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
|
| 494 | *(u64 *)(config + TXTCR_HEAP_SIZE));
|
| 495 | iounmap(config);
|
| 496 | if (!heap_base)
|
| 497 | return NULL;
|
| 498 |
|
| 499 |
|
| 500 |
|
| 501 | heap_ptr = heap_base + *(u64 *)heap_base;
|
| 502 |
|
| 503 | heap_ptr += *(u64 *)heap_ptr;
|
| 504 |
|
| 505 | heap_ptr += *(u64 *)heap_ptr;
|
| 506 |
|
| 507 | heap_ptr += sizeof(u64);
|
| 508 |
|
| 509 | dmar_tbl = (struct acpi_table_header *)(heap_ptr +
|
| 510 | ((struct sinit_mle_data *)heap_ptr)->vtd_dmars_off -
|
| 511 | sizeof(u64));
|
| 512 |
|
| 513 |
|
| 514 |
|
| 515 | return dmar_tbl;
|
| 516 | }
|