summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Durbin <adurbin@chromium.org>2016-12-03 17:08:08 -0600
committerAaron Durbin <adurbin@chromium.org>2016-12-08 16:11:41 +0100
commit256db40b14ea6b9e587f99106994e54a24e21d7f (patch)
treebfdc23fa2a8dc552c82317788bdc71a6a7dda78f
parent305c0cafaa2fe7d766918898692971b70259b4ac (diff)
commonlib: provide incoherent region device
The MRC cache uses an incoherent mechanism for updating the cache contents in that it assumes memory mapped boot device access for checking against latest data for update. However, it uses another driver for updating the underlying storage area. In order to aid in moving the MRC cache over to using region_devices for updates provide an implementation of a region_device which performs reads and writes to different region_devices so that different drivers can be used transparently. BUG=chrome-os-partner:56151 Change-Id: I30e858245c30cbccd6313aff5ebecd3fd98d7302 Signed-off-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: https://review.coreboot.org/17716 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
-rw-r--r--src/commonlib/include/commonlib/region.h23
-rw-r--r--src/commonlib/region.c79
2 files changed, 102 insertions, 0 deletions
diff --git a/src/commonlib/include/commonlib/region.h b/src/commonlib/include/commonlib/region.h
index bc3ded1d0a..3f7d3bd52f 100644
--- a/src/commonlib/include/commonlib/region.h
+++ b/src/commonlib/include/commonlib/region.h
@@ -245,4 +245,27 @@ void xlate_region_device_rw_init(struct xlate_region_device *xdev,
size_t sub_offset, size_t sub_size,
size_t parent_size);
+/* This type can be used for incoherent access where the read and write
+ * operations are backed by separate drivers. An example is x86 systems
+ * with memory mapped media for reading but use a spi flash driver for
+ * writing. One needs to ensure using this object is appropriate in context. */
+struct incoherent_rdev {
+ struct region_device rdev;
+ const struct region_device *read;
+ const struct region_device *write;
+};
+
+/* Initialize an incoherent_rdev based on the region as well as the read and
+ * write rdevs. The read and write rdevs should match in size to the passed
+ * in region. If not the initialization will fail returning NULL. Otherwise
+ * the function will return a pointer to the containing region_device to
+ * be used for region operations. Therefore, the lifetime of the returned
+ * pointer matches the lifetime of the incoherent_rdev object. Likewise,
+ * the lifetime of the read and write rdev need to match the lifetime of
+ * the incoherent_rdev object. */
+const struct region_device *incoherent_rdev_init(struct incoherent_rdev *irdev,
+ const struct region *r,
+ const struct region_device *read,
+ const struct region_device *write);
+
#endif /* _REGION_H_ */
diff --git a/src/commonlib/region.c b/src/commonlib/region.c
index ac0faf111f..bf53b9dda8 100644
--- a/src/commonlib/region.c
+++ b/src/commonlib/region.c
@@ -437,3 +437,82 @@ const struct region_device_ops xlate_rdev_rw_ops = {
.writeat = xlate_writeat,
.eraseat = xlate_eraseat,
};
+
+
+static void *incoherent_mmap(const struct region_device *rd, size_t offset,
+ size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_mmap(irdev->read, offset, size);
+}
+
+static int incoherent_munmap(const struct region_device *rd, void *mapping)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_munmap(irdev->read, mapping);
+}
+
+static ssize_t incoherent_readat(const struct region_device *rd, void *b,
+ size_t offset, size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_readat(irdev->read, b, offset, size);
+}
+
+static ssize_t incoherent_writeat(const struct region_device *rd, const void *b,
+ size_t offset, size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_writeat(irdev->write, b, offset, size);
+}
+
+static ssize_t incoherent_eraseat(const struct region_device *rd, size_t offset,
+ size_t size)
+{
+ const struct incoherent_rdev *irdev;
+
+ irdev = container_of(rd, const struct incoherent_rdev, rdev);
+
+ return rdev_eraseat(irdev->write, offset, size);
+}
+
+static const struct region_device_ops incoherent_rdev_ops = {
+ .mmap = incoherent_mmap,
+ .munmap = incoherent_munmap,
+ .readat = incoherent_readat,
+ .writeat = incoherent_writeat,
+ .eraseat = incoherent_eraseat,
+};
+
+const struct region_device *incoherent_rdev_init(struct incoherent_rdev *irdev,
+ const struct region *r,
+ const struct region_device *read,
+ const struct region_device *write)
+{
+ const size_t size = region_sz(r);
+
+ if (size != region_device_sz(read) || size != region_device_sz(write))
+ return NULL;
+
+ /* The region is represented as offset 0 to size. That way, the generic
+ * rdev operations can be called on the read or write implementation
+ * without any unnecessary translation because the offsets all start
+ * at 0. */
+ region_device_init(&irdev->rdev, &incoherent_rdev_ops, 0, size);
+ irdev->read = read;
+ irdev->write = write;
+
+ return &irdev->rdev;
+}