Last active
June 8, 2024 00:42
-
-
Save johnandersen777/d9c9a4e5b4dc2804be6bb0a93d524b3f to your computer and use it in GitHub Desktop.
Run USB VM (not working)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import subprocess | |
import re | |
from dataclasses import dataclass | |
from pathlib import Path | |
import glob | |
import argparse | |
@dataclass | |
class USBInterface: | |
""" | |
A dataclass to represent a USB Interface. | |
Attributes: | |
bInterfaceClass (int): The interface class of the USB device. | |
""" | |
bInterfaceClass: int | |
@staticmethod | |
def from_lsusb_output(lsusb_output: str): | |
""" | |
Parses the lsusb -v output to extract the bInterfaceClass value. | |
Args: | |
lsusb_output (str): The detailed output from lsusb -v. | |
Returns: | |
USBInterface: An instance of USBInterface with bInterfaceClass set. | |
""" | |
bInterfaceClass = None | |
for line in lsusb_output.splitlines(): | |
if 'bInterfaceClass' in line: | |
match = re.search(r'bInterfaceClass\s+(\d+)', line) | |
if match: | |
bInterfaceClass = int(match.group(1)) | |
break | |
return USBInterface(bInterfaceClass=bInterfaceClass) | |
def find_mass_storage_device(): | |
""" | |
Finds a USB device with the bInterfaceClass set to Mass Storage. | |
Returns: | |
tuple: A tuple containing the bus and device numbers of the found USB device. | |
""" | |
lsusb_output = subprocess.run(['lsusb'], capture_output=True, text=True).stdout | |
for line in lsusb_output.splitlines(): | |
bus, device = line.split()[1], line.split()[3][:-1] | |
lsusb_v_output = subprocess.run(['lsusb', '-v', '-s', f'{bus}:{device}'], capture_output=True, text=True).stdout | |
usb_interface = USBInterface.from_lsusb_output(lsusb_v_output) | |
if usb_interface.bInterfaceClass == 8: # Mass Storage | |
return bus, device | |
return None, None | |
def start_qemu_with_usb(bus, device, iso_image, qemu_path='qemu-system-x86_64'): | |
""" | |
Starts the QEMU virtual machine with the specified ISO image and USB device. | |
Args: | |
bus (str): The bus number of the USB device. | |
device (str): The device number of the USB device. | |
iso_image (str): The path to the ISO image to boot from. | |
qemu_path (str): The path to the QEMU binary. | |
""" | |
if not bus or not device: | |
print("No Mass Storage USB device found.") | |
return | |
usb_device_path = f'/dev/bus/usb/{bus}/{device}' | |
print(f"Mass Storage USB device found at {usb_device_path}") | |
qemu_command = [ | |
qemu_path, | |
'-m', '4096', | |
'-smp', '2', | |
'-cdrom', iso_image, | |
'-boot', 'd', | |
'-enable-kvm', | |
'-usb', | |
'-device', f'usb-host,hostbus={bus},hostaddr={device}', | |
'-vga', 'virtio', | |
'-display', 'sdl', | |
'-cpu', 'host' | |
] | |
subprocess.run(qemu_command) | |
def find_iso_image(directory: Path, pattern: str): | |
""" | |
Searches for an ISO file in the specified directory using the provided pattern. | |
Args: | |
directory (Path): The directory to search in. | |
pattern (str): The glob pattern to use for searching. | |
Returns: | |
Path: The path to the found ISO file, or None if no file was found. | |
""" | |
files = list(directory.glob(pattern)) | |
if not files: | |
print(f"No ISO files found in {directory} with pattern {pattern}.") | |
return None | |
return files[0] | |
def parse_arguments(): | |
""" | |
Parses command-line arguments. | |
Returns: | |
argparse.Namespace: The parsed arguments. | |
""" | |
parser = argparse.ArgumentParser(description='Run QEMU with a Fedora Workstation live ISO image and pass through a USB mass storage device.') | |
parser.add_argument('--iso', type=str, help='Path to the Fedora Workstation live ISO image.') | |
parser.add_argument('--bus', type=str, help='USB bus number.') | |
parser.add_argument('--device', type=str, help='USB device number.') | |
return parser.parse_args() | |
if __name__ == '__main__': | |
args = parse_arguments() | |
# Path to the Downloads directory | |
downloads_path = Path.home() / 'Downloads' | |
# Find the Fedora Workstation live ISO image | |
if args.iso: | |
iso_image = Path(args.iso) | |
else: | |
iso_image = find_iso_image(downloads_path, 'Fedora-Workstation-Live-*.iso') | |
if iso_image is None: | |
exit(1) | |
# Find the mass storage USB device | |
if args.bus and args.device: | |
bus, device = args.bus, args.device | |
else: | |
bus, device = find_mass_storage_device() | |
# Start the QEMU VM with the found USB device | |
start_qemu_with_usb(bus, device, str(iso_image)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Start Fedora VM with USB
(.venv) $ sudo lsusb -v -s 1:17
Bus 001 Device 017: ID 24a9:205a USB DISK
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.10
bDeviceClass 0 [unknown]
bDeviceSubClass 0 [unknown]
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x24a9
idProduct 0x205a USB DISK
bcdDevice 2.00
iManufacturer 2
iProduct 3 USB DISK
iSerial 4 97081911
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0020
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 200mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk-Only
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0