Serenity Operating System
at hosted 195 lines 6.5 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27//#define PATA_DEVICE_DEBUG 28 29#include <AK/Memory.h> 30#include <AK/StringView.h> 31#include <Kernel/Devices/PATAChannel.h> 32#include <Kernel/Devices/PATADiskDevice.h> 33#include <Kernel/FileSystem/FileDescription.h> 34 35namespace Kernel { 36 37NonnullRefPtr<PATADiskDevice> PATADiskDevice::create(PATAChannel& channel, DriveType type, int major, int minor) 38{ 39 return adopt(*new PATADiskDevice(channel, type, major, minor)); 40} 41 42PATADiskDevice::PATADiskDevice(PATAChannel& channel, DriveType type, int major, int minor) 43 : BlockDevice(major, minor, 512) 44 , m_drive_type(type) 45 , m_channel(channel) 46{ 47} 48 49PATADiskDevice::~PATADiskDevice() 50{ 51} 52 53const char* PATADiskDevice::class_name() const 54{ 55 return "PATADiskDevice"; 56} 57 58bool PATADiskDevice::read_blocks(unsigned index, u16 count, u8* out) 59{ 60 if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource()) 61 return read_sectors_with_dma(index, count, out); 62 return read_sectors(index, count, out); 63} 64 65bool PATADiskDevice::write_blocks(unsigned index, u16 count, const u8* data) 66{ 67 if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource()) 68 return write_sectors_with_dma(index, count, data); 69 for (unsigned i = 0; i < count; ++i) { 70 if (!write_sectors(index + i, 1, data + i * 512)) 71 return false; 72 } 73 return true; 74} 75 76void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt) 77{ 78 m_cylinders = cyls; 79 m_heads = heads; 80 m_sectors_per_track = spt; 81} 82 83ssize_t PATADiskDevice::read(FileDescription& fd, u8* outbuf, ssize_t len) 84{ 85 unsigned index = fd.offset() / block_size(); 86 u16 whole_blocks = len / block_size(); 87 ssize_t remaining = len % block_size(); 88 89 unsigned blocks_per_page = PAGE_SIZE / block_size(); 90 91 // PATAChannel will chuck a wobbly if we try to read more than PAGE_SIZE 92 // at a time, because it uses a single page for its DMA buffer. 93 if (whole_blocks >= blocks_per_page) { 94 whole_blocks = blocks_per_page; 95 remaining = 0; 96 } 97 98#ifdef PATA_DEVICE_DEBUG 99 klog() << "PATADiskDevice::read() index=" << index << " whole_blocks=" << whole_blocks << " remaining=" << remaining; 100#endif 101 102 if (whole_blocks > 0) { 103 if (!read_blocks(index, whole_blocks, outbuf)) 104 return -1; 105 } 106 107 off_t pos = whole_blocks * block_size(); 108 109 if (remaining > 0) { 110 auto buf = ByteBuffer::create_uninitialized(block_size()); 111 if (!read_blocks(index + whole_blocks, 1, buf.data())) 112 return pos; 113 memcpy(&outbuf[pos], buf.data(), remaining); 114 } 115 116 return pos + remaining; 117} 118 119bool PATADiskDevice::can_read(const FileDescription& fd) const 120{ 121 return static_cast<unsigned>(fd.offset()) < (m_cylinders * m_heads * m_sectors_per_track * block_size()); 122} 123 124ssize_t PATADiskDevice::write(FileDescription& fd, const u8* inbuf, ssize_t len) 125{ 126 unsigned index = fd.offset() / block_size(); 127 u16 whole_blocks = len / block_size(); 128 ssize_t remaining = len % block_size(); 129 130 unsigned blocks_per_page = PAGE_SIZE / block_size(); 131 132 // PATAChannel will chuck a wobbly if we try to write more than PAGE_SIZE 133 // at a time, because it uses a single page for its DMA buffer. 134 if (whole_blocks >= blocks_per_page) { 135 whole_blocks = blocks_per_page; 136 remaining = 0; 137 } 138 139#ifdef PATA_DEVICE_DEBUG 140 klog() << "PATADiskDevice::write() index=" << index << " whole_blocks=" << whole_blocks << " remaining=" << remaining; 141#endif 142 143 if (whole_blocks > 0) { 144 if (!write_blocks(index, whole_blocks, inbuf)) 145 return -1; 146 } 147 148 off_t pos = whole_blocks * block_size(); 149 150 // since we can only write in block_size() increments, if we want to do a 151 // partial write, we have to read the block's content first, modify it, 152 // then write the whole block back to the disk. 153 if (remaining > 0) { 154 auto buf = ByteBuffer::create_zeroed(block_size()); 155 if (!read_blocks(index + whole_blocks, 1, buf.data())) 156 return pos; 157 memcpy(buf.data(), &inbuf[pos], remaining); 158 if (!write_blocks(index + whole_blocks, 1, buf.data())) 159 return pos; 160 } 161 162 return pos + remaining; 163} 164 165bool PATADiskDevice::can_write(const FileDescription& fd) const 166{ 167 return static_cast<unsigned>(fd.offset()) < (m_cylinders * m_heads * m_sectors_per_track * block_size()); 168} 169 170bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) 171{ 172 return m_channel.ata_read_sectors_with_dma(lba, count, outbuf, is_slave()); 173} 174 175bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, u8* outbuf) 176{ 177 return m_channel.ata_read_sectors(start_sector, count, outbuf, is_slave()); 178} 179 180bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) 181{ 182 return m_channel.ata_write_sectors_with_dma(lba, count, inbuf, is_slave()); 183} 184 185bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const u8* inbuf) 186{ 187 return m_channel.ata_write_sectors(start_sector, count, inbuf, is_slave()); 188} 189 190bool PATADiskDevice::is_slave() const 191{ 192 return m_drive_type == DriveType::Slave; 193} 194 195}