SandboXP

Форк
0
/
pci.js 
610 строк · 17.7 Кб
1
"use strict";
2

3
// http://wiki.osdev.org/PCI
4

5
var
6
/** @const */ PCI_CONFIG_ADDRESS = 0xCF8,
7
/** @const */ PCI_CONFIG_DATA = 0xCFC;
8

9
/**
10
 * @constructor
11
 * @param {CPU} cpu
12
 */
13
function PCI(cpu)
14
{
15
    this.pci_addr = new Uint8Array(4);
16
    this.pci_value = new Uint8Array(4);
17
    this.pci_response = new Uint8Array(4);
18
    this.pci_status = new Uint8Array(4);
19

20
    this.pci_addr32 = new Int32Array(this.pci_addr.buffer);
21
    this.pci_value32 = new Int32Array(this.pci_value.buffer);
22
    this.pci_response32 = new Int32Array(this.pci_response.buffer);
23
    this.pci_status32 = new Int32Array(this.pci_status.buffer);
24

25
    this.device_spaces = [];
26
    this.devices = [];
27

28
    /** @const @type {CPU} */
29
    this.cpu = cpu;
30

31
    for(var i = 0; i < 256; i++)
32
    {
33
        this.device_spaces[i] = undefined;
34
        this.devices[i] = undefined;
35
    }
36

37
    this.io = cpu.io;
38

39
    cpu.io.register_write(PCI_CONFIG_DATA, this,
40
        function(value)
41
        {
42
            this.pci_write8(this.pci_addr32[0], value);
43
        },
44
        function(value)
45
        {
46
            this.pci_write16(this.pci_addr32[0], value);
47
        },
48
        function(value)
49
        {
50
            this.pci_write32(this.pci_addr32[0], value);
51
        });
52

53
    cpu.io.register_write(PCI_CONFIG_DATA + 1, this,
54
        function(value)
55
        {
56
            this.pci_write8(this.pci_addr32[0] + 1 | 0, value);
57
        });
58

59
    cpu.io.register_write(PCI_CONFIG_DATA + 2, this,
60
        function(value)
61
        {
62
            this.pci_write8(this.pci_addr32[0] + 2 | 0, value);
63
        },
64
        function(value)
65
        {
66
            this.pci_write16(this.pci_addr32[0] + 2 | 0, value);
67
        });
68

69
    cpu.io.register_write(PCI_CONFIG_DATA + 3, this,
70
        function(value)
71
        {
72
            this.pci_write8(this.pci_addr32[0] + 3 | 0, value);
73
        });
74

75
    cpu.io.register_read_consecutive(PCI_CONFIG_DATA, this,
76
        function()
77
        {
78
            return this.pci_response[0];
79
        },
80
        function()
81
        {
82
            return this.pci_response[1];
83
        },
84
        function()
85
        {
86
            return this.pci_response[2];
87
        },
88
        function()
89
        {
90
            return this.pci_response[3];
91
        }
92
    );
93

94
    cpu.io.register_read_consecutive(PCI_CONFIG_ADDRESS, this,
95
        function()
96
        {
97
            return this.pci_status[0];
98
        },
99
        function()
100
        {
101
            return this.pci_status[1];
102
        },
103
        function()
104
        {
105
            return this.pci_status[2];
106
        },
107
        function()
108
        {
109
            return this.pci_status[3];
110
        }
111
    );
112

113
    cpu.io.register_write_consecutive(PCI_CONFIG_ADDRESS, this,
114
        function(out_byte)
115
        {
116
            this.pci_addr[0] = out_byte & 0xFC;
117
        },
118
        function(out_byte)
119
        {
120
            if((this.pci_addr[1] & 0x06) === 0x02 && (out_byte & 0x06) === 0x06)
121
            {
122
                dbg_log("CPU reboot via PCI");
123
                cpu.reboot_internal();
124
                return;
125
            }
126

127
            this.pci_addr[1] = out_byte;
128
        },
129
        function(out_byte)
130
        {
131
            this.pci_addr[2] = out_byte;
132
        },
133
        function(out_byte)
134
        {
135
            this.pci_addr[3] = out_byte;
136
            this.pci_query();
137
        }
138
    );
139

140

141
    // Some experimental PCI devices taken from my PC:
142

143
    // 00:00.0 Host bridge: Intel Corporation 4 Series Chipset DRAM Controller (rev 02)
144
    //var host_bridge = {
145
    //    pci_id: 0,
146
    //    pci_space: [
147
    //        0x86, 0x80, 0x20, 0x2e, 0x06, 0x00, 0x90, 0x20, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
148
    //        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149
    //        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x10, 0xd3, 0x82,
150
    //        0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151
    //    ],
152
    //    pci_bars: [],
153
    //};
154

155
    // This needs to be set in order for seabios to not execute code outside of
156
    // mapped memory. While we map the BIOS into high memory, we don't allow
157
    // executing code there, which enables optimisations in read_imm8.
158
    // See [make_bios_writable_intel] in src/fw/shadow.c in seabios for details
159
    const PAM0 = 0x10;
160

161
    var host_bridge = {
162
        pci_id: 0,
163
        pci_space: [
164
            // 00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
165
            0x86, 0x80, 0x37, 0x12, 0x00, 0x00, 0x00, 0x00,  0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
166
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, PAM0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171
        ],
172
        pci_bars: [],
173
        name: "82441FX PMC",
174
    };
175
    this.register_device(host_bridge);
176

177
    this.isa_bridge = {
178
        pci_id: 1 << 3,
179
        pci_space: [
180
            // 00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
181
            0x86, 0x80, 0x00, 0x70, 0x07, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x80, 0x00,
182
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185
        ],
186
        pci_bars: [],
187
        name: "82371SB PIIX3 ISA",
188
    };
189
    this.isa_bridge_space = this.register_device(this.isa_bridge);
190
    this.isa_bridge_space8 = new Uint8Array(this.isa_bridge_space.buffer);
191

192
    // 00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 90)
193
    //this.register_device([
194
    //    0x86, 0x80, 0x4e, 0x24, 0x07, 0x01, 0x10, 0x00, 0x90, 0x01, 0x04, 0x06, 0x00, 0x00, 0x01, 0x00,
195
    //    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x20, 0xe0, 0xe0, 0x80, 0x22,
196
    //    0xb0, 0xfe, 0xb0, 0xfe, 0xf1, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197
    //    0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x02, 0x00,
198
    //], 0x1e << 3);
199
}
200

201
PCI.prototype.get_state = function()
202
{
203
    var state = [];
204

205
    for(var i = 0; i < 256; i++)
206
    {
207
        state[i] = this.device_spaces[i];
208
    }
209

210
    state[256] = this.pci_addr;
211
    state[257] = this.pci_value;
212
    state[258] = this.pci_response;
213
    state[259] = this.pci_status;
214

215
    return state;
216
};
217

218
PCI.prototype.set_state = function(state)
219
{
220
    for(var i = 0; i < 256; i++)
221
    {
222
        var device = this.devices[i];
223
        var space = state[i];
224

225
        if(!device || !space)
226
        {
227
            if(device)
228
            {
229
                dbg_log("Warning: While restoring PCI device: Device exists in current " +
230
                        "configuration but not in snapshot (" + device.name + ")");
231
            }
232
            if(space)
233
            {
234
                dbg_log("Warning: While restoring PCI device: Device doesn't exist in current " +
235
                        "configuration but does in snapshot (device " + h(i, 2) + ")");
236
            }
237
            continue;
238
        }
239

240
        for(var bar_nr = 0; bar_nr < device.pci_bars.length; bar_nr++)
241
        {
242
            var value = space[(0x10 >> 2) + bar_nr];
243

244
            if(value & 1)
245
            {
246
                var bar = device.pci_bars[bar_nr];
247
                var from = bar.original_bar & ~1 & 0xFFFF;
248
                var to = value & ~1 & 0xFFFF;
249
                this.set_io_bars(bar, from, to);
250
            }
251
            else
252
            {
253
                // memory, cannot be changed
254
            }
255
        }
256

257
        this.device_spaces[i].set(space);
258
    }
259

260
    this.pci_addr.set(state[256]);
261
    this.pci_value.set(state[257]);
262
    this.pci_response.set(state[258]);
263
    this.pci_status.set(state[259]);
264
};
265

266
PCI.prototype.pci_query = function()
267
{
268
    var dbg_line = "query";
269

270
    // Bit | .31                     .0
271
    // Fmt | EBBBBBBBBDDDDDFFFRRRRRR00
272

273
    var bdf = this.pci_addr[2] << 8 | this.pci_addr[1],
274
        addr = this.pci_addr[0] & 0xFC,
275
        //devfn = bdf & 0xFF,
276
        //bus = bdf >> 8,
277
        dev = bdf >> 3 & 0x1F,
278
        //fn = bdf & 7,
279
        enabled = this.pci_addr[3] >> 7;
280

281
    dbg_line += " enabled=" + enabled;
282
    dbg_line += " bdf=" + h(bdf, 4);
283
    dbg_line += " dev=" + h(dev, 2);
284
    dbg_line += " addr=" + h(addr, 2);
285

286
    var device = this.device_spaces[bdf];
287

288
    if(device !== undefined)
289
    {
290
        this.pci_status32[0] = 0x80000000 | 0;
291

292
        if(addr < device.byteLength)
293
        {
294
            this.pci_response32[0] = device[addr >> 2];
295
        }
296
        else
297
        {
298
            // required by freebsd-9.1
299
            this.pci_response32[0] = 0;
300
        }
301

302
        dbg_line += " " + h(this.pci_addr32[0] >>> 0, 8) + " -> " + h(this.pci_response32[0] >>> 0, 8);
303

304
        if(addr >= device.byteLength)
305
        {
306
            dbg_line += " (undef)";
307
        }
308

309
        dbg_line += " (" + this.devices[bdf].name + ")";
310

311
        dbg_log(dbg_line, LOG_PCI);
312
    }
313
    else
314
    {
315
        this.pci_response32[0] = -1;
316
        this.pci_status32[0] = 0;
317
    }
318
};
319

320
PCI.prototype.pci_write8 = function(address, written)
321
{
322
    var bdf = address >> 8 & 0xFFFF;
323
    var addr = address & 0xFF;
324

325
    var space = new Uint8Array(this.device_spaces[bdf].buffer);
326
    var device = this.devices[bdf];
327

328
    if(!space)
329
    {
330
        return;
331
    }
332

333
    dbg_assert(!(addr >= 0x10 && addr < 0x2C || addr >= 0x30 && addr < 0x34),
334
               "PCI: Expected 32-bit write, got 8-bit (addr: " + h(addr) + ")");
335

336
    dbg_log("PCI write8 dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
337
            " value=" + h(written, 2), LOG_PCI);
338

339
    space[addr] = written;
340
};
341

342
PCI.prototype.pci_write16 = function(address, written)
343
{
344
    dbg_assert((address & 1) === 0);
345

346
    var bdf = address >> 8 & 0xFFFF;
347
    var addr = address & 0xFF;
348

349
    var space = new Uint16Array(this.device_spaces[bdf].buffer);
350
    var device = this.devices[bdf];
351

352
    if(!space)
353
    {
354
        return;
355
    }
356

357
    if(addr >= 0x10 && addr < 0x2C)
358
    {
359
        // Bochs bios
360
        dbg_log("Warning: PCI: Expected 32-bit write, got 16-bit (addr: " + h(addr) + ")");
361
        return;
362
    }
363

364
    dbg_assert(!(addr >= 0x30 && addr < 0x34),
365
        "PCI: Expected 32-bit write, got 16-bit (addr: " + h(addr) + ")");
366

367
    dbg_log("PCI writ16 dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
368
            " value=" + h(written, 4), LOG_PCI);
369

370
    space[addr >>> 1] = written;
371
};
372

373
PCI.prototype.pci_write32 = function(address, written)
374
{
375
    dbg_assert((address & 3) === 0);
376

377
    var bdf = address >> 8 & 0xFFFF;
378
    var addr = address & 0xFF;
379

380
    var space = this.device_spaces[bdf];
381
    var device = this.devices[bdf];
382

383
    if(!space)
384
    {
385
        return;
386
    }
387

388
    if(addr >= 0x10 && addr < 0x28)
389
    {
390
        var bar_nr = addr - 0x10 >> 2;
391
        var bar = device.pci_bars[bar_nr];
392

393
        dbg_log("BAR" + bar_nr + " exists=" + (bar ? "y" : "n") + " changed to " +
394
                h(written >>> 0) + " dev=" + h(bdf >> 3, 2) + " (" + device.name + ") ", LOG_PCI);
395

396
        if(bar)
397
        {
398
            dbg_assert(!(bar.size & bar.size - 1), "bar size should be power of 2");
399

400
            var space_addr = addr >> 2;
401
            var type = space[space_addr] & 1;
402

403
            if((written | 3 | bar.size - 1)  === -1) // size check
404
            {
405
                written = ~(bar.size - 1) | type;
406

407
                if(type === 0)
408
                {
409
                    space[space_addr] = written;
410
                }
411
            }
412
            else
413
            {
414
                if(type === 0)
415
                {
416
                    // memory
417
                    var original_bar = bar.original_bar;
418

419
                    if((written & ~0xF) !== (original_bar & ~0xF))
420
                    {
421
                        // seabios
422
                        dbg_log("Warning: Changing memory bar not supported, ignored", LOG_PCI);
423
                    }
424

425
                    // changing isn't supported yet, reset to default
426
                    space[space_addr] = original_bar;
427
                }
428
            }
429

430
            if(type === 1)
431
            {
432
                // io
433
                dbg_assert(type === 1);
434

435
                var from = space[space_addr] & ~1 & 0xFFFF;
436
                var to = written & ~1 & 0xFFFF;
437
                dbg_log("io bar changed from " + h(from >>> 0, 8) +
438
                        " to " + h(to >>> 0, 8) + " size=" + bar.size, LOG_PCI);
439
                this.set_io_bars(bar, from, to);
440
                space[space_addr] = written | 1;
441
            }
442
        }
443
        else
444
        {
445
            space[addr >> 2] = 0;
446
        }
447

448
        dbg_log("BAR effective value: " + h(space[addr >> 2] >>> 0), LOG_PCI);
449
    }
450
    else if(addr === 0x30)
451
    {
452
        dbg_log("PCI write rom address dev=" + h(bdf >> 3, 2) + " (" + device.name + ")" +
453
                " value=" + h(written >>> 0, 8), LOG_PCI);
454

455
        if(device.pci_rom_size)
456
        {
457
            if((written | 0x7FF) === (0xFFFFFFFF|0))
458
            {
459
                space[addr >> 2] = -device.pci_rom_size | 0;
460
            }
461
            else
462
            {
463
                space[addr >> 2] = device.pci_rom_address | 0;
464
            }
465
        }
466
        else
467
        {
468
            space[addr >> 2] = 0;
469
        }
470
    }
471
    else if(addr === 0x04)
472
    {
473
        dbg_log("PCI write dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
474
                " value=" + h(written >>> 0, 8), LOG_PCI);
475
    }
476
    else
477
    {
478
        dbg_log("PCI write dev=" + h(bdf >> 3, 2) + " (" + device.name + ") addr=" + h(addr, 4) +
479
                " value=" + h(written >>> 0, 8), LOG_PCI);
480
        space[addr >>> 2] = written;
481
    }
482
};
483

484
PCI.prototype.register_device = function(device)
485
{
486
    dbg_assert(device.pci_id !== undefined);
487
    dbg_assert(device.pci_space !== undefined);
488
    dbg_assert(device.pci_bars !== undefined);
489

490
    var device_id = device.pci_id;
491

492
    dbg_log("PCI register bdf=" + h(device_id) + " (" + device.name + ")", LOG_PCI);
493

494
    dbg_assert(!this.devices[device_id]);
495
    dbg_assert(device.pci_space.length >= 64);
496
    dbg_assert(device_id < this.devices.length);
497

498
    // convert bytewise notation from lspci to double words
499
    var space = new Int32Array(64);
500
    space.set(new Int32Array(new Uint8Array(device.pci_space).buffer));
501
    this.device_spaces[device_id] = space;
502
    this.devices[device_id] = device;
503

504
    var bar_space = space.slice(4, 10);
505

506
    for(var i = 0; i < device.pci_bars.length; i++)
507
    {
508
        var bar = device.pci_bars[i];
509

510
        if(!bar)
511
        {
512
            continue;
513
        }
514

515
        var bar_base = bar_space[i];
516
        var type = bar_base & 1;
517

518
        bar.original_bar = bar_base;
519
        bar.entries = [];
520

521
        if(type === 0)
522
        {
523
            // memory, not needed currently
524
        }
525
        else
526
        {
527
            dbg_assert(type === 1);
528
            var port = bar_base & ~1;
529

530
            for(var j = 0; j < bar.size; j++)
531
            {
532
                bar.entries[j] = this.io.ports[port + j];
533
            }
534
        }
535
    }
536

537
    return space;
538
};
539

540
PCI.prototype.set_io_bars = function(bar, from, to)
541
{
542
    var count = bar.size;
543
    dbg_log("Move io bars: from=" + h(from) + " to=" + h(to) + " count=" + count, LOG_PCI);
544

545
    var ports = this.io.ports;
546

547
    for(var i = 0; i < count; i++)
548
    {
549
        var old_entry = ports[from + i];
550
        ports[from + i] = this.io.create_empty_entry();
551

552
        if(old_entry.read8 === this.io.empty_port_read8 &&
553
           old_entry.read16 === this.io.empty_port_read16 &&
554
           old_entry.read32 === this.io.empty_port_read32 &&
555
           old_entry.write8 === this.io.empty_port_write &&
556
           old_entry.write16 === this.io.empty_port_write &&
557
           old_entry.write32 === this.io.empty_port_write)
558
        {
559
            dbg_log("Warning: Bad IO bar: Source not mapped, port=" + h(from + i, 4), LOG_PCI);
560
        }
561

562
        var entry = bar.entries[i];
563
        var empty_entry = ports[to + i];
564
        dbg_assert(entry && empty_entry);
565

566
        ports[to + i] = entry;
567

568
        if(empty_entry.read8 === this.io.empty_port_read8 ||
569
            empty_entry.read16 === this.io.empty_port_read16 ||
570
            empty_entry.read32 === this.io.empty_port_read32 ||
571
            empty_entry.write8 === this.io.empty_port_write ||
572
            empty_entry.write16 === this.io.empty_port_write ||
573
            empty_entry.write32 === this.io.empty_port_write)
574
        {
575
            // These can fail if the os maps an io port in multiple bars (indicating a bug)
576
            // XXX: Fails during restore_state
577
            dbg_log("Warning: Bad IO bar: Target already mapped, port=" + h(to + i, 4), LOG_PCI);
578
        }
579
    }
580
};
581

582
PCI.prototype.raise_irq = function(pci_id)
583
{
584
    var space = this.device_spaces[pci_id];
585
    dbg_assert(space);
586

587
    var pin = (space[0x3C >>> 2] >> 8 & 0xFF) - 1;
588
    var device = (pci_id >> 3) - 1 & 0xFF;
589
    var parent_pin = pin + device & 3;
590
    var irq = this.isa_bridge_space8[0x60 + parent_pin];
591

592
    //dbg_log("PCI raise irq " + h(irq) + " dev=" + h(device, 2) +
593
    //        " (" + this.devices[pci_id].name + ")", LOG_PCI);
594
    this.cpu.device_raise_irq(irq);
595
};
596

597
PCI.prototype.lower_irq = function(pci_id)
598
{
599
    var space = this.device_spaces[pci_id];
600
    dbg_assert(space);
601

602
    var pin = space[0x3C >>> 2] >> 8 & 0xFF;
603
    var device = pci_id >> 3 & 0xFF;
604
    var parent_pin = pin + device - 2 & 3;
605
    var irq = this.isa_bridge_space8[0x60 + parent_pin];
606

607
    //dbg_log("PCI lower irq " + h(irq) + " dev=" + h(device, 2) +
608
    //        " (" + this.devices[pci_id].name + ")", LOG_PCI);
609
    this.cpu.device_lower_irq(irq);
610
};
611

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.