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 }