int
ahci_init_device(struct hba_port* port);
+void
+achi_register_ops(struct hba_port* port);
+
+unsigned int
+ahci_get_port_usage()
+{
+ return hba.ports_bmp;
+}
+
+struct hba_port*
+ahci_get_port(unsigned int index)
+{
+ if (index >= 32) {
+ return 0;
+ }
+ return hba.ports[index];
+}
+
void
ahci_init()
{
// As per section 3.1.1, this is 0 based value.
hba_reg_t cap = hba.base[HBA_RCAP];
+ hba_reg_t pmap = hba.base[HBA_RPI];
+
hba.ports_num = (cap & 0x1f) + 1; // CAP.PI
hba.cmd_slots = (cap >> 8) & 0x1f; // CAP.NCS
hba.version = hba.base[HBA_RVER];
+ hba.ports_bmp = pmap;
/* ------ HBA端口配置 ------ */
- hba_reg_t pmap = hba.base[HBA_RPI];
uintptr_t clb_pg_addr, fis_pg_addr, clb_pa, fis_pa;
for (size_t i = 0, fisp = 0, clbp = 0; i < 32;
i++, pmap >>= 1, fisp = (fisp + 1) % 16, clbp = (clbp + 1) % 4) {
}
kprintf("\t\t capacity: %d KiB\n",
(dev_info->max_lba * dev_info->block_size) >> 10);
- kprintf("\t\t sector size: %dB\n", dev_info->block_size);
+ kprintf("\t\t block size: %dB\n", dev_info->block_size);
kprintf("\t\t model: %s\n", &dev_info->model);
kprintf("\t\t serial: %s\n", &dev_info->serial_num);
}
void
sata_create_fis(struct sata_reg_fis* cmd_fis,
uint8_t command,
- uint32_t lba_lo,
- uint32_t lba_hi,
+ uint64_t lba,
uint16_t sector_count)
{
cmd_fis->head.type = SATA_REG_FIS_H2D;
cmd_fis->head.status_cmd = command;
cmd_fis->dev = 0;
- cmd_fis->lba0 = SATA_LBA_COMPONENT(lba_lo, 0);
- cmd_fis->lba8 = SATA_LBA_COMPONENT(lba_lo, 8);
- cmd_fis->lba16 = SATA_LBA_COMPONENT(lba_lo, 16);
- cmd_fis->lba24 = SATA_LBA_COMPONENT(lba_lo, 24);
+ cmd_fis->lba0 = SATA_LBA_COMPONENT(lba, 0);
+ cmd_fis->lba8 = SATA_LBA_COMPONENT(lba, 8);
+ cmd_fis->lba16 = SATA_LBA_COMPONENT(lba, 16);
+ cmd_fis->lba24 = SATA_LBA_COMPONENT(lba, 24);
- cmd_fis->lba32 = SATA_LBA_COMPONENT(lba_hi, 0);
- cmd_fis->lba40 = SATA_LBA_COMPONENT(lba_hi, 8);
+ cmd_fis->lba32 = SATA_LBA_COMPONENT(lba, 32);
+ cmd_fis->lba40 = SATA_LBA_COMPONENT(lba, 40);
cmd_fis->count = sector_count;
}
memset(cmd_table, 0, sizeof(*cmd_table));
// 将命令表挂到命令头上
- cmd_header->prdt_len = 1;
cmd_header->cmd_table_base = vmm_v2p(cmd_table);
cmd_header->options = HBA_CMDH_FIS_LEN(sizeof(struct sata_reg_fis)) |
HBA_CMDH_CLR_BUSY | (header_options & ~0x1f);
struct hba_cmdt* cmd_table;
struct hba_cmdh* cmd_header;
+ // 确保端口是空闲的
+ wait_until(!(port->regs[HBA_RPxTFD] & (HBA_PxTFD_BSY)));
+
int slot = hba_alloc_slot(port, &cmd_table, &cmd_header, 0);
// 清空任何待响应的中断
port->regs[HBA_RPxIS] = 0;
- port->device = valloc(sizeof(struct hba_device));
- port->device->signature = port->regs[HBA_RPxSIG];
+ port->device = vcalloc(sizeof(struct hba_device));
// 预备DMA接收缓存,用于存放HBA传回的数据
uint16_t* data_in = (uint16_t*)valloc_dma(512);
struct sata_reg_fis* cmd_fis = (struct sata_reg_fis*)cmd_table->command_fis;
// 根据设备类型使用合适的命令
- if (port->device->signature == HBA_DEV_SIG_ATA) {
+ if (port->regs[HBA_RPxSIG] == HBA_DEV_SIG_ATA) {
// ATA 一般为硬盘
- sata_create_fis(cmd_fis, ATA_IDENTIFY_DEVICE, 0, 0, 0);
+ sata_create_fis(cmd_fis, ATA_IDENTIFY_DEVICE, 0, 0);
} else {
// ATAPI 一般为光驱,软驱,或者磁带机
- sata_create_fis(cmd_fis, ATA_IDENTIFY_PAKCET_DEVICE, 0, 0, 0);
+ port->device->flags |= HBA_DEV_FATAPI;
+ sata_create_fis(cmd_fis, ATA_IDENTIFY_PAKCET_DEVICE, 0, 0);
}
// PxCI寄存器置位,告诉HBA这儿有个数据需要发送到SATA端口
wait_until(!(port->regs[HBA_RPxCI] & (1 << slot)));
+ if ((port->regs[HBA_RPxTFD] & HBA_PxTFD_ERR)) {
+ // 有错误
+ sata_read_error(port);
+ goto fail;
+ }
+
/*
等待数据到达内存
解析IDENTIFY DEVICE传回来的数据。
*/
ahci_parse_dev_info(port->device, data_in);
- if (port->device->signature == HBA_DEV_SIG_ATA) {
+ if (!(port->device->flags & HBA_DEV_FATAPI)) {
goto done;
}
* SATA AHCI HBA Spec, Section 5.3.7
* SCSI Command Reference Manual, Section 3.26
*/
- sata_create_fis(cmd_fis, ATA_PACKET, 512 << 8, 0, 0);
struct scsi_cdb16* cdb16 = (struct scsi_cdb16*)cmd_table->atapi_cmd;
- scsi_create_packet16(cdb16, SCSI_READ_CAPACITY_16, 0, 0, 512);
+
+ sata_create_fis(cmd_fis, ATA_PACKET, 512 << 8, 0);
+ scsi_create_packet16(cdb16, SCSI_READ_CAPACITY_16, 0, 512);
+
cdb16->misc1 = 0x10; // service action
cmd_header->transferred_size = 0;
cmd_header->options |= HBA_CMDH_ATAPI;
port->regs[HBA_RPxCI] = (1 << slot);
wait_until(!(port->regs[HBA_RPxCI] & (1 << slot)));
+ if ((port->regs[HBA_RPxTFD] & HBA_PxTFD_ERR)) {
+ // 有错误
+ sata_read_error(port);
+ goto fail;
+ }
+
scsi_parse_capacity(port->device, (uint32_t*)data_in);
done:
+ achi_register_ops(port);
+
vfree_dma(data_in);
vfree_dma(cmd_table);
return 1;
+
+fail:
+ vfree_dma(data_in);
+ vfree_dma(cmd_table);
+
+ return 0;
}
-// TODO: Support ATAPI Device.
\ No newline at end of file
+int
+ahci_identify_device(struct hba_port* port)
+{
+ // 用于重新识别设备(比如在热插拔的情况下)
+ vfree(port->device);
+ return ahci_init_device(port);
+}
+
+void
+achi_register_ops(struct hba_port* port)
+{
+ port->device->ops.identify = ahci_identify_device;
+ if (!(port->device->flags & HBA_DEV_FATAPI)) {
+ port->device->ops.read_buffer = sata_read_buffer;
+ port->device->ops.write_buffer = sata_write_buffer;
+ } else {
+ port->device->ops.read_buffer = scsi_read_buffer;
+ port->device->ops.write_buffer = scsi_write_buffer;
+ }
+}
\ No newline at end of file
--- /dev/null
+#include <hal/ahci/hba.h>
+#include <hal/ahci/sata.h>
+
+#include <lunaix/mm/valloc.h>
+#include <lunaix/spike.h>
+
+int
+__sata_buffer_io(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size,
+ int write)
+{
+ assert_msg(((uintptr_t)buffer & 0x3) == 0, "HBA: Bad buffer alignment");
+
+ struct hba_cmdh* header;
+ struct hba_cmdt* table;
+ int slot = hba_alloc_slot(port, &table, &header, 0);
+ int bitmask = 1 << slot;
+
+ // 确保端口是空闲的
+ wait_until(!(port->regs[HBA_RPxTFD] & (HBA_PxTFD_BSY)));
+
+ port->regs[HBA_RPxIS] = 0;
+
+ table->entries[0] =
+ (struct hba_prdte){ .byte_count = size - 1, .data_base = buffer };
+ header->prdt_len = 1;
+ header->options |= HBA_CMDH_WRITE * (write == 1);
+
+ uint16_t count = size / port->device->block_size;
+ if (count == 0 && size < port->device->block_size) {
+ // 一个special case: 当count=0的时候,表示的是65536个区块会被传输
+ // 如果这个0是因为不够除而导致的,那么说明size太小了
+ goto fail;
+ }
+
+ struct sata_reg_fis* fis = table->command_fis;
+ if ((port->device->flags & HBA_DEV_FEXTLBA)) {
+ // 如果该设备支持48位LBA寻址
+ sata_create_fis(
+ fis, write ? ATA_WRITE_DMA_EXT : ATA_READ_DMA_EXT, lba, count);
+ } else {
+ sata_create_fis(fis, write ? ATA_WRITE_DMA : ATA_READ_DMA, lba, count);
+ }
+
+ port->regs[HBA_RPxCI] = bitmask;
+
+ wait_until(!(port->regs[HBA_RPxCI] & bitmask));
+
+ if ((port->regs[HBA_RPxTFD] & HBA_PxTFD_ERR)) {
+ // 有错误
+ sata_read_error(port);
+ goto fail;
+ }
+
+ vfree_dma(table);
+ return 1;
+
+fail:
+ vfree_dma(table);
+ return 0;
+}
+
+int
+sata_read_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size)
+{
+ return __sata_buffer_io(port, lba, buffer, size, 0);
+}
+
+int
+sata_write_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size)
+{
+ return __sata_buffer_io(port, lba, buffer, size, 1);
+}
+
+void
+sata_read_error(struct hba_port* port)
+{
+ uint32_t tfd = port->regs[HBA_RPxTFD];
+ port->device->last_error = (tfd >> 8) & 0xff;
+}
\ No newline at end of file
#include <hal/ahci/hba.h>
+#include <hal/ahci/sata.h>
#include <hal/ahci/scsi.h>
#include <hal/ahci/utils.h>
void
scsi_create_packet16(struct scsi_cdb16* cdb,
uint8_t opcode,
- uint32_t lba_hi,
- uint32_t lba_lo,
+ uint64_t lba,
uint32_t alloc_size)
{
memset(cdb, 0, sizeof(*cdb));
cdb->opcode = opcode;
- cdb->lba_be_hi = SCSI_FLIP(lba_hi);
- cdb->lba_be_lo = SCSI_FLIP(lba_lo);
+ cdb->lba_be_hi = SCSI_FLIP(lba >> 32);
+ cdb->lba_be_lo = SCSI_FLIP((uint32_t)lba);
cdb->length = alloc_size;
}
{
device->max_lba = SCSI_FLIP(*(parameter + 1));
device->block_size = SCSI_FLIP(*(parameter + 2));
+}
+
+void
+__scsi_buffer_io(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size,
+ int write)
+{
+ assert_msg(((uintptr_t)buffer & 0x3) == 0, "HBA: Bad buffer alignment");
+
+ struct hba_cmdh* header;
+ struct hba_cmdt* table;
+ int slot = hba_alloc_slot(port, &table, &header, 0);
+ int bitmask = 1 << slot;
+
+ // 确保端口是空闲的
+ wait_until(!(port->regs[HBA_RPxTFD] & (HBA_PxTFD_BSY)));
+
+ port->regs[HBA_RPxIS] = 0;
+
+ table->entries[0] =
+ (struct hba_prdte){ .byte_count = size - 1, .data_base = buffer };
+ header->prdt_len = 1;
+ header->options |= (HBA_CMDH_WRITE * (write == 1)) | HBA_CMDH_ATAPI;
+
+ uint32_t count = size / port->device->block_size;
+ if (count == 0) {
+ // 对ATAPI设备来说,READ/WRITE (16/12) 没有这个count=0的 special case
+ // 但是对于READ/WRITE (6),就存在这个special case
+ goto fail;
+ }
+
+ struct sata_reg_fis* fis = table->command_fis;
+ void* cdb = table->atapi_cmd;
+ sata_create_fis(fis, ATA_PACKET, (size << 8) & 0xffffff, 0);
+
+ if (port->device->cbd_size == 16) {
+ scsi_create_packet16((struct scsi_cdb16*)cdb,
+ write ? SCSI_WRITE_BLOCKS_16 : SCSI_READ_BLOCKS_16,
+ lba,
+ count);
+ } else {
+ scsi_create_packet12((struct scsi_cdb12*)cdb,
+ write ? SCSI_WRITE_BLOCKS_12 : SCSI_READ_BLOCKS_12,
+ lba,
+ count);
+ }
+
+ port->regs[HBA_RPxCI] = bitmask;
+
+ wait_until(!(port->regs[HBA_RPxCI] & bitmask));
+
+ if ((port->regs[HBA_RPxTFD] & HBA_PxTFD_ERR)) {
+ // 有错误
+ sata_read_error(port);
+ goto fail;
+ }
+
+ vfree_dma(table);
+ return 1;
+
+fail:
+ vfree_dma(table);
+ return 0;
+}
+
+void
+scsi_read_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size)
+{
+ __scsi_buffer_io(port, lba, buffer, size, 0);
+}
+
+void
+scsi_write_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size)
+{
+ __scsi_buffer_io(port, lba, buffer, size, 1);
}
\ No newline at end of file
#include <hal/ahci/utils.h>
#include <klibc/string.h>
-#define IDDEV_MAXLBA_OFFSET 60
-#define IDDEV_LSECSIZE_OFFSET 117
-#define IDDEV_WWN_OFFSET 108
-#define IDDEV_SERIALNUM_OFFSET 10
-#define IDDEV_MODELNUM_OFFSET 27
+#define IDDEV_OFFMAXLBA 60
+#define IDDEV_OFFMAXLBA_EXT 230
+#define IDDEV_OFFLSECSIZE 117
+#define IDDEV_OFFWWN 108
+#define IDDEV_OFFSERIALNUM 10
+#define IDDEV_OFFMODELNUM 27
+#define IDDEV_OFFADDSUPPORT 69
void
ahci_parse_dev_info(struct hba_device* dev_info, uint16_t* data)
{
- dev_info->max_lba = *((uint32_t*)(data + IDDEV_MAXLBA_OFFSET));
- dev_info->block_size = *((uint32_t*)(data + IDDEV_LSECSIZE_OFFSET));
+ dev_info->max_lba = *((uint32_t*)(data + IDDEV_OFFMAXLBA));
+ dev_info->block_size = *((uint32_t*)(data + IDDEV_OFFLSECSIZE));
dev_info->cbd_size = (*data & 0x3) ? 12 : 16;
- memcpy(&dev_info->wwn, (uint8_t*)(data + IDDEV_WWN_OFFSET), 8);
+ dev_info->wwn = *(uint64_t*)(data + IDDEV_OFFWWN);
+
if (!dev_info->block_size) {
dev_info->block_size = 512;
}
- ahci_parsestr(&dev_info->serial_num, data + IDDEV_SERIALNUM_OFFSET, 10);
- ahci_parsestr(&dev_info->model, data + IDDEV_MODELNUM_OFFSET, 20);
+
+ if ((*(data + IDDEV_OFFADDSUPPORT) & 0x8)) {
+ dev_info->max_lba = *((uint64_t*)(data + IDDEV_OFFMAXLBA_EXT));
+ dev_info->flags |= HBA_DEV_FEXTLBA;
+ }
+
+ ahci_parsestr(&dev_info->serial_num, data + IDDEV_OFFSERIALNUM, 10);
+ ahci_parsestr(&dev_info->model, data + IDDEV_OFFMODELNUM, 20);
}
void
#define AHCI_HBA_CLASS 0x10601
-#define ATA_IDENTIFY_DEVICE 0xec
-#define ATA_IDENTIFY_PAKCET_DEVICE 0xa1
-#define ATA_PACKET 0xa0
-
/**
* @brief 初始化AHCI与HBA
*
void
ahci_list_device();
+unsigned int
+ahci_get_port_usage();
+
+struct hba_port*
+ahci_get_port(unsigned int index);
+
#endif /* __LUNAIX_AHCI_H */
#define HBA_PxCMD_ST (1)
#define HBA_PxINTR_DMA (1 << 2)
#define HBA_PxINTR_D2HR (1)
+#define HBA_PxTFD_ERR (1)
+#define HBA_PxTFD_BSY (1 << 7)
+#define HBA_PxTFD_DRQ (1 << 3)
#define HBA_RGHC_ACHI_ENABLE (1 << 31)
#define HBA_RGHC_INTR_ENABLE (1 << 1)
struct hba_prdte entries[3];
} __HBA_PACKED__;
+#define HBA_DEV_FEXTLBA 1
+#define HBA_DEV_FATAPI (1 << 1)
+
+struct hba_port;
+
struct hba_device
{
char serial_num[20];
char model[40];
- uint32_t signature;
- uint32_t max_lba;
+ uint32_t flags;
+ uint64_t max_lba;
uint32_t block_size;
- uint8_t wwn[8];
+ uint64_t wwn;
uint8_t cbd_size;
+ uint8_t last_error;
+ uint8_t last_status;
+
+ struct
+ {
+ int (*identify)(struct hba_port* port);
+ int (*read_buffer)(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size);
+ int (*write_buffer)(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size);
+ } ops;
};
struct hba_port
volatile hba_reg_t* regs;
unsigned int ssts;
struct hba_cmdh* cmdlst;
- struct sata_fis_head* fis;
+ void* fis;
struct hba_device* device;
};
{
volatile hba_reg_t* base;
unsigned int ports_num;
+ unsigned int ports_bmp;
unsigned int cmd_slots;
unsigned int version;
struct hba_port* ports[32];
#define SATA_REG_FIS_D2H 0x34
#define SATA_REG_FIS_H2D 0x27
#define SATA_REG_FIS_COMMAND 0x80
-#define SATA_LBA_COMPONENT(lba, offset) ((((lba_lo) >> (offset)) & 0xff))
+#define SATA_LBA_COMPONENT(lba, offset) ((((lba) >> (offset)) & 0xff))
+
+#define ATA_IDENTIFY_DEVICE 0xec
+#define ATA_IDENTIFY_PAKCET_DEVICE 0xa1
+#define ATA_PACKET 0xa0
+#define ATA_READ_DMA_EXT 0xc8
+#define ATA_READ_DMA 0x25
+#define ATA_WRITE_DMA_EXT 0xca
+#define ATA_WRITE_DMA 0x35
struct sata_fis_head
{
void
sata_create_fis(struct sata_reg_fis* cmd_fis,
uint8_t command,
- uint32_t lba_lo,
- uint32_t lba_hi,
+ uint64_t lba,
uint16_t sector_count);
+int
+sata_read_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size);
+
+int
+sata_write_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size);
+
#endif /* __LUNAIX_SATA_H */
void
scsi_create_packet16(struct scsi_cdb16* cdb,
uint8_t opcode,
- uint32_t lba_hi,
- uint32_t lba_lo,
+ uint64_t lba,
uint32_t alloc_size);
+void
+scsi_read_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size);
+
+void
+scsi_write_buffer(struct hba_port* port,
+ uint64_t lba,
+ void* buffer,
+ uint32_t size);
+
#endif /* __LUNAIX_ATAPI_H */
void
scsi_parse_capacity(struct hba_device* device, uint32_t* parameter);
+int
+sata_read_error(struct hba_port* port);
+
#endif /* __LUNAIX_UTILS_H */
void*
valloc(unsigned int size);
+void*
+vcalloc(unsigned int size);
+
void
vfree(void* ptr);
void*
valloc_dma(unsigned int size);
+void*
+vcalloc_dma(unsigned int size);
+
void
vfree_dma(void* ptr);
/**
- * @file valloc.c
+ * @file cake.c
* @author Lunaixsky (zelong56@gmail.com)
* @brief A simplified cake(slab) allocator.
* P.s. I call it cake as slab sounds more 'ridge' to me. :)
cake_grab(struct cake_pile* pile)
{
struct cake_s *pos, *n;
- llist_for_each(pos, n, &pile->partial, cakes)
- {
- if (pos->next_free != EO_FREE_PIECE) {
- goto found;
- }
- }
-
- if (llist_empty(&pile->free)) {
+ if (!llist_empty(&pile->partial)) {
+ pos = list_entry(pile->partial.next, typeof(*pos), cakes);
+ } else if (llist_empty(&pile->free)) {
pos = __new_cake(pile);
} else {
pos = list_entry(pile->free.next, typeof(*pos), cakes);
+#include <klibc/string.h>
#include <lunaix/mm/cake.h>
+#include <lunaix/mm/valloc.h>
#define MAX_CLASS 6
return __valloc(size, &piles);
}
+void*
+vcalloc(unsigned int size)
+{
+ void* ptr = __valloc(size, &piles);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
void
vfree(void* ptr)
{
return __valloc(size, &piles_dma);
}
+void*
+vcalloc_dma(unsigned int size)
+{
+ void* ptr = __valloc(size, &piles_dma);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
void
vfree_dma(void* ptr)
{