Stefan thinks they don't add value. Command used: sed -i -e '/file is part of /d' $(git grep "file is part of " |egrep ":( */\*.*\*/\$|#|;#|-- | *\* )" | cut -d: -f1 |grep -v crossgcc |grep -v gcov | grep -v /elf.h |grep -v nvramtool) The exceptions are for: - crossgcc (patch file) - gcov (imported from gcc) - elf.h (imported from GNU's libc) - nvramtool (more complicated header) The removed lines are: - fmt.Fprintln(f, "/* This file is part of the coreboot project. */") -# This file is part of a set of unofficial pre-commit hooks available -/* This file is part of coreboot */ -# This file is part of msrtool. -/* This file is part of msrtool. */ - * This file is part of ncurses, designed to be appended after curses.h.in -/* This file is part of pgtblgen. */ - * This file is part of the coreboot project. - /* This file is part of the coreboot project. */ -# This file is part of the coreboot project. -# This file is part of the coreboot project. -## This file is part of the coreboot project. --- This file is part of the coreboot project. -/* This file is part of the coreboot project */ -/* This file is part of the coreboot project. */ -;## This file is part of the coreboot project. -# This file is part of the coreboot project. It originated in the - * This file is part of the coreinfo project. -## This file is part of the coreinfo project. - * This file is part of the depthcharge project. -/* This file is part of the depthcharge project. */ -/* This file is part of the ectool project. */ - * This file is part of the GNU C Library. - * This file is part of the libpayload project. -## This file is part of the libpayload project. -/* This file is part of the Linux kernel. */ -## This file is part of the superiotool project. -/* This file is part of the superiotool project */ -/* This file is part of uio_usbdebug */ Change-Id: I82d872b3b337388c93d5f5bf704e9ee9e53ab3a9 Signed-off-by: Patrick Georgi <pgeorgi@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/41194 Reviewed-by: HAOUAS Elyes <ehaouas@noos.fr> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
		
			
				
	
	
		
			970 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			970 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0-only */
 | |
| 
 | |
| 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 indices
 | |
| // 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(indices []int) {
 | |
| 	// deletions need to be processed in reverse order so as not
 | |
| 	// delete the wrong entries
 | |
| 	sort.Sort(sort.Reverse(sort.IntSlice(indices)))
 | |
| 	for _, i := range indices {
 | |
| 		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)
 | |
| 		}
 | |
| 	}
 | |
| }
 |