In order to help the reader understand where things are generated from add a comment string that is composed of the command line used to generate the files. BUG=b:72728953 Change-Id: I1b93923f8b08192448ab19226fd27661cc09e853 Signed-off-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: https://review.coreboot.org/25834 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Furquan Shaikh <furquan@google.com> Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
		
			
				
	
	
		
			983 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			983 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
 * This file is part of the coreboot project.
 | 
						|
 *
 | 
						|
 * Copyright 2018 Google LLC
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation; version 2 of the License.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 */
 | 
						|
 | 
						|
package main
 | 
						|
 | 
						|
import "bufio"
 | 
						|
import "encoding/binary"
 | 
						|
import "encoding/csv"
 | 
						|
import "flag"
 | 
						|
import "fmt"
 | 
						|
import "io"
 | 
						|
import "log"
 | 
						|
import "os"
 | 
						|
import "path/filepath"
 | 
						|
import "sort"
 | 
						|
import "strconv"
 | 
						|
import "strings"
 | 
						|
 | 
						|
// This program generates 32-bit PAE page tables based on a CSV input file.
 | 
						|
// By default each PDPTE entry is allocated a PD page such that it's easy
 | 
						|
// fault in new entries that are 2MiB aligned and size.
 | 
						|
 | 
						|
var iomapFilePtr = flag.String("iomap_file", "", "CSV file detailing page table mapping")
 | 
						|
var ptCFilePtr = flag.String("pt_output_c_file", "", "File to write page tables to in C code")
 | 
						|
var ptBinFilePtr = flag.String("pt_output_bin_file", "", "File to write page tables to in binary")
 | 
						|
var pdptCFilePtr = flag.String("pdpt_output_c_file", "", "File to write PDPT to in C code")
 | 
						|
var pdptBinFilePtr = flag.String("pdpt_output_bin_file", "", "File to write PDPT to in binary")
 | 
						|
var pagesBaseAddress = flag.Uint64("metadata_base_address", BASE_ADDR, "Physical base address where metadata pages allocated from")
 | 
						|
 | 
						|
var generatedCodeLicense string =
 | 
						|
`/*
 | 
						|
 * Copyright 2018 Generated Code
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 * 3. The name of the author may not be used to endorse or promote products
 | 
						|
 *    derived from this software without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` + "``" + `AS IS'' AND
 | 
						|
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
						|
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
 * SUCH DAMAGE.
 | 
						|
 */
 | 
						|
`
 | 
						|
 | 
						|
const (
 | 
						|
	PAT_UC      = 0
 | 
						|
	PAT_WC      = 1
 | 
						|
	PAT_WT      = 4
 | 
						|
	PAT_WP      = 5
 | 
						|
	PAT_WB      = 6
 | 
						|
	PAT_UCMINUS = 7
 | 
						|
 | 
						|
	COMMENT_CHAR = '#'
 | 
						|
 | 
						|
	NUM_PDPTE = 4
 | 
						|
	NUM_PDE   = 512
 | 
						|
	NUM_PTE   = 512
 | 
						|
 | 
						|
	SIZE_4KiB = uint64(1 << 12)
 | 
						|
	MASK_4KiB = SIZE_4KiB - 1
 | 
						|
	SIZE_2MiB = uint64(1 << 21)
 | 
						|
	MASK_2MiB = SIZE_2MiB - 1
 | 
						|
 | 
						|
	// This is a fake physical address for doing fixups when loading
 | 
						|
	// the page tables. There's room for 4096 4KiB physical PD or PTE
 | 
						|
	// tables. Anything with the present bit set will be pointing to an
 | 
						|
	// offset based on this address. At runtime the entries will be fixed up
 | 
						|
	BASE_ADDR = uint64(0xaa000000)
 | 
						|
 | 
						|
	// Size of PD and PT structures
 | 
						|
	METADATA_TABLE_SIZE = 4096
 | 
						|
 | 
						|
	PDPTE_PRES = uint64(1 << 0)
 | 
						|
	PDPTE_PWT  = uint64(1 << 3)
 | 
						|
	PDPTE_PCD  = uint64(1 << 4)
 | 
						|
 | 
						|
	PDE_PRES = uint64(1 << 0)
 | 
						|
	PDE_RW   = uint64(1 << 1)
 | 
						|
	PDE_US   = uint64(1 << 2)
 | 
						|
	PDE_PWT  = uint64(1 << 3)
 | 
						|
	PDE_PCD  = uint64(1 << 4)
 | 
						|
	PDE_A    = uint64(1 << 5)
 | 
						|
	PDE_D    = uint64(1 << 6) // only valid with PS=1
 | 
						|
	PDE_PS   = uint64(1 << 7)
 | 
						|
	PDE_G    = uint64(1 << 8)  // only valid with PS=1
 | 
						|
	PDE_PAT  = uint64(1 << 12) // only valid with PS=1
 | 
						|
	PDE_XD   = uint64(1 << 63)
 | 
						|
 | 
						|
	PTE_PRES = uint64(1 << 0)
 | 
						|
	PTE_RW   = uint64(1 << 1)
 | 
						|
	PTE_US   = uint64(1 << 2)
 | 
						|
	PTE_PWT  = uint64(1 << 3)
 | 
						|
	PTE_PCD  = uint64(1 << 4)
 | 
						|
	PTE_A    = uint64(1 << 5)
 | 
						|
	PTE_D    = uint64(1 << 6)
 | 
						|
	PTE_PAT  = uint64(1 << 7)
 | 
						|
	PTE_G    = uint64(1 << 8)
 | 
						|
	PTE_XD   = uint64(1 << 63)
 | 
						|
 | 
						|
	PDPTE_IDX_SHIFT = 30
 | 
						|
	PDPTE_IDX_MASK  = 0x3
 | 
						|
 | 
						|
	PDE_IDX_SHIFT = 21
 | 
						|
	PDE_IDX_MASK  = 0x1ff
 | 
						|
 | 
						|
	PTE_IDX_SHIFT = 12
 | 
						|
	PTE_IDX_MASK  = 0x1ff
 | 
						|
)
 | 
						|
 | 
						|
// Different 'writers' implement this interface.
 | 
						|
type pageTableEntryWriter interface {
 | 
						|
	WritePageEntry(data interface{}) error
 | 
						|
}
 | 
						|
 | 
						|
// The full page objects, page directories and page tables, implement this
 | 
						|
// interface to write their entire contents out
 | 
						|
type pageTableWriter interface {
 | 
						|
	WritePage(wr pageTableEntryWriter) error
 | 
						|
}
 | 
						|
 | 
						|
type binaryWriter struct {
 | 
						|
	wr io.Writer
 | 
						|
}
 | 
						|
 | 
						|
func (bw *binaryWriter) WritePageEntry(data interface{}) error {
 | 
						|
	return binary.Write(bw.wr, binary.LittleEndian, data)
 | 
						|
}
 | 
						|
 | 
						|
type cWriter struct {
 | 
						|
	name         string
 | 
						|
	wr           io.Writer
 | 
						|
	totalEntries uint
 | 
						|
	currentIndex uint
 | 
						|
}
 | 
						|
 | 
						|
func newCWriter(wr io.Writer, name string, nr_entries uint) *cWriter {
 | 
						|
	cw := &cWriter{wr: wr, name: name, totalEntries: nr_entries}
 | 
						|
	return cw
 | 
						|
}
 | 
						|
 | 
						|
func (cw *cWriter) WritePageEntry(data interface{}) error {
 | 
						|
	var entry uint64
 | 
						|
	doPrint := false
 | 
						|
 | 
						|
	entry, ok := data.(uint64)
 | 
						|
	if !ok {
 | 
						|
		return fmt.Errorf("entry not uint64 %T", data)
 | 
						|
	}
 | 
						|
 | 
						|
	if cw.currentIndex == 0 {
 | 
						|
		if _, err := fmt.Fprint(cw.wr, generatedCodeLicense); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if _, err := fmt.Fprintf(cw.wr, "/* Generated by:\n  util/x86/%s %s\n */\n",
 | 
						|
			filepath.Base(os.Args[0]),
 | 
						|
			strings.Join(os.Args[1:], " ")); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		includes := []string{
 | 
						|
			"stdint.h",
 | 
						|
		}
 | 
						|
		for _, l := range includes {
 | 
						|
			if _, err := fmt.Fprintf(cw.wr, "#include <%s>\n", l); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if _, err := fmt.Fprintf(cw.wr, "uint64_t %s[] = {\n", cw.name); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if cw.currentIndex%NUM_PTE == 0 {
 | 
						|
		doPrint = true
 | 
						|
		page_num := cw.currentIndex / NUM_PTE
 | 
						|
		if _, err := fmt.Fprintf(cw.wr, "\t/* Page %d */\n", page_num); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// filter out 0 entries
 | 
						|
	if entry != 0 || doPrint {
 | 
						|
		_, err := fmt.Fprintf(cw.wr, "\t[%d] = %#016xULL,\n", cw.currentIndex, entry)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	cw.currentIndex += 1
 | 
						|
 | 
						|
	if cw.currentIndex == cw.totalEntries {
 | 
						|
		if _, err := fmt.Fprintln(cw.wr, "};"); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// This map represents what the IA32_PAT MSR should be at runtime. The indicies
 | 
						|
// are what the linux kernel uses. Reserved entries are not used.
 | 
						|
//  0    WB : _PAGE_CACHE_MODE_WB
 | 
						|
//  1    WC : _PAGE_CACHE_MODE_WC
 | 
						|
//  2    UC-: _PAGE_CACHE_MODE_UC_MINUS
 | 
						|
//  3    UC : _PAGE_CACHE_MODE_UC
 | 
						|
//  4    WB : Reserved
 | 
						|
//  5    WP : _PAGE_CACHE_MODE_WP
 | 
						|
//  6    UC-: Reserved
 | 
						|
//  7    WT : _PAGE_CACHE_MODE_WT
 | 
						|
// In order to use WP and WC then the IA32_PAT MSR needs to be updated
 | 
						|
// as these are not the power on reset values.
 | 
						|
var patMsrIndexByType = map[uint]uint{
 | 
						|
	PAT_WB:      0,
 | 
						|
	PAT_WC:      1,
 | 
						|
	PAT_UCMINUS: 2,
 | 
						|
	PAT_UC:      3,
 | 
						|
	PAT_WP:      5,
 | 
						|
	PAT_WT:      7,
 | 
						|
}
 | 
						|
 | 
						|
type addressRange struct {
 | 
						|
	begin uint64
 | 
						|
	end   uint64
 | 
						|
	pat   uint
 | 
						|
	nx    bool
 | 
						|
}
 | 
						|
 | 
						|
type addrRangeMerge func(a, b *addressRange) bool
 | 
						|
 | 
						|
func (ar *addressRange) Size() uint64 {
 | 
						|
	return ar.end - ar.begin
 | 
						|
}
 | 
						|
 | 
						|
func (ar *addressRange) Base() uint64 {
 | 
						|
	return ar.begin
 | 
						|
}
 | 
						|
 | 
						|
func (ar *addressRange) Pat() uint {
 | 
						|
	return ar.pat
 | 
						|
}
 | 
						|
 | 
						|
func (ar *addressRange) Nx() bool {
 | 
						|
	return ar.nx
 | 
						|
}
 | 
						|
 | 
						|
func (ar *addressRange) String() string {
 | 
						|
	var nx string
 | 
						|
	if ar.nx {
 | 
						|
		nx = "NX"
 | 
						|
	} else {
 | 
						|
		nx = "  "
 | 
						|
	}
 | 
						|
	return fmt.Sprintf("%016x -- %016x %s %s", ar.begin, ar.end, patTypeToString(ar.pat), nx)
 | 
						|
}
 | 
						|
 | 
						|
type pageTableEntry struct {
 | 
						|
	physAddr uint64
 | 
						|
	flags    uint64
 | 
						|
}
 | 
						|
 | 
						|
func (pte *pageTableEntry) Encode() uint64 {
 | 
						|
	return pte.physAddr | pte.flags
 | 
						|
}
 | 
						|
 | 
						|
func ptePatFlags(base uint64, pat uint) uint64 {
 | 
						|
	idx, ok := patMsrIndexByType[pat]
 | 
						|
	patStr, _ := patTypesToString[pat]
 | 
						|
 | 
						|
	if !ok {
 | 
						|
		log.Fatalf("Invalid pat entry for page %x: %s\n", base, patStr)
 | 
						|
	}
 | 
						|
 | 
						|
	switch idx {
 | 
						|
	case 0:
 | 
						|
		return 0
 | 
						|
	case 1:
 | 
						|
		return PTE_PWT
 | 
						|
	case 2:
 | 
						|
		return PTE_PCD
 | 
						|
	case 3:
 | 
						|
		return PTE_PCD | PTE_PWT
 | 
						|
	case 4:
 | 
						|
		return PTE_PAT
 | 
						|
	case 5:
 | 
						|
		return PTE_PAT | PTE_PWT
 | 
						|
	case 6:
 | 
						|
		return PTE_PAT | PTE_PCD
 | 
						|
	case 7:
 | 
						|
		return PTE_PAT | PTE_PCD | PTE_PWT
 | 
						|
	}
 | 
						|
 | 
						|
	log.Fatalf("Invalid PAT index %d for PTE %x %s\n", idx, base, patStr)
 | 
						|
	return 0
 | 
						|
}
 | 
						|
 | 
						|
func (pte *pageTableEntry) SetMapping(base uint64, pat uint, nx bool) {
 | 
						|
	// Present and accessed
 | 
						|
	pte.flags |= PTE_PRES | PTE_A
 | 
						|
 | 
						|
	// Non write protected entries mark as writable and dirty
 | 
						|
	if pat != PAT_WP {
 | 
						|
		pte.flags |= PTE_RW
 | 
						|
		pte.flags |= PTE_D
 | 
						|
	}
 | 
						|
 | 
						|
	if nx {
 | 
						|
		pte.flags |= PTE_XD
 | 
						|
	}
 | 
						|
 | 
						|
	pte.flags |= ptePatFlags(base, pat)
 | 
						|
	pte.physAddr = base
 | 
						|
}
 | 
						|
 | 
						|
type pageTable struct {
 | 
						|
	ptes [NUM_PTE]pageTableEntry
 | 
						|
}
 | 
						|
 | 
						|
func (pt *pageTable) WritePage(wr pageTableEntryWriter) error {
 | 
						|
	for i := range pt.ptes {
 | 
						|
		pte := &pt.ptes[i]
 | 
						|
		err := wr.WritePageEntry(pte.Encode())
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
type pageDirectoryEntry struct {
 | 
						|
	physAddr uint64
 | 
						|
	flags    uint64
 | 
						|
	pt       *pageTable
 | 
						|
}
 | 
						|
 | 
						|
func (pde *pageDirectoryEntry) Encode() uint64 {
 | 
						|
	return pde.physAddr | pde.flags
 | 
						|
}
 | 
						|
 | 
						|
func pdeTablePatFlags(pat uint) uint64 {
 | 
						|
	idx, ok := patMsrIndexByType[pat]
 | 
						|
	patStr, _ := patTypesToString[pat]
 | 
						|
 | 
						|
	if !ok || idx >= 4 {
 | 
						|
		log.Fatalf("Invalid pat entry for PDE page table %s\n", patStr)
 | 
						|
	}
 | 
						|
 | 
						|
	switch idx {
 | 
						|
	case 0:
 | 
						|
		return 0
 | 
						|
	case 1:
 | 
						|
		return PDE_PWT
 | 
						|
	case 2:
 | 
						|
		return PDE_PCD
 | 
						|
	case 3:
 | 
						|
		return PDE_PCD | PDE_PWT
 | 
						|
	}
 | 
						|
 | 
						|
	log.Fatalf("Invalid PAT index %d for PDE page table %s\n", idx, patStr)
 | 
						|
	return 0
 | 
						|
}
 | 
						|
 | 
						|
func pdeLargePatFlags(base uint64, pat uint) uint64 {
 | 
						|
	idx, ok := patMsrIndexByType[pat]
 | 
						|
	patStr, _ := patTypesToString[pat]
 | 
						|
 | 
						|
	if !ok {
 | 
						|
		log.Fatalf("Invalid pat entry for large page %x: %s\n", base, patStr)
 | 
						|
	}
 | 
						|
 | 
						|
	switch idx {
 | 
						|
	case 0:
 | 
						|
		return 0
 | 
						|
	case 1:
 | 
						|
		return PDE_PWT
 | 
						|
	case 2:
 | 
						|
		return PDE_PCD
 | 
						|
	case 3:
 | 
						|
		return PDE_PCD | PDE_PWT
 | 
						|
	case 4:
 | 
						|
		return PDE_PAT
 | 
						|
	case 5:
 | 
						|
		return PDE_PAT | PDE_PWT
 | 
						|
	case 6:
 | 
						|
		return PDE_PAT | PDE_PCD
 | 
						|
	case 7:
 | 
						|
		return PDE_PAT | PDE_PCD | PDE_PWT
 | 
						|
	}
 | 
						|
 | 
						|
	log.Fatalf("Invalid PAT index %d for PDE %x %s\n", idx, base, patStr)
 | 
						|
	return 0
 | 
						|
}
 | 
						|
 | 
						|
func (pde *pageDirectoryEntry) SetPageTable(pt_addr uint64, pat uint) {
 | 
						|
	// Set writable for whole region covered by page table. Individual
 | 
						|
	// ptes will have the correct writability flags
 | 
						|
	pde.flags |= PDE_PRES | PDE_A | PDE_RW
 | 
						|
 | 
						|
	pde.flags |= pdeTablePatFlags(pat)
 | 
						|
 | 
						|
	pde.physAddr = pt_addr
 | 
						|
}
 | 
						|
 | 
						|
func (pde *pageDirectoryEntry) SetMapping(base uint64, pat uint, nx bool) {
 | 
						|
	// Present, accessed, and large
 | 
						|
	pde.flags |= PDE_PRES | PDE_A | PDE_PS
 | 
						|
 | 
						|
	// Non write protected entries mark as writable and dirty
 | 
						|
	if pat != PAT_WP {
 | 
						|
		pde.flags |= PDE_RW
 | 
						|
		pde.flags |= PDE_D
 | 
						|
	}
 | 
						|
 | 
						|
	if nx {
 | 
						|
		pde.flags |= PDE_XD
 | 
						|
	}
 | 
						|
 | 
						|
	pde.flags |= pdeLargePatFlags(base, pat)
 | 
						|
	pde.physAddr = base
 | 
						|
}
 | 
						|
 | 
						|
type pageDirectory struct {
 | 
						|
	pdes [NUM_PDE]pageDirectoryEntry
 | 
						|
}
 | 
						|
 | 
						|
func (pd *pageDirectory) WritePage(wr pageTableEntryWriter) error {
 | 
						|
	for i := range pd.pdes {
 | 
						|
		pde := &pd.pdes[i]
 | 
						|
		err := wr.WritePageEntry(pde.Encode())
 | 
						|
		if err != nil {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
type pageDirectoryPointerEntry struct {
 | 
						|
	physAddr uint64
 | 
						|
	flags    uint64
 | 
						|
	pd       *pageDirectory
 | 
						|
}
 | 
						|
 | 
						|
func (pdpte *pageDirectoryPointerEntry) Encode() uint64 {
 | 
						|
	return pdpte.physAddr | pdpte.flags
 | 
						|
}
 | 
						|
 | 
						|
func (pdpte *pageDirectoryPointerEntry) Init(addr uint64, pat uint) {
 | 
						|
	idx, ok := patMsrIndexByType[pat]
 | 
						|
 | 
						|
	// Only 2 bits worth of PAT indexing in PDPTE
 | 
						|
	if !ok || idx >= 4 {
 | 
						|
		patStr, _ := patTypesToString[pat]
 | 
						|
		log.Fatalf("Can't use type '%s' as PDPTE type.\n", patStr)
 | 
						|
	}
 | 
						|
 | 
						|
	pdpte.physAddr = addr
 | 
						|
	pdpte.flags = PDPTE_PRES
 | 
						|
 | 
						|
	switch idx {
 | 
						|
	case 0:
 | 
						|
		pdpte.flags |= 0
 | 
						|
	case 1:
 | 
						|
		pdpte.flags |= PDPTE_PWT
 | 
						|
	case 2:
 | 
						|
		pdpte.flags |= PDPTE_PCD
 | 
						|
	case 3:
 | 
						|
		pdpte.flags |= PDPTE_PCD | PDPTE_PWT
 | 
						|
	default:
 | 
						|
		log.Fatalf("Invalid PAT index %d for PDPTE\n", idx)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type addressSpace struct {
 | 
						|
	ranges            []*addressRange
 | 
						|
	mergeFunc         addrRangeMerge
 | 
						|
	metatdataBaseAddr uint64
 | 
						|
	pdptes            [NUM_PDPTE]pageDirectoryPointerEntry
 | 
						|
	numMetaPages      uint
 | 
						|
	page_writers      []pageTableWriter
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) newPage(pw pageTableWriter) uint64 {
 | 
						|
	v := as.metatdataBaseAddr + METADATA_TABLE_SIZE*uint64(as.numMetaPages)
 | 
						|
	as.numMetaPages += 1
 | 
						|
	as.page_writers = append(as.page_writers, pw)
 | 
						|
	return v
 | 
						|
}
 | 
						|
 | 
						|
func newAddrSpace(mergeFunc addrRangeMerge, metatdataBaseAddr uint64) *addressSpace {
 | 
						|
	as := &addressSpace{mergeFunc: mergeFunc, metatdataBaseAddr: metatdataBaseAddr}
 | 
						|
	// Fill in all PDPTEs
 | 
						|
	for i := range as.pdptes {
 | 
						|
		pdpte := &as.pdptes[i]
 | 
						|
		pdpte.pd = &pageDirectory{}
 | 
						|
		// fetch paging structures as WB
 | 
						|
		pdpte.Init(as.newPage(pdpte.pd), PAT_WB)
 | 
						|
	}
 | 
						|
	return as
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) deleteEntries(indicies []int) {
 | 
						|
	// deletions need to be processed in reverse order so as not
 | 
						|
	// delete the wrong entries
 | 
						|
	sort.Sort(sort.Reverse(sort.IntSlice(indicies)))
 | 
						|
	for _, i := range indicies {
 | 
						|
		as.ranges = append(as.ranges[:i], as.ranges[i+1:]...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) mergeRanges() {
 | 
						|
	var toRemove []int
 | 
						|
	var prev *addressRange
 | 
						|
 | 
						|
	for i, cur := range as.ranges {
 | 
						|
		if prev == nil {
 | 
						|
			prev = cur
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		// merge previous with current
 | 
						|
		if as.mergeFunc(prev, cur) {
 | 
						|
			prev.end = cur.end
 | 
						|
			toRemove = append(toRemove, i)
 | 
						|
			cur = prev
 | 
						|
		}
 | 
						|
		prev = cur
 | 
						|
	}
 | 
						|
 | 
						|
	as.deleteEntries(toRemove)
 | 
						|
}
 | 
						|
 | 
						|
type addressRangeSlice []*addressRange
 | 
						|
 | 
						|
func (p addressRangeSlice) Len() int {
 | 
						|
	return len(p)
 | 
						|
}
 | 
						|
 | 
						|
func (p addressRangeSlice) Less(i, j int) bool {
 | 
						|
	return !p[i].After(p[j])
 | 
						|
}
 | 
						|
 | 
						|
func (p addressRangeSlice) Swap(i, j int) {
 | 
						|
	p[i], p[j] = p[j], p[i]
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) insertRange(r *addressRange) {
 | 
						|
	as.ranges = append(as.ranges, r)
 | 
						|
	sort.Sort(addressRangeSlice(as.ranges))
 | 
						|
}
 | 
						|
 | 
						|
// Remove complete entries or trim existing ones
 | 
						|
func (as *addressSpace) trimRanges(r *addressRange) {
 | 
						|
	var toRemove []int
 | 
						|
 | 
						|
	// First remove all entries that are completely overlapped
 | 
						|
	for i, cur := range as.ranges {
 | 
						|
		if r.FullyOverlaps(cur) {
 | 
						|
			toRemove = append(toRemove, i)
 | 
						|
			continue
 | 
						|
		}
 | 
						|
	}
 | 
						|
	as.deleteEntries(toRemove)
 | 
						|
 | 
						|
	var ar *addressRange
 | 
						|
 | 
						|
	// Process partial overlaps
 | 
						|
	for _, cur := range as.ranges {
 | 
						|
		// Overlapping may be at beginning, middle, end. Only the
 | 
						|
		// middle overlap needs to create a new range since the
 | 
						|
		// beginning and end overlap can just adjust the current
 | 
						|
		// range.
 | 
						|
		if r.Overlaps(cur) {
 | 
						|
 | 
						|
			// beginning overlap
 | 
						|
			if r.begin <= cur.begin {
 | 
						|
				cur.begin = r.end
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			// end overlap
 | 
						|
			if r.end >= cur.end {
 | 
						|
				cur.end = r.begin
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			// middle overlap. create new entry from the hole
 | 
						|
			// punched in the current entry. There's nothing
 | 
						|
			// further to do after this
 | 
						|
			begin := r.end
 | 
						|
			end := cur.end
 | 
						|
			pat := cur.pat
 | 
						|
			nx := cur.nx
 | 
						|
 | 
						|
			// current needs new ending
 | 
						|
			cur.end = r.begin
 | 
						|
 | 
						|
			ar = newAddrRange(begin, end, pat, nx)
 | 
						|
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ar != nil {
 | 
						|
		as.insertRange(ar)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) PrintEntries() {
 | 
						|
	for _, cur := range as.ranges {
 | 
						|
		log.Println(cur)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) AddRange(r *addressRange) {
 | 
						|
	as.trimRanges(r)
 | 
						|
	as.insertRange(r)
 | 
						|
	as.mergeRanges()
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) insertMapping(base uint64, size uint64, pat uint, nx bool) {
 | 
						|
	pdpteIndex := (base >> PDPTE_IDX_SHIFT) & PDPTE_IDX_MASK
 | 
						|
	pdeIndex := (base >> PDE_IDX_SHIFT) & PDE_IDX_MASK
 | 
						|
	pteIndex := (base >> PTE_IDX_SHIFT) & PTE_IDX_MASK
 | 
						|
 | 
						|
	pd := as.pdptes[pdpteIndex].pd
 | 
						|
	pde := &pd.pdes[pdeIndex]
 | 
						|
 | 
						|
	if size == SIZE_2MiB {
 | 
						|
		pde.SetMapping(base, pat, nx)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if pde.pt == nil {
 | 
						|
		pde.pt = &pageTable{}
 | 
						|
		// Fetch paging structures as WB
 | 
						|
		pde.SetPageTable(as.newPage(pde.pt), PAT_WB)
 | 
						|
	}
 | 
						|
 | 
						|
	pte := &pde.pt.ptes[pteIndex]
 | 
						|
	pte.SetMapping(base, pat, nx)
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) CreatePageTables() {
 | 
						|
	var size uint64
 | 
						|
	var base uint64
 | 
						|
 | 
						|
	for _, r := range as.ranges {
 | 
						|
		size = r.Size()
 | 
						|
		base = r.Base()
 | 
						|
		pat := r.Pat()
 | 
						|
		nx := r.Nx()
 | 
						|
 | 
						|
		numSmallEntries := 0
 | 
						|
		numBigEntries := 0
 | 
						|
 | 
						|
		for size != 0 {
 | 
						|
			mappingSize := SIZE_4KiB
 | 
						|
 | 
						|
			if (base&MASK_2MiB) == 0 && size >= SIZE_2MiB {
 | 
						|
				mappingSize = SIZE_2MiB
 | 
						|
				numBigEntries += 1
 | 
						|
			} else {
 | 
						|
				numSmallEntries += 1
 | 
						|
			}
 | 
						|
 | 
						|
			as.insertMapping(base, mappingSize, pat, nx)
 | 
						|
 | 
						|
			base += mappingSize
 | 
						|
			size -= mappingSize
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		log.Printf("%s : %d big %d small\n", r, numBigEntries, numSmallEntries)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) PageTableSize() uint {
 | 
						|
	return as.numMetaPages * METADATA_TABLE_SIZE
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) NumPages() uint {
 | 
						|
	return as.numMetaPages
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) WritePageTable(ptew pageTableEntryWriter) error {
 | 
						|
	for _, pw := range as.page_writers {
 | 
						|
		err := pw.WritePage(ptew)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (as *addressSpace) WritePageDirectoryPointerTable(ptew pageTableEntryWriter) error {
 | 
						|
	for i := range as.pdptes {
 | 
						|
		err := ptew.WritePageEntry(as.pdptes[i].Encode())
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
var pat_types_from_str = map[string]uint{
 | 
						|
	"UC":  PAT_UC,
 | 
						|
	"WC":  PAT_WC,
 | 
						|
	"WT":  PAT_WT,
 | 
						|
	"WP":  PAT_WP,
 | 
						|
	"WB":  PAT_WB,
 | 
						|
	"UC-": PAT_UCMINUS,
 | 
						|
}
 | 
						|
 | 
						|
var patTypesToString = map[uint]string{
 | 
						|
	PAT_UC:      "UC",
 | 
						|
	PAT_WC:      "WC",
 | 
						|
	PAT_WT:      "WT",
 | 
						|
	PAT_WP:      "WP",
 | 
						|
	PAT_WB:      "WB",
 | 
						|
	PAT_UCMINUS: "UC-",
 | 
						|
}
 | 
						|
 | 
						|
func openCsvFile(file string) (*csv.Reader, error) {
 | 
						|
	f, err := os.Open(file)
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	csvr := csv.NewReader(f)
 | 
						|
	csvr.Comment = COMMENT_CHAR
 | 
						|
	csvr.TrimLeadingSpace = true
 | 
						|
	return csvr, nil
 | 
						|
}
 | 
						|
 | 
						|
// After returns true if ar beings at or after other.end.
 | 
						|
func (ar addressRange) After(other *addressRange) bool {
 | 
						|
	return ar.begin >= other.end
 | 
						|
}
 | 
						|
 | 
						|
func (ar addressRange) FullyOverlaps(other *addressRange) bool {
 | 
						|
	return ar.begin <= other.begin && ar.end >= other.end
 | 
						|
}
 | 
						|
 | 
						|
func (ar addressRange) Overlaps(other *addressRange) bool {
 | 
						|
	if other.end <= ar.begin || other.begin >= ar.end {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func MergeByPat(a, b *addressRange) bool {
 | 
						|
	// 'b' is assumed to be following 'a'
 | 
						|
	if a.end != b.begin {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if a.pat != b.pat {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func MergeByNx(a, b *addressRange) bool {
 | 
						|
	// 'b' is assumed to be following 'a'
 | 
						|
	if a.end != b.begin {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if a.nx != b.nx {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func MergeByPatNx(a, b *addressRange) bool {
 | 
						|
	return MergeByPat(a, b) && MergeByNx(a, b)
 | 
						|
}
 | 
						|
 | 
						|
func hexNumber(s string) (uint64, error) {
 | 
						|
	return strconv.ParseUint(strings.TrimSpace(s), 0, 0)
 | 
						|
}
 | 
						|
 | 
						|
func patTypeToString(pat uint) string {
 | 
						|
	return patTypesToString[pat]
 | 
						|
}
 | 
						|
 | 
						|
func patTypeFromString(s string) (uint, error) {
 | 
						|
	s1 := strings.TrimSpace(s)
 | 
						|
	v, ok := pat_types_from_str[s1]
 | 
						|
 | 
						|
	if !ok {
 | 
						|
		return 0, fmt.Errorf("No PAT type '%s'", s1)
 | 
						|
	}
 | 
						|
 | 
						|
	return v, nil
 | 
						|
}
 | 
						|
 | 
						|
func removeComment(field, comment string) string {
 | 
						|
	str_slice := strings.Split(field, comment)
 | 
						|
	return strings.TrimSpace(str_slice[0])
 | 
						|
}
 | 
						|
 | 
						|
func newAddrRange(begin, end uint64, pat uint, nx bool) *addressRange {
 | 
						|
	return &addressRange{begin: begin, end: end, pat: pat, nx: nx}
 | 
						|
}
 | 
						|
 | 
						|
func readRecords(csvr *csv.Reader, as *addressSpace) {
 | 
						|
	i := 0
 | 
						|
	for true {
 | 
						|
		fields, err := csvr.Read()
 | 
						|
		i++
 | 
						|
 | 
						|
		if err == io.EOF {
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if len(fields) < 3 {
 | 
						|
			log.Fatal("Need at least 3 fields: begin, end, PAT\n")
 | 
						|
		}
 | 
						|
 | 
						|
		begin, err := hexNumber(fields[0])
 | 
						|
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		end, err := hexNumber(fields[1])
 | 
						|
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if begin&MASK_4KiB != 0 {
 | 
						|
			log.Fatalf("begin %x must be at least 4KiB aligned\n", begin)
 | 
						|
		}
 | 
						|
 | 
						|
		if end&MASK_4KiB != 0 {
 | 
						|
			log.Fatalf("end %x must be at least 4KiB aligned\n", end)
 | 
						|
		}
 | 
						|
		if begin >= end {
 | 
						|
			log.Fatalf("%x must be < %x at record %d\n", begin, end, i)
 | 
						|
		}
 | 
						|
 | 
						|
		pat, err := patTypeFromString(fields[2])
 | 
						|
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		var nx bool = false
 | 
						|
 | 
						|
		if len(fields) > 3 && len(removeComment(fields[3], string(COMMENT_CHAR))) > 0 {
 | 
						|
			nx = true
 | 
						|
		}
 | 
						|
 | 
						|
		as.AddRange(newAddrRange(begin, end, pat, nx))
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func main() {
 | 
						|
	log.SetFlags(0)
 | 
						|
	flag.Parse()
 | 
						|
	var ptWriters []pageTableEntryWriter
 | 
						|
	var pdptWriters []pageTableEntryWriter
 | 
						|
 | 
						|
	if *iomapFilePtr == "" {
 | 
						|
		log.Fatal("No iomap_file provided.\n")
 | 
						|
	}
 | 
						|
 | 
						|
	csvr, err := openCsvFile(*iomapFilePtr)
 | 
						|
	if err != nil {
 | 
						|
		log.Fatal(err)
 | 
						|
	}
 | 
						|
 | 
						|
	as := newAddrSpace(MergeByPatNx, *pagesBaseAddress)
 | 
						|
	readRecords(csvr, as)
 | 
						|
 | 
						|
	log.Println("Merged address space:")
 | 
						|
	as.CreatePageTables()
 | 
						|
	log.Println()
 | 
						|
	log.Printf("Total Pages of page tables: %d\n", as.NumPages())
 | 
						|
	log.Println()
 | 
						|
	log.Printf("Pages linked using base address of %#x.\n", *pagesBaseAddress)
 | 
						|
 | 
						|
	if *ptCFilePtr != "" {
 | 
						|
		f, err := os.Create(*ptCFilePtr)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
		defer f.Close()
 | 
						|
		bwr := bufio.NewWriter(f)
 | 
						|
		defer bwr.Flush()
 | 
						|
		cw := newCWriter(bwr, "page_tables", as.NumPages()*NUM_PTE)
 | 
						|
		ptWriters = append(ptWriters, cw)
 | 
						|
	}
 | 
						|
 | 
						|
	if *ptBinFilePtr != "" {
 | 
						|
		f, err := os.Create(*ptBinFilePtr)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
		defer f.Close()
 | 
						|
		bwr := bufio.NewWriter(f)
 | 
						|
		defer bwr.Flush()
 | 
						|
		bw := &binaryWriter{wr: bwr}
 | 
						|
		ptWriters = append(ptWriters, bw)
 | 
						|
	}
 | 
						|
 | 
						|
	if *pdptCFilePtr != "" {
 | 
						|
		f, err := os.Create(*pdptCFilePtr)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
		defer f.Close()
 | 
						|
		bwr := bufio.NewWriter(f)
 | 
						|
		defer bwr.Flush()
 | 
						|
		cw := newCWriter(bwr, "pdptes", NUM_PDPTE)
 | 
						|
		pdptWriters = append(pdptWriters, cw)
 | 
						|
	}
 | 
						|
 | 
						|
	if *pdptBinFilePtr != "" {
 | 
						|
		f, err := os.Create(*pdptBinFilePtr)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
		defer f.Close()
 | 
						|
		bwr := bufio.NewWriter(f)
 | 
						|
		defer bwr.Flush()
 | 
						|
		bw := &binaryWriter{wr: bwr}
 | 
						|
		pdptWriters = append(pdptWriters, bw)
 | 
						|
	}
 | 
						|
 | 
						|
	// Write out page tables
 | 
						|
	for _, w := range ptWriters {
 | 
						|
		err = as.WritePageTable(w)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Write out pdptes
 | 
						|
	for _, w := range pdptWriters {
 | 
						|
		err = as.WritePageDirectoryPointerTable(w)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |