2

Presently, I am trying to analyze a piece of hardware, to be specific the Broadcom 1570 PCI web cam inside a late MBPr 2013. I can pass through the device using QEMU, and have the gusest OS / VM detect the web cam, and I tested it out using FaceTime, and it appears to be working. However, there currently isn't a Linux driver for this device. I am aware of one being developed, and there is a project page on Github for the device.

What are some "attack vectors" or possibilities of using qemu to aid in the development of a Linux driver for this device? My knowledge with QEMU is limited, but I have successfully set up several VM's on my host Linux machince (MBPr), and I am eager to learn. Any help would be greatly appreciated.

1

1 Answer 1

3

To develop a driver, there's basically 3 steps you have to do:

  1. Learn about Linux driver programming in general. This is independent of your specifi hardware, and involves things like "How do i convert virtual memory addresses to physical and back? How do i read bytes / write bytes to hardware registers? How do i yield the CPU while waiting for the hardware to set a bit? If the device is interrupt-driven, how to i "stop" execution of the driver when it has written a command to the device, and how to i "restart" it when the device uses an interrupt to tell me the command has finished/data is ready for me to process?
  2. Learn how the hardware works. How many and which registers does it have, what's the meaning of the individual bits of those registers, what do i have to write to which register to configure the hardware, how (in the case of a web cam) do i read a pixel from the hardware, and how do i set up a DMA transfer of a whole image to memory?
  3. Apply the knowledge of 1) and 2) to your hardware, write a driver, and debug it if it doesn't work as it should. If your user level software tells the driver to configure the Webcam to 1024x768 pixels, does the driver write the correct data to the correct registers? Does it obey any timing the device imposes, or does it set your registers too fast sometimes?

While 1) ist just a lot of reading and understanding documentation, an emulator might be able to help with 2), and certainly with 3).

Learn how the hardware works

If you're lucky, you can do 2) looking at vendor's datasheets. In your case, it seems like the vendor (Broadcom) hasn't published anything, so the only way to find out is looking at what an existing driver does. This is where an emulator that allows logging of certain actions might come handy.

If you're running MacOS under Qemu under Linux, and the MacOS driver accesses the webcam hardware, then it doesn't really access the hardware. Every hardware access goes to Qemu, which will intercept the access and route it to the real hardware. Each of these actions can be logged. Now, you can fire up some software in your MacOS, and, for example, tell it to set the resolution to 1024x768 pixels. This might result in a QEMU log like this:

Write the value 01 to the memory address 1234500
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 00 to the memory address 1234502
Write the value 04 to the memory address 1234503
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 02 to the memory address 1234500
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 00 to the memory address 1234502
Write the value 03 to the memory address 1234503
Read memory address 1234501 and get 0x80
Read memory address 1234501 and get 0x00
Write the value 00 to the memory address 1234500

What does this tell us?

Well, your resolution values - 1024x768 - are 0400 and 0300 in hex. This corresponds to the bytes that are written to 1234502 and 1234503. So, these seem to be the registers that set the resolution. But unfortunately, the same registers seem to be used for width and height. This probably means that the byte written to 1234500 is a selector - writing a 1 there turns the other 2 registers into "height" mode, writing a 2 turns them into "width" mode.

And what about the repeated reading of 1234501?

Every time, after something is written to the device, bit 7 (0x80) of that register seems to be set, and the driver keeps reading it until it's clear; then proceeds to write some more. So it seems that bit is a "hardware ready" bit, meaning the hardware is processing your previous commands, and not able to accept further commands. Once everything is processed, the bit gets cleared, and the driver is allowed to write some more.

Of course, all of this would be much easier if you had documentation of the actual hardware. But, documentation of similar hardware might still help. Assume there was a different chip, the Broadcom 1571, which had a USB interface. And assume the documentation of that stated "To set the resolution to 1024x768, you'll have to send the bytes 0x01 0x00 0x04 0x02 0x00 0x03 0x00 over USB". You'd see that the byte sequence is the same, but the timing stuff is probably handled by the USB controller of the 1571 Chip. So you could easily translate the instruction "To turn on the LED, send 0x04 0x01 0x00, to turn it off send 0x04 0x00 0x00" to the corresponding PCI register sequence, and verify using your QEMU log whether or not the driver does the same thing when you turn the LED on/off from your emulated software.

Develop the driver

Once you have enough information about the hardware, you can start writing the linux driver. At this point, an emulator can help you in the same way as when you were tracing the other driver - running Linux with your driver in an emulator helps you debugging the code in just the same way as when you were tracing the original driver.

Also, you can use the emulator to prevent lots of crashes. Your driver might have some bugs that cause it to write bad data to bad addresses, and you certainly want to prevent it from sending a low level format command to the PCI/SATA adapter. You can for example, use QEMU to pass through only a specific range of PCI registers to the actual hardware. Or, if your device documentation states "Writing 0x01 to Register 7 sets a signal to ground, writing 0x02 sets it to +5V. Never set both bits at once, this will create a short circuit and fry your device within seconds", you could put an appropriate safeguard into the emulator to prevent this in case of a driver gone wild.

Can Qemu actually do what has been described?

Probably not the vanilla version. There's no reference to PCI register logging in the qemu description. But, it seems that there is a project on github to add this kind of logging to qemu. This project hasn't been updated in 4 years, which could mean it still works as intended (and there has never been a reason to update it), or the maintainer lost interest and it doesn't work at all anymore.

Some of the other uses described are just too specific to be easily configurable, so you'll probably need to hack qemu itself to implement them. But hey, that's what qemu is open source for.

Since there is already a project to develop drivers for your camera, chances are the maintainers of that project have done exactly that. I'd contact them for more information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.