More complete APIC and IOAPIC implementation (#86)
This commit is contained in:
parent
9e4b2c5ace
commit
96f61bb4d5
2
Makefile
2
Makefile
|
@ -74,7 +74,7 @@ TRANSPILE_ES6_FLAGS=\
|
|||
|
||||
|
||||
CORE_FILES=src/const.js src/io.js src/main.js src/lib.js src/fpu.js src/ide.js src/pci.js src/floppy.js src/memory.js\
|
||||
src/dma.js src/pit.js src/vga.js src/ps2.js src/pic.js src/rtc.js src/uart.js src/hpet.js src/acpi.js src/apic.js\
|
||||
src/dma.js src/pit.js src/vga.js src/ps2.js src/pic.js src/rtc.js src/uart.js src/hpet.js src/acpi.js src/apic.js src/ioapic.js\
|
||||
src/state.js src/ne2k.js src/virtio.js src/bus.js src/log.js src/config.js\
|
||||
src/cpu.js src/translate.js src/modrm.js src/string.js src/arith.js src/misc_instr.js src/instructions.js src/debug.js \
|
||||
src/elf.js
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
var CORE_FILES =
|
||||
"const.js config.js log.js cpu.js debug.js translate.js modrm.js string.js arith.js misc_instr.js instructions.js " +
|
||||
"io.js main.js lib.js ide.js fpu.js pci.js floppy.js " +
|
||||
"memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js acpi.js apic.js hpet.js " +
|
||||
"memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js acpi.js apic.js ioapic.js hpet.js " +
|
||||
"ne2k.js state.js virtio.js bus.js elf.js";
|
||||
|
||||
var BROWSER_FILES = "main.js screen.js keyboard.js mouse.js serial.js lib.js network.js starter.js worker_bus.js";
|
||||
|
|
859
src/apic.js
859
src/apic.js
|
@ -1,27 +1,14 @@
|
|||
"use strict";
|
||||
|
||||
// See Intel's System Programming Guide
|
||||
|
||||
|
||||
/** @const */
|
||||
var APIC_LOG_VERBOSE = false;
|
||||
|
||||
/** @const */
|
||||
var APIC_ADDRESS = 0xFEE00000;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_ADDRESS = 0xFEC00000;
|
||||
|
||||
/** @const */
|
||||
var IOREGSEL = 0;
|
||||
|
||||
/** @const */
|
||||
var IOWIN = 0x10;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_IRQ_COUNT = 24;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_ID = 0; // must match value in seabios
|
||||
|
||||
/** @const */
|
||||
var APIC_TIMER_FREQ = 1 * 1024;
|
||||
|
||||
/** @const */
|
||||
var APIC_TIMER_MODE_MASK = 3 << 17;
|
||||
|
||||
|
@ -35,225 +22,348 @@ var APIC_TIMER_MODE_PERIODIC = 1 << 17;
|
|||
var APIC_TIMER_MODE_TSC = 2 << 17;
|
||||
|
||||
|
||||
/** @const */
|
||||
var DELIVERY_MODES = [
|
||||
"Fixed (0)",
|
||||
"Lowest Prio (1)",
|
||||
"SMI (2)",
|
||||
"Reserved (3)",
|
||||
"NMI (4)",
|
||||
"INIT (5)",
|
||||
"Reserved (6)",
|
||||
"ExtINT (7)",
|
||||
];
|
||||
|
||||
/** @const */
|
||||
var DESTINATION_MODES = ["physical", "logical"];
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {CPU} cpu
|
||||
*/
|
||||
function APIC(cpu)
|
||||
{
|
||||
/** @type {CPU} */
|
||||
this.cpu = cpu;
|
||||
|
||||
var io = cpu.io;
|
||||
|
||||
this.ioredtlb = new Int32Array(0x10 + IOAPIC_IRQ_COUNT * 2);
|
||||
|
||||
for(var i = 0; i < this.ioredtlb.length; i += 2)
|
||||
{
|
||||
// disable interrupts
|
||||
this.ioredtlb[i] = 1 << 16;
|
||||
}
|
||||
this.apic_id = 0;
|
||||
|
||||
this.timer_divider = 0;
|
||||
this.timer_divider_shift = 1;
|
||||
this.timer_initial_count = 0;
|
||||
this.timer_current_count = 0;
|
||||
this.lvt_timer = 0x10000;
|
||||
|
||||
this.next_tick = v86.microtick();
|
||||
|
||||
this.irqs = [];
|
||||
this.irqs_set = 0;
|
||||
this.ioapic_id = IOAPIC_ID;
|
||||
this.lvt_timer = IOAPIC_CONFIG_MASKED;
|
||||
this.lvt_perf_counter = IOAPIC_CONFIG_MASKED;
|
||||
this.lvt_int0 = IOAPIC_CONFIG_MASKED;
|
||||
this.lvt_int1 = IOAPIC_CONFIG_MASKED;
|
||||
this.lvt_error = IOAPIC_CONFIG_MASKED;
|
||||
|
||||
this.lapic_tpr = 0;
|
||||
this.lapic_icr = new Int32Array(2);
|
||||
this.tpr = 0;
|
||||
this.icr0 = 0
|
||||
this.icr1 = 0
|
||||
|
||||
io.mmap_register(APIC_ADDRESS, 0x100000,
|
||||
function(addr)
|
||||
this.irr = new Int32Array(8);
|
||||
this.isr = new Int32Array(8);
|
||||
this.tmr = new Int32Array(8);
|
||||
|
||||
this.spurious_vector = 0xFE;
|
||||
this.destination_format = -1;
|
||||
this.local_destination = 0;
|
||||
|
||||
this.error = 0;
|
||||
this.read_error = 0;
|
||||
|
||||
cpu.io.mmap_register(APIC_ADDRESS, 0x100000,
|
||||
(addr) =>
|
||||
{
|
||||
//dbg_trace();
|
||||
dbg_log("unsupported read8 from apic: " + h(addr >>> 0), LOG_APIC);
|
||||
dbg_log("Unsupported read8 from apic: " + h(addr >>> 0), LOG_APIC);
|
||||
var off = addr & 3;
|
||||
addr &= ~3;
|
||||
return read32(addr) >> (off * 8) & 0xFF;
|
||||
return this.read32(addr) >> (off * 8) & 0xFF;
|
||||
},
|
||||
function(addr, value)
|
||||
(addr, value) =>
|
||||
{
|
||||
dbg_log("Unsupported write8 from apic: " + h(addr) + " <- " + h(value), LOG_APIC);
|
||||
dbg_trace();
|
||||
dbg_log("unsupported write8 from apic: " + h(addr) + " <- " + h(value), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
},
|
||||
read32,
|
||||
function(addr, value)
|
||||
{
|
||||
addr = addr - APIC_ADDRESS | 0;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case 0x80:
|
||||
// tpr
|
||||
me.lapic_tpr = value;
|
||||
break;
|
||||
|
||||
case 0xb0:
|
||||
//dbg_log("APIC write eoi: " + h(value, 8), LOG_APIC);
|
||||
break;
|
||||
|
||||
case 0x300:
|
||||
var vector = value & 0xFF;
|
||||
var delivery_mode = value >> 8 & 7;
|
||||
var destination_mode = value >> 11 & 1;
|
||||
var destination_shorthand = value >> 18 & 3;
|
||||
var destination = me.lapic_icr[1] >>> 24;
|
||||
dbg_log("APIC write icr0: " + h(value, 8) + " vector=" + h(vector, 2) + " " +
|
||||
"destination_mode=" + destination_mode + " delivery_mode=" + delivery_mode + " " +
|
||||
"destination_shorthand=" + ["no", "self", "all with self", "all without self"][destination_shorthand], LOG_APIC);
|
||||
|
||||
//value |= 0x1000;
|
||||
me.lapic_icr[0] = value;
|
||||
if(delivery_mode === 0 || delivery_mode === 1)
|
||||
{
|
||||
me.do_irq(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x310:
|
||||
dbg_log("APIC write icr1: " + h(value, 8), LOG_APIC);
|
||||
me.lapic_icr[1] = value;
|
||||
break;
|
||||
|
||||
case 0x320:
|
||||
dbg_log("timer lvt: " + h(value >>> 0, 8), LOG_APIC);
|
||||
me.lvt_timer = value;
|
||||
break;
|
||||
|
||||
case 0x3E0:
|
||||
dbg_log("timer divider: " + h(value >>> 0, 8), LOG_APIC);
|
||||
me.timer_divider = value;
|
||||
|
||||
var divide_shift = value & 0b11 | (value & 0b1000) >> 1;
|
||||
me.timer_divider_shift = divide_shift === 7 ? 0 : divide_shift + 1;
|
||||
break;
|
||||
|
||||
case 0x380:
|
||||
//dbg_log("timer initial: " + h(value >>> 0, 8), LOG_APIC);
|
||||
me.timer_initial_count = value >>> 0;
|
||||
me.timer_current_count = value >>> 0;
|
||||
me.next_tick = v86.microtick();
|
||||
break;
|
||||
|
||||
case 0x390:
|
||||
dbg_log("timer current: " + h(value >>> 0, 8), LOG_APIC);
|
||||
me.timer_current_count = value >>> 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_log("APIC write32 " + h(addr) + " <- " + h(value >>> 0, 8), LOG_APIC);
|
||||
}
|
||||
});
|
||||
|
||||
function read32(addr)
|
||||
{
|
||||
addr = addr - APIC_ADDRESS | 0;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case 0x30:
|
||||
// version
|
||||
dbg_log("APIC read version", LOG_APIC);
|
||||
return 0x50011
|
||||
|
||||
case 0x80:
|
||||
// tpr
|
||||
//dbg_log("APIC read tpr", LOG_APIC);
|
||||
return me.lapic_tpr;
|
||||
|
||||
case 0x300:
|
||||
dbg_log("APIC read icr0", LOG_APIC);
|
||||
return me.lapic_icr[0];
|
||||
|
||||
case 0x310:
|
||||
dbg_log("APIC read icr1", LOG_APIC);
|
||||
return me.lapic_icr[1];
|
||||
|
||||
case 0x320:
|
||||
dbg_log("read timer lvt", LOG_APIC);
|
||||
return me.lvt_timer;
|
||||
|
||||
case 0x3E0:
|
||||
// divider
|
||||
dbg_log("read timer divider", LOG_APIC);
|
||||
return me.timer_divider;
|
||||
|
||||
case 0x380:
|
||||
dbg_log("read timer initial count", LOG_APIC);
|
||||
return me.timer_initial_count;
|
||||
|
||||
case 0x390:
|
||||
//dbg_log("read timer current count: " + h(me.timer_current_count >>> 0, 8), LOG_APIC);
|
||||
return me.timer_current_count;
|
||||
|
||||
default:
|
||||
dbg_log("APIC read " + h(addr), LOG_APIC);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
this.iotable = new Int32Array(IOAPIC_IRQ_COUNT * 2);
|
||||
|
||||
// IOAPIC register selection
|
||||
var ioregsel = 0;
|
||||
|
||||
var me = this;
|
||||
|
||||
dbg_assert(MMAP_BLOCK_SIZE >= 0x20);
|
||||
|
||||
io.mmap_register(IOAPIC_ADDRESS, MMAP_BLOCK_SIZE,
|
||||
function(addr)
|
||||
{
|
||||
dbg_assert(false, "unsupported read8 from ioapic: " + h(addr));
|
||||
return 0;
|
||||
},
|
||||
function(addr, value)
|
||||
{
|
||||
dbg_assert(false, "unsupported write8 from ioapic: " + h(addr));
|
||||
},
|
||||
function(addr)
|
||||
{
|
||||
addr = addr - IOAPIC_ADDRESS | 0;
|
||||
|
||||
if(addr === IOREGSEL)
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register read ioregsel", LOG_APIC);
|
||||
}
|
||||
else if(addr === IOWIN)
|
||||
{
|
||||
dbg_log("IOAPIC read32 " + h(ioregsel), LOG_APIC);
|
||||
return me.ioapic_read(ioregsel);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register read: " + h(addr), LOG_APIC);
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
function(addr, value)
|
||||
{
|
||||
addr = addr - IOAPIC_ADDRESS | 0;
|
||||
|
||||
if(addr === IOREGSEL)
|
||||
{
|
||||
ioregsel = value;
|
||||
}
|
||||
else if(addr === IOWIN)
|
||||
{
|
||||
me.ioapic_write(ioregsel, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register write: " + h(addr) + " <- " + h(value >>> 0, 8), LOG_APIC);
|
||||
}
|
||||
});
|
||||
(addr) => this.read32(addr),
|
||||
(addr, value) => this.write32(addr, value)
|
||||
);
|
||||
}
|
||||
|
||||
APIC.prototype.read32 = function(addr)
|
||||
{
|
||||
addr = addr - APIC_ADDRESS | 0;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case 0x20:
|
||||
dbg_log("APIC read id", LOG_APIC);
|
||||
return this.apic_id;
|
||||
|
||||
case 0x30:
|
||||
// version
|
||||
dbg_log("APIC read version", LOG_APIC);
|
||||
return 0x50014;
|
||||
|
||||
case 0x80:
|
||||
APIC_LOG_VERBOSE && dbg_log("APIC read tpr", LOG_APIC);
|
||||
return this.tpr;
|
||||
|
||||
case 0xD0:
|
||||
dbg_log("Read local destination", LOG_APIC);
|
||||
return this.local_destination;
|
||||
|
||||
case 0xE0:
|
||||
dbg_log("Read destination format", LOG_APIC);
|
||||
return this.destination_format;
|
||||
|
||||
case 0xF0:
|
||||
return this.spurious_vector;
|
||||
|
||||
case 0x100:
|
||||
case 0x110:
|
||||
case 0x120:
|
||||
case 0x130:
|
||||
case 0x140:
|
||||
case 0x150:
|
||||
case 0x160:
|
||||
case 0x170:
|
||||
var index = addr - 0x100 >> 4;
|
||||
dbg_log("Read isr " + index + ": " + h(this.isr[index] >>> 0, 8), LOG_APIC);
|
||||
return this.isr[index];
|
||||
|
||||
case 0x180:
|
||||
case 0x190:
|
||||
case 0x1A0:
|
||||
case 0x1B0:
|
||||
case 0x1C0:
|
||||
case 0x1D0:
|
||||
case 0x1E0:
|
||||
case 0x1F0:
|
||||
var index = addr - 0x180 >> 4;
|
||||
dbg_log("Read tmr " + index + ": " + h(this.tmr[index] >>> 0, 8), LOG_APIC);
|
||||
return this.tmr[index];
|
||||
|
||||
case 0x200:
|
||||
case 0x210:
|
||||
case 0x220:
|
||||
case 0x230:
|
||||
case 0x240:
|
||||
case 0x250:
|
||||
case 0x260:
|
||||
case 0x270:
|
||||
var index = addr - 0x200 >> 4;
|
||||
dbg_log("Read irr " + index + ": " + h(this.irr[index] >>> 0, 8), LOG_APIC);
|
||||
return this.irr[index];
|
||||
|
||||
case 0x280:
|
||||
dbg_log("Read error: " + h(this.read_error >>> 0, 8), LOG_APIC);
|
||||
return this.read_error;
|
||||
|
||||
case 0x300:
|
||||
APIC_LOG_VERBOSE && dbg_log("APIC read icr0", LOG_APIC);
|
||||
return this.icr0;
|
||||
|
||||
case 0x310:
|
||||
dbg_log("APIC read icr1", LOG_APIC);
|
||||
return this.icr1;
|
||||
|
||||
case 0x320:
|
||||
dbg_log("read timer lvt", LOG_APIC);
|
||||
return this.lvt_timer;
|
||||
|
||||
case 0x340:
|
||||
dbg_log("read lvt perf counter", LOG_APIC);
|
||||
return this.lvt_perf_counter;
|
||||
|
||||
case 0x350:
|
||||
dbg_log("read lvt int0", LOG_APIC);
|
||||
return this.lvt_int0;
|
||||
|
||||
case 0x360:
|
||||
dbg_log("read lvt int1", LOG_APIC);
|
||||
return this.lvt_int1;
|
||||
|
||||
case 0x370:
|
||||
dbg_log("read lvt error", LOG_APIC);
|
||||
return this.lvt_error;
|
||||
|
||||
case 0x3E0:
|
||||
// divider
|
||||
dbg_log("read timer divider", LOG_APIC);
|
||||
return this.timer_divider;
|
||||
|
||||
case 0x380:
|
||||
dbg_log("read timer initial count", LOG_APIC);
|
||||
return this.timer_initial_count;
|
||||
|
||||
case 0x390:
|
||||
dbg_log("read timer current count: " + h(this.timer_current_count >>> 0, 8), LOG_APIC);
|
||||
return this.timer_current_count;
|
||||
|
||||
default:
|
||||
dbg_log("APIC read " + h(addr), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
APIC.prototype.write32 = function(addr, value)
|
||||
{
|
||||
addr = addr - APIC_ADDRESS | 0;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case 0x80:
|
||||
APIC_LOG_VERBOSE && dbg_log("Set tpr: " + h(value & 0xFF, 2), LOG_APIC);
|
||||
this.tpr = value & 0xFF;
|
||||
this.check_vector();
|
||||
break;
|
||||
|
||||
case 0xB0:
|
||||
var highest_isr = this.highest_isr();
|
||||
if(highest_isr !== -1)
|
||||
{
|
||||
APIC_LOG_VERBOSE && dbg_log("eoi: " + h(value >>> 0, 8) + " for vector " + h(highest_isr), LOG_APIC);
|
||||
this.register_clear_bit(this.isr, highest_isr);
|
||||
if(this.register_get_bit(this.tmr, highest_isr))
|
||||
{
|
||||
// Send eoi to all IO APICs
|
||||
this.cpu.devices.ioapic.remote_eoi(highest_isr);
|
||||
}
|
||||
this.check_vector();
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Bad eoi: No isr set", LOG_APIC);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xD0:
|
||||
dbg_log("Set local destination: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.local_destination = value & 0xFF000000;
|
||||
break;
|
||||
|
||||
case 0xE0:
|
||||
dbg_log("Set destination format: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.destination_format = value | 0xFFFFFF;
|
||||
break;
|
||||
|
||||
case 0xF0:
|
||||
dbg_log("Set spurious vector: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.spurious_vector = value;
|
||||
break;
|
||||
|
||||
case 0x280:
|
||||
// updated readable error register with real error
|
||||
dbg_log("Write error: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.read_error = this.error;
|
||||
this.error = 0;
|
||||
break;
|
||||
|
||||
case 0x300:
|
||||
var vector = value & 0xFF;
|
||||
var delivery_mode = value >> 8 & 7;
|
||||
var destination_mode = value >> 11 & 1;
|
||||
var is_level = value >> 15 & 1;
|
||||
var destination_shorthand = value >> 18 & 3;
|
||||
var destination = this.icr1 >>> 24;
|
||||
dbg_log("APIC write icr0: " + h(value, 8) + " vector=" + h(vector, 2) + " " +
|
||||
"destination_mode=" + DESTINATION_MODES[destination_mode] + " delivery_mode=" + DELIVERY_MODES[delivery_mode] + " " +
|
||||
"destination_shorthand=" + ["no", "self", "all with self", "all without self"][destination_shorthand], LOG_APIC);
|
||||
|
||||
value &= ~(1 << 12);
|
||||
this.icr0 = value;
|
||||
|
||||
if(destination_shorthand === 0)
|
||||
{
|
||||
// no shorthand
|
||||
this.route(vector, delivery_mode, is_level, destination, destination_mode);
|
||||
}
|
||||
else if(destination_shorthand === 1)
|
||||
{
|
||||
// self
|
||||
this.deliver(vector, IOAPIC_DELIVERY_FIXED, is_level);
|
||||
}
|
||||
else if(destination_shorthand === 2)
|
||||
{
|
||||
// all including self
|
||||
this.deliver(vector, delivery_mode, is_level);
|
||||
}
|
||||
else if(destination_shorthand === 3)
|
||||
{
|
||||
// all but self
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x310:
|
||||
dbg_log("APIC write icr1: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.icr1 = value;
|
||||
break;
|
||||
|
||||
case 0x320:
|
||||
dbg_log("timer lvt: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.lvt_timer = value;
|
||||
break;
|
||||
|
||||
case 0x340:
|
||||
dbg_log("lvt perf counter: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.lvt_perf_counter = value;
|
||||
break;
|
||||
|
||||
case 0x350:
|
||||
dbg_log("lvt int0: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.lvt_int0 = value;
|
||||
break;
|
||||
|
||||
case 0x360:
|
||||
dbg_log("lvt int1: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.lvt_int1 = value;
|
||||
break;
|
||||
|
||||
case 0x370:
|
||||
dbg_log("lvt error: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.lvt_error = value;
|
||||
break;
|
||||
|
||||
case 0x3E0:
|
||||
dbg_log("timer divider: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.timer_divider = value;
|
||||
|
||||
var divide_shift = value & 0b11 | (value & 0b1000) >> 1;
|
||||
this.timer_divider_shift = divide_shift === 0b111 ? 0 : divide_shift + 1;
|
||||
break;
|
||||
|
||||
case 0x380:
|
||||
dbg_log("timer initial: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.timer_initial_count = value >>> 0;
|
||||
this.timer_current_count = value >>> 0;
|
||||
|
||||
this.next_tick = v86.microtick();
|
||||
this.timer_active = true;
|
||||
break;
|
||||
|
||||
case 0x390:
|
||||
dbg_log("timer current: " + h(value >>> 0, 8), LOG_APIC);
|
||||
dbg_assert(false, "read-only register");
|
||||
break;
|
||||
|
||||
default:
|
||||
dbg_log("APIC write32 " + h(addr) + " <- " + h(value >>> 0, 8), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
APIC.prototype.timer = function(now)
|
||||
{
|
||||
if(this.timer_current_count === 0)
|
||||
|
@ -272,156 +382,243 @@ APIC.prototype.timer = function(now)
|
|||
this.next_tick += steps / APIC_TIMER_FREQ * (1 << this.timer_divider_shift);
|
||||
|
||||
this.timer_current_count -= steps;
|
||||
var mode = this.lvt_timer & APIC_TIMER_MODE_MASK;
|
||||
|
||||
if(mode === APIC_TIMER_MODE_PERIODIC)
|
||||
if(this.timer_current_count <= 0)
|
||||
{
|
||||
while(this.timer_current_count <= 0)
|
||||
var mode = this.lvt_timer & APIC_TIMER_MODE_MASK;
|
||||
|
||||
if(mode === APIC_TIMER_MODE_PERIODIC)
|
||||
{
|
||||
this.timer_current_count = this.timer_initial_count + this.timer_current_count % this.timer_initial_count;
|
||||
}
|
||||
// This isn't exact, because timer_current_count might already be
|
||||
// negative at this point since timer() fires late
|
||||
this.timer_current_count = this.timer_initial_count;
|
||||
|
||||
this.do_irq(this.lvt_timer);
|
||||
}
|
||||
else if(mode === APIC_TIMER_MODE_ONE_SHOT)
|
||||
{
|
||||
if(this.timer_current_count < 0)
|
||||
if((this.lvt_timer & IOAPIC_CONFIG_MASKED) === 0)
|
||||
{
|
||||
this.deliver(this.lvt_timer & 0xFF, IOAPIC_DELIVERY_FIXED, false);
|
||||
}
|
||||
}
|
||||
else if(mode === APIC_TIMER_MODE_ONE_SHOT)
|
||||
{
|
||||
this.timer_current_count = 0;
|
||||
dbg_log("XXX one shot end");
|
||||
dbg_log("APIC timer one shot end", LOG_APIC);
|
||||
|
||||
if((this.lvt_timer & IOAPIC_CONFIG_MASKED) === 0)
|
||||
{
|
||||
this.deliver(this.lvt_timer & 0xFF, IOAPIC_DELIVERY_FIXED, false);
|
||||
}
|
||||
}
|
||||
this.do_irq(this.lvt_timer);
|
||||
}
|
||||
};
|
||||
|
||||
APIC.prototype.check_irqs = function()
|
||||
APIC.prototype.route = function(vector, mode, is_level, destination, destination_mode)
|
||||
{
|
||||
if(!this.irqs.length)
|
||||
// TODO
|
||||
this.deliver(vector, mode, is_level);
|
||||
};
|
||||
|
||||
APIC.prototype.deliver = function(vector, mode, is_level)
|
||||
{
|
||||
APIC_LOG_VERBOSE && dbg_log("Deliver " + h(vector, 2) + " mode=" + mode + " level=" + is_level, LOG_APIC);
|
||||
|
||||
if(mode === IOAPIC_DELIVERY_INIT)
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode === IOAPIC_DELIVERY_NMI)
|
||||
{
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
if(vector < 0x10 || vector === 0xFF)
|
||||
{
|
||||
dbg_assert(false, "TODO: Invalid vector");
|
||||
}
|
||||
|
||||
if(this.register_get_bit(this.irr, vector))
|
||||
{
|
||||
dbg_log("Not delivered: irr already set, vector=" + h(vector, 2), LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
this.register_set_bit(this.irr, vector);
|
||||
|
||||
if(is_level)
|
||||
{
|
||||
this.register_set_bit(this.tmr, vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.register_clear_bit(this.tmr, vector);
|
||||
}
|
||||
|
||||
this.check_vector();
|
||||
};
|
||||
|
||||
APIC.prototype.highest_irr = function()
|
||||
{
|
||||
var highest = this.register_get_highest_bit(this.irr);
|
||||
dbg_assert(highest !== 0xFF);
|
||||
dbg_assert(highest >= 0x10 || highest === -1);
|
||||
return highest;
|
||||
};
|
||||
|
||||
APIC.prototype.highest_isr = function()
|
||||
{
|
||||
var highest = this.register_get_highest_bit(this.isr);
|
||||
dbg_assert(highest !== 0xFF);
|
||||
dbg_assert(highest >= 0x10 || highest === -1);
|
||||
return highest;
|
||||
};
|
||||
|
||||
APIC.prototype.check_vector = function()
|
||||
{
|
||||
var highest_irr = this.highest_irr();
|
||||
|
||||
if(highest_irr === -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var irq_number = this.irqs.pop();
|
||||
this.irqs_set &= ~(1 << irq_number);
|
||||
var highest_isr = this.highest_isr();
|
||||
|
||||
//if(irq_number !== 0x30) // timer interrupt on linux
|
||||
//{
|
||||
// dbg_log("Handle irq " + h(irq_number), LOG_APIC);
|
||||
//}
|
||||
|
||||
this.cpu.previous_ip = this.cpu.instruction_pointer;
|
||||
this.cpu.call_interrupt_vector(irq_number, false, false);
|
||||
};
|
||||
|
||||
APIC.prototype.set_irq = function(i)
|
||||
{
|
||||
//dbg_log("apic raise irq " + h(i), LOG_APIC);
|
||||
|
||||
var infos = this.ioredtlb[0x10 + (i << 1)]
|
||||
this.do_irq(infos);
|
||||
};
|
||||
|
||||
APIC.prototype.clear_irq = function(i)
|
||||
{
|
||||
};
|
||||
|
||||
APIC.prototype.do_irq = function(infos)
|
||||
{
|
||||
if(infos & (1 << 16))
|
||||
if(highest_isr >= highest_irr)
|
||||
{
|
||||
//dbg_log("irq " + h(i, 2) + " disabled", LOG_APIC);
|
||||
APIC_LOG_VERBOSE && dbg_log("Higher isr, isr=" + h(highest_isr) + " irr=" + h(highest_irr), LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
var irq = infos & 0xFF;
|
||||
if((highest_irr & 0xF0) <= (this.tpr & 0xF0))
|
||||
{
|
||||
APIC_LOG_VERBOSE && dbg_log("Higher tpr, tpr=" + h(this.tpr & 0xF0) + " irr=" + h(highest_irr), LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
this.irqs.push(irq);
|
||||
this.irqs_set |= 1 << irq;
|
||||
this.cpu.handle_irqs();
|
||||
};
|
||||
|
||||
APIC.prototype.ioapic_read = function(reg)
|
||||
APIC.prototype.acknowledge_irq = function()
|
||||
{
|
||||
switch(reg)
|
||||
var highest_irr = this.highest_irr();
|
||||
|
||||
if(highest_irr === -1)
|
||||
{
|
||||
case 0:
|
||||
return this.ioapic_id << 24;
|
||||
|
||||
case 1:
|
||||
return 0x11 | IOAPIC_IRQ_COUNT - 1 << 16;
|
||||
|
||||
case 2:
|
||||
// Arbitration ID
|
||||
return 0;
|
||||
|
||||
default:
|
||||
if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
|
||||
{
|
||||
var irq = reg - 0x10 >> 1;
|
||||
var index = reg & 1;
|
||||
|
||||
var value = this.ioredtlb[reg];
|
||||
|
||||
if(index)
|
||||
{
|
||||
dbg_log("Read destination irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Read config irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("IOAPIC register read outside of range " + h(reg), LOG_APIC);
|
||||
return 0;
|
||||
}
|
||||
//dbg_log("Spurious", LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
var highest_isr = this.highest_isr();
|
||||
|
||||
if(highest_isr >= highest_irr)
|
||||
{
|
||||
APIC_LOG_VERBOSE && dbg_log("Higher isr, isr=" + h(highest_isr) + " irr=" + h(highest_irr), LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
if((highest_irr & 0xF0) <= (this.tpr & 0xF0))
|
||||
{
|
||||
APIC_LOG_VERBOSE && dbg_log("Higher tpr, tpr=" + h(this.tpr & 0xF0) + " irr=" + h(highest_irr), LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
this.register_clear_bit(this.irr, highest_irr);
|
||||
this.register_set_bit(this.isr, highest_irr);
|
||||
|
||||
APIC_LOG_VERBOSE && dbg_log("Calling vector " + h(highest_irr), LOG_APIC);
|
||||
this.cpu.pic_call_irq(highest_irr);
|
||||
|
||||
this.check_vector();
|
||||
};
|
||||
|
||||
APIC.prototype.ioapic_write = function(reg, value)
|
||||
APIC.prototype.get_state = function()
|
||||
{
|
||||
dbg_log("IOAPIC write " + h(reg) + " <- " + h(value, 8), LOG_APIC);
|
||||
var state = [];
|
||||
|
||||
switch(reg)
|
||||
{
|
||||
case 0:
|
||||
this.ioapic_id = value >>> 24;
|
||||
break;
|
||||
state[0] = this.apic_id;
|
||||
state[1] = this.timer_divider;
|
||||
state[2] = this.timer_divider_shift;
|
||||
state[3] = this.timer_initial_count;
|
||||
state[4] = this.timer_current_count;
|
||||
state[5] = this.next_tick;
|
||||
state[6] = this.lvt_timer;
|
||||
state[7] = this.lvt_perf_counter;
|
||||
state[8] = this.lvt_int0;
|
||||
state[9] = this.lvt_int1;
|
||||
state[10] = this.lvt_error;
|
||||
state[11] = this.tpr;
|
||||
state[12] = this.icr0;
|
||||
state[13] = this.icr1;
|
||||
state[14] = this.irr;
|
||||
state[15] = this.isr;
|
||||
state[16] = this.tmr;
|
||||
state[17] = this.spurious_vector;
|
||||
state[18] = this.destination_format;
|
||||
state[19] = this.local_destination;
|
||||
state[20] = this.error;
|
||||
state[21] = this.read_error;
|
||||
|
||||
default:
|
||||
if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
|
||||
{
|
||||
this.ioredtlb[reg] = value;
|
||||
|
||||
var irq = reg - 0x10 >> 1;
|
||||
var index = reg & 1;
|
||||
|
||||
if(index)
|
||||
{
|
||||
dbg_log("Write destination " + h(value >>> 0, 8) + " irq=" + h(irq) + " dest=" + h(value >>> 24, 2), LOG_APIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
var vector = value & 0xFF;
|
||||
var delivery_mode = value >> 8 & 7;
|
||||
var destination_mode = value >> 11 & 1;
|
||||
var is_level = value >> 15 & 1;
|
||||
var disabled = value >> 16 & 1;
|
||||
|
||||
dbg_log("Write config " + h(value >>> 0, 8) +
|
||||
" irq=" + h(irq) +
|
||||
" vector=" + h(vector, 2) +
|
||||
" delivery=" + h(delivery_mode) +
|
||||
" destmode=" + destination_mode +
|
||||
" is_level=" + is_level +
|
||||
" disabled=" + disabled, LOG_APIC);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("IOAPIC register write outside of range " + h(reg) + ": " + h(value >>> 0, 8), LOG_APIC);
|
||||
}
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
APIC.prototype.set_state = function(state)
|
||||
{
|
||||
this.apic_id = state[0];
|
||||
this.timer_divider = state[1];
|
||||
this.timer_divider_shift = state[2];
|
||||
this.timer_initial_count = state[3];
|
||||
this.timer_current_count = state[4];
|
||||
this.next_tick = state[5];
|
||||
this.lvt_timer = state[6];
|
||||
this.lvt_perf_counter = state[7];
|
||||
this.lvt_int0 = state[8];
|
||||
this.lvt_int1 = state[9];
|
||||
this.lvt_error = state[10];
|
||||
this.tpr = state[11];
|
||||
this.icr0 = state[12];
|
||||
this.icr1 = state[13];
|
||||
this.irr = state[14];
|
||||
this.isr = state[15];
|
||||
this.tmr = state[16];
|
||||
this.spurious_vector = state[17];
|
||||
this.destination_format = state[18];
|
||||
this.local_destination = state[19];
|
||||
this.error = state[20];
|
||||
this.read_error = state[21];
|
||||
};
|
||||
|
||||
// functions operating on 256-bit registers (for irr, isr, tmr)
|
||||
APIC.prototype.register_get_bit = function(v, bit)
|
||||
{
|
||||
dbg_assert(bit >= 0 && bit < 256);
|
||||
return v[bit >> 5] >> (bit & 31) & 1;
|
||||
};
|
||||
|
||||
APIC.prototype.register_set_bit = function(v, bit)
|
||||
{
|
||||
dbg_assert(bit >= 0 && bit < 256);
|
||||
v[bit >> 5] |= 1 << (bit & 31);
|
||||
};
|
||||
|
||||
APIC.prototype.register_clear_bit = function(v, bit)
|
||||
{
|
||||
dbg_assert(bit >= 0 && bit < 256);
|
||||
v[bit >> 5] &= ~(1 << (bit & 31));
|
||||
};
|
||||
|
||||
APIC.prototype.register_get_highest_bit = function(v)
|
||||
{
|
||||
for(var i = 7; i >= 0; i--)
|
||||
{
|
||||
var word = v[i];
|
||||
|
||||
if(word)
|
||||
{
|
||||
return v86util.int_log2(word >>> 0) | i << 5;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ var LOG_LEVEL = LOG_ALL & ~LOG_PS2 & ~LOG_PIT & ~LOG_VIRTIO & ~LOG_9P & ~LOG_PIC
|
|||
var ENABLE_HPET = DEBUG && false;
|
||||
|
||||
/** @const */
|
||||
var ENABLE_ACPI = DEBUG && false;
|
||||
var ENABLE_ACPI = true;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -48,3 +48,7 @@ var TIME_PER_FRAME = 1;
|
|||
* How many ticks the TSC does per millisecond
|
||||
*/
|
||||
var TSC_RATE = 8 * 1024;
|
||||
|
||||
|
||||
/** @const */
|
||||
var APIC_TIMER_FREQ = TSC_RATE;
|
||||
|
|
|
@ -280,6 +280,14 @@ var IA32_KERNEL_GS_BASE = 0xC0000101 | 0;
|
|||
var MSR_PKG_C2_RESIDENCY = 0x60D;
|
||||
|
||||
|
||||
/** @const */
|
||||
var IA32_APIC_BASE_BSP = 1 << 8;
|
||||
/** @const */
|
||||
var IA32_APIC_BASE_EXTD = 1 << 10;
|
||||
/** @const */
|
||||
var IA32_APIC_BASE_EN = 1 << 11;
|
||||
|
||||
|
||||
/** @const */ var TSR_BACKLINK = 0x00;
|
||||
/** @const */ var TSR_CR3 = 0x1C;
|
||||
/** @const */ var TSR_EIP = 0x20;
|
||||
|
|
21
src/cpu.js
21
src/cpu.js
|
@ -200,6 +200,8 @@ function CPU()
|
|||
/** @type {number} */
|
||||
this.previous_ip = 0;
|
||||
|
||||
this.apic_enabled = true;
|
||||
|
||||
// managed in io.js
|
||||
/** @const */ this.memory_map_read8 = [];
|
||||
/** @const */ this.memory_map_write8 = [];
|
||||
|
@ -304,7 +306,7 @@ CPU.prototype.get_state = function()
|
|||
state[43] = this.fpu;
|
||||
|
||||
state[45] = this.devices.virtio;
|
||||
//state[46] = this.devices.apic;
|
||||
state[46] = this.devices.apic;
|
||||
state[47] = this.devices.rtc;
|
||||
state[48] = this.devices.pci;
|
||||
state[49] = this.devices.dma;
|
||||
|
@ -323,6 +325,8 @@ CPU.prototype.get_state = function()
|
|||
state[61] = this.a20_enabled;
|
||||
state[62] = this.fw_value;
|
||||
|
||||
state[63] = this.devices.ioapic;
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
|
@ -373,7 +377,7 @@ CPU.prototype.set_state = function(state)
|
|||
this.fpu = state[43];
|
||||
|
||||
this.devices.virtio = state[45];
|
||||
//this.devices.apic = state[46];
|
||||
this.devices.apic = state[46];
|
||||
this.devices.rtc = state[47];
|
||||
this.devices.pci = state[48];
|
||||
this.devices.dma = state[49];
|
||||
|
@ -392,6 +396,8 @@ CPU.prototype.set_state = function(state)
|
|||
this.a20_enabled = state[61];
|
||||
this.fw_value = state[62];
|
||||
|
||||
this.devices.ioapic = state[63];
|
||||
|
||||
this.mem16 = new Uint16Array(this.mem8.buffer, this.mem8.byteOffset, this.mem8.length >> 1);
|
||||
this.mem32s = new Int32Array(this.mem8.buffer, this.mem8.byteOffset, this.mem8.length >> 2);
|
||||
|
||||
|
@ -658,6 +664,7 @@ CPU.prototype.init = function(settings, device_bus)
|
|||
|
||||
if(ENABLE_ACPI)
|
||||
{
|
||||
this.devices.ioapic = new IOAPIC(this);
|
||||
this.devices.apic = new APIC(this);
|
||||
this.devices.acpi = new ACPI(this);
|
||||
}
|
||||
|
@ -3108,9 +3115,9 @@ CPU.prototype.device_raise_irq = function(i)
|
|||
this.devices.pic.set_irq(i);
|
||||
}
|
||||
|
||||
if(this.devices.apic)
|
||||
if(this.devices.ioapic)
|
||||
{
|
||||
this.devices.apic.set_irq(i);
|
||||
this.devices.ioapic.set_irq(i);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3121,9 +3128,9 @@ CPU.prototype.device_lower_irq = function(i)
|
|||
this.devices.pic.clear_irq(i);
|
||||
}
|
||||
|
||||
if(this.devices.apic)
|
||||
if(this.devices.ioapic)
|
||||
{
|
||||
this.devices.apic.clear_irq(i);
|
||||
this.devices.ioapic.clear_irq(i);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3206,7 +3213,7 @@ CPU.prototype.cpuid = function()
|
|||
vme | 1 << 3 | 1 << 4 | 1 << 5 | // vme, pse, tsc, msr
|
||||
1 << 8 | 1 << 11 | 1 << 13 | 1 << 15; // cx8, sep, pge, cmov
|
||||
|
||||
if(ENABLE_ACPI)
|
||||
if(ENABLE_ACPI && this.apic_enabled)
|
||||
{
|
||||
edx |= 1 << 9; // apic
|
||||
}
|
||||
|
|
|
@ -2112,7 +2112,11 @@ t[0x30] = cpu => {
|
|||
break;
|
||||
|
||||
case IA32_APIC_BASE_MSR:
|
||||
dbg_assert((low >>> 0) === APIC_ADDRESS, "Changing APIC address not supported");
|
||||
dbg_assert(high === 0, "Changing APIC address (high 32 bits) not supported");
|
||||
let address = low & ~(IA32_APIC_BASE_BSP | IA32_APIC_BASE_EXTD | IA32_APIC_BASE_EN);
|
||||
dbg_assert((address >>> 0) === APIC_ADDRESS, "Changing APIC address not supported");
|
||||
dbg_assert((low & IA32_APIC_BASE_EXTD) === 0, "x2apic not supported");
|
||||
cpu.apic_enabled = (low & IA32_APIC_BASE_EN) === IA32_APIC_BASE_EN;
|
||||
break;
|
||||
|
||||
case IA32_TIME_STAMP_COUNTER:
|
||||
|
@ -2200,6 +2204,11 @@ t[0x32] = cpu => {
|
|||
if(ENABLE_ACPI)
|
||||
{
|
||||
low = APIC_ADDRESS;
|
||||
|
||||
if(cpu.apic_enabled)
|
||||
{
|
||||
low |= IA32_APIC_BASE_EN;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
352
src/ioapic.js
Normal file
352
src/ioapic.js
Normal file
|
@ -0,0 +1,352 @@
|
|||
"use strict";
|
||||
|
||||
// http://download.intel.com/design/chipsets/datashts/29056601.pdf
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_ADDRESS = 0xFEC00000;
|
||||
|
||||
/** @const */
|
||||
var IOREGSEL = 0;
|
||||
|
||||
/** @const */
|
||||
var IOWIN = 0x10;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_IRQ_COUNT = 24;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_ID = 0; // must match value in seabios
|
||||
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_CONFIG_TRIGGER_MODE_LEVEL = 1 << 15;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_CONFIG_MASKED = 1 << 16;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_CONFIG_DELIVS = 1 << 12;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_CONFIG_REMOTE_IRR = 1 << 14;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_CONFIG_READONLY_MASK = IOAPIC_CONFIG_REMOTE_IRR | IOAPIC_CONFIG_DELIVS | 0xFFFE0000;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_DELIVERY_FIXED = 0;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_DELIVERY_LOWEST_PRIORITY = 1;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_DELIVERY_NMI = 4;
|
||||
|
||||
/** @const */
|
||||
var IOAPIC_DELIVERY_INIT = 5;
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {CPU} cpu
|
||||
*/
|
||||
function IOAPIC(cpu)
|
||||
{
|
||||
/** @type {CPU} */
|
||||
this.cpu = cpu;
|
||||
|
||||
this.ioredtbl_config = new Int32Array(IOAPIC_IRQ_COUNT);
|
||||
this.ioredtbl_destination = new Int32Array(IOAPIC_IRQ_COUNT);
|
||||
|
||||
for(var i = 0; i < this.ioredtbl_config.length; i++)
|
||||
{
|
||||
// disable interrupts
|
||||
this.ioredtbl_config[i] = IOAPIC_CONFIG_MASKED;
|
||||
}
|
||||
|
||||
// IOAPIC register selection
|
||||
this.ioregsel = 0;
|
||||
|
||||
this.ioapic_id = IOAPIC_ID;
|
||||
|
||||
this.irr = 0;
|
||||
this.irq_value = 0;
|
||||
|
||||
dbg_assert(MMAP_BLOCK_SIZE >= 0x20);
|
||||
cpu.io.mmap_register(IOAPIC_ADDRESS, MMAP_BLOCK_SIZE,
|
||||
(addr) =>
|
||||
{
|
||||
dbg_assert(false, "unsupported read8 from ioapic: " + h(addr));
|
||||
return 0;
|
||||
},
|
||||
(addr, value) =>
|
||||
{
|
||||
dbg_assert(false, "unsupported write8 from ioapic: " + h(addr));
|
||||
},
|
||||
(addr) =>
|
||||
{
|
||||
addr = addr - IOAPIC_ADDRESS | 0;
|
||||
|
||||
if(addr === IOREGSEL)
|
||||
{
|
||||
return this.ioregsel;
|
||||
}
|
||||
else if(addr === IOWIN)
|
||||
{
|
||||
return this.read(this.ioregsel);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register read: " + h(addr), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
(addr, value) =>
|
||||
{
|
||||
addr = addr - IOAPIC_ADDRESS | 0;
|
||||
|
||||
if(addr === IOREGSEL)
|
||||
{
|
||||
this.ioregsel = value;
|
||||
}
|
||||
else if(addr === IOWIN)
|
||||
{
|
||||
this.write(this.ioregsel, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register write: " + h(addr) + " <- " + h(value >>> 0, 8), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
IOAPIC.prototype.remote_eoi = function(vector)
|
||||
{
|
||||
for(var i = 0; i < IOAPIC_IRQ_COUNT; i++)
|
||||
{
|
||||
var config = this.ioredtbl_config[i];
|
||||
|
||||
if((config & 0xFF) === vector && (config & IOAPIC_CONFIG_REMOTE_IRR))
|
||||
{
|
||||
dbg_log("Clear remote IRR for irq=" + h(i), LOG_APIC);
|
||||
this.ioredtbl_config[i] &= ~IOAPIC_CONFIG_REMOTE_IRR;
|
||||
this.check_irq(i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IOAPIC.prototype.check_irq = function(irq)
|
||||
{
|
||||
var mask = 1 << irq;
|
||||
|
||||
if((this.irr & mask) === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var config = this.ioredtbl_config[irq];
|
||||
|
||||
if((config & IOAPIC_CONFIG_MASKED) === 0)
|
||||
{
|
||||
var delivery_mode = config >> 8 & 7;
|
||||
var destination_mode = config >> 11 & 1;
|
||||
var vector = config & 0xFF;
|
||||
var destination = this.ioredtbl_destination[irq] >>> 24;
|
||||
var is_level = (config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL) === IOAPIC_CONFIG_TRIGGER_MODE_LEVEL;
|
||||
|
||||
if((config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL) === 0)
|
||||
{
|
||||
this.irr &= ~mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ioredtbl_config[irq] |= IOAPIC_CONFIG_REMOTE_IRR;
|
||||
|
||||
if(config & IOAPIC_CONFIG_REMOTE_IRR)
|
||||
{
|
||||
dbg_log("No route: level interrupt and remote IRR still set", LOG_APIC)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(delivery_mode === IOAPIC_DELIVERY_FIXED || delivery_mode === IOAPIC_DELIVERY_LOWEST_PRIORITY)
|
||||
{
|
||||
this.cpu.devices.apic.route(vector, delivery_mode, is_level, destination, destination_mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false, "TODO");
|
||||
}
|
||||
|
||||
this.ioredtbl_config[irq] &= ~IOAPIC_CONFIG_DELIVS;
|
||||
}
|
||||
};
|
||||
|
||||
IOAPIC.prototype.set_irq = function(i)
|
||||
{
|
||||
if(i >= IOAPIC_IRQ_COUNT)
|
||||
{
|
||||
dbg_assert(false, "Bad irq: " + i, LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
var mask = 1 << i;
|
||||
|
||||
if((this.irq_value & mask) === 0)
|
||||
{
|
||||
APIC_LOG_VERBOSE && dbg_log("apic set irq " + i, LOG_APIC);
|
||||
|
||||
this.irq_value |= mask;
|
||||
|
||||
var config = this.ioredtbl_config[i];
|
||||
if((config & (IOAPIC_CONFIG_TRIGGER_MODE_LEVEL|IOAPIC_CONFIG_MASKED)) ===
|
||||
IOAPIC_CONFIG_MASKED)
|
||||
{
|
||||
// edge triggered and masked
|
||||
return;
|
||||
}
|
||||
|
||||
this.irr |= mask;
|
||||
|
||||
this.check_irq(i);
|
||||
}
|
||||
};
|
||||
|
||||
IOAPIC.prototype.clear_irq = function(i)
|
||||
{
|
||||
if(i >= IOAPIC_IRQ_COUNT)
|
||||
{
|
||||
dbg_assert(false, "Bad irq: " + i, LOG_APIC);
|
||||
return;
|
||||
}
|
||||
|
||||
var mask = 1 << i;
|
||||
|
||||
if((this.irq_value & mask) === mask)
|
||||
{
|
||||
this.irq_value &= ~mask;
|
||||
|
||||
var config = this.ioredtbl_config[i];
|
||||
if(config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL)
|
||||
{
|
||||
this.irr &= ~mask;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IOAPIC.prototype.read = function(reg)
|
||||
{
|
||||
if(reg === 0)
|
||||
{
|
||||
dbg_log("IOAPIC Read id", LOG_APIC);
|
||||
return this.ioapic_id << 24;
|
||||
}
|
||||
else if(reg === 1)
|
||||
{
|
||||
dbg_log("IOAPIC Read version", LOG_APIC);
|
||||
return 0x11 | IOAPIC_IRQ_COUNT - 1 << 16;
|
||||
}
|
||||
else if(reg === 2)
|
||||
{
|
||||
dbg_log("IOAPIC Read arbitration id", LOG_APIC);
|
||||
return this.ioapic_id << 24;
|
||||
}
|
||||
else if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
|
||||
{
|
||||
var irq = reg - 0x10 >> 1;
|
||||
var index = reg & 1;
|
||||
|
||||
if(index)
|
||||
{
|
||||
var value = this.ioredtbl_destination[irq];
|
||||
dbg_log("IOAPIC Read destination irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = this.ioredtbl_config[irq];
|
||||
dbg_log("IOAPIC Read config irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("IOAPIC register read outside of range " + h(reg), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
IOAPIC.prototype.write = function(reg, value)
|
||||
{
|
||||
//dbg_log("IOAPIC write " + h(reg) + " <- " + h(value, 8), LOG_APIC);
|
||||
|
||||
if(reg === 0)
|
||||
{
|
||||
this.ioapic_id = value >>> 24 & 0x0F;
|
||||
}
|
||||
else if(reg === 1 || reg === 2)
|
||||
{
|
||||
dbg_log("Invalid write: " + reg, LOG_APIC);
|
||||
}
|
||||
else if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
|
||||
{
|
||||
var irq = reg - 0x10 >> 1;
|
||||
var index = reg & 1;
|
||||
|
||||
if(index)
|
||||
{
|
||||
this.ioredtbl_destination[irq] = value & 0xFF000000;
|
||||
dbg_log("Write destination " + h(value >>> 0, 8) + " irq=" + h(irq) + " dest=" + h(value >>> 24, 2), LOG_APIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
var old_value = this.ioredtbl_config[irq];
|
||||
this.ioredtbl_config[irq] = value & ~IOAPIC_CONFIG_READONLY_MASK | old_value & IOAPIC_CONFIG_READONLY_MASK;
|
||||
|
||||
var vector = value & 0xFF;
|
||||
var delivery_mode = value >> 8 & 7;
|
||||
var destination_mode = value >> 11 & 1;
|
||||
var is_level = value >> 15 & 1;
|
||||
var disabled = value >> 16 & 1;
|
||||
|
||||
dbg_log("Write config " + h(value >>> 0, 8) +
|
||||
" irq=" + h(irq) +
|
||||
" vector=" + h(vector, 2) +
|
||||
" deliverymode=" + DELIVERY_MODES[delivery_mode] +
|
||||
" destmode=" + DESTINATION_MODES[destination_mode] +
|
||||
" is_level=" + is_level +
|
||||
" disabled=" + disabled, LOG_APIC);
|
||||
|
||||
this.check_irq(irq);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("IOAPIC register write outside of range " + h(reg) + ": " + h(value >>> 0, 8), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
IOAPIC.prototype.get_state = function()
|
||||
{
|
||||
var state = [];
|
||||
state[0] = this.ioredtbl_config;
|
||||
state[1] = this.ioredtbl_destination;
|
||||
state[2] = this.ioregsel;
|
||||
state[3] = this.ioapic_id;
|
||||
state[4] = this.irr;
|
||||
state[5] = this.irq_value;
|
||||
return state;
|
||||
};
|
||||
|
||||
IOAPIC.prototype.set_state = function(state)
|
||||
{
|
||||
this.ioredtbl_config = state[0];
|
||||
this.ioredtbl_destination = state[1];
|
||||
this.ioregsel = state[2];
|
||||
this.ioapic_id = state[3];
|
||||
this.irr = state[4]
|
||||
this.irq_value = state[5];
|
||||
};
|
Loading…
Reference in a new issue