github.com/zaolin/u-root@v0.0.0-20200428085104-64aaafd46c6d/pkg/boot/ibft/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 ibft defines the iSCSI Boot Firmware Table.
     6  //
     7  // An iBFT is placed in memory by a bootloader to tell an OS which iSCSI target
     8  // it was booted from and what additional iSCSI targets it shall connect to.
     9  //
    10  // An iBFT is typically placed in one of two places:
    11  //
    12  //  (1) an ACPI table named "iBFT", or
    13  //
    14  //  (2) in the 512K-1M physical memory range identified by its first 4 bytes.
    15  //
    16  // However, this package doesn't concern itself with the placement, just the
    17  // marshaling of the table's bytes.
    18  package ibft
    19  
    20  import (
    21  	"encoding/binary"
    22  	"fmt"
    23  	"net"
    24  
    25  	"github.com/u-root/u-root/pkg/uio"
    26  )
    27  
    28  var (
    29  	signature  = [4]byte{'i', 'B', 'F', 'T'}
    30  	oemID      = [6]byte{'G', 'o', 'o', 'g', 'l', 'e'}
    31  	oemTableID = [8]byte{'G', 'o', 'o', 'g', 'I', 'B', 'F', 'T'}
    32  )
    33  
    34  type structureID uint8
    35  
    36  const (
    37  	reserved structureID = iota
    38  	ibftControlID
    39  	ibftInitiatorID
    40  	ibftNICID
    41  	ibftTargetID
    42  	ibftExtensionsID
    43  )
    44  
    45  const (
    46  	ibftControlLen   uint16 = 18
    47  	ibftInitiatorLen uint16 = 74
    48  	ibftNICLen       uint16 = 102
    49  	ibftTargetLen    uint16 = 54
    50  )
    51  
    52  type acpiHeader struct {
    53  	Signature       [4]byte
    54  	Length          uint32
    55  	Revision        uint8
    56  	Checksum        uint8
    57  	OEMID           [6]byte
    58  	OEMTableID      [8]byte
    59  	OEMRevision     uint64
    60  	CreatorID       uint64
    61  	CreatorRevision uint64
    62  }
    63  
    64  func flags(s ...bool) uint8 {
    65  	var i uint8
    66  	for bit, f := range s {
    67  		if f {
    68  			i |= 1 << uint8(bit)
    69  		} else {
    70  			i &= ^(1 << uint8(bit))
    71  		}
    72  	}
    73  	return i
    74  }
    75  
    76  func writeIP6(l *uio.Lexer, ip net.IP) {
    77  	if ip == nil {
    78  		l.WriteBytes(net.IPv6zero)
    79  	} else {
    80  		l.WriteBytes(ip.To16())
    81  	}
    82  }
    83  
    84  // ibftStructHeader is the header of each iBFT structure, as defined by iBFT Spec 1.4.2.
    85  type ibftStructHeader struct {
    86  	StructureID structureID
    87  	Version     uint8
    88  	Length      uint16
    89  	Index       uint8
    90  	Flags       uint8
    91  }
    92  
    93  // Initiator defines an iSCSI initiator.
    94  type Initiator struct {
    95  	// Name is the name of the initiator.
    96  	//
    97  	// Some servers apparently use initiator names for permissions.
    98  	Name string
    99  
   100  	Valid bool
   101  
   102  	// Boot indicates that this initiator was used to boot.
   103  	Boot bool
   104  
   105  	// I have no clue what this stuff is.
   106  	SNSServer             net.IP
   107  	SLPServer             net.IP
   108  	PrimaryRadiusServer   net.IP
   109  	SecondaryRadiusServer net.IP
   110  }
   111  
   112  func (i *Initiator) marshal(h *heapTable) {
   113  	header := ibftStructHeader{
   114  		StructureID: ibftInitiatorID,
   115  		Version:     1,
   116  		Length:      ibftInitiatorLen,
   117  		Index:       0,
   118  		Flags:       flags(i.Valid, i.Boot),
   119  	}
   120  	h.Table.WriteData(&header)
   121  
   122  	writeIP6(h.Table, i.SNSServer)
   123  	writeIP6(h.Table, i.SLPServer)
   124  	writeIP6(h.Table, i.PrimaryRadiusServer)
   125  	writeIP6(h.Table, i.SecondaryRadiusServer)
   126  	h.writeHeap([]byte(i.Name))
   127  }
   128  
   129  // BDF is a Bus/Device/Function Identifier of a PCI device.
   130  type BDF struct {
   131  	Bus      uint8
   132  	Device   uint8
   133  	Function uint8
   134  }
   135  
   136  func (b BDF) marshal(h *heapTable) {
   137  	h.Table.Write16(uint16(uint16(b.Bus)<<8 | uint16(b.Device)<<3 | uint16(b.Function)))
   138  }
   139  
   140  // NIC defines NIC network configuration such as IP, gateway, DNS.
   141  type NIC struct {
   142  	// Valid NIC.
   143  	Valid bool
   144  
   145  	// Boot indicates this NIC was used to boot from.
   146  	Boot bool
   147  
   148  	// Global indicates a globally reachable IP (as opposed to a link-local IP).
   149  	Global bool
   150  
   151  	// IPNet is the IP and subnet mask for this network interface.
   152  	IPNet *net.IPNet
   153  
   154  	// Origin. Nobody knows what this is. Nobody cares.
   155  	//
   156  	// The spec links to a Microsoft.com 404 page. Yay.
   157  	Origin uint8
   158  
   159  	// Gateway is the network gateway. The gateway must be within the IPNet.
   160  	Gateway net.IP
   161  
   162  	// PrimaryDNS is the primary DNS server.
   163  	PrimaryDNS net.IP
   164  
   165  	// SecondaryDNS is a secondary DNS server.
   166  	SecondaryDNS net.IP
   167  
   168  	// DHCPServer is the address for the DHCP server, if one was used.
   169  	DHCPServer net.IP
   170  
   171  	// VLAN is the VLAN for this network interface.
   172  	VLAN uint16
   173  
   174  	// MACAddress is the MAC of the network interface.
   175  	MACAddress net.HardwareAddr
   176  
   177  	// PCIBDF is the Bus/Device/Function identifier of the PCI NIC device.
   178  	PCIBDF BDF
   179  
   180  	// HostName is the host name of the machine.
   181  	HostName string
   182  }
   183  
   184  func (n *NIC) marshal(h *heapTable) {
   185  	header := ibftStructHeader{
   186  		StructureID: ibftNICID,
   187  		Version:     1,
   188  		Length:      ibftNICLen,
   189  		Index:       0,
   190  		Flags:       flags(n.Valid, n.Boot, n.Global),
   191  	}
   192  	h.Table.WriteData(&header)
   193  
   194  	// IP + subnet mask prefix.
   195  	if n.IPNet == nil {
   196  		writeIP6(h.Table, net.IPv6zero)
   197  		h.Table.Write8(0)
   198  	} else {
   199  		writeIP6(h.Table, n.IPNet.IP)
   200  		ones, _ := n.IPNet.Mask.Size()
   201  		h.Table.Write8(uint8(ones))
   202  	}
   203  
   204  	h.Table.Write8(n.Origin)
   205  
   206  	writeIP6(h.Table, n.Gateway)
   207  	writeIP6(h.Table, n.PrimaryDNS)
   208  	writeIP6(h.Table, n.SecondaryDNS)
   209  	writeIP6(h.Table, n.DHCPServer)
   210  
   211  	h.Table.Write16(n.VLAN)
   212  	copy(h.Table.WriteN(6), n.MACAddress)
   213  	n.PCIBDF.marshal(h)
   214  	h.writeHeap([]byte(n.HostName))
   215  }
   216  
   217  // Target carries info about an iSCSI target server.
   218  type Target struct {
   219  	Valid bool
   220  	Boot  bool
   221  
   222  	CHAP  bool
   223  	RCHAP bool
   224  
   225  	// Target is the server to connect to.
   226  	Target *net.TCPAddr
   227  
   228  	// BootLUN is the LUN to connect to.
   229  	BootLUN uint64
   230  
   231  	CHAPType uint8
   232  
   233  	// NICAssociation is the Index of the NIC.
   234  	NICAssociation uint8
   235  
   236  	// TargetName is the name of the iSCSI target.
   237  	//
   238  	// The target name must be a valid iSCSI Qualifier Name or EUI.
   239  	TargetName string
   240  
   241  	CHAPName          string
   242  	CHAPSecret        string
   243  	ReverseCHAPName   string
   244  	ReverseCHAPSecret string
   245  }
   246  
   247  func (t *Target) marshal(h *heapTable) {
   248  	header := ibftStructHeader{
   249  		StructureID: ibftTargetID,
   250  		Version:     1,
   251  		Length:      ibftTargetLen,
   252  		Index:       0,
   253  		Flags:       flags(t.Valid, t.Boot, t.CHAP, t.RCHAP),
   254  	}
   255  	h.Table.WriteData(&header)
   256  
   257  	if t.Target == nil {
   258  		writeIP6(h.Table, net.IPv6zero)
   259  		h.Table.Write16(0)
   260  	} else {
   261  		writeIP6(h.Table, t.Target.IP)
   262  		h.Table.Write16(uint16(t.Target.Port))
   263  	}
   264  	h.Table.Write64(t.BootLUN)
   265  	h.Table.Write8(t.CHAPType)
   266  	h.Table.Write8(t.NICAssociation)
   267  
   268  	h.writeHeap([]byte(t.TargetName))
   269  	h.writeHeap([]byte(t.CHAPName))
   270  	h.writeHeap([]byte(t.CHAPSecret))
   271  	h.writeHeap([]byte(t.ReverseCHAPName))
   272  	h.writeHeap([]byte(t.ReverseCHAPSecret))
   273  }
   274  
   275  // IBFT defines the entire iSCSI boot firmware table.
   276  type IBFT struct {
   277  	SingleLoginMode bool
   278  
   279  	// Initiator offset: 0x50
   280  	Initiator Initiator
   281  
   282  	// NIC offset: 0xa0
   283  	NIC0 NIC
   284  
   285  	// Target offset: 0x110
   286  	Target0 Target
   287  }
   288  
   289  // String is a short summary of the iBFT contents.
   290  func (i *IBFT) String() string {
   291  	return fmt.Sprintf("iBFT(iSCSI target=%s, IP=%s)", i.Target0.Target, i.NIC0.IPNet)
   292  }
   293  
   294  type heapTable struct {
   295  	heapOffset uint64
   296  
   297  	Table *uio.Lexer
   298  	Heap  *uio.Lexer
   299  }
   300  
   301  func (h *heapTable) writeHeap(item []byte) {
   302  	if len(item) == 0 {
   303  		// Length.
   304  		h.Table.Write16(0)
   305  
   306  		// Offset.
   307  		h.Table.Write16(0)
   308  	} else {
   309  		offset := h.heapOffset + uint64(h.Heap.Len())
   310  
   311  		// Length.
   312  		h.Table.Write16(uint16(len(item)))
   313  
   314  		// Offset from beginning of iBFT.
   315  		h.Table.Write16(uint16(offset))
   316  
   317  		// Write a null-terminated array item.
   318  		//
   319  		// iBFT Spec, Section 1.3.5: "All array items stored in the
   320  		// Heap area will be followed by a separate NULL. This
   321  		// terminating NULL is not counted as part of the array
   322  		// [item's] length."
   323  		h.Heap.WriteBytes(item)
   324  		h.Heap.Write8(0)
   325  	}
   326  }
   327  
   328  type ibftControl struct {
   329  	SingleLoginMode bool
   330  	Initiator       uint16
   331  	NIC0            uint16
   332  	Target0         uint16
   333  	NIC1            uint16
   334  	Target1         uint16
   335  }
   336  
   337  func (c ibftControl) marshal(h *heapTable) {
   338  	header := ibftStructHeader{
   339  		StructureID: ibftControlID,
   340  		Version:     1,
   341  		Length:      ibftControlLen,
   342  		Index:       0,
   343  		Flags:       flags(c.SingleLoginMode),
   344  	}
   345  	h.Table.WriteData(&header)
   346  
   347  	// Extensions: none. EVER.
   348  	h.Table.Write16(0)
   349  
   350  	h.Table.Write16(c.Initiator)
   351  	h.Table.Write16(c.NIC0)
   352  	h.Table.Write16(c.Target0)
   353  	h.Table.Write16(c.NIC1)
   354  	h.Table.Write16(c.Target1)
   355  }
   356  
   357  func gencsum(b []byte) byte {
   358  	var csum byte
   359  	for _, bb := range b {
   360  		csum += bb
   361  	}
   362  	return ^csum + 1
   363  }
   364  
   365  const lengthOffset = 4
   366  const checksumOffset = 9
   367  
   368  func fixACPIHeader(b []byte) []byte {
   369  	binary.LittleEndian.PutUint16(b[lengthOffset:], uint16(len(b)))
   370  	b[checksumOffset] = gencsum(b)
   371  	return b
   372  }
   373  
   374  const (
   375  	controlOffset   = 0x30
   376  	initiatorOffset = 0x48
   377  	nic0Offset      = 0x98
   378  	target0Offset   = 0x100
   379  	heapOffset      = 0x138
   380  )
   381  
   382  // Marshal returns a binary representation of the iBFT.
   383  //
   384  // Pointers within an iBFT is relative, so this can be placed anywhere
   385  // necessary.
   386  func (i *IBFT) Marshal() []byte {
   387  	h := &heapTable{
   388  		heapOffset: heapOffset,
   389  
   390  		Table: uio.NewLittleEndianBuffer(nil),
   391  		Heap:  uio.NewLittleEndianBuffer(nil),
   392  	}
   393  
   394  	header := &acpiHeader{
   395  		Signature:       signature,
   396  		Length:          0,
   397  		Revision:        1,
   398  		Checksum:        0,
   399  		OEMID:           oemID,
   400  		OEMTableID:      oemTableID,
   401  		OEMRevision:     0,
   402  		CreatorID:       0,
   403  		CreatorRevision: 0,
   404  	}
   405  	h.Table.WriteData(header)
   406  
   407  	// iBFT spec, Section 1.4.4.4 "Each structure must be aligned on an 8
   408  	// byte boundary."
   409  
   410  	// 0x30
   411  	h.Table.Align(8)
   412  	control := ibftControl{
   413  		SingleLoginMode: i.SingleLoginMode,
   414  		Initiator:       initiatorOffset,
   415  		NIC0:            nic0Offset,
   416  		Target0:         target0Offset,
   417  	}
   418  	control.marshal(h)
   419  
   420  	// 0x48
   421  	h.Table.Align(8)
   422  	i.Initiator.marshal(h)
   423  
   424  	// 0x98
   425  	h.Table.Align(8)
   426  	i.NIC0.marshal(h)
   427  
   428  	// 0x100
   429  	h.Table.Align(8)
   430  	i.Target0.marshal(h)
   431  
   432  	// 0x138
   433  	h.Table.Align(8)
   434  	h.Table.WriteBytes(h.Heap.Data())
   435  
   436  	return fixACPIHeader(h.Table.Data())
   437  }