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 }