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