318 lines
7.4 KiB
C++
318 lines
7.4 KiB
C++
/*
|
|
* \brief ATA device class
|
|
* \author Sebastian.Sump <Sebastian.Sumpf@genode-labs.com>
|
|
* \date 2010-07-15
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2010-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <base/lock.h>
|
|
#include <io_port_session/io_port_session.h>
|
|
#include <irq_session/connection.h>
|
|
|
|
/* local includes */
|
|
#include "ata_device.h"
|
|
#include "ata_bus_master.h"
|
|
#include "io.h"
|
|
#include "mindrvr.h"
|
|
|
|
static const int verbose = 0;
|
|
|
|
using namespace Genode;
|
|
|
|
Ata::Device::Device(unsigned base_cmd, unsigned base_ctrl)
|
|
: _lba48(false), _host_protected_area(false)
|
|
{
|
|
_pio = new(env()->heap()) Ata::Io_port(base_cmd, base_ctrl);
|
|
}
|
|
|
|
|
|
Ata::Device::~Device()
|
|
{
|
|
destroy(env()->heap(), _pio);
|
|
|
|
if (_irq)
|
|
destroy(env()->heap(), _irq);
|
|
|
|
if (_bus_master)
|
|
destroy(env()->heap(), _bus_master);
|
|
|
|
}
|
|
|
|
|
|
void Ata::Device::set_bus_master(bool secondary)
|
|
{
|
|
_bus_master = new(env()->heap()) Ata::Bus_master(secondary);
|
|
}
|
|
|
|
|
|
void Ata::Device::set_irq(unsigned irq)
|
|
{
|
|
_irq = new(env()->heap()) Irq_connection(irq);
|
|
}
|
|
|
|
|
|
void Ata::Device::probe_dma()
|
|
{
|
|
/* find bus master interface base address */
|
|
if (!_bus_master->scan_pci())
|
|
return;
|
|
|
|
/* read device information */
|
|
reg_reset(dev_num());
|
|
|
|
unsigned char *buffer;
|
|
if (!(env()->heap()->alloc(4096, &buffer))) return;
|
|
|
|
unsigned char cmd = dynamic_cast<Atapi_device *>(this) ? CMD_IDENTIFY_DEVICE_PACKET : CMD_IDENTIFY_DEVICE;
|
|
if (!reg_pio_data_in_lba28(
|
|
dev_num(),
|
|
cmd,
|
|
0, 1,
|
|
0L,
|
|
buffer,
|
|
1L, 0 )) {
|
|
|
|
/* check command set's LBA48 bit */
|
|
_lba48 = !!(buffer[167] & 0x4);
|
|
/* check for host protected area feature */
|
|
_host_protected_area = !!(buffer[165] & 0x4);
|
|
|
|
Genode::printf( "Adress mode is LBA%d\n", _lba48 ? 48 : 28 );
|
|
Genode::printf("UDMA Modes supported:\n");
|
|
|
|
for (int i = 0; i <= 5; i++) {
|
|
Genode::printf("\t%d and below: %s enabled: %s\n",
|
|
i ,(buffer[176] & (1 << i)) ? "yes" : "no",
|
|
(buffer[177] & (1 << i)) ? "yes" : "no");
|
|
|
|
if (buffer[177] & (1 << i)) {
|
|
|
|
/* initialize PRD's */
|
|
dma_pci_config();
|
|
_dma = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
env()->heap()->free(buffer, 4096);
|
|
}
|
|
|
|
|
|
void Ata::Device::read_capacity()
|
|
{
|
|
_block_start = 0;
|
|
_block_size = 512;
|
|
|
|
enum {
|
|
CMD_NATIVE_MAX_ADDRESS = 0xf8, /* LBA28 */
|
|
CMD_NATIVE_MAX_ADDRESS_EXT = 0x27, /* LBA48 */
|
|
};
|
|
|
|
/*
|
|
* If both LBA48 is enabled and host protected area feature is set, then NATIVE MAX
|
|
* ADDRESS EXT becomes mandatory, use LBA28 otherwise
|
|
*/
|
|
if (_lba48 && _host_protected_area) {
|
|
if (!reg_non_data_lba48(dev_num(), CMD_NATIVE_MAX_ADDRESS_EXT, 0, 1, 0UL, 0UL)) {
|
|
|
|
_block_end = _pio->inb(CB_SN); /* 0 - 7 */
|
|
_block_end |= _pio->inb(CB_CL) << 8; /* 8 - 15 */
|
|
_block_end |= _pio->inb(CB_CH) << 16; /* 16 - 23 */
|
|
|
|
/* read higher order LBA registers */
|
|
_pio->outb(CB_DC, CB_DC_HOB);
|
|
_block_end |= _pio->inb(CB_SN) << 24; /* 24 - 31 */
|
|
/* FIXME: read bits 32 - 47 */
|
|
|
|
}
|
|
} else {
|
|
if (!reg_non_data_lba28(dev_num(), CMD_NATIVE_MAX_ADDRESS, 0, 1, 0UL)) {
|
|
|
|
_block_end = _pio->inb(CB_SN); /* 0 - 7 */
|
|
_block_end |= _pio->inb(CB_CL) << 8; /* 8 - 15 */
|
|
_block_end |= _pio->inb(CB_CH) << 16; /* 16 - 23 */
|
|
_block_end |= (_pio->inb(CB_DH) & 0xf) << 24; /* 24 - 27 */
|
|
}
|
|
}
|
|
|
|
PINF("First block: %u last block %u, block size %u",
|
|
_block_start, _block_end, _block_size);
|
|
}
|
|
|
|
|
|
void Ata::Device::_read(Block::sector_t block_nr,
|
|
Genode::size_t count,
|
|
char *buffer,
|
|
bool dma)
|
|
{
|
|
Genode::size_t offset = 0;
|
|
|
|
while (count > 0) {
|
|
Genode::size_t c = count > 255 ? 255 : count;
|
|
|
|
if (dma) {
|
|
|
|
if (verbose)
|
|
PDBG("DMA read: block %llu, c %zu, buffer: %p",
|
|
block_nr, c, (void*)(buffer + offset));
|
|
|
|
if (!_lba48)
|
|
{
|
|
if (dma_pci_lba28(dev_num(), CMD_READ_DMA, 0, c, block_nr,
|
|
(unsigned char*)(buffer + offset), c))
|
|
throw Io_error();
|
|
} else {
|
|
if (dma_pci_lba48(dev_num(), CMD_READ_DMA_EXT, 0, c, 0, block_nr,
|
|
(unsigned char*)(buffer + offset), c))
|
|
throw Io_error();
|
|
|
|
}
|
|
}
|
|
else {
|
|
if (!_lba48)
|
|
{
|
|
if (reg_pio_data_in_lba28(dev_num(), CMD_READ_SECTORS, 0, c, block_nr,
|
|
(unsigned char*)(buffer + offset), c, 0))
|
|
throw Io_error();
|
|
} else {
|
|
if (reg_pio_data_in_lba48(dev_num(), CMD_READ_SECTORS_EXT, 0, c, 0, block_nr,
|
|
(unsigned char*)(buffer + offset), c, 0))
|
|
throw Io_error();
|
|
}
|
|
}
|
|
|
|
count -= c;
|
|
block_nr += c;
|
|
offset += c * block_size();
|
|
}
|
|
}
|
|
|
|
|
|
void Ata::Device::_write(Block::sector_t block_nr,
|
|
Genode::size_t count,
|
|
char const *buffer,
|
|
bool dma)
|
|
{
|
|
Genode::size_t offset = 0;
|
|
|
|
while (count > 0) {
|
|
Genode::size_t c = count > 255 ? 255 : count;
|
|
|
|
if (dma) {
|
|
|
|
if (verbose)
|
|
PDBG("DMA read: block %llu, c %zu, buffer: %p",
|
|
block_nr, c, (void*)(buffer + offset));
|
|
|
|
if (!_lba48)
|
|
{
|
|
if (dma_pci_lba28(dev_num(), CMD_WRITE_DMA, 0, c, block_nr,
|
|
(unsigned char*)(buffer + offset), c))
|
|
throw Io_error();
|
|
}
|
|
else {
|
|
if (dma_pci_lba48(dev_num(), CMD_WRITE_DMA_EXT, 0, c, 0, block_nr,
|
|
(unsigned char*)(buffer + offset), c))
|
|
throw Io_error();
|
|
|
|
}
|
|
}
|
|
else {
|
|
if (!_lba48)
|
|
{
|
|
if (reg_pio_data_out_lba28(dev_num(), CMD_WRITE_SECTORS, 0, c, block_nr,
|
|
(unsigned char*)(buffer + offset), c, 0))
|
|
throw Io_error();
|
|
} else {
|
|
if (reg_pio_data_out_lba48(dev_num(), CMD_WRITE_SECTORS_EXT, 0, c, 0, block_nr,
|
|
(unsigned char*)(buffer + offset), c, 0))
|
|
throw Io_error();
|
|
}
|
|
}
|
|
|
|
count -= c;
|
|
block_nr += c;
|
|
offset += c * block_size();
|
|
}
|
|
}
|
|
|
|
|
|
Ata::Device * Ata::Device::probe_legacy(int search_type)
|
|
{
|
|
unsigned base[] = { 0x1f0, 0x170 };
|
|
unsigned dev_irq[] = { 14, 15 };
|
|
|
|
const char *type = "";
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
Device *dev = new (env()->heap() )Device(base[i], base[i] + 0x200);
|
|
Device::current(dev);
|
|
|
|
/* Scan for devices */
|
|
reg_config();
|
|
|
|
for (unsigned char j = 0; j < 2; j++) {
|
|
|
|
switch (reg_config_info[j]) {
|
|
|
|
case REG_CONFIG_TYPE_NONE:
|
|
type = "none"; break;
|
|
|
|
case REG_CONFIG_TYPE_UNKN:
|
|
type = "unknown"; break;
|
|
|
|
case REG_CONFIG_TYPE_ATA:
|
|
type = "ATA"; break;
|
|
|
|
case REG_CONFIG_TYPE_ATAPI:
|
|
type = "ATAPI";
|
|
destroy (env()->heap(), dev);
|
|
dev = new (env()->heap() )Atapi_device(base[i], base[i] + 0x200);
|
|
break;
|
|
|
|
default:
|
|
type = "";
|
|
}
|
|
|
|
Genode::printf("IDE %d Device %d: %s IRQ: %u\n", i, j, type, dev_irq[i]);
|
|
|
|
/* prepare device */
|
|
if (reg_config_info[j] == search_type) {
|
|
|
|
dev->set_bus_master(i);
|
|
dev->set_dev_num(j);
|
|
dev->set_irq(dev_irq[i]);
|
|
|
|
dev->probe_dma();
|
|
Genode::printf("Device initialized! Enabling interrupts ...\n");
|
|
int_use_intr_flag = 1;
|
|
reg_reset(dev->dev_num());
|
|
|
|
return dev;
|
|
}
|
|
}
|
|
|
|
type = "";
|
|
destroy(env()->heap(), dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
Block::Session::Operations Ata::Device::ops()
|
|
{
|
|
Block::Session::Operations o;
|
|
o.set_operation(Block::Packet_descriptor::READ);
|
|
o.set_operation(Block::Packet_descriptor::WRITE);
|
|
return o;
|
|
}
|