Skip to content

Commit

Permalink
x86/tdx: Add device filter support for x86 TDX guest platform
Browse files Browse the repository at this point in the history
For Confidential VM guests like TDX, the host is untrusted and hence
the devices emulated by the host or any data coming from the host
cannot be trusted. So the drivers that interact with the outside world
have to be hardened and the allowed devices have to be filtered. More
details about the need for device/driver filter in confidential guest
can be found in article [1] titled "firewall for device drivers".

So use the "authorized" device attribute to allow only the trusted list
of the devices. Add support for cc_guest_authorized() which can be used
by BUS drivers to consult the arch specific device allow list and
initialize the "authorized" attribute. To deny all devices other than
what is specified in TDX allow list, set dev_default_authorization
default policy as DENY_ALL.

The default audited list of drivers that a protected guest may trust
are:

 * virtio-blk
 * virtio-console
 * virtio-net
 * virtio-pci
 * virtio_rproc_serial

Add a new flag CC_ATTR_GUEST_DEVICE_FILTER to conditionally enable
device filter related code in generic drivers (using cc_platform_has()
API).

[1] - https://lwn.net/Articles/865918/

Reviewed-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
  • Loading branch information
Kuppuswamy Sathyanarayanan committed Sep 22, 2021
1 parent 8e9591e commit b0040b6
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 1 deletion.
8 changes: 8 additions & 0 deletions arch/x86/include/asm/tdx.h
Expand Up @@ -68,6 +68,7 @@ enum tdx_map_type {
#ifdef CONFIG_INTEL_TDX_GUEST

void __init tdx_early_init(void);
void __init tdx_filter_init(void);

/* Helper function used to communicate with the TDX module */
u64 __tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
Expand Down Expand Up @@ -95,6 +96,8 @@ int tdx_hcall_get_quote(u64 data);

extern void (*tdx_event_notify_handler)(void);

bool tdx_guest_authorized(struct device *dev);

/*
* To support I/O port access in decompressor or early kernel init
* code, since #VE exception handler cannot be used, use paravirt
Expand Down Expand Up @@ -167,6 +170,11 @@ static inline int tdx_hcall_gpa_intent(phys_addr_t gpa, int numpages,
return -ENODEV;
}

static inline bool tdx_guest_authorized(struct device *dev)
{
return dev->authorized;
}

#endif /* CONFIG_INTEL_TDX_GUEST */

#if defined(CONFIG_KVM_GUEST) && defined(CONFIG_INTEL_TDX_GUEST)
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/Makefile
Expand Up @@ -128,7 +128,7 @@ obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o
obj-$(CONFIG_X86_PMEM_LEGACY_DEVICE) += pmem.o

obj-$(CONFIG_JAILHOUSE_GUEST) += jailhouse.o
obj-$(CONFIG_INTEL_TDX_GUEST) += tdcall.o tdx.o
obj-$(CONFIG_INTEL_TDX_GUEST) += tdcall.o tdx.o tdx-filter.o

obj-$(CONFIG_EISA) += eisa.o
obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o
Expand Down
13 changes: 13 additions & 0 deletions arch/x86/kernel/cc_platform.c
Expand Up @@ -9,8 +9,12 @@

#include <linux/export.h>
#include <linux/cc_platform.h>
#include <linux/cc_device.h>
#include <linux/mem_encrypt.h>
#include <linux/processor.h>
#include <linux/device.h>

#include <asm/tdx.h>

bool cc_platform_has(enum cc_attr attr)
{
Expand All @@ -22,3 +26,12 @@ bool cc_platform_has(enum cc_attr attr)
return false;
}
EXPORT_SYMBOL_GPL(cc_platform_has);

bool cc_guest_authorized(struct device *dev)
{
if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
return tdx_guest_authorized(dev);

return dev->authorized;
}
EXPORT_SYMBOL_GPL(cc_guest_authorized);
1 change: 1 addition & 0 deletions arch/x86/kernel/cpu/intel.c
Expand Up @@ -84,6 +84,7 @@ bool intel_cc_platform_has(enum cc_attr attr)
case CC_ATTR_GUEST_MEM_ENCRYPT:
case CC_ATTR_GUEST_SHARED_MAPPING_INIT:
case CC_ATTR_MEM_ENCRYPT:
case CC_ATTR_GUEST_DEVICE_FILTER:
return cpu_feature_enabled(X86_FEATURE_TDX_GUEST);
default:
return false;
Expand Down
131 changes: 131 additions & 0 deletions arch/x86/kernel/tdx-filter.c
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 Intel Corporation
*/
#define pr_fmt(fmt) "TDX: " fmt

#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/cc_platform.h>
#include <linux/export.h>

#include <asm/tdx.h>
#include <asm/cmdline.h>

/*
* To support regex formats like (ALL:ALL), device allow
* list uses char* type. Alternative choices like device_id
* model will only add additional complexity. Using char*
* will make it easier to add command line overrides.
*/
struct authorize_node {
const char *bus;
const char *dev_list;
};

/* Temporary string for storing device name */
static char dev_str[16];

/*
* Allow list for PCI bus
*
* NOTE: Device ID is duplicated here. But for small list
* of devices, it is easier to maintain the duplicated list
* here verses exporting the device ID table from the driver
* and use it.
*/
static const char pci_allow_list[] =
"0x1af4:0x1000," /* Virtio NET */
"0x1af4:0x1001," /* Virtio block */
"0x1af4:0x1003," /* Virtio console */
"0x1af4:0x1009," /* Virtio FS */
"0x1af4:0x1041," /* Virtio 1.0 NET */
"0x1af4:0x1042," /* Virtio 1.0 block */
"0x1af4:0x1043," /* Virtio 1.0 console */
"0x1af4:0x1049"; /* Virtio 1.0 FS */

static struct authorize_node allow_list[] = {
/* Enable all devices in "virtio" bus */
{ "virtio", "ALL" },
/* Allow devices in pci_allow_list in "pci" bus */
{ "pci", pci_allow_list },
};

static bool authorized_node_match(struct authorize_node *node,
const char *bus_name, const char *dev_list)
{
const char *n;
int len;

/* If bus and dev_list matches "ALL", return true */
if (!strcmp(node->bus, "ALL") && !strcmp(node->dev_list, "ALL"))
return true;

/*
* Since next step involves bus specific comparison, make
* sure the bus name matches with filter node. If not
* return false.
*/
if (strcmp(node->bus, bus_name))
return false;

/* If device name is "ALL", allow all */
if (!strcmp(node->dev_list, "ALL"))
return true;

for (n = node->dev_list; *n; n += len) {
if (*n == ',')
n++;
len = strcspn(n, ",");
if (!strncmp(dev_list, n, len))
return true;
}

return false;
}

char *get_dev_name(struct device *dev)
{
struct pci_dev *pdev;

/* For PCI, use format vendor:device */
if (!strncmp(dev->bus->name, "pci", 3)) {
pdev = to_pci_dev(dev);
sprintf(dev_str, "0x%x:0x%x", pdev->vendor, pdev->device);

return dev_str;
}

/* For other bus, just use device name */
return (char *)dev_name(dev);
}

bool tdx_guest_authorized(struct device *dev)
{
int i;

if (!dev->bus)
return dev->authorized;

/* Lookup arch allow list */
for (i = 0; i < ARRAY_SIZE(allow_list); i++) {
if (authorized_node_match(&allow_list[i], dev->bus->name,
get_dev_name(dev)))
return true;
}

return dev_default_authorization;
}
EXPORT_SYMBOL_GPL(tdx_guest_authorized);

void __init tdx_filter_init(void)
{
if (!cc_platform_has(CC_ATTR_GUEST_DEVICE_FILTER))
return;

/* Set default authorization as disabled */
dev_default_authorization = false;

pr_info("Enabled TDX guest device filter\n");
}
2 changes: 2 additions & 0 deletions arch/x86/kernel/tdx.c
Expand Up @@ -778,6 +778,8 @@ void __init tdx_early_init(void)

tdx_get_info();

tdx_filter_init();

pv_ops.irq.safe_halt = tdx_safe_halt;
pv_ops.irq.halt = tdx_halt;

Expand Down
42 changes: 42 additions & 0 deletions include/linux/cc_device.h
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Confidential Computing Device Authorization Header
*
* Copyright (C) 2021 Intel Corporation, Inc.
*
* Author: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
*/

#ifndef _CC_DEVICE_AUTHORIZED_H
#define _CC_DEVICE_AUTHORIZED_H

# ifndef __ASSEMBLY__

# include <linux/device.h>
# include <linux/cc_platform.h>

/*
* cc_guest_authorized() - Used to get ARCH specific authorized status
* of the given device.
* @dev - device structure
*
* Return True to allow the device or False to deny it.
*
*/

# ifdef CONFIG_ARCH_HAS_CC_PLATFORM

bool cc_guest_authorized(struct device *dev);

# else /* !CONFIG_ARCH_HAS_CC_PLATFORM */

static inline bool cc_guest_authorized(struct device *dev)
{
return dev->authorized;
}

# endif /* CONFIG_ARCH_HAS_CC_PLATFORM */

# endif /* __ASSEMBLY__ */

#endif /* _CC_PLATFORM_H */
11 changes: 11 additions & 0 deletions include/linux/cc_platform.h
Expand Up @@ -94,6 +94,17 @@ enum cc_attr {
*/
CC_ATTR_GUEST_SHARED_MAPPING_INIT,

/**
* @CC_ATTR_GUEST_DEVICE_FILTER: Filter device enumeration as per
* platform specific allow list.
*
* The platform/OS is running as a guest/virtual machine and allows or
* dis-allows device enumeration as per platform specific allow or
* deny list.
*
* Examples include TDX guest.
*/
CC_ATTR_GUEST_DEVICE_FILTER,
};

#ifdef CONFIG_ARCH_HAS_CC_PLATFORM
Expand Down

0 comments on commit b0040b6

Please sign in to comment.