github.com/platinasystems/nvram@v1.0.1-0.20190709235807-51a23abd5aec/coreboot_table.go (about)

     1  package nvram
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/platinasystems/nvram/debug"
     6  	"os"
     7  	"syscall"
     8  	"unsafe"
     9  )
    10  
    11  type lbHeader struct {
    12  	signature      uint32
    13  	headerBytes    uint32
    14  	headerChecksum uint32
    15  	tableBytes     uint32
    16  	tableChecksum  uint32
    17  	tableEntries   uint32
    18  }
    19  
    20  type lbRecord struct {
    21  	tag  uint32
    22  	size uint32
    23  }
    24  
    25  type lbForward struct {
    26  	lbRecord
    27  	forward uint64
    28  }
    29  
    30  type cmosOptionTable struct {
    31  	lbRecord
    32  	headerLength uint32
    33  }
    34  
    35  type CoreBootTable struct {
    36  	mem_file *os.File
    37  	mem      []byte
    38  	baseAddr uintptr
    39  
    40  	header *lbHeader
    41  	recs   []*lbRecord
    42  }
    43  
    44  func (t *CoreBootTable) Open() (err error) {
    45  	defer func() {
    46  		if err != nil {
    47  			t.Close()
    48  		}
    49  	}()
    50  
    51  	t.mem_file, err = os.OpenFile("/dev/mem", os.O_RDONLY, 0)
    52  	if err != nil {
    53  		return
    54  	}
    55  
    56  	err = t.openTable(0x00000000, 0x00000fff)
    57  	if err != nil {
    58  		err = t.openTable(0x000f0000, 0x000fffff)
    59  	}
    60  	if err != nil {
    61  		return
    62  	}
    63  
    64  	return
    65  }
    66  
    67  func (t *CoreBootTable) Close() (err error) {
    68  	debug.Trace(debug.LevelMSG1, "Closing Coreboot table\n")
    69  
    70  	if len(t.mem) > 0 {
    71  		syscall.Munmap(t.mem)
    72  		t.mem = nil
    73  	}
    74  
    75  	if t.mem_file != nil {
    76  		t.mem_file.Close()
    77  		t.mem_file = nil
    78  	}
    79  
    80  	t.baseAddr = 0
    81  	t.header = nil
    82  	t.recs = nil
    83  	return
    84  }
    85  
    86  func (t *CoreBootTable) FindCMOSOptionTable() (c *cmosOptionTable, ok bool) {
    87  	for _, lbrec := range t.recs {
    88  		if lbrec.tag == 0xc8 {
    89  			return (*cmosOptionTable)(unsafe.Pointer(lbrec)), true
    90  		}
    91  	}
    92  	return nil, false
    93  }
    94  
    95  func (t *CoreBootTable) openTable(start, end uintptr) (err error) {
    96  
    97  	debug.Trace(debug.LevelMSG1, "Looking for table @0x%08X\n", start)
    98  
    99  	defer func() {
   100  		if err != nil {
   101  			t.header = nil
   102  			t.recs = nil
   103  		}
   104  	}()
   105  
   106  	t.mapPages(start, end)
   107  
   108  	for i := 0; i < len(t.mem); i += 16 {
   109  		var header = (*lbHeader)(unsafe.Pointer(&t.mem[i]))
   110  		if header.signature == 0x4f49424c {
   111  			debug.Trace(debug.LevelMSG1, "Table found @0x%08X\n", unsafe.Pointer(header))
   112  			if t.computeIpChecksum(uintptr(unsafe.Pointer(header)), uint64(header.headerBytes)) != 0 {
   113  				debug.Trace(debug.LevelMSG1, "Header checksum bad\n")
   114  				continue
   115  			}
   116  
   117  			phyAddr := t.baseAddr + uintptr(i)
   118  			t.mapPages(phyAddr, phyAddr+uintptr(header.tableBytes))
   119  			virtAddr := uintptr(unsafe.Pointer(&t.mem[0])) + phyAddr - t.baseAddr
   120  			header = (*lbHeader)(unsafe.Pointer(virtAddr))
   121  
   122  			var lbrec = (*lbRecord)(unsafe.Pointer(virtAddr + uintptr(header.headerBytes)))
   123  
   124  			if t.computeIpChecksum(uintptr(unsafe.Pointer(lbrec)), uint64(header.tableBytes)) != header.tableChecksum {
   125  				debug.Trace(debug.LevelMSG1, "Table checksum bad\n")
   126  				continue
   127  			}
   128  
   129  			t.header = header
   130  			t.recs = nil
   131  			var lbforward *lbForward
   132  			for i := uint32(0); i < header.tableBytes; {
   133  				debug.Trace(debug.LevelMSG3, "Found lbRecord tag = %X len = %d\n", lbrec.tag, lbrec.size)
   134  
   135  				if lbforward == nil && lbrec.tag == 0x11 {
   136  					lbforward = (*lbForward)(unsafe.Pointer(lbrec))
   137  				}
   138  
   139  				t.recs = append(t.recs, lbrec)
   140  				i += lbrec.size
   141  				lbrec = (*lbRecord)(unsafe.Pointer(uintptr(unsafe.Pointer(lbrec)) + uintptr(lbrec.size)))
   142  			}
   143  
   144  			if len(t.recs) != int(header.tableEntries) {
   145  				debug.Trace(debug.LevelMSG1, "Unexpected number of table entries.\n")
   146  				continue
   147  			}
   148  
   149  			if lbforward != nil {
   150  				debug.Trace(debug.LevelMSG1, "Forwarding table found.\n")
   151  				err = t.openTable(uintptr(lbforward.forward), uintptr(lbforward.forward)+uintptr(os.Getpagesize()))
   152  				return
   153  			}
   154  
   155  			return
   156  		}
   157  	}
   158  
   159  	err = fmt.Errorf("Coreboot table not found.")
   160  	return
   161  }
   162  
   163  func (t *CoreBootTable) mapPages(start, end uintptr) (err error) {
   164  	t.baseAddr = start
   165  	length := end - start
   166  	pagesize := uintptr(os.Getpagesize())
   167  
   168  	numPages := (length +
   169  		(t.baseAddr & (pagesize - 1)) +
   170  		pagesize - 1) / pagesize
   171  	t.baseAddr &= ^(pagesize - 1)
   172  
   173  	if len(t.mem) > 0 {
   174  		syscall.Munmap(t.mem)
   175  		t.mem = nil
   176  	}
   177  
   178  	t.mem, err = syscall.Mmap(int(t.mem_file.Fd()),
   179  		int64(t.baseAddr), int(numPages*pagesize),
   180  		syscall.PROT_READ, syscall.MAP_SHARED)
   181  	if err != nil {
   182  		return
   183  	}
   184  
   185  	return
   186  }
   187  
   188  func (t *CoreBootTable) computeIpChecksum(start uintptr, length uint64) uint32 {
   189  
   190  	sum := uint32(0)
   191  
   192  	for i := start; i < (start + uintptr(length)); i++ {
   193  		ptr := (*byte)(unsafe.Pointer(i))
   194  		value := uint32(*ptr)
   195  		if (i & 1) != 0 {
   196  			value <<= 8
   197  		}
   198  
   199  		sum += value
   200  
   201  		if sum > 0xFFFF {
   202  			sum = (sum + (sum >> 16)) & 0xFFFF
   203  		}
   204  	}
   205  	return (^sum) & 0xFFFF
   206  }