Add support for devicetree files alongside the kernel and initramfs
Much like the (optional) initramfs at `/usr/lib/ostree-boot/initramfs-<SHA256>` or `/usr/lib/modules/$kver/initramfs` you can now optionally include a flattened devicetree (.dtb) file alongside the kernel at `/usr/lib/ostree-boot/devicetree-<SHA256>` or `/usr/lib/modules/$kver/devicetree`. This is useful for embedded ARM systems which need the devicetree file loaded by the bootloader for the kernel to discover and initialise hardware. See https://en.wikipedia.org/wiki/Device_tree for more information. This patch was mostly produced by copy-pasting code for initramfs handling and renaming `s/initramfs/devicetree/g`. It's not beautiful, but it is fairly straightforward. It may be useful to extend device-tree support in a number ways in the future. Device trees dependant on many details of the hardware they support. This makes them unlike kernels, which may support many different hardware variants as long as the instruction-set matches. This means that a ostree tree created with a device-tree in this manner will only boot on a single model of hardware. This is sufficient for my purposes, but may not be for others'. I've tested this on my NVidia Tegra TK1 device which has u-boot running in syslinux-compatible mode. Closes: #1411 Approved by: cgwalters
This commit is contained in:
parent
c5112c25e4
commit
720e2ec9bc
|
|
@ -95,7 +95,7 @@ collected at any point.
|
||||||
## The /ostree/boot directory
|
## The /ostree/boot directory
|
||||||
|
|
||||||
However, we want to optimize for the case where the set of
|
However, we want to optimize for the case where the set of
|
||||||
kernel/initramfs pairs is the same between both the old and new
|
kernel/initramfs/devicetree sets is the same between both the old and new
|
||||||
deployment lists. This happens when doing an upgrade that does not
|
deployment lists. This happens when doing an upgrade that does not
|
||||||
include the kernel; think of a simple translation update. OSTree
|
include the kernel; think of a simple translation update. OSTree
|
||||||
optimizes for this case because on some systems `/boot` may be on a
|
optimizes for this case because on some systems `/boot` may be on a
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,11 @@ paths, which are `vmlinuz(-.*)?-$checksum` in either `/boot` or `/usr/lib/ostree
|
||||||
The checksum should be a SHA256 hash of the kernel contents; it must be
|
The checksum should be a SHA256 hash of the kernel contents; it must be
|
||||||
pre-computed before storing the kernel in the repository. Optionally,
|
pre-computed before storing the kernel in the repository. Optionally,
|
||||||
the directory can also contain an initramfs, stored as
|
the directory can also contain an initramfs, stored as
|
||||||
`initramfs(-.*)?-$checksum`. If this exists, the checksum must include
|
`initramfs(-.*)?-$checksum` and/or a device tree, stored as
|
||||||
both the kernel and initramfs contents. OSTree will use this to
|
`devicetree(-.*)?-$checksum`. If an initramfs or devicetree exist,
|
||||||
determine which kernels are shared. The rationale for this is to avoid
|
the checksum must include all of the kernel, initramfs and devicetree contents.
|
||||||
computing checksums on the client by default.
|
OSTree will use this to determine which kernels are shared. The rationale for
|
||||||
|
this is to avoid computing checksums on the client by default.
|
||||||
|
|
||||||
The deployment should not have a traditional UNIX `/etc`; instead, it
|
The deployment should not have a traditional UNIX `/etc`; instead, it
|
||||||
should include `/usr/etc`. This is the "default configuration". When
|
should include `/usr/etc`. This is the "default configuration". When
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ sysroot_flags_to_copy_flags (GLnxFileCopyFlags defaults,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try a hardlink if we can, otherwise fall back to copying. Used
|
/* Try a hardlink if we can, otherwise fall back to copying. Used
|
||||||
* right now for kernels/initramfs in /boot, where we can just
|
* right now for kernels/initramfs/device trees in /boot, where we can just
|
||||||
* hardlink if we're on the same partition.
|
* hardlink if we're on the same partition.
|
||||||
*/
|
*/
|
||||||
static gboolean
|
static gboolean
|
||||||
|
|
@ -894,6 +894,8 @@ typedef struct {
|
||||||
char *kernel_namever;
|
char *kernel_namever;
|
||||||
char *initramfs_srcpath;
|
char *initramfs_srcpath;
|
||||||
char *initramfs_namever;
|
char *initramfs_namever;
|
||||||
|
char *devicetree_srcpath;
|
||||||
|
char *devicetree_namever;
|
||||||
char *bootcsum;
|
char *bootcsum;
|
||||||
} OstreeKernelLayout;
|
} OstreeKernelLayout;
|
||||||
static void
|
static void
|
||||||
|
|
@ -904,6 +906,8 @@ _ostree_kernel_layout_free (OstreeKernelLayout *layout)
|
||||||
g_free (layout->kernel_namever);
|
g_free (layout->kernel_namever);
|
||||||
g_free (layout->initramfs_srcpath);
|
g_free (layout->initramfs_srcpath);
|
||||||
g_free (layout->initramfs_namever);
|
g_free (layout->initramfs_namever);
|
||||||
|
g_free (layout->devicetree_srcpath);
|
||||||
|
g_free (layout->devicetree_namever);
|
||||||
g_free (layout->bootcsum);
|
g_free (layout->bootcsum);
|
||||||
g_free (layout);
|
g_free (layout);
|
||||||
}
|
}
|
||||||
|
|
@ -1025,6 +1029,22 @@ get_kernel_from_tree_usrlib_modules (int deployment_dfd,
|
||||||
if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error))
|
if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
g_clear_object (&in);
|
||||||
|
glnx_close_fd (&fd);
|
||||||
|
|
||||||
|
if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "devicetree", &fd, error))
|
||||||
|
return FALSE;
|
||||||
|
if (fd != -1)
|
||||||
|
{
|
||||||
|
ret_layout->devicetree_srcpath = g_strdup ("devicetree");
|
||||||
|
ret_layout->devicetree_namever = g_strdup_printf ("devicetree-%s", kver);
|
||||||
|
in = g_unix_input_stream_new (fd, FALSE);
|
||||||
|
if (!ot_gio_splice_update_checksum (NULL, in, &checksum, cancellable, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object (&in);
|
||||||
|
glnx_close_fd (&fd);
|
||||||
|
|
||||||
char hexdigest[OSTREE_SHA256_STRING_LEN+1];
|
char hexdigest[OSTREE_SHA256_STRING_LEN+1];
|
||||||
ot_checksum_get_hexdigest (&checksum, hexdigest, sizeof (hexdigest));
|
ot_checksum_get_hexdigest (&checksum, hexdigest, sizeof (hexdigest));
|
||||||
|
|
@ -1044,6 +1064,7 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd,
|
||||||
const char *legacy_paths[] = {"usr/lib/ostree-boot", "boot"};
|
const char *legacy_paths[] = {"usr/lib/ostree-boot", "boot"};
|
||||||
g_autofree char *kernel_checksum = NULL;
|
g_autofree char *kernel_checksum = NULL;
|
||||||
g_autofree char *initramfs_checksum = NULL;
|
g_autofree char *initramfs_checksum = NULL;
|
||||||
|
g_autofree char *devicetree_checksum = NULL;
|
||||||
g_autoptr(OstreeKernelLayout) ret_layout = _ostree_kernel_layout_new ();
|
g_autoptr(OstreeKernelLayout) ret_layout = _ostree_kernel_layout_new ();
|
||||||
|
|
||||||
for (guint i = 0; i < G_N_ELEMENTS (legacy_paths); i++)
|
for (guint i = 0; i < G_N_ELEMENTS (legacy_paths); i++)
|
||||||
|
|
@ -1110,9 +1131,23 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd,
|
||||||
ret_layout->initramfs_namever = g_strndup (name, dash - name);
|
ret_layout->initramfs_namever = g_strndup (name, dash - name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* See if this is the devicetree */
|
||||||
|
else if (ret_layout->devicetree_srcpath == NULL && g_str_has_prefix (name, "devicetree-"))
|
||||||
|
{
|
||||||
|
const char *dash = strrchr (name, '-');
|
||||||
|
g_assert (dash);
|
||||||
|
if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
|
||||||
|
{
|
||||||
|
devicetree_checksum = g_strdup (dash + 1);
|
||||||
|
ret_layout->devicetree_srcpath = g_strdup (name);
|
||||||
|
ret_layout->devicetree_namever = g_strndup (name, dash - name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If we found both a kernel and initramfs, break out of the loop */
|
/* If we found a kernel, an initramfs and a devicetree, break out of the loop */
|
||||||
if (ret_layout->kernel_srcpath != NULL && ret_layout->initramfs_srcpath != NULL)
|
if (ret_layout->kernel_srcpath != NULL &&
|
||||||
|
ret_layout->initramfs_srcpath != NULL &&
|
||||||
|
ret_layout->devicetree_srcpath != NULL)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1132,6 +1167,19 @@ get_kernel_from_tree_legacy_layouts (int deployment_dfd,
|
||||||
return glnx_throw (error, "Mismatched kernel checksum vs initrd");
|
return glnx_throw (error, "Mismatched kernel checksum vs initrd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The kernel/devicetree checksums must be the same */
|
||||||
|
if (ret_layout->devicetree_srcpath != NULL)
|
||||||
|
{
|
||||||
|
g_assert (kernel_checksum != NULL);
|
||||||
|
g_assert (devicetree_checksum != NULL);
|
||||||
|
if (strcmp (kernel_checksum, devicetree_checksum) != 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
||||||
|
"Mismatched kernel checksum vs device tree in tree");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret_layout->bootcsum = g_steal_pointer (&kernel_checksum);
|
ret_layout->bootcsum = g_steal_pointer (&kernel_checksum);
|
||||||
|
|
||||||
*out_layout = g_steal_pointer (&ret_layout);
|
*out_layout = g_steal_pointer (&ret_layout);
|
||||||
|
|
@ -1567,7 +1615,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||||
&deployment_dfd, error))
|
&deployment_dfd, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
/* Find the kernel/initramfs in the tree */
|
/* Find the kernel/initramfs/devicetree in the tree */
|
||||||
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
g_autoptr(OstreeKernelLayout) kernel_layout = NULL;
|
||||||
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
if (!get_kernel_from_tree (deployment_dfd, &kernel_layout,
|
||||||
cancellable, error))
|
cancellable, error))
|
||||||
|
|
@ -1628,6 +1676,21 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kernel_layout->devicetree_srcpath)
|
||||||
|
{
|
||||||
|
g_assert (kernel_layout->devicetree_namever);
|
||||||
|
if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->devicetree_namever, &stbuf, 0, error))
|
||||||
|
return FALSE;
|
||||||
|
if (errno == ENOENT)
|
||||||
|
{
|
||||||
|
if (!hardlink_or_copy_at (kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath,
|
||||||
|
bootcsum_dfd, kernel_layout->devicetree_namever,
|
||||||
|
sysroot->debug_flags,
|
||||||
|
cancellable, error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_autofree char *contents = NULL;
|
g_autofree char *contents = NULL;
|
||||||
if (!glnx_fstatat_allow_noent (deployment_dfd, "usr/lib/os-release", &stbuf, 0, error))
|
if (!glnx_fstatat_allow_noent (deployment_dfd, "usr/lib/os-release", &stbuf, 0, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
@ -1717,6 +1780,12 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||||
_ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg));
|
_ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (kernel_layout->devicetree_namever)
|
||||||
|
{
|
||||||
|
g_autofree char * boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_namever, NULL);
|
||||||
|
ostree_bootconfig_parser_set (bootconfig, "devicetree", boot_relpath);
|
||||||
|
}
|
||||||
|
|
||||||
/* Note this is parsed in ostree-impl-system-generator.c */
|
/* Note this is parsed in ostree-impl-system-generator.c */
|
||||||
g_autofree char *ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d",
|
g_autofree char *ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d",
|
||||||
new_bootversion, osname, bootcsum,
|
new_bootversion, osname, bootcsum,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
. $(dirname $0)/libtest.sh
|
. $(dirname $0)/libtest.sh
|
||||||
|
|
||||||
echo "1..7"
|
echo "1..10"
|
||||||
|
|
||||||
setup_os_repository "archive-z2" "uboot"
|
setup_os_repository "archive-z2" "uboot"
|
||||||
|
|
||||||
|
|
@ -20,11 +20,12 @@ echo "ok deployment with initramfs"
|
||||||
pull_test_tree() {
|
pull_test_tree() {
|
||||||
kernel_contents=$1
|
kernel_contents=$1
|
||||||
initramfs_contents=$2
|
initramfs_contents=$2
|
||||||
|
devicetree_contents=$3
|
||||||
|
|
||||||
printf "TEST SETUP:\n kernel: %s\n initramfs: %s\n layout: %s\n" \
|
printf "TEST SETUP:\n kernel: %s\n initramfs: %s\n devicetree: %s\n layout: %s\n" \
|
||||||
"$kernel_contents" "$initramfs_contents" "$layout"
|
"$kernel_contents" "$initramfs_contents" "$devicetree_contents" "$layout"
|
||||||
|
|
||||||
rm -rf ${test_tmpdir}/osdata/usr/lib/modules/3.6.0/{initramfs.img,vmlinuz} \
|
rm -rf ${test_tmpdir}/osdata/usr/lib/modules/3.6.0/{initramfs.img,vmlinuz,devicetree} \
|
||||||
${test_tmpdir}/osdata/usr/lib/ostree-boot \
|
${test_tmpdir}/osdata/usr/lib/ostree-boot \
|
||||||
${test_tmpdir}/osdata/boot
|
${test_tmpdir}/osdata/boot
|
||||||
if [ "$layout" = "/usr/lib/modules" ]; then
|
if [ "$layout" = "/usr/lib/modules" ]; then
|
||||||
|
|
@ -32,14 +33,16 @@ pull_test_tree() {
|
||||||
cd ${test_tmpdir}/osdata/usr/lib/modules/3.6.0
|
cd ${test_tmpdir}/osdata/usr/lib/modules/3.6.0
|
||||||
echo -n "$kernel_contents" > vmlinuz
|
echo -n "$kernel_contents" > vmlinuz
|
||||||
[ -n "$initramfs_contents" ] && echo -n "$initramfs_contents" > initramfs.img
|
[ -n "$initramfs_contents" ] && echo -n "$initramfs_contents" > initramfs.img
|
||||||
|
[ -n "$devicetree_contents" ] && echo -n "$devicetree_contents" > devicetree
|
||||||
elif [ "$layout" = "/usr/lib/ostree-boot" ] || [ "$layout" = "/boot" ]; then
|
elif [ "$layout" = "/usr/lib/ostree-boot" ] || [ "$layout" = "/boot" ]; then
|
||||||
# "Legacy" layout
|
# "Legacy" layout
|
||||||
mkdir -p "${test_tmpdir}/osdata/$layout"
|
mkdir -p "${test_tmpdir}/osdata/$layout"
|
||||||
cd "${test_tmpdir}/osdata/$layout"
|
cd "${test_tmpdir}/osdata/$layout"
|
||||||
bootcsum=$(echo -n "$kernel_contents$initramfs_contents" \
|
bootcsum=$(echo -n "$kernel_contents$initramfs_contents$devicetree_contents" \
|
||||||
| sha256sum | cut -f 1 -d ' ')
|
| sha256sum | cut -f 1 -d ' ')
|
||||||
echo -n "$kernel_contents" > vmlinuz-${bootcsum}
|
echo -n "$kernel_contents" > vmlinuz-${bootcsum}
|
||||||
[ -n "$initramfs_contents" ] && echo -n "$initramfs_contents" > initramfs-${bootcsum}
|
[ -n "$initramfs_contents" ] && echo -n "$initramfs_contents" > initramfs-${bootcsum}
|
||||||
|
[ -n "$devicetree_contents" ] && echo -n "$devicetree_contents" > devicetree-${bootcsum}
|
||||||
else
|
else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -75,4 +78,13 @@ do
|
||||||
assert_not_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'init='
|
assert_not_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'init='
|
||||||
|
|
||||||
echo "ok switching from no initramfs to initramfs enabled sysroot layout=$layout"
|
echo "ok switching from no initramfs to initramfs enabled sysroot layout=$layout"
|
||||||
|
|
||||||
|
pull_test_tree "the kernel" "" "my .dtb file"
|
||||||
|
${CMD_PREFIX} ostree admin deploy --os=testos testos:testos/buildmaster/x86_64-runtime
|
||||||
|
|
||||||
|
assert_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'init='
|
||||||
|
assert_file_has_content sysroot/boot/"$(get_key_from_bootloader_conf sysroot/boot/loader/entries/ostree-testos-0.conf 'devicetree')" "my .dtb file"
|
||||||
|
assert_not_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf 'initrd'
|
||||||
|
|
||||||
|
echo "ok switching from initramfs to no initramfs sysroot with devicetree layout=$layout"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue