@aptre/v86 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/v86.js CHANGED
@@ -106,6 +106,7 @@ var FW_CFG_FILE_START = 49152;
106
106
  var FW_CFG_SIGNATURE_QEMU = 1431127377;
107
107
  var WASM_TABLE_SIZE = 900;
108
108
  var WASM_TABLE_OFFSET = 1024;
109
+ var WASM_PAGE_SIZE = 65536;
109
110
  var MIXER_CHANNEL_LEFT = 0;
110
111
  var MIXER_CHANNEL_RIGHT = 1;
111
112
  var MIXER_CHANNEL_BOTH = 2;
@@ -5420,17 +5421,18 @@ var VirtIO = class {
5420
5421
  LOG_VIRTIO
5421
5422
  );
5422
5423
  }
5423
- if (data & ~this.device_status & VIRTIO_STATUS_DRIVER_OK && this.device_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET) {
5424
- this.notify_config_changes();
5425
- }
5426
5424
  if (!this.features_ok) {
5427
5425
  if (false) {
5428
5426
  dbg_log("Removing FEATURES_OK", LOG_VIRTIO);
5429
5427
  }
5430
5428
  data &= ~VIRTIO_STATUS_FEATURES_OK;
5431
5429
  }
5430
+ const prev_status = this.device_status;
5432
5431
  this.device_status = data;
5433
- if (data & ~this.device_status & VIRTIO_STATUS_DRIVER_OK) {
5432
+ if (data & ~prev_status & VIRTIO_STATUS_DRIVER_OK) {
5433
+ if (prev_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET) {
5434
+ this.notify_config_changes();
5435
+ }
5434
5436
  options.on_driver_ok();
5435
5437
  }
5436
5438
  }
@@ -5667,7 +5669,7 @@ var VirtIO = class {
5667
5669
  // Call only within constructor.
5668
5670
  init_capabilities(capabilities) {
5669
5671
  let cap_next = this.pci_space[52] = 64;
5670
- let cap_ptr = cap_next;
5672
+ let cap_ptr;
5671
5673
  for (const cap of capabilities) {
5672
5674
  const cap_len2 = VIRTIO_PCI_CAP_LENGTH + cap.extra.length;
5673
5675
  cap_ptr = cap_next;
@@ -6888,7 +6890,7 @@ var PCI = class {
6888
6890
  (out_byte) => {
6889
6891
  if ((this.pci_addr[1] & 6) === 2 && (out_byte & 6) === 6) {
6890
6892
  dbg_log("CPU reboot via PCI");
6891
- cpu.reboot_internal();
6893
+ cpu.reboot();
6892
6894
  return;
6893
6895
  }
6894
6896
  this.pci_addr[1] = out_byte;
@@ -6994,6 +6996,7 @@ var PCI = class {
6994
6996
  0,
6995
6997
  0,
6996
6998
  0,
6999
+ 0,
6997
7000
  PAM0,
6998
7001
  0,
6999
7002
  0,
@@ -7306,7 +7309,11 @@ var PCI = class {
7306
7309
  dbg_assert(device.pci_space.length >= 64);
7307
7310
  dbg_assert(device_id < this.devices.length);
7308
7311
  const space = new Int32Array(64);
7309
- space.set(new Int32Array(new Uint8Array(device.pci_space).buffer));
7312
+ const pci_bytes = new Uint8Array(device.pci_space);
7313
+ const aligned_len = pci_bytes.length & ~3;
7314
+ if (aligned_len > 0) {
7315
+ space.set(new Int32Array(pci_bytes.buffer, 0, aligned_len >> 2));
7316
+ }
7310
7317
  this.device_spaces[device_id] = space;
7311
7318
  this.devices[device_id] = device;
7312
7319
  const bar_space = space.slice(4, 10);
@@ -7957,7 +7964,7 @@ var PS2 = class {
7957
7964
  break;
7958
7965
  case 254:
7959
7966
  dbg_log("CPU reboot via PS2");
7960
- this.cpu.reboot_internal();
7967
+ this.cpu.reboot();
7961
7968
  break;
7962
7969
  default:
7963
7970
  dbg_log(
@@ -15102,12 +15109,930 @@ var VirtioBalloon = class {
15102
15109
  }
15103
15110
  };
15104
15111
 
15112
+ // src/virtio_mem.ts
15113
+ var VIRTIO_MEM_REQ_PLUG = 0;
15114
+ var VIRTIO_MEM_REQ_UNPLUG = 1;
15115
+ var VIRTIO_MEM_REQ_STATE = 3;
15116
+ var VIRTIO_MEM_RESP_ACK = 0;
15117
+ var VIRTIO_MEM_RESP_NACK = 1;
15118
+ var VIRTIO_MEM_RESP_ERROR = 3;
15119
+ var DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024;
15120
+ var VirtioMem = class {
15121
+ bus;
15122
+ cpu;
15123
+ virtio;
15124
+ block_size;
15125
+ region_addr;
15126
+ region_size;
15127
+ usable_region_size;
15128
+ plugged_size;
15129
+ requested_size;
15130
+ constructor(cpu, bus, region_addr, region_size, block_size) {
15131
+ this.bus = bus;
15132
+ this.cpu = cpu;
15133
+ this.block_size = block_size || DEFAULT_BLOCK_SIZE;
15134
+ this.region_addr = region_addr;
15135
+ this.region_size = region_size;
15136
+ this.usable_region_size = region_size;
15137
+ this.plugged_size = 0;
15138
+ this.requested_size = 0;
15139
+ const queues = [{ size_supported: 32, notify_offset: 0 }];
15140
+ this.virtio = new VirtIO(cpu, {
15141
+ name: "virtio-mem",
15142
+ pci_id: 13 << 3,
15143
+ device_id: 4184,
15144
+ subsystem_device_id: 24,
15145
+ common: {
15146
+ initial_port: 59392,
15147
+ queues,
15148
+ features: [VIRTIO_F_VERSION_1],
15149
+ on_driver_ok: () => {
15150
+ dbg_log("virtio-mem setup", LOG_PCI);
15151
+ }
15152
+ },
15153
+ notification: {
15154
+ initial_port: 59648,
15155
+ single_handler: false,
15156
+ handlers: [
15157
+ (queue_id) => {
15158
+ this.handle_request(queue_id);
15159
+ }
15160
+ ]
15161
+ },
15162
+ isr_status: {
15163
+ initial_port: 59136
15164
+ },
15165
+ device_specific: {
15166
+ initial_port: 58880,
15167
+ struct: [
15168
+ // block_size low
15169
+ {
15170
+ bytes: 4,
15171
+ name: "block_size_low",
15172
+ read: () => this.block_size >>> 0,
15173
+ write: () => {
15174
+ }
15175
+ },
15176
+ // block_size high
15177
+ {
15178
+ bytes: 4,
15179
+ name: "block_size_high",
15180
+ read: () => 0,
15181
+ write: () => {
15182
+ }
15183
+ },
15184
+ // node_id (u16 padded to u32)
15185
+ {
15186
+ bytes: 2,
15187
+ name: "node_id",
15188
+ read: () => 0,
15189
+ write: () => {
15190
+ }
15191
+ },
15192
+ // padding (6 bytes as 2+4)
15193
+ {
15194
+ bytes: 2,
15195
+ name: "padding0",
15196
+ read: () => 0,
15197
+ write: () => {
15198
+ }
15199
+ },
15200
+ {
15201
+ bytes: 4,
15202
+ name: "padding1",
15203
+ read: () => 0,
15204
+ write: () => {
15205
+ }
15206
+ },
15207
+ // addr low
15208
+ {
15209
+ bytes: 4,
15210
+ name: "addr_low",
15211
+ read: () => this.region_addr >>> 0,
15212
+ write: () => {
15213
+ }
15214
+ },
15215
+ // addr high
15216
+ {
15217
+ bytes: 4,
15218
+ name: "addr_high",
15219
+ read: () => 0,
15220
+ write: () => {
15221
+ }
15222
+ },
15223
+ // region_size low
15224
+ {
15225
+ bytes: 4,
15226
+ name: "region_size_low",
15227
+ read: () => this.region_size >>> 0,
15228
+ write: () => {
15229
+ }
15230
+ },
15231
+ // region_size high
15232
+ {
15233
+ bytes: 4,
15234
+ name: "region_size_high",
15235
+ read: () => 0,
15236
+ write: () => {
15237
+ }
15238
+ },
15239
+ // usable_region_size low
15240
+ {
15241
+ bytes: 4,
15242
+ name: "usable_region_size_low",
15243
+ read: () => this.usable_region_size >>> 0,
15244
+ write: () => {
15245
+ }
15246
+ },
15247
+ // usable_region_size high
15248
+ {
15249
+ bytes: 4,
15250
+ name: "usable_region_size_high",
15251
+ read: () => 0,
15252
+ write: () => {
15253
+ }
15254
+ },
15255
+ // plugged_size low
15256
+ {
15257
+ bytes: 4,
15258
+ name: "plugged_size_low",
15259
+ read: () => this.plugged_size >>> 0,
15260
+ write: () => {
15261
+ }
15262
+ },
15263
+ // plugged_size high
15264
+ {
15265
+ bytes: 4,
15266
+ name: "plugged_size_high",
15267
+ read: () => 0,
15268
+ write: () => {
15269
+ }
15270
+ },
15271
+ // requested_size low
15272
+ {
15273
+ bytes: 4,
15274
+ name: "requested_size_low",
15275
+ read: () => this.requested_size >>> 0,
15276
+ write: () => {
15277
+ }
15278
+ },
15279
+ // requested_size high
15280
+ {
15281
+ bytes: 4,
15282
+ name: "requested_size_high",
15283
+ read: () => 0,
15284
+ write: () => {
15285
+ }
15286
+ }
15287
+ ]
15288
+ }
15289
+ });
15290
+ }
15291
+ handle_request(queue_id) {
15292
+ const queue = this.virtio.queues[queue_id];
15293
+ while (queue.has_request()) {
15294
+ const bufchain = queue.pop_request();
15295
+ const request = new Uint8Array(bufchain.length_readable);
15296
+ bufchain.get_next_blob(request);
15297
+ const type = request[0] | request[1] << 8;
15298
+ const resp = new Uint8Array(8);
15299
+ let resp_type = VIRTIO_MEM_RESP_ERROR;
15300
+ switch (type) {
15301
+ case VIRTIO_MEM_REQ_PLUG:
15302
+ resp_type = this.handle_plug(request);
15303
+ break;
15304
+ case VIRTIO_MEM_REQ_UNPLUG:
15305
+ resp_type = VIRTIO_MEM_RESP_NACK;
15306
+ break;
15307
+ case VIRTIO_MEM_REQ_STATE:
15308
+ resp_type = this.handle_state(request, resp);
15309
+ break;
15310
+ default:
15311
+ dbg_log("virtio-mem: unknown request type " + type, LOG_PCI);
15312
+ }
15313
+ resp[0] = resp_type & 255;
15314
+ resp[1] = resp_type >> 8 & 255;
15315
+ bufchain.set_next_blob(resp);
15316
+ queue.push_reply(bufchain);
15317
+ }
15318
+ queue.flush_replies();
15319
+ }
15320
+ handle_plug(request) {
15321
+ const addr = (request[8] | request[9] << 8 | request[10] << 16 | request[11] << 24) >>> 0;
15322
+ const nb_blocks = request[16] | request[17] << 8;
15323
+ const size = nb_blocks * this.block_size;
15324
+ const new_plugged = this.plugged_size + size;
15325
+ if (new_plugged > this.usable_region_size) {
15326
+ return VIRTIO_MEM_RESP_NACK;
15327
+ }
15328
+ const new_memory_size = this.cpu.memory_size[0] + size;
15329
+ this.cpu.resize_memory(new_memory_size);
15330
+ this.cpu.full_clear_tlb();
15331
+ this.plugged_size = new_plugged;
15332
+ dbg_log(
15333
+ "virtio-mem: plugged " + nb_blocks + " blocks at 0x" + addr.toString(16) + " (" + (size >> 20) + "MB)",
15334
+ LOG_PCI
15335
+ );
15336
+ return VIRTIO_MEM_RESP_ACK;
15337
+ }
15338
+ handle_state(request, resp) {
15339
+ const addr = (request[8] | request[9] << 8 | request[10] << 16 | request[11] << 24) >>> 0;
15340
+ const nb_blocks = request[16] | request[17] << 8;
15341
+ const offset = addr - this.region_addr;
15342
+ const end = offset + nb_blocks * this.block_size;
15343
+ const all_plugged = end <= this.plugged_size ? 1 : 0;
15344
+ resp[8] = all_plugged & 255;
15345
+ resp[9] = all_plugged >> 8 & 255;
15346
+ return VIRTIO_MEM_RESP_ACK;
15347
+ }
15348
+ set_requested_size(size) {
15349
+ this.requested_size = size;
15350
+ if (this.virtio.device_status & 4) {
15351
+ this.virtio.notify_config_changes();
15352
+ }
15353
+ }
15354
+ get_state() {
15355
+ const state = [];
15356
+ state[0] = this.virtio;
15357
+ state[1] = this.block_size;
15358
+ state[2] = this.region_addr;
15359
+ state[3] = this.region_size;
15360
+ state[4] = this.usable_region_size;
15361
+ state[5] = this.plugged_size;
15362
+ state[6] = this.requested_size;
15363
+ return state;
15364
+ }
15365
+ set_state(state) {
15366
+ this.virtio.set_state(state[0]);
15367
+ this.block_size = state[1];
15368
+ this.region_addr = state[2];
15369
+ this.region_size = state[3];
15370
+ this.usable_region_size = state[4];
15371
+ this.plugged_size = state[5];
15372
+ this.requested_size = state[6];
15373
+ }
15374
+ };
15375
+
15376
+ // src/virtio_v86fs.ts
15377
+ var V86FS_MSG_MOUNT = 0;
15378
+ var V86FS_MSG_LOOKUP = 1;
15379
+ var V86FS_MSG_GETATTR = 2;
15380
+ var V86FS_MSG_READDIR = 3;
15381
+ var V86FS_MSG_OPEN = 4;
15382
+ var V86FS_MSG_CLOSE = 5;
15383
+ var V86FS_MSG_READ = 6;
15384
+ var V86FS_MSG_CREATE = 7;
15385
+ var V86FS_MSG_WRITE = 8;
15386
+ var V86FS_MSG_MKDIR = 9;
15387
+ var V86FS_MSG_SETATTR = 10;
15388
+ var V86FS_MSG_FSYNC = 11;
15389
+ var V86FS_MSG_UNLINK = 12;
15390
+ var V86FS_MSG_RENAME = 13;
15391
+ var V86FS_MSG_SYMLINK = 14;
15392
+ var V86FS_MSG_READLINK = 15;
15393
+ var V86FS_MSG_STATFS = 16;
15394
+ var V86FS_MSG_INVALIDATE = 32;
15395
+ var V86FS_MSG_INVALIDATE_DIR = 33;
15396
+ var V86FS_MSG_MOUNT_R = 128;
15397
+ var V86FS_MSG_LOOKUP_R = 129;
15398
+ var V86FS_MSG_GETATTR_R = 130;
15399
+ var V86FS_MSG_READDIR_R = 131;
15400
+ var V86FS_MSG_OPEN_R = 132;
15401
+ var V86FS_MSG_CLOSE_R = 133;
15402
+ var V86FS_MSG_READ_R = 134;
15403
+ var V86FS_MSG_CREATE_R = 135;
15404
+ var V86FS_MSG_WRITE_R = 136;
15405
+ var V86FS_MSG_MKDIR_R = 137;
15406
+ var V86FS_MSG_SETATTR_R = 138;
15407
+ var V86FS_MSG_FSYNC_R = 139;
15408
+ var V86FS_MSG_UNLINK_R = 140;
15409
+ var V86FS_MSG_RENAME_R = 141;
15410
+ var V86FS_MSG_SYMLINK_R = 142;
15411
+ var V86FS_MSG_READLINK_R = 143;
15412
+ var V86FS_MSG_STATFS_R = 144;
15413
+ var ATTR_MODE = 1;
15414
+ var ATTR_SIZE = 8;
15415
+ var V86FS_STATUS_OK = 0;
15416
+ var V86FS_STATUS_ENOENT = 2;
15417
+ var DT_DIR = 4;
15418
+ var DT_REG = 8;
15419
+ var DT_LNK = 10;
15420
+ var S_IFDIR = 16384;
15421
+ var S_IFREG = 32768;
15422
+ var S_IFLNK = 40960;
15423
+ var textDecoder = new TextDecoder();
15424
+ var textEncoder = new TextEncoder();
15425
+ var FS_ENTRIES = /* @__PURE__ */ new Map([
15426
+ [
15427
+ 1,
15428
+ [
15429
+ {
15430
+ inode_id: 2,
15431
+ name: "hello.txt",
15432
+ mode: S_IFREG | 420,
15433
+ size: 12,
15434
+ dt_type: DT_REG,
15435
+ mtime_sec: 17115e5,
15436
+ mtime_nsec: 0,
15437
+ content: textEncoder.encode("hello world\n")
15438
+ },
15439
+ {
15440
+ inode_id: 3,
15441
+ name: "subdir",
15442
+ mode: S_IFDIR | 493,
15443
+ size: 0,
15444
+ dt_type: DT_DIR,
15445
+ mtime_sec: 17115e5,
15446
+ mtime_nsec: 0
15447
+ }
15448
+ ]
15449
+ ]
15450
+ ]);
15451
+ var INODE_MAP = /* @__PURE__ */ new Map([
15452
+ [
15453
+ 1,
15454
+ {
15455
+ inode_id: 1,
15456
+ name: "",
15457
+ mode: S_IFDIR | 493,
15458
+ size: 0,
15459
+ dt_type: DT_DIR,
15460
+ mtime_sec: 17115e5,
15461
+ mtime_nsec: 0
15462
+ }
15463
+ ]
15464
+ ]);
15465
+ for (const entries of FS_ENTRIES.values()) {
15466
+ for (const e of entries) {
15467
+ INODE_MAP.set(e.inode_id, e);
15468
+ }
15469
+ }
15470
+ var V86FS_HDR_SIZE = 7;
15471
+ function packU32(buf, offset, val) {
15472
+ buf[offset] = val & 255;
15473
+ buf[offset + 1] = val >>> 8 & 255;
15474
+ buf[offset + 2] = val >>> 16 & 255;
15475
+ buf[offset + 3] = val >>> 24 & 255;
15476
+ }
15477
+ function packU64(buf, offset, val) {
15478
+ packU32(buf, offset, val);
15479
+ packU32(buf, offset + 4, 0);
15480
+ }
15481
+ function packU16(buf, offset, val) {
15482
+ buf[offset] = val & 255;
15483
+ buf[offset + 1] = val >>> 8 & 255;
15484
+ }
15485
+ function makeResp(size, type, tag) {
15486
+ const resp = new Uint8Array(size);
15487
+ packU32(resp, 0, size);
15488
+ resp[4] = type;
15489
+ resp[5] = tag & 255;
15490
+ resp[6] = tag >> 8 & 255;
15491
+ return resp;
15492
+ }
15493
+ function readU32(buf, offset) {
15494
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | buf[offset + 3] << 24 >>> 0;
15495
+ }
15496
+ function readU16(buf, offset) {
15497
+ return buf[offset] | buf[offset + 1] << 8;
15498
+ }
15499
+ function readU64(buf, offset) {
15500
+ return buf[offset] | buf[offset + 1] << 8 | buf[offset + 2] << 16 | (buf[offset + 3] << 24 >>> 0) + (buf[offset + 4] | buf[offset + 5] << 8 | buf[offset + 6] << 16 | buf[offset + 7] << 24 >>> 0) * 4294967296;
15501
+ }
15502
+ var VirtioV86FS = class {
15503
+ bus;
15504
+ virtio;
15505
+ next_handle_id;
15506
+ next_inode_id;
15507
+ open_handles;
15508
+ // handle_id -> inode_id
15509
+ read_count;
15510
+ constructor(cpu, bus) {
15511
+ this.bus = bus;
15512
+ this.next_handle_id = 1;
15513
+ this.next_inode_id = 100;
15514
+ this.open_handles = /* @__PURE__ */ new Map();
15515
+ this.read_count = 0;
15516
+ const queues = [
15517
+ // Queue 0: hipriq - high-priority metadata (LOOKUP, GETATTR)
15518
+ { size_supported: 128, notify_offset: 0 },
15519
+ // Queue 1: requestq - data requests (READ, WRITE, READDIR)
15520
+ { size_supported: 128, notify_offset: 1 },
15521
+ // Queue 2: notifyq - host-to-guest push invalidation
15522
+ { size_supported: 128, notify_offset: 2 }
15523
+ ];
15524
+ this.virtio = new VirtIO(cpu, {
15525
+ name: "virtio-v86fs",
15526
+ pci_id: 14 << 3,
15527
+ device_id: 4223,
15528
+ subsystem_device_id: 63,
15529
+ common: {
15530
+ initial_port: 63488,
15531
+ queues,
15532
+ features: [VIRTIO_F_VERSION_1],
15533
+ on_driver_ok: () => {
15534
+ console.log("v86fs: driver ok");
15535
+ this.bus.send("virtio-v86fs-driver-ok");
15536
+ }
15537
+ },
15538
+ notification: {
15539
+ initial_port: 63744,
15540
+ single_handler: false,
15541
+ handlers: [
15542
+ // Queue 0: hipriq
15543
+ (queue_id) => {
15544
+ this.handle_queue(queue_id);
15545
+ },
15546
+ // Queue 1: requestq
15547
+ (queue_id) => {
15548
+ this.handle_queue(queue_id);
15549
+ },
15550
+ // Queue 2: notifyq
15551
+ (_queue_id) => {
15552
+ dbg_log("v86fs: notifyq notification", LOG_PCI);
15553
+ }
15554
+ ]
15555
+ },
15556
+ isr_status: {
15557
+ initial_port: 63232
15558
+ }
15559
+ });
15560
+ }
15561
+ handle_queue(queue_id) {
15562
+ const queue = this.virtio.queues[queue_id];
15563
+ while (queue.has_request()) {
15564
+ const bufchain = queue.pop_request();
15565
+ const req = new Uint8Array(bufchain.length_readable);
15566
+ bufchain.get_next_blob(req);
15567
+ const resp = this.handle_message(req);
15568
+ if (resp && bufchain.length_writable > 0) {
15569
+ bufchain.set_next_blob(resp);
15570
+ }
15571
+ queue.push_reply(bufchain);
15572
+ }
15573
+ queue.flush_replies();
15574
+ }
15575
+ handle_message(req) {
15576
+ if (req.length < V86FS_HDR_SIZE) {
15577
+ console.warn("v86fs: message too short:", req.length);
15578
+ return null;
15579
+ }
15580
+ const type = req[4];
15581
+ const tag = readU16(req, 5);
15582
+ switch (type) {
15583
+ case V86FS_MSG_MOUNT:
15584
+ return this.handle_mount(req, tag);
15585
+ case V86FS_MSG_LOOKUP:
15586
+ return this.handle_lookup(req, tag);
15587
+ case V86FS_MSG_GETATTR:
15588
+ return this.handle_getattr(req, tag);
15589
+ case V86FS_MSG_READDIR:
15590
+ return this.handle_readdir(req, tag);
15591
+ case V86FS_MSG_OPEN:
15592
+ return this.handle_open(req, tag);
15593
+ case V86FS_MSG_CLOSE:
15594
+ return this.handle_close(req, tag);
15595
+ case V86FS_MSG_READ:
15596
+ return this.handle_read(req, tag);
15597
+ case V86FS_MSG_CREATE:
15598
+ return this.handle_create(req, tag);
15599
+ case V86FS_MSG_WRITE:
15600
+ return this.handle_write(req, tag);
15601
+ case V86FS_MSG_MKDIR:
15602
+ return this.handle_mkdir(req, tag);
15603
+ case V86FS_MSG_SETATTR:
15604
+ return this.handle_setattr(req, tag);
15605
+ case V86FS_MSG_FSYNC:
15606
+ return this.handle_fsync(req, tag);
15607
+ case V86FS_MSG_UNLINK:
15608
+ return this.handle_unlink(req, tag);
15609
+ case V86FS_MSG_RENAME:
15610
+ return this.handle_rename(req, tag);
15611
+ case V86FS_MSG_SYMLINK:
15612
+ return this.handle_symlink(req, tag);
15613
+ case V86FS_MSG_READLINK:
15614
+ return this.handle_readlink(req, tag);
15615
+ case V86FS_MSG_STATFS:
15616
+ return this.handle_statfs(tag);
15617
+ default:
15618
+ console.warn("v86fs: unknown message type:", type);
15619
+ return null;
15620
+ }
15621
+ }
15622
+ handle_mount(req, tag) {
15623
+ const name_len = readU16(req, 7);
15624
+ const name = name_len > 0 ? textDecoder.decode(req.subarray(9, 9 + name_len)) : "";
15625
+ console.log("v86fs: mount:", name || "(default)");
15626
+ this.bus.send("virtio-v86fs-mount", name);
15627
+ const resp = new Uint8Array(23);
15628
+ packU32(resp, 0, 23);
15629
+ resp[4] = V86FS_MSG_MOUNT_R;
15630
+ resp[5] = tag & 255;
15631
+ resp[6] = tag >> 8 & 255;
15632
+ packU32(resp, 7, 0);
15633
+ packU64(resp, 11, 1);
15634
+ packU32(resp, 19, 16877);
15635
+ return resp;
15636
+ }
15637
+ handle_getattr(req, tag) {
15638
+ const inode_id = readU64(req, 7);
15639
+ const entry = INODE_MAP.get(inode_id);
15640
+ const resp = makeResp(35, V86FS_MSG_GETATTR_R, tag);
15641
+ if (!entry) {
15642
+ packU32(resp, 7, V86FS_STATUS_ENOENT);
15643
+ return resp;
15644
+ }
15645
+ packU32(resp, 7, V86FS_STATUS_OK);
15646
+ packU32(resp, 11, entry.mode);
15647
+ packU64(resp, 15, entry.size);
15648
+ packU64(resp, 23, entry.mtime_sec);
15649
+ packU32(resp, 31, entry.mtime_nsec);
15650
+ return resp;
15651
+ }
15652
+ handle_lookup(req, tag) {
15653
+ const parent_id = readU64(req, 7);
15654
+ const name_len = readU16(req, 15);
15655
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15656
+ const entries = FS_ENTRIES.get(parent_id);
15657
+ const entry = entries?.find((e) => e.name === name);
15658
+ const resp = new Uint8Array(31);
15659
+ packU32(resp, 0, 31);
15660
+ resp[4] = V86FS_MSG_LOOKUP_R;
15661
+ resp[5] = tag & 255;
15662
+ resp[6] = tag >> 8 & 255;
15663
+ if (!entry) {
15664
+ packU32(resp, 7, V86FS_STATUS_ENOENT);
15665
+ return resp;
15666
+ }
15667
+ packU32(resp, 7, V86FS_STATUS_OK);
15668
+ packU64(resp, 11, entry.inode_id);
15669
+ packU32(resp, 19, entry.mode);
15670
+ packU64(resp, 23, entry.size);
15671
+ return resp;
15672
+ }
15673
+ handle_readdir(req, tag) {
15674
+ const dir_id = readU64(req, 7);
15675
+ const entries = FS_ENTRIES.get(dir_id) || [];
15676
+ const encodedNames = entries.map((e) => textEncoder.encode(e.name));
15677
+ let size = 7 + 4 + 4;
15678
+ for (const nameBytes of encodedNames) {
15679
+ size += 8 + 1 + 2 + nameBytes.length;
15680
+ }
15681
+ const resp = makeResp(size, V86FS_MSG_READDIR_R, tag);
15682
+ packU32(resp, 7, V86FS_STATUS_OK);
15683
+ packU32(resp, 11, entries.length);
15684
+ let off = 15;
15685
+ for (let i = 0; i < entries.length; i++) {
15686
+ const e = entries[i];
15687
+ const nameBytes = encodedNames[i];
15688
+ packU64(resp, off, e.inode_id);
15689
+ resp[off + 8] = e.dt_type;
15690
+ packU16(resp, off + 9, nameBytes.length);
15691
+ resp.set(nameBytes, off + 11);
15692
+ off += 11 + nameBytes.length;
15693
+ }
15694
+ return resp;
15695
+ }
15696
+ handle_open(req, tag) {
15697
+ const inode_id = readU64(req, 7);
15698
+ const handle_id = this.next_handle_id++;
15699
+ this.open_handles.set(handle_id, inode_id);
15700
+ this.bus.send("virtio-v86fs-open", inode_id);
15701
+ const resp = makeResp(19, V86FS_MSG_OPEN_R, tag);
15702
+ packU32(resp, 7, V86FS_STATUS_OK);
15703
+ packU64(resp, 11, handle_id);
15704
+ return resp;
15705
+ }
15706
+ handle_close(req, tag) {
15707
+ const handle_id = readU64(req, 7);
15708
+ this.open_handles.delete(handle_id);
15709
+ this.bus.send("virtio-v86fs-close", handle_id);
15710
+ const resp = makeResp(11, V86FS_MSG_CLOSE_R, tag);
15711
+ packU32(resp, 7, V86FS_STATUS_OK);
15712
+ return resp;
15713
+ }
15714
+ handle_read(req, tag) {
15715
+ const handle_id = readU64(req, 7);
15716
+ const offset = readU64(req, 15);
15717
+ const size = readU32(req, 23);
15718
+ const inode_id = this.open_handles.get(handle_id) ?? handle_id;
15719
+ const entry = INODE_MAP.get(inode_id);
15720
+ const content = entry?.content;
15721
+ this.read_count++;
15722
+ this.bus.send("virtio-v86fs-read", {
15723
+ handle_id,
15724
+ inode_id,
15725
+ offset,
15726
+ size
15727
+ });
15728
+ if (!content || offset >= content.length) {
15729
+ const resp2 = makeResp(15, V86FS_MSG_READ_R, tag);
15730
+ packU32(resp2, 7, V86FS_STATUS_OK);
15731
+ packU32(resp2, 11, 0);
15732
+ return resp2;
15733
+ }
15734
+ const start = Math.min(offset, content.length);
15735
+ const end = Math.min(start + size, content.length);
15736
+ const data = content.subarray(start, end);
15737
+ const resp = new Uint8Array(15 + data.length);
15738
+ packU32(resp, 0, 15 + data.length);
15739
+ resp[4] = V86FS_MSG_READ_R;
15740
+ resp[5] = tag & 255;
15741
+ resp[6] = tag >> 8 & 255;
15742
+ packU32(resp, 7, V86FS_STATUS_OK);
15743
+ packU32(resp, 11, data.length);
15744
+ resp.set(data, 15);
15745
+ return resp;
15746
+ }
15747
+ handle_create(req, tag) {
15748
+ const parent_id = readU64(req, 7);
15749
+ const name_len = readU16(req, 15);
15750
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15751
+ const mode = readU32(req, 17 + name_len);
15752
+ const inode_id = this.next_inode_id++;
15753
+ const entry = {
15754
+ inode_id,
15755
+ name,
15756
+ mode: mode | S_IFREG,
15757
+ size: 0,
15758
+ dt_type: DT_REG,
15759
+ mtime_sec: Math.floor(Date.now() / 1e3),
15760
+ mtime_nsec: 0,
15761
+ content: new Uint8Array(0)
15762
+ };
15763
+ let children = FS_ENTRIES.get(parent_id);
15764
+ if (!children) {
15765
+ children = [];
15766
+ FS_ENTRIES.set(parent_id, children);
15767
+ }
15768
+ children.push(entry);
15769
+ INODE_MAP.set(inode_id, entry);
15770
+ const resp = makeResp(23, V86FS_MSG_CREATE_R, tag);
15771
+ packU32(resp, 7, V86FS_STATUS_OK);
15772
+ packU64(resp, 11, inode_id);
15773
+ packU32(resp, 19, entry.mode);
15774
+ return resp;
15775
+ }
15776
+ handle_write(req, tag) {
15777
+ const inode_id = readU64(req, 7);
15778
+ const offset = readU64(req, 15);
15779
+ const size = readU32(req, 23);
15780
+ const data = req.subarray(27, 27 + size);
15781
+ const entry = INODE_MAP.get(inode_id);
15782
+ if (entry) {
15783
+ const needed = offset + size;
15784
+ if (!entry.content || entry.content.length < needed) {
15785
+ const newContent = new Uint8Array(needed);
15786
+ if (entry.content) {
15787
+ newContent.set(entry.content);
15788
+ }
15789
+ entry.content = newContent;
15790
+ }
15791
+ entry.content.set(data, offset);
15792
+ if (needed > entry.size) {
15793
+ entry.size = needed;
15794
+ }
15795
+ }
15796
+ const resp = makeResp(15, V86FS_MSG_WRITE_R, tag);
15797
+ packU32(resp, 7, V86FS_STATUS_OK);
15798
+ packU32(resp, 11, size);
15799
+ return resp;
15800
+ }
15801
+ handle_mkdir(req, tag) {
15802
+ const parent_id = readU64(req, 7);
15803
+ const name_len = readU16(req, 15);
15804
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15805
+ const mode = readU32(req, 17 + name_len);
15806
+ const inode_id = this.next_inode_id++;
15807
+ const entry = {
15808
+ inode_id,
15809
+ name,
15810
+ mode: mode | S_IFDIR,
15811
+ size: 0,
15812
+ dt_type: DT_DIR,
15813
+ mtime_sec: Math.floor(Date.now() / 1e3),
15814
+ mtime_nsec: 0
15815
+ };
15816
+ let children = FS_ENTRIES.get(parent_id);
15817
+ if (!children) {
15818
+ children = [];
15819
+ FS_ENTRIES.set(parent_id, children);
15820
+ }
15821
+ children.push(entry);
15822
+ INODE_MAP.set(inode_id, entry);
15823
+ FS_ENTRIES.set(inode_id, []);
15824
+ const resp = makeResp(23, V86FS_MSG_MKDIR_R, tag);
15825
+ packU32(resp, 7, V86FS_STATUS_OK);
15826
+ packU64(resp, 11, inode_id);
15827
+ packU32(resp, 19, entry.mode);
15828
+ return resp;
15829
+ }
15830
+ handle_setattr(req, tag) {
15831
+ const inode_id = readU64(req, 7);
15832
+ const valid = readU32(req, 15);
15833
+ const mode = readU32(req, 19);
15834
+ const size = readU64(req, 23);
15835
+ const entry = INODE_MAP.get(inode_id);
15836
+ if (entry) {
15837
+ if (valid & ATTR_MODE) {
15838
+ entry.mode = entry.mode & 61440 | mode & 4095;
15839
+ }
15840
+ if (valid & ATTR_SIZE) {
15841
+ entry.size = size;
15842
+ if (entry.content) {
15843
+ if (size === 0) {
15844
+ entry.content = new Uint8Array(0);
15845
+ } else if (size < entry.content.length) {
15846
+ entry.content = entry.content.subarray(0, size);
15847
+ }
15848
+ }
15849
+ }
15850
+ }
15851
+ const resp = makeResp(11, V86FS_MSG_SETATTR_R, tag);
15852
+ packU32(resp, 7, V86FS_STATUS_OK);
15853
+ return resp;
15854
+ }
15855
+ handle_fsync(req, tag) {
15856
+ const resp = makeResp(11, V86FS_MSG_FSYNC_R, tag);
15857
+ packU32(resp, 7, V86FS_STATUS_OK);
15858
+ return resp;
15859
+ }
15860
+ handle_unlink(req, tag) {
15861
+ const parent_id = readU64(req, 7);
15862
+ const name_len = readU16(req, 15);
15863
+ const name = textDecoder.decode(req.subarray(17, 17 + name_len));
15864
+ const children = FS_ENTRIES.get(parent_id);
15865
+ let status = V86FS_STATUS_ENOENT;
15866
+ if (children) {
15867
+ const idx = children.findIndex((e) => e.name === name);
15868
+ if (idx >= 0) {
15869
+ const entry = children[idx];
15870
+ INODE_MAP.delete(entry.inode_id);
15871
+ FS_ENTRIES.delete(entry.inode_id);
15872
+ children.splice(idx, 1);
15873
+ status = V86FS_STATUS_OK;
15874
+ }
15875
+ }
15876
+ const resp = makeResp(11, V86FS_MSG_UNLINK_R, tag);
15877
+ packU32(resp, 7, status);
15878
+ return resp;
15879
+ }
15880
+ handle_rename(req, tag) {
15881
+ let off = 7;
15882
+ const old_parent_id = readU64(req, off);
15883
+ off += 8;
15884
+ const old_name_len = readU16(req, off);
15885
+ off += 2;
15886
+ const old_name = textDecoder.decode(
15887
+ req.subarray(off, off + old_name_len)
15888
+ );
15889
+ off += old_name_len;
15890
+ const new_parent_id = readU64(req, off);
15891
+ off += 8;
15892
+ const new_name_len = readU16(req, off);
15893
+ off += 2;
15894
+ const new_name = textDecoder.decode(
15895
+ req.subarray(off, off + new_name_len)
15896
+ );
15897
+ let status = V86FS_STATUS_ENOENT;
15898
+ const old_children = FS_ENTRIES.get(old_parent_id);
15899
+ if (old_children) {
15900
+ const idx = old_children.findIndex((e) => e.name === old_name);
15901
+ if (idx >= 0) {
15902
+ const entry = old_children[idx];
15903
+ old_children.splice(idx, 1);
15904
+ entry.name = new_name;
15905
+ let new_children = FS_ENTRIES.get(new_parent_id);
15906
+ if (!new_children) {
15907
+ new_children = [];
15908
+ FS_ENTRIES.set(new_parent_id, new_children);
15909
+ }
15910
+ const existing = new_children.findIndex(
15911
+ (e) => e.name === new_name
15912
+ );
15913
+ if (existing >= 0) {
15914
+ const old_entry = new_children[existing];
15915
+ INODE_MAP.delete(old_entry.inode_id);
15916
+ new_children.splice(existing, 1);
15917
+ }
15918
+ new_children.push(entry);
15919
+ status = V86FS_STATUS_OK;
15920
+ }
15921
+ }
15922
+ const resp = makeResp(11, V86FS_MSG_RENAME_R, tag);
15923
+ packU32(resp, 7, status);
15924
+ return resp;
15925
+ }
15926
+ handle_symlink(req, tag) {
15927
+ let off = 7;
15928
+ const parent_id = readU64(req, off);
15929
+ off += 8;
15930
+ const name_len = readU16(req, off);
15931
+ off += 2;
15932
+ const name = textDecoder.decode(req.subarray(off, off + name_len));
15933
+ off += name_len;
15934
+ const target_len = readU16(req, off);
15935
+ off += 2;
15936
+ const target = textDecoder.decode(req.subarray(off, off + target_len));
15937
+ const inode_id = this.next_inode_id++;
15938
+ const entry = {
15939
+ inode_id,
15940
+ name,
15941
+ mode: S_IFLNK | 511,
15942
+ size: target.length,
15943
+ dt_type: DT_LNK,
15944
+ mtime_sec: Math.floor(Date.now() / 1e3),
15945
+ mtime_nsec: 0,
15946
+ symlink_target: target
15947
+ };
15948
+ let children = FS_ENTRIES.get(parent_id);
15949
+ if (!children) {
15950
+ children = [];
15951
+ FS_ENTRIES.set(parent_id, children);
15952
+ }
15953
+ children.push(entry);
15954
+ INODE_MAP.set(inode_id, entry);
15955
+ const resp = makeResp(23, V86FS_MSG_SYMLINK_R, tag);
15956
+ packU32(resp, 7, V86FS_STATUS_OK);
15957
+ packU64(resp, 11, inode_id);
15958
+ packU32(resp, 19, entry.mode);
15959
+ return resp;
15960
+ }
15961
+ handle_readlink(req, tag) {
15962
+ const inode_id = readU64(req, 7);
15963
+ const entry = INODE_MAP.get(inode_id);
15964
+ if (!entry || !entry.symlink_target) {
15965
+ const resp2 = makeResp(11, V86FS_MSG_READLINK_R, tag);
15966
+ packU32(resp2, 7, V86FS_STATUS_ENOENT);
15967
+ return resp2;
15968
+ }
15969
+ const target_bytes = textEncoder.encode(entry.symlink_target);
15970
+ const resp_len = 11 + 2 + target_bytes.length;
15971
+ const resp = makeResp(resp_len, V86FS_MSG_READLINK_R, tag);
15972
+ packU32(resp, 7, V86FS_STATUS_OK);
15973
+ packU16(resp, 11, target_bytes.length);
15974
+ resp.set(target_bytes, 13);
15975
+ return resp;
15976
+ }
15977
+ handle_statfs(tag) {
15978
+ const resp = makeResp(55, V86FS_MSG_STATFS_R, tag);
15979
+ packU32(resp, 7, V86FS_STATUS_OK);
15980
+ packU64(resp, 11, 1024 * 1024);
15981
+ packU64(resp, 19, 512 * 1024);
15982
+ packU64(resp, 27, 512 * 1024);
15983
+ packU64(resp, 35, 1024 * 1024);
15984
+ packU64(resp, 43, 512 * 1024);
15985
+ packU32(resp, 51, 4096);
15986
+ return resp;
15987
+ }
15988
+ /** Push an INVALIDATE notification to the guest via notifyq.
15989
+ * Guest kernel will invalidate page cache for the given inode. */
15990
+ invalidate_inode(inode_id) {
15991
+ const queue = this.virtio.queues[2];
15992
+ if (!queue.has_request()) return false;
15993
+ const bufchain = queue.pop_request();
15994
+ const msg = new Uint8Array(15);
15995
+ packU32(msg, 0, 15);
15996
+ msg[4] = V86FS_MSG_INVALIDATE;
15997
+ packU16(msg, 5, 0);
15998
+ packU64(msg, 7, inode_id);
15999
+ bufchain.set_next_blob(msg);
16000
+ queue.push_reply(bufchain);
16001
+ queue.flush_replies();
16002
+ return true;
16003
+ }
16004
+ /** Push an INVALIDATE_DIR notification to the guest via notifyq.
16005
+ * Guest kernel will invalidate dcache for the given directory inode. */
16006
+ invalidate_dir(inode_id) {
16007
+ const queue = this.virtio.queues[2];
16008
+ if (!queue.has_request()) return false;
16009
+ const bufchain = queue.pop_request();
16010
+ const msg = new Uint8Array(15);
16011
+ packU32(msg, 0, 15);
16012
+ msg[4] = V86FS_MSG_INVALIDATE_DIR;
16013
+ packU16(msg, 5, 0);
16014
+ packU64(msg, 7, inode_id);
16015
+ bufchain.set_next_blob(msg);
16016
+ queue.push_reply(bufchain);
16017
+ queue.flush_replies();
16018
+ return true;
16019
+ }
16020
+ get_state() {
16021
+ const state = [];
16022
+ state[0] = this.virtio;
16023
+ return state;
16024
+ }
16025
+ set_state(state) {
16026
+ this.virtio.set_state(state[0]);
16027
+ }
16028
+ };
16029
+
15105
16030
  // lib/filesystem.ts
15106
16031
  var S_IFMT = 61440;
15107
16032
  var S_IFSOCK = 49152;
15108
- var S_IFLNK = 40960;
15109
- var S_IFREG = 32768;
15110
- var S_IFDIR = 16384;
16033
+ var S_IFLNK2 = 40960;
16034
+ var S_IFREG2 = 32768;
16035
+ var S_IFDIR2 = 16384;
15111
16036
  var STATUS_INVALID = -1;
15112
16037
  var STATUS_OK = 0;
15113
16038
  var STATUS_ON_STORAGE = 2;
@@ -15200,11 +16125,11 @@ var Inode = class {
15200
16125
  get_state() {
15201
16126
  const state = [];
15202
16127
  state[0] = this.mode;
15203
- if ((this.mode & S_IFMT) === S_IFDIR) {
16128
+ if ((this.mode & S_IFMT) === S_IFDIR2) {
15204
16129
  state[1] = [...this.direntries];
15205
- } else if ((this.mode & S_IFMT) === S_IFREG) {
16130
+ } else if ((this.mode & S_IFMT) === S_IFREG2) {
15206
16131
  state[1] = this.sha256sum;
15207
- } else if ((this.mode & S_IFMT) === S_IFLNK) {
16132
+ } else if ((this.mode & S_IFMT) === S_IFLNK2) {
15208
16133
  state[1] = this.symlink;
15209
16134
  } else if ((this.mode & S_IFMT) === S_IFSOCK) {
15210
16135
  state[1] = [this.minor, this.major];
@@ -15227,14 +16152,14 @@ var Inode = class {
15227
16152
  }
15228
16153
  set_state(state) {
15229
16154
  this.mode = state[0];
15230
- if ((this.mode & S_IFMT) === S_IFDIR) {
16155
+ if ((this.mode & S_IFMT) === S_IFDIR2) {
15231
16156
  this.direntries = /* @__PURE__ */ new Map();
15232
16157
  for (const [name, entry] of state[1]) {
15233
16158
  this.direntries.set(name, entry);
15234
16159
  }
15235
- } else if ((this.mode & S_IFMT) === S_IFREG) {
16160
+ } else if ((this.mode & S_IFMT) === S_IFREG2) {
15236
16161
  this.sha256sum = state[1];
15237
- } else if ((this.mode & S_IFMT) === S_IFLNK) {
16162
+ } else if ((this.mode & S_IFMT) === S_IFLNK2) {
15238
16163
  this.symlink = state[1];
15239
16164
  } else if ((this.mode & S_IFMT) === S_IFSOCK) {
15240
16165
  ;
@@ -15303,7 +16228,7 @@ var FS = class {
15303
16228
  state[1] = this.qidcounter.last_qidnumber;
15304
16229
  state[2] = [];
15305
16230
  for (const [id, data] of Object.entries(this.inodedata)) {
15306
- if ((this.inodes[Number(id)].mode & S_IFDIR) === 0) {
16231
+ if ((this.inodes[Number(id)].mode & S_IFDIR2) === 0) {
15307
16232
  state[2].push([id, data]);
15308
16233
  }
15309
16234
  }
@@ -15354,15 +16279,15 @@ var FS = class {
15354
16279
  inode.uid = data[JSONFS_IDX_UID];
15355
16280
  inode.gid = data[JSONFS_IDX_GID];
15356
16281
  const ifmt = inode.mode & S_IFMT;
15357
- if (ifmt === S_IFDIR) {
16282
+ if (ifmt === S_IFDIR2) {
15358
16283
  this.PushInode(inode, parentid, name);
15359
16284
  this.LoadDir(this.inodes.length - 1, data[JSONFS_IDX_TARGET]);
15360
- } else if (ifmt === S_IFREG) {
16285
+ } else if (ifmt === S_IFREG2) {
15361
16286
  inode.status = STATUS_ON_STORAGE;
15362
16287
  inode.sha256sum = data[JSONFS_IDX_SHA256];
15363
16288
  dbg_assert(!!inode.sha256sum);
15364
16289
  this.PushInode(inode, parentid, name);
15365
- } else if (ifmt === S_IFLNK) {
16290
+ } else if (ifmt === S_IFLNK2) {
15366
16291
  inode.symlink = data[JSONFS_IDX_TARGET];
15367
16292
  this.PushInode(inode, parentid, name);
15368
16293
  } else if (ifmt === S_IFSOCK) {
@@ -15534,13 +16459,13 @@ var FS = class {
15534
16459
  return this.create_forwarder(parent_inode.mount_id, foreign_id);
15535
16460
  }
15536
16461
  const x = this.CreateInode();
15537
- x.mode = 511 | S_IFDIR;
16462
+ x.mode = 511 | S_IFDIR2;
15538
16463
  if (parentid >= 0) {
15539
16464
  x.uid = this.inodes[parentid].uid;
15540
16465
  x.gid = this.inodes[parentid].gid;
15541
- x.mode = this.inodes[parentid].mode & 511 | S_IFDIR;
16466
+ x.mode = this.inodes[parentid].mode & 511 | S_IFDIR2;
15542
16467
  }
15543
- x.qid.type = S_IFDIR >> 8;
16468
+ x.qid.type = S_IFDIR2 >> 8;
15544
16469
  this.PushInode(x, parentid, name);
15545
16470
  this.NotifyListeners(this.inodes.length - 1, "newdir");
15546
16471
  return this.inodes.length - 1;
@@ -15558,8 +16483,8 @@ var FS = class {
15558
16483
  const x = this.CreateInode();
15559
16484
  x.uid = this.inodes[parentid].uid;
15560
16485
  x.gid = this.inodes[parentid].gid;
15561
- x.qid.type = S_IFREG >> 8;
15562
- x.mode = this.inodes[parentid].mode & 438 | S_IFREG;
16486
+ x.qid.type = S_IFREG2 >> 8;
16487
+ x.mode = this.inodes[parentid].mode & 438 | S_IFREG2;
15563
16488
  this.PushInode(x, parentid, filename);
15564
16489
  this.NotifyListeners(this.inodes.length - 1, "newfile");
15565
16490
  return this.inodes.length - 1;
@@ -15600,9 +16525,9 @@ var FS = class {
15600
16525
  const x = this.CreateInode();
15601
16526
  x.uid = this.inodes[parentid].uid;
15602
16527
  x.gid = this.inodes[parentid].gid;
15603
- x.qid.type = S_IFLNK >> 8;
16528
+ x.qid.type = S_IFLNK2 >> 8;
15604
16529
  x.symlink = symlink;
15605
- x.mode = S_IFLNK;
16530
+ x.mode = S_IFLNK2;
15606
16531
  this.PushInode(x, parentid, filename);
15607
16532
  return this.inodes.length - 1;
15608
16533
  }
@@ -15647,7 +16572,7 @@ var FS = class {
15647
16572
  if (this.is_forwarder(inode)) {
15648
16573
  return await this.follow_fs(inode).OpenInode(inode.foreign_id, mode);
15649
16574
  }
15650
- if ((inode.mode & S_IFMT) === S_IFDIR) {
16575
+ if ((inode.mode & S_IFMT) === S_IFDIR2) {
15651
16576
  this.FillDirectory(id);
15652
16577
  }
15653
16578
  }
@@ -16002,7 +16927,7 @@ var FS = class {
16002
16927
  let parentid = -1;
16003
16928
  let id = 0;
16004
16929
  let forward_path = null;
16005
- let i = 0;
16930
+ let i;
16006
16931
  for (i = 0; i < n; i++) {
16007
16932
  parentid = id;
16008
16933
  id = this.Search(parentid, walk[i]);
@@ -16068,13 +16993,13 @@ var FS = class {
16068
16993
  DeleteNode(path) {
16069
16994
  const ids = this.SearchPath(path);
16070
16995
  if (ids.id === -1) return;
16071
- if ((this.inodes[ids.id].mode & S_IFMT) === S_IFREG) {
16996
+ if ((this.inodes[ids.id].mode & S_IFMT) === S_IFREG2) {
16072
16997
  const ret = this.Unlink(ids.parentid, ids.name);
16073
16998
  dbg_assert(
16074
16999
  ret === 0,
16075
17000
  "Filesystem DeleteNode failed with error code: " + -ret
16076
17001
  );
16077
- } else if ((this.inodes[ids.id].mode & S_IFMT) === S_IFDIR) {
17002
+ } else if ((this.inodes[ids.id].mode & S_IFMT) === S_IFDIR2) {
16078
17003
  this.RecursiveDelete(path);
16079
17004
  const ret = this.Unlink(ids.parentid, ids.name);
16080
17005
  dbg_assert(
@@ -16178,7 +17103,7 @@ var FS = class {
16178
17103
  if (this.is_forwarder(inode)) {
16179
17104
  return this.follow_fs(inode).IsDirectory(inode.foreign_id);
16180
17105
  }
16181
- return (inode.mode & S_IFMT) === S_IFDIR;
17106
+ return (inode.mode & S_IFMT) === S_IFDIR2;
16182
17107
  }
16183
17108
  IsEmpty(idx) {
16184
17109
  const inode = this.inodes[idx];
@@ -16708,7 +17633,7 @@ var Virtio9p = class {
16708
17633
  bufchain.get_next_blob(buffer);
16709
17634
  const state = { offset: 0 };
16710
17635
  const header = Unmarshall(["w", "b", "h"], buffer, state);
16711
- let size = header[0];
17636
+ let size;
16712
17637
  const id = header[1];
16713
17638
  const tag = header[2];
16714
17639
  switch (id) {
@@ -16769,7 +17694,7 @@ var Virtio9p = class {
16769
17694
  name
16770
17695
  );
16771
17696
  if (ret < 0) {
16772
- let error_message = "";
17697
+ let error_message;
16773
17698
  if (ret === -EPERM)
16774
17699
  error_message = "Operation not permitted";
16775
17700
  else {
@@ -16882,7 +17807,7 @@ var Virtio9p = class {
16882
17807
  this.fids[fid].inodeid
16883
17808
  );
16884
17809
  const inode = this.fs.GetInode(idx);
16885
- inode.mode = mode | S_IFDIR;
17810
+ inode.mode = mode | S_IFDIR2;
16886
17811
  inode.uid = this.fids[fid].uid;
16887
17812
  inode.gid = gid;
16888
17813
  Marshall(["Q"], [inode.qid], this.replybuffer, 7);
@@ -16913,7 +17838,7 @@ var Virtio9p = class {
16913
17838
  const inode = this.fs.GetInode(idx);
16914
17839
  inode.uid = this.fids[fid].uid;
16915
17840
  inode.gid = gid;
16916
- inode.mode = mode | S_IFREG;
17841
+ inode.mode = mode | S_IFREG2;
16917
17842
  Marshall(
16918
17843
  ["Q", "w"],
16919
17844
  [inode.qid, this.msize - 24],
@@ -17238,7 +18163,7 @@ var Virtio9p = class {
17238
18163
  newname
17239
18164
  );
17240
18165
  if (ret < 0) {
17241
- let error_message = "";
18166
+ let error_message;
17242
18167
  if (ret === -ENOENT)
17243
18168
  error_message = "No such file or directory";
17244
18169
  else if (ret === -EPERM)
@@ -17287,7 +18212,7 @@ var Virtio9p = class {
17287
18212
  }
17288
18213
  const ret = this.fs.Unlink(this.fids[dirfd].inodeid, name);
17289
18214
  if (ret < 0) {
17290
- let error_message = "";
18215
+ let error_message;
17291
18216
  if (ret === -ENOTEMPTY)
17292
18217
  error_message = "Directory not empty";
17293
18218
  else if (ret === -EPERM)
@@ -17861,6 +18786,7 @@ var CPU = class {
17861
18786
  wasm_memory;
17862
18787
  memory_size;
17863
18788
  mem8;
18789
+ mem8_offset = 0;
17864
18790
  mem32s;
17865
18791
  segment_is_null;
17866
18792
  segment_offsets;
@@ -17939,7 +18865,6 @@ var CPU = class {
17939
18865
  get_eflags;
17940
18866
  handle_irqs;
17941
18867
  main_loop;
17942
- reboot_internal;
17943
18868
  set_jit_config;
17944
18869
  read8;
17945
18870
  read16;
@@ -17988,13 +18913,49 @@ var CPU = class {
17988
18913
  this.name = "cpu";
17989
18914
  this.stop_idling = stop_idling;
17990
18915
  this.wm = wm;
18916
+ this.wasm_memory = wm.wasm_memory;
17991
18917
  this.wasm_patch();
17992
18918
  this.create_jit_imports();
17993
- const memory = this.wm.exports["memory"];
17994
- this.wasm_memory = memory;
17995
- this.memory_size = view(Uint32Array, memory, 812, 1);
17996
18919
  this.mem8 = new Uint8Array(0);
17997
18920
  this.mem32s = new Int32Array(this.mem8.buffer);
18921
+ this.rebuild_wasm_views();
18922
+ this.devices = {};
18923
+ this.memory_map_read8 = [];
18924
+ this.memory_map_write8 = [];
18925
+ this.memory_map_read32 = [];
18926
+ this.memory_map_write32 = [];
18927
+ this.bios = {
18928
+ main: null,
18929
+ vga: null
18930
+ };
18931
+ this.fpu_stack_empty[0] = 255;
18932
+ this.fpu_stack_ptr[0] = 0;
18933
+ this.fpu_control_word[0] = 895;
18934
+ this.fpu_status_word[0] = 0;
18935
+ this.fpu_ip[0] = 0;
18936
+ this.fpu_ip_selector[0] = 0;
18937
+ this.fpu_opcode[0] = 0;
18938
+ this.fpu_dp[0] = 0;
18939
+ this.fpu_dp_selector[0] = 0;
18940
+ this.fw_value = [];
18941
+ this.fw_pointer = 0;
18942
+ this.option_roms = [];
18943
+ this.io = void 0;
18944
+ this.bus = bus;
18945
+ this.set_tsc(0, 0);
18946
+ if (false) {
18947
+ this.seen_code = {};
18948
+ this.seen_code_uncompiled = {};
18949
+ }
18950
+ }
18951
+ /**
18952
+ * Rebuild all TypedArray views into WASM linear memory.
18953
+ * Must be called after any wasm_memory.grow() since growth
18954
+ * detaches the old ArrayBuffer, invalidating all views.
18955
+ */
18956
+ rebuild_wasm_views() {
18957
+ const memory = this.wasm_memory;
18958
+ this.memory_size = view(Uint32Array, memory, 812, 1);
17998
18959
  this.segment_is_null = view(Uint8Array, memory, 724, 8);
17999
18960
  this.segment_offsets = view(Int32Array, memory, 736, 8);
18000
18961
  this.segment_limits = view(Uint32Array, memory, 768, 8);
@@ -18023,40 +18984,22 @@ var CPU = class {
18023
18984
  this.last_op1 = view(Int32Array, memory, 104, 1);
18024
18985
  this.last_result = view(Int32Array, memory, 112, 1);
18025
18986
  this.current_tsc = view(Uint32Array, memory, 960, 2);
18026
- this.devices = {};
18027
18987
  this.instruction_pointer = view(Int32Array, memory, 556, 1);
18028
18988
  this.previous_ip = view(Int32Array, memory, 560, 1);
18029
18989
  this.apic_enabled = view(Uint8Array, memory, 548, 1);
18030
18990
  this.acpi_enabled = view(Uint8Array, memory, 552, 1);
18031
- this.memory_map_read8 = [];
18032
- this.memory_map_write8 = [];
18033
- this.memory_map_read32 = [];
18034
- this.memory_map_write32 = [];
18035
- this.bios = {
18036
- main: null,
18037
- vga: null
18038
- };
18039
18991
  this.instruction_counter = view(Uint32Array, memory, 664, 1);
18040
18992
  this.reg32 = view(Int32Array, memory, 64, 8);
18041
18993
  this.fpu_st = view(Int32Array, memory, 1152, 4 * 8);
18042
18994
  this.fpu_stack_empty = view(Uint8Array, memory, 816, 1);
18043
- this.fpu_stack_empty[0] = 255;
18044
18995
  this.fpu_stack_ptr = view(Uint8Array, memory, 1032, 1);
18045
- this.fpu_stack_ptr[0] = 0;
18046
18996
  this.fpu_control_word = view(Uint16Array, memory, 1036, 1);
18047
- this.fpu_control_word[0] = 895;
18048
18997
  this.fpu_status_word = view(Uint16Array, memory, 1040, 1);
18049
- this.fpu_status_word[0] = 0;
18050
18998
  this.fpu_ip = view(Int32Array, memory, 1048, 1);
18051
- this.fpu_ip[0] = 0;
18052
18999
  this.fpu_ip_selector = view(Int32Array, memory, 1052, 1);
18053
- this.fpu_ip_selector[0] = 0;
18054
19000
  this.fpu_opcode = view(Int32Array, memory, 1044, 1);
18055
- this.fpu_opcode[0] = 0;
18056
19001
  this.fpu_dp = view(Int32Array, memory, 1056, 1);
18057
- this.fpu_dp[0] = 0;
18058
19002
  this.fpu_dp_selector = view(Int32Array, memory, 1060, 1);
18059
- this.fpu_dp_selector[0] = 0;
18060
19003
  this.reg_xmm32s = view(Int32Array, memory, 832, 8 * 4);
18061
19004
  this.mxcsr = view(Int32Array, memory, 824, 1);
18062
19005
  this.sreg = view(Uint16Array, memory, 668, 8);
@@ -18064,16 +19007,6 @@ var CPU = class {
18064
19007
  this.reg_pdpte = view(Int32Array, memory, 968, 8);
18065
19008
  this.svga_dirty_bitmap_min_offset = view(Uint32Array, memory, 716, 1);
18066
19009
  this.svga_dirty_bitmap_max_offset = view(Uint32Array, memory, 720, 1);
18067
- this.fw_value = [];
18068
- this.fw_pointer = 0;
18069
- this.option_roms = [];
18070
- this.io = void 0;
18071
- this.bus = bus;
18072
- this.set_tsc(0, 0);
18073
- if (false) {
18074
- this.seen_code = {};
18075
- this.seen_code_uncompiled = {};
18076
- }
18077
19010
  }
18078
19011
  mmap_read8(addr) {
18079
19012
  const value = this.memory_map_read8[addr >>> MMAP_BLOCK_BITS](addr);
@@ -18136,7 +19069,7 @@ var CPU = class {
18136
19069
  }
18137
19070
  create_jit_imports() {
18138
19071
  const jit_imports = /* @__PURE__ */ Object.create(null);
18139
- jit_imports["m"] = this.wm.exports["memory"];
19072
+ jit_imports["m"] = this.wasm_memory;
18140
19073
  for (const name of Object.keys(this.wm.exports)) {
18141
19074
  if (name.startsWith("_") || name.startsWith("zstd") || name.endsWith("_js")) {
18142
19075
  continue;
@@ -18157,7 +19090,6 @@ var CPU = class {
18157
19090
  this.get_eflags = get_import("get_eflags");
18158
19091
  this.handle_irqs = get_import("handle_irqs");
18159
19092
  this.main_loop = get_import("main_loop");
18160
- this.reboot_internal = get_import("reboot_internal");
18161
19093
  this.set_jit_config = get_import("set_jit_config");
18162
19094
  this.read8 = get_import("read8");
18163
19095
  this.read16 = get_import("read16");
@@ -18314,6 +19246,8 @@ var CPU = class {
18314
19246
  state[86] = this.last_result;
18315
19247
  state[87] = this.fpu_status_word;
18316
19248
  state[88] = this.mxcsr;
19249
+ state[89] = this.devices.virtio_mem;
19250
+ state[90] = this.devices.virtio_v86fs;
18317
19251
  return state;
18318
19252
  }
18319
19253
  get_state_pic() {
@@ -18374,8 +19308,33 @@ var CPU = class {
18374
19308
  IOAPIC_STRUCT_SIZE
18375
19309
  );
18376
19310
  }
19311
+ resize_memory(new_size) {
19312
+ const mem8_offset = this.mem8_offset;
19313
+ const needed_total = mem8_offset + new_size;
19314
+ const current_buffer = this.wasm_memory.buffer.byteLength;
19315
+ if (needed_total > current_buffer) {
19316
+ const grow_pages = Math.ceil(
19317
+ (needed_total - current_buffer) / WASM_PAGE_SIZE
19318
+ );
19319
+ this.wasm_memory.grow(grow_pages);
19320
+ this.rebuild_wasm_views();
19321
+ }
19322
+ this.mem8 = view(Uint8Array, this.wasm_memory, mem8_offset, new_size);
19323
+ this.mem32s = view(
19324
+ Int32Array,
19325
+ this.wasm_memory,
19326
+ mem8_offset,
19327
+ new_size >> 2
19328
+ );
19329
+ this.memory_size[0] = new_size;
19330
+ }
18377
19331
  set_state(state) {
18378
- this.memory_size[0] = state[0];
19332
+ const saved_memory_size = state[0];
19333
+ if (saved_memory_size > this.memory_size[0]) {
19334
+ this.resize_memory(saved_memory_size);
19335
+ } else {
19336
+ this.memory_size[0] = saved_memory_size;
19337
+ }
18379
19338
  if (this.mem8.length !== this.memory_size[0]) {
18380
19339
  console.warn(
18381
19340
  "Note: Memory size mismatch. we=" + this.mem8.length + " state=" + this.memory_size[0]
@@ -18473,6 +19432,10 @@ var CPU = class {
18473
19432
  this.devices.virtio_net.set_state(state[83]);
18474
19433
  if (this.devices.virtio_balloon)
18475
19434
  this.devices.virtio_balloon.set_state(state[84]);
19435
+ if (this.devices.virtio_mem && state[89])
19436
+ this.devices.virtio_mem.set_state(state[89]);
19437
+ if (this.devices.virtio_v86fs && state[90])
19438
+ this.devices.virtio_v86fs.set_state(state[90]);
18476
19439
  this.fw_value = state[62];
18477
19440
  if (state[63]) this.set_state_ioapic(state[63]);
18478
19441
  this.tss_size_32[0] = state[64];
@@ -18671,8 +19634,10 @@ var CPU = class {
18671
19634
  this.memory_size[0] === 0,
18672
19635
  "Expected uninitialised memory"
18673
19636
  );
18674
- this.memory_size[0] = size;
18675
19637
  const memory_offset = this.allocate_memory(size);
19638
+ this.rebuild_wasm_views();
19639
+ this.memory_size[0] = size;
19640
+ this.mem8_offset = memory_offset;
18676
19641
  this.mem8 = view(Uint8Array, this.wasm_memory, memory_offset, size);
18677
19642
  this.mem32s = view(
18678
19643
  Uint32Array,
@@ -18883,6 +19848,19 @@ var CPU = class {
18883
19848
  device_bus
18884
19849
  );
18885
19850
  }
19851
+ if (settings.virtio_mem) {
19852
+ const mem_cfg = settings.virtio_mem;
19853
+ this.devices.virtio_mem = new VirtioMem(
19854
+ this,
19855
+ device_bus,
19856
+ mem_cfg.region_addr,
19857
+ mem_cfg.region_size,
19858
+ mem_cfg.block_size
19859
+ );
19860
+ }
19861
+ if (settings.virtio_v86fs) {
19862
+ this.devices.virtio_v86fs = new VirtioV86FS(this, device_bus);
19863
+ }
18886
19864
  this.devices.sb16 = new SB16(this, device_bus);
18887
19865
  }
18888
19866
  if (settings.multiboot) {
@@ -18903,6 +19881,21 @@ var CPU = class {
18903
19881
  }
18904
19882
  }
18905
19883
  this.debug_init();
19884
+ this.rebuild_wasm_views();
19885
+ if (this.mem8_offset > 0) {
19886
+ this.mem8 = view(
19887
+ Uint8Array,
19888
+ this.wasm_memory,
19889
+ this.mem8_offset,
19890
+ this.memory_size[0]
19891
+ );
19892
+ this.mem32s = view(
19893
+ Uint32Array,
19894
+ this.wasm_memory,
19895
+ this.mem8_offset,
19896
+ this.memory_size[0] >> 2
19897
+ );
19898
+ }
18906
19899
  }
18907
19900
  load_multiboot(buffer) {
18908
19901
  if (this.bios.main) {
@@ -19108,7 +20101,6 @@ var CPU = class {
19108
20101
  cpu.write32(multiboot_data + 4, ramdisk_top);
19109
20102
  cpu.write32(multiboot_data + 8, 0);
19110
20103
  cpu.write32(multiboot_data + 12, 0);
19111
- multiboot_data += 16;
19112
20104
  dbg_assert(ramdisk_top < cpu.memory_size[0]);
19113
20105
  cpu.write_blob(new Uint8Array(initrd), ramdisk_address);
19114
20106
  }
@@ -25182,20 +26174,11 @@ var ServerFileStorageWrapper = class {
25182
26174
  };
25183
26175
 
25184
26176
  // src/browser/starter.ts
25185
- var FileExistsError = class {
25186
- message;
25187
- constructor(message) {
25188
- this.message = message || "File already exists";
25189
- }
25190
- };
25191
- FileExistsError.prototype = Error.prototype;
25192
- var FileNotFoundError = class {
25193
- message;
26177
+ var FileNotFoundError = class extends Error {
25194
26178
  constructor(message) {
25195
- this.message = message || "File not found";
26179
+ super(message || "File not found");
25196
26180
  }
25197
26181
  };
25198
- FileNotFoundError.prototype = Error.prototype;
25199
26182
  var V86 = class {
25200
26183
  cpu_is_running = false;
25201
26184
  cpu_exception_hook = function(_n) {
@@ -25223,12 +26206,25 @@ var V86 = class {
25223
26206
  this.bus = bus[0];
25224
26207
  this.emulator_bus = bus[1];
25225
26208
  let cpu;
25226
- let wasm_memory;
26209
+ const memory_size = options.memory_size || 64 * 1024 * 1024;
26210
+ const vga_memory_size = options.vga_memory_size || 8 * 1024 * 1024;
26211
+ const memory_max = options.memory_max || (memory_size + vga_memory_size) * 4;
26212
+ const WASM_PAGE_SIZE2 = 65536;
26213
+ const wasm_initial_pages = 256;
26214
+ const wasm_max_pages = Math.max(
26215
+ wasm_initial_pages,
26216
+ Math.min(Math.ceil(memory_max / WASM_PAGE_SIZE2), 65536)
26217
+ );
26218
+ const wasm_memory = new WebAssembly.Memory({
26219
+ initial: wasm_initial_pages,
26220
+ maximum: wasm_max_pages
26221
+ });
25227
26222
  const wasm_table = new WebAssembly.Table({
25228
26223
  element: "anyfunc",
25229
26224
  initial: WASM_TABLE_SIZE + WASM_TABLE_OFFSET
25230
26225
  });
25231
26226
  const wasm_shared_funcs = {
26227
+ memory: wasm_memory,
25232
26228
  cpu_exception_hook: (n) => this.cpu_exception_hook(n),
25233
26229
  run_hardware_timers: function(a, t) {
25234
26230
  return cpu.run_hardware_timers(a, t);
@@ -25321,9 +26317,10 @@ var V86 = class {
25321
26317
  "v86.wasm",
25322
26318
  "v86-fallback.wasm"
25323
26319
  );
25324
- } else if (typeof window === "undefined" && typeof __dirname === "string") {
25325
- v86_bin = __dirname + "/" + v86_bin;
25326
- v86_bin_fallback = __dirname + "/" + v86_bin_fallback;
26320
+ } else if (typeof window === "undefined") {
26321
+ const root = new URL("../../", import.meta.url).pathname;
26322
+ v86_bin = root + "build/" + v86_bin;
26323
+ v86_bin_fallback = root + "build/" + v86_bin_fallback;
25327
26324
  } else {
25328
26325
  v86_bin = "build/" + v86_bin;
25329
26326
  v86_bin_fallback = "build/" + v86_bin_fallback;
@@ -25362,11 +26359,11 @@ var V86 = class {
25362
26359
  };
25363
26360
  }
25364
26361
  wasm_fn({ env: wasm_shared_funcs }).then((exports) => {
25365
- wasm_memory = exports.memory;
25366
26362
  exports["rust_init"]();
25367
26363
  const emulator = this.v86 = new v86(this.emulator_bus, {
25368
26364
  exports,
25369
- wasm_table
26365
+ wasm_table,
26366
+ wasm_memory
25370
26367
  });
25371
26368
  cpu = emulator.cpu;
25372
26369
  this.continue_init(emulator, options);
@@ -25408,7 +26405,9 @@ var V86 = class {
25408
26405
  settings.mac_address_translation = options.mac_address_translation;
25409
26406
  settings.cpuid_level = options.cpuid_level;
25410
26407
  settings.virtio_balloon = options.virtio_balloon;
26408
+ settings.virtio_mem = options.virtio_mem;
25411
26409
  settings.virtio_console = !!options.virtio_console;
26410
+ settings.virtio_v86fs = !!options.virtio_v86fs;
25412
26411
  const relay_url = options.network_relay_url || options.net_device && options.net_device.relay_url;
25413
26412
  if (relay_url) {
25414
26413
  if (relay_url === "fetch") {
@@ -25886,6 +26885,26 @@ var V86 = class {
25886
26885
  async run() {
25887
26886
  this.v86.run();
25888
26887
  }
26888
+ /**
26889
+ * Grow guest memory to the specified size in bytes. Stops the VM,
26890
+ * grows WASM linear memory, updates memory_size, clears the TLB,
26891
+ * and resumes execution.
26892
+ */
26893
+ async growMemory(newSizeBytes) {
26894
+ const cpu = this.v86.cpu;
26895
+ if (newSizeBytes <= cpu.memory_size[0]) {
26896
+ return;
26897
+ }
26898
+ const wasRunning = this.cpu_is_running;
26899
+ if (wasRunning) {
26900
+ await this.stop();
26901
+ }
26902
+ cpu.resize_memory(newSizeBytes);
26903
+ cpu.full_clear_tlb();
26904
+ if (wasRunning) {
26905
+ await this.run();
26906
+ }
26907
+ }
25889
26908
  /**
25890
26909
  * Stop emulation. Do nothing if emulator is not running. Can be asynchronous.
25891
26910
  */