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