github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/mount/scuzz/ata.go (about) 1 // Copyright 2019 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 scuzz 6 7 import ( 8 "bytes" 9 "encoding/binary" 10 "fmt" 11 "strings" 12 ) 13 14 // direction is the transfer direction. 15 type direction int32 16 17 // These are the acceptable constants for the sg_io_hdr dxfer_direction field 18 // defined by Linux. 19 const ( 20 _SG_DXFER_NONE direction = -1 21 _SG_DXFER_TO_DEV direction = -2 22 _SG_DXFER_FROM_DEV direction = -3 23 ) 24 25 const ( 26 oldSchoolBlockLen = 512 27 28 // ataUsingLBA uint8 = (1 << 6) nolint:golint,unused 29 // ataStatDRQ uint8 = (1 << 3) nolint:golint,unused 30 // ataStatErr uint8 = (1 << 0) nolint:golint,unused 31 32 // read uint8 = 0 nolint:golint,unused 33 //ataTo int32 = 1 34 35 // ioPIO indicates we are doing programmed IO 36 // ioPIO = 0 nolint:golint,unused 37 // ioDMA indicates we are doing DMA 38 // ioDMA = 1 nolint:golint,unused 39 40 // opCheckCondition = 0x02 nolint:golint,unused 41 // opDriverSense = 0x08 nolint:golint,unused 42 43 ata16 = 0x85 44 ata16Len = 16 45 cdbSize = ata16Len 46 47 maxStatusBlockLen = 32 48 49 lba48 = 1 50 nonData = (3 << 1) 51 pioIn = (4 << 1) 52 pioOut = (5 << 1) 53 protoDMA = (6 << 1) 54 55 // tlenNoData = 0 << 0 nolint:golint,unused 56 // tlenFeat = 1 << 0 nolint:golint,unused 57 tlenNsect = 2 << 0 58 59 // tlenBytes = 0 << 2 nolint:golint,unused 60 tlenSectors = 1 << 2 61 62 tdirTo = 0 << 3 63 tdirFrom = 1 << 3 64 checkCond = 1 << 5 65 ) 66 67 type ( 68 // Cmd is an ATA command. See the ATA standard starting in the 80s. 69 Cmd uint8 70 71 // dataBlock is a classic 512-byte ATA block. 72 // It is not completely clear we need to do SG operations 73 // in units of a block, but libraries and commands 74 // seem to think so, and we're not sure we can verify 75 // for all kernels what the rules are. 76 dataBlock [oldSchoolBlockLen]byte 77 78 // Some ATA blocks are best dealt with as blocks of "words". 79 // They are in big-endian order. 80 wordBlock [oldSchoolBlockLen / 2]uint16 81 82 // commandDataBlock defines a command and any associated data. 83 commandDataBlock [ata16Len]byte 84 85 // statusBlock is the status returned from a drive operation. 86 statusBlock [maxStatusBlockLen]byte 87 ) 88 89 func (b dataBlock) toWordBlock() (wordBlock, error) { 90 var w wordBlock 91 err := binary.Read(bytes.NewBuffer(b[:]), binary.BigEndian, &w) 92 return w, err 93 } 94 95 // mustLBA confirms that we are dealing with an LBA device. 96 // This means a post-2003 device. The standard hdparm command deals 97 // with all kinds of obsolete stuff; we don't care. 98 // The spec does not have a name for the bits or the offsets 99 // other than "Obsolete", "Retired", "Must be zero" or "Must be one". 100 // We follow the practice of existing code of not naming them either. 101 func (w wordBlock) mustLBA() error { 102 var check = []struct { 103 off int 104 mask uint16 105 bit uint16 106 }{ 107 {0, 0x8000, 0x8000}, 108 {49, 0x200, 0x200}, 109 {83, 0xc000, 0x4000}, 110 {86, 0x400, 0x400}, 111 } 112 for _, c := range check { 113 v, m, b := w[c.off], c.mask, c.bit 114 if (v & m) != b { 115 return fmt.Errorf("unsupported and probably non-ATA48 ddevice: word at offset %d: %#x and should be %#x", c.off, c.mask&v, b) 116 } 117 } 118 return nil 119 } 120 121 // ataString writes out an ATAString in decoded human-readable form. 122 // 123 // ATA Command Set 4, Section 3.4.9: "Each pair of bytes in an ATA string is 124 // swapped ...". That the space character is used as padding is not mandated in 125 // the spec, but is used in the ATA string example, and on every device we've 126 // tried. 127 func ataString(a []byte) string { 128 var s strings.Builder 129 for i := 0; i < len(a); i += 2 { 130 s.WriteByte(a[i+1]) 131 s.WriteByte(a[i]) 132 } 133 return strings.TrimSpace(string(s.String())) 134 } 135 136 // unpackIdentify unpacks a wordBlock into an Info. 137 func unpackIdentify(s statusBlock, d dataBlock, w wordBlock) *Info { 138 var info Info 139 info.NumberSectors = binary.LittleEndian.Uint64(d[200:208]) 140 141 info.ECCBytes = uint(w[22]) 142 143 info.OrigSerial = string(d[20:40]) 144 info.OrigFirmwareRevision = string(d[46:54]) 145 info.OrigModel = string(d[54:94]) 146 147 info.Serial = ataString(d[20:40]) 148 info.FirmwareRevision = ataString(d[46:54]) 149 info.Model = ataString(d[54:94]) 150 151 info.MasterPasswordRev = w[92] 152 info.SecurityStatus = w[128] 153 info.TrustedComputingSupport = w[48] 154 return &info 155 }