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 }