]> git.buserror.net Git - polintos/scott/priv.git/blob - kernel/include/kern/mem.h
Lots of stuff.
[polintos/scott/priv.git] / kernel / include / kern / mem.h
1 #ifndef _KERN_MEM_H
2 #define _KERN_MEM_H
3
4 #include <System/Mem.h>
5
6 #include <kern/kernel.h>
7 #include <kern/orb.h>
8
9 #include <arch/mem.h>
10 #include <arch/addrs.h>
11
12 #include <util/rbtree.h>
13 #include <util/list.h>
14 #include <util/lock.h>
15 #include <kernel/region.h>
16 #include <lowlevel/bitops.h>
17
18 namespace Mem {
19         // Used for allocating memory at boot time before the page allocator
20         // is running.  Alignment must be a power of 2.  Because nothing other
21         // than the kernel is guaranteed to be mapped from the beginning on
22         // all architectures, no generic code should use this until after
23         // Arch::arch_init() has run and set up physical memory mappings.
24         //
25         // This function may not be used after the page allocator has
26         // been initialized by architecture code.
27         //
28         // Architectures must provide Arch::next_free_bootmem initalized
29         // to the first free piece of bootmem.
30         
31         static inline void *get_bootmem(size_t size, size_t align)
32         {
33                 uintptr_t ret = (Arch::next_free_bootmem + align - 1) & ~(align - 1);
34                 Arch::next_free_bootmem = ret + size;
35                 return reinterpret_cast<void *>(ret);
36         }
37         
38         typedef System::Mem::AddrSpace IAddrSpace;
39         typedef System::Mem::Mappable IMappable;
40         using System::Mem::Cacheable;
41         using System::Mem::Region;
42         using System::Mem::RegionWithOffset;
43         using System::Mem::AllocFlags;
44         using System::Mem::MapFlags;
45         using System::Mem::AccessFlags;
46
47         union PTEFlags {
48                 struct {
49                         // This must be kept in sync with include/kern/generic-pte.h
50                 
51 #ifdef BITFIELD_LE
52                         // Readable, Writeable, and Executable are for permission only,
53                         // not for implementing copy on write, swapping, etc.
54                         
55                         ulong Valid:1;
56                         ulong Writeable:1;
57                         ulong Readable:1;
58                         ulong Executable:1;
59                         ulong User:1;
60                         ulong Accessed:1;
61                         ulong Dirty:1;
62                         
63                         // If set, then on a write access, the page is copied and this
64                         // address space gets the new, anonymous version.  The rmap list
65                         // is then traversed; all downstream mappings will share the new
66                         // copy.
67                         //
68                         // For vareas that directly map something other than an address
69                         // space, the action to be taken on a write fault is
70                         // mapping-specific.
71
72                         ulong FaultOnWrite:1;
73                         
74                         // VArea Only:
75                         // Do not allow the user to unmap or modify flags.
76                         // Used for the shared user/kernel mappings. 
77                         
78                         ulong Protected:1;
79
80 #elif defined(BITFIELD_BE)
81                         ulong pad:_LL_LONG_BYTES * 8 - 9;
82                         ulong Protected:1;
83                         ulong FaultOnWrite:1;
84                         ulong Dirty:1;
85                         ulong Accessed:1;
86                         ulong User:1;
87                         ulong Executable:1;
88                         ulong Readable:1;
89                         ulong Writeable:1;
90                         ulong Valid:1;
91 #else
92 #error Unspecified/unrecognized bitfield endianness
93 #endif
94                 };
95                 
96                 ulong raw;
97                 
98                 PTEFlags(ulong init) : raw(init)
99                 {
100                 }
101                 
102                 PTEFlags() : raw(0)
103                 {
104                 }
105                 
106                 operator ulong()
107                 {
108                         return raw;
109                 }
110         };
111
112         using Arch::kvirt_to_phys;
113         using Arch::phys_to_kvirt;
114         class PageTable;
115         class AddrSpace;
116
117         struct VirtualArea;
118         typedef Util::RBTree<VirtualArea, Region, u64> VirtualAreaTree;
119         
120         class Mappable {
121         protected:
122                 // This linked list keeps track of the virtual areas that map this
123                 // mappable (this is not transitive; vareas that map a varea that
124                 // maps this mappable are not on this list).
125                 //
126                 // OPT: rbtree keyed on mapped address range?
127                 
128                 Util::List mappings;
129                 Lock::SpinLock mappings_lock;
130                 
131         public:
132                 bool is_aspace;
133                 
134                 virtual void get_size(u64 *size) = 0;
135                 
136                 virtual void get_block_size(u64 *block_size)
137                 {
138                         *block_size = Arch::page_size;
139                 }
140                 
141                 // Register/unregister varea as mapping this mappable.
142                 
143                 virtual void map(VirtualArea *varea);
144                 virtual void unmap(VirtualArea *varea);
145                 
146                 // Make the specified page available for mapping.  This must be
147                 // done before map() will succeed.  It is possible (though
148                 // unlikely) that the pages will be removed before map() is called,
149                 // causing map() to return false.  In such a case, pagein should be
150                 // called again by the fault handler.  If the mapping fails for
151                 // other reasons (such as lack of permission, a hole in a stacked
152                 // aspace, or an I/O error) then pagein() will throw a BadPageFault
153                 // exception.
154
155                 virtual void pagein(u64 vaddr, PTEFlags reqflags) = 0;
156                 
157                 // Returns the physical address and flags associated with a given
158                 // virtual address.  If flags.Valid is not set, then phys and all
159                 // other flags are undefined, and pagein() should be retried.
160                 // rmap_lock must be held.
161                 
162                 virtual void get_mapping(u64 vaddr, u64 *phys, PTEFlags *flags) = 0;
163
164                 #include <servers/mem/addrspace/Mem/Mappable.h>
165                 
166                 Mappable()
167                 {
168                         init_iface();
169                         is_aspace = false;
170                 }
171                 
172                 virtual ~Mappable()
173                 {
174                 }
175         };
176         
177         struct VirtualArea {
178                 AddrSpace *aspace;
179         
180                 // The red/black tree is used to find a region based on address. 
181                 //
182                 // The linked list is kept in order and is used to iterate over
183                 // vmas in a region (after looking up the starting point in the
184                 // tree, unless the region is the entire address space).
185         
186                 VirtualAreaTree::Node rbtree_node;
187                 Util::List list_node;
188                 Util::List mappings_node;
189
190                 PTEFlags flags;
191                 Mappable *ma;
192                 
193                 // This is added to the virtual address to get the offset
194                 // into the mappable.
195                 s64 offset;
196                 
197                 Region &region()
198                 {
199                         return rbtree_node.value;
200                 }
201         };
202         
203         
204         struct BadPageFault {
205         };
206         
207         class ASpaceMappable : public Mappable {
208                 AddrSpace *aspace;
209                 
210                 static bool rec_pagein(AddrSpace *aspace, u64 vaddr,
211                                        PTEFlags reqflags);
212
213         public:
214                 ASpaceMappable (AddrSpace *ASPACE) : aspace(ASPACE)
215                 {
216                         is_aspace = true;
217                 }
218
219                 void get_size(u64 *size);
220         
221                 // Unexported
222                 virtual void pagein(u64 vaddr, PTEFlags reqflags);
223                 virtual void get_mapping(u64 vaddr, u64 *phys, PTEFlags *flags);
224
225                 friend class AddrSpace;
226         };
227         
228         class AddrSpace {
229                 // OPT: Coalesce vareas when possible (except when setting flags to
230                 // match surrounding vareas, as the flags are likely to change
231                 // again if they've already changed).
232                 
233                 // OPT: A subclass of AddrSpace that doesn't use
234                 // VirtualArea::offset, but rather has its own virtual method that
235                 // figures out offsets to the next level using its own data
236                 // structures (such as filesystem block tables).  This would avoid
237                 // excessive vareas for fragmented files.  Whether the excess of
238                 // vareas is significant enough for this to be worthwhile remains
239                 // to be seen.
240
241                 VirtualAreaTree varea_tree;
242                 Util::List varea_list;
243                 Lock::Lock lock;
244
245                 // This defines the start and end of the aspace; mappings outside
246                 // this range may not be done, and will not be returned by
247                 // get_free_region().  For process aspaces, this goes from
248                 // Arch::user_start to Arch::user_end.  For non-proc aspaces, this
249                 // can be anything.
250
251                 Region aspace_region;
252
253                 // Returns true if there is a mapped region that overlaps the given
254                 // region.  If there is a collision, then the first overlapping
255                 // varea is returned in va.  Otherwise, it returns the last mapped
256                 // area before the region in va (if there are no areas, or the
257                 // region is before the first area, then prev is NULL).  The aspace
258                 // lock must be held.
259                 
260                 bool check_overlap(Region region, VirtualArea *&va);
261                 
262                 // Finds a free region of the requested length and puts it in
263                 // region.  Returns true if an appropriate area is found.  The prev
264                 // pointer is as in check_overlap.  The aspace lock must be held.
265                 
266                 bool get_free_region(ulong len, Region &region, VirtualArea *&prev);
267
268                 // This is the value after the last region returned by
269                 // get_free_region.  If there was an intervening unmap for a lower
270                 // address, then it is set to that address instead.
271
272                 u64 cached_free_region;
273
274                 static u64 rec_unmap(AddrSpace *aspace, Region region,
275                                      PTEFlags reqflags, VirtualArea *va);
276                 
277                 // If there are multiple virtual areas that cover the specified region,
278                 // split them at the region's boundaries.  The first varea in the region
279                 // (if any) is returned.  The aspace lock must be held.
280                 
281                 VirtualArea *split_varea(Region region);
282
283                 void break_copy_on_write(VirtualArea *va, u64 vaddr, u64 phys);
284                 bool map(VirtualArea *va, u64 vaddr, PTEFlags reqflags);
285
286         protected:
287                 bool is_process;
288
289         public:
290                 #include <servers/mem/addrspace/Mem/AddrSpace.h>
291
292                 ASpaceMappable mappable;
293                 PageTable *page_table;
294                 
295                 AddrSpace(PageTable *ptbl = NULL);
296                 
297                 // Returns true if the fault was "good"; otherwise, the caller
298                 // should dump regs.  exec should only be used if the CPU
299                 // implements per-page exec protection; otherwise, treat it
300                 // as a read.
301                 
302                 bool handle_fault(ulong addr, bool write, bool exec, bool user);
303                 
304                 void get_mappable(IMappable *ma);
305                 void clone(IAddrSpace *addrspace, u8 clone_is_real);
306
307                 enum {
308                         map_user,
309                         map_protected,
310                         map_kernel
311                 };
312
313                 void map(IMappable ma, Region region, u64 *vstart, MapFlags mflags,
314                          int map_type = map_user);
315                 void unmap(Region region, bool from_kernel = false);
316                 
317                 void set_mapflags(Region region, MapFlags mflags);
318                 void get_mapflags(Region region, MapFlags *mflags, uint8_t *all_same);
319                 void get_mapping(Region region, IMappable *ma, u64 *offset);
320                 
321                 void get_page_size(u32 *page_size);
322                 void get_min_align(u32 *min_align);
323                 void get_size(u64 *size);
324
325                 friend class ASpaceMappable;
326         };
327
328         class ProcAddrSpace : public AddrSpace {
329         public:
330                 ProcAddrSpace();
331                 ProcAddrSpace(void *page_table);
332                 
333                 ORB::IDSpace idspace;
334         };
335         
336         extern Factory addr_space_factory, proc_addr_space_factory;
337
338         using ::System::RunTime::orbmm;
339         
340         static inline bool page_aligned(u64 addr)
341         {
342                 return !(addr & (u64)(Arch::page_size - 1));
343         }
344
345         static inline u64 page_align(u64 addr)
346         {
347                 return addr & ~(u64)(Arch::page_size - 1);
348         }
349         
350         // FIXME: Valid user addr?  Paging holes?
351         static inline bool valid_addr(uint64_t addr)
352         {
353                 if (sizeof(void *) == 8)
354                         return true;
355                 
356                 return (addr >> 32) == 0;
357         }
358 };
359
360 #endif