1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
231625340SJoseph Cihula /*
331625340SJoseph Cihula * tboot.c: main implementation of helper functions used by kernel for
431625340SJoseph Cihula * runtime support of Intel(R) Trusted Execution Technology
531625340SJoseph Cihula *
631625340SJoseph Cihula * Copyright (c) 2006-2009, Intel Corporation
731625340SJoseph Cihula */
831625340SJoseph Cihula
931625340SJoseph Cihula #include <linux/init_task.h>
1031625340SJoseph Cihula #include <linux/spinlock.h>
1169c60c88SPaul Gortmaker #include <linux/export.h>
1269575d38SShane Wang #include <linux/delay.h>
1331625340SJoseph Cihula #include <linux/sched.h>
1431625340SJoseph Cihula #include <linux/init.h>
1531625340SJoseph Cihula #include <linux/dmar.h>
1669575d38SShane Wang #include <linux/cpu.h>
1731625340SJoseph Cihula #include <linux/pfn.h>
1831625340SJoseph Cihula #include <linux/mm.h>
1969575d38SShane Wang #include <linux/tboot.h>
2013bfd47aSQiaowei Ren #include <linux/debugfs.h>
2131625340SJoseph Cihula
22c9b77ccbSJarkko Sakkinen #include <asm/realmode.h>
2331625340SJoseph Cihula #include <asm/processor.h>
2431625340SJoseph Cihula #include <asm/bootparam.h>
2531625340SJoseph Cihula #include <asm/pgalloc.h>
2681e2d7b3SH. Peter Anvin #include <asm/fixmap.h>
2758c41d28SH. Peter Anvin #include <asm/proto.h>
2831625340SJoseph Cihula #include <asm/setup.h>
2966441bd3SIngo Molnar #include <asm/e820/api.h>
3031625340SJoseph Cihula #include <asm/io.h>
3131625340SJoseph Cihula
32bf8b88e9SJarkko Sakkinen #include "../realmode/rm/wakeup.h"
3331625340SJoseph Cihula
3431625340SJoseph Cihula /* Global pointer to shared data; NULL means no measured launch. */
35767dea21SChristoph Hellwig static struct tboot *tboot __read_mostly;
3631625340SJoseph Cihula
3731625340SJoseph Cihula /* timeout for APs (in secs) to enter wait-for-SIPI state during shutdown */
3831625340SJoseph Cihula #define AP_WAIT_TIMEOUT 1
3931625340SJoseph Cihula
4031625340SJoseph Cihula #undef pr_fmt
4131625340SJoseph Cihula #define pr_fmt(fmt) "tboot: " fmt
4231625340SJoseph Cihula
4331625340SJoseph Cihula static u8 tboot_uuid[16] __initdata = TBOOT_UUID;
4431625340SJoseph Cihula
tboot_enabled(void)45767dea21SChristoph Hellwig bool tboot_enabled(void)
46767dea21SChristoph Hellwig {
47767dea21SChristoph Hellwig return tboot != NULL;
48767dea21SChristoph Hellwig }
49767dea21SChristoph Hellwig
50cdc34cb8SArnd Bergmann /* noinline to prevent gcc from warning about dereferencing constant fixaddr */
check_tboot_version(void)51cdc34cb8SArnd Bergmann static noinline __init bool check_tboot_version(void)
52cdc34cb8SArnd Bergmann {
53cdc34cb8SArnd Bergmann if (memcmp(&tboot_uuid, &tboot->uuid, sizeof(tboot->uuid))) {
54cdc34cb8SArnd Bergmann pr_warn("tboot at 0x%llx is invalid\n", boot_params.tboot_addr);
55cdc34cb8SArnd Bergmann return false;
56cdc34cb8SArnd Bergmann }
57cdc34cb8SArnd Bergmann
58cdc34cb8SArnd Bergmann if (tboot->version < 5) {
59cdc34cb8SArnd Bergmann pr_warn("tboot version is invalid: %u\n", tboot->version);
60cdc34cb8SArnd Bergmann return false;
61cdc34cb8SArnd Bergmann }
62cdc34cb8SArnd Bergmann
63cdc34cb8SArnd Bergmann pr_info("found shared page at phys addr 0x%llx:\n",
64cdc34cb8SArnd Bergmann boot_params.tboot_addr);
65cdc34cb8SArnd Bergmann pr_debug("version: %d\n", tboot->version);
66cdc34cb8SArnd Bergmann pr_debug("log_addr: 0x%08x\n", tboot->log_addr);
67cdc34cb8SArnd Bergmann pr_debug("shutdown_entry: 0x%x\n", tboot->shutdown_entry);
68cdc34cb8SArnd Bergmann pr_debug("tboot_base: 0x%08x\n", tboot->tboot_base);
69cdc34cb8SArnd Bergmann pr_debug("tboot_size: 0x%x\n", tboot->tboot_size);
70cdc34cb8SArnd Bergmann
71cdc34cb8SArnd Bergmann return true;
72cdc34cb8SArnd Bergmann }
73cdc34cb8SArnd Bergmann
tboot_probe(void)7431625340SJoseph Cihula void __init tboot_probe(void)
7531625340SJoseph Cihula {
7631625340SJoseph Cihula /* Look for valid page-aligned address for shared page. */
7731625340SJoseph Cihula if (!boot_params.tboot_addr)
7831625340SJoseph Cihula return;
7931625340SJoseph Cihula /*
8031625340SJoseph Cihula * also verify that it is mapped as we expect it before calling
8131625340SJoseph Cihula * set_fixmap(), to reduce chance of garbage value causing crash
8231625340SJoseph Cihula */
833bce64f0SIngo Molnar if (!e820__mapped_any(boot_params.tboot_addr,
8409821ff1SIngo Molnar boot_params.tboot_addr, E820_TYPE_RESERVED)) {
858d3bcc44SKefeng Wang pr_warn("non-0 tboot_addr but it is not of type E820_TYPE_RESERVED\n");
8631625340SJoseph Cihula return;
8731625340SJoseph Cihula }
8831625340SJoseph Cihula
8931625340SJoseph Cihula /* Map and check for tboot UUID. */
9031625340SJoseph Cihula set_fixmap(FIX_TBOOT_BASE, boot_params.tboot_addr);
91cdc34cb8SArnd Bergmann tboot = (void *)fix_to_virt(FIX_TBOOT_BASE);
92cdc34cb8SArnd Bergmann if (!check_tboot_version())
9331625340SJoseph Cihula tboot = NULL;
9431625340SJoseph Cihula }
9531625340SJoseph Cihula
9611520e5eSLinus Torvalds static pgd_t *tboot_pg_dir;
9711520e5eSLinus Torvalds static struct mm_struct tboot_mm = {
98*d4af56c5SLiam R. Howlett .mm_mt = MTREE_INIT_EXT(mm_mt, MM_MT_FLAGS, tboot_mm.mmap_lock),
9911520e5eSLinus Torvalds .pgd = swapper_pg_dir,
10011520e5eSLinus Torvalds .mm_users = ATOMIC_INIT(2),
10111520e5eSLinus Torvalds .mm_count = ATOMIC_INIT(1),
10257efa1feSJason Gunthorpe .write_protect_seq = SEQCNT_ZERO(tboot_mm.write_protect_seq),
10314c3656bSMichel Lespinasse MMAP_LOCK_INITIALIZER(init_mm)
10411520e5eSLinus Torvalds .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
10511520e5eSLinus Torvalds .mmlist = LIST_HEAD_INIT(init_mm.mmlist),
10611520e5eSLinus Torvalds };
10711520e5eSLinus Torvalds
switch_to_tboot_pt(void)10831625340SJoseph Cihula static inline void switch_to_tboot_pt(void)
10931625340SJoseph Cihula {
11011520e5eSLinus Torvalds write_cr3(virt_to_phys(tboot_pg_dir));
11111520e5eSLinus Torvalds }
11211520e5eSLinus Torvalds
map_tboot_page(unsigned long vaddr,unsigned long pfn,pgprot_t prot)11311520e5eSLinus Torvalds static int map_tboot_page(unsigned long vaddr, unsigned long pfn,
11411520e5eSLinus Torvalds pgprot_t prot)
11511520e5eSLinus Torvalds {
11611520e5eSLinus Torvalds pgd_t *pgd;
117e0c4f675SKirill A. Shutemov p4d_t *p4d;
11811520e5eSLinus Torvalds pud_t *pud;
11911520e5eSLinus Torvalds pmd_t *pmd;
12011520e5eSLinus Torvalds pte_t *pte;
12111520e5eSLinus Torvalds
12211520e5eSLinus Torvalds pgd = pgd_offset(&tboot_mm, vaddr);
123e0c4f675SKirill A. Shutemov p4d = p4d_alloc(&tboot_mm, pgd, vaddr);
124e0c4f675SKirill A. Shutemov if (!p4d)
125e0c4f675SKirill A. Shutemov return -1;
126e0c4f675SKirill A. Shutemov pud = pud_alloc(&tboot_mm, p4d, vaddr);
12711520e5eSLinus Torvalds if (!pud)
12811520e5eSLinus Torvalds return -1;
12911520e5eSLinus Torvalds pmd = pmd_alloc(&tboot_mm, pud, vaddr);
13011520e5eSLinus Torvalds if (!pmd)
13111520e5eSLinus Torvalds return -1;
1323ed3a4f0SKirill A. Shutemov pte = pte_alloc_map(&tboot_mm, pmd, vaddr);
13311520e5eSLinus Torvalds if (!pte)
13411520e5eSLinus Torvalds return -1;
13511520e5eSLinus Torvalds set_pte_at(&tboot_mm, vaddr, pte, pfn_pte(pfn, prot));
13611520e5eSLinus Torvalds pte_unmap(pte);
137445b69e3SDave Hansen
138445b69e3SDave Hansen /*
139445b69e3SDave Hansen * PTI poisons low addresses in the kernel page tables in the
140445b69e3SDave Hansen * name of making them unusable for userspace. To execute
141445b69e3SDave Hansen * code at such a low address, the poison must be cleared.
142445b69e3SDave Hansen *
143445b69e3SDave Hansen * Note: 'pgd' actually gets set in p4d_alloc() _or_
144445b69e3SDave Hansen * pud_alloc() depending on 4/5-level paging.
145445b69e3SDave Hansen */
146445b69e3SDave Hansen pgd->pgd &= ~_PAGE_NX;
147445b69e3SDave Hansen
14811520e5eSLinus Torvalds return 0;
14911520e5eSLinus Torvalds }
15011520e5eSLinus Torvalds
map_tboot_pages(unsigned long vaddr,unsigned long start_pfn,unsigned long nr)15111520e5eSLinus Torvalds static int map_tboot_pages(unsigned long vaddr, unsigned long start_pfn,
15211520e5eSLinus Torvalds unsigned long nr)
15311520e5eSLinus Torvalds {
15411520e5eSLinus Torvalds /* Reuse the original kernel mapping */
15511520e5eSLinus Torvalds tboot_pg_dir = pgd_alloc(&tboot_mm);
15611520e5eSLinus Torvalds if (!tboot_pg_dir)
15711520e5eSLinus Torvalds return -1;
15811520e5eSLinus Torvalds
15911520e5eSLinus Torvalds for (; nr > 0; nr--, vaddr += PAGE_SIZE, start_pfn++) {
16011520e5eSLinus Torvalds if (map_tboot_page(vaddr, start_pfn, PAGE_KERNEL_EXEC))
16111520e5eSLinus Torvalds return -1;
16211520e5eSLinus Torvalds }
16311520e5eSLinus Torvalds
16411520e5eSLinus Torvalds return 0;
16511520e5eSLinus Torvalds }
16611520e5eSLinus Torvalds
tboot_create_trampoline(void)16711520e5eSLinus Torvalds static void tboot_create_trampoline(void)
16811520e5eSLinus Torvalds {
16911520e5eSLinus Torvalds u32 map_base, map_size;
17011520e5eSLinus Torvalds
17111520e5eSLinus Torvalds /* Create identity map for tboot shutdown code. */
17211520e5eSLinus Torvalds map_base = PFN_DOWN(tboot->tboot_base);
17311520e5eSLinus Torvalds map_size = PFN_UP(tboot->tboot_size);
17411520e5eSLinus Torvalds if (map_tboot_pages(map_base << PAGE_SHIFT, map_base, map_size))
17511520e5eSLinus Torvalds panic("tboot: Error mapping tboot pages (mfns) @ 0x%x, 0x%x\n",
17611520e5eSLinus Torvalds map_base, map_size);
17731625340SJoseph Cihula }
17831625340SJoseph Cihula
17958c41d28SH. Peter Anvin #ifdef CONFIG_ACPI_SLEEP
18058c41d28SH. Peter Anvin
add_mac_region(phys_addr_t start,unsigned long size)18158c41d28SH. Peter Anvin static void add_mac_region(phys_addr_t start, unsigned long size)
18231625340SJoseph Cihula {
18358c41d28SH. Peter Anvin struct tboot_mac_region *mr;
18458c41d28SH. Peter Anvin phys_addr_t end = start + size;
18558c41d28SH. Peter Anvin
1864bd96a7aSShane Wang if (tboot->num_mac_regions >= MAX_TB_MAC_REGIONS)
1874bd96a7aSShane Wang panic("tboot: Too many MAC regions\n");
1884bd96a7aSShane Wang
18958c41d28SH. Peter Anvin if (start && size) {
19058c41d28SH. Peter Anvin mr = &tboot->mac_regions[tboot->num_mac_regions++];
19158c41d28SH. Peter Anvin mr->start = round_down(start, PAGE_SIZE);
19258c41d28SH. Peter Anvin mr->size = round_up(end, PAGE_SIZE) - mr->start;
19331625340SJoseph Cihula }
19458c41d28SH. Peter Anvin }
19558c41d28SH. Peter Anvin
tboot_setup_sleep(void)19658c41d28SH. Peter Anvin static int tboot_setup_sleep(void)
19758c41d28SH. Peter Anvin {
1984bd96a7aSShane Wang int i;
1994bd96a7aSShane Wang
20058c41d28SH. Peter Anvin tboot->num_mac_regions = 0;
20158c41d28SH. Peter Anvin
202bf495573SIngo Molnar for (i = 0; i < e820_table->nr_entries; i++) {
20309821ff1SIngo Molnar if ((e820_table->entries[i].type != E820_TYPE_RAM)
20409821ff1SIngo Molnar && (e820_table->entries[i].type != E820_TYPE_RESERVED_KERN))
2054bd96a7aSShane Wang continue;
20662a3207bSH. Peter Anvin
207bf495573SIngo Molnar add_mac_region(e820_table->entries[i].addr, e820_table->entries[i].size);
2084bd96a7aSShane Wang }
20958c41d28SH. Peter Anvin
210c9b77ccbSJarkko Sakkinen tboot->acpi_sinfo.kernel_s3_resume_vector =
211b429dbf6SJarkko Sakkinen real_mode_header->wakeup_start;
21258c41d28SH. Peter Anvin
21358c41d28SH. Peter Anvin return 0;
21458c41d28SH. Peter Anvin }
21558c41d28SH. Peter Anvin
21658c41d28SH. Peter Anvin #else /* no CONFIG_ACPI_SLEEP */
21758c41d28SH. Peter Anvin
tboot_setup_sleep(void)21858c41d28SH. Peter Anvin static int tboot_setup_sleep(void)
21958c41d28SH. Peter Anvin {
22058c41d28SH. Peter Anvin /* S3 shutdown requested, but S3 not supported by the kernel... */
22158c41d28SH. Peter Anvin BUG();
22258c41d28SH. Peter Anvin return -1;
22358c41d28SH. Peter Anvin }
22458c41d28SH. Peter Anvin
22558c41d28SH. Peter Anvin #endif
22631625340SJoseph Cihula
tboot_shutdown(u32 shutdown_type)22731625340SJoseph Cihula void tboot_shutdown(u32 shutdown_type)
22831625340SJoseph Cihula {
22931625340SJoseph Cihula void (*shutdown)(void);
23031625340SJoseph Cihula
23131625340SJoseph Cihula if (!tboot_enabled())
23231625340SJoseph Cihula return;
23331625340SJoseph Cihula
23411520e5eSLinus Torvalds /*
23511520e5eSLinus Torvalds * if we're being called before the 1:1 mapping is set up then just
23611520e5eSLinus Torvalds * return and let the normal shutdown happen; this should only be
23711520e5eSLinus Torvalds * due to very early panic()
23811520e5eSLinus Torvalds */
23911520e5eSLinus Torvalds if (!tboot_pg_dir)
24011520e5eSLinus Torvalds return;
24111520e5eSLinus Torvalds
24231625340SJoseph Cihula /* if this is S3 then set regions to MAC */
24331625340SJoseph Cihula if (shutdown_type == TB_SHUTDOWN_S3)
24458c41d28SH. Peter Anvin if (tboot_setup_sleep())
24558c41d28SH. Peter Anvin return;
24631625340SJoseph Cihula
24731625340SJoseph Cihula tboot->shutdown_type = shutdown_type;
24831625340SJoseph Cihula
24931625340SJoseph Cihula switch_to_tboot_pt();
25031625340SJoseph Cihula
25131625340SJoseph Cihula shutdown = (void(*)(void))(unsigned long)tboot->shutdown_entry;
25231625340SJoseph Cihula shutdown();
25331625340SJoseph Cihula
25431625340SJoseph Cihula /* should not reach here */
25531625340SJoseph Cihula while (1)
25631625340SJoseph Cihula halt();
25731625340SJoseph Cihula }
25831625340SJoseph Cihula
tboot_copy_fadt(const struct acpi_table_fadt * fadt)25931625340SJoseph Cihula static void tboot_copy_fadt(const struct acpi_table_fadt *fadt)
26031625340SJoseph Cihula {
26131625340SJoseph Cihula #define TB_COPY_GAS(tbg, g) \
26231625340SJoseph Cihula tbg.space_id = g.space_id; \
26331625340SJoseph Cihula tbg.bit_width = g.bit_width; \
26431625340SJoseph Cihula tbg.bit_offset = g.bit_offset; \
26531625340SJoseph Cihula tbg.access_width = g.access_width; \
26631625340SJoseph Cihula tbg.address = g.address;
26731625340SJoseph Cihula
26831625340SJoseph Cihula TB_COPY_GAS(tboot->acpi_sinfo.pm1a_cnt_blk, fadt->xpm1a_control_block);
26931625340SJoseph Cihula TB_COPY_GAS(tboot->acpi_sinfo.pm1b_cnt_blk, fadt->xpm1b_control_block);
27031625340SJoseph Cihula TB_COPY_GAS(tboot->acpi_sinfo.pm1a_evt_blk, fadt->xpm1a_event_block);
27131625340SJoseph Cihula TB_COPY_GAS(tboot->acpi_sinfo.pm1b_evt_blk, fadt->xpm1b_event_block);
27231625340SJoseph Cihula
27331625340SJoseph Cihula /*
27431625340SJoseph Cihula * We need phys addr of waking vector, but can't use virt_to_phys() on
27531625340SJoseph Cihula * &acpi_gbl_FACS because it is ioremap'ed, so calc from FACS phys
27631625340SJoseph Cihula * addr.
27731625340SJoseph Cihula */
27831625340SJoseph Cihula tboot->acpi_sinfo.wakeup_vector = fadt->facs +
27931625340SJoseph Cihula offsetof(struct acpi_table_facs, firmware_waking_vector);
28031625340SJoseph Cihula }
28131625340SJoseph Cihula
tboot_sleep(u8 sleep_state,u32 pm1a_control,u32 pm1b_control)282a1f37788SKonrad Rzeszutek Wilk static int tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
28331625340SJoseph Cihula {
28431625340SJoseph Cihula static u32 acpi_shutdown_map[ACPI_S_STATE_COUNT] = {
28531625340SJoseph Cihula /* S0,1,2: */ -1, -1, -1,
28631625340SJoseph Cihula /* S3: */ TB_SHUTDOWN_S3,
28731625340SJoseph Cihula /* S4: */ TB_SHUTDOWN_S4,
28831625340SJoseph Cihula /* S5: */ TB_SHUTDOWN_S5 };
28931625340SJoseph Cihula
29031625340SJoseph Cihula if (!tboot_enabled())
291a1f37788SKonrad Rzeszutek Wilk return 0;
29231625340SJoseph Cihula
29331625340SJoseph Cihula tboot_copy_fadt(&acpi_gbl_FADT);
29431625340SJoseph Cihula tboot->acpi_sinfo.pm1a_cnt_val = pm1a_control;
29531625340SJoseph Cihula tboot->acpi_sinfo.pm1b_cnt_val = pm1b_control;
29631625340SJoseph Cihula /* we always use the 32b wakeup vector */
29731625340SJoseph Cihula tboot->acpi_sinfo.vector_width = 32;
29831625340SJoseph Cihula
29931625340SJoseph Cihula if (sleep_state >= ACPI_S_STATE_COUNT ||
30031625340SJoseph Cihula acpi_shutdown_map[sleep_state] == -1) {
3018d3bcc44SKefeng Wang pr_warn("unsupported sleep state 0x%x\n", sleep_state);
302a1f37788SKonrad Rzeszutek Wilk return -1;
30331625340SJoseph Cihula }
30431625340SJoseph Cihula
30531625340SJoseph Cihula tboot_shutdown(acpi_shutdown_map[sleep_state]);
30609f98a82STang Liang return 0;
30709f98a82STang Liang }
30831625340SJoseph Cihula
tboot_extended_sleep(u8 sleep_state,u32 val_a,u32 val_b)30901c6a6afSBen Guthro static int tboot_extended_sleep(u8 sleep_state, u32 val_a, u32 val_b)
31001c6a6afSBen Guthro {
31101c6a6afSBen Guthro if (!tboot_enabled())
31201c6a6afSBen Guthro return 0;
31301c6a6afSBen Guthro
3148d3bcc44SKefeng Wang pr_warn("tboot is not able to suspend on platforms with reduced hardware sleep (ACPIv5)");
31501c6a6afSBen Guthro return -ENODEV;
31601c6a6afSBen Guthro }
31701c6a6afSBen Guthro
31869575d38SShane Wang static atomic_t ap_wfs_count;
31969575d38SShane Wang
tboot_wait_for_aps(int num_aps)32069575d38SShane Wang static int tboot_wait_for_aps(int num_aps)
32131625340SJoseph Cihula {
32231625340SJoseph Cihula unsigned long timeout;
32331625340SJoseph Cihula
32469575d38SShane Wang timeout = AP_WAIT_TIMEOUT*HZ;
32569575d38SShane Wang while (atomic_read((atomic_t *)&tboot->num_in_wfs) != num_aps &&
32669575d38SShane Wang timeout) {
32769575d38SShane Wang mdelay(1);
32869575d38SShane Wang timeout--;
32969575d38SShane Wang }
33069575d38SShane Wang
33169575d38SShane Wang if (timeout)
3328d3bcc44SKefeng Wang pr_warn("tboot wait for APs timeout\n");
33369575d38SShane Wang
33469575d38SShane Wang return !(atomic_read((atomic_t *)&tboot->num_in_wfs) == num_aps);
33569575d38SShane Wang }
33669575d38SShane Wang
tboot_dying_cpu(unsigned int cpu)337ae6a8a2eSRichard Cochran static int tboot_dying_cpu(unsigned int cpu)
33869575d38SShane Wang {
33969575d38SShane Wang atomic_inc(&ap_wfs_count);
340ae6a8a2eSRichard Cochran if (num_online_cpus() == 1) {
34169575d38SShane Wang if (tboot_wait_for_aps(atomic_read(&ap_wfs_count)))
342ae6a8a2eSRichard Cochran return -EBUSY;
34369575d38SShane Wang }
344ae6a8a2eSRichard Cochran return 0;
34569575d38SShane Wang }
34669575d38SShane Wang
34713bfd47aSQiaowei Ren #ifdef CONFIG_DEBUG_FS
34813bfd47aSQiaowei Ren
34913bfd47aSQiaowei Ren #define TBOOT_LOG_UUID { 0x26, 0x25, 0x19, 0xc0, 0x30, 0x6b, 0xb4, 0x4d, \
35013bfd47aSQiaowei Ren 0x4c, 0x84, 0xa3, 0xe9, 0x53, 0xb8, 0x81, 0x74 }
35113bfd47aSQiaowei Ren
35213bfd47aSQiaowei Ren #define TBOOT_SERIAL_LOG_ADDR 0x60000
35313bfd47aSQiaowei Ren #define TBOOT_SERIAL_LOG_SIZE 0x08000
35413bfd47aSQiaowei Ren #define LOG_MAX_SIZE_OFF 16
35513bfd47aSQiaowei Ren #define LOG_BUF_OFF 24
35613bfd47aSQiaowei Ren
35713bfd47aSQiaowei Ren static uint8_t tboot_log_uuid[16] = TBOOT_LOG_UUID;
35813bfd47aSQiaowei Ren
tboot_log_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)35913bfd47aSQiaowei Ren static ssize_t tboot_log_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
36013bfd47aSQiaowei Ren {
36113bfd47aSQiaowei Ren void __iomem *log_base;
36213bfd47aSQiaowei Ren u8 log_uuid[16];
36313bfd47aSQiaowei Ren u32 max_size;
36413bfd47aSQiaowei Ren void *kbuf;
36513bfd47aSQiaowei Ren int ret = -EFAULT;
36613bfd47aSQiaowei Ren
3674bdc0d67SChristoph Hellwig log_base = ioremap(TBOOT_SERIAL_LOG_ADDR, TBOOT_SERIAL_LOG_SIZE);
36813bfd47aSQiaowei Ren if (!log_base)
36913bfd47aSQiaowei Ren return ret;
37013bfd47aSQiaowei Ren
37113bfd47aSQiaowei Ren memcpy_fromio(log_uuid, log_base, sizeof(log_uuid));
37213bfd47aSQiaowei Ren if (memcmp(&tboot_log_uuid, log_uuid, sizeof(log_uuid)))
37313bfd47aSQiaowei Ren goto err_iounmap;
37413bfd47aSQiaowei Ren
37513bfd47aSQiaowei Ren max_size = readl(log_base + LOG_MAX_SIZE_OFF);
37613bfd47aSQiaowei Ren if (*ppos >= max_size) {
37713bfd47aSQiaowei Ren ret = 0;
37813bfd47aSQiaowei Ren goto err_iounmap;
37913bfd47aSQiaowei Ren }
38013bfd47aSQiaowei Ren
38113bfd47aSQiaowei Ren if (*ppos + count > max_size)
38213bfd47aSQiaowei Ren count = max_size - *ppos;
38313bfd47aSQiaowei Ren
38413bfd47aSQiaowei Ren kbuf = kmalloc(count, GFP_KERNEL);
38513bfd47aSQiaowei Ren if (!kbuf) {
38613bfd47aSQiaowei Ren ret = -ENOMEM;
38713bfd47aSQiaowei Ren goto err_iounmap;
38813bfd47aSQiaowei Ren }
38913bfd47aSQiaowei Ren
39013bfd47aSQiaowei Ren memcpy_fromio(kbuf, log_base + LOG_BUF_OFF + *ppos, count);
39113bfd47aSQiaowei Ren if (copy_to_user(user_buf, kbuf, count))
39213bfd47aSQiaowei Ren goto err_kfree;
39313bfd47aSQiaowei Ren
39413bfd47aSQiaowei Ren *ppos += count;
39513bfd47aSQiaowei Ren
39613bfd47aSQiaowei Ren ret = count;
39713bfd47aSQiaowei Ren
39813bfd47aSQiaowei Ren err_kfree:
39913bfd47aSQiaowei Ren kfree(kbuf);
40013bfd47aSQiaowei Ren
40113bfd47aSQiaowei Ren err_iounmap:
40213bfd47aSQiaowei Ren iounmap(log_base);
40313bfd47aSQiaowei Ren
40413bfd47aSQiaowei Ren return ret;
40513bfd47aSQiaowei Ren }
40613bfd47aSQiaowei Ren
40713bfd47aSQiaowei Ren static const struct file_operations tboot_log_fops = {
40813bfd47aSQiaowei Ren .read = tboot_log_read,
40913bfd47aSQiaowei Ren .llseek = default_llseek,
41013bfd47aSQiaowei Ren };
41113bfd47aSQiaowei Ren
41213bfd47aSQiaowei Ren #endif /* CONFIG_DEBUG_FS */
41313bfd47aSQiaowei Ren
tboot_late_init(void)41469575d38SShane Wang static __init int tboot_late_init(void)
41569575d38SShane Wang {
41631625340SJoseph Cihula if (!tboot_enabled())
41731625340SJoseph Cihula return 0;
41831625340SJoseph Cihula
41911520e5eSLinus Torvalds tboot_create_trampoline();
42011520e5eSLinus Torvalds
42169575d38SShane Wang atomic_set(&ap_wfs_count, 0);
42273c1b41eSThomas Gleixner cpuhp_setup_state(CPUHP_AP_X86_TBOOT_DYING, "x86/tboot:dying", NULL,
423ae6a8a2eSRichard Cochran tboot_dying_cpu);
42413bfd47aSQiaowei Ren #ifdef CONFIG_DEBUG_FS
42513bfd47aSQiaowei Ren debugfs_create_file("tboot_log", S_IRUSR,
42613bfd47aSQiaowei Ren arch_debugfs_dir, NULL, &tboot_log_fops);
42713bfd47aSQiaowei Ren #endif
42813bfd47aSQiaowei Ren
429a1f37788SKonrad Rzeszutek Wilk acpi_os_set_prepare_sleep(&tboot_sleep);
43001c6a6afSBen Guthro acpi_os_set_prepare_extended_sleep(&tboot_extended_sleep);
43169575d38SShane Wang return 0;
43231625340SJoseph Cihula }
43331625340SJoseph Cihula
43469575d38SShane Wang late_initcall(tboot_late_init);
43569575d38SShane Wang
43631625340SJoseph Cihula /*
43731625340SJoseph Cihula * TXT configuration registers (offsets from TXT_{PUB, PRIV}_CONFIG_REGS_BASE)
43831625340SJoseph Cihula */
43931625340SJoseph Cihula
44031625340SJoseph Cihula #define TXT_PUB_CONFIG_REGS_BASE 0xfed30000
44131625340SJoseph Cihula #define TXT_PRIV_CONFIG_REGS_BASE 0xfed20000
44231625340SJoseph Cihula
44331625340SJoseph Cihula /* # pages for each config regs space - used by fixmap */
44431625340SJoseph Cihula #define NR_TXT_CONFIG_PAGES ((TXT_PUB_CONFIG_REGS_BASE - \
44531625340SJoseph Cihula TXT_PRIV_CONFIG_REGS_BASE) >> PAGE_SHIFT)
44631625340SJoseph Cihula
44731625340SJoseph Cihula /* offsets from pub/priv config space */
44831625340SJoseph Cihula #define TXTCR_HEAP_BASE 0x0300
44931625340SJoseph Cihula #define TXTCR_HEAP_SIZE 0x0308
45031625340SJoseph Cihula
45131625340SJoseph Cihula #define SHA1_SIZE 20
45231625340SJoseph Cihula
45331625340SJoseph Cihula struct sha1_hash {
45431625340SJoseph Cihula u8 hash[SHA1_SIZE];
45531625340SJoseph Cihula };
45631625340SJoseph Cihula
45731625340SJoseph Cihula struct sinit_mle_data {
45831625340SJoseph Cihula u32 version; /* currently 6 */
45931625340SJoseph Cihula struct sha1_hash bios_acm_id;
46031625340SJoseph Cihula u32 edx_senter_flags;
46131625340SJoseph Cihula u64 mseg_valid;
46231625340SJoseph Cihula struct sha1_hash sinit_hash;
46331625340SJoseph Cihula struct sha1_hash mle_hash;
46431625340SJoseph Cihula struct sha1_hash stm_hash;
46531625340SJoseph Cihula struct sha1_hash lcp_policy_hash;
46631625340SJoseph Cihula u32 lcp_policy_control;
46731625340SJoseph Cihula u32 rlp_wakeup_addr;
46831625340SJoseph Cihula u32 reserved;
46931625340SJoseph Cihula u32 num_mdrs;
47031625340SJoseph Cihula u32 mdrs_off;
47131625340SJoseph Cihula u32 num_vtd_dmars;
47231625340SJoseph Cihula u32 vtd_dmars_off;
47331625340SJoseph Cihula } __packed;
47431625340SJoseph Cihula
tboot_get_dmar_table(struct acpi_table_header * dmar_tbl)47531625340SJoseph Cihula struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl)
47631625340SJoseph Cihula {
47731625340SJoseph Cihula void *heap_base, *heap_ptr, *config;
47831625340SJoseph Cihula
47931625340SJoseph Cihula if (!tboot_enabled())
48031625340SJoseph Cihula return dmar_tbl;
48131625340SJoseph Cihula
48231625340SJoseph Cihula /*
48331625340SJoseph Cihula * ACPI tables may not be DMA protected by tboot, so use DMAR copy
48431625340SJoseph Cihula * SINIT saved in SinitMleData in TXT heap (which is DMA protected)
48531625340SJoseph Cihula */
48631625340SJoseph Cihula
48731625340SJoseph Cihula /* map config space in order to get heap addr */
48831625340SJoseph Cihula config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
48931625340SJoseph Cihula PAGE_SIZE);
49031625340SJoseph Cihula if (!config)
49131625340SJoseph Cihula return NULL;
49231625340SJoseph Cihula
49331625340SJoseph Cihula /* now map TXT heap */
49431625340SJoseph Cihula heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
49531625340SJoseph Cihula *(u64 *)(config + TXTCR_HEAP_SIZE));
49631625340SJoseph Cihula iounmap(config);
49731625340SJoseph Cihula if (!heap_base)
49831625340SJoseph Cihula return NULL;
49931625340SJoseph Cihula
50031625340SJoseph Cihula /* walk heap to SinitMleData */
50131625340SJoseph Cihula /* skip BiosData */
50231625340SJoseph Cihula heap_ptr = heap_base + *(u64 *)heap_base;
50331625340SJoseph Cihula /* skip OsMleData */
50431625340SJoseph Cihula heap_ptr += *(u64 *)heap_ptr;
50531625340SJoseph Cihula /* skip OsSinitData */
50631625340SJoseph Cihula heap_ptr += *(u64 *)heap_ptr;
50731625340SJoseph Cihula /* now points to SinitMleDataSize; set to SinitMleData */
50831625340SJoseph Cihula heap_ptr += sizeof(u64);
50931625340SJoseph Cihula /* get addr of DMAR table */
51031625340SJoseph Cihula dmar_tbl = (struct acpi_table_header *)(heap_ptr +
51131625340SJoseph Cihula ((struct sinit_mle_data *)heap_ptr)->vtd_dmars_off -
51231625340SJoseph Cihula sizeof(u64));
51331625340SJoseph Cihula
51431625340SJoseph Cihula /* don't unmap heap because dmar.c needs access to this */
51531625340SJoseph Cihula
51631625340SJoseph Cihula return dmar_tbl;
51731625340SJoseph Cihula }
518