8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/processor/IoBase.h"
10#include "pedigree/kernel/time/Time.h"
12#include "modules/drivers/common/ata/ata-common.h"
18 : m_EventCount(0), m_AlternateRun(0), m_PhaseCount(0), m_LastEvent(0), m_Valid(true) {}
20 void alternateStatus() {
22 if (m_AlternateRun > 4) {
28 void commandStatus() {
30 m_Valid &= m_AlternateRun == 4;
32 }
else if (m_LastEvent !=
'S') {
46 bool valid(
size_t expectedPhases)
const {
47 return m_Valid && !m_AlternateRun && m_PhaseCount == expectedPhases;
50 bool finalEventWasStatus()
const {
51 return m_EventCount && m_LastEvent ==
'S';
55 void record(
char event) {
56 if (m_EventCount <
sizeof(m_Events)) {
57 m_Events[m_EventCount++] = event;
65 size_t m_AlternateRun;
72class ScriptedAtaIo final :
public IoBase {
74 ScriptedAtaIo(
bool command, IoEventTrace* trace,
const uint8_t* statuses =
nullptr,
75 size_t statusCount = 0,
const uint16_t* expectedWords =
nullptr,
76 size_t expectedWordCount = 0)
80 m_StatusCount(statusCount),
82 m_ExpectedWords(expectedWords),
83 m_ExpectedWordCount(expectedWordCount),
88 m_UnexpectedAccess(false) {}
90 size_t size()
const override {
94 uint8_t
read8(
size_t offset = 0)
override {
95 if (m_Command && offset == 7 && m_StatusCount) {
96 const size_t index = m_StatusIndex < m_StatusCount ? m_StatusIndex++ : m_StatusCount - 1;
98 m_Trace->commandStatus();
99 return m_Statuses[index];
101 if (!m_Command && offset == 2) {
103 m_Trace->alternateStatus();
107 m_UnexpectedAccess =
true;
111 uint16_t
read16(
size_t offset = 0)
override {
113 m_UnexpectedAccess =
true;
117 uint32_t
read32(
size_t offset = 0)
override {
119 m_UnexpectedAccess =
true;
124 uint64_t
read64(
size_t offset = 0)
override {
126 m_UnexpectedAccess =
true;
131 void write8(uint8_t value,
size_t offset = 0)
override {
134 m_UnexpectedAccess =
true;
137 void write16(uint16_t value,
size_t offset = 0)
override {
138 if (!m_Command || offset || !m_ExpectedWords || m_DataWrites >= m_ExpectedWordCount ||
139 value != m_ExpectedWords[m_DataWrites]) {
140 m_WritesMatch =
false;
146 void write32(uint32_t value,
size_t offset = 0)
override {
149 m_UnexpectedAccess =
true;
153 void write64(uint64_t value,
size_t offset = 0)
override {
156 m_UnexpectedAccess =
true;
160 operator bool()
const override {
164 size_t statusReads()
const {
165 return m_StatusReads;
168 size_t alternateReads()
const {
169 return m_AlternateReads;
172 size_t dataWrites()
const {
176 bool writesMatch()
const {
177 return m_WritesMatch;
180 bool unexpectedAccess()
const {
181 return m_UnexpectedAccess;
186 IoEventTrace* m_Trace;
187 const uint8_t* m_Statuses;
188 size_t m_StatusCount;
189 size_t m_StatusIndex;
190 const uint16_t* m_ExpectedWords;
191 size_t m_ExpectedWordCount;
192 size_t m_StatusReads;
193 size_t m_AlternateReads;
196 bool m_UnexpectedAccess;
199bool check(
bool condition,
const char* test,
const char* detail) {
204 ERROR(
"HOSTED-WAIT-TEST: FAIL " << test <<
": " << detail);
208void makeFixtureData(uint16_t* words,
size_t count) {
209 for (
size_t i = 0; i < count; ++i) {
210 words[i] =
static_cast<uint16_t
>(0xA500U ^ i);
215 AtaPioPollBudget budget = {Time::getTicks(), 30 * Time::Multiplier::Second, 0, maximumPolls};
219bool successfulTwoSectorTransfer() {
220 constexpr const char* Test =
"ata-pio-success";
221 const uint8_t statuses[] = {0x48, 0x48, 0x50};
223 makeFixtureData(words, 512);
225 ScriptedAtaIo command(
true, &trace, statuses, 3, words, 512);
226 ScriptedAtaIo control(
false, &trace);
230 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 2, budget, finalStatus);
231 const bool passed = check(
232 result && finalStatus.
__reg_contents == 0x50 && budget.polls == 3 &&
233 command.statusReads() == 3 && control.alternateReads() == 12 &&
234 command.dataWrites() == 512 && command.writesMatch() && trace.valid(3) &&
235 trace.finalEventWasStatus() && !command.unexpectedAccess() && !control.unexpectedAccess(),
236 Test,
"the helper did not complete two ordered data phases and a terminal status phase");
238 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-success");
243bool busyProgressesWithinOneBudget() {
244 constexpr const char* Test =
"ata-pio-busy-progress";
245 const uint8_t statuses[] = {0x80, 0x48, 0x80, 0x50};
247 makeFixtureData(words, 256);
249 ScriptedAtaIo command(
true, &trace, statuses, 4, words, 256);
250 ScriptedAtaIo control(
false, &trace);
254 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
256 check(result && finalStatus.
__reg_contents == 0x50 && budget.polls == 4 &&
257 command.statusReads() == 4 && control.alternateReads() == 8 &&
258 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
259 trace.finalEventWasStatus(),
260 Test,
"BSY progress did not stay within the cumulative data-out budget");
262 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-busy-progress");
267bool ignoresStaleStatusWhileBusy() {
268 constexpr const char* Test =
"ata-pio-busy-stale-status";
269 const uint8_t statuses[] = {0x81, 0xA0, 0x48, 0x50};
271 makeFixtureData(words, 256);
273 ScriptedAtaIo command(
true, &trace, statuses, 4, words, 256);
274 ScriptedAtaIo control(
false, &trace);
278 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
280 check(result && finalStatus.
__reg_contents == 0x50 && budget.polls == 4 &&
281 command.statusReads() == 4 && control.alternateReads() == 8 &&
282 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
283 trace.finalEventWasStatus(),
284 Test,
"ERR or device-fault bits were interpreted while BSY made them stale");
286 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-busy-stale-status");
291bool rejectsLateError() {
292 constexpr const char* Test =
"ata-pio-late-error";
293 const uint8_t statuses[] = {0x08, 0x41};
295 makeFixtureData(words, 256);
297 ScriptedAtaIo command(
true, &trace, statuses, 2, words, 256);
298 ScriptedAtaIo control(
false, &trace);
302 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
304 check(!result && finalStatus.
__reg_contents == 0x41 && budget.polls == 2 &&
305 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
306 trace.finalEventWasStatus(),
307 Test,
"an ERR status after the final data word was reported as success");
309 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-late-error");
314bool rejectsDeviceFault() {
315 constexpr const char* Test =
"ata-pio-device-fault";
316 const uint8_t statuses[] = {0x08, 0x60};
318 makeFixtureData(words, 256);
320 ScriptedAtaIo command(
true, &trace, statuses, 2, words, 256);
321 ScriptedAtaIo control(
false, &trace);
325 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
327 check(!result && finalStatus.
__reg_contents == 0x60 && budget.polls == 2 &&
328 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
329 trace.finalEventWasStatus(),
330 Test,
"a device fault after the final data word was reported as success");
332 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-device-fault");
337bool requiresDrqForData() {
338 constexpr const char* Test =
"ata-pio-requires-drq";
339 const uint8_t statuses[] = {0x40, 0x08};
341 makeFixtureData(words, 256);
343 ScriptedAtaIo command(
true, &trace, statuses, 2, words, 256);
344 ScriptedAtaIo control(
false, &trace);
348 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
350 check(!result && finalStatus.
__reg_contents == 0x40 && budget.polls == 1 &&
351 command.statusReads() == 1 && control.alternateReads() == 4 &&
352 command.dataWrites() == 0 && trace.valid(1) && trace.finalEventWasStatus(),
353 Test,
"DRDY without DRQ was treated as progress into a later data phase");
355 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-requires-drq");
360bool rejectsUnexpectedExtraDataPhase() {
361 constexpr const char* Test =
"ata-pio-extra-data-phase";
362 const uint8_t statuses[] = {0x08, 0x08, 0x40};
364 makeFixtureData(words, 256);
366 ScriptedAtaIo command(
true, &trace, statuses, 3, words, 256);
367 ScriptedAtaIo control(
false, &trace);
371 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
373 check(!result && finalStatus.
__reg_contents == 0x08 && budget.polls == 2 &&
374 command.statusReads() == 2 && control.alternateReads() == 8 &&
375 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
376 trace.finalEventWasStatus(),
377 Test,
"an extra data phase was treated as progress into a later completion state");
379 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-extra-data-phase");
384bool requiresDrdyForCompletion() {
385 constexpr const char* Test =
"ata-pio-terminal-requires-drdy";
386 const uint8_t statuses[] = {0x08, 0x10};
388 makeFixtureData(words, 256);
390 ScriptedAtaIo command(
true, &trace, statuses, 2, words, 256);
391 ScriptedAtaIo control(
false, &trace);
395 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
397 check(!result && finalStatus.
__reg_contents == 0x10 && budget.polls == 2 &&
398 command.statusReads() == 2 && control.alternateReads() == 8 &&
399 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
400 trace.finalEventWasStatus(),
401 Test,
"a nonzero terminal status without DRDY was reported as completion");
403 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-terminal-requires-drdy");
408bool rejectsSecondSectorErrorBeforeData() {
409 constexpr const char* Test =
"ata-pio-second-sector-error";
410 const uint8_t statuses[] = {0x08, 0x41};
412 makeFixtureData(words, 512);
414 ScriptedAtaIo command(
true, &trace, statuses, 2, words, 512);
415 ScriptedAtaIo control(
false, &trace);
419 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 2, budget, finalStatus);
421 check(!result && finalStatus.
__reg_contents == 0x41 && budget.polls == 2 &&
422 command.statusReads() == 2 && control.alternateReads() == 8 &&
423 command.dataWrites() == 256 && command.writesMatch() && trace.valid(2) &&
424 trace.finalEventWasStatus(),
425 Test,
"a second-sector error allowed another sector of data to be written");
427 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-second-sector-error");
432bool rejectsAlreadyExpiredBudget() {
433 constexpr const char* Test =
"ata-pio-expired-timeout";
434 const uint8_t statuses[] = {0x08};
436 makeFixtureData(words, 256);
438 ScriptedAtaIo command(
true, &trace, statuses, 1, words, 256);
439 ScriptedAtaIo control(
false, &trace);
443 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
445 check(!result && finalStatus.
__reg_contents == 0 && budget.polls == 0 &&
446 command.statusReads() == 0 && control.alternateReads() == 0 &&
447 command.dataWrites() == 0 && trace.valid(0) && !trace.finalEventWasStatus(),
448 Test,
"an expired command budget touched status, control, or data registers");
450 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-expired-timeout");
455bool rejectsZeroStatus() {
456 constexpr const char* Test =
"ata-pio-zero-status";
457 const uint8_t statuses[] = {0x00};
459 makeFixtureData(words, 256);
461 ScriptedAtaIo command(
true, &trace, statuses, 1, words, 256);
462 ScriptedAtaIo control(
false, &trace);
466 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
467 const bool passed = check(!result && finalStatus.
__reg_contents == 0 && budget.polls == 1 &&
468 command.statusReads() == 1 && command.dataWrites() == 0 &&
469 trace.valid(1) && trace.finalEventWasStatus(),
470 Test,
"an absent-device status was retried or reported as success");
472 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-zero-status");
477bool stopsAtExactPollBoundary() {
478 constexpr const char* Test =
"ata-pio-poll-boundary";
479 const uint8_t statuses[] = {0x80};
481 makeFixtureData(words, 256);
483 ScriptedAtaIo command(
true, &trace, statuses, 1, words, 256);
484 ScriptedAtaIo control(
false, &trace);
488 const bool result = ataPioWrite512ByteSectors(&command, &control, words, 1, budget, finalStatus);
490 check(!result && finalStatus.
__reg_contents == 0x80 && budget.polls == 3 &&
491 command.statusReads() == 3 && control.alternateReads() == 4 &&
492 command.dataWrites() == 0 && trace.valid(1) && trace.finalEventWasStatus(),
493 Test,
"sticky BSY sampled beyond or escaped the exact command poll budget");
495 NOTICE(
"HOSTED-WAIT-TEST: PASS ata-pio-poll-boundary");
501bool runHostedAtaPioRegressions() {
503 passed &= successfulTwoSectorTransfer();
504 passed &= busyProgressesWithinOneBudget();
505 passed &= ignoresStaleStatusWhileBusy();
506 passed &= rejectsLateError();
507 passed &= rejectsDeviceFault();
508 passed &= requiresDrqForData();
509 passed &= rejectsUnexpectedExtraDataPhase();
510 passed &= requiresDrdyForCompletion();
511 passed &= rejectsSecondSectorErrorBeforeData();
512 passed &= rejectsAlreadyExpiredBudget();
513 passed &= rejectsZeroStatus();
514 passed &= stopsAtExactPollBoundary();
Abstrace base class for hardware I/O capabilities.
virtual uint8_t read8(size_t offset=0)=0
virtual void write32(uint32_t value, size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
virtual void write8(uint8_t value, size_t offset=0)=0
virtual void write16(uint16_t value, size_t offset=0)=0
virtual uint64_t read64(size_t offset=0)=0
virtual uint16_t read16(size_t offset=0)=0
virtual size_t size() const =0
virtual void write64(uint64_t value, size_t offset=0)=0
uint8_t __reg_contents
"Hidden" integer which contains the actual register contents