@aptre/v86 0.5.0 → 0.6.0

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.
@@ -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
+ }