Re: [RFC v3][PATCH 2/2] intel_txt: Intel(R) TXT and tboot kernel support

Andrew Morton
2009-05-08T07:02:19Z

On Thu, 07 May 2009 21:49:07 -0700 Joseph Cihula <joseph.cihula@intel.com> wrote:

> Linux support for Intel(R) Trusted Execution Technology.
>
> --- linux-2.6.30-rc4/arch/x86/include/asm/fixmap.h 2009-04-29 21:48:16.000000000 -0700
> +++ linux-2.6.30-rc4-lkml/arch/x86/include/asm/fixmap.h 2009-05-07 08:07:17.000000000 -0700
> @@ -132,6 +132,9 @@ enum fixed_addresses {
> #ifdef CONFIG_X86_32
> FIX_WP_TEST,
> #endif
> +#ifdef CONFIG_INTEL_TXT
> + FIX_TBOOT_SHARED_BASE,
> +#endif

Curious. Does this "shared" page get documented anywhere in the code?

It can't use ioremap() or early_ioremap()?

> __end_of_fixed_addresses
> };
>
>
> ...
>
> +struct tboot_uuid {
> + u32 data1;
> + u16 data2;
> + u16 data3;
> + u16 data4;
> + u8 data5[6];
> +} __attribute__ ((__packed__));

Please use __packed everywhere.

> +/* used to communicate between tboot and the launched kernel */
> +
> +#define TB_KEY_SIZE 64 /* 512 bits */
> +
> +#define MAX_TB_MAC_REGIONS 32
> +struct tboot_mac_region {
> + u64 start; /* must be 64 byte -aligned */
> + u32 size; /* must be 64 byte -granular */
> +} __attribute__ ((__packed__));
> +
> +/* GAS - Generic Address Structure (ACPI 2.0+) */
> +struct tboot_acpi_generic_address {
> + u8 space_id;
> + u8 bit_width;
> + u8 bit_offset;
> + u8 access_width;
> + u64 address;
> +} __attribute__ ((__packed__));
> +
> +/* combines Sx info from FADT and FACS tables per ACPI 2.0+ spec
> + (http://www.acpi.info/) */
> +struct tboot_acpi_sleep_info {
> + struct tboot_acpi_generic_address pm1a_cnt_blk;
> + struct tboot_acpi_generic_address pm1b_cnt_blk;
> + struct tboot_acpi_generic_address pm1a_evt_blk;
> + struct tboot_acpi_generic_address pm1b_evt_blk;
> + u16 pm1a_cnt_val;
> + u16 pm1b_cnt_val;
> + u64 wakeup_vector;
> + u32 vector_width;
> + u64 kernel_s3_resume_vector;

Indenting broke in many places.

I didn't see a `depends on ACPI' in Kconfig. Is it needed?

> +} __attribute__ ((__packed__));
> +
> +struct tboot_shared {
> + /* version 3+ fields: */
> + struct tboot_uuid uuid; /* TBOOT_SHARED_UUID */
> + u32 version; /* Version number: 5 is current */
> + u32 log_addr; /* physical addr of tb_log_t log */
> + u32 shutdown_entry; /* entry point for tboot shutdown */
> + u32 shutdown_type; /* type of shutdown (TB_SHUTDOWN_*) */
> + struct tboot_acpi_sleep_info
> + acpi_sinfo; /* where kernel put acpi sleep info in Sx */
> + u32 tboot_base; /* starting addr for tboot */
> + u32 tboot_size; /* size of tboot */
> + u8 num_mac_regions; /* number mem regions to MAC on S3 */
> + /* contig regions memory to MAC on S3 */
> + struct tboot_mac_region mac_regions[MAX_TB_MAC_REGIONS];
> + /* version 4+ fields: */
> + /* populated by tboot; will be encrypted */
> + u8 s3_key[TB_KEY_SIZE];
> + /* version 5+ fields: */
> + u8 reserved_align[3]; /* used to 4byte-align num_in_wfs */
> + u32 num_in_wfs; /* number of processors in wait-for-SIPI */
> +} __attribute__ ((__packed__));
> +
> +/* UUID for tboot_shared data struct to facilitate matching */
> +/* {663C8DFF-E8B3-4b82-AABF-19EA4D057A08} */
> +#define TBOOT_SHARED_UUID \
> + ((struct tboot_uuid){ 0x663c8dff, 0xe8b3, 0x4b82, 0xaabf, \
> + { 0x19, 0xea, 0x4d, 0x5, 0x7a, 0x8 } })

Strange. A

static struct tboot_uuid uuid __initdata = { ... };

within tboot_probe() would suffice.

> +extern struct tboot_shared *tboot_shared;
> +
> +static inline int tboot_in_measured_env(void)
> +{
> + return tboot_shared != NULL;
> +}
> +
>
> ...
>
> +
> +#include <linux/init.h>
> +#include <linux/sched.h>
> +#include <linux/pfn.h>
> +#include <linux/mm.h>
> +#include <linux/spinlock.h>
> +#include <linux/init_task.h>

a newline here is typical

> +#include <asm/pgtable.h>
> +#include <asm/pgalloc.h>
> +#include <asm/processor.h>
> +#include <asm/bootparam.h>
> +#include <asm/setup.h>
> +#include <asm/io.h>
> +#include <asm/e820.h>
> +#include <asm/tboot.h>
> +
> +/* Global pointer to shared data; NULL means no measured launch. */
> +struct tboot_shared *tboot_shared __read_mostly;
> +
> +void __init tboot_probe(void)
> +{
> + /* Look for valid page-aligned address for shared page. */
> + if (boot_params.tboot_shared_addr == 0)
> + return;
> + /* also verify that it is mapped as we expect it before calling
> + set_fixmap(), to reduce chance of garbage value causing crash */
> + if (!e820_any_mapped(boot_params.tboot_shared_addr,
> + boot_params.tboot_shared_addr, E820_UNUSABLE)) {
> + printk(KERN_WARNING "TXT: non-0 tboot_shared_addr but it is not of type E820_UNUSABLE\n");
> + return;
> + }
> +
> + /* only a natively booted kernel should be using TXT */
> + if (paravirt_enabled()) {
> + printk(KERN_WARNING "TXT: non-0 tboot_shared_addr but pv_ops is enabled\n");
> + return;
> + }
> +
> + /* Map and check for tboot UUID. */
> + set_fixmap(FIX_TBOOT_SHARED_BASE, boot_params.tboot_shared_addr);
> + tboot_shared = (struct tboot_shared *)
> + fix_to_virt(FIX_TBOOT_SHARED_BASE);
> + if (memcmp(&TBOOT_SHARED_UUID, &tboot_shared->uuid,
> + sizeof(struct tboot_uuid))) {
> + printk(KERN_WARNING "TXT: tboot_shared at 0x%lx is invalid\n",
> + (unsigned long)boot_params.tboot_shared_addr);

That's a peculiar way of printing a u64, especially on 32 bit.

The code appears to be enabled for 32-bit. Is that a
supported/tested/realistic combination?

> + tboot_shared = NULL;
> + return;
> + }
> + if (tboot_shared->version < 5) {
> + printk(KERN_WARNING "TXT: tboot_shared version is invalid: %u\n",
> + tboot_shared->version);
> + tboot_shared = NULL;
> + return;
> + }
> +
> + printk(KERN_INFO "TXT: found shared page at phys addr 0x%lx:\n",
> + (unsigned long)boot_params.tboot_shared_addr);
> + printk(KERN_DEBUG "TXT: version: %d\n", tboot_shared->version);
> + printk(KERN_DEBUG "TXT: log_addr: 0x%08x\n", tboot_shared->log_addr);
> + printk(KERN_DEBUG "TXT: shutdown_entry: 0x%x\n",
> + tboot_shared->shutdown_entry);
> + printk(KERN_DEBUG "TXT: tboot_base: 0x%08x\n",
> + tboot_shared->tboot_base);
> + printk(KERN_DEBUG "TXT: tboot_size: 0x%x\n",
> + tboot_shared->tboot_size);
> +}
> +
>
> ...
>
> +void tboot_shutdown(u32 shutdown_type)
> +{
> + if (!tboot_in_measured_env())
> + return;
> +
> + /* if we're being called before the 1:1 mapping is set up then just
> + return and let the normal shutdown happen; this should only be
> + due to very early panic() */
> + if (!tboot_pg_dir)
> + return;
> +
> + local_irq_disable();

Mystery local_irq_disable() needs a comment, methinks.

> + /* if this is S3 then set regions to MAC */
> + if (shutdown_type == TB_SHUTDOWN_S3) {
> + tboot_shared->num_mac_regions = 3;
> + /* S3 resume code */
> + tboot_shared->mac_regions[0].start =
> + PFN_PHYS(PFN_DOWN(acpi_wakeup_address));
> + tboot_shared->mac_regions[0].size =
> + PFN_UP(WAKEUP_SIZE) << PAGE_SHIFT;
> + /* AP trampoline code */
> + tboot_shared->mac_regions[1].start =
> + PFN_PHYS(PFN_DOWN(virt_to_phys(trampoline_base)));
> + tboot_shared->mac_regions[1].size =
> + PFN_UP(TRAMPOLINE_SIZE) << PAGE_SHIFT;
> + /* kernel code + data + bss */
> + tboot_shared->mac_regions[2].start =
> + PFN_PHYS(PFN_DOWN(virt_to_phys(&_text)));
> + tboot_shared->mac_regions[2].size =
> + PFN_PHYS(PFN_UP(virt_to_phys(&_end))) -
> + PFN_PHYS(PFN_DOWN(virt_to_phys(&_text)));
> + }
> +
> + tboot_shared->shutdown_type = shutdown_type;
> +
> + switch_to_tboot_pt();
> +
> + ((void(*)(void))(unsigned long)tboot_shared->shutdown_entry)();
> +
> + /* should not reach here */
> + while (1)
> + halt();
> +}
> +
>
> ...
>
> +struct acpi_table_header *tboot_get_dmar_table(void)
> +{
> + void *heap_base, *heap_ptr, *config;
> + struct acpi_table_header *dmar_table;
> +
> + /* ACPI tables may not be DMA protected by tboot, so use DMAR copy */
> + /* SINIT saved in SinitMleData in TXT heap (which is DMA protected) */
> +
> + /* map config space in order to get heap addr */
> + config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES *
> + PAGE_SIZE);
> + if (config == NULL)
> + return NULL;
> +
> + /* now map TXT heap */
> + heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE),
> + *(u64 *)(config + TXTCR_HEAP_SIZE));
> + iounmap(config);
> + if (heap_base == NULL)
> + return NULL;
> +
> + /* walk heap to SinitMleData */
> + /* skip BiosData */
> + heap_ptr = heap_base + *(u64 *)heap_base;
> + /* skip OsMleData */
> + heap_ptr += *(u64 *)heap_ptr;
> + /* skip OsSinitData */
> + heap_ptr += *(u64 *)heap_ptr;
> + /* now points to SinitMleDataSize; set to SinitMleData */
> + heap_ptr += sizeof(u64);
> + /* get addr of DMAR table */
> + dmar_table = (struct acpi_table_header *)(heap_ptr +
> + ((struct sinit_mle_data *)heap_ptr)->vtd_dmars_off -
> + sizeof(u64));
> +
> + /* don't unmap heap because dmar.c needs access to this */
> +
> + return dmar_table;
> +}

This function trusts BIOS authors rather a lot.

>
> ...
>
> +config INTEL_TXT
> + bool "Enable Intel(R) Trusted Execution Technology (Intel(R) TXT)"
> + depends on EXPERIMENTAL && X86 && DMAR
> + help
> + This option enables support for booting the kernel with the
> + Trusted Boot (tboot) module. This will utilize
> + Intel(R) Trusted Execution Technology to perform a measured launch
> + of the kernel. If the system does not support Intel(R) TXT, this
> + will have no effect.
> +
> + Intel TXT will provide higher assurance of sysem configuration and
> + initial state as well as data reset protection. This is used to
> + create a robust initial kernel measurement and verification.
> +
> + See <http://www.intel.com/technology/security/> for more information
> + about Intel(R) TXT.
> + See <http://tboot.sourceforge.net> for more information about tboot.
> + See Documentation/intel_txt.txt for a description of how to enable
> + Intel TXT support in a kernel boot.
> +
> + If you are unsure as to whether this is required, answer N.
> +

The help uses a mix of tab- and space-indenting which is why it looks
all messy when quoted.