amt-ider-module.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. /*
  2. Copyright 2020-2021 Intel Corporation
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. @description Intel AMT IDER module
  13. @author Ylian Saint-Hilaire
  14. @version v0.3.0
  15. */
  16. /*jslint node: true */
  17. /*jshint node: true */
  18. /*jshint strict:false */
  19. /*jshint -W097 */
  20. /*jshint esversion: 6 */
  21. "use strict";
  22. // Construct a Intel AMT IDER object
  23. module.exports.CreateAmtRemoteIder = function (webserver, meshcentral) {
  24. const fs = require('fs');
  25. var obj = {};
  26. obj.protocol = 3; // IDER
  27. obj.bytesToAmt = 0;
  28. obj.bytesFromAmt = 0;
  29. obj.rx_timeout = 30000; // Default 30000
  30. obj.tx_timeout = 0; // Default 0
  31. obj.heartbeat = 20000; // Default 20000
  32. obj.version = 1;
  33. obj.acc = null;
  34. obj.inSequence = 0;
  35. obj.outSequence = 0;
  36. obj.iderinfo = null;
  37. obj.enabled = false;
  38. obj.iderStart = 0; // OnReboot = 0, Graceful = 1, Now = 2
  39. obj.floppy = null;
  40. obj.cdrom = null;
  41. obj.floppyReady = false;
  42. obj.cdromReady = false;
  43. //obj.pingTimer = null;
  44. obj.sectorStats = null;
  45. obj.debug = false;
  46. // Private method
  47. function debug() { if (obj.debug) { console.log(...arguments); } }
  48. // Mode Sense
  49. var IDE_ModeSence_LS120Disk_Page_Array = Buffer.from([0x00, 0x26, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00]);
  50. var IDE_ModeSence_3F_LS120_Array = Buffer.from([0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x10, 0xA9, 0x08, 0x20, 0x02, 0x00, 0x03, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31]);
  51. var IDE_ModeSence_FloppyDisk_Page_Array = Buffer.from([0x00, 0x26, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x05, 0x1E, 0x04, 0xB0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xD0, 0x00, 0x00]);
  52. var IDE_ModeSence_3F_Floppy_Array = Buffer.from([0x00, 0x5c, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x16, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x1e, 0x04, 0xb0, 0x02, 0x12, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x00, 0x00, 0x08, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x11, 0x24, 0x31]);
  53. var IDE_ModeSence_CD_1A_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
  54. //var IDE_ModeSence_CD_1B_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
  55. var IDE_ModeSence_CD_1D_Array = Buffer.from([0x00, 0x12, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
  56. var IDE_ModeSence_CD_2A_Array = Buffer.from([0x00, 0x20, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
  57. //var IDE_ModeSence_CD_01_Array = Buffer.from([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00]);
  58. var IDE_ModeSence_3F_CD_Array = Buffer.from([0x00, 0x28, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x18, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
  59. // 0x46 constant data
  60. var IDE_CD_ConfigArrayHeader = Buffer.from([0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08]);
  61. var IDE_CD_ConfigArrayProfileList = Buffer.from([0x00, 0x00, 0x03, 0x04, 0x00, 0x08, 0x01, 0x00]);
  62. var IDE_CD_ConfigArrayCore = Buffer.from([0x00, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00, 0x02]);
  63. var IDE_CD_Morphing = Buffer.from([0x00, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00]);
  64. var IDE_CD_ConfigArrayRemovable = Buffer.from([0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x02]);
  65. var IDE_CD_ConfigArrayRandom = Buffer.from([0x00, 0x10, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00]);
  66. var IDE_CD_Read = Buffer.from([0x00, 0x1E, 0x03, 0x00]);
  67. var IDE_CD_PowerManagement = Buffer.from([0x01, 0x00, 0x03, 0x00]);
  68. var IDE_CD_Timeout = Buffer.from([0x01, 0x05, 0x03, 0x00]);
  69. // 0x01 constant data
  70. var IDE_ModeSence_FloppyError_Recovery_Array = Buffer.from([0x00, 0x12, 0x24, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
  71. var IDE_ModeSence_Ls120Error_Recovery_Array = Buffer.from([0x00, 0x12, 0x31, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]);
  72. var IDE_ModeSence_CDError_Recovery_Array = Buffer.from([0x00, 0x0E, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00]);
  73. // CD info and performance
  74. var RD_CD_DiskInfo = Buffer.from([0x00, 0x20, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x20, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
  75. var RD_CD_Performance = Buffer.from([0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x00]);
  76. // Private method, called by parent when it change state
  77. obj.xxStateChange = function (newstate) {
  78. if (obj.debug) console.log("IDER-StateChange", newstate);
  79. if (newstate == 0) { obj.Stop(); }
  80. if (newstate == 3) { obj.Start(); }
  81. }
  82. obj.diskSetup = function (floppyPath, cdromPath) {
  83. debug(floppyPath, cdromPath);
  84. // Setup floppy
  85. if (floppyPath != null) {
  86. try {
  87. if (fs.existsSync(floppyPath) == false) { return 1; } // Floppy disk image does not exist
  88. var stats = fs.statSync(floppyPath);
  89. if ((stats.size % 512) != 0) { return 2; } // Invalid floppy disk image
  90. obj.floppy = { size: stats.size, ptr: fs.openSync(floppyPath, 'r') };
  91. } catch (ex) { return 3; } // Unable to open floppy disk image
  92. }
  93. // Setup CDROM
  94. if (cdromPath != null) {
  95. try {
  96. if (fs.existsSync(cdromPath) == false) { return 4; } // CDROM disk image does not exist
  97. var stats = fs.statSync(cdromPath);
  98. if ((stats.size % 512) != 0) { return 5; } // Invalid CDROM disk image
  99. obj.cdrom = { size: stats.size, ptr: fs.openSync(cdromPath, 'r') };
  100. } catch (ex) { return 6; } // Unable to open CDROM disk image
  101. }
  102. if ((obj.cdrom == null) && (obj.floppy == null)) { return 7; } // Can't do IDER with no disk images
  103. return 0;
  104. }
  105. obj.Start = function () {
  106. if (obj.debug) { console.log('IDER-Start'); console.log(obj.floppy, obj.cdrom); }
  107. obj.bytesToAmt = 0;
  108. obj.bytesFromAmt = 0;
  109. obj.inSequence = 0;
  110. obj.outSequence = 0;
  111. g_readQueue = [];
  112. // Send first command, OPEN_SESSION
  113. obj.SendCommand(0x40, Buffer.concat([ShortToStrX(obj.rx_timeout), ShortToStrX(obj.tx_timeout), ShortToStrX(obj.heartbeat), IntToStrX(obj.version)]));
  114. // Send sector stats
  115. if (obj.sectorStats) {
  116. obj.sectorStats(0, 0, obj.floppy ? (obj.floppy.size >> 9) : 0);
  117. obj.sectorStats(0, 1, obj.cdrom ? (obj.cdrom.size >> 11) : 0);
  118. }
  119. // Setup the ping timer
  120. //obj.pingTimer = setInterval(function () { obj.SendCommand(0x44); }, 5000);
  121. }
  122. obj.Stop = function () {
  123. if (obj.debug) console.log('IDER-Stop');
  124. //if (obj.pingTimer) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
  125. obj.parent.Stop();
  126. }
  127. // Private method
  128. obj.ProcessData = function (data) {
  129. data = Buffer.from(data, 'binary');
  130. obj.bytesFromAmt += data.length;
  131. if (obj.acc == null) { obj.acc = data; } else { obj.acc = Buffer.concat([obj.acc, data]); }
  132. if (obj.debug) console.log('IDER-ProcessData', obj.acc.length, obj.acc.toString('hex'));
  133. // Process as many commands as possible
  134. while (obj.acc != null) {
  135. var len = obj.ProcessDataEx();
  136. if (len == 0) return;
  137. if (obj.inSequence != ReadIntX(obj.acc, 4)) {
  138. if (obj.debug) console.log('ERROR: Out of sequence', obj.inSequence, ReadIntX(obj.acc, 4));
  139. obj.Stop();
  140. return;
  141. }
  142. obj.inSequence++;
  143. if (len == obj.acc.length) { obj.acc = null; } else { obj.acc = obj.acc.slice(len); }
  144. }
  145. }
  146. // Private method
  147. obj.SendCommand = function (cmdid, data, completed, dma) {
  148. if (data == null) { data = Buffer.alloc(0); }
  149. var attributes = ((cmdid > 50) && (completed == true)) ? 2 : 0;
  150. if (dma) { attributes += 1; }
  151. var x = Buffer.concat([Buffer.from([cmdid, 0, 0, attributes]), IntToStrX(obj.outSequence++), data]);
  152. obj.parent.xxSend(x);
  153. obj.bytesToAmt += x.length;
  154. //if (cmdid != 0x4B) { console.log('IDER-SendData', x.length, x.toString('hex')); }
  155. }
  156. // CommandEndResponse (SCSI_SENSE)
  157. obj.SendCommandEndResponse = function (error, sense, device, asc, asq) {
  158. if (error) { obj.SendCommand(0x51, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc5, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0]), true); }
  159. else { obj.SendCommand(0x51, Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x87, (sense << 4), 3, 0, 0, 0, device, 0x51, sense, asc, asq]), true); }
  160. }
  161. // DataToHost (SCSI_READ)
  162. obj.SendDataToHost = function (device, completed, data, dma) {
  163. var dmalen = (dma) ? 0 : data.length;
  164. if (completed == true) {
  165. obj.SendCommand(0x54, Buffer.concat([Buffer.from([0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0x85, 0, 3, 0, 0, 0, device, 0x50, 0, 0, 0, 0, 0, 0]), data]), completed, dma);
  166. } else {
  167. obj.SendCommand(0x54, Buffer.concat([Buffer.from([0, (data.length & 0xff), (data.length >> 8), 0, dma ? 0xb4 : 0xb5, 0, 2, 0, (dmalen & 0xff), (dmalen >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), data]), completed, dma);
  168. }
  169. }
  170. // GetDataFromHost (SCSI_CHUNK)
  171. obj.SendGetDataFromHost = function (device, chunksize) {
  172. obj.SendCommand(0x52, Buffer.from([0, (chunksize & 0xff), (chunksize >> 8), 0, 0xb5, 0, 0, 0, (chunksize & 0xff), (chunksize >> 8), device, 0x58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), false);
  173. }
  174. // DisableEnableFeatures (STATUS_DATA)
  175. // If type is REGS_TOGGLE (3), 4 bytes of data must be provided.
  176. obj.SendDisableEnableFeatures = function (type, data) { if (data == null) { data = ''; } obj.SendCommand(0x48, Buffer.concat([Buffer.from([type]), data])); }
  177. // Private method
  178. obj.ProcessDataEx = function () {
  179. if (obj.acc.length < 8) return 0;
  180. // First 8 bytes are the header
  181. // CommandID + 0x000000 + Sequence Number
  182. //console.log('ProcessDataEx', obj.acc[0], obj.acc);
  183. switch (obj.acc[0]) {
  184. case 0x41: // OPEN_SESSION
  185. if (obj.acc.length < 30) return 0;
  186. var len = obj.acc[29];
  187. if (obj.acc.length < (30 + len)) return 0;
  188. obj.iderinfo = {};
  189. obj.iderinfo.major = obj.acc[8];
  190. obj.iderinfo.minor = obj.acc[9];
  191. obj.iderinfo.fwmajor = obj.acc[10];
  192. obj.iderinfo.fwminor = obj.acc[11];
  193. obj.iderinfo.readbfr = ReadShortX(obj.acc, 16);
  194. obj.iderinfo.writebfr = ReadShortX(obj.acc, 18);
  195. obj.iderinfo.proto = obj.acc[21];
  196. obj.iderinfo.iana = ReadIntX(obj.acc, 25);
  197. if (obj.debug) console.log(obj.iderinfo);
  198. if (obj.iderinfo.proto != 0) {
  199. if (obj.debug) console.log("Unknown proto", obj.iderinfo.proto);
  200. obj.Stop();
  201. }
  202. if (obj.iderinfo.readbfr > 8192) {
  203. if (obj.debug) console.log("Illegal read buffer size", obj.iderinfo.readbfr);
  204. obj.Stop();
  205. }
  206. if (obj.iderinfo.writebfr > 8192) {
  207. if (obj.debug) console.log("Illegal write buffer size", obj.iderinfo.writebfr);
  208. obj.Stop();
  209. }
  210. if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
  211. else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
  212. else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
  213. //obj.SendDisableEnableFeatures(1); // GetSupportedFeatures
  214. return 30 + len;
  215. case 0x43: // CLOSE
  216. if (obj.debug) console.log('CLOSE');
  217. obj.Stop();
  218. return 8;
  219. case 0x44: // KEEPALIVEPING
  220. obj.SendCommand(0x45); // Send PONG back
  221. return 8;
  222. case 0x45: // KEEPALIVEPONG
  223. if (obj.debug) console.log('PONG');
  224. return 8;
  225. case 0x46: // RESETOCCURED
  226. if (obj.acc.length < 9) return 0;
  227. var resetMask = obj.acc[8];
  228. if (g_media === null) {
  229. // No operations are pending
  230. obj.SendCommand(0x47); // Send ResetOccuredResponse
  231. if (obj.debug) console.log('RESETOCCURED1', resetMask);
  232. } else {
  233. // Operations are being done, sent the reset once completed.
  234. g_reset = true;
  235. if (obj.debug) console.log('RESETOCCURED2', resetMask);
  236. }
  237. return 9;
  238. case 0x49: // STATUS_DATA - DisableEnableFeaturesReply
  239. if (obj.acc.length < 13) return 0;
  240. var type = obj.acc[8];
  241. var value = ReadIntX(obj.acc, 9);
  242. if (obj.debug) console.log('STATUS_DATA', type, value);
  243. switch (type) {
  244. case 1: // REGS_AVAIL
  245. if (value & 1) {
  246. if (obj.iderStart == 0) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x08)); } // OnReboot
  247. else if (obj.iderStart == 1) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x10)); } // Graceful
  248. else if (obj.iderStart == 2) { obj.SendDisableEnableFeatures(3, IntToStrX(0x01 + 0x18)); } // Now
  249. }
  250. break;
  251. case 2: // REGS_STATUS
  252. obj.enabled = (value & 2) ? true : false;
  253. if (obj.debug) console.log("IDER Status: " + obj.enabled);
  254. break;
  255. case 3: // REGS_TOGGLE
  256. if (value != 1) {
  257. if (obj.debug) console.log("Register toggle failure");
  258. } //else { obj.SendDisableEnableFeatures(2); }
  259. break;
  260. }
  261. return 13;
  262. case 0x4A: // ERROR OCCURED
  263. if (obj.acc.length < 11) return 0;
  264. if (obj.debug) console.log('IDER: ABORT', obj.acc[8]);
  265. //obj.Stop();
  266. return 11;
  267. case 0x4B: // HEARTBEAT
  268. //console.log('HEARTBEAT');
  269. return 8;
  270. case 0x50: // COMMAND WRITTEN
  271. if (obj.acc.length < 28) return 0;
  272. var device = (obj.acc[14] & 0x10) ? 0xB0 : 0xA0;
  273. var deviceFlags = obj.acc[14];
  274. var cdb = obj.acc.slice(16, 28);
  275. var featureRegister = obj.acc[9];
  276. if (obj.debug) console.log('SCSI_CMD', device, cdb.toString('hex'), featureRegister, deviceFlags);
  277. handleSCSI(device, cdb, featureRegister, deviceFlags);
  278. return 28;
  279. case 0x53: // DATA FROM HOST
  280. if (obj.acc.length < 14) return 0;
  281. var len = ReadShortX(obj.acc, 9);
  282. if (obj.acc.length < (14 + len)) return 0;
  283. if (obj.debug) console.log('SCSI_WRITE, len = ' + (14 + len));
  284. obj.SendCommand(0x51, Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x70, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x51, 0x07, 0x27, 0x00]), true);
  285. return 14 + len;
  286. default:
  287. if (obj.debug) console.log('Unknown IDER command', obj.acc[0]);
  288. obj.Stop();
  289. break;
  290. }
  291. return 0;
  292. }
  293. function handleSCSI(dev, cdb, featureRegister, deviceFlags) {
  294. var lba;
  295. var len;
  296. switch (cdb[0]) {
  297. case 0x00: // TEST_UNIT_READY:
  298. if (obj.debug) console.log("SCSI: TEST_UNIT_READY", dev);
  299. switch (dev) {
  300. case 0xA0: // DEV_FLOPPY
  301. if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  302. if (obj.floppyReady == false) { obj.floppyReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
  303. break;
  304. case 0xB0: // DEV_CDDVD
  305. if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  306. if (obj.cdromReady == false) { obj.cdromReady = true; obj.SendCommandEndResponse(1, 0x06, dev, 0x28, 0x00); return -1; } // Switch to ready
  307. break;
  308. default:
  309. if (obj.debug) console.log("SCSI Internal error 3", dev);
  310. return -1;
  311. }
  312. obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); // Indicate ready
  313. break;
  314. case 0x08: // READ_6
  315. lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
  316. len = cdb[4];
  317. if (len == 0) { len = 256; }
  318. if (obj.debug) console.log("SCSI: READ_6", dev, lba, len);
  319. sendDiskData(dev, lba, len, featureRegister);
  320. break;
  321. case 0x0a: // WRITE_6
  322. lba = ((cdb[1] & 0x1f) << 16) + (cdb[2] << 8) + cdb[3];
  323. len = cdb[4];
  324. if (len == 0) { len = 256; }
  325. if (obj.debug) console.log("SCSI: WRITE_6", dev, lba, len);
  326. obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); // Write is not supported, remote no medium.
  327. return -1;
  328. /*
  329. case 0x15: // MODE_SELECT_6:
  330. console.log("SCSI ERROR: MODE_SELECT_6", dev);
  331. obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
  332. return -1;
  333. */
  334. case 0x1a: // MODE_SENSE_6
  335. if (obj.debug) console.log("SCSI: MODE_SENSE_6", dev);
  336. if ((cdb[2] == 0x3f) && (cdb[3] == 0x00)) {
  337. var a = 0, b = 0;
  338. switch (dev) {
  339. case 0xA0: // DEV_FLOPPY
  340. if (obj.floppy == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  341. a = 0x00;
  342. b = 0x80; // Read only = 0x80, Read write = 0x00
  343. break;
  344. case 0xB0: // DEV_CDDVD
  345. if (obj.cdrom == null) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  346. a = 0x05;
  347. b = 0x80;
  348. break;
  349. default:
  350. if (obj.debug) console.log("SCSI Internal error 6", dev);
  351. return -1;
  352. }
  353. obj.SendDataToHost(dev, true, Buffer.from([0, a, b, 0]), featureRegister & 1);
  354. return;
  355. }
  356. obj.SendCommandEndResponse(1, 0x05, dev, 0x24, 0x00);
  357. break;
  358. case 0x1b: // START_STOP (Called when you eject the CDROM)
  359. //var immediate = cdb[1] & 0x01;
  360. //var loej = cdb[4] & 0x02;
  361. //var start = cdb[4] & 0x01;
  362. obj.SendCommandEndResponse(1, 0, dev);
  363. break;
  364. case 0x1e: // LOCK_UNLOCK - ALLOW_MEDIUM_REMOVAL
  365. if (obj.debug) console.log("SCSI: ALLOW_MEDIUM_REMOVAL", dev);
  366. if ((dev == 0xA0) && (obj.floppy == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  367. if ((dev == 0xB0) && (obj.cdrom == null)) { obj.SendCommandEndResponse(1, 0x02, dev, 0x3a, 0x00); return -1; }
  368. obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00);
  369. break;
  370. case 0x23: // READ_FORMAT_CAPACITIES (Floppy only)
  371. if (obj.debug) console.log("SCSI: READ_FORMAT_CAPACITIES", dev);
  372. var buflen = ReadShort(cdb, 7);
  373. var mediaStatus = 0, sectors;
  374. var mcSize = buflen / 8; // Capacity descriptor size is 8
  375. switch (dev) {
  376. case 0xA0: // DEV_FLOPPY
  377. if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
  378. sectors = (obj.floppy.size >> 9) - 1;
  379. break;
  380. case 0xB0: // DEV_CDDVD
  381. if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x05, dev, 0x24, 0x00); return -1; }
  382. sectors = (obj.cdrom.size >> 11) - 1; // Number 2048 byte blocks
  383. break;
  384. default:
  385. if (obj.debug) console.log("SCSI Internal error 4", dev);
  386. return -1;
  387. }
  388. obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(8), Buffer.from([0x00, 0x00, 0x0b, 0x40, 0x02, 0x00, 0x02, 0x00])]), featureRegister & 1);
  389. break;
  390. case 0x25: // READ_CAPACITY
  391. if (obj.debug) console.log("SCSI: READ_CAPACITY", dev);
  392. var len = 0;
  393. switch (dev) {
  394. case 0xA0: // DEV_FLOPPY
  395. if ((obj.floppy == null) || (obj.floppy.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
  396. if (obj.floppy != null) { len = (obj.floppy.size >> 9) - 1; }
  397. if (obj.debug) console.log('DEV_FLOPPY', len); // Number 512 byte blocks
  398. break;
  399. case 0xB0: // DEV_CDDVD
  400. if ((obj.cdrom == null) || (obj.cdrom.size == 0)) { obj.SendCommandEndResponse(0, 0x02, dev, 0x3a, 0x00); return -1; }
  401. if (obj.cdrom != null) { len = (obj.cdrom.size >> 11) - 1; } // Number 2048 byte blocks
  402. if (obj.debug) console.log('DEV_CDDVD', len);
  403. break;
  404. default:
  405. if (obj.debug) console.log("SCSI Internal error 4", dev);
  406. return -1;
  407. }
  408. //if (dev == 0xA0) { dev = 0x00; } else { dev = 0x10; } // Weird but seems to work.
  409. if (obj.debug) console.log("SCSI: READ_CAPACITY2", dev, deviceFlags);
  410. obj.SendDataToHost(deviceFlags, true, Buffer.concat([IntToStr(len), Buffer.from([0, 0, ((dev == 0xB0) ? 0x08 : 0x02), 0])]), featureRegister & 1);
  411. break;
  412. case 0x28: // READ_10
  413. lba = ReadInt(cdb, 2);
  414. len = ReadShort(cdb, 7);
  415. if (obj.debug) console.log("SCSI: READ_10", dev, lba, len);
  416. sendDiskData(dev, lba, len, featureRegister);
  417. break;
  418. case 0x2a: // WRITE_10 (Floppy only)
  419. case 0x2e: // WRITE_AND_VERIFY (Floppy only)
  420. lba = ReadInt(cdb, 2);
  421. len = ReadShort(cdb, 7);
  422. if (obj.debug) console.log("SCSI: WRITE_10", dev, lba, len);
  423. obj.SendGetDataFromHost(dev, 512 * len); // Floppy writes only, accept sectors of 512 bytes
  424. break;
  425. case 0x43: // READ_TOC (CD Audio only)
  426. var buflen = ReadShort(cdb, 7);
  427. var msf = cdb[1] & 0x02;
  428. var format = cdb[2] & 0x07;
  429. if (format == 0) { format = cdb[9] >> 6; }
  430. if (obj.debug) console.log("SCSI: READ_TOC, dev=" + dev + ", buflen=" + buflen + ", msf=" + msf + ", format=" + format);
  431. switch (dev) {
  432. case 0xA0: // DEV_FLOPPY
  433. obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00); // Not implemented
  434. return -1;
  435. case 0xB0: // DEV_CDDVD
  436. // NOP
  437. break;
  438. default:
  439. if (obj.debug) console.log("SCSI Internal error 9", dev);
  440. return -1;
  441. }
  442. if (format == 1) { obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x0a, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1); }
  443. else if (format == 0) {
  444. if (msf) {
  445. obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x34, 0x13]), featureRegister & 1);
  446. } else {
  447. obj.SendDataToHost(dev, true, Buffer.from([0x00, 0x12, 0x01, 0x01, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00]), featureRegister & 1);
  448. }
  449. }
  450. break;
  451. case 0x46: // GET_CONFIGURATION
  452. var sendall = (cdb[1] != 2);
  453. var firstcode = ReadShort(cdb, 2);
  454. var buflen = ReadShort(cdb, 7);
  455. if (obj.debug) console.log("SCSI: GET_CONFIGURATION", dev, sendall, firstcode, buflen);
  456. if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
  457. // Set the header
  458. var r = null;
  459. // Add the data
  460. if (firstcode == 0) { r = IDE_CD_ConfigArrayProfileList; }
  461. if ((firstcode == 0x1) || (sendall && (firstcode < 0x1))) { r = IDE_CD_ConfigArrayCore; }
  462. if ((firstcode == 0x2) || (sendall && (firstcode < 0x2))) { r = IDE_CD_Morphing; }
  463. if ((firstcode == 0x3) || (sendall && (firstcode < 0x3))) { r = IDE_CD_ConfigArrayRemovable; }
  464. if ((firstcode == 0x10) || (sendall && (firstcode < 0x10))) { r = IDE_CD_ConfigArrayRandom; }
  465. if ((firstcode == 0x1E) || (sendall && (firstcode < 0x1E))) { r = IDE_CD_Read; }
  466. if ((firstcode == 0x100) || (sendall && (firstcode < 0x100))) { r = IDE_CD_PowerManagement; }
  467. if ((firstcode == 0x105) || (sendall && (firstcode < 0x105))) { r = IDE_CD_Timeout; }
  468. if (r == null) {
  469. //console.log('NOT RIGHT', sendall, firstcode, cdb[2], cdb[3]);
  470. //process.exit(0);
  471. r = Buffer.concat([IntToStr(0x0008), IntToStr(4)]);
  472. } else {
  473. r = Buffer.concat([IntToStr(0x0008), IntToStr(r.length + 4), r]);
  474. }
  475. // Cut the length to buflen if needed
  476. if (r.length > buflen) { r = r.slice(0, buflen); }
  477. obj.SendDataToHost(dev, true, r, featureRegister & 1);
  478. return -1;
  479. case 0x4a: // GET_EV_STATUS - GET_EVENT_STATUS_NOTIFICATION
  480. //var buflen = (cdb[7] << 8) + cdb[8];
  481. //if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
  482. if (obj.debug) console.log("SCSI: GET_EVENT_STATUS_NOTIFICATION", dev, cdb[1], cdb[4], cdb[9]);
  483. if ((cdb[1] != 0x01) && (cdb[4] != 0x10)) {
  484. if (obj.debug) console.log('SCSI ERROR');
  485. obj.SendCommandEndResponse(1, 0x05, dev, 0x26, 0x01);
  486. break;
  487. }
  488. var present = 0x00;
  489. if ((dev == 0xA0) && (obj.floppy != null)) { present = 0x02; }
  490. else if ((dev == 0xB0) && (obj.cdrom != null)) { present = 0x02; }
  491. obj.SendDataToHost(dev, true, Buffer.from([0x00, present, 0x80, 0x00]), featureRegister & 1); // This is the original version, 4 bytes long
  492. break;
  493. case 0x4c:
  494. obj.SendCommand(0x51, Buffer.concat([IntToStrX(0), IntToStrX(0), IntToStrX(0), Buffer.from([0x87, 0x50, 0x03, 0x00, 0x00, 0x00, 0xb0, 0x51, 0x05, 0x20, 0x00])]), true);
  495. break;
  496. case 0x51: // READ_DISC_INFO
  497. if (obj.debug) console.log("SCSI READ_DISC_INFO", dev);
  498. obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // Correct
  499. return -1;
  500. case 0x55: // MODE_SELECT_10:
  501. if (obj.debug) console.log("SCSI ERROR: MODE_SELECT_10", dev);
  502. obj.SendCommandEndResponse(1, 0x05, dev, 0x20, 0x00);
  503. return -1;
  504. case 0x5a: // MODE_SENSE_10
  505. if (obj.debug) console.log("SCSI: MODE_SENSE_10", dev, cdb[2] & 0x3f);
  506. var buflen = ReadShort(cdb, 7);
  507. //var pc = cdb[2] & 0xc0;
  508. var r = null;
  509. if (buflen == 0) { obj.SendDataToHost(dev, true, Buffer.concat([IntToStr(0x003c), IntToStr(0x0008)]), featureRegister & 1); return -1; } // TODO: Fixed this return, it's not correct.
  510. // 1.44 mb floppy or LS120 (sectorCount == 0x3c300)
  511. var sectorCount = 0;
  512. if (dev == 0xA0) {
  513. if (obj.floppy != null) { sectorCount = (obj.floppy.size >> 9); }
  514. } else {
  515. if (obj.cdrom != null) { sectorCount = (obj.cdrom.size >> 11); }
  516. }
  517. switch (cdb[2] & 0x3f) {
  518. case 0x01: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_FloppyError_Recovery_Array : IDE_ModeSence_Ls120Error_Recovery_Array; } else { r = IDE_ModeSence_CDError_Recovery_Array; } break;
  519. case 0x05: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_FloppyDisk_Page_Array : IDE_ModeSence_LS120Disk_Page_Array; } break;
  520. case 0x3f: if (dev == 0xA0) { r = (sectorCount <= 0xb40) ? IDE_ModeSence_3F_Floppy_Array : IDE_ModeSence_3F_LS120_Array; } else { r = IDE_ModeSence_3F_CD_Array; } break;
  521. case 0x1A: if (dev == 0xB0) { r = IDE_ModeSence_CD_1A_Array; } break;
  522. case 0x1D: if (dev == 0xB0) { r = IDE_ModeSence_CD_1D_Array; } break;
  523. case 0x2A: if (dev == 0xB0) { r = IDE_ModeSence_CD_2A_Array; } break;
  524. }
  525. if (r == null) {
  526. obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00); // TODO: Send proper error!!!
  527. } else {
  528. // Set disk to read only (we don't support write).
  529. //ms_data[3] = ms_data[3] | 0x80;
  530. obj.SendDataToHost(dev, true, r, featureRegister & 1);
  531. }
  532. break;
  533. case 0x51: // READ_DISK_INFORMATION
  534. obj.SendDataToHost(dev, true, RD_CD_DiskInfo, featureRegister & 1);
  535. break;
  536. case 0xAC: // GET_PERFORMANCE
  537. obj.SendDataToHost(dev, true, RD_CD_Performance, featureRegister & 1);
  538. break;
  539. default: // UNKNOWN COMMAND
  540. if (obj.debug) console.log("IDER: Unknown SCSI command", cdb[0]);
  541. obj.SendCommandEndResponse(0, 0x05, dev, 0x20, 0x00);
  542. return -1;
  543. }
  544. return 0;
  545. }
  546. function sendDiskData(dev, lba, len, featureRegister) {
  547. var media = null;
  548. var mediaBlocks = 0;
  549. if (dev == 0xA0) { media = obj.floppy; if (obj.floppy != null) { mediaBlocks = (obj.floppy.size >> 9); } }
  550. if (dev == 0xB0) { media = obj.cdrom; if (obj.cdrom != null) { mediaBlocks = (obj.cdrom.size >> 11); } }
  551. if ((len < 0) || (lba + len > mediaBlocks)) { obj.SendCommandEndResponse(1, 0x05, dev, 0x21, 0x00); return 0; }
  552. if (len == 0) { obj.SendCommandEndResponse(1, 0x00, dev, 0x00, 0x00); return 0; }
  553. if (media != null) {
  554. // Send sector stats
  555. if (obj.sectorStats) { obj.sectorStats(1, (dev == 0xA0) ? 0 : 1, mediaBlocks, lba, len); }
  556. if (dev == 0xA0) { lba <<= 9; len <<= 9; } else { lba <<= 11; len <<= 11; }
  557. if (g_media !== null) {
  558. // Queue read operation
  559. g_readQueue.push({ media: media, dev: dev, lba: lba, len: len, fr: featureRegister });
  560. } else {
  561. // obj.iderinfo.readbfr // TODO: MaxRead
  562. g_media = media;
  563. g_dev = dev;
  564. g_lba = lba;
  565. g_len = len;
  566. sendDiskDataEx(featureRegister);
  567. }
  568. }
  569. }
  570. var g_readQueue = [], g_dev, g_lba, g_len, g_media = null, g_reset = false;
  571. function sendDiskDataEx(featureRegister) {
  572. var len = g_len, lba = g_lba;
  573. if (g_len > obj.iderinfo.readbfr) { len = obj.iderinfo.readbfr; }
  574. g_len -= len;
  575. g_lba += len;
  576. var buffer = Buffer.alloc(len);
  577. fs.read(g_media.ptr, buffer, 0, len, lba, function (error, bytesRead, buffer) {
  578. obj.SendDataToHost(g_dev, (g_len == 0), buffer, featureRegister & 1);
  579. if ((g_len > 0) && (g_reset == false)) {
  580. sendDiskDataEx(featureRegister);
  581. } else {
  582. g_media = null;
  583. if (g_reset) { obj.SendCommand(0x47); g_readQueue = []; g_reset = false; } // Send ResetOccuredResponse
  584. else if (g_readQueue.length > 0) { var op = g_readQueue.shift(); g_media = op.media; g_dev = op.dev; g_lba = op.lba; g_len = op.len; sendDiskDataEx(op.fr); } // Un-queue read operation
  585. }
  586. });
  587. }
  588. return obj;
  589. }
  590. function ShortToStr(v) { return Buffer.from([(v >> 8) & 0xFF, v & 0xFF]); }
  591. function ShortToStrX(v) { return Buffer.from([v & 0xFF, (v >> 8) & 0xFF]); }
  592. function IntToStr(v) { return Buffer.from([(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF]); }
  593. function IntToStrX(v) { return Buffer.from([v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF]); }
  594. function ReadShort(v, p) { return (v[p] << 8) + v[p + 1]; }
  595. function ReadShortX(v, p) { return (v[p + 1] << 8) + v[p]; }
  596. function ReadInt(v, p) { return (v[p] * 0x1000000) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
  597. function ReadSInt(v, p) { return (v[p] << 24) + (v[p + 1] << 16) + (v[p + 2] << 8) + v[p + 3]; }
  598. function ReadIntX(v, p) { return (v[p + 3] * 0x1000000) + (v[p + 2] << 16) + (v[p + 1] << 8) + v[p]; }