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

cxl/mem: Add a "RAW" send command

The CXL memory device send interface will have a number of supported
commands. The raw command is not such a command. Raw commands allow
userspace to send a specified opcode to the underlying hardware and
bypass all driver checks on the command. The primary use for this
command is to [begrudgingly] allow undocumented vendor specific hardware
commands.

While not the main motivation, it also allows prototyping new hardware
commands without a driver patch and rebuild.

While this all sounds very powerful it comes with a couple of caveats:
1. Bug reports using raw commands will not get the same level of
attention as bug reports using supported commands (via taint).
2. Supported commands will be rejected by the RAW command.

With this comes new debugfs knob to allow full access to your toes with
your weapon of choice.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com> (v2)
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Ariel Sibley <Ariel.Sibley@microchip.com>
Link: https://lore.kernel.org/r/20210217040958.1354670-6-ben.widawsky@intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

authored by

Ben Widawsky and committed by
Dan Williams
13237183 583fa5e7

+161 -1
+18
drivers/cxl/Kconfig
··· 32 32 Chapter 2.3 Type 3 CXL Device in the CXL 2.0 specification. 33 33 34 34 If unsure say 'm'. 35 + 36 + config CXL_MEM_RAW_COMMANDS 37 + bool "RAW Command Interface for Memory Devices" 38 + depends on CXL_MEM 39 + help 40 + Enable CXL RAW command interface. 41 + 42 + The CXL driver ioctl interface may assign a kernel ioctl command 43 + number for each specification defined opcode. At any given point in 44 + time the number of opcodes that the specification defines and a device 45 + may implement may exceed the kernel's set of associated ioctl function 46 + numbers. The mismatch is either by omission, specification is too new, 47 + or by design. When prototyping new hardware, or developing / debugging 48 + the driver it is useful to be able to submit any possible command to 49 + the hardware, even commands that may crash the kernel due to their 50 + potential impact to memory currently in use by the kernel. 51 + 52 + If developing CXL hardware or the driver say Y, otherwise say N. 35 53 endif
+132
drivers/cxl/mem.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ 3 3 #include <uapi/linux/cxl_mem.h> 4 + #include <linux/security.h> 5 + #include <linux/debugfs.h> 4 6 #include <linux/module.h> 5 7 #include <linux/mutex.h> 6 8 #include <linux/cdev.h> ··· 44 42 45 43 enum opcode { 46 44 CXL_MBOX_OP_INVALID = 0x0000, 45 + CXL_MBOX_OP_RAW = CXL_MBOX_OP_INVALID, 46 + CXL_MBOX_OP_ACTIVATE_FW = 0x0202, 47 47 CXL_MBOX_OP_IDENTIFY = 0x4000, 48 + CXL_MBOX_OP_SET_PARTITION_INFO = 0x4101, 49 + CXL_MBOX_OP_SET_LSA = 0x4103, 50 + CXL_MBOX_OP_SET_SHUTDOWN_STATE = 0x4204, 51 + CXL_MBOX_OP_SCAN_MEDIA = 0x4304, 52 + CXL_MBOX_OP_GET_SCAN_MEDIA = 0x4305, 48 53 CXL_MBOX_OP_MAX = 0x10000 49 54 }; 50 55 ··· 105 96 106 97 static int cxl_mem_major; 107 98 static DEFINE_IDA(cxl_memdev_ida); 99 + static struct dentry *cxl_debugfs; 100 + static bool cxl_raw_allow_all; 108 101 109 102 /** 110 103 * struct cxl_mem_command - Driver representation of a memory device command ··· 143 132 */ 144 133 static struct cxl_mem_command mem_commands[] = { 145 134 CXL_CMD(IDENTIFY, 0, 0x43), 135 + #ifdef CONFIG_CXL_MEM_RAW_COMMANDS 136 + CXL_CMD(RAW, ~0, ~0), 137 + #endif 138 + }; 139 + 140 + /* 141 + * Commands that RAW doesn't permit. The rationale for each: 142 + * 143 + * CXL_MBOX_OP_ACTIVATE_FW: Firmware activation requires adjustment / 144 + * coordination of transaction timeout values at the root bridge level. 145 + * 146 + * CXL_MBOX_OP_SET_PARTITION_INFO: The device memory map may change live 147 + * and needs to be coordinated with HDM updates. 148 + * 149 + * CXL_MBOX_OP_SET_LSA: The label storage area may be cached by the 150 + * driver and any writes from userspace invalidates those contents. 151 + * 152 + * CXL_MBOX_OP_SET_SHUTDOWN_STATE: Set shutdown state assumes no writes 153 + * to the device after it is marked clean, userspace can not make that 154 + * assertion. 155 + * 156 + * CXL_MBOX_OP_[GET_]SCAN_MEDIA: The kernel provides a native error list that 157 + * is kept up to date with patrol notifications and error management. 158 + */ 159 + static u16 cxl_disabled_raw_commands[] = { 160 + CXL_MBOX_OP_ACTIVATE_FW, 161 + CXL_MBOX_OP_SET_PARTITION_INFO, 162 + CXL_MBOX_OP_SET_LSA, 163 + CXL_MBOX_OP_SET_SHUTDOWN_STATE, 164 + CXL_MBOX_OP_SCAN_MEDIA, 165 + CXL_MBOX_OP_GET_SCAN_MEDIA, 166 + }; 167 + 168 + /* 169 + * Command sets that RAW doesn't permit. All opcodes in this set are 170 + * disabled because they pass plain text security payloads over the 171 + * user/kernel boundary. This functionality is intended to be wrapped 172 + * behind the keys ABI which allows for encrypted payloads in the UAPI 173 + */ 174 + static u8 security_command_sets[] = { 175 + 0x44, /* Sanitize */ 176 + 0x45, /* Persistent Memory Data-at-rest Security */ 177 + 0x46, /* Security Passthrough */ 146 178 }; 147 179 148 180 #define cxl_for_each_cmd(cmd) \ ··· 214 160 dev_dbg(&cxlm->pdev->dev, "Doorbell wait took %dms", 215 161 jiffies_to_msecs(end) - jiffies_to_msecs(start)); 216 162 return 0; 163 + } 164 + 165 + static bool cxl_is_security_command(u16 opcode) 166 + { 167 + int i; 168 + 169 + for (i = 0; i < ARRAY_SIZE(security_command_sets); i++) 170 + if (security_command_sets[i] == (opcode >> 8)) 171 + return true; 172 + return false; 217 173 } 218 174 219 175 static void cxl_mem_mbox_timeout(struct cxl_mem *cxlm, ··· 495 431 cxl_command_names[cmd->info.id].name, mbox_cmd.opcode, 496 432 cmd->info.size_in); 497 433 434 + dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW, 435 + "raw command path used\n"); 436 + 498 437 rc = __cxl_mem_mbox_send_cmd(cxlm, &mbox_cmd); 499 438 cxl_mem_mbox_put(cxlm); 500 439 if (rc) ··· 527 460 return rc; 528 461 } 529 462 463 + static bool cxl_mem_raw_command_allowed(u16 opcode) 464 + { 465 + int i; 466 + 467 + if (!IS_ENABLED(CONFIG_CXL_MEM_RAW_COMMANDS)) 468 + return false; 469 + 470 + if (security_locked_down(LOCKDOWN_NONE)) 471 + return false; 472 + 473 + if (cxl_raw_allow_all) 474 + return true; 475 + 476 + if (cxl_is_security_command(opcode)) 477 + return false; 478 + 479 + for (i = 0; i < ARRAY_SIZE(cxl_disabled_raw_commands); i++) 480 + if (cxl_disabled_raw_commands[i] == opcode) 481 + return false; 482 + 483 + return true; 484 + } 485 + 530 486 /** 531 487 * cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND. 532 488 * @cxlm: &struct cxl_mem device whose mailbox will be used. ··· 561 471 * * %-ENOTTY - Invalid command specified. 562 472 * * %-EINVAL - Reserved fields or invalid values were used. 563 473 * * %-ENOMEM - Input or output buffer wasn't sized properly. 474 + * * %-EPERM - Attempted to use a protected command. 564 475 * 565 476 * The result of this command is a fully validated command in @out_cmd that is 566 477 * safe to send to the hardware. ··· 585 494 */ 586 495 if (send_cmd->in.size > cxlm->payload_size) 587 496 return -EINVAL; 497 + 498 + /* 499 + * Checks are bypassed for raw commands but a WARN/taint will occur 500 + * later in the callchain 501 + */ 502 + if (send_cmd->id == CXL_MEM_COMMAND_ID_RAW) { 503 + const struct cxl_mem_command temp = { 504 + .info = { 505 + .id = CXL_MEM_COMMAND_ID_RAW, 506 + .flags = 0, 507 + .size_in = send_cmd->in.size, 508 + .size_out = send_cmd->out.size, 509 + }, 510 + .opcode = send_cmd->raw.opcode 511 + }; 512 + 513 + if (send_cmd->raw.rsvd) 514 + return -EINVAL; 515 + 516 + /* 517 + * Unlike supported commands, the output size of RAW commands 518 + * gets passed along without further checking, so it must be 519 + * validated here. 520 + */ 521 + if (send_cmd->out.size > cxlm->payload_size) 522 + return -EINVAL; 523 + 524 + if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode)) 525 + return -EPERM; 526 + 527 + memcpy(out_cmd, &temp, sizeof(temp)); 528 + 529 + return 0; 530 + } 588 531 589 532 if (send_cmd->flags & ~CXL_MEM_COMMAND_FLAG_MASK) 590 533 return -EINVAL; ··· 1291 1166 1292 1167 static __init int cxl_mem_init(void) 1293 1168 { 1169 + struct dentry *mbox_debugfs; 1294 1170 dev_t devt; 1295 1171 int rc; 1296 1172 ··· 1308 1182 return rc; 1309 1183 } 1310 1184 1185 + cxl_debugfs = debugfs_create_dir("cxl", NULL); 1186 + mbox_debugfs = debugfs_create_dir("mbox", cxl_debugfs); 1187 + debugfs_create_bool("raw_allow_all", 0600, mbox_debugfs, 1188 + &cxl_raw_allow_all); 1189 + 1311 1190 return 0; 1312 1191 } 1313 1192 1314 1193 static __exit void cxl_mem_exit(void) 1315 1194 { 1195 + debugfs_remove_recursive(cxl_debugfs); 1316 1196 pci_unregister_driver(&cxl_mem_driver); 1317 1197 unregister_chrdev_region(MKDEV(cxl_mem_major, 0), CXL_MEM_MAX_DEVS); 1318 1198 }
+11 -1
include/uapi/linux/cxl_mem.h
··· 22 22 #define CXL_CMDS \ 23 23 ___C(INVALID, "Invalid Command"), \ 24 24 ___C(IDENTIFY, "Identify Command"), \ 25 + ___C(RAW, "Raw device command"), \ 25 26 ___C(MAX, "invalid / last command") 26 27 27 28 #define ___C(a, b) CXL_MEM_COMMAND_ID_##a ··· 116 115 * @id: The command to send to the memory device. This must be one of the 117 116 * commands returned by the query command. 118 117 * @flags: Flags for the command (input). 118 + * @raw: Special fields for raw commands 119 + * @raw.opcode: Opcode passed to hardware when using the RAW command. 120 + * @raw.rsvd: Must be zero. 119 121 * @rsvd: Must be zero. 120 122 * @retval: Return value from the memory device (output). 121 123 * @in: Parameters associated with input payload. ··· 141 137 struct cxl_send_command { 142 138 __u32 id; 143 139 __u32 flags; 144 - __u32 rsvd; 140 + union { 141 + struct { 142 + __u16 opcode; 143 + __u16 rsvd; 144 + } raw; 145 + __u32 rsvd; 146 + }; 145 147 __u32 retval; 146 148 147 149 struct {