|
1 | 1 | # Linux-loader
|
2 | 2 |
|
3 |
| -## Short-description |
| 3 | +The `linux-loader` crate offers support for loading raw ELF (`vmlinux`) and |
| 4 | +compressed big zImage (`bzImage`) format kernel images on `x86_64` and PE |
| 5 | +(`Image`) kernel images on `aarch64`. ELF support includes the |
| 6 | +[Linux](https://www.kernel.org/doc/Documentation/x86/boot.txt) and |
| 7 | +[PVH](https://xenbits.xen.org/docs/unstable/misc/pvh.html) boot protocols. |
4 | 8 |
|
5 |
| -* Parsing and loading vmlinux (raw ELF image), bzImage and PE images |
6 |
| -* Linux command line parsing and generation |
7 |
| -* Loading device tree blobs |
8 |
| -* Definitions and helpers for the Linux boot protocol |
| 9 | +The `linux-loader` crate is not yet fully independent and self-sufficient, and |
| 10 | +much of the boot process remains the VMM's responsibility. See [Usage] for details. |
9 | 11 |
|
10 |
| -## How to build |
| 12 | +## Supported features |
11 | 13 |
|
| 14 | +- Parsing and loading kernel images into guest memory. |
| 15 | + - `x86_64`: `vmlinux` (raw ELF image), `bzImage` |
| 16 | + - `aarch64`: `Image` |
| 17 | +- Parsing and building the kernel command line. |
| 18 | +- Loading device tree blobs (`aarch64`). |
| 19 | +- Configuring boot parameters using the exported primitives. |
| 20 | + - `x86_64` Linux boot: |
| 21 | + - [`setup_header`](https://elixir.bootlin.com/linux/latest/source/arch/x86/include/uapi/asm/bootparam.h#L65) |
| 22 | + - [`boot_params`](https://elixir.bootlin.com/linux/latest/source/arch/x86/include/uapi/asm/bootparam.h#L175) |
| 23 | + - `x86_64` PVH boot: |
| 24 | + - [`hvm_start_info`](https://elixir.bootlin.com/linux/latest/source/include/xen/interface/hvm/start_info.h#L125) |
| 25 | + - [`hvm_modlist_entry`](https://elixir.bootlin.com/linux/latest/source/include/xen/interface/hvm/start_info.h#L145) |
| 26 | + - [`hvm_memmap_table_entry`](https://elixir.bootlin.com/linux/latest/source/include/xen/interface/hvm/start_info.h#L152) |
| 27 | + - `aarch64` boot: |
| 28 | + - [`arm64_image_header`](https://elixir.bootlin.com/linux/latest/source/arch/arm64/include/asm/image.h#L44) |
| 29 | + |
| 30 | +## Usage |
| 31 | + |
| 32 | +Booting a guest using the `linux-loader` crate involves several steps, |
| 33 | +depending on the boot protocol used. A simplified overview follows. |
| 34 | + |
| 35 | +Consider an `x86_64` VMM that: |
| 36 | +- interfaces with `linux-loader`; |
| 37 | +- uses `GuestMemoryMmap` for its guest memory backend; |
| 38 | +- loads an ELF kernel image from a `File`. |
| 39 | + |
| 40 | +### Loading the kernel |
| 41 | + |
| 42 | +One of the first steps in starting the guest is to load the kernel from a |
| 43 | +[`Read`er](https://doc.rust-lang.org/std/io/trait.Read.html) into guest memory. |
| 44 | +For this step, the VMM is required to have configured its guest memory. |
| 45 | + |
| 46 | +In this example, the VMM specifies both the kernel starting address and the |
| 47 | +starting address of high memory. |
| 48 | + |
| 49 | +```rust |
| 50 | +use linux_loader::loader::elf::Elf as Loader; |
| 51 | +use vm_memory::GuestMemoryMmap; |
| 52 | + |
| 53 | +use std::fs::File; |
| 54 | +use std::result::Result; |
| 55 | + |
| 56 | +impl MyVMM { |
| 57 | + fn start_vm(&mut self) { |
| 58 | + let guest_memory = self.create_guest_memory(); |
| 59 | + let kernel_file = self.open_kernel_file(); |
| 60 | + |
| 61 | + let load_result = Loader::load::<File, GuestMemoryMmap>( |
| 62 | + &guest_memory, |
| 63 | + Some(self.kernel_start_addr()), |
| 64 | + &mut kernel_file, |
| 65 | + Some(self.himem_start_addr()), |
| 66 | + ) |
| 67 | + .expect("Failed to load kernel"); |
| 68 | + } |
| 69 | +} |
12 | 70 | ```
|
13 |
| -cd linux-loader |
14 |
| -cargo build |
| 71 | + |
| 72 | +### Configuring the devices and kernel command line |
| 73 | + |
| 74 | +After the guest memory has been created and the kernel parsed and loaded, the |
| 75 | +VMM will optionally configure devices and the kernel command line. The latter |
| 76 | +can then be loaded in guest memory. |
| 77 | + |
| 78 | +```rust |
| 79 | +impl MyVMM { |
| 80 | + fn start_vm(&mut self) { |
| 81 | + ... |
| 82 | + let cmdline_size = self.kernel_cmdline().as_str().len() + 1; |
| 83 | + linux_loader::loader::load_cmdline::<GuestMemoryMmap>( |
| 84 | + &guest_memory, |
| 85 | + self.cmdline_start_addr(), |
| 86 | + &CString::new(kernel_cmdline).expect("Failed to parse cmdline") |
| 87 | + ).expect("Failed to load cmdline"); |
| 88 | + } |
15 | 89 | ```
|
16 | 90 |
|
17 |
| -## Tests |
| 91 | +### Configuring boot parameters |
18 | 92 |
|
19 |
| -Our Continuous Integration (CI) pipeline is implemented on top of |
20 |
| -[Buildkite](https://buildkite.com/). |
21 |
| -For the complete list of tests, check our |
22 |
| -[CI pipeline](https://buildkite.com/rust-vmm/rust-vmm-ci). |
| 93 | +The VMM sets up initial registry values in this phase, without using |
| 94 | +`linux-loader`. It can also configure additional boot parameters, using the |
| 95 | +structs exported by `linux-loader`. |
23 | 96 |
|
24 |
| -Each individual test runs in a container. To reproduce a test locally, you can |
25 |
| -use the dev-container on both x86 and arm64. |
| 97 | +```rust |
| 98 | +use linux_loader::configurator::linux::LinuxBootConfigurator; |
| 99 | +use linux_loader::configurator::{BootConfigurator, BootParams}; |
26 | 100 |
|
27 |
| -```bash |
28 |
| -container_version=5 |
29 |
| -docker run -it \ |
30 |
| - --security-opt seccomp=unconfined \ |
31 |
| - --volume $(pwd):/linux-loader \ |
32 |
| - rustvmm/dev:v${container_version} |
33 |
| -cd linux-loader/ |
34 |
| -cargo test |
| 101 | +impl MyVMM { |
| 102 | + fn start_vm(&mut self) { |
| 103 | + ... |
| 104 | + let mut bootparams = boot_params::default(); |
| 105 | + self.configure_bootparams(&mut bootparams); |
| 106 | + LinuxBootConfigurator::write_bootparams( |
| 107 | + BootParams::new(¶ms, self.zeropage_addr()), |
| 108 | + &guest_memory, |
| 109 | + ).expect("Failed to write boot params in guest memory"); |
| 110 | + } |
35 | 111 | ```
|
36 | 112 |
|
37 |
| -### bzImage test |
38 |
| - |
39 |
| -As we don't want to distribute an entire kernel bzImage, the `load_bzImage` |
40 |
| -test is ignored by default. In order to test the bzImage support, one needs to |
41 |
| -locally build a bzImage, copy it to the `src/loader` directory and run |
42 |
| -`cargo test`: |
43 |
| - |
44 |
| -```bash |
45 |
| -# Assuming your linux-loader and linux-stable are both under ${LINUX_LOADER}: |
46 |
| -git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ${LINUX_LOADER}/linux-stable |
47 |
| -cd linux-stable |
48 |
| -make bzImage |
49 |
| -cp linux-stable/arch/x86/boot/bzImage ${LINUX_LOADER}/linux-loader/src/loader/ |
50 |
| -cd ${LINUX_LOADER}/linux-loader |
51 |
| -container_version=5 |
52 |
| -docker run -it \ |
53 |
| - --security-opt seccomp=unconfined \ |
54 |
| - --volume $(pwd):/linux-loader \ |
55 |
| - rustvmm/dev:v${container_version} |
56 |
| -cd linux-loader/ |
57 |
| -cargo test |
58 |
| -``` |
| 113 | +Done! |
| 114 | + |
| 115 | +## Testing |
| 116 | + |
| 117 | +See [`docs/TESTING.md`](docs/TESTING.md). |
| 118 | + |
| 119 | +## License |
| 120 | + |
| 121 | +This project is licensed under either of: |
| 122 | +- [Apache License](LICENSE-APACHE), Version 2.0 |
| 123 | +- [BSD-3-Clause License](LICENSE-BSD-3-Clause) |
0 commit comments