Skip to content

How to sniff verbs from a Windows sound driver

Ryan Prescott edited this page Apr 18, 2021 · 10 revisions

If you have trouble with any of these steps, check the bottom of this article for the Troubleshooting section.

Building the virtual Windows environment

Before we can capture any output from the sound driver, we first need to get a working Windows 10 environment built.

  1. Create a new directory in your home folder where we will be building the virtual environment.

    mkdir ~/windows_vm
    cd ~/windows_vm
    
  2. Install the necessary dependencies:

    sudo apt update
    sudo apt install git qemu qemu-utils build-essential build-dep
    
  3. Clone the custom QEMU build created by @jcs.

    git clone https://github.com/jcs/qemu
    
  4. Build QEMU from source. NOTE: Your mileage may vary on this step. This is a basic example that may need to be tailored to your specific build environment. Open an issue and describe the errors you receive if any, and I can help you through this and add it to my troubleshooting section.

    cd qemu
    ./configure
    make -j$(nproc)
    
  5. Copy the QEMU binary into your working directory and delete the QEMU source directory.

    cd ..
    cp qemu/x86_64-softmmu/qemu-system-x86_64 .
    rm -rf ./qemu
    
  6. Create a new virtual disk for your Windows installation.

    qemu-img create -f qcow2 win10.img 20G
    
  7. Get a 64-bit Windows 10 ISO from here, rename it to "win10.iso" and copy it to your working directory.

  8. Install Windows 10 using the following command:

    ./qemu-system-x86_64 -enable-kvm -hda win10.img -cdrom win10.iso -m 4G -smp 4
    

    NOTE: If Windows refuses to boot, you can grab older builds from here. I've had success with Windows 10 1809.

    If you do not wish to use a Microsoft account, disconnect your computer from the internet during the second stage of the install, then wait for Windows to prompt you to create a local account.

  9. After you have verified that installation was successful and you have a working Windows 10 virtual machine, power down the virtual machine.

Setting up VFIO and passing your sound card to the virtual machine

We now need to set up your computer to pass the sound device through to the virtual machine.

  1. First, let's get the information about your multimedia audio controller:

    lspci -nn | grep audio
    

    The output should be similar to this:

    00:1f.3 Multimedia audio controller [0401]: Intel Corporation Cannon Lake PCH cAVS [8086:a348] (rev 10)
    

    Keep note of these two parts. We will need this information to bind the sound card to your virtual machine.

    00:1f.3 <--- bus, device, and function numbers
    8086:a348 <--- PCI device ID
    
  2. Now, let's set the kernel parameters that allow your system to pass the device through to the virtual machine.

    sudo kernelstub -a "pci-stub.ids=8086:a348 iommu=pt intel_iommu=on"
    

    Replace 8086:a348 above with whatever your actual PCI device ID is. If you have an AMD processor, replace intel_iommu with amd_iommu.

  3. We're going to borrow a script from @Conmanx360 to bind the sound card. Create a new file called "vfio-bind.sh" in your working directory with the following content:

    modprobe vfio-pci
    
    for dev in "$@"; do
            vendor=$(cat /sys/bus/pci/devices/$dev/vendor)
            device=$(cat /sys/bus/pci/devices/$dev/device)
            if [ -e /sys/bus/pci/devices/$dev/driver ]; then
                    echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
            fi
            echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
    done
    

    Then, in a terminal, make it executable:

    chmod +x vfio-bind.sh
    
  4. We're also going to create a script to start the Windows 10 virtual machine with the sound card bound to it. Create a new file in your working directory called "startvm.sh" with the following content:

    sudo ./qemu-system-x86_64 -enable-kvm -hda win10.img -m 4G -smp 4 -device vfio-pci,host=0000:xx:xx.x,x-no-mmap=true -trace events=./events.txt -monitor stdio
    

    where "xx:xx.x" is the bus, device and function numbers from earlier.

    Again, make it executable:

    chmod +x startvm.sh
    
  5. Finally, create a file called "events.txt" with the following content:

    -vfio_region_read
    vfio_region_write
    
  6. Now, reboot your machine and we can start capturing the verbs from the sound card.

Capturing verbs

  1. Open a new terminal and cd into your working directory.

  2. Execute the following to bind your sound card:

    sudo ./vfio-bind.sh 0000:xx:xx.x
    
  3. Now start your virtual machine with the following:

    ./startvm.sh
    

We can't start capturing verbs yet because you most likely don't have the sound drivers installed for your sound card. Inside the virtual machine, go online and download and install the sound drivers. Reboot the virtual machine and ensure that you are able to hear sound from both speakers. When you know the sound is working, shut the virtual machine down.

  1. We can now start capturing the verbs. Start the virtual machine and write the output to a file: ./startvm.sh > qemu-output.txt
  2. After booting into Windows, mess with the volume knob and capture plenty of output.
  3. Shut down the virtual machine and reboot your computer.

Cleaning up and testing captured verbs

  1. Open a new terminal and cd into your working directory.

  2. Clone my realtek-verb-tools package.

    git clone https://github.com/ryanprescott/realtek-verb-tools
    
  3. Ensure that you have python3 installed:

    sudo apt install python3
    
  4. Run the following command to clean up the QEMU output into a list of relevant verbs:

    python3 realtek-verb-tools/cleanverbs.py qemu-output.txt > clean-verbs.txt
    
  5. Open the "clean-verbs.txt" file and verify that you have a list of verbs. You should see hundreds, possibly thousands of lines that look like this:

    0x20 0x500 0x1a
    0x20 0x500 0x1a
    0x20 0x48c 0x3
    0x20 0x500 0x1a
    0x20 0x48c 0x3
    
  6. Now we can test the verbs. Play some sound in the background, i.e. a looping MP3 file in VLC, and execute the following command to start replaying the verbs that you captured:

    sudo python3 realtek-verb-tools/applyverbs.py clean-verbs.txt -d
    

    The -d flag puts a delay between the application of each verb, and also shows the line number of each verb.

  7. When you hear sound coming out of both speakers, press Ctrl-C in the terminal and note the line number. You can now make a copy of your "clean-verbs.txt" file called "necessary-verbs.txt" with only the verbs up until that line.

  8. Run the command from step 6 again against "necessary-verbs.txt", and you may now continue to remove verbs from the top of the file until a) the codec becomes unresponsive and you need to reboot, or b) the sound no longer comes out of both speakers. After that point, you need to walk back to the previous edit. These are your final verbs.

  9. Reboot the machine, cd into your working directory and run this command to apply them without the delay / debug information: sudo python3 realtek-verb-tools/applyverbs.py necessary-verbs.py

  10. If you hear sound coming from both speakers, congratulations! If not, walk back to step 7 and be a little more conservative when removing verbs from the "necessary-verbs.txt" file. I ended up with 475 verbs necessary to turn my speakers on.

  11. Armed with a working configuration, you can present this information when filing a kernel bug report and hopefully help fellow users with the same issue.

Cleaning up

  1. Run the following command to remove the kernel changes from earlier:

    sudo kernelstub -d "pci-stub.ids=8086:a348 iommu=pt intel_iommu=on"
    

Troubleshooting

Problem

You receive an error similar to the following:

qemu-system-x86_64: -device vfio-pci,host=0000:00:1f.3,x-no-mmap=true: vfio error: 0000:00:1f.3: group 12 is not viable
Please ensure all devices within the iommu_group are bound to their vfio bus driver.

Solution

You need to bind a few more devices with vfio-bind.sh.

Figure out which devices are in the same group as your sound card by running lspci -nn | grep 00\:xx, where "xx" is the device number (e.g. 1f for device 00:1f:3). Take note of those devices and bind them the same way you bound the sound card with vfio-bind.sh. For example:

sudo ./vfio-bind.sh 0000:00:1f.0 0000:00:1f.4 0000:00:1f.5

Do not bind the sound card again. This will cause errors and you will need to reboot the machine.

Clone this wiki locally