1/**
2 * \file
3 * \brief x86-64 kernel page-table structures.
4 */
5
6/*
7 * Copyright (c) 2007-2013 ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#ifndef KERNEL_TARGET_X86_64_PAGING_H
16#define KERNEL_TARGET_X86_64_PAGING_H
17
18#include <capabilities.h>
19#include <barrelfish_kpi/paging_arch.h>
20
21// Functions defined elsewhere. Move the declerations to appropriate includes
22int paging_x86_64_map_memory(lpaddr_t base, size_t size);
23lvaddr_t paging_x86_64_map_device(lpaddr_t base, size_t size);
24void paging_x86_64_reset(void);
25void paging_x86_64_make_good_pml4(lpaddr_t base);
26
27/// All flags valid for page access protection from user-space
28#define X86_64_PTABLE_ACCESS_MASK \
29    (X86_64_PTABLE_EXECUTE_DISABLE | X86_64_PTABLE_USER_SUPERVISOR | \
30     X86_64_PTABLE_READ_WRITE)
31
32/// All arch-specific flags valid to be set from user-space
33#ifdef __k1om__
34#define X86_64_PTABLE_FLAGS_MASK                                        \
35    (X86_64_PTABLE_ATTR_INDEX | X86_64_PTABLE_DIRTY |                   \
36     X86_64_PTABLE_ACCESSED | X86_64_PTABLE_CACHE_DISABLED |            \
37     X86_64_PTABLE_WRITE_THROUGH)
38#else
39#define X86_64_PTABLE_FLAGS_MASK                                        \
40    (X86_64_PTABLE_GLOBAL_PAGE | X86_64_PTABLE_ATTR_INDEX |             \
41     X86_64_PTABLE_DIRTY | X86_64_PTABLE_ACCESSED |                     \
42     X86_64_PTABLE_CACHE_DISABLED | X86_64_PTABLE_WRITE_THROUGH)
43#endif
44
45/// Mask out all arch-specific flags except those valid from user-space
46#define X86_64_PTABLE_FLAGS(flags)     (flags & X86_64_PTABLE_FLAGS_MASK)
47
48/// Mask out all flags except those for access protection
49#define X86_64_PTABLE_ACCESS(flags)    (flags & X86_64_PTABLE_ACCESS_MASK)
50
51/** True if page entry is present in memory */
52#define X86_64_IS_PRESENT(entry)                        \
53    ((*(uint64_t *)(entry)) & X86_64_PTABLE_PRESENT)
54
55/**
56 * A page directory entry.
57 */
58union x86_64_pdir_entry {
59    uint64_t raw;
60    struct {
61        uint64_t        present         :1;
62        uint64_t        read_write      :1;
63        uint64_t        user_supervisor :1;
64        uint64_t        write_through   :1;
65        uint64_t        cache_disabled  :1;
66        uint64_t        accessed        :1;
67        uint64_t        reserved        :3;
68        uint64_t        available       :3;
69        uint64_t        base_addr       :28;
70        uint64_t        reserved2       :12;
71        uint64_t        available2      :11;
72        uint64_t        execute_disable :1;
73    } d;
74};
75
76#ifdef __k1om__
77#define X86_64_PHYSADDR_BITS X1OM_PADDR_SPACE_BITS // TODO: Take that from offsets target
78#else
79#define X86_64_PHYSADDR_BITS X86_64_PADDR_SPACE_BITS
80#endif
81#define X86_64_PAGING_ENTRY_SIZE 64
82#define X86_64_PAGING_AVAIL2_BITS 11
83#define X86_64_PAGING_FLAG_BITS 12
84#define X86_64_PAGING_LARGE_FLAGE_BITS 21
85#define X86_64_PAGING_RESERVED_BITS \
86                (X86_64_PAGING_ENTRY_SIZE - X86_64_PHYSADDR_BITS - \
87                 X86_64_PAGING_AVAIL2_BITS - 1)
88#define X86_64_PAGING_LARGE_BASE_BITS \
89                (X86_64_PHYSADDR_BITS - X86_64_PAGING_LARGE_FLAGE_BITS)
90#define X86_64_PAGING_BASE_BASE_BITS \
91                (X86_64_PHYSADDR_BITS - X86_64_PAGING_FLAG_BITS)
92
93
94/**
95 * A page table entry.
96 */
97union x86_64_ptable_entry {
98    uint64_t raw;
99    struct {
100        uint64_t        present         :1;
101        uint64_t        read_write      :1;
102        uint64_t        user_supervisor :1;
103        uint64_t        write_through   :1;
104        uint64_t        cache_disabled  :1;
105        uint64_t        accessed        :1;
106        uint64_t        dirty           :1;
107        uint64_t        always1         :1;
108        uint64_t        global          :1;
109        uint64_t        available       :2;
110        uint64_t        vtd_snoop       :1;
111        uint64_t        attr_index      :1;
112        uint64_t        reserved        :17;
113        uint64_t        base_addr       :10;
114        uint64_t        reserved2       :12;
115        uint64_t        available2      :11;
116        uint64_t        execute_disable :1;
117    } huge;
118    struct {
119        uint64_t        present         :1;
120        uint64_t        read_write      :1;
121        uint64_t        user_supervisor :1;
122        uint64_t        write_through   :1;
123        uint64_t        cache_disabled  :1;
124        uint64_t        accessed        :1;
125        uint64_t        dirty           :1;
126        uint64_t        always1         :1;
127        uint64_t        global          :1;
128        uint64_t        available       :2;
129        uint64_t        vtd_snoop       :1;
130        uint64_t        attr_index      :1;
131        uint64_t        reserved        :8;
132        uint64_t        base_addr       :X86_64_PAGING_LARGE_BASE_BITS;
133        uint64_t        reserved2       :X86_64_PAGING_RESERVED_BITS;
134        uint64_t        available2      :11;
135        uint64_t        execute_disable :1;
136    } large;
137    struct {
138        uint64_t        present         :1;
139        uint64_t        read_write      :1;
140        uint64_t        user_supervisor :1;
141        uint64_t        write_through   :1;
142        uint64_t        cache_disabled  :1;
143        uint64_t        accessed        :1;
144        uint64_t        dirty           :1;
145        uint64_t        attr_index      :1;
146        uint64_t        global          :1;
147        uint64_t        available       :2;
148        uint64_t        vtd_snoop       :1;
149        uint64_t        base_addr       :X86_64_PAGING_BASE_BASE_BITS;
150        uint64_t        reserved2       :X86_64_PAGING_RESERVED_BITS;
151        uint64_t        available2      :11;
152        uint64_t        execute_disable :1;
153    } base;
154};
155
156STATIC_ASSERT_SIZEOF(union x86_64_ptable_entry, (X86_64_PAGING_ENTRY_SIZE / 8));
157
158/**
159 * \brief Clear page directory.
160 *
161 * Clears page directory pointed to by 'p'.
162 *
163 * \param p     Pointer to page directory to clear.
164 */
165static inline void paging_x86_64_clear_pdir(union x86_64_pdir_entry * COUNT(X86_64_PTABLE_SIZE)
166                                            NONNULL p)
167{
168    for (int i = 0; i < X86_64_PTABLE_SIZE; i++) {
169        p[i].raw = X86_64_PTABLE_CLEAR;
170    }
171}
172
173/**
174 * \brief Clear page table.
175 *
176 * Clears page table pointed to by 'p'.
177 *
178 * \param p     Pointer to page table to clear.
179 */
180static inline void paging_x86_64_clear_ptable(union x86_64_ptable_entry * COUNT(X86_64_PTABLE_SIZE)
181                                              NONNULL p)
182{
183    for (int i = 0; i < X86_64_PTABLE_SIZE; i++) {
184        p[i].raw = X86_64_PTABLE_CLEAR;
185    }
186}
187
188/**
189 * \brief Maps from page directory entry to page directory/table.
190 *
191 * Maps page directory or table, based at 'base', from page directory entry
192 * pointed to by 'entry'.
193 *
194 * \param entry Pointer to page directory entry to point from.
195 * \param base  Base virtual address of page directory/table to point to.
196 */
197static inline void paging_x86_64_map_table(union x86_64_pdir_entry *entry,
198                                           lpaddr_t base)
199{
200    union x86_64_pdir_entry tmp;
201    tmp.raw = X86_64_PTABLE_CLEAR;
202
203    tmp.d.present = 1;
204    tmp.d.read_write = 1;
205    tmp.d.user_supervisor = 1;
206    tmp.d.base_addr = base >> 12;
207
208    *entry = tmp;
209}
210
211/**
212 * \brief Maps a huge page.
213 *
214 * From huge page table entry, pointed to by 'entry', maps physical address
215 * 'base' with page attribute bitmap 'bitmap'.
216 *
217 * \param entry         Pointer to page table entry to map from.
218 * \param base          Physical address to map to (will be page-aligned).
219 * \param bitmap        Bitmap to apply to page attributes.
220 */
221static inline void paging_x86_64_map_huge(union x86_64_ptable_entry *entry,
222                                           lpaddr_t base, uint64_t bitmap)
223{
224    union x86_64_ptable_entry tmp;
225    tmp.raw = X86_64_PTABLE_CLEAR;
226
227    tmp.huge.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
228    tmp.huge.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
229    tmp.huge.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
230    tmp.huge.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
231    tmp.huge.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
232    tmp.huge.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
233    tmp.huge.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
234    tmp.huge.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
235    tmp.huge.always1 = 1;
236    tmp.huge.base_addr = base >> X86_64_HUGE_PAGE_BITS;
237
238    *entry = tmp;
239}
240
241/**
242 * \brief Maps a large page.
243 *
244 * From large page table entry, pointed to by 'entry', maps physical address
245 * 'base' with page attribute bitmap 'bitmap'.
246 *
247 * \param entry         Pointer to page table entry to map from.
248 * \param base          Physical address to map to (will be page-aligned).
249 * \param bitmap        Bitmap to apply to page attributes.
250 */
251static inline void paging_x86_64_map_large(union x86_64_ptable_entry *entry,
252                                           lpaddr_t base, uint64_t bitmap)
253{
254    union x86_64_ptable_entry tmp;
255    tmp.raw = X86_64_PTABLE_CLEAR;
256
257    tmp.large.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
258    tmp.large.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
259    tmp.large.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
260    tmp.large.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
261    tmp.large.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
262#ifdef __k1om__
263	/* The Xeon Phi has no support for global pages */
264	tmp.large.global = 0;
265#else
266    tmp.large.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
267#endif
268    tmp.large.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
269    tmp.large.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
270    tmp.large.always1 = 1;
271    tmp.large.vtd_snoop = bitmap & X86_64_VTD_PAGE_SNOOP ? 1 : 0;
272    tmp.large.base_addr = base >> 21;
273
274    *entry = tmp;
275}
276
277/**
278 * \brief Maps a normal (small) page.
279 *
280 * From small page table entry, pointed to by 'entry', maps physical address
281 * 'base' with page attribute bitmap 'bitmap'.
282 *
283 * \param entry         Pointer to page table entry to map from.
284 * \param base          Physical address to map to (will be page-aligned).
285 * \param bitmap        Bitmap to apply to page attributes.
286 */
287static inline void paging_x86_64_map(union x86_64_ptable_entry * NONNULL entry,
288                                     lpaddr_t base, uint64_t bitmap)
289{
290    union x86_64_ptable_entry tmp;
291    tmp.raw = X86_64_PTABLE_CLEAR;
292
293    tmp.base.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
294    tmp.base.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
295    tmp.base.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
296    tmp.base.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
297    tmp.base.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
298    tmp.base.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
299#ifdef __k1om__
300	/* The Xeon Phi has no support for global pages */
301	tmp.base.global = 0;
302#else
303    tmp.base.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
304#endif
305    tmp.base.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
306    tmp.base.vtd_snoop = bitmap & X86_64_VTD_PAGE_SNOOP ? 1 : 0;
307    tmp.base.base_addr = base >> 12;
308
309    *entry = tmp;
310}
311
312
313/**
314 * \brief Modify flags of a huge page.
315 *
316 * From small page table entry, pointed to by 'entry', maps physical address
317 * 'base' with page attribute bitmap 'bitmap'.
318 *
319 * \param entry         Pointer to page table entry to map from.
320 * \param bitmap        Bitmap to apply to page attributes.
321 */
322static inline void paging_x86_64_modify_flags_huge(union x86_64_ptable_entry * entry,
323                                                   uint64_t bitmap)
324{
325    union x86_64_ptable_entry tmp = *entry;
326
327    tmp.huge.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
328    tmp.huge.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
329    tmp.huge.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
330    tmp.huge.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
331    tmp.huge.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
332#ifdef __k1om__
333    tmp.huge.global = 0;
334#else
335    tmp.huge.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
336#endif
337    tmp.huge.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
338    tmp.huge.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
339    tmp.huge.always1 = 1;
340
341    *entry = tmp;
342}
343
344
345/**
346 * \brief Modify flags of a large page.
347 *
348 * From small page table entry, pointed to by 'entry', maps physical address
349 * 'base' with page attribute bitmap 'bitmap'.
350 *
351 * \param entry         Pointer to page table entry to map from.
352 * \param bitmap        Bitmap to apply to page attributes.
353 */
354static inline void paging_x86_64_modify_flags_large(union x86_64_ptable_entry *entry,
355                                              uint64_t bitmap)
356{
357    union x86_64_ptable_entry tmp = *entry;
358
359    tmp.large.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
360    tmp.large.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
361    tmp.large.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
362    tmp.large.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
363    tmp.large.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
364#ifdef __k1om__
365    /* The Xeon Phi has no support for global pages */
366    tmp.large.global = 0;
367#else
368    tmp.large.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
369#endif
370    tmp.large.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
371    tmp.large.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
372    tmp.large.always1 = 1;
373
374    *entry = tmp;
375}
376
377/**
378 * \brief Modify flags of a normal (small) page.
379 *
380 * From small page table entry, pointed to by 'entry', maps physical address
381 * 'base' with page attribute bitmap 'bitmap'.
382 *
383 * \param entry         Pointer to page table entry to map from.
384 * \param bitmap        Bitmap to apply to page attributes.
385 */
386static inline void paging_x86_64_modify_flags(union x86_64_ptable_entry * NONNULL entry,
387                                              uint64_t bitmap)
388{
389    union x86_64_ptable_entry tmp = *entry;
390
391    tmp.base.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
392    tmp.base.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
393    tmp.base.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
394    tmp.base.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
395    tmp.base.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
396    tmp.base.attr_index = bitmap & X86_64_PTABLE_ATTR_INDEX ? 1 : 0;
397#ifdef __k1om__
398	/* XXX: The Xeon Phi does no support global pages */
399	tmp.base.global =  0;
400#else
401    tmp.base.global = bitmap & X86_64_PTABLE_GLOBAL_PAGE ? 1 : 0;
402#endif
403    tmp.base.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
404
405    *entry = tmp;
406}
407static inline void paging_x86_64_pdir_modify_flags(union x86_64_pdir_entry * NONNULL entry,
408                                              uint64_t bitmap)
409{
410    union x86_64_pdir_entry tmp = *entry;
411
412    tmp.d.present = bitmap & X86_64_PTABLE_PRESENT ? 1 : 0;
413    tmp.d.read_write = bitmap & X86_64_PTABLE_READ_WRITE ? 1 : 0;
414    tmp.d.user_supervisor = bitmap & X86_64_PTABLE_USER_SUPERVISOR ? 1 : 0;
415    tmp.d.write_through = bitmap & X86_64_PTABLE_WRITE_THROUGH ? 1 : 0;
416    tmp.d.cache_disabled = bitmap & X86_64_PTABLE_CACHE_DISABLED ? 1 : 0;
417    tmp.d.execute_disable = bitmap & X86_64_PTABLE_EXECUTE_DISABLE ? 1 : 0;
418
419    *entry = tmp;
420}
421
422static inline void paging_unmap(union x86_64_ptable_entry * NONNULL entry)
423{
424    entry->raw = X86_64_PTABLE_CLEAR;
425}
426
427/**
428 * \brief Convert Capability access rights to X86-64 page flags.
429 *
430 * Returns corresponding X86-64 page flags to given capability access rights
431 * mask 'rights'.
432 *
433 * \param rights        Capability rightsmask.
434 *
435 * \return X86-64 page flags.
436 */
437static inline uint64_t paging_x86_64_cap_to_page_flags(CapRights rights)
438{
439    uint64_t pageflags = 0;
440
441    // Sanity-check given flags
442    if(!(rights & CAPRIGHTS_READ) &&
443       (rights & CAPRIGHTS_WRITE || rights & CAPRIGHTS_EXECUTE)) {
444        printk(LOG_ERR, "Page mapped writable and/or executable, but not "
445               "readable. Impossible on X86! Will map non-everything "
446               "instead.\n");
447    }
448
449    // Convert flags
450    pageflags |= rights & CAPRIGHTS_READ ? X86_64_PTABLE_USER_SUPERVISOR : 0;
451    pageflags |= rights & CAPRIGHTS_WRITE ? X86_64_PTABLE_READ_WRITE : 0;
452    pageflags |= rights & CAPRIGHTS_EXECUTE ? 0 : X86_64_PTABLE_EXECUTE_DISABLE;
453
454    return pageflags;
455}
456
457/**
458 * \brief Switch context.
459 *
460 * Assigns given physical base address of PML4 'pml4' to the CR3
461 * register, effectively switching context to new address space. Be
462 * cautious that you only switch to "good" (as explained in
463 * paging_make_good_pml4()) PML4s!
464 *
465 * \param pml4  Physical base address of PML4 table.
466 */
467static void inline paging_x86_64_context_switch(lpaddr_t pml4)
468{
469    __asm volatile("mov %[pml4], %%cr3"
470                   : /* No output */
471                   :
472                   [pml4] "r" (pml4)
473                   );
474}
475
476/**
477 * \brief Mask out page attributes.
478 *
479 * Masks out all attributes and access rights from 'attr' according to
480 * 'mask'. This is architecture-specific. On x86-64, except for the
481 * execute disable attribute, rights are given by setting a
482 * corresponding bit. Thus, setting that bit within 'mask' to zero,
483 * masks out the right. For the execute disable bit, the right is
484 * masked out when the bit is set, so the mask works the other way
485 * around in this case: When the bit is set in 'mask', but not set in
486 * 'attr', it will be set in the return value, so mask-out behavior is
487 * preserved.
488 *
489 * \param attr  The page attributes to mask.
490 * \param mask  Mask for the page attributes.
491 *
492 * \return Masked version of 'attr'.
493 */
494static inline uint64_t paging_x86_64_mask_attrs(uint64_t attr, uint64_t mask)
495{
496    // First, mask out all "bit-sets-enabled" attributes
497    attr &= mask | X86_64_PTABLE_EXECUTE_DISABLE;
498
499    // Now, mask out all "bit-sets-disabled" attributes
500    attr |= mask & X86_64_PTABLE_EXECUTE_DISABLE;
501
502    return attr;
503}
504
505#endif // KERNEL_TARGET_X86_64_PAGING_H
506