1 /*
2 * (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
3 * economic rights: Technische Universität Dresden (Germany)
4 *
5 * This file is part of TUD:OS and distributed under the terms of the
6 * GNU General Public License 2.
7 * Please see the COPYING-GPL-2 file for details.
8 */
9 #include "dataspace_util.h"
10 #include "dataspace_noncont.h"
11 #include "pages.h"
12
13 #include <cstring>
14 #include <l4/cxx/iostream>
15 #include <l4/cxx/minmax>
16 #include <l4/sys/cache.h>
17
18 using cxx::min;
19 using Moe::Dataspace;
20 using Moe::Dataspace_noncont;
21
22 namespace {
23
trunc_page(unsigned long page_size,unsigned long addr)24 unsigned long trunc_page(unsigned long page_size, unsigned long addr)
25 { return addr & ~(page_size-1); }
26
27 inline void
__do_real_copy(Dataspace * dst,unsigned long & dst_offs,Dataspace const * src,unsigned long & src_offs,unsigned long sz)28 __do_real_copy(Dataspace *dst, unsigned long &dst_offs,
29 Dataspace const *src, unsigned long &src_offs, unsigned long sz)
30 {
31 while (sz)
32 {
33 l4_addr_t src_addr, dst_addr;
34 unsigned long src_size, dst_size;
35 if ( src->copy_address(src_offs, L4Re::Dataspace::F::R,
36 &src_addr, &src_size) < 0
37 || dst->copy_address(dst_offs, L4Re::Dataspace::F::W,
38 &dst_addr, &dst_size) < 0)
39 return;
40
41 unsigned long b_sz = min(min(src_size, dst_size), sz);
42 if (!b_sz)
43 return;
44
45 memcpy((void*)dst_addr, (void const *)src_addr, b_sz);
46 // FIXME: we should change the API to pass a flag for executable target pages,
47 // and do the I cache coherence only in this case.
48 // And we should change the cache API to allow for a single call to handle
49 // I-cache coherency and D-cache writeback.
50 l4_cache_coherent(dst_addr, dst_addr + b_sz);
51 l4_cache_clean_data(dst_addr, dst_addr + b_sz);
52
53 src_offs += b_sz;
54 dst_offs += b_sz;
55 sz -= b_sz;
56 }
57 }
58
59 inline void
__do_cow_copy(Dataspace_noncont * dst,unsigned long & dst_offs,unsigned dst_pg_sz,Dataspace const * src,unsigned long & src_offs,unsigned long sz)60 __do_cow_copy(Dataspace_noncont *dst, unsigned long &dst_offs, unsigned dst_pg_sz,
61 Dataspace const *src, unsigned long &src_offs, unsigned long sz)
62 {
63 while (sz)
64 {
65 Dataspace::Address src_a = src->address(src_offs, L4Re::Dataspace::F::R);
66 Dataspace_noncont::Page &dst_p = dst->alloc_page(dst_offs);
67 dst->free_page(dst_p);
68 void *src_p = (void*)trunc_page(dst_pg_sz,src_a.adr<unsigned long>());
69 Moe::Pages::share(src_p);
70 dst_p.set(src_p, Dataspace_noncont::Page_cow);
71
72 src_offs += dst_pg_sz;
73 dst_offs += dst_pg_sz;
74 sz -= dst_pg_sz;
75 }
76 }
77
78 inline void
__do_cow_copy2(Dataspace_noncont * dst,unsigned long & dst_offs,unsigned dst_pg_sz,Dataspace_noncont const * src,unsigned long & src_offs,unsigned long sz)79 __do_cow_copy2(Dataspace_noncont *dst, unsigned long &dst_offs, unsigned dst_pg_sz,
80 Dataspace_noncont const *src, unsigned long &src_offs, unsigned long sz)
81 {
82 //L4::cout << "real COW\n";
83 while (sz)
84 {
85 Dataspace_noncont::Page &src_p = src->page(src_offs);
86 Dataspace_noncont::Page *dst_p;
87 if (src_p.valid())
88 dst_p = &dst->alloc_page(dst_offs);
89 else
90 dst_p = &dst->page(dst_offs);
91
92 dst->free_page(*dst_p);
93 if (*src_p)
94 {
95 Moe::Pages::share(*src_p);
96 if (!(src_p.flags() & Dataspace_noncont::Page_cow))
97 {
98 src->unmap_page(src_p, true);
99 src_p.set(*src_p, src_p.flags() | Dataspace_noncont::Page_cow);
100 }
101
102 dst_p->set(*src_p, src_p.flags() | Dataspace_noncont::Page_cow);
103 }
104
105 src_offs += dst_pg_sz;
106 dst_offs += dst_pg_sz;
107 sz -= dst_pg_sz;
108 }
109 }
110
111 unsigned long
__do_eager_copy(Dataspace * dst,unsigned long dst_offs,Dataspace const * src,unsigned long src_offs,unsigned long size)112 __do_eager_copy(Dataspace *dst, unsigned long dst_offs,
113 Dataspace const *src, unsigned long src_offs, unsigned long size)
114 {
115 unsigned long dst_sz = dst->size();
116 unsigned long src_sz = src->round_size();
117 if (dst_offs >= dst_sz || src_offs >= src_sz)
118 return 0;
119
120 size = min(min(size, dst_sz - dst_offs), src_sz - src_offs);
121
122 __do_real_copy(dst, dst_offs, src, src_offs, size);
123 return size;
124 }
125
126
127 bool
__do_lazy_copy(Dataspace_noncont * dst,unsigned long dst_offs,Dataspace const * src,unsigned long src_offs,unsigned long & size)128 __do_lazy_copy(Dataspace_noncont *dst, unsigned long dst_offs,
129 Dataspace const *src, unsigned long src_offs, unsigned long &size)
130 {
131 unsigned long dst_sz = dst->size();
132 unsigned long src_sz = src->round_size();
133 if (dst_offs >= dst_sz || src_offs >= src_sz)
134 {
135 size = 0;
136 return true;
137 }
138
139 unsigned dst_pg_sz = dst->page_size();
140
141 if (src->page_size() < dst_pg_sz)
142 {
143 //L4::cout << "page sizes do not map\n";
144 return false;
145 }
146
147 unsigned long dst_align = dst_offs & (dst_pg_sz-1);
148
149 if (dst_align != (src_offs & (dst_pg_sz-1)))
150 {
151 if (0)
152 L4::cout << "alignment error " << L4::hex << src_offs
153 << " " << dst_offs << L4::dec << '\n';
154
155 return false;
156 }
157 if (0)
158 L4::cout << "do copy on write\n";
159
160 unsigned long copy_sz = size
161 = min(min(size, dst_sz - dst_offs), src_sz - src_offs);
162
163 if (dst_align)
164 {
165 unsigned long cp_sz = min(copy_sz, dst_pg_sz - dst_align);
166 copy_sz -= cp_sz;
167
168 if (0)
169 L4::cout << "ensure cow starts on page: cp=" << cp_sz << '\n';
170
171 __do_real_copy(dst, dst_offs, src, src_offs, cp_sz);
172 }
173
174 unsigned long cow_sz = trunc_page(dst_pg_sz, copy_sz);
175 unsigned long cp_sz = copy_sz - cow_sz;
176
177 if (0)
178 L4::cout << "cow_sz=" << cow_sz << "; cp_sz=" << cp_sz << '\n';
179
180 __do_cow_copy(dst, dst_offs, dst_pg_sz, src, src_offs, cow_sz);
181 __do_real_copy(dst, dst_offs, src, src_offs, cp_sz);
182
183 return true;
184 }
185
186 bool
__do_lazy_copy2(Dataspace_noncont * dst,unsigned long dst_offs,Dataspace_noncont const * src,unsigned long src_offs,unsigned long & size)187 __do_lazy_copy2(Dataspace_noncont *dst, unsigned long dst_offs,
188 Dataspace_noncont const *src, unsigned long src_offs, unsigned long &size)
189 {
190 unsigned long dst_sz = dst->size();
191 unsigned long src_sz = src->round_size();
192 if (dst_offs >= dst_sz || src_offs >= src_sz)
193 {
194 size = 0;
195 return true;
196 }
197
198 unsigned dst_pg_sz = dst->page_size();
199
200 if (src->page_size() != dst_pg_sz)
201 {
202 //L4::cout << "page sizes do not map\n";
203 return false;
204 }
205
206 unsigned long dst_align = dst_offs & (dst_pg_sz-1);
207
208 if (dst_align != (src_offs & (dst_pg_sz-1)))
209 {
210 if (0)
211 L4::cout << "alignment error " << L4::hex << src_offs
212 << " " << dst_offs << L4::dec << '\n';
213
214 return false;
215 }
216
217 if (0)
218 L4::cout << "do copy on write\n";
219
220 unsigned long copy_sz = size
221 = min(min(size, dst_sz - dst_offs), src_sz - src_offs);
222
223 if (dst_align)
224 {
225 unsigned long cp_sz = min(copy_sz, dst_pg_sz - dst_align);
226 copy_sz -= cp_sz;
227
228 if (0)
229 L4::cout << "ensure cow starts on page: cp=" << cp_sz << '\n';
230
231 __do_real_copy(dst, dst_offs, src, src_offs, cp_sz);
232 }
233
234 unsigned long cow_sz = trunc_page(dst_pg_sz, copy_sz);
235 unsigned long cp_sz = copy_sz - cow_sz;
236
237 if (0)
238 L4::cout << "cow_sz=" << cow_sz << "; cp_sz=" << cp_sz << '\n';
239
240 __do_cow_copy2(dst, dst_offs, dst_pg_sz, src, src_offs, cow_sz);
241 __do_real_copy(dst, dst_offs, src, src_offs, cp_sz);
242
243 return true;
244 }
245
246 }; // and local anon namespace
247
248 unsigned long
copy(Dataspace * dst,unsigned long dst_offs,Dataspace const * src,unsigned long src_offs,unsigned long size)249 Dataspace_util::copy(Dataspace *dst, unsigned long dst_offs,
250 Dataspace const *src, unsigned long src_offs, unsigned long size)
251 {
252 if (src->can_cow() && dst->can_cow())
253 {
254 if (!src->map_flags().w() && src->is_static())
255 {
256 Dataspace_noncont *nc = dynamic_cast<Dataspace_noncont*>(dst);
257 if (nc && __do_lazy_copy(nc, dst_offs, src, src_offs, size))
258 return size;
259 }
260 else
261 {
262 Dataspace_noncont *dst_n = dynamic_cast<Dataspace_noncont*>(dst);
263 Dataspace_noncont const *src_n = dynamic_cast<Dataspace_noncont const *>(src);
264 if (dst_n && src_n
265 && __do_lazy_copy2(dst_n, dst_offs, src_n, src_offs, size))
266 return size;
267 }
268 }
269
270 return __do_eager_copy(dst, dst_offs, src, src_offs, size);
271 }
272
273
274