mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-31 21:18:22 +03:00
bootloader: Add an aboot (Android) bootloader backend
aboot is special in that it packages kernel, initrd, cmdline, dtb and signature one combined image (similar to upcoming unified kernel images). This is then loaded as an image into an aboot partition. This image is signed by the OS vendor and covers everything in the image. So locally on the deployed system it should not be possible to boot an unsigned image (unless signature checking is turned off). We call a shell script aboot-deploy when it is required to write a new image to the aboot partition (a file typically starting with aboot and ending in .img extension). This shell script may also read some configurations from a .cfg file. Signed-off-by: Eric Curtin <ecurtin@redhat.com>
This commit is contained in:
parent
ad96b8afea
commit
97117753e3
@ -111,6 +111,8 @@ libostree_1_la_SOURCES = \
|
||||
src/libostree/ostree-deployment.c \
|
||||
src/libostree/ostree-bootloader.h \
|
||||
src/libostree/ostree-bootloader.c \
|
||||
src/libostree/ostree-bootloader-aboot.h \
|
||||
src/libostree/ostree-bootloader-aboot.c \
|
||||
src/libostree/ostree-bootloader-grub2.h \
|
||||
src/libostree/ostree-bootloader-grub2.c \
|
||||
src/libostree/ostree-bootloader-zipl.h \
|
||||
|
@ -333,6 +333,14 @@ pub struct _OstreeBootloaderSyslinux {
|
||||
|
||||
pub type OstreeBootloaderSyslinux = *mut _OstreeBootloaderSyslinux;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct _OstreeBootloaderAboot {
|
||||
_data: [u8; 0],
|
||||
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
|
||||
}
|
||||
|
||||
pub type OstreeBootloaderAboot = *mut _OstreeBootloaderAboot;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct _OstreeBootloaderUboot {
|
||||
_data: [u8; 0],
|
||||
|
227
src/libostree/ostree-bootloader-aboot.c
Normal file
227
src/libostree/ostree-bootloader-aboot.c
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Eric Curtin <ericcurtin17@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the licence or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ostree-sysroot-private.h"
|
||||
#include "ostree-bootloader-aboot.h"
|
||||
#include "ostree-deployment-private.h"
|
||||
#include "ostree-libarchive-private.h"
|
||||
#include "otutil.h"
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* This is specific to aboot and zipl today, but in the future we could also
|
||||
* use it for the grub2-mkconfig case.
|
||||
*/
|
||||
static const char aboot_requires_execute_path[] = "boot/ostree-bootloader-update.stamp";
|
||||
|
||||
struct _OstreeBootloaderAboot
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
OstreeSysroot *sysroot;
|
||||
};
|
||||
|
||||
typedef GObjectClass OstreeBootloaderAbootClass;
|
||||
static void _ostree_bootloader_aboot_bootloader_iface_init (OstreeBootloaderInterface *iface);
|
||||
G_DEFINE_TYPE_WITH_CODE (OstreeBootloaderAboot, _ostree_bootloader_aboot, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_BOOTLOADER, _ostree_bootloader_aboot_bootloader_iface_init));
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_aboot_query (OstreeBootloader *bootloader,
|
||||
gboolean *out_is_active,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
/* We don't auto-detect this one; should be explicitly chosen right now.
|
||||
* see also https://github.com/coreos/coreos-assembler/pull/849
|
||||
*/
|
||||
*out_is_active = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
_ostree_bootloader_aboot_get_name (OstreeBootloader *bootloader)
|
||||
{
|
||||
return "aboot";
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_aboot_write_config (OstreeBootloader *bootloader,
|
||||
int bootversion,
|
||||
GPtrArray *new_deployments,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeBootloaderAboot *self = OSTREE_BOOTLOADER_ABOOT (bootloader);
|
||||
|
||||
/* Write our stamp file */
|
||||
if (!glnx_file_replace_contents_at (self->sysroot->sysroot_fd, aboot_requires_execute_path,
|
||||
(guint8*)"", 0, GLNX_FILE_REPLACE_NODATASYNC,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_aboot_get_bls_config (OstreeBootloaderAboot *self,
|
||||
int bootversion,
|
||||
gchar **aboot,
|
||||
gchar **abootcfg,
|
||||
gchar **version,
|
||||
gchar **vmlinuz,
|
||||
gchar **initramfs,
|
||||
gchar **options,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr (GPtrArray) configs = NULL;
|
||||
if ( !_ostree_sysroot_read_boot_loader_configs (self->sysroot, bootversion, &configs, cancellable, error))
|
||||
return glnx_prefix_error (error, "aboot: loading bls configs");
|
||||
|
||||
if (!configs || configs->len == 0)
|
||||
return glnx_throw (error, "aboot: no bls config");
|
||||
|
||||
OstreeBootconfigParser *parser = (OstreeBootconfigParser *) g_ptr_array_index (configs, 0);
|
||||
const gchar *val = NULL;
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "aboot");
|
||||
if (!val) {
|
||||
return glnx_throw (error, "aboot: no \"aboot\" key in bootloader config");
|
||||
}
|
||||
*aboot = g_strdup(val);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "abootcfg");
|
||||
if (!val) {
|
||||
return glnx_throw (error, "aboot: no \"abootcfg\" key in bootloader config");
|
||||
}
|
||||
*abootcfg = g_strdup(val);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "version");
|
||||
if (!val)
|
||||
return glnx_throw (error, "aboot: no \"version\" key in bootloader config");
|
||||
*version = g_strdup(val);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "linux");
|
||||
if (!val)
|
||||
return glnx_throw (error, "aboot: no \"linux\" key in bootloader config");
|
||||
*vmlinuz = g_build_filename ("/boot", val, NULL);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "initrd");
|
||||
if (!val)
|
||||
return glnx_throw (error, "aboot: no \"initrd\" key in bootloader config");
|
||||
*initramfs = g_build_filename ("/boot", val, NULL);
|
||||
|
||||
val = ostree_bootconfig_parser_get (parser, "options");
|
||||
if (!val)
|
||||
return glnx_throw (error, "aboot: no \"options\" key in bootloader config");
|
||||
*options = g_strdup(val);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_ostree_bootloader_aboot_post_bls_sync (OstreeBootloader *bootloader,
|
||||
int bootversion,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
OstreeBootloaderAboot *self = OSTREE_BOOTLOADER_ABOOT (bootloader);
|
||||
|
||||
/* Note that unlike the grub2-mkconfig backend, we make no attempt to
|
||||
* chroot().
|
||||
*/
|
||||
// g_assert (self->sysroot->booted_deployment);
|
||||
|
||||
if (!glnx_fstatat_allow_noent (self->sysroot->sysroot_fd, aboot_requires_execute_path, NULL, 0, error))
|
||||
return FALSE;
|
||||
|
||||
/* If there's no stamp file, nothing to do */
|
||||
if (errno == ENOENT)
|
||||
return TRUE;
|
||||
|
||||
g_autofree gchar* aboot = NULL;
|
||||
g_autofree gchar* abootcfg = NULL;
|
||||
g_autofree gchar* version = NULL;
|
||||
g_autofree gchar* vmlinuz = NULL;
|
||||
g_autofree gchar* initramfs = NULL;
|
||||
g_autofree gchar* options = NULL;
|
||||
if (!_ostree_aboot_get_bls_config (self, bootversion, &aboot, &abootcfg, &version, &vmlinuz, &initramfs, &options, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *path_str = g_file_get_path(self->sysroot->path);
|
||||
|
||||
const char *const aboot_argv[] = {"aboot-deploy", "-r", path_str, "-c", abootcfg, aboot, NULL};
|
||||
int estatus;
|
||||
if (!g_spawn_sync (NULL, (char**)aboot_argv, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, NULL, NULL, &estatus, error)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!g_spawn_check_exit_status (estatus, error)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!glnx_unlinkat (self->sysroot->sysroot_fd, aboot_requires_execute_path, 0, error)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_bootloader_aboot_finalize (GObject *object)
|
||||
{
|
||||
OstreeBootloaderAboot *self = OSTREE_BOOTLOADER_ABOOT (object);
|
||||
|
||||
g_clear_object (&self->sysroot);
|
||||
|
||||
G_OBJECT_CLASS (_ostree_bootloader_aboot_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
void
|
||||
_ostree_bootloader_aboot_init (OstreeBootloaderAboot *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_ostree_bootloader_aboot_bootloader_iface_init (OstreeBootloaderInterface *iface)
|
||||
{
|
||||
iface->query = _ostree_bootloader_aboot_query;
|
||||
iface->get_name = _ostree_bootloader_aboot_get_name;
|
||||
iface->write_config = _ostree_bootloader_aboot_write_config;
|
||||
iface->post_bls_sync = _ostree_bootloader_aboot_post_bls_sync;
|
||||
}
|
||||
|
||||
void
|
||||
_ostree_bootloader_aboot_class_init (OstreeBootloaderAbootClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = _ostree_bootloader_aboot_finalize;
|
||||
}
|
||||
|
||||
OstreeBootloaderAboot *
|
||||
_ostree_bootloader_aboot_new (OstreeSysroot *sysroot)
|
||||
{
|
||||
OstreeBootloaderAboot *self = g_object_new (OSTREE_TYPE_BOOTLOADER_ABOOT, NULL);
|
||||
self->sysroot = g_object_ref (sysroot);
|
||||
return self;
|
||||
}
|
33
src/libostree/ostree-bootloader-aboot.h
Normal file
33
src/libostree/ostree-bootloader-aboot.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Eric Curtin <ericcurtin17@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the licence or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ostree-bootloader.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define OSTREE_TYPE_BOOTLOADER_ABOOT (_ostree_bootloader_aboot_get_type ())
|
||||
#define OSTREE_BOOTLOADER_ABOOT(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), OSTREE_TYPE_BOOTLOADER_ABOOT, OstreeBootloaderAboot))
|
||||
#define OSTREE_IS_BOOTLOADER_ABOOT(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), OSTREE_TYPE_BOOTLOADER_ABOOT))
|
||||
|
||||
typedef struct _OstreeBootloaderAboot OstreeBootloaderAboot;
|
||||
|
||||
GType _ostree_bootloader_aboot_get_type (void) G_GNUC_CONST;
|
||||
|
||||
OstreeBootloaderAboot * _ostree_bootloader_aboot_new (OstreeSysroot *sysroot);
|
||||
G_END_DECLS
|
@ -124,6 +124,7 @@ typedef enum {
|
||||
CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX,
|
||||
CFG_SYSROOT_BOOTLOADER_OPT_UBOOT,
|
||||
CFG_SYSROOT_BOOTLOADER_OPT_ZIPL,
|
||||
CFG_SYSROOT_BOOTLOADER_OPT_ABOOT,
|
||||
/* Non-exhaustive */
|
||||
} OstreeCfgSysrootBootloaderOpt;
|
||||
|
||||
@ -135,6 +136,7 @@ static const char* const CFG_SYSROOT_BOOTLOADER_OPTS_STR[] = {
|
||||
"syslinux",
|
||||
"uboot",
|
||||
"zipl",
|
||||
"aboot",
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -992,6 +992,8 @@ typedef struct {
|
||||
char *initramfs_namever;
|
||||
char *devicetree_srcpath;
|
||||
char *devicetree_namever;
|
||||
char *aboot_srcpath;
|
||||
char *aboot_namever;
|
||||
char *bootcsum;
|
||||
} OstreeKernelLayout;
|
||||
static void
|
||||
@ -1006,6 +1008,8 @@ _ostree_kernel_layout_free (OstreeKernelLayout *layout)
|
||||
g_free (layout->initramfs_namever);
|
||||
g_free (layout->devicetree_srcpath);
|
||||
g_free (layout->devicetree_namever);
|
||||
g_free (layout->aboot_srcpath);
|
||||
g_free (layout->aboot_namever);
|
||||
g_free (layout->bootcsum);
|
||||
g_free (layout);
|
||||
}
|
||||
@ -1131,6 +1135,21 @@ get_kernel_from_tree_usrlib_modules (OstreeSysroot *sysroot,
|
||||
g_clear_object (&in);
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
/* look for a aboot.img file. */
|
||||
if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "aboot.img", &fd, error))
|
||||
return FALSE;
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
ret_layout->aboot_srcpath = g_strdup ("aboot.img");
|
||||
ret_layout->aboot_namever = g_strdup_printf ("aboot-%s.img", kver);
|
||||
}
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
/* look for a aboot.cfg file. */
|
||||
if (!ot_openat_ignore_enoent (ret_layout->boot_dfd, "aboot.cfg", &fd, error))
|
||||
return FALSE;
|
||||
|
||||
/* Testing aid for https://github.com/ostreedev/ostree/issues/2154 */
|
||||
const gboolean no_dtb = (sysroot->debug_flags & OSTREE_SYSROOT_DEBUG_TEST_NO_DTB) > 0;
|
||||
if (!no_dtb)
|
||||
@ -1923,6 +1942,21 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
}
|
||||
}
|
||||
|
||||
if (kernel_layout->aboot_srcpath)
|
||||
{
|
||||
g_assert (kernel_layout->aboot_namever);
|
||||
if (!glnx_fstatat_allow_noent (bootcsum_dfd, kernel_layout->aboot_namever, &stbuf, 0, error))
|
||||
return FALSE;
|
||||
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->aboot_srcpath,
|
||||
bootcsum_dfd, kernel_layout->aboot_namever,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
g_autoptr(GPtrArray) overlay_initrds = NULL;
|
||||
for (char **it = _ostree_deployment_get_overlay_initrds (deployment); it && *it; it++)
|
||||
{
|
||||
@ -2059,6 +2093,30 @@ install_deployment_kernel (OstreeSysroot *sysroot,
|
||||
ostree_kernel_args_replace_take (kargs, g_steal_pointer (&prepare_root_arg));
|
||||
}
|
||||
|
||||
const char* aboot_fn = NULL;
|
||||
if (kernel_layout->aboot_namever)
|
||||
{
|
||||
aboot_fn = kernel_layout->aboot_namever;
|
||||
}
|
||||
else if (kernel_layout->aboot_srcpath)
|
||||
{
|
||||
aboot_fn = kernel_layout->aboot_srcpath;
|
||||
}
|
||||
|
||||
if (aboot_fn)
|
||||
{
|
||||
g_autofree char * aboot_relpath = g_strconcat ("/", bootcsumdir, "/", aboot_fn, NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "aboot", aboot_relpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree char * aboot_relpath = g_strconcat ("/", deployment_dirpath, "/usr/lib/ostree-boot/aboot.img", NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "aboot", aboot_relpath);
|
||||
}
|
||||
|
||||
g_autofree char * abootcfg_relpath = g_strconcat ("/", deployment_dirpath, "/usr/lib/ostree-boot/aboot.cfg", NULL);
|
||||
ostree_bootconfig_parser_set (bootconfig, "abootcfg", abootcfg_relpath);
|
||||
|
||||
if (kernel_layout->devicetree_namever)
|
||||
{
|
||||
g_autofree char * dt_boot_relpath = g_strconcat ("/", bootcsumdir, "/", kernel_layout->devicetree_namever, NULL);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "ostree-sepolicy-private.h"
|
||||
#include "ostree-sysroot-private.h"
|
||||
#include "ostree-deployment-private.h"
|
||||
#include "ostree-bootloader-aboot.h"
|
||||
#include "ostree-bootloader-uboot.h"
|
||||
#include "ostree-bootloader-syslinux.h"
|
||||
#include "ostree-bootloader-grub2.h"
|
||||
@ -1475,6 +1476,8 @@ _ostree_sysroot_new_bootloader_by_type (
|
||||
return (OstreeBootloader*) _ostree_bootloader_grub2_new (sysroot);
|
||||
case CFG_SYSROOT_BOOTLOADER_OPT_SYSLINUX:
|
||||
return (OstreeBootloader*) _ostree_bootloader_syslinux_new (sysroot);
|
||||
case CFG_SYSROOT_BOOTLOADER_OPT_ABOOT:
|
||||
return (OstreeBootloader*) _ostree_bootloader_aboot_new (sysroot);
|
||||
case CFG_SYSROOT_BOOTLOADER_OPT_UBOOT:
|
||||
return (OstreeBootloader*) _ostree_bootloader_uboot_new (sysroot);
|
||||
case CFG_SYSROOT_BOOTLOADER_OPT_ZIPL:
|
||||
|
Loading…
Reference in New Issue
Block a user