summaryrefslogtreecommitdiff
path: root/xen/arch/arm/include/asm/lpae.h
blob: 3fdd5d0de28ed2484f342b4ebec6107779356a2b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#ifndef __ARM_LPAE_H__
#define __ARM_LPAE_H__

#ifndef __ASSEMBLY__

#include <xen/page-defs.h>
#include <xen/mm-frame.h>

/*
 * WARNING!  Unlike the x86 pagetable code, where l1 is the lowest level and
 * l4 is the root of the trie, the ARM pagetables follow ARM's documentation:
 * the levels are called first, second &c in the order that the MMU walks them
 * (i.e. "first" is the root of the trie).
 */

/******************************************************************************
 * ARMv7-A LPAE pagetables: 3-level trie, mapping 40-bit input to
 * 40-bit output addresses.  Tables at all levels have 512 64-bit entries
 * (i.e. are 4Kb long).
 *
 * The bit-shuffling that has the permission bits in branch nodes in a
 * different place from those in leaf nodes seems to be to allow linear
 * pagetable tricks.  If we're not doing that then the set of permission
 * bits that's not in use in a given node type can be used as
 * extra software-defined bits.
 */

typedef struct __packed {
    /* These are used in all kinds of entry. */
    unsigned long valid:1;      /* Valid mapping */
    unsigned long table:1;      /* == 1 in 4k map entries too */

    /*
     * These ten bits are only used in Block entries and are ignored
     * in Table entries.
     */
    unsigned long ai:3;         /* Attribute Index */
    unsigned long ns:1;         /* Not-Secure */
    unsigned long up:1;         /* Unpriviledged access */
    unsigned long ro:1;         /* Read-Only */
    unsigned long sh:2;         /* Shareability */
    unsigned long af:1;         /* Access Flag */
    unsigned long ng:1;         /* Not-Global */

    /* The base address must be appropriately aligned for Block entries */
    unsigned long long base:36; /* Base address of block or next table */
    unsigned long sbz:4;        /* Must be zero */

    /*
     * These seven bits are only used in Block entries and are ignored
     * in Table entries.
     */
    unsigned long contig:1;     /* In a block of 16 contiguous entries */
    unsigned long pxn:1;        /* Privileged-XN */
    unsigned long xn:1;         /* eXecute-Never */
    unsigned long avail:4;      /* Ignored by hardware */

    /*
     * These 5 bits are only used in Table entries and are ignored in
     * Block entries.
     */
    unsigned long pxnt:1;       /* Privileged-XN */
    unsigned long xnt:1;        /* eXecute-Never */
    unsigned long apt:2;        /* Access Permissions */
    unsigned long nst:1;        /* Not-Secure */
} lpae_pt_t;

/*
 * The p2m tables have almost the same layout, but some of the permission
 * and cache-control bits are laid out differently (or missing).
 */
typedef struct __packed {
    /* These are used in all kinds of entry. */
    unsigned long valid:1;      /* Valid mapping */
    unsigned long table:1;      /* == 1 in 4k map entries too */

    /*
     * These ten bits are only used in Block entries and are ignored
     * in Table entries.
     */
    unsigned long mattr:4;      /* Memory Attributes */
    unsigned long read:1;       /* Read access */
    unsigned long write:1;      /* Write access */
    unsigned long sh:2;         /* Shareability */
    unsigned long af:1;         /* Access Flag */
    unsigned long sbz4:1;

    /* The base address must be appropriately aligned for Block entries */
    unsigned long long base:36; /* Base address of block or next table */
    unsigned long sbz3:4;

    /*
     * These seven bits are only used in Block entries and are ignored
     * in Table entries.
     */
    unsigned long contig:1;     /* In a block of 16 contiguous entries */
    unsigned long sbz2:1;
    unsigned long xn:1;         /* eXecute-Never */
    unsigned long type:4;       /* Ignore by hardware. Used to store p2m types */

    unsigned long sbz1:5;
} lpae_p2m_t;

/* Permission mask: xn, write, read */
#define P2M_PERM_MASK (0x00400000000000C0ULL)
#define P2M_CLEAR_PERM(pte) ((pte).bits & ~P2M_PERM_MASK)

/*
 * Walk is the common bits of p2m and pt entries which are needed to
 * simply walk the table (e.g. for debug).
 */
typedef struct __packed {
    /* These are used in all kinds of entry. */
    unsigned long valid:1;      /* Valid mapping */
    unsigned long table:1;      /* == 1 in 4k map entries too */

    unsigned long pad2:10;

    /* The base address must be appropriately aligned for Block entries */
    unsigned long long base:36; /* Base address of block or next table */

    unsigned long pad1:16;
} lpae_walk_t;

typedef union {
    uint64_t bits;
    lpae_pt_t pt;
    lpae_p2m_t p2m;
    lpae_walk_t walk;
} lpae_t;

static inline bool lpae_is_valid(lpae_t pte)
{
    return pte.walk.valid;
}

/*
 * lpae_is_* don't check the valid bit. This gives an opportunity for the
 * callers to operate on the entry even if they are not valid. For
 * instance to store information in advance.
 */
static inline bool lpae_is_table(lpae_t pte, unsigned int level)
{
    return (level < 3) && pte.walk.table;
}

static inline bool lpae_is_mapping(lpae_t pte, unsigned int level)
{
    if ( level == 3 )
        return pte.walk.table;
    else
        return !pte.walk.table;
}

static inline bool lpae_is_superpage(lpae_t pte, unsigned int level)
{
    return (level < 3) && lpae_is_mapping(pte, level);
}

#define lpae_get_mfn(pte)    (_mfn((pte).walk.base))
#define lpae_set_mfn(pte, mfn)  ((pte).walk.base = mfn_x(mfn))

/* Generate an array @var containing the offset for each level from @addr */
#define DECLARE_OFFSETS(var, addr)          \
    const unsigned int var[4] = {           \
        zeroeth_table_offset(addr),         \
        first_table_offset(addr),           \
        second_table_offset(addr),          \
        third_table_offset(addr)            \
    }

/*
 * Standard entry type that we'll use to build Xen's own pagetables.
 * We put the same permissions at every level, because they're ignored
 * by the walker in non-leaf entries.
 */
lpae_t mfn_to_xen_entry(mfn_t mfn, unsigned int attr);

#endif /* __ASSEMBLY__ */

/*
 * AArch64 supports pages with different sizes (4K, 16K, and 64K).
 * Provide a set of generic helpers that will compute various
 * information based on the page granularity.
 *
 * Note the parameter 'gs' is the page shift of the granularity used.
 * Some macro will evaluate 'gs' twice rather than storing in a
 * variable. This is to allow using the macros in assembly.
 */

/*
 * Granularity | PAGE_SHIFT | LPAE_SHIFT
 * -------------------------------------
 * 4K          | 12         | 9
 * 16K         | 14         | 11
 * 64K         | 16         | 13
 *
 * This is equivalent to LPAE_SHIFT = PAGE_SHIFT - 3
 */
#define LPAE_SHIFT_GS(gs)         ((gs) - 3)
#define LPAE_ENTRIES_GS(gs)       (_AC(1, U) << LPAE_SHIFT_GS(gs))
#define LPAE_ENTRY_MASK_GS(gs)    (LPAE_ENTRIES_GS(gs) - 1)

#define LEVEL_ORDER_GS(gs, lvl)   ((3 - (lvl)) * LPAE_SHIFT_GS(gs))
#define LEVEL_SHIFT_GS(gs, lvl)   (LEVEL_ORDER_GS(gs, lvl) + (gs))
#define LEVEL_SIZE_GS(gs, lvl)    (_AT(paddr_t, 1) << LEVEL_SHIFT_GS(gs, lvl))

/* Offset in the table at level 'lvl' */
#define LPAE_TABLE_INDEX_GS(gs, lvl, addr)   \
    (((addr) >> LEVEL_SHIFT_GS(gs, lvl)) & LPAE_ENTRY_MASK_GS(gs))

/*
 * These numbers add up to a 48-bit input address space.
 *
 * On 32-bit the zeroeth level does not exist, therefore the total is
 * 39-bits. The ARMv7-A architecture actually specifies a 40-bit input
 * address space for the p2m, with an 8K (1024-entry) top-level table.
 * However Xen only supports 16GB of RAM on 32-bit ARM systems and
 * therefore 39-bits are sufficient.
 */

#define XEN_PT_LPAE_SHIFT         LPAE_SHIFT_GS(PAGE_SHIFT)
#define XEN_PT_LPAE_ENTRIES       LPAE_ENTRIES_GS(PAGE_SHIFT)
#define XEN_PT_LPAE_ENTRY_MASK    LPAE_ENTRY_MASK_GS(PAGE_SHIFT)

#define XEN_PT_LEVEL_SHIFT(lvl)   LEVEL_SHIFT_GS(PAGE_SHIFT, lvl)
#define XEN_PT_LEVEL_ORDER(lvl)   LEVEL_ORDER_GS(PAGE_SHIFT, lvl)
#define XEN_PT_LEVEL_SIZE(lvl)    LEVEL_SIZE_GS(PAGE_SHIFT, lvl)
#define XEN_PT_LEVEL_MASK(lvl)    (~(XEN_PT_LEVEL_SIZE(lvl) - 1))

/* Convenience aliases */
#define THIRD_SHIFT         XEN_PT_LEVEL_SHIFT(3)
#define THIRD_ORDER         XEN_PT_LEVEL_ORDER(3)
#define THIRD_SIZE          XEN_PT_LEVEL_SIZE(3)
#define THIRD_MASK          XEN_PT_LEVEL_MASK(3)

#define SECOND_SHIFT        XEN_PT_LEVEL_SHIFT(2)
#define SECOND_ORDER        XEN_PT_LEVEL_ORDER(2)
#define SECOND_SIZE         XEN_PT_LEVEL_SIZE(2)
#define SECOND_MASK         XEN_PT_LEVEL_MASK(2)

#define FIRST_SHIFT         XEN_PT_LEVEL_SHIFT(1)
#define FIRST_ORDER         XEN_PT_LEVEL_ORDER(1)
#define FIRST_SIZE          XEN_PT_LEVEL_SIZE(1)
#define FIRST_MASK          XEN_PT_LEVEL_MASK(1)

#define ZEROETH_SHIFT       XEN_PT_LEVEL_SHIFT(0)
#define ZEROETH_ORDER       XEN_PT_LEVEL_ORDER(0)
#define ZEROETH_SIZE        XEN_PT_LEVEL_SIZE(0)
#define ZEROETH_MASK        XEN_PT_LEVEL_MASK(0)

/* Calculate the offsets into the pagetables for a given VA */
#define zeroeth_linear_offset(va) ((va) >> ZEROETH_SHIFT)
#define first_linear_offset(va) ((va) >> FIRST_SHIFT)
#define second_linear_offset(va) ((va) >> SECOND_SHIFT)
#define third_linear_offset(va) ((va) >> THIRD_SHIFT)

#define TABLE_OFFSET(offs) (_AT(unsigned int, offs) & XEN_PT_LPAE_ENTRY_MASK)
#define first_table_offset(va)  TABLE_OFFSET(first_linear_offset(va))
#define second_table_offset(va) TABLE_OFFSET(second_linear_offset(va))
#define third_table_offset(va)  TABLE_OFFSET(third_linear_offset(va))
#define zeroeth_table_offset(va)  TABLE_OFFSET(zeroeth_linear_offset(va))

/*
 * Macros to define page-tables:
 *  - DEFINE_BOOT_PAGE_TABLE is used to define page-table that are used
 *  in assembly code before BSS is zeroed.
 *  - DEFINE_PAGE_TABLE{,S} are used to define one or multiple
 *  page-tables to be used after BSS is zeroed (typically they are only used
 *  in C).
 */
#define DEFINE_BOOT_PAGE_TABLE(name)                                          \
lpae_t __aligned(PAGE_SIZE) __section(".data.page_aligned")                   \
    name[XEN_PT_LPAE_ENTRIES]

#define DEFINE_PAGE_TABLES(name, nr)                    \
lpae_t __aligned(PAGE_SIZE) name[XEN_PT_LPAE_ENTRIES * (nr)]

#define DEFINE_PAGE_TABLE(name) DEFINE_PAGE_TABLES(name, 1)

#endif /* __ARM_LPAE_H__ */

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */