github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/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 ) 12 13 const ( 14 // golangci's requirements around unused constants 15 // are inconsistent with how one writes user level drivers. 16 // Generally, in a user level driver, one wants to lay out all 17 // constants for all registers, unused or not; it makes 18 // the code far easier to read and work with. 19 // Anyway, I'll comment stuff out until used, since the directives 20 // don't work and are ugly. 21 // Sure, you can add a nolint tag, but as your 22 // bidi int8 = -4 nolint:golint,unused 23 from direction = -3 24 to direction = -2 25 //none direction = -1 26 27 oldSchoolBlockLen = 512 28 29 // ataUsingLBA uint8 = (1 << 6) nolint:golint,unused 30 // ataStatDRQ uint8 = (1 << 3) nolint:golint,unused 31 // ataStatErr uint8 = (1 << 0) nolint:golint,unused 32 33 // read uint8 = 0 nolint:golint,unused 34 //ataTo int32 = 1 35 36 // ioPIO indicates we are doing programmed IO 37 // ioPIO = 0 nolint:golint,unused 38 // ioDMA indicates we are doing DMA 39 // ioDMA = 1 nolint:golint,unused 40 41 // opCheckCondition = 0x02 nolint:golint,unused 42 // opDriverSense = 0x08 nolint:golint,unused 43 44 ata16 = 0x85 45 ata16Len = 16 46 cdbSize = ata16Len 47 48 maxStatusBlockLen = 32 49 50 lba48 = 1 51 nonData = (3 << 1) 52 pioIn = (4 << 1) 53 pioOut = (5 << 1) 54 protoDMA = (6 << 1) 55 56 // tlenNoData = 0 << 0 nolint:golint,unused 57 // tlenFeat = 1 << 0 nolint:golint,unused 58 tlenNsect = 2 << 0 59 60 // tlenBytes = 0 << 2 nolint:golint,unused 61 tlenSectors = 1 << 2 62 63 tdirTo = 0 << 3 64 tdirFrom = 1 << 3 65 checkCond = 1 << 5 66 ) 67 68 // Commands. We only export those we implement. 69 const ( 70 // identify gets identify information 71 identify = 0xec 72 // securityUnlock unlocks the drive with a given 32-byte password 73 securityUnlock = 0xf2 74 ) 75 76 type ( 77 // Cmd is an ATA command. See the ATA standard starting in the 80s. 78 Cmd uint8 79 80 // dataBlock is a classic 512-byte ATA block. 81 // It is not completely clear we need to do SG operations 82 // in units of a block, but libraries and commands 83 // seem to think so, and we're not sure we can verify 84 // for all kernels what the rules are. 85 dataBlock [oldSchoolBlockLen]byte 86 87 // Some ATA blocks are best dealt with as blocks of "words". 88 // They are in big-endian order. 89 wordBlock [oldSchoolBlockLen / 2]uint16 90 91 // commandDataBlock defines a command and any associated data. 92 commandDataBlock [ata16Len]byte 93 94 // statusBlock is the status returned from a drive operation. 95 statusBlock [maxStatusBlockLen]byte 96 97 direction int32 98 99 // ataString is 10 words. Each word contains two bytes of the string, 100 // in BigEndian order, i.e., byte order is 1032547698 101 // Hence, code can not just take a string from the 102 // drive and use it: it must swap the bytes in each word. 103 ataString [10]uint16 104 ) 105 106 func (b dataBlock) toWordBlock() (wordBlock, error) { 107 var w wordBlock 108 err := binary.Read(bytes.NewBuffer(b[:]), binary.BigEndian, &w) 109 return w, err 110 } 111 112 // mustLBA confirms that we are dealing with an LBA device. 113 // This means a post-2003 device. The standard hdparm command deals 114 // with all kinds of obsolete stuff; we don't care. 115 // The spec does not have a name for the bits or the offsets 116 // other than "Obsolete", "Retired", "Must be zero" or "Must be one". 117 // We follow the practice of existing code of not naming them either. 118 func (w wordBlock) mustLBA() error { 119 var check = []struct { 120 off int 121 mask uint16 122 bit uint16 123 }{ 124 {0, 0x8000, 0x8000}, 125 {49, 0x200, 0x200}, 126 {83, 0xc000, 0x4000}, 127 {86, 0x400, 0x400}, 128 } 129 for _, c := range check { 130 v, m, b := w[c.off], c.mask, c.bit 131 if (v & m) != b { 132 return fmt.Errorf("unsupported and probably non-ATA48 ddevice: word at offset %d: 0x%#x and should be 0x%#x", c.off, c.mask&v, b) 133 } 134 } 135 return nil 136 } 137 138 // String is a stringer for ataString 139 func (a ataString) String() string { 140 var s []byte 141 for i := range a { 142 s = append(s, byte(a[i]), byte(a[i]>>8)) 143 } 144 return string(s) 145 } 146 147 // unpackIdentify unpacks a wordBlock into an Info. 148 func unpackIdentify(s statusBlock, w wordBlock) (*Info, error) { 149 // Double check that this is a true LBA48 packet. 150 if err := w.mustLBA(); err != nil { 151 return nil, err 152 } 153 var nsects uint64 154 var info = &Info{} 155 for i := 103; i >= 104; i-- { 156 nsects <<= 16 157 nsects |= uint64(w[i]) 158 } 159 info.NumberSectors = nsects 160 // If you look at the Linux hdparm source, you will see it uses 161 // some values this function does not. 162 // Per the standard, 163 // offsets 1, 3, and 6 are obsolete; 164 // 4, 5, 9 are retired. 165 // hdparm presents these as RawCHS, TrkSize, and SecSize. 166 // We ignore them. 167 info.ECCBytes = uint(w[22]) 168 return info, nil 169 }