1781 lines
48 KiB
C
1781 lines
48 KiB
C
/*
|
|
* ifdtool - dump Intel Firmware Descriptor information
|
|
*
|
|
* Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
|
|
*
|
|
* 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 <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <commonlib/helpers.h>
|
|
#include <fmap.h>
|
|
#include "ifdtool.h"
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
/**
|
|
* PTR_IN_RANGE - examine whether a pointer falls in [base, base + limit)
|
|
* @param ptr: the non-void* pointer to a single arbitrary-sized object.
|
|
* @param base: base address represented with char* type.
|
|
* @param limit: upper limit of the legal address.
|
|
*
|
|
*/
|
|
#define PTR_IN_RANGE(ptr, base, limit) \
|
|
((const char *)(ptr) >= (base) && \
|
|
(const char *)&(ptr)[1] <= (base) + (limit))
|
|
|
|
static int ifd_version;
|
|
static int chipset;
|
|
static unsigned int max_regions = 0;
|
|
static int selected_chip = 0;
|
|
static int platform = -1;
|
|
|
|
static const struct region_name region_names[MAX_REGIONS] = {
|
|
{ "Flash Descriptor", "fd", "flashregion_0_flashdescriptor.bin", "SI_DESC" },
|
|
{ "BIOS", "bios", "flashregion_1_bios.bin", "SI_BIOS" },
|
|
{ "Intel ME", "me", "flashregion_2_intel_me.bin", "SI_ME" },
|
|
{ "GbE", "gbe", "flashregion_3_gbe.bin", "SI_GBE" },
|
|
{ "Platform Data", "pd", "flashregion_4_platform_data.bin", "SI_PDR" },
|
|
{ "Reserved", "res1", "flashregion_5_reserved.bin", NULL },
|
|
{ "Reserved", "res2", "flashregion_6_reserved.bin", NULL },
|
|
{ "Reserved", "res3", "flashregion_7_reserved.bin", NULL },
|
|
{ "EC", "ec", "flashregion_8_ec.bin", "SI_EC" },
|
|
};
|
|
|
|
/* port from flashrom */
|
|
static const char *const ich_chipset_names[] = {
|
|
"Unknown ICH",
|
|
"ICH",
|
|
"ICH2345",
|
|
"ICH6",
|
|
"SCH U",
|
|
"Atom E6xx",
|
|
"Atom S1220 S1240 S1260",
|
|
"ICH7",
|
|
"ICH8",
|
|
"ICH9",
|
|
"ICH10",
|
|
"5 series Ibex Peak",
|
|
"6 series Cougar Point",
|
|
"7 series Panther Point",
|
|
"8 series Lynx Point",
|
|
"Baytrail",
|
|
"8 series Lynx Point LP",
|
|
"8 series Wellsburg",
|
|
"9 series Wildcat Point",
|
|
"9 series Wildcat Point LP",
|
|
"100 series Sunrise Point",
|
|
"C620 series Lewisburg",
|
|
NULL
|
|
};
|
|
|
|
static fdbar_t *find_fd(char *image, int size)
|
|
{
|
|
int i, found = 0;
|
|
|
|
/* Scan for FD signature */
|
|
for (i = 0; i < (size - 4); i += 4) {
|
|
if (*(uint32_t *) (image + i) == 0x0FF0A55A) {
|
|
found = 1;
|
|
break; // signature found.
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
printf("No Flash Descriptor found in this image\n");
|
|
return NULL;
|
|
}
|
|
|
|
fdbar_t *fdb = (fdbar_t *) (image + i);
|
|
return PTR_IN_RANGE(fdb, image, size) ? fdb : NULL;
|
|
}
|
|
|
|
static char *find_flumap(char *image, int size)
|
|
{
|
|
/* The upper map is located in the word before the 256B-long OEM section
|
|
* at the end of the 4kB-long flash descriptor. In the official
|
|
* documentation this is defined as FDBAR + 0xEFC. However, starting
|
|
* with B-Step of Ibex Peak (5 series) the signature (and thus FDBAR)
|
|
* has moved 16 bytes back to offset 0x10 of the image. Although
|
|
* official documentation still maintains the offset relative to FDBAR
|
|
* this is wrong and a simple fixed offset from the start of the image
|
|
* works.
|
|
*/
|
|
char *flumap = image + 4096 - 256 - 4;
|
|
return PTR_IN_RANGE(flumap, image, size) ? flumap : NULL;
|
|
}
|
|
|
|
static fcba_t *find_fcba(char *image, int size)
|
|
{
|
|
fdbar_t *fdb = find_fd(image, size);
|
|
if (!fdb)
|
|
return NULL;
|
|
fcba_t *fcba = (fcba_t *) (image + ((fdb->flmap0 & 0xff) << 4));
|
|
return PTR_IN_RANGE(fcba, image, size) ? fcba : NULL;
|
|
|
|
}
|
|
|
|
static fmba_t *find_fmba(char *image, int size)
|
|
{
|
|
fdbar_t *fdb = find_fd(image, size);
|
|
if (!fdb)
|
|
return NULL;
|
|
fmba_t *fmba = (fmba_t *) (image + ((fdb->flmap1 & 0xff) << 4));
|
|
return PTR_IN_RANGE(fmba, image, size) ? fmba : NULL;
|
|
}
|
|
|
|
static frba_t *find_frba(char *image, int size)
|
|
{
|
|
fdbar_t *fdb = find_fd(image, size);
|
|
if (!fdb)
|
|
return NULL;
|
|
frba_t *frba =
|
|
(frba_t *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4));
|
|
return PTR_IN_RANGE(frba, image, size) ? frba : NULL;
|
|
}
|
|
|
|
static fpsba_t *find_fpsba(char *image, int size)
|
|
{
|
|
fdbar_t *fdb = find_fd(image, size);
|
|
if (!fdb)
|
|
return NULL;
|
|
fpsba_t *fpsba =
|
|
(fpsba_t *) (image + (((fdb->flmap1 >> 16) & 0xff) << 4));
|
|
return PTR_IN_RANGE(fpsba, image, size) ? fpsba : NULL;
|
|
}
|
|
|
|
static fmsba_t *find_fmsba(char *image, int size)
|
|
{
|
|
fdbar_t *fdb = find_fd(image, size);
|
|
if (!fdb)
|
|
return NULL;
|
|
fmsba_t *fmsba = (fmsba_t *) (image + ((fdb->flmap2 & 0xff) << 4));
|
|
return PTR_IN_RANGE(fmsba, image, size) ? fmsba : NULL;
|
|
}
|
|
|
|
/* port from flashrom */
|
|
static enum ich_chipset guess_ich_chipset(const fdbar_t *fdb)
|
|
{
|
|
uint32_t iccriba = (fdb->flmap2 >> 16) & 0xff;
|
|
uint32_t msl = (fdb->flmap2 >> 8) & 0xff;
|
|
uint32_t isl = (fdb->flmap1 >> 24);
|
|
uint32_t nm = (fdb->flmap1 >> 8) & 0x7;
|
|
|
|
if (iccriba == 0x00) {
|
|
if (msl == 0 && isl <= 2)
|
|
return CHIPSET_ICH8;
|
|
else if (isl <= 2)
|
|
return CHIPSET_ICH9;
|
|
else if (isl <= 10)
|
|
return CHIPSET_ICH10;
|
|
else if (isl <= 16)
|
|
return CHIPSET_5_SERIES_IBEX_PEAK;
|
|
printf("Peculiar firmware descriptor, assuming Ibex Peak compatibility.\n");
|
|
return CHIPSET_5_SERIES_IBEX_PEAK;
|
|
} else if (iccriba < 0x31 && (fdb->flmap2 & 0xff) < 0x30) {
|
|
if (msl == 0 && isl <= 17)
|
|
return CHIPSET_BAYTRAIL;
|
|
else if (msl <= 1 && isl <= 18)
|
|
return CHIPSET_6_SERIES_COUGAR_POINT;
|
|
else if (msl <= 1 && isl <= 21)
|
|
return CHIPSET_8_SERIES_LYNX_POINT;
|
|
printf("Peculiar firmware descriptor, assuming Wildcat Point compatibility.\n");
|
|
return CHIPSET_9_SERIES_WILDCAT_POINT;
|
|
} else if (nm == 6) {
|
|
return CHIPSET_C620_SERIES_LEWISBURG;
|
|
} else {
|
|
return CHIPSET_100_SERIES_SUNRISE_POINT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Some newer platforms have re-defined the FCBA field that was used to
|
|
* distinguish IFD v1 v/s v2. Define a list of platforms that we know do not
|
|
* have the required FCBA field, but are IFD v2 and return true if current
|
|
* platform is one of them.
|
|
*/
|
|
static int is_platform_ifd_2(void)
|
|
{
|
|
static const int ifd_2_platforms[] = {
|
|
PLATFORM_GLK,
|
|
PLATFORM_CNL,
|
|
PLATFORM_ICL,
|
|
PLATFORM_TGL,
|
|
PLATFORM_JSL,
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ifd_2_platforms); i++) {
|
|
if (platform == ifd_2_platforms[i])
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* There is no version field in the descriptor so to determine
|
|
* if this is a new descriptor format we check the hardcoded SPI
|
|
* read frequency to see if it is fixed at 20MHz or 17MHz.
|
|
*/
|
|
static int get_ifd_version_from_fcba(char *image, int size)
|
|
{
|
|
int read_freq;
|
|
const fcba_t *fcba = find_fcba(image, size);
|
|
const fdbar_t *fdb = find_fd(image, size);
|
|
if (!fcba || !fdb)
|
|
exit(EXIT_FAILURE);
|
|
|
|
chipset = guess_ich_chipset(fdb);
|
|
/* TODO: port ifd_version and max_regions
|
|
* against guess_ich_chipset()
|
|
*/
|
|
read_freq = (fcba->flcomp >> 17) & 7;
|
|
|
|
switch (read_freq) {
|
|
case SPI_FREQUENCY_20MHZ:
|
|
return IFD_VERSION_1;
|
|
case SPI_FREQUENCY_17MHZ:
|
|
case SPI_FREQUENCY_50MHZ_30MHZ:
|
|
return IFD_VERSION_2;
|
|
default:
|
|
fprintf(stderr, "Unknown descriptor version: %d\n",
|
|
read_freq);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void check_ifd_version(char *image, int size)
|
|
{
|
|
if (is_platform_ifd_2())
|
|
ifd_version = IFD_VERSION_2;
|
|
else
|
|
ifd_version = get_ifd_version_from_fcba(image, size);
|
|
|
|
if (ifd_version == IFD_VERSION_1)
|
|
max_regions = MAX_REGIONS_OLD;
|
|
else
|
|
max_regions = MAX_REGIONS;
|
|
}
|
|
|
|
static region_t get_region(const frba_t *frba, unsigned int region_type)
|
|
{
|
|
int base_mask;
|
|
int limit_mask;
|
|
uint32_t flreg;
|
|
region_t region;
|
|
|
|
if (ifd_version >= IFD_VERSION_2)
|
|
base_mask = 0x7fff;
|
|
else
|
|
base_mask = 0xfff;
|
|
|
|
limit_mask = base_mask << 16;
|
|
|
|
if (region_type >= max_regions) {
|
|
fprintf(stderr, "Invalid region type %d.\n", region_type);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
flreg = frba->flreg[region_type];
|
|
region.base = (flreg & base_mask) << 12;
|
|
region.limit = ((flreg & limit_mask) >> 4) | 0xfff;
|
|
region.size = region.limit - region.base + 1;
|
|
|
|
if (region.size < 0)
|
|
region.size = 0;
|
|
|
|
return region;
|
|
}
|
|
|
|
static void set_region(frba_t *frba, unsigned int region_type,
|
|
const region_t *region)
|
|
{
|
|
if (region_type >= max_regions) {
|
|
fprintf(stderr, "Invalid region type %u.\n", region_type);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
frba->flreg[region_type] =
|
|
(((region->limit >> 12) & 0x7fff) << 16) |
|
|
((region->base >> 12) & 0x7fff);
|
|
}
|
|
|
|
static const char *region_name(unsigned int region_type)
|
|
{
|
|
if (region_type >= max_regions) {
|
|
fprintf(stderr, "Invalid region type.\n");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
return region_names[region_type].pretty;
|
|
}
|
|
|
|
static const char *region_name_short(unsigned int region_type)
|
|
{
|
|
if (region_type >= max_regions) {
|
|
fprintf(stderr, "Invalid region type.\n");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
return region_names[region_type].terse;
|
|
}
|
|
|
|
static int region_num(const char *name)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < max_regions; i++) {
|
|
if (strcasecmp(name, region_names[i].pretty) == 0)
|
|
return i;
|
|
if (strcasecmp(name, region_names[i].terse) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static const char *region_filename(unsigned int region_type)
|
|
{
|
|
if (region_type >= max_regions) {
|
|
fprintf(stderr, "Invalid region type %d.\n", region_type);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
return region_names[region_type].filename;
|
|
}
|
|
|
|
static void dump_region(unsigned int num, const frba_t *frba)
|
|
{
|
|
region_t region = get_region(frba, num);
|
|
printf(" Flash Region %d (%s): %08x - %08x %s\n",
|
|
num, region_name(num), region.base, region.limit,
|
|
region.size < 1 ? "(unused)" : "");
|
|
}
|
|
|
|
static void dump_region_layout(char *buf, size_t bufsize, unsigned int num,
|
|
const frba_t *frba)
|
|
{
|
|
region_t region = get_region(frba, num);
|
|
snprintf(buf, bufsize, "%08x:%08x %s\n",
|
|
region.base, region.limit, region_name_short(num));
|
|
}
|
|
|
|
static void dump_frba(const frba_t *frba)
|
|
{
|
|
unsigned int i;
|
|
printf("Found Region Section\n");
|
|
for (i = 0; i < max_regions; i++) {
|
|
printf("FLREG%u: 0x%08x\n", i, frba->flreg[i]);
|
|
dump_region(i, frba);
|
|
}
|
|
}
|
|
|
|
static void dump_frba_layout(const frba_t *frba, const char *layout_fname)
|
|
{
|
|
char buf[LAYOUT_LINELEN];
|
|
size_t bufsize = LAYOUT_LINELEN;
|
|
unsigned int i;
|
|
|
|
int layout_fd = open(layout_fname, O_WRONLY | O_CREAT | O_TRUNC,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
if (layout_fd == -1) {
|
|
perror("Could not open file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (i = 0; i < max_regions; i++) {
|
|
region_t region = get_region(frba, i);
|
|
/* is region invalid? */
|
|
if (region.size < 1)
|
|
continue;
|
|
|
|
dump_region_layout(buf, bufsize, i, frba);
|
|
if (write(layout_fd, buf, strlen(buf)) < 0) {
|
|
perror("Could not write to file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
close(layout_fd);
|
|
printf("Wrote layout to %s\n", layout_fname);
|
|
}
|
|
|
|
static void decode_spi_frequency(unsigned int freq)
|
|
{
|
|
switch (freq) {
|
|
case SPI_FREQUENCY_20MHZ:
|
|
printf("20MHz");
|
|
break;
|
|
case SPI_FREQUENCY_33MHZ:
|
|
printf("33MHz");
|
|
break;
|
|
case SPI_FREQUENCY_48MHZ:
|
|
printf("48MHz");
|
|
break;
|
|
case SPI_FREQUENCY_50MHZ_30MHZ:
|
|
switch (ifd_version) {
|
|
case IFD_VERSION_1:
|
|
printf("50MHz");
|
|
break;
|
|
case IFD_VERSION_2:
|
|
printf("30MHz");
|
|
break;
|
|
}
|
|
break;
|
|
case SPI_FREQUENCY_17MHZ:
|
|
printf("17MHz");
|
|
break;
|
|
default:
|
|
printf("unknown<%x>MHz", freq);
|
|
}
|
|
}
|
|
|
|
static void decode_component_density(unsigned int density)
|
|
{
|
|
switch (density) {
|
|
case COMPONENT_DENSITY_512KB:
|
|
printf("512KB");
|
|
break;
|
|
case COMPONENT_DENSITY_1MB:
|
|
printf("1MB");
|
|
break;
|
|
case COMPONENT_DENSITY_2MB:
|
|
printf("2MB");
|
|
break;
|
|
case COMPONENT_DENSITY_4MB:
|
|
printf("4MB");
|
|
break;
|
|
case COMPONENT_DENSITY_8MB:
|
|
printf("8MB");
|
|
break;
|
|
case COMPONENT_DENSITY_16MB:
|
|
printf("16MB");
|
|
break;
|
|
case COMPONENT_DENSITY_32MB:
|
|
printf("32MB");
|
|
break;
|
|
case COMPONENT_DENSITY_64MB:
|
|
printf("64MB");
|
|
break;
|
|
case COMPONENT_DENSITY_UNUSED:
|
|
printf("UNUSED");
|
|
break;
|
|
default:
|
|
printf("unknown<%x>MB", density);
|
|
}
|
|
}
|
|
|
|
static void dump_fcba(const fcba_t *fcba)
|
|
{
|
|
printf("\nFound Component Section\n");
|
|
printf("FLCOMP 0x%08x\n", fcba->flcomp);
|
|
printf(" Dual Output Fast Read Support: %ssupported\n",
|
|
(fcba->flcomp & (1 << 30))?"":"not ");
|
|
printf(" Read ID/Read Status Clock Frequency: ");
|
|
decode_spi_frequency((fcba->flcomp >> 27) & 7);
|
|
printf("\n Write/Erase Clock Frequency: ");
|
|
decode_spi_frequency((fcba->flcomp >> 24) & 7);
|
|
printf("\n Fast Read Clock Frequency: ");
|
|
decode_spi_frequency((fcba->flcomp >> 21) & 7);
|
|
printf("\n Fast Read Support: %ssupported",
|
|
(fcba->flcomp & (1 << 20))?"":"not ");
|
|
printf("\n Read Clock Frequency: ");
|
|
decode_spi_frequency((fcba->flcomp >> 17) & 7);
|
|
|
|
switch (ifd_version) {
|
|
case IFD_VERSION_1:
|
|
printf("\n Component 2 Density: ");
|
|
decode_component_density((fcba->flcomp >> 3) & 7);
|
|
printf("\n Component 1 Density: ");
|
|
decode_component_density(fcba->flcomp & 7);
|
|
break;
|
|
case IFD_VERSION_2:
|
|
printf("\n Component 2 Density: ");
|
|
decode_component_density((fcba->flcomp >> 4) & 0xf);
|
|
printf("\n Component 1 Density: ");
|
|
decode_component_density(fcba->flcomp & 0xf);
|
|
break;
|
|
}
|
|
|
|
printf("\n");
|
|
printf("FLILL 0x%08x\n", fcba->flill);
|
|
printf(" Invalid Instruction 3: 0x%02x\n",
|
|
(fcba->flill >> 24) & 0xff);
|
|
printf(" Invalid Instruction 2: 0x%02x\n",
|
|
(fcba->flill >> 16) & 0xff);
|
|
printf(" Invalid Instruction 1: 0x%02x\n",
|
|
(fcba->flill >> 8) & 0xff);
|
|
printf(" Invalid Instruction 0: 0x%02x\n",
|
|
fcba->flill & 0xff);
|
|
printf("FLPB 0x%08x\n", fcba->flpb);
|
|
printf(" Flash Partition Boundary Address: 0x%06x\n\n",
|
|
(fcba->flpb & 0xfff) << 12);
|
|
}
|
|
|
|
static void dump_fpsba(const fpsba_t *fpsba)
|
|
{
|
|
unsigned int i;
|
|
printf("Found PCH Strap Section\n");
|
|
for (i = 0; i < ARRAY_SIZE(fpsba->pchstrp); i++)
|
|
printf("PCHSTRP%u:%s 0x%08x\n", i,
|
|
i < 10 ? " " : "", fpsba->pchstrp[i]);
|
|
|
|
if (ifd_version >= IFD_VERSION_2) {
|
|
printf("HAP bit is %sset\n",
|
|
fpsba->pchstrp[0] & (1 << 16) ? "" : "not ");
|
|
} else if (chipset >= CHIPSET_ICH8
|
|
&& chipset <= CHIPSET_ICH10) {
|
|
printf("ICH_MeDisable bit is %sset\n",
|
|
fpsba->pchstrp[0] & 1 ? "" : "not ");
|
|
} else {
|
|
printf("AltMeDisable bit is %sset\n",
|
|
fpsba->pchstrp[10] & (1 << 7) ? "" : "not ");
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
static void decode_flmstr(uint32_t flmstr)
|
|
{
|
|
int wr_shift, rd_shift;
|
|
if (ifd_version >= IFD_VERSION_2) {
|
|
wr_shift = FLMSTR_WR_SHIFT_V2;
|
|
rd_shift = FLMSTR_RD_SHIFT_V2;
|
|
} else {
|
|
wr_shift = FLMSTR_WR_SHIFT_V1;
|
|
rd_shift = FLMSTR_RD_SHIFT_V1;
|
|
}
|
|
|
|
/* EC region access only available on v2+ */
|
|
if (ifd_version >= IFD_VERSION_2)
|
|
printf(" EC Region Write Access: %s\n",
|
|
(flmstr & (1 << (wr_shift + 8))) ?
|
|
"enabled" : "disabled");
|
|
printf(" Platform Data Region Write Access: %s\n",
|
|
(flmstr & (1 << (wr_shift + 4))) ? "enabled" : "disabled");
|
|
printf(" GbE Region Write Access: %s\n",
|
|
(flmstr & (1 << (wr_shift + 3))) ? "enabled" : "disabled");
|
|
printf(" Intel ME Region Write Access: %s\n",
|
|
(flmstr & (1 << (wr_shift + 2))) ? "enabled" : "disabled");
|
|
printf(" Host CPU/BIOS Region Write Access: %s\n",
|
|
(flmstr & (1 << (wr_shift + 1))) ? "enabled" : "disabled");
|
|
printf(" Flash Descriptor Write Access: %s\n",
|
|
(flmstr & (1 << wr_shift)) ? "enabled" : "disabled");
|
|
|
|
if (ifd_version >= IFD_VERSION_2)
|
|
printf(" EC Region Read Access: %s\n",
|
|
(flmstr & (1 << (rd_shift + 8))) ?
|
|
"enabled" : "disabled");
|
|
printf(" Platform Data Region Read Access: %s\n",
|
|
(flmstr & (1 << (rd_shift + 4))) ? "enabled" : "disabled");
|
|
printf(" GbE Region Read Access: %s\n",
|
|
(flmstr & (1 << (rd_shift + 3))) ? "enabled" : "disabled");
|
|
printf(" Intel ME Region Read Access: %s\n",
|
|
(flmstr & (1 << (rd_shift + 2))) ? "enabled" : "disabled");
|
|
printf(" Host CPU/BIOS Region Read Access: %s\n",
|
|
(flmstr & (1 << (rd_shift + 1))) ? "enabled" : "disabled");
|
|
printf(" Flash Descriptor Read Access: %s\n",
|
|
(flmstr & (1 << rd_shift)) ? "enabled" : "disabled");
|
|
|
|
/* Requestor ID doesn't exist for ifd 2 */
|
|
if (ifd_version < IFD_VERSION_2)
|
|
printf(" Requester ID: 0x%04x\n\n",
|
|
flmstr & 0xffff);
|
|
}
|
|
|
|
static void dump_fmba(const fmba_t *fmba)
|
|
{
|
|
printf("Found Master Section\n");
|
|
printf("FLMSTR1: 0x%08x (Host CPU/BIOS)\n", fmba->flmstr1);
|
|
decode_flmstr(fmba->flmstr1);
|
|
printf("FLMSTR2: 0x%08x (Intel ME)\n", fmba->flmstr2);
|
|
decode_flmstr(fmba->flmstr2);
|
|
printf("FLMSTR3: 0x%08x (GbE)\n", fmba->flmstr3);
|
|
decode_flmstr(fmba->flmstr3);
|
|
if (ifd_version >= IFD_VERSION_2) {
|
|
printf("FLMSTR5: 0x%08x (EC)\n", fmba->flmstr5);
|
|
decode_flmstr(fmba->flmstr5);
|
|
}
|
|
}
|
|
|
|
static void dump_fmsba(const fmsba_t *fmsba)
|
|
{
|
|
unsigned int i;
|
|
printf("Found Processor Strap Section\n");
|
|
for (i = 0; i < ARRAY_SIZE(fmsba->data); i++)
|
|
printf("????: 0x%08x\n", fmsba->data[i]);
|
|
|
|
if (chipset >= CHIPSET_ICH8 && chipset <= CHIPSET_ICH10) {
|
|
printf("MCH_MeDisable bit is %sset\n",
|
|
fmsba->data[0] & 1 ? "" : "not ");
|
|
printf("MCH_AltMeDisable bit is %sset\n",
|
|
fmsba->data[0] & (1 << 7) ? "" : "not ");
|
|
}
|
|
}
|
|
|
|
static void dump_jid(uint32_t jid)
|
|
{
|
|
printf(" SPI Component Vendor ID: 0x%02x\n",
|
|
jid & 0xff);
|
|
printf(" SPI Component Device ID 0: 0x%02x\n",
|
|
(jid >> 8) & 0xff);
|
|
printf(" SPI Component Device ID 1: 0x%02x\n",
|
|
(jid >> 16) & 0xff);
|
|
}
|
|
|
|
static void dump_vscc(uint32_t vscc)
|
|
{
|
|
printf(" Lower Erase Opcode: 0x%02x\n",
|
|
vscc >> 24);
|
|
printf(" Lower Write Enable on Write Status: 0x%02x\n",
|
|
vscc & (1 << 20) ? 0x06 : 0x50);
|
|
printf(" Lower Write Status Required: %s\n",
|
|
vscc & (1 << 19) ? "Yes" : "No");
|
|
printf(" Lower Write Granularity: %d bytes\n",
|
|
vscc & (1 << 18) ? 64 : 1);
|
|
printf(" Lower Block / Sector Erase Size: ");
|
|
switch ((vscc >> 16) & 0x3) {
|
|
case 0:
|
|
printf("256 Byte\n");
|
|
break;
|
|
case 1:
|
|
printf("4KB\n");
|
|
break;
|
|
case 2:
|
|
printf("8KB\n");
|
|
break;
|
|
case 3:
|
|
printf("64KB\n");
|
|
break;
|
|
}
|
|
|
|
printf(" Upper Erase Opcode: 0x%02x\n",
|
|
(vscc >> 8) & 0xff);
|
|
printf(" Upper Write Enable on Write Status: 0x%02x\n",
|
|
vscc & (1 << 4) ? 0x06 : 0x50);
|
|
printf(" Upper Write Status Required: %s\n",
|
|
vscc & (1 << 3) ? "Yes" : "No");
|
|
printf(" Upper Write Granularity: %d bytes\n",
|
|
vscc & (1 << 2) ? 64 : 1);
|
|
printf(" Upper Block / Sector Erase Size: ");
|
|
switch (vscc & 0x3) {
|
|
case 0:
|
|
printf("256 Byte\n");
|
|
break;
|
|
case 1:
|
|
printf("4KB\n");
|
|
break;
|
|
case 2:
|
|
printf("8KB\n");
|
|
break;
|
|
case 3:
|
|
printf("64KB\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void dump_vtba(const vtba_t *vtba, int vtl)
|
|
{
|
|
int i;
|
|
int max_len = sizeof(vtba_t)/sizeof(vscc_t);
|
|
int num = (vtl >> 1) < max_len ? (vtl >> 1) : max_len;
|
|
|
|
printf("ME VSCC table:\n");
|
|
for (i = 0; i < num; i++) {
|
|
printf(" JID%d: 0x%08x\n", i, vtba->entry[i].jid);
|
|
dump_jid(vtba->entry[i].jid);
|
|
printf(" VSCC%d: 0x%08x\n", i, vtba->entry[i].vscc);
|
|
dump_vscc(vtba->entry[i].vscc);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void dump_oem(const uint8_t *oem)
|
|
{
|
|
int i, j;
|
|
printf("OEM Section:\n");
|
|
for (i = 0; i < 4; i++) {
|
|
printf("%02x:", i << 4);
|
|
for (j = 0; j < 16; j++)
|
|
printf(" %02x", oem[(i<<4)+j]);
|
|
printf ("\n");
|
|
}
|
|
printf ("\n");
|
|
}
|
|
|
|
static void dump_fd(char *image, int size)
|
|
{
|
|
const fdbar_t *fdb = find_fd(image, size);
|
|
if (!fdb)
|
|
exit(EXIT_FAILURE);
|
|
|
|
printf("ICH Revision: %s\n", ich_chipset_names[chipset]);
|
|
printf("FLMAP0: 0x%08x\n", fdb->flmap0);
|
|
printf(" NR: %d\n", (fdb->flmap0 >> 24) & 7);
|
|
printf(" FRBA: 0x%x\n", ((fdb->flmap0 >> 16) & 0xff) << 4);
|
|
printf(" NC: %d\n", ((fdb->flmap0 >> 8) & 3) + 1);
|
|
printf(" FCBA: 0x%x\n", ((fdb->flmap0) & 0xff) << 4);
|
|
|
|
printf("FLMAP1: 0x%08x\n", fdb->flmap1);
|
|
printf(" ISL: 0x%02x\n", (fdb->flmap1 >> 24) & 0xff);
|
|
printf(" FPSBA: 0x%x\n", ((fdb->flmap1 >> 16) & 0xff) << 4);
|
|
printf(" NM: %d\n", (fdb->flmap1 >> 8) & 3);
|
|
printf(" FMBA: 0x%x\n", ((fdb->flmap1) & 0xff) << 4);
|
|
|
|
printf("FLMAP2: 0x%08x\n", fdb->flmap2);
|
|
printf(" PSL: 0x%04x\n", (fdb->flmap2 >> 8) & 0xffff);
|
|
printf(" FMSBA: 0x%x\n", ((fdb->flmap2) & 0xff) << 4);
|
|
|
|
char *flumap = find_flumap(image, size);
|
|
uint32_t flumap1 = *(uint32_t *)flumap;
|
|
printf("FLUMAP1: 0x%08x\n", flumap1);
|
|
printf(" Intel ME VSCC Table Length (VTL): %d\n",
|
|
(flumap1 >> 8) & 0xff);
|
|
printf(" Intel ME VSCC Table Base Address (VTBA): 0x%06x\n\n",
|
|
(flumap1 & 0xff) << 4);
|
|
dump_vtba((vtba_t *)
|
|
(image + ((flumap1 & 0xff) << 4)),
|
|
(flumap1 >> 8) & 0xff);
|
|
dump_oem((const uint8_t *)image + 0xf00);
|
|
|
|
const frba_t *frba = find_frba(image, size);
|
|
const fcba_t *fcba = find_fcba(image, size);
|
|
const fpsba_t *fpsba = find_fpsba(image, size);
|
|
const fmba_t *fmba = find_fmba(image, size);
|
|
const fmsba_t *fmsba = find_fmsba(image, size);
|
|
|
|
if (frba && fcba && fpsba && fmba && fmsba) {
|
|
dump_frba(frba);
|
|
dump_fcba(fcba);
|
|
dump_fpsba(fpsba);
|
|
dump_fmba(fmba);
|
|
dump_fmsba(fmsba);
|
|
} else {
|
|
printf("FD is corrupted!\n");
|
|
}
|
|
}
|
|
|
|
static void dump_layout(char *image, int size, const char *layout_fname)
|
|
{
|
|
const frba_t *frba = find_frba(image, size);
|
|
if (!frba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
dump_frba_layout(frba, layout_fname);
|
|
}
|
|
|
|
static void write_regions(char *image, int size)
|
|
{
|
|
unsigned int i;
|
|
const frba_t *frba = find_frba(image, size);
|
|
|
|
if (!frba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
for (i = 0; i < max_regions; i++) {
|
|
region_t region = get_region(frba, i);
|
|
dump_region(i, frba);
|
|
if (region.size > 0) {
|
|
int region_fd;
|
|
region_fd = open(region_filename(i),
|
|
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
if (region_fd < 0) {
|
|
perror("Error while trying to open file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (write(region_fd, image + region.base, region.size) != region.size)
|
|
perror("Error while writing");
|
|
close(region_fd);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void validate_layout(char *image, int size)
|
|
{
|
|
uint i, errors = 0;
|
|
struct fmap *fmap;
|
|
long int fmap_loc = fmap_find((uint8_t *)image, size);
|
|
const frba_t *frba = find_frba(image, size);
|
|
|
|
if (fmap_loc < 0 || !frba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
fmap = (struct fmap *)(image + fmap_loc);
|
|
|
|
for (i = 0; i < max_regions; i++) {
|
|
if (region_names[i].fmapname == NULL)
|
|
continue;
|
|
|
|
region_t region = get_region(frba, i);
|
|
|
|
if (region.size == 0)
|
|
continue;
|
|
|
|
const struct fmap_area *area =
|
|
fmap_find_area(fmap, region_names[i].fmapname);
|
|
|
|
if (!area)
|
|
continue;
|
|
|
|
if ((uint)region.base != area->offset ||
|
|
(uint)region.size != area->size) {
|
|
printf("Region mismatch between %s and %s\n",
|
|
region_names[i].terse, area->name);
|
|
printf(" Descriptor region %s:\n", region_names[i].terse);
|
|
printf(" offset: 0x%08x\n", region.base);
|
|
printf(" length: 0x%08x\n", region.size);
|
|
printf(" FMAP area %s:\n", area->name);
|
|
printf(" offset: 0x%08x\n", area->offset);
|
|
printf(" length: 0x%08x\n", area->size);
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
if (errors > 0)
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static void write_image(const char *filename, char *image, int size)
|
|
{
|
|
char new_filename[FILENAME_MAX]; // allow long file names
|
|
int new_fd;
|
|
|
|
// - 5: leave room for ".new\0"
|
|
strncpy(new_filename, filename, FILENAME_MAX - 5);
|
|
strncat(new_filename, ".new", FILENAME_MAX - strlen(filename));
|
|
|
|
printf("Writing new image to %s\n", new_filename);
|
|
|
|
// Now write out new image
|
|
new_fd = open(new_filename,
|
|
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
if (new_fd < 0) {
|
|
perror("Error while trying to open file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (write(new_fd, image, size) != size)
|
|
perror("Error while writing");
|
|
close(new_fd);
|
|
}
|
|
|
|
static void set_spi_frequency(const char *filename, char *image, int size,
|
|
enum spi_frequency freq)
|
|
{
|
|
fcba_t *fcba = find_fcba(image, size);
|
|
if (!fcba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
/* clear bits 21-29 */
|
|
fcba->flcomp &= ~0x3fe00000;
|
|
/* Read ID and Read Status Clock Frequency */
|
|
fcba->flcomp |= freq << 27;
|
|
/* Write and Erase Clock Frequency */
|
|
fcba->flcomp |= freq << 24;
|
|
/* Fast Read Clock Frequency */
|
|
fcba->flcomp |= freq << 21;
|
|
|
|
write_image(filename, image, size);
|
|
}
|
|
|
|
static void set_em100_mode(const char *filename, char *image, int size)
|
|
{
|
|
fcba_t *fcba = find_fcba(image, size);
|
|
if (!fcba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
int freq;
|
|
|
|
switch (ifd_version) {
|
|
case IFD_VERSION_1:
|
|
freq = SPI_FREQUENCY_20MHZ;
|
|
break;
|
|
case IFD_VERSION_2:
|
|
freq = SPI_FREQUENCY_17MHZ;
|
|
break;
|
|
default:
|
|
freq = SPI_FREQUENCY_17MHZ;
|
|
break;
|
|
}
|
|
|
|
fcba->flcomp &= ~(1 << 30);
|
|
set_spi_frequency(filename, image, size, freq);
|
|
}
|
|
|
|
static void set_chipdensity(const char *filename, char *image, int size,
|
|
unsigned int density)
|
|
{
|
|
fcba_t *fcba = find_fcba(image, size);
|
|
uint8_t mask, chip2_offset;
|
|
if (!fcba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
printf("Setting chip density to ");
|
|
decode_component_density(density);
|
|
printf("\n");
|
|
|
|
switch (ifd_version) {
|
|
case IFD_VERSION_1:
|
|
/* fail if selected density is not supported by this version */
|
|
if ( (density == COMPONENT_DENSITY_32MB) ||
|
|
(density == COMPONENT_DENSITY_64MB) ||
|
|
(density == COMPONENT_DENSITY_UNUSED) ) {
|
|
printf("error: Selected density not supported in IFD version 1.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
mask = 0x7;
|
|
chip2_offset = 3;
|
|
break;
|
|
case IFD_VERSION_2:
|
|
mask = 0xf;
|
|
chip2_offset = 4;
|
|
break;
|
|
default:
|
|
printf("error: Unknown IFD version\n");
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
|
|
/* clear chip density for corresponding chip */
|
|
switch (selected_chip) {
|
|
case 1:
|
|
fcba->flcomp &= ~mask;
|
|
break;
|
|
case 2:
|
|
fcba->flcomp &= ~(mask << chip2_offset);
|
|
break;
|
|
default: /*both chips*/
|
|
fcba->flcomp &= ~(mask | (mask << chip2_offset));
|
|
break;
|
|
}
|
|
|
|
/* set the new density */
|
|
if (selected_chip == 1 || selected_chip == 0)
|
|
fcba->flcomp |= (density); /* first chip */
|
|
if (selected_chip == 2 || selected_chip == 0)
|
|
fcba->flcomp |= (density << chip2_offset); /* second chip */
|
|
|
|
write_image(filename, image, size);
|
|
}
|
|
|
|
static int check_region(const frba_t *frba, unsigned int region_type)
|
|
{
|
|
region_t region;
|
|
|
|
if (!frba)
|
|
return 0;
|
|
|
|
region = get_region(frba, region_type);
|
|
return !!((region.base < region.limit) && (region.size > 0));
|
|
}
|
|
|
|
static void lock_descriptor(const char *filename, char *image, int size)
|
|
{
|
|
int wr_shift, rd_shift;
|
|
fmba_t *fmba = find_fmba(image, size);
|
|
const frba_t *frba = find_frba(image, size);
|
|
if (!fmba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
if (ifd_version >= IFD_VERSION_2) {
|
|
wr_shift = FLMSTR_WR_SHIFT_V2;
|
|
rd_shift = FLMSTR_RD_SHIFT_V2;
|
|
|
|
/* Clear non-reserved bits */
|
|
fmba->flmstr1 &= 0xff;
|
|
fmba->flmstr2 &= 0xff;
|
|
fmba->flmstr3 &= 0xff;
|
|
fmba->flmstr5 &= 0xff;
|
|
} else {
|
|
wr_shift = FLMSTR_WR_SHIFT_V1;
|
|
rd_shift = FLMSTR_RD_SHIFT_V1;
|
|
|
|
fmba->flmstr1 = 0;
|
|
fmba->flmstr2 = 0;
|
|
/* Requestor ID */
|
|
fmba->flmstr3 = 0x118;
|
|
}
|
|
|
|
switch (platform) {
|
|
case PLATFORM_APL:
|
|
case PLATFORM_GLK:
|
|
/* CPU/BIOS can read descriptor and BIOS */
|
|
fmba->flmstr1 |= 0x3 << rd_shift;
|
|
/* CPU/BIOS can write BIOS */
|
|
fmba->flmstr1 |= 0x2 << wr_shift;
|
|
/* TXE can read descriptor, BIOS and Device Expansion */
|
|
fmba->flmstr2 |= 0x23 << rd_shift;
|
|
/* TXE can only write Device Expansion */
|
|
fmba->flmstr2 |= 0x20 << wr_shift;
|
|
break;
|
|
case PLATFORM_CNL:
|
|
case PLATFORM_ICL:
|
|
case PLATFORM_SKLKBL:
|
|
case PLATFORM_TGL:
|
|
/* CPU/BIOS can read descriptor and BIOS. */
|
|
fmba->flmstr1 |= (1 << REGION_DESC) << rd_shift;
|
|
fmba->flmstr1 |= (1 << REGION_BIOS) << rd_shift;
|
|
/* CPU/BIOS can write BIOS. */
|
|
fmba->flmstr1 |= (1 << REGION_BIOS) << wr_shift;
|
|
/* ME can read descriptor and ME. */
|
|
fmba->flmstr2 |= (1 << REGION_DESC) << rd_shift;
|
|
fmba->flmstr2 |= (1 << REGION_ME) << rd_shift;
|
|
/* ME can write ME. */
|
|
fmba->flmstr2 |= (1 << REGION_ME) << wr_shift;
|
|
if (check_region(frba, REGION_GBE)) {
|
|
/* BIOS can read/write GbE. */
|
|
fmba->flmstr1 |= (1 << REGION_GBE) << rd_shift;
|
|
fmba->flmstr1 |= (1 << REGION_GBE) << wr_shift;
|
|
/* ME can read GbE. */
|
|
fmba->flmstr2 |= (1 << REGION_GBE) << rd_shift;
|
|
/* GbE can read descriptor and read/write GbE.. */
|
|
fmba->flmstr3 |= (1 << REGION_DESC) << rd_shift;
|
|
fmba->flmstr3 |= (1 << REGION_GBE) << rd_shift;
|
|
fmba->flmstr3 |= (1 << REGION_GBE) << wr_shift;
|
|
}
|
|
if (check_region(frba, REGION_PDR)) {
|
|
/* BIOS can read/write PDR. */
|
|
fmba->flmstr1 |= (1 << REGION_PDR) << rd_shift;
|
|
fmba->flmstr1 |= (1 << REGION_PDR) << wr_shift;
|
|
}
|
|
if (check_region(frba, REGION_EC)) {
|
|
/* BIOS can read EC. */
|
|
fmba->flmstr1 |= (1 << REGION_EC) << rd_shift;
|
|
/* EC can read descriptor and read/write EC. */
|
|
fmba->flmstr5 |= (1 << REGION_DESC) << rd_shift;
|
|
fmba->flmstr5 |= (1 << REGION_EC) << rd_shift;
|
|
fmba->flmstr5 |= (1 << REGION_EC) << wr_shift;
|
|
}
|
|
break;
|
|
default:
|
|
/* CPU/BIOS can read descriptor and BIOS. */
|
|
fmba->flmstr1 |= (1 << REGION_DESC) << rd_shift;
|
|
fmba->flmstr1 |= (1 << REGION_BIOS) << rd_shift;
|
|
/* CPU/BIOS can write BIOS. */
|
|
fmba->flmstr1 |= (1 << REGION_BIOS) << wr_shift;
|
|
/* ME can read descriptor and ME. */
|
|
fmba->flmstr2 |= (1 << REGION_DESC) << rd_shift;
|
|
fmba->flmstr2 |= (1 << REGION_ME) << rd_shift;
|
|
/* ME can write ME. */
|
|
fmba->flmstr2 |= (1 << REGION_ME) << wr_shift;
|
|
if (check_region(frba, REGION_GBE)) {
|
|
/* BIOS can read GbE. */
|
|
fmba->flmstr1 |= (1 << REGION_GBE) << rd_shift;
|
|
/* BIOS can write GbE. */
|
|
fmba->flmstr1 |= (1 << REGION_GBE) << wr_shift;
|
|
/* ME can read GbE. */
|
|
fmba->flmstr2 |= (1 << REGION_GBE) << rd_shift;
|
|
/* ME can write GbE. */
|
|
fmba->flmstr2 |= (1 << REGION_GBE) << wr_shift;
|
|
/* GbE can write GbE. */
|
|
fmba->flmstr3 |= (1 << REGION_GBE) << rd_shift;
|
|
/* GbE can read GbE. */
|
|
fmba->flmstr3 |= (1 << REGION_GBE) << wr_shift;
|
|
}
|
|
break;
|
|
}
|
|
|
|
write_image(filename, image, size);
|
|
}
|
|
|
|
static void unlock_descriptor(const char *filename, char *image, int size)
|
|
{
|
|
fmba_t *fmba = find_fmba(image, size);
|
|
if (!fmba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
if (ifd_version >= IFD_VERSION_2) {
|
|
/* Access bits for each region are read: 19:8 write: 31:20 */
|
|
fmba->flmstr1 = 0xffffff00 | (fmba->flmstr1 & 0xff);
|
|
fmba->flmstr2 = 0xffffff00 | (fmba->flmstr2 & 0xff);
|
|
fmba->flmstr3 = 0xffffff00 | (fmba->flmstr3 & 0xff);
|
|
fmba->flmstr5 = 0xffffff00 | (fmba->flmstr5 & 0xff);
|
|
} else {
|
|
fmba->flmstr1 = 0xffff0000;
|
|
fmba->flmstr2 = 0xffff0000;
|
|
/* Keep chipset specific Requester ID */
|
|
fmba->flmstr3 = 0x08080000 | (fmba->flmstr3 & 0xffff);
|
|
}
|
|
|
|
write_image(filename, image, size);
|
|
}
|
|
|
|
/* Set the AltMeDisable (or HAP for >= IFD_VERSION_2) */
|
|
static void fpsba_set_altmedisable(fpsba_t *fpsba, fmsba_t *fmsba, bool altmedisable)
|
|
{
|
|
if (ifd_version >= IFD_VERSION_2) {
|
|
printf("%sting the HAP bit to %s Intel ME...\n",
|
|
altmedisable?"Set":"Unset",
|
|
altmedisable?"disable":"enable");
|
|
if (altmedisable)
|
|
fpsba->pchstrp[0] |= (1 << 16);
|
|
else
|
|
fpsba->pchstrp[0] &= ~(1 << 16);
|
|
} else {
|
|
if (chipset >= CHIPSET_ICH8 && chipset <= CHIPSET_ICH10) {
|
|
printf("%sting the ICH_MeDisable, MCH_MeDisable, "
|
|
"and MCH_AltMeDisable to %s Intel ME...\n",
|
|
altmedisable?"Set":"Unset",
|
|
altmedisable?"disable":"enable");
|
|
if (altmedisable) {
|
|
/* MCH_MeDisable */
|
|
fmsba->data[0] |= 1;
|
|
/* MCH_AltMeDisable */
|
|
fmsba->data[0] |= (1 << 7);
|
|
/* ICH_MeDisable */
|
|
fpsba->pchstrp[0] |= 1;
|
|
} else {
|
|
fmsba->data[0] &= ~1;
|
|
fmsba->data[0] &= ~(1 << 7);
|
|
fpsba->pchstrp[0] &= ~1;
|
|
}
|
|
} else {
|
|
printf("%sting the AltMeDisable to %s Intel ME...\n",
|
|
altmedisable?"Set":"Unset",
|
|
altmedisable?"disable":"enable");
|
|
if (altmedisable)
|
|
fpsba->pchstrp[10] |= (1 << 7);
|
|
else
|
|
fpsba->pchstrp[10] &= ~(1 << 7);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void inject_region(const char *filename, char *image, int size,
|
|
unsigned int region_type, const char *region_fname)
|
|
{
|
|
frba_t *frba = find_frba(image, size);
|
|
if (!frba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
region_t region = get_region(frba, region_type);
|
|
if (region.size <= 0xfff) {
|
|
fprintf(stderr, "Region %s is disabled in target. Not injecting.\n",
|
|
region_name(region_type));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int region_fd = open(region_fname, O_RDONLY | O_BINARY);
|
|
if (region_fd == -1) {
|
|
perror("Could not open file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
struct stat buf;
|
|
if (fstat(region_fd, &buf) == -1) {
|
|
perror("Could not stat file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
int region_size = buf.st_size;
|
|
|
|
printf("File %s is %d bytes\n", region_fname, region_size);
|
|
|
|
if ( (region_size > region.size) || ((region_type != 1) &&
|
|
(region_size > region.size))) {
|
|
fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x)"
|
|
" bytes. Not injecting.\n",
|
|
region_name(region_type), region.size,
|
|
region.size, region_size, region_size);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int offset = 0;
|
|
if ((region_type == 1) && (region_size < region.size)) {
|
|
fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x)"
|
|
" bytes. Padding before injecting.\n",
|
|
region_name(region_type), region.size,
|
|
region.size, region_size, region_size);
|
|
offset = region.size - region_size;
|
|
memset(image + region.base, 0xff, offset);
|
|
}
|
|
|
|
if (size < region.base + offset + region_size) {
|
|
fprintf(stderr, "Output file is too small. (%d < %d)\n",
|
|
size, region.base + offset + region_size);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (read(region_fd, image + region.base + offset, region_size)
|
|
!= region_size) {
|
|
perror("Could not read file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
close(region_fd);
|
|
|
|
printf("Adding %s as the %s section of %s\n",
|
|
region_fname, region_name(region_type), filename);
|
|
write_image(filename, image, size);
|
|
}
|
|
|
|
static unsigned int next_pow2(unsigned int x)
|
|
{
|
|
unsigned int y = 1;
|
|
if (x == 0)
|
|
return 0;
|
|
while (y <= x)
|
|
y = y << 1;
|
|
|
|
return y;
|
|
}
|
|
|
|
/**
|
|
* Determine if two memory regions overlap.
|
|
*
|
|
* @param r1, r2 Memory regions to compare.
|
|
* @return 0 if the two regions are separate
|
|
* @return 1 if the two regions overlap
|
|
*/
|
|
static int regions_collide(const region_t *r1, const region_t *r2)
|
|
{
|
|
if ((r1->size == 0) || (r2->size == 0))
|
|
return 0;
|
|
|
|
/* r1 should be either completely below or completely above r2 */
|
|
return !(r1->limit < r2->base || r1->base > r2->limit);
|
|
}
|
|
|
|
static void new_layout(const char *filename, char *image, int size,
|
|
const char *layout_fname)
|
|
{
|
|
FILE *romlayout;
|
|
char tempstr[256];
|
|
char layout_region_name[256];
|
|
unsigned int i, j;
|
|
int region_number;
|
|
region_t current_regions[MAX_REGIONS];
|
|
region_t new_regions[MAX_REGIONS];
|
|
int new_extent = 0;
|
|
char *new_image;
|
|
|
|
/* load current descriptor map and regions */
|
|
frba_t *frba = find_frba(image, size);
|
|
if (!frba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
for (i = 0; i < max_regions; i++) {
|
|
current_regions[i] = get_region(frba, i);
|
|
new_regions[i] = get_region(frba, i);
|
|
}
|
|
|
|
/* read new layout */
|
|
romlayout = fopen(layout_fname, "r");
|
|
|
|
if (!romlayout) {
|
|
perror("Could not read layout file.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
while (!feof(romlayout)) {
|
|
char *tstr1, *tstr2;
|
|
|
|
if (2 != fscanf(romlayout, "%255s %255s\n", tempstr,
|
|
layout_region_name))
|
|
continue;
|
|
|
|
region_number = region_num(layout_region_name);
|
|
if (region_number < 0)
|
|
continue;
|
|
|
|
tstr1 = strtok(tempstr, ":");
|
|
tstr2 = strtok(NULL, ":");
|
|
if (!tstr1 || !tstr2) {
|
|
fprintf(stderr, "Could not parse layout file.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
new_regions[region_number].base = strtol(tstr1,
|
|
(char **)NULL, 16);
|
|
new_regions[region_number].limit = strtol(tstr2,
|
|
(char **)NULL, 16);
|
|
new_regions[region_number].size =
|
|
new_regions[region_number].limit -
|
|
new_regions[region_number].base + 1;
|
|
|
|
if (new_regions[region_number].size < 0)
|
|
new_regions[region_number].size = 0;
|
|
}
|
|
fclose(romlayout);
|
|
|
|
/* check new layout */
|
|
for (i = 0; i < max_regions; i++) {
|
|
if (new_regions[i].size == 0)
|
|
continue;
|
|
|
|
if (new_regions[i].size < current_regions[i].size) {
|
|
printf("DANGER: Region %s is shrinking.\n",
|
|
region_name(i));
|
|
printf(" The region will be truncated to fit.\n");
|
|
printf(" This may result in an unusable image.\n");
|
|
}
|
|
|
|
for (j = i + 1; j < max_regions; j++) {
|
|
if (regions_collide(&new_regions[i], &new_regions[j])) {
|
|
fprintf(stderr, "Regions would overlap.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* detect if the image size should grow */
|
|
if (new_extent < new_regions[i].limit)
|
|
new_extent = new_regions[i].limit;
|
|
}
|
|
|
|
/* check if the image is actually a Flash Descriptor region */
|
|
if (size == new_regions[0].size) {
|
|
printf("The image is a single Flash Descriptor:\n");
|
|
printf(" Only the descriptor will be modified\n");
|
|
new_extent = size;
|
|
} else {
|
|
new_extent = next_pow2(new_extent - 1);
|
|
if (new_extent != size) {
|
|
printf("The image has changed in size.\n");
|
|
printf("The old image is %d bytes.\n", size);
|
|
printf("The new image is %d bytes.\n", new_extent);
|
|
}
|
|
}
|
|
|
|
/* copy regions to a new image */
|
|
new_image = malloc(new_extent);
|
|
memset(new_image, 0xff, new_extent);
|
|
for (i = 0; i < max_regions; i++) {
|
|
int copy_size = new_regions[i].size;
|
|
int offset_current = 0, offset_new = 0;
|
|
const region_t *current = ¤t_regions[i];
|
|
const region_t *new = &new_regions[i];
|
|
|
|
if (new->size == 0)
|
|
continue;
|
|
|
|
if (new->size > current->size) {
|
|
/* copy from the end of the current region */
|
|
copy_size = current->size;
|
|
if (i == REGION_BIOS)
|
|
offset_new = new->size - current->size;
|
|
}
|
|
|
|
if ((i == REGION_BIOS) && (new->size < current->size)) {
|
|
/* copy BIOS region to the end of the new region */
|
|
offset_current = current->size - new->size;
|
|
}
|
|
|
|
if (size < current->base + offset_current + copy_size) {
|
|
printf("Skip descriptor %d (%s) (region missing in the old image)\n", i,
|
|
region_name(i));
|
|
continue;
|
|
};
|
|
|
|
printf("Copy Descriptor %d (%s) (%d bytes)\n", i,
|
|
region_name(i), copy_size);
|
|
printf(" from %08x+%08x:%08x (%10d)\n", current->base,
|
|
offset_current, current->limit, current->size);
|
|
printf(" to %08x+%08x:%08x (%10d)\n", new->base,
|
|
offset_new, new->limit, new->size);
|
|
|
|
memcpy(new_image + new->base + offset_new,
|
|
image + current->base + offset_current,
|
|
copy_size);
|
|
}
|
|
|
|
/* update new descriptor regions */
|
|
frba = find_frba(new_image, new_extent);
|
|
if (!frba)
|
|
exit(EXIT_FAILURE);
|
|
|
|
printf("Modify Flash Descriptor regions\n");
|
|
for (i = 1; i < max_regions; i++)
|
|
set_region(frba, i, &new_regions[i]);
|
|
|
|
write_image(filename, new_image, new_extent);
|
|
free(new_image);
|
|
}
|
|
|
|
static void print_version(void)
|
|
{
|
|
printf("ifdtool v%s -- ", IFDTOOL_VERSION);
|
|
printf("Copyright (C) 2011 Google Inc.\n\n");
|
|
printf
|
|
("This program is free software: you can redistribute it and/or modify\n"
|
|
"it under the terms of the GNU General Public License as published by\n"
|
|
"the Free Software Foundation, version 2 of the License.\n\n"
|
|
"This program is distributed in the hope that it will be useful,\n"
|
|
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
|
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
|
"GNU General Public License for more details.\n\n");
|
|
}
|
|
|
|
static void print_usage(const char *name)
|
|
{
|
|
printf("usage: %s [-vhdix?] <filename>\n", name);
|
|
printf("\n"
|
|
" -d | --dump: dump intel firmware descriptor\n"
|
|
" -f | --layout <filename> dump regions into a flashrom layout file\n"
|
|
" -t | --validate Validate that the firmware descriptor layout matches the fmap layout\n"
|
|
" -x | --extract: extract intel fd modules\n"
|
|
" -i | --inject <region>:<module> inject file <module> into region <region>\n"
|
|
" -n | --newlayout <filename> update regions using a flashrom layout file\n"
|
|
" -s | --spifreq <17|20|30|33|48|50> set the SPI frequency\n"
|
|
" -D | --density <512|1|2|4|8|16|32|64> set chip density (512 in KByte, others in MByte)\n"
|
|
" -C | --chip <0|1|2> select spi chip on which to operate\n"
|
|
" can only be used once per run:\n"
|
|
" 0 - both chips (default), 1 - first chip, 2 - second chip\n"
|
|
" -e | --em100 set SPI frequency to 20MHz and disable\n"
|
|
" Dual Output Fast Read Support\n"
|
|
" -l | --lock Lock firmware descriptor and ME region\n"
|
|
" -u | --unlock Unlock firmware descriptor and ME region\n"
|
|
" -M | --altmedisable <0|1> Set the MeDisable and AltMeDisable (or HAP for skylake or newer platform)\n"
|
|
" bits to disable ME\n"
|
|
" -p | --platform Add platform-specific quirks\n"
|
|
" aplk - Apollo Lake\n"
|
|
" cnl - Cannon Lake\n"
|
|
" glk - Gemini Lake\n"
|
|
" sklkbl - Skylake/Kaby Lake\n"
|
|
" -v | --version: print the version\n"
|
|
" -h | --help: print this help\n\n"
|
|
"<region> is one of Descriptor, BIOS, ME, GbE, Platform\n"
|
|
"\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int opt, option_index = 0;
|
|
int mode_dump = 0, mode_extract = 0, mode_inject = 0, mode_spifreq = 0;
|
|
int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0, mode_validate = 0;
|
|
int mode_layout = 0, mode_newlayout = 0, mode_density = 0;
|
|
int mode_altmedisable = 0, altmedisable = 0;
|
|
char *region_type_string = NULL, *region_fname = NULL;
|
|
const char *layout_fname = NULL;
|
|
int region_type = -1, inputfreq = 0;
|
|
unsigned int new_density = 0;
|
|
enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ;
|
|
|
|
static const struct option long_options[] = {
|
|
{"dump", 0, NULL, 'd'},
|
|
{"layout", 1, NULL, 'f'},
|
|
{"extract", 0, NULL, 'x'},
|
|
{"inject", 1, NULL, 'i'},
|
|
{"newlayout", 1, NULL, 'n'},
|
|
{"spifreq", 1, NULL, 's'},
|
|
{"density", 1, NULL, 'D'},
|
|
{"chip", 1, NULL, 'C'},
|
|
{"altmedisable", 1, NULL, 'M'},
|
|
{"em100", 0, NULL, 'e'},
|
|
{"lock", 0, NULL, 'l'},
|
|
{"unlock", 0, NULL, 'u'},
|
|
{"version", 0, NULL, 'v'},
|
|
{"help", 0, NULL, 'h'},
|
|
{"platform", 0, NULL, 'p'},
|
|
{"validate", 0, NULL, 't'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
while ((opt = getopt_long(argc, argv, "df:D:C:M:xi:n:s:p:eluvth?",
|
|
long_options, &option_index)) != EOF) {
|
|
switch (opt) {
|
|
case 'd':
|
|
mode_dump = 1;
|
|
break;
|
|
case 'f':
|
|
mode_layout = 1;
|
|
layout_fname = strdup(optarg);
|
|
if (!layout_fname) {
|
|
fprintf(stderr, "No layout file specified\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'x':
|
|
mode_extract = 1;
|
|
break;
|
|
case 'i':
|
|
// separate type and file name
|
|
region_type_string = strdup(optarg);
|
|
region_fname = strchr(region_type_string, ':');
|
|
if (!region_fname) {
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
region_fname[0] = '\0';
|
|
region_fname++;
|
|
// Descriptor, BIOS, ME, GbE, Platform
|
|
// valid type?
|
|
if (!strcasecmp("Descriptor", region_type_string))
|
|
region_type = 0;
|
|
else if (!strcasecmp("BIOS", region_type_string))
|
|
region_type = 1;
|
|
else if (!strcasecmp("ME", region_type_string))
|
|
region_type = 2;
|
|
else if (!strcasecmp("GbE", region_type_string))
|
|
region_type = 3;
|
|
else if (!strcasecmp("Platform", region_type_string))
|
|
region_type = 4;
|
|
else if (!strcasecmp("EC", region_type_string))
|
|
region_type = 8;
|
|
if (region_type == -1) {
|
|
fprintf(stderr, "No such region type: '%s'\n\n",
|
|
region_type_string);
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
mode_inject = 1;
|
|
break;
|
|
case 'n':
|
|
mode_newlayout = 1;
|
|
layout_fname = strdup(optarg);
|
|
if (!layout_fname) {
|
|
fprintf(stderr, "No layout file specified\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'D':
|
|
mode_density = 1;
|
|
new_density = strtoul(optarg, NULL, 0);
|
|
switch (new_density) {
|
|
case 512:
|
|
new_density = COMPONENT_DENSITY_512KB;
|
|
break;
|
|
case 1:
|
|
new_density = COMPONENT_DENSITY_1MB;
|
|
break;
|
|
case 2:
|
|
new_density = COMPONENT_DENSITY_2MB;
|
|
break;
|
|
case 4:
|
|
new_density = COMPONENT_DENSITY_4MB;
|
|
break;
|
|
case 8:
|
|
new_density = COMPONENT_DENSITY_8MB;
|
|
break;
|
|
case 16:
|
|
new_density = COMPONENT_DENSITY_16MB;
|
|
break;
|
|
case 32:
|
|
new_density = COMPONENT_DENSITY_32MB;
|
|
break;
|
|
case 64:
|
|
new_density = COMPONENT_DENSITY_64MB;
|
|
break;
|
|
case 0:
|
|
new_density = COMPONENT_DENSITY_UNUSED;
|
|
break;
|
|
default:
|
|
printf("error: Unknown density\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'C':
|
|
selected_chip = strtol(optarg, NULL, 0);
|
|
if (selected_chip > 2) {
|
|
fprintf(stderr, "error: Invalid chip selection\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'M':
|
|
mode_altmedisable = 1;
|
|
altmedisable = strtol(optarg, NULL, 0);
|
|
if (altmedisable > 1) {
|
|
fprintf(stderr, "error: Illegal value\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 's':
|
|
// Parse the requested SPI frequency
|
|
inputfreq = strtol(optarg, NULL, 0);
|
|
switch (inputfreq) {
|
|
case 17:
|
|
spifreq = SPI_FREQUENCY_17MHZ;
|
|
break;
|
|
case 20:
|
|
spifreq = SPI_FREQUENCY_20MHZ;
|
|
break;
|
|
case 30:
|
|
spifreq = SPI_FREQUENCY_50MHZ_30MHZ;
|
|
break;
|
|
case 33:
|
|
spifreq = SPI_FREQUENCY_33MHZ;
|
|
break;
|
|
case 48:
|
|
spifreq = SPI_FREQUENCY_48MHZ;
|
|
break;
|
|
case 50:
|
|
spifreq = SPI_FREQUENCY_50MHZ_30MHZ;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "Invalid SPI Frequency: %d\n",
|
|
inputfreq);
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
mode_spifreq = 1;
|
|
break;
|
|
case 'e':
|
|
mode_em100 = 1;
|
|
break;
|
|
case 'l':
|
|
mode_locked = 1;
|
|
if (mode_unlocked == 1) {
|
|
fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'u':
|
|
mode_unlocked = 1;
|
|
if (mode_locked == 1) {
|
|
fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
case 'p':
|
|
if (!strcmp(optarg, "aplk")) {
|
|
platform = PLATFORM_APL;
|
|
} else if (!strcmp(optarg, "cnl")) {
|
|
platform = PLATFORM_CNL;
|
|
} else if (!strcmp(optarg, "glk")) {
|
|
platform = PLATFORM_GLK;
|
|
} else if (!strcmp(optarg, "icl")) {
|
|
platform = PLATFORM_ICL;
|
|
} else if (!strcmp(optarg, "jsl")) {
|
|
platform = PLATFORM_JSL;
|
|
} else if (!strcmp(optarg, "sklkbl")) {
|
|
platform = PLATFORM_SKLKBL;
|
|
} else if (!strcmp(optarg, "tgl")) {
|
|
platform = PLATFORM_TGL;
|
|
} else {
|
|
fprintf(stderr, "Unknown platform: %s\n", optarg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
fprintf(stderr, "Platform is: %s\n", optarg);
|
|
break;
|
|
case 't':
|
|
mode_validate = 1;
|
|
break;
|
|
case 'v':
|
|
print_version();
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
default:
|
|
print_usage(argv[0]);
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((mode_dump + mode_layout + mode_extract + mode_inject +
|
|
mode_newlayout + (mode_spifreq | mode_em100 | mode_unlocked |
|
|
mode_locked) + mode_altmedisable + mode_validate) > 1) {
|
|
fprintf(stderr, "You may not specify more than one mode.\n\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if ((mode_dump + mode_layout + mode_extract + mode_inject +
|
|
mode_newlayout + mode_spifreq + mode_em100 + mode_locked +
|
|
mode_unlocked + mode_density + mode_altmedisable + mode_validate) == 0) {
|
|
fprintf(stderr, "You need to specify a mode.\n\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (optind + 1 != argc) {
|
|
fprintf(stderr, "You need to specify a file.\n\n");
|
|
print_usage(argv[0]);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
char *filename = argv[optind];
|
|
int bios_fd = open(filename, O_RDONLY | O_BINARY);
|
|
if (bios_fd == -1) {
|
|
perror("Could not open file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
struct stat buf;
|
|
if (fstat(bios_fd, &buf) == -1) {
|
|
perror("Could not stat file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
int size = buf.st_size;
|
|
|
|
printf("File %s is %d bytes\n", filename, size);
|
|
|
|
char *image = malloc(size);
|
|
if (!image) {
|
|
printf("Out of memory.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (read(bios_fd, image, size) != size) {
|
|
perror("Could not read file");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
close(bios_fd);
|
|
|
|
check_ifd_version(image, size);
|
|
|
|
if (mode_dump)
|
|
dump_fd(image, size);
|
|
|
|
if (mode_layout)
|
|
dump_layout(image, size, layout_fname);
|
|
|
|
if (mode_extract)
|
|
write_regions(image, size);
|
|
|
|
if (mode_validate)
|
|
validate_layout(image, size);
|
|
|
|
if (mode_inject)
|
|
inject_region(filename, image, size, region_type,
|
|
region_fname);
|
|
|
|
if (mode_newlayout)
|
|
new_layout(filename, image, size, layout_fname);
|
|
|
|
if (mode_spifreq)
|
|
set_spi_frequency(filename, image, size, spifreq);
|
|
|
|
if (mode_density)
|
|
set_chipdensity(filename, image, size, new_density);
|
|
|
|
if (mode_em100)
|
|
set_em100_mode(filename, image, size);
|
|
|
|
if (mode_locked)
|
|
lock_descriptor(filename, image, size);
|
|
|
|
if (mode_unlocked)
|
|
unlock_descriptor(filename, image, size);
|
|
|
|
if (mode_altmedisable) {
|
|
fpsba_t *fpsba = find_fpsba(image, size);
|
|
fmsba_t *fmsba = find_fmsba(image, size);
|
|
fpsba_set_altmedisable(fpsba, fmsba, altmedisable);
|
|
write_image(filename, image, size);
|
|
}
|
|
|
|
free(image);
|
|
|
|
return 0;
|
|
}
|