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  }