1 /* SPDX-License-Identifier: GPL-2.0-only */
6 import "encoding/binary"
13 import "path/filepath"
18 // This program generates 32-bit PAE page tables based on a CSV input file.
19 // By default each PDPTE entry is allocated a PD page such that it's easy
20 // fault in new entries that are 2MiB aligned and size.
22 var iomapFilePtr
= flag
.String("iomap_file", "", "CSV file detailing page table mapping")
23 var ptCFilePtr
= flag
.String("pt_output_c_file", "", "File to write page tables to in C code")
24 var ptBinFilePtr
= flag
.String("pt_output_bin_file", "", "File to write page tables to in binary")
25 var pdptCFilePtr
= flag
.String("pdpt_output_c_file", "", "File to write PDPT to in C code")
26 var pdptBinFilePtr
= flag
.String("pdpt_output_bin_file", "", "File to write PDPT to in binary")
27 var pagesBaseAddress
= flag
.Uint64("metadata_base_address", BASE_ADDR
, "Physical base address where metadata pages allocated from")
29 var generatedCodeLicense
string =
31 * Copyright 2018 Generated Code
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. The name of the author may not be used to endorse or promote products
42 * derived from this software without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` + "``" + `AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72 SIZE_4KiB
= uint64(1 << 12)
73 MASK_4KiB
= SIZE_4KiB
- 1
74 SIZE_2MiB
= uint64(1 << 21)
75 MASK_2MiB
= SIZE_2MiB
- 1
77 // This is a fake physical address for doing fixups when loading
78 // the page tables. There's room for 4096 4KiB physical PD or PTE
79 // tables. Anything with the present bit set will be pointing to an
80 // offset based on this address. At runtime the entries will be fixed up
81 BASE_ADDR
= uint64(0xaa000000)
83 // Size of PD and PT structures
84 METADATA_TABLE_SIZE
= 4096
86 PDPTE_PRES
= uint64(1 << 0)
87 PDPTE_PWT
= uint64(1 << 3)
88 PDPTE_PCD
= uint64(1 << 4)
90 PDE_PRES
= uint64(1 << 0)
91 PDE_RW
= uint64(1 << 1)
92 PDE_US
= uint64(1 << 2)
93 PDE_PWT
= uint64(1 << 3)
94 PDE_PCD
= uint64(1 << 4)
95 PDE_A
= uint64(1 << 5)
96 PDE_D
= uint64(1 << 6) // only valid with PS=1
97 PDE_PS
= uint64(1 << 7)
98 PDE_G
= uint64(1 << 8) // only valid with PS=1
99 PDE_PAT
= uint64(1 << 12) // only valid with PS=1
100 PDE_XD
= uint64(1 << 63)
102 PTE_PRES
= uint64(1 << 0)
103 PTE_RW
= uint64(1 << 1)
104 PTE_US
= uint64(1 << 2)
105 PTE_PWT
= uint64(1 << 3)
106 PTE_PCD
= uint64(1 << 4)
107 PTE_A
= uint64(1 << 5)
108 PTE_D
= uint64(1 << 6)
109 PTE_PAT
= uint64(1 << 7)
110 PTE_G
= uint64(1 << 8)
111 PTE_XD
= uint64(1 << 63)
123 // Different 'writers' implement this interface.
124 type pageTableEntryWriter
interface {
125 WritePageEntry(data
interface{}) error
128 // The full page objects, page directories and page tables, implement this
129 // interface to write their entire contents out
130 type pageTableWriter
interface {
131 WritePage(wr pageTableEntryWriter
) error
134 type binaryWriter
struct {
138 func (bw
*binaryWriter
) WritePageEntry(data
interface{}) error
{
139 return binary
.Write(bw
.wr
, binary
.LittleEndian
, data
)
142 type cWriter
struct {
149 func newCWriter(wr io
.Writer
, name
string, nr_entries
uint) *cWriter
{
150 cw
:= &cWriter
{wr
: wr
, name
: name
, totalEntries
: nr_entries
}
154 func (cw
*cWriter
) WritePageEntry(data
interface{}) error
{
158 entry
, ok
:= data
.(uint64)
160 return fmt
.Errorf("entry not uint64 %T", data
)
163 if cw
.currentIndex
== 0 {
164 if _
, err
:= fmt
.Fprint(cw
.wr
, generatedCodeLicense
); err
!= nil {
167 if _
, err
:= fmt
.Fprintf(cw
.wr
, "/* Generated by:\n util/x86/%s %s\n */\n",
168 filepath
.Base(os
.Args
[0]),
169 strings
.Join(os
.Args
[1:], " ")); err
!= nil {
172 includes
:= []string{
175 for _
, l
:= range includes
{
176 if _
, err
:= fmt
.Fprintf(cw
.wr
, "#include <%s>\n", l
); err
!= nil {
181 if _
, err
:= fmt
.Fprintf(cw
.wr
, "uint64_t %s[] = {\n", cw
.name
); err
!= nil {
186 if cw
.currentIndex%NUM_PTE
== 0 {
188 page_num
:= cw
.currentIndex
/ NUM_PTE
189 if _
, err
:= fmt
.Fprintf(cw
.wr
, "\t/* Page %d */\n", page_num
); err
!= nil {
194 // filter out 0 entries
195 if entry
!= 0 || doPrint
{
196 _
, err
:= fmt
.Fprintf(cw
.wr
, "\t[%d] = %#016xULL,\n", cw
.currentIndex
, entry
)
204 if cw
.currentIndex
== cw
.totalEntries
{
205 if _
, err
:= fmt
.Fprintln(cw
.wr
, "};"); err
!= nil {
213 // This map represents what the IA32_PAT MSR should be at runtime. The indices
214 // are what the linux kernel uses. Reserved entries are not used.
215 // 0 WB : _PAGE_CACHE_MODE_WB
216 // 1 WC : _PAGE_CACHE_MODE_WC
217 // 2 UC-: _PAGE_CACHE_MODE_UC_MINUS
218 // 3 UC : _PAGE_CACHE_MODE_UC
220 // 5 WP : _PAGE_CACHE_MODE_WP
222 // 7 WT : _PAGE_CACHE_MODE_WT
223 // In order to use WP and WC then the IA32_PAT MSR needs to be updated
224 // as these are not the power on reset values.
225 var patMsrIndexByType
= map[uint]uint{
234 type addressRange
struct {
241 type addrRangeMerge
func(a
, b
*addressRange
) bool
243 func (ar
*addressRange
) Size() uint64 {
244 return ar
.end
- ar
.begin
247 func (ar
*addressRange
) Base() uint64 {
251 func (ar
*addressRange
) Pat() uint {
255 func (ar
*addressRange
) Nx() bool {
259 func (ar
*addressRange
) String() string {
266 return fmt
.Sprintf("%016x -- %016x %s %s", ar
.begin
, ar
.end
, patTypeToString(ar
.pat
), nx
)
269 type pageTableEntry
struct {
274 func (pte
*pageTableEntry
) Encode() uint64 {
275 return pte
.physAddr | pte
.flags
278 func ptePatFlags(base
uint64, pat
uint) uint64 {
279 idx
, ok
:= patMsrIndexByType
[pat
]
280 patStr
, _
:= patTypesToString
[pat
]
283 log
.Fatalf("Invalid pat entry for page %x: %s\n", base
, patStr
)
294 return PTE_PCD | PTE_PWT
298 return PTE_PAT | PTE_PWT
300 return PTE_PAT | PTE_PCD
302 return PTE_PAT | PTE_PCD | PTE_PWT
305 log
.Fatalf("Invalid PAT index %d for PTE %x %s\n", idx
, base
, patStr
)
309 func (pte
*pageTableEntry
) SetMapping(base
uint64, pat
uint, nx
bool) {
310 // Present and accessed
311 pte
.flags |
= PTE_PRES | PTE_A
313 // Non write protected entries mark as writable and dirty
323 pte
.flags |
= ptePatFlags(base
, pat
)
327 type pageTable
struct {
328 ptes
[NUM_PTE
]pageTableEntry
331 func (pt
*pageTable
) WritePage(wr pageTableEntryWriter
) error
{
332 for i
:= range pt
.ptes
{
334 err
:= wr
.WritePageEntry(pte
.Encode())
342 type pageDirectoryEntry
struct {
348 func (pde
*pageDirectoryEntry
) Encode() uint64 {
349 return pde
.physAddr | pde
.flags
352 func pdeTablePatFlags(pat
uint) uint64 {
353 idx
, ok
:= patMsrIndexByType
[pat
]
354 patStr
, _
:= patTypesToString
[pat
]
357 log
.Fatalf("Invalid pat entry for PDE page table %s\n", patStr
)
368 return PDE_PCD | PDE_PWT
371 log
.Fatalf("Invalid PAT index %d for PDE page table %s\n", idx
, patStr
)
375 func pdeLargePatFlags(base
uint64, pat
uint) uint64 {
376 idx
, ok
:= patMsrIndexByType
[pat
]
377 patStr
, _
:= patTypesToString
[pat
]
380 log
.Fatalf("Invalid pat entry for large page %x: %s\n", base
, patStr
)
391 return PDE_PCD | PDE_PWT
395 return PDE_PAT | PDE_PWT
397 return PDE_PAT | PDE_PCD
399 return PDE_PAT | PDE_PCD | PDE_PWT
402 log
.Fatalf("Invalid PAT index %d for PDE %x %s\n", idx
, base
, patStr
)
406 func (pde
*pageDirectoryEntry
) SetPageTable(pt_addr
uint64, pat
uint) {
407 // Set writable for whole region covered by page table. Individual
408 // ptes will have the correct writability flags
409 pde
.flags |
= PDE_PRES | PDE_A | PDE_RW
411 pde
.flags |
= pdeTablePatFlags(pat
)
413 pde
.physAddr
= pt_addr
416 func (pde
*pageDirectoryEntry
) SetMapping(base
uint64, pat
uint, nx
bool) {
417 // Present, accessed, and large
418 pde
.flags |
= PDE_PRES | PDE_A | PDE_PS
420 // Non write protected entries mark as writable and dirty
430 pde
.flags |
= pdeLargePatFlags(base
, pat
)
434 type pageDirectory
struct {
435 pdes
[NUM_PDE
]pageDirectoryEntry
438 func (pd
*pageDirectory
) WritePage(wr pageTableEntryWriter
) error
{
439 for i
:= range pd
.pdes
{
441 err
:= wr
.WritePageEntry(pde
.Encode())
449 type pageDirectoryPointerEntry
struct {
455 func (pdpte
*pageDirectoryPointerEntry
) Encode() uint64 {
456 return pdpte
.physAddr | pdpte
.flags
459 func (pdpte
*pageDirectoryPointerEntry
) Init(addr
uint64, pat
uint) {
460 idx
, ok
:= patMsrIndexByType
[pat
]
462 // Only 2 bits worth of PAT indexing in PDPTE
464 patStr
, _
:= patTypesToString
[pat
]
465 log
.Fatalf("Can't use type '%s' as PDPTE type.\n", patStr
)
468 pdpte
.physAddr
= addr
469 pdpte
.flags
= PDPTE_PRES
475 pdpte
.flags |
= PDPTE_PWT
477 pdpte
.flags |
= PDPTE_PCD
479 pdpte
.flags |
= PDPTE_PCD | PDPTE_PWT
481 log
.Fatalf("Invalid PAT index %d for PDPTE\n", idx
)
485 type addressSpace
struct {
486 ranges
[]*addressRange
487 mergeFunc addrRangeMerge
488 metatdataBaseAddr
uint64
489 pdptes
[NUM_PDPTE
]pageDirectoryPointerEntry
491 page_writers
[]pageTableWriter
494 func (as
*addressSpace
) newPage(pw pageTableWriter
) uint64 {
495 v
:= as
.metatdataBaseAddr
+ METADATA_TABLE_SIZE
*uint64(as
.numMetaPages
)
497 as
.page_writers
= append(as
.page_writers
, pw
)
501 func newAddrSpace(mergeFunc addrRangeMerge
, metatdataBaseAddr
uint64) *addressSpace
{
502 as
:= &addressSpace
{mergeFunc
: mergeFunc
, metatdataBaseAddr
: metatdataBaseAddr
}
503 // Fill in all PDPTEs
504 for i
:= range as
.pdptes
{
505 pdpte
:= &as
.pdptes
[i
]
506 pdpte
.pd
= &pageDirectory
{}
507 // fetch paging structures as WB
508 pdpte
.Init(as
.newPage(pdpte
.pd
), PAT_WB
)
513 func (as
*addressSpace
) deleteEntries(indices
[]int) {
514 // deletions need to be processed in reverse order so as not
515 // delete the wrong entries
516 sort
.Sort(sort
.Reverse(sort
.IntSlice(indices
)))
517 for _
, i
:= range indices
{
518 as
.ranges
= append(as
.ranges
[:i
], as
.ranges
[i
+1:]...)
522 func (as
*addressSpace
) mergeRanges() {
524 var prev
*addressRange
526 for i
, cur
:= range as
.ranges
{
532 // merge previous with current
533 if as
.mergeFunc(prev
, cur
) {
535 toRemove
= append(toRemove
, i
)
541 as
.deleteEntries(toRemove
)
544 type addressRangeSlice
[]*addressRange
546 func (p addressRangeSlice
) Len() int {
550 func (p addressRangeSlice
) Less(i
, j
int) bool {
551 return !p
[i
].After(p
[j
])
554 func (p addressRangeSlice
) Swap(i
, j
int) {
555 p
[i
], p
[j
] = p
[j
], p
[i
]
558 func (as
*addressSpace
) insertRange(r
*addressRange
) {
559 as
.ranges
= append(as
.ranges
, r
)
560 sort
.Sort(addressRangeSlice(as
.ranges
))
563 // Remove complete entries or trim existing ones
564 func (as
*addressSpace
) trimRanges(r
*addressRange
) {
567 // First remove all entries that are completely overlapped
568 for i
, cur
:= range as
.ranges
{
569 if r
.FullyOverlaps(cur
) {
570 toRemove
= append(toRemove
, i
)
574 as
.deleteEntries(toRemove
)
578 // Process partial overlaps
579 for _
, cur
:= range as
.ranges
{
580 // Overlapping may be at beginning, middle, end. Only the
581 // middle overlap needs to create a new range since the
582 // beginning and end overlap can just adjust the current
587 if r
.begin
<= cur
.begin
{
593 if r
.end
>= cur
.end
{
598 // middle overlap. create new entry from the hole
599 // punched in the current entry. There's nothing
600 // further to do after this
606 // current needs new ending
609 ar
= newAddrRange(begin
, end
, pat
, nx
)
620 func (as
*addressSpace
) PrintEntries() {
621 for _
, cur
:= range as
.ranges
{
626 func (as
*addressSpace
) AddRange(r
*addressRange
) {
632 func (as
*addressSpace
) insertMapping(base
uint64, size
uint64, pat
uint, nx
bool) {
633 pdpteIndex
:= (base
>> PDPTE_IDX_SHIFT
) & PDPTE_IDX_MASK
634 pdeIndex
:= (base
>> PDE_IDX_SHIFT
) & PDE_IDX_MASK
635 pteIndex
:= (base
>> PTE_IDX_SHIFT
) & PTE_IDX_MASK
637 pd
:= as
.pdptes
[pdpteIndex
].pd
638 pde
:= &pd
.pdes
[pdeIndex
]
640 if size
== SIZE_2MiB
{
641 pde
.SetMapping(base
, pat
, nx
)
646 pde
.pt
= &pageTable
{}
647 // Fetch paging structures as WB
648 pde
.SetPageTable(as
.newPage(pde
.pt
), PAT_WB
)
651 pte
:= &pde
.pt
.ptes
[pteIndex
]
652 pte
.SetMapping(base
, pat
, nx
)
655 func (as
*addressSpace
) CreatePageTables() {
659 for _
, r
:= range as
.ranges
{
669 mappingSize
:= SIZE_4KiB
671 if (base
&MASK_2MiB
) == 0 && size
>= SIZE_2MiB
{
672 mappingSize
= SIZE_2MiB
678 as
.insertMapping(base
, mappingSize
, pat
, nx
)
685 log
.Printf("%s : %d big %d small\n", r
, numBigEntries
, numSmallEntries
)
689 func (as
*addressSpace
) PageTableSize() uint {
690 return as
.numMetaPages
* METADATA_TABLE_SIZE
693 func (as
*addressSpace
) NumPages() uint {
694 return as
.numMetaPages
697 func (as
*addressSpace
) WritePageTable(ptew pageTableEntryWriter
) error
{
698 for _
, pw
:= range as
.page_writers
{
699 err
:= pw
.WritePage(ptew
)
708 func (as
*addressSpace
) WritePageDirectoryPointerTable(ptew pageTableEntryWriter
) error
{
709 for i
:= range as
.pdptes
{
710 err
:= ptew
.WritePageEntry(as
.pdptes
[i
].Encode())
719 var pat_types_from_str
= map[string]uint{
728 var patTypesToString
= map[uint]string{
737 func openCsvFile(file
string) (*csv
.Reader
, error
) {
738 f
, err
:= os
.Open(file
)
744 csvr
:= csv
.NewReader(f
)
745 csvr
.Comment
= COMMENT_CHAR
746 csvr
.TrimLeadingSpace
= true
750 // After returns true if ar beings at or after other.end.
751 func (ar addressRange
) After(other
*addressRange
) bool {
752 return ar
.begin
>= other
.end
755 func (ar addressRange
) FullyOverlaps(other
*addressRange
) bool {
756 return ar
.begin
<= other
.begin
&& ar
.end
>= other
.end
759 func (ar addressRange
) Overlaps(other
*addressRange
) bool {
760 if other
.end
<= ar
.begin || other
.begin
>= ar
.end
{
766 func MergeByPat(a
, b
*addressRange
) bool {
767 // 'b' is assumed to be following 'a'
768 if a
.end
!= b
.begin
{
779 func MergeByNx(a
, b
*addressRange
) bool {
780 // 'b' is assumed to be following 'a'
781 if a
.end
!= b
.begin
{
792 func MergeByPatNx(a
, b
*addressRange
) bool {
793 return MergeByPat(a
, b
) && MergeByNx(a
, b
)
796 func hexNumber(s
string) (uint64, error
) {
797 return strconv
.ParseUint(strings
.TrimSpace(s
), 0, 0)
800 func patTypeToString(pat
uint) string {
801 return patTypesToString
[pat
]
804 func patTypeFromString(s
string) (uint, error
) {
805 s1
:= strings
.TrimSpace(s
)
806 v
, ok
:= pat_types_from_str
[s1
]
809 return 0, fmt
.Errorf("No PAT type '%s'", s1
)
815 func removeComment(field
, comment
string) string {
816 str_slice
:= strings
.Split(field
, comment
)
817 return strings
.TrimSpace(str_slice
[0])
820 func newAddrRange(begin
, end
uint64, pat
uint, nx
bool) *addressRange
{
821 return &addressRange
{begin
: begin
, end
: end
, pat
: pat
, nx
: nx
}
824 func readRecords(csvr
*csv
.Reader
, as
*addressSpace
) {
827 fields
, err
:= csvr
.Read()
839 log
.Fatal("Need at least 3 fields: begin, end, PAT\n")
842 begin
, err
:= hexNumber(fields
[0])
848 end
, err
:= hexNumber(fields
[1])
854 if begin
&MASK_4KiB
!= 0 {
855 log
.Fatalf("begin %x must be at least 4KiB aligned\n", begin
)
858 if end
&MASK_4KiB
!= 0 {
859 log
.Fatalf("end %x must be at least 4KiB aligned\n", end
)
862 log
.Fatalf("%x must be < %x at record %d\n", begin
, end
, i
)
865 pat
, err
:= patTypeFromString(fields
[2])
873 if len(fields
) > 3 && len(removeComment(fields
[3], string(COMMENT_CHAR
))) > 0 {
877 as
.AddRange(newAddrRange(begin
, end
, pat
, nx
))
884 var ptWriters
[]pageTableEntryWriter
885 var pdptWriters
[]pageTableEntryWriter
887 if *iomapFilePtr
== "" {
888 log
.Fatal("No iomap_file provided.\n")
891 csvr
, err
:= openCsvFile(*iomapFilePtr
)
896 as
:= newAddrSpace(MergeByPatNx
, *pagesBaseAddress
)
897 readRecords(csvr
, as
)
899 log
.Println("Merged address space:")
900 as
.CreatePageTables()
902 log
.Printf("Total Pages of page tables: %d\n", as
.NumPages())
904 log
.Printf("Pages linked using base address of %#x.\n", *pagesBaseAddress
)
906 if *ptCFilePtr
!= "" {
907 f
, err
:= os
.Create(*ptCFilePtr
)
912 bwr
:= bufio
.NewWriter(f
)
914 cw
:= newCWriter(bwr
, "page_tables", as
.NumPages()*NUM_PTE
)
915 ptWriters
= append(ptWriters
, cw
)
918 if *ptBinFilePtr
!= "" {
919 f
, err
:= os
.Create(*ptBinFilePtr
)
924 bwr
:= bufio
.NewWriter(f
)
926 bw
:= &binaryWriter
{wr
: bwr
}
927 ptWriters
= append(ptWriters
, bw
)
930 if *pdptCFilePtr
!= "" {
931 f
, err
:= os
.Create(*pdptCFilePtr
)
936 bwr
:= bufio
.NewWriter(f
)
938 cw
:= newCWriter(bwr
, "pdptes", NUM_PDPTE
)
939 pdptWriters
= append(pdptWriters
, cw
)
942 if *pdptBinFilePtr
!= "" {
943 f
, err
:= os
.Create(*pdptBinFilePtr
)
948 bwr
:= bufio
.NewWriter(f
)
950 bw
:= &binaryWriter
{wr
: bwr
}
951 pdptWriters
= append(pdptWriters
, bw
)
954 // Write out page tables
955 for _
, w
:= range ptWriters
{
956 err
= as
.WritePageTable(w
)
963 for _
, w
:= range pdptWriters
{
964 err
= as
.WritePageDirectoryPointerTable(w
)