From 5d5f4b3c84677213d669d2218bc9a21e4177dcf7 Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Thu, 26 Mar 2015 14:39:07 -0500 Subject: coreboot: add region infrastructure The region infrastructure provides a means of abstracting access to different types of storage such as SPI flash, MMC, or just plain memory. The regions are represented by region devices which can be chained together forming subregions of the larger region. This allows the call sites to be agnostic about the implementations behind the regions. Additionally, this prepares for a cleaner API for CBFS accesses. Change-Id: I803f97567ef0505691a69975c282fde1215ea6da Signed-off-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/9128 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/include/region.h | 100 ++++++++++++++++++++++++++++++++++++++++++ src/lib/Makefile.inc | 4 ++ src/lib/region.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 src/include/region.h create mode 100644 src/lib/region.c diff --git a/src/include/region.h b/src/include/region.h new file mode 100644 index 0000000000..cb911023b3 --- /dev/null +++ b/src/include/region.h @@ -0,0 +1,100 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 Google Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + */ + +#ifndef _REGION_H_ +#define _REGION_H_ + +#include +#include + +/* + * Region support. + * + * Regions are intended to abstract away the access mechanisms for blocks of + * data. This could be SPI, eMMC, or a memory region as the backing store. + * They are accessed through a region_device. Subregions can be made by + * chaining together multiple region_devices. + */ + +struct region_device; + +/* + * Returns NULL on error otherwise a buffer is returned with the conents of + * the requested data at offset of size. + */ +void *rdev_mmap(const struct region_device *rd, size_t offset, size_t size); + +/* Unmap a previously mapped area. Returns 0 on success, < 0 on error. */ +int rdev_munmap(const struct region_device *rd, void *mapping); + +/* + * Returns < 0 on error otherwise returns size of data read at provided + * offset filling in the buffer passed. + */ +ssize_t rdev_readat(const struct region_device *rd, void *b, size_t offset, + size_t size); + + +/**************************************** + * Implementation of a region device * + ****************************************/ + +/* + * Create a child region of the parent provided the sub-region is within + * the parent's region. Returns < 0 on error otherwise 0 on success. Note + * that the child device only calls through the parent's operations. + */ +int rdev_chain(struct region_device *child, const struct region_device *parent, + size_t offset, size_t size); + + +/* A region_device operations. */ +struct region_device_ops { + void *(*mmap)(const struct region_device *, size_t, size_t); + int (*munmap)(const struct region_device *, void *); + ssize_t (*readat)(const struct region_device *, void *, size_t, size_t); +}; + +struct region { + size_t offset; + size_t size; +}; + +struct region_device { + const struct region_device *root; + const struct region_device_ops *ops; + struct region region; +}; + +#define REGION_DEV_INIT(ops_, offset_, size_) \ + { \ + .root = NULL, \ + .ops = (ops_), \ + .region = { \ + .offset = (offset_), \ + .size = (size_), \ + }, \ + } + +static inline size_t region_sz(const struct region *r) +{ + return r->size; +} + +#endif /* _REGION_H_ */ diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index d6d7b84190..c547c70ae4 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -29,6 +29,7 @@ bootblock-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c bootblock-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c bootblock-y += memchr.c bootblock-y += memcmp.c +bootblock-y += region.c verstage-y += prog_ops.c verstage-y += delay.c @@ -37,6 +38,7 @@ verstage-y += cbfs_core.c verstage-y += halt.c verstage-y += memcmp.c verstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c +verstage-y += region.c verstage-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c verstage-$(CONFIG_COMMON_CBFS_SPI_WRAPPER) += cbfs_spi.c @@ -132,6 +134,8 @@ ramstage-$(CONFIG_RELOCATABLE_RAMSTAGE) += cbmem_stage_cache.c romstage-$(CONFIG_RELOCATABLE_RAMSTAGE) += cbmem_stage_cache.c endif +romstage-y += region.c +ramstage-y += region.c smm-y += cbfs.c cbfs_core.c memcmp.c smm-$(CONFIG_COMPILER_GCC) += gcc.c diff --git a/src/lib/region.c b/src/lib/region.c new file mode 100644 index 0000000000..948addd2ca --- /dev/null +++ b/src/lib/region.c @@ -0,0 +1,121 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 Google Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc. + */ + +#include +#include + +static inline size_t region_offset(const struct region *r) +{ + return r->offset; +} + +static inline size_t region_end(const struct region *r) +{ + return region_sz(r) + region_offset(r); +} + +static int is_subregion(const struct region *p, const struct region *c) +{ + if (region_offset(c) < region_offset(p)) + return 0; + + if (region_sz(c) > region_sz(p)) + return 0; + + if (region_end(c) > region_end(p)) + return 0; + + return 1; +} + +static int normalize_and_ok(const struct region *outer, struct region *inner) +{ + inner->offset += region_offset(outer); + return is_subregion(outer, inner); +} + +static const struct region_device *rdev_root(const struct region_device *rdev) +{ + if (rdev->root == NULL) + return rdev; + return rdev->root; +} + +void *rdev_mmap(const struct region_device *rd, size_t offset, size_t size) +{ + const struct region_device *rdev; + struct region req = { + .offset = offset, + .size = size, + }; + + if (!normalize_and_ok(&rd->region, &req)) + return NULL; + + rdev = rdev_root(rd); + + return rdev->ops->mmap(rdev, req.offset, req.size); +} + +int rdev_munmap(const struct region_device *rd, void *mapping) +{ + const struct region_device *rdev; + + rdev = rdev_root(rd); + + return rdev->ops->munmap(rdev, mapping); +} + +ssize_t rdev_readat(const struct region_device *rd, void *b, size_t offset, + size_t size) +{ + const struct region_device *rdev; + struct region req = { + .offset = offset, + .size = size, + }; + + if (!normalize_and_ok(&rd->region, &req)) + return -1; + + rdev = rdev_root(rd); + + return rdev->ops->readat(rdev, b, req.offset, req.size); +} + +int rdev_chain(struct region_device *child, const struct region_device *parent, + size_t offset, size_t size) +{ + struct region req = { + .offset = offset, + .size = size, + }; + + if (!normalize_and_ok(&parent->region, &req)) + return -1; + + /* Keep track of root region device. Note the offsets are relative + * to the root device. */ + child->root = rdev_root(parent); + child->ops = NULL; + child->region.offset = req.offset; + child->region.size = req.size; + + return 0; +} -- cgit v1.2.3