blob: 26743cb5998082eee9e44adf80b0e600a227964b [file] [log] [blame]
/* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1 /* for strcasestr() */
#endif
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include "mosys/alloc.h"
#include "mosys/callbacks.h"
#include "mosys/globals.h"
#include "mosys/log.h"
#include "mosys/platform.h"
#include "lib/acpi.h"
#include "lib/file.h"
#include "lib/probe.h"
#include "lib/smbios.h"
#include "lib/string.h"
#ifndef LINE_MAX
#define LINE_MAX 512
#endif
int probe_hwid(const char *hwids[])
{
char *id;
int ret = 0;
if (acpi_get_hwid(&id) < 0)
return 0;
if (strlfind(id, hwids, 0)) {
ret = 1;
lprintf(LOG_DEBUG, "%s: matched id \"%s\"\n", __func__, id);
}
free(id);
return ret;
}
int probe_frid(const char *hwids[])
{
int ret = 0;
off_t len;
static char *id = NULL;
if (!id) {
char *raw_frid = NULL, *tmp;
if (acpi_get_frid(&raw_frid) < 0)
goto probe_frid_done;
/* FRID begins with platform name, followed by a dot, followed
* by revision ID */
tmp = strchr(raw_frid, '.');
if (!tmp) {
lprintf(LOG_DEBUG, "%s: Invalid FRID: \"%s\"\n",
__func__, raw_frid);
free(raw_frid);
goto probe_frid_done;
}
len = tmp - raw_frid + 1;
id = mosys_malloc(len + 1);
snprintf(id, len, "%s", raw_frid);
lprintf(LOG_DEBUG, "%s: Platform name: \"%s\"\n", __func__, id);
free(raw_frid);
add_destroy_callback(free, id);
}
if (strlfind(id, hwids, 0)) {
lprintf(LOG_DEBUG, "%s: matched id \"%s\"\n", __func__, id);
ret = 1;
}
probe_frid_done:
return ret;
}
int probe_smbios(struct platform_intf *intf, const char *ids[])
{
static char *id = NULL;
int ret = 0;
if (id)
goto probe_smbios_cmp;
/* Attempt platform-specific SMBIOS handler if one exists, else use the
* default approach. */
if (intf->cb->smbios && intf->cb->smbios->system_name) {
id = intf->cb->smbios->system_name(intf);
} else {
struct smbios_table table;
if (smbios_find_table(intf, SMBIOS_TYPE_SYSTEM, 0, &table,
SMBIOS_LEGACY_ENTRY_BASE,
SMBIOS_LEGACY_ENTRY_LEN) == 0)
id = table.string[table.data.system.name];
}
probe_smbios_cmp:
if (!id) {
ret = 0;
lprintf(LOG_SPEW, "%s: cannot find product name\n", __func__);
} else if (strlfind(id, ids, 0)) {
ret = 1;
lprintf(LOG_DEBUG, "%s: matched id \"%s\"\n", __func__, id);
}
return ret;
}
int probe_cpuinfo(struct platform_intf *intf,
const char *key, const char *value)
{
FILE *cpuinfo;
int ret = 0;
char path[PATH_MAX];
int key_found = 0;
char line[LINE_MAX], *ptr;
sprintf(path, "%s/proc/cpuinfo", mosys_get_root_prefix());
cpuinfo = fopen(path, "rb");
if (!cpuinfo)
return 0;
while (!feof(cpuinfo)) {
if (fgets(line, sizeof(line), cpuinfo) == NULL)
break;
ptr = line;
if (!strncmp(ptr, key, strlen(key))) {
key_found = 1;
break;
}
}
if (key_found) {
int i;
char tmp[LINE_MAX];
ptr += strlen(key);
while (isspace((unsigned char)*ptr) || (*ptr == ':'))
ptr++;
memset(tmp, 0, sizeof(tmp));
for (i = 0; !isspace((unsigned char)*ptr); i++) {
tmp[i] = *ptr;
ptr++;
}
lprintf(LOG_DEBUG, "\"%s\" == \"%s\" ? ", tmp, value);
if (strncasecmp(tmp, value, strlen(value))) {
lprintf(LOG_DEBUG, "no\n");
} else {
lprintf(LOG_DEBUG, "yes\n");
ret = 1;
}
}
fclose(cpuinfo);
return ret;
}
const char *extract_cpuinfo(const char *key)
{
FILE *cpuinfo;
char *ret = NULL;
char path[PATH_MAX];
sprintf(path, "%s/proc/cpuinfo", mosys_get_root_prefix());
cpuinfo = fopen(path, "rb");
if (!cpuinfo)
return 0;
while (!feof(cpuinfo)) {
char line[LINE_MAX], *ptr;
int i = 0;
if (fgets(line, sizeof(line), cpuinfo) == NULL)
break;
ptr = line;
if (strncmp(ptr, key, strlen(key)))
continue;
ptr += strlen(key);
while (isspace((unsigned char)*ptr) || (*ptr == ':'))
ptr++;
ret = mosys_malloc(strlen(ptr) + 1);
while (!isspace((unsigned char)*ptr)) {
ret[i] = *ptr;
ptr++;
i++;
}
ret[i] = '\0';
}
fclose(cpuinfo);
return (const char *)ret;
}
int probe_cmdline(const char *key, int cs)
{
FILE *cmdline;
char path[PATH_MAX];
char line[LINE_MAX];
int ret = 0;
if ((cs < 0) || (cs > 1))
return -1;
sprintf(path, "%s/proc/cmdline", mosys_get_root_prefix());
cmdline = fopen(path, "rb");
if (!cmdline)
goto probe_cmdline_done;
if (fgets(line, sizeof(line), cmdline) == NULL)
goto probe_cmdline_done;
if (cs) {
if (strstr(line, key))
ret = 1;
} else {
if (strcasestr(line, key))
ret = 1;
}
if (ret)
lprintf(LOG_DEBUG, "Found match on kernel command-line\n", key);
probe_cmdline_done:
fclose(cmdline);
return ret;
}
/*
* extract_block_device_model_name - Gets model name of block storage device.
*
* @device: String containing device name, ex "sda".
*
* Returns pointer to allocated model name string on success, NULL on failure.
*/
const char *extract_block_device_model_name(const char *device)
{
FILE *file;
char *model_name = NULL;
char path[PATH_MAX];
int len;
sprintf(path, "/sys/class/block/%s/device/model", device);
file = fopen(path, "r");
if (!file)
return NULL;
model_name = mosys_malloc(PATH_MAX);
fgets(model_name, PATH_MAX, file);
fclose(file);
/* Remove trailing newline. */
len = strlen(model_name);
if (len > 0 && model_name[len-1] == '\n')
model_name[len-1] = '\0';
return (const char *)model_name;
}
#define FDT_MODEL_NODE "/proc/device-tree/model"
char *fdt_model(void)
{
int fd;
static char model[32];
int len;
fd = file_open(FDT_MODEL_NODE, FILE_READ);
if (fd < 0) {
lperror(LOG_DEBUG, "Unable to open %s", FDT_MODEL_NODE);
return NULL;
}
memset(model, 0, sizeof(model));
len = read(fd, &model, sizeof(model));
if (len < 0) {
lprintf(LOG_DEBUG, "%s: Could not read FDT\n", __func__);
return NULL;
}
return model;
}
#define FDT_COMPATIBLE "/proc/device-tree/compatible"
int probe_fdt_compatible(const char *id_list[], int num_ids, int allow_partial)
{
int ret = -1, i, fd;
char path[PATH_MAX];
char compat[64]; /* arbitrarily chosen max size */
char *p;
lprintf(LOG_DEBUG, "Probing platform with FDT compatible node\n");
snprintf(path, PATH_MAX, "%s/%s",
mosys_get_root_prefix(), FDT_COMPATIBLE);
fd = file_open(path, FILE_READ);
if (fd < 0) {
lprintf(LOG_DEBUG, "Cannot open %s\n", path);
return -1;
}
/*
* Device tree "compatible" data consists of a list of comma-separated
* pairs with a NULL after each pair. For example, "foo,bar\0bam,baz\0"
* is foo,bar and bam,baz.
*/
p = &compat[0];
while (read(fd, p, 1) == 1) {
if (*p != 0) {
p++;
if (p - &compat[0] > sizeof(compat)) {
lprintf(LOG_ERR,
"FDT compatible identifier too long\n");
goto probe_fdt_compatible_exit;
}
continue;
}
for (i = 0; (i < num_ids) && id_list[i]; i++) {
int cmp = 0;
lprintf(LOG_DEBUG, "\t\"%s\" == \"%s\" ? ",
&compat[0], id_list[i]);
if (allow_partial) {
cmp = strncmp(&compat[0],
id_list[i], strlen(id_list[i]));
} else {
cmp = strcmp(&compat[0], id_list[i]);
}
if (!cmp) {
lprintf(LOG_DEBUG, "yes\n");
ret = i;
goto probe_fdt_compatible_exit;
} else {
lprintf(LOG_DEBUG, "no\n");
}
}
p = &compat[0];
}
probe_fdt_compatible_exit:
close(fd);
return ret;
}
struct cros_compat_tuple *cros_fdt_tuple(void)
{
struct cros_compat_tuple *ret = NULL;
int fd;
char path[PATH_MAX];
char compat[64]; /* arbitrarily chosen max size */
char family[32];
char name[32];
char revision[8];
char *endptr; /* end of current compat string */
char *p0, *p1; /* placeholders for detokenizing string */
snprintf(path, PATH_MAX, "%s/%s",
mosys_get_root_prefix(), FDT_COMPATIBLE);
fd = file_open(path, FILE_READ);
if (fd < 0) {
lprintf(LOG_DEBUG, "Cannot open %s\n", path);
return NULL;
}
/*
* Device tree "compatible" data consists of a list of comma-separated
* pairs with a NULL after each pair. For example, "foo,bar\0bam,baz\0"
* is foo,bar and bam,baz.
*/
endptr = &compat[0];
while (read(fd, endptr, 1) == 1) {
if (*endptr != 0) {
endptr++;
if (endptr - &compat[0] > sizeof(compat)) {
lprintf(LOG_ERR,
"FDT compatible identifier too long\n");
break;
}
continue;
}
p0 = &compat[0];
if (strncmp(p0, "google,", strlen("google,"))) {
endptr = &compat[0];
continue;
}
p0 += strlen("google,");
/* family */
p1 = strchr(p0, '-');
if (p1 == NULL) {
endptr = &compat[0];
continue;
}
snprintf(family, p1 - p0 + 1, p0);
/* name */
p0 = p1 + 1;
p1 = strchr(p0, '-');
if (p1 == NULL) {
endptr = &compat[0];
continue;
}
snprintf(name, p1 - p0 + 1, p0);
/* revision */
p0 = p1 + 1;
if (sscanf(p0, "%s", revision) != 1) {
endptr = &compat[0];
continue;
}
lprintf(LOG_DEBUG, "%s: family: %s, name: %s, revision: %s\n",
__func__, family, name, revision);
ret = mosys_malloc(sizeof(*ret));
ret->family = mosys_strdup(family);
ret->name = mosys_strdup(name);
ret->revision = mosys_strdup(revision);
add_destroy_callback(free, (void *)ret->family);
add_destroy_callback(free, (void *)ret->name);
add_destroy_callback(free, (void *)ret->revision);
add_destroy_callback(free, (void *)ret);
break;
}
close(fd);
return ret;
}