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