github.com/zaolin/u-root@v0.0.0-20200428085104-64aaafd46c6d/pkg/mei/mei_linux.go (about)

     1  // Copyright 2020 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 mei implements a wrapper on top of Linux's MEI (Intel ME Interface,
     6  // formerly known as HECI). This module requires Linux, and the `mei_me` driver.
     7  // Once loaded, this driver will expose a `/dev/mei0` device, that can be
     8  // accessed through this library.
     9  package mei
    10  
    11  import (
    12  	"encoding/binary"
    13  	"fmt"
    14  	"os"
    15  	"syscall"
    16  	"unsafe"
    17  )
    18  
    19  // DefaultMEIDevicePath is the path of the default MEI device. This file will be
    20  // present if the "mei_me" kernel module is loaded.
    21  var DefaultMEIDevicePath = "/dev/mei0"
    22  
    23  // HECIGuids maps the known HECI GUIDs to their values. The MEI interface wants
    24  // little-endian. See all the GUIDs at
    25  // https://github.com/intel/lms/blob/master/MEIClient/Include/HECI_if.h
    26  var (
    27  	// "8e6a6715-9abc-4043-88ef-9e39c6f63e0f"
    28  	MKHIGuid = ClientGUID{0x15, 0x67, 0x6a, 0x8e, 0xbc, 0x9a, 0x43, 0x40, 0x88, 0xef, 0x9e, 0x39, 0xc6, 0xf6, 0x3e, 0xf}
    29  )
    30  
    31  // see include/uapi/linux/mei.h
    32  var (
    33  	IoctlMEIConnectClient = IOWR('H', 0x01, uintptr(len(ClientGUID{})))
    34  )
    35  
    36  // ClientGUID is the data buffer to pass to `ioctl` to connect to
    37  // MEI. See include/uapi/linux/mei.h .
    38  type ClientGUID [16]byte
    39  
    40  // ClientProperties is the data buffer returned by `ioctl` after connecting to
    41  // MEI. See include/uapi/linux/mei.h .
    42  type ClientProperties [6]byte
    43  
    44  // MaxMsgLength is the maximum size of a message for this client.
    45  func (c ClientProperties) MaxMsgLength() uint32 {
    46  	return binary.LittleEndian.Uint32(c[:4])
    47  }
    48  
    49  // ProtocolVersion is this client's protocol version.
    50  func (c ClientProperties) ProtocolVersion() uint8 {
    51  	return c[4]
    52  }
    53  
    54  // MEI represents an Intel ME Interface object.
    55  type MEI struct {
    56  	fd               *os.File
    57  	ClientProperties ClientProperties
    58  }
    59  
    60  // Open opens the specified MEI device, using the client type defined by the
    61  // given name. See `HECIGuids` in this package.
    62  func (m *MEI) Open(meiPath string, guid ClientGUID) error {
    63  	fd, err := os.OpenFile(meiPath, os.O_RDWR, 0755)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	data := [16]byte(guid)
    68  	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), IoctlMEIConnectClient, uintptr(unsafe.Pointer(&data))); err != 0 {
    69  		return fmt.Errorf("ioctl IOCTL_MEI_CONNECT_CLIENT failed: %v", err)
    70  	}
    71  	// can be racy, unless protected by a mutex
    72  	m.fd = fd
    73  	copy(m.ClientProperties[:], data[:])
    74  	return nil
    75  }
    76  
    77  // Close closes the MEI device, if open, and does nothing otherwise.
    78  func (m *MEI) Close() error {
    79  	if m.fd != nil {
    80  		err := m.fd.Close()
    81  		m.fd = nil
    82  		return err
    83  	}
    84  	return nil
    85  }
    86  
    87  // Write writes to the MEI file descriptor.
    88  func (m *MEI) Write(p []byte) (int, error) {
    89  	// use syscall.Write instead of m.fd.Write to avoid epoll
    90  	return syscall.Write(int(m.fd.Fd()), p)
    91  }
    92  
    93  // Read reads from the MEI file descriptor.
    94  func (m *MEI) Read(p []byte) (int, error) {
    95  	// use syscall.Read instead of m.fd.Read to avoid epoll
    96  	return syscall.Read(int(m.fd.Fd()), p)
    97  }