blob: d5d37629e931fb29d55f065c256e311913b07054 [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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc.
18 */
19
20#include <region.h>
21#include <string.h>
22
Aaron Durbin5d5f4b32015-03-26 14:39:07 -050023static inline size_t region_end(const struct region *r)
24{
25 return region_sz(r) + region_offset(r);
26}
27
28static int is_subregion(const struct region *p, const struct region *c)
29{
30 if (region_offset(c) < region_offset(p))
31 return 0;
32
33 if (region_sz(c) > region_sz(p))
34 return 0;
35
36 if (region_end(c) > region_end(p))
37 return 0;
38
39 return 1;
40}
41
42static int normalize_and_ok(const struct region *outer, struct region *inner)
43{
44 inner->offset += region_offset(outer);
45 return is_subregion(outer, inner);
46}
47
48static const struct region_device *rdev_root(const struct region_device *rdev)
49{
50 if (rdev->root == NULL)
51 return rdev;
52 return rdev->root;
53}
54
55void *rdev_mmap(const struct region_device *rd, size_t offset, size_t size)
56{
57 const struct region_device *rdev;
58 struct region req = {
59 .offset = offset,
60 .size = size,
61 };
62
63 if (!normalize_and_ok(&rd->region, &req))
64 return NULL;
65
66 rdev = rdev_root(rd);
67
68 return rdev->ops->mmap(rdev, req.offset, req.size);
69}
70
71int rdev_munmap(const struct region_device *rd, void *mapping)
72{
73 const struct region_device *rdev;
74
75 rdev = rdev_root(rd);
76
77 return rdev->ops->munmap(rdev, mapping);
78}
79
80ssize_t rdev_readat(const struct region_device *rd, void *b, size_t offset,
81 size_t size)
82{
83 const struct region_device *rdev;
84 struct region req = {
85 .offset = offset,
86 .size = size,
87 };
88
89 if (!normalize_and_ok(&rd->region, &req))
90 return -1;
91
92 rdev = rdev_root(rd);
93
94 return rdev->ops->readat(rdev, b, req.offset, req.size);
95}
96
97int rdev_chain(struct region_device *child, const struct region_device *parent,
98 size_t offset, size_t size)
99{
100 struct region req = {
101 .offset = offset,
102 .size = size,
103 };
104
105 if (!normalize_and_ok(&parent->region, &req))
106 return -1;
107
108 /* Keep track of root region device. Note the offsets are relative
109 * to the root device. */
110 child->root = rdev_root(parent);
111 child->ops = NULL;
112 child->region.offset = req.offset;
113 child->region.size = req.size;
114
115 return 0;
116}
Aaron Durbinb419c1a82015-03-27 01:03:45 -0500117
118void mem_region_device_init(struct mem_region_device *mdev, void *base,
119 size_t size)
120{
121 memset(mdev, 0, sizeof(*mdev));
122 mdev->base = base;
123 mdev->rdev.ops = &mem_rdev_ops;
124 mdev->rdev.region.size = size;
125}
126
127static void *mdev_mmap(const struct region_device *rd, size_t offset,
128 size_t size)
129{
130 const struct mem_region_device *mdev;
131
132 mdev = container_of(rd, typeof(*mdev), rdev);
133
134 return &mdev->base[offset];
135}
136
137static int mdev_munmap(const struct region_device *rd, void *mapping)
138{
139 return 0;
140}
141
142static ssize_t mdev_readat(const struct region_device *rd, void *b,
143 size_t offset, size_t size)
144{
145 const struct mem_region_device *mdev;
146
147 mdev = container_of(rd, typeof(*mdev), rdev);
148
149 memcpy(b, &mdev->base[offset], size);
150
151 return size;
152}
153
154const struct region_device_ops mem_rdev_ops = {
155 .mmap = mdev_mmap,
156 .munmap = mdev_munmap,
157 .readat = mdev_readat,
158};
Aaron Durbine62cf522015-03-27 01:58:06 -0500159
160void mmap_helper_device_init(struct mmap_helper_region_device *mdev,
161 void *cache, size_t cache_size)
162{
163 mem_pool_init(&mdev->pool, cache, cache_size);
164}
165
166void *mmap_helper_rdev_mmap(const struct region_device *rd, size_t offset,
167 size_t size)
168{
169 struct mmap_helper_region_device *mdev;
170 void *mapping;
171
172 mdev = container_of((void *)rd, typeof(*mdev), rdev);
173
174 mapping = mem_pool_alloc(&mdev->pool, size);
175
176 if (mapping == NULL)
177 return NULL;
178
179 if (rd->ops->readat(rd, mapping, offset, size) != size) {
180 mem_pool_free(&mdev->pool, mapping);
181 return NULL;
182 }
183
184 return mapping;
185}
186
187int mmap_helper_rdev_munmap(const struct region_device *rd, void *mapping)
188{
189 struct mmap_helper_region_device *mdev;
190
191 mdev = container_of((void *)rd, typeof(*mdev), rdev);
192
193 mem_pool_free(&mdev->pool, mapping);
194
195 return 0;
196}