cbmem: Add support for new 'coreboot' compatible device tree binding

This patch brings the cbmem utility in line with the recent change to
coreboot's device tree binding. Since trying to find the right node to
place this binding has been so hard (and still isn't quite agreed upon),
and because it's really the more correct thing to do, this code searches
through the device tree for the 'coreboot' compatible property instead
of looking up a hardcoded path. It also provides bullet-proof
'#address-cells' handling that should work for any endianness and size.

BUG=chrome-os-partner:29311
TEST=Ran cbmem -c and cbmem -t on Nyan_Big. Also straced the to make
sure everything looks as expected. 'time cbmem -t' = ~35ms shows that
there is no serious performance problem from the more thorough lookup
code.

Original-Change-Id: I806a21270ba6cec6e81232075749016eaf18508b
Original-Signed-off-by: Julius Werner <jwerner@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/204274
Original-Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
(cherry picked from commit 3e64e28f684e60e8b300906c1abffee75ec6a5c2)
Signed-off-by: Marc Jones <marc.jones@se-eng.com>

Change-Id: I0a0a4f69330d3d8c5c3ea92b55f5dde4d43fca65
Reviewed-on: http://review.coreboot.org/8141
Tested-by: build bot (Jenkins)
Reviewed-by: Edward O'Callaghan <eocallaghan@alterapraxis.com>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Julius Werner 2014-06-16 23:02:03 -07:00 committed by Marc Jones
parent b3e0202fd4
commit 337de4c0e5
1 changed files with 158 additions and 17 deletions

View File

@ -24,6 +24,7 @@
#include <unistd.h>
#include <inttypes.h>
#include <getopt.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
@ -40,6 +41,7 @@
#include "boot/coreboot_tables.h"
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
@ -54,7 +56,7 @@ static int verbose = 0;
#define debug(x...) if(verbose) printf(x)
/* File handle used to access /dev/mem */
static int fd;
static int mem_fd;
/*
* calculate ip checksum (16 bit quantities) on a passed in buffer. In case
@ -118,7 +120,7 @@ static void *map_memory_size(u64 physical, size_t size)
debug("Mapping %zuMB of physical memory at 0x%jx.\n",
size_to_mib(size), (intmax_t)p);
v = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, p);
v = mmap(NULL, size, PROT_READ, MAP_SHARED, mem_fd, p);
if (v == MAP_FAILED) {
fprintf(stderr, "Failed to mmap /dev/mem: %s\n",
@ -823,6 +825,121 @@ static void print_usage(const char *name)
exit(1);
}
#ifdef __arm__
static void dt_update_cells(const char *name, int *addr_cells_ptr,
int *size_cells_ptr)
{
if (*addr_cells_ptr >= 0 && *size_cells_ptr >= 0)
return;
int buffer;
size_t nlen = strlen(name);
char *prop = alloca(nlen + sizeof("/#address-cells"));
strcpy(prop, name);
if (*addr_cells_ptr < 0) {
strcpy(prop + nlen, "/#address-cells");
int fd = open(prop, O_RDONLY);
if (fd < 0 && errno != ENOENT) {
perror(prop);
} else if (fd >= 0) {
if (read(fd, &buffer, sizeof(int)) < 0)
perror(prop);
else
*addr_cells_ptr = ntohl(buffer);
close(fd);
}
}
if (*size_cells_ptr < 0) {
strcpy(prop + nlen, "/#size-cells");
int fd = open(prop, O_RDONLY);
if (fd < 0 && errno != ENOENT) {
perror(prop);
} else if (fd >= 0) {
if (read(fd, &buffer, sizeof(int)) < 0)
perror(prop);
else
*size_cells_ptr = ntohl(buffer);
close(fd);
}
}
}
static char *dt_find_compat(const char *parent, const char *compat,
int *addr_cells_ptr, int *size_cells_ptr)
{
char *ret = NULL;
struct dirent *entry;
DIR *dir;
if (!(dir = opendir(parent))) {
perror(parent);
return NULL;
}
/* Loop through all files in the directory (DT node). */
while ((entry = readdir(dir))) {
/* We only care about compatible props or subnodes. */
if (entry->d_name[0] == '.' || !((entry->d_type & DT_DIR) ||
!strcmp(entry->d_name, "compatible")))
continue;
/* Assemble the file name (on the stack, for speed). */
size_t plen = strlen(parent);
char *name = alloca(plen + strlen(entry->d_name) + 2);
strcpy(name, parent);
name[plen] = '/';
strcpy(name + plen + 1, entry->d_name);
/* If it's a subnode, recurse. */
if (entry->d_type & DT_DIR) {
ret = dt_find_compat(name, compat, addr_cells_ptr,
size_cells_ptr);
/* There is only one matching node to find, abort. */
if (ret) {
/* Gather cells values on the way up. */
dt_update_cells(parent, addr_cells_ptr,
size_cells_ptr);
break;
}
continue;
}
/* If it's a compatible string, see if it's the right one. */
int fd = open(name, O_RDONLY);
int clen = strlen(compat);
char *buffer = alloca(clen + 1);
if (fd < 0) {
perror(name);
continue;
}
if (read(fd, buffer, clen + 1) < 0) {
perror(name);
close(fd);
continue;
}
close(fd);
if (!strcmp(compat, buffer)) {
/* Initialize these to "unset" for the way up. */
*addr_cells_ptr = *size_cells_ptr = -1;
/* Can't leave string on the stack or we'll lose it! */
ret = strdup(parent);
break;
}
}
closedir(dir);
return ret;
}
#endif /* __arm__ */
int main(int argc, char** argv)
{
int print_defaults = 1;
@ -883,33 +1000,57 @@ int main(int argc, char** argv)
}
}
fd = open("/dev/mem", O_RDONLY, 0);
if (fd < 0) {
mem_fd = open("/dev/mem", O_RDONLY, 0);
if (mem_fd < 0) {
fprintf(stderr, "Failed to gain memory access: %s\n",
strerror(errno));
return 1;
}
#ifdef __arm__
int dt_fd;
uint32_t cbtable_base;
int addr_cells, size_cells;
char *coreboot_node = dt_find_compat("/proc/device-tree", "coreboot",
&addr_cells, &size_cells);
dt_fd = open("/proc/device-tree/firmware/coreboot/coreboot-table",
O_RDONLY, 0);
if (dt_fd < 0) {
fprintf(stderr, "Failed to open device tree node: %s\n",
strerror(errno));
if (!coreboot_node) {
fprintf(stderr, "Could not find 'coreboot' compatible node!\n");
return 1;
}
if (read(dt_fd, &cbtable_base, 4) != 4) {
fprintf(stderr, "Failed to read device tree node: %s\n",
strerror(errno));
if (addr_cells < 0) {
fprintf(stderr, "Warning: no #address-cells node in tree!\n");
addr_cells = 1;
}
int nlen = strlen(coreboot_node);
char *reg = alloca(nlen + sizeof("/reg"));
strcpy(reg, coreboot_node);
strcpy(reg + nlen, "/reg");
free(coreboot_node);
int fd = open(reg, O_RDONLY);
if (fd < 0) {
perror(reg);
return 1;
}
close(dt_fd);
parse_cbtable(ntohl(cbtable_base));
int i;
u8 *baseaddr_buffer = alloca(addr_cells * 4);
if (read(fd, baseaddr_buffer, addr_cells * 4) < 0) {
perror(reg);
return 1;
}
close(fd);
/* No variable-length byte swap function anywhere in C... how sad. */
u64 baseaddr = 0;
for (i = 0; i < addr_cells * 4; i++) {
baseaddr <<= 8;
baseaddr |= baseaddr_buffer[i];
}
parse_cbtable(baseaddr);
#else
int j;
static const int possible_base_addresses[] = { 0, 0xf0000 };
@ -936,6 +1077,6 @@ int main(int argc, char** argv)
if (print_defaults || print_timestamps)
dump_timestamps();
close(fd);
close(mem_fd);
return 0;
}