Files
OpenCellular/host/arch/arm/lib/crossystem_arch.c
Simon Glass 47779880b2 Improve kernel tests to pass valgrind
At present the kernel tests produce valgrind errors since the GPT data is
sometimes accessed before it is read. This is unnecessary, so update the
code to avoid this.

BUG=chrome-os-partner:21115
BRANCH=pit
TEST=manual
valgrind --leak-check=full  ./build/tests/vboot_kernel_tests

See that we no longer get valgrind errors.

Change-Id: I9e9660e38a62a735cf01a37c2d81ddb5ab8b1528
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/66173
2013-08-25 16:57:27 -07:00

583 lines
15 KiB
C

/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include "vboot_common.h"
#include "vboot_nvstorage.h"
#include "host_common.h"
#include "crossystem.h"
#include "crossystem_arch.h"
#define MOSYS_PATH "/usr/sbin/mosys"
/* Base name for firmware FDT files */
#define FDT_BASE_PATH "/proc/device-tree/firmware/chromeos"
/* Path to compatible FDT entry */
#define FDT_COMPATIBLE_PATH "/proc/device-tree/compatible"
/* Path to the chromeos_arm platform device */
#define PLATFORM_DEV_PATH "/sys/devices/platform/chromeos_arm"
/* Device for NVCTX write */
#define NVCTX_PATH "/dev/mmcblk%d"
/* Base name for GPIO files */
#define GPIO_BASE_PATH "/sys/class/gpio"
#define GPIO_EXPORT_PATH GPIO_BASE_PATH "/export"
/* Name of NvStorage type property */
#define FDT_NVSTORAGE_TYPE_PROP "nonvolatile-context-storage"
/* Errors */
#define E_FAIL -1
#define E_FILEOP -2
#define E_MEM -3
/* Common constants */
#define FNAME_SIZE 80
#define SECTOR_SIZE 512
#define MAX_NMMCBLK 9
typedef struct PlatformFamily {
const char* compatible_string; /* Last string in FDT compatible entry */
const char* platform_string; /* String to return */
} PlatformFamily;
/* Array of platform family names, terminated with a NULL entry */
const PlatformFamily platform_family_array[] = {
{"nvidia,tegra250", "Tegra2"},
{"nvidia,tegra20", "Tegra2"},
{"ti,omap4", "OMAP4"},
{"ti,omap3", "OMAP3"},
{"samsung,exynos4210", "EXYNOS4"},
{"samsung,exynos5250", "EXYNOS5"},
{"samsung,exynos5420", "EXYNOS5"},
/* Terminate with NULL entry */
{NULL, NULL}
};
static int FindEmmcDev(void) {
int mmcblk;
char filename[FNAME_SIZE];
for (mmcblk = 0; mmcblk < MAX_NMMCBLK; mmcblk++) {
/* Get first non-removable mmc block device */
snprintf(filename, sizeof(filename), "/sys/block/mmcblk%d/removable",
mmcblk);
if (ReadFileInt(filename) == 0)
return mmcblk;
}
/* eMMC not found */
return E_FAIL;
}
static int ReadFdtValue(const char *property, int *value) {
char filename[FNAME_SIZE];
FILE *file;
int data = 0;
snprintf(filename, sizeof(filename), FDT_BASE_PATH "/%s", property);
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Unable to open FDT property %s\n", property);
return E_FILEOP;
}
fread(&data, 1, sizeof(data), file);
fclose(file);
if (value)
*value = ntohl(data); /* FDT is network byte order */
return 0;
}
static int ReadFdtInt(const char *property) {
int value = 0;
if (ReadFdtValue(property, &value))
return E_FAIL;
return value;
}
static void GetFdtPropertyPath(const char *property, char *path, size_t size) {
if (property[0] == '/')
StrCopy(path, property, size);
else
snprintf(path, size, FDT_BASE_PATH "/%s", property);
}
static int FdtPropertyExist(const char *property) {
char filename[FNAME_SIZE];
struct stat file_status;
GetFdtPropertyPath(property, filename, sizeof(filename));
if (!stat(filename, &file_status))
return 1; // It exists!
else
return 0; // It does not exist or some error happened.
}
static int ReadFdtBlock(const char *property, void **block, size_t *size) {
char filename[FNAME_SIZE];
FILE *file;
size_t property_size;
char *data;
if (!block)
return E_FAIL;
GetFdtPropertyPath(property, filename, sizeof(filename));
file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Unable to open FDT property %s\n", property);
return E_FILEOP;
}
fseek(file, 0, SEEK_END);
property_size = ftell(file);
rewind(file);
data = malloc(property_size +1);
if (!data) {
fclose(file);
return E_MEM;
}
data[property_size] = 0;
if (1 != fread(data, property_size, 1, file)) {
fprintf(stderr, "Unable to read from property %s\n", property);
fclose(file);
free(data);
return E_FILEOP;
}
fclose(file);
*block = data;
if (size)
*size = property_size;
return 0;
}
static char * ReadFdtString(const char *property) {
void *str = NULL;
/* Do not need property size */
ReadFdtBlock(property, &str, 0);
return (char *)str;
}
static char * ReadFdtPlatformFamily(void) {
char *compat = NULL;
char *s;
const PlatformFamily* p;
size_t size = 0;
int slen;
if(ReadFdtBlock(FDT_COMPATIBLE_PATH, (void **)&compat, &size))
return NULL;
if (size > 0)
compat[size-1] = 0;
/* Check each null separated string in compatible against the family array */
s = compat;
while ((s-compat) < size) {
slen = strlen(s);
for (p = platform_family_array; p->compatible_string; p++) {
if (!strcmp(s, p->compatible_string)) {
free(compat);
return strdup(p->platform_string);
}
}
s += slen + 1;
}
/* No recognized 'compatible' entry found */
free(compat);
return NULL;
}
static int VbGetPlatformGpioStatus(const char* name) {
char gpio_name[FNAME_SIZE];
int value;
snprintf(gpio_name, sizeof(gpio_name), "%s/%s/value",
PLATFORM_DEV_PATH, name);
value = ReadFileInt(gpio_name);
return value;
}
static int VbGetGpioStatus(unsigned gpio_number) {
char gpio_name[FNAME_SIZE];
int value;
snprintf(gpio_name, sizeof(gpio_name), "%s/gpio%d/value",
GPIO_BASE_PATH, gpio_number);
value = ReadFileInt(gpio_name);
if (value == -1) {
/* Try exporting the GPIO */
FILE* f = fopen(GPIO_EXPORT_PATH, "wt");
if (!f)
return -1;
fprintf(f, "%d", gpio_number);
fclose(f);
/* Try re-reading the GPIO value */
value = ReadFileInt(gpio_name);
}
return value;
}
static int VbGetVarGpio(const char* name) {
int gpio_num;
void *pp = NULL;
int *prop;
size_t proplen = 0;
int ret = 0;
/* TODO: This should at some point in the future use the phandle
* to find the gpio chip and thus the base number. Assume 0 now,
* which isn't 100% future-proof (i.e. if one of the switches gets
* moved to an offchip gpio controller.
*/
ret = ReadFdtBlock(name, &pp, &proplen);
if (ret || !pp || proplen != 12) {
ret = 2;
goto out;
}
prop = pp;
gpio_num = ntohl(prop[1]);
/*
* TODO(chrome-os-partner:11296): Use gpio_num == 0 to denote non-exist
* GPIO for now, at the risk that one day we might actually want to read
* from a GPIO port 0. We should figure out how to represent "non-exist"
* properly.
*/
if (gpio_num)
ret = VbGetGpioStatus(gpio_num);
else
ret = -1;
out:
if (pp)
free(pp);
return ret;
}
static int ExecuteMosys(char * const argv[], char *buf, size_t bufsize) {
int status, mosys_to_crossystem[2];
pid_t pid;
ssize_t n;
if (pipe(mosys_to_crossystem) < 0) {
VBDEBUG(("pipe() error\n"));
return -1;
}
if ((pid = fork()) < 0) {
VBDEBUG(("fork() error\n"));
close(mosys_to_crossystem[0]);
close(mosys_to_crossystem[1]);
return -1;
} else if (!pid) { /* Child */
close(mosys_to_crossystem[0]);
/* Redirect pipe's write-end to mosys' stdout */
if (STDOUT_FILENO != mosys_to_crossystem[1]) {
if (dup2(mosys_to_crossystem[1], STDOUT_FILENO) != STDOUT_FILENO) {
VBDEBUG(("stdout dup2() failed (mosys)\n"));
close(mosys_to_crossystem[1]);
exit(1);
}
}
/* Execute mosys */
execv(MOSYS_PATH, argv);
/* We shouldn't be here; exit now! */
VBDEBUG(("execv() of mosys failed\n"));
close(mosys_to_crossystem[1]);
exit(1);
} else { /* Parent */
close(mosys_to_crossystem[1]);
if (bufsize) {
bufsize--; /* Reserve 1 byte for '\0' */
while ((n = read(mosys_to_crossystem[0], buf, bufsize)) > 0) {
buf += n;
bufsize -= n;
}
*buf = '\0';
} else {
n = 0;
}
close(mosys_to_crossystem[0]);
if (n < 0)
VBDEBUG(("read() error while reading output from mosys\n"));
if (waitpid(pid, &status, 0) < 0 || status) {
VBDEBUG(("waitpid() or mosys error\n"));
fprintf(stderr, "waitpid() or mosys error\n");
return -1;
}
if (n < 0)
return -1;
}
return 0;
}
static int VbReadNvStorage_mkbp(VbNvContext* vnc) {
char hexstring[VBNV_BLOCK_SIZE * 2 + 32]; /* Reserve extra 32 bytes */
char * const argv[] = {
MOSYS_PATH, "nvram", "vboot", "read", NULL
};
char hexdigit[3];
int i;
if (ExecuteMosys(argv, hexstring, sizeof(hexstring)))
return -1;
hexdigit[2] = '\0';
for (i = 0; i < VBNV_BLOCK_SIZE; i++) {
hexdigit[0] = hexstring[i * 2];
hexdigit[1] = hexstring[i * 2 + 1];
vnc->raw[i] = strtol(hexdigit, NULL, 16);
}
return 0;
}
static int VbWriteNvStorage_mkbp(VbNvContext* vnc) {
char hexstring[VBNV_BLOCK_SIZE * 2 + 1];
char * const argv[] = {
MOSYS_PATH, "nvram", "vboot", "write", hexstring, NULL
};
int i;
for (i = 0; i < VBNV_BLOCK_SIZE; i++)
snprintf(hexstring + i * 2, 3, "%02x", vnc->raw[i]);
hexstring[sizeof(hexstring) - 1] = '\0';
if (ExecuteMosys(argv, NULL, 0))
return -1;
return 0;
}
static int VbReadNvStorage_disk(VbNvContext* vnc) {
int nvctx_fd = -1;
uint8_t sector[SECTOR_SIZE];
int rv = -1;
char nvctx_path[FNAME_SIZE];
int emmc_dev;
int lba = ReadFdtInt("nonvolatile-context-lba");
int offset = ReadFdtInt("nonvolatile-context-offset");
int size = ReadFdtInt("nonvolatile-context-size");
emmc_dev = FindEmmcDev();
if (emmc_dev < 0)
return E_FAIL;
snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
if (size != sizeof(vnc->raw) || (size + offset > SECTOR_SIZE))
return E_FAIL;
nvctx_fd = open(nvctx_path, O_RDONLY);
if (nvctx_fd == -1) {
fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__, nvctx_path);
goto out;
}
lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
rv = read(nvctx_fd, sector, SECTOR_SIZE);
if (size <= 0) {
fprintf(stderr, "%s: failed to read nvctx from device %s\n",
__FUNCTION__, nvctx_path);
goto out;
}
Memcpy(vnc->raw, sector+offset, size);
rv = 0;
out:
if (nvctx_fd > 0)
close(nvctx_fd);
return rv;
}
static int VbWriteNvStorage_disk(VbNvContext* vnc) {
int nvctx_fd = -1;
uint8_t sector[SECTOR_SIZE];
int rv = -1;
char nvctx_path[FNAME_SIZE];
int emmc_dev;
int lba = ReadFdtInt("nonvolatile-context-lba");
int offset = ReadFdtInt("nonvolatile-context-offset");
int size = ReadFdtInt("nonvolatile-context-size");
emmc_dev = FindEmmcDev();
if (emmc_dev < 0)
return E_FAIL;
snprintf(nvctx_path, sizeof(nvctx_path), NVCTX_PATH, emmc_dev);
if (size != sizeof(vnc->raw) || (size + offset > SECTOR_SIZE))
return E_FAIL;
do {
nvctx_fd = open(nvctx_path, O_RDWR);
if (nvctx_fd == -1) {
fprintf(stderr, "%s: failed to open %s\n", __FUNCTION__, nvctx_path);
break;
}
lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
rv = read(nvctx_fd, sector, SECTOR_SIZE);
if (rv <= 0) {
fprintf(stderr, "%s: failed to read nvctx from device %s\n",
__FUNCTION__, nvctx_path);
break;
}
Memcpy(sector+offset, vnc->raw, size);
lseek(nvctx_fd, lba * SECTOR_SIZE, SEEK_SET);
rv = write(nvctx_fd, sector, SECTOR_SIZE);
if (rv <= 0) {
fprintf(stderr, "%s: failed to write nvctx to device %s\n",
__FUNCTION__, nvctx_path);
break;
}
/* Must flush buffer cache here to make sure it goes to disk */
rv = ioctl(nvctx_fd, BLKFLSBUF, 0);
if (rv < 0) {
fprintf(stderr, "%s: failed to flush nvctx to device %s\n",
__FUNCTION__, nvctx_path);
break;
}
rv = 0;
} while (0);
if (nvctx_fd > 0)
close(nvctx_fd);
return rv;
}
int VbReadNvStorage(VbNvContext* vnc) {
/* Default to disk for older firmware which does not provide storage type */
char *media;
if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
return VbReadNvStorage_disk(vnc);
media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
if (!strcmp(media, "disk"))
return VbReadNvStorage_disk(vnc);
if (!strcmp(media, "mkbp"))
return VbReadNvStorage_mkbp(vnc);
return -1;
}
int VbWriteNvStorage(VbNvContext* vnc) {
/* Default to disk for older firmware which does not provide storage type */
char *media;
if (!FdtPropertyExist(FDT_NVSTORAGE_TYPE_PROP))
return VbWriteNvStorage_disk(vnc);
media = ReadFdtString(FDT_NVSTORAGE_TYPE_PROP);
if (!strcmp(media, "disk"))
return VbWriteNvStorage_disk(vnc);
if (!strcmp(media, "mkbp"))
return VbWriteNvStorage_mkbp(vnc);
return -1;
}
VbSharedDataHeader *VbSharedDataRead(void) {
void *block = NULL;
size_t size = 0;
if (ReadFdtBlock("vboot-shared-data", &block, &size))
return NULL;
VbSharedDataHeader *p = (VbSharedDataHeader *)block;
if (p->magic != VB_SHARED_DATA_MAGIC) {
fprintf(stderr, "%s: failed to validate magic in "
"VbSharedDataHeader (%x != %x)\n",
__FUNCTION__, p->magic, VB_SHARED_DATA_MAGIC);
return NULL;
}
return (VbSharedDataHeader *)block;
}
int VbGetArchPropertyInt(const char* name) {
if (!strcasecmp(name, "fmap_base")) {
return ReadFdtInt("fmap-offset");
} else if (!strcasecmp(name, "devsw_cur")) {
/* Systems with virtual developer switches return at-boot value */
int flags = VbGetSystemPropertyInt("vdat_flags");
if ((flags != -1) && (flags & VBSD_HONOR_VIRT_DEV_SWITCH))
return VbGetSystemPropertyInt("devsw_boot");
return VbGetVarGpio("developer-switch");
} else if (!strcasecmp(name, "recoverysw_cur")) {
return VbGetVarGpio("recovery-switch");
} else if (!strcasecmp(name, "wpsw_cur")) {
int value;
/* Try finding the GPIO through the chromeos_arm platform device first. */
value = VbGetPlatformGpioStatus("write-protect");
if (value != -1)
return value;
return VbGetVarGpio("write-protect-switch");
} else if (!strcasecmp(name, "recoverysw_ec_boot"))
/* TODO: read correct value using ectool */
return 0;
else
return -1;
}
const char* VbGetArchPropertyString(const char* name, char* dest, int size) {
char *str = NULL;
char *rv = NULL;
char *prop = NULL;
if (!strcasecmp(name,"arch"))
return StrCopy(dest, "arm", size);
/* Properties from fdt */
if (!strcasecmp(name, "ro_fwid"))
prop = "readonly-firmware-version";
else if (!strcasecmp(name, "hwid"))
prop = "hardware-id";
else if (!strcasecmp(name, "fwid"))
prop = "firmware-version";
else if (!strcasecmp(name, "mainfw_type"))
prop = "firmware-type";
else if (!strcasecmp(name, "ecfw_act"))
prop = "active-ec-firmware";
else if (!strcasecmp(name, "ddr_type"))
prop = "ddr-type";
if (prop)
str = ReadFdtString(prop);
if (!strcasecmp(name, "platform_family"))
str = ReadFdtPlatformFamily();
if (str) {
rv = StrCopy(dest, str, size);
free(str);
return rv;
}
return NULL;
}
int VbSetArchPropertyInt(const char* name, int value) {
/* All is handled in arch independent fashion */
return -1;
}
int VbSetArchPropertyString(const char* name, const char* value) {
/* All is handled in arch independent fashion */
return -1;
}
int VbArchInit(void)
{
return 0;
}