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