/*
* Copyright (C) 2011 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdint.h>
#include "fw_version_check.h"
#define DEVICE_NAME "/sys/kernel/fw_update/fw_info/fw_version"
#define FIP_PATTERN 0x50494624
#define SCU_IPC_VERSION_LEN_LONG 32
#define READ_SZ 256
struct fip_version_block {
uint16_t minor;
uint16_t major;
uint8_t checksum;
uint8_t reserved8;
uint16_t reserved16;
};
struct fip_version_block_chxx {
uint16_t minor;
uint16_t major;
uint8_t checksum;
uint8_t reserved8;
uint16_t reserved16;
uint16_t size;
uint16_t dest;
};
struct FIP_header {
uint32_t FIP_SIG;
struct fip_version_block umip_rev;
struct fip_version_block spat_rev;
struct fip_version_block spct_rev;
struct fip_version_block rpch_rev;
struct fip_version_block ch00_rev;
struct fip_version_block mipd_rev;
struct fip_version_block mipn_rev;
struct fip_version_block scuc_rev;
struct fip_version_block hvm_rev;
struct fip_version_block mia_rev;
struct fip_version_block ia32_rev;
struct fip_version_block oem_rev;
struct fip_version_block ved_rev;
struct fip_version_block vec_rev;
struct fip_version_block mos_rev;
struct fip_version_block pos_rev;
struct fip_version_block cos_rev;
struct fip_version_block_chxx ch01_rev;
struct fip_version_block_chxx ch02_rev;
struct fip_version_block_chxx ch03_rev;
struct fip_version_block_chxx ch04_rev;
struct fip_version_block_chxx ch05_rev;
struct fip_version_block_chxx ch06_rev;
struct fip_version_block_chxx ch07_rev;
struct fip_version_block_chxx ch08_rev;
struct fip_version_block_chxx ch09_rev;
struct fip_version_block_chxx ch10_rev;
struct fip_version_block_chxx ch11_rev;
struct fip_version_block_chxx ch12_rev;
struct fip_version_block_chxx ch13_rev;
struct fip_version_block_chxx ch14_rev;
struct fip_version_block_chxx ch15_rev;
struct fip_version_block dnx_rev;
struct fip_version_block reserved0_rev;
struct fip_version_block reserved1_rev;
struct fip_version_block ifwi_rev;
};
static int read_fw_revision(unsigned int *fw_revision, int len)
{
int i, fw_info, ret;
const char *sep = " ";
char *p, *save;
char buf[READ_SZ];
fw_info = open(DEVICE_NAME, O_RDONLY);
if (fw_info < 0) {
fprintf(stderr, "failed to open %s ", DEVICE_NAME);
return fw_info;
}
ret = read(fw_info, buf, READ_SZ - 1);
if (ret < 0) {
fprintf(stderr, "failed to read fw_revision, ret = %d\n", ret);
goto err;
}
buf[ret] = 0;
p = strtok_r(buf, sep, &save);
for (i = 0; p && i < len; i++) {
ret = sscanf(p, "%x", &fw_revision[i]);
if (ret != 1) {
fprintf(stderr, "failed to parse fw_revision, ret = %d\n", ret);
goto err;
}
p = strtok_r(NULL, sep, &save);
}
ret = 0;
err:
close(fw_info);
return ret;
}
/* Bytes in scu_ipc_version after the ioctl():
* 00 SCU Boot Strap Firmware Minor Revision Low
* 01 SCU Boot Strap Firmware Minor Revision High
* 02 SCU Boot Strap Firmware Major Revision Low
* 03 SCU Boot Strap Firmware Major Revision High
* 04 SCU Firmware Minor Revision Low
* 05 SCU Firmware Minor Revision High
* 06 SCU Firmware Major Revision Low
* 07 SCU Firmware Major Revision High
* 08 IA Firmware Minor Revision Low
* 09 IA Firmware Minor Revision High
* 10 IA Firmware Major Revision Low
* 11 IA Firmware Major Revision High
* 12 Validation Hooks Firmware Minor Revision Low
* 13 Validation Hooks Firmware Minor Revision High
* 14 Validation Hooks Firmware Major Revision Low
* 15 Validation Hooks Firmware Major Revision High
* 16 IFWI Firmware Minor Revision Low
* 17 IFWI Firmware Minor Revision High
* 18 IFWI Firmware Major Revision Low
* 19 IFWI Firmware Major Revision High
* 20 Chaabi Firmware Minor Revision Low
* 21 Chaabi Firmware Minor Revision High
* 22 Chaabi Firmware Major Revision Low
* 23 Chaabi Firmware Major Revision High
* 24 mIA Firmware Minor Revision Low
* 25 mIA Firmware Minor Revision High
* 26 mIA Firmware Major Revision Low
* 27 mIA Firmware Major Revision High
*/
int get_current_fw_rev(struct firmware_versions *v)
{
int ret;
unsigned int fw_revision[SCU_IPC_VERSION_LEN_LONG] = { 0 };
ret = read_fw_revision(fw_revision, SCU_IPC_VERSION_LEN_LONG);
if (ret)
return ret;
v->scubootstrap.minor = fw_revision[1] << 8 | fw_revision[0];
v->scubootstrap.major = fw_revision[3] << 8 | fw_revision[2];
v->scu.minor = fw_revision[5] << 8 | fw_revision[4];
v->scu.major = fw_revision[7] << 8 | fw_revision[6];
v->ia32.minor = fw_revision[9] << 8 | fw_revision[8];
v->ia32.major = fw_revision[11] << 8 | fw_revision[10];
v->valhooks.minor = fw_revision[13] << 8 | fw_revision[12];
v->valhooks.major = fw_revision[15] << 8 | fw_revision[14];
v->ifwi.minor = fw_revision[17] << 8 | fw_revision[16];
v->ifwi.major = fw_revision[19] << 8 | fw_revision[18];
v->chaabi.minor = fw_revision[21] << 8 | fw_revision[20];
v->chaabi.major = fw_revision[23] << 8 | fw_revision[22];
v->mia.minor = fw_revision[25] << 8 | fw_revision[24];
v->mia.major = fw_revision[27] << 8 | fw_revision[26];
return ret;
}
int get_image_fw_rev(void *data, unsigned sz, struct firmware_versions *v)
{
struct FIP_header fip;
unsigned char *databytes = (unsigned char *)data;
int magic;
int magic_found = 0;
if (v == NULL) {
fprintf(stderr, "Null pointer !\n");
return -1;
} else
memset((void *)v, 0, sizeof(struct firmware_versions));
while (sz >= sizeof(fip)) {
/* Scan for the FIP magic */
while (sz >= sizeof(fip)) {
memcpy(&magic, databytes, sizeof(magic));
if (magic == FIP_PATTERN) {
magic_found = 1;
break;
}
databytes += sizeof(magic);
sz -= sizeof(magic);
}
if (!magic_found) {
fprintf(stderr, "Couldn't find FIP magic in image!\n");
return -1;
}
if (sz < sizeof(fip)) {
break;
}
memcpy(&fip, databytes, sizeof(fip));
/* not available in ifwi file */
v->scubootstrap.minor = 0;
v->scubootstrap.major = 0;
/* don't update if null */
if (fip.scuc_rev.minor != 0)
v->scu.minor = fip.scuc_rev.minor;
if (fip.scuc_rev.major != 0)
v->scu.major = fip.scuc_rev.major;
if (fip.ia32_rev.minor != 0)
v->ia32.minor = fip.ia32_rev.minor;
if (fip.ia32_rev.major != 0)
v->ia32.major = fip.ia32_rev.major;
if (fip.oem_rev.minor != 0)
v->valhooks.minor = fip.oem_rev.minor;
if (fip.oem_rev.major != 0)
v->valhooks.major = fip.oem_rev.major;
if (fip.ifwi_rev.minor != 0)
v->ifwi.minor = fip.ifwi_rev.minor;
if (fip.ifwi_rev.major != 0)
v->ifwi.major = fip.ifwi_rev.major;
if (fip.ch00_rev.minor != 0)
v->chaabi.minor = fip.ch00_rev.minor;
if (fip.ch00_rev.major != 0)
v->chaabi.major = fip.ch00_rev.major;
if (fip.mia_rev.minor != 0)
v->mia.minor = fip.mia_rev.minor;
if (fip.mia_rev.major != 0)
v->mia.major = fip.mia_rev.major;
databytes += sizeof(magic);
sz -= sizeof(magic);
}
return 0;
}