Skip to content

Commit

Permalink
wip(lab/6): ata driver
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed Mar 19, 2024
1 parent e6aed35 commit b66a9b4
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 29 deletions.
68 changes: 68 additions & 0 deletions docs/labs/0x06/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,74 @@ storage = { package = "ysos_storage", path = "../storage" }

这种设计类似于过往的实现中将 `serial``uart16550` 分开,不过磁盘会有自己的 `model` 等信息,需要获取并存储在 `AtaDrive` 中。

### 发送命令

为了与磁盘进行交互,需要向磁盘发送命令,在 `drivers/ata/consts.rs` 中,定义了 `AtaCommand` 枚举,它表示了一系列的命令。

在本实验中,你需要实现 28-bit 模式下的 LBA 读写命令,并且还会使用到 `IdentifyDevice` 命令,用于获取磁盘的信息。

上述三个命令的调用过程比较类似,因此可以把发送命令并等待设备就绪的过程封装为一个函数,它被定义在 `drivers/ata/bus.rs` 中,可以参考 [x86 28-bit PIO - OSDev](https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO) 的内容,这里给出发送命令的过程:

1. 将当前块的 LBA 偏移分别存入四个寄存器中
2. 同时使用 `drive` 寄存器选择磁盘
3. 发送命令
4. 等待设备就绪,判断是否出错
5. 等待数据请求就绪

28-bit 的 LBA 地址应当按照如下方式存入寄存器,最高四位被放置在 `drive` 寄存器的低四位:

<table class="inst">
<tr>
<td class="inst-numnodel">28</td>
<td class="inst-numnode" colspan="2"></td>
<td class="inst-numnoder">24</td>
<td class="inst-numnode" colspan="6"></td>
<td class="inst-numnoder">16</td>
<td class="inst-numnode" colspan="6"></td>
<td class="inst-numnoder">8</td>
<td class="inst-numnode" colspan="6"></td>
<td class="inst-numnoder">0</td>
</tr>
<tr>
<td colspan="4" class="inst-node-little">drive</td>
<td colspan="7" class="inst-node-little">lba_high</td>
<td colspan="7" class="inst-node-little">lba_mid</td>
<td colspan="7" class="inst-node-little">lba_low</td>
</tr>
</table>

`drive` 寄存器的高四位则用来进行磁盘及寻址方式的选择,具体定义如下:

<table border="2" cellpadding="4" cellspacing="0" class="wikitable"><tbody><tr><th> Bit</th><th> Abbreviation</th><th> Function</th></tr><tr><td> 0 - 3</td><td></td><td> In CHS addressing, bits 0 to 3 of the head. In LBA addressing, bits 24 to 27 of the block number.</td></tr><tr><td> 4</td><td> DRV</td><td> Selects the drive number.</td></tr><tr><td> 5</td><td> 1</td><td> Always set.</td></tr><tr><td> 6</td><td> LBA</td><td> Uses CHS addressing if clear or LBA addressing if set.</td></tr><tr><td> 7</td><td> 1</td><td> Always set.</td></tr></tbody></table>

### 磁盘识别

在完成命令发送部分后,尝试补全 `identify_drive` 函数。可以直接调用上文实现好的 `write_command` 函数,根据规范,`block` 参数使用 `0` 进行传递。

对于识别出的磁盘,会带有一个 512 字节的数据块,你需要根据 ATA 规范中的定义,参考 [IDE - OSDev](https://wiki.osdev.org/IDE),将这些数据解析为 `AtaDrive` 的相关信息,这里给出部分会用于补全 `drivers/ata/mod.rs` 的信息。

```c
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
#define ATA_IDENT_HEADS 6
#define ATA_IDENT_SECTORS 12
#define ATA_IDENT_SERIAL 20 // 20 bytes
#define ATA_IDENT_MODEL 54 // 40 bytes
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID 106
#define ATA_IDENT_MAX_LBA 120 // 4 bytes (unsigned int)
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
```
!!! success "阶段性成果"
在完成命令发送,并按照注释补全 `identify_drive` 函数后,你可以自行修改相关函数,测试 `AtaDrive` 的 `open` 函数。
在操作系统初始化结束后,使用 `AtaDrive::open(0, 0)` 获取磁盘信息并打印出来。为了确保通过编译,可以先不用关心 `filesystem.rs` 中的内容。
### 读写数据
## FAT16 文件系统
## 思考题
Expand Down
34 changes: 5 additions & 29 deletions src/0x06/pkg/kernel/src/drivers/ata/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,29 +128,10 @@ impl AtaBus {
warn!("ATA status register : {:?}", self.status());
}

/// Selects the given drive (0 or 1) by writing to the `drive` port.
///
/// - 0: Master
/// - 1: Slave
fn select_drive(&mut self, drive: u8) {
debug_assert!(drive < 2);
self.poll(AtaStatus::BUSY, false);
self.poll(AtaStatus::DATA_REQUEST_READY, false);

unsafe {
// FIXME: select the drive
}

self.poll(AtaStatus::BUSY, false);
self.poll(AtaStatus::DATA_REQUEST_READY, false);
}

/// Sets up the PIO mode for the given drive and block.
/// Writes the given command
///
/// reference: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO
fn setup_pio(&mut self, drive: u8, block: u32, cmd: AtaCommand) {
self.select_drive(drive);

fn write_command(&mut self, drive: u8, block: u32, cmd: AtaCommand) -> storage::Result<()> {
let bytes = block.to_le_bytes(); // a trick to convert u32 to [u8; 4]
unsafe {
// just 1 sector for current implementation
Expand All @@ -161,14 +142,9 @@ impl AtaBus {
// FIXME: enable LBA28 mode
// FIXME: write the command register (cmd as u8)
}
}

/// Writes the given command
fn write_command(&mut self, drive: u8, block: u32, cmd: AtaCommand) -> storage::Result<()> {
self.setup_pio(drive, block, cmd);

if self.status().is_empty() {
// drive does not exist
// unknown drive
return Err(storage::DeviceError::UnknownDevice.into());
}

Expand All @@ -187,14 +163,14 @@ impl AtaBus {

/// Identifies the drive at the given `drive` number (0 or 1).
///
/// reference: <https://wiki.osdev.org/ATA_PIO_Mode#IDENTIFY_command>
/// reference: https://wiki.osdev.org/ATA_PIO_Mode#IDENTIFY_command
pub(super) fn identify_drive(&mut self, drive: u8) -> storage::Result<AtaDeviceType> {
info!("Identifying drive {}", drive);

// FIXME: use `AtaCommand::IdentifyDevice` to identify the drive
// - call `write_command` with `drive` and `0` as the block number
// - if the status is empty, return `AtaDeviceType::None`
// - else return `DeviceError::UnknownDevice` as `FsError`
// - else return `DeviceError::Unknown` as `FsError`

// FIXME: poll for the status to be not BUSY

Expand Down

0 comments on commit b66a9b4

Please sign in to comment.