|
| 1 | +--- |
| 2 | +title: "Device attach - part 1: virtio" |
| 3 | +weight: 2 |
| 4 | + |
| 5 | +### FIXED, DO NOT MODIFY |
| 6 | +layout: learningpathall |
| 7 | +--- |
| 8 | + |
| 9 | +This section is a high level overview of VirtIO and Bounce Buffers, and how they |
| 10 | +relate to CCA Realms. |
| 11 | + |
| 12 | +A Realm has to use physical devices at some point to interact with the external |
| 13 | +and / or physical world. The easiest way to do this is by using VirtIO, which |
| 14 | +provides a fast high level emulation layer. This can be seen as the level-0 of |
| 15 | +device attach. |
| 16 | + |
| 17 | +As will be seen later in this learning path, more evolved device attach can be |
| 18 | +performed leveraging hardware security features like PCIe-TDISP (**T**EE |
| 19 | +**D**evice **I**nterface **S**ecurity **P**rotocol) and PCIe-IDE (**I**ntegrity |
| 20 | +and **D**ata **E**ncryption), where the host OS can assign a physical device to |
| 21 | +a realm, which will be able to make security measurements on the physical device |
| 22 | +and include it in its base and measurements. |
| 23 | + |
| 24 | +## VirtIO |
| 25 | + |
| 26 | +### What is VirtIO ? |
| 27 | + |
| 28 | +VirtIO is an abstraction layer for virtual devices in virtualized environments. |
| 29 | +It provides standardized and efficient interfaces between guest virtual machines |
| 30 | +(VMs) and host devices, making it easier to develop _paravirtualized_ drivers. |
| 31 | +_Paravirtualized_ means that the guest OS is aware it’s running in a virtualized |
| 32 | +environment and can use optimized drivers (VirtIO) to communicate with virtual |
| 33 | +hardware. Emulating hardware devices (like NICs or disks) for VMs is slow and |
| 34 | +inefficient. VirtIO provides a standardized and efficient interface that allows |
| 35 | +VMs to bypass full device emulation and instead use optimized drivers. |
| 36 | + |
| 37 | +VirtIO is most commonly used with KVM/QEMU virtualization. Example drivers are: |
| 38 | +- `virtio-net`: Paravirtualized networking |
| 39 | +- `virtio-blk`: Block device (disk) access |
| 40 | +- `virtio-fs`: File sharing (host ↔ guest) |
| 41 | +- `virtio-balloon`: Dynamic memory management |
| 42 | +- `virtio-rng`: Random number source |
| 43 | +- `virtio-console`: Simple console interface |
| 44 | +- ... |
| 45 | + |
| 46 | +### How VirtIO works in VMs |
| 47 | + |
| 48 | +1. The Host Hypervisor (e.g., QEMU/KVM) exposes VirtIO “backend” devices. |
| 49 | +2. The guest OS loads VirtIO _frontend_ drivers (e.g., `virtio_net`, |
| 50 | + `virtio_blk`) that speak the VirtIO protocol. |
| 51 | +3. Communication happens via shared memory (`virtqueues`) for I/O operations, |
| 52 | + avoiding full device emulation. |
| 53 | +4. Devices are exposed over the PCI or MMIO bus to the guest. |
| 54 | + |
| 55 | +For example, instead of emulating an Intel e1000 NIC, the host exposes a |
| 56 | +`virtio-net` interface to the guest OS and the guest OS uses the `virtio-net` |
| 57 | +driver to send/receive packets via shared buffers. |
| 58 | + |
| 59 | +## Bounce buffers |
| 60 | + |
| 61 | +### What are bounce buffers? |
| 62 | + |
| 63 | +Bounce buffers are a generic mechanism part of the Linux kernel. They are |
| 64 | +temporary memory buffers used in the Linux kernel to handle situations where |
| 65 | +direct memory access (DMA) can’t be performed directly on the original data |
| 66 | +buffer. This often happens because: |
| 67 | +1. The original buffer is not physically contiguous. |
| 68 | +2. The buffer is in high memory or not accessible to the device. |
| 69 | +3. The buffer doesn’t meet alignment or boundary requirements of the device. |
| 70 | + |
| 71 | +### Why _bounce_ buffers? |
| 72 | + |
| 73 | +Data _bounces_ between: |
| 74 | +- The original buffer (in user/kernel space) and |
| 75 | +- The DMA-capable bounce buffer (used for I/O with the device) |
| 76 | + |
| 77 | +This ensures that data transfers can still happen even when the original memory |
| 78 | +is not suitable or accessible for transfers. |
| 79 | + |
| 80 | +## CCA Realms, VirtIO and bounce buffers |
| 81 | + |
| 82 | +The defining feature of a Realm is that its memory (called *Realm memory*) is |
| 83 | +cryptographically isolated from both the Normal and Secure Worlds. This means |
| 84 | +that: |
| 85 | +- Realm memory is encrypted using keys that are unique to each Realm. |
| 86 | +- Non-Realm entities (like the host OS or hypervisor) cannot directly read or |
| 87 | + write Realm memory. |
| 88 | +- Even Direct Memory Access (DMA) from peripherals or untrusted drivers cannot |
| 89 | + access Realm data. |
| 90 | + |
| 91 | +This design ensures confidentiality but introduces a problem: *how can Realms |
| 92 | +interact with untrusted components*, such as: |
| 93 | +- Network stacks in the host OS, |
| 94 | +- Storage subsystems, |
| 95 | +- I/O devices managed by untrusted drivers? |
| 96 | + |
| 97 | +The solution to safely exchange data between a Realm and the outside World is to |
| 98 | +use _bounce buffers_ as an intermediary ! |
| 99 | + |
| 100 | +### How bounce buffers are used with RME |
| 101 | + |
| 102 | +1. Exporting Data: |
| 103 | + - A Realm application prepares some data (e.g., results of computation). |
| 104 | + - It copies this data from protected Realm memory into a bounce buffer. |
| 105 | + - The Realm notifies the untrusted host or hypervisor that the data is ready. |
| 106 | + - The host retrieves the data from the bounce buffer. |
| 107 | + |
| 108 | +2. Importing Data: |
| 109 | + - The host places data (e.g., input from a file or device) into a bounce buffer. |
| 110 | + - The Realm is notified and validates the source. |
| 111 | + - The Realm copies the data from the bounce buffer into its protected memory. |
| 112 | + |
| 113 | +This pattern preserves confidentiality and integrity of Realm data, since: |
| 114 | +- The Realm never allows direct access to its memory. |
| 115 | +- It can validate and sanitize any data received via bounce buffers. |
| 116 | +- No sensitive data is exposed without explicit copying. |
| 117 | + |
| 118 | +### Confidentiality preserved with bounce buffers, really? |
| 119 | + |
| 120 | +In the previous section, it was mentioned that _bounce buffers preserves |
| 121 | +confidentiality_, a sentence which deserves a bit more thoughts. Bounce buffers |
| 122 | +are nothing more than an explicitly shared temporary area between the Realm |
| 123 | +world and the outside world. This does indeed preserve the confidentiality of |
| 124 | +all the rest of the Realm data. On the other hand, for the data being |
| 125 | +transferred, it is leaving the Realm world and will only remain confidential if it |
| 126 | +is encrypted in some ways, e.g. for network traffic, TLS should be used. |
| 127 | + |
| 128 | +## Seeing a Realm's bounce buffers at work |
| 129 | + |
| 130 | +Let's put this to work and check for ourselves that bounce buffers are used. This |
| 131 | +will build on the Key Broker demo that was used in the [CCA |
| 132 | +Essentials learning path](/learning-paths/servers-and-cloud-computing/cca-essentials/example/), |
| 133 | +demonstrating an end-to-end attestation. |
| 134 | + |
| 135 | +### Start the **K**eyb **B**roker **Server** (KBS) |
| 136 | + |
| 137 | +First, pull the docker container image with the pre-built KBS, and then run the container: |
| 138 | + |
| 139 | +```bash |
| 140 | +docker pull armswdev/cca-learning-path:cca-key-broker-v2 |
| 141 | +docker run --rm -it armswdev/cca-learning-path:cca-key-broker-v2 |
| 142 | +``` |
| 143 | + |
| 144 | +Now within your running docker container, get a list of network interfaces: |
| 145 | + |
| 146 | +```bash |
| 147 | +ip -c a |
| 148 | +``` |
| 149 | + |
| 150 | +The output should look like: |
| 151 | + |
| 152 | +```output |
| 153 | +1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 |
| 154 | + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 |
| 155 | + inet 127.0.0.1/8 scope host lo |
| 156 | + valid_lft forever preferred_lft forever |
| 157 | + inet6 ::1/128 scope host |
| 158 | + valid_lft forever preferred_lft forever |
| 159 | +20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default |
| 160 | + link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 |
| 161 | + inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 |
| 162 | + valid_lft forever preferred_lft forever |
| 163 | +``` |
| 164 | + |
| 165 | +Start the KBS on the `eth0` network interface, and replace 172.17.0.2 shown in |
| 166 | +the command below with the IP address corresponding to eth0 in the output of `ip |
| 167 | +-c a` above. |
| 168 | + |
| 169 | +```bash |
| 170 | +./keybroker-server -v --addr 172.17.0.2 |
| 171 | +``` |
| 172 | + |
| 173 | +The output should look like: |
| 174 | + |
| 175 | +```output |
| 176 | +INFO starting 16 workers |
| 177 | +INFO Actix runtime found; starting in Actix runtime |
| 178 | +INFO starting service: "actix-web-service-172.17.0.2:8088", workers: 16, listening on: 172.17.0.2:8088 |
| 179 | +``` |
| 180 | + |
| 181 | +### Get into a Realm |
| 182 | + |
| 183 | +With the Key Broker Server running in one terminal, open up a new terminal in |
| 184 | +which you will run the **K**ey **B**roker **C**lient (KBC). The intent is to |
| 185 | +observe that the data transmitted over the network (thru `virtio_net`) are |
| 186 | +indeed using bounce buffers. |
| 187 | + |
| 188 | +Pull the docker container image with the pre-built KBC, and then run the container: |
| 189 | + |
| 190 | +```bash |
| 191 | +docker pull armswdev/cca-learning-path:cca-simulation-v2 |
| 192 | +docker run --rm -it armswdev/cca-learning-path:cca-simulation-v2 |
| 193 | +``` |
| 194 | + |
| 195 | +Within the running container, launch the `run-cca-fvp.sh` script to run the Arm |
| 196 | +CCA pre-built binaries on the FVP: |
| 197 | + |
| 198 | +```bash |
| 199 | +./run-cca-fvp.sh |
| 200 | +``` |
| 201 | +The `run-cca-fvp.sh` script uses the screen command to connect to the different |
| 202 | +UARTs in the FVP. |
| 203 | + |
| 204 | +You should see the host Linux kernel boot on your terminal and you will be |
| 205 | +prompted to log in to the host. Enter root as the username: |
| 206 | + |
| 207 | +```output |
| 208 | +[ 4.169458] Run /sbin/init as init process |
| 209 | +[ 4.273748] EXT4-fs (vda): re-mounted 64d1bcff-5d03-412c-83c6-48ec4253590e r/w. Quota mode: none. |
| 210 | +Starting syslogd: OK |
| 211 | +Starting klogd: OK |
| 212 | +Running sysctl: OK |
| 213 | +Starting network: [ 5.254843] smc91x 1a000000.ethernet eth0: link up, 10Mbps, half-duplex, lpa 0x0000 |
| 214 | +udhcpc: started, v1.36.1 |
| 215 | +udhcpc: broadcasting discover |
| 216 | +udhcpc: broadcasting select for 172.20.51.1, server 172.20.51.254 |
| 217 | +udhcpc: lease of 172.20.51.1 obtained from 172.20.51.254, lease time 86400 |
| 218 | +deleting routers |
| 219 | +adding dns 172.20.51.254 |
| 220 | +OK |
| 221 | +
|
| 222 | +Welcome to the CCA host |
| 223 | +host login: root |
| 224 | +(host) # |
| 225 | +``` |
| 226 | + |
| 227 | +Change directory to `/cca` and use `lkvm` to launch a guest Linux in a Realm: |
| 228 | +```bash |
| 229 | +cd /cca |
| 230 | +./lkvm run --realm --disable-sve --irqchip=gicv3-its --firmware KVMTOOL_EFI.fd -c 1 -m 512 --no-pvtime --disk guest-disk.img --restricted_mem --virtio-transport pci --pmu --network mode=user |
| 231 | +``` |
| 232 | + |
| 233 | +You should see the realm boot. Note that `lkvm` is invoked with `--network |
| 234 | +mode=user`, which makes the guest see the network thru a VirtIO device. |
| 235 | + |
| 236 | +After boot up, which might take some time, you will be prompted to log in at the |
| 237 | +guest Linux prompt. Use root again as the username: |
| 238 | + |
| 239 | +```output |
| 240 | +Starting syslogd: OK |
| 241 | +Starting klogd: OK |
| 242 | +Running sysctl: OK |
| 243 | +Starting network: udhcpc: started, v1.36.1 |
| 244 | +udhcpc: broadcasting discover |
| 245 | +udhcpc: broadcasting select for 192.168.33.15, server 192.168.33.1 |
| 246 | +udhcpc: lease of 192.168.33.15 obtained from 192.168.33.1, lease time 14400 |
| 247 | +deleting routers |
| 248 | +adding dns 172.20.51.254 |
| 249 | +OK |
| 250 | +
|
| 251 | +Welcome to the CCA realm |
| 252 | +realm login: root |
| 253 | +(realm) # |
| 254 | +``` |
| 255 | + |
| 256 | +### Observe bounce buffer usage in the realm |
| 257 | + |
| 258 | +First, check that the Linux kernel has tracing support: |
| 259 | + |
| 260 | +```bash { output_lines="2-46" } |
| 261 | +ls /sys/kernel/debug/tracing/events/ |
| 262 | +9p i2c_slave qcom_glink |
| 263 | +alarmtimer icmp qcom_smp2p |
| 264 | +asoc initcall qdisc |
| 265 | +block interconnect ras |
| 266 | +bpf_test_run io_uring raw_syscalls |
| 267 | +bpf_trace iomap rcu |
| 268 | +bridge iommu regmap |
| 269 | +capability ipi regulator |
| 270 | +cgroup irq rpcgss |
| 271 | +chipidea jbd2 rpm |
| 272 | +clk kmem rpmh |
| 273 | +cma ksm rseq |
| 274 | +compaction kvm rtc |
| 275 | +cpuhp kyber sched |
| 276 | +cros_ec libata scmi |
| 277 | +csd lock scsi |
| 278 | +dev lockd signal |
| 279 | +devfreq maple_tree skb |
| 280 | +devlink mdio smbus |
| 281 | +dma memcg sock |
| 282 | +dma_fence migrate spi |
| 283 | +dpaa2_eth mmap spmi |
| 284 | +dpaa_eth mmap_lock sunrpc |
| 285 | +dwc3 mmc swiotlb |
| 286 | +e1000e_trace module task |
| 287 | +enable mtu3 tcp |
| 288 | +error_report musb tegra_apb_dma |
| 289 | +ext4 napi thermal |
| 290 | +fib neigh thermal_power_allocator |
| 291 | +filelock net thp |
| 292 | +filemap netfs timer |
| 293 | +fsl_edma netlink timer_migration |
| 294 | +ftrace nfs timestamp |
| 295 | +gadget nfs4 tlb |
| 296 | +gpio notifier udp |
| 297 | +gpu_mem oom ufs |
| 298 | +handshake optee vmalloc |
| 299 | +header_event page_isolation vmscan |
| 300 | +header_page page_pool watchdog |
| 301 | +hns3 pagemap workqueue |
| 302 | +huge_memory percpu writeback |
| 303 | +hugetlbfs power xdp |
| 304 | +hw_pressure printk xhci-hcd |
| 305 | +hwmon pwm |
| 306 | +i2c qcom_aoss |
| 307 | +``` |
| 308 | + |
| 309 | +As shown in the above transcript, you should get a list of the available trace |
| 310 | +points. |
| 311 | + |
| 312 | +Now, enable the kernel tracing infrastructure together with the bounce buffer |
| 313 | +tracing, read the trace in the background (filtering on `keybroker-app-`) and |
| 314 | +run the Key Broker Client application in the realm, using the endpoint address |
| 315 | +that the Key Broker Server is listening on (from the other terminal): |
| 316 | + |
| 317 | +```bash |
| 318 | +echo 1 > /sys/kernel/debug/tracing/tracing_on |
| 319 | +echo 1 > /sys/kernel/debug/tracing/events/swiotlb/enable |
| 320 | +grep keybroker-app- /sys/kernel/debug/tracing/trace_pipe & |
| 321 | +keybroker-app -v --endpoint http://172.17.0.2:8088 skywalker |
| 322 | +``` |
| 323 | + |
| 324 | +In the `keybroker-app`command above, `skywalker` is the key name that is |
| 325 | +requested from the KBS. |
| 326 | + |
| 327 | +You should get an output looking like: |
| 328 | + |
| 329 | +```output |
| 330 | +INFO Requesting key named 'skywalker' from the keybroker server with URL http://172.17.0.2:8088/keys/v1/key/skywalker |
| 331 | +INFO Challenge (64 bytes) = [5c, ec, 1e, f5, 93, 54, 4a, 8a, ee, 2e, 46, a0, 50, 0d, 41, dd, d4, 60, b0, 58, 5b, 51, 71, 76, d1, 66, d3, b7, 38, e8, af, ae, 0a, 07, 4e, c5, 60, dc, 4a, c0, b8, 73, 98, d9, bd, af, 41, 96, 99, 6d, 74, cc, 19, 70, 24, c4, c9, 5c, 21, 61, 1a, cb, 76, 75] |
| 332 | +INFO Submitting evidence to URL http://172.17.0.2:8088/keys/v1/evidence/1928844131 |
| 333 | +INFO Attestation success :-) ! The key returned from the keybroker is 'May the force be with you.' |
| 334 | + keybroker-app-143 [000] b..2. 1772.607321: swiotlb_bounced: dev_name: 0000:00:00.0 dma_mask=ffffffffffffffff dev_addr=80b6717e size=66 FORCE |
| 335 | + keybroker-app-143 [000] b..2. 1772.644478: swiotlb_bounced: dev_name: 0000:00:00.0 dma_mask=ffffffffffffffff dev_addr=80b6717e size=66 FORCE |
| 336 | +``` |
| 337 | + |
| 338 | +Note that the interleaving of the trace messages and KBC messages might differ |
| 339 | +from one run to another. |
0 commit comments