Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vhost-device-spi: Add initial implementation #640

Merged
merged 1 commit into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"vhost-device-scsi",
"vhost-device-scmi",
"vhost-device-sound",
"vhost-device-spi",
"vhost-device-template",
"vhost-device-vsock",
]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Here is the list of device backends that we support:
- [SCMI](https://github.com/rust-vmm/vhost-device/blob/main/vhost-device-scmi/README.md)
- [SCSI](https://github.com/rust-vmm/vhost-device/blob/main/vhost-device-scsi/README.md)
- [Sound](https://github.com/rust-vmm/vhost-device/blob/main/vhost-device-sound/README.md)
- [SPI](https://github.com/rust-vmm/vhost-device/blob/main/vhost-device-spi/README.md)
stefano-garzarella marked this conversation as resolved.
Show resolved Hide resolved
- [VSOCK](https://github.com/rust-vmm/vhost-device/blob/main/vhost-device-vsock/README.md)

The vhost-device workspace also provides a
Expand Down
2 changes: 1 addition & 1 deletion coverage_config_x86_64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 77.63,
"coverage_score": 75.76,
"exclude_path": "",
"crate_features": ""
}
15 changes: 15 additions & 0 deletions vhost-device-spi/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog
## [Unreleased]

### Added

### Changed

### Fixed

### Deprecated

## [0.1.0]

First release

35 changes: 35 additions & 0 deletions vhost-device-spi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "vhost-device-spi"
version = "0.1.0"
authors = ["Haixu Cui <[email protected]>"]
description = "vhost spi backend device"
repository = "https://github.com/rust-vmm/vhost-device"
readme = "README.md"
keywords = ["spi", "vhost", "virt", "backend"]
categories = ["virtualization"]
license = "Apache-2.0 OR BSD-3-Clause"
HaixuCui marked this conversation as resolved.
Show resolved Hide resolved
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"]

[dependencies]
clap = { version = "4.5", features = ["derive"] }
env_logger = "0.11"
libc = "0.2"
log = "0.4"
thiserror = "1.0"
vhost = { version = "0.11", features = ["vhost-user-backend"] }
vhost-user-backend = "0.15"
virtio-bindings = "0.2.2"
virtio-queue = "0.12"
vm-memory = "0.14.1"
vmm-sys-util = "0.12"
bitflags = "2.4.0"

[dev-dependencies]
assert_matches = "1.5"
virtio-queue = { version = "0.12", features = ["test-utils"] }
vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-atomic"] }
1 change: 1 addition & 0 deletions vhost-device-spi/LICENSE-APACHE
1 change: 1 addition & 0 deletions vhost-device-spi/LICENSE-BSD-3-Clause
80 changes: 80 additions & 0 deletions vhost-device-spi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# vhost-device-spi - SPI emulation backend daemon

## Description
This program is a vhost-user backend that emulates a VirtIO SPI bus.
This program takes the layout of the spi bus and its devices on the host
OS and then talks to them via the `/dev/spidevX.Y` interface when a request
comes from the guest OS for a SPI device.

## Synopsis

```shell
vhost-device-spi [OPTIONS]
```

## Options
```text
-h, --help

Print help.

-s, --socket-path=PATH

Location of vhost-user Unix domain sockets, this path will be suffixed with
0,1,2..socket_count-1.

-c, --socket-count=INT

Number of guests (sockets) to attach to, default set to 1.

-l, --device=SPI-DEVICES

Spi device full path at the host OS in the format:
/dev/spidevX.Y
vireshk marked this conversation as resolved.
Show resolved Hide resolved

Here,
X: is spi controller's bus number.
Y: is chip select index.
```

## Examples

### Dependencies
For testing the device the required dependencies are:
- Linux:
- Integrate *virtio-spi* driver:
- https://lwn.net/Articles/966715/
- Set `CONFIG_SPI_VIRTIO=y`
- QEMU:
- Integrate vhost-user-spi QEMU device:
- https://lore.kernel.org/all/[email protected]/

### Test the device
First start the daemon on the host machine::

````suggestion
```console
vhost-device-spi --socket-path=vspi.sock --socket-count=1 --device "/dev/spidev0.0"
```
````

The QEMU invocation needs to create a chardev socket the device spi
use to communicate as well as share the guests memory over a memfd.

````suggestion
```console
qemu-system-aarch64 -m 1G \
-chardev socket,path=/home/root/vspi.sock0,id=vspi \
-device vhost-user-spi-pci,chardev=vspi,id=spi \
-object memory-backend-file,id=mem,size=1G,mem-path=/dev/shm,share=on \
-numa node,memdev=mem \
...
```
````

## License

This project is licensed under either of

- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0
- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause)
77 changes: 77 additions & 0 deletions vhost-device-spi/src/linux_spi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Linux SPI bindings
//
// Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
// Haixu Cui <[email protected]>
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

use bitflags::bitflags;
use vmm_sys_util::{ioctl_ioc_nr, ioctl_ior_nr, ioctl_iow_nr};

/// Describes a single SPI transfer
#[derive(Debug)]
#[repr(C)]
pub struct SpiIocTransfer {
/// Holds pointer to userspace buffer with transmit data, or null
pub tx_buf: u64,
/// Holds pointer to userspace buffer for receive data, or null.
pub rx_buf: u64,
/// Length of tx and rx buffers, in bytes.
pub len: u32,
/// Temporary override of the device's bitrate.
pub speed_hz: u32,
/// If nonzero, how long to delay after the last bit transfer
/// before optionally deselecting the device before the next transfer.
pub delay_usecs: u16,
/// Temporary override of the device's wordsize.
pub bits_per_word: u8,
/// True to deselect device before starting the next transfer.
pub cs_change: u8,
/// Number of bits used for writing.
pub tx_nbits: u8,
/// Number of bits used for reading.
pub rx_nbits: u8,
/// If nonzero, how long to wait between words within one
/// transfer. This property needs explicit support in the SPI controller,
/// otherwise it is silently ignored
pub word_delay_usecs: u8,
pub _padding: u8,
}

/// Linux SPI definitions
/// IOCTL commands, refer Linux's Documentation/spi/spidev.rst for further details.
const _IOC_SIZEBITS: u32 = 14;
const _IOC_SIZESHIFT: u32 = 16;
const SPI_IOC_MESSAGE_BASE: u32 = 0x40006b00;

ioctl_ior_nr!(SPI_IOC_RD_BITS_PER_WORD, 107, 3, u8);
ioctl_iow_nr!(SPI_IOC_WR_BITS_PER_WORD, 107, 3, u8);
ioctl_ior_nr!(SPI_IOC_RD_MAX_SPEED_HZ, 107, 4, u32);
ioctl_iow_nr!(SPI_IOC_WR_MAX_SPEED_HZ, 107, 4, u32);
ioctl_ior_nr!(SPI_IOC_RD_MODE32, 107, 5, u32);
ioctl_iow_nr!(SPI_IOC_WR_MODE32, 107, 5, u32);

// Corresponds to the SPI_IOC_MESSAGE macro in Linux
pub fn spi_ioc_message(n: u32) -> u64 {
let mut size: u32 = 0;
if n * 32 < (1 << _IOC_SIZEBITS) {
size = n * 32;
}
(SPI_IOC_MESSAGE_BASE | (size << _IOC_SIZESHIFT)) as u64
}

bitflags! {
pub struct LnxSpiMode: u32 {
const CPHA = 1 << 0;
const CPOL = 1 << 1;
const CS_HIGH = 1 << 2;
const LSB_FIRST = 1 << 3;
const LOOP = 1 << 5;
const TX_DUAL = 1 << 8;
const TX_QUAD = 1 << 9;
const TX_OCTAL = 1 << 13;
const RX_DUAL = 1 << 10;
const RX_QUAD = 1 << 11;
const RX_OCTAL = 1 << 14;
}
}
Loading