github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/pkg/acpi/ibft.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 // The IBFT is another brain-dead design. 8 // Lots of flexibility, which is basically impossible to use due to lots of limits. 9 // Basic layout is like this: 10 // Name size offset description 11 // Header 48 0 Primary Header 12 // Control variable 48 Extended Header 13 // Initiator variable Initiator 14 // NIC variable NIC 15 // Target variable Target 16 // Heap variable Offsets point here. 17 18 import ( 19 "bytes" 20 "fmt" 21 "reflect" 22 ) 23 24 const ( 25 reserved uint8 = iota 26 ibftControl 27 ibftInitiator 28 ibftNIC 29 ibftTarget 30 ibftExtensions 31 ) 32 33 const ( 34 ibftHeaderLen uint16 = 48 35 ibftControlLen uint16 = 18 36 ibftInitiatorLen uint16 = 74 37 ibftNICLen uint16 = 102 38 ibftTargetLen uint16 = 54 39 ibftHeadersLen uint16 = ibftHeaderLen + ibftControlLen + ibftInitiatorLen + ibftNICLen + ibftTargetLen + ibftNICLen + ibftTargetLen 40 ) 41 42 const ( 43 ibftVersion uint8 = 1 // in all cases since 2009 44 ) 45 46 // We created a bunch of structs here so you don't have to go read the Big Bad Book of ACPI. 47 // It turned out to be easier to use the structs we defined further below. 48 // These structs were generated by running scripts across the pdf. 49 50 // acpiIBFTHeader defines the acpiIBFTHeader 51 type acpiIBFTHeader struct { 52 Signature [4]byte `offset:"0" desc:"iBFT Signature for the iSCSI Boot Firmware Table"` 53 Length uint32 `offset:"4" desc:"Length in bytes of the entire IBFT, including the signature"` 54 Revision uint8 `offset:"8" desc:"Revision = 1"` 55 Checksum uint8 `offset:"9" desc:"Entire table must sum to zero"` 56 OEMID [6]byte `offset:"10" desc:"OEM ID. All unused trailing bytes must be zero. [acpi-OEMID]"` 57 OEMTableID [8]byte `offset:"16" desc:"For the iBFT the Table ID is the Manufacturer‟s Model ID. All unused trailing bytes must be zero."` 58 Reserved [24]byte `offset:"24" desc:"Reserved"` 59 } 60 61 var ( 62 rawIBTFHeader = "IBFT\x00\x08\x00\x001\x00ACPIXXACPISUCK1234567890abcdef01234567" 63 _ = Tabler(&IBFT{}) 64 ) 65 66 // acpiIBFTStructHeader defines the common components of the structure headers. 67 // In the standard, IBM made the flags common, even though the values 68 // are all over the place. We don't do that. 69 // In a fit of genius, whoever designed this made Bit 0 sometimes 70 // mean valid, sometimes not. Awesome! 71 type acpiIBFTStructHeader struct { 72 ID byte `offset:"0" desc:"ID"` 73 Version uint8 `offset:"1" desc:"Structure Version"` 74 Length uint16 `offset:"2" desc:"Structure Length"` 75 Index uint8 `offset:"4" desc:"Index"` 76 } 77 78 type acpiIBFTControlFlags uint8 79 80 const ( 81 ibftMultiLogin acpiIBFTControlFlags = iota 82 ibftSingleLogin 83 ) 84 85 type acpiIBFTControl struct { 86 acpiIBFTStructHeader 87 Flags acpiIBFTControlFlags `offset:"0" desc:"Bit 0 : Target Login Mode Control 0 = Multi-Login Mode 1 = Single Login Mode"` 88 Extensions uint16 `offset:"6" desc:"Optional. If unused must be zero. If used, must point to an Extensions Structure with a standard Structure header."` 89 Initiator uint16 `offset:"8" desc:""` 90 NIC0 uint16 `offset:"10" desc:""` 91 Target0 uint16 `offset:"12" desc:""` 92 NIC1 uint16 `offset:"14" desc:""` 93 Target1 uint16 `offset:"16" desc:""` 94 } 95 96 // pre-filled-in control structure. 97 var control = acpiIBFTControl{ 98 acpiIBFTStructHeader: acpiIBFTStructHeader{ 99 ID: ibftControl, 100 Version: 1, 101 Length: 80, // round the next struct to 128 bytes 102 Index: 0, 103 }, 104 Flags: ibftSingleLogin, 105 // Just 64-byte align everything. Anything else is painful. 106 Extensions: 0, 107 Initiator: ibftHeaderLen + ibftControlLen, 108 NIC0: ibftHeaderLen + ibftControlLen + ibftInitiatorLen, 109 Target0: ibftHeaderLen + ibftControlLen + ibftInitiatorLen + ibftNICLen, 110 NIC1: ibftHeaderLen + ibftControlLen + ibftInitiatorLen + ibftNICLen + ibftTargetLen, 111 Target1: ibftHeaderLen + ibftControlLen + ibftInitiatorLen + ibftNICLen + ibftTargetLen + ibftNICLen, 112 // heap starts here. 113 } 114 115 type acpiIBFTInitiatorFlags uint8 116 117 const ( 118 acpiIBFTInitiatorValid acpiIBFTInitiatorFlags = 1 119 acpiIBFTInitiatorFirmwareBoot = 2 120 ) 121 122 type acpiIBFTInitiator struct { 123 acpiIBFTStructHeader 124 Flags acpiIBFTInitiatorFlags `desc:"Bit0: block valid flag 0 = no, 1 = yes Bit1 : Firmware Boot Selected Flag 0 = no, 1 = yes"` 125 iSNSServer [16]uint8 `offset:"6" desc:"IP Address"` 126 SLPServer [16]uint8 `offset:"22" desc:"IP Address"` 127 PrimaryRadiusServer [16]uint8 `offset:"38" desc:"IP Address"` 128 SecondaryRadiusServer [16]uint8 `offset:"54" desc:"IP Address"` 129 InitiatorNameLength uint16 `offset:"70" desc:"Heap Entry Length"` 130 InitiatorNameOffset uint16 `offset:"72" desc:"Offset from the beginning of the iBFT"` 131 } 132 133 type acpiIBFTNICFlags uint8 134 135 const ( 136 acpiIBFTNICValid acpiIBFTNICFlags = 1 137 acpiIBFTNICBootSelected = 2 138 acpiIBFTNICGlobal = 4 139 ) 140 141 type acpiIBFTNIC struct { 142 acpiIBFTStructHeader 143 Flags acpiIBFTNICFlags `desc:"Bit0: block valid flag 0 = no, 1 = yes Bit1 : Firmware Boot Selected Flag 0 = no, 1 = yes Bit2 : Global / Link Local 0 = Link Local, 1 = Global"` 144 IPAddress [16]uint8 `offset:"6" desc:"IP Address Subnet Mask Prefix 1 22 The mask prefix length. For example, 255.255.255.0 has a prefix length of 24"` 145 Origin uint8 `offset:"23" desc:"See [origin]"` 146 Gateway [16]uint8 `offset:"24" desc:"IP Address"` 147 PrimaryDNS [16]uint8 `offset:"40" desc:"IP Address"` 148 SecondaryDNS [16]uint8 `offset:"56" desc:"IP Address"` 149 DHCP [16]uint8 `offset:"72" desc:"IP Address"` 150 VLAN uint16 `offset:"88" desc:"VLAN"` 151 MACAddress [6]uint8 `offset:"90" desc:"MAC Address"` 152 PCIBDF uint16 `offset:"96" desc:"Bus = 8 bits Device = 5 bits Function = 3 bits"` 153 HostNameLength uint16 `offset:"98" desc:"Heap Entry Length"` 154 HostNameOffset uint16 `offset:"100" desc:"Offset from the beginning of the iBFT In a DHCP scenario this can be the name stored as Option 12 host-name."` 155 } 156 157 type acpiIBFTTargetFlags uint8 158 159 const ( 160 acpiIBFTTargetValid acpiIBFTTargetFlags = 1 161 acpiIBFTTargetBootSelected = 2 162 acpiIBFTTargetRadiusChap = 4 163 acpiIBFTTargetRadiusrChap = 8 164 ) 165 166 type acpiIBFTTarget struct { 167 acpiIBFTStructHeader 168 Flags acpiIBFTTargetFlags `desc:"Bit0: block valid flag 0 = no, 1 = yes Bit1 : Firmware Boot Selected Flag 0 = no, 1 = yes Bit2 : Use Radius CHAP 0 = no, 1 = yes Bit3 : Use Radius rCHAP 0 = no, 1 = yes"` 169 TargetIPAddress [16]uint8 `offset:"6" desc:"IP Address"` 170 TargetIPSocket uint16 `offset:"22" desc:"Likely 3260"` 171 TargetBootLUN uint64 `offset:"24" desc:"See [iscsi] Little Endian Quad Word"` 172 CHAPType uint8 `offset:"32" desc:"0 = No CHAP 1 = CHAP 2 = Mutual CHAP"` 173 NICAssociation uint8 `offset:"33" desc:"NIC Index"` 174 TargetNameLength uint16 `offset:"34" desc:"Heap Entry Length"` 175 TargetNameOffset uint16 `offset:"36" desc:"Offset from the beginning of the iBFT"` 176 CHAPNameLength uint16 `offset:"38" desc:"Heap Entry Length"` 177 CHAPNameOffset uint16 `offset:"40" desc:"Offset from the beginning of the iBFT"` 178 CHAPSecretLength uint16 `offset:"42" desc:"Heap Entry Length"` 179 CHAPSecretOffset uint16 `offset:"44" desc:"Offset from the beginning of the iBFT"` 180 ReverseCHAPNameLength uint16 `offset:"46" desc:"Heap Entry Length"` 181 ReverseCHAPNameOffset uint16 `offset:"48" desc:"Offset from the beginning of the iBFT"` 182 ReverseCHAPSecretLength uint16 `offset:"50" desc:"Heap Entry Length"` 183 ReverseCHAPSecretOffset uint16 `offset:"52" desc:"Offset from the beginning of the iBFT"` 184 } 185 186 // The structs below are designed to be JSON friendly. 187 // Things are strings, but they are typed, to make marshaling 188 // to a table easy and setting up initialized values easy. 189 // The jury is out on whether this is right, but so far 190 // we're finding it very convenient. 191 // Of partiular value is having an exported struct member 192 // which can be ignore at marshal time. Of course, this is also 193 // very easy with tags, but in trying to use both, I found 194 // this schema like scheme easier to write to. 195 196 // IBFTInitiator defines an initiator 197 type IBFTInitiator struct { 198 Valid flag 199 Boot flag 200 SNSServer ipaddr 201 SLPServer ipaddr 202 PrimaryRadiusServer ipaddr 203 SecondaryRadiusServer ipaddr 204 Name sheap 205 } 206 207 // IBFTNIC defines an IBFT NIC structure. 208 type IBFTNIC struct { 209 Valid flag 210 Boot flag 211 Global flag 212 Index flag 213 IPAddress ipaddr 214 SubNet u8 215 Origin u8 216 Gateway ipaddr 217 PrimaryDNS ipaddr 218 SecondaryDNS ipaddr 219 DHCP ipaddr 220 VLAN u16 221 MACAddress mac 222 PCIBDF bdf 223 HostName sheap 224 } 225 226 // IBFTTarget defines an IBFT target, a.k.a. server 227 type IBFTTarget struct { 228 Valid flag 229 Boot flag 230 CHAP flag 231 RCHAP flag // can you do both? Standard implies yes. 232 Index flag // 0 or 1 233 TargetIP sockaddr // in host:port format 234 BootLUN u64 235 ChapType u8 236 Association u8 237 TargetName sheap 238 CHAPName sheap 239 CHAPSecret sheap 240 ReverseCHAPName sheap 241 ReverseCHAPSecret sheap 242 } 243 244 // IBFT defines all the bits of an IBFT users might want to set. 245 type IBFT struct { 246 Generic 247 // Control 248 Multi flag 249 Initiator IBFTInitiator 250 NIC0 IBFTNIC 251 Target0 IBFTTarget 252 NIC1 IBFTNIC 253 Target1 IBFTTarget 254 } 255 256 // Marshal marshals an IBFT to a byte slice. It is somewhat complicated 257 // by the fact that we need to marshal to two things, a header and a heao; 258 // and record pointers to the heap in the head. 259 func (ibft *IBFT) Marshal() ([]byte, error) { 260 var h = HeapTable{Head: &bytes.Buffer{}, Heap: &bytes.Buffer{}} 261 Debug("IBFT") 262 f, err := flags(ibft.Multi) 263 if err != nil { 264 return nil, err 265 } 266 control.Flags = acpiIBFTControlFlags(f) 267 w(h.Head, 1, []byte(rawIBTFHeader), control) 268 Debug("Done IBFTHeader: head is %d bytes", h.Head.Len()) 269 if err := mIBFT(&h, ibft); err != nil { 270 return nil, err 271 } 272 if h.Head.Len() != int(ibftHeadersLen) { 273 return nil, fmt.Errorf("Expected headers len is wrong; got %d, want %d", h.Head.Len(), ibftHeadersLen) 274 } 275 w(h.Head, 1, h.Heap.Bytes()) 276 277 return h.Head.Bytes(), nil 278 } 279 280 // mIBFT is the workhorse of IBFT marshaling. 281 func mIBFT(h *HeapTable, i interface{}) error { 282 nt := reflect.TypeOf(i).Elem() 283 nv := reflect.ValueOf(i).Elem() 284 for i := 0; i < nt.NumField(); i++ { 285 f := nt.Field(i) 286 ft := f.Type 287 fv := nv.Field(i) 288 289 Debug("Field %d: (%d, %d) ml %v %T (%v, %v)", i, h.Head.Len(), h.Heap.Len(), f, f, ft, fv) 290 switch s := fv.Interface().(type) { 291 case Generic: 292 // This is not used yet. When we started this work, we never thought we'd 293 // need to go this far. 294 case IBFTInitiator: 295 f, err := flags(s.Valid, s.Boot) 296 if err != nil { 297 return fmt.Errorf("Parsing %v: %v", []flag{s.Valid, s.Boot}, err) 298 } 299 // we can do this hack with Index; it will only ever be 300 // 0 or 1. The IBFT allows lots, in principle, but only 2, in practice. 301 w(h.Head, ibftInitiator, ibftVersion, ibftInitiatorLen, uint8(0), f) 302 Debug("Wrote initiatior header len is %d", h.Head.Len()) 303 if err := mIBFT(h, &s); err != nil { 304 return err 305 } 306 case IBFTNIC: 307 f, err := flags(s.Valid, s.Boot, s.Global) 308 if err != nil { 309 return fmt.Errorf("Parsing %v: %v", []flag{s.Valid, s.Boot, s.Global}, err) 310 } 311 // we can do this hack with Index; it will only ever be 312 // 0 or 1. See above snarky comment. 313 x, err := flags(s.Index) 314 if err != nil { 315 return fmt.Errorf("Parsing NICIndex %s: %v", s.Index, err) 316 } 317 w(h.Head, ibftNIC, ibftVersion, ibftNICLen, x, f) 318 if err := mIBFT(h, &s); err != nil { 319 return err 320 } 321 322 case IBFTTarget: 323 f, err := flags(s.Valid, s.Boot, s.CHAP, s.RCHAP) 324 if err != nil { 325 return fmt.Errorf("Parsing %v: %v", []flag{s.Valid, s.Boot, s.CHAP, s.RCHAP}, err) 326 } 327 x, err := flags(s.Index) 328 if err != nil { 329 return fmt.Errorf("Parsing NICIndex %s: %v", s.Index, err) 330 } 331 w(h.Head, ibftTarget, ibftVersion, ibftTargetLen, x, f) 332 if err := mIBFT(h, &s); err != nil { 333 return err 334 } 335 336 default: 337 if err := h.Marshal(s); err != nil { 338 return err 339 } 340 } 341 } 342 Debug("mIBFT done, head is %d bytes, heap is %d bytes", h.Head.Len(), h.Heap.Len()) 343 return nil 344 }