xref: /openbmc/linux/arch/x86/kernel/tboot.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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