github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/acpi/rsdp.go (about)

     1  // Copyright 2019 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  // Package acpi can find and parse the RSDP pointer and struct.
     6  package acpi
     7  
     8  import (
     9  	"bufio"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"log"
    13  	"os"
    14  	"strconv"
    15  	"strings"
    16  
    17  	"github.com/u-root/u-root/pkg/memio"
    18  )
    19  
    20  const (
    21  	// checksum1 offset in RSDP struct.
    22  	cSUM1Off = 8
    23  
    24  	// checksum2 offset in RSDP struct.
    25  	cSUM2Off    = 32
    26  	xSDTLenOff  = 20
    27  	xSDTAddrOff = 24
    28  
    29  	// headerLength is a common header length for (almost)
    30  	// all ACPI tables.
    31  	headerLength = 36
    32  )
    33  
    34  var (
    35  	defaultRSDP = []byte("RSDP PTR U-ROOT\x02")
    36  )
    37  
    38  // gencsum generates a uint8 checksum of a []uint8
    39  func gencsum(b []uint8) uint8 {
    40  	var csum uint8
    41  	for _, bb := range b {
    42  		csum += bb
    43  	}
    44  	return ^csum + 1
    45  }
    46  
    47  // RSDP is the v2 version of the ACPI RSDP struct.
    48  type RSDP struct {
    49  	// base is the base address of the RSDP struct in physical memory.
    50  	base uint64
    51  
    52  	data [headerLength]byte
    53  }
    54  
    55  // NewRSDP returns a new and partially initialized RSDP, setting only
    56  // the defaultRSDP values, address, length, and signature.
    57  func NewRSDP(addr uintptr, len uint) []byte {
    58  	var r [headerLength]byte
    59  	copy(r[:], defaultRSDP)
    60  
    61  	// This is a bit of a cheat. All the fields are 0.  So we get a
    62  	// checksum, set up the XSDT fields, get the second checksum.
    63  	r[cSUM1Off] = gencsum(r[:])
    64  	binary.LittleEndian.PutUint32(r[xSDTLenOff:], uint32(len))
    65  	binary.LittleEndian.PutUint64(r[xSDTAddrOff:], uint64(addr))
    66  	r[cSUM2Off] = gencsum(r[:])
    67  	return r[:]
    68  }
    69  
    70  // Len returns the RSDP length
    71  func (r *RSDP) Len() uint32 {
    72  	return uint32(len(r.data))
    73  }
    74  
    75  // AllData returns the RSDP as a []byte
    76  func (r *RSDP) AllData() []byte {
    77  	return r.data[:]
    78  }
    79  
    80  // TableData returns the RSDP table data as a []byte
    81  func (r *RSDP) TableData() []byte {
    82  	return r.data[36:]
    83  }
    84  
    85  // Sig returns the RSDP signature
    86  func (r *RSDP) Sig() string {
    87  	return string(r.data[:8])
    88  }
    89  
    90  // OEMID returns the RSDP OEMID
    91  func (r *RSDP) OEMID() string {
    92  	return string(r.data[9:15])
    93  }
    94  
    95  // RSDPAddr returns the physical base address of the RSDP.
    96  func (r *RSDP) RSDPAddr() uint64 {
    97  	return r.base
    98  }
    99  
   100  // SDTAddr returns a base address or the [RX]SDT.
   101  //
   102  // It will preferentially return the XSDT, but if that is
   103  // 0 it will return the RSDT address.
   104  func (r *RSDP) SDTAddr() uint64 {
   105  	b := uint64(binary.LittleEndian.Uint32(r.data[16:20]))
   106  	if b != 0 {
   107  		return b
   108  	}
   109  	return uint64(binary.LittleEndian.Uint64(r.data[24:32]))
   110  }
   111  
   112  func readRSDP(base uint64) (*RSDP, error) {
   113  	r := &RSDP{}
   114  	r.base = base
   115  
   116  	dat := memio.ByteSlice(make([]byte, len(r.data)))
   117  	if err := memio.Read(int64(base), &dat); err != nil {
   118  		return nil, err
   119  	}
   120  	copy(r.data[:], dat)
   121  	return r, nil
   122  }
   123  
   124  func getRSDPEFI() (*RSDP, error) {
   125  	file, err := os.Open("/sys/firmware/efi/systab")
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	defer file.Close()
   130  
   131  	const (
   132  		acpi20 = "ACPI20="
   133  		acpi   = "ACPI="
   134  	)
   135  
   136  	scanner := bufio.NewScanner(file)
   137  	for scanner.Scan() {
   138  		line := scanner.Text()
   139  		start := ""
   140  		if strings.HasPrefix(line, acpi20) {
   141  			start = strings.TrimPrefix(line, acpi20)
   142  		}
   143  		if strings.HasPrefix(line, acpi) {
   144  			start = strings.TrimPrefix(line, acpi)
   145  		}
   146  		if start == "" {
   147  			continue
   148  		}
   149  		base, err := strconv.ParseUint(start, 0, 64)
   150  		if err != nil {
   151  			continue
   152  		}
   153  		rsdp, err := readRSDP(base)
   154  		if err != nil {
   155  			continue
   156  		}
   157  		return rsdp, nil
   158  	}
   159  	if err := scanner.Err(); err != nil {
   160  		log.Printf("error while reading EFI systab: %v", err)
   161  	}
   162  	return nil, fmt.Errorf("invalid /sys/firmware/efi/systab file")
   163  }
   164  
   165  // getRSDPmem is the option of last choice, it just grovels through
   166  // the e0000-ffff0 area, 16 bytes at a time, trying to find an RSDP.
   167  // These are well-known addresses for 20+ years.
   168  func getRSDPmem() (*RSDP, error) {
   169  	for base := uint64(0xe0000); base < 0xffff0; base += 16 {
   170  		var r memio.Uint64
   171  		if err := memio.Read(int64(base), &r); err != nil {
   172  			continue
   173  		}
   174  		if r != 0x2052545020445352 {
   175  			continue
   176  		}
   177  		rsdp, err := readRSDP(base)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  		return rsdp, nil
   182  	}
   183  	return nil, fmt.Errorf("could not find ACPI RSDP via /dev/mem")
   184  }
   185  
   186  // You can change the getters if you wish for testing.
   187  var getters = []func() (*RSDP, error){getRSDPEFI, getRSDPmem}
   188  
   189  // GetRSDP finds the RSDP pointer and struct in memory.
   190  //
   191  // It is able to use several methods, because there is no consistency
   192  // about how it is done.
   193  func GetRSDP() (*RSDP, error) {
   194  	for _, f := range getters {
   195  		r, err := f()
   196  		if err != nil {
   197  			log.Print(err)
   198  		}
   199  		if err == nil {
   200  			return r, nil
   201  		}
   202  	}
   203  	return nil, fmt.Errorf("cannot find an RSDP")
   204  }