]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/kmem-ia32.cpp
update
[l4.git] / kernel / fiasco / src / kern / ia32 / kmem-ia32.cpp
1 // our own implementation of C++ memory management: disallow dynamic
2 // allocation (except where class-specific new/delete functions exist)
3 //
4 // more specialized memory allocation/deallocation functions follow
5 // below in the "Kmem" namespace
6
7 INTERFACE [ia32,amd64,ux]:
8
9 #include "globalconfig.h"
10 #include "initcalls.h"
11 #include "kip.h"
12 #include "mem_layout.h"
13
14 class Cpu;
15 class Pdir;
16 class Tss;
17
18 class Device_map
19 {
20 public:
21   enum
22   {
23     Max = 16,
24     Virt_base = 0x20000000,
25   };
26
27 private:
28   Address _map[Max];
29
30 };
31
32 /**
33  * The system's base facilities for kernel-memory management.
34  * The kernel memory is a singleton object.  We access it through a
35  * static class interface.
36  */
37 class Kmem : public Mem_layout
38 {
39   friend class Device_map;
40   friend class Jdb;
41   friend class Jdb_dbinfo;
42   friend class Jdb_kern_info_misc;
43   friend class Kdb;
44   friend class Profile;
45   friend class Vmem_alloc;
46
47 private:
48   Kmem();                       // default constructors are undefined
49   Kmem (const Kmem&);
50   static unsigned long pmem_cpu_page, cpu_page_vm;
51
52 public:
53   static Device_map dev_map;
54
55   enum
56   {
57     mem_user_max = Mem_layout::User_max,
58   };
59
60   static void init_pageing(Cpu const &boot_cpu);
61   static void init_boot_cpu(Cpu const &boot_cpu);
62   static void init_app_cpu(Cpu const &cpu);
63   static Mword is_kmem_page_fault(Address pfa, Mword error);
64   static Mword is_ipc_page_fault(Address pfa, Mword error);
65   static Mword is_io_bitmap_page_fault(Address pfa);
66   static Address kcode_start();
67   static Address kcode_end();
68   static Address virt_to_phys(const void *addr);
69
70 };
71
72 typedef Kmem Kmem_space;
73
74
75 //----------------------------------------------------------------------------
76 INTERFACE [ia32, amd64]:
77
78 EXTENSION
79 class Kmem
80 {
81   friend class Kernel_task;
82
83 public:
84   static Address     user_max();
85
86 private:
87   static Unsigned8   *io_bitmap_delimiter;
88   static Address kphys_start, kphys_end;
89   static Pdir *kdir; ///< Kernel page directory
90 };
91
92
93 //--------------------------------------------------------------------------
94 IMPLEMENTATION [ia32, amd64]:
95
96 #include "cpu.h"
97 #include "l4_types.h"
98 #include "mapped_alloc.h"
99 #include "mem_unit.h"
100 #include "panic.h"
101 #include "paging.h"
102 #include "pic.h"
103 #include "std_macros.h"
104
105 #include <cstdio>
106
107 PUBLIC
108 void
109 Device_map::init()
110 {
111   for (unsigned i = 0; i < Max; ++i)
112     _map[i] = ~0UL;
113 }
114
115 PRIVATE
116 unsigned
117 Device_map::lookup_idx(Address phys)
118 {
119   Address p = phys & (~0UL << Config::SUPERPAGE_SHIFT);
120   for (unsigned i = 0; i < Max; ++i)
121     if (p == _map[i])
122       return i;
123
124   return ~0U;
125 }
126
127
128 PUBLIC
129 template< typename T >
130 T *
131 Device_map::lookup(T *phys)
132 {
133   unsigned idx = lookup_idx((Address)phys);
134   if (idx == ~0U)
135     return (T*)~0UL;
136
137   return (T*)((Virt_base + idx * Config::SUPERPAGE_SIZE)
138       | ((Address)phys & ~(~0UL << Config::SUPERPAGE_SHIFT)));
139 }
140
141 PRIVATE
142 Address
143 Device_map::map(Address phys, bool /*cache*/)
144 {
145   unsigned idx = lookup_idx(phys);
146   if (idx != ~0U)
147     return (Virt_base + idx * Config::SUPERPAGE_SIZE)
148       | (phys & ~(~0UL << Config::SUPERPAGE_SHIFT));
149
150   Address p = phys & (~0UL << Config::SUPERPAGE_SHIFT);
151   Mapped_allocator *const alloc = Mapped_allocator::allocator();
152   for (unsigned i = 0; i < Max; ++i)
153     if (_map[i] == ~0UL)
154       {
155         Kmem::kdir->map(p,
156             Virt_addr(Virt_base + (i*Config::SUPERPAGE_SIZE)),
157             Virt_size(Config::SUPERPAGE_SIZE),
158             Pt_entry::Dirty | Pt_entry::Writable | Pt_entry::Referenced,
159             Pdir::super_level(), alloc);
160         _map[i] = p;
161
162         return (Virt_base + (i*Config::SUPERPAGE_SIZE))
163           | (phys & ~(~0UL << Config::SUPERPAGE_SHIFT));
164       }
165
166   return ~0UL;
167 }
168
169 PUBLIC
170 template< typename T >
171 T *
172 Device_map::map(T *phys, bool cache = true)
173 { return (T*)map((Address)phys, cache); }
174
175 PUBLIC
176 void
177 Device_map::unmap(void const *phys)
178 {
179   unsigned idx = lookup_idx((Address)phys);
180   if (idx == ~0U)
181     return;
182
183   Address v = Virt_base + (idx * Config::SUPERPAGE_SIZE);
184
185   Kmem::kdir->unmap(Virt_addr(v), Virt_size(Config::SUPERPAGE_SIZE), -1);
186 }
187
188
189 Unsigned8    *Kmem::io_bitmap_delimiter;
190 Address Kmem::kphys_start, Kmem::kphys_end;
191 Device_map Kmem::dev_map;
192
193
194 PUBLIC static inline
195 Address
196 Kmem::io_bitmap_delimiter_page()
197 {
198   return reinterpret_cast<Address>(io_bitmap_delimiter);
199 }
200
201
202 /**
203  * Compute physical address from a kernel-virtual address.
204  * @param addr a virtual address
205  * @return corresponding physical address if a mappings exists.
206  *         -1 otherwise.
207  */
208 IMPLEMENT inline NEEDS["paging.h","std_macros.h","mem_layout.h"]
209 Address
210 Kmem::virt_to_phys(const void *addr)
211 {
212   Address a = reinterpret_cast<Address>(addr);
213
214   if (EXPECT_TRUE(Mem_layout::in_pmem(a)))
215     return Mem_layout::pmem_to_phys(a);
216
217   if (EXPECT_TRUE(Mem_layout::in_kernel_image(a)))
218     return a - Mem_layout::Kernel_image_offset;
219
220   return kdir->virt_to_phys(a);
221 }
222
223
224 // Only used for initialization and kernel debugger
225 PUBLIC static
226 Address
227 Kmem::map_phys_page_tmp(Address phys, Mword idx)
228 {
229   unsigned long pte = phys & Pt_entry::Pfn;
230   Address    virt;
231
232   switch (idx)
233     {
234     case 0:  virt = Mem_layout::Kmem_tmp_page_1; break;
235     case 1:  virt = Mem_layout::Kmem_tmp_page_2; break;
236     default: return ~0UL;
237     }
238
239   static unsigned long tmp_phys_pte[2] = { ~0UL, ~0UL };
240
241   if (pte != tmp_phys_pte[idx])
242     {
243       // map two consecutive pages as to be able to access 
244       map_phys_page(phys,        virt,        false, true);
245       map_phys_page(phys+0x1000, virt+0x1000, false, true);
246       tmp_phys_pte[idx] = pte;
247     }
248
249   return virt + phys - pte;
250 }
251
252 PUBLIC static inline
253 Address Kmem::kernel_image_start()
254 { return virt_to_phys(&Mem_layout::image_start) & Config::PAGE_MASK; }
255
256 IMPLEMENT inline Address Kmem::kcode_start()
257 { return virt_to_phys(&Mem_layout::start) & Config::PAGE_MASK; }
258
259 IMPLEMENT inline Address Kmem::kcode_end()
260 {
261   return (virt_to_phys(&Mem_layout::end) + Config::PAGE_SIZE)
262          & Config::PAGE_MASK;
263 }
264
265
266 /** Return number of IPC slots to copy */
267 PUBLIC static inline NEEDS["config.h"]
268 unsigned
269 Kmem::ipc_slots()
270 { return (8 << 20) / Config::SUPERPAGE_SIZE; }
271
272 IMPLEMENT inline NEEDS["mem_layout.h"]
273 Mword
274 Kmem::is_io_bitmap_page_fault(Address addr)
275 {
276   return addr >= Mem_layout::Io_bitmap &&
277          addr <= Mem_layout::Io_bitmap + Mem_layout::Io_port_max / 8;
278 }
279
280 IMPLEMENT inline NEEDS["mem_layout.h"]
281 Mword
282 Kmem::is_kmem_page_fault(Address addr, Mword /*error*/)
283 {
284   return addr >= mem_user_max;
285 }
286
287
288 //
289 // helper functions
290 //
291
292 // Establish a 4k-mapping
293 PUBLIC static
294 void
295 Kmem::map_phys_page(Address phys, Address virt,
296                     bool cached, bool global, Address *offs=0)
297 {
298   Pdir::Iter i = kdir->walk(Virt_addr(virt), 100, Mapped_allocator::allocator()
299 );
300   Pte_base *e = i.e;
301   Mword pte = phys & Config::PAGE_MASK;
302
303   assert(i.shift() == Config::PAGE_SHIFT);
304
305   *e = pte | Pt_entry::Valid | Pt_entry::Writable
306            | Pt_entry::Referenced | Pt_entry::Dirty
307            | (cached ? 0 : (Pt_entry::Write_through | Pt_entry::Noncacheable))
308            | (global ? Pt_entry::global() : 0);
309   Mem_unit::tlb_flush(virt);
310
311   if (offs)
312     *offs = phys - pte;
313 }
314
315
316 PUBLIC static FIASCO_INIT
317 void
318 Kmem::init_mmu()
319 {
320   dev_map.init();
321   Mapped_allocator *const alloc = Mapped_allocator::allocator();
322
323   kdir = (Pdir*)alloc->alloc(Config::PAGE_SHIFT);
324   memset (kdir, 0, Config::PAGE_SIZE);
325
326   unsigned long cpu_features = Cpu::get_features();
327   bool superpages = cpu_features & FEAT_PSE;
328
329   printf("Superpages: %s\n", superpages?"yes":"no");
330
331   Pdir::have_superpages(superpages);
332   if (superpages)
333     Cpu::set_cr4(Cpu::get_cr4() | CR4_PSE);
334
335   if (cpu_features & FEAT_PGE)
336     {
337       Pt_entry::enable_global();
338       Cpu::set_cr4 (Cpu::get_cr4() | CR4_PGE);
339     }
340
341   // set up the kernel mapping for physical memory.  mark all pages as
342   // referenced and modified (so when touching the respective pages
343   // later, we save the CPU overhead of marking the pd/pt entries like
344   // this)
345
346   // we also set up a one-to-one virt-to-phys mapping for two reasons:
347   // (1) so that we switch to the new page table early and re-use the
348   //     segment descriptors set up by boot_cpu.cc.  (we'll set up our
349   //     own descriptors later.) we only need the first 4MB for that.
350   // (2) a one-to-one phys-to-virt mapping in the kernel's page directory
351   //     sometimes comes in handy (mostly useful for debugging)
352
353   // first 4MB page
354   kdir->map(0, Virt_addr(0), Virt_size(4 << 20),
355       Pt_entry::Dirty | Pt_entry::Writable | Pt_entry::Referenced,
356       Pdir::super_level(), alloc);
357
358
359   kdir->map(Mem_layout::Kernel_image_phys,
360             Virt_addr(Mem_layout::Kernel_image),
361             Virt_size(Config::SUPERPAGE_SIZE),
362             Pt_entry::Dirty | Pt_entry::Writable | Pt_entry::Referenced 
363             | Pt_entry::global(), Pdir::super_level(), alloc);
364
365    if (!Mem_layout::Adap_in_kernel_image)
366      kdir->map(Mem_layout::Adap_image_phys,
367                Virt_addr(Mem_layout::Adap_image),
368                Virt_size(Config::SUPERPAGE_SIZE),
369                Pt_entry::Dirty | Pt_entry::Writable | Pt_entry::Referenced 
370                | Pt_entry::global(), Pdir::super_level(), alloc);
371
372   // map the last 64MB of physical memory as kernel memory
373   kdir->map(Mem_layout::pmem_to_phys(Mem_layout::Physmem),
374             Virt_addr(Mem_layout::Physmem), Virt_size(Mem_layout::pmem_size),
375             Pt_entry::Writable | Pt_entry::Referenced | Pt_entry::global(),
376             Pdir::super_level(), alloc);
377
378   // The service page directory entry points to an universal usable
379   // page table which is currently used for the Local APIC and the
380   // jdb adapter page.
381   assert((Mem_layout::Service_page & ~Config::SUPERPAGE_MASK) == 0);
382
383   Pdir::Iter pt = kdir->walk(Virt_addr(Mem_layout::Service_page), 100, alloc);
384
385   // kernel mode should acknowledge write-protected page table entries
386   Cpu::set_cr0(Cpu::get_cr0() | CR0_WP);
387
388   // now switch to our new page table
389   Cpu::set_pdbr(Mem_layout::pmem_to_phys(kdir));
390
391   assert((Mem_layout::Io_bitmap & ~Config::SUPERPAGE_MASK) == 0);
392
393   long cpu_page_size
394     = 0x10 + Config::Max_num_cpus * (sizeof(Tss) + 256);
395
396   if (cpu_page_size < Config::PAGE_SIZE)
397     cpu_page_size = Config::PAGE_SIZE;
398
399   pmem_cpu_page = Mem_layout::pmem_to_phys(alloc->unaligned_alloc(cpu_page_size));
400
401   printf("Kmem:: cpu page at %lx (%ldBytes)\n", pmem_cpu_page, cpu_page_size);
402
403   if (superpages
404       && Config::SUPERPAGE_SIZE - (pmem_cpu_page & ~Config::SUPERPAGE_MASK) < 0x10000)
405     {
406       // can map as 4MB page because the cpu_page will land within a
407       // 16-bit range from io_bitmap
408       *(kdir->walk(Virt_addr(Mem_layout::Io_bitmap - Config::SUPERPAGE_SIZE),
409                    Pdir::Super_level, alloc).e)
410         = (pmem_cpu_page & Config::SUPERPAGE_MASK)
411         | Pt_entry::Pse_bit
412         | Pt_entry::Writable | Pt_entry::Referenced
413         | Pt_entry::Dirty | Pt_entry::global() | Pt_entry::Valid;
414
415       cpu_page_vm = (pmem_cpu_page & ~Config::SUPERPAGE_MASK)
416                   + (Mem_layout::Io_bitmap - Config::SUPERPAGE_SIZE);
417     }
418   else
419     {
420       unsigned i;
421       for (i = 0; cpu_page_size > 0; ++i, cpu_page_size -= Config::PAGE_SIZE)
422         {
423           pt = kdir->walk(Virt_addr(Mem_layout::Io_bitmap - Config::PAGE_SIZE * (i+1)),
424                           100, alloc);
425
426           *pt.e = (pmem_cpu_page + i*Config::PAGE_SIZE)
427                   | Pt_entry::Valid | Pt_entry::Writable
428                   | Pt_entry::Referenced | Pt_entry::Dirty
429                   | Pt_entry::global();
430         }
431
432       cpu_page_vm = Mem_layout::Io_bitmap - Config::PAGE_SIZE * i;
433     }
434
435   if (Config::enable_io_protection)
436     {
437       // the IO bitmap must be followed by one byte containing 0xff
438       // if this byte is not present, then one gets page faults
439       // (or general protection) when accessing the last port
440       // at least on a Pentium 133.
441       //
442       // Therefore we write 0xff in the first byte of the cpu_page
443       // and map this page behind every IO bitmap
444       io_bitmap_delimiter =
445         reinterpret_cast<Unsigned8 *>(cpu_page_vm);
446
447       cpu_page_vm += 0x10;
448
449       // did we really get the first byte ??
450       assert((reinterpret_cast<Address>(io_bitmap_delimiter)
451                & ~Config::PAGE_MASK) == 0);
452       *io_bitmap_delimiter = 0xff;
453     }
454 }
455
456
457 PUBLIC static FIASCO_INIT_CPU
458 void
459 Kmem::init_cpu(Cpu &cpu)
460 {
461
462   void *cpu_mem = Mapped_allocator::allocator()->unaligned_alloc(1024);
463   printf("Allocate cpu_mem @ %p\n", cpu_mem);
464   
465   // now initialize the global descriptor table
466   cpu.init_gdt (__alloc(&cpu_mem, Gdt::gdt_max), user_max());
467
468   // Allocate the task segment as the last thing from cpu_page_vm
469   // because with IO protection enabled the task segment includes the
470   // rest of the page and the following IO bitmap (2 pages).
471   //
472   // Allocate additional 256 bytes for emergency stack right beneath
473   // the tss. It is needed if we get an NMI or debug exception at
474   // entry_sys_fast_ipc/entry_sys_fast_ipc_c/entry_sys_fast_ipc_log.
475   Address tss_mem  = alloc_tss(sizeof(Tss) + 256);
476   assert(tss_mem + sizeof(Tss) + 256 < Mem_layout::Io_bitmap);
477   size_t tss_size;
478
479   if (Config::enable_io_protection)
480     // this is actually tss_size +1, including the io_bitmap_delimiter byte
481     tss_size = Mem_layout::Io_bitmap + (Mem_layout::Io_port_max / 8) - tss_mem;
482   else
483     tss_size = sizeof(Tss) - 1;
484
485   assert(tss_size < 0x100000); // must fit into 20 Bits
486
487   cpu.init_tss(tss_mem, tss_size);
488
489   // force GDT... to memory before loading the registers
490   asm volatile ( "" : : : "memory" );
491
492   // set up the x86 CPU's memory model
493   cpu.set_gdt();
494   cpu.set_ldt(0);
495
496   cpu.set_ds(Gdt::data_segment());
497   cpu.set_es(Gdt::data_segment());
498   cpu.set_ss(Gdt::gdt_data_kernel | Gdt::Selector_kernel);
499   cpu.set_fs(Gdt::gdt_data_user   | Gdt::Selector_user);
500   cpu.set_gs(Gdt::gdt_data_user   | Gdt::Selector_user);
501   cpu.set_cs();
502
503   // and finally initialize the TSS
504   cpu.set_tss();
505
506   init_cpu_arch(cpu, &cpu_mem);
507 }
508
509
510 //---------------------------------------------------------------------------
511 IMPLEMENTATION [ia32 || amd64]:
512
513 IMPLEMENT inline Address Kmem::user_max() { return ~0UL; }
514
515
516 //--------------------------------------------------------------------------
517 IMPLEMENTATION [ia32,ux,amd64]:
518
519 #include <cstdlib>
520 #include <cstddef>              // size_t
521 #include <cstring>              // memset
522
523 #include "boot_info.h"
524 #include "config.h"
525 #include "cpu.h"
526 #include "gdt.h"
527 #include "globals.h"
528 #include "paging.h"
529 #include "regdefs.h"
530 #include "std_macros.h"
531 #include "tss.h"
532
533 // static class variables
534 unsigned long Kmem::pmem_cpu_page, Kmem::cpu_page_vm;
535 Pdir *Kmem::kdir;
536
537
538 static inline Address FIASCO_INIT_CPU
539 Kmem::__alloc(void **p, unsigned long size)
540 {
541   Address r = ((unsigned long)*p + 0xf) & ~0xf;
542   *p = (void*)(r + size);
543   return r;
544 }
545
546 /**
547  * Compute a kernel-virtual address for a physical address.
548  * This function always returns virtual addresses within the
549  * physical-memory region.
550  * @pre addr <= highest kernel-accessible RAM address
551  * @param addr a physical address
552  * @return kernel-virtual address.
553  */
554 PUBLIC static inline
555 void *
556 Kmem::phys_to_virt(Address addr)
557 {
558   return reinterpret_cast<void *>(Mem_layout::phys_to_pmem(addr));
559 }
560
561 /** Allocate some bytes from a memory page */
562 PUBLIC static inline
563 Address
564 Kmem::alloc_tss(Address size)
565 {
566   Address ret = cpu_page_vm;
567   cpu_page_vm += (size + 0xf) & ~0xf;
568
569   return ret;
570 }
571
572
573 /**
574  * Return Global page directory.
575  * This is the master copy of the kernel's page directory. Kernel-memory
576  * allocations are kept here and copied to task page directories lazily
577  * upon page fault.
578  * @return kernel's global page directory
579  */
580 PUBLIC static inline const Pdir* Kmem::dir() { return kdir; }
581