gopkg.in/hugelgupf/u-root.v2@v2.0.0-20180831055005-3f8fdb0ce09d/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(×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 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 }