Serenity Operating System
at portability 193 lines 6.4 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 <Kernel/Devices/PATAChannel.h> 30#include <Kernel/Devices/PATADiskDevice.h> 31#include <Kernel/FileSystem/FileDescription.h> 32 33namespace Kernel { 34 35NonnullRefPtr<PATADiskDevice> PATADiskDevice::create(PATAChannel& channel, DriveType type, int major, int minor) 36{ 37 return adopt(*new PATADiskDevice(channel, type, major, minor)); 38} 39 40PATADiskDevice::PATADiskDevice(PATAChannel& channel, DriveType type, int major, int minor) 41 : BlockDevice(major, minor, 512) 42 , m_drive_type(type) 43 , m_channel(channel) 44{ 45} 46 47PATADiskDevice::~PATADiskDevice() 48{ 49} 50 51const char* PATADiskDevice::class_name() const 52{ 53 return "PATADiskDevice"; 54} 55 56bool PATADiskDevice::read_blocks(unsigned index, u16 count, u8* out) 57{ 58 if (m_channel.m_bus_master_base && m_channel.m_dma_enabled.resource()) 59 return read_sectors_with_dma(index, count, out); 60 return read_sectors(index, count, out); 61} 62 63bool PATADiskDevice::write_blocks(unsigned index, u16 count, const u8* data) 64{ 65 if (m_channel.m_bus_master_base && m_channel.m_dma_enabled.resource()) 66 return write_sectors_with_dma(index, count, data); 67 for (unsigned i = 0; i < count; ++i) { 68 if (!write_sectors(index + i, 1, data + i * 512)) 69 return false; 70 } 71 return true; 72} 73 74void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt) 75{ 76 m_cylinders = cyls; 77 m_heads = heads; 78 m_sectors_per_track = spt; 79} 80 81ssize_t PATADiskDevice::read(FileDescription& fd, u8* outbuf, ssize_t len) 82{ 83 unsigned index = fd.offset() / block_size(); 84 u16 whole_blocks = len / block_size(); 85 ssize_t remaining = len % block_size(); 86 87 unsigned blocks_per_page = PAGE_SIZE / block_size(); 88 89 // PATAChannel will chuck a wobbly if we try to read more than PAGE_SIZE 90 // at a time, because it uses a single page for its DMA buffer. 91 if (whole_blocks >= blocks_per_page) { 92 whole_blocks = blocks_per_page; 93 remaining = 0; 94 } 95 96#ifdef PATA_DEVICE_DEBUG 97 kprintf("PATADiskDevice::read() index=%d whole_blocks=%d remaining=%d\n", index, whole_blocks, remaining); 98#endif 99 100 if (whole_blocks > 0) { 101 if (!read_blocks(index, whole_blocks, outbuf)) 102 return -1; 103 } 104 105 off_t pos = whole_blocks * block_size(); 106 107 if (remaining > 0) { 108 auto buf = ByteBuffer::create_uninitialized(block_size()); 109 if (!read_blocks(index + whole_blocks, 1, buf.data())) 110 return pos; 111 memcpy(&outbuf[pos], buf.data(), remaining); 112 } 113 114 return pos + remaining; 115} 116 117bool PATADiskDevice::can_read(const FileDescription& fd) const 118{ 119 return static_cast<unsigned>(fd.offset()) < (m_cylinders * m_heads * m_sectors_per_track * block_size()); 120} 121 122ssize_t PATADiskDevice::write(FileDescription& fd, const u8* inbuf, ssize_t len) 123{ 124 unsigned index = fd.offset() / block_size(); 125 u16 whole_blocks = len / block_size(); 126 ssize_t remaining = len % block_size(); 127 128 unsigned blocks_per_page = PAGE_SIZE / block_size(); 129 130 // PATAChannel will chuck a wobbly if we try to write more than PAGE_SIZE 131 // at a time, because it uses a single page for its DMA buffer. 132 if (whole_blocks >= blocks_per_page) { 133 whole_blocks = blocks_per_page; 134 remaining = 0; 135 } 136 137#ifdef PATA_DEVICE_DEBUG 138 kprintf("PATADiskDevice::write() index=%d whole_blocks=%d remaining=%d\n", index, whole_blocks, remaining); 139#endif 140 141 if (whole_blocks > 0) { 142 if (!write_blocks(index, whole_blocks, inbuf)) 143 return -1; 144 } 145 146 off_t pos = whole_blocks * block_size(); 147 148 // since we can only write in block_size() increments, if we want to do a 149 // partial write, we have to read the block's content first, modify it, 150 // then write the whole block back to the disk. 151 if (remaining > 0) { 152 auto buf = ByteBuffer::create_zeroed(block_size()); 153 if (!read_blocks(index + whole_blocks, 1, buf.data())) 154 return pos; 155 memcpy(buf.data(), &inbuf[pos], remaining); 156 if (!write_blocks(index + whole_blocks, 1, buf.data())) 157 return pos; 158 } 159 160 return pos + remaining; 161} 162 163bool PATADiskDevice::can_write(const FileDescription& fd) const 164{ 165 return static_cast<unsigned>(fd.offset()) < (m_cylinders * m_heads * m_sectors_per_track * block_size()); 166} 167 168bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf) 169{ 170 return m_channel.ata_read_sectors_with_dma(lba, count, outbuf, is_slave()); 171} 172 173bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, u8* outbuf) 174{ 175 return m_channel.ata_read_sectors(start_sector, count, outbuf, is_slave()); 176} 177 178bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf) 179{ 180 return m_channel.ata_write_sectors_with_dma(lba, count, inbuf, is_slave()); 181} 182 183bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const u8* inbuf) 184{ 185 return m_channel.ata_write_sectors(start_sector, count, inbuf, is_slave()); 186} 187 188bool PATADiskDevice::is_slave() const 189{ 190 return m_drive_type == DriveType::Slave; 191} 192 193}