This guide provides step-by-step instructions to set up, build, and configure Linux kernel version 6.14 with Rust support on an Ubuntu 24.04 virtual machine (VM) running in QEMU. It includes version prerequisites, enabling Rust support, and building/testing sample Rust kernel modules (rust_mymodule
and rust_usb_serial
). The setup supports a physical QinHeng CH340 USB-serial device (0x1a86:0x7523
) passed through to the VM and a virtual USB device (0x1234:0x5678
) for testing.
- OS: Ubuntu 24.04 LTS (VM disk:
u22s_rust.qcow2
) - CPU: x86_64 architecture (QEMU with
-cpu host
) - Memory: 16 GB RAM (QEMU with
-m 16G
) - Disk Space: ~40 GB for kernel source, tools, and build artifacts
- QEMU: Configured with KVM, VirtIO, and USB host passthrough
- Internet: Required for downloading sources and tools
- Rust: 1.83.0
- Clang/LLVM: 19.1.7 or later
- Bindgen: 0.71.1 or later
- GCC: For kernel build dependencies
- Make, Git, and Build Tools: Standard development tools
Install required packages in the Ubuntu 24.04 VM:
sudo apt update
sudo apt install -y build-essential flex bison libssl-dev libelf-dev libncurses-dev bc dwarves git python3 pahole cpio clang lld cargo rustc libclang-dev linux-headers-$(uname -r)
-
Install Rust: Install Rust via
rustup
:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source ~/.cargo/env
-
Set Rust Version: Linux 6.14 requires Rust 1.83.0:
rustup install 1.83.0 rustup default 1.83.0 rustup component add rust-src rustc --version # Should output: rustc 1.83.0
-
Install Bindgen: Install
bindgen-cli
for Rust bindings:cargo install --locked --version 0.71.1 bindgen-cli bindgen --version # Should output: bindgen 0.71.1
-
Install LLVM/Clang: Install Clang 19 for LLVM-based builds:
wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 19 echo 'export PATH=/usr/lib/llvm-19/bin:$PATH' >> ~/.bashrc source ~/.bashrc sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100 sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100 clang --version # Should output: Ubuntu clang version 19.1.7
-
VM Configuration: Ensure the VM is running with the following QEMU command:
sudo qemu-system-x86_64 -enable-kvm -cpu host -m 16G -smp "$(nproc)" \ -drive file=/home/mighty/script/vm/u22s_rust.qcow2,if=virtio,cache=none,aio=native,format=qcow2 \ -net nic,model=virtio -net user,hostfwd=tcp::6657-:22 \ -vga std -display gtk -boot menu=on \ -device virtio-balloon -device virtio-rng-pci \ -usb -device usb-host,vendorid=0x1a86,productid=0x7523 # only if using arduino uno or mega
-
Verify USB Passthrough: Inside the VM, check for the CH340 device:
lsusb
Expected output:
Bus 001 Device 002: ID 1a86:7523 QinHeng Electronics CH340 serial converter
-
Clone Linux 6.14: Clone the kernel source:
git clone --branch v6.14 --depth 1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ~/linux-6.14 cd ~/linux-6.14
-
Verify Rust Availability:
rustup component add rust-src make LLVM=1 rustavailable
Ensure no errors. If issues arise, verify
rustc
,bindgen
, andlibclang-dev
. -
Creating Config: Use the current kernel config:
make LLVM=1 defconfig
-
Enable Rust and USB Support: Configure the kernel:
make LLVM=1 menuconfig
- Enable Rust:
General setup
->Rust language support
-> SetCONFIG_RUST=y
- Enable USB and TTY:
Device Drivers
->USB support
-> SetCONFIG_USB=y
andCONFIG_USB_SERIAL=y
Device Drivers
->Character devices
-> SetCONFIG_TTY=y
- Enable Rust samples:
Samples
->Rust samples
-> Enable:Minimal
(CONFIG_SAMPLE_RUST_MINIMAL=m
)Printing macros
(CONFIG_SAMPLE_RUST_PRINT=m
)Rust custom module sample
(CONFIG_SAMPLE_RUST_MYMODULE=m
) (after adding)Rust virtual USB-serial driver sample
(CONFIG_SAMPLE_RUST_USB_SERIAL=m
) (after adding)
- Save and exit.
- Enable Rust:
A simple module that prints messages on load and unload.
-
Create
rust_mymodule.rs
:cat > samples/rust/rust_mymodule.rs << 'EOF' // SPDX-License-Identifier: GPL-2.0 //! Rust custom module sample. use kernel::prelude::*; module! { type: MyModule, name: "rust_mymodule", author: "YourName", description: "A custom Rust kernel module", license: "GPL", } struct MyModule; impl kernel::Module for MyModule { fn init(_module: &'static ThisModule) -> Result<Self> { pr_info!("Hello from My Rust Module!\n"); Ok(MyModule) } } impl Drop for MyModule { fn drop(&mut self) { pr_info!("Goodbye from My Rust Module!\n"); } } EOF
-
Update
samples/rust/Kconfig
:echo ' config SAMPLE_RUST_MYMODULE tristate "Rust custom module sample" depends on RUST help This option builds the Rust custom module sample. To compile this as a module, choose M here. ' >> samples/rust/Kconfig
-
Update
samples/rust/Makefile
:echo 'obj-$(CONFIG_SAMPLE_RUST_MYMODULE) += rust_mymodule.o' >> samples/rust/Makefile
-
Enable the Module:
make LLVM=1 menuconfig
Set
Rust custom module sample
toM
.
Important: USB module part of code is untested
A driver that registers a USB device and exposes /dev/ttyUSB0
, supporting both a virtual device (0x1234:0x5678
) and the CH340 (0x1a86:0x7523
).
-
Create
rust_usb_serial.rs
:cat > samples/rust/rust_usb_serial.rs << 'EOF' // SPDX-License-Identifier: GPL-2.0 #![no_std] use kernel::prelude::*; use kernel::{ c_str, error::code::ENOMEM, serial::tty::{self, TtyOperations, TtyPort, TtyPortOperations}, usb::{self, Interface, UsbDevice}, }; module! { type: RustUsbSerial, name: "rust_usb_serial", author: "Pranav", description: "Virtual USB-serial driver in Rust", license: "GPL", params: {}, } const VENDOR_ID: u16 = 0x1234; const PRODUCT_ID: u16 = 0x5678; const CH340_VENDOR_ID: u16 = 0x1a86; const CH340_PRODUCT_ID: u16 = 0x7523; const TTY_NAME: &str = "ttyUSB"; kernel::module_usb_driver! { type: UsbSerialDriver, id_table: [ { idVendor: VENDOR_ID, idProduct: PRODUCT_ID }, { idVendor: CH340_VENDOR_ID, idProduct: CH340_PRODUCT_ID }, ], } struct UsbSerialDriver; #[vtable] impl usb::Driver for UsbSerialDriver { type Data = Pin<Box<TtyPort>>; fn probe(device: &mut UsbDevice, _interface: Interface) -> Result<()> { pr_info!("Rust USB-serial: Probing device {:04x}:{:04x}\n", device.vendor_id(), device.product_id()); let port = Pin::from(Box::try_new(TtyPort::new())?); port.as_ref().register(c_str!(TTY_NAME), 0, &device.device())?; device.set_data(port); Ok(()) } fn disconnect(device: &mut UsbDevice) { pr_info!("Rust USB-serial: Disconnecting device\n"); } } struct SerialPortOps; #[vtable] impl TtyPortOperations for SerialPortOps { fn port_activate(port: &TtyPort, _tty: &tty::Struct) -> Result<()> { pr_info!("Rust USB-serial: Port activated\n"); Ok(()) } fn port_shutdown(port: &TtyPort) { pr_info!("Rust USB-serial: Port shutdown\n"); } } struct SerialTtyOps; #[vtable] impl TtyOperations for SerialTtyOps { type PortOps = SerialPortOps; fn open(tty: &tty::Struct, _file: &kernel::file::File) -> Result<()> { pr_info!("Hello, world!\n"); tty::port::open::<Self>(tty)?; Ok(()) } fn close(tty: &tty::Struct, _file: &kernel::file::File) { pr_info!("Rust USB-serial: TTY closed\n"); tty::port::close::<Self>(tty); } fn install(port: &TtyPort, tty: &tty::Struct) -> Result<()> { tty::port::install::<Self>(port, tty)?; Ok(()) } } struct RustUsbSerial; impl kernel::Module for RustUsbSerial { fn init(_module: &'static ThisModule) -> Result<Self> { pr_info!("Rust USB-serial: Initializing\n"); Ok(RustUsbSerial) } } impl Drop for RustUsbSerial { fn drop(&mut self) { pr_info!("Rust USB-serial: Exiting\n"); } } EOF
-
Update
samples/rust/Kconfig
:echo ' config SAMPLE_RUST_USB_SERIAL tristate "Rust virtual USB-serial driver sample" depends on RUST && USB && TTY help This option builds a Rust virtual USB-serial driver sample that registers a USB device (vendor 0x1234:0x5678 or 0x1a86:0x7523) and exposes a TTY device. To compile this as a module, choose M here. ' >> samples/rust/Kconfig
-
Update
samples/rust/Makefile
:echo 'obj-$(CONFIG_SAMPLE_RUST_USB_SERIAL) += rust_usb_serial.o' >> samples/rust/Makefile
-
Enable the Module:
make LLVM=1 menuconfig
Set
Rust virtual USB-serial driver sample
toM
.
-
Clean the Build:
make LLVM=1 M=samples/rust clean
-
Build Rust Modules:
make LLVM=1 -j$(nproc) M=samples/rust
-
Build Kernel and Modules:
make LLVM=1 -j$(nproc) make LLVM=1 modules
-
Install:
sudo make LLVM=1 modules_install sudo make LLVM=1 install sudo update-grub sudo reboot
-
Load:
sudo modprobe rust_mymodule
-
Check Logs:
dmesg | tail -n 20
Expected:
[ 123.456789] Hello from My Rust Module!
-
Unload:
sudo rmmod rust_mymodule
Expected:
[ 123.456789] Goodbye from My Rust Module!
-
Load:
sudo rmmod ch341 # Unload default CH340 driver sudo modprobe rust_usb_serial
-
Test with CH340:
ls /dev/ttyUSB0 sudo cat /dev/ttyUSB0
Or:
echo "test" | sudo tee /dev/ttyUSB0
-
Test with Virtual Device: In a separate terminal, run QEMU with a virtual USB device:
sudo qemu-system-x86_64 -m 512 -kernel /boot/vmlinuz-$(uname -r) \ -initrd /boot/initrd.img-$(uname -r) \ -append "root=/dev/sda console=ttyS0" \ -nographic -usb -device usb-ehci,id=ehci \ -device usb-serial,vendorid=0x1234,productid=0x5678
Then:
ls /dev/ttyUSB0 sudo cat /dev/ttyUSB0
-
Check Logs:
dmesg | tail -n 20
Expected:
[ 123.456789] Rust USB-serial: Initializing [ 123.456790] Rust USB-serial: Probing device 1a86:7523 # or 1234:5678 [ 123.456791] Rust USB-serial: Port activated [ 123.456792] Hello, world!
-
Unload:
sudo rmmod rust_usb_serial sudo modprobe ch341 # Restore CH340 driver
- Rust Build Errors:
- Verify versions:
rustc --version
,bindgen --version
,clang --version
. - Check logs:
grep -i "error\|warning" build.log
- Verify versions:
- No
/dev/ttyUSB0
:- Ensure
CONFIG_USB=y
,CONFIG_TTY=y
,CONFIG_USB_SERIAL=y
:grep CONFIG_USB .config grep CONFIG_TTY .config grep CONFIG_USB_SERIAL .config
- Check
dmesg
for probe errors.
- Ensure
- Module Load Fails:
- Verify kernel/module version match:
uname -r
. - Check
CONFIG_MODVERSIONS
:grep CONFIG_MODVERSIONS .config grep CONFIG_EXTENDED_MODVERSIONS .config
- Verify kernel/module version match:
- CH340 Issues:
- Ensure USB passthrough:
lsusb
. - Unload conflicting drivers:
sudo rmmod ch341
.
- Ensure USB passthrough:
-
QEMU VM Configuration:
- Your QEMU command passes the CH340 device (
0x1a86:0x7523
) to the VM, confirmed bylsusb
. Therust_usb_serial
driver in theREADME.md
includes both the virtual device (0x1234:0x5678
) and CH340 IDs to support testing in both scenarios. - The VM uses 16 GB RAM and all CPU cores (
-smp "$(nproc)"
), ensuring sufficient resources for kernel builds.
- Your QEMU command passes the CH340 device (
-
CH340 Device:
- The CH340 is typically handled by the
ch341
module. To test withrust_usb_serial
, you must unloadch341
first (sudo rmmod ch341
). TheREADME.md
includes this step. - If you prefer to test only the virtual device, remove the CH340 ID from the
id_table
inrust_usb_serial.rs
.
- The CH340 is typically handled by the
-
Recent Build Issues:
- Your
rust_mymodule
build failed due toallocator_api
andglobal_asm
features (command #448). The fixed version in theREADME.md
(same as your command #453) removes these, ensuring it builds successfully. - Confirm the build worked:
ls -l samples/rust/rust_mymodule.ko sudo modprobe rust_mymodule dmesg | tail -n 20
- Your
-
Virtual USB Device Testing:
- Your history shows use of
VirtualUSBDevice
(commands #486–508). TheREADME.md
includes QEMU-based testing, but you can also useVirtualUSBDevice
:cd ~/VirtualUSBDevice make clean && make sudo modprobe vhci-hcd sudo ./VirtualUSBDevice sudo modprobe rust_usb_serial ls /dev/ttyUSB0
- Your history shows use of
-
Tool Versions:
rustc 1.83.0
,bindgen 0.71.1
, andclang 19.1.7
are correct and exceed the minimum requirements for Linux 6.14.
To finalize the setup and ensure everything works:
-
Kernel Config Confirmation:
grep CONFIG_USB /boot/config-$(uname -r) grep CONFIG_TTY /boot/config-$(uname -r) grep CONFIG_USB_SERIAL /boot/config-$(uname -r) grep CONFIG_SAMPLE_RUST_USB_SERIAL .config
Ensure
CONFIG_USB=y
,CONFIG_TTY=y
,CONFIG_USB_SERIAL=y
, andCONFIG_SAMPLE_RUST_USB_SERIAL=m
. -
Current Kernel Version:
uname -a
Confirm it’s Linux 6.14 and matches the built modules.
-
Build Status of
rust_usb_serial
: Have you created and builtrust_usb_serial.rs
? If not, follow theREADME.md
steps.ls samples/rust/rust_usb_serial.rs ls -l samples/rust/rust_usb_serial.ko
-
CH340 vs. Virtual Device: Do you want
rust_usb_serial
to prioritize the CH340 (0x1a86:0x7523
) or the virtual device (0x1234:0x5678
)? TheREADME.md
supports both, but I can adjust theid_table
if you prefer one. -
Build Logs: If any builds fail, share:
grep -i "error\|warning" build.log
-
Follow the
README.md
:- Set up
rust_mymodule
andrust_usb_serial
as described. - Build and test both modules.
- Use the QEMU or
VirtualUSBDevice
instructions for virtual device testing.
- Set up
-
Test with CH340:
sudo rmmod ch341 sudo modprobe rust_usb_serial ls /dev/ttyUSB0 sudo cat /dev/ttyUSB0 dmesg | tail -n 20
-
Share Results:
- Confirm
rust_mymodule
works (dmesg
output). - Report if
rust_usb_serial
builds and creates/dev/ttyUSB0
. - Provide the requested config outputs.
- Confirm
-
Optional Enhancements:
- Add read/write support to
rust_usb_serial
for data transfer. - Move modules to
rust-out-of-tree-module
(sharels -R ~/rust-out-of-tree-module
if needed).
- Add read/write support to
https://mirrors.edge.kernel.org/pub/tools/llvm/rust/