]> 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
129 //---------------------------------------------------------------------------
130 IMPLEMENTATION [arm && vcache]:
131
132 PUBLIC static inline
133 bool
134 Pte::need_cache_clean()
135 {
136   return false;
137 }
138
139 //---------------------------------------------------------------------------
140 IMPLEMENTATION [arm && !vcache && !armca9]:
141
142 PUBLIC static inline
143 bool
144 Pte::need_cache_clean()
145 {
146   return true;
147 }
148
149 //---------------------------------------------------------------------------
150 IMPLEMENTATION [arm && !vcache && armca9]:
151
152 PUBLIC static inline
153 bool
154 Pte::need_cache_clean()
155 {
156   return false;
157 }
158
159 //---------------------------------------------------------------------------
160 IMPLEMENTATION [arm]:
161
162 #include <cassert>
163 #include <cstring>
164
165 #include "mem_unit.h"
166 #include "kdb_ke.h"
167 #include "ram_quota.h"
168
169 PUBLIC inline explicit
170 Mem_page_attr::Mem_page_attr(unsigned long attr) : _a(attr)
171 {}
172
173 PRIVATE inline
174 unsigned long
175 Mem_page_attr::raw() const
176 { return _a; }
177
178 PUBLIC inline
179 void
180 Mem_page_attr::set_caching(unsigned long del, unsigned long set)
181 {
182   del &= Page::Cache_mask;
183   set &= Page::Cache_mask;
184   _a = (_a & ~del) | set;
185 }
186
187 PUBLIC inline NEEDS[Mem_page_attr::get_ap]
188 unsigned long
189 Mem_page_attr::get_abstract() const
190 { return get_ap() | (_a & Page::Cache_mask); }
191
192 PUBLIC inline NEEDS[Mem_page_attr::set_ap]
193 void
194 Mem_page_attr::set_abstract(unsigned long a)
195 {
196   _a = (_a & ~Page::Cache_mask) | (a & Page::Cache_mask);
197   set_ap(a);
198 }
199
200 PUBLIC inline NEEDS[Mem_page_attr::get_ap]
201 bool
202 Mem_page_attr::permits(unsigned long attr)
203 { return (get_ap() & attr) == attr; }
204
205 PUBLIC inline
206 unsigned long
207 Pte::valid() const
208 { return *_pte & 3; }
209
210 PUBLIC inline
211 unsigned long
212 Pte::phys() const
213 {
214   switch(_pt & 3)
215     {
216     case 0:
217       switch (*_pte & 3)
218         {
219         case 2:  return *_pte & ~((1 << 20) - 1); // 1MB
220         default: return ~0UL;
221         }
222     case 1:
223       switch (*_pte & 3)
224         {
225         case 2: return *_pte & ~((4 << 10) - 1);
226         default: return ~0UL;
227         }
228     default: return ~0UL;
229     }
230 }
231
232 PUBLIC inline
233 unsigned long
234 Pte::phys(void *virt)
235 {
236   unsigned long p = phys();
237   return p | (((unsigned long)virt) & (size()-1));
238 }
239
240 PUBLIC inline
241 unsigned long
242 Pte::lvl() const
243 { return (_pt & 3); }
244
245 PUBLIC inline
246 unsigned long
247 Pte::raw() const
248 { return *_pte; }
249
250 PUBLIC inline
251 bool
252 Pte::superpage() const
253 { return !(_pt & 3) && ((*_pte & 3) == 2); }
254
255 PUBLIC inline
256 unsigned long
257 Pte::size() const
258 {
259   switch(_pt & 3)
260     {
261     case 0:
262       switch (*_pte & 3)
263         {
264         case 2:  return 1 << 20; // 1MB
265         default: return 1 << 20;
266         }
267     case 1:
268       switch (*_pte & 3)
269         {
270         case 1: return 64 << 10;
271         case 2: return 4 << 10;
272         case 3: return 1 << 10;
273         default: return 4 << 10;
274         }
275     default: return 0;
276     }
277 }
278
279
280 PRIVATE inline NEEDS["mem_unit.h"]
281 void
282 Pte::__set(unsigned long v, bool write_back)
283 {
284   *_pte = v;
285   if (write_back || need_cache_clean())
286     Mem_unit::clean_dcache(_pte);
287 }
288
289 PUBLIC inline NEEDS[Pte::__set]
290 void
291 Pte::set_invalid(unsigned long val, bool write_back)
292 { __set(val & ~3, write_back); }
293
294
295 //-----------------------------------------------------------------------------
296 IMPLEMENTATION [arm && armv5]:
297
298 IMPLEMENT inline
299 unsigned long
300 Mem_page_attr::get_ap() const
301 {
302   static unsigned char const _map[4] = { 0x8, 0x4, 0x0, 0xc };
303   return ((unsigned long)_map[(_a >> 10) & 0x3]) << 8UL;
304 }
305
306 IMPLEMENT inline
307 void
308 Mem_page_attr::set_ap(unsigned long ap)
309 {
310   static unsigned char const _map[4] = { 0x4, 0x4, 0x0, 0xc };
311   _a = (_a & ~0xc00) | (((unsigned long)_map[(ap >> 10) & 0x3]) << 8UL);
312 }
313
314 PUBLIC inline NEEDS[Pte::__set, Mem_page_attr::raw]
315 void
316 Pte::set(Address phys, unsigned long size, Mem_page_attr const &attr,
317     bool write_back)
318 {
319   switch (_pt & 3)
320     {
321     case 0:
322       if (size != (1 << 20))
323         return;
324       __set(phys | (attr.raw() & Page::MAX_ATTRIBS) | 2, write_back);
325       break;
326     case 1:
327         {
328           if (size != (4 << 10))
329             return;
330           unsigned long ap = attr.raw() & 0xc00; ap |= ap >> 2; ap |= ap >> 4;
331           __set(phys | (attr.raw() & 0x0c) | ap | 2, write_back);
332         }
333       break;
334     }
335 }
336
337 PUBLIC inline NEEDS[Mem_page_attr::Mem_page_attr]
338 Mem_page_attr
339 Pte::attr() const { return Mem_page_attr(*_pte & 0xc0c); }
340
341 PUBLIC inline NEEDS["mem_unit.h"]
342 void
343 Pte::attr(Mem_page_attr const &attr, bool write_back)
344 {
345   switch (_pt & 3)
346     {
347     case 0:
348       __set((*_pte & ~0xc0c) | (attr.raw() & 0xc0c), write_back);
349       break;
350     case 1:
351         {
352           unsigned long ap = attr.raw() & 0xc00; ap |= ap >> 2; ap |= ap >> 4;
353           __set((*_pte & ~0xffc) | (attr.raw() & 0x0c) | ap, write_back);
354         }
355       break;
356     }
357 }
358
359 PUBLIC /*inline*/
360 void Page_table::activate()
361 {
362   Pte p = walk(this, 0, false, Ptab::Null_alloc(), 0);
363   Mem_unit::flush_vcache();
364   asm volatile (
365       "mcr p15, 0, r0, c8, c7, 0 \n" // TLB flush
366       "mcr p15, 0, %0, c2, c0    \n" // pdbr
367
368       "mrc p15, 0, r1, c2, c0    \n"
369       "mov r1, r1                \n"
370       "sub pc, pc, #4            \n"
371       :
372       : "r" (p.phys(this))
373       : "r1");
374 }
375
376
377 //-----------------------------------------------------------------------------
378 IMPLEMENTATION [arm && (armv6 || armv7)]:
379
380 IMPLEMENT inline
381 unsigned long
382 Mem_page_attr::get_ap() const
383 {
384   return (_a & User) | ((_a & Write) ^ Write);
385 }
386
387 IMPLEMENT inline NEEDS[Mem_page_attr::raw]
388 void
389 Mem_page_attr::set_ap(unsigned long ap)
390 {
391   _a = (_a & ~(User | Write)) | (ap & User) | ((ap & Write) ^ Write) | 0x10;
392 }
393
394 PUBLIC inline NEEDS[Pte::__set]
395 void
396 Pte::set(Address phys, unsigned long size, Mem_page_attr const &attr,
397          bool write_back)
398 {
399   switch (_pt & 3)
400     {
401     case 0:
402       if (size != (1 << 20))
403         return;
404       {
405         unsigned long a = attr.raw() & 0x0c; // C & B
406         a |= ((attr.raw() & 0xff0) | Mem_page_attr::Shared) << 6;
407         __set(phys | a | 0x2, write_back);
408       }
409       break;
410     case 1:
411       if (size != (4 << 10))
412         return;
413       __set(phys | (attr.raw() & Page::MAX_ATTRIBS) | 0x2 | Mem_page_attr::Shared, write_back);
414       break;
415     }
416 }
417
418 PUBLIC inline NEEDS[Mem_page_attr::raw]
419 Mem_page_attr
420 Pte::attr() const
421 {
422   switch (_pt & 3)
423     {
424     case 0:
425         {
426           unsigned long a = *_pte & 0x0c; // C & B
427           a |= (*_pte >> 6) & 0xff0;
428           return Mem_page_attr(a);
429         }
430     case 1:
431     default:
432       return Mem_page_attr(*_pte & Page::MAX_ATTRIBS);
433     }
434 }
435
436 PUBLIC inline NEEDS["mem_unit.h", Mem_page_attr::raw]
437 void
438 Pte::attr(Mem_page_attr const &attr, bool write_back)
439 {
440   switch (_pt & 3)
441     {
442     case 1:
443       __set((*_pte & ~Page::MAX_ATTRIBS)
444             | (attr.raw() & Page::MAX_ATTRIBS), write_back);
445       break;
446     case 0:
447         {
448           unsigned long a = attr.raw() & 0x0c;
449           a |= (attr.raw() & 0xff0) << 6;
450           __set((*_pte & ~0x3fcc) | a, write_back);
451         }
452       break;
453     }
454 }
455
456 //-----------------------------------------------------------------------------
457 IMPLEMENTATION [armv6 || armca8]:
458
459 PUBLIC
460 void Page_table::activate(unsigned long asid)
461 {
462   Pte p = walk(this, 0, false, Ptab::Null_alloc(), 0);
463   asm volatile (
464       "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
465       "mcr p15, 0, r0, c7, c10, 4   \n" // dsb
466       "mcr p15, 0, %0, c2, c0       \n" // set TTBR
467       "mcr p15, 0, r0, c7, c10, 4   \n" // dsb
468       "mcr p15, 0, %1, c13, c0, 1   \n" // set new ASID value
469       "mcr p15, 0, r0, c7, c5, 4    \n" // isb
470       "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
471       "mrc p15, 0, r1, c2, c0       \n"
472       "mov r1, r1                   \n"
473       "sub pc, pc, #4               \n"
474       :
475       : "r" (p.phys(this) | Ttbr_bits), "r"(asid), "r" (0)
476       : "r1");
477 }
478
479 //-----------------------------------------------------------------------------
480 IMPLEMENTATION [armv7 && armca9]:
481
482 PUBLIC
483 void Page_table::activate(unsigned long asid)
484 {
485   Pte p = walk(this, 0, false, Ptab::Null_alloc(), 0);
486   asm volatile (
487       "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
488       "dsb                          \n"
489       "mcr p15, 0, %2, c13, c0, 1   \n" // change ASID to 0
490       "isb                          \n"
491       "mcr p15, 0, %0, c2, c0       \n" // set TTBR
492       "isb                          \n"
493       "mcr p15, 0, %1, c13, c0, 1   \n" // set new ASID value
494       "isb                          \n"
495       "mcr p15, 0, %2, c7, c5, 6    \n" // bt flush
496       "isb                          \n"
497       "mov r1, r1                   \n"
498       "sub pc, pc, #4               \n"
499       :
500       : "r" (p.phys(this) | Ttbr_bits), "r"(asid), "r" (0)
501       : "r1");
502 }
503
504 //-----------------------------------------------------------------------------
505 IMPLEMENTATION [arm && !mp]:
506
507 PRIVATE inline
508 Mword
509 Page_table::current_virt_to_phys(void *virt)
510 {
511   return walk(virt, 0, false, Ptab::Null_alloc(), 0).phys(virt);
512 }
513
514 //-----------------------------------------------------------------------------
515 IMPLEMENTATION [arm && mp]:
516
517 /*
518  * This version for MP avoids calling Page_table::current() which calls
519  * current_cpu() which is not available on the boot-stack. That's why we use
520  * the following way of doing a virt->phys translation on the current page
521  * table.
522  */
523 PRIVATE inline
524 Mword
525 Page_table::current_virt_to_phys(void *virt)
526 {
527   Mword phys;
528   Mword offset = (Mword)virt & ~Config::PAGE_MASK;
529   asm volatile("mcr p15,0,%1,c7,c8,0 \n"
530                "mrc p15,0,%0,c7,c4,0 \n"
531                : "=r" (phys)
532                : "r" ((Mword)virt & Config::PAGE_MASK));
533   return (phys & Config::PAGE_MASK) | offset;
534 }
535
536
537 //-----------------------------------------------------------------------------
538 IMPLEMENTATION [arm]:
539
540 #include <cassert>
541 #include "bug.h"
542 #include "auto_quota.h"
543 #include "mem.h"
544 #if 0
545 IMPLEMENT
546 void *Page_table::operator new(size_t s) throw()
547 {
548   (void)s;
549   assert(s == 16*1024);
550   return alloc()->alloc(14); // 2^14 = 16K
551 }
552
553 IMPLEMENT
554 void Page_table::operator delete(void *b)
555 {
556   alloc()->free(14, b);
557 }
558 #endif
559
560 IMPLEMENT
561 Page_table::Page_table()
562 {
563   Mem::memset_mwords(raw, 0, sizeof(raw) / sizeof(Mword));
564   if (Pte::need_cache_clean())
565     Mem_unit::clean_dcache(raw, (char *)raw + sizeof(raw));
566 }
567
568
569 PUBLIC template< typename ALLOC >
570 void Page_table::free_page_tables(void *start, void *end, ALLOC const &a)
571 {
572   for (unsigned i = (Address)start >> 20; i < ((Address)end >> 20); ++i)
573     {
574       Pte p(this, 0, raw + i);
575       if (p.valid() && !p.superpage())
576         {
577           void *pt = (void*)Mem_layout::phys_to_pmem(p.raw() & Pt_base_mask);
578
579           BUG_ON(pt == (void*)~0UL, "cannot get virtual (pmem) address for %lx (pte @ %p)\n",
580               p.raw() & Pt_base_mask, p._pte);
581
582           a.free(pt, 1<<10);
583         }
584     }
585 }
586
587 PRIVATE inline
588 static unsigned Page_table::pd_index( void const *const address )
589 {
590   return (Mword)address >> 20; // 1MB steps
591 }
592
593 PRIVATE inline
594 static unsigned Page_table::pt_index( void const *const address )
595 {
596   return ((Mword)address >> 12) & 255; // 4KB steps for coarse pts
597 }
598
599 PUBLIC template< typename Alloc >
600 inline NEEDS[<cassert>, "bug.h", Page_table::pd_index,
601              Page_table::current_virt_to_phys, Page_table::pt_index]
602 Pte
603 Page_table::walk(void *va, unsigned long size, bool write_back, Alloc const &q,
604                  Page_table *ldir)
605 {
606   unsigned const pd_idx = pd_index(va);
607
608   Mword *pt = 0;
609
610   Pte pde(this, 0, raw + pd_idx);
611
612   if (!pde.valid())
613     {
614       if (size == (4 << 10))
615         {
616           assert (q.valid());
617           pt = (Mword*)q.alloc(1<<10);
618           if (EXPECT_FALSE(!pt))
619             return pde;
620
621           Mem::memset_mwords(pt, 0, 1024 >> 2);
622
623           if (write_back || Pte::need_cache_clean())
624             Mem_unit::clean_dcache(pt, (char*)pt + 1024);
625
626           raw[pd_idx] = ldir->current_virt_to_phys(pt) | Pde_type_coarse;
627
628           if (write_back || Pte::need_cache_clean())
629             Mem_unit::clean_dcache(raw + pd_idx);
630         }
631       else
632         return pde;
633     }
634   else if (pde.superpage())
635     return pde;
636
637   if (!pt)
638     pt = (Mword *)Mem_layout::phys_to_pmem(pde.raw() & Pt_base_mask);
639
640   BUG_ON(pt == (void*)~0UL, "could not get virtual address for %lx (from pte @%p)\n",
641          pde.raw(), pde._pte);
642
643   return Pte(this, 1, pt + pt_index(va));
644 }
645
646
647 IMPLEMENT
648 void Page_table::init()
649 {
650   unsigned domains      = 0x0001;
651
652   asm volatile (
653       "mcr p15, 0, %0, c3, c0       \n" // domains
654       :
655       : "r"(domains) );
656 }
657
658
659 IMPLEMENT /*inline*/
660 void Page_table::copy_in(void *my_base, Page_table *o,
661                          void *base, size_t size, unsigned long asid)
662 {
663   unsigned pd_idx = pd_index(my_base);
664   unsigned pd_idx_max = pd_index(my_base) + pd_index((void*)size);
665   unsigned o_pd_idx = pd_index(base);
666   bool need_flush = false;
667
668   //printf("copy_in: %03x-%03x from %03x\n", pd_idx, pd_idx_max, o_pd_idx);
669
670   if (asid != ~0UL)
671     {
672       for (unsigned i = pd_idx;  i < pd_idx_max; ++i)
673         if (Pte(this, 0, raw + i).valid())
674           {
675             Mem_unit::flush_vdcache();
676             need_flush = true;
677             break;
678           }
679     }
680
681   for (unsigned i = pd_idx; i < pd_idx_max; ++i, ++o_pd_idx)
682     raw[i] = o->raw[o_pd_idx];
683
684   if (Pte::need_cache_clean())
685     Mem_unit::clean_dcache(raw + pd_idx, raw + pd_idx_max);
686
687   if (need_flush && (asid != ~0UL))
688     Mem_unit::dtlb_flush(asid);
689 }
690
691 #if 0
692 PUBLIC
693 void
694 Page_table::invalidate(void *my_base, unsigned size, unsigned long asid = ~0UL)
695 {
696   unsigned pd_idx = pd_index(my_base);
697   unsigned pd_idx_max = pd_index(my_base) + pd_index((void*)size);
698   bool need_flush = false;
699
700   //printf("invalidate: %03x-%03x\n", pd_idx, pd_idx_max);
701
702   if (asid != ~0UL)
703     {
704       for (unsigned i = pd_idx; i < pd_idx_max; ++i)
705         if (Pte(this, 0, raw + i).valid())
706           {
707             Mem_unit::flush_vdcache();
708             need_flush = true;
709             break;
710           }
711     }
712
713   for (unsigned i = pd_idx; i < pd_idx_max; ++i)
714     raw[i] = 0;
715
716   // clean the caches if manipulating the current pt or in the case if phys.
717   // tagged caches.
718   if ((asid != ~0UL) || Pte::need_cache_clean())
719     Mem_unit::clean_dcache(raw + pd_idx, raw + pd_idx_max);
720
721   if (need_flush && (asid != ~0UL))
722     Mem_unit::tlb_flush(asid);
723 }
724 #endif
725
726 IMPLEMENT
727 void *
728 Page_table::dir() const
729 {
730   return const_cast<Page_table *>(this);
731 }
732