cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

client.rst (17231B)


      1.. SPDX-License-Identifier: GPL-2.0+
      2
      3.. |ssam_controller| replace:: :c:type:`struct ssam_controller <ssam_controller>`
      4.. |ssam_device| replace:: :c:type:`struct ssam_device <ssam_device>`
      5.. |ssam_device_driver| replace:: :c:type:`struct ssam_device_driver <ssam_device_driver>`
      6.. |ssam_client_bind| replace:: :c:func:`ssam_client_bind`
      7.. |ssam_client_link| replace:: :c:func:`ssam_client_link`
      8.. |ssam_get_controller| replace:: :c:func:`ssam_get_controller`
      9.. |ssam_controller_get| replace:: :c:func:`ssam_controller_get`
     10.. |ssam_controller_put| replace:: :c:func:`ssam_controller_put`
     11.. |ssam_device_alloc| replace:: :c:func:`ssam_device_alloc`
     12.. |ssam_device_add| replace:: :c:func:`ssam_device_add`
     13.. |ssam_device_remove| replace:: :c:func:`ssam_device_remove`
     14.. |ssam_device_driver_register| replace:: :c:func:`ssam_device_driver_register`
     15.. |ssam_device_driver_unregister| replace:: :c:func:`ssam_device_driver_unregister`
     16.. |module_ssam_device_driver| replace:: :c:func:`module_ssam_device_driver`
     17.. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE`
     18.. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register`
     19.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
     20.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
     21.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
     22
     23
     24======================
     25Writing Client Drivers
     26======================
     27
     28For the API documentation, refer to:
     29
     30.. toctree::
     31   :maxdepth: 2
     32
     33   client-api
     34
     35
     36Overview
     37========
     38
     39Client drivers can be set up in two main ways, depending on how the
     40corresponding device is made available to the system. We specifically
     41differentiate between devices that are presented to the system via one of
     42the conventional ways, e.g. as platform devices via ACPI, and devices that
     43are non-discoverable and instead need to be explicitly provided by some
     44other mechanism, as discussed further below.
     45
     46
     47Non-SSAM Client Drivers
     48=======================
     49
     50All communication with the SAM EC is handled via the |ssam_controller|
     51representing that EC to the kernel. Drivers targeting a non-SSAM device (and
     52thus not being a |ssam_device_driver|) need to explicitly establish a
     53connection/relation to that controller. This can be done via the
     54|ssam_client_bind| function. Said function returns a reference to the SSAM
     55controller, but, more importantly, also establishes a device link between
     56client device and controller (this can also be done separate via
     57|ssam_client_link|). It is important to do this, as it, first, guarantees
     58that the returned controller is valid for use in the client driver for as
     59long as this driver is bound to its device, i.e. that the driver gets
     60unbound before the controller ever becomes invalid, and, second, as it
     61ensures correct suspend/resume ordering. This setup should be done in the
     62driver's probe function, and may be used to defer probing in case the SSAM
     63subsystem is not ready yet, for example:
     64
     65.. code-block:: c
     66
     67   static int client_driver_probe(struct platform_device *pdev)
     68   {
     69           struct ssam_controller *ctrl;
     70
     71           ctrl = ssam_client_bind(&pdev->dev);
     72           if (IS_ERR(ctrl))
     73                   return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);
     74
     75           // ...
     76
     77           return 0;
     78   }
     79
     80The controller may be separately obtained via |ssam_get_controller| and its
     81lifetime be guaranteed via |ssam_controller_get| and |ssam_controller_put|.
     82Note that none of these functions, however, guarantee that the controller
     83will not be shut down or suspended. These functions essentially only operate
     84on the reference, i.e. only guarantee a bare minimum of accessibility
     85without any guarantees at all on practical operability.
     86
     87
     88Adding SSAM Devices
     89===================
     90
     91If a device does not already exist/is not already provided via conventional
     92means, it should be provided as |ssam_device| via the SSAM client device
     93hub. New devices can be added to this hub by entering their UID into the
     94corresponding registry. SSAM devices can also be manually allocated via
     95|ssam_device_alloc|, subsequently to which they have to be added via
     96|ssam_device_add| and eventually removed via |ssam_device_remove|. By
     97default, the parent of the device is set to the controller device provided
     98for allocation, however this may be changed before the device is added. Note
     99that, when changing the parent device, care must be taken to ensure that the
    100controller lifetime and suspend/resume ordering guarantees, in the default
    101setup provided through the parent-child relation, are preserved. If
    102necessary, by use of |ssam_client_link| as is done for non-SSAM client
    103drivers and described in more detail above.
    104
    105A client device must always be removed by the party which added the
    106respective device before the controller shuts down. Such removal can be
    107guaranteed by linking the driver providing the SSAM device to the controller
    108via |ssam_client_link|, causing it to unbind before the controller driver
    109unbinds. Client devices registered with the controller as parent are
    110automatically removed when the controller shuts down, but this should not be
    111relied upon, especially as this does not extend to client devices with a
    112different parent.
    113
    114
    115SSAM Client Drivers
    116===================
    117
    118SSAM client device drivers are, in essence, no different than other device
    119driver types. They are represented via |ssam_device_driver| and bind to a
    120|ssam_device| via its UID (:c:type:`struct ssam_device.uid <ssam_device>`)
    121member and the match table
    122(:c:type:`struct ssam_device_driver.match_table <ssam_device_driver>`),
    123which should be set when declaring the driver struct instance. Refer to the
    124|SSAM_DEVICE| macro documentation for more details on how to define members
    125of the driver's match table.
    126
    127The UID for SSAM client devices consists of a ``domain``, a ``category``,
    128a ``target``, an ``instance``, and a ``function``. The ``domain`` is used
    129differentiate between physical SAM devices
    130(:c:type:`SSAM_DOMAIN_SERIALHUB <ssam_device_domain>`), i.e. devices that can
    131be accessed via the Surface Serial Hub, and virtual ones
    132(:c:type:`SSAM_DOMAIN_VIRTUAL <ssam_device_domain>`), such as client-device
    133hubs, that have no real representation on the SAM EC and are solely used on
    134the kernel/driver-side. For physical devices, ``category`` represents the
    135target category, ``target`` the target ID, and ``instance`` the instance ID
    136used to access the physical SAM device. In addition, ``function`` references
    137a specific device functionality, but has no meaning to the SAM EC. The
    138(default) name of a client device is generated based on its UID.
    139
    140A driver instance can be registered via |ssam_device_driver_register| and
    141unregistered via |ssam_device_driver_unregister|. For convenience, the
    142|module_ssam_device_driver| macro may be used to define module init- and
    143exit-functions registering the driver.
    144
    145The controller associated with a SSAM client device can be found in its
    146:c:type:`struct ssam_device.ctrl <ssam_device>` member. This reference is
    147guaranteed to be valid for at least as long as the client driver is bound,
    148but should also be valid for as long as the client device exists. Note,
    149however, that access outside of the bound client driver must ensure that the
    150controller device is not suspended while making any requests or
    151(un-)registering event notifiers (and thus should generally be avoided). This
    152is guaranteed when the controller is accessed from inside the bound client
    153driver.
    154
    155
    156Making Synchronous Requests
    157===========================
    158
    159Synchronous requests are (currently) the main form of host-initiated
    160communication with the EC. There are a couple of ways to define and execute
    161such requests, however, most of them boil down to something similar as shown
    162in the example below. This example defines a write-read request, meaning
    163that the caller provides an argument to the SAM EC and receives a response.
    164The caller needs to know the (maximum) length of the response payload and
    165provide a buffer for it.
    166
    167Care must be taken to ensure that any command payload data passed to the SAM
    168EC is provided in little-endian format and, similarly, any response payload
    169data received from it is converted from little-endian to host endianness.
    170
    171.. code-block:: c
    172
    173   int perform_request(struct ssam_controller *ctrl, u32 arg, u32 *ret)
    174   {
    175           struct ssam_request rqst;
    176           struct ssam_response resp;
    177           int status;
    178
    179           /* Convert request argument to little-endian. */
    180           __le32 arg_le = cpu_to_le32(arg);
    181           __le32 ret_le = cpu_to_le32(0);
    182
    183           /*
    184            * Initialize request specification. Replace this with your values.
    185            * The rqst.payload field may be NULL if rqst.length is zero,
    186            * indicating that the request does not have any argument.
    187            *
    188            * Note: The request parameters used here are not valid, i.e.
    189            *       they do not correspond to an actual SAM/EC request.
    190            */
    191           rqst.target_category = SSAM_SSH_TC_SAM;
    192           rqst.target_id = 0x01;
    193           rqst.command_id = 0x02;
    194           rqst.instance_id = 0x03;
    195           rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
    196           rqst.length = sizeof(arg_le);
    197           rqst.payload = (u8 *)&arg_le;
    198
    199           /* Initialize request response. */
    200           resp.capacity = sizeof(ret_le);
    201           resp.length = 0;
    202           resp.pointer = (u8 *)&ret_le;
    203
    204           /*
    205            * Perform actual request. The response pointer may be null in case
    206            * the request does not have any response. This must be consistent
    207            * with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification
    208            * above.
    209            */
    210           status = ssam_request_sync(ctrl, &rqst, &resp);
    211
    212           /*
    213            * Alternatively use
    214            *
    215            *   ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
    216            *
    217            * to perform the request, allocating the message buffer directly
    218            * on the stack as opposed to allocation via kzalloc().
    219            */
    220
    221           /*
    222            * Convert request response back to native format. Note that in the
    223            * error case, this value is not touched by the SSAM core, i.e.
    224            * 'ret_le' will be zero as specified in its initialization.
    225            */
    226           *ret = le32_to_cpu(ret_le);
    227
    228           return status;
    229   }
    230
    231Note that |ssam_request_sync| in its essence is a wrapper over lower-level
    232request primitives, which may also be used to perform requests. Refer to its
    233implementation and documentation for more details.
    234
    235An arguably more user-friendly way of defining such functions is by using
    236one of the generator macros, for example via:
    237
    238.. code-block:: c
    239
    240   SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, {
    241           .target_category = SSAM_SSH_TC_TMP,
    242           .target_id       = 0x01,
    243           .command_id      = 0x03,
    244           .instance_id     = 0x00,
    245   });
    246
    247This example defines a function
    248
    249.. code-block:: c
    250
    251   static int __ssam_tmp_perf_mode_set(struct ssam_controller *ctrl, const __le32 *arg);
    252
    253executing the specified request, with the controller passed in when calling
    254said function. In this example, the argument is provided via the ``arg``
    255pointer. Note that the generated function allocates the message buffer on
    256the stack. Thus, if the argument provided via the request is large, these
    257kinds of macros should be avoided. Also note that, in contrast to the
    258previous non-macro example, this function does not do any endianness
    259conversion, which has to be handled by the caller. Apart from those
    260differences the function generated by the macro is similar to the one
    261provided in the non-macro example above.
    262
    263The full list of such function-generating macros is
    264
    265- :c:func:`SSAM_DEFINE_SYNC_REQUEST_N` for requests without return value and
    266  without argument.
    267- :c:func:`SSAM_DEFINE_SYNC_REQUEST_R` for requests with return value but no
    268  argument.
    269- :c:func:`SSAM_DEFINE_SYNC_REQUEST_W` for requests without return value but
    270  with argument.
    271
    272Refer to their respective documentation for more details. For each one of
    273these macros, a special variant is provided, which targets request types
    274applicable to multiple instances of the same device type:
    275
    276- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_N`
    277- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_R`
    278- :c:func:`SSAM_DEFINE_SYNC_REQUEST_MD_W`
    279
    280The difference of those macros to the previously mentioned versions is, that
    281the device target and instance IDs are not fixed for the generated function,
    282but instead have to be provided by the caller of said function.
    283
    284Additionally, variants for direct use with client devices, i.e.
    285|ssam_device|, are also provided. These can, for example, be used as
    286follows:
    287
    288.. code-block:: c
    289
    290   SSAM_DEFINE_SYNC_REQUEST_CL_R(ssam_bat_get_sta, __le32, {
    291           .target_category = SSAM_SSH_TC_BAT,
    292           .command_id      = 0x01,
    293   });
    294
    295This invocation of the macro defines a function
    296
    297.. code-block:: c
    298
    299   static int ssam_bat_get_sta(struct ssam_device *sdev, __le32 *ret);
    300
    301executing the specified request, using the device IDs and controller given
    302in the client device. The full list of such macros for client devices is:
    303
    304- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_N`
    305- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_R`
    306- :c:func:`SSAM_DEFINE_SYNC_REQUEST_CL_W`
    307
    308
    309Handling Events
    310===============
    311
    312To receive events from the SAM EC, an event notifier must be registered for
    313the desired event via |ssam_notifier_register|. The notifier must be
    314unregistered via |ssam_notifier_unregister| once it is not required any
    315more.
    316
    317Event notifiers are registered by providing (at minimum) a callback to call
    318in case an event has been received, the registry specifying how the event
    319should be enabled, an event ID specifying for which target category and,
    320optionally and depending on the registry used, for which instance ID events
    321should be enabled, and finally, flags describing how the EC will send these
    322events. If the specific registry does not enable events by instance ID, the
    323instance ID must be set to zero. Additionally, a priority for the respective
    324notifier may be specified, which determines its order in relation to any
    325other notifier registered for the same target category.
    326
    327By default, event notifiers will receive all events for the specific target
    328category, regardless of the instance ID specified when registering the
    329notifier. The core may be instructed to only call a notifier if the target
    330ID or instance ID (or both) of the event match the ones implied by the
    331notifier IDs (in case of target ID, the target ID of the registry), by
    332providing an event mask (see |ssam_event_mask|).
    333
    334In general, the target ID of the registry is also the target ID of the
    335enabled event (with the notable exception being keyboard input events on the
    336Surface Laptop 1 and 2, which are enabled via a registry with target ID 1,
    337but provide events with target ID 2).
    338
    339A full example for registering an event notifier and handling received
    340events is provided below:
    341
    342.. code-block:: c
    343
    344   u32 notifier_callback(struct ssam_event_notifier *nf,
    345                         const struct ssam_event *event)
    346   {
    347           int status = ...
    348
    349           /* Handle the event here ... */
    350
    351           /* Convert return value and indicate that we handled the event. */
    352           return ssam_notifier_from_errno(status) | SSAM_NOTIF_HANDLED;
    353   }
    354
    355   int setup_notifier(struct ssam_device *sdev,
    356                      struct ssam_event_notifier *nf)
    357   {
    358           /* Set priority wrt. other handlers of same target category. */
    359           nf->base.priority = 1;
    360
    361           /* Set event/notifier callback. */
    362           nf->base.fn = notifier_callback;
    363
    364           /* Specify event registry, i.e. how events get enabled/disabled. */
    365           nf->event.reg = SSAM_EVENT_REGISTRY_KIP;
    366
    367           /* Specify which event to enable/disable */
    368           nf->event.id.target_category = sdev->uid.category;
    369           nf->event.id.instance = sdev->uid.instance;
    370
    371           /*
    372            * Specify for which events the notifier callback gets executed.
    373            * This essentially tells the core if it can skip notifiers that
    374            * don't have target or instance IDs matching those of the event.
    375            */
    376           nf->event.mask = SSAM_EVENT_MASK_STRICT;
    377
    378           /* Specify event flags. */
    379           nf->event.flags = SSAM_EVENT_SEQUENCED;
    380
    381           return ssam_notifier_register(sdev->ctrl, nf);
    382   }
    383
    384Multiple event notifiers can be registered for the same event. The event
    385handler core takes care of enabling and disabling events when notifiers are
    386registered and unregistered, by keeping track of how many notifiers for a
    387specific event (combination of registry, event target category, and event
    388instance ID) are currently registered. This means that a specific event will
    389be enabled when the first notifier for it is being registered and disabled
    390when the last notifier for it is being unregistered. Note that the event
    391flags are therefore only used on the first registered notifier, however, one
    392should take care that notifiers for a specific event are always registered
    393with the same flag and it is considered a bug to do otherwise.