Ramdisk in the manifest, simplify initrd generation
We currently support loading a ramdisk for the primary. In fact, all
primaries are required to have one called 'initrd.img'. This makes
the ramdisk optional and specifies its filename in the manifest.
With that, generate_initrd.py loses the last remaining hardcoded
assumption about the structure of the Hafnium ramdisk. Refactor the
script and the build so that generate_initrd.py simply accepts a list of
(name in ramdisk, path on host) pairs.
Bug: 117551352
Change-Id: Iff2d70205940a1740c473d1cac34d5a05d6d6791
diff --git a/build/image/generate_initrd.py b/build/image/generate_initrd.py
index 4d8f281..aba5ec1 100644
--- a/build/image/generate_initrd.py
+++ b/build/image/generate_initrd.py
@@ -26,41 +26,27 @@
import subprocess
import sys
-
def Main():
parser = argparse.ArgumentParser()
- parser.add_argument("--primary_name", required=True)
- parser.add_argument("--primary_vm", required=True)
- parser.add_argument("--primary_vm_initrd")
- parser.add_argument(
- "--secondary_vm",
- action="append",
- nargs=2,
- metavar=("NAME", "IMAGE"))
- parser.add_argument("--staging", required=True)
- parser.add_argument("--output", required=True)
+ parser.add_argument("-f", "--file",
+ action="append", nargs=2,
+ metavar=("NAME", "PATH"),
+ help="File at host location PATH to be added to the RAM disk as NAME")
+ parser.add_argument("-s", "--staging", required=True)
+ parser.add_argument("-o", "--output", required=True)
args = parser.parse_args()
- staged_files = [args.primary_name, "initrd.img"]
# Create staging folder if needed.
if not os.path.isdir(args.staging):
os.makedirs(args.staging)
- # Prepare the primary VM image.
- shutil.copyfile(args.primary_vm,
- os.path.join(args.staging, args.primary_name))
- # Prepare the primary VM's initrd.
- if args.primary_vm_initrd:
- shutil.copyfile(args.primary_vm_initrd,
- os.path.join(args.staging, "initrd.img"))
- else:
- open(os.path.join(args.staging, "initrd.img"), "w").close()
- # Prepare the secondary VMs.
- if args.secondary_vm:
- for vm in args.secondary_vm:
- (vm_name, vm_image) = vm
- staged_files.append(vm_name)
- shutil.copy(vm_image, os.path.join(args.staging, vm_name))
+ # Copy files into the staging folder.
+ staged_files = []
+ for name, path in args.file:
+ shutil.copyfile(path, os.path.join(args.staging, name))
+ assert name not in staged_files
+ staged_files.append(name)
+
# Package files into an initial RAM disk.
with open(args.output, "w") as initrd:
# Move into the staging directory so the file names taken by cpio don't
@@ -74,6 +60,5 @@
cpio.communicate(input="\n".join(staged_files).encode("utf-8"))
return 0
-
if __name__ == "__main__":
sys.exit(Main())
diff --git a/build/image/image.gni b/build/image/image.gni
index 2c29f4a..ebde0c2 100644
--- a/build/image/image.gni
+++ b/build/image/image.gni
@@ -205,9 +205,8 @@
invoker.primary_vm,
]
args = [
- "--primary_name",
+ "--file",
invoker.primary_name,
- "--primary_vm",
rebase_path(primary_vm_image),
"--staging",
rebase_path(initrd_staging),
@@ -219,7 +218,8 @@
deps += [ invoker.primary_initrd ]
primary_initrd_outputs = get_target_outputs(invoker.primary_initrd)
args += [
- "--primary_vm_initrd",
+ "--file",
+ "initrd.img",
rebase_path(primary_initrd_outputs[0]),
]
}
@@ -233,7 +233,7 @@
foreach(vm, invoker.secondary_vms) {
deps += [ vm[1] ]
args += [
- "--secondary_vm",
+ "--file",
vm[0],
rebase_path(get_label_info(vm[1], "target_out_dir") + "/" +
get_label_info(vm[1], "name") + ".bin"),
diff --git a/docs/Manifest.md b/docs/Manifest.md
index ff1cf0e..4f69076 100644
--- a/docs/Manifest.md
+++ b/docs/Manifest.md
@@ -13,6 +13,7 @@
vm1 {
debug_name = "name";
kernel_filename = "vmlinuz";
+ ramdisk_filename = "initrd.img";
};
vm2 {
@@ -37,7 +38,8 @@
(matches filename in Hafnium's [ramdisk](HafniumRamDisk.md)). The second has 2MB
of memory, 4 CPUs and, by omitting the `kernel_filename` property, a kernel
preloaded into memory. The primary VM is given all remaining memory, the same
-number of CPUs as the hardware and a kernel image called `vmlinuz`.
+number of CPUs as the hardware, a kernel image called `vmlinuz` and a ramdisk
+`initrd.img`. Secondaries cannot have a ramdisk.
```
/dts-v1/;
@@ -48,6 +50,7 @@
vm1 {
debug_name = "primary VM";
kernel_filename = "vmlinuz";
+ ramdisk_filename = "initrd.img";
};
vm2 {
diff --git a/inc/hf/boot_flow.h b/inc/hf/boot_flow.h
index 3ecf25f..3307fb0 100644
--- a/inc/hf/boot_flow.h
+++ b/inc/hf/boot_flow.h
@@ -26,5 +26,6 @@
struct mpool *ppool);
bool boot_flow_update(struct mm_stage1_locked stage1_locked,
+ const struct manifest *manifest,
struct boot_params_update *p, struct memiter *cpio,
struct mpool *ppool);
diff --git a/inc/hf/cpio.h b/inc/hf/cpio.h
index d67f8d4..aebe1d5 100644
--- a/inc/hf/cpio.h
+++ b/inc/hf/cpio.h
@@ -17,7 +17,6 @@
#pragma once
#include <stdbool.h>
-#include <stddef.h>
#include "hf/memiter.h"
#include "hf/string.h"
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index 23689d9..3e9f76f 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -29,11 +29,17 @@
struct string debug_name;
struct string kernel_filename;
- /* Properties specific to secondary VMs. */
- struct {
- uint64_t mem_size;
- spci_vcpu_count_t vcpu_count;
- } secondary;
+ union {
+ /* Properties specific to the primary VM. */
+ struct {
+ struct string ramdisk_filename;
+ } primary;
+ /* Properties specific to secondary VMs. */
+ struct {
+ uint64_t mem_size;
+ spci_vcpu_count_t vcpu_count;
+ } secondary;
+ };
};
/**
diff --git a/inc/hf/plat/boot_flow.h b/inc/hf/plat/boot_flow.h
index 5a6b02b..b5d8145 100644
--- a/inc/hf/plat/boot_flow.h
+++ b/inc/hf/plat/boot_flow.h
@@ -19,6 +19,7 @@
#include "hf/addr.h"
#include "hf/boot_params.h"
#include "hf/fdt.h"
+#include "hf/manifest.h"
#include "hf/memiter.h"
#include "hf/mm.h"
@@ -27,5 +28,6 @@
bool plat_boot_flow_get_initrd_range(const struct fdt_node *fdt_root,
paddr_t *begin, paddr_t *end);
bool plat_boot_flow_update(struct mm_stage1_locked stage1_locked,
+ const struct manifest *manifest,
struct boot_params_update *p, struct memiter *cpio,
struct mpool *ppool);
diff --git a/inc/vmapi/hf/types.h b/inc/vmapi/hf/types.h
index f54d7d7..1612099 100644
--- a/inc/vmapi/hf/types.h
+++ b/inc/vmapi/hf/types.h
@@ -40,12 +40,13 @@
#define HF_VM_ID_OFFSET 1
/**
- * The ID of the primary VM, which is responsible for scheduling.
+ * The index and ID of the primary VM, which is responsible for scheduling.
*
- * Starts at the offset because ID 0 is reserved for the hypervisor itself.
- * All other VM IDs come after the primary.
+ * These are not equal because ID 0 is reserved for the hypervisor itself.
+ * Primary VM therefore gets ID 1 and all other VMs come after that.
*/
-#define HF_PRIMARY_VM_ID HF_VM_ID_OFFSET
+#define HF_PRIMARY_VM_INDEX 0
+#define HF_PRIMARY_VM_ID (HF_VM_ID_OFFSET + HF_PRIMARY_VM_INDEX)
/** Sleep value for an indefinite period of time. */
#define HF_SLEEP_INDEFINITE 0xffffffffffffff
diff --git a/src/boot_flow/android.c b/src/boot_flow/android.c
index cf7246e..0221674 100644
--- a/src/boot_flow/android.c
+++ b/src/boot_flow/android.c
@@ -51,10 +51,12 @@
* Android boot flow does not change based on the updates.
*/
bool plat_boot_flow_update(struct mm_stage1_locked stage1_locked,
+ const struct manifest *manifest,
struct boot_params_update *p, struct memiter *cpio,
struct mpool *ppool)
{
(void)stage1_locked;
+ (void)manifest;
(void)p;
(void)cpio;
(void)ppool;
diff --git a/src/boot_flow/common.c b/src/boot_flow/common.c
index 586d3d2..b295824 100644
--- a/src/boot_flow/common.c
+++ b/src/boot_flow/common.c
@@ -87,8 +87,9 @@
* Takes action on any updates that were generated.
*/
bool boot_flow_update(struct mm_stage1_locked stage1_locked,
+ const struct manifest *manifest,
struct boot_params_update *p, struct memiter *cpio,
struct mpool *ppool)
{
- return plat_boot_flow_update(stage1_locked, p, cpio, ppool);
+ return plat_boot_flow_update(stage1_locked, manifest, p, cpio, ppool);
}
diff --git a/src/boot_flow/linux.c b/src/boot_flow/linux.c
index 9d4d886..e1e257c 100644
--- a/src/boot_flow/linux.c
+++ b/src/boot_flow/linux.c
@@ -52,14 +52,19 @@
}
bool plat_boot_flow_update(struct mm_stage1_locked stage1_locked,
+ const struct manifest *manifest,
struct boot_params_update *update,
struct memiter *cpio, struct mpool *ppool)
{
- static struct string filename = STRING_INIT("initrd.img");
struct memiter primary_initrd;
+ const struct string *filename =
+ &manifest->vm[HF_PRIMARY_VM_INDEX].primary.ramdisk_filename;
- if (!cpio_get_file(cpio, &filename, &primary_initrd)) {
- dlog("Unable to find initrd.img\n");
+ if (string_is_empty(filename)) {
+ memiter_init(&primary_initrd, NULL, 0);
+ } else if (!cpio_get_file(cpio, filename, &primary_initrd)) {
+ dlog("Unable to find primary initrd \"%s\".\n",
+ string_data(filename));
return false;
}
diff --git a/src/init.c b/src/init.c
index 4948697..6d9e8ad 100644
--- a/src/init.c
+++ b/src/init.c
@@ -116,7 +116,8 @@
panic("Unable to load VMs.");
}
- if (!boot_flow_update(mm_stage1_locked, &update, &cpio, &ppool)) {
+ if (!boot_flow_update(mm_stage1_locked, &manifest, &update, &cpio,
+ &ppool)) {
panic("Unable to update boot flow.");
}
diff --git a/src/load.c b/src/load.c
index 7138824..01640f8 100644
--- a/src/load.c
+++ b/src/load.c
@@ -100,8 +100,8 @@
struct mpool *ppool)
{
paddr_t primary_begin = layout_primary_begin();
- const struct manifest_vm *manifest_vm =
- &manifest->vm[HF_PRIMARY_VM_ID - HF_VM_ID_OFFSET];
+ struct vm *vm;
+ struct vcpu_locked vcpu_locked;
/*
* TODO: This bound is currently meaningless but will be addressed when
@@ -109,46 +109,41 @@
*/
paddr_t primary_end = pa_add(primary_begin, 0x8000000);
- if (!load_kernel(stage1_locked, primary_begin, primary_end, manifest_vm,
- cpio, ppool)) {
+ if (!load_kernel(stage1_locked, primary_begin, primary_end,
+ &manifest->vm[HF_PRIMARY_VM_INDEX], cpio, ppool)) {
dlog("Unable to load primary kernel.");
return false;
}
- {
- struct vm *vm;
- struct vcpu_locked vcpu_locked;
-
- if (!vm_init(MAX_CPUS, ppool, &vm)) {
- dlog("Unable to initialise primary vm\n");
- return false;
- }
-
- if (vm->id != HF_PRIMARY_VM_ID) {
- dlog("Primary vm was not given correct id\n");
- return false;
- }
-
- /* Map the 1TB of memory. */
- /* TODO: We should do a whitelist rather than a blacklist. */
- if (!mm_vm_identity_map(
- &vm->ptable, pa_init(0),
- pa_init(UINT64_C(1024) * 1024 * 1024 * 1024),
- MM_MODE_R | MM_MODE_W | MM_MODE_X, NULL, ppool)) {
- dlog("Unable to initialise memory for primary vm\n");
- return false;
- }
-
- if (!mm_vm_unmap_hypervisor(&vm->ptable, ppool)) {
- dlog("Unable to unmap hypervisor from primary vm\n");
- return false;
- }
-
- vcpu_locked = vcpu_lock(vm_get_vcpu(vm, 0));
- vcpu_on(vcpu_locked, ipa_from_pa(primary_begin), kernel_arg);
- vcpu_unlock(&vcpu_locked);
+ if (!vm_init(MAX_CPUS, ppool, &vm)) {
+ dlog("Unable to initialise primary vm\n");
+ return false;
}
+ if (vm->id != HF_PRIMARY_VM_ID) {
+ dlog("Primary vm was not given correct id\n");
+ return false;
+ }
+
+ /* Map the 1TB of memory. */
+ /* TODO: We should do a whitelist rather than a blacklist. */
+ if (!mm_vm_identity_map(&vm->ptable, pa_init(0),
+ pa_init(UINT64_C(1024) * 1024 * 1024 * 1024),
+ MM_MODE_R | MM_MODE_W | MM_MODE_X, NULL,
+ ppool)) {
+ dlog("Unable to initialise memory for primary vm\n");
+ return false;
+ }
+
+ if (!mm_vm_unmap_hypervisor(&vm->ptable, ppool)) {
+ dlog("Unable to unmap hypervisor from primary vm\n");
+ return false;
+ }
+
+ vcpu_locked = vcpu_lock(vm_get_vcpu(vm, 0));
+ vcpu_on(vcpu_locked, ipa_from_pa(primary_begin), kernel_arg);
+ vcpu_unlock(&vcpu_locked);
+
return true;
}
diff --git a/src/manifest.c b/src/manifest.c
index 6d8b4ba..222716f 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -209,7 +209,10 @@
TRY(read_string(node, "debug_name", &vm->debug_name));
TRY(read_optional_string(node, "kernel_filename",
&vm->kernel_filename));
- if (vm_id != HF_PRIMARY_VM_ID) {
+ if (vm_id == HF_PRIMARY_VM_ID) {
+ TRY(read_optional_string(node, "ramdisk_filename",
+ &vm->primary.ramdisk_filename));
+ } else {
TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
}
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index d7690b4..902cf21 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -161,6 +161,11 @@
return StringProperty("kernel_filename", value);
}
+ ManifestDtBuilder &RamdiskFilename(const std::string_view &value)
+ {
+ return StringProperty("ramdisk_filename", value);
+ }
+
ManifestDtBuilder &VcpuCount(uint64_t value)
{
return IntegerProperty("vcpu_count", value);
@@ -398,6 +403,29 @@
MANIFEST_ERROR_INTEGER_OVERFLOW);
}
+TEST(manifest, no_ramdisk_primary)
+{
+ struct manifest m;
+ struct fdt_node fdt_root;
+
+ /* clang-format off */
+ std::vector<char> dtb = ManifestDtBuilder()
+ .StartChild("hypervisor")
+ .Compatible()
+ .StartChild("vm1")
+ .DebugName("primary_vm")
+ .EndChild()
+ .EndChild()
+ .Build();
+ /* clang-format on */
+
+ ASSERT_TRUE(get_fdt_root(dtb, &fdt_root));
+ ASSERT_EQ(manifest_init(&m, &fdt_root), MANIFEST_SUCCESS);
+ ASSERT_EQ(m.vm_count, 1);
+ ASSERT_STREQ(string_data(&m.vm[0].debug_name), "primary_vm");
+ ASSERT_STREQ(string_data(&m.vm[0].primary.ramdisk_filename), "");
+}
+
TEST(manifest, valid)
{
struct manifest m;
@@ -411,6 +439,7 @@
.StartChild("vm1")
.DebugName("primary_vm")
.KernelFilename("primary_kernel")
+ .RamdiskFilename("primary_ramdisk")
.EndChild()
.StartChild("vm3")
.DebugName("second_secondary_vm")
@@ -435,6 +464,8 @@
vm = &m.vm[0];
ASSERT_STREQ(string_data(&vm->debug_name), "primary_vm");
ASSERT_STREQ(string_data(&vm->kernel_filename), "primary_kernel");
+ ASSERT_STREQ(string_data(&vm->primary.ramdisk_filename),
+ "primary_ramdisk");
vm = &m.vm[1];
ASSERT_STREQ(string_data(&vm->debug_name), "first_secondary_vm");
diff --git a/test/linux/manifest.dts b/test/linux/manifest.dts
index 71c891e..29e2075 100644
--- a/test/linux/manifest.dts
+++ b/test/linux/manifest.dts
@@ -23,6 +23,7 @@
vm1 {
debug_name = "linux_test";
kernel_filename = "vmlinuz";
+ ramdisk_filename = "initrd.img";
};
vm2 {