| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2014-2019 Siemens AG |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program 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 General Public License for more details. |
| */ |
| |
| #include <cbfs.h> |
| #include <string.h> |
| #include <console/console.h> |
| #include <device/mmio.h> |
| #include <arch/early_variables.h> |
| #include <types.h> |
| |
| #include "hwilib.h" |
| |
| #define MAX_BLOCK_NUM 4 |
| #define LEN_HIB 0x1fd |
| #define LEN_SIB 0x121 |
| #define LEN_EIB 0x0b5 |
| #define MIN_LEN_XIB 0x201 |
| #define NEXT_OFFSET_HIB 0x1dc |
| #define NEXT_OFFSET_SIB 0x104 |
| #define NEXT_OFFSET_EIB 0x0b0 |
| #define NEXT_OFFSET_XIB 0x014 |
| #define LEN_UNIQUEL_NUM 0x010 |
| #define LEN_HW_REV 0x002 |
| #define LEN_MAC_ADDRESS 0x006 |
| #define LEN_SPD 0x080 |
| #define LEN_EDID 0x080 |
| #define LEN_OFFSET 0x00c |
| #define EIB_FEATRUE_OFFSET 0x00e |
| #define LEN_MAGIC_NUM 0x007 |
| #define BLOCK_MAGIC "H1W2M3I" |
| #define HWI_MAX_NAME_LEN 32 |
| |
| /* Define all supported block types. */ |
| enum { |
| BLK_HIB, |
| BLK_SIB, |
| BLK_EIB, |
| BLK_XIB |
| }; |
| |
| /* This structure holds a valid position for a given field |
| * Every field can have multiple positions of which the first available |
| * will be taken by the library. |
| */ |
| struct param_pos { |
| uint8_t blk_type; /* Valid for a specific block type */ |
| uint32_t offset; /* Offset in given block */ |
| uint32_t len; /* Length for the field in this block */ |
| }; |
| |
| /* This structure holds all the needed information for a given field type |
| * and a pointer to a function which is able to extract the desired data. |
| */ |
| struct param_info { |
| struct param_pos pos[MAX_BLOCK_NUM]; |
| uint64_t mask; |
| uint8_t mask_offset; |
| uint32_t (*get_field)(const struct param_info *param, uint8_t *dst, |
| uint32_t maxlen); |
| }; |
| |
| /* Storage for pointers to the different blocks. The contents will be filled |
| * in hwilib_find_blocks(). |
| */ |
| static uint8_t *all_blocks[MAX_BLOCK_NUM] CAR_GLOBAL; |
| |
| /* As the length of extended block is variable, save all length to a global |
| * variable so that they can be used later to check boundaries. |
| */ |
| static uint16_t all_blk_size[MAX_BLOCK_NUM] CAR_GLOBAL; |
| |
| /* Storage for the cbfs file name of the currently open hwi file. */ |
| static char current_hwi[HWI_MAX_NAME_LEN] CAR_GLOBAL; |
| |
| |
| static uint32_t hwilib_read_bytes (const struct param_info *param, uint8_t *dst, |
| uint32_t maxlen); |
| |
| /* Add all supported fields to this variable. It is important to use the |
| * field type of a given field as the array index so that all the information |
| * is on the appropriate place inside the array. In this way one do not need |
| * to search for fields but can simply use an index into the array. |
| */ |
| static const struct param_info params[] = { |
| [HIB_VerID] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x8, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [SIB_VerID] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0x8, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [EIB_VerID] = { |
| .pos[0] = {.blk_type = BLK_EIB, .offset = 0x8, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [XIB_VerID] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x8, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [HIB_HwRev] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xbe, .len = 2}, |
| .get_field = hwilib_read_bytes }, |
| [SIB_HwRev] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0xc8, .len = 2}, |
| .get_field = hwilib_read_bytes }, |
| [HWID] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1a8, .len = 4}, |
| .pos[1] = {.blk_type = BLK_SIB, .offset = 0xd0, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [UniqueNum] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xa2, .len = 10}, |
| .pos[1] = {.blk_type = BLK_SIB, .offset = 0xa2, .len = 10}, |
| .get_field = hwilib_read_bytes }, |
| [Mac1] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xc0, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [Mac1Aux] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xc6, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [Mac2] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xc8, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [Mac2Aux] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xce, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [Mac3] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xd0, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [Mac3Aux] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xd6, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [Mac4] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xd8, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [Mac4Aux] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xde, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [SPD] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0xe0, .len = 0x80}, |
| .get_field = hwilib_read_bytes }, |
| [FF_FreezeDis] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1b8, .len = 1}, |
| .mask = 0x10, |
| .mask_offset = 4, |
| .get_field = hwilib_read_bytes }, |
| [FF_FanReq] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1b9, .len = 1}, |
| .mask = 0x04, |
| .mask_offset = 2, |
| .get_field = hwilib_read_bytes }, |
| [NvramVirtTimeDsaveReset] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1be, .len = 2}, |
| .get_field = hwilib_read_bytes }, |
| [BiosFlags] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1c0, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [MacMapping1] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1cc, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [MacMapping2] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1d0, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [MacMapping3] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1d4, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [MacMapping4] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1d8, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [RTCType] = { |
| .pos[0] = {.blk_type = BLK_HIB, .offset = 0x1e8, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [BL_Brightness] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0xd8, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [PF_PwmFreq] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0xe7, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [PF_Color_Depth] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0xea, .len = 1}, |
| .mask = 0x03, |
| .mask_offset = 0, |
| .get_field = hwilib_read_bytes }, |
| [PF_DisplType] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0xe3, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [PF_DisplCon] = { |
| .pos[0] = {.blk_type = BLK_SIB, .offset = 0xf2, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [Edid] = { |
| .pos[0] = {.blk_type = BLK_EIB, .offset = 0x10, .len = 0x80}, |
| .get_field = hwilib_read_bytes }, |
| [VddRef] = { |
| .pos[0] = {.blk_type = BLK_EIB, .offset = 0x90, .len = 2}, |
| .get_field = hwilib_read_bytes }, |
| [XMac1] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0xfc, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac1Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x102, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac2] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x114, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac2Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x11a, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac3] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x12c, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac3Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x132, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac4] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x144, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac4Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x14a, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac5] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x15c, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac5Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x162, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac6] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x174, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac6Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x17a, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac7] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x18c, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac7Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x192, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac8] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1a4, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac8Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1aa, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac9] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1bc, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac9Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1c2, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac10] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1d4, .len = 6}, |
| .get_field = hwilib_read_bytes }, |
| [XMac10Aux] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1da, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [XMac1Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0xec, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac2Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x104, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac3Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x11c, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac4Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x134, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac5Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x14c, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac6Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x164, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac7Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x17c, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac8Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x194, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac9Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1ac, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [XMac10Mapping] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1c4, .len = 16}, |
| .get_field = hwilib_read_bytes }, |
| [netKind1] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x103, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind2] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x11b, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind3] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x133, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind4] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x14b, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind5] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x163, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind6] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x17b, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind7] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x193, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind8] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1ab, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind9] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1c3, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [netKind10] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1db, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [T_Warn] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x18, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [T_Crit] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x1c, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANSamplingTime] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x20, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANSetPoint] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x24, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANKp] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x28, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANKi] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x2c, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANKd] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x30, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANHystVal] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x34, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANHystThreshold] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x38, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANHystCtrl] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x3c, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANMaxSpeed] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x40, .len = 2}, |
| .get_field = hwilib_read_bytes }, |
| [FANStartSpeed] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x42, .len = 2}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorDelay] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x44, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorNum] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x48, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorSelect] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x49, .len = 1}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg0] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x4c, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg1] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x60, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg2] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x74, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg3] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x88, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg4] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x9c, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg5] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0xb0, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg6] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0xc4, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [FANSensorCfg7] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0xd8, .len = 20}, |
| .get_field = hwilib_read_bytes }, |
| [LegacyDelay] = { |
| .pos[0] = {.blk_type = BLK_XIB, .offset = 0x20c, .len = 4}, |
| .get_field = hwilib_read_bytes }, |
| }; |
| |
| /** \brief This functions reads the given field from the first valid hwinfo |
| * block |
| * @param *param Parameter to read from hwinfo |
| * @param *dst Pointer to memory where the data will be stored in |
| * @return number of copied bytes on success, 0 on error |
| */ |
| static uint32_t hwilib_read_bytes (const struct param_info *param, uint8_t *dst, |
| uint32_t maxlen) |
| { |
| uint8_t i = 0, *blk = NULL; |
| uint8_t **blk_ptr = car_get_var_ptr(&all_blocks[0]); |
| uint16_t *all_blk_size_ptr = car_get_var_ptr(&all_blk_size[0]); |
| |
| if (!param || !dst) |
| return 0; |
| /* Take the first valid block to get the parameter from */ |
| do { |
| if ((param->pos[i].len) && (param->pos[i].offset) && |
| (blk_ptr[param->pos[i].blk_type])) { |
| blk = blk_ptr[param->pos[i].blk_type]; |
| break; |
| } |
| i++; |
| } while (i < MAX_BLOCK_NUM); |
| |
| /* Ensure there is a valid block available for this parameter and |
| * the length of the parameter do not exceed maxlen or block len. |
| */ |
| if ((!blk) || (param->pos[i].len > maxlen) || |
| (param->pos[i].len + param->pos[i].offset > |
| all_blk_size_ptr[param->pos[i].blk_type])) |
| return 0; |
| /* We can now copy the wanted data. */ |
| memcpy(dst, (blk + param->pos[i].offset), param->pos[i].len); |
| /* If there is a mask given, apply it only for parameters with a |
| * length of 1, 2, 4 or 8 bytes. |
| */ |
| if (param->mask) { |
| switch (param->pos[i].len) { |
| case 1: |
| /* Apply a mask on a 8 bit value */ |
| *dst &= (param->mask & 0xff); |
| *dst >>= (param->mask_offset); |
| break; |
| case 2: |
| /* Apply mask on a 16 bit value */ |
| *((uint16_t *)(dst)) &= (param->mask & 0xffff); |
| *((uint16_t *)(dst)) >>= (param->mask_offset); |
| break; |
| case 4: |
| /* Apply mask on a 32 bit value */ |
| *((uint32_t *)(dst)) &= (param->mask & 0xffffffff); |
| *((uint32_t *)(dst)) >>= (param->mask_offset); |
| break; |
| case 8: |
| /* Apply mask on a 64 bit value */ |
| *((uint64_t *)(dst)) &= (param->mask); |
| *((uint64_t *)(dst)) >>= (param->mask_offset); |
| break; |
| default: |
| /* Warn if there is a mask for an invalid length. */ |
| printk(BIOS_WARNING, |
| "HWILIB: Invalid field length for given mask.\n"); |
| break; |
| } |
| } |
| return param->pos[i].len; |
| } |
| |
| /** \brief This function finds all available block types in a given cbfs file. |
| * @param *hwi_filename Name of the cbfs-file to use. |
| * @return CB_SUCCESS when no error, otherwise error code |
| */ |
| enum cb_err hwilib_find_blocks (const char *hwi_filename) |
| { |
| uint8_t *ptr = NULL, *base = NULL; |
| uint32_t next_offset = 1; |
| uint8_t **blk_ptr = car_get_var_ptr(&all_blocks[0]); |
| uint16_t *all_blk_size_ptr = car_get_var_ptr(&all_blk_size[0]); |
| char *curr_hwi_name_ptr = car_get_var_ptr(¤t_hwi); |
| size_t filesize = 0; |
| |
| /* Check for a valid parameter */ |
| if (!hwi_filename) |
| return CB_ERR_ARG; |
| /* Check if this file is already open. If yes, just leave as there is |
| nothing left to do here. */ |
| if (curr_hwi_name_ptr && |
| !strncmp(curr_hwi_name_ptr, hwi_filename, HWI_MAX_NAME_LEN)) { |
| printk(BIOS_SPEW, "HWILIB: File \"%s\" already open.\n", |
| hwi_filename); |
| return CB_SUCCESS; |
| } |
| |
| ptr = cbfs_boot_map_with_leak(hwi_filename, CBFS_TYPE_RAW, &filesize); |
| if (!ptr) { |
| printk(BIOS_ERR,"HWILIB: Missing file \"%s\" in cbfs.\n", |
| hwi_filename); |
| return CB_ERR; |
| } |
| /* Ensure the block has the right magic */ |
| if (strncmp((char*)ptr, BLOCK_MAGIC, LEN_MAGIC_NUM)) { |
| printk(BIOS_ERR, "HWILIB: Bad magic at start of block!\n"); |
| return CB_ERR; |
| } |
| /* Reset all static pointers to blocks as they might have been set |
| * in prior calls to this function. |
| * This way the caller do not need to "close" already opened blocks. |
| */ |
| memset(blk_ptr, 0, (MAX_BLOCK_NUM * sizeof (uint8_t *))); |
| /* Check which blocks are available by examining the length field. */ |
| base = ptr; |
| /* Fill in sizes of all fixed length blocks. */ |
| all_blk_size_ptr[BLK_HIB] = LEN_HIB; |
| all_blk_size_ptr[BLK_SIB] = LEN_SIB; |
| all_blk_size_ptr[BLK_EIB] = LEN_EIB; |
| /* Length of BLK_XIB is variable and will be filled if block is found */ |
| all_blk_size_ptr[BLK_XIB] = 0; |
| while(!(strncmp((char *)ptr, BLOCK_MAGIC, LEN_MAGIC_NUM)) && |
| next_offset) { |
| uint16_t len = read16(ptr + LEN_OFFSET); |
| /* Ensure file size boundaries for a given block. */ |
| if ((ptr - base + len) > filesize) |
| break; |
| if (len == LEN_HIB) { |
| blk_ptr[BLK_HIB] = ptr; |
| next_offset = read32(ptr + NEXT_OFFSET_HIB); |
| if (next_offset) |
| ptr = base + next_offset; |
| } else if (len == LEN_SIB) { |
| blk_ptr[BLK_SIB] = ptr; |
| next_offset = read32(ptr + NEXT_OFFSET_SIB); |
| if (next_offset) |
| ptr = base + next_offset; |
| } else if (len == LEN_EIB) { |
| /* Skip preliminary blocks */ |
| if (!(read16(ptr + EIB_FEATRUE_OFFSET) & 0x01)) |
| blk_ptr[BLK_EIB] = ptr; |
| next_offset = read32(ptr + NEXT_OFFSET_EIB); |
| if (next_offset) |
| ptr = base + next_offset; |
| } else if (len >= MIN_LEN_XIB) { |
| blk_ptr[BLK_XIB] = ptr; |
| next_offset = read32(ptr + NEXT_OFFSET_XIB); |
| all_blk_size_ptr[BLK_XIB] = len; |
| if (next_offset) |
| ptr = base + next_offset; |
| } else { |
| next_offset = 0; |
| } |
| } |
| /* We should have found at least one valid block */ |
| if (blk_ptr[BLK_HIB] || blk_ptr[BLK_SIB] || blk_ptr[BLK_EIB] || |
| blk_ptr[BLK_XIB]) { |
| /* Save currently opened hwi filename. */ |
| strncpy(curr_hwi_name_ptr, hwi_filename, HWI_MAX_NAME_LEN); |
| return CB_SUCCESS; |
| } |
| else |
| return CB_ERR; |
| } |
| |
| /** \brief This functions is used from caller to get a specific field from |
| * hwinfo block. |
| * @param field Field type to read from hwinfo |
| * @param *dst Pointer to memory where the data will be stored in |
| * @return number of copied bytes on success, 0 on error |
| */ |
| uint32_t hwilib_get_field (hwinfo_field_t field, uint8_t *dst, uint32_t maxlen) |
| { |
| /* Check the boundaries of params-variable */ |
| if ((uint32_t)field < ARRAY_SIZE(params)) |
| return params[field].get_field(¶ms[field], dst, maxlen); |
| else |
| return 0; |
| } |