Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Please login to access the resource
SUSE:SLE-15-SP1:GA
s390-tools
s390-tools-sles15sp1-03-zdev-Add-support-for-re...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File s390-tools-sles15sp1-03-zdev-Add-support-for-reading-firmware-configuration-.patch of Package s390-tools
Subject: zdev: Add support for reading firmware configuration files From: Peter Oberparleiter <oberpar@linux.ibm.com> Summary: zdev: Add support for handling I/O configuration data Description: LPARs that are running in IBM Dynamic Partition Manager (DPM) mode can access a firmware-generated I/O configuration data file that contains s390-specific information about available I/O devices such as qeth device numbers and parameters, and FCP device IDs. This data file is intended to remove the need for users to manually enter the corresponding device data during installation. Linux kernels with the corresponding support make the I/O configuration data available at the following location: /sys/firmware/sclp_sd/config/data This patch set adds support for handling this data file using the chzdev and lszdev tools: - I/O configuration data can be applied using chzdev's --import option - Initial RAM-Disk scripts automatically apply the I/O configuration data to the system configuration - lszdev can be used to display the applied auto-configuration data - chzdev can be used to manually override the auto-configuration data Upstream-ID: 7d355b0fec964ad84ecaf88eb946121d39486070 Problem-ID: LS1604 Upstream-Description: zdev: Add support for reading firmware configuration files Add support for reading firmware-provided I/O configuration data files. Such configuration files are generated by the Dynamic Partition Manager and made available via a kernel interface for consumption by Linux. To read a firmware configuration file, use the existing --import option: # chzdev --import /sys/firmware/sclp_sd/config/data This will apply all I/O configuration data found in the specified file to the persistent configuration. Signed-off-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com> Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com> --- zdev/include/firmware.h | 25 + zdev/man/chzdev.8 | 16 zdev/src/Makefile | 2 zdev/src/chzdev.c | 21 - zdev/src/firmware.c | 676 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 730 insertions(+), 10 deletions(-) --- /dev/null +++ b/zdev/include/firmware.h @@ -0,0 +1,25 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * Copyright IBM Corp. 2017 + * + * s390-tools is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include <stdio.h> +#include <stdbool.h> + +#include "exit_code.h" +#include "misc.h" + +struct util_list; + +bool firmware_detect(FILE *fd); +exit_code_t firmware_read(FILE *fd, const char *filename, long skip, + config_t config, struct util_list *objects); + +#endif /* FIRMWARE_H */ --- a/zdev/man/chzdev.8 +++ b/zdev/man/chzdev.8 @@ -409,13 +409,23 @@ default value. .PP . .OD import "" "FILENAME" "|-" -Import configuration data from a text file. +Import configuration data from a text or machine-provided file. Reads configuration data from FILENAME and applies it. If a single hyphen ("-") is specified as FILENAME data is read from the standard input stream. The -input format of the data read must be the same format as produced by the -chzdev \-\-export action. +input format must be either in the format as produced by the chzdev \-\-export +action, or in the format of a machine-provided I/O configuration data file. +.B Machine-provided data: +Some machine models provide I/O configuration data which is made available +by the Linux kernel via a sysfs interface. While this data is intended for +automatic consumption during the boot phase, you can also apply it manually +using the \-\-import action like in the following example + +.B Example: +.CL chzdev --import /sys/firmware/sclp_sd/config/data + +.B Note: By default all configuration data that is read is also applied. To reduce the scope of imported configuration data, you can select specific devices, a device type, or define whether only data for the active or persistent configuration --- a/zdev/src/Makefile +++ b/zdev/src/Makefile @@ -8,7 +8,7 @@ ALL_CPPFLAGS += -I ../include -std=gnu99 chzdev_objects += attrib.o chzdev.o device.o devnode.o devtype.o exit_code.o \ export.o hash.o inuse.o misc.o namespace.o opts.o path.o \ root.o select.o setting.o subtype.o table.o table_attribs.o \ - table_types.o net.o + table_types.o net.o firmware.o # Devtype Helpers chzdev_objects += blkinfo.o ccw.o ccwgroup.o findmnt.o modprobe.o module.o \ --- a/zdev/src/chzdev.c +++ b/zdev/src/chzdev.c @@ -27,6 +27,7 @@ #include "devnode.h" #include "devtype.h" #include "export.h" +#include "firmware.h" #include "inuse.h" #include "misc.h" #include "module.h" @@ -2500,9 +2501,9 @@ static exit_code_t do_export(struct opti /* Open output stream. */ if (strcmp(opts->export, "-") == 0) { fd = stdout; - info("Exporting configuration data to standard output\n"); + info("Exporting data to standard output\n"); } else { - info("Exporting configuration data to %s\n", opts->export); + info("Exporting data to %s\n", opts->export); if (!util_path_exists(opts->export)) { rc = path_create(opts->export); if (rc) @@ -2735,6 +2736,7 @@ static exit_code_t do_import(struct opti exit_code_t drc = EXIT_OK; const char *filename; int found; + bool is_firmware; /* Open input stream. */ if (strcmp(opts->import, "-") == 0) { @@ -2744,16 +2746,23 @@ static exit_code_t do_import(struct opti fd = fopen(opts->import, "r"); filename = opts->import; } - info("Importing configuration data from %s\n", filename); + if (!fd) { error("Could not open file %s: %s\n", opts->import, strerror(errno)); return EXIT_RUNTIME_ERROR; } + is_firmware = firmware_detect(fd); + info("Importing data from %s%s\n", filename, + is_firmware ? " (firmware format)" : ""); + /* Read data. */ objects = ptrlist_new(); - rc = export_read(fd, filename, objects); + if (is_firmware) + rc = firmware_read(fd, filename, -1, opts->config, objects); + else + rc = export_read(fd, filename, objects); if (rc) goto out; @@ -2766,8 +2775,8 @@ static exit_code_t do_import(struct opti "selection\n", filename); rc = EXIT_EMPTY_SELECTION; } else { - error("%s: No settings found to import\n", filename); - rc = EXIT_NO_DATA; + info("%s: No settings found to import\n", filename); + rc = EXIT_OK; } goto out; } --- /dev/null +++ b/zdev/src/firmware.c @@ -0,0 +1,676 @@ +/* + * zdev - Modify and display the persistent configuration of devices + * + * Copyright IBM Corp. 2017 + * + * 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 <err.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "attrib.h" +#include "ccw.h" +#include "ccwgroup.h" +#include "dasd.h" +#include "device.h" +#include "export.h" +#include "firmware.h" +#include "misc.h" +#include "qeth.h" +#include "subtype.h" +#include "zfcp_host.h" +#include "zfcp_lun.h" + +/* In-memory firmware file representation. */ +struct fw_file { + const char *name; + char *buffer; + size_t size; + char *last_access; + size_t last_size; +}; + +/* Record access to fields of the buffered file for use in warning messages. */ +#define fwacc(f, x) ((f)->last_access = (char *) &(x), \ + (f)->last_size = sizeof(x), x) + +/* + * Firmware file format definitions. + */ + +/* Firmware file header. */ +struct fw_filehdr { + uint32_t magic; + uint16_t ver; + uint16_t hdr_len; + uint32_t file_len; + uint32_t seq; + uint32_t zeroes; + uint16_t de_count; + char unused[10]; +} __packed; + +#define FW_HDR_MAGIC 0x7a646576 /* ASCII "zdev" */ +#define FW_HDR_VER_Z14 0x0000 + +/* I/O device ID. */ +struct fw_iodevid { + uint8_t cssid; + uint8_t ssid; + uint16_t devno; +} __packed; + +#define FW_IODEVID_FLAG_MCSS 0x01 + +/* Device setting. */ +struct fw_setting { + uint16_t len; + uint8_t key_type; + uint8_t key_len; + uint8_t val_type; + uint8_t val_len; + char data[]; +} __packed; + +#define FW_SETTING_KEYTYPE_ASCII 0x00 +#define FW_SETTING_VALTYPE_ASCII 0x00 +#define FW_SETTING_VALTYPE_UINT 0x01 + +/* Device settings list. */ +struct fw_setlist { + uint16_t len; + char data[]; +} __packed; + +/* Device entry header. */ +struct fw_dehdr { + uint16_t type; + uint16_t len; + uint32_t seq; +} __packed; + +#define FW_DE_HDR_TYPE_DASD 0x0001 +#define FW_DE_HDR_TYPE_ZFCP_HOST 0x0002 +#define FW_DE_HDR_TYPE_ZFCP_LUN 0x0003 +#define FW_DE_HDR_TYPE_QETH 0x0004 + +/* DASD device entry. */ +struct fw_dasd { + struct fw_dehdr hdr; + uint8_t id_flags; + struct fw_iodevid id; + char settings[]; +} __packed; + +/* zFCP host device entry. */ +struct fw_zfcp_host { + struct fw_dehdr hdr; + uint8_t id_flags; + struct fw_iodevid id; + char settings[]; +} __packed; + +/* zFCP LUN device entry. */ +struct fw_zfcp_lun { + struct fw_dehdr hdr; + uint8_t id_flags; + struct fw_iodevid id; + uint64_t wwpn; + uint64_t fcp_lun; + char settings[]; +} __packed; + +/* QETH device entry. */ +struct fw_qeth { + struct fw_dehdr hdr; + uint8_t id_flags; + struct fw_iodevid read_id; + struct fw_iodevid write_id; + struct fw_iodevid data_id; + char settings[]; +} __packed; + +/* Emit a warning that refers to a position in a firmware file. */ +static void fwwarn(struct fw_file *f, const char *fmt, ...) +{ + va_list args; + off_t start = (off_t) (f->last_access - f->buffer), + end = start + f->last_size - 1; + + fprintf(stderr, "%s: ", f->name); + if (start == end) + fprintf(stderr, "Byte 0x%zx: ", start); + else + fprintf(stderr, "Bytes 0x%zx-0x%zx: ", start, end); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +/* Basic file format header sanity check. */ +static bool check_header(struct fw_file *f, struct fw_filehdr *hdr) +{ + if (fwacc(f, hdr->magic) != FW_HDR_MAGIC) + fwwarn(f, "Invalid file magic (0x%08x)", hdr->magic); + else if (fwacc(f, hdr->ver) != FW_HDR_VER_Z14) + fwwarn(f, "Unsupported file version (0x%04x)", hdr->ver); + else + return true; + + return false; +} + +#define READ_RETRY 3 + +/* Read a firmware configuration file. */ +static exit_code_t read_fw(struct fw_file *file, FILE *fd, const char *filename) +{ + struct fw_file f = { NULL }; + struct fw_filehdr *hdr; + char *buffer, *buffer2; + size_t size, size2; + int retry; + exit_code_t rc = EXIT_OK; + + for (retry = 0; retry < READ_RETRY; retry++) { + /* Read complete file once */ + rc = misc_read_fd(fd, (void **) &buffer, &size); + if (rc) { + warn("%s: Could not read file", filename); + return rc; + } + if (!buffer) { + /* Empty file - skip silently as this is the default + * on machines without firmware support. */ + return rc; + } + + /* Re-read complete file to detect in-flight modifications. */ + if (fseek(fd, 0, SEEK_SET) == -1) { + /* Could be a pipe, socket, or FIFO - accept v1. */ + break; + } + rc = misc_read_fd(fd, (void **) &buffer2, &size2); + if (rc || !buffer2) { + /* Could not get second version - accept v1. */ + break; + } + + if (size == size2 && memcmp(buffer, buffer2, size) == 0) { + /* No change */ + free(buffer2); + break; + } + + free(buffer); + free(buffer2); + } + + if (retry >= READ_RETRY) { + warnx("%s: File changed %d times while reading - aborting", + filename, retry); + return EXIT_RUNTIME_ERROR; + } + + /* Perform basic checks */ + f.name = filename; + f.buffer = buffer; + f.size = size; + hdr = (void *) buffer; + if (!check_header(&f, hdr)) { + free(buffer); + return EXIT_FORMAT_ERROR; + } + if (fwacc(&f, hdr->file_len) > size) { + fwwarn(&f, "File length mismatch (expect %zu) - adjusting", + size); + hdr->file_len = size; + } + + *file = f; + + return EXIT_OK; +} + +/* Return textual representation of a device entry type. */ +static const char *type_to_str(uint16_t type) +{ + switch (type) { + case FW_DE_HDR_TYPE_DASD: + return "dasd"; + case FW_DE_HDR_TYPE_ZFCP_HOST: + return "zfcp-host"; + case FW_DE_HDR_TYPE_ZFCP_LUN: + return "zfcp-lun"; + case FW_DE_HDR_TYPE_QETH: + return "qeth"; + default: + return "<unknown>"; + } +} + +/* Convert a binary format device setting of the specified length to integer. */ +static unsigned long parse_value(char *data, uint8_t len) +{ + switch (len) { + case 1: + return (unsigned long) *((uint8_t *) data); + case 2: + return (unsigned long) *((uint16_t *) data); + case 4: + return (unsigned long) *((uint32_t *) data); + case 8: + return (unsigned long) *((uint64_t *) data); + default: + return 0; + } +} + +/* Perform sanity checks on device setting. */ +static bool check_setting(struct fw_file *f, struct fw_setting *set) +{ + /* Key sanity checks */ + if (fwacc(f, set->key_type) != FW_SETTING_KEYTYPE_ASCII) { + fwwarn(f, "Unsupported key type: %d", set->key_type); + return false; + } + if (fwacc(f, set->key_len) < 1) { + fwwarn(f, "Unsupported key length: %d", set->key_len); + return false; + } + if (sizeof(struct fw_setting) + fwacc(f, set->key_len) > set->len) { + fwwarn(f, "Key length exceeds setting"); + return false; + } + if (fwacc(f, set->data[set->key_len - 1])) { + fwwarn(f, "Key not null-terminated"); + return false; + } + + /* Value sanity checks */ + if (fwacc(f, set->val_type) != FW_SETTING_VALTYPE_UINT && + fwacc(f, set->val_type) != FW_SETTING_VALTYPE_ASCII) { + fwwarn(f, "Unsupported value type: %d", set->val_type); + return false; + } + if (fwacc(f, set->val_len) < 1) { + fwwarn(f, "Unsupported value length: %d", set->val_len); + return false; + } + if (sizeof(struct fw_setting) + set->key_len + + fwacc(f, set->val_len) > set->len) { + fwwarn(f, "Value length exceeds setting"); + return false; + } + if ((set->val_type == FW_SETTING_VALTYPE_ASCII) && + fwacc(f, set->data[set->key_len + set->val_len - 1])) { + fwwarn(f, "Value not null-terminated"); + return false; + } + if (set->val_type == FW_SETTING_VALTYPE_UINT) { + switch (fwacc(f, set->val_len)) { + case 1: + case 2: + case 4: + case 8: + break; + default: + fwwarn(f, "Unsupported integer value length: %d", + set->val_len); + return false; + } + } + + return true; +} + +/* Add a setting to the device. Emit a warning if the setting is not known. */ +static void _add_setting(const char *filename, struct device *dev, + config_t config, const char *key, const char *value) +{ + struct attrib *a; + struct setting_list *list; + + list = device_get_setting_list(dev, config); + a = attrib_find(dev->subtype->dev_attribs, key); + if (!a) { + warnx("%s: Applying unknown device setting %s=%s", filename, + key, value); + } + setting_list_apply(list, a, key, value); +} + +static void add_setting(const char *filename, struct device *dev, + config_t config, const char *key, const char *value) +{ + if (SCOPE_ACTIVE(config)) + _add_setting(filename, dev, config_active, key, value); + if (SCOPE_PERSISTENT(config)) + _add_setting(filename, dev, config_persistent, key, value); +} + +/* Parse a single device setting in firmware format and apply it to the + * specified device. */ +static void parse_setting(struct fw_file *f, struct fw_setting *set, + struct device *dev, config_t config) +{ + char *ascii_key, *ascii_val; + unsigned long ulong_val; + + if (!check_setting(f, set)) + return; + + ascii_key = &set->data[0]; + if (set->val_type == FW_SETTING_VALTYPE_UINT) { + ulong_val = parse_value(&set->data[set->key_len], set->val_len); + ascii_val = misc_asprintf("%lu", ulong_val); + add_setting(f->name, dev, config, ascii_key, ascii_val); + free(ascii_val); + } else { + ascii_val = &set->data[set->key_len]; + add_setting(f->name, dev, config, ascii_key, ascii_val); + } +} + +/* Parse a device settings list in firmware format and apply the resulting + * settings to the specified device. */ +static void parse_settings(struct fw_file *f, char *data, struct device *dev, + config_t config) +{ + struct fw_setlist *list = (struct fw_setlist *) data; + struct fw_setting *set; + uint16_t off; + + for (off = sizeof(struct fw_setlist); off < list->len; + off += set->len) { + set = (struct fw_setting *) &data[off]; + if (fwacc(f, set->len) < sizeof(struct fw_setting)) { + fwwarn(f, "Setting too short"); + break; + } + if (off + fwacc(f, set->len) > list->len) { + fwwarn(f, "Setting too long"); + break; + } + parse_setting(f, set, dev, config); + } +} + +/* Perform sanity checks on an I/O device ID. */ +static bool check_iodevid(struct fw_file *f, uint8_t *flags, + struct fw_iodevid *id) +{ + + if (fwacc(f, *flags) & FW_IODEVID_FLAG_MCSS) { + fwwarn(f, "Unsupported entry in non-default CSS"); + return false; + } + if (fwacc(f, id->cssid) != 0) { + fwwarn(f, "Non-zero CSS-ID"); + return false; + } + return true; +} + +/* Perform sanity checks on a device entry. */ +static bool check_de_size(struct fw_file *f, struct fw_dehdr *de, size_t size) +{ + if (fwacc(f, de->len) < size) { + fwwarn(f, "Device entry too short (expect %zu)", size); + return false; + } + return true; +} + +/* Convert an I/O device ID to CCW device ID format. */ +static void io_to_ccw(struct ccw_devid *c, struct fw_iodevid *i) +{ + c->cssid = i->cssid; + c->ssid = i->ssid; + c->devno = i->devno; +} + +/* Register a new device configuration. */ +static struct device *add_device(struct fw_file *f, struct subtype *st, + const char *id, config_t config, + struct util_list *objects) +{ + struct device *dev; + + if (!st->devices) + st->devices = device_list_new(st); + + dev = device_list_find(st->devices, id, NULL); + if (!dev) { + dev = device_new(st, id); + if (!dev) { + warnx("%s: Skipping invalid %s device ID %s", f->name, + st->name, id); + return NULL; + } + device_list_add(st->devices, dev); + } + ptrlist_add(objects, object_new(export_device, dev)); + + /* Prepare device for new settings. */ + if (SCOPE_ACTIVE(config)) { + setting_list_clear(dev->active.settings); + if (dev->subtype->support_definable) + dev->active.definable = 1; + else + dev->active.exists = 1; + } + if (SCOPE_PERSISTENT(config)) { + setting_list_clear(dev->persistent.settings); + dev->persistent.exists = 1; + } + + return dev; +} + +/* Parse a DASD device entry. */ +static void parse_dasd(struct fw_file *f, struct fw_dehdr *de, config_t config, + struct util_list *objects) +{ + struct fw_dasd *dasd = (struct fw_dasd *) de; + struct ccw_devid devid; + struct device *dev_eckd, *dev_fba; + char *id; + + if (!check_de_size(f, de, sizeof(struct fw_dasd))) + return; + if (!check_iodevid(f, &dasd->id_flags, &dasd->id)) + return; + + /* Could be either dasd_eckd or dasd_fba - add both entries */ + io_to_ccw(&devid, &dasd->id); + id = ccw_devid_to_str(&devid); + dev_eckd = add_device(f, &dasd_subtype_eckd, id, config, objects); + dev_fba = add_device(f, &dasd_subtype_fba, id, config, objects); + free(id); + + if (dasd->hdr.len > sizeof(struct fw_dasd)) { + if (dev_eckd) + parse_settings(f, dasd->settings, dev_eckd, config); + if (dev_fba) + parse_settings(f, dasd->settings, dev_fba, config); + } +} + +/* Parse a zFCP host device entry. */ +static void parse_zfcp_host(struct fw_file *f, struct fw_dehdr *de, + config_t config, struct util_list *objects) +{ + struct fw_zfcp_host *zfcp_host = (struct fw_zfcp_host *) de; + struct ccw_devid devid; + struct device *dev; + char *id; + + if (!check_de_size(f, de, sizeof(struct fw_zfcp_host))) + return; + if (!check_iodevid(f, &zfcp_host->id_flags, &zfcp_host->id)) + return; + + /* Add zfcp_host entry */ + io_to_ccw(&devid, &zfcp_host->id); + id = ccw_devid_to_str(&devid); + dev = add_device(f, &zfcp_host_subtype, id, config, objects); + free(id); + + if (dev && zfcp_host->hdr.len > sizeof(struct fw_zfcp_host)) + parse_settings(f, zfcp_host->settings, dev, config); +} + +/* Parse a zFCP LUN device entry. */ +static void parse_zfcp_lun(struct fw_file *f, struct fw_dehdr *de, + config_t config, struct util_list *objects) +{ + struct fw_zfcp_lun *zfcp_lun = (struct fw_zfcp_lun *) de; + struct zfcp_lun_devid devid; + struct device *dev; + char *id; + + if (!check_de_size(f, de, sizeof(struct fw_zfcp_lun))) + return; + if (!check_iodevid(f, &zfcp_lun->id_flags, &zfcp_lun->id)) + return; + + /* Add zfcp_lun entry */ + io_to_ccw(&devid.fcp_dev, &zfcp_lun->id); + devid.wwpn = zfcp_lun->wwpn; + devid.lun = zfcp_lun->fcp_lun; + id = zfcp_lun_devid_to_str(&devid); + dev = add_device(f, &zfcp_lun_subtype, id, config, objects); + free(id); + + if (dev && zfcp_lun->hdr.len > sizeof(struct fw_zfcp_lun)) + parse_settings(f, zfcp_lun->settings, dev, config); + +} + +/* Parse a QETH device entry. */ +static void parse_qeth(struct fw_file *f, struct fw_dehdr *de, config_t config, + struct util_list *objects) +{ + struct fw_qeth *qeth = (struct fw_qeth *) de; + struct ccwgroup_devid devid; + struct device *dev; + char *id; + + if (!check_de_size(f, de, sizeof(struct fw_qeth))) + return; + if (!check_iodevid(f, &qeth->id_flags, &qeth->read_id) || + !check_iodevid(f, &qeth->id_flags, &qeth->write_id) || + !check_iodevid(f, &qeth->id_flags, &qeth->data_id)) + return; + + /* Add qeth entry */ + devid.num = 3; + io_to_ccw(&devid.devid[0], &qeth->read_id); + io_to_ccw(&devid.devid[1], &qeth->write_id); + io_to_ccw(&devid.devid[2], &qeth->data_id); + id = ccwgroup_devid_to_str(&devid); + dev = add_device(f, &qeth_subtype_qeth, id, config, objects); + free(id); + + if (dev && qeth->hdr.len > sizeof(struct fw_qeth)) + parse_settings(f, qeth->settings, dev, config); +} + +/* Parse a firmware file. */ +static void parse_fw(struct fw_file *f, long skip, config_t config, + struct util_list *objects) +{ + char *data = f->buffer; + struct fw_filehdr *hdr = (struct fw_filehdr *) data; + struct fw_dehdr *de; + uint16_t count = 0; + uint32_t off; + + for (off = hdr->hdr_len; off < hdr->file_len; off += de->len) { + count++; + de = (struct fw_dehdr *) &data[off]; + if (fwacc(f, de->len) == 0) { + fwwarn(f, "Empty device entry"); + break; + } + if (off + fwacc(f, de->len) > hdr->file_len) { + fwwarn(f, "Device entry too long"); + break; + } + if (skip >= 0 && de->seq <= skip) { + debug("Skipping %s entry due to sequence (%08x)\n", + type_to_str(de->type), de->seq); + continue; + } + switch (fwacc(f, de->type)) { + case FW_DE_HDR_TYPE_DASD: + parse_dasd(f, de, config, objects); + break; + case FW_DE_HDR_TYPE_ZFCP_HOST: + parse_zfcp_host(f, de, config, objects); + break; + case FW_DE_HDR_TYPE_ZFCP_LUN: + parse_zfcp_lun(f, de, config, objects); + break; + case FW_DE_HDR_TYPE_QETH: + parse_qeth(f, de, config, objects); + break; + default: + fwwarn(f, "Unknown entry (type=%04x)", de->type); + break; + } + } + + if (count != fwacc(f, hdr->de_count)) + fwwarn(f, "Device entry count mismatch"); +} + +/* Read configuration objects from @fd in firmware file format. Add pointers to + * newly allocated struct export_objects to ptrlist @objects. If @skip is a + * positive number, skip over entries with a sequence number equal to or + * greater than @skip. */ +exit_code_t firmware_read(FILE *fd, const char *filename, long skip, + config_t config, struct util_list *objects) +{ + struct fw_file file; + exit_code_t rc; + + rc = read_fw(&file, fd, filename); + if (rc) + return rc; + + parse_fw(&file, skip, config, objects); + free(file.buffer); + + return rc; +} + +/* Check if @fd refers to a file in binary firmware format. */ +bool firmware_detect(FILE *fd) +{ + int c; + + c = fgetc(fd); + ungetc(c, fd); + + /* Note: A full check would require looking at least at the first 4 + * bytes, but fd might be non-seekable (e.g. pipe). Since there is no + * way that a textual import file can start with a 'z', looking at + * the first char should be enough. */ + + return c == 'z'; +}
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor