zipl: add basic support of environment block by zipl(8)

When installing a boot record for "ipl" and "menu" job, always add
an environment block as a boot component located in bootmap file
at offset alinged on file system block size boundary. When adding,
first try to import environment from a file at location specified
by newly introduced "--environment" zipl option, or by default at
"/etc/ziplenv". If nothing was imported, then add a blank environment
block. Optionally print the content of the environment block.

Store environment block size and address (as of boot component) in
stage3_parms.

Change interface of add_ipl_program(): add 2 additional arguments:
a pointer to bootmap file name and a predicate indicating if we
need to add environment block as a boot component.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Eduard Shishkin
2021-06-29 13:27:50 +02:00
committed by Jan Höppner
parent bbeb0f8445
commit 1fb859729e
13 changed files with 768 additions and 49 deletions

View File

@@ -36,8 +36,10 @@ struct stage3_parms {
unsigned long long flags; /* flags (e.g. STAGE3_FLAG_KDUMP) */
unsigned long long image_len; /* length of kernel */
unsigned long long image_addr; /* target address of kernel */
unsigned long long envblk_addr; /* address of environment block */
unsigned long long envblk_len; /* size of environment block */
};
STATIC_ASSERT(sizeof(struct stage3_parms) == 8 * 8)
STATIC_ASSERT(sizeof(struct stage3_parms) == 8 * 10)
extern struct stage3_parms _stage3_parms;
extern void kdump_stage3();

View File

@@ -296,7 +296,8 @@ int boot_get_eckd_stage2(void** data, size_t* size, struct job_data* job);
int boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
address_t initrd_addr, size_t initrd_len,
address_t entry, int extra_parm, uint64_t flags,
address_t image_addr, size_t image_len);
address_t image_addr, size_t image_len,
address_t envblk_addr, size_t envblk_len);
int boot_get_tape_ipl(void** data, size_t* size, address_t parm_addr,
address_t initrd_addr, address_t image_addr);
int boot_get_tape_dump(void** data, size_t* size, uint64_t mem);

View File

@@ -17,10 +17,20 @@
#include "disk.h"
#include "job.h"
#include "zipl.h"
#include "stddef.h"
#define BOOTMAP_HEADER_VERSION 1
#define SIGNATURE_MAGIC "~Module signature appended~\n"
#define PKCS7_FORMAT 0x01
struct bootmap_header {
char header_text[48];
u64 version;
u64 envblk_offset;
char reserved[448];
};
struct signature_header {
uint8_t format;
uint8_t reserved[3];
@@ -50,6 +60,9 @@ struct file_signature {
#define PKEY_ID_PKCS7 0x02
int bootmap_header_init(int fd);
int bootmap_header_read(int fd, struct bootmap_header *bh);
int bootmap_header_write(int fd, struct bootmap_header *bh);
int bootmap_create(struct job_data* job, disk_blockptr_t* program_table,
disk_blockptr_t *scsi_dump_sb_blockptr,
disk_blockptr_t** stage2_list, blocknum_t* stage2_count,

34
zipl/include/envblk.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* s390-tools/zipl/include/bootmap.h
* Functions to handle environment block.
*
* Copyright IBM Corp. 2020
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
*/
#include "boot/s390.h" /* for PAGE_SIZE */
#define ZIPL_ENVBLK_SIGNATURE "# zIPL Environment Block\n"
#define ENVBLK_DEFAULT_IMPORT_SOURCE "/etc/ziplenv"
/*
* The following limit can be increased. It requires modifications
* of the code, which is responsible for handling environment in
* stage3, which will increase its memory footprint.
*/
#define ENVBLK_MAX_LINES (PAGE_SIZE / 8 /* sizeof(struct env_hash_entry */)
#define ENVBLK_MAX_IMPORT_SIZE(envblk_size) \
(envblk_size - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1)
int envblk_offset_get(int fd, off_t *off);
int envblk_offset_set(int fd, off_t off);
int envblk_size_get(int fd, int *size);
int envblk_check_name(char *name, int len);
int envblk_import(char *from, char *to, int size);
char *envblk_next_line(char *s, const char *end);
int envblk_scan(char *envblk, unsigned int envblk_size,
void (*actor)(char *name));
void envblk_print(char *envblk, unsigned int envblk_size);
void envblk_create_blank(char *envblk, int envblk_len);

View File

@@ -46,9 +46,15 @@ struct job_ipl_data {
address_t image_addr;
address_t parm_addr;
address_t ramdisk_addr;
address_t envblk_addr;
int is_kdump;
};
struct job_envblk_data {
int size;
char *buf;
};
struct job_segment_data {
char* segment;
address_t segment_addr;
@@ -108,6 +114,7 @@ struct job_menu_data {
struct job_data {
enum job_id id;
struct job_target_data target;
struct job_envblk_data envblk;
char* name;
union {
struct job_ipl_data ipl;

View File

@@ -138,6 +138,11 @@ the target directory. Supported devices are DASD and SCSI disks.
It is not possible to specify both this parameter and the name of a menu
or configuration section on the command line at the same time.
.TP
.BR "\-\-environment=<FILE>"
Import zIPL environment from specified <FILE>. If none is supplied,
it will be imported from "/etc/ziplenv".
.TP
.BR "\-\-targetbase=<BASE DEVICE>"
Install the actual boot loader on the device node specified by BASE DEVICE.

View File

@@ -10,7 +10,7 @@ ALL_LDFLAGS += -Wl,-z,noexecstack $(NO_PIE_LDFLAGS)
libs = $(rootdir)/libutil/libutil.a
objects = misc.o error.o scan.o job.o boot.o bootmap.o disk.o \
install.o zipl.o $(rootdir)/zipl/boot/data.o
bootmap_header.o envblk.o install.o zipl.o $(rootdir)/zipl/boot/data.o
zipl_helpers = $(basename $(wildcard zipl_helper.*.c))
chreipl_helpers = $(subst zipl_,chreipl_, $(zipl_helpers))

View File

@@ -82,7 +82,8 @@ int
boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
address_t initrd_addr, size_t initrd_len,
address_t entry, int extra_parm, uint64_t flags,
address_t image_addr, size_t image_len)
address_t image_addr, size_t image_len,
address_t envblk_addr, size_t envblk_len)
{
struct stage3_parms params;
void* data;
@@ -106,6 +107,8 @@ boot_get_stage3_parms(void **buffer, size_t *bytecount, address_t parm_addr,
params.flags = flags;
params.image_len = (uint64_t) image_len;
params.image_addr = (uint64_t) image_addr;
params.envblk_addr = (uint64_t) envblk_addr;
params.envblk_len = (uint64_t) envblk_len;
/* Initialize buffer */
memcpy(data, &params, sizeof(params));
*buffer = data;

View File

@@ -27,19 +27,15 @@
#include "boot.h"
#include "bootmap.h"
#include "envblk.h"
#include "disk.h"
#include "error.h"
#include "install.h"
#include "misc.h"
/* Header text of the bootmap file */
static const char header_text[] = "zSeries bootmap file\n"
"created by zIPL\n";
/* Pointer to dedicated empty block in bootmap. */
disk_blockptr_t empty_block;
/* Get size of a bootmap block pointer for disk with given INFO. */
static int
get_blockptr_size(struct disk_info* info)
@@ -522,8 +518,9 @@ check_remaining_filesize(size_t filesize, size_t signature_size,
}
}
static int
add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
static int add_ipl_program(int fd, char *filename,
bool add_envblk, struct job_envblk_data *envblk,
struct job_ipl_data *ipl, disk_blockptr_t *program,
int verbose, int add_files, component_header_type type,
struct disk_info* info, struct job_target_data* target,
int is_secure)
@@ -533,12 +530,13 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
size_t ramdisk_size, image_size;
bool secure_boot_supported;
size_t stage3_params_size;
const char *comp_name[10];
const char *comp_name[11];
size_t signature_size;
int offset;
uint64_t flags = 0;
void *stage3_params;
struct stat stats;
off_t envblk_off;
void *signature;
int comp_nr = 0;
void *table;
@@ -676,7 +674,8 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
ipl->is_kdump ? IMAGE_ENTRY_KDUMP :
IMAGE_ENTRY,
(info->type == disk_type_scsi) ? 0 : 1,
flags, ipl->image_addr, image_size);
flags, ipl->image_addr, image_size,
ipl->envblk_addr, envblk->size);
if (rc) {
free(table);
return rc;
@@ -769,8 +768,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
comp_name[comp_nr] = "parmline";
comp_nr++;
}
/* finally add ramdisk */
/* add ramdisk */
if (ipl->ramdisk != NULL) {
signature_size = extract_signature(ipl->ramdisk, &signature,
&sig_head);
@@ -815,6 +813,65 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program,
comp_name[comp_nr] = "initial ramdisk";
comp_nr++;
}
if (add_envblk == true) {
/*
* finally add environment block
*/
rc = envblk_offset_get(fd, &envblk_off);
if (rc) {
free(table);
return rc;
}
if (envblk_off == 0) {
/*
* write with fs_block_size alignment to make sure that the
* logical environment block will get to single file system
* block
*/
rc = add_component_buffer_align(fd,
envblk->buf, envblk->size,
(component_data)ipl->envblk_addr,
VOID_ADD(table, offset),
info, &comp_loc[comp_nr],
component_load,
info->fs_block_size,
&envblk_off);
if (rc) {
error_text("Could not add environment block");
free(table);
return rc;
}
assert(envblk_off % info->fs_block_size == 0);
/*
* store environment block location
* in the bootmap header
*/
rc = envblk_offset_set(fd, envblk_off);
if (rc) {
error_text("Could not store environment block location");
free(table);
return rc;
}
} else {
struct file_range reg;
reg.offset = envblk_off;
reg.len = envblk->size;
rc = add_component_file_range(fd, filename, &reg,
ipl->envblk_addr, 0,
VOID_ADD(table, offset),
0, info, target,
&comp_loc[comp_nr]);
if (rc) {
error_text("Could not add environment block");
free(table);
return rc;
}
}
offset += sizeof(struct component_entry);
comp_name[comp_nr] = "environment blk";
comp_nr++;
}
if (verbose)
print_components(comp_name, comp_loc, comp_nr);
/* Terminate component table */
@@ -962,7 +1019,7 @@ add_dump_program(int fd, struct job_dump_data* dump,
if (rc)
return rc;
ipl.parm_addr = dump->parm_addr;
return add_ipl_program(fd, &ipl, program, verbose, 1,
return add_ipl_program(fd, NULL, false, NULL, &ipl, program, verbose, 1,
type, info, target, SECURE_BOOT_DISABLED);
}
@@ -970,8 +1027,8 @@ add_dump_program(int fd, struct job_dump_data* dump,
/* Build a program table from job data and set pointer to program table
* block upon success. */
static int
build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
struct disk_info* info)
build_program_table(int fd, char *filename, struct job_data *job,
disk_blockptr_t *pointer, struct disk_info *info)
{
disk_blockptr_t* table;
int entries, component_header;
@@ -985,6 +1042,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
entries);
if (table == NULL)
return -1;
memset((void *) table, 0, sizeof(disk_blockptr_t) * entries);
/* Add programs */
switch (job->id) {
@@ -998,8 +1056,9 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
component_header = component_header_dump;
else
component_header = component_header_ipl;
rc = add_ipl_program(fd, &job->data.ipl, &table[0],
verbose || job->command_line,
rc = add_ipl_program(fd, filename,
true, &job->envblk, &job->data.ipl,
&table[0], verbose || job->command_line,
job->add_files, component_header,
info, &job->target, job->is_secure);
break;
@@ -1052,7 +1111,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
else
is_secure =
job->data.menu.entry[i].is_secure;
rc = add_ipl_program(fd,
rc = add_ipl_program(fd, filename,
true, &job->envblk,
&job->data.menu.entry[i].data.ipl,
&table[job->data.menu.entry[i].pos],
verbose || job->command_line,
@@ -1086,6 +1146,9 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer,
rc = -1;
break;
}
if (job->envblk.buf && verbose)
envblk_print(job->envblk.buf, job->envblk.size);
if (rc == 0) {
/* Add program table block */
rc = add_program_table(fd, table, entries, pointer, info);
@@ -1244,9 +1307,9 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
scsi_sb.dump_size = unused_size;
}
/* Write bootmap header */
if (misc_write(fd, header_text, sizeof(header_text))) {
error_text("Could not write to file '%s'", filename);
/* Initialize bootmap header */
if (bootmap_header_init(fd)) {
error_text("Could not init bootmap header at '%s'", filename);
goto out_misc_free_temp_dev;
}
/* Write empty block to be read in place of holes in files */
@@ -1255,7 +1318,7 @@ bootmap_create(struct job_data *job, disk_blockptr_t *program_table,
goto out_misc_free_temp_dev;
}
/* Build program table */
if (build_program_table(fd, job, program_table, info))
if (build_program_table(fd, filename, job, program_table, info))
goto out_misc_free_temp_dev;
if (job->id == job_dump_partition) {
scsi_sb.magic = SCSI_DUMP_SB_MAGIC;

64
zipl/src/bootmap_header.c Normal file
View File

@@ -0,0 +1,64 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to manipulate with bootmap header
*
* Copyright IBM Corp. 2021
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <string.h>
#include "bootmap.h"
#include "error.h"
#include "misc.h"
int bootmap_header_init(int fd)
{
struct bootmap_header bh = {
.header_text =
"zSeries bootmap file\n"
"created by zIPL\n",
.version = BOOTMAP_HEADER_VERSION
};
return misc_write(fd, &bh, sizeof(bh));
}
static int bootmap_header_access(int fd, struct bootmap_header *bh, int read)
{
off_t cur_off;
int ret;
cur_off = lseek(fd, 0, SEEK_CUR);
if (cur_off == -1) {
error_reason(strerror(errno));
return -1;
}
if (lseek(fd, 0, SEEK_SET) == -1) {
error_reason(strerror(errno));
ret = -1;
goto out;
}
if (read)
ret = misc_read(fd, bh, sizeof(*bh));
else
ret = misc_write(fd, bh, sizeof(*bh));
out:
if (lseek(fd, cur_off, SEEK_SET) == -1)
return -1;
return ret;
}
int bootmap_header_read(int fd, struct bootmap_header *bh)
{
return bootmap_header_access(fd, bh, 1);
}
int bootmap_header_write(int fd, struct bootmap_header *bh)
{
return bootmap_header_access(fd, bh, 0);
}

View File

@@ -23,9 +23,10 @@
#include <unistd.h>
#include <linux/fs.h>
#include <linux/fiemap.h>
#include <assert.h>
#include "lib/util_proc.h"
#include "lib/util_sys.h"
#include "disk.h"
#include "error.h"
#include "install.h"
@@ -1085,9 +1086,9 @@ disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
}
/**
* Retrieve a list of pointers to the disk blocks that make up the whole
* file specified by FILENAME, or only its part specified by REG (if the
* last one is not NULL).
* Retrieve a list of pointers to the disk blocks that make up a continuous
* region REG in a file specified by FILENAME. If REG is NULL, then retrieve
* a list of pointers for the whole file.
* Upon success, return the number of blocks and set BLOCKLIST to point to
* the uncompacted list. INFO provides information about the device which
* contains the file. Return zero otherwise
@@ -1121,18 +1122,19 @@ disk_get_blocklist_from_file(const char *filename, struct file_range *reg,
return 0;
}
if (reg) {
/*
* case of not block-aligned offsets is not implemented
*/
assert(reg->offset % info->phy_block_size == 0);
off = reg->offset;
count = reg->len;
} else {
off = 0;
count = stats.st_size;
}
if (off >= stats.st_size) {
error_reason("Offset %llu is outside the file '%s'",
off, filename);
close(fd);
return 0;
}
assert(off < stats.st_size);
if (off + count > (size_t)stats.st_size)
count = stats.st_size - off;

452
zipl/src/envblk.c Normal file
View File

@@ -0,0 +1,452 @@
/*
* zipl - zSeries Initial Program Loader tool
*
* Functions to manipulate with environment block
*
* Copyright IBM Corp. 2020
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bootmap.h"
#include "envblk.h"
#include "error.h"
#include "misc.h"
/* from linux/fs.h */
#define FIGETBSZ _IO(0x00, 2)
void envblk_create_blank(char *envblk, int envblk_len)
{
memcpy(envblk, ZIPL_ENVBLK_SIGNATURE,
sizeof(ZIPL_ENVBLK_SIGNATURE) - 1);
memset(envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1, '\0',
envblk_len - sizeof(ZIPL_ENVBLK_SIGNATURE) + 1);
}
/**
* Find out environment block location.
* FD: bootmap file descriptor.
* On success the searched location is saved in variable pointed out by OFF
*/
int envblk_offset_get(int fd, off_t *off)
{
struct bootmap_header bh;
if (bootmap_header_read(fd, &bh)) {
error_reason("Could not read bootmap_header");
return -1;
}
*off = bh.envblk_offset;
return 0;
}
/**
* Save environment block location specified by OFF
* FD: bootmap file descriptor.
* On success the location is saved in the bootmap header.
*/
int envblk_offset_set(int fd, off_t off)
{
struct bootmap_header bh;
if (bootmap_header_read(fd, &bh)) {
error_reason("Could not read bootmap header");
return -1;
}
bh.envblk_offset = off;
if (bootmap_header_write(fd, &bh)) {
error_reason("Could not write bootmap header");
return -1;
}
return 0;
}
/**
* Find out environment block size.
* FD: bootmap file descriptor.
* On success the searched size is stored in RESULT
*/
int envblk_size_get(int fd, int *result)
{
if (ioctl(fd, FIGETBSZ, result) == -1) {
error_reason(strerror(errno));
return -1;
}
return 0;
}
#define LINE_HASH_SIZE (16)
/**
* Calculate hash function of "environment line"
*
* LINE: pointer to a string, containing NANE=VALUE
* LEN: length of the NAME with trailing delimiter "="
*/
static unsigned int linehash(char *line, int len, unsigned int hash_size)
{
unsigned int sum = 0;
while (len) {
sum += 5 * *(line++);
len--;
}
return sum % hash_size;
}
struct line_hash_entry {
struct line_hash_entry *next;
char *line; /* pointer to an "environment line",
* a string containing NAME=VALUE with
* optional trailing end-of-line symbol
*/
};
/**
* Check if @name meets the requirements for shell environment variables
* (IEEE Std 1003.1-2001), mitigated with lowercase letter allowance
*
* Return 0, if it meets, and 1 otherwise
*/
int envblk_check_name(char *name, int len)
{
int i;
if (len == 0)
return 1;
if (!isalpha(name[0]) && name[0] != '_')
return 1;
for (i = 1; i < len; i++)
if (!isalpha(name[i]) && !isdigit(name[i]) && name[i] != '_')
return 1;
return 0;
}
/**
* Add environment line to hash table
*
* LINE: pointer to a string containing NAME=VALUE
* LEN: length of NAME with trailing delimiter "="
*/
static int hash_table_add(struct line_hash_entry **table, char *line, int len)
{
unsigned int hash;
struct line_hash_entry *new;
new = malloc(sizeof(*new));
if (!new)
return -ENOMEM;
memset(new, 0, sizeof(*new));
new->line = misc_strdup(line);
if (!new->line) {
free(new);
return -ENOMEM;
}
hash = linehash(new->line, len, LINE_HASH_SIZE);
new->next = table[hash];
table[hash] = new;
return 0;
}
/**
* Find environment line in the hash table by name
*
* LINE: string contailing NAME=VALUE
* LEN: length of the NAME with trailing delimiter "="
*/
struct line_hash_entry *hash_table_find(struct line_hash_entry **table,
char *name, size_t len)
{
struct line_hash_entry *item;
item = table[linehash(name, len, LINE_HASH_SIZE)];
while (item) {
if (!strncmp(item->line, name, len))
return item;
item = item->next;
}
return NULL;
}
static int hash_entry_replace(struct line_hash_entry *item, char *name,
int *offset)
{
char *dup;
dup = misc_strdup(name);
if (!dup)
return -ENOMEM;
*offset -= strlen(item->line);
free(item->line);
item->line = dup;
*offset += strlen(item->line);
return 1;
}
/**
* Scan hash table TABLE, and call ACTOR for each its entry.
* The ACTOR is allowed even to release the entry.
*/
static void hash_table_scan(struct line_hash_entry **table,
void (*actor)(struct line_hash_entry *entry))
{
int i;
for (i = 0; i < LINE_HASH_SIZE; i++) {
struct line_hash_entry *item = table[i];
while (item) {
struct line_hash_entry *next = item->next;
actor(item);
item = next;
}
}
}
static void hash_entry_free(struct line_hash_entry *this)
{
free(this->line);
free(this);
}
static void hash_table_free(struct line_hash_entry **table)
{
hash_table_scan(table, hash_entry_free);
}
/**
* Scan hash table TABLE, and for each its entry copy its line to the
* in-memory environment block specified by DST at the offset OFF.
* Insert end-of-line symbol, if needed. The caller should guarantee
* enough space in the buffer DST
*/
static void hash_table_flush(struct line_hash_entry **table, char *dst,
int off)
{
int i;
for (i = 0; i < LINE_HASH_SIZE; i++) {
struct line_hash_entry *item = table[i];
while (item) {
int str_len;
str_len = strlen(item->line);
memcpy(dst + off, item->line, str_len);
off += str_len;
if (dst[off - 1] != '\n') {
dst[off] = '\n';
off += 1;
}
item = item->next;
}
}
}
/**
* Put a zIPL environment line into hash table.
*
* Identify a name in the line THIS which contains a pair NAME=VALUE, and
* look for an entry in the hash table TABLE with a similar name. If such
* entry exists, then replace the line in that entry with the new one
* specified by THIS. Othewise, insert a new entry to the hash table.
*
* THIS: pointer to a NULL-terminated string, which contains the line to
* be imported. If the line was successfully imported, and no line with the
* same NAME was added before, then return 0.
* If the line should be ignored, or it replaced previously added line with
* the same NAME, then return > 0. On errors return < 0.
*/
static int import_one_line(char *this, struct line_hash_entry **table,
int *offset)
{
struct line_hash_entry *item;
char *p;
if (*this == '#')
/* Ignore "commented" line */
return 1;
p = strchr(this, '=');
if (p == NULL || p == this)
/* Could not identify NAME in the line. Ignore */
return 1;
if (envblk_check_name(this, p - this)) {
error_reason("Unacceptable name '%.*s'", p - this, this);
return -EINVAL;
}
/*
* Try to find an entry with similar NAME.
*/
item = hash_table_find(table, this, p - this + 1);
return item != NULL ? hash_entry_replace(item, this, offset) :
hash_table_add(table, this, p - this + 1);
}
/**
* Import environment line-by-line from a regular file specified
* by its name FROM_FILE to a pre-allocated buffer TO_BUF. Ignore
* malformed, or "commented" lines. If the file doesn't end with
* end-of-line symbol, then append that symbol in the destination
* buffer. Return 0 on success.
*
* ENVBLK_SIZE: size of the destination buffer TO_BUF.
*/
int envblk_import(char *from_file, char *to_buf, int envblk_size)
{
const char *err_prefix = "Failed to import environment file";
struct line_hash_entry *table[LINE_HASH_SIZE];
unsigned int lines_imported = 0;
char *line = NULL;
size_t len = 0;
FILE *stream;
ssize_t read;
int ret = 0;
int offset;
stream = fopen(from_file, "r");
if (stream == NULL)
return 0;
memset(table, 0, LINE_HASH_SIZE*sizeof(void *));
offset = sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
while ((read = getline(&line, &len, stream)) != -1) {
if (read > envblk_size - offset) {
error_reason("%s %s: maximal size (%d) exceeded",
err_prefix, from_file,
ENVBLK_MAX_IMPORT_SIZE(envblk_size));
ret = -EINVAL;
break;
}
if (read < 2)
/* malformed line. Ignored */
continue;
if (line[read - 1] != '\n' && read == envblk_size - offset) {
/*
* file's tail doesn't contain end-of-line symbol at
* the end, whereas there is no enough space in the
* destination buffer to insert that symbol, which is
* mandatory according to the environment block format
*/
error_reason("%s %s: maximal size (%d) exceeded",
err_prefix, from_file,
ENVBLK_MAX_IMPORT_SIZE(envblk_size));
ret = -EINVAL;
break;
}
ret = import_one_line(line, table, &offset);
if (ret < 0) {
/* error */
error_text("%s %s", err_prefix, from_file);
break;
} else if (ret > 0) {
/*
* either this line was ignored, or previously
* imported line was overwritten by it
*/
ret = 0;
continue;
}
if (lines_imported >= ENVBLK_MAX_LINES) {
error_reason("%s %s: maximum number of lines (%d) exceeded",
err_prefix, from_file, ENVBLK_MAX_LINES);
ret = -EINVAL;
break;
}
offset += read;
lines_imported++;
}
hash_table_flush(table, to_buf, sizeof(ZIPL_ENVBLK_SIGNATURE) - 1);
hash_table_free(table);
free(line);
fclose(stream);
return ret;
}
char *envblk_next_line(char *s, const char *end)
{
while (s < end) {
if (*s == '\n')
break;
s++;
}
return s + 1;
}
/**
* Scan installed environment block and call ACTOR for each found NAME.
* Return -1 on corrupted environment blocks. Otherwise, return 0
*/
int envblk_scan(char *envblk, unsigned int envblk_size,
void (*actor)(char *name))
{
unsigned int lines_scanned = 0;
const char *reason;
char *s, *end;
char *name;
s = envblk + sizeof(ZIPL_ENVBLK_SIGNATURE) - 1;
end = envblk + envblk_size;
while (s < end && *s != 0) {
if (lines_scanned > ENVBLK_MAX_LINES) {
reason = "maximum number of lines exceeded";
goto corrupted;
}
name = s;
while (s < end && *s != '=')
s++;
if (s == end) {
/* delimiter "=" not found */
reason = "missed delimiter";
goto corrupted;
}
/* here @s points to "=" */
s++;
while (s < end && *s != '\n')
s++;
if (s == end) {
/* end of line not found */
reason = "missed EOL";
goto corrupted;
}
*s = '\0';
actor(name);
*s = '\n';
lines_scanned++;
s = envblk_next_line(s, end);
}
return 0;
corrupted:
error_reason("Found corrupted environment block (%s) - please run zipl",
reason);
return -1;
}
static void print_name_value(char *this)
{
printf(" %s\n", this);
}
void envblk_print(char *envblk, unsigned int envblk_size)
{
printf("Environment block content:\n");
envblk_scan(envblk, envblk_size, print_name_value);
}

View File

@@ -14,6 +14,7 @@
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -26,6 +27,7 @@
#include "misc.h"
#include "scan.h"
#include "zipl.h"
#include "envblk.h"
/* Command line options */
static struct option options[] = {
@@ -37,6 +39,7 @@ static struct option options[] = {
{ "targetgeometry", required_argument, NULL, 0xac},
{ "targetblocksize", required_argument, NULL, 0xad},
{ "targetoffset", required_argument, NULL, 0xae},
{ "environment", required_argument, NULL, 0xaf},
{ "image", required_argument, NULL, 'i'},
{ "ramdisk", required_argument, NULL, 'r'},
{ "parmfile", required_argument, NULL, 'p'},
@@ -73,6 +76,7 @@ static const char *zipl_conf[] = {
struct command_line {
char* data[SCAN_KEYWORD_NUM];
char* config;
char *envblk_import_hint;
char *blsdir;
char* menu;
char* section;
@@ -87,7 +91,6 @@ struct command_line {
enum scan_section_type type;
};
static int
store_option(struct command_line* cmdline, enum scan_keyword_id keyword,
char* value)
@@ -211,6 +214,13 @@ get_command_line(int argc, char* argv[], struct command_line* line)
rc = store_option(&cmdline, scan_keyword_targetoffset,
optarg);
break;
case 0xaf:
if (cmdline.envblk_import_hint != NULL) {
error_reason("Option 'environment' specified more than once");
rc = -1;
} else
cmdline.envblk_import_hint = optarg;
break;
case 'T':
is_keyword = 1;
rc = store_option(&cmdline, scan_keyword_tape,
@@ -351,6 +361,12 @@ free_target_data(struct job_target_data* data)
free(data->targetbase);
}
static void
free_envblk_data(struct job_envblk_data *data)
{
free(data->buf);
}
static void
free_ipl_data(struct job_ipl_data* data)
{
@@ -444,6 +460,7 @@ job_free(struct job_data* job)
if (job->target.bootmap_dir != NULL)
free(job->target.bootmap_dir);
free_target_data(&job->target);
free_envblk_data(&job->envblk);
if (job->name != NULL)
free(job->name);
switch (job->id) {
@@ -480,7 +497,7 @@ struct component_loc {
};
static int
set_cl_element(struct component_loc *cl, char *name, char *filename,
set_cl_element(struct component_loc *cl, char *name, const char *filename,
address_t *addrp, size_t size, off_t off, off_t align)
{
struct stat stats;
@@ -528,17 +545,16 @@ sort_cl_array(struct component_loc* cl, int elements)
}
}
}
static int
get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
int *nump)
int *nump, struct job_envblk_data *envblk)
{
struct component_loc *cl;
int num;
int rc;
/* Get memory for image, parmline, ramdisk */
cl = misc_calloc(3, sizeof(struct component_loc));
/* Get memory for image, parmline, ramdisk and environment block */
cl = misc_calloc(4, sizeof(struct component_loc));
if (cl == NULL)
return -1;
/* Fill in component data */
@@ -561,6 +577,12 @@ get_ipl_components(struct job_ipl_data *ipl, struct component_loc **clp,
if (rc)
goto error;
}
rc = set_cl_element(&cl[num++], "environment block",
NULL,
&ipl->envblk_addr, envblk->size, 0,
MAXIMUM_PHYSICAL_BLOCKSIZE);
if (rc)
goto error;
*clp = cl;
*nump = num;
@@ -753,7 +775,8 @@ finalize_component_address_data(struct component_loc *cl, int num,
static int
finalize_ipl_address_data(struct job_ipl_data* ipl, char *name)
finalize_ipl_address_data(struct job_ipl_data *ipl, char *name,
struct job_envblk_data *envblk)
{
unsigned long address_limit;
struct component_loc *cl;
@@ -762,7 +785,7 @@ finalize_ipl_address_data(struct job_ipl_data* ipl, char *name)
address_limit = ipl->is_kdump ?
MIN(util_arch_hsa_maxsize(), ADDRESS_LIMIT) : ADDRESS_LIMIT;
rc = get_ipl_components(ipl, &cl, &num);
rc = get_ipl_components(ipl, &cl, &num, envblk);
if (rc)
return rc;
sort_cl_array(cl, num);
@@ -828,7 +851,8 @@ out_free:
static int
check_job_ipl_data(struct job_ipl_data *ipl, char* name)
check_job_ipl_data(struct job_ipl_data *ipl, char *name,
struct job_envblk_data *envblk)
{
int rc;
@@ -856,7 +880,7 @@ check_job_ipl_data(struct job_ipl_data *ipl, char* name)
return rc;
}
}
return finalize_ipl_address_data(ipl, name);
return finalize_ipl_address_data(ipl, name, envblk);
}
@@ -935,7 +959,7 @@ check_job_dump_images(struct job_dump_data* dump, char* name)
static int
check_job_menu_data(struct job_menu_data* menu)
check_job_menu_data(struct job_menu_data *menu, struct job_envblk_data *envblk)
{
int rc;
int i;
@@ -944,7 +968,8 @@ check_job_menu_data(struct job_menu_data* menu)
switch (menu->entry[i].id) {
case job_ipl:
rc = check_job_ipl_data(&menu->entry[i].data.ipl,
menu->entry[i].name);
menu->entry[i].name,
envblk);
if (rc)
return rc;
break;
@@ -1110,10 +1135,11 @@ check_job_data(struct job_data* job)
rc = 0;
break;
case job_ipl:
rc = check_job_ipl_data(&job->data.ipl, job->name);
rc = check_job_ipl_data(&job->data.ipl, job->name,
&job->envblk);
break;
case job_menu:
rc = check_job_menu_data(&job->data.menu);
rc = check_job_menu_data(&job->data.menu, &job->envblk);
break;
case job_segment:
rc = check_job_segment_data(&job->data.segment, job->name);
@@ -1372,6 +1398,9 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section)
&job->data.ipl.parm_addr, section);
if (rc)
return rc;
/* Fill in environment block */
job->data.ipl.envblk_addr = UNSPECIFIED_ADDRESS;
/* Fill in name and address of ramdisk file */
if (data[(int) scan_keyword_ramdisk] != NULL) {
job->data.ipl.ramdisk =
@@ -1898,6 +1927,45 @@ get_job_from_config_file(struct command_line* cmdline, struct job_data* job)
return rc;
}
static int get_job_envblk_data(struct job_data *job, char *import_hint)
{
struct job_envblk_data *data = &job->envblk;
int fd;
switch (job->id) {
case job_ipl:
case job_menu:
break;
default:
return 0;
}
fd = open(job->target.bootmap_dir, O_RDONLY);
if (fd < 0) {
error_reason(strerror(errno));
error_text("Could not open bootmap dir");
return -1;
}
if (envblk_size_get(fd, &data->size)) {
close(fd);
error_text("Could not get environment block size");
return -1;
}
close(fd);
data->buf = misc_malloc(data->size);
if (data->buf == NULL) {
error_text("Could not allocate environment block");
return -1;
}
envblk_create_blank(data->buf, data->size);
if (envblk_import(import_hint ?: ENVBLK_DEFAULT_IMPORT_SOURCE,
data->buf, data->size)) {
free(data->buf);
data->buf = NULL;
return -1;
}
return 0;
}
int
job_get(int argc, char* argv[], struct job_data** data)
@@ -1946,6 +2014,11 @@ job_get(int argc, char* argv[], struct job_data** data)
else if (job->id != job_menu && job->is_secure == SECURE_BOOT_UNDEFINED)
job->is_secure = SECURE_BOOT_AUTO;
rc = get_job_envblk_data(job, cmdline.envblk_import_hint);
if (rc) {
job_free(job);
return -1;
}
/* Check job data for validity */
rc = check_job_data(job);
if (rc) {