pci.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. "use strict";
  2. // http://wiki.osdev.org/PCI
  3. var
  4. /** @const */ PCI_CONFIG_ADDRESS = 0xCF8,
  5. /** @const */ PCI_CONFIG_DATA = 0xCFC;
  6. /**
  7. * @constructor
  8. * @param {CPU} cpu
  9. */
  10. function PCI(cpu)
  11. {
  12. this.pci_addr = new Uint8Array(4);
  13. this.pci_value = new Uint8Array(4);
  14. this.pci_response = new Uint8Array(4);
  15. this.pci_status = new Uint8Array(4);
  16. this.pci_addr32 = new Int32Array(this.pci_addr.buffer);
  17. this.pci_value32 = new Int32Array(this.pci_value.buffer);
  18. this.pci_response32 = new Int32Array(this.pci_response.buffer);
  19. this.pci_status32 = new Int32Array(this.pci_status.buffer);
  20. this.device_spaces = [];
  21. this.devices = [];
  22. /** @const @type {CPU} */
  23. this.cpu = cpu;
  24. for(var i = 0; i < 256; i++)
  25. {
  26. this.device_spaces[i] = undefined;
  27. this.devices[i] = undefined;
  28. }
  29. this.io = cpu.io;
  30. cpu.io.register_write(PCI_CONFIG_DATA, this,
  31. function(value)
  32. {
  33. this.pci_write8(this.pci_addr32[0], value);
  34. },
  35. function(value)
  36. {
  37. this.pci_write16(this.pci_addr32[0], value);
  38. },
  39. function(value)
  40. {
  41. this.pci_write32(this.pci_addr32[0], value);
  42. });
  43. cpu.io.register_write(PCI_CONFIG_DATA + 1, this,
  44. function(value)
  45. {
  46. this.pci_write8(this.pci_addr32[0] + 1 | 0, value);
  47. });
  48. cpu.io.register_write(PCI_CONFIG_DATA + 2, this,
  49. function(value)
  50. {
  51. this.pci_write8(this.pci_addr32[0] + 2 | 0, value);
  52. },
  53. function(value)
  54. {
  55. this.pci_write16(this.pci_addr32[0] + 2 | 0, value);
  56. });
  57. cpu.io.register_write(PCI_CONFIG_DATA + 3, this,
  58. function(value)
  59. {
  60. this.pci_write8(this.pci_addr32[0] + 3 | 0, value);
  61. });
  62. cpu.io.register_read_consecutive(PCI_CONFIG_DATA, this,
  63. function()
  64. {
  65. return this.pci_response[0];
  66. },
  67. function()
  68. {
  69. return this.pci_response[1];
  70. },
  71. function()
  72. {
  73. return this.pci_response[2];
  74. },
  75. function()
  76. {
  77. return this.pci_response[3];
  78. }
  79. );
  80. cpu.io.register_read_consecutive(PCI_CONFIG_ADDRESS, this,
  81. function()
  82. {
  83. return this.pci_status[0];
  84. },
  85. function()
  86. {
  87. return this.pci_status[1];
  88. },
  89. function()
  90. {
  91. return this.pci_status[2];
  92. },
  93. function()
  94. {
  95. return this.pci_status[3];
  96. }
  97. );
  98. cpu.io.register_write_consecutive(PCI_CONFIG_ADDRESS, this,
  99. function(out_byte)
  100. {
  101. this.pci_addr[0] = out_byte & 0xFC;
  102. },
  103. function(out_byte)
  104. {
  105. if((this.pci_addr[1] & 0x06) === 0x02 && (out_byte & 0x06) === 0x06)
  106. {
  107. dbg_log("CPU reboot via PCI");
  108. cpu.reboot_internal();
  109. return;
  110. }
  111. this.pci_addr[1] = out_byte;
  112. },
  113. function(out_byte)
  114. {
  115. this.pci_addr[2] = out_byte;
  116. },
  117. function(out_byte)
  118. {
  119. this.pci_addr[3] = out_byte;
  120. this.pci_query();
  121. }
  122. );
  123. // Some experimental PCI devices taken from my PC:
  124. // 00:00.0 Host bridge: Intel Corporation 4 Series Chipset DRAM Controller (rev 02)
  125. //var host_bridge = {
  126. // pci_id: 0,
  127. // pci_space: [
  128. // 0x86, 0x80, 0x20, 0x2e, 0x06, 0x00, 0x90, 0x20, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
  129. // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  130. // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x10, 0xd3, 0x82,
  131. // 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  132. // ],
  133. // pci_bars: [],
  134. //};
  135. // This needs to be set in order for seabios to not execute code outside of
  136. // mapped memory. While we map the BIOS into high memory, we don't allow
  137. // executing code there, which enables optimisations in read_imm8.
  138. // See [make_bios_writable_intel] in src/fw/shadow.c in seabios for details
  139. const PAM0 = 0x10;
  140. var host_bridge = {
  141. pci_id: 0,
  142. pci_space: [
  143. // 00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
  144. 0x86, 0x80, 0x37, 0x12, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
  145. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  146. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  147. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  148. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  149. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, PAM0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  150. ],
  151. pci_bars: [],
  152. name: "82441FX PMC",
  153. };
  154. this.register_device(host_bridge);
  155. this.isa_bridge = {
  156. pci_id: 1 << 3,
  157. pci_space: [
  158. // 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
  159. 0x86, 0x80, 0x00, 0x70, 0x07, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x80, 0x00,
  160. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  161. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  162. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  163. ],
  164. pci_bars: [],
  165. name: "82371SB PIIX3 ISA",
  166. };
  167. this.isa_bridge_space = this.register_device(this.isa_bridge);
  168. this.isa_bridge_space8 = new Uint8Array(this.isa_bridge_space.buffer);
  169. // 00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 90)
  170. //this.register_device([
  171. // 0x86, 0x80, 0x4e, 0x24, 0x07, 0x01, 0x10, 0x00, 0x90, 0x01, 0x04, 0x06, 0x00, 0x00, 0x01, 0x00,
  172. // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x20, 0xe0, 0xe0, 0x80, 0x22,
  173. // 0xb0, 0xfe, 0xb0, 0xfe, 0xf1, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  174. // 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x02, 0x00,
  175. //], 0x1e << 3);
  176. }
  177. PCI.prototype.get_state = function()
  178. {
  179. var state = [];
  180. for(var i = 0; i < 256; i++)
  181. {
  182. state[i] = this.device_spaces[i];
  183. }
  184. state[256] = this.pci_addr;
  185. state[257] = this.pci_value;
  186. state[258] = this.pci_response;
  187. state[259] = this.pci_status;
  188. return state;
  189. };
  190. PCI.prototype.set_state = function(state)
  191. {
  192. for(var i = 0; i < 256; i++)
  193. {
  194. var device = this.devices[i];
  195. var space = state[i];
  196. if(!device || !space)
  197. {
  198. if(device)
  199. {
  200. dbg_log("Warning: While restoring PCI device: Device exists in current " +
  201. "configuration but not in snapshot (" + device.name + ")");
  202. }
  203. if(space)
  204. {
  205. dbg_log("Warning: While restoring PCI device: Device doesn't exist in current " +
  206. "configuration but does in snapshot (device " + h(i, 2) + ")");
  207. }
  208. continue;
  209. }
  210. for(var bar_nr = 0; bar_nr < device.pci_bars.length; bar_nr++)
  211. {
  212. var value = space[(0x10 >> 2) + bar_nr];
  213. if(value & 1)
  214. {
  215. var bar = device.pci_bars[bar_nr];
  216. var from = bar.original_bar & ~1 & 0xFFFF;
  217. var to = value & ~1 & 0xFFFF;
  218. this.set_io_bars(bar, from, to);
  219. }
  220. else
  221. {
  222. // memory, cannot be changed
  223. }
  224. }
  225. this.device_spaces[i].set(space);
  226. }
  227. this.pci_addr.set(state[256]);
  228. this.pci_value.set(state[257]);
  229. this.pci_response.set(state[258]);
  230. this.pci_status.set(state[259]);
  231. };
  232. PCI.prototype.pci_query = function()
  233. {
  234. var dbg_line = "query";
  235. // Bit | .31 .0
  236. // Fmt | EBBBBBBBBDDDDDFFFRRRRRR00
  237. var bdf = this.pci_addr[2] << 8 | this.pci_addr[1],
  238. addr = this.pci_addr[0] & 0xFC,
  239. //devfn = bdf & 0xFF,
  240. //bus = bdf >> 8,
  241. dev = bdf >> 3 & 0x1F,
  242. //fn = bdf & 7,
  243. enabled = this.pci_addr[3] >> 7;
  244. dbg_line += " enabled=" + enabled;
  245. dbg_line += " bdf=" + h(bdf, 4);
  246. dbg_line += " dev=" + h(dev, 2);
  247. dbg_line += " addr=" + h(addr, 2);
  248. var device = this.device_spaces[bdf];
  249. if(device !== undefined)
  250. {
  251. this.pci_status32[0] = 0x80000000 | 0;
  252. if(addr < device.byteLength)
  253. {
  254. this.pci_response32[0] = device[addr >> 2];
  255. }
  256. else
  257. {
  258. // required by freebsd-9.1
  259. this.pci_response32[0] = 0;
  260. }
  261. dbg_line += " " + h(this.pci_addr32[0] >>> 0, 8) + " -> " + h(this.pci_response32[0] >>> 0, 8);
  262. if(addr >= device.byteLength)
  263. {
  264. dbg_line += " (undef)";
  265. }
  266. dbg_line += " (" + this.devices[bdf].name + ")";
  267. dbg_log(dbg_line, LOG_PCI);
  268. }
  269. else
  270. {
  271. this.pci_response32[0] = -1;
  272. this.pci_status32[0] = 0;
  273. }
  274. };
  275. PCI.prototype.pci_write8 = function(address, written)
  276. {
  277. var bdf = address >> 8 & 0xFFFF;
  278. var addr = address & 0xFF;
  279. var space = new Uint8Array(this.device_spaces[bdf].buffer);
  280. var device = this.devices[bdf];
  281. if(!space)
  282. {
  283. return;
  284. }
  285. dbg_assert(!(addr >= 0x10 && addr < 0x2C || addr >= 0x30 && addr < 0x34),
  286. "PCI: Expected 32-bit write, got 8-bit (addr: " + h(addr) + ")");
  287. dbg_log("PCI write8 dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
  288. " value=" + h(written, 2), LOG_PCI);
  289. space[addr] = written;
  290. };
  291. PCI.prototype.pci_write16 = function(address, written)
  292. {
  293. dbg_assert((address & 1) === 0);
  294. var bdf = address >> 8 & 0xFFFF;
  295. var addr = address & 0xFF;
  296. var space = new Uint16Array(this.device_spaces[bdf].buffer);
  297. var device = this.devices[bdf];
  298. if(!space)
  299. {
  300. return;
  301. }
  302. if(addr >= 0x10 && addr < 0x2C)
  303. {
  304. // Bochs bios
  305. dbg_log("Warning: PCI: Expected 32-bit write, got 16-bit (addr: " + h(addr) + ")");
  306. return;
  307. }
  308. dbg_assert(!(addr >= 0x30 && addr < 0x34),
  309. "PCI: Expected 32-bit write, got 16-bit (addr: " + h(addr) + ")");
  310. dbg_log("PCI writ16 dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
  311. " value=" + h(written, 4), LOG_PCI);
  312. space[addr >>> 1] = written;
  313. };
  314. PCI.prototype.pci_write32 = function(address, written)
  315. {
  316. dbg_assert((address & 3) === 0);
  317. var bdf = address >> 8 & 0xFFFF;
  318. var addr = address & 0xFF;
  319. var space = this.device_spaces[bdf];
  320. var device = this.devices[bdf];
  321. if(!space)
  322. {
  323. return;
  324. }
  325. if(addr >= 0x10 && addr < 0x28)
  326. {
  327. var bar_nr = addr - 0x10 >> 2;
  328. var bar = device.pci_bars[bar_nr];
  329. dbg_log("BAR" + bar_nr + " exists=" + (bar ? "y" : "n") + " changed to " +
  330. h(written >>> 0) + " dev=" + h(bdf >> 3, 2) + " (" + device.name + ") ", LOG_PCI);
  331. if(bar)
  332. {
  333. dbg_assert(!(bar.size & bar.size - 1), "bar size should be power of 2");
  334. var space_addr = addr >> 2;
  335. var type = space[space_addr] & 1;
  336. if((written | 3 | bar.size - 1) === -1) // size check
  337. {
  338. written = ~(bar.size - 1) | type;
  339. if(type === 0)
  340. {
  341. space[space_addr] = written;
  342. }
  343. }
  344. else
  345. {
  346. if(type === 0)
  347. {
  348. // memory
  349. var original_bar = bar.original_bar;
  350. if((written & ~0xF) !== (original_bar & ~0xF))
  351. {
  352. // seabios
  353. dbg_log("Warning: Changing memory bar not supported, ignored", LOG_PCI);
  354. }
  355. // changing isn't supported yet, reset to default
  356. space[space_addr] = original_bar;
  357. }
  358. }
  359. if(type === 1)
  360. {
  361. // io
  362. dbg_assert(type === 1);
  363. var from = space[space_addr] & ~1 & 0xFFFF;
  364. var to = written & ~1 & 0xFFFF;
  365. dbg_log("io bar changed from " + h(from >>> 0, 8) +
  366. " to " + h(to >>> 0, 8) + " size=" + bar.size, LOG_PCI);
  367. this.set_io_bars(bar, from, to);
  368. space[space_addr] = written | 1;
  369. }
  370. }
  371. else
  372. {
  373. space[addr >> 2] = 0;
  374. }
  375. dbg_log("BAR effective value: " + h(space[addr >> 2] >>> 0), LOG_PCI);
  376. }
  377. else if(addr === 0x30)
  378. {
  379. dbg_log("PCI write rom address dev=" + h(bdf >> 3, 2) + " (" + device.name + ")" +
  380. " value=" + h(written >>> 0, 8), LOG_PCI);
  381. if(device.pci_rom_size)
  382. {
  383. if((written | 0x7FF) === (0xFFFFFFFF|0))
  384. {
  385. space[addr >> 2] = -device.pci_rom_size | 0;
  386. }
  387. else
  388. {
  389. space[addr >> 2] = device.pci_rom_address | 0;
  390. }
  391. }
  392. else
  393. {
  394. space[addr >> 2] = 0;
  395. }
  396. }
  397. else if(addr === 0x04)
  398. {
  399. dbg_log("PCI write dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
  400. " value=" + h(written >>> 0, 8), LOG_PCI);
  401. }
  402. else
  403. {
  404. dbg_log("PCI write dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
  405. " value=" + h(written >>> 0, 8), LOG_PCI);
  406. space[addr >>> 2] = written;
  407. }
  408. };
  409. PCI.prototype.register_device = function(device)
  410. {
  411. dbg_assert(device.pci_id !== undefined);
  412. dbg_assert(device.pci_space !== undefined);
  413. dbg_assert(device.pci_bars !== undefined);
  414. var device_id = device.pci_id;
  415. dbg_log("PCI register bdf=" + h(device_id) + " (" + device.name + ")", LOG_PCI);
  416. dbg_assert(!this.devices[device_id]);
  417. dbg_assert(device.pci_space.length >= 64);
  418. dbg_assert(device_id < this.devices.length);
  419. // convert bytewise notation from lspci to double words
  420. var space = new Int32Array(64);
  421. space.set(new Int32Array(new Uint8Array(device.pci_space).buffer));
  422. this.device_spaces[device_id] = space;
  423. this.devices[device_id] = device;
  424. var bar_space = space.slice(4, 10);
  425. for(var i = 0; i < device.pci_bars.length; i++)
  426. {
  427. var bar = device.pci_bars[i];
  428. if(!bar)
  429. {
  430. continue;
  431. }
  432. var bar_base = bar_space[i];
  433. var type = bar_base & 1;
  434. bar.original_bar = bar_base;
  435. bar.entries = [];
  436. if(type === 0)
  437. {
  438. // memory, not needed currently
  439. }
  440. else
  441. {
  442. dbg_assert(type === 1);
  443. var port = bar_base & ~1;
  444. for(var j = 0; j < bar.size; j++)
  445. {
  446. bar.entries[j] = this.io.ports[port + j];
  447. }
  448. }
  449. }
  450. return space;
  451. };
  452. PCI.prototype.set_io_bars = function(bar, from, to)
  453. {
  454. var count = bar.size;
  455. dbg_log("Move io bars: from=" + h(from) + " to=" + h(to) + " count=" + count, LOG_PCI);
  456. var ports = this.io.ports;
  457. for(var i = 0; i < count; i++)
  458. {
  459. var old_entry = ports[from + i];
  460. if(from + i >= 0x1000)
  461. {
  462. ports[from + i] = this.io.create_empty_entry();
  463. }
  464. if(old_entry.read8 === this.io.empty_port_read8 &&
  465. old_entry.read16 === this.io.empty_port_read16 &&
  466. old_entry.read32 === this.io.empty_port_read32 &&
  467. old_entry.write8 === this.io.empty_port_write &&
  468. old_entry.write16 === this.io.empty_port_write &&
  469. old_entry.write32 === this.io.empty_port_write)
  470. {
  471. // happens when a device doesn't register its full range (currently ne2k and virtio)
  472. dbg_log("Warning: Bad IO bar: Source not mapped, port=" + h(from + i, 4), LOG_PCI);
  473. }
  474. var entry = bar.entries[i];
  475. var empty_entry = ports[to + i];
  476. dbg_assert(entry && empty_entry);
  477. if(to + i >= 0x1000)
  478. {
  479. ports[to + i] = entry;
  480. }
  481. if(empty_entry.read8 === this.io.empty_port_read8 ||
  482. empty_entry.read16 === this.io.empty_port_read16 ||
  483. empty_entry.read32 === this.io.empty_port_read32 ||
  484. empty_entry.write8 === this.io.empty_port_write ||
  485. empty_entry.write16 === this.io.empty_port_write ||
  486. empty_entry.write32 === this.io.empty_port_write)
  487. {
  488. // These can fail if the os maps an io port in multiple bars (indicating a bug)
  489. // XXX: Fails during restore_state
  490. dbg_log("Warning: Bad IO bar: Target already mapped, port=" + h(to + i, 4), LOG_PCI);
  491. }
  492. }
  493. };
  494. PCI.prototype.raise_irq = function(pci_id)
  495. {
  496. var space = this.device_spaces[pci_id];
  497. dbg_assert(space);
  498. var pin = (space[0x3C >>> 2] >> 8 & 0xFF) - 1;
  499. var device = (pci_id >> 3) - 1 & 0xFF;
  500. var parent_pin = pin + device & 3;
  501. var irq = this.isa_bridge_space8[0x60 + parent_pin];
  502. //dbg_log("PCI raise irq " + h(irq) + " dev=" + h(device, 2) +
  503. // " (" + this.devices[pci_id].name + ")", LOG_PCI);
  504. this.cpu.device_raise_irq(irq);
  505. };
  506. PCI.prototype.lower_irq = function(pci_id)
  507. {
  508. var space = this.device_spaces[pci_id];
  509. dbg_assert(space);
  510. var pin = space[0x3C >>> 2] >> 8 & 0xFF;
  511. var device = pci_id >> 3 & 0xFF;
  512. var parent_pin = pin + device - 2 & 3;
  513. var irq = this.isa_bridge_space8[0x60 + parent_pin];
  514. //dbg_log("PCI lower irq " + h(irq) + " dev=" + h(device, 2) +
  515. // " (" + this.devices[pci_id].name + ")", LOG_PCI);
  516. this.cpu.device_lower_irq(irq);
  517. };