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  }