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