QEMU fw cfg

From OSDev.wiki
Revision as of 20:16, 5 July 2018 by osdev>Doug16k (Some missing formatting, add link to CPUID 0x40000000, improve references links)
Jump to navigation Jump to search

QEMU provides a facility for passing strings and files into the VM. This facility is useful for passing kernel parameters, files, or other resources into a guest.

QEMU command line

To pass strings or files into QEMU to appear in the fw_cfg device, use one or more command line parameters like those shown below:

 -fw_cfg name=opt/com.name.domain.your.example,string=1
 -fw_cfg name=opt/com.name.domain.your.example,file=example_filename.png
 -fw_cfg opt/com.name.domain.your.example,file=example_filename.png

Names that begin with "opt/" are reserved for users. It is strongly recommended to use a RFQDN (Reversed Fully Qualified Domain Name) as a prefix for the resource name. In the example above, it is assumed you are the owner of the domain "your.domain.name.com", and your resource is named "example". Following this advice guarantees that there will never be a conflict, since only one entity can own a given domain name. The name may be up to 55 characters long, including the "opt/" prefix. The "name=" part of the argument may be omitted, as shown in the third example above.

Detecting QEMU

It is a very good idea for your OS to first make sure it is running under QEMU, before attempting to access the fw_cfg device. Accessing I/O ports for devices that don't exist on a real machine may have undefined behavior. To detect the presence of QEMU, you should read CPUID leaf 0x40000000 and check for the hypervisor signature in the ebx, ecx, edx registers. Under QEMU, those registers will contain the signature "TCGTCGTCGTCG" or "KVMKVMKVM\0\0\0". A link to a CPUID hypervisor leaf reference is at the end of the article.

Accessing the device from your OS

QEMU provides three I/O ports on x86, or three MMIO addresses on other architectures (details for other architectures are omitted in this article for now, see the QEMU documentation file below if you need to use fw_cfg on another architecture).

  #define FW_CFG_PORT_SEL     0x510
  #define FW_CFG_PORT_DATA    0x511
  #define FW_CFG_PORT_DMA     0x514

FW_CFG_PORT_SEL is a 16-bit port at I/O address 0x510. You write a "selector" to this port to control which data will be read when reading FW_CFG_PORT_DATA. More about selectors below.

FW_CFG_PORT_DATA is an 8-bit port at I/O address 0x511. You read one byte at a time of the file from this port, after selecting a file by writing a value to FW_CFG_PORT_SEL.

FW_CFG_PORT_DMA is a 32-bit port at I/O address 0x514. DMA is not yet covered in this article, and is not likely to be significantly faster than rep insb either, unless you need to read very large files into discontiguous ranges of memory.

Selectors

  #define FW_CFG_SIGNATURE    0x0000
  #define FW_CFG_ID           0x0001
  #define FW_CFG_FILE_DIR     0x0019

Listed above are a few fixed-purpose selectors for probing for the existence of the device, and for getting the number of files present in the device. Besides those selectors, each file will be assigned a selector. You determine the selector to use for a file you wish to read by reading the directory file FW_CFG_FILE_DIR.

You should ensure that the fw_cfg device is present by outputting FW_CFG_SIGNATURE to I/O port FW_CFG_PORT_SEL, then read four bytes from FW_CFG_PORT_DATA. If you read "QEMU" from the data port, the fw_cfg device is present.

Enumerating Files

The first four bytes of the FW_CFG_FILE_DIR selector will be a 32-bit big-endian number which is the number of files present in the directory.

Following the count, there is a sequence of entries with the following structure:

  struct FWCfgFile {		/* an individual file entry, 64 bytes total */
      uint32_t size;		/* size of referenced fw_cfg item, big-endian */
      uint16_t select;		/* selector key of fw_cfg item, big-endian */
      uint16_t reserved;
      char name[56];		/* fw_cfg item name, NUL-terminated ascii */
  };

These can be read one at a time by reading sizeof(FWCfgFile) bytes into a single variable of type FWCfgFile and examined one by one until you find a structure with the desired name. You may also allocate a block of memory large enough for all of the filenames and input the whole list all at once.

Reading files

Once you have found the FWCfgFile structure which has the desired name, read the select field from the structure. It will contain the big-endian selector for the file. After byte-swapping the selector, output it to FW_CFG_PORT_SEL, then read size bytes from FW_CFG_PORT_DATA and write them to the desired location in memory.

An example implementation is provided in the references below.

References

QEMU fw_cfg documentation on GitHub

Example implementation

CPUID hypervisor leaf