Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4839bb9689 | ||
|
|
1341158c5c | ||
|
|
e9079cb7ee | ||
|
|
f16fd2e9c6 | ||
|
|
28e8bf546e | ||
|
|
e8f0413e39 | ||
|
|
2b83e01ad4 | ||
|
|
000b94f1ac | ||
|
|
aeed44352c | ||
|
|
269bb48e2c | ||
|
|
d1ec4b9861 | ||
|
|
01f5e8dc67 | ||
|
|
24af6fe93e |
209
dev/block/ahci/ahci.cpp
Normal file
209
dev/block/ahci/ahci.cpp
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#include "ahci.h"
|
||||||
|
|
||||||
|
#include <arch/atomic.h>
|
||||||
|
#include <dev/bus/pci.h>
|
||||||
|
#include <kernel/event.h>
|
||||||
|
#include <kernel/thread.h>
|
||||||
|
#include <kernel/vm.h>
|
||||||
|
#include <lk/bits.h>
|
||||||
|
#include <lk/cpp.h>
|
||||||
|
#include <lk/err.h>
|
||||||
|
#include <lk/init.h>
|
||||||
|
#include <lk/list.h>
|
||||||
|
#include <lk/trace.h>
|
||||||
|
#include <platform/interrupts.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "ahci_hw.h"
|
||||||
|
#include "disk.h"
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
#define LOCAL_TRACE 1
|
||||||
|
|
||||||
|
volatile int ahci::global_count_= 0;
|
||||||
|
|
||||||
|
ahci::ahci() = default;
|
||||||
|
ahci::~ahci() = default;
|
||||||
|
|
||||||
|
status_t ahci::init_device(pci_location_t loc) {
|
||||||
|
char str[32];
|
||||||
|
loc_ = loc;
|
||||||
|
|
||||||
|
LTRACEF("pci location %s\n", pci_loc_string(loc_, str));
|
||||||
|
|
||||||
|
pci_bar_t bars[6];
|
||||||
|
status_t err = pci_bus_mgr_read_bars(loc_, bars);
|
||||||
|
if (err != NO_ERROR) return err;
|
||||||
|
|
||||||
|
LTRACEF("ahci BARS:\n");
|
||||||
|
if (LOCAL_TRACE) pci_dump_bars(bars, 6);
|
||||||
|
|
||||||
|
if (!bars[5].valid || !bars[5].addr) {
|
||||||
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate a unit number
|
||||||
|
unit_ = atomic_add(&global_count_, 1);
|
||||||
|
|
||||||
|
// map bar 5, main memory mapped register interface, 4K
|
||||||
|
snprintf(str, sizeof(str), "ahci%d abar", unit_);
|
||||||
|
err = vmm_alloc_physical(vmm_get_kernel_aspace(), str, PAGE_ALIGN(bars[5].size), &abar_regs_, 0,
|
||||||
|
bars[5].addr, /* vmm_flags */ 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
LTRACEF("ABAR mapped to %p\n", abar_regs_);
|
||||||
|
|
||||||
|
pci_bus_mgr_enable_device(loc_);
|
||||||
|
|
||||||
|
LTRACEF("CAP %#x\n", read_reg(ahci_reg::CAP));
|
||||||
|
LTRACEF("PI %#x\n", read_reg(ahci_reg::PI));
|
||||||
|
|
||||||
|
// mask all irqs
|
||||||
|
write_reg(ahci_reg::GHC, read_reg(ahci_reg::GHC) & ~(1U << 1)); // clear GHC.IE
|
||||||
|
|
||||||
|
static auto irq_handler_wrapper = [](void *arg) -> handler_return {
|
||||||
|
ahci *a = (ahci *)arg;
|
||||||
|
return a->irq_handler();
|
||||||
|
};
|
||||||
|
|
||||||
|
// allocate an MSI interrupt
|
||||||
|
uint irq_base;
|
||||||
|
err = pci_bus_mgr_allocate_msi(loc_, 1, &irq_base);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
// fall back to regular IRQs
|
||||||
|
err = pci_bus_mgr_allocate_irq(loc_, &irq_base);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
printf("ahci: unable to allocate IRQ\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
register_int_handler(irq_base, irq_handler_wrapper, this);
|
||||||
|
} else {
|
||||||
|
register_int_handler_msi(irq_base, irq_handler_wrapper, this, true);
|
||||||
|
}
|
||||||
|
LTRACEF("IRQ number %#x\n", irq_base);
|
||||||
|
|
||||||
|
unmask_interrupt(irq_base);
|
||||||
|
|
||||||
|
// enable interrupts
|
||||||
|
write_reg(ahci_reg::GHC, read_reg(ahci_reg::GHC) | (1U << 1)); // set GHC.IE
|
||||||
|
|
||||||
|
// probe every port marked implemented
|
||||||
|
uint32_t port_bitmap = read_reg(ahci_reg::PI);
|
||||||
|
size_t port_count = 0;
|
||||||
|
for (size_t port = 0; port < 32; port++) {
|
||||||
|
if ((port_bitmap & (1U << port)) == 0) {
|
||||||
|
// skip port not implemented
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
port_count++;
|
||||||
|
|
||||||
|
ports_[port] = new ahci_port(*this, port);
|
||||||
|
auto *p = ports_[port];
|
||||||
|
|
||||||
|
ahci_disk *disk = nullptr;
|
||||||
|
err = p->probe(&disk);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_ASSERT(disk);
|
||||||
|
|
||||||
|
// add the disk to a list for further processing
|
||||||
|
list_add_tail(&disk_list_, &disk->node_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ahci::disk_probe_worker() {
|
||||||
|
LTRACE_ENTRY;
|
||||||
|
|
||||||
|
ahci_disk *disk;
|
||||||
|
list_for_every_entry(&disk_list_, disk, ahci_disk, node_) {
|
||||||
|
disk->identify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t ahci::start_disk_probe() {
|
||||||
|
auto probe_worker = [](void *arg) -> int {
|
||||||
|
ahci *a = (ahci *)arg;
|
||||||
|
|
||||||
|
a->disk_probe_worker();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
disk_probe_thread_ = thread_create("ahci disk probe", probe_worker, this, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
|
||||||
|
thread_resume(disk_probe_thread_);
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler_return ahci::irq_handler() {
|
||||||
|
LTRACE_ENTRY;
|
||||||
|
|
||||||
|
const auto orig_is = read_reg(ahci_reg::IS);
|
||||||
|
auto is = orig_is;
|
||||||
|
LTRACEF("is %#x\n", is);
|
||||||
|
|
||||||
|
// cycle through the ports that have interrupts queued
|
||||||
|
handler_return ret = INT_NO_RESCHEDULE;
|
||||||
|
while (is != 0) {
|
||||||
|
int port = __builtin_ctz(is);
|
||||||
|
|
||||||
|
LTRACEF("interrupt on port %d\n", port);
|
||||||
|
|
||||||
|
DEBUG_ASSERT(ports_[port] != nullptr);
|
||||||
|
|
||||||
|
if (ports_[port]->irq_handler() == INT_RESCHEDULE) {
|
||||||
|
ret = INT_RESCHEDULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
is &= ~(1U<<port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the interrupt status
|
||||||
|
write_reg(ahci_reg::IS, orig_is);
|
||||||
|
|
||||||
|
LTRACE_EXIT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hook called at init time to iterate through pci bus and find all of the ahci devices
|
||||||
|
static void ahci_init(uint level) {
|
||||||
|
LTRACE_ENTRY;
|
||||||
|
|
||||||
|
// probe pci to find a device
|
||||||
|
for (size_t i = 0; ; i++) {
|
||||||
|
pci_location_t loc;
|
||||||
|
status_t err = pci_bus_mgr_find_device_by_class(&loc, 0x1, 0x6, 0x1, i);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we maybe found one, create a new device and initialize it
|
||||||
|
auto a = new ahci;
|
||||||
|
err = a->init_device(loc);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
char str[14];
|
||||||
|
printf("ahci: device at %s failed to initialize\n", pci_loc_string(loc, str));
|
||||||
|
delete a;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up any disks we've found
|
||||||
|
a->start_disk_probe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LK_INIT_HOOK(ahci, &ahci_init, LK_INIT_LEVEL_PLATFORM + 1);
|
||||||
|
|
||||||
92
dev/block/ahci/ahci.h
Normal file
92
dev/block/ahci/ahci.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dev/bus/pci.h>
|
||||||
|
#include <lk/cpp.h>
|
||||||
|
#include <lk/list.h>
|
||||||
|
#include <kernel/spinlock.h>
|
||||||
|
#include <kernel/thread.h>
|
||||||
|
|
||||||
|
#include "ahci_hw.h"
|
||||||
|
|
||||||
|
class ahci_port;
|
||||||
|
|
||||||
|
class ahci {
|
||||||
|
public:
|
||||||
|
ahci();
|
||||||
|
~ahci();
|
||||||
|
|
||||||
|
DISALLOW_COPY_ASSIGN_AND_MOVE(ahci);
|
||||||
|
|
||||||
|
int get_unit_num() const { return unit_; }
|
||||||
|
|
||||||
|
// initialize the device at passed in pci location.
|
||||||
|
// probe each of the active ports for disks and save
|
||||||
|
// a list of them for future probing.
|
||||||
|
status_t init_device(pci_location_t loc);
|
||||||
|
|
||||||
|
// start a thread and probe all of the disks found
|
||||||
|
status_t start_disk_probe();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ahci_port;
|
||||||
|
|
||||||
|
uint32_t read_reg(ahci_reg reg);
|
||||||
|
void write_reg(ahci_reg reg, uint32_t val);
|
||||||
|
|
||||||
|
uint32_t read_port_reg(uint port, ahci_port_reg reg);
|
||||||
|
void write_port_reg(uint port, ahci_port_reg reg, uint32_t val);
|
||||||
|
|
||||||
|
handler_return irq_handler();
|
||||||
|
void disk_probe_worker();
|
||||||
|
|
||||||
|
// counter of configured deices
|
||||||
|
static volatile int global_count_;
|
||||||
|
int unit_ = 0;
|
||||||
|
|
||||||
|
// main spinlock
|
||||||
|
spin_lock_t lock_ = SPIN_LOCK_INITIAL_VALUE;
|
||||||
|
|
||||||
|
// configuration
|
||||||
|
pci_location_t loc_ = {};
|
||||||
|
void *abar_regs_ = nullptr;
|
||||||
|
|
||||||
|
// array of ports
|
||||||
|
ahci_port *ports_[32] = {};
|
||||||
|
|
||||||
|
// list of disks we've found
|
||||||
|
thread_t *disk_probe_thread_ = nullptr;
|
||||||
|
list_node disk_list_ = LIST_INITIAL_VALUE(disk_list_);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint32_t ahci::read_reg(ahci_reg reg) {
|
||||||
|
volatile uint32_t *r = (volatile uint32_t *)((uintptr_t)abar_regs_ + (size_t)reg);
|
||||||
|
|
||||||
|
return *r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ahci::write_reg(ahci_reg reg, uint32_t val) {
|
||||||
|
volatile uint32_t *r = (volatile uint32_t *)((uintptr_t)abar_regs_ + (size_t)reg);
|
||||||
|
|
||||||
|
*r = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t ahci::read_port_reg(uint port, ahci_port_reg reg) {
|
||||||
|
volatile uint32_t *r = (volatile uint32_t *)((uintptr_t)abar_regs_ + (size_t)reg + 0x100 + 0x80 * port);
|
||||||
|
|
||||||
|
return *r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ahci::write_port_reg(uint port, ahci_port_reg reg, uint32_t val) {
|
||||||
|
volatile uint32_t *r = (volatile uint32_t *)((uintptr_t)abar_regs_ + (size_t)reg + 0x100 + 0x80 * port);
|
||||||
|
|
||||||
|
*r = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
99
dev/block/ahci/ahci_hw.h
Normal file
99
dev/block/ahci/ahci_hw.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
// From serial-ata-ahci-spec-rev-1-3-1.pdf
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// registers relative to the base of the ABAR
|
||||||
|
enum class ahci_reg {
|
||||||
|
CAP = 0x0, // capability
|
||||||
|
GHC = 0x4, // global HBA control
|
||||||
|
IS = 0x8, // interrupt status
|
||||||
|
PI = 0xc, // ports implemented
|
||||||
|
VS = 0x10, // version
|
||||||
|
CCC_CTL = 0x14, // command completion coalescing control
|
||||||
|
CCC_PORTS = 0x18, // command completion coalescing ports
|
||||||
|
EM_LOC = 0x1c, // enclosure management location
|
||||||
|
EM_CTL = 0x20, // enclosure management control
|
||||||
|
CAP2 = 0x24, // HBA capabilities extended
|
||||||
|
BOHC = 0x28, // BIOS/OS handoff control and status
|
||||||
|
|
||||||
|
// registers 0xa0 to 0xff are vendor specific
|
||||||
|
|
||||||
|
// port specific registers are enumerated below,
|
||||||
|
// repeated every 0x80 starting at 0x100
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ahci_port_reg {
|
||||||
|
PxCLB = 0x0,
|
||||||
|
PxCLBU = 0x4,
|
||||||
|
PxFB = 0x8,
|
||||||
|
PxFBU = 0xc,
|
||||||
|
PxIS = 0x10,
|
||||||
|
PxIE = 0x14,
|
||||||
|
PxCMD = 0x18,
|
||||||
|
|
||||||
|
PxTFD = 0x20,
|
||||||
|
PxSIG = 0x24,
|
||||||
|
PxSSTS = 0x28,
|
||||||
|
PxSCTL = 0x2c,
|
||||||
|
PxSERR = 0x30,
|
||||||
|
PxSACT = 0x34,
|
||||||
|
PxCI = 0x38,
|
||||||
|
PxSNTF = 0x3c,
|
||||||
|
PxFBS = 0x40,
|
||||||
|
PxDEVSLP = 0x44,
|
||||||
|
|
||||||
|
PxVS = 0x70,
|
||||||
|
};
|
||||||
|
|
||||||
|
// command header
|
||||||
|
struct ahci_cmd_header {
|
||||||
|
union {
|
||||||
|
uint32_t dw[8]; // raw 8 byte words
|
||||||
|
struct {
|
||||||
|
uint16_t cmd; // raw command bits
|
||||||
|
uint16_t prdtl; // physical region descriptor entry count
|
||||||
|
uint32_t prdbc; // physical region descriptor byte count
|
||||||
|
uint32_t ctba; // command table base address
|
||||||
|
uint32_t ctbau; // command table base address upper
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(ahci_cmd_header) == 0x20, "");
|
||||||
|
|
||||||
|
// physical region descriptor (PRDT entry)
|
||||||
|
struct ahci_prd {
|
||||||
|
union {
|
||||||
|
uint32_t dw[4]; // raw 4 byte words
|
||||||
|
struct {
|
||||||
|
uint32_t dba; // data base address
|
||||||
|
uint32_t dbau; // data base address upper
|
||||||
|
uint32_t _reserved;
|
||||||
|
uint32_t byte_count_ioc; // byte count [0:21], interrupt on completion [31]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(ahci_prd) == 0x10, "");
|
||||||
|
|
||||||
|
struct ahci_cmd_table {
|
||||||
|
uint8_t cfis[64];
|
||||||
|
|
||||||
|
// offset 0x40
|
||||||
|
uint8_t acmd[16];
|
||||||
|
|
||||||
|
// offset 0x80
|
||||||
|
uint8_t _reserved[0x80 - 0x50];
|
||||||
|
|
||||||
|
ahci_prd pdrt[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(ahci_cmd_table) == 0x80, "");
|
||||||
11
dev/block/ahci/ata.cpp
Normal file
11
dev/block/ahci/ata.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
#include "ata.h"
|
||||||
|
|
||||||
|
#include <lk/cpp.h>
|
||||||
|
|
||||||
22
dev/block/ahci/ata.h
Normal file
22
dev/block/ahci/ata.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <hw/ata.h>
|
||||||
|
|
||||||
|
// ata helper routines
|
||||||
|
inline FIS_REG_H2D ata_cmd_identify() {
|
||||||
|
FIS_REG_H2D fis = {};
|
||||||
|
fis.fis_type = FIS_TYPE_REG_H2D;
|
||||||
|
fis.command = ATA_CMD_IDENTIFY;
|
||||||
|
fis.device = 0;
|
||||||
|
fis.c = 1;
|
||||||
|
|
||||||
|
return fis;
|
||||||
|
}
|
||||||
|
|
||||||
99
dev/block/ahci/disk.cpp
Normal file
99
dev/block/ahci/disk.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#include "disk.h"
|
||||||
|
|
||||||
|
#include <lk/bits.h>
|
||||||
|
#include <lk/debug.h>
|
||||||
|
#include <lk/err.h>
|
||||||
|
#include <lk/trace.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ata.h"
|
||||||
|
|
||||||
|
#define LOCAL_TRACE 1
|
||||||
|
|
||||||
|
// offsets in the 256 word (2 byte word) IDENTIFY structure
|
||||||
|
enum ata_identify_words {
|
||||||
|
ATA_IDENTIFY_MODEL_NUMBER = 27, // 40 bytes
|
||||||
|
ATA_IDENTIFY_LOGICAL_SECTOR_COUNT_QWORD = 100, // 4 words of logical sector count
|
||||||
|
ATA_IDENTIFY_PHYS_TO_LOGICAL_SECTOR = 106, // phys size / logical size
|
||||||
|
ATA_IDENTIFY_LOGICAL_SECTOR_SIZE_DWORD = 117, // dword of logical sector size
|
||||||
|
};
|
||||||
|
|
||||||
|
ahci_disk::ahci_disk(ahci_port &p) : port_(p) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ahci_disk::~ahci_disk() = default;
|
||||||
|
|
||||||
|
status_t ahci_disk::identify() {
|
||||||
|
LTRACE_ENTRY;
|
||||||
|
|
||||||
|
__ALIGNED(512) static uint16_t identify_data[256];
|
||||||
|
FIS_REG_H2D fis = ata_cmd_identify();
|
||||||
|
|
||||||
|
int slot;
|
||||||
|
auto err = port_.queue_command(&fis, sizeof(fis), identify_data, sizeof(identify_data), false, &slot);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for it to complete
|
||||||
|
err = port_.wait_for_completion(slot);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LTRACEF("identify data:\n");
|
||||||
|
hexdump8(identify_data, sizeof(identify_data));
|
||||||
|
|
||||||
|
char model[20*2 + 1] = {};
|
||||||
|
for (auto i = 0; i < 20; i++) {
|
||||||
|
model[i * 2] = identify_data[ATA_IDENTIFY_MODEL_NUMBER + i] >> 8;
|
||||||
|
model[i * 2 + 1] = identify_data[ATA_IDENTIFY_MODEL_NUMBER + i] & 0xff;
|
||||||
|
}
|
||||||
|
LTRACEF("model '%s'\n", model);
|
||||||
|
|
||||||
|
// assumes LBA48
|
||||||
|
bool lba48 = identify_data[83] & (1 << 10);
|
||||||
|
if (!lba48) {
|
||||||
|
printf("AHCI: LBA48 required, aborting\n");
|
||||||
|
return ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sector count is 4 words at offset 100
|
||||||
|
uint64_t sector_count = identify_data[ATA_IDENTIFY_LOGICAL_SECTOR_COUNT_QWORD] |
|
||||||
|
((uint64_t)identify_data[ATA_IDENTIFY_LOGICAL_SECTOR_COUNT_QWORD + 1] << 16) |
|
||||||
|
((uint64_t)identify_data[ATA_IDENTIFY_LOGICAL_SECTOR_COUNT_QWORD + 2] << 32) |
|
||||||
|
((uint64_t)identify_data[ATA_IDENTIFY_LOGICAL_SECTOR_COUNT_QWORD + 3] << 48);
|
||||||
|
|
||||||
|
LTRACEF("logical sector count %#llx\n", sector_count);
|
||||||
|
|
||||||
|
// defaults to 512 bytes
|
||||||
|
uint32_t logical_sector_size = 512;
|
||||||
|
uint32_t physical_sector_size = 512;
|
||||||
|
|
||||||
|
auto phys_to_logical_sector = identify_data[ATA_IDENTIFY_PHYS_TO_LOGICAL_SECTOR];
|
||||||
|
//LTRACEF("phys size / logical size %#hx\n", identify_data[ATA_IDENTIFY_PHYS_TO_LOGICAL_SECTOR]);
|
||||||
|
if (BITS(phys_to_logical_sector, 15, 14) == (1 << 14)) { // word 106 has valid info
|
||||||
|
if (BIT(phys_to_logical_sector, 12)) {
|
||||||
|
// logical sector size is specified in word 117..118
|
||||||
|
logical_sector_size = identify_data[ATA_IDENTIFY_LOGICAL_SECTOR_SIZE_DWORD] |
|
||||||
|
((uint32_t)identify_data[ATA_IDENTIFY_LOGICAL_SECTOR_SIZE_DWORD + 1] << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bits 3:0 have physical sector size in power of 2 times logical size
|
||||||
|
physical_sector_size = (1U << BITS(phys_to_logical_sector, 3, 0)) * logical_sector_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
LTRACEF("logical sector size %#x\n", logical_sector_size);
|
||||||
|
LTRACEF("physical sector size %#x\n", physical_sector_size);
|
||||||
|
LTRACEF("total size %#llx\n", sector_count * logical_sector_size);
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
27
dev/block/ahci/disk.h
Normal file
27
dev/block/ahci/disk.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lk/cpp.h>
|
||||||
|
#include <lk/list.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
class ahci_disk {
|
||||||
|
public:
|
||||||
|
ahci_disk(ahci_port &p);
|
||||||
|
~ahci_disk();
|
||||||
|
|
||||||
|
DISALLOW_COPY_ASSIGN_AND_MOVE(ahci_disk);
|
||||||
|
|
||||||
|
status_t identify();
|
||||||
|
|
||||||
|
list_node node_ = LIST_INITIAL_CLEARED_VALUE;
|
||||||
|
private:
|
||||||
|
ahci_port &port_;
|
||||||
|
};
|
||||||
271
dev/block/ahci/port.cpp
Normal file
271
dev/block/ahci/port.cpp
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#include "port.h"
|
||||||
|
|
||||||
|
#include <lk/bits.h>
|
||||||
|
#include <lk/err.h>
|
||||||
|
#include <lk/trace.h>
|
||||||
|
#include <kernel/vm.h>
|
||||||
|
#include <kernel/thread.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ata.h"
|
||||||
|
#include "disk.h"
|
||||||
|
|
||||||
|
#define LOCAL_TRACE 1
|
||||||
|
|
||||||
|
ahci_port::ahci_port(ahci &a, uint num) : ahci_(a), num_(num) {
|
||||||
|
for (auto &e : cmd_complete_event_) {
|
||||||
|
event_init(&e, false, EVENT_FLAG_AUTOUNSIGNAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ahci_port::~ahci_port() {
|
||||||
|
if (mem_region_) {
|
||||||
|
vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)mem_region_);
|
||||||
|
}
|
||||||
|
for (auto &e : cmd_complete_event_) {
|
||||||
|
event_destroy(&e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t ahci_port::probe(ahci_disk **found_disk) {
|
||||||
|
// mask all IRQS on this port regardless if we want to use it
|
||||||
|
write_port_reg(ahci_port_reg::PxIE, 0);
|
||||||
|
|
||||||
|
// clear any pending bits
|
||||||
|
write_port_reg(ahci_port_reg::PxIS, 0xffffffff);
|
||||||
|
|
||||||
|
// check if drive is present
|
||||||
|
auto ssts = read_port_reg(ahci_port_reg::PxSSTS);
|
||||||
|
if (BITS(ssts, 3, 0) != 3) { // check SSTS.DET == 3 (device present and phy comm established)
|
||||||
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
if (BITS_SHIFT(ssts, 11, 8) != 1) { // check SSTS.IPM == 1 (interface in active state)
|
||||||
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
dprintf(INFO, "ahci%d port %u: ssts %#x device present and interface in active state\n",
|
||||||
|
ahci_.get_unit_num(), num_, ssts);
|
||||||
|
|
||||||
|
auto sig = read_port_reg(ahci_port_reg::PxSIG);
|
||||||
|
LTRACEF("port %u: sig %#x\n", num_, sig);
|
||||||
|
|
||||||
|
// if sig is all 1s then it hasn't been scanned yet, so assume it's a disk.
|
||||||
|
// otherwise if its 0x101 it's a disk
|
||||||
|
if (sig != 0xffffffff && sig != 0x101) {
|
||||||
|
TRACEF("skipping unhandled signature %#x\n", sig);
|
||||||
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
LTRACEF("port %u: PxCLB %#x\n", num_, read_port_reg(ahci_port_reg::PxCLB));
|
||||||
|
LTRACEF("port %u: PxCMD %#x\n", num_, read_port_reg(ahci_port_reg::PxCMD));
|
||||||
|
|
||||||
|
// stop the port so we can reset addresses
|
||||||
|
auto cmd_reg = read_port_reg(ahci_port_reg::PxCMD);
|
||||||
|
cmd_reg &= ~((1<<4) | // clear CMD.FRE (fis receive enable)
|
||||||
|
(1<<0)); // clear CMD.ST (start)
|
||||||
|
write_port_reg(ahci_port_reg::PxCMD, cmd_reg);
|
||||||
|
// TODO: wait for CMD.FR to stop
|
||||||
|
|
||||||
|
// allocate a block of contiguous memory for
|
||||||
|
// 32 command list heads (32 * 0x20)
|
||||||
|
// a FIS struct (256 bytes)
|
||||||
|
// 32 command tables with 16 PRDTs per
|
||||||
|
const size_t size = (CMD_COUNT * sizeof(ahci_cmd_header)) + 256 +
|
||||||
|
(CMD_COUNT * CMD_TABLE_ENTRY_SIZE);
|
||||||
|
|
||||||
|
// allocate a contiguous block of ram
|
||||||
|
char str[32];
|
||||||
|
snprintf(str, sizeof(str), "ahci%d.%u cmd/fis", ahci_.get_unit_num(), num_);
|
||||||
|
status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), str, size,
|
||||||
|
(void **)&mem_region_, 0, /* vmm_flags */ 0,
|
||||||
|
ARCH_MMU_FLAG_UNCACHED_DEVICE);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
return ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
memset(mem_region_, 0, size);
|
||||||
|
mem_region_paddr_ = vaddr_to_paddr(mem_region_);
|
||||||
|
|
||||||
|
LTRACEF("cmd_list/fis mapped to %p, pa %#lx\n", mem_region_, mem_region_paddr_);
|
||||||
|
|
||||||
|
// carve up the pointers into this space
|
||||||
|
cmd_list_ = (volatile ahci_cmd_header *)mem_region_;
|
||||||
|
fis_ = (volatile uint8_t *)((uintptr_t)mem_region_ + 32 * sizeof(ahci_cmd_header));
|
||||||
|
cmd_table_ = (volatile ahci_cmd_table *)(fis_ + 256);
|
||||||
|
|
||||||
|
LTRACEF("command list at %p, FIS at %p, per command table at %p\n",
|
||||||
|
cmd_list_, fis_, cmd_table_);
|
||||||
|
|
||||||
|
// set the AHCI port to point to the command header and global fis
|
||||||
|
write_port_reg(ahci_port_reg::PxCLB, vaddr_to_paddr((void *)cmd_list_));
|
||||||
|
write_port_reg(ahci_port_reg::PxFB, vaddr_to_paddr((void *)fis_));
|
||||||
|
#if __INTPTR_WIDTH__ == 64
|
||||||
|
write_port_reg(ahci_port_reg::PxCLBU, vaddr_to_paddr((void *)cmd_list_) >> 32);
|
||||||
|
write_port_reg(ahci_port_reg::PxFBU, vaddr_to_paddr((void *)fis_) >> 32);
|
||||||
|
#else
|
||||||
|
write_port_reg(ahci_port_reg::PxCLBU, 0);
|
||||||
|
write_port_reg(ahci_port_reg::PxFBU, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// set up the command headers
|
||||||
|
auto cmd_table_pa = vaddr_to_paddr((void *)cmd_table_);
|
||||||
|
for (auto i = 0; i < 32; i++) {
|
||||||
|
volatile auto *cmd = &cmd_list_[i];
|
||||||
|
|
||||||
|
// point the cmd header at the corresponding cmd table
|
||||||
|
cmd->ctba = (cmd_table_pa + sizeof(ahci_cmd_table) * i) & 0xffffffff;
|
||||||
|
#if __INTPTR_WIDTH__ == 64
|
||||||
|
cmd->ctbau = (cmd_table_pa + sizeof(ahci_cmd_table) * i) >> 32;
|
||||||
|
#else
|
||||||
|
cmd->ctbau = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart the port
|
||||||
|
cmd_reg |= (1<<4); // set CMD.FRE (fis receive enable)
|
||||||
|
write_port_reg(ahci_port_reg::PxCMD, cmd_reg);
|
||||||
|
cmd_reg |= (1<<0); // set CMD.ST (start)
|
||||||
|
write_port_reg(ahci_port_reg::PxCMD, cmd_reg);
|
||||||
|
|
||||||
|
// unmask some irqs on this port
|
||||||
|
// TODO: unmask more if needed
|
||||||
|
uint32_t ie = (1U << 5) | // Descriptor Processed (DPS)
|
||||||
|
(1U << 3) | // Set device bits interrupt (SDBS)
|
||||||
|
(1U << 2) | // DMA setup FIS (DSS)
|
||||||
|
(1U << 1) | // PIO setup FIS (PSS)
|
||||||
|
(1U << 0); // Device to Host Register FIS (DHRS)
|
||||||
|
write_port_reg(ahci_port_reg::PxIE, ie);
|
||||||
|
|
||||||
|
// we found a disk above, create an object and pass it back
|
||||||
|
auto *disk = new ahci_disk(*this);
|
||||||
|
*found_disk = disk;
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ahci_port::find_free_cmdslot() {
|
||||||
|
uint32_t all_slots = read_port_reg(ahci_port_reg::PxSACT) |
|
||||||
|
read_port_reg(ahci_port_reg::PxCI);
|
||||||
|
|
||||||
|
LTRACEF("all_slots %#x\n", all_slots);
|
||||||
|
|
||||||
|
if (unlikely(all_slots == 0xffffffff)) {
|
||||||
|
// all slots are full
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int avail = __builtin_clz(~all_slots);
|
||||||
|
LTRACEF("avail %u\n", avail);
|
||||||
|
|
||||||
|
return avail;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t ahci_port::queue_command(const void *fis, size_t fis_len, void *buf, size_t buf_len, bool write, int *slot_out) {
|
||||||
|
LTRACEF("fis %p len %zu buf %p len %zu write %d\n", fis, fis_len, buf, buf_len, write);
|
||||||
|
|
||||||
|
DEBUG_ASSERT(fis);
|
||||||
|
DEBUG_ASSERT(fis_len > 0 && fis_len <= 64 && IS_ALIGNED(fis_len, 4));
|
||||||
|
DEBUG_ASSERT(buf || buf_len == 0);
|
||||||
|
|
||||||
|
AutoSpinLock guard(&lock_);
|
||||||
|
|
||||||
|
auto slot = find_free_cmdslot();
|
||||||
|
|
||||||
|
LTRACEF("slot %u\n", slot);
|
||||||
|
|
||||||
|
// clear interrupt status for this port
|
||||||
|
write_port_reg(ahci_port_reg::PxIS, 0xf);
|
||||||
|
|
||||||
|
auto *cmd_table = cmd_table_ptr(slot);
|
||||||
|
|
||||||
|
// set up physical descriptor of a run of memory
|
||||||
|
// XXX for now assume single run
|
||||||
|
auto *prdt = &cmd_table->pdrt[0];
|
||||||
|
auto buf_pa = vaddr_to_paddr(buf);
|
||||||
|
prdt->dba = buf_pa;
|
||||||
|
#if __INTPTR_WIDTH__ == 64
|
||||||
|
prdt->dbau = buf_pa >> 32;
|
||||||
|
#else
|
||||||
|
prdt->dbau = 0;
|
||||||
|
#endif
|
||||||
|
prdt->byte_count_ioc = (buf_len - 1) | (1U<<31); // byte count, interrupt on completion
|
||||||
|
|
||||||
|
// copy command into the command table
|
||||||
|
// TODO: replace with wordwise copy
|
||||||
|
memcpy((void *)cmd_table->cfis, fis, fis_len);
|
||||||
|
|
||||||
|
// set up the command header
|
||||||
|
auto *cmd = &cmd_list_[slot];
|
||||||
|
cmd->cmd = (fis_len / sizeof(uint32_t)) | (write ? (1<<6) : (0<<6)); // command fis size in words, read/write from device
|
||||||
|
cmd->prdtl = 1; // 1 prdt
|
||||||
|
|
||||||
|
//LTRACEF("cmd_table %p\n", cmd_table);
|
||||||
|
//hexdump((const void *)cmd_table, sizeof(*cmd_table) + CMD_TABLE_ENTRY_SIZE);
|
||||||
|
|
||||||
|
// TODO: barrier here
|
||||||
|
// rmb();
|
||||||
|
|
||||||
|
LTRACEF("IS %#x (before kick)\n", read_port_reg(ahci_port_reg::PxIS));
|
||||||
|
|
||||||
|
cmd_pending_ |= (1U << slot);
|
||||||
|
|
||||||
|
// kick the command
|
||||||
|
write_port_reg(ahci_port_reg::PxCI, (1U << slot)); // TODO: RMW?
|
||||||
|
|
||||||
|
*slot_out = slot;
|
||||||
|
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t ahci_port::wait_for_completion(int slot) {
|
||||||
|
DEBUG_ASSERT(slot >= 0 && slot < (int)CMD_COUNT);
|
||||||
|
|
||||||
|
auto err = event_wait(&cmd_complete_event_[slot]);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler_return ahci_port::irq_handler() {
|
||||||
|
LTRACE_ENTRY;
|
||||||
|
|
||||||
|
AutoSpinLockNoIrqSave guard(&lock_);
|
||||||
|
|
||||||
|
const auto raw_is = read_port_reg(ahci_port_reg::PxIS);
|
||||||
|
const auto is = raw_is & read_port_reg(ahci_port_reg::PxIE); // filter by things we're masking
|
||||||
|
|
||||||
|
LTRACEF("raw is %#x is %#x\n", raw_is, is);
|
||||||
|
|
||||||
|
// see if any commands completed
|
||||||
|
const auto ci = read_port_reg(ahci_port_reg::PxCI);
|
||||||
|
auto cmd_complete_bitmap = cmd_pending_ & ~ci;
|
||||||
|
|
||||||
|
LTRACEF("command complete bitmap %#x\n", cmd_complete_bitmap);
|
||||||
|
|
||||||
|
handler_return ret = INT_NO_RESCHEDULE;
|
||||||
|
while (cmd_complete_bitmap != 0) {
|
||||||
|
const size_t cmd_slot = __builtin_ctz(cmd_complete_bitmap);
|
||||||
|
|
||||||
|
DEBUG_ASSERT(cmd_slot < CMD_COUNT);
|
||||||
|
|
||||||
|
LTRACEF("slot %zu completed\n", cmd_slot);
|
||||||
|
|
||||||
|
// this slot completed
|
||||||
|
event_signal(&cmd_complete_event_[cmd_slot], false);
|
||||||
|
ret = INT_RESCHEDULE;
|
||||||
|
|
||||||
|
// mark the command as not pending anymore
|
||||||
|
cmd_pending_ &= ~(1U << cmd_slot);
|
||||||
|
|
||||||
|
// move to the next pending slot (if any)
|
||||||
|
cmd_complete_bitmap &= ~(1U << cmd_slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ack everything for now
|
||||||
|
write_port_reg(ahci_port_reg::PxIS, is);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
75
dev/block/ahci/port.h
Normal file
75
dev/block/ahci/port.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <lk/cpp.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <kernel/spinlock.h>
|
||||||
|
#include <kernel/event.h>
|
||||||
|
|
||||||
|
#include "ahci.h"
|
||||||
|
#include "ahci_hw.h"
|
||||||
|
|
||||||
|
class ahci_disk;
|
||||||
|
|
||||||
|
// per port AHCI object
|
||||||
|
class ahci_port {
|
||||||
|
public:
|
||||||
|
ahci_port(ahci &a, uint num);
|
||||||
|
~ahci_port();
|
||||||
|
|
||||||
|
DISALLOW_COPY_ASSIGN_AND_MOVE(ahci_port);
|
||||||
|
|
||||||
|
handler_return irq_handler();
|
||||||
|
|
||||||
|
status_t probe(ahci_disk **found_disk);
|
||||||
|
|
||||||
|
status_t queue_command(const void *fis, size_t fis_len, void *buf, size_t buf_len, bool write, int *slot_out);
|
||||||
|
status_t wait_for_completion(int slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t read_port_reg(ahci_port_reg reg);
|
||||||
|
void write_port_reg(ahci_port_reg reg, uint32_t val);
|
||||||
|
|
||||||
|
int find_free_cmdslot();
|
||||||
|
volatile ahci_cmd_table *cmd_table_ptr(uint cmd_slot);
|
||||||
|
|
||||||
|
// constants
|
||||||
|
static const size_t CMD_COUNT = 32; // number of active command slots
|
||||||
|
static const size_t PRD_PER_CMD = 16; // physical descriptors per command slot
|
||||||
|
static const size_t CMD_TABLE_ENTRY_SIZE = sizeof(ahci_cmd_table) + sizeof(ahci_prd) * PRD_PER_CMD;
|
||||||
|
|
||||||
|
// members
|
||||||
|
ahci &ahci_;
|
||||||
|
uint num_;
|
||||||
|
|
||||||
|
// per port spinlock
|
||||||
|
spin_lock_t lock_ = SPIN_LOCK_INITIAL_VALUE;
|
||||||
|
|
||||||
|
// pending command bitmap
|
||||||
|
uint32_t cmd_pending_ = 0;
|
||||||
|
event cmd_complete_event_[CMD_COUNT];
|
||||||
|
|
||||||
|
void *mem_region_ = nullptr;
|
||||||
|
paddr_t mem_region_paddr_ = 0;
|
||||||
|
volatile ahci_cmd_header *cmd_list_ = nullptr;
|
||||||
|
volatile uint8_t *fis_ = nullptr;
|
||||||
|
volatile ahci_cmd_table *cmd_table_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint32_t ahci_port::read_port_reg(ahci_port_reg reg) {
|
||||||
|
return ahci_.read_port_reg(num_, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ahci_port::write_port_reg(ahci_port_reg reg, uint32_t val) {
|
||||||
|
ahci_.write_port_reg(num_, reg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline volatile ahci_cmd_table *ahci_port::cmd_table_ptr(uint cmd_slot) {
|
||||||
|
return (volatile ahci_cmd_table *)((uintptr_t)cmd_table_+ CMD_TABLE_ENTRY_SIZE * cmd_slot);
|
||||||
|
}
|
||||||
|
|
||||||
20
dev/block/ahci/rules.mk
Normal file
20
dev/block/ahci/rules.mk
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||||
|
|
||||||
|
# At the moment, this can only be built with hardware MMU available.
|
||||||
|
ifeq (true,$(call TOBOOL,$(WITH_KERNEL_VM)))
|
||||||
|
|
||||||
|
MODULE := $(LOCAL_DIR)
|
||||||
|
|
||||||
|
MODULE_SRCS += $(LOCAL_DIR)/ahci.cpp
|
||||||
|
MODULE_SRCS += $(LOCAL_DIR)/ata.cpp
|
||||||
|
MODULE_SRCS += $(LOCAL_DIR)/disk.cpp
|
||||||
|
MODULE_SRCS += $(LOCAL_DIR)/port.cpp
|
||||||
|
|
||||||
|
MODULE_DEPS += dev/bus/pci
|
||||||
|
MODULE_DEPS += lib/bio
|
||||||
|
|
||||||
|
MODULE_CPPFLAGS += -Wno-invalid-offsetof
|
||||||
|
|
||||||
|
include make/module.mk
|
||||||
|
|
||||||
|
endif # WITH_KERNEL_VM
|
||||||
@@ -2,4 +2,5 @@
|
|||||||
#
|
#
|
||||||
MODULES += dev/bus/pci
|
MODULES += dev/bus/pci
|
||||||
|
|
||||||
|
MODULES += dev/block/ahci
|
||||||
MODULES += dev/net/e1000
|
MODULES += dev/net/e1000
|
||||||
|
|||||||
180
dev/include/hw/ata.h
Normal file
180
dev/include/hw/ata.h
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2022 Travis Geiselbrecht
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
// portions from https://wiki.osdev.org/AHCI
|
||||||
|
//
|
||||||
|
// TODO: reconsider use of bitfields, endian
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device
|
||||||
|
FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host
|
||||||
|
FIS_TYPE_DMA_ACT = 0x39, // DMA activate FIS - device to host
|
||||||
|
FIS_TYPE_DMA_SETUP = 0x41, // DMA setup FIS - bidirectional
|
||||||
|
FIS_TYPE_DATA = 0x46, // Data FIS - bidirectional
|
||||||
|
FIS_TYPE_BIST = 0x58, // BIST activate FIS - bidirectional
|
||||||
|
FIS_TYPE_PIO_SETUP = 0x5F, // PIO setup FIS - device to host
|
||||||
|
FIS_TYPE_DEV_BITS = 0xA1, // Set device bits FIS - device to host
|
||||||
|
} FIS_TYPE;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ATA_CMD_IDENTIFY = 0xec,
|
||||||
|
ATA_CMD_PACKET = 0xa0,
|
||||||
|
ATA_CMD_PACKET_IDENTIFY = 0xa1,
|
||||||
|
} ATA_CMD;
|
||||||
|
|
||||||
|
typedef struct tagFIS_REG_H2D
|
||||||
|
{
|
||||||
|
// DWORD 0
|
||||||
|
uint8_t fis_type; // FIS_TYPE_REG_H2D
|
||||||
|
|
||||||
|
uint8_t pmport:4; // Port multiplier
|
||||||
|
uint8_t rsv0:3; // Reserved
|
||||||
|
uint8_t c:1; // 1: Command, 0: Control
|
||||||
|
|
||||||
|
uint8_t command; // Command register
|
||||||
|
uint8_t featurel; // Feature register, 7:0
|
||||||
|
|
||||||
|
// DWORD 1
|
||||||
|
uint8_t lba0; // LBA low register, 7:0
|
||||||
|
uint8_t lba1; // LBA mid register, 15:8
|
||||||
|
uint8_t lba2; // LBA high register, 23:16
|
||||||
|
uint8_t device; // Device register
|
||||||
|
|
||||||
|
// DWORD 2
|
||||||
|
uint8_t lba3; // LBA register, 31:24
|
||||||
|
uint8_t lba4; // LBA register, 39:32
|
||||||
|
uint8_t lba5; // LBA register, 47:40
|
||||||
|
uint8_t featureh; // Feature register, 15:8
|
||||||
|
|
||||||
|
// DWORD 3
|
||||||
|
uint8_t countl; // Count register, 7:0
|
||||||
|
uint8_t counth; // Count register, 15:8
|
||||||
|
uint8_t icc; // Isochronous command completion
|
||||||
|
uint8_t control; // Control register
|
||||||
|
|
||||||
|
// DWORD 4
|
||||||
|
uint8_t rsv1[4]; // Reserved
|
||||||
|
} FIS_REG_H2D;
|
||||||
|
|
||||||
|
typedef struct tagFIS_REG_D2H
|
||||||
|
{
|
||||||
|
// DWORD 0
|
||||||
|
uint8_t fis_type; // FIS_TYPE_REG_D2H
|
||||||
|
|
||||||
|
uint8_t pmport:4; // Port multiplier
|
||||||
|
uint8_t rsv0:2; // Reserved
|
||||||
|
uint8_t i:1; // Interrupt bit
|
||||||
|
uint8_t rsv1:1; // Reserved
|
||||||
|
|
||||||
|
uint8_t status; // Status register
|
||||||
|
uint8_t error; // Error register
|
||||||
|
|
||||||
|
// DWORD 1
|
||||||
|
uint8_t lba0; // LBA low register, 7:0
|
||||||
|
uint8_t lba1; // LBA mid register, 15:8
|
||||||
|
uint8_t lba2; // LBA high register, 23:16
|
||||||
|
uint8_t device; // Device register
|
||||||
|
|
||||||
|
// DWORD 2
|
||||||
|
uint8_t lba3; // LBA register, 31:24
|
||||||
|
uint8_t lba4; // LBA register, 39:32
|
||||||
|
uint8_t lba5; // LBA register, 47:40
|
||||||
|
uint8_t rsv2; // Reserved
|
||||||
|
|
||||||
|
// DWORD 3
|
||||||
|
uint8_t countl; // Count register, 7:0
|
||||||
|
uint8_t counth; // Count register, 15:8
|
||||||
|
uint8_t rsv3[2]; // Reserved
|
||||||
|
|
||||||
|
// DWORD 4
|
||||||
|
uint8_t rsv4[4]; // Reserved
|
||||||
|
} FIS_REG_D2H;
|
||||||
|
|
||||||
|
typedef struct tagFIS_DATA
|
||||||
|
{
|
||||||
|
// DWORD 0
|
||||||
|
uint8_t fis_type; // FIS_TYPE_DATA
|
||||||
|
|
||||||
|
uint8_t pmport:4; // Port multiplier
|
||||||
|
uint8_t rsv0:4; // Reserved
|
||||||
|
|
||||||
|
uint8_t rsv1[2]; // Reserved
|
||||||
|
|
||||||
|
// DWORD 1 ~ N
|
||||||
|
uint32_t data[1]; // Payload
|
||||||
|
} FIS_DATA;
|
||||||
|
|
||||||
|
typedef struct tagFIS_PIO_SETUP
|
||||||
|
{
|
||||||
|
// DWORD 0
|
||||||
|
uint8_t fis_type; // FIS_TYPE_PIO_SETUP
|
||||||
|
|
||||||
|
uint8_t pmport:4; // Port multiplier
|
||||||
|
uint8_t rsv0:1; // Reserved
|
||||||
|
uint8_t d:1; // Data transfer direction, 1 - device to host
|
||||||
|
uint8_t i:1; // Interrupt bit
|
||||||
|
uint8_t rsv1:1;
|
||||||
|
|
||||||
|
uint8_t status; // Status register
|
||||||
|
uint8_t error; // Error register
|
||||||
|
|
||||||
|
// DWORD 1
|
||||||
|
uint8_t lba0; // LBA low register, 7:0
|
||||||
|
uint8_t lba1; // LBA mid register, 15:8
|
||||||
|
uint8_t lba2; // LBA high register, 23:16
|
||||||
|
uint8_t device; // Device register
|
||||||
|
|
||||||
|
// DWORD 2
|
||||||
|
uint8_t lba3; // LBA register, 31:24
|
||||||
|
uint8_t lba4; // LBA register, 39:32
|
||||||
|
uint8_t lba5; // LBA register, 47:40
|
||||||
|
uint8_t rsv2; // Reserved
|
||||||
|
|
||||||
|
// DWORD 3
|
||||||
|
uint8_t countl; // Count register, 7:0
|
||||||
|
uint8_t counth; // Count register, 15:8
|
||||||
|
uint8_t rsv3; // Reserved
|
||||||
|
uint8_t e_status; // New value of status register
|
||||||
|
|
||||||
|
// DWORD 4
|
||||||
|
uint16_t tc; // Transfer count
|
||||||
|
uint8_t rsv4[2]; // Reserved
|
||||||
|
} FIS_PIO_SETUP;
|
||||||
|
|
||||||
|
typedef struct tagFIS_DMA_SETUP
|
||||||
|
{
|
||||||
|
// DWORD 0
|
||||||
|
uint8_t fis_type; // FIS_TYPE_DMA_SETUP
|
||||||
|
|
||||||
|
uint8_t pmport:4; // Port multiplier
|
||||||
|
uint8_t rsv0:1; // Reserved
|
||||||
|
uint8_t d:1; // Data transfer direction, 1 - device to host
|
||||||
|
uint8_t i:1; // Interrupt bit
|
||||||
|
uint8_t a:1; // Auto-activate. Specifies if DMA Activate FIS is needed
|
||||||
|
|
||||||
|
uint8_t rsved[2]; // Reserved
|
||||||
|
|
||||||
|
// DWORD 1&2
|
||||||
|
uint64_t DMAbufferID; // DMA Buffer Identifier. Used to Identify DMA buffer in host memory.
|
||||||
|
// SATA Spec says host specific and not in Spec. Trying AHCI spec might work.
|
||||||
|
|
||||||
|
// DWORD 3
|
||||||
|
uint32_t rsvd; // Reserved
|
||||||
|
|
||||||
|
// DWORD 4
|
||||||
|
uint32_t DMAbufOffset; // Byte offset into buffer. First 2 bits must be 0
|
||||||
|
|
||||||
|
// DWORD 5
|
||||||
|
uint32_t TransferCount; // Number of bytes to transfer. Bit 0 must be 0
|
||||||
|
|
||||||
|
// DWORD 6
|
||||||
|
uint32_t resvd; // Reserved
|
||||||
|
} FIS_DMA_SETUP;
|
||||||
Reference in New Issue
Block a user