]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/arm/pagetable-arch.cpp
update
[l4.git] / kernel / fiasco / src / kern / arm / pagetable-arch.cpp
1 //---------------------------------------------------------------------------
2 INTERFACE[arm]:
3
4 class Mem_page_attr
5 {
6   friend class Pte;
7
8 public:
9   unsigned long get_ap() const;
10   void set_ap(unsigned long ap);
11
12 private:
13   unsigned long _a;
14 };
15
16 //---------------------------------------------------------------------------
17 INTERFACE[arm && armv5]:
18
19 EXTENSION class Mem_page_attr
20 {
21 public:
22   enum
23   {
24     Write   = 0x400,
25     User    = 0x800,
26     Ap_mask = 0xc00,
27   };
28 };
29
30 //---------------------------------------------------------------------------
31 INTERFACE[arm && (armv6 || armv7)]:
32
33 EXTENSION class Mem_page_attr
34 {
35 public:
36   enum
37   {
38     Write   = 0x200,
39     User    = 0x020,
40     Ap_mask = 0x220,
41   };
42 };
43
44 //---------------------------------------------------------------------------
45 INTERFACE[arm && !(mpcore || armca9)]:
46
47 EXTENSION class Mem_page_attr
48 {
49 public:
50   // do not use Shaed bit on non MP CPUs because this leads to uncached memory
51   // accesses most of the time!
52   enum { Shared  = 0 };
53 };
54
55 EXTENSION class Page_table
56 {
57 public:
58   enum { Ttbr_bits = 0x0 };
59 };
60
61 //---------------------------------------------------------------------------
62 INTERFACE[arm && (mpcore || armca9)]:
63
64 EXTENSION class Mem_page_attr
65 {
66 public:
67   // use shared bit on MP CPUs as we need cache coherency
68   enum { Shared  = 0x400 };
69 };
70
71 //---------------------------------------------------------------------------
72 INTERFACE[arm && mpcore]:
73
74 EXTENSION class Page_table
75 {
76 public:
77   enum { Ttbr_bits = 0xa };
78 };
79
80 //---------------------------------------------------------------------------
81 INTERFACE[arm && armca9]:
82
83 EXTENSION class Page_table
84 {
85 public:
86   enum
87   {
88     Ttbr_bits =    (1 << 1)            // S, Shareable bit
89                 |  (1 << 3)            // RGN, Region bits, Outer WriteBackWriteAlloc
90                 |  (0 << 0) | (1 << 6) // IRGN, Inner region bits, WB-WA
91                 |  (1 << 5)            // NOS
92                 ,
93   };
94 };
95
96 //---------------------------------------------------------------------------
97 INTERFACE [arm]:
98
99 #include "paging.h"
100 #include "per_cpu_data.h"
101
102 class Ram_quota;
103
104 class Pte
105 {
106 public:
107 //private:
108   unsigned long _pt;
109   Mword *_pte;
110
111 public:
112   Pte(Page_table *pt, unsigned level, Mword *pte)
113   : _pt((unsigned long)pt | level), _pte(pte)
114   {}
115 };
116
117
118 EXTENSION class Page_table
119 {
120 private:
121   Mword raw[4096];
122   enum
123   {
124     Pt_base_mask     = 0xfffffc00,
125     Pde_type_coarse  = 0x01,
126   };
127
128 private:
129   static Per_cpu<Page_table *> _current;
130 };
131
132 //---------------------------------------------------------------------------
133 IMPLEMENTATION [arm && vcache]:
134
135 PUBLIC static inline
136 bool
137 Pte::need_cache_clean()
138 {
139   return false;
140 }
141
142 //---------------------------------------------------------------------------
143 IMPLEMENTATION [arm && !vcache && !armca9]:
144
145 PUBLIC static inline
146 bool
147 Pte::need_cache_clean()
148 {
149   return true;
150 }
151
152 //---------------------------------------------------------------------------
153 IMPLEMENTATION [arm && !vcache && armca9]:
154
155 PUBLIC static inline
156 bool
157 Pte::need_cache_clean()
158 {
159   return false;
160 }
161
162 //---------------------------------------------------------------------------
163 IMPLEMENTATION [arm]:
164
165 #include <cassert>
166 #include <cstring>
167
168 #include "mem_unit.h"
169 #include "kdb_ke.h"
170 #include "ram_quota.h"
171
172 PUBLIC inline explicit
173 Mem_page_attr::Mem_page_attr(unsigned long attr) : _a(attr)
174 {}
175
176 PRIVATE inline
177 unsigned long
178 Mem_page_attr::raw() const
179 { return _a; }
180
181 PUBLIC inline
182 void
183 Mem_page_attr::set_caching(unsigned long del, unsigned long set)
184 {
185   del &= Page::Cache_mask;
186   set &= Page::Cache_mask;
187   _a = (_a & ~del) | set;
188 }
189
190 PUBLIC inline NEEDS[Mem_page_attr::get_ap]
191 unsigned long
192 Mem_page_attr::get_abstract() const
193 { return get_ap() | (_a & Page::Cache_mask); }
194
195 PUBLIC inline NEEDS[Mem_page_attr::set_ap]
196 void
197 Mem_page_attr::set_abstract(unsigned long a)
198 {
199   _a = (_a & ~Page::Cache_mask) | (a & Page::Cache_mask);
200   set_ap(a);
201 }
202
203 PUBLIC inline NEEDS[Mem_page_attr::get_ap]
204 bool
205 Mem_page_attr::permits(unsigned long attr)
206 { return (get_ap() & attr) == attr; }
207
208 Per_cpu<Page_table *> DEFINE_PER_CPU Page_table::_current;
209
210 PUBLIC inline
211 unsigned long
212 Pte::valid() const
213 { return *_pte & 3; }
214
215 PUBLIC inline
216 unsigned long
217 Pte::phys() const
218 {
219   switch(_pt & 3)
220     {
221     case 0:
222       switch (*_pte & 3)
223         {
224         case 2:  return *_pte & ~((1 << 20) - 1); // 1MB
225         default: return ~0UL;
226         }
227     case 1:
228       switch (*_pte & 3)
229         {
230         case 2: return *_pte & ~((4 << 10) - 1);
231         default: return ~0UL;
232         }
233     default: return ~0UL;
234     }
235 }
236
237 PUBLIC inline
238 unsigned long
239 Pte::phys(void *virt)
240 {
241   unsigned long p = phys();
242   return p | (((unsigned long)virt) & (size()-1));
243 }
244
245 PUBLIC inline
246 unsigned long
247 Pte::lvl() const
248 { return (_pt & 3); }
249
250 PUBLIC inline
251 unsigned long
252 Pte::raw() const
253 { return *_pte; }
254
255 PUBLIC inline
256 bool
257 Pte::superpage() const
258 { return !(_pt & 3) && ((*_pte & 3) == 2); }
259
260 PUBLIC inline
261 unsigned long
262 Pte::size() const
263 {
264   switch(_pt & 3)
265     {
266     case 0:
267       switch (*_pte & 3)
268         {
269         case 2:  return 1 << 20; // 1MB
270         default: return 1 << 20;
271         }
272     case 1:
273       switch (*_pte & 3)
274         {
275         case 1: return 64 << 10;
276         case 2: return 4 << 10;
277         case 3: return 1 << 10;
278         default: return 4 << 10;
279         }
280     default: return 0;
281     }
282 }
283
284
285 PRIVATE inline NEEDS["mem_unit.h"]
286 void
287 Pte::__set(unsigned long v, bool write_back)
288 {
289   *_pte = v;
290   if (write_back || need_cache_clean())
291     Mem_unit::clean_dcache(_pte);
292 }
293
294 PUBLIC inline NEEDS[Pte::__set]
295 void
296 Pte::set_invalid(unsigned long val, bool write_back)
297 { __set(val & ~3, write_back); }
298
299
300 //-----------------------------------------------------------------------------
301 IMPLEMENTATION [arm && armv5]:
302
303 IMPLEMENT inline
304 unsigned long
305 Mem_page_attr::get_ap() const
306 {
307   static unsigned char const _map[4] = { 0x8, 0x4, 0x0, 0xc };
308   return ((unsigned long)_map[(_a >> 10) & 0x3]) << 8UL;
309 }
310
311 IMPLEMENT inline
312 void
313 Mem_page_attr::set_ap(unsigned long ap)
314 {
315   static unsigned char const _map[4] = { 0x4, 0x4, 0x0, 0xc };
316   _a = (_a & ~0xc00) | (((unsigned long)_map[(ap >> 10) & 0x3]) << 8UL);
317 }
318
319 PUBLIC inline NEEDS[Pte::__set, Mem_page_attr::raw]
320 void
321 Pte::set(Address phys, unsigned long size, Mem_page_attr const &attr,
322     bool write_back)
323 {
324   switch (_pt & 3)
325     {
326     case 0:
327       if (size != (1 << 20))
328         return;
329       __set(phys | (attr.raw() & Page::MAX_ATTRIBS) | 2, write_back);
330       break;
331     case 1:
332         {
333           if (size != (4 << 10))
334             return;
335           unsigned long ap = attr.raw() & 0xc00; ap |= ap >> 2; ap |= ap >> 4;
336           __set(phys | (attr.raw() & 0x0c) | ap | 2, write_back);
337         }
338       break;
339     }
340 }
341
342 PUBLIC inline NEEDS[Mem_page_attr::Mem_page_attr]
343 Mem_page_attr
344 Pte::attr() const { return Mem_page_attr(*_pte & 0xc0c); }
345
346 PUBLIC inline NEEDS["mem_unit.h"]
347 void
348 Pte::attr(Mem_page_attr const &attr, bool write_back)
349 {
350   switch (_pt & 3)
351     {
352     case 0:
353       __set((*_pte & ~0xc0c) | (attr.raw() & 0xc0c), write_back);
354       break;
355     case 1:
356         {
357           unsigned long ap = attr.raw() & 0xc00; ap |= ap >> 2; ap |= ap >> 4;
358           __set((*_pte & ~0xffc) | (attr.raw() & 0x0c) | ap, write_back);
359         }
360       break;
361     }
362 }
363
364 PUBLIC /*inline*/
365 void Page_table::activate()
366 {
367   Pte p = walk(this, 0, false, 0);
368   if (_current.cpu(current_cpu()) != this)
369     {
370       _current.cpu(current_cpu()) = this;
371       Mem_unit::flush_vcache();
372       asm volatile (
373           "mcr p15, 0, r0, c8, c7, 0 \n" // TLB flush
374           "mcr p15, 0, %0, c2, c0    \n" // pdbr
375
376           "mrc p15, 0, r1, c2, c0    \n"
377           "mov r1, r1                \n"
378           "sub pc, pc, #4            \n"
379           :
380           : "r" (p.phys(this))
381           : "r1");
382     }
383 }
384
385
386 //-----------------------------------------------------------------------------
387 IMPLEMENTATION [arm && (armv6 || armv7)]:
388
389 IMPLEMENT inline
390 unsigned long
391 Mem_page_attr::get_ap() const
392 {
393   return (_a & User) | ((_a & Write) ^ Write);
394 }
395
396 IMPLEMENT inline NEEDS[Mem_page_attr::raw]
397 void
398 Mem_page_attr::set_ap(unsigned long ap)
399 {
400   _a = (_a & ~(User | Write)) | (ap & User) | ((ap & Write) ^ Write) | 0x10;
401 }
402
403 PUBLIC inline NEEDS[Pte::__set]
404 void
405 Pte::set(Address phys, unsigned long size, Mem_page_attr const &attr,
406          bool write_back)
407 {
408   switch (_pt & 3)
409     {
410     case 0:
411       if (size != (1 << 20))
412         return;
413       {
414         unsigned long a = attr.raw() & 0x0c; // C & B
415         a |= ((attr.raw() & 0xff0) | Mem_page_attr::Shared) << 6;
416         __set(phys | a | 0x2, write_back);
417       }
418       break;
419     case 1:
420       if (size != (4 << 10))
421         return;
422       __set(phys | (attr.raw() & Page::MAX_ATTRIBS) | 0x2 | Mem_page_attr::Shared, write_back);
423       break;
424     }
425 }
426
427 PUBLIC inline NEEDS[Mem_page_attr::raw]
428 Mem_page_attr
429 Pte::attr() const
430 {
431   switch (_pt & 3)
432     {
433     case 0:
434         {
435           unsigned long a = *_pte & 0x0c; // C & B
436           a |= (*_pte >> 6) & 0xff0;
437           return Mem_page_attr(a);
438         }
439     case 1:
440     default:
441       return Mem_page_attr(*_pte & Page::MAX_ATTRIBS);
442     }
443 }
444
445 PUBLIC inline NEEDS["mem_unit.h", Mem_page_attr::raw]
446 void
447 Pte::attr(Mem_page_attr const &attr, bool write_back)
448 {
449   switch (_pt & 3)
450     {
451     case 1:
452       __set((*_pte & ~Page::MAX_ATTRIBS)
453             | (attr.raw() & Page::MAX_ATTRIBS), write_back);
454       break;
455     case 0:
456         {
457           unsigned long a = attr.raw() & 0x0c;
458           a |= (attr.raw() & 0xff0) << 6;
459           __set((*_pte & ~0x3fcc) | a, write_back);
460         }
461       break;
462     }
463 }
464
465 //-----------------------------------------------------------------------------
466 IMPLEMENTATION [armv6 || armca8]:
467
468 PUBLIC
469 void Page_table::activate(unsigned long asid)
470 {
471   Pte p = walk(this, 0, false, 0);
472   if (_current.cpu(current_cpu()) != this)
473     {
474       _current.cpu(current_cpu()) = this;
475       asm volatile (
476           "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
477           "mcr p15, 0, r0, c7, c10, 4   \n" // dsb
478           "mcr p15, 0, %0, c2, c0       \n" // set TTBR
479           "mcr p15, 0, r0, c7, c10, 4   \n" // dsb
480           "mcr p15, 0, %1, c13, c0, 1   \n" // set new ASID value
481           "mcr p15, 0, r0, c7, c5, 4    \n" // isb
482           "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
483           "mrc p15, 0, r1, c2, c0       \n"
484           "mov r1, r1                   \n"
485           "sub pc, pc, #4               \n"
486           :
487           : "r" (p.phys(this) | Ttbr_bits), "r"(asid), "r" (0)
488           : "r1");
489     }
490 }
491
492 //-----------------------------------------------------------------------------
493 IMPLEMENTATION [armv7 && armca9]:
494
495 PUBLIC
496 void Page_table::activate(unsigned long asid)
497 {
498   Pte p = walk(this, 0, false, 0);
499   if (_current.cpu(current_cpu()) != this)
500     {
501       _current.cpu(current_cpu()) = this;
502       asm volatile (
503           "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
504           "dsb                          \n"
505           "mcr p15, 0, %2, c13, c0, 1   \n" // change ASID to 0
506           "isb                          \n"
507           "mcr p15, 0, %0, c2, c0       \n" // set TTBR
508           "isb                          \n"
509           "mcr p15, 0, %1, c13, c0, 1   \n" // set new ASID value
510           "isb                          \n"
511           "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
512           "isb                          \n"
513           "mov r1, r1                   \n"
514           "sub pc, pc, #4               \n"
515           :
516           : "r" (p.phys(this) | Ttbr_bits), "r"(asid), "r" (0)
517           : "r1");
518     }
519 }
520
521 //-----------------------------------------------------------------------------
522 IMPLEMENTATION [arm && !mp]:
523
524 PRIVATE inline
525 Mword
526 Page_table::current_virt_to_phys(void *virt)
527 {
528   return current()->walk(virt, 0, false, 0).phys(virt);
529 }
530
531 //-----------------------------------------------------------------------------
532 IMPLEMENTATION [arm && mp]:
533
534 /*
535  * This version for MP avoids calling Page_table::current() which calls
536  * current_cpu() which is not available on the boot-stack. That's why we use
537  * the following way of doing a virt->phys translation on the current page
538  * table.
539  */
540 PRIVATE inline
541 Mword
542 Page_table::current_virt_to_phys(void *virt)
543 {
544   Mword phys;
545   Mword offset = (Mword)virt & ~Config::PAGE_MASK;
546   asm volatile("mcr p15,0,%1,c7,c8,0 \n"
547                "mrc p15,0,%0,c7,c4,0 \n"
548                : "=r" (phys)
549                : "r" ((Mword)virt & Config::PAGE_MASK));
550   return (phys & Config::PAGE_MASK) | offset;
551 }
552
553
554 //-----------------------------------------------------------------------------
555 IMPLEMENTATION [arm]:
556
557 #include "globals.h"
558 #include "mem.h"
559
560 IMPLEMENT
561 void *Page_table::operator new(size_t s)
562 {
563   (void)s;
564   assert(s == 16*1024);
565   return alloc()->alloc(14); // 2^14 = 16K
566 }
567
568 IMPLEMENT
569 void Page_table::operator delete(void *b)
570 {
571   alloc()->free(14, b);
572 }
573
574
575 IMPLEMENT
576 Page_table::Page_table()
577 {
578   Mem::memset_mwords(raw, 0, sizeof(raw) / sizeof(Mword));
579   if (Pte::need_cache_clean())
580     Mem_unit::clean_dcache(raw, (char *)raw + sizeof(raw));
581 }
582
583 PUBLIC
584 void Page_table::free_page_tables(void *start, void *end)
585 {
586   for (unsigned i = (Address)start >> 20; i < ((Address)end >> 20); ++i)
587     {
588       Pte p(this, 0, raw + i);
589       if (p.valid() && !p.superpage())
590         {
591           void *pt = (void*)Mem_layout::phys_to_pmem(p.raw() & Pt_base_mask);
592           alloc()->free(10, pt);
593         }
594     }
595 }
596
597 PRIVATE inline
598 static unsigned Page_table::pd_index( void const *const address )
599 {
600   return (Mword)address >> 20; // 1MB steps
601 }
602
603 PRIVATE inline
604 static unsigned Page_table::pt_index( void const *const address )
605 {
606   return ((Mword)address >> 12) & 255; // 4KB steps for coarse pts
607 }
608
609 PUBLIC
610 Pte
611 Page_table::walk(void *va, unsigned long size, bool write_back, Ram_quota *q)
612 {
613   unsigned const pd_idx = pd_index(va);
614
615   Mword *pt = 0;
616
617   Pte pde(this, 0, raw + pd_idx);
618
619   if (!pde.valid())
620     {
621       if (size == (4 << 10))
622         {
623           assert(q);
624           if (q->alloc(1<<10))
625             {
626               pt = (Mword*)alloc()->alloc(10);
627               if (EXPECT_FALSE(!pt))
628                 q->free(1<<10);
629             }
630           if (!pt)
631             return pde;
632
633           Mem::memset_mwords(pt, 0, 1024 >> 2);
634
635           if (write_back || Pte::need_cache_clean())
636             Mem_unit::clean_dcache(pt, (char*)pt + 1024);
637
638           raw[pd_idx] = current_virt_to_phys(pt) | Pde_type_coarse;
639
640           if (write_back || Pte::need_cache_clean())
641             Mem_unit::clean_dcache(raw + pd_idx);
642         }
643       else
644         return pde;
645     }
646   else if (pde.superpage())
647     return pde;
648
649   if (!pt)
650     pt = (Mword *)Mem_layout::phys_to_pmem(pde.raw() & Pt_base_mask);
651
652   unsigned const pt_idx = pt_index(va);
653
654   return Pte(this, 1, pt + pt_idx);
655 }
656
657
658 IMPLEMENT
659 void Page_table::init(Page_table *current)
660 {
661   unsigned domains      = 0x0001;
662   _current.cpu(0) = current;
663
664   asm volatile (
665       "mcr p15, 0, %0, c3, c0       \n" // domains
666       :
667       : "r"(domains) );
668 }
669
670
671 IMPLEMENT /*inline*/
672 void Page_table::copy_in(void *my_base, Page_table *o,
673                          void *base, size_t size, unsigned long asid)
674 {
675   unsigned pd_idx = pd_index(my_base);
676   unsigned pd_idx_max = pd_index(my_base) + pd_index((void*)size);
677   unsigned o_pd_idx = pd_index(base);
678   bool need_flush = false;
679
680   //printf("copy_in: %03x-%03x from %03x\n", pd_idx, pd_idx_max, o_pd_idx);
681
682   if (asid != ~0UL)
683     {
684       for (unsigned i = pd_idx;  i < pd_idx_max; ++i)
685         if (Pte(this, 0, raw + i).valid())
686           {
687             Mem_unit::flush_vdcache();
688             need_flush = true;
689             break;
690           }
691     }
692
693   for (unsigned i = pd_idx; i < pd_idx_max; ++i, ++o_pd_idx)
694     raw[i] = o->raw[o_pd_idx];
695
696   if (Pte::need_cache_clean())
697     Mem_unit::clean_dcache(raw + pd_idx, raw + pd_idx_max);
698
699   if (need_flush && (asid != ~0UL))
700     Mem_unit::dtlb_flush(asid);
701 }
702
703 #if 0
704 PUBLIC
705 void
706 Page_table::invalidate(void *my_base, unsigned size, unsigned long asid = ~0UL)
707 {
708   unsigned pd_idx = pd_index(my_base);
709   unsigned pd_idx_max = pd_index(my_base) + pd_index((void*)size);
710   bool need_flush = false;
711
712   //printf("invalidate: %03x-%03x\n", pd_idx, pd_idx_max);
713
714   if (asid != ~0UL)
715     {
716       for (unsigned i = pd_idx; i < pd_idx_max; ++i)
717         if (Pte(this, 0, raw + i).valid())
718           {
719             Mem_unit::flush_vdcache();
720             need_flush = true;
721             break;
722           }
723     }
724
725   for (unsigned i = pd_idx; i < pd_idx_max; ++i)
726     raw[i] = 0;
727
728   // clean the caches if manipulating the current pt or in the case if phys.
729   // tagged caches.
730   if ((asid != ~0UL) || Pte::need_cache_clean())
731     Mem_unit::clean_dcache(raw + pd_idx, raw + pd_idx_max);
732
733   if (need_flush && (asid != ~0UL))
734     Mem_unit::tlb_flush(asid);
735 }
736 #endif
737
738 PUBLIC static inline
739 Page_table *Page_table::current(unsigned cpu)
740 { return _current.cpu(cpu); }
741
742 IMPLEMENT inline NEEDS["globals.h"]
743 Page_table *Page_table::current()
744 { return _current.cpu(current_cpu()); }
745
746 IMPLEMENT
747 void *
748 Page_table::dir() const
749 {
750   return const_cast<Page_table *>(this);
751 }
752