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 
32 BusMasterIde::BusMasterIde()
33  : m_pBase(0), m_PrdTableLock(false), m_PrdTable(0), m_LastPrdTableOffset(0),
34  m_PrdTablePhys(0), m_PrdTableMemRegion("bus-master-ide"), m_bActive(false)
35 {
36 }
37 
38 BusMasterIde::~BusMasterIde()
39 {
40  if (m_pBase)
41  {
42  delete m_pBase;
43  m_pBase = 0;
44  }
45 }
46 
48 {
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.
61  m_PrdTableMemRegion, 1, PhysicalMemoryManager::continuous,
63  {
64  ERROR("BusMasterIde: Couldn't allocate the PRD table!");
65  return false;
66  }
67  else
68  {
69  m_PrdTable = reinterpret_cast<PhysicalRegionDescriptor *>(
70  m_PrdTableMemRegion.virtualAddress());
71  m_PrdTablePhys = m_PrdTableMemRegion.physicalAddress();
72  NOTICE(
73  "BusMasterIde: PRD table at v="
74  << Hex
75  << reinterpret_cast<uintptr_t>(m_PrdTableMemRegion.virtualAddress())
76  << ", p=" << m_PrdTablePhys << ".");
77  }
78 
79  // Reset active state.
80  m_bActive = false;
81 
82  // All is well, set our internal port base and return success!
83  m_pBase = pBase;
84  return true;
85 }
86 bool BusMasterIde::add(uintptr_t buffer, size_t nBytes)
87 {
88  // Sanity check
89  if (!buffer || !nBytes || !m_pBase)
90  return false;
91 
92  // A couple of useful things to know
93  size_t prdTableEntries =
94  (m_PrdTableMemRegion.size() / sizeof(PhysicalRegionDescriptor));
95 
96  // Firstly check if we need to wait a bit... If an operation
97  m_pBase->read8(Status);
99 
100  // Okay, we're good to go - check that the buffer is mapped in first
101  LockGuard<Mutex> guard(m_PrdTableLock);
102  VirtualAddressSpace &va = Processor::information().getVirtualAddressSpace();
103  if (va.isMapped(reinterpret_cast<void *>(buffer)))
104  {
105  // Okay, the address is mapped, so we can start inserting PRD table
106  // entries.
107  size_t nRemainingBytes = nBytes, currOffset = 0, i = 0;
108  while (nRemainingBytes &&
109  ((m_LastPrdTableOffset + i) < prdTableEntries))
110  {
111  // Grab the mapping of this specfic portion of the block. Each PRD
112  // entry covers a 4096-byte region of memory (it can cover up to
113  // 65,536 bytes, but by using only 4096-byte regions it is possible
114  // to avoid the contiguous physical RAM requirement).
115  void *loc = reinterpret_cast<void *>(buffer + currOffset);
116  if (va.isMapped(loc))
117  {
118  physical_uintptr_t physPage = 0;
119  size_t flags = 0;
120  va.getMapping(
121  reinterpret_cast<void *>(buffer + currOffset), physPage,
122  flags);
123  // NOTICE("v=" << Hex << (buffer + currOffset) << ", p=" <<
124  // physPage);
125 
126  // DMA can only work with pages under 4G
127  if (physPage >= (1ULL << 32ULL))
128  {
129  ERROR(
130  "BusMasterIde: physical pages must be in the first 4GB "
131  "of address space [given page: "
132  << Hex << physPage << "]!");
133  return false;
134  }
135 
136  // Add in whatever offset into the page we may have in the
137  // buffer parameter.
138  size_t pageOffset = 0;
139  if (i == 0)
140  pageOffset = (buffer & 0xFFF);
141 
142  // Install into the PRD table now
143  // NOTICE("PRD[" << Dec << m_LastPrdTableOffset + i << Hex <<
144  // "].addr=" << (physPage + pageOffset) << ".");
145  m_PrdTable[m_LastPrdTableOffset + i].physAddr =
146  physPage + pageOffset;
147 
148  // Determine the transfer size we should use
149  size_t transferSize = nRemainingBytes;
150  if (transferSize > 4096)
151  transferSize = 4096 - pageOffset;
152  // NOTICE("PRD[" << Dec << m_LastPrdTableOffset + i << Hex <<
153  // "].size=" << transferSize << ".");
154  m_PrdTable[m_LastPrdTableOffset + i].byteCount =
155  transferSize & 0xFFFF;
156 
157  // Complete the PRD entry after determining the next offset
158  // NOTICE("BEFORE: Current offset = " << currOffset << ",
159  // remaining bytes: " << nRemainingBytes);
160  currOffset += transferSize;
161  nRemainingBytes -= transferSize;
162  // NOTICE("AFTER: Current offset = " << currOffset << ",
163  // remaining bytes: " << nRemainingBytes);
164  if (!nRemainingBytes)
165  m_PrdTable[m_LastPrdTableOffset + i].rsvdEot =
166  0x8000; // End-of-table?
167  else
168  m_PrdTable[m_LastPrdTableOffset + i].rsvdEot = 0;
169  i++;
170  }
171  else
172  FATAL(
173  "BusMasterIde: Part of the incoming buffer was not mapped ("
174  << loc << ")!");
175  }
176 
177  // If we added an entry, remove the EOT from any previous PRD that was
178  // present.
179  if (i && m_LastPrdTableOffset)
180  m_PrdTable[m_LastPrdTableOffset - 1].rsvdEot = 0;
181  // If we didn't add an entry, just return failure
182  else if (!i)
183  return false;
184 
185  m_LastPrdTableOffset += i;
186  return true;
187  }
188  else
189  return false; // Buffer not mapped - nothing we can do!
190 }
191 
192 bool BusMasterIde::begin(bool bWrite)
193 {
194  // Beginning a DMA transfer.
195  m_bActive = true;
196 
197  // If no other command is running, set the PRD physical address and
198  // begin the command.
199  uint8_t statusReg = m_pBase->read8(Status);
200  if (!(statusReg & 0x1))
201  {
202  // Write the physical address to the table address register
203  m_pBase->write32(m_PrdTablePhys, PrdTableAddr);
204 
205  // Begin the command
206  uint8_t cmdReg = m_pBase->read8(Command);
207  if (cmdReg & 0x1)
208  FATAL(
209  "BusMaster IDE status and command registers don't make sense");
210  cmdReg = (cmdReg & 0xF6) | 0x1 | (bWrite ? 0 : 8);
211  m_pBase->write8(cmdReg, BusMasterIde::Command);
212  }
213  else
214  {
215  // Oops, something went wrong - no more transfer.
216  WARNING("BusMaster IDE hit a bad status in begin(): " << statusReg);
217  m_bActive = false;
218  return false;
219  }
220 
221  return true;
222 }
223 
225 {
226  // Sanity check
227  if (!m_pBase)
228  return false;
229 
230  // Easy check here
231  uint8_t statusReg = m_pBase->read8(Status);
232  return (statusReg & 0x4);
233 }
234 
236 {
237  // Sanity check
238  if (!m_pBase)
239  return false;
240 
241  // Easy check here
242  uint8_t statusReg = m_pBase->read8(Status);
243  return (statusReg & 0x2);
244 }
245 
247 {
248  // Sanity check
249  if (!m_pBase)
250  return false;
251 
252  // Easy check here
253  uint8_t statusReg = m_pBase->read8(Status);
254  return (statusReg & 0x1) == 0;
255 }
256 
258 {
259  // Sanity check
260  if (!m_pBase)
261  return;
262 
263  // Read the status register to dump information about the command completion
264  uint8_t statusReg = m_pBase->read8(Status);
265 #if BUSMASTER_VERBOSE_LOGGING
266  if ((statusReg & 0x1) && (!(statusReg & 0x4)))
267  {
268  // DMA transfer in progress. Abort the transfer.
269  NOTICE("BusMasterIde: aborting transfer in progress");
270  }
271  else if ((!(statusReg & 0x1)) && (statusReg & 0x4))
272  {
273  // IDE device triggered an interrupt, successful transfer
274  NOTICE("BusMasterIde: successful transfer, exact transfer size");
275  }
276  else if ((statusReg & 0x1) && (statusReg & 0x4))
277  {
278  // IDE device triggered an interrupt, successful transfer
279  NOTICE(
280  "BusMasterIde: successful transfer, more buffer space than needed");
281  }
282  else
283  {
284  // Error condition
285  NOTICE("Status register = " << statusReg << ".");
286  if (!(statusReg & 0x1))
287  {
288  NOTICE("BusMasterIde: not enough buffer space provided");
289  }
290  else
291  {
292  NOTICE("BusMasterIde: device/controller signalled an error");
293  }
294  }
295 #endif
296 
297  // Whatever happened, we need to reset state
298  uint8_t cmdReg = m_pBase->read8(Command);
299  m_pBase->write8(cmdReg & 0xF6, Command);
300 
301  // And ack whatever's in the status register too
302  m_pBase->write8(statusReg, Status);
303 
304  // Now cleared the status register - okay.
305  m_bActive = false;
306 
307  // This transfer is now complete.
308  m_LastPrdTableOffset = 0;
309 }
bool add(uintptr_t buffer, size_t nBytes)
Adds a buffer to a DMA transaction.
Definition: BusMasterIde.cc:86
static PhysicalMemoryManager & instance()
virtual void getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
virtual size_t size() const =0
virtual bool isMapped(void *virtualAddress)=0
bool initialise(IoBase *pBase)
Initialises DMA for a specific channel.
Definition: BusMasterIde.cc:47
static ProcessorInformation & information()
Definition: Processor.cc:45
Abstrace base class for hardware I/O capabilities.
Definition: IoBase.h:31
#define WARNING(text)
Definition: Log.h:78
bool hasCompleted()
Determines if the CURRENT TRANSFER has completed.
bool hasInterrupt()
Determines if an INTERRUPT has occurred on this channel.
#define NOTICE(text)
Definition: Log.h:74
bool begin(bool bWrite)
Begin a DMA operation.
Definition: Log.h:136
void commandComplete()
Called by drivers when a command completes.
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
#define ERROR(text)
Definition: Log.h:82
bool hasError()
Determines if an ERROR has occurred on this channel.
#define FATAL(text)
Definition: Log.h:89