Bios工程师手边事—SATA

本文详细介绍了计算机中SATA接口的工作原理,包括IDE和AHCI两种模式的初始化步骤,以及ATA/ATAPI命令如READ SECTORS和IDENTIFY DEVICE的执行过程。在IDE模式下,通过IO方式进行访问,而在AHCI模式下,通过构建Command List和Receive FIS实现命令传输。文章还探讨了两种模式下的数据传输方式和结构差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:诫命是是灯,法则是光,训诲的责备是生命的道。


作为计算机,除了运算能力,还要求有存储能力。就像一个人一样,有逻辑思维能力还不行,还需要有上佳的记忆能力。只有这样,才能凭着人生丰富的阅历,对未来的事情做出聪颖的判断。

SATA就是为计算机存储所做的一个接口。INTEL SATA控制器一般可以支持三种操作模式:IDE,AHCI,RAID。

对于RAID操作模式,BIOS代码中有一个专门的Legacy OptionROM和一个二进制的EFI driver来初始化和操作,具体细节无法看到。但IDE模式和AHCI模式没有那种限制,我们可以看到其所有的代码和操作,下面就谈一下IDE和AHCI吧。

 

1. 初始化控制器

1.1 设置模式

主要设置两方面。一方面为PI寄存器,详情如下:

MODE

Base Class

Sub Class

Program Interface

AHCI

01

06

01

Legacy IDE

01

01

8X

Enhanced IDE

01

01

8F

 

另一方面,要设置MAP寄存器的SATA Mode Select位。

MODE

Value

00

IDE

01

AHCI

10

RAID

 

1.2 使能SATA端口

将PCS的PxE位置1,然后检查相应的PxP位。如果PxP为1,则说明PortX有设备存在。否则,说明没有此PortX没有下挂任何设备。

如果挂载设备,我们要保持使PxE的使能状态。如果没有下挂设备,我们要将其禁掉,以保证平台其它信号稳定。但如果用户将此PortX设为HotPlug功能或设为TestMode,我们也需要保留其使能状态。以保证测试正常进行或HotPlug功能的正常使用。

 

1.3 AHCIMemory Bar初始化

(1) 设定MemBAR值,将MemBar值写入SATA Config Space的offset 0x24 Bar处,并将offset 0x04 Command寄存器的BIT1置1,表明SATA Controller支持Memory space的访问。

    (2) 设置Cap寄存器(Px register)。

    (3) 设置PxCMD寄存器(Px register)。

    (4) 设置AHCI Enable为1(ABAR global register)。

 

2. ATA和ATAPI命令

ATA/ATAPI是HOST端与存储设备间的接口,是都遵守的标准。系统制造商,系统集成开发者,软件人员和存储设备商均按照此规范开发自己的产品。可以认为AT/ATAPI是一组命令集,HOST端发送命令,DEVICE响应命令,将HOST端所需的数据传给HOST端。

来看看两个Command吧:

READ SECTORS,command code为0x20,描述如下:

Register

Description

Features

 

Sector Count

要读的扇区数

Sector Number

扇区地址

Cylinder Low

柱面地址低位

Cylinder High

柱面地址高位

Device/Head

磁头地址

Command

命令寄存器=0x20

命令执行完后,就可以通过数据寄存器接收该扇区的值了。

 

IDENTIFY DEVICE:command code为0xEC,描述如下:

Register

Description

Features

 

Sector Count

 

Sector Number

 

Cylinder Low

 

Cylinder High

 

Device/Head

磁头地址

Command

命令寄存器=0xEC

    命令执行后,就可以通过相应的寄存器读取Identify数据了,从中可以截取自己想要的信息。

 

IDE模式和AHCI模式虽然都遵循ATA/ATAPI标准,但实现ATA/ATAPI的方式是不同的。下面将分别以Identify Device command介绍。

2.1 IDE方式

无论是Legacy IDE模式,还是EnhancedIDE模式,都是通过IO方式进行访问。不同的是,LegacyIDE的IO是固定的,而Enhanced IDE的IO要从Bar0和Bar1中读取。

下表是IDE IO寄存器的说明:




Data register(Base+ 0):

通过这个寄存器,可以实现HOST端和Device端的数据交换。一般用于发送命令后,从Device端读数据。

CmdOrStatus register(Base + 7):

读时为Status寄存器时,写时为Command寄存器。

作为Status寄存器,可以反映出设备的状态,也可以反映出命令执行的进度状态。

作为Command寄存器时,ATA/ATAPI命令集所包含的命令需要写入这个寄存器中,来实现其功能。

我们来看一下UDK2014是如何做的:




从上图可以看到,此IDENTIFY DEVICE传了两个参数。除了必须要有的Command寄存器外,还传了DeviceHead寄存器,用于区分primary和secondary。AtaIdentify()调用了AtaPiodDataInOut(),AtaPioDataInOut()调用AtaIssueCommand(),下面看一下AtaIssueCommand()的原型:

EFI_STATUS

EFIAPI

AtaIssueCommand(

IN  EFI_PCI_IO_PROTOCOL       *PciIo,

  IN EFI_IDE_REGISTERS        *IdeRegisters,

  IN EFI_ATA_COMMAND_BLOCK     *AtaCommandBlock,

  IN UINT64                    Timeout

  )

{

  EFI_STATUS Status;

  UINT8      DeviceHead;

  UINT8      AtaCommand;

  ASSERT (PciIo != NULL);

  ASSERT (IdeRegisters != NULL);

  ASSERT (AtaCommandBlock != NULL);

  DeviceHead = AtaCommandBlock->AtaDeviceHead;

  AtaCommand = AtaCommandBlock->AtaCommand;

  Status = WaitForBSYClear (PciIo,IdeRegisters, Timeout);

  if (EFI_ERROR (Status)) {

    return EFI_DEVICE_ERROR;

  }

  IdeWritePortB (PciIo, IdeRegisters->Head,(UINT8) (0xe0 | DeviceHead));

  Status = DRQClear2 (PciIo, IdeRegisters,Timeout);

  if (EFI_ERROR (Status)) {

    return EFI_DEVICE_ERROR;

  }

  //此处填写IO寄存器,略去一些行

  IdeWritePortB (PciIo, IdeRegisters->CmdOrStatus,AtaCommand);

  MicroSecondDelay (400);

  return EFI_SUCCESS;

}

将命令发出后,我们就可以一点点地检查DRQ=1,然后读出命令所需要相应长度的数据了。

 

2.2 AHCI方式

使用AHCI模式,首先要设置好环境。SATA Controller配置空间的Bar5要设置好,这样才可以映射到ATA设备的寄存器上。然后需要对ABAR初始化。为了实现ATA/ATAPI command,我们首先需要构建好CommandList和ReceiveFIS。然后将其首地址填入Command List Base address和FIS Base address中。这里面要构建好三个重要的数据结构:COMMAND_LIST,COMMAND_TABE和RECEIVED_FIS。

我们来看一下他们的结构:

EFI_AHCI_COMMAND_LIST的结构

typedef struct {

  UINT32   AhciCmdCfl:5;      //Command FIS Length

  UINT32   AhciCmdA:1;        //ATAPI

  UINT32   AhciCmdW:1;        //Write

  UINT32   AhciCmdP:1;        //Prefetchable

  UINT32   AhciCmdR:1;        //Reset

  UINT32   AhciCmdB:1;        //BIST

  UINT32   AhciCmdC:1;        //Clear Busy upon R_OK

  UINT32   AhciCmdRsvd:1;

  UINT32   AhciCmdPmp:4;      //Port Multiplier Port

  UINT32   AhciCmdPrdtl:16;   //Physical Region Descriptor Table Length

  UINT32   AhciCmdPrdbc;      //Physical Region Descriptor Byte Count

  UINT32   AhciCmdCtba;       //Command Table Descriptor Base Address

  UINT32   AhciCmdCtbau;      //Command Table Descriptor Base AddressUpper 32-BITs

  UINT32   AhciCmdRsvd1[4];

} EFI_AHCI_COMMAND_LIST;

我们可以看到其有两字段AhciCmdCtba和AhciCmdCtbau。这两个字段指示Ata Command table的地址。

EFI_AHCI_COMMAND_TABLE的结构

 typedef struct {

 EFI_AHCI_COMMAND_FIS     CommandFis;       // A softwareconstructed FIS.

 EFI_AHCI_ATAPI_COMMAND    AtapiCmd;         // 12 or 16 bytes ATAPI cmd.

  UINT8                     Reserved[0x30];

 EFI_AHCI_COMMAND_PRDT    PrdtTable[65535];     // Thescatter/gather list for data transfer

} EFI_AHCI_COMMAND_TABLE;

第一个字段落CommandFis是我们重点构建的对象,它的作用和我们IDE模式下的几个IO端口的作用类似。

PrdtTable字段相当于我们IDE模式下的Data Register。所不同的是,IDE模式下,我们需要借助Data Register一次一个WORD地读,而AHCI模式下,不需要这么麻烦,我们填好PrdtTable的值,不再需要软件的参与,ATA Device会自动帮我们填好数据。

EFI_AHCI_RECEIVED_FIS的结构

typedef struct {

  UINT8    AhciDmaSetupFis[0x1C];         // Dma Setup Fis: offset 0x00

  UINT8    AhciDmaSetupFisRsvd[0x04];

  UINT8    AhciPioSetupFis[0x14];         // Pio Setup Fis: offset 0x20

  UINT8    AhciPioSetupFisRsvd[0x0C];    

  UINT8    AhciD2HRegisterFis[0x14];      // D2H Register Fis: offset 0x40

  UINT8    AhciD2HRegisterFisRsvd[0x04];

  UINT64   AhciSetDeviceBitsFis;          // Set Device Bits Fix: offset 0x58

  UINT8    AhciUnknownFis[0x40];          // Unkonwn Fis: offset 0x60

  UINT8    AhciUnknownFisRsvd[0x60];     

} EFI_AHCI_RECEIVED_FIS;

    从这个结构中,我们可以看出,ATA/ATAPI Command的传输方式有多种。BIOS常用的读扇区和发IDENTIFY_DEVICE命令属于PIO的方式。除此外,还有DMA的传输方式。PIO一般用于小数据的传输,DMA用于大数据的传输。就像HDA Controller一样,要是读一下设备VID和DID,只需要使用PIO即可,但你要是录音或播放音乐,肯定要使用DMA了。

 

下面来看下UDK2014如何在AHCI模式下做IDENTIFY_DEVICE命令的:




传进来的几个参数中,Buffer代表了Identify_device命令结束后数据存储的位置。在此函数中,会使用PIO方式传输。此函数调用AhciPioTransfer(),AhciPioTransfer()调用AhciBuildCommand(),AhciBuildCommand()是整个Transfer中重要的一环,我们来看一下函数体,它是如何Build Command的:




构建Command前,要清空Received_FIS和CommandFis,因为它们靠内存传输的。如果不清空,它们的字段会带着前一次Command的残留值。RECEIVED_FIS中有字段DMA SETUP和PIO SETUP,我们可以认为它们是IDE模式下的Status寄存器。




IDENTIFY_DEVICE命令结束后,ATA device会往AhciPrdtDba所指向的地址输出数据。我们可以认为它是IDE模式下的Data寄存器。




Command Table属于Command List中的字段。AHCI通过Port Register的CLB寄存器,构建起Command List,然后Command List中的字段可以链接Command Table,Command Table中再构建Command FIS,然后整个ABAR就将SATA控制器的内存空间和SATA device的寄存器映射起来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值