Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 1 | ======== |
| 2 | Debug FS |
| 3 | ======== |
| 4 | |
| 5 | .. contents:: |
| 6 | |
| 7 | Overview |
| 8 | -------- |
| 9 | |
| 10 | The *DebugFS* feature is primarily aimed at exposing firmware debug data to |
| 11 | higher SW layers such as a non-secure component. Such component can be the |
| 12 | TFTF test payload or a Linux kernel module. |
| 13 | |
| 14 | Virtual filesystem |
| 15 | ------------------ |
| 16 | |
| 17 | The core functionality lies in a virtual file system based on a 9p file server |
Olivier Deprez | f4feb7c | 2020-02-07 16:54:36 +0100 | [diff] [blame] | 18 | interface (`Notes on the Plan 9 Kernel Source`_ and |
| 19 | `Linux 9p remote filesystem protocol`_). |
| 20 | The implementation permits exposing virtual files, firmware drivers, and file blobs. |
Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 21 | |
| 22 | Namespace |
| 23 | ~~~~~~~~~ |
| 24 | |
| 25 | Two namespaces are exposed: |
| 26 | |
| 27 | - # is used as root for drivers (e.g. #t0 is the first uart) |
| 28 | - / is used as root for virtual "files" (e.g. /fip, or /dev/uart) |
| 29 | |
| 30 | 9p interface |
| 31 | ~~~~~~~~~~~~ |
| 32 | |
| 33 | The associated primitives are: |
| 34 | |
| 35 | - Unix-like: |
| 36 | |
| 37 | - open(): create a file descriptor that acts as a handle to the file passed as |
| 38 | an argument. |
| 39 | - close(): close the file descriptor created by open(). |
| 40 | - read(): read from a file to a buffer. |
| 41 | - write(): write from a buffer to a file. |
| 42 | - seek(): set the file position indicator of a file descriptor either to a |
| 43 | relative or an absolute offset. |
| 44 | - stat(): get information about a file (type, mode, size, ...). |
| 45 | |
| 46 | .. code:: c |
| 47 | |
| 48 | int open(const char *name, int flags); |
| 49 | int close(int fd); |
| 50 | int read(int fd, void *buf, int n); |
| 51 | int write(int fd, void *buf, int n); |
| 52 | int seek(int fd, long off, int whence); |
| 53 | int stat(char *path, dir_t *dir); |
| 54 | |
| 55 | - Specific primitives : |
| 56 | |
| 57 | - mount(): create a link between a driver and spec. |
| 58 | - create(): create a file in a specific location. |
| 59 | - bind(): expose the content of a directory to another directory. |
| 60 | |
| 61 | .. code:: c |
| 62 | |
| 63 | int mount(char *srv, char *mnt, char *spec); |
| 64 | int create(const char *name, int flags); |
| 65 | int bind(char *path, char *where); |
| 66 | |
| 67 | This interface is embedded into the BL31 run-time payload when selected by build |
| 68 | options. The interface multiplexes drivers or emulated "files": |
| 69 | |
| 70 | - Debug data can be partitioned into different virtual files e.g. expose PMF |
| 71 | measurements through a file, and internal firmware state counters through |
| 72 | another file. |
| 73 | - This permits direct access to a firmware driver, mainly for test purposes |
| 74 | (e.g. a hardware device that may not be accessible to non-privileged/ |
| 75 | non-secure layers, or for which no support exists in the NS side). |
| 76 | |
| 77 | SMC interface |
| 78 | ------------- |
| 79 | |
| 80 | The communication with the 9p layer in BL31 is made through an SMC conduit |
Olivier Deprez | f4feb7c | 2020-02-07 16:54:36 +0100 | [diff] [blame] | 81 | (`SMC Calling Convention PDD`_), using a specific SiP Function Id. An NS |
| 82 | shared buffer is used to pass path string parameters, or e.g. to exchange |
| 83 | data on a read operation. Refer to `ARM SiP Services`_ for a description |
| 84 | of the SMC interface. |
Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 85 | |
| 86 | Security considerations |
| 87 | ----------------------- |
| 88 | |
| 89 | - Due to the nature of the exposed data, the feature is considered experimental |
| 90 | and importantly **shall only be used in debug builds**. |
| 91 | - Several primitive imply string manipulations and usage of string formats. |
| 92 | - Special care is taken with the shared buffer to avoid TOCTOU attacks. |
| 93 | |
| 94 | Limitations |
| 95 | ----------- |
| 96 | |
| 97 | - In order to setup the shared buffer, the component consuming the interface |
| 98 | needs to allocate a physical page frame and transmit its address. |
| 99 | - In order to map the shared buffer, BL31 requires enabling the dynamic xlat |
| 100 | table option. |
| 101 | - Data exchange is limited by the shared buffer length. A large read operation |
| 102 | might be split into multiple read operations of smaller chunks. |
| 103 | - On concurrent access, a spinlock is implemented in the BL31 service to protect |
| 104 | the internal work buffer, and re-entrancy into the filesystem layers. |
| 105 | - Notice, a physical device driver if exposed by the firmware may conflict with |
| 106 | the higher level OS if the latter implements its own driver for the same |
| 107 | physical device. |
| 108 | |
| 109 | Applications |
| 110 | ------------ |
| 111 | |
| 112 | The SMC interface is accessible from an NS environment, that is: |
| 113 | |
| 114 | - a test payload, bootloader or hypervisor running at NS-EL2 |
| 115 | - a Linux kernel driver running at NS-EL1 |
| 116 | - a Linux userspace application through the kernel driver |
| 117 | |
Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 118 | -------------- |
| 119 | |
Olivier Deprez | f4feb7c | 2020-02-07 16:54:36 +0100 | [diff] [blame] | 120 | *Copyright (c) 2019-2020, Arm Limited and Contributors. All rights reserved.* |
Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 121 | |
laurenw-arm | 03e7e61 | 2020-04-16 10:02:17 -0500 | [diff] [blame] | 122 | .. _SMC Calling Convention: https://developer.arm.com/docs/den0028/latest |
Ambroise Vincent | 9660dc1 | 2019-07-12 13:47:03 +0100 | [diff] [blame] | 123 | .. _Notes on the Plan 9 Kernel Source: http://lsub.org/who/nemo/9.pdf |
| 124 | .. _Linux 9p remote filesystem protocol: https://www.kernel.org/doc/Documentation/filesystems/9p.txt |
| 125 | .. _ARM SiP Services: arm-sip-service.rst |