Spaces:
Runtime error
Runtime error
// https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-2900003 | |
import { dbg_assert } from "./log.js"; | |
import { VirtIO, VIRTIO_F_VERSION_1 } from "./virtio.js"; | |
import { format_mac } from "./ne2k.js"; | |
import * as marshall from "../lib/marshall.js"; | |
// For Types Only | |
import { CPU } from "./cpu.js"; | |
import { BusConnector } from "./bus.js"; | |
const VIRTIO_NET_F_MAC = 5; | |
const VIRTIO_NET_F_CTRL_VQ = 17; | |
const VIRTIO_NET_F_STATUS = 16; | |
const VIRTIO_NET_F_MQ = 22; | |
const VIRTIO_NET_F_CTRL_MAC_ADDR = 23; | |
const VIRTIO_NET_F_MTU = 3; | |
const VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET = 0; | |
const VIRTIO_NET_CTRL_MAC_ADDR_SET = 1; | |
/** | |
* @constructor | |
* @param {CPU} cpu | |
* @param {BusConnector} bus | |
* @param {Boolean} preserve_mac_from_state_image | |
*/ | |
export function VirtioNet(cpu, bus, preserve_mac_from_state_image) | |
{ | |
/** @const @type {BusConnector} */ | |
this.bus = bus; | |
this.id = cpu.devices.net ? 1 : 0; | |
this.pairs = 1; | |
this.status = 1; | |
this.preserve_mac_from_state_image = preserve_mac_from_state_image; | |
this.mac = new Uint8Array([ | |
0x00, 0x22, 0x15, | |
Math.random() * 255 | 0, | |
Math.random() * 255 | 0, | |
Math.random() * 255 | 0, | |
]); | |
this.bus.send("net" + this.id + "-mac", format_mac(this.mac)); | |
const queues = []; | |
for(let i = 0; i < this.pairs; ++i) | |
{ | |
queues.push({size_supported: 1024, notify_offset: 0}); | |
queues.push({size_supported: 1024, notify_offset: 1}); | |
} | |
queues.push({ | |
size_supported: 16, | |
notify_offset: 2, | |
}); | |
/** @type {VirtIO} */ | |
this.virtio = new VirtIO(cpu, | |
{ | |
name: "virtio-net", | |
pci_id: 0x0A << 3, | |
device_id: 0x1041, | |
subsystem_device_id: 1, | |
common: | |
{ | |
initial_port: 0xC800, | |
queues: queues, | |
features: | |
[ | |
VIRTIO_NET_F_MAC, | |
VIRTIO_NET_F_STATUS, | |
VIRTIO_NET_F_MQ, | |
VIRTIO_NET_F_MTU, | |
VIRTIO_NET_F_CTRL_VQ, | |
VIRTIO_NET_F_CTRL_MAC_ADDR, | |
VIRTIO_F_VERSION_1, | |
], | |
on_driver_ok: () => {}, | |
}, | |
notification: | |
{ | |
initial_port: 0xC900, | |
single_handler: false, | |
handlers: | |
[ | |
(queue_id) => | |
{ | |
}, | |
(queue_id) => | |
{ | |
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); | |
this.bus.send("net" + this.id + "-send", buffer.subarray(12)); | |
this.bus.send("eth-transmit-end", [buffer.length - 12]); | |
this.virtio.queues[queue_id].push_reply(bufchain); | |
} | |
this.virtio.queues[queue_id].flush_replies(); | |
}, | |
(queue_id) => | |
{ | |
if(queue_id !== this.pairs * 2) | |
{ | |
dbg_assert(false, "VirtioNet 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(["b", "b"], buffer, { offset : 0 }); | |
const xclass = parts[0]; | |
const command = parts[1]; | |
//this.Ack(queue_id, bufchain); | |
switch(xclass << 8 | command) { | |
case 4 << 8 | VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET: | |
const data = marshall.Unmarshall(["h"], buffer, { offset : 2 }); | |
dbg_assert(data[0] === 1); | |
this.Send(queue_id, bufchain, new Uint8Array([0])); | |
break; | |
case 1 << 8 | VIRTIO_NET_CTRL_MAC_ADDR_SET: | |
this.mac = buffer.subarray(2, 8); | |
this.Send(queue_id, bufchain, new Uint8Array([0])); | |
this.bus.send("net" + this.id + "-mac", format_mac(this.mac)); | |
break; | |
default: | |
dbg_assert(false," VirtioNet received unknown command: " + xclass + ":" + command); | |
this.Send(queue_id, bufchain, new Uint8Array([1])); | |
return; | |
} | |
} | |
}, | |
], | |
}, | |
isr_status: | |
{ | |
initial_port: 0xC700, | |
}, | |
device_specific: | |
{ | |
initial_port: 0xC600, | |
struct: | |
[0,1,2,3,4,5].map((v,k) => ({ | |
bytes: 1, | |
name: "mac_" + k, | |
read: () => this.mac[k], | |
write: data => { /* read only */ }, | |
})).concat( | |
[ | |
{ | |
bytes: 2, | |
name: "status", | |
read: () => this.status, | |
write: data => { /* read only */ }, | |
}, | |
{ | |
bytes: 2, | |
name: "max_pairs", | |
read: () => this.pairs, | |
write: data => { /* read only */ }, | |
}, | |
{ | |
bytes: 2, | |
name: "mtu", | |
read: () => 1500, | |
write: data => {}, | |
} | |
]) | |
}, | |
}); | |
this.bus.register("net" + this.id + "-receive", data => { | |
this.bus.send("eth-receive-end", [data.length]); | |
const with_header = new Uint8Array(12 + data.byteLength); | |
const view = new DataView(with_header.buffer, with_header.byteOffset, with_header.byteLength); | |
view.setInt16(10, 1); | |
with_header.set(data, 12); | |
const queue = this.virtio.queues[0]; | |
if(queue.has_request()) { | |
const bufchain = queue.pop_request(); | |
bufchain.set_next_blob(with_header); | |
this.virtio.queues[0].push_reply(bufchain); | |
this.virtio.queues[0].flush_replies(); | |
} else { | |
console.log("No buffer to write into!"); | |
} | |
}, this); | |
} | |
VirtioNet.prototype.get_state = function() | |
{ | |
const state = []; | |
state[0] = this.virtio; | |
state[1] = this.id; | |
state[2] = this.mac; | |
return state; | |
}; | |
VirtioNet.prototype.set_state = function(state) | |
{ | |
this.virtio.set_state(state[0]); | |
this.id = state[1]; | |
if(this.preserve_mac_from_state_image) | |
{ | |
this.mac = state[2]; | |
this.bus.send("net" + this.id + "-mac", format_mac(this.mac)); | |
} | |
}; | |
VirtioNet.prototype.reset = function() { | |
this.virtio.reset(); | |
}; | |
VirtioNet.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(); | |
}; | |
VirtioNet.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(); | |
}; | |