The Pedigree Project  0.1
ScsiDisk.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 "ScsiDisk.h"
21 #include "ScsiCommands.h"
22 #include "ScsiController.h"
23 #include "pedigree/kernel/Log.h"
24 #include "pedigree/kernel/Service.h"
25 #include "pedigree/kernel/ServiceFeatures.h"
26 #include "pedigree/kernel/ServiceManager.h"
27 #include "pedigree/kernel/processor/PhysicalMemoryManager.h"
28 #include "pedigree/kernel/processor/types.h"
29 #include "pedigree/kernel/time/Time.h"
30 #include "pedigree/kernel/utilities/Cache.h"
31 #include "pedigree/kernel/utilities/PointerGuard.h"
32 #include "pedigree/kernel/utilities/assert.h"
33 #include "pedigree/kernel/utilities/utility.h"
34 
35 #define READAHEAD_ENABLED 0
36 
37 #ifdef SCSI_DEBUG
38 #define SCSI_DEBUG_LOG DEBUG_LOG
39 #else
40 #define SCSI_DEBUG_LOG(...)
41 #endif
42 
43 void ScsiDisk::cacheCallback(
44  CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page,
45  void *meta)
46 {
47  ScsiDisk *pDisk = reinterpret_cast<ScsiDisk *>(meta);
48 
49  switch (cause)
50  {
51  case CacheConstants::WriteBack:
52  {
53  pDisk->flush(loc);
54  }
55  break;
56  case CacheConstants::Eviction:
57  // no-op for ScsiDisk
58  break;
59  default:
60  WARNING("ScsiDisk: unknown cache callback -- could indicate "
61  "potential future I/O issues.");
62  break;
63  }
64 }
65 
66 ScsiDisk::ScsiDisk()
67  : Disk(), m_Cache(PhysicalMemoryManager::below4GB), m_Inquiry(0),
68  m_nAlignPoints(0), m_NumBlocks(0), m_BlockSize(0x1000),
69  m_DeviceType(NoDevice)
70 {
71  m_Cache.setCallback(cacheCallback, this);
72 }
73 
74 ScsiDisk::~ScsiDisk()
75 {
76 }
77 
78 bool ScsiDisk::initialise(ScsiController *pController, size_t nUnit)
79 {
80  m_pController = pController;
81  m_pParent = pController;
82  m_nUnit = nUnit;
83 
84  m_Inquiry = new Inquiry;
85 
86  // Inquire as to the device's state
88  ScsiCommand *pCommand = new ScsiCommands::Inquiry(sizeof(Inquiry), false);
89  bool success = sendCommand(
90  pCommand, reinterpret_cast<uintptr_t>(m_Inquiry), sizeof(Inquiry));
91  if (!success)
92  {
93  ERROR("ScsiDisk: INQUIRY failed!");
94  delete pCommand;
95  return false;
96  }
97  delete pCommand;
98 
99  // Get the peripheral type out of the data.
100  m_DeviceType =
101  static_cast<ScsiPeripheralType>(m_Inquiry->Peripheral & 0x1F);
102 
103  // Ensure the unit is ready before we attempt to do anything more
104  if (!unitReady())
105  {
106  // Grab sense data
107  Sense *s = new Sense;
108  PointerGuard<Sense> guard2(s);
109  readSense(s);
110  SCSI_DEBUG_LOG(
111  "ScsiDisk: Unit not yet ready, sense data: [sk="
112  << s->SenseKey << ", asc=" << s->Asc << ", ascq=" << s->AscQ
113  << "]");
114 
115  if (s->SenseKey == 0x2)
116  {
117  if (s->Asc == 0x4)
118  {
119  if (s->AscQ ==
120  0x2) // Logical Unit Not Ready, START UNIT Required
121  {
122  // Start the unit
123  pCommand =
124  new ScsiCommands::StartStop(false, true, 1, true);
125  success = sendCommand(pCommand, 0, 0, true);
126  if (!success)
127  {
128  readSense(s);
129  ERROR(
130  "ScsiDisk: unit startup failed! Sense data: [sk="
131  << s->SenseKey << ", asc=" << s->Asc
132  << ", ascq=" << s->AscQ << "]");
133  }
134  delete pCommand;
135  }
136  }
137  }
138 
139  Time::delay(100 * Time::Multiplier::Millisecond);
140 
141  // Attempt to see if the unit is ready again
142  if (!unitReady())
143  {
144  readSense(s);
145  SCSI_DEBUG_LOG(
146  "ScsiDisk: Unit not yet ready, sense data: [sk="
147  << s->SenseKey << ", asc=" << s->Asc << ", ascq=" << s->AscQ
148  << "]");
149 
150  Time::delay(100 * Time::Multiplier::Millisecond);
151 
152  if (!unitReady())
153  {
154  readSense(s);
155  ERROR(
156  "ScsiDisk: disk never became ready. Sense data: [sk="
157  << s->SenseKey << ", asc=" << s->Asc << ", ascq=" << s->AscQ
158  << "]");
159  return false;
160  }
161  }
162  }
163 
164  // Get the capacity of the device
165  getCapacityInternal(&m_NumBlocks, &m_NativeBlockSize);
166  SCSI_DEBUG_LOG(
167  "ScsiDisk: Capacity: " << Dec << m_NumBlocks << " blocks, each "
168  << m_NativeBlockSize << " bytes - "
169  << (m_NativeBlockSize * m_NumBlocks) << Hex
170  << " bytes in total.");
171 
172  // Chat to the partition service and let it pick up that we're around now
173  ServiceFeatures *pFeatures =
174  ServiceManager::instance().enumerateOperations(String("partition"));
175  Service *pService =
176  ServiceManager::instance().getService(String("partition"));
177  if (pFeatures && pFeatures->provides(ServiceFeatures::touch))
178  {
179  NOTICE("Attempting to inform the partitioner of our presence...");
180  if (pService)
181  {
182  if (pService->serve(
183  ServiceFeatures::touch, static_cast<Disk *>(this),
184  sizeof(static_cast<Disk *>(this))))
185  NOTICE("Successful.");
186  else
187  ERROR("Failed.");
188  }
189  else
190  ERROR("ScsiDisk: Couldn't tell the partition service about the new "
191  "disk presence");
192  }
193  else
194  ERROR("ScsiDisk: Partition service doesn't appear to support touch");
195  return true;
196 }
197 
199 {
200  ByteSet(sense, 0xFF, sizeof(Sense));
201 
202  // Maximum size of sense data is 252 bytes
203  ScsiCommand *pCommand = new ScsiCommands::ReadSense(0, sizeof(Sense));
204 
205  uint8_t *response = new uint8_t[sizeof(Sense)];
206  bool success = sendCommand(
207  pCommand, reinterpret_cast<uintptr_t>(response), sizeof(Sense));
208  if (!success)
209  {
210  WARNING("ScsiDisk: SENSE command failed");
211  delete[] response;
212  return false;
213  }
214 
216  MemoryCopy(sense, response, sizeof(Sense));
217 
218  delete[] response;
219 
220  return ((sense->ResponseCode & 0x70) == 0x70);
221 }
222 
224 {
225  ScsiCommand *pCommand = new ScsiCommands::UnitReady();
226  bool success = sendCommand(pCommand, 0, 0);
227  delete pCommand;
228 
231  return success;
232 }
233 
234 bool ScsiDisk::getCapacityInternal(size_t *blockNumber, size_t *blockSize)
235 {
236  if (!unitReady())
237  {
238  WARNING("ScsiDisk::getCapacityInternal - returning to defaults, unit "
239  "not ready");
240  *blockNumber = 0;
241  *blockSize = defaultBlockSize();
242  return false;
243  }
244 
245  Capacity *capacity = new Capacity;
246  PointerGuard<Capacity> guard(capacity);
247  ByteSet(capacity, 0, sizeof(Capacity));
248 
249  ScsiCommand *pCommand = new ScsiCommands::ReadCapacity10();
250  bool success = sendCommand(
251  pCommand, reinterpret_cast<uintptr_t>(capacity), sizeof(Capacity),
252  false);
253  delete pCommand;
254  if (!success)
255  {
256  WARNING("ScsiDisk::getCapacityInternal - READ CAPACITY command failed");
257  return false;
258  }
259 
260  *blockNumber = BIG_TO_HOST32(capacity->LBA);
261  uint32_t blockSz = BIG_TO_HOST32(capacity->BlockSize);
262  *blockSize = blockSz ? blockSz : defaultBlockSize();
263 
264  return true;
265 }
266 
267 bool ScsiDisk::sendCommand(
268  ScsiCommand *pCommand, uintptr_t pRespBuffer, uint16_t nRespBytes,
269  bool bWrite)
270 {
271  uintptr_t pCommandBuffer = 0;
272  size_t nCommandSize = pCommand->serialise(pCommandBuffer);
273  return m_pController->sendCommand(
274  m_nUnit, pCommandBuffer, nCommandSize, pRespBuffer, nRespBytes, bWrite);
275 }
276 
277 uintptr_t ScsiDisk::read(uint64_t location)
278 {
279  if (!(getBlockSize() && getNativeBlockSize()))
280  {
281  ERROR("ScsiDisk::read - block size is zero.");
282  return 0;
283  }
284 
285  size_t endLocation = location + getNativeBlockSize();
286  if (endLocation > getSize())
287  {
288  ERROR(
289  "ScsiDisk::read - location too high (end location "
290  << endLocation << " > " << getSize() << ")");
291  return 0;
292  }
293 
294  size_t blockNum = location / getNativeBlockSize();
295  if (blockNum > getBlockCount())
296  {
297  ERROR(
298  "ScsiDisk::read - location too high (block "
299  << blockNum << " > " << getBlockCount() << ")");
300  return 0;
301  }
302 
303  // Look through the align points
304  uint64_t alignPoint = 0;
305  for (size_t i = 0; i < m_nAlignPoints; i++)
306  if (m_AlignPoints[i] <= location && m_AlignPoints[i] > alignPoint)
307  alignPoint = m_AlignPoints[i];
308 
309  // Calculate the offset to get location on a page boundary.
310  ssize_t offs = -((location - alignPoint) % 4096);
311 
312  uintptr_t buffer;
313  if ((buffer = m_Cache.lookup(location + offs)))
314  {
315  return buffer - offs;
316  }
317 
318  // Grab an aligned location to read.
319  size_t loc = (location + offs) & ~(getBlockSize() - 1);
320 
321  ScsiController *pParent = static_cast<ScsiController *>(m_pParent);
322  uint64_t numRead = pParent->addRequest(
323  0, SCSI_REQUEST_READ, reinterpret_cast<uint64_t>(this), loc);
324  if (numRead < getBlockSize())
325  {
326  // Failed to read for some reason, expose the failure to our caller.
327  WARNING("ScsiDisk::read - short read!");
328  return 0;
329  }
330 #if READAHEAD_ENABLED
331  // Read the next block.
332  for (size_t i = 0; i < 2; ++i)
333  {
334  loc += getBlockSize();
335  pParent->addAsyncRequest(
336  0, SCSI_REQUEST_READ, reinterpret_cast<uint64_t>(this), loc);
337  }
338 #endif
339  return m_Cache.lookup(location + offs) - offs;
340 }
341 
342 void ScsiDisk::write(uint64_t location)
343 {
344 #ifndef CRIPPLE_HDD
345  if (!(getBlockSize() && getNativeBlockSize()))
346  {
347  ERROR("ScsiDisk::write - block size is zero.");
348  return;
349  }
350 
351  if ((location + getNativeBlockSize()) > getSize())
352  {
353  ERROR("ScsiDisk::write - location too high");
354  ERROR(" -> " << location << " vs " << getSize());
355  return;
356  }
357 
358  if ((location / getNativeBlockSize()) > getBlockCount())
359  {
360  ERROR("ScsiDisk::write - location too high");
361  ERROR(
362  " -> block " << (location / getNativeBlockSize()) << " vs "
363  << getBlockCount());
364  return;
365  }
366 
367  // Look through the align points
368  uint64_t alignPoint = 0;
369  for (size_t i = 0; i < m_nAlignPoints; i++)
370  if (m_AlignPoints[i] <= location && m_AlignPoints[i] > alignPoint)
371  alignPoint = m_AlignPoints[i];
372 
373  // Calculate the offset to get location on a page boundary.
374  ssize_t offs = -((location - alignPoint) % 4096);
375 
376  uintptr_t buffer;
377  if (!(buffer = m_Cache.lookup(location + offs)))
378  {
379  ERROR("ScsiDisk::write - no buffer!");
380  return;
381  }
382 
383  // Implicit pin caused by lookup() must be released by the write request
384  // handler, to ensure the refcount is correct.
385  ScsiController *pParent = static_cast<ScsiController *>(m_pParent);
386  pParent->addAsyncRequest(
387  0, SCSI_REQUEST_WRITE, reinterpret_cast<uint64_t>(this),
388  location + offs);
389 #endif
390 }
391 
392 void ScsiDisk::flush(uint64_t location)
393 {
394 #ifndef CRIPPLE_HDD
395  if (!(getBlockSize() && getNativeBlockSize()))
396  {
397  ERROR("ScsiDisk::flush - block size is zero.");
398  return;
399  }
400 
401  if ((location + getNativeBlockSize()) > getSize())
402  {
403  ERROR("ScsiDisk::flush - location too high");
404  return;
405  }
406 
407  if ((location / getNativeBlockSize()) > getBlockCount())
408  {
409  ERROR("ScsiDisk::flush - location too high");
410  return;
411  }
412 
413  // Look through the align points
414  uint64_t alignPoint = 0;
415  for (size_t i = 0; i < m_nAlignPoints; i++)
416  if (m_AlignPoints[i] <= location && m_AlignPoints[i] > alignPoint)
417  alignPoint = m_AlignPoints[i];
418 
419  // Calculate the offset to get location on a page boundary.
420  ssize_t offs = -((location - alignPoint) % 4096);
421 
422  uintptr_t buffer;
423  if (!(buffer = m_Cache.lookup(location + offs)))
424  {
425  return;
426  }
427 
428  // Make sure this page remains for both the write AND the sync.
429  m_Cache.pin(location + offs);
430 
431  ScsiController *pParent = static_cast<ScsiController *>(m_pParent);
432  pParent->addRequest(
433  0, SCSI_REQUEST_WRITE, reinterpret_cast<uint64_t>(this),
434  location + offs);
435  pParent->addRequest(0, reinterpret_cast<uint64_t>(this), location + offs);
436 
437  // Undo our pin for write+sync.
438  m_Cache.release(location + offs);
439 #endif
440 }
441 
442 void ScsiDisk::align(uint64_t location)
443 {
444  assert(m_nAlignPoints < 8);
445  m_AlignPoints[m_nAlignPoints++] = location;
446 }
447 
448 uint64_t ScsiDisk::doRead(uint64_t location)
449 {
450  // Wait for the unit to be ready before reading
451  bool bReady = false;
452  for (int i = 0; i < 3; i++)
453  {
454  if ((bReady = unitReady()))
455  break;
456  }
457 
458  if (!bReady)
459  {
460  ERROR("ScsiDisk::doRead - unit not ready");
461  return 0;
462  }
463 
464  // Handle the case where a read took place while we were waiting in the
465  // RequestQueue - don't double up the cache.
466  uintptr_t buffer = m_Cache.lookup(location);
467  if (buffer)
468  {
469  WARNING(
470  "ScsiDisk::doRead(" << location
471  << ") - buffer was already in cache");
472  m_Cache.release(location);
473  return 0;
474  }
475  buffer = m_Cache.insert(location, getBlockSize());
476  if (!buffer)
477  {
478  FATAL("ScsiDisk::doRead - no buffer");
479  }
480 
481  size_t blockNum = location / getNativeBlockSize();
482  size_t blockCount = getBlockSize() / getNativeBlockSize();
483 
484  bool bOk = false;
485  ScsiCommand *pCommand;
486 
487  // TOC?
488  if (m_DeviceType == CdDvdDevice)
489  {
491  pCommand = new ScsiCommands::ReadTocCommand(getNativeBlockSize());
492  uint8_t *toc = new uint8_t[getNativeBlockSize()];
493  PointerGuard<uint8_t> tmpBuffGuard(toc, true);
494  bOk = sendCommand(
495  pCommand, reinterpret_cast<uintptr_t>(toc), getNativeBlockSize());
496  delete pCommand;
497  if (!bOk)
498  {
499  WARNING("ScsiDisk::doRead - could not find data track (READ TOC "
500  "failed)");
501  return 0;
502  }
503 
504  uint16_t i;
505  bool bHaveTrack = false;
507  uint16_t bufLen = BIG_TO_HOST16((toc[0] << 8) | toc[1]);
509  reinterpret_cast<ScsiCommands::ReadTocCommand::TocEntry *>(toc + 4);
510  for (i = 0; i < (bufLen / 8); i++)
511  {
512  if (Toc[i].Flags == 0x14)
513  {
514  bHaveTrack = true;
515  break;
516  }
517  }
518 
519  if (!bHaveTrack)
520  {
521  WARNING(
522  "ScsiDisk::doRead - could not find data track (no data track)");
523  return 0;
524  }
525 
526  uint32_t trackStart = BIG_TO_HOST32(Toc[i].TrackStart);
527  if ((blockNum + trackStart) < blockNum)
528  {
529  WARNING("ScsiDisk::doRead - TOC overflow");
530  return 0;
531  }
532 
533  blockNum += trackStart;
534  }
535 
536  for (int i = 0; i < 3 && !bOk; i++)
537  {
538  SCSI_DEBUG_LOG("SCSI: trying read(10)");
539  pCommand = new ScsiCommands::Read10(blockNum, blockCount);
540  bOk = sendCommand(pCommand, buffer, getBlockSize());
541  delete pCommand;
542  }
543  for (int i = 0; i < 3 && !bOk; i++)
544  {
545  SCSI_DEBUG_LOG("SCSI: trying read(12)");
546  pCommand = new ScsiCommands::Read12(blockNum, blockCount);
547  bOk = sendCommand(pCommand, buffer, getBlockSize());
548  delete pCommand;
549  }
550  for (int i = 0; i < 3 && !bOk; i++)
551  {
552  SCSI_DEBUG_LOG("SCSI: trying read(16)");
553  pCommand = new ScsiCommands::Read16(blockNum, blockCount);
554  bOk = sendCommand(pCommand, buffer, getBlockSize());
555  delete pCommand;
556  }
557 
558  if (bOk)
559  {
560  m_Cache.markNoLongerEditing(location, getBlockSize());
561  }
562  else
563  {
564  ERROR("SCSI: reading failed?");
565  }
566 
567  return getBlockSize();
568 }
569 
570 uint64_t ScsiDisk::doWrite(uint64_t location)
571 {
572  // Wait for the unit to be ready before writing
573  bool bReady = false;
574  for (int i = 0; i < 3; i++)
575  {
576  if ((bReady = unitReady()))
577  break;
578  }
579 
580  if (!bReady)
581  {
582  ERROR("ScsiDisk::doWrite - unit not ready");
583  return 0;
584  }
585 
586  // Handle the case where a read took place while we were waiting in the
587  // RequestQueue - don't double up the cache.
588  uintptr_t buffer = m_Cache.lookup(location);
589  if (!buffer)
590  {
591  WARNING(
592  "ScsiDisk::doWrite(" << location << ") - buffer was not in cache");
593  return 0;
594  }
595 
596  // Make sure we don't hold the refcnt once we exit this method.
597  CachePageGuard guard(m_Cache, location);
598 
599  size_t block = location / getNativeBlockSize();
600  size_t count = 4096 / getNativeBlockSize();
601 
602  bool bOk = false;
603  ScsiCommand *pCommand;
604 
605  for (int i = 0; i < 3; i++)
606  {
607  SCSI_DEBUG_LOG("SCSI: trying write(10)");
608  pCommand = new ScsiCommands::Write10(block, count);
609  bOk = sendCommand(pCommand, buffer, 4096, true);
610  delete pCommand;
611  if (bOk)
612  break;
613  }
614  if (!bOk)
615  {
616  for (int i = 0; i < 3; i++)
617  {
618  SCSI_DEBUG_LOG("SCSI: trying write(12)");
619  pCommand = new ScsiCommands::Write12(block, count);
620  bOk = sendCommand(pCommand, buffer, 4096, true);
621  delete pCommand;
622  if (bOk)
623  break;
624  }
625  }
626  if (!bOk)
627  {
628  for (int i = 0; i < 3; i++)
629  {
630  SCSI_DEBUG_LOG("SCSI: trying write(16)");
631  pCommand = new ScsiCommands::Write16(block, count);
632  bOk = sendCommand(pCommand, buffer, 4096, true);
633  delete pCommand;
634  if (bOk)
635  break;
636  }
637  }
638 
639  if (!bOk)
640  {
641  ERROR("SCSI: writing failed?");
642  }
643 
644  return getBlockSize();
645 }
646 
647 uint64_t ScsiDisk::doSync(uint64_t location)
648 {
649  // Wait for the unit to be ready before writing
650  bool bReady = false;
651  for (int i = 0; i < 3; i++)
652  {
653  if ((bReady = unitReady()))
654  break;
655  }
656 
657  if (!bReady)
658  {
659  ERROR("ScsiDisk::doSync - unit not ready");
660  return 0;
661  }
662 
663  size_t block = location / getNativeBlockSize();
664  size_t count = 4096 / getNativeBlockSize();
665 
666  bool bOk = false;
667  ScsiCommand *pCommand;
668 
669  // Kick off a synchronise (this will be slow, but will ensure the data is on
670  // disk)
671  for (int i = 0; i < 3; i++)
672  {
673  SCSI_DEBUG_LOG("SCSI: trying synchronise(10)");
674  pCommand = new ScsiCommands::Synchronise10(block, count);
675  bOk = sendCommand(pCommand, 0, 0);
676  delete pCommand;
677  if (bOk)
678  break;
679  }
680 
681  if (!bOk)
682  {
683  for (int i = 0; i < 3; i++)
684  {
685  SCSI_DEBUG_LOG("SCSI: trying synchronise(16)");
686  pCommand = new ScsiCommands::Synchronise16(block, count);
687  bOk = sendCommand(pCommand, 0, 0);
688  delete pCommand;
689  if (bOk)
690  break;
691  }
692  }
693 
694  return getBlockSize();
695 }
696 
697 void ScsiDisk::pin(uint64_t location)
698 {
699  m_Cache.pin(location);
700 }
701 
702 void ScsiDisk::unpin(uint64_t location)
703 {
704  m_Cache.release(location);
705 }
virtual void write(uint64_t location)
Definition: ScsiDisk.cc:342
virtual void flush(uint64_t location)
Flush a cached page to disk.
Definition: ScsiDisk.cc:392
uint64_t addAsyncRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
Device * m_pParent
Definition: Device.h:366
virtual void align(uint64_t location)
Sets the page boundary alignment after a specific location on the disk.
Definition: ScsiDisk.cc:442
Definition: String.h:49
virtual bool provides(Type service)
Definition: Disk.h:32
bool readSense(Sense *s)
Definition: ScsiDisk.cc:198
#define WARNING(text)
Definition: Log.h:78
#define NOTICE(text)
Definition: Log.h:74
virtual uint64_t doRead(uint64_t location)
Definition: ScsiDisk.cc:448
Definition: Log.h:136
#define assert(x)
Definition: assert.h:37
bool unitReady()
Definition: ScsiDisk.cc:223
Service * getService(const String &serviceName)
virtual void unpin(uint64_t location)
Definition: ScsiDisk.cc:702
virtual bool serve(ServiceFeatures::Type type, void *pData, size_t dataLen)=0
bool initialise(class ScsiController *pController, size_t nUnit)
Definition: ScsiDisk.cc:78
#define ERROR(text)
Definition: Log.h:82
MUST_USE_RESULT uint64_t addRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
Definition: Log.h:138
#define FATAL(text)
Definition: Log.h:89
virtual uintptr_t read(uint64_t location)
Definition: ScsiDisk.cc:277
ServiceFeatures * enumerateOperations(const String &serviceName)
virtual void pin(uint64_t location)
Pins a cache page.
Definition: ScsiDisk.cc:697