github.com/usbarmory/GoTEE@v0.0.0-20240405084336-c52770d9fcdb/syscall/rpc.go (about)

     1  // Copyright (c) WithSecure Corporation
     2  // https://foundry.withsecure.com
     3  //
     4  // Use of this source code is governed by the license
     5  // that can be found in the LICENSE file.
     6  
     7  package syscall
     8  
     9  import (
    10  	"io"
    11  	"net/rpc"
    12  	"net/rpc/jsonrpc"
    13  	"sync"
    14  )
    15  
    16  var mux sync.Mutex
    17  
    18  // Stream implements a data stream interface to exchange data buffers between
    19  // the security monitor and a lower privilege execution context over syscalls.
    20  //
    21  // It is used by NewClient() to stream JSON-RPC calls from an applet and
    22  // receive responses from the supervisor, over syscalls.
    23  //
    24  // The implementation is not safe against concurrent reads and writes, which
    25  // should be avoided.
    26  type Stream struct {
    27  	// ReadSyscall is the syscall number associated to Read()
    28  	ReadSyscall uint
    29  	// ReadSyscall is the syscall number associated to Write()
    30  	WriteSyscall uint
    31  }
    32  
    33  // Read reads up to len(p) bytes into p, it never returns an error. The read is
    34  // requested, through the Stream ReadSyscall, to the supervisor.
    35  func (s *Stream) Read(p []byte) (n int, err error) {
    36  	if n = Read(s.ReadSyscall, p, uint(len(p))); n <= 0 {
    37  		return 0, io.EOF
    38  	}
    39  
    40  	return
    41  }
    42  
    43  // Write writes len(p) bytes from p to the underlying data stream, it never
    44  // returns an error. The write is issued, through the Stream WriteSyscall, to
    45  // the supervisor.
    46  func (s *Stream) Write(p []byte) (n int, err error) {
    47  	n = len(p)
    48  	Write(s.WriteSyscall, p, uint(n))
    49  	return
    50  }
    51  
    52  // Close has no effect.
    53  func (s *Stream) Close() error {
    54  	return nil
    55  }
    56  
    57  // NewClient returns a new client suitable for RPC calls to the supervisor. The
    58  // client automatically closes after Call() is invoked on it the first time,
    59  // therefore a new instance is needed for each call (also see Call()).
    60  func NewClient() *rpc.Client {
    61  	return jsonrpc.NewClient(&Stream{
    62  		ReadSyscall:  SYS_RPC_RES,
    63  		WriteSyscall: SYS_RPC_REQ,
    64  	})
    65  }
    66  
    67  // Call is a convenience method that issues an RPC call on a disposable client
    68  // created with NewClient(), to avoid concurrent reads and writes a mutex is
    69  // held to prevent interleaved invocations.
    70  func Call(serviceMethod string, args interface{}, reply interface{}) error {
    71  	mux.Lock()
    72  	defer mux.Unlock()
    73  
    74  	return NewClient().Call(serviceMethod, args, reply)
    75  }