Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion tools/wasm/src/virtio/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,19 @@ export function consoleDevice(
// console port instead of being dropped.
const receive_chains: VirtqueueChain[] = [];
const pending_input: Uint8Array[] = [];
let guest_ready = false;

function reset() {
// Receive descriptors belong to the old queue, but host input belongs to
// the console and must survive the reset Linux performs during probing.
receive_chains.length = 0;
guest_ready = false;
}

function flush_input() {
while (receive_chains.length > 0 && pending_input.length > 0) {
while (
guest_ready && receive_chains.length > 0 && pending_input.length > 0
) {
const chain = receive_chains.shift()!;
const chunk = pending_input[0]!;
const [desc, trailing] = chain;
Expand Down Expand Up @@ -100,6 +104,13 @@ export function consoleDevice(
}

async function notify_output(queue: Virtqueue) {
// Linux exposes the legacy console receive queue before hvc0 is ready to
// consume it. Its first output kick proves that the console handoff has
// completed, so input queued before boot is safe to release from here.
if (!guest_ready) {
guest_ready = true;
flush_input();
}
for (const chain of queue) {
let n = 0;
for (const { array, writable } of chain) {
Expand Down
18 changes: 18 additions & 0 deletions tools/wasm/test/wasm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,24 @@ test("console input is held until the guest opens its port", async () => {
replacement.setUint16(14, (1 << 7) | (1 << 1), true);
imports.enable_vring(0, 0, 1, replacement_ring, 1);
imports.notify(0, 0);
assert.deepEqual(
[...new Uint8Array(console_memory.buffer, 256, 2)],
[0, 0],
"input remains queued until the guest console can consume it",
);

const output_ring = 384;
const output_address = 512;
new Uint8Array(console_memory.buffer, output_address, 5).set(
new TextEncoder().encode("ready"),
);
const output_descriptor = new DataView(console_memory.buffer, output_ring, 16);
output_descriptor.setBigUint64(0, BigInt(output_address), true);
output_descriptor.setUint32(8, 5, true);
output_descriptor.setUint16(12, 0, true);
output_descriptor.setUint16(14, 1 << 7);
imports.enable_vring(0, 1, 1, output_ring, 2);
imports.notify(0, 1);
const undelivered = new Promise((resolve) => setTimeout(resolve, 50));
await Promise.race([delivered.promise, undelivered]);

Expand Down
Loading