The Pedigree Project 0.1
BusMasterIde.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "BusMasterIde.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/processor/IoBase.h"
24#include "pedigree/kernel/processor/MemoryRegion.h"
25#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
26#include "pedigree/kernel/processor/Processor.h"
27#include "pedigree/kernel/processor/ProcessorInformation.h"
28#include "pedigree/kernel/processor/VirtualAddressSpace.h"
29#include "pedigree/kernel/processor/types.h"
30#include "pedigree/kernel/utilities/new"
31
32BusMasterIde::BusMasterIde()
33 : m_pBase(0),
34 m_PrdTableLock(),
35 m_PrdTable(0),
36 m_LastPrdTableOffset(0),
37 m_PrdTablePhys(0),
38 m_PrdTableMemRegion("bus-master-ide"),
39 m_bActive(false) {}
40
41BusMasterIde::~BusMasterIde() {
42 if (m_pBase) {
43 delete m_pBase;
44 m_pBase = 0;
45 }
46}
47
49 // Firstly verify the incoming ports
50 if (!pBase)
51 return false;
52 // Deviation from convention a bit here... The controller creating the
53 // IoBase for us to use will split the port range into Primary/Secondary
54 // ranges, which simplifies driver code.
55 if (pBase->size() != 8)
56 return false;
57
58 // Right, it seems the I/O base checks out... Try and allocate some memory
59 // for the PRDT now.
63 ERROR("BusMasterIde: Couldn't allocate the PRD table!");
64 return false;
65 } else {
66 m_PrdTable = reinterpret_cast<PhysicalRegionDescriptor*>(m_PrdTableMemRegion.virtualAddress());
68 NOTICE("BusMasterIde: PRD table at v="
69 << Hex << reinterpret_cast<uintptr_t>(m_PrdTableMemRegion.virtualAddress())
70 << ", p=" << m_PrdTablePhys << ".");
71 }
72
73 // Reset active state.
74 m_bActive = false;
75
76 // All is well, set our internal port base and return success!
77 m_pBase = pBase;
78 return true;
79}
80
82 if (!m_pBase) {
83 return false;
84 }
85
86 return m_bActive.compareAndSwap(false, true);
87}
88
89bool BusMasterIde::add(uintptr_t buffer, size_t nBytes) {
90 // Sanity check
91 if (!buffer || !nBytes || !m_pBase)
92 return false;
93 if (!m_bActive) {
94 ERROR("BusMasterIde::add called without transaction ownership");
95 return false;
96 }
97
98 // A couple of useful things to know
99 size_t prdTableEntries = (m_PrdTableMemRegion.size() / sizeof(PhysicalRegionDescriptor));
100
101 // Firstly check if we need to wait a bit... If an operation
102 m_pBase->read8(Status);
104
105 // Okay, we're good to go - check that the buffer is mapped in first
107 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
108 if (va.isMapped(reinterpret_cast<void*>(buffer))) {
109 // Okay, the address is mapped, so we can start inserting PRD table
110 // entries.
111 size_t nRemainingBytes = nBytes, currOffset = 0, i = 0;
112 while (nRemainingBytes && ((m_LastPrdTableOffset + i) < prdTableEntries)) {
113 // Grab the mapping of this specfic portion of the block. Each PRD
114 // entry covers a 4096-byte region of memory (it can cover up to
115 // 65,536 bytes, but by using only 4096-byte regions it is possible
116 // to avoid the contiguous physical RAM requirement).
117 void* loc = reinterpret_cast<void*>(buffer + currOffset);
118 if (va.isMapped(loc)) {
119 physical_uintptr_t physPage = 0;
120 size_t flags = 0;
121 va.getMapping(reinterpret_cast<void*>(buffer + currOffset), physPage, flags);
122 // NOTICE("v=" << Hex << (buffer + currOffset) << ", p=" <<
123 // physPage);
124
125 // DMA can only work with pages under 4G
126 if (physPage >= (1ULL << 32ULL)) {
127 ERROR(
128 "BusMasterIde: physical pages must be in the first 4GB "
129 "of address space [given page: "
130 << Hex << physPage << "]!");
131 return false;
132 }
133
134 // Add in whatever offset into the page we may have in the
135 // buffer parameter.
136 size_t pageOffset = 0;
137 if (i == 0)
138 pageOffset = (buffer & 0xFFF);
139
140 // Install into the PRD table now
141 // NOTICE("PRD[" << Dec << m_LastPrdTableOffset + i << Hex <<
142 // "].addr=" << (physPage + pageOffset) << ".");
143 m_PrdTable[m_LastPrdTableOffset + i].physAddr = physPage + pageOffset;
144
145 // Determine the transfer size we should use
146 size_t transferSize = nRemainingBytes;
147 if (transferSize > 4096)
148 transferSize = 4096 - pageOffset;
149 // NOTICE("PRD[" << Dec << m_LastPrdTableOffset + i << Hex <<
150 // "].size=" << transferSize << ".");
151 m_PrdTable[m_LastPrdTableOffset + i].byteCount = transferSize & 0xFFFF;
152
153 // Complete the PRD entry after determining the next offset
154 // NOTICE("BEFORE: Current offset = " << currOffset << ",
155 // remaining bytes: " << nRemainingBytes);
156 currOffset += transferSize;
157 nRemainingBytes -= transferSize;
158 // NOTICE("AFTER: Current offset = " << currOffset << ",
159 // remaining bytes: " << nRemainingBytes);
160 if (!nRemainingBytes)
161 m_PrdTable[m_LastPrdTableOffset + i].rsvdEot = 0x8000; // End-of-table?
162 else
163 m_PrdTable[m_LastPrdTableOffset + i].rsvdEot = 0;
164 i++;
165 } else
166 FATAL("BusMasterIde: Part of the incoming buffer was not mapped (" << loc << ")!");
167 }
168
169 // If we added an entry, remove the EOT from any previous PRD that was
170 // present.
171 if (i && m_LastPrdTableOffset)
172 m_PrdTable[m_LastPrdTableOffset - 1].rsvdEot = 0;
173 // If we didn't add an entry, just return failure
174 else if (!i)
175 return false;
176
178 return true;
179 } else
180 return false; // Buffer not mapped - nothing we can do!
181}
182
183bool BusMasterIde::begin(bool bWrite) {
184 if (!m_bActive) {
185 ERROR("BusMasterIde::begin called without transaction ownership");
186 return false;
187 }
188
189 // If no other command is running, set the PRD physical address and
190 // begin the command.
191 uint8_t statusReg = m_pBase->read8(Status);
192 if (!(statusReg & 0x1)) {
193 // Write the physical address to the table address register
194 m_pBase->write32(m_PrdTablePhys, PrdTableAddr);
195
196 // Begin the command
197 uint8_t cmdReg = m_pBase->read8(Command);
198 if (cmdReg & 0x1)
199 FATAL("BusMaster IDE status and command registers don't make sense");
200 cmdReg = (cmdReg & 0xF6) | 0x1 | (bWrite ? 0 : 8);
201 m_pBase->write8(cmdReg, BusMasterIde::Command);
202 } else {
203 // Oops, something went wrong - no more transfer.
204 WARNING("BusMaster IDE hit a bad status in begin(): " << statusReg);
205 return false;
206 }
207
208 return true;
209}
210
212 // Sanity check
213 if (!m_pBase)
214 return false;
215
216 // Easy check here
217 uint8_t statusReg = m_pBase->read8(Status);
218 return (statusReg & 0x4);
219}
220
222 // Sanity check
223 if (!m_pBase)
224 return false;
225
226 // Easy check here
227 uint8_t statusReg = m_pBase->read8(Status);
228 return (statusReg & 0x2);
229}
230
232 // Sanity check
233 if (!m_pBase)
234 return false;
235
236 // Easy check here
237 uint8_t statusReg = m_pBase->read8(Status);
238 return (statusReg & 0x1) == 0;
239}
240
242 // Sanity check
243 if (!m_pBase) {
244 m_bActive = false;
245 return;
246 }
247
248 // Read the status register to dump information about the command completion
249 uint8_t statusReg = m_pBase->read8(Status);
250 EMIT_IF(BusMasterVerboseLogging) {
251 if ((statusReg & 0x1) && (!(statusReg & 0x4))) {
252 // DMA transfer in progress. Abort the transfer.
253 NOTICE("BusMasterIde: aborting transfer in progress");
254 } else if ((!(statusReg & 0x1)) && (statusReg & 0x4)) {
255 // IDE device triggered an interrupt, successful transfer
256 NOTICE("BusMasterIde: successful transfer, exact transfer size");
257 } else if ((statusReg & 0x1) && (statusReg & 0x4)) {
258 // IDE device triggered an interrupt, successful transfer
259 NOTICE("BusMasterIde: successful transfer, more buffer space than needed");
260 } else {
261 // Error condition
262 NOTICE("Status register = " << statusReg << ".");
263 if (!(statusReg & 0x1)) {
264 NOTICE("BusMasterIde: not enough buffer space provided");
265 } else {
266 NOTICE("BusMasterIde: device/controller signalled an error");
267 }
268 }
269 }
270
271 // Whatever happened, we need to reset state
272 uint8_t cmdReg = m_pBase->read8(Command);
273 m_pBase->write8(cmdReg & 0xF6, Command);
274
275 // And ack whatever's in the status register too
276 m_pBase->write8(statusReg, Status);
277
278 // This transfer is now complete.
280
281 // Publish idle only after every transaction-owned field is reset.
282 m_bActive = false;
283}
284
286 if (!m_bActive.compareAndSwap(false, true)) {
287 return false;
288 }
289
291 return true;
292}
bool beginTransaction()
bool add(uintptr_t buffer, size_t nBytes)
Adds a buffer to a DMA transaction.
bool hasError()
Determines if an ERROR has occurred on this channel.
size_t m_LastPrdTableOffset
bool completeIdleTransaction()
Atomic< bool > m_bActive
IoBase * m_pBase
bool hasCompleted()
Determines if the CURRENT TRANSFER has completed.
bool initialise(IoBase *pBase)
Initialises DMA for a specific channel.
void commandComplete()
Called by drivers when a command completes.
bool hasInterrupt()
Determines if an INTERRUPT has occurred on this channel.
Mutex m_PrdTableLock
MemoryRegion m_PrdTableMemRegion
bool begin(bool bWrite)
Begin a DMA operation.
physical_uintptr_t m_PrdTablePhys
PhysicalRegionDescriptor * m_PrdTable
Abstrace base class for hardware I/O capabilities.
Definition IoBase.h:32
virtual uint8_t read8(size_t offset=0)=0
virtual void write32(uint32_t value, size_t offset=0)=0
virtual void write8(uint8_t value, size_t offset=0)=0
virtual size_t size() const =0
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
size_t size() const
static PhysicalMemoryManager & instance()
static ProcessorInformation & information()
virtual bool isMapped(void *virtualAddress)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
@ Hex
Definition Log.h:124