mirror of
https://github.com/copy/v86.git
synced 2025-12-31 04:23:15 +00:00
303 lines
9.5 KiB
JavaScript
303 lines
9.5 KiB
JavaScript
import { dbg_assert } from "./log.js";
|
|
import { VirtIO, VIRTIO_F_VERSION_1 } from "./virtio.js";
|
|
import * as marshall from "../lib/marshall.js";
|
|
|
|
// For Types Only
|
|
import { CPU } from "./cpu.js";
|
|
import { BusConnector } from "./bus.js";
|
|
|
|
// 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
|
|
*/
|
|
export function VirtioConsole(cpu, bus)
|
|
{
|
|
/** @const @type {BusConnector} */
|
|
this.bus = bus;
|
|
this.rows = 25;
|
|
this.cols = 80;
|
|
this.ports = 4;
|
|
|
|
const 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: 0x0C << 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) =>
|
|
{
|
|
|
|
},
|
|
(queue_id) =>
|
|
{
|
|
const queue = this.virtio.queues[queue_id];
|
|
const 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;
|
|
}
|
|
const 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;
|
|
}
|
|
const 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);
|
|
|
|
|
|
const parts = marshall.Unmarshall(["w", "h", "h"], buffer, { offset : 0 });
|
|
const port = parts[0];
|
|
const event = parts[1];
|
|
const 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) {
|
|
const queue_id = port === 0 ? 0 : port * 2 + 2;
|
|
this.bus.register("virtio-console" + port + "-input-bytes", function(data) {
|
|
const 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) {
|
|
if(port === 0) {
|
|
this.cols = size[0];
|
|
this.rows = size[1];
|
|
}
|
|
|
|
if(this.virtio.queues[2].is_configured() && this.virtio.queues[2].has_request()) {
|
|
this.SendWindowSize(port, size[0], size[1]);
|
|
}
|
|
}, this);
|
|
}
|
|
}
|
|
|
|
VirtioConsole.prototype.SendWindowSize = function(port, cols = undefined, rows = undefined)
|
|
{
|
|
rows = rows || this.rows;
|
|
cols = cols || this.cols;
|
|
const bufchain = this.virtio.queues[2].pop_request();
|
|
const buf = new Uint8Array(12);
|
|
marshall.Marshall(["w", "h", "h", "h", "h"], [port, VIRTIO_CONSOLE_RESIZE, 0, rows, cols], buf, 0);
|
|
this.Send(2, bufchain, buf);
|
|
};
|
|
|
|
VirtioConsole.prototype.SendName = function(port, name)
|
|
{
|
|
const bufchain = this.virtio.queues[2].pop_request();
|
|
const namex = new TextEncoder().encode(name);
|
|
const 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()
|
|
{
|
|
const 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() {
|
|
this.virtio.reset();
|
|
};
|
|
|
|
VirtioConsole.prototype.SendEvent = function(port, event, value)
|
|
{
|
|
const queue = this.virtio.queues[2];
|
|
const bufchain = queue.pop_request();
|
|
|
|
const 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();
|
|
};
|