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 }