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  }