]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/io_space.cpp
b46ef3567eaa88fc6390dd7ae5f56b22276f1139
[l4.git] / kernel / fiasco / src / kern / io_space.cpp
1 INTERFACE [io]:
2
3 #include "types.h"
4 #include "mem_space.h"
5
6 class Mem_space;
7 class Space;
8
9 /** Wrapper class for io_{map,unmap}.  This class serves as an adapter
10     for map<Generic_io_space> to Mem_space.
11  */
12 template< typename SPACE >
13 class Generic_io_space
14 {
15   friend class Jdb_iomap;
16
17 public:
18   static char const * const name;
19
20   typedef Page_number Addr;
21   typedef Page_count Size;
22   typedef Page_number Phys_addr;
23   typedef void Reap_list;
24
25   enum
26   {
27     Need_insert_tlb_flush = 0,
28     Need_xcpu_tlb_flush = 0,
29     Map_page_size = 1,
30     Page_shift = 0,
31     Map_superpage_shift = 16,
32     Map_superpage_size = 0x10000,
33     Map_max_address = 0x10000,
34     Whole_space = 16,
35     Identity_map = 1,
36   };
37
38   // We'd rather like to use a "using Mem_space::Status" declaration here,
39   // but that wouldn't make the enum values accessible as
40   // Generic_io_space::Insert_ok and so on.
41   enum Status
42   {
43     Insert_ok = 0,              ///< Mapping was added successfully.
44     Insert_warn_exists,         ///< Mapping already existed
45     Insert_warn_attrib_upgrade, ///< Mapping already existed, attribs upgrade
46     Insert_err_nomem,           ///< Couldn't alloc new page table
47     Insert_err_exists           ///< A mapping already exists at the target addr
48   };
49
50   enum Page_attribs
51   {
52     Page_writable = Mem_space::Page_writable,
53     Page_user_accessible = Mem_space::Page_user_accessible,
54     Page_references = 0,
55     Page_all_attribs = Page_writable | Page_user_accessible
56   };
57
58   static Addr map_max_address()
59   { return Addr(Map_max_address); }
60
61   static Address superpage_size()
62   { return Map_superpage_size; }
63
64   static bool has_superpages()
65   { return true; }
66
67   static Phys_addr page_address(Phys_addr o, Size s)
68   { return o.trunc(s); }
69
70   static Phys_addr subpage_address(Phys_addr addr, Size offset)
71   { return addr | offset; }
72
73 private:
74   // DATA
75   Mword _io_counter;
76
77   Mem_space const *mem_space() const
78   { return static_cast<SPACE const *>(this); }
79
80   Mem_space *mem_space()
81   { return static_cast<SPACE *>(this); }
82 };
83
84 template< typename SPACE>
85 char const * const Generic_io_space<SPACE>::name = "Io_space";
86
87
88 //----------------------------------------------------------------------------
89 IMPLEMENTATION [io]:
90
91 #include <cassert>
92 #include <cstring>
93
94 #include "atomic.h"
95 #include "config.h"
96 #include "l4_types.h"
97 #include "kmem_alloc.h"
98 #include "panic.h"
99 #include "paging.h"
100   
101
102 PUBLIC template< typename SPACE >
103 static inline
104 Mword
105 Generic_io_space<SPACE>::xlate_flush(unsigned char rights)
106 {
107   if (rights)
108     return Page_all_attribs;
109   else
110     return 0;
111 }
112
113 PUBLIC template< typename SPACE >
114 static inline
115 Mword
116 Generic_io_space<SPACE>::is_full_flush(unsigned char rights)
117 {
118   return rights;
119 }
120
121 PUBLIC template< typename SPACE >
122 static inline
123 Mword
124 Generic_io_space<SPACE>::xlate_flush_result(Mword /*attribs*/)
125 { return 0; }
126
127 PUBLIC template< typename SPACE >
128 inline
129 Generic_io_space<SPACE>::Generic_io_space()
130   : _io_counter(0)
131 {}
132
133
134 PUBLIC template< typename SPACE >
135 Generic_io_space<SPACE>::~Generic_io_space()
136 {
137   if (!mem_space()->dir())
138     return;
139
140   Pdir::Iter iopte = mem_space()->dir()->walk(Virt_addr(Mem_layout::Io_bitmap));
141
142   // do we have an IO bitmap?
143   if (iopte.e->valid())
144     {
145       // sanity check
146       assert (iopte.shift() != Config::SUPERPAGE_SHIFT);
147
148       Kmem_alloc::allocator()
149         ->q_free_phys(ram_quota(), Config::PAGE_SHIFT,
150                       iopte.e[0].addr());
151       
152       if (iopte.e[1].valid())
153         Kmem_alloc::allocator()
154           ->q_free_phys(ram_quota(), Config::PAGE_SHIFT,
155                         iopte.e[1].addr());
156   
157       Pdir::Iter iopde = mem_space()->dir()->walk(Virt_addr(Mem_layout::Io_bitmap), 0);
158       
159       // free the page table
160       Kmem_alloc::allocator()
161         ->q_free_phys(ram_quota(), Config::PAGE_SHIFT,
162                       iopde.e->addr());
163
164       // free reference
165       *iopde.e = 0;
166     }
167 }
168
169 PUBLIC template< typename SPACE >
170 inline
171 Ram_quota *
172 Generic_io_space<SPACE>::ram_quota() const
173 { return static_cast<SPACE const *>(this)->ram_quota(); }
174
175 PRIVATE template< typename SPACE >
176 inline
177 bool
178 Generic_io_space<SPACE>::is_superpage()
179 { return _io_counter & 0x10000000; }
180
181
182 // 
183 // Utilities for map<Generic_io_space> and unmap<Generic_io_space>
184 // 
185
186 PUBLIC template< typename SPACE >
187 virtual
188 bool
189 Generic_io_space<SPACE>::v_fabricate(Addr address, Phys_addr *phys,
190                                      Size *size, unsigned *attribs = 0)
191 {
192   return Generic_io_space::v_lookup(address.trunc(Size(Map_page_size)),
193       phys, size, attribs);
194
195 }
196
197 PUBLIC template< typename SPACE >
198 inline NEEDS[Generic_io_space::is_superpage] 
199 bool 
200 Generic_io_space<SPACE>::v_lookup(Addr virt, Phys_addr *phys = 0,
201                                   Size *size = 0, unsigned *attribs = 0)
202 {
203   if (is_superpage())
204     {
205       if (size) *size = Size(Map_superpage_size);
206       if (phys) *phys = Phys_addr(0);
207       if (attribs) *attribs = Page_writable | Page_user_accessible;
208       return true;
209     }
210
211   if (size) *size = Size(1);
212
213   if (io_lookup(virt.value()))
214     {
215       if (phys) *phys = virt;
216       if (attribs) *attribs = Page_writable | Page_user_accessible;
217       return true;
218     }
219
220   if (get_io_counter() == 0)
221     {
222       if (size) *size = Size(Map_superpage_size);
223       if (phys) *phys = Phys_addr(0);
224     }
225
226   return false;
227 }
228
229 PUBLIC template< typename SPACE >
230 inline NEEDS [Generic_io_space::is_superpage]
231 unsigned long
232 Generic_io_space<SPACE>::v_delete(Addr virt, Size size,
233                                   unsigned long page_attribs = Page_all_attribs)
234 {
235   (void)size;
236   (void)page_attribs;
237   assert (page_attribs == Page_all_attribs);
238
239   if (is_superpage())
240     {
241       assert (size.value() == Map_superpage_size);
242
243       for (unsigned p = 0; p < Map_max_address; ++p)
244         io_delete(p);
245
246       _io_counter = 0;
247       return Page_writable | Page_user_accessible;
248     }
249
250   assert (size.value() == 1);
251
252   return io_delete(virt.value());
253 }
254
255 PUBLIC template< typename SPACE >
256 inline
257 typename Generic_io_space<SPACE>::Status
258 Generic_io_space<SPACE>::v_insert(Phys_addr phys, Addr virt, Size size,
259                                   unsigned page_attribs)
260 {
261   (void)phys;
262   (void)size;
263   (void)page_attribs;
264
265   assert (phys == virt);
266   if (is_superpage() && size.value() == Map_superpage_size)
267     return Insert_warn_exists;
268
269   if (get_io_counter() == 0 && size.value() == Map_superpage_size)
270     {
271       for (unsigned p = 0; p < Map_max_address; ++p)
272         io_insert(p);
273       _io_counter |= 0x10000000;
274
275       return Insert_ok;
276     }
277
278   assert (size.value() == 1);
279
280   return typename Generic_io_space::Status(io_insert(virt.value()));
281 }
282
283
284 PUBLIC template< typename SPACE >
285 inline static
286 void
287 Generic_io_space<SPACE>::tlb_flush()
288 {}
289
290 PUBLIC template< typename SPACE >
291 inline static
292 bool 
293 Generic_io_space<SPACE>::need_tlb_flush()
294 { return false; }
295
296 //
297 // IO lookup / insert / delete / counting
298 //
299
300 /** return the IO counter.
301  *  @return number of IO ports mapped / 0 if not mapped
302  */
303 PUBLIC template< typename SPACE >
304 inline NEEDS["paging.h"]
305 Mword
306 Generic_io_space<SPACE>::get_io_counter() const
307 {
308   return _io_counter & ~0x10000000;
309 }
310
311
312 /** Add something the the IO counter.
313     @param incr number to add
314     @pre 2nd level page table for IO bitmap is present
315 */
316 template< typename SPACE >
317 inline NEEDS["paging.h"]
318 void
319 Generic_io_space<SPACE>::addto_io_counter(int incr)
320 {
321   atomic_add (&_io_counter, incr);
322 }
323
324
325 /** Lookup one IO port in the IO space.
326     @param port_number port address to lookup; 
327     @return true if mapped
328      false if not 
329  */
330 PROTECTED template< typename SPACE >
331 bool
332 Generic_io_space<SPACE>::io_lookup(Address port_number)
333 {
334   assert(port_number < Mem_layout::Io_port_max);
335
336   // be careful, do not cause page faults here
337   // there might be nothing mapped in the IO bitmap
338
339   Address port_addr = get_phys_port_addr(port_number);
340
341   if(port_addr == ~0UL)
342     return false;               // no bitmap -> no ports
343
344   // so there is memory mapped in the IO bitmap
345   char * port = static_cast<char *>(Kmem::phys_to_virt(port_addr));
346   
347   // bit == 1 disables the port
348   // bit == 0 enables the port
349   return !(*port & get_port_bit(port_number));
350 }
351
352
353 /** Enable one IO port in the IO space.
354     This function is called in the context of the IPC sender!
355     @param port_number address of the port
356     @return Insert_warn_exists if some ports were mapped in that IO page
357        Insert_err_nomem if memory allocation failed
358        Insert_ok if otherwise insertion succeeded
359  */
360 PROTECTED template< typename SPACE >
361 typename Generic_io_space<SPACE>::Status
362 Generic_io_space<SPACE>::io_insert(Address port_number)
363 {
364   assert(port_number < Mem_layout::Io_port_max);
365
366   Address port_virt = Mem_layout::Io_bitmap + (port_number >> 3);
367   Address port_phys = mem_space()->virt_to_phys(port_virt);
368
369   if (port_phys == ~0UL)
370     {
371       // nothing mapped! Get a page and map it in the IO bitmap
372       void *page;
373       if (!(page=Kmem_alloc::allocator()->q_alloc(ram_quota(),
374               Config::PAGE_SHIFT)))
375         return Insert_err_nomem;
376
377       // clear all IO ports
378       // bit == 1 disables the port
379       // bit == 0 enables the port
380       memset(page, 0xff, Config::PAGE_SIZE);
381
382       Mem_space::Status status =
383         mem_space()->v_insert(
384             Mem_space::Phys_addr(Mem_layout::pmem_to_phys(page)),
385             Mem_space::Addr(port_virt & Config::PAGE_MASK),
386             Mem_space::Size(Config::PAGE_SIZE), Page_writable);
387
388       if (status == Mem_space::Insert_err_nomem)
389         {
390           Kmem_alloc::allocator()->free(Config::PAGE_SHIFT,page);
391           ram_quota()->free(Config::PAGE_SIZE);
392           return Insert_err_nomem;
393         }
394
395       // we've been careful, so insertion should have succeeded
396       assert(status == Mem_space::Insert_ok); 
397
398       port_phys = mem_space()->virt_to_phys(port_virt);
399       assert(port_phys != ~0UL);
400     }
401
402   // so there is memory mapped in the IO bitmap -- write the bits now
403   Unsigned8 *port = static_cast<Unsigned8 *> (Kmem::phys_to_virt(port_phys));
404
405   if (*port & get_port_bit(port_number)) // port disabled?
406     {
407       *port &= ~ get_port_bit(port_number);
408       addto_io_counter(1);
409       return Insert_ok;
410     }
411
412   // already enabled
413   return Insert_warn_exists;
414 }
415
416
417 /** Disable one IO port in the IO space.
418     @param port_number port to disable
419  */
420 PROTECTED template< typename SPACE >
421 unsigned
422 Generic_io_space<SPACE>::io_delete(Address port_number)
423 {
424   assert(port_number < Mem_layout::Io_port_max);
425
426   // be careful, do not cause page faults here
427   // there might be nothing mapped in the IO bitmap
428
429   Address port_addr = get_phys_port_addr(port_number);
430
431   if (port_addr == ~0UL)
432     // nothing mapped -> nothing to delete
433     return 0;
434
435   // so there is memory mapped in the IO bitmap -> disable the ports
436   char * port = static_cast<char *> (Kmem::phys_to_virt(port_addr));
437
438   // bit == 1 disables the port
439   // bit == 0 enables the port
440   if(!(*port & get_port_bit(port_number)))    // port enabled ??
441     {
442       *port |= get_port_bit(port_number);
443       addto_io_counter(-1);
444
445       return Page_writable | Page_user_accessible;
446     }
447
448   return 0;
449 }
450
451 template< typename SPACE >
452 INLINE NEEDS["config.h"]
453 Address
454 Generic_io_space<SPACE>::get_phys_port_addr(Address const port_number) const
455 {
456   return mem_space()->virt_to_phys(Mem_layout::Io_bitmap + (port_number >> 3));
457 }
458
459 template< typename SPACE >
460 INLINE 
461 Unsigned8
462 Generic_io_space<SPACE>::get_port_bit(Address const port_number) const
463 {
464   return 1 << (port_number & 7);
465 }
466
467
468 PUBLIC template< typename SPACE >
469 inline static
470 Page_number
471 Generic_io_space<SPACE>::canonize(Page_number v)
472 { return v; }