s390utils/s390-tools-zipl-Add-BootLoaderSpec-support.patch
Javier Martinez Canillas 6aab78b451
- add BLS support to zipl and add kernel-install scripts to create BLS files
Add patches to support population IPL sections using BootLoaderSpec files
instead of having them defined in the zipl.conf file. This allows to have
a static zipl.conf file, and update the boot entries by just dropping BLS
fragments files in a directory.

In this configuration the grubby tool isn't used, and instead all needed
tasks (copy kernel, generate an initramfs, copy BLS configs, etc) should
be made by kernel-install scripts.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
2018-05-30 14:50:30 +02:00

412 lines
12 KiB
Diff

From 4d3fb60159566dfb011a855d899425e972ed951a Mon Sep 17 00:00:00 2001
From: Javier Martinez Canillas <javierm@redhat.com>
Date: Wed, 2 May 2018 12:26:58 +0200
Subject: [PATCH] zipl: Add BootLoaderSpec support
The BootLoaderSpec (BLS) defines a file format for boot configurations,
so bootloaders can parse these files and create their boot menu entries
by using the information provided by them [0].
This allow to configure the boot items as drop-in files in a directory
instead of having to parse and modify a bootloader configuration file.
If the /boot/loader/entries exists and there are BLS files there, then
these are parsed and configuration sections are added without the need
to have these in a zipl.conf file.
A different BLS directory can be specified from the command line using
the --blsdir option.
[0]: https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---
zipl/include/scan.h | 1 +
zipl/include/zipl.h | 1 +
zipl/man/zipl.8 | 12 ++-
zipl/man/zipl.conf.5 | 21 +++++
zipl/src/job.c | 24 +++++-
zipl/src/scan.c | 179 +++++++++++++++++++++++++++++++++++++++++++
zipl/src/zipl.c | 1 +
7 files changed, 237 insertions(+), 2 deletions(-)
diff --git a/zipl/include/scan.h b/zipl/include/scan.h
index f2c7b1b094a..0fe415c9b2d 100644
--- a/zipl/include/scan.h
+++ b/zipl/include/scan.h
@@ -119,6 +119,7 @@ extern enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM];
int scan_file(const char* filename, struct scan_token** array);
+int scan_bls(const char* blsdir, struct scan_token** token, int scan_size);
void scan_free(struct scan_token* array);
char* scan_keyword_name(enum scan_keyword_id id);
int scan_check_defaultboot(struct scan_token* scan);
diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h
index 8dc6b6db1a5..2aed54fb2b5 100644
--- a/zipl/include/zipl.h
+++ b/zipl/include/zipl.h
@@ -43,6 +43,7 @@
#define ZIPL_CONF_VAR "ZIPLCONF"
#define ZIPL_DEFAULT_CONF "/etc/zipl.conf"
+#define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries"
#define MENU_DEFAULT_PROMPT 0
#define MENU_DEFAULT_TIMEOUT 0
diff --git a/zipl/man/zipl.8 b/zipl/man/zipl.8
index 873d680465a..989197590e4 100644
--- a/zipl/man/zipl.8
+++ b/zipl/man/zipl.8
@@ -25,7 +25,7 @@ loading a data file to initialize named saved segments (NSS)
Each of these operations is characterized by a boot configuration, i.e. a
set of required parameters.
.B zipl
-supports two ways of specifying a boot configuration:
+supports three ways of specifying a boot configuration:
.IP " -"
.B command line:
all parameters are provided through the command line switches described below.
@@ -37,6 +37,11 @@ parameters are provided by sections defined in a configuration file (see
Using a configuration file, you can either specify a single boot configuration
or a menu, i.e. a list of configurations from which users can choose at boot
time.
+.IP " -"
+.B BLS config files:
+boot configurations are specified using BootLoaderSpec (BLS) configuration files. The
+.BR zipl.conf (5)
+configuration file is still used to specify parameters or aditional boot configurations.
.PP
To use a single boot configuration section, provide its name as parameter to
@@ -117,6 +122,11 @@ Print version information, then exit.
Use the specified <CONFIG FILE>. If none is supplied, the environment
variable ZIPLCONF is evaluated if set, otherwise /etc/zipl.conf is used.
+.TP
+.BR "\-b <BLS DIRECTORY>" " or " "\-\-blsdir=<BLS DIRECTORY>"
+Use the specified <BLS DIRECTORY> to parse BootLoaderSpec config files.
+If none is supplied, the /boot/loader/entries directory is used.
+
.TP
.BR "\-t <TARGET DIRECTORY>" " or " "\-\-target=<TARGET DIRECTORY>"
Use the specified <TARGET DIRECTORY>.
diff --git a/zipl/man/zipl.conf.5 b/zipl/man/zipl.conf.5
index 8c454cd01e6..d4877d884fc 100644
--- a/zipl/man/zipl.conf.5
+++ b/zipl/man/zipl.conf.5
@@ -117,6 +117,27 @@ timeout = 0
.br
.PP
+.B BootLoaderSpec configuration files
+
+Another way to specify IPL sections is to use BLS config files. These are
+configuration files located by default at /boot/loader/entries, but another
+location can be specified using the '\-\-blsdir' option of zipl.
+
+.IP
+# An example for a BLS configuration file
+.br
+
+version 4.15.9
+.br
+linux /vmlinuz-4.15.9
+.br
+initrd /initramfs-4.15.9
+.br
+options root=/dev/dasda1 console=ttyS0
+.PP
+
+The location of the linux and initrd has to be specified relative to the boot partition. The BLS config files are only used to specify the IPL sections, a zipl.conf configuration files is still needed for global parameters.
+
.B Boot menu
The
diff --git a/zipl/src/job.c b/zipl/src/job.c
index 9d5f00b6bdc..e6d298188d5 100644
--- a/zipl/src/job.c
+++ b/zipl/src/job.c
@@ -27,6 +27,7 @@
/* Command line options */
static struct option options[] = {
{ "config", required_argument, NULL, 'c'},
+ { "blsdir", required_argument, NULL, 'b'},
{ "target", required_argument, NULL, 't'},
{ "targetbase", required_argument, NULL, 0xaa},
{ "targettype", required_argument, NULL, 0xab},
@@ -55,11 +56,12 @@ static struct option options[] = {
};
/* Command line option abbreviations */
-static const char option_string[] = "-c:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:";
+static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:";
struct command_line {
char* data[SCAN_KEYWORD_NUM];
char* config;
+ char *blsdir;
char* menu;
char* section;
int help;
@@ -197,6 +199,14 @@ get_command_line(int argc, char* argv[], struct command_line* line)
} else
cmdline.config = optarg;
break;
+ case 'b':
+ if (cmdline.blsdir != NULL) {
+ error_reason("Option 'blsdir' specified more "
+ "than once");
+ rc = -1;
+ } else
+ cmdline.blsdir = optarg;
+ break;
case 'm':
if (cmdline.menu != NULL) {
error_reason("Option 'menu' specified more "
@@ -1731,6 +1741,7 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
struct scan_token* scan;
struct scan_token* new_scan;
char* filename;
+ char *blsdir;
char* source;
int rc, scan_size;
@@ -1755,6 +1766,17 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
error_text("Config file '%s'", filename);
return scan_size;
}
+ /* Check if a BLS directory was provided on command line */
+ if (cmdline->blsdir != NULL) {
+ blsdir = cmdline->blsdir;
+ } else {
+ blsdir = ZIPL_DEFAULT_BLSDIR;
+ }
+ rc = scan_bls(blsdir, &scan, scan_size);
+ if (rc) {
+ error_text("BLS parsing '%s'", blsdir);
+ return rc;
+ }
if ((cmdline->menu == NULL) && (cmdline->section == NULL)) {
rc = scan_check_defaultboot(scan);
if (rc < 0) {
diff --git a/zipl/src/scan.c b/zipl/src/scan.c
index adb288e0626..fe72e9ab13d 100644
--- a/zipl/src/scan.c
+++ b/zipl/src/scan.c
@@ -16,13 +16,21 @@
#define __USE_ISOC99
#endif
+/* Need GNU function strverscmp() in dirent.h */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#include <ctype.h>
+#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+
#include "lib/util_base.h"
#include "boot.h"
@@ -633,6 +641,177 @@ scan_file(const char* filename, struct scan_token** token)
}
+static int
+bls_filter(const struct dirent *ent)
+{
+ int offset = strlen(ent->d_name) - strlen(".conf");
+
+ if (offset < 0)
+ return 0;
+
+ return strncmp(ent->d_name + offset, ".conf", strlen(".conf")) == 0;
+}
+
+
+static int
+bls_sort(const struct dirent **ent_a, const struct dirent **ent_b)
+{
+ return strverscmp((*ent_a)->d_name, (*ent_b)->d_name);
+}
+
+
+static int
+scan_append_section_heading(struct scan_token* scan, int* index, char* name);
+static int
+scan_append_keyword_assignment(struct scan_token* scan, int* index,
+ enum scan_keyword_id id, char* value);
+
+
+static int
+scan_bls_field(struct misc_file_buffer *file, struct scan_token* scan,
+ int* index)
+{
+ int current;
+ int key_start, key_end;
+ int val_start, val_end;
+ char *val;
+
+ for (key_start = file->pos; ; file->pos++) {
+ current = misc_get_char(file, 0);
+
+ if (isblank(current)) {
+ key_end = file->pos;
+ skip_blanks(file);
+ val_start = file->pos;
+ break;
+ }
+
+ if (!isalnum(current) && current != '_' && current != '-')
+ return -1;
+ }
+
+ for (; ; file->pos++) {
+ current = misc_get_char(file, 0);
+
+ if (current == '\n' || current == EOF)
+ break;
+ }
+
+ val_end = file->pos;
+ file->buffer[key_end] = '\0';
+ file->buffer[val_end] = '\0';
+
+ if (strncmp("version", &file->buffer[key_start], key_end - key_start) == 0) {
+ scan_append_section_heading(scan, index, &file->buffer[val_start]);
+ }
+
+ if (strncmp("linux", &file->buffer[key_start], key_end - key_start) == 0) {
+ misc_asprintf(&val, "%s", &file->buffer[val_start]);
+ scan_append_keyword_assignment(scan, index, scan_keyword_image, val);
+ free(val);
+ }
+
+ if (strncmp("options", &file->buffer[key_start], key_end - key_start) == 0) {
+ scan_append_keyword_assignment(scan, index, scan_keyword_parameters,
+ &file->buffer[val_start]);
+ }
+
+ if (strncmp("initrd", &file->buffer[key_start], key_end - key_start) == 0) {
+ misc_asprintf(&val, "%s", &file->buffer[val_start]);
+ scan_append_keyword_assignment(scan, index, scan_keyword_ramdisk, val);
+ free(val);
+ }
+
+ return 0;
+}
+
+
+int
+scan_bls(const char* blsdir, struct scan_token** token, int scan_size)
+{
+ int count = 0;
+ int size, remaining = 0, n, current, rc = -1;
+ struct scan_token* buffer;
+ struct scan_token* array = *token;
+ struct dirent** bls_entries;
+ struct misc_file_buffer file;
+ struct stat sb;
+ char filename[PATH_MAX];
+
+ if (!(stat(blsdir, &sb) == 0 && S_ISDIR(sb.st_mode)))
+ return 0;
+
+ n = scandir(blsdir, &bls_entries, bls_filter, bls_sort);
+ if (n <= 0)
+ return n;
+
+ while (array[count].id != 0)
+ count++;
+
+ remaining = scan_size - count;
+
+ if (remaining < n) {
+ size = scan_size - remaining + n;
+ buffer = (struct scan_token *)misc_malloc(size * sizeof(struct scan_token));
+ if (!buffer)
+ goto err;
+ memset(buffer, 0, size * sizeof(struct scan_token));
+ memcpy(buffer, array, count * sizeof(struct scan_token));
+ } else {
+ buffer = array;
+ }
+
+ while (n--) {
+ sprintf(filename, "%s/%s", blsdir, bls_entries[n]->d_name);
+ printf("Using BLS config file '%s'\n", filename);
+
+ rc = misc_get_file_buffer(filename, &file);
+ if (rc)
+ goto err;
+
+ while ((size_t)file.pos < file.length) {
+ current = misc_get_char(&file, 0);
+ switch (current) {
+ case '#':
+ file.pos++;
+ skip_line(&file);
+ break;
+ case EOF:
+ break;
+ case '\t':
+ case '\0':
+ case ' ':
+ file.pos++;
+ break;
+ default:
+ rc = scan_bls_field(&file, buffer, &count);
+ if (rc) {
+ error_reason("Incorrect BLS field in "
+ "config file %s\n", filename);
+ goto err;
+ }
+ break;
+ }
+ }
+
+ misc_free_file_buffer(&file);
+ free(bls_entries[n]);
+ }
+
+ *token = buffer;
+ rc = 0;
+err:
+ if (n > 0) {
+ do {
+ free(bls_entries[n]);
+ } while (n-- > 0);
+ }
+
+ free(bls_entries);
+ return rc;
+}
+
+
/* Search scanned tokens SCAN for a section/menu heading (according to
* TYPE) of the given NAME, beginning at token OFFSET. Return the index of
* the section/menu heading on success, a negative value on error. */
diff --git a/zipl/src/zipl.c b/zipl/src/zipl.c
index 1eaa82b9f43..65eefdb2d99 100644
--- a/zipl/src/zipl.c
+++ b/zipl/src/zipl.c
@@ -54,6 +54,7 @@ static const char* usage_text[] = {
"-h, --help Print this help, then exit",
"-v, --version Print version information, then exit",
"-c, --config CONFIGFILE Read configuration from CONFIGFILE",
+"-b, --blsdir BLSDIR Parse BootLoaderSpec files from BLSDIR",
"-t, --target TARGETDIR Write bootmap file to TARGETDIR and install",
" bootloader on device containing TARGETDIR",
" --targetbase BASEDEVICE Install bootloader on BASEDEVICE",
--
2.17.0