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(×tamps, "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(×tamps, "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 }