Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/rpc/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func rpcExec(Input *C.char, Output **C.char, ErrOutput **C.char) int {
if err != nil {
log.Error(AccessErrMsg)
captureAndRestoreStderr()

return handleError(err)
}

Expand All @@ -81,6 +82,7 @@ func rpcExec(Input *C.char, Output **C.char, ErrOutput **C.char) int {
if err != nil {
log.Error(err.Error())
captureAndRestoreStderr()

return utils.InvalidParameterCombination.Code
}

Expand All @@ -106,6 +108,7 @@ func rpcExec(Input *C.char, Output **C.char, ErrOutput **C.char) int {
func handleError(err error) int {
if customErr, ok := err.(utils.CustomError); ok {
log.Error(customErr.Error())

return customErr.Code
} else {
errorMsg := err.Error()
Expand All @@ -114,6 +117,7 @@ func handleError(err error) int {
if strings.Contains(errorMsg, "unexpected argument") {
return utils.InvalidParameterCombination.Code
}

return utils.GenericFailure.Code
}
}
49 changes: 43 additions & 6 deletions internal/rps/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os/signal"
"sync"
"syscall"
"time"

"github.com/device-management-toolkit/rpc-go/v2/internal/lm"
"github.com/device-management-toolkit/rpc-go/v2/pkg/utils"
Expand Down Expand Up @@ -54,15 +55,51 @@ func NewExecutor(config ExecutorConfig) (Executor, error) {
// TEST CONNECTION TO SEE IF LMS EXISTS
err := client.localManagement.Connect()
if err != nil {
log.Debugf("LMS connection failed: %v", err)

if config.LocalTlsEnforced {
return client, utils.LMSConnectionFailed
// Try non-TLS LMS as fallback before LME
nonTlsLMS := lm.NewLMSConnection(utils.LMSAddress, utils.LMSPort, false, lmDataChannel, lmErrorChannel, config.ControlMode, config.SkipAmtCertCheck)

err = nonTlsLMS.Connect()
if err == nil {
client.localManagement = nonTlsLMS
client.localManagement.Close() // Test connection, close for now
} else {
log.Trace("LMS TLS connection failed. Attempting LME fallback...")
}
} else {
// Try TLS LMS as fallback before LME
tlsLMS := lm.NewLMSConnection(utils.LMSAddress, utils.LMSTLSPort, true, lmDataChannel, lmErrorChannel, config.ControlMode, config.SkipAmtCertCheck)

err = tlsLMS.Connect()
if err == nil {
client.localManagement = tlsLMS
client.localManagement.Close() // Test connection, close for now
} else {
log.Trace("LMS not running. Using LME Connection\\n")
}
}
// client.localManagement.Close()
log.Trace("LMS not running. Using LME Connection\n")

client.localManagement = lm.NewLMEConnection(lmDataChannel, lmErrorChannel, client.waitGroup)
client.isLME = true
client.localManagement.Initialize()
if err != nil {
// Wait a moment before attempting LME to allow any MEI operations to complete
time.Sleep(2 * time.Second)

// Try LME connection
lmeConnection := lm.NewLMEConnection(lmDataChannel, lmErrorChannel, client.waitGroup)

err = lmeConnection.Initialize()
if err == nil {
client.localManagement = lmeConnection
client.isLME = true
} else {
if config.LocalTlsEnforced {
return client, utils.LMSConnectionFailed
}

return client, utils.LMSConnectionFailed
}
}
} else {
log.Trace("Using existing LMS\n")
client.localManagement.Close()
Expand Down
137 changes: 111 additions & 26 deletions pkg/heci/linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"encoding/binary"
"os"
"syscall"
"time"
"unsafe"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -42,20 +43,65 @@ func NewDriver() *Driver {
return &Driver{}
}

// ResetMEIDevice attempts to reset MEI device state by waiting for it to become available
func (heci *Driver) ResetMEIDevice() error {
if heci.meiDevice != nil {
heci.meiDevice.Close()
heci.meiDevice = nil
}

log.Debug("Waiting for MEI device to reset...")
time.Sleep(10 * time.Second)

return nil
}

func (heci *Driver) Init(useLME, useWD bool) error {
var err error

heci.meiDevice, err = os.OpenFile(Device, syscall.O_RDWR, 0)
if err != nil {
if err.Error() == "open /dev/mei0: permission denied" {
log.Error("need administrator privileges")
} else if err.Error() == "open /dev/mei0: no such file or directory" {
log.Error("AMT not found: MEI/driver is missing or the call to the HECI driver failed")
} else {
log.Error("Cannot open MEI Device")
// Close existing connection if switching to LME to ensure clean state
if heci.meiDevice != nil && useLME {
heci.meiDevice.Close()
heci.meiDevice = nil

time.Sleep(3 * time.Second)
}

// For PTHI/WD, always reopen to ensure fresh connection
// For LME, only open if not already open
if !useLME || heci.meiDevice == nil {
// Close any existing connection for PTHI
if heci.meiDevice != nil && !useLME {
heci.meiDevice.Close()
heci.meiDevice = nil
}

return err
// Open MEI device with retry for device busy
for attempt := 1; attempt <= 2; attempt++ {
heci.meiDevice, err = os.OpenFile(Device, syscall.O_RDWR, 0)
if err == nil {
break
}

if err.Error() == "open /dev/mei0: permission denied" {
log.Error("need administrator privileges")

return err
} else if err.Error() == "open /dev/mei0: no such file or directory" {
log.Error("AMT not found: MEI/driver is missing or the call to the HECI driver failed")

return err
} else if err.Error() == "open /dev/mei0: device or resource busy" && attempt == 1 {
log.Debug("MEI device busy, waiting before retry...")
time.Sleep(5 * time.Second)

continue
} else {
log.Error("Cannot open MEI Device")

return err
}
}
}

data := CMEIConnectClientData{}
Expand All @@ -67,14 +113,7 @@ func (heci *Driver) Init(useLME, useWD bool) error {
data.data = MEI_IAMTHIF
}

// we try up to 3 times in case the resource/device is still busy from previous call.
for i := 0; i < 3; i++ {
err = Ioctl(heci.meiDevice.Fd(), IOCTL_MEI_CONNECT_CLIENT, uintptr(unsafe.Pointer(&data)))
if err == nil {
break
}
}

err = Ioctl(heci.meiDevice.Fd(), IOCTL_MEI_CONNECT_CLIENT, uintptr(unsafe.Pointer(&data)))
if err != nil {
return err
}
Expand All @@ -97,30 +136,76 @@ func (heci *Driver) GetBufferSize() uint32 {
}

func (heci *Driver) SendMessage(buffer []byte, done *uint32) (bytesWritten int, err error) {
size, err := syscall.Write(int(heci.meiDevice.Fd()), buffer)
if err != nil {
// Validate file descriptor before attempting write
if heci.meiDevice == nil {
return 0, syscall.EBADF
}

// Retry write operations on interrupted system call
for i := 0; i < 3; i++ {
size, err := syscall.Write(int(heci.meiDevice.Fd()), buffer)
if err == nil {
return size, nil
}

// Retry on interrupted system call
if err == syscall.EINTR {
time.Sleep(50 * time.Millisecond)

continue
}

return 0, err
}

return size, nil
return 0, syscall.EINTR
}

func (driver *Driver) ReceiveMessage(buffer []byte, done *uint32) (bytesRead int, err error) {
read, err := unix.Read(int(driver.meiDevice.Fd()), buffer)
if err != nil {
// Validate file descriptor before attempting read
if driver.meiDevice == nil {
return 0, syscall.EBADF
}

// Retry read operations on interrupted system call
for i := 0; i < 3; i++ {
read, err := unix.Read(int(driver.meiDevice.Fd()), buffer)
if err == nil {
return read, nil
}

// Retry on interrupted system call
if err == syscall.EINTR {
time.Sleep(50 * time.Millisecond)

continue
}

return 0, err
}

return read, nil
return 0, syscall.EINTR
}

func Ioctl(fd, op, arg uintptr) error {
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, op, arg)
if ep != 0 {
// Retry IOCTL on interrupted system call (EINTR)
for i := 0; i < 3; i++ {
_, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, op, arg)
if ep == 0 {
return nil
}

// Retry on interrupted system call (EINTR = 4)
if ep == syscall.EINTR {
time.Sleep(100 * time.Millisecond)

continue
}

return ep
}

return nil
return syscall.EINTR
}

func (heci *Driver) Close() {
Expand Down
Loading