summaryrefslogtreecommitdiffstats
path: root/src/include/kernel/ptmgr.H
blob: 462a6400ec70a9976fd3320c953345acb2127018 (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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#ifndef __KERNEL_PTMGR_H
#define __KERNEL_PTMGR_H

#include <stdint.h>
#include <util/lockfree/stack.H>
#include <kernel/vmmmgr.H>

/**
 * @class PageTableManager
 * @brief Manages the Page Table in the hardware
 */
class PageTableManager
{
  public:
    // Public Constants

    /**
     * Status Values
     */
    enum
    {
        PTE_UNKNOWN          = 0x0000000000000000,  /**< Entry wasn't found */
        PTE_PRESENT          = 0x0000000000000001,  /**< Entry is present in table */
        PTE_VALID            = 0x0000000000000002,  /**< Entry is valid */
        PTE_READ_ONLY        = 0x0000000000000004,  /**< Read-Only */
        PTE_EXECUTE          = 0x0000000000000008,  /**< Execute permission */
        PTE_CACHE_INHIBITED  = 0x0000000000000010,  /**< Cache-Inhibited Access */
        PTE_MODIFIED         = 0x0000000000000020,  /**< Page has been modified */
        PTE_ACCESSED         = 0x0000000000000040,  /**< Page has been accessed */
    };

    /**
     * Page Table Constants
     */
    enum
    {
        PT_SIZE = (1 << 18),  /**< Size of Page Table in bytes */
        PTE_SIZE = 2*sizeof(uint64_t),   /**< Size of a single Page Table Entry in bytes */
        PTEG_SIZE = 8,  /**< Number of PTEs in a single PTE Group */
        PTEG_COUNT = (PT_SIZE / PTE_SIZE) / PTEG_SIZE,  /**< Number of PTEGs in the Page Table */
        INVALID_PN = 0xFFFFFFFFFFFFFFFF,  /**< Invalid value for a Page Number return */
    };

    /**
     * @brief Static Initializer
     */
    static void init();

    /**
     * @brief Add an entry to the hardware page table
     *
     * @param[in] i_vAddr  Virtual Address within the page to be mapped  (full address)
     * @param[in] i_page  Real physical page number to map to  (page number)
     * @param[in] i_accessType  Type of access page will be given
     */
    static void addEntry( uint64_t i_vAddr,
                          uint64_t i_page,
                          VmmManager::ACCESS_TYPES i_accessType );

    /**
     * @brief Remove an entry from the hardware page table
     *
     * @param[in] i_vAddr  Virtual Address within the page to be removed  (full address)
     */
    static void delEntry( uint64_t i_vAddr );

    /**
     * @brief Remove a range of entries from the hardware page table
     *
     * @param[in] i_vAddrStart  Beginning of VA range to remove  (full address)
     * @param[in] i_vAddrFinish  End of VA range to remove  (full address)
     */
    static void delRangeVA( uint64_t i_vAddrStart,
                            uint64_t i_vAddrFinish );


    /**
     * @brief Remove a range of entries from the hardware page table
     *
     * @param[in] i_pnStart  First Physical Page to remove  (page number)
     * @param[in] i_pnFinish  Last Physical Page to remove  (page number)
     */
    static void delRangePN( uint64_t i_pnStart,
                            uint64_t i_pnFinish );

    /**
     * @brief Return status information about an entry in the hardware page table
     *
     * @param[in] i_vAddr  Virtual Address within the page to be queried  (full address)
     * @param[out] o_pn  Real physical page number to map to, INVALID_PN if PTE is invalid
     *
     * @return uint64_t  ORed combination of status flags
     */
    static uint64_t getStatus( uint64_t i_vAddr,
                               uint64_t& o_pn );

    /**
     * @brief  Print out the contents of a PTE to the printk buffer
     *
     * @param[in] i_label  string to display along with the PTE data
     * @param[in] i_pteAddr  address/pointer to the PTE to display
     * @param[in] i_verbose  true=break out all attributes, false=abbreviated output
     */
    static void printPTE( const char* i_label,
                          uint64_t i_pteAddr,
                          bool i_verbose = false );

    /**
     * @brief  Print out the contents of a PTE based on a VA
     *
     * @param[in] i_va  Virtual address that is part of the page the PTE points to
     * @param[in] i_verbose  true=break out all attributes, false=abbreviated output
     */
    static void printPTE( uint64_t i_va,
                          bool i_verbose = false );

    /**
     * @brief  Print out the entire Page Table
     */
    static void printPT( void );


  protected:
    /**
     * @brief  Constructor
     *
     * @param[in] i_userSpace  true=declare a local page table for user-space testing
     */
    PageTableManager( bool i_userSpace = false );

    /**
     * @brief  Destructor
     */
    ~PageTableManager();

  private:
    /**
     * Local copy of Page Table for user-space testing
     *  (set to NULL for kernel instance)
     */
    char* ivTABLE;


    /**
     * Represents a single entry in the page table
     */
    struct PageTableEntry
    {
        /**
         * Dword0
         */
        union {
            struct {  /**< Dword0 Attributes */
                uint64_t B:2;    /**< 0:1   Segment Size  */
                uint64_t AVA:55; /**< 2:56  Abbreviated Virtual Address = VA w/o bottom 23 bits  */
                uint64_t SW:2;   /**< 57:58 =SW[0:1] - Reserved  */
                uint64_t LRU:2;  /**< 59:60 =SW[2:3] - Used for LRU algorithm  */
                uint64_t L:1;    /**< 61    Virtual page size  */
                uint64_t H:1;    /**< 62    Hash function identifier  */
                uint64_t V:1;    /**< 63    Entry valid  */
            };
            uint64_t dword0;  /**< Full Dword0 */
        };

        /**
         * Dword1
         */
        union {
            struct {  /**< Dword0 Attributes */
                uint64_t pp0:1;    /**< 0     Page Protection bit 0  */
                uint64_t rsv:1;    /**< 1     Reserved  */
                uint64_t key0_1:2; /**< 2:3   KEY bits 0:1  <unused>  */
                uint64_t PN:48;    /**< 4:52  Abbreviated Real Page Number + Large page size selector  */
                uint64_t key2_4:3; /**< 53:54 KEY bits 2:4  <unused>  */
                uint64_t R:1;      /**< 55    Reference bit  */
                uint64_t C:1;      /**< 56    Change bit  */
                uint64_t WIMG:4;   /**< 57:60 Storage control bits  */
                uint64_t N:1;      /**< 61    No-execute page (N==1)  */
                uint64_t pp1_2:2;  /**< 62:63 Page Protection bits 1:2  */
            };
            uint64_t dword1;  /**< Full Dword1 */
        };
    } PACKED;

    /**
     * Internal Constants
     */
    enum {
        PTE_ACCESS_BITS = 0x800000000000007B,  /**< pp0 + WIMG + pp1_2  */
        PTEG_SIZE_BYTES = (sizeof(PageTableEntry)*8),  /**< Size of PTE Group in bytes */

        SLBE_b = 12,  /**< Page Size in bits per SLBE  */
        SLBE_s = 40,  /**< Segment Size in bits per SLBE  */

    };

    /**
     * @brief  Return the hash value of the VA
     *
     * @param[in] i_vAddr  Virtual Address to hash
     *
     * @return uint64_t  hash value used by Page Table hardware
     */
    uint64_t computeHash( uint64_t i_vAddr );

    /**
     * @brief  Find the real address of the PTEG that matches the given VA
     *
     * @param[in] i_vAddr  Virtual Address to look up
     *
     * @return uint64_t  PTEG address
     */
    uint64_t findPTEG( uint64_t i_vAddr );

    /**
     * @brief  Find the PTE that matches the given VA
     *
     * @param[in] i_vAddr  Virtual Address to search for
     *
     * @return PageTableEntry*  Pointer to PTE, this is a real address
     */
    PageTableEntry* findPTE( uint64_t i_vAddr );

    /**
     * @brief  Find the real address of the PTE that matches the given address
     *
     * @param[in] i_vAddr  Virtual Address to search for
     * @param[in] i_ptegAddr  Real Address of PTEG that would own this PTE
     *
     * @return PageTableEntry*  Pointer to PTE, this is a real address
     */
    PageTableEntry* findPTE( uint64_t i_vAddr,
                             uint64_t i_ptegAddr );

    /**
     * @brief  Find the real address of a PTE that that is empty or invalid
     *
     * @param[in] i_ptegAddr  Real Address of PTEG that would own this PTE
     *
     * @return PageTableEntry*  Pointer to PTE slot, this is a real address
     */
    PageTableEntry* findEmptyPTE( uint64_t i_ptegAddr );

    /**
     * @brief  Find the real address of a PTE that can be invalidated
     *   and replaced
     *
     * @param[in] i_ptegAddr  Real Address of PTEG that would own this PTE
     *
     * @return PageTableEntry*  Pointer to PTE, this is a real address
     */
    PageTableEntry* findOldPTE( uint64_t i_ptegAddr );

    /**
     * @brief  Write a PTE to memory and update caches appropriately
     *
     * @param[in] i_pte  Local pointer to PTE data
     * @param[in] i_dest  Real Address inside page table to write i_pte data into
     * @param[in] i_valid  true=set Valid bit, false=clear Valid bit
     */
    void writePTE( PageTableEntry* i_pte,
                   PageTableEntry* i_dest,
                   bool i_valid );

    /**
     * @brief Add an entry to the hardware page table
     *
     * @param[in] i_vAddr  Virtual Address within the page to be mapped
     * @param[in] i_page  Real physical page number to map to
     * @param[in] i_accessType  Type of access page will be given
     */
    void _addEntry( uint64_t i_vAddr,
                    uint64_t i_page,
                    VmmManager::ACCESS_TYPES i_accessType );

    /**
     * @brief Remove an entry from the hardware page table
     *
     * @param[in] i_vAddr  Virtual Address within the page to be removed
     */
    void _delEntry( uint64_t i_vAddr );

    /**
     * @brief Remove an entry from the hardware page table
     *
     * @param[in] i_pte  Pointer to real PTE in the page table
     */
    void delEntry( PageTableEntry* i_pte );

    /**
     * @brief Remove a range of entries from the hardware page table
     *
     * @param[in] i_vAddrStart  Beginning of VA range to remove
     * @param[in] i_vAddrFinish  End of VA range to remove
     */
    void _delRangeVA( uint64_t i_vAddrStart,
                      uint64_t i_vAddrFinish );

    /**
     * @brief Remove a range of entries from the hardware page table
     *
     * @param[in] i_pnStart  First Physical Page to remove
     * @param[in] i_pnFinish  Last Physical Page to remove
     */
    void _delRangePN( uint64_t i_pnStart,
                      uint64_t i_pnFinish );

    /**
     * @brief Return status information about an entry in the hardware page table
     *
     * @param[in] i_vAddr  Virtual Address within the page to be queried
     * @param[out] o_pn  Real physical page number to map to, INVALID_PN if PTE is invalid
     *
     * @return uint64_t  ORed combination of status flags
     */
    uint64_t _getStatus( uint64_t i_vAddr,
                         uint64_t& o_pn );

    /**
     * @brief Translate a PTE into the status bits
     *
     * @param[in] i_pte  Pointer to PTE, could be local memory or part of page table
     *
     * @return uint64_t  ORed combination of status flags
     */
    uint64_t getStatus( PageTableEntry* i_pte );

    /**
     * @brief Update the LRU statistics for other PTEs in the same PTEG as the target
     *
     * @param[in] i_newPTE  Real address of PTE that is being added to the Page Table
     */
    void updateLRU( const PageTableEntry* i_newPTE );

    /**
     * @brief Invalidate TLB for a PTE
     *
     * @param[in] i_newPTE  Real address of PTE that is being modified
     */
    void invalidateTLB( PageTableEntry* i_pte );

    /**
     * @brief Invalidate all PTEs in the table
     */
    void invalidatePT( void );

    /**
     * @brief  Print out the contents of a PTE to the printk buffer
     *
     * @param[in] i_label  string to display along with the PTE data
     * @param[in] i_pteAddr  pointer to the PTE to display
     * @param[in] i_verbose  true=break out all attributes, false=abbreviated output
     */
    void printPTE( const char* i_label,
                   const PageTableEntry* i_pte,
                   bool i_verbose = false );

    /**
     * @brief  Print out the entire Page Table to the printk buffer
     */
    void _printPT( void );

    /**
     * @brief  Return the real address of the page table in memory
     * @return uint64_t  Page Table Address
     */
    uint64_t getAddress( void );

    /**
     * @brief  Return the size of the page table in memory in bytes
     * @return uint64_t  Size of Page Table in bytes
     */
    uint64_t getSize( void );

    /**
     * @brief  Set bits in PTE for the given ACCESS_TYPES
     *
     * @param[out] o_pte  PTE to modify
     * @param[in] i_accessType  Access parameter to apply to PTE
     */
    void setAccessBits( PageTableEntry* o_pte,
                        VmmManager::ACCESS_TYPES i_accessType );

    /**
     * @brief  Convert the bits from a PTE into a ACCESS_TYPES
     *
     * @param[in] i_pte  PTE to examine
     *
     * @return ACCESS_TYPES  Access parameters of given PTE
     */
    VmmManager::ACCESS_TYPES getAccessType( const PageTableEntry* i_pte );

    /**
     * @brief  Fill in default values for the PTE
     *
     * @param[out] o_pte  PTE to modify
     */
    static void setupDefaultPTE( PageTableEntry* o_pte );



    // Allow testcase to see inside
    friend class ptmgrtest;
};

#endif
OpenPOWER on IntegriCloud