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>
This commit is contained in:
Aaron Durbin 2016-12-03 17:08:08 -06:00
parent 305c0cafaa
commit 256db40b14
2 changed files with 102 additions and 0 deletions

View File

@ -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_ */

View File

@ -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;
}