Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

thunderbolt: Add support for preboot ACL

Preboot ACL is a mechanism that allows connecting Thunderbolt devices
boot time in more secure way than the legacy Thunderbolt boot support.
As with the legacy boot option, this also needs to be enabled from the
BIOS before booting is allowed. Difference to the legacy mode is that
the userspace software explicitly adds device UUIDs by sending a special
message to the ICM firmware. Only the devices listed in the boot ACL are
connected automatically during the boot. This works in both "user" and
"secure" security levels.

We implement this in Linux by exposing a new sysfs attribute (boot_acl)
below each Thunderbolt domain. The userspace software can then update
the full list as needed.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

+335 -10
+23
Documentation/ABI/testing/sysfs-bus-thunderbolt
··· 1 + What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl 2 + Date: Jun 2018 3 + KernelVersion: 4.17 4 + Contact: thunderbolt-software@lists.01.org 5 + Description: Holds a comma separated list of device unique_ids that 6 + are allowed to be connected automatically during system 7 + startup (e.g boot devices). The list always contains 8 + maximum supported number of unique_ids where unused 9 + entries are empty. This allows the userspace software 10 + to determine how many entries the controller supports. 11 + If there are multiple controllers, each controller has 12 + its own ACL list and size may be different between the 13 + controllers. 14 + 15 + System BIOS may have an option "Preboot ACL" or similar 16 + that needs to be selected before this list is taken into 17 + consideration. 18 + 19 + Software always updates a full list in each write. 20 + 21 + If a device is authorized automatically during boot its 22 + boot attribute is set to 1. 23 + 1 24 What: /sys/bus/thunderbolt/devices/.../domainX/security 2 25 Date: Sep 2017 3 26 KernelVersion: 4.13
+123
drivers/thunderbolt/domain.c
··· 119 119 [TB_SECURITY_DPONLY] = "dponly", 120 120 }; 121 121 122 + static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr, 123 + char *buf) 124 + { 125 + struct tb *tb = container_of(dev, struct tb, dev); 126 + uuid_t *uuids; 127 + ssize_t ret; 128 + int i; 129 + 130 + uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL); 131 + if (!uuids) 132 + return -ENOMEM; 133 + 134 + if (mutex_lock_interruptible(&tb->lock)) { 135 + ret = -ERESTARTSYS; 136 + goto out; 137 + } 138 + ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl); 139 + if (ret) { 140 + mutex_unlock(&tb->lock); 141 + goto out; 142 + } 143 + mutex_unlock(&tb->lock); 144 + 145 + for (ret = 0, i = 0; i < tb->nboot_acl; i++) { 146 + if (!uuid_is_null(&uuids[i])) 147 + ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb", 148 + &uuids[i]); 149 + 150 + ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s", 151 + i < tb->nboot_acl - 1 ? "," : "\n"); 152 + } 153 + 154 + out: 155 + kfree(uuids); 156 + return ret; 157 + } 158 + 159 + static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr, 160 + const char *buf, size_t count) 161 + { 162 + struct tb *tb = container_of(dev, struct tb, dev); 163 + char *str, *s, *uuid_str; 164 + ssize_t ret = 0; 165 + uuid_t *acl; 166 + int i = 0; 167 + 168 + /* 169 + * Make sure the value is not bigger than tb->nboot_acl * UUID 170 + * length + commas and optional "\n". Also the smallest allowable 171 + * string is tb->nboot_acl * ",". 172 + */ 173 + if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1) 174 + return -EINVAL; 175 + if (count < tb->nboot_acl - 1) 176 + return -EINVAL; 177 + 178 + str = kstrdup(buf, GFP_KERNEL); 179 + if (!str) 180 + return -ENOMEM; 181 + 182 + acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL); 183 + if (!acl) { 184 + ret = -ENOMEM; 185 + goto err_free_str; 186 + } 187 + 188 + uuid_str = strim(str); 189 + while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) { 190 + size_t len = strlen(s); 191 + 192 + if (len) { 193 + if (len != UUID_STRING_LEN) { 194 + ret = -EINVAL; 195 + goto err_free_acl; 196 + } 197 + ret = uuid_parse(s, &acl[i]); 198 + if (ret) 199 + goto err_free_acl; 200 + } 201 + 202 + i++; 203 + } 204 + 205 + if (s || i < tb->nboot_acl) { 206 + ret = -EINVAL; 207 + goto err_free_acl; 208 + } 209 + 210 + if (mutex_lock_interruptible(&tb->lock)) { 211 + ret = -ERESTARTSYS; 212 + goto err_free_acl; 213 + } 214 + ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl); 215 + mutex_unlock(&tb->lock); 216 + 217 + err_free_acl: 218 + kfree(acl); 219 + err_free_str: 220 + kfree(str); 221 + 222 + return ret ?: count; 223 + } 224 + static DEVICE_ATTR_RW(boot_acl); 225 + 122 226 static ssize_t security_show(struct device *dev, struct device_attribute *attr, 123 227 char *buf) 124 228 { ··· 233 129 static DEVICE_ATTR_RO(security); 234 130 235 131 static struct attribute *domain_attrs[] = { 132 + &dev_attr_boot_acl.attr, 236 133 &dev_attr_security.attr, 237 134 NULL, 238 135 }; 239 136 137 + static umode_t domain_attr_is_visible(struct kobject *kobj, 138 + struct attribute *attr, int n) 139 + { 140 + struct device *dev = container_of(kobj, struct device, kobj); 141 + struct tb *tb = container_of(dev, struct tb, dev); 142 + 143 + if (attr == &dev_attr_boot_acl.attr) { 144 + if (tb->nboot_acl && 145 + tb->cm_ops->get_boot_acl && 146 + tb->cm_ops->set_boot_acl) 147 + return attr->mode; 148 + return 0; 149 + } 150 + 151 + return attr->mode; 152 + } 153 + 240 154 static struct attribute_group domain_attr_group = { 155 + .is_visible = domain_attr_is_visible, 241 156 .attrs = domain_attrs, 242 157 }; 243 158
+149 -9
drivers/thunderbolt/icm.c
··· 56 56 * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides 57 57 * (only set when @upstream_port is not %NULL) 58 58 * @safe_mode: ICM is in safe mode 59 + * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) 59 60 * @is_supported: Checks if we can support ICM on this controller 60 61 * @get_mode: Read and return the ICM firmware mode (optional) 61 62 * @get_route: Find a route string for given switch ··· 70 69 struct mutex request_lock; 71 70 struct delayed_work rescan_work; 72 71 struct pci_dev *upstream_port; 72 + size_t max_boot_acl; 73 73 int vnd_cap; 74 74 bool safe_mode; 75 75 bool (*is_supported)(struct tb *tb); 76 76 int (*get_mode)(struct tb *tb); 77 77 int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); 78 78 int (*driver_ready)(struct tb *tb, 79 - enum tb_security_level *security_level); 79 + enum tb_security_level *security_level, 80 + size_t *nboot_acl); 80 81 void (*device_connected)(struct tb *tb, 81 82 const struct icm_pkg_header *hdr); 82 83 void (*device_disconnected)(struct tb *tb, ··· 253 250 } 254 251 255 252 static int 256 - icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level) 253 + icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level, 254 + size_t *nboot_acl) 257 255 { 258 256 struct icm_fr_pkg_driver_ready_response reply; 259 257 struct icm_pkg_driver_ready request = { ··· 831 827 return nhi_mailbox_mode(nhi); 832 828 } 833 829 830 + static int 831 + icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level, 832 + size_t *nboot_acl) 833 + { 834 + struct icm_ar_pkg_driver_ready_response reply; 835 + struct icm_pkg_driver_ready request = { 836 + .hdr.code = ICM_DRIVER_READY, 837 + }; 838 + int ret; 839 + 840 + memset(&reply, 0, sizeof(reply)); 841 + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 842 + 1, ICM_TIMEOUT); 843 + if (ret) 844 + return ret; 845 + 846 + if (security_level) 847 + *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK; 848 + if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED)) 849 + *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >> 850 + ICM_AR_INFO_BOOT_ACL_SHIFT; 851 + return 0; 852 + } 853 + 834 854 static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route) 835 855 { 836 856 struct icm_ar_pkg_get_route_response reply; ··· 874 846 return -EIO; 875 847 876 848 *route = get_route(reply.route_hi, reply.route_lo); 849 + return 0; 850 + } 851 + 852 + static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids) 853 + { 854 + struct icm_ar_pkg_preboot_acl_response reply; 855 + struct icm_ar_pkg_preboot_acl request = { 856 + .hdr = { .code = ICM_PREBOOT_ACL }, 857 + }; 858 + int ret, i; 859 + 860 + memset(&reply, 0, sizeof(reply)); 861 + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 862 + 1, ICM_TIMEOUT); 863 + if (ret) 864 + return ret; 865 + 866 + if (reply.hdr.flags & ICM_FLAGS_ERROR) 867 + return -EIO; 868 + 869 + for (i = 0; i < nuuids; i++) { 870 + u32 *uuid = (u32 *)&uuids[i]; 871 + 872 + uuid[0] = reply.acl[i].uuid_lo; 873 + uuid[1] = reply.acl[i].uuid_hi; 874 + 875 + if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) { 876 + /* Map empty entries to null UUID */ 877 + uuid[0] = 0; 878 + uuid[1] = 0; 879 + } else { 880 + /* Upper two DWs are always one's */ 881 + uuid[2] = 0xffffffff; 882 + uuid[3] = 0xffffffff; 883 + } 884 + } 885 + 886 + return ret; 887 + } 888 + 889 + static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids, 890 + size_t nuuids) 891 + { 892 + struct icm_ar_pkg_preboot_acl_response reply; 893 + struct icm_ar_pkg_preboot_acl request = { 894 + .hdr = { 895 + .code = ICM_PREBOOT_ACL, 896 + .flags = ICM_FLAGS_WRITE, 897 + }, 898 + }; 899 + int ret, i; 900 + 901 + for (i = 0; i < nuuids; i++) { 902 + const u32 *uuid = (const u32 *)&uuids[i]; 903 + 904 + if (uuid_is_null(&uuids[i])) { 905 + /* 906 + * Map null UUID to the empty (all one) entries 907 + * for ICM. 908 + */ 909 + request.acl[i].uuid_lo = 0xffffffff; 910 + request.acl[i].uuid_hi = 0xffffffff; 911 + } else { 912 + /* Two high DWs need to be set to all one */ 913 + if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff) 914 + return -EINVAL; 915 + 916 + request.acl[i].uuid_lo = uuid[0]; 917 + request.acl[i].uuid_hi = uuid[1]; 918 + } 919 + } 920 + 921 + memset(&reply, 0, sizeof(reply)); 922 + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), 923 + 1, ICM_TIMEOUT); 924 + if (ret) 925 + return ret; 926 + 927 + if (reply.hdr.flags & ICM_FLAGS_ERROR) 928 + return -EIO; 929 + 877 930 return 0; 878 931 } 879 932 ··· 1004 895 } 1005 896 1006 897 static int 1007 - __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level) 898 + __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, 899 + size_t *nboot_acl) 1008 900 { 1009 901 struct icm *icm = tb_priv(tb); 1010 902 unsigned int retries = 50; 1011 903 int ret; 1012 904 1013 - ret = icm->driver_ready(tb, security_level); 905 + ret = icm->driver_ready(tb, security_level, nboot_acl); 1014 906 if (ret) { 1015 907 tb_err(tb, "failed to send driver ready to ICM\n"); 1016 908 return ret; ··· 1278 1168 return 0; 1279 1169 } 1280 1170 1281 - return __icm_driver_ready(tb, &tb->security_level); 1171 + ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl); 1172 + if (ret) 1173 + return ret; 1174 + 1175 + /* 1176 + * Make sure the number of supported preboot ACL matches what we 1177 + * expect or disable the whole feature. 1178 + */ 1179 + if (tb->nboot_acl > icm->max_boot_acl) 1180 + tb->nboot_acl = 0; 1181 + 1182 + return 0; 1282 1183 } 1283 1184 1284 1185 static int icm_suspend(struct tb *tb) ··· 1385 1264 * Now all existing children should be resumed, start events 1386 1265 * from ICM to get updated status. 1387 1266 */ 1388 - __icm_driver_ready(tb, NULL); 1267 + __icm_driver_ready(tb, NULL, NULL); 1389 1268 1390 1269 /* 1391 1270 * We do not get notifications of devices that have been ··· 1438 1317 return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0); 1439 1318 } 1440 1319 1441 - /* Falcon Ridge and Alpine Ridge */ 1320 + /* Falcon Ridge */ 1442 1321 static const struct tb_cm_ops icm_fr_ops = { 1443 1322 .driver_ready = icm_driver_ready, 1444 1323 .start = icm_start, ··· 1446 1325 .suspend = icm_suspend, 1447 1326 .complete = icm_complete, 1448 1327 .handle_event = icm_handle_event, 1328 + .approve_switch = icm_fr_approve_switch, 1329 + .add_switch_key = icm_fr_add_switch_key, 1330 + .challenge_switch_key = icm_fr_challenge_switch_key, 1331 + .disconnect_pcie_paths = icm_disconnect_pcie_paths, 1332 + .approve_xdomain_paths = icm_fr_approve_xdomain_paths, 1333 + .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, 1334 + }; 1335 + 1336 + /* Alpine Ridge */ 1337 + static const struct tb_cm_ops icm_ar_ops = { 1338 + .driver_ready = icm_driver_ready, 1339 + .start = icm_start, 1340 + .stop = icm_stop, 1341 + .suspend = icm_suspend, 1342 + .complete = icm_complete, 1343 + .handle_event = icm_handle_event, 1344 + .get_boot_acl = icm_ar_get_boot_acl, 1345 + .set_boot_acl = icm_ar_set_boot_acl, 1449 1346 .approve_switch = icm_fr_approve_switch, 1450 1347 .add_switch_key = icm_fr_add_switch_key, 1451 1348 .challenge_switch_key = icm_fr_challenge_switch_key, ··· 1503 1364 case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI: 1504 1365 case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI: 1505 1366 case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: 1367 + icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; 1506 1368 icm->is_supported = icm_ar_is_supported; 1507 1369 icm->get_mode = icm_ar_get_mode; 1508 1370 icm->get_route = icm_ar_get_route; 1509 - icm->driver_ready = icm_fr_driver_ready; 1371 + icm->driver_ready = icm_ar_driver_ready; 1510 1372 icm->device_connected = icm_fr_device_connected; 1511 1373 icm->device_disconnected = icm_fr_device_disconnected; 1512 1374 icm->xdomain_connected = icm_fr_xdomain_connected; 1513 1375 icm->xdomain_disconnected = icm_fr_xdomain_disconnected; 1514 - tb->cm_ops = &icm_fr_ops; 1376 + tb->cm_ops = &icm_ar_ops; 1515 1377 break; 1516 1378 } 1517 1379
+4
drivers/thunderbolt/tb.h
··· 200 200 * @suspend: Connection manager specific suspend 201 201 * @complete: Connection manager specific complete 202 202 * @handle_event: Handle thunderbolt event 203 + * @get_boot_acl: Get boot ACL list 204 + * @set_boot_acl: Set boot ACL list 203 205 * @approve_switch: Approve switch 204 206 * @add_switch_key: Add key to switch 205 207 * @challenge_switch_key: Challenge switch using key ··· 219 217 void (*complete)(struct tb *tb); 220 218 void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type, 221 219 const void *buf, size_t size); 220 + int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids); 221 + int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids); 222 222 int (*approve_switch)(struct tb *tb, struct tb_switch *sw); 223 223 int (*add_switch_key)(struct tb *tb, struct tb_switch *sw); 224 224 int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw,
+34 -1
drivers/thunderbolt/tb_msgs.h
··· 102 102 ICM_ADD_DEVICE_KEY = 0x6, 103 103 ICM_GET_ROUTE = 0xa, 104 104 ICM_APPROVE_XDOMAIN = 0x10, 105 + ICM_PREBOOT_ACL = 0x18, 105 106 }; 106 107 107 108 enum icm_event_code { ··· 123 122 #define ICM_FLAGS_NO_KEY BIT(1) 124 123 #define ICM_FLAGS_SLEVEL_SHIFT 3 125 124 #define ICM_FLAGS_SLEVEL_MASK GENMASK(4, 3) 125 + #define ICM_FLAGS_WRITE BIT(7) 126 126 127 127 struct icm_pkg_driver_ready { 128 128 struct icm_pkg_header hdr; 129 129 }; 130 130 131 - /* Falcon Ridge & Alpine Ridge common messages */ 131 + /* Falcon Ridge only messages */ 132 132 133 133 struct icm_fr_pkg_driver_ready_response { 134 134 struct icm_pkg_header hdr; ··· 139 137 }; 140 138 141 139 #define ICM_FR_SLEVEL_MASK 0xf 140 + 141 + /* Falcon Ridge & Alpine Ridge common messages */ 142 142 143 143 struct icm_fr_pkg_get_topology { 144 144 struct icm_pkg_header hdr; ··· 278 274 279 275 /* Alpine Ridge only messages */ 280 276 277 + struct icm_ar_pkg_driver_ready_response { 278 + struct icm_pkg_header hdr; 279 + u8 romver; 280 + u8 ramver; 281 + u16 info; 282 + }; 283 + 284 + #define ICM_AR_INFO_SLEVEL_MASK GENMASK(3, 0) 285 + #define ICM_AR_INFO_BOOT_ACL_SHIFT 7 286 + #define ICM_AR_INFO_BOOT_ACL_MASK GENMASK(11, 7) 287 + #define ICM_AR_INFO_BOOT_ACL_SUPPORTED BIT(13) 288 + 281 289 struct icm_ar_pkg_get_route { 282 290 struct icm_pkg_header hdr; 283 291 u16 reserved; ··· 302 286 u16 link_info; 303 287 u32 route_hi; 304 288 u32 route_lo; 289 + }; 290 + 291 + struct icm_ar_boot_acl_entry { 292 + u32 uuid_lo; 293 + u32 uuid_hi; 294 + }; 295 + 296 + #define ICM_AR_PREBOOT_ACL_ENTRIES 16 297 + 298 + struct icm_ar_pkg_preboot_acl { 299 + struct icm_pkg_header hdr; 300 + struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES]; 301 + }; 302 + 303 + struct icm_ar_pkg_preboot_acl_response { 304 + struct icm_pkg_header hdr; 305 + struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES]; 305 306 }; 306 307 307 308 /* XDomain messages */
+2
include/linux/thunderbolt.h
··· 65 65 * @cm_ops: Connection manager specific operations vector 66 66 * @index: Linux assigned domain number 67 67 * @security_level: Current security level 68 + * @nboot_acl: Number of boot ACLs the domain supports 68 69 * @privdata: Private connection manager specific data 69 70 */ 70 71 struct tb { ··· 78 77 const struct tb_cm_ops *cm_ops; 79 78 int index; 80 79 enum tb_security_level security_level; 80 + size_t nboot_acl; 81 81 unsigned long privdata[0]; 82 82 }; 83 83