123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- "use strict";
- // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-2900003
- const VIRTIO_CONSOLE_DEVICE_READY = 0;
- const VIRTIO_CONSOLE_DEVICE_ADD = 1;
- const VIRTIO_CONSOLE_DEVICE_REMOVE = 2;
- const VIRTIO_CONSOLE_PORT_READY = 3;
- const VIRTIO_CONSOLE_CONSOLE_PORT = 4;
- const VIRTIO_CONSOLE_RESIZE = 5;
- const VIRTIO_CONSOLE_PORT_OPEN = 6;
- const VIRTIO_CONSOLE_PORT_NAME = 7;
- const VIRTIO_CONSOLE_F_SIZE = 0;
- const VIRTIO_CONSOLE_F_MULTIPORT = 1;
- const VIRTIO_CONSOLE_F_EMERG_WRITE = 2;
- /**
- * @constructor
- *
- * @param {CPU} cpu
- */
- function VirtioConsole(cpu, bus)
- {
- /** @const @type {BusConnector} */
- this.bus = bus;
- this.rows = 25;
- this.cols = 80;
- this.ports = 4;
- let queues = [
- {
- size_supported: 16,
- notify_offset: 0,
- },
- {
- size_supported: 16,
- notify_offset: 1,
- },
- {
- size_supported: 16,
- notify_offset: 2,
- },
- {
- size_supported: 16,
- notify_offset: 3,
- },
- ];
- for (let i = 1; i < this.ports; ++i)
- {
- queues.push({size_supported: 16, notify_offset: 0});
- queues.push({size_supported: 8, notify_offset: 1});
- }
- /** @type {VirtIO} */
- this.virtio = new VirtIO(cpu,
- {
- name: "virtio-console",
- pci_id: 0x07 << 3,
- device_id: 0x1043,
- subsystem_device_id: 3,
- common:
- {
- initial_port: 0xB800,
- queues: queues,
- features:
- [
- VIRTIO_CONSOLE_F_SIZE,
- VIRTIO_CONSOLE_F_MULTIPORT,
- VIRTIO_F_VERSION_1,
- ],
- on_driver_ok: () => {},
- },
- notification:
- {
- initial_port: 0xB900,
- single_handler: false,
- handlers:
- [
- (queue_id) =>
- {
- let queue = this.virtio.queues[queue_id];
- // TODO: Full buffer looks like an empty buffer so prevent it from filling
- while (queue.count_requests() > queue.size - 2) queue.pop_request();
- },
- (queue_id) =>
- {
- let queue = this.virtio.queues[queue_id];
- let port = queue_id > 3 ? (queue_id-3 >> 1) : 0;
- while(queue.has_request())
- {
- const bufchain = queue.pop_request();
- const buffer = new Uint8Array(bufchain.length_readable);
- bufchain.get_next_blob(buffer);
- this.bus.send("virtio-console" + port + "-output-bytes", buffer);
- this.Ack(queue_id, bufchain);
- }
- },
- (queue_id) =>
- {
- if(queue_id != 2)
- {
- dbg_assert(false, "VirtioConsole Notified for wrong queue: " + queue_id +
- " (expected queue_id of 2)");
- return;
- }
- let queue = this.virtio.queues[queue_id];
- // Full buffer looks like an empty buffer so prevent it from filling
- while (queue.count_requests() > queue.size - 2) queue.pop_request();
- },
- (queue_id) =>
- {
- if(queue_id != 3)
- {
- dbg_assert(false, "VirtioConsole Notified for wrong queue: " + queue_id +
- " (expected queue_id of 3)");
- return;
- }
- let queue = this.virtio.queues[queue_id];
- while(queue.has_request())
- {
- const bufchain = queue.pop_request();
- const buffer = new Uint8Array(bufchain.length_readable);
- bufchain.get_next_blob(buffer);
- let parts = marshall.Unmarshall(["w", "h", "h"], buffer, { offset : 0 });
- let port = parts[0];
- let event = parts[1];
- let value = parts[2];
- this.Ack(queue_id, bufchain);
- switch(event) {
- case VIRTIO_CONSOLE_DEVICE_READY:
- for (let i = 0; i < this.ports; ++i) {
- this.SendEvent(i, VIRTIO_CONSOLE_DEVICE_ADD, 0);
- }
- break;
- case VIRTIO_CONSOLE_PORT_READY:
- this.Ack(queue_id, bufchain);
- this.SendEvent(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
- this.SendName(port, "virtio-" + port);
- this.SendEvent(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
- break;
- case VIRTIO_CONSOLE_PORT_OPEN:
- this.Ack(queue_id, bufchain);
- if (port == 0) {
- this.SendWindowSize(port);
- }
- break;
- default:
- dbg_assert(false," VirtioConsole received unknown event: " + event[1]);
- return;
- }
- }
- },
- ],
- },
- isr_status:
- {
- initial_port: 0xB700,
- },
- device_specific:
- {
- initial_port: 0xB600,
- struct:
- [
- {
- bytes: 2,
- name: "cols",
- read: () => this.cols,
- write: data => { /* read only */ },
- },
- {
- bytes: 2,
- name: "rows",
- read: () => this.rows,
- write: data => { /* read only */ },
- },
- {
- bytes: 4,
- name: "max_nr_ports",
- read: () => this.ports,
- write: data => { /* read only */ },
- },
- {
- bytes: 4,
- name: "emerg_wr",
- read: () => 0,
- write: data => {
- dbg_assert(false, "Emergency write!");
- },
- },
- ]
- },
- });
- for (let port = 0; port < this.ports; ++port) {
- let queue_id = port == 0 ? 0 : port * 2 + 2;
- this.bus.register("virtio-console" + port + "-input-bytes", function(data) {
- let queue = this.virtio.queues[queue_id];
- if (queue.has_request()) {
- const bufchain = queue.pop_request();
- this.Send(queue_id, bufchain, new Uint8Array(data));
- } else {
- //TODO: Buffer
- }
- }, this);
- this.bus.register("virtio-console" + port + "-resize", function(size) {
- this.cols = size[0];
- this.rows = size[1];
- if (this.virtio.queues[2].is_configured() && this.virtio.queues[2].has_request()) {
- this.SendWindowSize(port);
- }
- }, this);
- }
- }
- VirtioConsole.prototype.SendWindowSize = function(port)
- {
- const bufchain = this.virtio.queues[2].pop_request();
- let buf = new Uint8Array(12);
- marshall.Marshall(["w", "h", "h", "h", "h"], [port, VIRTIO_CONSOLE_RESIZE, 0, this.rows, this.cols], buf, 0);
- this.Send(2, bufchain, buf);
- };
- VirtioConsole.prototype.SendName = function(port, name)
- {
- const bufchain = this.virtio.queues[2].pop_request();
- let namex = new TextEncoder().encode(name);
- let buf = new Uint8Array(8 + namex.length + 1);
- marshall.Marshall(["w", "h", "h"], [port, VIRTIO_CONSOLE_PORT_NAME, 1], buf, 0);
- for ( let i = 0; i < namex.length; ++i ) {
- buf[i+8] = namex[i];
- }
- buf[8 + namex.length] = 0;
- this.Send(2, bufchain, buf);
- };
- VirtioConsole.prototype.get_state = function()
- {
- let state = [];
- state[0] = this.virtio;
- state[1] = this.rows;
- state[2] = this.cols;
- state[3] = this.ports;
- return state;
- };
- VirtioConsole.prototype.set_state = function(state)
- {
- this.virtio.set_state(state[0]);
- this.rows = state[1];
- this.cols = state[2];
- this.ports = state[3];
- };
- VirtioConsole.prototype.Reset = function() {
- };
- VirtioConsole.prototype.SendEvent = function(port, event, value)
- {
- let queue = this.virtio.queues[2];
- const bufchain = queue.pop_request();
- let buf = new Uint8Array(8);
- marshall.Marshall(["w","h","h"], [port, event, value], buf, 0);
- this.Send(2, bufchain, buf);
- };
- VirtioConsole.prototype.Send = function (queue_id, bufchain, blob)
- {
- bufchain.set_next_blob(blob);
- this.virtio.queues[queue_id].push_reply(bufchain);
- this.virtio.queues[queue_id].flush_replies();
- };
- VirtioConsole.prototype.Ack = function (queue_id, bufchain)
- {
- bufchain.set_next_blob(new Uint8Array(0));
- this.virtio.queues[queue_id].push_reply(bufchain);
- this.virtio.queues[queue_id].flush_replies();
- };
|