code.flowtr.dev/mirrors/u-root@v1.0.0/cmds/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  				s, err := bufio.NewReader(io.NewSectionReader(r, j, 65536)).ReadString(0)
   118  				if err != nil {
   119  					log.Fatalf("Trying to read string for %s: %v", n, err)
   120  				}
   121  				cbmem.StringVars[n] = s[:len(s)-1]
   122  			case LB_TAG_SERIAL:
   123  				var s serialEntry
   124  				readOne(r, &s, start)
   125  				cbmem.UART = append(cbmem.UART, s)
   126  
   127  			case LB_TAG_CONSOLE:
   128  				var c uint32
   129  				readOne(r, &c, j)
   130  				cbmem.Consoles = append(cbmem.Consoles, consoleNames[c])
   131  			case LB_TAG_VERSION_TIMESTAMP:
   132  				readOne(r, &cbmem.VersionTimeStamp, start)
   133  			case LB_TAG_BOOT_MEDIA_PARAMS:
   134  				readOne(r, &cbmem.BootMediaParams, start)
   135  			case LB_TAG_CBMEM_ENTRY:
   136  				var c cbmemEntry
   137  				readOne(r, &c, start)
   138  				cbmem.CBMemory = append(cbmem.CBMemory, c)
   139  			case LB_TAG_MEMORY:
   140  				debug("    Found memory map.\n")
   141  				cbmem.Memory = &memoryEntry{Record: rec}
   142  				nel := (int64(cbmem.Memory.Size) - (j - start)) / int64(reflect.TypeOf(memoryRange{}).Size())
   143  				cbmem.Memory.Maps = make([]memoryRange, nel)
   144  				readOne(r, cbmem.Memory.Maps, j)
   145  			case LB_TAG_TIMESTAMPS:
   146  				debug("    Found timestamp table.\n")
   147  				var t timestampEntry
   148  				readOne(r, &t, start)
   149  				cbmem.TimeStamps = append(cbmem.TimeStamps, t)
   150  			case LB_TAG_MAINBOARD:
   151  				// The mainboard entry is a bit weird.
   152  				// There is a byte after the Record
   153  				// for the Vendor Index and a byte after
   154  				// that for the Part Number Index.
   155  				// In general, the vx is 0, and it's also
   156  				// null terminated. The struct is a bit
   157  				// over-general, actually, and the indexes
   158  				// can be safely ignored.
   159  				cbmem.MainBoard.Record = rec
   160  				v, err := bufio.NewReader(io.NewSectionReader(r, j+2, 65536)).ReadString(0)
   161  				if err != nil {
   162  					log.Fatalf("Trying to read string for %s: %v", n, err)
   163  				}
   164  				p, err := bufio.NewReader(io.NewSectionReader(r, j+2+int64(len(v)), 65536)).ReadString(0)
   165  				if err != nil {
   166  					log.Fatalf("Trying to read string for %s: %v", n, err)
   167  				}
   168  				cbmem.MainBoard.Vendor = v[:len(v)-1]
   169  				cbmem.MainBoard.PartNumber = p[:len(p)-1]
   170  			case LB_TAG_HWRPB:
   171  				readOne(r, &cbmem.Hwrpb, start)
   172  			case LB_TAG_CBMEM_CONSOLE:
   173  				var c = &memconsoleEntry{Record: rec}
   174  				debug("    Found cbmem console, %d byte record.\n", c.Size)
   175  				readOne(r, &c.CSize, j)
   176  				j += int64(reflect.TypeOf(c.CSize).Size())
   177  				readOne(r, &c.Cursor, j)
   178  				j += int64(reflect.TypeOf(c.Cursor).Size())
   179  				if c.CSize > c.Cursor {
   180  					c.CSize = c.Cursor
   181  				}
   182  				debug("CSize is %d, and Cursor is at %d", c.CSize, c.Cursor)
   183  				c.Data = make([]byte, c.CSize)
   184  				readOne(r, c.Data, j)
   185  				cbmem.MemConsole = c
   186  			case LB_TAG_FORWARD:
   187  				var newTable int64
   188  				readOne(r, &newTable, j)
   189  				debug("Forward to %08x", newTable)
   190  				return parseCBtable(r, newTable, 1048576)
   191  			default:
   192  				if n, ok := tagNames[rec.Tag]; ok {
   193  					log.Printf("Ignoring record %v", n)
   194  					cbmem.Ignored = append(cbmem.Ignored, n)
   195  					break
   196  				}
   197  				log.Printf("Unknown tag record %v", r)
   198  				cbmem.Unknown = append(cbmem.Unknown, rec.Tag)
   199  				break
   200  
   201  			}
   202  			j = start + int64(rec.Size)
   203  		}
   204  	}
   205  	return cbmem, found
   206  }
   207  
   208  func DumpMem(cbmem *CBmem) {
   209  	if cbmem.Memory == nil {
   210  		fmt.Printf("No cbmem table name")
   211  	}
   212  	m := cbmem.Memory.Maps
   213  	if len(m) == 0 {
   214  		fmt.Printf("No cbmem map entries")
   215  	}
   216  	fmt.Printf("%19s %8s %8s\n", "Name", "Start", "Size")
   217  	for _, e := range m {
   218  		fmt.Printf("%19s %08x %08x\n", memTags[e.Mtype], e.Start, e.Size)
   219  	}
   220  }
   221  
   222  func main() {
   223  	flag.Parse()
   224  	if version {
   225  		fmt.Println("cbmem in Go, a superset of cbmem v1.1 from coreboot")
   226  		os.Exit(0)
   227  	}
   228  	if verbose {
   229  		debug = log.Printf
   230  	}
   231  
   232  	mf, err := os.Open(*mem)
   233  	if err != nil {
   234  		log.Fatal(err)
   235  	}
   236  
   237  	var cbmem *CBmem
   238  	for _, addr := range []int64{0, 0xf0000} {
   239  		if cbmem, err = parseCBtable(mf, addr, TableSize); err == nil {
   240  			break
   241  		}
   242  	}
   243  	if err != nil {
   244  		log.Fatalf("Reading coreboot table: %v", err)
   245  	}
   246  	if *dumpJSON {
   247  		b, err := json.MarshalIndent(cbmem, "", "\t")
   248  		if err != nil {
   249  			log.Fatalf("json marshal: %v", err)
   250  		}
   251  		fmt.Printf("%s\n", b)
   252  	}
   253  	// list is kind of misnamed I think. It really just prints
   254  	// memory table entries.
   255  	if list {
   256  		DumpMem(cbmem)
   257  	}
   258  	if console && cbmem.MemConsole != nil {
   259  		fmt.Printf("Console is %d bytes and cursor is at %d\n", len(cbmem.MemConsole.Data), cbmem.MemConsole.Cursor)
   260  		fmt.Printf("%s%s", cbmem.MemConsole.Data[cbmem.MemConsole.Cursor:], cbmem.MemConsole.Data[0:cbmem.MemConsole.Cursor])
   261  	}
   262  
   263  }