Skip to content
Permalink
Browse files
x86/tdx: Add device filter support for x86 TDX guest platform
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 this requirement 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 a new variable "dev_default_authorization" in
driver core which is used to set the default policy (ALLOW ALL/
DENY ALL) of the "authorized" attribute in device_initialize(). But
arch code can override the default value by updating
dev_default_authorization.

Add support for prot_guest_authorized() which can be used by BUS
drivers to consult the arch specific device allow list and initialize
the "authorized" attribute.

To allow user to override the arch specific allow list use,
"authorize_allow_devs" and "authorize_deny_devs" command line options.

Currently in TDX guest, only following devices are allowed:

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

Also enable PATTR_GUEST_DEVICE_FILTER protected guest feature for the
TDX guest.

[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 8, 2021
1 parent 3a330a5 commit 1ab75a6ac332c35e871f73765bd531170798b615
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 1 deletion.
@@ -359,6 +359,19 @@
autoconf= [IPV6]
See Documentation/networking/ipv6.rst.

authorize_deny_devs=
authorize_allow_devs= [KNL]
Format: bus_name:dev_name
Override default authorization of given device.
Multiple devices can be specified in comma
separated list. Multiple bus/devices combinations
can be specified separated by semicolon. For example,
to allow vritio device and PCI device,
use virtio:virtio0;pci:vendor:device;. To allow all
devices in PCI bus use pci:ALL or to allow all bus
and devices use ALL:ALL. Bus drivers can scan through
this list before initializing the authorized attribute.

show_lapic= [APIC,X86] Advanced Programmable Interrupt Controller
Limit apic dumping. The parameter defines the maximal
number of local apics being dumped. Also it is possible
@@ -12,6 +12,9 @@

#include <linux/mem_encrypt.h>
#include <linux/processor.h>
#include <linux/device.h>

#include <asm/tdx.h>

#ifndef __ASSEMBLY__

@@ -31,6 +34,14 @@ static inline bool prot_guest_has(unsigned int attr)
return false;
}

static inline bool prot_guest_authorized(struct device *dev, char *dev_str)
{
if (cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
return tdx_guest_authorized(dev, dev_str);

return dev->authorized;
}

#endif /* __ASSEMBLY__ */

#endif /* _X86_PROTECTED_GUEST_H */
@@ -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,
@@ -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, char *dev_str);

/*
* To support I/O port access in decompressor or early kernel init
* code, since #VE exception handler cannot be used, use paravirt
@@ -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, char *dev_str)
{
return dev->authorized;
}

#endif /* CONFIG_INTEL_TDX_GUEST */

#ifdef CONFIG_KVM_GUEST
@@ -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
@@ -84,6 +84,7 @@ bool intel_prot_guest_has(unsigned int flag)
case PATTR_GUEST_MEM_ENCRYPT:
case PATTR_GUEST_SHARED_MAPPING_INIT:
case PATTR_MEM_ENCRYPT:
case PATTR_GUEST_DEVICE_FILTER:
return cpu_feature_enabled(X86_FEATURE_TDX_GUEST);
}

@@ -0,0 +1,216 @@
// 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/protected_guest.h>

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

#define CMDLINE_MAX_NODES 100
#define CMDLINE_MAX_LEN 1000

#define ADD_FILTER_NODE(bname, dlist) \
{ \
.bus = bname, \
.dev_str = dlist, \
}

struct authorize_node {
const char *bus;
const char *dev_str;
};

/*
* Memory to store data passed via command line options
* authorize_allow_devs/authorize_deny_devs.
*/
static char cmd_authorized_devices[CMDLINE_MAX_LEN];
static char cmd_denied_devices[CMDLINE_MAX_LEN];
static struct authorize_node cmd_allowed_nodes[CMDLINE_MAX_NODES];
static struct authorize_node cmd_denied_nodes[CMDLINE_MAX_NODES];
static int cmd_allowed_nodes_len;
static int cmd_denied_nodes_len;

/* Set true if authorize_allow_devs/authorize_deny_devs is used */
static bool filter_overridden;

/* Allow list for PCI bus */
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 */
ADD_FILTER_NODE("virtio", "ALL"),
/* Allow devices in pci_allow_list in "pci" bus */
ADD_FILTER_NODE("pci", pci_allow_list),
};

/* Block all devices by default */
static struct authorize_node deny_list[] = {
ADD_FILTER_NODE("ALL", "ALL")
};

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

/* If bus and dev_str matches "ALL", return true */
if (!strcmp(node->bus, "ALL") && !strcmp(node->dev_str, "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_str, "ALL"))
return true;

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

return false;
}

static __init void add_authorize_nodes(char *p, bool allow)
{
struct authorize_node *n;
int j = 0;
char *k;

while ((k = strsep(&p, ";")) != NULL) {
if (j >= CMDLINE_MAX_NODES) {
pr_err("Authorize nodes exceeds MAX allowed\n");
break;
}
if (allow)
n = &cmd_allowed_nodes[j++];
else
n = &cmd_denied_nodes[j++];

n->bus = strsep(&k, ":");
n->dev_str = k;
}

if (j) {
if (allow)
cmd_allowed_nodes_len = j;
else
cmd_denied_nodes_len = j;
}
}

static __init int allowed_cmdline_setup(char *buf)
{
if (strlen(buf) >= CMDLINE_MAX_LEN)
pr_warn("Authorized allowed devices list exceed %d chars\n",
CMDLINE_MAX_LEN);

strscpy(cmd_authorized_devices, buf, CMDLINE_MAX_LEN);

add_authorize_nodes(cmd_authorized_devices, 1);

filter_overridden = true;

return 0;
}
__setup("authorize_allow_devs=", allowed_cmdline_setup);

static __init int denied_cmdline_setup(char *buf)
{
if (strlen(buf) >= CMDLINE_MAX_LEN)
pr_warn("Authorized denied devices list exceed %d chars\n",
CMDLINE_MAX_LEN);

strscpy(cmd_denied_devices, buf, CMDLINE_MAX_LEN);

add_authorize_nodes(cmd_denied_devices, 0);

filter_overridden = true;

return 0;
}
__setup("authorize_deny_devs=", denied_cmdline_setup);

bool tdx_guest_authorized(struct device *dev, char *dev_str)
{
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,
dev_str))
return true;
}

/* Lookup command line allow list */
for (i = 0; i < cmd_allowed_nodes_len; i++) {
if (authorized_node_match(&cmd_allowed_nodes[i], dev->bus->name,
dev_str))
return true;
}

/* Lookup arch deny list */
for (i = 0; i < ARRAY_SIZE(deny_list); i++) {
if (authorized_node_match(&deny_list[i], dev->bus->name,
dev_str))
return false;
}

/* Lookup command line deny list */
for (i = 0; i < cmd_denied_nodes_len; i++) {
if (authorized_node_match(&cmd_denied_nodes[i], dev->bus->name,
dev_str))
return false;
}

return dev->authorized;
}

void __init tdx_filter_init(void)
{
if (!prot_guest_has(PATTR_GUEST_DEVICE_FILTER))
return;

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

if (filter_overridden) {
/*
* Since the default allow/deny list is overridden
* to make sure new drivers use ioremap_host_shared,
* force it on all drivers.
*/
ioremap_force_shared = true;
add_taint(TAINT_CONF_NO_LOCKDOWN, LOCKDEP_STILL_OK);
}

pr_info("Enabled TDX guest device filter\n");
}
@@ -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;

@@ -46,6 +46,12 @@ static int __init sysfs_deprecated_setup(char *arg)
early_param("sysfs.deprecated", sysfs_deprecated_setup);
#endif

/*
* Default authorization status set as allow all. It can be
* overridden by arch code.
*/
bool __ro_after_init dev_default_authorization = true;

/* Device links support. */
static LIST_HEAD(deferred_sync);
static unsigned int defer_sync_state_count = 1;
@@ -2849,6 +2855,7 @@ void device_initialize(struct device *dev)
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
dev->dma_coherent = dma_default_coherent;
#endif
dev->authorized = dev_default_authorization;
}
EXPORT_SYMBOL_GPL(device_initialize);

@@ -955,6 +955,8 @@ int devtmpfs_mount(void);
static inline int devtmpfs_mount(void) { return 0; }
#endif

extern bool dev_default_authorization;

/* drivers/base/power/shutdown.c */
void device_shutdown(void);

0 comments on commit 1ab75a6

Please sign in to comment.