github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/exp/cbmem/main.go (about)

     1  // Copyright 2016-2017 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // cbmem prints out coreboot mem table information in JSON by default,
     6  // and also implements the basic cbmem -list and -console commands.
     7  // TODO: checksum tables.
     8  package main
     9  
    10  import (
    11  	"bufio"
    12  	"encoding/json"
    13  	"flag"
    14  	"fmt"
    15  	"io"
    16  	"log"
    17  	"os"
    18  	"reflect"
    19  )
    20  
    21  // The C version of cbmem has a complex function to list
    22  // numbers in xx,xxx,xxx form. I personally think this
    23  // is a terrible idea (what about the EU among other things?)
    24  // If you decide yor really want this, then don't write a function,
    25  // do this.
    26  //    "golang.org/x/text/language"
    27  //    "golang.org/x/text/message"
    28  //    p := message.NewPrinter(language.English)
    29  //    p.Printf("%d\n", 1000)
    30  
    31  var (
    32  	mem                 = flag.String("mem", "/dev/mem", "file for coreboot image")
    33  	debug               = func(string, ...interface{}) {}
    34  	addr                int64
    35  	size                int
    36  	console             bool
    37  	coverage            bool
    38  	list                bool
    39  	hexdump             bool
    40  	timestamps          bool
    41  	parseabletimestamps bool
    42  	verbose             bool
    43  	version             bool
    44  	dumpJSON            = flag.Bool("json", true, "Output tables in JSON format")
    45  )
    46  
    47  func init() {
    48  	flag.BoolVar(&console, "console", false, "print cbmem console")
    49  	flag.BoolVar(&coverage, "coverage", false, "dump coverage information")
    50  	flag.BoolVar(&list, "list", false, "print cbmem table of contents")
    51  	flag.BoolVar(&hexdump, "hexdump", false, "print hexdump of cbmem area")
    52  	flag.BoolVar(&timestamps, "timestamps", false, "print timestamp information")
    53  	flag.BoolVar(&parseabletimestamps, "parseable-timestamps", false, "print parseable timestamps")
    54  	flag.BoolVar(&verbose, "verbose", false, "verbose (debugging) output")
    55  	flag.BoolVar(&version, "version", false, "print the version")
    56  	flag.BoolVar(&console, "c", false, "print cbmem console")
    57  	flag.BoolVar(&coverage, "C", false, "dump coverage information")
    58  	flag.BoolVar(&list, "l", false, "print cbmem table of contents")
    59  	flag.BoolVar(&hexdump, "x", false, "print hexdump of cbmem area")
    60  	flag.BoolVar(&timestamps, "t", false, "print timestamp information")
    61  	flag.BoolVar(&parseabletimestamps, "T", false, "print parseable timestamps")
    62  	flag.BoolVar(&verbose, "v", false, "verbose (debugging) output")
    63  	flag.BoolVar(&version, "V", false, "print the version")
    64  }
    65  
    66  func parseCBtable(r io.ReaderAt, address int64, sz int) (*CBmem, error) {
    67  	debug("Looking for coreboot table at %v %v bytes", address, sz)
    68  	var (
    69  		i     int64
    70  		lbh   Header
    71  		found = fmt.Errorf("No cb table found")
    72  		cbmem = &CBmem{StringVars: make(map[string]string)}
    73  	)
    74  
    75  	for i = address; i < address+0x1000 && found != nil; i += 0x10 {
    76  		readOne(r, &lbh, i)
    77  		debug("header is %q", lbh)
    78  		if string(lbh.Signature[:]) != "LBIO" {
    79  			debug("no LBIO at %v", i)
    80  			continue
    81  		}
    82  		if lbh.HeaderSz == 0 {
    83  			debug("HeaderSz is 0 at %v", i)
    84  		}
    85  		// TODO: checksum the header.
    86  		// Although I know of no case in 10 years where that
    87  		// was useful.
    88  		addr = i + int64(lbh.HeaderSz)
    89  		found = nil
    90  		debug("Found!\n")
    91  
    92  		/* Keep reference to lbtable. */
    93  		size = int(lbh.TableSz)
    94  		j := addr
    95  		for j < addr+int64(lbh.TableSz) {
    96  			var rec Record
    97  			debug("  coreboot table entry 0x%02x\n", rec.Tag)
    98  			readOne(r, &rec, j)
    99  			debug("Found Tag %s (%v) Size %v", tagNames[rec.Tag], rec.Tag, rec.Size)
   100  			start := j
   101  			j += int64(reflect.TypeOf(r).Size())
   102  			n := tagNames[rec.Tag]
   103  			switch rec.Tag {
   104  			case LB_TAG_BOARD_ID:
   105  				readOne(r, &cbmem.BoardID, start)
   106  			case
   107  				LB_TAG_VERSION,
   108  				LB_TAG_EXTRA_VERSION,
   109  				LB_TAG_BUILD,
   110  				LB_TAG_COMPILE_TIME,
   111  				LB_TAG_COMPILE_BY,
   112  				LB_TAG_COMPILE_HOST,
   113  				LB_TAG_COMPILE_DOMAIN,
   114  				LB_TAG_COMPILER,
   115  				LB_TAG_LINKER,
   116  				LB_TAG_ASSEMBLER,
   117  				LB_TAG_PLATFORM_BLOB_VERSION:
   118  				s, err := bufio.NewReader(io.NewSectionReader(r, j, 65536)).ReadString(0)
   119  				if err != nil {
   120  					log.Fatalf("Trying to read string for %s: %v", n, err)
   121  				}
   122  				cbmem.StringVars[n] = s[:len(s)-1]
   123  			case LB_TAG_SERIAL:
   124  				var s serialEntry
   125  				readOne(r, &s, start)
   126  				cbmem.UART = append(cbmem.UART, s)
   127  
   128  			case LB_TAG_CONSOLE:
   129  				var c uint32
   130  				readOne(r, &c, j)
   131  				cbmem.Consoles = append(cbmem.Consoles, consoleNames[c])
   132  			case LB_TAG_VERSION_TIMESTAMP:
   133  				readOne(r, &cbmem.VersionTimeStamp, start)
   134  			case LB_TAG_BOOT_MEDIA_PARAMS:
   135  				readOne(r, &cbmem.BootMediaParams, start)
   136  			case LB_TAG_CBMEM_ENTRY:
   137  				var c cbmemEntry
   138  				readOne(r, &c, start)
   139  				cbmem.CBMemory = append(cbmem.CBMemory, c)
   140  			case LB_TAG_MEMORY:
   141  				debug("    Found memory map.\n")
   142  				cbmem.Memory = &memoryEntry{Record: rec}
   143  				nel := (int64(cbmem.Memory.Size) - (j - start)) / int64(reflect.TypeOf(memoryRange{}).Size())
   144  				cbmem.Memory.Maps = make([]memoryRange, nel)
   145  				readOne(r, cbmem.Memory.Maps, j)
   146  			case LB_TAG_TIMESTAMPS:
   147  				debug("    Found timestamp table.\n")
   148  				var t timestampEntry
   149  				readOne(r, &t, start)
   150  				cbmem.TimeStamps = append(cbmem.TimeStamps, t)
   151  			case LB_TAG_MAINBOARD:
   152  				// The mainboard entry is a bit weird.
   153  				// There is a byte after the Record
   154  				// for the Vendor Index and a byte after
   155  				// that for the Part Number Index.
   156  				// In general, the vx is 0, and it's also
   157  				// null terminated. The struct is a bit
   158  				// over-general, actually, and the indexes
   159  				// can be safely ignored.
   160  				cbmem.MainBoard.Record = rec
   161  				v, err := bufio.NewReader(io.NewSectionReader(r, j+2, 65536)).ReadString(0)
   162  				if err != nil {
   163  					log.Fatalf("Trying to read string for %s: %v", n, err)
   164  				}
   165  				p, err := bufio.NewReader(io.NewSectionReader(r, j+2+int64(len(v)), 65536)).ReadString(0)
   166  				if err != nil {
   167  					log.Fatalf("Trying to read string for %s: %v", n, err)
   168  				}
   169  				cbmem.MainBoard.Vendor = v[:len(v)-1]
   170  				cbmem.MainBoard.PartNumber = p[:len(p)-1]
   171  			case LB_TAG_HWRPB:
   172  				readOne(r, &cbmem.Hwrpb, start)
   173  			case LB_TAG_CBMEM_CONSOLE:
   174  				var c = &memconsoleEntry{Record: rec}
   175  				debug("    Found cbmem console, %d byte record.\n", c.Size)
   176  				readOne(r, &c.CSize, j)
   177  				j += int64(reflect.TypeOf(c.CSize).Size())
   178  				readOne(r, &c.Cursor, j)
   179  				j += int64(reflect.TypeOf(c.Cursor).Size())
   180  				if c.CSize > c.Cursor {
   181  					c.CSize = c.Cursor
   182  				}
   183  				debug("CSize is %d, and Cursor is at %d", c.CSize, c.Cursor)
   184  				c.Data = make([]byte, c.CSize)
   185  				readOne(r, c.Data, j)
   186  				cbmem.MemConsole = c
   187  			case LB_TAG_FORWARD:
   188  				var newTable int64
   189  				readOne(r, &newTable, j)
   190  				debug("Forward to %08x", newTable)
   191  				return parseCBtable(r, newTable, 1048576)
   192  			default:
   193  				if n, ok := tagNames[rec.Tag]; ok {
   194  					log.Printf("Ignoring record %v", n)
   195  					cbmem.Ignored = append(cbmem.Ignored, n)
   196  					break
   197  				}
   198  				log.Printf("Unknown tag record %v", r)
   199  				cbmem.Unknown = append(cbmem.Unknown, rec.Tag)
   200  				break
   201  
   202  			}
   203  			j = start + int64(rec.Size)
   204  		}
   205  	}
   206  	return cbmem, found
   207  }
   208  
   209  func DumpMem(cbmem *CBmem) {
   210  	if cbmem.Memory == nil {
   211  		fmt.Printf("No cbmem table name")
   212  	}
   213  	m := cbmem.Memory.Maps
   214  	if len(m) == 0 {
   215  		fmt.Printf("No cbmem map entries")
   216  	}
   217  	fmt.Printf("%19s %8s %8s\n", "Name", "Start", "Size")
   218  	for _, e := range m {
   219  		fmt.Printf("%19s %08x %08x\n", memTags[e.Mtype], e.Start, e.Size)
   220  	}
   221  }
   222  
   223  func main() {
   224  	flag.Parse()
   225  	if version {
   226  		fmt.Println("cbmem in Go, a superset of cbmem v1.1 from coreboot")
   227  		os.Exit(0)
   228  	}
   229  	if verbose {
   230  		debug = log.Printf
   231  	}
   232  
   233  	mf, err := os.Open(*mem)
   234  	if err != nil {
   235  		log.Fatal(err)
   236  	}
   237  
   238  	var cbmem *CBmem
   239  	for _, addr := range []int64{0, 0xf0000} {
   240  		if cbmem, err = parseCBtable(mf, addr, TableSize); err == nil {
   241  			break
   242  		}
   243  	}
   244  	if err != nil {
   245  		log.Fatalf("Reading coreboot table: %v", err)
   246  	}
   247  	if *dumpJSON {
   248  		b, err := json.MarshalIndent(cbmem, "", "\t")
   249  		if err != nil {
   250  			log.Fatalf("json marshal: %v", err)
   251  		}
   252  		fmt.Printf("%s\n", b)
   253  	}
   254  	// list is kind of misnamed I think. It really just prints
   255  	// memory table entries.
   256  	if list {
   257  		DumpMem(cbmem)
   258  	}
   259  	if console && cbmem.MemConsole != nil {
   260  		fmt.Printf("Console is %d bytes and cursor is at %d\n", len(cbmem.MemConsole.Data), cbmem.MemConsole.Cursor)
   261  		fmt.Printf("%s%s", cbmem.MemConsole.Data[cbmem.MemConsole.Cursor:], cbmem.MemConsole.Data[0:cbmem.MemConsole.Cursor])
   262  	}
   263  
   264  }