This project allows creation of self-contained runner for QEMU with embedded command line arguments. Command line arguments are described in files called layers. They are simple INI files that can be combined together to express more complex command lines.
> cat ./arm_virt.ini
[general]
engine = qemu-system-arm
[machine]
@=virt
> cat ./ram_2G.ini
[general]
memory = 2G
> qemu_make_runner -l ./arm_virt.ini ./ram_2G.ini -o ./my_runner.pyz
> python ./my_runner.pyz --dry-run kernel.elf arg1 arg2 # --dry-run to see effective command line instead of running QEMU
qemu-system-arm -machine virt -kernel kernel.elf -append 'arg1 arg2'
Resulting my_runner.pyz
file is ZIP file with qemu_runner
package and layers. Runner has no extra dependencies, using only Python 3.8+ standard library. QEMU is found according to search precedence described below.
Existing runner can be used as base for next runner. Derived runner will contain all layers from base runner along with additional layers specified when deriving. This features allows extending base runner with project specific settings without being aware of base settings.
> cat ./semihosting.ini
[semihosting-config]
enable=on
target=native
> python ./my_runner.pyz --layers ./semihosting.ini --derive ./derived.pyz
> python ./derived.pyz --dry-run kernel.elf arg1 arg2 # --dry-run to see effective command line instead of running QEMU
qemu-system-arm -machine virt -semihosting-config enable=on,target=native -kernel kernel.elf -append 'arg1 arg2'
Runner provides following features, consult --help
output for details:
- GDB server settings
- Start with CPU halted
- Inspect command line
If environment variable QEMU_DEV
is set, it is used as path to QEMU executable.
If environment variable QEMU_DEV
is not set but argument --qemu
is specified it is used as path to QEMU executable.
If QEMU_DEV
is not set, directories are searched for QEMU executable with name specified as engine
in
combined layer:
- Directory specified by
QEMU_DIR
environment variable - Directory specified by
--qemu-dir
argument - Each ancestor directory containing runner, up to the root directory and
qemu
subdirectory at each level. If runner's path isc:\dir1\dir2\runner.pyz
, then following directories are checked:c:\dir1\dir2
c:\dir1\dir2\qemu
c:\dir1\
c:\dir1\qemu
c:\
c:\qemu
- Repeat step 2 with path of base runner in case of derived runners with
--tract-qemu
option. - Repeat step 2 with path of passed as
--qemu-dir
when runner was derived. - Directories in
PATH
environment variable.
On Windows, PATHEXT
variable is used to determine executable extension.
Several environment variables influences the way QEMU command line is constructed:
QEMU_FLAGS
- arguments to be added to the QEMU command line during executionQEMU_RUNNER_FLAGS
- arguments will be interpreted exactly as if they were added to runner execution.
Example:
shell> QEMU_FLAGS = '-d int' QEMU_RUNNER_FLAGS = '--halted' ./runner.pyz --dry-run kernel.elf
qemu-system-arm -machine virt -d int -S -kernel kernel.elf
If layer path is absolute and file is not found, search process fails immediately.
If layer path is relative, following directories are searched:
- Current directory
- Packages declaring entry point
qemu_runner_layer_packages
(see below)
Layers are plain INI files with sections describing QEMU command line. Layers can be combined together allowing user to build bigger command line from simpler building blocks.
Values are interpreted as strings unless specified otherwise. When value is described as boolean, values 1
, yes
, true
and on
are interpreted as true, values 0
, no
, false
and off
are interpreted as false. Other values are invalid.
Section [general]
describes most common QEMU arguments.
Available settings:
engine
- Name of QEMU executable (e.g.:qemu-system-arm
,qemu-system-sparc
)cpu
- CPU to use in machine (-cpu option
)memory
- RAM memory size, supports suffixes likeM
,G
(e.g.20M
,4G
)gdb
(boolean) - If true QEMU will be started with gdbserver enabled.gdb_dev
- Use specified value as gdbserver listend address. If not used, default QEMU address will be used (tcp::1234
at the time this document is written). Note that specifing onlygdb_dev
does not enable gdbserver.halted
- Freeze QEMU CPU at startup.
Each section corresponds to single QEMU argument, e.g. section [machine]
corresponds to -machine
argument. Value specified as @
key will be used as direct argument value (machine name, device type, etc). Remaining arguments will be added as key-value properties (note: for id
property see next section).
For example, layer:
[machine]
@=virt
usb=on
gic-version=2
will be translated into
-machine virt,usb=on,gic-version=2
As INI file syntax does not allow duplicated section names it is not possible to describe many QEMU arguments without additional syntax: -device
, -netdev
, etc. These arguments can be differentiated by id
property which can be specified as section name in format argument_name:id
.
For example, layer:
[device:d1]
@=type1
arg1=10
arg2=20
[device:d2]
@=type1
arg1=10
arg2=20
[device:d3]
@=type2
arg3=10
arg4=20
translates into:
-device type1,id=d1,arg1=10,arg2=20 -device type1,id=d2,arg1=10,arg2=20 -device type2,id=d3,arg3=10,arg4=20
In sections [name]
and [name:id]
it is possible to use variables which will be resolved directly before building complete command lines. Variables are in form ${VARIABLE_NAME}
.
Currently available variables:
Variable name | Value |
---|---|
KERNEL_DIR |
Directory containing kernel executable (path is not normalized) |
Layers can be combined by applying one layer on top of the another. Operation 'build layer LResult
by applying layer LAdd
on top of LBase
' is defined as follows:
[general]
(exceptcmdline
) -LResult
contains all settings from layersLAdd
andLBase
, values inLAdd
override values inLBase
[general]
,cmdline
value -LResult
containscmdline
fromLBase
followed byLAdd
(command line arguments are combined)[name]
- If
LBase
does not contain section[name]
,LResult
will contain section fromLAdd
. - If
LBase
contains section[name]
,LResult
will contain[name]
with all settings fromLBase
andLAdd
, values inLAdd
override values inLBase
.
- If
[name:id]
- The same rules as with section
[name]
applies,id
is treated as part of section name.
- The same rules as with section
Note:
- It is not possible to remove section by applying another layer
- It is not possible to change
id
property - It is not possible to remove argument from section
It is possible to distribute layers as pure Python package that can be installed using pip
. Layers distributes in that way are always visible and there is no need to specify full path to file.
Creating package with layers:
- Create Python package
layers_pkg
, add empty__init__.py
file. - Put layer files into
layers_pkg/layers
folder, it is possible to use more complex directory structure. - Add
MANIFEST.in
file withrecursive-include layers_pkg/layers *.ini
in it. - Add
setup.py
file:
from setuptools import setup
setup(
name='layers-pkg',
version='1.0.0',
packages=['layers_pkg'],
zip_safe=True,
include_package_data=True,
entry_points={
'qemu_runner_layer_packages': ['layers_pkg=layers_pkg'] # Always use `package_name=package_name`
}
)
- Create Python package using tools of your choice (
python setup.py
orpyproject-build
)
Package might contain other files, as it is normal Python package.
NOTE: This is simplified process of creating Python package, refer to Python documentation for more details.
qemu_runner
tools uses qemu_runner_layer_packages
entry point to discover all registered packages, from each entry point module portion is used in search for layers.