github.com/transparency-dev/armored-witness-applet@v0.1.1/trusted_applet/internal/storage/internal.go (about)

     1  // Copyright 2022 The Armored Witness Applet authors. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package storage provides support for accessing the SD/eMMC storage provided
    16  // by the USB Armory.
    17  // Note that these are very low-level primitives, and care must be taken when
    18  // using them not to overwrite existing data (e.g. the unikernel itself!)
    19  package storage
    20  
    21  import (
    22  	"runtime"
    23  
    24  	"github.com/transparency-dev/armored-witness-os/api/rpc"
    25  	"github.com/usbarmory/GoTEE/syscall"
    26  	"github.com/usbarmory/tamago/soc/nxp/usdhc"
    27  	"k8s.io/klog/v2"
    28  )
    29  
    30  var (
    31  	// MaxTransferBytes is the largest transfer we'll attempt.
    32  	// If we're asked to read or write more data than can fit into available DMA memeory
    33  	// we'll had a bad time, so we'll chunk into requests of at most MaxTransferBytes bytes.
    34  	MaxTransferBytes = 32 * 1024
    35  )
    36  
    37  // Device allows writing to one of the USB Armory storage peripherals, hiding some
    38  // of the sharp edges around DMA etc.
    39  type Device struct {
    40  	CardInfo *usdhc.CardInfo
    41  }
    42  
    43  // BlockSize returns the size in bytes of the each block in the underlying storage.
    44  func (d *Device) BlockSize() uint {
    45  	return uint(d.CardInfo.BlockSize)
    46  }
    47  
    48  // WriteBlocks writes the data in b to the device blocks starting at the given block address.
    49  // If the final block to be written is partial, it will be padded with zeroes to ensure that
    50  // full blocks are written.
    51  // Returns the number of blocks written, or an error.
    52  func (d *Device) WriteBlocks(lba uint, b []byte) (uint, error) {
    53  	if len(b) == 0 {
    54  		return 0, nil
    55  	}
    56  	bs := int(d.BlockSize())
    57  	if r := len(b) % bs; r != 0 {
    58  		b = append(b, make([]byte, bs-r)...)
    59  	}
    60  	numBlocks := uint(len(b) / bs)
    61  	for len(b) > 0 {
    62  		bl := len(b)
    63  		if bl > MaxTransferBytes {
    64  			bl = MaxTransferBytes
    65  		}
    66  		xfer := rpc.WriteBlocks{
    67  			LBA:  int(lba),
    68  			Data: b[:bl],
    69  		}
    70  
    71  		// Since this could be a long-running operation, we need to play nice with the scheduler.
    72  		runtime.Gosched()
    73  
    74  		if err := syscall.Call("RPC.WriteBlocks", &xfer, nil); err != nil {
    75  			klog.Infof("syscall.Write(%d, ...) = %v", xfer.LBA, err)
    76  			return 0, err
    77  		}
    78  		b = b[bl:]
    79  		lba += uint(bl / bs)
    80  	}
    81  	return numBlocks, nil
    82  }
    83  
    84  // ReadBlocks reads data from the storage device at the given address into b.
    85  // b must be a multiple of the underlying device's block size.
    86  func (d *Device) ReadBlocks(lba uint, b []byte) error {
    87  	if len(b) == 0 {
    88  		return nil
    89  	}
    90  	bs := int(d.BlockSize())
    91  	for len(b) > 0 {
    92  		bl := len(b)
    93  		if bl > MaxTransferBytes {
    94  			bl = MaxTransferBytes
    95  		}
    96  		xfer := rpc.Read{
    97  			Offset: int64(lba) * int64(bs),
    98  			Size:   int64(bl),
    99  		}
   100  
   101  		// Since this could be a long-running operation, we need to play nice with the scheduler.
   102  		runtime.Gosched()
   103  
   104  		var readBuf []byte
   105  		if err := syscall.Call("RPC.Read", xfer, &readBuf); err != nil {
   106  			klog.Errorf("syscall.Read(%d, %d) = %v", xfer.Offset, xfer.Size, err)
   107  			return err
   108  		}
   109  		copy(b, readBuf)
   110  		b = b[bl:]
   111  		lba += uint(bl / bs)
   112  	}
   113  	return nil
   114  }