github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/pkg/acpi/sdt.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
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"encoding/hex"
    11  	"fmt"
    12  	"os"
    13  )
    14  
    15  // SDT represents either an RSDT or XSDT. It has a Generic header
    16  // and Tables, which are pointers. In the RSDT they are 32 bits;
    17  // in the XSDT, 64. We unmarshal to 64 bits, and when we marshal,
    18  // we use the signature to determine whether the table is 32 or 64.
    19  type SDT struct {
    20  	Generic
    21  	Tables []int64
    22  	Base   int64
    23  }
    24  
    25  func init() {
    26  	addUnMarshaler("RSDT", unmarshalSDT)
    27  	addUnMarshaler("XSDT", unmarshalSDT)
    28  }
    29  
    30  func unmarshalSDT(t Tabler) (Tabler, error) {
    31  	s := &SDT{
    32  		Generic: Generic{
    33  			Header: *GetHeader(t),
    34  			data:   t.AllData(),
    35  		},
    36  	}
    37  
    38  	sig := s.Sig()
    39  	if sig != "RSDT" && sig != "XSDT" {
    40  		return nil, fmt.Errorf("%v is not RSDT or XSDT", sig)
    41  	}
    42  
    43  	// Now the fun. In 1999, 64-bit micros had been out for about 10 years.
    44  	// Intel had announced the ia64 years earlier. In 2000 the ACPI committee
    45  	// chose 32-bit pointers anyway, then had to backfill a bunch of table
    46  	// types to do 64 bits shortly thereafter (i.e. v2). Geez.
    47  	esize := 4
    48  	if sig == "XSDT" {
    49  		esize = 8
    50  	}
    51  	d := t.TableData()
    52  
    53  	for i := 0; i < len(d); i += esize {
    54  		val := int64(0)
    55  		if sig == "XSDT" {
    56  			val = int64(binary.LittleEndian.Uint64(d[i : i+8]))
    57  		} else {
    58  			val = int64(binary.LittleEndian.Uint32(d[i : i+4]))
    59  		}
    60  		s.Tables = append(s.Tables, val)
    61  	}
    62  	return s, nil
    63  }
    64  
    65  // Marshal marshals an [RX]SDT. If it has tables, it marshals them too.
    66  // Note that tables are just pointers in this case. Most users will likely
    67  // remove the tables (s->Tables = nil) and add their own in the call to
    68  // MarshalAll.
    69  func (s *SDT) Marshal() ([]byte, error) {
    70  	h, err := s.Generic.Header.Marshal()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	b := bytes.NewBuffer(h)
    75  	x := s.Sig() == "XSDT"
    76  	for _, p := range s.Tables {
    77  		if x {
    78  			w(b, p)
    79  		} else {
    80  			w(b, uint32(p))
    81  		}
    82  	}
    83  	return b.Bytes(), nil
    84  }
    85  
    86  // MarshalAll marshals out an SDT, and all the tables, in a blob
    87  // suitable for kexec. The most common use of this call would be to
    88  // set s->Tables = nil and then pass in the desired Tables as
    89  // parameters to this function.  For passed-in tables, all addresses
    90  // are recomputed, as there may be more tables. Further, even if
    91  // tables were scattered all over, we unify them into one segment.
    92  // There is one potential problem, which we can fix if needed:
    93  // it is possible the [XR]SDT is placed so close to the top of memory
    94  // there is no room for the table. In the unlikely event that ever
    95  // happens, we will just figure out how to place the tables in memory
    96  // lower than the [XR]SDT.
    97  func (s *SDT) MarshalAll(t ...Tabler) ([]byte, error) {
    98  	var tabs [][]byte
    99  	Debug("SDT MarshalAll has %d tables %d extra tables", len(s.Tables), len(t))
   100  
   101  	// Serialize the tables from pointers in the SDT. Note that
   102  	// this pointer can be nil. Depending on your kernel and its
   103  	// config settings, you won't be able to read these anyway. In
   104  	// the case of u-root kexec, this pointer is always nil as a
   105  	// defensive measure.
   106  	for i, addr := range s.Tables {
   107  		t, err := ReadRaw(addr)
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		Debug("SDT MarshalAll: processed table %d to %d bytes", i, len(t.AllData()))
   112  		tabs = append(tabs, t.AllData())
   113  	}
   114  
   115  	// Serialize the extra tables.
   116  	for i, tt := range t {
   117  		b, err := Marshal(tt)
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  		Debug("SDT MarshalAll: processed extra table %d to %d bytes", i, len(b))
   122  		tabs = append(tabs, b)
   123  	}
   124  
   125  	Debug("processed tables")
   126  	// The length of the SDT is SSDTSize + len(s.Tables) * pointersize.
   127  	// The number of tables will likely be different, so the
   128  	// current value in the header is almost certainly wrong.
   129  	// The easiest path here is to replace the
   130  	// data with the new data, but first we have to compute the
   131  	// pointers. So we do this as follows: truncate ssd to just
   132  	// the header, serialize pointers, then get the size.
   133  	s.Generic.data = s.Generic.data[:HeaderLength]
   134  	var (
   135  		addrs bytes.Buffer
   136  		st    []byte
   137  	)
   138  
   139  	base := s.Base + HeaderLength // This is where the pointers start
   140  	x := s.Sig() == "XSDT"
   141  	if x {
   142  		base += int64(len(tabs) * 8)
   143  	} else {
   144  		base += int64(len(tabs) * 4)
   145  	}
   146  
   147  	// We use base as a basic bump allocator.
   148  	for i, t := range tabs {
   149  		Debug("Table %d: len %d, base %#x", i, len(t), base)
   150  		st = append(st, t...)
   151  		if x {
   152  			w(&addrs, uint64(base))
   153  		} else {
   154  			w(&addrs, uint32(base))
   155  		}
   156  		base += int64(len(t))
   157  	}
   158  	s.Generic.data = append(s.Generic.data, addrs.Bytes()...)
   159  	h, err := s.Generic.Marshal()
   160  	// If you get really desperate ...
   161  	if false {
   162  		Debug("marshalled sdt is ")
   163  		d := hex.Dumper(os.Stdout)
   164  		d.Write(h)
   165  		d.Close()
   166  	}
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	// Append the tables. We have to do this after Marshaling the SDT
   172  	// as the ACPI tables length should not be included in the SDT length.
   173  	h = append(h, st...)
   174  	return h, nil
   175  }
   176  
   177  // ReadSDT reads an SDT in from memory, using UnMarshalSDT, which uses
   178  // the io package. This is increasingly unlikely to work over time.
   179  func ReadSDT() (*SDT, error) {
   180  	_, r, err := GetRSDP()
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  	s, err := UnMarshalSDT(r)
   185  	return s, err
   186  }
   187  
   188  // NewSDT creates a new SDT, defaulting to XSDT.
   189  func NewSDT(opt ...func(*SDT)) (*SDT, error) {
   190  	var s = &SDT{
   191  		Generic: Generic{
   192  			Header: Header{
   193  				Sig:             "XSDT",
   194  				Length:          HeaderLength,
   195  				Revision:        1,
   196  				OEMID:           "GOOGLE",
   197  				OEMTableID:      "ACPI=TOY",
   198  				OEMRevision:     1,
   199  				CreatorID:       1,
   200  				CreatorRevision: 1,
   201  			},
   202  		},
   203  	}
   204  	for _, o := range opt {
   205  		o(s)
   206  	}
   207  	// It may seem odd to check for a marshaling error
   208  	// in something that does no I/O, but consider this
   209  	// is a good place to see that the user did not set
   210  	// something wrong.
   211  	h, err := s.Marshal()
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	s.data = h
   216  	return s, nil
   217  }