mirror of
https://github.com/copy/v86.git
synced 2025-12-31 04:23:15 +00:00
port apic & ioapic to rust
- handle mmap access and port io directly in rust - call handle_irqs after port/memory writes, rather than from the interrupt hw directly; this makes it more obvious that handle_irqs is dangerous as it can change control flow state images produced by this version are not backwards-compatible (older stage images will still be working)
This commit is contained in:
parent
244a877989
commit
c6c2017ba8
13 changed files with 1161 additions and 1180 deletions
2
Makefile
2
Makefile
|
|
@ -80,7 +80,7 @@ CARGO_FLAGS=$(CARGO_FLAGS_SAFE) -C target-feature=+bulk-memory -C target-feature
|
|||
|
||||
CORE_FILES=cjs.js const.js io.js main.js lib.js buffer.js ide.js pci.js floppy.js \
|
||||
dma.js pit.js vga.js ps2.js rtc.js uart.js \
|
||||
acpi.js apic.js ioapic.js iso9660.js \
|
||||
acpi.js iso9660.js \
|
||||
state.js ne2k.js sb16.js virtio.js virtio_console.js virtio_net.js virtio_balloon.js \
|
||||
bus.js log.js cpu.js \
|
||||
elf.js kernel.js
|
||||
|
|
|
|||
649
src/apic.js
649
src/apic.js
|
|
@ -1,649 +0,0 @@
|
|||
// See Intel's System Programming Guide
|
||||
|
||||
import { v86 } from "./main.js";
|
||||
import { LOG_APIC } from "../src/const.js";
|
||||
import { h, int_log2 } from "./lib.js";
|
||||
import { dbg_assert, dbg_log, dbg_trace } from "./log.js";
|
||||
import { IOAPIC_CONFIG_MASKED, IOAPIC_DELIVERY_INIT, IOAPIC_DELIVERY_NMI, IOAPIC_DELIVERY_FIXED } from "./ioapic.js";
|
||||
|
||||
// For Types Only
|
||||
import { CPU } from "./cpu.js";
|
||||
|
||||
export const APIC_LOG_VERBOSE = false;
|
||||
|
||||
// should probably be kept in sync with TSC_RATE in cpu.rs
|
||||
const APIC_TIMER_FREQ = 1 * 1000 * 1000;
|
||||
|
||||
const APIC_ADDRESS = 0xFEE00000;
|
||||
|
||||
const APIC_TIMER_MODE_MASK = 3 << 17;
|
||||
|
||||
const APIC_TIMER_MODE_ONE_SHOT = 0;
|
||||
|
||||
const APIC_TIMER_MODE_PERIODIC = 1 << 17;
|
||||
|
||||
const APIC_TIMER_MODE_TSC = 2 << 17;
|
||||
|
||||
|
||||
export const DELIVERY_MODES = [
|
||||
"Fixed (0)",
|
||||
"Lowest Prio (1)",
|
||||
"SMI (2)",
|
||||
"Reserved (3)",
|
||||
"NMI (4)",
|
||||
"INIT (5)",
|
||||
"Reserved (6)",
|
||||
"ExtINT (7)",
|
||||
];
|
||||
|
||||
export const DESTINATION_MODES = ["physical", "logical"];
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {CPU} cpu
|
||||
*/
|
||||
export function APIC(cpu)
|
||||
{
|
||||
/** @type {CPU} */
|
||||
this.cpu = cpu;
|
||||
|
||||
this.apic_id = 0;
|
||||
|
||||
this.timer_divider = 0;
|
||||
this.timer_divider_shift = 1;
|
||||
this.timer_initial_count = 0;
|
||||
this.timer_current_count = 0;
|
||||
|
||||
this.next_tick = v86.microtick();
|
||||
|
||||
this.lvt_timer = IOAPIC_CONFIG_MASKED;
|
||||
this.lvt_thermal_sensor = 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.tpr = 0;
|
||||
this.icr0 = 0;
|
||||
this.icr1 = 0;
|
||||
|
||||
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_log("Unsupported read8 from apic: " + h(addr >>> 0), LOG_APIC);
|
||||
var off = addr & 3;
|
||||
addr &= ~3;
|
||||
return this.read32(addr) >> (off * 8) & 0xFF;
|
||||
},
|
||||
(addr, value) =>
|
||||
{
|
||||
dbg_log("Unsupported write8 from apic: " + h(addr) + " <- " + h(value), LOG_APIC);
|
||||
dbg_trace();
|
||||
dbg_assert(false);
|
||||
},
|
||||
(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 0x330:
|
||||
dbg_log("read lvt thermal sensor", LOG_APIC);
|
||||
return this.lvt_thermal_sensor;
|
||||
|
||||
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 0x20:
|
||||
dbg_log("APIC write id: " + h(value >>> 8, 8), LOG_APIC);
|
||||
this.apic_id = value;
|
||||
break;
|
||||
|
||||
case 0x30:
|
||||
// version
|
||||
dbg_log("APIC write version: " + h(value >>> 0, 8) + ", ignored", LOG_APIC);
|
||||
break;
|
||||
|
||||
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 0x330:
|
||||
dbg_log("lvt thermal sensor: " + h(value >>> 0, 8), LOG_APIC);
|
||||
this.lvt_thermal_sensor = 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)
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
const freq = APIC_TIMER_FREQ / (1 << this.timer_divider_shift);
|
||||
|
||||
const steps = (now - this.next_tick) * freq >>> 0;
|
||||
|
||||
this.next_tick += steps / freq;
|
||||
this.timer_current_count -= steps;
|
||||
|
||||
if(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_current_count % this.timer_initial_count;
|
||||
|
||||
if(this.timer_current_count <= 0)
|
||||
{
|
||||
this.timer_current_count += this.timer_initial_count;
|
||||
}
|
||||
dbg_assert(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("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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Math.max(0, this.timer_current_count / freq);
|
||||
};
|
||||
|
||||
APIC.prototype.route = function(vector, mode, is_level, destination, destination_mode)
|
||||
{
|
||||
// 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 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.cpu.handle_irqs();
|
||||
};
|
||||
|
||||
APIC.prototype.acknowledge_irq = function()
|
||||
{
|
||||
var highest_irr = this.highest_irr();
|
||||
|
||||
if(highest_irr === -1)
|
||||
{
|
||||
//dbg_log("Spurious", LOG_APIC);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 -1;
|
||||
}
|
||||
|
||||
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 -1;
|
||||
}
|
||||
|
||||
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.check_vector();
|
||||
|
||||
return highest_irr;
|
||||
};
|
||||
|
||||
APIC.prototype.get_state = function()
|
||||
{
|
||||
var state = [];
|
||||
|
||||
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;
|
||||
state[22] = this.lvt_thermal_sensor;
|
||||
|
||||
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];
|
||||
this.lvt_thermal_sensor = state[22] || IOAPIC_CONFIG_MASKED;
|
||||
};
|
||||
|
||||
// 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 int_log2(word >>> 0) | i << 5;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
|
@ -67,7 +67,6 @@ export function V86(options)
|
|||
"abort": function() { dbg_assert(false); },
|
||||
"microtick": v86.microtick,
|
||||
"get_rand_int": function() { return get_rand_int(); },
|
||||
"apic_acknowledge_irq": function() { return cpu.devices.apic.acknowledge_irq(); },
|
||||
"stop_idling": function() { return cpu.stop_idling(); },
|
||||
|
||||
"io_port_read8": function(addr) { return cpu.io.port_read8(addr); },
|
||||
|
|
@ -637,8 +636,7 @@ V86.prototype.zstd_decompress_worker = async function(decompressed_size, src)
|
|||
{
|
||||
const env = Object.fromEntries([
|
||||
"cpu_exception_hook", "run_hardware_timers",
|
||||
"cpu_event_halt", "microtick", "get_rand_int",
|
||||
"apic_acknowledge_irq", "stop_idling",
|
||||
"cpu_event_halt", "microtick", "get_rand_int", "stop_idling",
|
||||
"io_port_read8", "io_port_read16", "io_port_read32",
|
||||
"io_port_write8", "io_port_write16", "io_port_write32",
|
||||
"mmap_read8", "mmap_read16", "mmap_read32",
|
||||
|
|
|
|||
154
src/cpu.js
154
src/cpu.js
|
|
@ -15,8 +15,6 @@ import { h, view, pads, Bitmap, dump_file } from "./lib.js";
|
|||
import { dbg_assert, dbg_log } from "./log.js";
|
||||
|
||||
import { SB16 } from "./sb16.js";
|
||||
import { IOAPIC } from "./ioapic.js";
|
||||
import { APIC } from "./apic.js";
|
||||
import { ACPI } from "./acpi.js";
|
||||
import { PIT } from "./pit.js";
|
||||
import { DMA } from "./dma.js";
|
||||
|
|
@ -407,8 +405,10 @@ CPU.prototype.wasm_patch = function()
|
|||
|
||||
this.set_cpuid_level = get_import("set_cpuid_level");
|
||||
|
||||
this.pic_set_irq = get_import("pic_set_irq");
|
||||
this.pic_clear_irq = get_import("pic_clear_irq");
|
||||
this.device_raise_irq = get_import("device_raise_irq");
|
||||
this.device_lower_irq = get_import("device_lower_irq");
|
||||
|
||||
this.apic_timer = get_import("apic_timer");
|
||||
|
||||
if(DEBUG)
|
||||
{
|
||||
|
|
@ -430,27 +430,14 @@ CPU.prototype.wasm_patch = function()
|
|||
|
||||
this.get_pic_addr_master = get_import("get_pic_addr_master");
|
||||
this.get_pic_addr_slave = get_import("get_pic_addr_slave");
|
||||
this.get_apic_addr = get_import("get_apic_addr");
|
||||
this.get_ioapic_addr = get_import("get_ioapic_addr");
|
||||
|
||||
this.zstd_create_ctx = get_import("zstd_create_ctx");
|
||||
this.zstd_get_src_ptr = get_import("zstd_get_src_ptr");
|
||||
this.zstd_free_ctx = get_import("zstd_free_ctx");
|
||||
this.zstd_read = get_import("zstd_read");
|
||||
this.zstd_read_free = get_import("zstd_read_free");
|
||||
|
||||
this.port20_read = get_import("port20_read");
|
||||
this.port21_read = get_import("port21_read");
|
||||
this.portA0_read = get_import("portA0_read");
|
||||
this.portA1_read = get_import("portA1_read");
|
||||
|
||||
this.port20_write = get_import("port20_write");
|
||||
this.port21_write = get_import("port21_write");
|
||||
this.portA0_write = get_import("portA0_write");
|
||||
this.portA1_write = get_import("portA1_write");
|
||||
|
||||
this.port4D0_read = get_import("port4D0_read");
|
||||
this.port4D1_read = get_import("port4D1_read");
|
||||
this.port4D0_write = get_import("port4D0_write");
|
||||
this.port4D1_write = get_import("port4D1_write");
|
||||
};
|
||||
|
||||
CPU.prototype.jit_force_generate = function(addr)
|
||||
|
|
@ -525,7 +512,7 @@ CPU.prototype.get_state = function()
|
|||
state[43] = this.current_tsc;
|
||||
|
||||
state[45] = this.devices.virtio_9p;
|
||||
state[46] = this.devices.apic;
|
||||
state[46] = this.get_state_apic();
|
||||
state[47] = this.devices.rtc;
|
||||
state[48] = this.devices.pci;
|
||||
state[49] = this.devices.dma;
|
||||
|
|
@ -559,7 +546,7 @@ CPU.prototype.get_state = function()
|
|||
|
||||
state[62] = this.fw_value;
|
||||
|
||||
state[63] = this.devices.ioapic;
|
||||
state[63] = this.get_state_ioapic();
|
||||
|
||||
state[64] = this.tss_size_32[0];
|
||||
|
||||
|
|
@ -629,6 +616,18 @@ CPU.prototype.get_state_pic = function()
|
|||
return state;
|
||||
};
|
||||
|
||||
CPU.prototype.get_state_apic = function()
|
||||
{
|
||||
const APIC_STRUCT_SIZE = 4 * 46; // keep in sync with apic.rs
|
||||
return new Uint8Array(this.wasm_memory.buffer, this.get_apic_addr(), APIC_STRUCT_SIZE);
|
||||
};
|
||||
|
||||
CPU.prototype.get_state_ioapic = function()
|
||||
{
|
||||
const IOAPIC_STRUCT_SIZE = 4 * 52; // keep in sync with ioapic.rs
|
||||
return new Uint8Array(this.wasm_memory.buffer, this.get_ioapic_addr(), IOAPIC_STRUCT_SIZE);
|
||||
};
|
||||
|
||||
CPU.prototype.set_state = function(state)
|
||||
{
|
||||
this.memory_size[0] = state[0];
|
||||
|
|
@ -695,7 +694,7 @@ CPU.prototype.set_state = function(state)
|
|||
this.set_tsc(state[43][0], state[43][1]);
|
||||
|
||||
this.devices.virtio_9p && this.devices.virtio_9p.set_state(state[45]);
|
||||
this.devices.apic && this.devices.apic.set_state(state[46]);
|
||||
state[46] && this.set_state_apic(state[46]);
|
||||
this.devices.rtc && this.devices.rtc.set_state(state[47]);
|
||||
this.devices.dma && this.devices.dma.set_state(state[49]);
|
||||
this.devices.acpi && this.devices.acpi.set_state(state[50]);
|
||||
|
|
@ -744,7 +743,7 @@ CPU.prototype.set_state = function(state)
|
|||
|
||||
this.fw_value = state[62];
|
||||
|
||||
this.devices.ioapic && this.devices.ioapic.set_state(state[63]);
|
||||
state[63] && this.set_state_ioapic(state[63]);
|
||||
|
||||
this.tss_size_32[0] = state[64];
|
||||
|
||||
|
|
@ -809,6 +808,75 @@ CPU.prototype.set_state_pic = function(state)
|
|||
pic_slave[12] = state_slave[12]; // special_mask_mode (undefined in old state images)
|
||||
};
|
||||
|
||||
CPU.prototype.set_state_apic = function(state)
|
||||
{
|
||||
const APIC_STRUCT_SIZE = 4 * 46; // keep in sync with apic.rs
|
||||
const IOAPIC_CONFIG_MASKED = 1 << 16;
|
||||
|
||||
if(state instanceof Array)
|
||||
{
|
||||
// old js state image; delete this code path when the state version changes
|
||||
const apic = new Int32Array(this.wasm_memory.buffer, this.get_apic_addr(), APIC_STRUCT_SIZE >> 2);
|
||||
apic[0] = state[0]; // apic_id
|
||||
apic[1] = state[1]; // timer_divier
|
||||
apic[2] = state[2]; // timer_divider_shift
|
||||
apic[3] = state[3]; // timer_initial_count
|
||||
apic[4] = state[4]; // timer_current_count
|
||||
// skip next_tick (in js: state[4]; in rust: apic[5] and apic[6])
|
||||
apic[7] = state[6]; // lvt_timer
|
||||
apic[8] = state[7]; // lvt_perf_counter
|
||||
apic[9] = state[8]; // lvt_int0
|
||||
apic[10] = state[9]; // lvt_int1
|
||||
apic[11] = state[10]; // lvt_error
|
||||
apic[12] = state[11]; // tpr
|
||||
apic[13] = state[12]; // icr0
|
||||
apic[14] = state[13]; // icr1
|
||||
apic.set(state[15], 16); // irr
|
||||
apic.set(state[15], 24); // isr
|
||||
apic.set(state[16], 32); // tmr
|
||||
apic[40] = state[17]; // spurious_vector
|
||||
apic[41] = state[18]; // destination_format
|
||||
apic[42] = state[19]; // local_destination
|
||||
apic[43] = state[20]; // error
|
||||
apic[44] = state[21]; // read_error
|
||||
apic[45] = state[22] || IOAPIC_CONFIG_MASKED; // lvt_thermal_sensor
|
||||
}
|
||||
else
|
||||
{
|
||||
const apic = new Uint8Array(this.wasm_memory.buffer, this.get_apic_addr(), APIC_STRUCT_SIZE);
|
||||
dbg_assert(state instanceof Uint8Array);
|
||||
dbg_assert(state.length === apic.length); // later versions might need to handle state upgrades here
|
||||
apic.set(state);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.set_state_ioapic = function(state)
|
||||
{
|
||||
const IOAPIC_STRUCT_SIZE = 4 * 52; // keep in sync with ioapic.rs
|
||||
|
||||
if(state instanceof Array)
|
||||
{
|
||||
// old js state image; delete this code path when the state version changes
|
||||
dbg_assert(state[0].length === 24);
|
||||
dbg_assert(state[1].length === 24);
|
||||
dbg_assert(state.length === 6);
|
||||
const ioapic = new Int32Array(this.wasm_memory.buffer, this.get_ioapic_addr(), IOAPIC_STRUCT_SIZE >> 2);
|
||||
ioapic.set(state[0], 0); // ioredtbl_config
|
||||
ioapic.set(state[1], 24); // ioredtbl_destination
|
||||
ioapic[48] = state[2]; // ioregsel
|
||||
ioapic[49] = state[3]; // ioapic_id
|
||||
ioapic[50] = state[4]; // irr
|
||||
ioapic[51] = state[5]; // irq_value
|
||||
}
|
||||
else
|
||||
{
|
||||
const ioapic = new Uint8Array(this.wasm_memory.buffer, this.get_ioapic_addr(), IOAPIC_STRUCT_SIZE);
|
||||
dbg_assert(state instanceof Uint8Array);
|
||||
dbg_assert(state.length === ioapic.length); // later versions might need to handle state upgrades here
|
||||
ioapic.set(state);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.pack_memory = function()
|
||||
{
|
||||
dbg_assert((this.mem8.length & 0xFFF) === 0);
|
||||
|
|
@ -1086,21 +1154,6 @@ CPU.prototype.init = function(settings, device_bus)
|
|||
io.register_write(0xE9, this, function(out_byte) {});
|
||||
}
|
||||
|
||||
io.register_read(0x20, this, this.port20_read);
|
||||
io.register_read(0x21, this, this.port21_read);
|
||||
io.register_read(0xA0, this, this.portA0_read);
|
||||
io.register_read(0xA1, this, this.portA1_read);
|
||||
|
||||
io.register_write(0x20, this, this.port20_write);
|
||||
io.register_write(0x21, this, this.port21_write);
|
||||
io.register_write(0xA0, this, this.portA0_write);
|
||||
io.register_write(0xA1, this, this.portA1_write);
|
||||
|
||||
io.register_read(0x4D0, this, this.port4D0_read);
|
||||
io.register_read(0x4D1, this, this.port4D1_read);
|
||||
io.register_write(0x4D0, this, this.port4D0_write);
|
||||
io.register_write(0x4D1, this, this.port4D1_write);
|
||||
|
||||
this.devices = {};
|
||||
|
||||
// TODO: Make this more configurable
|
||||
|
|
@ -1110,8 +1163,6 @@ CPU.prototype.init = function(settings, device_bus)
|
|||
|
||||
if(this.acpi_enabled[0])
|
||||
{
|
||||
this.devices.ioapic = new IOAPIC(this);
|
||||
this.devices.apic = new APIC(this);
|
||||
this.devices.acpi = new ACPI(this);
|
||||
}
|
||||
|
||||
|
|
@ -1854,33 +1905,12 @@ CPU.prototype.run_hardware_timers = function(acpi_enabled, now)
|
|||
if(acpi_enabled)
|
||||
{
|
||||
acpi_time = this.devices.acpi.timer(now);
|
||||
apic_time = this.devices.apic.timer(now);
|
||||
apic_time = this.apic_timer(now);
|
||||
}
|
||||
|
||||
return Math.min(pit_time, rtc_time, acpi_time, apic_time);
|
||||
};
|
||||
|
||||
CPU.prototype.device_raise_irq = function(i)
|
||||
{
|
||||
dbg_assert(arguments.length === 1);
|
||||
this.pic_set_irq(i);
|
||||
|
||||
if(this.devices.ioapic)
|
||||
{
|
||||
this.devices.ioapic.set_irq(i);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.device_lower_irq = function(i)
|
||||
{
|
||||
this.pic_clear_irq(i);
|
||||
|
||||
if(this.devices.ioapic)
|
||||
{
|
||||
this.devices.ioapic.clear_irq(i);
|
||||
}
|
||||
};
|
||||
|
||||
CPU.prototype.debug_init = function()
|
||||
{
|
||||
if(!DEBUG) return;
|
||||
|
|
|
|||
353
src/ioapic.js
353
src/ioapic.js
|
|
@ -1,353 +0,0 @@
|
|||
// http://download.intel.com/design/chipsets/datashts/29056601.pdf
|
||||
|
||||
import { LOG_APIC, MMAP_BLOCK_SIZE } from "./const.js";
|
||||
import { h } from "./lib.js";
|
||||
import { dbg_assert, dbg_log } from "./log.js";
|
||||
import { DELIVERY_MODES, DESTINATION_MODES, APIC_LOG_VERBOSE } from "./apic.js";
|
||||
|
||||
// For Types Only
|
||||
import { CPU } from "./cpu.js";
|
||||
|
||||
|
||||
export const IOAPIC_ADDRESS = 0xFEC00000;
|
||||
|
||||
export const IOREGSEL = 0;
|
||||
|
||||
export const IOWIN = 0x10;
|
||||
|
||||
export const IOAPIC_IRQ_COUNT = 24;
|
||||
|
||||
export const IOAPIC_ID = 0; // must match value in seabios
|
||||
|
||||
export const IOAPIC_CONFIG_TRIGGER_MODE_LEVEL = 1 << 15;
|
||||
|
||||
export const IOAPIC_CONFIG_MASKED = 1 << 16;
|
||||
|
||||
export const IOAPIC_CONFIG_DELIVS = 1 << 12;
|
||||
|
||||
export const IOAPIC_CONFIG_REMOTE_IRR = 1 << 14;
|
||||
|
||||
export const IOAPIC_CONFIG_READONLY_MASK = IOAPIC_CONFIG_REMOTE_IRR | IOAPIC_CONFIG_DELIVS | 0xFFFE0000;
|
||||
|
||||
export const IOAPIC_DELIVERY_FIXED = 0;
|
||||
export const IOAPIC_DELIVERY_LOWEST_PRIORITY = 1;
|
||||
export const IOAPIC_DELIVERY_NMI = 4;
|
||||
export const IOAPIC_DELIVERY_INIT = 5;
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {CPU} cpu
|
||||
*/
|
||||
export 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) =>
|
||||
{
|
||||
addr = addr - IOAPIC_ADDRESS | 0;
|
||||
|
||||
if(addr >= IOWIN && addr < IOWIN + 4)
|
||||
{
|
||||
const byte = addr - IOWIN;
|
||||
dbg_log("ioapic read8 byte " + byte + " " + h(this.ioregsel), LOG_APIC);
|
||||
return this.read(this.ioregsel) >> (8 * byte) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("Unexpected IOAPIC register read: " + h(addr >>> 0), LOG_APIC);
|
||||
dbg_assert(false);
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
(addr, value) =>
|
||||
{
|
||||
dbg_assert(false, "unsupported write8 from ioapic: " + h(addr >>> 0));
|
||||
},
|
||||
(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 >>> 0), 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 >>> 0) + " <- " + 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];
|
||||
};
|
||||
597
src/rust/cpu/apic.rs
Normal file
597
src/rust/cpu/apic.rs
Normal file
|
|
@ -0,0 +1,597 @@
|
|||
// See Intel's System Programming Guide
|
||||
|
||||
use crate::cpu::{cpu::js, global_pointers::acpi_enabled, ioapic};
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
const APIC_LOG_VERBOSE: bool = false;
|
||||
|
||||
// should probably be kept in sync with TSC_RATE in cpu.rs
|
||||
const APIC_TIMER_FREQ: f64 = 1.0 * 1000.0 * 1000.0;
|
||||
|
||||
const APIC_TIMER_MODE_MASK: u32 = 3 << 17;
|
||||
|
||||
const APIC_TIMER_MODE_ONE_SHOT: u32 = 0;
|
||||
const APIC_TIMER_MODE_PERIODIC: u32 = 1 << 17;
|
||||
|
||||
const _APIC_TIMER_MODE_TSC: u32 = 2 << 17;
|
||||
|
||||
const DELIVERY_MODES: [&str; 8] = [
|
||||
"Fixed (0)",
|
||||
"Lowest Prio (1)",
|
||||
"SMI (2)",
|
||||
"Reserved (3)",
|
||||
"NMI (4)",
|
||||
"INIT (5)",
|
||||
"Reserved (6)",
|
||||
"ExtINT (7)",
|
||||
];
|
||||
|
||||
const DESTINATION_MODES: [&str; 2] = ["physical", "logical"];
|
||||
|
||||
const IOAPIC_CONFIG_MASKED: u32 = 0x10000;
|
||||
|
||||
const IOAPIC_DELIVERY_INIT: u8 = 5;
|
||||
const IOAPIC_DELIVERY_NMI: u8 = 4;
|
||||
const IOAPIC_DELIVERY_FIXED: u8 = 0;
|
||||
|
||||
// keep in sync with cpu.js
|
||||
#[allow(dead_code)]
|
||||
const APIC_STRUCT_SIZE: usize = 4 * 46;
|
||||
|
||||
// Note: JavaScript (cpu.get_state_apic) depens on this layout
|
||||
const _: () = assert!(std::mem::offset_of!(Apic, icr0) == 14 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Apic, irr) == 16 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Apic, isr) == 24 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Apic, tmr) == 32 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Apic, spurious_vector) == 40 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Apic, lvt_thermal_sensor) == 45 * 4);
|
||||
const _: () = assert!(std::mem::size_of::<Apic>() == APIC_STRUCT_SIZE);
|
||||
#[repr(C)]
|
||||
pub struct Apic {
|
||||
apic_id: u32,
|
||||
timer_divider: u32,
|
||||
timer_divider_shift: u32,
|
||||
timer_initial_count: u32,
|
||||
timer_current_count: u32,
|
||||
next_tick: f64,
|
||||
lvt_timer: u32,
|
||||
lvt_perf_counter: u32,
|
||||
lvt_int0: u32,
|
||||
lvt_int1: u32,
|
||||
lvt_error: u32,
|
||||
tpr: u32,
|
||||
icr0: u32,
|
||||
icr1: u32,
|
||||
irr: [u32; 8],
|
||||
isr: [u32; 8],
|
||||
tmr: [u32; 8],
|
||||
spurious_vector: u32,
|
||||
destination_format: u32,
|
||||
local_destination: u32,
|
||||
error: u32,
|
||||
read_error: u32,
|
||||
lvt_thermal_sensor: u32,
|
||||
}
|
||||
|
||||
static APIC: Mutex<Apic> = Mutex::new(Apic {
|
||||
apic_id: 0,
|
||||
timer_divider: 0,
|
||||
timer_divider_shift: 1,
|
||||
timer_initial_count: 0,
|
||||
timer_current_count: 0,
|
||||
next_tick: 0.0,
|
||||
lvt_timer: IOAPIC_CONFIG_MASKED,
|
||||
lvt_thermal_sensor: IOAPIC_CONFIG_MASKED,
|
||||
lvt_perf_counter: IOAPIC_CONFIG_MASKED,
|
||||
lvt_int0: IOAPIC_CONFIG_MASKED,
|
||||
lvt_int1: IOAPIC_CONFIG_MASKED,
|
||||
lvt_error: IOAPIC_CONFIG_MASKED,
|
||||
tpr: 0,
|
||||
icr0: 0,
|
||||
icr1: 0,
|
||||
irr: [0; 8],
|
||||
isr: [0; 8],
|
||||
tmr: [0; 8],
|
||||
spurious_vector: 0xFE,
|
||||
destination_format: !0,
|
||||
local_destination: 0,
|
||||
error: 0,
|
||||
read_error: 0,
|
||||
});
|
||||
|
||||
pub fn get_apic() -> MutexGuard<'static, Apic> { APIC.try_lock().unwrap() }
|
||||
|
||||
#[no_mangle]
|
||||
pub fn get_apic_addr() -> u32 { &raw mut *get_apic() as u32 }
|
||||
|
||||
pub fn read32(addr: u32) -> u32 {
|
||||
if unsafe { !*acpi_enabled } {
|
||||
return 0;
|
||||
}
|
||||
read32_internal(&mut get_apic(), addr)
|
||||
}
|
||||
|
||||
fn read32_internal(apic: &mut Apic, addr: u32) -> u32 {
|
||||
match addr {
|
||||
0x20 => {
|
||||
dbg_log!("APIC read id");
|
||||
apic.apic_id
|
||||
},
|
||||
|
||||
0x30 => {
|
||||
// version
|
||||
dbg_log!("APIC read version");
|
||||
0x50014
|
||||
},
|
||||
|
||||
0x80 => {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("APIC read tpr");
|
||||
}
|
||||
apic.tpr
|
||||
},
|
||||
|
||||
0xB0 => {
|
||||
// write-only (written by DSL)
|
||||
dbg_log!("APIC read eio register");
|
||||
0
|
||||
},
|
||||
|
||||
0xD0 => {
|
||||
dbg_log!("Read local destination");
|
||||
apic.local_destination
|
||||
},
|
||||
|
||||
0xE0 => {
|
||||
dbg_log!("Read destination format");
|
||||
apic.destination_format
|
||||
},
|
||||
|
||||
0xF0 => apic.spurious_vector,
|
||||
|
||||
0x100 | 0x110 | 0x120 | 0x130 | 0x140 | 0x150 | 0x160 | 0x170 => {
|
||||
let index = ((addr - 0x100) >> 4) as usize;
|
||||
dbg_log!("Read isr {}: {:08x}", index, apic.isr[index] as u32);
|
||||
apic.isr[index]
|
||||
},
|
||||
|
||||
0x180 | 0x190 | 0x1A0 | 0x1B0 | 0x1C0 | 0x1D0 | 0x1E0 | 0x1F0 => {
|
||||
let index = ((addr - 0x180) >> 4) as usize;
|
||||
dbg_log!("Read tmr {}: {:08x}", index, apic.tmr[index] as u32);
|
||||
apic.tmr[index]
|
||||
},
|
||||
|
||||
0x200 | 0x210 | 0x220 | 0x230 | 0x240 | 0x250 | 0x260 | 0x270 => {
|
||||
let index = ((addr - 0x200) >> 4) as usize;
|
||||
dbg_log!("Read irr {}: {:08x}", index, apic.irr[index] as u32);
|
||||
apic.irr[index]
|
||||
},
|
||||
|
||||
0x280 => {
|
||||
dbg_log!("Read error: {:08x}", apic.read_error);
|
||||
apic.read_error
|
||||
},
|
||||
|
||||
0x300 => {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("APIC read icr0");
|
||||
}
|
||||
apic.icr0
|
||||
},
|
||||
|
||||
0x310 => {
|
||||
dbg_log!("APIC read icr1");
|
||||
apic.icr1
|
||||
},
|
||||
|
||||
0x320 => {
|
||||
dbg_log!("read timer lvt");
|
||||
apic.lvt_timer
|
||||
},
|
||||
|
||||
0x330 => {
|
||||
dbg_log!("read lvt thermal sensor");
|
||||
apic.lvt_thermal_sensor
|
||||
},
|
||||
|
||||
0x340 => {
|
||||
dbg_log!("read lvt perf counter");
|
||||
apic.lvt_perf_counter
|
||||
},
|
||||
|
||||
0x350 => {
|
||||
dbg_log!("read lvt int0");
|
||||
apic.lvt_int0
|
||||
},
|
||||
|
||||
0x360 => {
|
||||
dbg_log!("read lvt int1");
|
||||
apic.lvt_int1
|
||||
},
|
||||
|
||||
0x370 => {
|
||||
dbg_log!("read lvt error");
|
||||
apic.lvt_error
|
||||
},
|
||||
|
||||
0x3E0 => {
|
||||
// divider
|
||||
dbg_log!("read timer divider");
|
||||
apic.timer_divider
|
||||
},
|
||||
|
||||
0x380 => {
|
||||
dbg_log!("read timer initial count");
|
||||
apic.timer_initial_count
|
||||
},
|
||||
|
||||
0x390 => {
|
||||
dbg_log!("read timer current count: {:08x}", apic.timer_current_count);
|
||||
apic.timer_current_count
|
||||
},
|
||||
|
||||
_ => {
|
||||
dbg_log!("APIC read {:x}", addr);
|
||||
dbg_assert!(false);
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write32(addr: u32, value: u32) {
|
||||
if unsafe { !*acpi_enabled } {
|
||||
return;
|
||||
}
|
||||
write32_internal(&mut get_apic(), addr, value)
|
||||
}
|
||||
|
||||
fn write32_internal(apic: &mut Apic, addr: u32, value: u32) {
|
||||
match addr {
|
||||
0x20 => {
|
||||
dbg_log!("APIC write id: {:08x}", value >> 8);
|
||||
apic.apic_id = value;
|
||||
},
|
||||
|
||||
0x30 => {
|
||||
// version
|
||||
dbg_log!("APIC write version: {:08x}, ignored", value);
|
||||
},
|
||||
|
||||
0x80 => {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("Set tpr: {:02x}", value & 0xFF);
|
||||
}
|
||||
apic.tpr = value & 0xFF;
|
||||
},
|
||||
|
||||
0xB0 => {
|
||||
if let Some(highest_isr) = highest_isr(apic) {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("eoi: {:08x} for vector {:x}", value, highest_isr);
|
||||
}
|
||||
register_clear_bit(&mut apic.isr, highest_isr);
|
||||
if register_get_bit(&apic.tmr, highest_isr) {
|
||||
// Send eoi to all IO APICs
|
||||
ioapic::remote_eoi(apic, highest_isr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dbg_log!("Bad eoi: No isr set");
|
||||
}
|
||||
},
|
||||
|
||||
0xD0 => {
|
||||
dbg_log!("Set local destination: {:08x}", value);
|
||||
apic.local_destination = value & 0xFF000000;
|
||||
},
|
||||
|
||||
0xE0 => {
|
||||
dbg_log!("Set destination format: {:08x}", value);
|
||||
apic.destination_format = value | 0xFFFFFF;
|
||||
},
|
||||
|
||||
0xF0 => {
|
||||
dbg_log!("Set spurious vector: {:08x}", value);
|
||||
apic.spurious_vector = value;
|
||||
},
|
||||
|
||||
0x280 => {
|
||||
// updated readable error register with real error
|
||||
dbg_log!("Write error: {:08x}", value);
|
||||
apic.read_error = apic.error;
|
||||
apic.error = 0;
|
||||
},
|
||||
|
||||
0x300 => {
|
||||
let vector = (value & 0xFF) as u8;
|
||||
let delivery_mode = ((value >> 8) & 7) as u8;
|
||||
let destination_mode = ((value >> 11) & 1) as u8;
|
||||
let is_level = value & ioapic::IOAPIC_CONFIG_TRIGGER_MODE_LEVEL
|
||||
== ioapic::IOAPIC_CONFIG_TRIGGER_MODE_LEVEL;
|
||||
let destination_shorthand = (value >> 18) & 3;
|
||||
let destination = (apic.icr1 >> 24) as u8;
|
||||
dbg_log!(
|
||||
"APIC write icr0: {:08x} vector={:02x} destination_mode={} delivery_mode={} destination_shorthand={}",
|
||||
value,
|
||||
vector,
|
||||
DESTINATION_MODES[destination_mode as usize],
|
||||
DELIVERY_MODES[delivery_mode as usize],
|
||||
["no", "self", "all with self", "all without self"][destination_shorthand as usize]
|
||||
);
|
||||
|
||||
let mut value = value;
|
||||
value &= !(1 << 12);
|
||||
apic.icr0 = value;
|
||||
|
||||
if destination_shorthand == 0 {
|
||||
// no shorthand
|
||||
route(
|
||||
apic,
|
||||
vector,
|
||||
delivery_mode,
|
||||
is_level,
|
||||
destination,
|
||||
destination_mode,
|
||||
);
|
||||
}
|
||||
else if destination_shorthand == 1 {
|
||||
// self
|
||||
deliver(apic, vector, IOAPIC_DELIVERY_FIXED, is_level);
|
||||
}
|
||||
else if destination_shorthand == 2 {
|
||||
// all including self
|
||||
deliver(apic, vector, delivery_mode, is_level);
|
||||
}
|
||||
else if destination_shorthand == 3 {
|
||||
// all but self
|
||||
}
|
||||
else {
|
||||
dbg_assert!(false);
|
||||
}
|
||||
},
|
||||
|
||||
0x310 => {
|
||||
dbg_log!("APIC write icr1: {:08x}", value);
|
||||
apic.icr1 = value;
|
||||
},
|
||||
|
||||
0x320 => {
|
||||
dbg_log!("timer lvt: {:08x}", value);
|
||||
apic.lvt_timer = value;
|
||||
},
|
||||
|
||||
0x330 => {
|
||||
dbg_log!("lvt thermal sensor: {:08x}", value);
|
||||
apic.lvt_thermal_sensor = value;
|
||||
},
|
||||
|
||||
0x340 => {
|
||||
dbg_log!("lvt perf counter: {:08x}", value);
|
||||
apic.lvt_perf_counter = value;
|
||||
},
|
||||
|
||||
0x350 => {
|
||||
dbg_log!("lvt int0: {:08x}", value);
|
||||
apic.lvt_int0 = value;
|
||||
},
|
||||
|
||||
0x360 => {
|
||||
dbg_log!("lvt int1: {:08x}", value);
|
||||
apic.lvt_int1 = value;
|
||||
},
|
||||
|
||||
0x370 => {
|
||||
dbg_log!("lvt error: {:08x}", value);
|
||||
apic.lvt_error = value;
|
||||
},
|
||||
|
||||
0x3E0 => {
|
||||
dbg_log!("timer divider: {:08x}", value);
|
||||
apic.timer_divider = value;
|
||||
|
||||
let divide_shift = (value & 0b11) | ((value & 0b1000) >> 1);
|
||||
apic.timer_divider_shift = if divide_shift == 0b111 { 0 } else { divide_shift + 1 };
|
||||
},
|
||||
|
||||
0x380 => {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("timer initial: {:08x}", value);
|
||||
}
|
||||
apic.timer_initial_count = value;
|
||||
apic.timer_current_count = value;
|
||||
|
||||
apic.next_tick = unsafe { js::microtick() };
|
||||
},
|
||||
|
||||
0x390 => {
|
||||
dbg_log!("timer current: {:08x}", value);
|
||||
dbg_assert!(false, "read-only register");
|
||||
},
|
||||
|
||||
_ => {
|
||||
dbg_log!("APIC write32 {:x} <- {:08x}", addr, value);
|
||||
dbg_assert!(false);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn apic_timer(now: f64) -> f64 { timer(&mut get_apic(), now) }
|
||||
|
||||
fn timer(apic: &mut Apic, now: f64) -> f64 {
|
||||
if apic.timer_current_count == 0 {
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
let freq = APIC_TIMER_FREQ / (1 << apic.timer_divider_shift) as f64;
|
||||
let steps = ((now - apic.next_tick) * freq).trunc() as u32;
|
||||
apic.next_tick += steps as f64 / freq;
|
||||
|
||||
match apic.timer_current_count.checked_sub(steps) {
|
||||
Some(t) => apic.timer_current_count = if t == 0 { 1 } else { t },
|
||||
None => {
|
||||
let mode = apic.lvt_timer & APIC_TIMER_MODE_MASK;
|
||||
|
||||
if mode == APIC_TIMER_MODE_PERIODIC {
|
||||
apic.timer_current_count = apic.timer_initial_count; // XXX
|
||||
|
||||
if apic.timer_current_count == 0 {
|
||||
apic.timer_current_count += apic.timer_initial_count;
|
||||
}
|
||||
dbg_assert!(apic.timer_current_count != 0);
|
||||
|
||||
if apic.lvt_timer & IOAPIC_CONFIG_MASKED == 0 {
|
||||
deliver(
|
||||
apic,
|
||||
(apic.lvt_timer & 0xFF) as u8,
|
||||
IOAPIC_DELIVERY_FIXED,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
else if mode == APIC_TIMER_MODE_ONE_SHOT {
|
||||
apic.timer_current_count = 0;
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("APIC timer one shot end");
|
||||
}
|
||||
|
||||
if apic.lvt_timer & IOAPIC_CONFIG_MASKED == 0 {
|
||||
deliver(
|
||||
apic,
|
||||
(apic.lvt_timer & 0xFF) as u8,
|
||||
IOAPIC_DELIVERY_FIXED,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
dbg_assert!(false, "apic unimplemented timer mode: {:x}", mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(apic.timer_current_count as f64 / freq).max(0.0)
|
||||
}
|
||||
|
||||
pub fn route(
|
||||
apic: &mut Apic,
|
||||
vector: u8,
|
||||
mode: u8,
|
||||
is_level: bool,
|
||||
_destination: u8,
|
||||
_destination_mode: u8,
|
||||
) {
|
||||
// TODO
|
||||
deliver(apic, vector, mode, is_level);
|
||||
}
|
||||
|
||||
fn deliver(apic: &mut Apic, vector: u8, mode: u8, is_level: bool) {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("Deliver {:02x} mode={} level={}", vector, mode, is_level);
|
||||
}
|
||||
|
||||
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 register_get_bit(&apic.irr, vector) {
|
||||
dbg_log!("Not delivered: irr already set, vector={:02x}", vector);
|
||||
return;
|
||||
}
|
||||
|
||||
register_set_bit(&mut apic.irr, vector);
|
||||
|
||||
if is_level {
|
||||
register_set_bit(&mut apic.tmr, vector);
|
||||
}
|
||||
else {
|
||||
register_clear_bit(&mut apic.tmr, vector);
|
||||
}
|
||||
}
|
||||
|
||||
fn highest_irr(apic: &mut Apic) -> Option<u8> {
|
||||
let highest = register_get_highest_bit(&apic.irr);
|
||||
if let Some(x) = highest {
|
||||
dbg_assert!(x >= 0x10);
|
||||
dbg_assert!(x != 0xFF);
|
||||
}
|
||||
highest
|
||||
}
|
||||
|
||||
fn highest_isr(apic: &mut Apic) -> Option<u8> {
|
||||
let highest = register_get_highest_bit(&apic.isr);
|
||||
if let Some(x) = highest {
|
||||
dbg_assert!(x >= 0x10);
|
||||
dbg_assert!(x != 0xFF);
|
||||
}
|
||||
highest
|
||||
}
|
||||
|
||||
pub fn acknowledge_irq() -> Option<u8> { acknowledge_irq_internal(&mut get_apic()) }
|
||||
|
||||
fn acknowledge_irq_internal(apic: &mut Apic) -> Option<u8> {
|
||||
let highest_irr = match highest_irr(apic) {
|
||||
None => return None,
|
||||
Some(x) => x,
|
||||
};
|
||||
|
||||
if let Some(highest_isr) = highest_isr(apic) {
|
||||
if highest_isr >= highest_irr {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("Higher isr, isr={:x} irr={:x}", highest_isr, highest_irr);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if highest_irr & 0xF0 <= apic.tpr as u8 & 0xF0 {
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!(
|
||||
"Higher tpr, tpr={:x} irr={:x}",
|
||||
apic.tpr & 0xF0,
|
||||
highest_irr
|
||||
);
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
register_clear_bit(&mut apic.irr, highest_irr);
|
||||
register_set_bit(&mut apic.isr, highest_irr);
|
||||
|
||||
if APIC_LOG_VERBOSE {
|
||||
dbg_log!("Calling vector {:x}", highest_irr);
|
||||
}
|
||||
|
||||
dbg_assert!(acknowledge_irq_internal(apic).is_none());
|
||||
|
||||
Some(highest_irr)
|
||||
}
|
||||
|
||||
// functions operating on 256-bit registers (for irr, isr, tmr)
|
||||
fn register_get_bit(v: &[u32; 8], bit: u8) -> bool { v[(bit >> 5) as usize] & 1 << (bit & 31) != 0 }
|
||||
|
||||
fn register_set_bit(v: &mut [u32; 8], bit: u8) { v[(bit >> 5) as usize] |= 1 << (bit & 31); }
|
||||
|
||||
fn register_clear_bit(v: &mut [u32; 8], bit: u8) { v[(bit >> 5) as usize] &= !(1 << (bit & 31)); }
|
||||
|
||||
fn register_get_highest_bit(v: &[u32; 8]) -> Option<u8> {
|
||||
for i in (0..8).rev() {
|
||||
let word = v[i];
|
||||
|
||||
if word != 0 {
|
||||
return Some(word.ilog2() as u8 | (i as u8) << 5);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
|
@ -1,23 +1,5 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
|
||||
extern "C" {
|
||||
fn cpu_exception_hook(interrupt: i32) -> bool;
|
||||
fn call_indirect1(f: i32, x: u16);
|
||||
pub fn microtick() -> f64;
|
||||
pub fn run_hardware_timers(acpi_enabled: bool, t: f64) -> f64;
|
||||
pub fn cpu_event_halt();
|
||||
pub fn apic_acknowledge_irq() -> i32;
|
||||
pub fn stop_idling();
|
||||
|
||||
pub fn io_port_read8(port: i32) -> i32;
|
||||
pub fn io_port_read16(port: i32) -> i32;
|
||||
pub fn io_port_read32(port: i32) -> i32;
|
||||
|
||||
pub fn io_port_write8(port: i32, value: i32);
|
||||
pub fn io_port_write16(port: i32, value: i32);
|
||||
pub fn io_port_write32(port: i32, value: i32);
|
||||
}
|
||||
|
||||
use crate::config;
|
||||
use crate::cpu::fpu::fpu_set_tag_word;
|
||||
use crate::cpu::global_pointers::*;
|
||||
|
|
@ -27,7 +9,7 @@ use crate::cpu::misc_instr::{
|
|||
push16, push32,
|
||||
};
|
||||
use crate::cpu::modrm::{resolve_modrm16, resolve_modrm32};
|
||||
use crate::cpu::pic;
|
||||
use crate::cpu::{apic, ioapic, pic};
|
||||
use crate::dbg::dbg_trace;
|
||||
use crate::gen;
|
||||
use crate::jit;
|
||||
|
|
@ -44,6 +26,32 @@ use crate::state_flags::CachedStateFlags;
|
|||
use std::collections::HashSet;
|
||||
use std::ptr;
|
||||
|
||||
mod wasm {
|
||||
extern "C" {
|
||||
pub fn call_indirect1(f: i32, x: u16);
|
||||
}
|
||||
}
|
||||
|
||||
pub mod js {
|
||||
extern "C" {
|
||||
pub fn cpu_exception_hook(interrupt: i32) -> bool;
|
||||
pub fn microtick() -> f64;
|
||||
pub fn run_hardware_timers(acpi_enabled: bool, t: f64) -> f64;
|
||||
pub fn cpu_event_halt();
|
||||
pub fn stop_idling();
|
||||
|
||||
pub fn io_port_read8(port: i32) -> i32;
|
||||
pub fn io_port_read16(port: i32) -> i32;
|
||||
pub fn io_port_read32(port: i32) -> i32;
|
||||
|
||||
pub fn io_port_write8(port: i32, value: i32);
|
||||
pub fn io_port_write16(port: i32, value: i32);
|
||||
pub fn io_port_write32(port: i32, value: i32);
|
||||
|
||||
pub fn get_rand_int() -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
/// The offset for our generated functions in the wasm table. Every index less than this is
|
||||
/// reserved for rustc's indirect functions
|
||||
pub const WASM_TABLE_OFFSET: u32 = 1024;
|
||||
|
|
@ -235,7 +243,10 @@ pub const IA32_APIC_BASE_BSP: i32 = 1 << 8;
|
|||
pub const IA32_APIC_BASE_EXTD: i32 = 1 << 10;
|
||||
pub const IA32_APIC_BASE_EN: i32 = 1 << 11;
|
||||
|
||||
pub const APIC_ADDRESS: i32 = 0xFEE00000u32 as i32;
|
||||
pub const IOAPIC_MEM_ADDRESS: u32 = 0xFEC00000;
|
||||
pub const IOAPIC_MEM_SIZE: u32 = 32;
|
||||
pub const APIC_MEM_ADDRESS: u32 = 0xFEE00000;
|
||||
pub const APIC_MEM_SIZE: u32 = 0x1000;
|
||||
|
||||
pub const MXCSR_MASK: i32 = 0xffff;
|
||||
pub const MXCSR_FZ: i32 = 1 << 15;
|
||||
|
|
@ -2226,7 +2237,7 @@ pub unsafe fn trigger_fault_end_jit() {
|
|||
#[allow(static_mut_refs)]
|
||||
let (code, error_code) = jit_fault.take().unwrap();
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(code) {
|
||||
if js::cpu_exception_hook(code) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2950,7 +2961,7 @@ pub unsafe fn cycle_internal() {
|
|||
{
|
||||
in_jit = true;
|
||||
}
|
||||
call_indirect1(
|
||||
wasm::call_indirect1(
|
||||
wasm_table_index as i32 + WASM_TABLE_OFFSET as i32,
|
||||
initial_state,
|
||||
);
|
||||
|
|
@ -3135,11 +3146,11 @@ pub unsafe fn segment_prefix_op(seg: i32) {
|
|||
pub unsafe fn main_loop() -> f64 {
|
||||
profiler::stat_increment(stat::MAIN_LOOP);
|
||||
|
||||
let start = microtick();
|
||||
let start = js::microtick();
|
||||
|
||||
if *in_hlt {
|
||||
if *flags & FLAG_INTERRUPT != 0 {
|
||||
let t = run_hardware_timers(*acpi_enabled, start);
|
||||
let t = js::run_hardware_timers(*acpi_enabled, start);
|
||||
handle_irqs();
|
||||
if *in_hlt {
|
||||
profiler::stat_increment(stat::MAIN_LOOP_IDLE);
|
||||
|
|
@ -3155,8 +3166,8 @@ pub unsafe fn main_loop() -> f64 {
|
|||
loop {
|
||||
do_many_cycles_native();
|
||||
|
||||
let now = microtick();
|
||||
let t = run_hardware_timers(*acpi_enabled, now);
|
||||
let now = js::microtick();
|
||||
let t = js::run_hardware_timers(*acpi_enabled, now);
|
||||
handle_irqs();
|
||||
if *in_hlt {
|
||||
return t;
|
||||
|
|
@ -3185,7 +3196,7 @@ pub unsafe fn trigger_de() {
|
|||
dbg_log!("#de");
|
||||
*instruction_pointer = *previous_ip;
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(CPU_EXCEPTION_DE) {
|
||||
if js::cpu_exception_hook(CPU_EXCEPTION_DE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -3198,7 +3209,7 @@ pub unsafe fn trigger_ud() {
|
|||
dbg_trace();
|
||||
*instruction_pointer = *previous_ip;
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(CPU_EXCEPTION_UD) {
|
||||
if js::cpu_exception_hook(CPU_EXCEPTION_UD) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -3211,7 +3222,7 @@ pub unsafe fn trigger_nm() {
|
|||
dbg_trace();
|
||||
*instruction_pointer = *previous_ip;
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(CPU_EXCEPTION_NM) {
|
||||
if js::cpu_exception_hook(CPU_EXCEPTION_NM) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -3223,7 +3234,7 @@ pub unsafe fn trigger_gp(code: i32) {
|
|||
dbg_log!("#gp");
|
||||
*instruction_pointer = *previous_ip;
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(CPU_EXCEPTION_GP) {
|
||||
if js::cpu_exception_hook(CPU_EXCEPTION_GP) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -4093,7 +4104,7 @@ pub unsafe fn set_tsc(low: u32, high: u32) {
|
|||
|
||||
#[no_mangle]
|
||||
pub unsafe fn read_tsc() -> u64 {
|
||||
let value = (microtick() * TSC_RATE) as u64 - tsc_offset;
|
||||
let value = (js::microtick() * TSC_RATE) as u64 - tsc_offset;
|
||||
|
||||
if !TSC_ENABLE_IMPRECISE_BROWSER_WORKAROUND {
|
||||
return value;
|
||||
|
|
@ -4278,7 +4289,7 @@ pub unsafe fn trigger_np(code: i32) {
|
|||
dbg_log!("#np");
|
||||
*instruction_pointer = *previous_ip;
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(CPU_EXCEPTION_NP) {
|
||||
if js::cpu_exception_hook(CPU_EXCEPTION_NP) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -4290,7 +4301,7 @@ pub unsafe fn trigger_ss(code: i32) {
|
|||
dbg_log!("#ss");
|
||||
*instruction_pointer = *previous_ip;
|
||||
if DEBUG {
|
||||
if cpu_exception_hook(CPU_EXCEPTION_SS) {
|
||||
if js::cpu_exception_hook(CPU_EXCEPTION_SS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -4301,17 +4312,14 @@ pub unsafe fn trigger_ss(code: i32) {
|
|||
pub unsafe fn store_current_tsc() { *current_tsc = read_tsc(); }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn handle_irqs() { handle_irqs_internal(&mut pic::get_pic()) }
|
||||
|
||||
pub unsafe fn handle_irqs_internal(pic: &mut pic::Pic) {
|
||||
pub unsafe fn handle_irqs() {
|
||||
if *flags & FLAG_INTERRUPT != 0 {
|
||||
if let Some(irq) = pic::pic_acknowledge_irq(pic) {
|
||||
if let Some(irq) = pic::pic_acknowledge_irq() {
|
||||
pic_call_irq(irq)
|
||||
}
|
||||
else if *acpi_enabled {
|
||||
let irq = apic_acknowledge_irq();
|
||||
if irq >= 0 {
|
||||
pic_call_irq(irq as u8)
|
||||
if let Some(irq) = apic::acknowledge_irq() {
|
||||
pic_call_irq(irq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4320,12 +4328,68 @@ pub unsafe fn handle_irqs_internal(pic: &mut pic::Pic) {
|
|||
unsafe fn pic_call_irq(interrupt_nr: u8) {
|
||||
*previous_ip = *instruction_pointer; // XXX: What if called after instruction (port IO)
|
||||
if *in_hlt {
|
||||
stop_idling();
|
||||
js::stop_idling();
|
||||
*in_hlt = false;
|
||||
}
|
||||
call_interrupt_vector(interrupt_nr as i32, false, None);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn device_raise_irq(i: u8) {
|
||||
pic::set_irq(i);
|
||||
if *acpi_enabled {
|
||||
ioapic::set_irq(i);
|
||||
}
|
||||
handle_irqs()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn device_lower_irq(i: u8) {
|
||||
pic::clear_irq(i);
|
||||
if *acpi_enabled {
|
||||
ioapic::clear_irq(i);
|
||||
}
|
||||
handle_irqs()
|
||||
}
|
||||
|
||||
pub fn io_port_read8(port: i32) -> i32 {
|
||||
unsafe {
|
||||
match port {
|
||||
0x20 => pic::port20_read() as i32,
|
||||
0x21 => pic::port21_read() as i32,
|
||||
0xA0 => pic::portA0_read() as i32,
|
||||
0xA1 => pic::portA1_read() as i32,
|
||||
0x4D0 => pic::port4D0_read() as i32,
|
||||
0x4D1 => pic::port4D1_read() as i32,
|
||||
_ => js::io_port_read8(port),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn io_port_read16(port: i32) -> i32 { unsafe { js::io_port_read16(port) } }
|
||||
pub fn io_port_read32(port: i32) -> i32 { unsafe { js::io_port_read32(port) } }
|
||||
|
||||
pub fn io_port_write8(port: i32, value: i32) {
|
||||
unsafe {
|
||||
match port {
|
||||
0x20 | 0x21 | 0xA0 | 0xA1 | 0x4D0 | 0x4D1 => {
|
||||
match port {
|
||||
0x20 => pic::port20_write(value as u8),
|
||||
0x21 => pic::port21_write(value as u8),
|
||||
0xA0 => pic::portA0_write(value as u8),
|
||||
0xA1 => pic::portA1_write(value as u8),
|
||||
0x4D0 => pic::port4D0_write(value as u8),
|
||||
0x4D1 => pic::port4D1_write(value as u8),
|
||||
_ => dbg_assert!(false),
|
||||
};
|
||||
handle_irqs()
|
||||
},
|
||||
_ => js::io_port_write8(port, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn io_port_write16(port: i32, value: i32) { unsafe { js::io_port_write16(port, value) } }
|
||||
pub fn io_port_write32(port: i32, value: i32) { unsafe { js::io_port_write32(port, value) } }
|
||||
|
||||
#[no_mangle]
|
||||
#[cfg(debug_assertions)]
|
||||
pub unsafe fn check_page_switch(block_addr: u32, next_block_addr: u32) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::cpu::arith::*;
|
||||
use crate::cpu::cpu::js;
|
||||
use crate::cpu::cpu::*;
|
||||
use crate::cpu::fpu::*;
|
||||
use crate::cpu::global_pointers::*;
|
||||
|
|
@ -2162,12 +2163,12 @@ pub unsafe fn instr_F4() {
|
|||
// due it will immediately call call_interrupt_vector and continue
|
||||
// execution without an unnecessary cycle through do_run
|
||||
if *flags & FLAG_INTERRUPT != 0 {
|
||||
run_hardware_timers(*acpi_enabled, microtick());
|
||||
js::run_hardware_timers(*acpi_enabled, js::microtick());
|
||||
handle_irqs();
|
||||
}
|
||||
else {
|
||||
// execution can never resume (until NMIs are supported)
|
||||
cpu_event_halt();
|
||||
js::cpu_event_halt();
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
extern "C" {
|
||||
fn get_rand_int() -> i32;
|
||||
}
|
||||
|
||||
unsafe fn undefined_instruction() {
|
||||
dbg_assert!(false, "Undefined instructions");
|
||||
trigger_ud()
|
||||
|
|
@ -1207,7 +1203,7 @@ pub unsafe fn instr_0F30() {
|
|||
);
|
||||
let address = low & !(IA32_APIC_BASE_BSP | IA32_APIC_BASE_EXTD | IA32_APIC_BASE_EN);
|
||||
dbg_assert!(
|
||||
address == APIC_ADDRESS,
|
||||
address == APIC_MEM_ADDRESS as i32,
|
||||
"Changing APIC address not supported"
|
||||
);
|
||||
dbg_assert!(low & IA32_APIC_BASE_EXTD == 0, "x2apic not supported");
|
||||
|
|
@ -1285,7 +1281,7 @@ pub unsafe fn instr_0F32() {
|
|||
IA32_PLATFORM_ID => {},
|
||||
IA32_APIC_BASE => {
|
||||
if *acpi_enabled {
|
||||
low = APIC_ADDRESS;
|
||||
low = APIC_MEM_ADDRESS as i32;
|
||||
if *apic_enabled {
|
||||
low |= IA32_APIC_BASE_EN
|
||||
}
|
||||
|
|
@ -3943,7 +3939,7 @@ pub unsafe fn instr32_0FC7_1_mem(addr: i32) { instr16_0FC7_1_mem(addr) }
|
|||
#[no_mangle]
|
||||
pub unsafe fn instr16_0FC7_6_reg(r: i32) {
|
||||
// rdrand
|
||||
let rand = get_rand_int();
|
||||
let rand = js::get_rand_int();
|
||||
write_reg16(r, rand);
|
||||
*flags &= !FLAGS_ALL;
|
||||
*flags |= 1;
|
||||
|
|
@ -3952,7 +3948,7 @@ pub unsafe fn instr16_0FC7_6_reg(r: i32) {
|
|||
#[no_mangle]
|
||||
pub unsafe fn instr32_0FC7_6_reg(r: i32) {
|
||||
// rdrand
|
||||
let rand = get_rand_int();
|
||||
let rand = js::get_rand_int();
|
||||
write_reg32(r, rand);
|
||||
*flags &= !FLAGS_ALL;
|
||||
*flags |= 1;
|
||||
|
|
|
|||
316
src/rust/cpu/ioapic.rs
Normal file
316
src/rust/cpu/ioapic.rs
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
// http://download.intel.com/design/chipsets/datashts/29056601.pdf
|
||||
|
||||
use crate::cpu::{apic, global_pointers::acpi_enabled};
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
const IOAPIC_LOG_VERBOSE: bool = false;
|
||||
|
||||
const IOREGSEL: u32 = 0;
|
||||
const IOWIN: u32 = 0x10;
|
||||
|
||||
const IOAPIC_IRQ_COUNT: usize = 24;
|
||||
|
||||
const IOAPIC_FIRST_IRQ_REG: u32 = 0x10;
|
||||
const IOAPIC_LAST_IRQ_REG: u32 = 0x10 + 2 * IOAPIC_IRQ_COUNT as u32;
|
||||
|
||||
const IOAPIC_ID: u32 = 0; // must match value in seabios
|
||||
|
||||
pub const IOAPIC_CONFIG_TRIGGER_MODE_LEVEL: u32 = 1 << 15;
|
||||
|
||||
const IOAPIC_CONFIG_MASKED: u32 = 1 << 16;
|
||||
const IOAPIC_CONFIG_DELIVS: u32 = 1 << 12;
|
||||
const IOAPIC_CONFIG_REMOTE_IRR: u32 = 1 << 14;
|
||||
const IOAPIC_CONFIG_READONLY_MASK: u32 =
|
||||
IOAPIC_CONFIG_REMOTE_IRR | IOAPIC_CONFIG_DELIVS | 0xFFFE0000;
|
||||
|
||||
const IOAPIC_DELIVERY_FIXED: u8 = 0;
|
||||
const IOAPIC_DELIVERY_LOWEST_PRIORITY: u8 = 1;
|
||||
const _IOAPIC_DELIVERY_NMI: u8 = 4;
|
||||
const _IOAPIC_DELIVERY_INIT: u8 = 5;
|
||||
|
||||
const DELIVERY_MODES: [&str; 8] = [
|
||||
"Fixed (0)",
|
||||
"Lowest Prio (1)",
|
||||
"SMI (2)",
|
||||
"Reserved (3)",
|
||||
"NMI (4)",
|
||||
"INIT (5)",
|
||||
"Reserved (6)",
|
||||
"ExtINT (7)",
|
||||
];
|
||||
|
||||
const DESTINATION_MODES: [&str; 2] = ["physical", "logical"];
|
||||
|
||||
// keep in sync with cpu.js
|
||||
#[allow(dead_code)]
|
||||
const IOAPIC_STRUCT_SIZE: usize = 4 * 52;
|
||||
|
||||
// Note: JavaScript (cpu.get_state_apic) depens on this layout
|
||||
const _: () = assert!(std::mem::offset_of!(Ioapic, ioredtbl_destination) == 24 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Ioapic, ioregsel) == 48 * 4);
|
||||
const _: () = assert!(std::mem::offset_of!(Ioapic, irq_value) == 51 * 4);
|
||||
const _: () = assert!(std::mem::size_of::<Ioapic>() == IOAPIC_STRUCT_SIZE);
|
||||
#[repr(C)]
|
||||
struct Ioapic {
|
||||
ioredtbl_config: [u32; IOAPIC_IRQ_COUNT],
|
||||
ioredtbl_destination: [u32; IOAPIC_IRQ_COUNT],
|
||||
ioregsel: u32,
|
||||
ioapic_id: u32,
|
||||
irr: u32,
|
||||
irq_value: u32,
|
||||
}
|
||||
|
||||
static IOAPIC: Mutex<Ioapic> = Mutex::new(Ioapic {
|
||||
ioredtbl_config: [IOAPIC_CONFIG_MASKED; IOAPIC_IRQ_COUNT],
|
||||
ioredtbl_destination: [0; IOAPIC_IRQ_COUNT],
|
||||
ioregsel: 0,
|
||||
ioapic_id: IOAPIC_ID,
|
||||
irr: 0,
|
||||
irq_value: 0,
|
||||
});
|
||||
|
||||
fn get_ioapic() -> MutexGuard<'static, Ioapic> { IOAPIC.try_lock().unwrap() }
|
||||
|
||||
#[no_mangle]
|
||||
pub fn get_ioapic_addr() -> u32 { &raw mut *get_ioapic() as u32 }
|
||||
|
||||
pub fn remote_eoi(apic: &mut apic::Apic, vector: u8) {
|
||||
remote_eoi_internal(&mut get_ioapic(), apic, vector);
|
||||
}
|
||||
|
||||
fn remote_eoi_internal(ioapic: &mut Ioapic, apic: &mut apic::Apic, vector: u8) {
|
||||
for i in 0..IOAPIC_IRQ_COUNT as u8 {
|
||||
let config = ioapic.ioredtbl_config[i as usize];
|
||||
|
||||
if (config & 0xFF) as u8 == vector && config & IOAPIC_CONFIG_REMOTE_IRR != 0 {
|
||||
dbg_log!("Clear remote IRR for irq={:x}", i);
|
||||
ioapic.ioredtbl_config[i as usize] &= !IOAPIC_CONFIG_REMOTE_IRR;
|
||||
check_irq(ioapic, apic, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_irq(ioapic: &mut Ioapic, apic: &mut apic::Apic, irq: u8) {
|
||||
let mask = 1 << irq;
|
||||
|
||||
if ioapic.irr & mask == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let config = ioapic.ioredtbl_config[irq as usize];
|
||||
|
||||
if config & IOAPIC_CONFIG_MASKED == 0 {
|
||||
let delivery_mode = ((config >> 8) & 7) as u8;
|
||||
let destination_mode = ((config >> 11) & 1) as u8;
|
||||
let vector = (config & 0xFF) as u8;
|
||||
let destination = (ioapic.ioredtbl_destination[irq as usize] >> 24) as u8;
|
||||
let is_level =
|
||||
config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL == IOAPIC_CONFIG_TRIGGER_MODE_LEVEL;
|
||||
|
||||
if config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL == 0 {
|
||||
ioapic.irr &= !mask;
|
||||
}
|
||||
else {
|
||||
ioapic.ioredtbl_config[irq as usize] |= IOAPIC_CONFIG_REMOTE_IRR;
|
||||
|
||||
if config & IOAPIC_CONFIG_REMOTE_IRR != 0 {
|
||||
dbg_log!("No route: level interrupt and remote IRR still set");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if delivery_mode == IOAPIC_DELIVERY_FIXED
|
||||
|| delivery_mode == IOAPIC_DELIVERY_LOWEST_PRIORITY
|
||||
{
|
||||
apic::route(
|
||||
apic,
|
||||
vector,
|
||||
delivery_mode,
|
||||
is_level,
|
||||
destination,
|
||||
destination_mode,
|
||||
);
|
||||
}
|
||||
else {
|
||||
dbg_assert!(false, "TODO");
|
||||
}
|
||||
|
||||
ioapic.ioredtbl_config[irq as usize] &= !IOAPIC_CONFIG_DELIVS;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_irq(i: u8) { set_irq_internal(&mut get_ioapic(), &mut apic::get_apic(), i) }
|
||||
|
||||
fn set_irq_internal(ioapic: &mut Ioapic, apic: &mut apic::Apic, i: u8) {
|
||||
if i as usize >= IOAPIC_IRQ_COUNT {
|
||||
dbg_assert!(false, "Bad irq: {}", i);
|
||||
return;
|
||||
}
|
||||
|
||||
let mask = 1 << i;
|
||||
|
||||
if ioapic.irq_value & mask == 0 {
|
||||
if IOAPIC_LOG_VERBOSE {
|
||||
dbg_log!("apic set irq {}", i);
|
||||
}
|
||||
|
||||
ioapic.irq_value |= mask;
|
||||
|
||||
let config = ioapic.ioredtbl_config[i as usize];
|
||||
if config & (IOAPIC_CONFIG_TRIGGER_MODE_LEVEL | IOAPIC_CONFIG_MASKED)
|
||||
== IOAPIC_CONFIG_MASKED
|
||||
{
|
||||
// edge triggered and masked
|
||||
return;
|
||||
}
|
||||
|
||||
ioapic.irr |= mask;
|
||||
|
||||
check_irq(ioapic, apic, i);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_irq(i: u8) { clear_irq_internal(&mut get_ioapic(), i) }
|
||||
|
||||
fn clear_irq_internal(ioapic: &mut Ioapic, i: u8) {
|
||||
if i as usize >= IOAPIC_IRQ_COUNT {
|
||||
dbg_assert!(false, "Bad irq: {}", i);
|
||||
return;
|
||||
}
|
||||
|
||||
let mask = 1 << i;
|
||||
|
||||
if ioapic.irq_value & mask == mask {
|
||||
ioapic.irq_value &= !mask;
|
||||
|
||||
let config = ioapic.ioredtbl_config[i as usize];
|
||||
if config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL != 0 {
|
||||
ioapic.irr &= !mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read32(addr: u32) -> u32 {
|
||||
if unsafe { !*acpi_enabled } {
|
||||
return 0;
|
||||
}
|
||||
read32_internal(&mut get_ioapic(), addr)
|
||||
}
|
||||
|
||||
fn read32_internal(ioapic: &mut Ioapic, addr: u32) -> u32 {
|
||||
match addr {
|
||||
IOREGSEL => ioapic.ioregsel,
|
||||
IOWIN => match ioapic.ioregsel {
|
||||
0 => {
|
||||
dbg_log!("IOAPIC Read id");
|
||||
ioapic.ioapic_id << 24
|
||||
},
|
||||
1 => {
|
||||
dbg_log!("IOAPIC Read version");
|
||||
0x11 | (IOAPIC_IRQ_COUNT as u32 - 1) << 16
|
||||
},
|
||||
2 => {
|
||||
dbg_log!("IOAPIC Read arbitration id");
|
||||
ioapic.ioapic_id << 24
|
||||
},
|
||||
IOAPIC_FIRST_IRQ_REG..IOAPIC_LAST_IRQ_REG => {
|
||||
let irq = ((ioapic.ioregsel - IOAPIC_FIRST_IRQ_REG) >> 1) as u8;
|
||||
let index = ioapic.ioregsel & 1;
|
||||
|
||||
if index != 0 {
|
||||
let value = ioapic.ioredtbl_destination[irq as usize];
|
||||
dbg_log!("IOAPIC Read destination irq={:x} -> {:08x}", irq, value);
|
||||
value
|
||||
}
|
||||
else {
|
||||
let value = ioapic.ioredtbl_config[irq as usize];
|
||||
dbg_log!("IOAPIC Read config irq={:x} -> {:08x}", irq, value);
|
||||
value
|
||||
}
|
||||
},
|
||||
reg => {
|
||||
dbg_assert!(false, "IOAPIC register read outside of range {:x}", reg);
|
||||
0
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
dbg_assert!(false, "Unaligned or oob IOAPIC memory read: {:x}", addr);
|
||||
0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write32(addr: u32, value: u32) {
|
||||
if unsafe { !*acpi_enabled } {
|
||||
return;
|
||||
}
|
||||
write32_internal(&mut get_ioapic(), &mut apic::get_apic(), addr, value)
|
||||
}
|
||||
|
||||
fn write32_internal(ioapic: &mut Ioapic, apic: &mut apic::Apic, addr: u32, value: u32) {
|
||||
//dbg_log!("IOAPIC write {:x} <- {:08x}", reg, value);
|
||||
|
||||
match addr {
|
||||
IOREGSEL => ioapic.ioregsel = value,
|
||||
IOWIN => match ioapic.ioregsel {
|
||||
0 => ioapic.ioapic_id = (value >> 24) & 0x0F,
|
||||
1 | 2 => {
|
||||
dbg_log!("IOAPIC Invalid write: {}", ioapic.ioregsel);
|
||||
},
|
||||
IOAPIC_FIRST_IRQ_REG..IOAPIC_LAST_IRQ_REG => {
|
||||
let irq = ((ioapic.ioregsel - IOAPIC_FIRST_IRQ_REG) >> 1) as u8;
|
||||
let index = ioapic.ioregsel & 1;
|
||||
|
||||
if index != 0 {
|
||||
dbg_log!(
|
||||
"Write destination {:08x} irq={:x} dest={:02x}",
|
||||
value,
|
||||
irq,
|
||||
value >> 24
|
||||
);
|
||||
ioapic.ioredtbl_destination[irq as usize] = value & 0xFF000000;
|
||||
}
|
||||
else {
|
||||
let old_value = ioapic.ioredtbl_config[irq as usize] as u32;
|
||||
ioapic.ioredtbl_config[irq as usize] = (value & !IOAPIC_CONFIG_READONLY_MASK)
|
||||
| (old_value & IOAPIC_CONFIG_READONLY_MASK);
|
||||
|
||||
let vector = value & 0xFF;
|
||||
let delivery_mode = (value >> 8) & 7;
|
||||
let destination_mode = (value >> 11) & 1;
|
||||
let is_level = (value >> 15) & 1;
|
||||
let disabled = (value >> 16) & 1;
|
||||
|
||||
dbg_log!(
|
||||
"Write config {:08x} irq={:x} vector={:02x} deliverymode={} destmode={} is_level={} disabled={}",
|
||||
value,
|
||||
irq,
|
||||
vector,
|
||||
DELIVERY_MODES[delivery_mode as usize],
|
||||
DESTINATION_MODES[destination_mode as usize],
|
||||
is_level,
|
||||
disabled
|
||||
);
|
||||
|
||||
check_irq(ioapic, apic, irq);
|
||||
}
|
||||
},
|
||||
reg => {
|
||||
dbg_assert!(
|
||||
false,
|
||||
"IOAPIC register write outside of range {:x} <- {:x}",
|
||||
reg,
|
||||
value
|
||||
)
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
dbg_assert!(
|
||||
false,
|
||||
"Unaligned or oob IOAPIC memory write: {:x} <- {:x}",
|
||||
addr,
|
||||
value
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,12 @@ mod ext {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::cpu::cpu::reg128;
|
||||
use crate::cpu::apic;
|
||||
use crate::cpu::cpu::{
|
||||
handle_irqs, reg128, APIC_MEM_ADDRESS, APIC_MEM_SIZE, IOAPIC_MEM_ADDRESS, IOAPIC_MEM_SIZE,
|
||||
};
|
||||
use crate::cpu::global_pointers::memory_size;
|
||||
use crate::cpu::ioapic;
|
||||
use crate::cpu::vga;
|
||||
use crate::jit;
|
||||
use crate::page::Page;
|
||||
|
|
@ -122,6 +126,12 @@ pub fn read32s(addr: u32) -> i32 {
|
|||
ptr::read_unaligned(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *const i32)
|
||||
} // XXX
|
||||
}
|
||||
else if addr >= APIC_MEM_ADDRESS && addr < APIC_MEM_ADDRESS + APIC_MEM_SIZE {
|
||||
apic::read32(addr - APIC_MEM_ADDRESS) as i32
|
||||
}
|
||||
else if addr >= IOAPIC_MEM_ADDRESS && addr < IOAPIC_MEM_ADDRESS + IOAPIC_MEM_SIZE {
|
||||
ioapic::read32(addr - IOAPIC_MEM_ADDRESS) as i32
|
||||
}
|
||||
else {
|
||||
unsafe { ext::mmap_read32(addr) }
|
||||
}
|
||||
|
|
@ -206,7 +216,7 @@ pub unsafe fn write32(addr: u32, value: i32) {
|
|||
else {
|
||||
jit::jit_dirty_cache_small(addr, addr + 4);
|
||||
write32_no_mmap_or_dirty_check(addr, value);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn write32_no_mmap_or_dirty_check(addr: u32, value: i32) {
|
||||
|
|
@ -276,6 +286,14 @@ pub unsafe fn mmap_write32(addr: u32, value: i32) {
|
|||
value,
|
||||
)
|
||||
}
|
||||
else if addr >= APIC_MEM_ADDRESS && addr < APIC_MEM_ADDRESS + APIC_MEM_SIZE {
|
||||
apic::write32(addr - APIC_MEM_ADDRESS, value as u32);
|
||||
handle_irqs();
|
||||
}
|
||||
else if addr >= IOAPIC_MEM_ADDRESS && addr < IOAPIC_MEM_ADDRESS + IOAPIC_MEM_SIZE {
|
||||
ioapic::write32(addr - IOAPIC_MEM_ADDRESS, value as u32);
|
||||
handle_irqs();
|
||||
}
|
||||
else {
|
||||
ext::mmap_write32(addr, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod apic;
|
||||
pub mod arith;
|
||||
pub mod call_indirect;
|
||||
pub mod cpu;
|
||||
|
|
@ -5,6 +6,7 @@ pub mod fpu;
|
|||
pub mod global_pointers;
|
||||
pub mod instructions;
|
||||
pub mod instructions_0f;
|
||||
pub mod ioapic;
|
||||
pub mod memory;
|
||||
pub mod misc_instr;
|
||||
pub mod modrm;
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
// Programmable Interrupt Controller
|
||||
// http://stanislavs.org/helppc/8259.html
|
||||
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
pub const PIC_LOG: bool = false;
|
||||
pub const PIC_LOG_VERBOSE: bool = false;
|
||||
|
||||
use crate::cpu::cpu;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
// Note: This layout is deliberately chosen to match the old JavaScript pic state
|
||||
// (cpu.get_state_pic depens on this layout)
|
||||
#[repr(C, packed)]
|
||||
const _: () = assert!(std::mem::offset_of!(Pic0, special_mask_mode) == 12);
|
||||
#[repr(C)]
|
||||
struct Pic0 {
|
||||
irq_mask: u8,
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ struct Pic0 {
|
|||
special_mask_mode: bool,
|
||||
}
|
||||
|
||||
pub struct Pic {
|
||||
struct Pic {
|
||||
master: Pic0,
|
||||
slave: Pic0,
|
||||
}
|
||||
|
|
@ -91,14 +91,13 @@ static PIC: Mutex<Pic> = Mutex::new(Pic {
|
|||
},
|
||||
});
|
||||
|
||||
pub fn get_pic() -> MutexGuard<'static, Pic> { PIC.try_lock().unwrap() }
|
||||
fn get_pic() -> MutexGuard<'static, Pic> { PIC.try_lock().unwrap() }
|
||||
|
||||
// Checking for callable interrupts:
|
||||
// (cpu changes interrupt flag) -> cpu.handle_irqs -> pic_acknowledge_irq
|
||||
// (pic changes isr/irr) -> pic.check_irqs -> cpu.handle_irqs -> ...
|
||||
|
||||
// triggering irqs:
|
||||
// (io device has irq) -> cpu.device_raise_irq -> pic.set_irq -> pic.check_irqs -> cpu.handle_irqs -> (see above)
|
||||
// called from javascript for saving/restoring state
|
||||
#[no_mangle]
|
||||
pub unsafe fn get_pic_addr_master() -> u32 { &raw mut get_pic().master as u32 }
|
||||
#[no_mangle]
|
||||
pub unsafe fn get_pic_addr_slave() -> u32 { &raw mut get_pic().slave as u32 }
|
||||
|
||||
impl Pic0 {
|
||||
unsafe fn get_irq(&mut self) -> Option<u8> {
|
||||
|
|
@ -157,10 +156,7 @@ impl Pic {
|
|||
if dev.irq_value & mask == 0 || dev.elcr & mask != 0 {
|
||||
dev.irr |= mask;
|
||||
dev.irq_value |= mask;
|
||||
if i < 8 {
|
||||
self.check_irqs_master()
|
||||
}
|
||||
else {
|
||||
if i >= 8 {
|
||||
self.check_irqs_slave()
|
||||
}
|
||||
}
|
||||
|
|
@ -172,10 +168,7 @@ impl Pic {
|
|||
dev.irq_value &= !mask;
|
||||
if dev.elcr & mask != 0 {
|
||||
dev.irr &= !mask;
|
||||
if i < 8 {
|
||||
self.check_irqs_master()
|
||||
}
|
||||
else {
|
||||
if i >= 8 {
|
||||
self.check_irqs_slave()
|
||||
}
|
||||
}
|
||||
|
|
@ -245,10 +238,7 @@ impl Pic {
|
|||
dev.isr &= dev.isr - 1;
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
self.check_irqs_master()
|
||||
}
|
||||
else {
|
||||
if index == 1 {
|
||||
self.check_irqs_slave()
|
||||
}
|
||||
}
|
||||
|
|
@ -273,10 +263,7 @@ impl Pic {
|
|||
dbg_log!("interrupt mask: {:x}", dev.irq_mask);
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
self.check_irqs_master()
|
||||
}
|
||||
else {
|
||||
if index == 1 {
|
||||
self.check_irqs_slave()
|
||||
}
|
||||
}
|
||||
|
|
@ -294,12 +281,6 @@ impl Pic {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn check_irqs_master(&mut self) {
|
||||
let is_set = self.master.get_irq().is_some();
|
||||
if is_set {
|
||||
cpu::handle_irqs_internal(self);
|
||||
}
|
||||
}
|
||||
unsafe fn check_irqs_slave(&mut self) {
|
||||
let is_set = self.slave.get_irq().is_some();
|
||||
if is_set {
|
||||
|
|
@ -312,7 +293,8 @@ impl Pic {
|
|||
}
|
||||
|
||||
// called by the cpu
|
||||
pub unsafe fn pic_acknowledge_irq(pic: &mut Pic) -> Option<u8> {
|
||||
pub unsafe fn pic_acknowledge_irq() -> Option<u8> {
|
||||
let mut pic = get_pic();
|
||||
let irq = match pic.master.get_irq() {
|
||||
Some(i) => i,
|
||||
None => return None,
|
||||
|
|
@ -343,7 +325,7 @@ pub unsafe fn pic_acknowledge_irq(pic: &mut Pic) -> Option<u8> {
|
|||
dbg_assert!(pic.master.get_irq().is_none());
|
||||
|
||||
if irq == 2 {
|
||||
acknowledge_irq_slave(pic)
|
||||
acknowledge_irq_slave(&mut pic)
|
||||
}
|
||||
else {
|
||||
Some(pic.master.irq_map | irq)
|
||||
|
|
@ -384,9 +366,7 @@ unsafe fn acknowledge_irq_slave(pic: &mut Pic) -> Option<u8> {
|
|||
Some(pic.slave.irq_map | irq)
|
||||
}
|
||||
|
||||
// called by javascript
|
||||
#[no_mangle]
|
||||
pub unsafe fn pic_set_irq(i: u8) {
|
||||
pub unsafe fn set_irq(i: u8) {
|
||||
dbg_assert!(i < 16);
|
||||
|
||||
if PIC_LOG_VERBOSE {
|
||||
|
|
@ -396,9 +376,7 @@ pub unsafe fn pic_set_irq(i: u8) {
|
|||
get_pic().set_irq(i)
|
||||
}
|
||||
|
||||
// called by javascript
|
||||
#[no_mangle]
|
||||
pub unsafe fn pic_clear_irq(i: u8) {
|
||||
pub unsafe fn clear_irq(i: u8) {
|
||||
dbg_assert!(i < 16);
|
||||
|
||||
if PIC_LOG_VERBOSE {
|
||||
|
|
@ -408,36 +386,19 @@ pub unsafe fn pic_clear_irq(i: u8) {
|
|||
get_pic().clear_irq(i)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn port20_read() -> u32 { get_pic().master.port0_read() }
|
||||
#[no_mangle]
|
||||
pub unsafe fn port21_read() -> u32 { get_pic().master.port1_read() }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn portA0_read() -> u32 { get_pic().slave.port0_read() }
|
||||
#[no_mangle]
|
||||
pub unsafe fn portA1_read() -> u32 { get_pic().slave.port1_read() }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn port20_write(v: u8) { get_pic().port0_write(0, v) }
|
||||
#[no_mangle]
|
||||
pub unsafe fn port21_write(v: u8) { get_pic().port1_write(0, v) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn portA0_write(v: u8) { get_pic().port0_write(1, v) }
|
||||
#[no_mangle]
|
||||
pub unsafe fn portA1_write(v: u8) { get_pic().port1_write(1, v) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn port4D0_read() -> u32 { get_pic().master.elcr as u32 }
|
||||
#[no_mangle]
|
||||
pub unsafe fn port4D1_read() -> u32 { get_pic().slave.elcr as u32 }
|
||||
#[no_mangle]
|
||||
pub unsafe fn port4D0_write(v: u8) { get_pic().master.elcr = v }
|
||||
#[no_mangle]
|
||||
pub unsafe fn port4D1_write(v: u8) { get_pic().slave.elcr = v }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn get_pic_addr_master() -> u32 { &raw const get_pic().master as u32 }
|
||||
#[no_mangle]
|
||||
pub unsafe fn get_pic_addr_slave() -> u32 { &raw const get_pic().slave as u32 }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue