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

Configure Feed

Select the types of activity you want to include in your feed.

platform/surface: Add Surface Aggregator user-space interface

Add a misc-device providing user-space access to the Surface Aggregator
EC, mainly intended for debugging, testing, and reverse-engineering.
This interface gives user-space applications the ability to send
requests to the EC and receive the corresponding responses.

The device-file is managed by a pseudo platform-device and corresponding
driver to avoid dependence on the dedicated bus, allowing it to be
loaded in a minimal configuration.

A python library and scripts to access this device can be found at [1].

[1]: https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
Link: https://lore.kernel.org/r/20201221183959.1186143-9-luzmaximilian@gmail.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Maximilian Luz and committed by
Hans de Goede
178f6ab7 8d779282

+501 -1
+87
Documentation/driver-api/surface_aggregator/clients/cdev.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0+ 2 + 3 + .. |u8| replace:: :c:type:`u8 <u8>` 4 + .. |u16| replace:: :c:type:`u16 <u16>` 5 + .. |ssam_cdev_request| replace:: :c:type:`struct ssam_cdev_request <ssam_cdev_request>` 6 + .. |ssam_cdev_request_flags| replace:: :c:type:`enum ssam_cdev_request_flags <ssam_cdev_request_flags>` 7 + 8 + ============================== 9 + User-Space EC Interface (cdev) 10 + ============================== 11 + 12 + The ``surface_aggregator_cdev`` module provides a misc-device for the SSAM 13 + controller to allow for a (more or less) direct connection from user-space to 14 + the SAM EC. It is intended to be used for development and debugging, and 15 + therefore should not be used or relied upon in any other way. Note that this 16 + module is not loaded automatically, but instead must be loaded manually. 17 + 18 + The provided interface is accessible through the ``/dev/surface/aggregator`` 19 + device-file. All functionality of this interface is provided via IOCTLs. 20 + These IOCTLs and their respective input/output parameter structs are defined in 21 + ``include/uapi/linux/surface_aggregator/cdev.h``. 22 + 23 + A small python library and scripts for accessing this interface can be found 24 + at https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam. 25 + 26 + 27 + Controller IOCTLs 28 + ================= 29 + 30 + The following IOCTLs are provided: 31 + 32 + .. flat-table:: Controller IOCTLs 33 + :widths: 1 1 1 1 4 34 + :header-rows: 1 35 + 36 + * - Type 37 + - Number 38 + - Direction 39 + - Name 40 + - Description 41 + 42 + * - ``0xA5`` 43 + - ``1`` 44 + - ``WR`` 45 + - ``REQUEST`` 46 + - Perform synchronous SAM request. 47 + 48 + 49 + ``REQUEST`` 50 + ----------- 51 + 52 + Defined as ``_IOWR(0xA5, 1, struct ssam_cdev_request)``. 53 + 54 + Executes a synchronous SAM request. The request specification is passed in 55 + as argument of type |ssam_cdev_request|, which is then written to/modified 56 + by the IOCTL to return status and result of the request. 57 + 58 + Request payload data must be allocated separately and is passed in via the 59 + ``payload.data`` and ``payload.length`` members. If a response is required, 60 + the response buffer must be allocated by the caller and passed in via the 61 + ``response.data`` member. The ``response.length`` member must be set to the 62 + capacity of this buffer, or if no response is required, zero. Upon 63 + completion of the request, the call will write the response to the response 64 + buffer (if its capacity allows it) and overwrite the length field with the 65 + actual size of the response, in bytes. 66 + 67 + Additionally, if the request has a response, this must be indicated via the 68 + request flags, as is done with in-kernel requests. Request flags can be set 69 + via the ``flags`` member and the values correspond to the values found in 70 + |ssam_cdev_request_flags|. 71 + 72 + Finally, the status of the request itself is returned in the ``status`` 73 + member (a negative errno value indicating failure). Note that failure 74 + indication of the IOCTL is separated from failure indication of the request: 75 + The IOCTL returns a negative status code if anything failed during setup of 76 + the request (``-EFAULT``) or if the provided argument or any of its fields 77 + are invalid (``-EINVAL``). In this case, the status value of the request 78 + argument may be set, providing more detail on what went wrong (e.g. 79 + ``-ENOMEM`` for out-of-memory), but this value may also be zero. The IOCTL 80 + will return with a zero status code in case the request has been set up, 81 + submitted, and completed (i.e. handed back to user-space) successfully from 82 + inside the IOCTL, but the request ``status`` member may still be negative in 83 + case the actual execution of the request failed after it has been submitted. 84 + 85 + A full definition of the argument struct is provided below: 86 + 87 + .. kernel-doc:: include/uapi/linux/surface_aggregator/cdev.h
+11 -1
Documentation/driver-api/surface_aggregator/clients/index.rst
··· 7 7 This is the documentation for client drivers themselves. Refer to 8 8 :doc:`../client` for documentation on how to write client drivers. 9 9 10 - .. Place documentation for individual client drivers here. 10 + .. toctree:: 11 + :maxdepth: 1 12 + 13 + cdev 14 + 15 + .. only:: subproject and html 16 + 17 + Indices 18 + ======= 19 + 20 + * :ref:`genindex`
+2
Documentation/userspace-api/ioctl/ioctl-number.rst
··· 324 324 0xA3 90-9F linux/dtlk.h 325 325 0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem 326 326 0xA4 00-1F uapi/asm/sgx.h <mailto:linux-sgx@vger.kernel.org> 327 + 0xA5 01 linux/surface_aggregator/cdev.h Microsoft Surface Platform System Aggregator 328 + <mailto:luzmaximilian@gmail.com> 327 329 0xAA 00-3F linux/uapi/linux/userfaultfd.h 328 330 0xAB 00-1F linux/nbd.h 329 331 0xAC 00-1F linux/raw.h
+2
MAINTAINERS
··· 11820 11820 C: irc://chat.freenode.net/##linux-surface 11821 11821 F: Documentation/driver-api/surface_aggregator/ 11822 11822 F: drivers/platform/surface/aggregator/ 11823 + F: drivers/platform/surface/surface_aggregator_cdev.c 11823 11824 F: include/linux/surface_aggregator/ 11825 + F: include/uapi/linux/surface_aggregator/ 11824 11826 11825 11827 MICROTEK X6 SCANNER 11826 11828 M: Oliver Neukum <oliver@neukum.org>
+17
drivers/platform/surface/Kconfig
··· 41 41 This driver provides support for ACPI operation 42 42 region of the Surface 3 battery platform driver. 43 43 44 + config SURFACE_AGGREGATOR_CDEV 45 + tristate "Surface System Aggregator Module User-Space Interface" 46 + depends on SURFACE_AGGREGATOR 47 + help 48 + Provides a misc-device interface to the Surface System Aggregator 49 + Module (SSAM) controller. 50 + 51 + This option provides a module (called surface_aggregator_cdev), that, 52 + when loaded, will add a client device (and its respective driver) to 53 + the SSAM controller. Said client device manages a misc-device 54 + interface (/dev/surface/aggregator), which can be used by user-space 55 + tools to directly communicate with the SSAM EC by sending requests and 56 + receiving the corresponding responses. 57 + 58 + The provided interface is intended for debugging and development only, 59 + and should not be used otherwise. 60 + 44 61 config SURFACE_GPE 45 62 tristate "Surface GPE/Lid Support Driver" 46 63 depends on DMI
+1
drivers/platform/surface/Makefile
··· 8 8 obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o 9 9 obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o 10 10 obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/ 11 + obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o 11 12 obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o 12 13 obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
+303
drivers/platform/surface/surface_aggregator_cdev.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Provides user-space access to the SSAM EC via the /dev/surface/aggregator 4 + * misc device. Intended for debugging and development. 5 + * 6 + * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com> 7 + */ 8 + 9 + #include <linux/fs.h> 10 + #include <linux/kernel.h> 11 + #include <linux/kref.h> 12 + #include <linux/miscdevice.h> 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/rwsem.h> 16 + #include <linux/slab.h> 17 + #include <linux/uaccess.h> 18 + 19 + #include <linux/surface_aggregator/cdev.h> 20 + #include <linux/surface_aggregator/controller.h> 21 + 22 + #define SSAM_CDEV_DEVICE_NAME "surface_aggregator_cdev" 23 + 24 + struct ssam_cdev { 25 + struct kref kref; 26 + struct rw_semaphore lock; 27 + struct ssam_controller *ctrl; 28 + struct miscdevice mdev; 29 + }; 30 + 31 + static void __ssam_cdev_release(struct kref *kref) 32 + { 33 + kfree(container_of(kref, struct ssam_cdev, kref)); 34 + } 35 + 36 + static struct ssam_cdev *ssam_cdev_get(struct ssam_cdev *cdev) 37 + { 38 + if (cdev) 39 + kref_get(&cdev->kref); 40 + 41 + return cdev; 42 + } 43 + 44 + static void ssam_cdev_put(struct ssam_cdev *cdev) 45 + { 46 + if (cdev) 47 + kref_put(&cdev->kref, __ssam_cdev_release); 48 + } 49 + 50 + static int ssam_cdev_device_open(struct inode *inode, struct file *filp) 51 + { 52 + struct miscdevice *mdev = filp->private_data; 53 + struct ssam_cdev *cdev = container_of(mdev, struct ssam_cdev, mdev); 54 + 55 + filp->private_data = ssam_cdev_get(cdev); 56 + return stream_open(inode, filp); 57 + } 58 + 59 + static int ssam_cdev_device_release(struct inode *inode, struct file *filp) 60 + { 61 + ssam_cdev_put(filp->private_data); 62 + return 0; 63 + } 64 + 65 + static long ssam_cdev_request(struct ssam_cdev *cdev, unsigned long arg) 66 + { 67 + struct ssam_cdev_request __user *r; 68 + struct ssam_cdev_request rqst; 69 + struct ssam_request spec; 70 + struct ssam_response rsp; 71 + const void __user *plddata; 72 + void __user *rspdata; 73 + int status = 0, ret = 0, tmp; 74 + 75 + r = (struct ssam_cdev_request __user *)arg; 76 + ret = copy_struct_from_user(&rqst, sizeof(rqst), r, sizeof(*r)); 77 + if (ret) 78 + goto out; 79 + 80 + plddata = u64_to_user_ptr(rqst.payload.data); 81 + rspdata = u64_to_user_ptr(rqst.response.data); 82 + 83 + /* Setup basic request fields. */ 84 + spec.target_category = rqst.target_category; 85 + spec.target_id = rqst.target_id; 86 + spec.command_id = rqst.command_id; 87 + spec.instance_id = rqst.instance_id; 88 + spec.flags = 0; 89 + spec.length = rqst.payload.length; 90 + spec.payload = NULL; 91 + 92 + if (rqst.flags & SSAM_CDEV_REQUEST_HAS_RESPONSE) 93 + spec.flags |= SSAM_REQUEST_HAS_RESPONSE; 94 + 95 + if (rqst.flags & SSAM_CDEV_REQUEST_UNSEQUENCED) 96 + spec.flags |= SSAM_REQUEST_UNSEQUENCED; 97 + 98 + rsp.capacity = rqst.response.length; 99 + rsp.length = 0; 100 + rsp.pointer = NULL; 101 + 102 + /* Get request payload from user-space. */ 103 + if (spec.length) { 104 + if (!plddata) { 105 + ret = -EINVAL; 106 + goto out; 107 + } 108 + 109 + spec.payload = kzalloc(spec.length, GFP_KERNEL); 110 + if (!spec.payload) { 111 + ret = -ENOMEM; 112 + goto out; 113 + } 114 + 115 + if (copy_from_user((void *)spec.payload, plddata, spec.length)) { 116 + ret = -EFAULT; 117 + goto out; 118 + } 119 + } 120 + 121 + /* Allocate response buffer. */ 122 + if (rsp.capacity) { 123 + if (!rspdata) { 124 + ret = -EINVAL; 125 + goto out; 126 + } 127 + 128 + rsp.pointer = kzalloc(rsp.capacity, GFP_KERNEL); 129 + if (!rsp.pointer) { 130 + ret = -ENOMEM; 131 + goto out; 132 + } 133 + } 134 + 135 + /* Perform request. */ 136 + status = ssam_request_sync(cdev->ctrl, &spec, &rsp); 137 + if (status) 138 + goto out; 139 + 140 + /* Copy response to user-space. */ 141 + if (rsp.length && copy_to_user(rspdata, rsp.pointer, rsp.length)) 142 + ret = -EFAULT; 143 + 144 + out: 145 + /* Always try to set response-length and status. */ 146 + tmp = put_user(rsp.length, &r->response.length); 147 + if (tmp) 148 + ret = tmp; 149 + 150 + tmp = put_user(status, &r->status); 151 + if (tmp) 152 + ret = tmp; 153 + 154 + /* Cleanup. */ 155 + kfree(spec.payload); 156 + kfree(rsp.pointer); 157 + 158 + return ret; 159 + } 160 + 161 + static long __ssam_cdev_device_ioctl(struct ssam_cdev *cdev, unsigned int cmd, 162 + unsigned long arg) 163 + { 164 + switch (cmd) { 165 + case SSAM_CDEV_REQUEST: 166 + return ssam_cdev_request(cdev, arg); 167 + 168 + default: 169 + return -ENOTTY; 170 + } 171 + } 172 + 173 + static long ssam_cdev_device_ioctl(struct file *file, unsigned int cmd, 174 + unsigned long arg) 175 + { 176 + struct ssam_cdev *cdev = file->private_data; 177 + long status; 178 + 179 + /* Ensure that controller is valid for as long as we need it. */ 180 + if (down_read_killable(&cdev->lock)) 181 + return -ERESTARTSYS; 182 + 183 + if (!cdev->ctrl) { 184 + up_read(&cdev->lock); 185 + return -ENODEV; 186 + } 187 + 188 + status = __ssam_cdev_device_ioctl(cdev, cmd, arg); 189 + 190 + up_read(&cdev->lock); 191 + return status; 192 + } 193 + 194 + static const struct file_operations ssam_controller_fops = { 195 + .owner = THIS_MODULE, 196 + .open = ssam_cdev_device_open, 197 + .release = ssam_cdev_device_release, 198 + .unlocked_ioctl = ssam_cdev_device_ioctl, 199 + .compat_ioctl = ssam_cdev_device_ioctl, 200 + .llseek = noop_llseek, 201 + }; 202 + 203 + static int ssam_dbg_device_probe(struct platform_device *pdev) 204 + { 205 + struct ssam_controller *ctrl; 206 + struct ssam_cdev *cdev; 207 + int status; 208 + 209 + ctrl = ssam_client_bind(&pdev->dev); 210 + if (IS_ERR(ctrl)) 211 + return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl); 212 + 213 + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 214 + if (!cdev) 215 + return -ENOMEM; 216 + 217 + kref_init(&cdev->kref); 218 + init_rwsem(&cdev->lock); 219 + cdev->ctrl = ctrl; 220 + 221 + cdev->mdev.parent = &pdev->dev; 222 + cdev->mdev.minor = MISC_DYNAMIC_MINOR; 223 + cdev->mdev.name = "surface_aggregator"; 224 + cdev->mdev.nodename = "surface/aggregator"; 225 + cdev->mdev.fops = &ssam_controller_fops; 226 + 227 + status = misc_register(&cdev->mdev); 228 + if (status) { 229 + kfree(cdev); 230 + return status; 231 + } 232 + 233 + platform_set_drvdata(pdev, cdev); 234 + return 0; 235 + } 236 + 237 + static int ssam_dbg_device_remove(struct platform_device *pdev) 238 + { 239 + struct ssam_cdev *cdev = platform_get_drvdata(pdev); 240 + 241 + misc_deregister(&cdev->mdev); 242 + 243 + /* 244 + * The controller is only guaranteed to be valid for as long as the 245 + * driver is bound. Remove controller so that any lingering open files 246 + * cannot access it any more after we're gone. 247 + */ 248 + down_write(&cdev->lock); 249 + cdev->ctrl = NULL; 250 + up_write(&cdev->lock); 251 + 252 + ssam_cdev_put(cdev); 253 + return 0; 254 + } 255 + 256 + static struct platform_device *ssam_cdev_device; 257 + 258 + static struct platform_driver ssam_cdev_driver = { 259 + .probe = ssam_dbg_device_probe, 260 + .remove = ssam_dbg_device_remove, 261 + .driver = { 262 + .name = SSAM_CDEV_DEVICE_NAME, 263 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 264 + }, 265 + }; 266 + 267 + static int __init ssam_debug_init(void) 268 + { 269 + int status; 270 + 271 + ssam_cdev_device = platform_device_alloc(SSAM_CDEV_DEVICE_NAME, 272 + PLATFORM_DEVID_NONE); 273 + if (!ssam_cdev_device) 274 + return -ENOMEM; 275 + 276 + status = platform_device_add(ssam_cdev_device); 277 + if (status) 278 + goto err_device; 279 + 280 + status = platform_driver_register(&ssam_cdev_driver); 281 + if (status) 282 + goto err_driver; 283 + 284 + return 0; 285 + 286 + err_driver: 287 + platform_device_del(ssam_cdev_device); 288 + err_device: 289 + platform_device_put(ssam_cdev_device); 290 + return status; 291 + } 292 + module_init(ssam_debug_init); 293 + 294 + static void __exit ssam_debug_exit(void) 295 + { 296 + platform_driver_unregister(&ssam_cdev_driver); 297 + platform_device_unregister(ssam_cdev_device); 298 + } 299 + module_exit(ssam_debug_exit); 300 + 301 + MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>"); 302 + MODULE_DESCRIPTION("User-space interface for Surface System Aggregator Module"); 303 + MODULE_LICENSE("GPL");
+78
include/uapi/linux/surface_aggregator/cdev.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 + /* 3 + * Surface System Aggregator Module (SSAM) user-space EC interface. 4 + * 5 + * Definitions, structs, and IOCTLs for the /dev/surface/aggregator misc 6 + * device. This device provides direct user-space access to the SSAM EC. 7 + * Intended for debugging and development. 8 + * 9 + * Copyright (C) 2020 Maximilian Luz <luzmaximilian@gmail.com> 10 + */ 11 + 12 + #ifndef _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H 13 + #define _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H 14 + 15 + #include <linux/ioctl.h> 16 + #include <linux/types.h> 17 + 18 + /** 19 + * enum ssam_cdev_request_flags - Request flags for SSAM cdev request IOCTL. 20 + * 21 + * @SSAM_CDEV_REQUEST_HAS_RESPONSE: 22 + * Specifies that the request expects a response. If not set, the request 23 + * will be directly completed after its underlying packet has been 24 + * transmitted. If set, the request transport system waits for a response 25 + * of the request. 26 + * 27 + * @SSAM_CDEV_REQUEST_UNSEQUENCED: 28 + * Specifies that the request should be transmitted via an unsequenced 29 + * packet. If set, the request must not have a response, meaning that this 30 + * flag and the %SSAM_CDEV_REQUEST_HAS_RESPONSE flag are mutually 31 + * exclusive. 32 + */ 33 + enum ssam_cdev_request_flags { 34 + SSAM_CDEV_REQUEST_HAS_RESPONSE = 0x01, 35 + SSAM_CDEV_REQUEST_UNSEQUENCED = 0x02, 36 + }; 37 + 38 + /** 39 + * struct ssam_cdev_request - Controller request IOCTL argument. 40 + * @target_category: Target category of the SAM request. 41 + * @target_id: Target ID of the SAM request. 42 + * @command_id: Command ID of the SAM request. 43 + * @instance_id: Instance ID of the SAM request. 44 + * @flags: Request flags (see &enum ssam_cdev_request_flags). 45 + * @status: Request status (output). 46 + * @payload: Request payload (input data). 47 + * @payload.data: Pointer to request payload data. 48 + * @payload.length: Length of request payload data (in bytes). 49 + * @response: Request response (output data). 50 + * @response.data: Pointer to response buffer. 51 + * @response.length: On input: Capacity of response buffer (in bytes). 52 + * On output: Length of request response (number of bytes 53 + * in the buffer that are actually used). 54 + */ 55 + struct ssam_cdev_request { 56 + __u8 target_category; 57 + __u8 target_id; 58 + __u8 command_id; 59 + __u8 instance_id; 60 + __u16 flags; 61 + __s16 status; 62 + 63 + struct { 64 + __u64 data; 65 + __u16 length; 66 + __u8 __pad[6]; 67 + } payload; 68 + 69 + struct { 70 + __u64 data; 71 + __u16 length; 72 + __u8 __pad[6]; 73 + } response; 74 + } __attribute__((__packed__)); 75 + 76 + #define SSAM_CDEV_REQUEST _IOWR(0xA5, 1, struct ssam_cdev_request) 77 + 78 + #endif /* _UAPI_LINUX_SURFACE_AGGREGATOR_CDEV_H */