blob: 8b0b0d9748381916c8d0d2428df57942edc44b9e [file] [log] [blame]
Aaron Durbin5d5f4b32015-03-26 14:39:07 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2015 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Aaron Durbin5d5f4b32015-03-26 14:39:07 -050014 */
15
Aaron Durbindc9f5cd2015-09-08 13:34:43 -050016#include <commonlib/region.h>
Aaron Durbin5d5f4b32015-03-26 14:39:07 -050017#include <string.h>
18
Aaron Durbin5d5f4b32015-03-26 14:39:07 -050019static inline size_t region_end(const struct region *r)
20{
21 return region_sz(r) + region_offset(r);
22}
23
24static int is_subregion(const struct region *p, const struct region *c)
25{
26 if (region_offset(c) < region_offset(p))
27 return 0;
28
29 if (region_sz(c) > region_sz(p))
30 return 0;
31
32 if (region_end(c) > region_end(p))
33 return 0;
34
35 return 1;
36}
37
38static int normalize_and_ok(const struct region *outer, struct region *inner)
39{
40 inner->offset += region_offset(outer);
41 return is_subregion(outer, inner);
42}
43
44static const struct region_device *rdev_root(const struct region_device *rdev)
45{
46 if (rdev->root == NULL)
47 return rdev;
48 return rdev->root;
49}
50
51void *rdev_mmap(const struct region_device *rd, size_t offset, size_t size)
52{
53 const struct region_device *rdev;
54 struct region req = {
55 .offset = offset,
56 .size = size,
57 };
58
59 if (!normalize_and_ok(&rd->region, &req))
60 return NULL;
61
62 rdev = rdev_root(rd);
63
64 return rdev->ops->mmap(rdev, req.offset, req.size);
65}
66
67int rdev_munmap(const struct region_device *rd, void *mapping)
68{
69 const struct region_device *rdev;
70
71 rdev = rdev_root(rd);
72
73 return rdev->ops->munmap(rdev, mapping);
74}
75
76ssize_t rdev_readat(const struct region_device *rd, void *b, size_t offset,
77 size_t size)
78{
79 const struct region_device *rdev;
80 struct region req = {
81 .offset = offset,
82 .size = size,
83 };
84
85 if (!normalize_and_ok(&rd->region, &req))
86 return -1;
87
88 rdev = rdev_root(rd);
89
90 return rdev->ops->readat(rdev, b, req.offset, req.size);
91}
92
93int rdev_chain(struct region_device *child, const struct region_device *parent,
94 size_t offset, size_t size)
95{
96 struct region req = {
97 .offset = offset,
98 .size = size,
99 };
100
101 if (!normalize_and_ok(&parent->region, &req))
102 return -1;
103
104 /* Keep track of root region device. Note the offsets are relative
105 * to the root device. */
106 child->root = rdev_root(parent);
107 child->ops = NULL;
108 child->region.offset = req.offset;
109 child->region.size = req.size;
110
111 return 0;
112}
Aaron Durbinb419c1a82015-03-27 01:03:45 -0500113
114void mem_region_device_init(struct mem_region_device *mdev, void *base,
115 size_t size)
116{
117 memset(mdev, 0, sizeof(*mdev));
118 mdev->base = base;
119 mdev->rdev.ops = &mem_rdev_ops;
120 mdev->rdev.region.size = size;
121}
122
123static void *mdev_mmap(const struct region_device *rd, size_t offset,
124 size_t size)
125{
126 const struct mem_region_device *mdev;
127
128 mdev = container_of(rd, typeof(*mdev), rdev);
129
130 return &mdev->base[offset];
131}
132
133static int mdev_munmap(const struct region_device *rd, void *mapping)
134{
135 return 0;
136}
137
138static ssize_t mdev_readat(const struct region_device *rd, void *b,
139 size_t offset, size_t size)
140{
141 const struct mem_region_device *mdev;
142
143 mdev = container_of(rd, typeof(*mdev), rdev);
144
145 memcpy(b, &mdev->base[offset], size);
146
147 return size;
148}
149
150const struct region_device_ops mem_rdev_ops = {
151 .mmap = mdev_mmap,
152 .munmap = mdev_munmap,
153 .readat = mdev_readat,
154};
Aaron Durbine62cf522015-03-27 01:58:06 -0500155
156void mmap_helper_device_init(struct mmap_helper_region_device *mdev,
157 void *cache, size_t cache_size)
158{
159 mem_pool_init(&mdev->pool, cache, cache_size);
160}
161
162void *mmap_helper_rdev_mmap(const struct region_device *rd, size_t offset,
163 size_t size)
164{
165 struct mmap_helper_region_device *mdev;
166 void *mapping;
167
168 mdev = container_of((void *)rd, typeof(*mdev), rdev);
169
170 mapping = mem_pool_alloc(&mdev->pool, size);
171
172 if (mapping == NULL)
173 return NULL;
174
175 if (rd->ops->readat(rd, mapping, offset, size) != size) {
176 mem_pool_free(&mdev->pool, mapping);
177 return NULL;
178 }
179
180 return mapping;
181}
182
183int mmap_helper_rdev_munmap(const struct region_device *rd, void *mapping)
184{
185 struct mmap_helper_region_device *mdev;
186
187 mdev = container_of((void *)rd, typeof(*mdev), rdev);
188
189 mem_pool_free(&mdev->pool, mapping);
190
191 return 0;
192}
Aaron Durbin5907eb82015-10-28 16:09:42 -0500193
194static void *xlate_mmap(const struct region_device *rd, size_t offset,
195 size_t size)
196{
197 const struct xlate_region_device *xldev;
198 struct region req = {
199 .offset = offset,
200 .size = size,
201 };
202
203 xldev = container_of(rd, typeof(*xldev), rdev);
204
205 if (!is_subregion(&xldev->sub_region, &req))
206 return NULL;
207
208 offset -= region_offset(&xldev->sub_region);
209
210 return rdev_mmap(xldev->access_dev, offset, size);
211}
212
213static int xlate_munmap(const struct region_device *rd, void *mapping)
214{
215 const struct xlate_region_device *xldev;
216
217 xldev = container_of(rd, typeof(*xldev), rdev);
218
219 return rdev_munmap(xldev->access_dev, mapping);
220}
221
222static ssize_t xlate_readat(const struct region_device *rd, void *b,
223 size_t offset, size_t size)
224{
225 struct region req = {
226 .offset = offset,
227 .size = size,
228 };
229 const struct xlate_region_device *xldev;
230
231 xldev = container_of(rd, typeof(*xldev), rdev);
232
233 if (!is_subregion(&xldev->sub_region, &req))
234 return -1;
235
236 offset -= region_offset(&xldev->sub_region);
237
238 return rdev_readat(xldev->access_dev, b, offset, size);
239}
240
241const struct region_device_ops xlate_rdev_ops = {
242 .mmap = xlate_mmap,
243 .munmap = xlate_munmap,
244 .readat = xlate_readat,
245};