v86 icon indicating copy to clipboard operation
v86 copied to clipboard

Stream comunication between JavaScript and guest OS

Open nwtgck opened this issue 2 years ago • 4 comments

Hi, thank you so much for the fantastic project!

I'd like to comunicate or stream data between JavaScript and guest OS. I know emulator.serial0_send() and "serial0-output-line" event, using in the Lua intepreter, but the output might have noises.

For example, when we have device files such as /dev/jsread and /dev/jswrite, we can compress in a guest OS and use in JavaScript by using cat /dev/jsread | gzip | /dev/jswrite. You can also do cat /dev/jsread | lua | /dev/jswrite.

Another my idea is to use named pipes (FIFO). I tried to use them, but unfortunelly they don't use FS.prototype.Read orFS.prototype.Write, so I could not give or obtain data to the named pipe from JavaScript.

Could you tell me how to comunicate between JavaScript and guest OS streamingly?

poor workaround

The script as follows create a large file/root/myjsread and emulator.fs9p.Read is modified to give arbitrary data to guest OS from JavaScript.

    var emulator = new V86Starter({...});

    const rootId = emulator.fs9p.SearchPath("/root").id;
    const filename = "myjsread";
    const parentid = rootId;
    const x = emulator.fs9p.CreateInode();
    x.uid = emulator.fs9p.inodes[parentid].uid;
    x.gid = emulator.fs9p.inodes[parentid].gid;
    x.qid.type = S_IFREG >> 8;
    x.mode = (emulator.fs9p.inodes[parentid].mode & 0x1B6) | S_IFREG;
    emulator.fs9p.PushInode(x, parentid, filename);
    emulator.fs9p.NotifyListeners(emulator.fs9p.inodes.length-1, 'newfile');
    var id = emulator.fs9p.inodes.length-1;
    x.size = Number.MAX_SAFE_INTEGER;

    const originalRead = emulator.fs9p.Read.bind(emulator.fs9p);

    console.log("ID", id);
    emulator.fs9p.Read = async function (nodeid, offset, count) {
        if (id === nodeid) {
            console.log("!! READ HOOK !! reading ", arguments);
            // You can return bytes what you want, but I just streams infinite "A"
            return new Uint8Array(Array(count).fill(65));
        }
        return originalRead(nodeid, offset, count);
    };

An example usage in a guest OS is as follows.

$ cat myjsread
AAAAAAAAAAAA...
AAAAAAAAAAAA...
...

In my experiment, reading the /root/myjsread stopped arround 2^32 bytes.

nwtgck avatar Sep 24 '21 00:09 nwtgck

I finally made it, ~~but it has a problem~~ ! I specified uartN: true bellow, then we can read/write /dev/ttyS1 and/dev/ttyS2 in guest OS.

var emulator = new V86Starter({
        ...
        uart1: true,
        uart2: true,
        uart3: true,
        ...
});

JavaScript → guest OS

//  JavaScript → guest OS
setInterval(() => {
        console.log("send now!");
        emulator.bus.send("serial1-input", 65);
}, 1000);
stty raw < /dev/ttyS1
cat /dev/ttyS1

guest OS → JavaScript

stty raw < /dev/ttyS2
echo hello > /dev/ttyS2
    emulator.add_listener("serial2-output-char", function(char)
    {
        console.log("outchar2", char);
    });

~~The problem is the line buffer.~~

nwtgck avatar Sep 25 '21 05:09 nwtgck

I found this very, very useful. Thanks!

ZXMushroom63 avatar Sep 30 '23 02:09 ZXMushroom63

I'm glad to hear that!

nwtgck avatar Sep 30 '23 03:09 nwtgck

This is very helpful, thank you for sharing!

farhan-ct avatar Mar 11 '24 07:03 farhan-ct