VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/testcase/tstOhciRegisterAccess.cpp@ 77807

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.8 KB
Line 
1/* $Id: tstOhciRegisterAccess.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * tstOhciRegisterAccess - OHCI Register Access Tests / Experiments.
4 */
5
6/*
7 * Copyright (C) 2011-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <VBox/log.h>
23#include <iprt/mem.h>
24#include <iprt/memobj.h>
25#include <iprt/string.h>
26#include <iprt/asm-amd64-x86.h>
27#include <iprt/param.h>
28
29#include <VBox/sup.h>
30#undef LogRel
31#define LogRel(a) SUPR0Printf a
32
33
34/*********************************************************************************************************************************
35* Global Variables *
36*********************************************************************************************************************************/
37/** Register names. */
38static const char * const g_apszRegNms[] =
39{
40 /* 00 */ "HcRevision",
41 /* 01 */ "HcControl",
42 /* 02 */ "HcCommandStatus",
43 /* 03 */ "HcInterruptStatus",
44 /* 04 */ "HcInterruptEnable",
45 /* 05 */ "HcInterruptDisable",
46 /* 06 */ "HcHCCA",
47 /* 07 */ "HcPeriodCurrentED",
48 /* 08 */ "HcControlHeadED",
49 /* 09 */ "HcControlCurrentED",
50 /* 10 */ "HcBulkHeadED",
51 /* 11 */ "HcBulkCurrentED",
52 /* 12 */ "HcDoneHead",
53 /* 13 */ "HcFmInterval",
54 /* 14 */ "HcFmRemaining",
55 /* 15 */ "HcFmNumber",
56 /* 16 */ "HcPeriodicStart",
57 /* 17 */ "HcLSThreshold",
58 /* 18 */ "HcRhDescriptorA",
59 /* 19 */ "HcRhDescriptorB",
60 /* 20 */ "HcRhStatus",
61 /* Variable number of root hub ports: */
62 /* 21 */ "HcRhPortStatus[0]",
63 /* 22 */ "HcRhPortStatus[1]",
64 /* 23 */ "HcRhPortStatus[2]",
65 /* 24 */ "HcRhPortStatus[3]",
66 /* 25 */ "HcRhPortStatus[4]",
67 /* 26 */ "HcRhPortStatus[5]",
68 /* 27 */ "HcRhPortStatus[6]",
69 /* 28 */ "HcRhPortStatus[7]"
70};
71
72
73static bool TestOhciWrites(RTVPTRUNION uPtr)
74{
75 static struct
76 {
77 unsigned iReg;
78 uint32_t fMask;
79 uint32_t uVal1;
80 uint32_t uVal2;
81 } const s_aRegs[] =
82 {
83#if 0 /* deadly when missing bytes are taken as zero. */
84 { 13 /* HcFmInterval */, 0xffffffff, 0x58871120, 0x01010101 },
85#endif
86 { 16 /* HcPeriodicStart */, 0x00003fff, 0x01020304, 0x02010403 },
87 { 17 /* HcLSThreshold */, 0x00000fff, 0xffffffff, 0x66666666 },
88 { 10 /* HcBulkHeadED */, 0xfffffff0, 0xffffffff, 0xfefefef8 }, /* a bit risky... */
89 { 11 /* HcBulkCurrentED */, 0xfffffff0, 0xffffffff, 0xfefefef8 }, /* a bit risky... */
90 };
91
92 bool fSuccess = true;
93 for (unsigned i = 0; i < RT_ELEMENTS(s_aRegs); i++)
94 {
95 uint32_t const iReg = s_aRegs[i].iReg;
96 RTVPTRUNION uPtrReg;
97 uPtrReg.pu32 = &uPtr.pu32[iReg];
98
99 uint32_t uInitialValue = *uPtrReg.pu32;
100 LogRel(("TestOhciWrites: %p iReg=%2d %20s = %08RX32\n", uPtrReg.pv, iReg, g_apszRegNms[iReg], uInitialValue));
101
102 bool fTryAgain = true;
103 const char *pszError = NULL;
104 uint32_t u32A = 0;
105 uint32_t uChangedValue = 0;
106 uint32_t uExpectedValue = 0;
107
108 for (uint32_t iTry = 0; fTryAgain && iTry < 1024; iTry++)
109 {
110 pszError = NULL;
111 fTryAgain = false;
112 u32A = 0;
113 uChangedValue = 0;
114 uExpectedValue = 0;
115
116 RTCCUINTREG const fFlags = ASMIntDisableFlags();
117 uInitialValue = *uPtrReg.pu32;
118
119 /*
120 * DWORD writes.
121 */
122 if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue)))
123 break;
124 *uPtrReg.pu32 = uInitialValue;
125 u32A = *uPtrReg.pu32;
126 uChangedValue = s_aRegs[i].uVal1 != uInitialValue ? s_aRegs[i].uVal1 : s_aRegs[i].uVal2;
127 if (u32A == uInitialValue)
128 {
129 /* Change the value. */
130 *uPtrReg.pu32 = uChangedValue;
131 u32A = *uPtrReg.pu32;
132 *uPtrReg.pu32 = uInitialValue;
133 uExpectedValue = uChangedValue & s_aRegs[i].fMask;
134 if (u32A != uExpectedValue)
135 pszError = "Writing changed value failed";
136 else
137 {
138 u32A = *uPtrReg.pu32;
139 if (u32A != uInitialValue)
140 pszError = "Restore error 1";
141 }
142 }
143 else
144 pszError = "Writing back initial value failed";
145
146 /*
147 * Write aligned word changes.
148 */
149 for (unsigned iWord = 0; iWord < 2 && !pszError && !fTryAgain; iWord++)
150 {
151 if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue)))
152 break;
153
154 /* Change the value. */
155 uPtrReg.pu16[iWord] = (uint16_t)(uChangedValue >> iWord * 16);
156 u32A = *uPtrReg.pu32;
157 *uPtrReg.pu32 = uInitialValue;
158 uExpectedValue = (uChangedValue & UINT32_C(0xffff) << iWord * 16) & s_aRegs[i].fMask;
159 if (u32A != uExpectedValue)
160 {
161 static const char * const s_apsz[] = { "word 0", "word 1" };
162 pszError = s_apsz[iWord];
163 }
164 else
165 {
166 u32A = *uPtrReg.pu32;
167 if (u32A != uInitialValue)
168 pszError = "Restore error 2";
169 }
170 }
171
172 /*
173 * Write aligned word change. We have to keep within the register,
174 * unfortunately.
175 */
176 if (!pszError && !fTryAgain)
177 {
178 fTryAgain = *uPtrReg.pu32 != uInitialValue;
179 if (!fTryAgain)
180 {
181 /* Change the value. */
182 *(uint16_t volatile *)&uPtrReg.pu8[1] = (uint16_t)(uChangedValue >> 8);
183 u32A = *uPtrReg.pu32;
184 *uPtrReg.pu32 = uInitialValue;
185 uExpectedValue = (uChangedValue & UINT32_C(0x00ffff00)) & s_aRegs[i].fMask;
186 if (u32A != uExpectedValue)
187 pszError = "Unaligned word access";
188 else
189 {
190 u32A = *uPtrReg.pu32;
191 if (u32A != uInitialValue)
192 pszError = "Restore error 3";
193 }
194 }
195 }
196
197 /*
198 * Write byte changes.
199 */
200 for (unsigned iByte = 0; iByte < 4 && !pszError && !fTryAgain; iByte++)
201 {
202 if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue)))
203 break;
204
205 /* Change the value. */
206 uPtrReg.pu8[iByte] = (uint8_t)(uChangedValue >> iByte * 8);
207 u32A = *uPtrReg.pu32;
208 *uPtrReg.pu32 = uInitialValue;
209 uExpectedValue = (uChangedValue & UINT32_C(0xff) << iByte * 8) & s_aRegs[i].fMask;
210 if (u32A != uExpectedValue)
211 {
212 static const char * const s_apsz[] = { "byte 0", "byte 1", "byte 2", "byte 3" };
213 pszError = s_apsz[iByte];
214 }
215 else
216 {
217 u32A = *uPtrReg.pu32;
218 if (u32A != uInitialValue)
219 pszError = "Restore error 4";
220 }
221 }
222
223 ASMSetFlags(fFlags);
224 ASMNopPause();
225 }
226
227 /*
228 * Complain on failure.
229 */
230 if (fTryAgain)
231 LogRel(("TestOhciWrites: Warning! Register %s was never stable enough for testing! %08RX32 %08RX32 %08RX32\n",
232 g_apszRegNms[iReg], uInitialValue, u32A, uChangedValue, uInitialValue));
233 else if (pszError)
234 {
235 LogRel(("TestOhciWrites: Error! Register %s failed: %s; Initial=%08RX32 Changed=%08RX32 Expected=%08RX32 u32A=%08RX32\n",
236 g_apszRegNms[iReg], pszError, uInitialValue, uChangedValue, uExpectedValue, u32A));
237 fSuccess = false;
238 }
239 }
240
241 return fSuccess;
242}
243
244
245static bool TestOhciReadOnly(RTVPTRUNION uPtr)
246{
247 static struct
248 {
249 unsigned iReg;
250 uint32_t cValues;
251 uint32_t auValues[8];
252 } const s_aRegs[] =
253 {
254 { 0 /* HcRevision */, 8, { 0, UINT32_MAX, 0x10100110, 0x200, 0x111, 0x11f, 0xf110, 0x0f10 } },
255 { 12 /* HcDoneHead */, 3, { 0, UINT32_MAX, 0x55555555, 0, 0, 0, 0, 0 } },
256 { 14 /* HcFmRemaining */, 3, { 0, UINT32_MAX, 0x55555555, 0, 0, 0, 0, 0 } },
257 { 15 /* HcFmNumber */, 5, { 0, UINT32_MAX, 0x55555555, 0x7899, 0x00012222, 0, 0, 0 } },
258#if 0 /* HCD can write this */
259 { 17 /* HcLSThreshold */, 5, { 0x627, 0x628, 0x629, 0x666, 0x599, 0, 0, 0 } } /* ??? */
260#endif
261 };
262
263 bool fSuccess = true;
264 for (unsigned i = 0; i < RT_ELEMENTS(s_aRegs); i++)
265 {
266 uint32_t const iReg = s_aRegs[i].iReg;
267 RTVPTRUNION uPtrReg;
268 uPtrReg.pu32 = &uPtr.pu32[iReg];
269
270 uint32_t uInitialValue = *uPtrReg.pu32;
271 LogRel(("TestOhciReadOnly: %p iReg=%2d %20s = %08RX32\n", uPtrReg.pv, iReg, g_apszRegNms[iReg], uInitialValue));
272
273 bool fTryAgain = true;
274 const char *pszError = NULL;
275 uint32_t uChangedValue = 0;
276 uint32_t u32A = 0;
277
278 for (uint32_t iTry = 0; fTryAgain && iTry < 1024; iTry++)
279 {
280 pszError = NULL;
281 fTryAgain = false;
282 u32A = 0;
283 uChangedValue = 0;
284
285 RTCCUINTREG const fFlags = ASMIntDisableFlags();
286 uInitialValue = *uPtrReg.pu32;
287
288 /*
289 * Try aligned dword, word and byte writes for now.
290 */
291 for (unsigned iValue = 0; iValue < s_aRegs[i].cValues && !pszError && !fTryAgain; iValue++)
292 {
293 uChangedValue = s_aRegs[i].auValues[iValue];
294 if (uInitialValue == uChangedValue)
295 continue;
296
297 /* dword */
298 if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue)))
299 break;
300
301 *uPtrReg.pu32 = uChangedValue;
302 u32A = *uPtrReg.pu32;
303 *uPtrReg.pu32 = uInitialValue;
304 if (u32A != uInitialValue)
305 pszError = "dword access";
306 else
307 {
308 u32A = *uPtrReg.pu32;
309 if (u32A != uInitialValue)
310 pszError = "Restore error 1";
311 }
312
313 /* word */
314 for (unsigned iWord = 0; iWord < 2 && !pszError && !fTryAgain; iWord++)
315 {
316 if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue)))
317 break;
318 uPtrReg.pu16[iWord] = (uint16_t)(uChangedValue >> iWord * 16);
319 u32A = *uPtrReg.pu32;
320 *uPtrReg.pu32 = uInitialValue;
321 if (u32A != uInitialValue)
322 pszError = iWord == 0 ? "aligned word 0 access" : "aligned word 1 access";
323 else
324 {
325 u32A = *uPtrReg.pu32;
326 if (u32A != uInitialValue)
327 pszError = "Restore error 2";
328 }
329 }
330
331 /* byte */
332 for (unsigned iByte = 0; iByte < 4 && !pszError && !fTryAgain; iByte++)
333 {
334 if ((fTryAgain = (*uPtrReg.pu32 != uInitialValue)))
335 break;
336 uPtrReg.pu8[iByte] = (uint8_t)(uChangedValue >> iByte * 8);
337 u32A = *uPtrReg.pu32;
338 *uPtrReg.pu32 = uInitialValue;
339 if (u32A != uInitialValue)
340 {
341 static const char * const s_apsz[] = { "byte 0", "byte 1", "byte 2", "byte 3" };
342 pszError = s_apsz[iByte];
343 }
344 else
345 {
346 u32A = *uPtrReg.pu32;
347 if (u32A != uInitialValue)
348 pszError = "Restore error 3";
349 }
350 }
351 }
352
353 ASMSetFlags(fFlags);
354 ASMNopPause();
355 }
356
357 /*
358 * Complain on failure.
359 */
360 if (fTryAgain)
361 LogRel(("TestOhciReadOnly: Warning! Register %s was never stable enough for testing! %08RX32 %08RX32 %08RX32\n",
362 g_apszRegNms[iReg], uInitialValue, u32A, uChangedValue, uInitialValue));
363 else if (pszError)
364 {
365 LogRel(("TestOhciReadOnly: Error! Register %s failed: %s; uInitialValue=%08RX32 uChangedValue=%08RX32 u32A=%08RX32\n",
366 g_apszRegNms[iReg], pszError, uInitialValue, uChangedValue, u32A));
367 fSuccess = false;
368 }
369 }
370
371 return fSuccess;
372}
373
374
375static bool TestOhciReads(RTVPTRUNION uPtr)
376{
377 /*
378 * We can read just about any register we like since read shouldn't have
379 * any side effects. However, some registers are volatile and makes for
380 * difficult targets, thus the ugly code.
381 */
382 bool fSuccess = true;
383 uint32_t cMaxReg = RT_ELEMENTS(g_apszRegNms);
384 for (uint32_t iReg = 0; iReg < cMaxReg; iReg++, uPtr.pu32++)
385 {
386 const char *pszError = NULL;
387 bool fDone = false;
388 uint32_t uInitialValue = *uPtr.pu32;
389 uint32_t u32A = 0;
390 uint32_t u32B = 0;
391 uint32_t u32C = 0;
392 LogRel(("TestOhciReads: %p iReg=%2d %20s = %08RX32\n", uPtr.pv, iReg, g_apszRegNms[iReg], uInitialValue));
393
394 for (uint32_t iTry = 0; !fDone && iTry < 1024; iTry++)
395 {
396 pszError = NULL;
397 fDone = true;
398 u32A = u32B = u32C = 0;
399
400 RTCCUINTREG const fFlags = ASMIntDisableFlags();
401 uInitialValue = *uPtr.pu32;
402
403 /* Test byte access. */
404 for (unsigned iByte = 0; iByte < 4; iByte++)
405 {
406 u32A = *uPtr.pu32;
407 u32B = uPtr.pu8[iByte];
408 u32C = *uPtr.pu32;
409 if (u32A != uInitialValue || u32C != uInitialValue)
410 {
411 fDone = false;
412 break;
413 }
414
415 static uint32_t const a_au32Masks[] =
416 {
417 UINT32_C(0xffffff00), UINT32_C(0xffff00ff), UINT32_C(0xff00ffff), UINT32_C(0x00ffffff)
418 };
419 u32B <<= iByte * 8;
420 u32B |= uInitialValue & a_au32Masks[iByte];
421 if (u32B != uInitialValue)
422 {
423 static const char * const s_apsz[] = { "byte 0", "byte 1", "byte 2", "byte 3" };
424 pszError = s_apsz[iByte];
425 break;
426 }
427 }
428
429 /* Test aligned word access. */
430 if (fDone)
431 {
432 for (unsigned iWord = 0; iWord < 2; iWord++)
433 {
434 u32A = *uPtr.pu32;
435 u32B = uPtr.pu16[iWord];
436 u32C = *uPtr.pu32;
437 if (u32A != uInitialValue || u32C != uInitialValue)
438 {
439 fDone = false;
440 break;
441 }
442
443 u32B <<= iWord * 16;
444 u32B |= uInitialValue & (iWord == 0 ? UINT32_C(0xffff0000) : UINT32_C(0x0000ffff));
445 if (u32B != uInitialValue)
446 {
447 pszError = iWord == 0 ? "aligned word 0 access" : "aligned word 1 access";
448 break;
449 }
450 }
451 }
452
453 /* Test unaligned word access. */
454 if (fDone)
455 {
456 for (int iWord = ((uintptr_t)uPtr.pv & PAGE_OFFSET_MASK) == 0; iWord < 3; iWord++)
457 {
458 u32A = *uPtr.pu32;
459 u32B = *(volatile uint16_t *)&uPtr.pu8[iWord * 2 - 1];
460 u32C = *uPtr.pu32;
461 if (u32A != uInitialValue || u32C != uInitialValue)
462 {
463 fDone = false;
464 break;
465 }
466
467 switch (iWord)
468 {
469 case 0: u32B = (u32B >> 8) | (u32A & UINT32_C(0xffffff00)); break;
470 case 1: u32B = (u32B << 8) | (u32A & UINT32_C(0xff0000ff)); break;
471 case 2: u32B = (u32B << 24) | (u32A & UINT32_C(0x00ffffff)); break;
472 }
473 if (u32B != u32A)
474 {
475 static const char * const s_apsz[] = { "unaligned word 0", "unaligned word 1", "unaligned word 2" };
476 pszError = s_apsz[iWord];
477 break;
478 }
479 }
480 }
481
482 /* Test unaligned dword access. */
483 if (fDone)
484 {
485 for (int iByte = ((uintptr_t)uPtr.pv & PAGE_OFFSET_MASK) == 0 ? 0 : -3; iByte < 4; iByte++)
486 {
487 u32A = *uPtr.pu32;
488 u32B = *(volatile uint32_t *)&uPtr.pu8[iByte];
489 u32C = *uPtr.pu32;
490 if (u32A != uInitialValue || u32C != uInitialValue)
491 {
492 fDone = false;
493 break;
494 }
495
496 switch (iByte)
497 {
498 case -3: u32B = (u32B >> 24) | (uInitialValue & UINT32_C(0xffffff00)); break;
499 case -2: u32B = (u32B >> 16) | (uInitialValue & UINT32_C(0xffff0000)); break;
500 case -1: u32B = (u32B >> 8) | (uInitialValue & UINT32_C(0xff000000)); break;
501 case 0: break;
502 case 1: u32B = (u32B << 8) | (uInitialValue & UINT32_C(0x000000ff)); break;
503 case 2: u32B = (u32B << 16) | (uInitialValue & UINT32_C(0x0000ffff)); break;
504 case 3: u32B = (u32B << 24) | (uInitialValue & UINT32_C(0x00ffffff)); break;
505
506 }
507 if (u32B != u32A)
508 {
509 static const char * const s_apsz[] =
510 {
511 "unaligned dword -3", "unaligned dword -2", "unaligned dword -1",
512 "unaligned dword 0", "unaligned dword 1", "unaligned dword 2", "unaligned dword 3"
513 };
514 pszError = s_apsz[iByte + 3];
515 break;
516 }
517
518 }
519 }
520
521 ASMSetFlags(fFlags);
522 ASMNopPause();
523 } /* try loop */
524
525 /*
526 * Complain on failure.
527 */
528 if (!fDone)
529 LogRel(("TestOhciReads: Warning! Register %s was never stable enough for testing! %08RX32 %08RX32 %08RX32\n",
530 g_apszRegNms[iReg], uInitialValue, u32A, u32C));
531 else if (pszError)
532 {
533 LogRel(("TestOhciReads: Error! Register %s failed: %s; uInitialValue=%08RX32 u32B=%08RX32\n",
534 g_apszRegNms[iReg], pszError, uInitialValue, u32B));
535 fSuccess = false;
536 }
537 }
538
539 return fSuccess;
540}
541
542
543int tstOhciRegisterAccess(RTHCPHYS HCPhysOHCI)
544{
545 LogRel(("tstOhciRegisterAccess: HCPhysOHCI=%RHp\n", HCPhysOHCI));
546
547 /*
548 * Map the OHCI registers so we can access them.
549 */
550 RTR0MEMOBJ hMemObj;
551 int rc = RTR0MemObjEnterPhys(&hMemObj, HCPhysOHCI, PAGE_SIZE, RTMEM_CACHE_POLICY_MMIO);
552 if (RT_FAILURE(rc))
553 {
554 LogRel(("tstOhciRegisterAccess: Failed to enter OHCI memory at %RHp: %Rrc\n", HCPhysOHCI, rc));
555 return rc;
556 }
557 RTR0MEMOBJ hMapObj;
558 rc = RTR0MemObjMapKernel(&hMapObj, hMemObj, (void *)-1, 0 /*uAlignment*/, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
559 if (RT_SUCCESS(rc))
560 {
561 RTVPTRUNION uPtr;
562 uPtr.pv = (void volatile *)RTR0MemObjAddress(hMapObj);
563 LogRel(("tstOhciRegisterAccess: mapping address %p\n", uPtr.pv));
564 if (RT_VALID_PTR(uPtr.pv))
565 {
566 LogRel(("tstOhciRegisterAccess: HcRevision=%#x\n", *uPtr.pu32));
567
568 /*
569 * Do the access tests.
570 */
571 bool fSuccess = TestOhciReads(uPtr);
572 if (fSuccess)
573 fSuccess = TestOhciReadOnly(uPtr);
574 if (fSuccess)
575 fSuccess = TestOhciWrites(uPtr);
576 if (fSuccess)
577 LogRel(("tstOhciRegisterAccess: Success!\n"));
578 else
579 LogRel(("tstOhciRegisterAccess: Failed!\n"));
580 }
581 else
582 rc = VERR_INTERNAL_ERROR_2;
583
584 /*
585 * Clean up.
586 */
587 RTR0MemObjFree(hMapObj, false);
588 }
589 else
590 LogRel(("tstOhciRegisterAccess: Failed to map OHCI memory at %RHp: %Rrc\n", HCPhysOHCI, rc));
591 RTR0MemObjFree(hMemObj, false);
592 LogRel(("tstOhciRegisterAccess: returns %Rrc\n", rc));
593 return rc;
594}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette