Skip to content

Commit a434b28

Browse files
committed
add ata-pio driver and improve lowlevel io driver
1 parent 528cc20 commit a434b28

File tree

5 files changed

+240
-0
lines changed

5 files changed

+240
-0
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
commit 528cc20f905bd6803deb9861b83412baad1ed38a
2+
Author: Alexeev Bronislav <[email protected]>
3+
Date: Thu Aug 28 22:00:25 2025 +0700
4+
5+
small updates in kklibc/mem.c
6+
17
commit 1a32b6cff7252ca9e6a9c59f8cd77122a7d0fc40
28
Author: Alexeev Bronislav <[email protected]>
39
Date: Thu Aug 28 21:48:24 2025 +0700

src/kernel/drivers/ata_pio.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#include "ata_pio.h"
2+
#include "lowlevel_io.h"
3+
4+
// https://wiki.osdev.org/ATA_PIO_Mode
5+
6+
static void ata_wait_ready() {
7+
while (port_byte_in(ATA_REG_STATUS) & ATA_SR_BSY);
8+
}
9+
10+
static void ata_cache_flush() {
11+
ata_wait_ready();
12+
port_byte_out(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
13+
ata_wait_ready();
14+
}
15+
16+
void ata_read_sectors(u32 lba, u32 sector_count, const u8* buffer) {
17+
ata_wait_ready();
18+
19+
// Select drive (Master)
20+
port_byte_out(ATA_REG_DRIVE_SELECT, 0xE0 | ((lba >> 24) & 0x0F));
21+
22+
// Waste time
23+
port_byte_out(ATA_REG_ERROR, 0x00);
24+
25+
port_byte_out(ATA_REG_SECCOUNT, sector_count); // Read one sector
26+
port_byte_out(ATA_REG_LBA0, (u8)(lba & 0xFF));
27+
port_byte_out(ATA_REG_LBA1, (u8)((lba >> 8) & 0xFF));
28+
port_byte_out(ATA_REG_LBA2, (u8)((lba >> 16) & 0xFF));
29+
port_byte_out(ATA_REG_COMMAND, ATA_CMD_READ_SECTORS);
30+
31+
ata_wait_ready();
32+
33+
for (u8 sector = 0; sector < sector_count; sector++) {
34+
while (!(port_byte_in(ATA_REG_STATUS) & ATA_SR_DRQ));
35+
// NOTE: If bytes are entered in small endian, does it call it back the right way
36+
rep_insw(ATA_REG_DATA, (void*)(&buffer[sector * SECTOR_BYTES]), SECTOR_WORDS); // 256 words (512 bytes)
37+
}
38+
}
39+
40+
void ata_write_sectors(u32 lba, u32 sector_count, const u8* buffer) {
41+
42+
ata_wait_ready();
43+
44+
// Select drive (Master)
45+
port_byte_out(ATA_REG_DRIVE_SELECT, 0xE0 | ((lba >> 24) & 0x0F));
46+
47+
// Waste time
48+
port_byte_out(ATA_REG_ERROR, 0x00);
49+
50+
port_byte_out(ATA_REG_SECCOUNT, (u8)sector_count); // Read one sector
51+
port_byte_out(ATA_REG_LBA0, (u8)(lba & 0xFF));
52+
port_byte_out(ATA_REG_LBA1, (u8)((lba >> 8) & 0xFF));
53+
port_byte_out(ATA_REG_LBA2, (u8)((lba >> 16) & 0xFF));
54+
port_byte_out(ATA_REG_COMMAND, ATA_CMD_WRITE_SECTORS);
55+
56+
for (u8 sector = 0; sector < sector_count; sector++) {
57+
while (!(port_byte_in(ATA_REG_STATUS) & ATA_SR_DRQ));
58+
for (u32 sw = 0; sw < SECTOR_WORDS; sw++) {
59+
// looks like big endian
60+
outsw(ATA_REG_DATA, ((u16)buffer[sector * SECTOR_BYTES + sw * 2 + 1]) << 8
61+
| (u16)(buffer[sector * SECTOR_BYTES + sw * 2]));
62+
}
63+
}
64+
65+
ata_cache_flush();
66+
}
67+
68+
void ata_select_drive(int is_master) {
69+
port_byte_out(ATA_REG_DRIVE_SELECT, is_master ? 0xA0 : 0xB0);
70+
}
71+
72+
void ata_identify() {
73+
ata_select_drive(1); // Select master drive
74+
75+
port_byte_out(ATA_REG_SECCOUNT, 0);
76+
port_byte_out(ATA_REG_LBA0, 0);
77+
port_byte_out(ATA_REG_LBA1, 0);
78+
port_byte_out(ATA_REG_LBA2, 0);
79+
port_byte_out(ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
80+
}
81+
82+
int ata_wait() {
83+
while (1) {
84+
u8 status = port_byte_in(ATA_REG_STATUS);
85+
if (!(status & 0x80)) return 1; // Check BSY (Busy) bit
86+
}
87+
}
88+
89+
void ata_read_buffer(u16* buffer) {
90+
// reads an entire sector, 256 words, 512 bytes
91+
for (int i = 0; i < 256; i++) {
92+
buffer[i] = port_word_in(ATA_REG_DATA);
93+
}
94+
}
95+
96+
u32 ata_get_disk_size() {
97+
// 1024^2 = 1048576 (1 MiB)
98+
u16 ata_buffer[256];
99+
ata_identify();
100+
ata_wait();
101+
ata_read_buffer(ata_buffer);
102+
u32 total_sectors = ((u32)ata_buffer[61] << 16) | ata_buffer[60];
103+
return (u32)total_sectors * 512; // Convert to bytes
104+
}
105+
106+
void ata_read_blocks(u32 block_num, const u8* buffer, u32 count) {
107+
ata_read_sectors(block_num * SECTORS_PER_BLOCK, SECTORS_PER_BLOCK * count, buffer);
108+
}
109+
110+
// these are wasteful, just writes past buffer, regardless of length
111+
void ata_write_blocks(u32 block_num, const u8* buffer, u32 count) {
112+
ata_write_sectors(block_num * SECTORS_PER_BLOCK, SECTORS_PER_BLOCK * count, buffer);
113+
}

src/kernel/drivers/ata_pio.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#ifndef ATA_PIO_H
2+
#define ATA_PIO_H
3+
4+
#include "../kklibc/ctypes.h"
5+
6+
#define SECTOR_BYTES 512
7+
#define SECTOR_WORDS 256
8+
9+
// ATA I/O Порты
10+
#define ATA_PRIMARY_IO_BASE 0x1F0
11+
#define ATA_PRIMARY_CTRL_BASE 0x3F6
12+
#define ATA_REG_DATA (ATA_PRIMARY_IO_BASE + 0)
13+
#define ATA_REG_ERROR (ATA_PRIMARY_IO_BASE + 1)
14+
#define ATA_REG_SECCOUNT (ATA_PRIMARY_IO_BASE + 2)
15+
#define ATA_REG_LBA0 (ATA_PRIMARY_IO_BASE + 3)
16+
#define ATA_REG_LBA1 (ATA_PRIMARY_IO_BASE + 4)
17+
#define ATA_REG_LBA2 (ATA_PRIMARY_IO_BASE + 5)
18+
#define ATA_REG_DRIVE_SELECT (ATA_PRIMARY_IO_BASE + 6)
19+
#define ATA_REG_COMMAND (ATA_PRIMARY_IO_BASE + 7)
20+
#define ATA_REG_STATUS (ATA_PRIMARY_IO_BASE + 7)
21+
#define ATA_REG_ALTSTATUS (ATA_PRIMARY_CTRL_BASE + 0)
22+
#define ATA_REG_CONTROL (ATA_PRIMARY_CTRL_BASE + 0)
23+
24+
// ATA Команда
25+
#define ATA_CMD_READ_SECTORS 0x20
26+
#define ATA_CMD_WRITE_SECTORS 0x30
27+
#define ATA_CMD_CACHE_FLUSH 0xE7
28+
#define ATA_CMD_IDENTIFY 0xEC
29+
30+
// Статус флаги
31+
#define ATA_SR_BSY 0x80 // Занятость
32+
#define ATA_SR_DRDY 0x40 // Готовность драйва
33+
#define ATA_SR_DRQ 0x08 // Запрос на готовность данных
34+
35+
// Типы драйвов
36+
#define ATA_MASTER 0xA0
37+
#define ATA_SLAVE 0xB0
38+
39+
#define SECTORS_PER_BLOCK 0x8 // для ФС блоки по 4KB
40+
#define BLOCK_BYTES SECTORS_PER_BLOCK*SECTOR_BYTES
41+
42+
/**
43+
* @brief Чтение секторов
44+
*
45+
* @param lba LBA
46+
* @param sector_count количество секторов
47+
* @param buffer буфер
48+
**/
49+
void ata_read_sectors(u32 lba, u32 sector_count, const u8* buffer);
50+
51+
/**
52+
* @brief Запись секторов
53+
*
54+
* @param lba LBA
55+
* @param sector_count количество секторов
56+
* @param buffer буфер
57+
**/
58+
void ata_write_sectors(u32 lba, u32 sector_count, const u8* buffer);
59+
60+
/**
61+
* @brief Чтение буфера
62+
*
63+
* @param buffer буфер
64+
**/
65+
void ata_read_buffer(u16* buffer);
66+
67+
/**
68+
* @brief Получение размера диска
69+
*
70+
* @return u32
71+
**/
72+
u32 ata_get_disk_size();
73+
74+
/**
75+
* @brief Чтение блоков
76+
*
77+
* @param block_num количество блоков
78+
* @param buffer буфер
79+
* @param count число
80+
**/
81+
void ata_read_blocks(u32 block_num, const u8* buffer, u32 count);
82+
83+
/**
84+
* @brief Запись блоков
85+
*
86+
* @param block_num количество блоков
87+
* @param buffer буфер
88+
* @param count число
89+
**/
90+
void ata_write_blocks(u32 block_num, const u8* buffer, u32 count);
91+
92+
#endif // ATA_PIO_H

src/kernel/drivers/lowlevel_io.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
* придется использовать inline assembly.
2929
* ----------------------------------------------------------------------------*/
3030

31+
#include "lowlevel_io.h"
32+
3133
unsigned char port_byte_in(unsigned short port) {
3234
/* Функция-обертка над assembly, читающая 1 байт из параметра port */
3335
/* unsigned short port: адрес регистра какого-либо девайса, из которого */
@@ -74,3 +76,11 @@ void port_word_out(unsigned short port, unsigned short data) {
7476
/* Функция-обертка над assembly, пишущая data (2 байта, т.е. word) в port */
7577
__asm__("out %%ax, %%dx" : : "a"(data), "d"(port));
7678
}
79+
80+
void outsw(u16 port, u16 value) {
81+
__asm__ volatile ("outw %0, %1" : : "a"(value), "Nd"(port));
82+
}
83+
84+
void rep_insw(u16 port, void *addr, u32 count) {
85+
__asm__ volatile ("rep insw" : "+D"(addr), "+c"(count) : "d"(port) : "memory");
86+
}

src/kernel/drivers/lowlevel_io.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* Title: Заголовочный файл для drivers/lowlevel_io.c
55
*/
66

7+
#include "../kklibc/ctypes.h"
8+
79
/**
810
* @brief Port byte in
911
*
@@ -34,3 +36,20 @@ unsigned char port_word_in(unsigned short port);
3436
* @param data данные
3537
**/
3638
void port_word_out(unsigned short port, unsigned short data);
39+
40+
/**
41+
* @brief Outsw
42+
*
43+
* @param port порт
44+
* @param value значение
45+
**/
46+
void outsw(u16 port, u16 value);
47+
48+
/**
49+
* @brief rep_insw
50+
*
51+
* @param port порт
52+
* @param addr адрес
53+
* @param count количество
54+
**/
55+
void rep_insw(u16 port, void *addr, u32 count);

0 commit comments

Comments
 (0)