/* * \brief ATA device class * \author Sebastian.Sump * \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 #include #include /* 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(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; }