@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/src/virtio.ts CHANGED
@@ -520,18 +520,6 @@ export class VirtIO {
520
520
  )
521
521
  }
522
522
 
523
- if (
524
- data &
525
- ~this.device_status &
526
- VIRTIO_STATUS_DRIVER_OK &&
527
- this.device_status &
528
- VIRTIO_STATUS_DEVICE_NEEDS_RESET
529
- ) {
530
- // We couldn't notify NEEDS_RESET earlier because DRIVER_OK was not set.
531
- // Now it has been set, notify now.
532
- this.notify_config_changes()
533
- }
534
-
535
523
  // Don't set FEATURES_OK if our device doesn't support requested features.
536
524
  if (!this.features_ok) {
537
525
  if (DEBUG && data & VIRTIO_STATUS_FEATURES_OK) {
@@ -540,13 +528,17 @@ export class VirtIO {
540
528
  data &= ~VIRTIO_STATUS_FEATURES_OK
541
529
  }
542
530
 
531
+ const prev_status = this.device_status
543
532
  this.device_status = data
544
533
 
545
- if (
546
- data &
547
- ~this.device_status &
548
- VIRTIO_STATUS_DRIVER_OK
549
- ) {
534
+ if (data & ~prev_status & VIRTIO_STATUS_DRIVER_OK) {
535
+ if (
536
+ prev_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET
537
+ ) {
538
+ // We couldn't notify NEEDS_RESET earlier because DRIVER_OK was not set.
539
+ // Now it has been set, notify now.
540
+ this.notify_config_changes()
541
+ }
550
542
  options.on_driver_ok()
551
543
  }
552
544
  },
@@ -837,7 +829,7 @@ export class VirtIO {
837
829
  let cap_next = (this.pci_space[0x34] = 0x40)
838
830
 
839
831
  // Current offset.
840
- let cap_ptr = cap_next
832
+ let cap_ptr
841
833
 
842
834
  for (const cap of capabilities) {
843
835
  const cap_len = VIRTIO_PCI_CAP_LENGTH + cap.extra.length
@@ -0,0 +1,349 @@
1
+ // https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-4500006
2
+
3
+ import { LOG_PCI } from './const.js'
4
+ import { dbg_log } from './log.js'
5
+ import { VirtIO, VIRTIO_F_VERSION_1 } from './virtio.js'
6
+ import * as _marshall from '../lib/marshall.js'
7
+ import { BusConnector } from './bus.js'
8
+
9
+ interface VirtioMemCPU {
10
+ io: {
11
+ register_read(
12
+ port: number,
13
+ device: object,
14
+ r8?: ((port: number) => number) | undefined,
15
+ r16?: ((port: number) => number) | undefined,
16
+ r32?: ((port: number) => number) | undefined,
17
+ ): void
18
+ register_write(
19
+ port: number,
20
+ device: object,
21
+ w8?: ((port: number) => void) | undefined,
22
+ w16?: ((port: number) => void) | undefined,
23
+ w32?: ((port: number) => void) | undefined,
24
+ ): void
25
+ }
26
+ devices: {
27
+ pci: {
28
+ register_device(device: VirtIO): void
29
+ raise_irq(pci_id: number): void
30
+ lower_irq(pci_id: number): void
31
+ }
32
+ }
33
+ read16(addr: number): number
34
+ read32s(addr: number): number
35
+ write16(addr: number, value: number): void
36
+ write32(addr: number, value: number): void
37
+ read_blob(addr: number, length: number): Uint8Array
38
+ write_blob(blob: Uint8Array, addr: number): void
39
+ zero_memory(addr: number, length: number): void
40
+ memory_size: Int32Array
41
+ resize_memory(new_size: number): void
42
+ full_clear_tlb(): void
43
+ }
44
+
45
+ // Request types
46
+ const VIRTIO_MEM_REQ_PLUG = 0
47
+ const VIRTIO_MEM_REQ_UNPLUG = 1
48
+ const _VIRTIO_MEM_REQ_UNPLUG_ALL = 2
49
+ const VIRTIO_MEM_REQ_STATE = 3
50
+
51
+ // Response types
52
+ const VIRTIO_MEM_RESP_ACK = 0
53
+ const VIRTIO_MEM_RESP_NACK = 1
54
+ const _VIRTIO_MEM_RESP_BUSY = 2
55
+ const VIRTIO_MEM_RESP_ERROR = 3
56
+
57
+ // Default block size: 128MB
58
+ const DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024
59
+
60
+ export class VirtioMem {
61
+ bus: BusConnector
62
+ cpu: VirtioMemCPU
63
+ virtio: VirtIO
64
+
65
+ block_size: number
66
+ region_addr: number
67
+ region_size: number
68
+ usable_region_size: number
69
+ plugged_size: number
70
+ requested_size: number
71
+
72
+ constructor(
73
+ cpu: VirtioMemCPU,
74
+ bus: BusConnector,
75
+ region_addr: number,
76
+ region_size: number,
77
+ block_size?: number,
78
+ ) {
79
+ this.bus = bus
80
+ this.cpu = cpu
81
+ this.block_size = block_size || DEFAULT_BLOCK_SIZE
82
+ this.region_addr = region_addr
83
+ this.region_size = region_size
84
+ this.usable_region_size = region_size
85
+ this.plugged_size = 0
86
+ this.requested_size = 0
87
+
88
+ const queues = [{ size_supported: 32, notify_offset: 0 }]
89
+
90
+ this.virtio = new VirtIO(cpu, {
91
+ name: 'virtio-mem',
92
+ pci_id: 0x0d << 3,
93
+ device_id: 0x1058,
94
+ subsystem_device_id: 24,
95
+ common: {
96
+ initial_port: 0xe800,
97
+ queues,
98
+ features: [VIRTIO_F_VERSION_1],
99
+ on_driver_ok: () => {
100
+ dbg_log('virtio-mem setup', LOG_PCI)
101
+ },
102
+ },
103
+ notification: {
104
+ initial_port: 0xe900,
105
+ single_handler: false,
106
+ handlers: [
107
+ (queue_id: number) => {
108
+ this.handle_request(queue_id)
109
+ },
110
+ ],
111
+ },
112
+ isr_status: {
113
+ initial_port: 0xe700,
114
+ },
115
+ device_specific: {
116
+ initial_port: 0xe600,
117
+ struct: [
118
+ // block_size low
119
+ {
120
+ bytes: 4,
121
+ name: 'block_size_low',
122
+ read: () => this.block_size >>> 0,
123
+ write: () => {},
124
+ },
125
+ // block_size high
126
+ {
127
+ bytes: 4,
128
+ name: 'block_size_high',
129
+ read: () => 0,
130
+ write: () => {},
131
+ },
132
+ // node_id (u16 padded to u32)
133
+ {
134
+ bytes: 2,
135
+ name: 'node_id',
136
+ read: () => 0,
137
+ write: () => {},
138
+ },
139
+ // padding (6 bytes as 2+4)
140
+ {
141
+ bytes: 2,
142
+ name: 'padding0',
143
+ read: () => 0,
144
+ write: () => {},
145
+ },
146
+ {
147
+ bytes: 4,
148
+ name: 'padding1',
149
+ read: () => 0,
150
+ write: () => {},
151
+ },
152
+ // addr low
153
+ {
154
+ bytes: 4,
155
+ name: 'addr_low',
156
+ read: () => this.region_addr >>> 0,
157
+ write: () => {},
158
+ },
159
+ // addr high
160
+ {
161
+ bytes: 4,
162
+ name: 'addr_high',
163
+ read: () => 0,
164
+ write: () => {},
165
+ },
166
+ // region_size low
167
+ {
168
+ bytes: 4,
169
+ name: 'region_size_low',
170
+ read: () => this.region_size >>> 0,
171
+ write: () => {},
172
+ },
173
+ // region_size high
174
+ {
175
+ bytes: 4,
176
+ name: 'region_size_high',
177
+ read: () => 0,
178
+ write: () => {},
179
+ },
180
+ // usable_region_size low
181
+ {
182
+ bytes: 4,
183
+ name: 'usable_region_size_low',
184
+ read: () => this.usable_region_size >>> 0,
185
+ write: () => {},
186
+ },
187
+ // usable_region_size high
188
+ {
189
+ bytes: 4,
190
+ name: 'usable_region_size_high',
191
+ read: () => 0,
192
+ write: () => {},
193
+ },
194
+ // plugged_size low
195
+ {
196
+ bytes: 4,
197
+ name: 'plugged_size_low',
198
+ read: () => this.plugged_size >>> 0,
199
+ write: () => {},
200
+ },
201
+ // plugged_size high
202
+ {
203
+ bytes: 4,
204
+ name: 'plugged_size_high',
205
+ read: () => 0,
206
+ write: () => {},
207
+ },
208
+ // requested_size low
209
+ {
210
+ bytes: 4,
211
+ name: 'requested_size_low',
212
+ read: () => this.requested_size >>> 0,
213
+ write: () => {},
214
+ },
215
+ // requested_size high
216
+ {
217
+ bytes: 4,
218
+ name: 'requested_size_high',
219
+ read: () => 0,
220
+ write: () => {},
221
+ },
222
+ ],
223
+ },
224
+ })
225
+ }
226
+
227
+ handle_request(queue_id: number): void {
228
+ const queue = this.virtio.queues[queue_id]
229
+ while (queue.has_request()) {
230
+ const bufchain = queue.pop_request()
231
+ const request = new Uint8Array(bufchain.length_readable)
232
+ bufchain.get_next_blob(request)
233
+
234
+ const type = request[0] | (request[1] << 8)
235
+ const resp = new Uint8Array(8)
236
+
237
+ let resp_type = VIRTIO_MEM_RESP_ERROR
238
+ switch (type) {
239
+ case VIRTIO_MEM_REQ_PLUG:
240
+ resp_type = this.handle_plug(request)
241
+ break
242
+ case VIRTIO_MEM_REQ_UNPLUG:
243
+ resp_type = VIRTIO_MEM_RESP_NACK
244
+ break
245
+ case VIRTIO_MEM_REQ_STATE:
246
+ resp_type = this.handle_state(request, resp)
247
+ break
248
+ default:
249
+ dbg_log('virtio-mem: unknown request type ' + type, LOG_PCI)
250
+ }
251
+
252
+ resp[0] = resp_type & 0xff
253
+ resp[1] = (resp_type >> 8) & 0xff
254
+
255
+ bufchain.set_next_blob(resp)
256
+ queue.push_reply(bufchain)
257
+ }
258
+ queue.flush_replies()
259
+ }
260
+
261
+ handle_plug(request: Uint8Array): number {
262
+ // Request layout after type (8 bytes header):
263
+ // addr: u64 at offset 8
264
+ // nb_blocks: u16 at offset 16
265
+ const addr =
266
+ (request[8] |
267
+ (request[9] << 8) |
268
+ (request[10] << 16) |
269
+ (request[11] << 24)) >>>
270
+ 0
271
+ const nb_blocks = request[16] | (request[17] << 8)
272
+
273
+ const size = nb_blocks * this.block_size
274
+ const new_plugged = this.plugged_size + size
275
+
276
+ if (new_plugged > this.usable_region_size) {
277
+ return VIRTIO_MEM_RESP_NACK
278
+ }
279
+
280
+ // Grow guest memory to accommodate the plug
281
+ const new_memory_size = this.cpu.memory_size[0] + size
282
+ this.cpu.resize_memory(new_memory_size)
283
+ this.cpu.full_clear_tlb()
284
+ this.plugged_size = new_plugged
285
+
286
+ dbg_log(
287
+ 'virtio-mem: plugged ' +
288
+ nb_blocks +
289
+ ' blocks at 0x' +
290
+ addr.toString(16) +
291
+ ' (' +
292
+ (size >> 20) +
293
+ 'MB)',
294
+ LOG_PCI,
295
+ )
296
+
297
+ return VIRTIO_MEM_RESP_ACK
298
+ }
299
+
300
+ handle_state(request: Uint8Array, resp: Uint8Array): number {
301
+ const addr =
302
+ (request[8] |
303
+ (request[9] << 8) |
304
+ (request[10] << 16) |
305
+ (request[11] << 24)) >>>
306
+ 0
307
+ const nb_blocks = request[16] | (request[17] << 8)
308
+
309
+ // Report all requested blocks as plugged if within plugged_size
310
+ const offset = addr - this.region_addr
311
+ const end = offset + nb_blocks * this.block_size
312
+ const all_plugged = end <= this.plugged_size ? 1 : 0
313
+
314
+ // State response: count field at offset 8
315
+ resp[8] = all_plugged & 0xff
316
+ resp[9] = (all_plugged >> 8) & 0xff
317
+
318
+ return VIRTIO_MEM_RESP_ACK
319
+ }
320
+
321
+ set_requested_size(size: number): void {
322
+ this.requested_size = size
323
+ if (this.virtio.device_status & 4) {
324
+ this.virtio.notify_config_changes()
325
+ }
326
+ }
327
+
328
+ get_state(): any[] {
329
+ const state: any[] = []
330
+ state[0] = this.virtio
331
+ state[1] = this.block_size
332
+ state[2] = this.region_addr
333
+ state[3] = this.region_size
334
+ state[4] = this.usable_region_size
335
+ state[5] = this.plugged_size
336
+ state[6] = this.requested_size
337
+ return state
338
+ }
339
+
340
+ set_state(state: any[]): void {
341
+ this.virtio.set_state(state[0])
342
+ this.block_size = state[1]
343
+ this.region_addr = state[2]
344
+ this.region_size = state[3]
345
+ this.usable_region_size = state[4]
346
+ this.plugged_size = state[5]
347
+ this.requested_size = state[6]
348
+ }
349
+ }