ata.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // ata.c: ATA simulator for vblade
  2. #include "config.h"
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <sys/types.h>
  6. #include "dat.h"
  7. #include "fns.h"
  8. enum {
  9. // err bits
  10. UNC = 1<<6,
  11. MC = 1<<5,
  12. IDNF = 1<<4,
  13. MCR = 1<<3,
  14. ABRT = 1<<2,
  15. NM = 1<<1,
  16. // status bits
  17. BSY = 1<<7,
  18. DRDY = 1<<6,
  19. DF = 1<<5,
  20. DRQ = 1<<3,
  21. ERR = 1<<0,
  22. };
  23. static ushort ident[256] = {
  24. [47] 0x8000,
  25. [49] 0x0200,
  26. [50] 0x4000,
  27. [83] 0x5400,
  28. [84] 0x4000,
  29. [86] 0x1400,
  30. [87] 0x4000,
  31. [93] 0x400b,
  32. };
  33. static void
  34. setfld(ushort *a, int idx, int len, char *str) // set field in ident
  35. {
  36. uchar *p;
  37. p = (uchar *)(a+idx);
  38. while (len > 0) {
  39. if (*str == 0)
  40. p[1] = ' ';
  41. else
  42. p[1] = *str++;
  43. if (*str == 0)
  44. p[0] = ' ';
  45. else
  46. p[0] = *str++;
  47. p += 2;
  48. len -= 2;
  49. }
  50. }
  51. static void
  52. setlba28(ushort *ident, vlong lba)
  53. {
  54. uchar *cp;
  55. cp = (uchar *) &ident[60];
  56. *cp++ = lba;
  57. *cp++ = lba >>= 8;
  58. *cp++ = lba >>= 8;
  59. *cp++ = (lba >>= 8) & 0xf;
  60. }
  61. static void
  62. setlba48(ushort *ident, vlong lba)
  63. {
  64. uchar *cp;
  65. cp = (uchar *) &ident[100];
  66. *cp++ = lba;
  67. *cp++ = lba >>= 8;
  68. *cp++ = lba >>= 8;
  69. *cp++ = lba >>= 8;
  70. *cp++ = lba >>= 8;
  71. *cp++ = lba >>= 8;
  72. }
  73. void
  74. atainit(void)
  75. {
  76. char buf[64];
  77. setfld(ident, 27, 40, "Coraid EtherDrive vblade");
  78. sprintf(buf, "V%d\n", VBLADE_VERSION);
  79. setfld(ident, 23, 8, buf);
  80. setfld(ident, 10, 20, "SSN HERE");
  81. }
  82. /* The ATA spec is weird in that you specify the device size as number
  83. * of sectors and then address the sectors with an offset. That means
  84. * with LBA 28 you shouldn't see an LBA of all ones. Still, we don't
  85. * check for that.
  86. */
  87. void
  88. atacmd(Ataregs *p, uchar *dp) // do the ata cmd
  89. {
  90. vlong lba;
  91. ushort *ip;
  92. int n;
  93. enum { MAXLBA28SIZE = 0x0fffffff };
  94. switch (p->cmd) {
  95. case 0x20: // read sectors
  96. case 0x30: // write sectors
  97. lba = p->lba & MAXLBA28SIZE;
  98. break;
  99. case 0x24: // read sectors ext
  100. case 0x34: // write sectors ext
  101. lba = p->lba & 0x0000ffffffffffffLL; // full 48
  102. break;
  103. case 0xe7: // flush cache
  104. return;
  105. case 0xec: // identify device
  106. memmove(dp, ident, 512);
  107. ip = (ushort *)dp;
  108. if (size & ~MAXLBA28SIZE)
  109. setlba28(ip, MAXLBA28SIZE);
  110. else
  111. setlba28(ip, size);
  112. setlba48(ip, size);
  113. p->err = 0;
  114. p->status = DRDY;
  115. return;
  116. case 0xe5: // check power mode
  117. p->err = 0;
  118. p->sectors = 0xff; // the device is active or idle
  119. p->status = DRDY;
  120. return;
  121. default:
  122. p->status = DRDY | ERR;
  123. p->err = ABRT;
  124. return;
  125. }
  126. if (lba + p->sectors > size) {
  127. p->err = IDNF;
  128. p->status = DRDY | ERR;
  129. p->lba = lba;
  130. return;
  131. }
  132. if (p->cmd == 0x20 || p->cmd == 0x24) {
  133. n = getsec(bfd, dp, lba, p->sectors);
  134. if (n == -1) {
  135. perror("read block device");
  136. p->status |= ERR;
  137. }
  138. } else {
  139. n = putsec(bfd, dp, lba, p->sectors);
  140. if (n == -1) {
  141. perror("write block device");
  142. p->status |= ERR;
  143. }
  144. }
  145. p->lba += p->sectors;
  146. p->sectors = 0;
  147. p->status = DRDY;
  148. p->err = 0;
  149. }