2013-11-07 21:30:18 +01:00
|
|
|
"use strict";
|
|
|
|
|
2017-03-16 02:02:27 +01:00
|
|
|
/** @const */
|
|
|
|
var CPU_LOG_VERBOSE = true;
|
|
|
|
|
|
|
|
|
2016-07-17 18:44:54 +02:00
|
|
|
// Resources:
|
|
|
|
// https://pdos.csail.mit.edu/6.828/2006/readings/i386/toc.htm
|
|
|
|
// https://www-ssl.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
|
|
|
|
// http://ref.x86asm.net/geek32.html
|
|
|
|
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @constructor */
|
2014-12-21 18:11:22 +01:00
|
|
|
function CPU()
|
2014-01-03 22:02:43 +01:00
|
|
|
{
|
2016-02-15 00:50:35 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.memory_size = 0;
|
2014-01-08 03:22:30 +01:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
// Note: Currently unused (degrades performance and not required by any OS
|
|
|
|
// that we support)
|
|
|
|
this.a20_enabled = true;
|
|
|
|
|
2017-04-06 04:03:32 +02:00
|
|
|
this.mem_page_infos = undefined;
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
this.mem8 = new Uint8Array(0);
|
|
|
|
this.mem16 = new Uint16Array(this.mem8.buffer);
|
|
|
|
this.mem32s = new Int32Array(this.mem8.buffer);
|
|
|
|
|
2017-04-06 04:03:32 +02:00
|
|
|
this.segment_is_null = new Uint8Array(8);
|
|
|
|
this.segment_limits = new Uint32Array(8);
|
|
|
|
//this.segment_infos = new Uint32Array(8);
|
|
|
|
this.segment_offsets = new Int32Array(8);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
/**
|
2015-09-14 01:45:51 +02:00
|
|
|
* Translation Lookaside Buffer
|
2015-01-12 18:05:10 +01:00
|
|
|
* @const
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2015-01-12 18:05:10 +01:00
|
|
|
this.tlb_data = new Int32Array(1 << 20);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
/**
|
2013-11-07 21:30:18 +01:00
|
|
|
* Information about which pages are cached in the tlb.
|
|
|
|
* By bit:
|
|
|
|
* 0 system, read
|
|
|
|
* 1 system, write
|
|
|
|
* 2 user, read
|
|
|
|
* 3 user, write
|
2015-01-12 18:05:10 +01:00
|
|
|
* @const
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2015-01-12 18:05:10 +01:00
|
|
|
this.tlb_info = new Uint8Array(1 << 20);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
/**
|
2013-11-07 21:30:18 +01:00
|
|
|
* Same as tlb_info, except it only contains global pages
|
2015-01-12 18:05:10 +01:00
|
|
|
* @const
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2015-01-12 18:05:10 +01:00
|
|
|
this.tlb_info_global = new Uint8Array(1 << 20);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/**
|
2013-11-07 21:30:18 +01:00
|
|
|
* Wheter or not in protected mode
|
2015-09-14 01:45:51 +02:00
|
|
|
* @type {boolean}
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.protected_mode = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/**
|
2013-11-07 21:30:18 +01:00
|
|
|
* interrupt descriptor table
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.idtr_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.idtr_offset = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/**
|
2013-11-07 21:30:18 +01:00
|
|
|
* global descriptor table register
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.gdtr_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.gdtr_offset = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
this.tss_size_32 = false;
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
/*
|
|
|
|
* whether or not a page fault occured
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.page_fault = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr = new Int32Array(8);
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[0] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[3] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {number} */
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[4] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// current privilege level
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// if false, pages are 4 KiB, else 4 Mib
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.page_size_extensions = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-04-06 02:57:24 +02:00
|
|
|
// current operand/address size
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {boolean} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.is_32 = false;
|
2017-04-06 02:57:24 +02:00
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
/** @type {boolean} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stack_size_32 = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
2014-06-15 22:25:17 +02:00
|
|
|
* Was the last instruction a hlt?
|
2013-11-07 21:30:18 +01:00
|
|
|
* @type {boolean}
|
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.in_hlt = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_virt_eip = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.eip_phys = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_virt_esp = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.esp_phys = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
|
2014-07-26 01:24:07 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.sysenter_cs = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.sysenter_esp = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.sysenter_eip = 0;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
2017-03-07 01:03:06 +01:00
|
|
|
this.prefixes = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.flags = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/**
|
2014-06-15 22:25:17 +02:00
|
|
|
* bitmap of flags which are not updated in the flags variable
|
|
|
|
* changed by arithmetic instructions, so only relevant to arithmetic flags
|
|
|
|
* @type {number}
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags_changed = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/**
|
2014-06-15 22:25:17 +02:00
|
|
|
* the last 2 operators and the result and size of the last arithmetic operation
|
2015-09-14 01:45:51 +02:00
|
|
|
* @type {number}
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_op1 = 0;
|
|
|
|
/** @type {number} */
|
|
|
|
this.last_op2 = 0;
|
|
|
|
/** @type {number} */
|
|
|
|
this.last_op_size = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_add_result = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.last_result = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-10-01 23:22:10 +02:00
|
|
|
this.mul32_result = new Int32Array(2);
|
|
|
|
this.div32_result = new Float64Array(2);
|
|
|
|
|
2014-07-26 23:04:01 +02:00
|
|
|
this.tsc_offset = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.modrm_byte = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.phys_addr = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.phys_addr_high = 0;
|
|
|
|
|
2017-04-06 02:57:24 +02:00
|
|
|
/** @type {!Object} */
|
|
|
|
this.devices = {};
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table = [];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// paging enabled
|
|
|
|
/** @type {boolean} */
|
|
|
|
this.paging = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/** @type {number} */
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.previous_ip = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-04-02 18:52:51 +02:00
|
|
|
this.apic_enabled = true;
|
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
/**
|
2014-06-15 22:25:17 +02:00
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.timestamp_counter = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// registers
|
|
|
|
this.reg32s = new Int32Array(8);
|
|
|
|
this.reg32 = new Uint32Array(this.reg32s.buffer);
|
|
|
|
this.reg16s = new Int16Array(this.reg32s.buffer);
|
|
|
|
this.reg16 = new Uint16Array(this.reg32s.buffer);
|
2014-09-29 07:10:47 +02:00
|
|
|
this.reg8s = new Int8Array(this.reg32s.buffer);
|
|
|
|
this.reg8 = new Uint8Array(this.reg32s.buffer);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-07 02:37:40 +02:00
|
|
|
// segment registers, tr and ldtr
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg = new Uint16Array(8);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// debug registers
|
|
|
|
this.dreg = new Int32Array(8);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-04-06 02:57:24 +02:00
|
|
|
|
|
|
|
// managed in io.js
|
|
|
|
/** @const */ this.memory_map_read8 = [];
|
|
|
|
/** @const */ this.memory_map_write8 = [];
|
|
|
|
/** @const */ this.memory_map_read32 = [];
|
|
|
|
/** @const */ this.memory_map_write32 = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @const
|
|
|
|
* @type {{main: ArrayBuffer, vga: ArrayBuffer}}
|
|
|
|
*/
|
|
|
|
this.bios = {
|
|
|
|
main: null,
|
|
|
|
vga: null,
|
|
|
|
};
|
|
|
|
|
2017-03-23 19:03:12 +01:00
|
|
|
/** @type {number} */
|
|
|
|
this.fw_value = 0;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.io = undefined;
|
|
|
|
this.fpu = undefined;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
dbg_assert(this.table16 && this.table32);
|
|
|
|
dbg_assert(this.table0F_16 && this.table0F_32);
|
|
|
|
|
2014-09-29 07:10:47 +02:00
|
|
|
this.update_operand_size();
|
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.tsc_offset = v86.microtick();
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
this.debug_init();
|
2016-08-02 04:15:24 +02:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
this.init2();
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
//Object.seal(this);
|
2015-05-18 22:18:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CPU.prototype.get_state = function()
|
|
|
|
{
|
|
|
|
var state = [];
|
|
|
|
|
|
|
|
state[0] = this.memory_size;
|
|
|
|
state[1] = this.segment_is_null;
|
|
|
|
state[2] = this.segment_offsets;
|
|
|
|
state[3] = this.segment_limits;
|
|
|
|
state[4] = this.protected_mode;
|
|
|
|
state[5] = this.idtr_offset;
|
|
|
|
state[6] = this.idtr_size;
|
|
|
|
state[7] = this.gdtr_offset;
|
|
|
|
state[8] = this.gdtr_size;
|
|
|
|
state[9] = this.page_fault;
|
|
|
|
state[10] = this.cr;
|
|
|
|
state[11] = this.cpl;
|
|
|
|
state[12] = this.page_size_extensions;
|
|
|
|
state[13] = this.is_32;
|
2017-03-07 01:03:06 +01:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
state[16] = this.stack_size_32;
|
|
|
|
state[17] = this.in_hlt;
|
|
|
|
state[18] = this.last_virt_eip;
|
|
|
|
state[19] = this.eip_phys;
|
|
|
|
state[20] = this.last_virt_esp;
|
|
|
|
state[21] = this.esp_phys;
|
|
|
|
state[22] = this.sysenter_cs;
|
|
|
|
state[23] = this.sysenter_eip;
|
|
|
|
state[24] = this.sysenter_esp;
|
2017-03-07 01:03:06 +01:00
|
|
|
state[25] = this.prefixes;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[26] = this.flags;
|
|
|
|
state[27] = this.flags_changed;
|
|
|
|
state[28] = this.last_op1;
|
|
|
|
state[29] = this.last_op2;
|
|
|
|
state[30] = this.last_op_size;
|
|
|
|
state[31] = this.last_add_result;
|
|
|
|
state[32] = this.modrm_byte;
|
|
|
|
|
|
|
|
state[36] = this.paging;
|
|
|
|
state[37] = this.instruction_pointer;
|
|
|
|
state[38] = this.previous_ip;
|
|
|
|
state[39] = this.reg32s;
|
|
|
|
state[40] = this.sreg;
|
|
|
|
state[41] = this.dreg;
|
2016-08-02 04:15:24 +02:00
|
|
|
state[42] = this.mem8;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[43] = this.fpu;
|
|
|
|
|
|
|
|
state[45] = this.devices.virtio;
|
2017-04-02 18:52:51 +02:00
|
|
|
state[46] = this.devices.apic;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[47] = this.devices.rtc;
|
|
|
|
state[48] = this.devices.pci;
|
|
|
|
state[49] = this.devices.dma;
|
2017-04-01 00:16:43 +02:00
|
|
|
state[50] = this.devices.acpi;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[51] = this.devices.hpet;
|
|
|
|
state[52] = this.devices.vga;
|
|
|
|
state[53] = this.devices.ps2;
|
|
|
|
state[54] = this.devices.uart;
|
|
|
|
state[55] = this.devices.fdc;
|
|
|
|
state[56] = this.devices.cdrom;
|
|
|
|
state[57] = this.devices.hda;
|
|
|
|
state[58] = this.devices.pit;
|
|
|
|
state[59] = this.devices.net;
|
|
|
|
state[60] = this.devices.pic;
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
state[61] = this.a20_enabled;
|
2017-03-23 19:03:12 +01:00
|
|
|
state[62] = this.fw_value;
|
2016-08-02 04:15:24 +02:00
|
|
|
|
2017-04-02 18:52:51 +02:00
|
|
|
state[63] = this.devices.ioapic;
|
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
state[64] = this.tss_size_32;
|
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
return state;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.set_state = function(state)
|
|
|
|
{
|
|
|
|
this.memory_size = state[0];
|
|
|
|
this.segment_is_null = state[1];
|
|
|
|
this.segment_offsets = state[2];
|
|
|
|
this.segment_limits = state[3];
|
|
|
|
this.protected_mode = state[4];
|
|
|
|
this.idtr_offset = state[5];
|
|
|
|
this.idtr_size = state[6];
|
|
|
|
this.gdtr_offset = state[7];
|
|
|
|
this.gdtr_size = state[8];
|
|
|
|
this.page_fault = state[9];
|
|
|
|
this.cr = state[10];
|
|
|
|
this.cpl = state[11];
|
|
|
|
this.page_size_extensions = state[12];
|
|
|
|
this.is_32 = state[13];
|
2017-03-07 01:03:06 +01:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.stack_size_32 = state[16];
|
2017-03-07 01:03:06 +01:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.in_hlt = state[17];
|
|
|
|
this.last_virt_eip = state[18];
|
|
|
|
this.eip_phys = state[19];
|
|
|
|
this.last_virt_esp = state[20];
|
|
|
|
this.esp_phys = state[21];
|
|
|
|
this.sysenter_cs = state[22];
|
|
|
|
this.sysenter_eip = state[23];
|
|
|
|
this.sysenter_esp = state[24];
|
2017-03-07 01:03:06 +01:00
|
|
|
this.prefixes = state[25];
|
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.flags = state[26];
|
|
|
|
this.flags_changed = state[27];
|
2016-07-18 03:47:48 +02:00
|
|
|
this.last_op1 = state[28];
|
|
|
|
this.last_op2 = state[29];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.last_op_size = state[30];
|
|
|
|
this.last_add_result = state[31];
|
|
|
|
this.modrm_byte = state[32];
|
|
|
|
|
|
|
|
this.paging = state[36];
|
|
|
|
this.instruction_pointer = state[37];
|
|
|
|
this.previous_ip = state[38];
|
2016-07-18 03:47:48 +02:00
|
|
|
this.reg32s = state[39];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.sreg = state[40];
|
|
|
|
this.dreg = state[41];
|
2016-08-02 04:15:24 +02:00
|
|
|
this.mem8 = state[42];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.fpu = state[43];
|
|
|
|
|
|
|
|
this.devices.virtio = state[45];
|
2017-04-02 18:52:51 +02:00
|
|
|
this.devices.apic = state[46];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.devices.rtc = state[47];
|
|
|
|
this.devices.pci = state[48];
|
|
|
|
this.devices.dma = state[49];
|
2017-04-01 00:16:43 +02:00
|
|
|
this.devices.acpi = state[50];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.devices.hpet = state[51];
|
|
|
|
this.devices.vga = state[52];
|
2016-07-18 03:47:48 +02:00
|
|
|
this.devices.ps2 = state[53];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.devices.uart = state[54];
|
|
|
|
this.devices.fdc = state[55];
|
|
|
|
this.devices.cdrom = state[56];
|
|
|
|
this.devices.hda = state[57];
|
|
|
|
this.devices.pit = state[58];
|
|
|
|
this.devices.net = state[59];
|
|
|
|
this.devices.pic = state[60];
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
this.a20_enabled = state[61];
|
2017-03-23 19:03:12 +01:00
|
|
|
this.fw_value = state[62];
|
2016-08-02 04:15:24 +02:00
|
|
|
|
2017-04-02 18:52:51 +02:00
|
|
|
this.devices.ioapic = state[63];
|
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
this.tss_size_32 = state[64];
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
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);
|
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
|
|
|
|
this.full_clear_tlb();
|
|
|
|
// tsc_offset?
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.reg32 = new Uint32Array(this.reg32s.buffer);
|
|
|
|
this.reg16s = new Int16Array(this.reg32s.buffer);
|
|
|
|
this.reg16 = new Uint16Array(this.reg32s.buffer);
|
|
|
|
this.reg8s = new Int8Array(this.reg32s.buffer);
|
|
|
|
this.reg8 = new Uint8Array(this.reg32s.buffer);
|
2015-01-12 18:05:10 +01:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
this.update_operand_size();
|
2014-09-29 07:10:47 +02:00
|
|
|
};
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-06 04:26:39 +01:00
|
|
|
/**
|
|
|
|
* @return {number} time in ms until this method should becalled again
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.main_run = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-08-02 02:41:30 +02:00
|
|
|
if(this.in_hlt)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-08-02 02:41:43 +02:00
|
|
|
//if(false)
|
|
|
|
//{
|
|
|
|
// var _t = this.hlt_loop();
|
2016-08-04 18:21:41 +02:00
|
|
|
// var t = 0;
|
2016-08-02 02:41:43 +02:00
|
|
|
//}
|
|
|
|
//else
|
2017-04-06 02:57:24 +02:00
|
|
|
//{
|
2016-08-04 18:21:41 +02:00
|
|
|
var t = this.hlt_loop();
|
2017-04-06 02:57:24 +02:00
|
|
|
//}
|
2016-08-04 18:21:41 +02:00
|
|
|
|
|
|
|
if(this.in_hlt)
|
|
|
|
{
|
|
|
|
return t;
|
2014-06-22 19:10:35 +02:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-08-04 18:21:41 +02:00
|
|
|
|
|
|
|
this.do_run();
|
2017-03-07 01:03:06 +01:00
|
|
|
|
2016-08-04 18:21:41 +02:00
|
|
|
return 0;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.exception_cleanup = function(e)
|
2013-12-06 20:47:37 +01:00
|
|
|
{
|
2014-05-06 20:24:46 +02:00
|
|
|
if(e === MAGIC_CPU_EXCEPTION)
|
2013-12-06 20:47:37 +01:00
|
|
|
{
|
|
|
|
// A legit CPU exception (for instance, a page fault happened)
|
|
|
|
// call_interrupt_vector has already been called at this point,
|
|
|
|
// so we just need to reset some state
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.page_fault = false;
|
2013-12-06 20:47:37 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// restore state from prefixes
|
2015-04-28 11:12:40 +02:00
|
|
|
this.clear_prefixes();
|
2013-12-06 20:47:37 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
console.log(e);
|
|
|
|
console.log(e.stack);
|
2015-09-14 01:45:51 +02:00
|
|
|
//var e = new Error(e.message);
|
|
|
|
//Error.captureStackTrace && Error.captureStackTrace(e);
|
2013-12-06 20:47:37 +01:00
|
|
|
throw e;
|
|
|
|
}
|
2017-03-07 01:03:06 +01:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.reboot_internal = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-12-21 19:19:04 +01:00
|
|
|
this.reset();
|
|
|
|
this.load_bios();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 19:19:04 +01:00
|
|
|
CPU.prototype.reset = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-08-02 04:15:24 +02:00
|
|
|
this.a20_enabled = true;
|
|
|
|
|
2017-04-06 04:03:32 +02:00
|
|
|
for(let i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
this.segment_is_null[i] = 0;
|
|
|
|
this.segment_limits[i] = 0;
|
|
|
|
//this.segment_infos = new Uint32Array(8);
|
|
|
|
this.segment_offsets[i] = 0;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
this.full_clear_tlb();
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2017-04-06 04:03:32 +02:00
|
|
|
for(let i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
this.reg32s[i] = 0;
|
|
|
|
this.sreg[i] = 0;
|
|
|
|
this.cr[i] = 0;
|
|
|
|
this.dreg[i] = 0;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.protected_mode = false;
|
|
|
|
|
|
|
|
// http://www.sandpile.org/x86/initial.htm
|
|
|
|
this.idtr_size = 0;
|
|
|
|
this.idtr_offset = 0;
|
|
|
|
|
|
|
|
this.gdtr_size = 0;
|
|
|
|
this.gdtr_offset = 0;
|
|
|
|
|
|
|
|
this.page_fault = false;
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[0] = 1 << 30 | 1 << 29 | 1 << 4;
|
|
|
|
this.cr[2] = 0;
|
|
|
|
this.cr[3] = 0;
|
|
|
|
this.cr[4] = 0;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.dreg[6] = 0xFFFF0FF0|0;
|
|
|
|
this.dreg[7] = 0x400;
|
|
|
|
this.cpl = 0;
|
|
|
|
this.paging = false;
|
|
|
|
this.page_size_extensions = 0;
|
|
|
|
this.is_32 = false;
|
|
|
|
this.stack_size_32 = false;
|
2017-03-16 02:05:00 +01:00
|
|
|
this.prefixes = 0;
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2017-03-16 02:26:31 +01:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
this.update_operand_size();
|
|
|
|
|
|
|
|
this.timestamp_counter = 0;
|
|
|
|
this.previous_ip = 0;
|
|
|
|
this.in_hlt = false;
|
|
|
|
|
2014-07-26 01:24:07 +02:00
|
|
|
this.sysenter_cs = 0;
|
|
|
|
this.sysenter_esp = 0;
|
|
|
|
this.sysenter_eip = 0;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags = flags_default;
|
|
|
|
this.flags_changed = 0;
|
|
|
|
|
|
|
|
this.last_result = 0;
|
|
|
|
this.last_add_result = 0;
|
|
|
|
this.last_op1 = 0;
|
|
|
|
this.last_op2 = 0;
|
|
|
|
this.last_op_size = 0;
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
this.tsc_offset = v86.microtick();
|
2014-07-26 23:04:01 +02:00
|
|
|
|
2014-12-21 19:19:04 +01:00
|
|
|
this.instruction_pointer = 0xFFFF0;
|
2016-08-01 22:26:10 +02:00
|
|
|
this.switch_cs_real_mode(0xF000);
|
2016-02-15 00:51:27 +01:00
|
|
|
|
2014-12-21 19:19:04 +01:00
|
|
|
this.switch_seg(reg_ss, 0x30);
|
|
|
|
this.reg16[reg_sp] = 0x100;
|
2015-04-17 23:46:25 +02:00
|
|
|
|
|
|
|
if(this.devices.virtio)
|
|
|
|
{
|
|
|
|
this.devices.virtio.reset();
|
|
|
|
}
|
2017-03-23 19:03:12 +01:00
|
|
|
|
|
|
|
this.fw_value = 0;
|
2014-12-21 19:19:04 +01:00
|
|
|
};
|
|
|
|
|
2016-07-26 21:45:39 +02:00
|
|
|
/** @export */
|
2016-08-02 05:33:02 +02:00
|
|
|
CPU.prototype.create_memory = function(size)
|
2016-07-26 21:45:39 +02:00
|
|
|
{
|
2016-10-15 02:12:57 +02:00
|
|
|
if(size < 1024 * 1024)
|
|
|
|
{
|
|
|
|
size = 1024 * 1024;
|
|
|
|
}
|
|
|
|
else if((size | 0) < 0)
|
|
|
|
{
|
|
|
|
size = Math.pow(2, 31) - MMAP_BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = ((size - 1) | (MMAP_BLOCK_SIZE - 1)) + 1 | 0;
|
|
|
|
dbg_assert((size | 0) > 0);
|
|
|
|
dbg_assert((size & MMAP_BLOCK_SIZE - 1) === 0);
|
|
|
|
|
2016-07-26 21:45:39 +02:00
|
|
|
this.memory_size = size;
|
2016-08-02 04:15:24 +02:00
|
|
|
|
2016-08-02 05:33:02 +02:00
|
|
|
var buffer = new ArrayBuffer(size);
|
2016-08-02 04:15:24 +02:00
|
|
|
|
2016-08-02 05:33:02 +02:00
|
|
|
this.mem8 = new Uint8Array(buffer);
|
|
|
|
this.mem16 = new Uint16Array(buffer);
|
|
|
|
this.mem32s = new Int32Array(buffer);
|
2016-07-26 21:45:39 +02:00
|
|
|
};
|
|
|
|
|
2014-12-27 00:18:35 +01:00
|
|
|
CPU.prototype.init = function(settings, device_bus)
|
2014-12-21 19:19:04 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
this.create_memory(typeof settings.memory_size === "number" ?
|
|
|
|
settings.memory_size : 1024 * 1024 * 64);
|
2014-12-21 19:19:04 +01:00
|
|
|
|
|
|
|
this.reset();
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var io = new IO(this);
|
2014-07-27 01:07:51 +02:00
|
|
|
this.io = io;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-01-12 18:05:10 +01:00
|
|
|
this.bios.main = settings.bios;
|
|
|
|
this.bios.vga = settings.vga_bios;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 19:19:04 +01:00
|
|
|
this.load_bios();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
var a20_byte = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-05 17:06:19 +01:00
|
|
|
io.register_read(0xB3, this, function()
|
|
|
|
{
|
|
|
|
// seabios smm_relocate_and_restore
|
|
|
|
dbg_log("port 0xB3 read");
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(0x92, this, function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-20 22:04:58 +01:00
|
|
|
return a20_byte;
|
|
|
|
});
|
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x92, this, function(out_byte)
|
2013-12-20 22:04:58 +01:00
|
|
|
{
|
|
|
|
a20_byte = out_byte;
|
|
|
|
});
|
|
|
|
|
2017-03-11 00:10:59 +01:00
|
|
|
io.register_read(0x511, this, function()
|
|
|
|
{
|
2017-03-23 19:03:12 +01:00
|
|
|
// bios config port (used by seabios and kvm-unit-test)
|
|
|
|
let result = this.fw_value & 0xFF;
|
|
|
|
this.fw_value >>>= 8;
|
|
|
|
return result;
|
|
|
|
});
|
|
|
|
io.register_write(0x510, this, undefined, function(value)
|
|
|
|
{
|
|
|
|
dbg_log("bios config port, index=" + h(value));
|
|
|
|
|
|
|
|
if(value === FW_CFG_SIGNATURE)
|
|
|
|
{
|
|
|
|
// We could pretend to be QEMU here to control certain options in
|
|
|
|
// seabios, but for now this isn't needed
|
|
|
|
this.fw_value = 0xfab0fab0|0;
|
|
|
|
}
|
|
|
|
else if(value === FW_CFG_RAM_SIZE)
|
|
|
|
{
|
|
|
|
this.fw_value = this.memory_size;
|
|
|
|
}
|
|
|
|
else if(value === FW_CFG_NB_CPUS)
|
|
|
|
{
|
|
|
|
this.fw_value = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_assert(false, "Unimplemented fw index: " + h(value));
|
|
|
|
this.fw_value = 0;
|
|
|
|
}
|
2017-03-11 00:10:59 +01:00
|
|
|
});
|
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
// Use by linux for port-IO delay
|
|
|
|
// Avoid generating tons of debug messages
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x80, this, function(out_byte)
|
2013-12-20 22:04:58 +01:00
|
|
|
{
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-07-22 22:59:18 +02:00
|
|
|
this.devices = {};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
// TODO: Make this more configurable
|
2014-12-12 21:31:16 +01:00
|
|
|
if(settings.load_devices)
|
2013-12-20 22:04:58 +01:00
|
|
|
{
|
2014-07-27 01:07:51 +02:00
|
|
|
this.devices.pic = new PIC(this);
|
2015-09-14 01:45:51 +02:00
|
|
|
this.devices.pci = new PCI(this);
|
|
|
|
|
|
|
|
if(ENABLE_ACPI)
|
|
|
|
{
|
2017-04-02 18:52:51 +02:00
|
|
|
this.devices.ioapic = new IOAPIC(this);
|
2015-09-14 01:45:51 +02:00
|
|
|
this.devices.apic = new APIC(this);
|
|
|
|
this.devices.acpi = new ACPI(this);
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
this.devices.rtc = new RTC(this);
|
|
|
|
this.fill_cmos(this.devices.rtc, settings);
|
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
this.devices.dma = new DMA(this);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
if(ENABLE_HPET)
|
|
|
|
{
|
2014-07-27 01:07:51 +02:00
|
|
|
this.devices.hpet = new HPET(this);
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
this.devices.vga = new VGAScreen(this, device_bus,
|
2014-12-25 02:32:18 +01:00
|
|
|
settings.vga_memory_size || 8 * 1024 * 1024);
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.fpu = new FPU(this);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-25 02:32:18 +01:00
|
|
|
this.devices.ps2 = new PS2(this, device_bus);
|
|
|
|
|
2015-01-09 03:58:18 +01:00
|
|
|
this.devices.uart = new UART(this, 0x3F8, device_bus);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-12 21:31:16 +01:00
|
|
|
this.devices.fdc = new FloppyController(this, settings.fda, settings.fdb);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
var ide_device_count = 0;
|
|
|
|
|
2015-12-31 00:31:08 +01:00
|
|
|
if(settings.hda)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-12-31 00:31:08 +01:00
|
|
|
this.devices.hda = new IDEDevice(this, settings.hda, false, ide_device_count++, device_bus);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2015-12-31 00:31:08 +01:00
|
|
|
if(settings.cdrom)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-12-31 00:31:08 +01:00
|
|
|
this.devices.cdrom = new IDEDevice(this, settings.cdrom, true, ide_device_count++, device_bus);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
if(settings.hdb)
|
2014-01-10 04:24:56 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
this.devices.hdb = new IDEDevice(this, settings.hdb, false, ide_device_count++, device_bus);
|
2014-01-10 04:24:56 +01:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
this.devices.pit = new PIT(this);
|
2014-10-11 02:15:37 +02:00
|
|
|
|
2015-01-10 02:19:22 +01:00
|
|
|
if(settings.enable_ne2k)
|
2014-10-11 02:15:37 +02:00
|
|
|
{
|
2014-12-25 02:32:18 +01:00
|
|
|
this.devices.net = new Ne2k(this, device_bus);
|
2014-10-11 02:15:37 +02:00
|
|
|
}
|
2014-12-02 19:01:13 +01:00
|
|
|
|
|
|
|
if(settings.fs9p)
|
|
|
|
{
|
2015-01-13 04:51:31 +01:00
|
|
|
this.devices.virtio = new VirtIO(this, device_bus, settings.fs9p);
|
2014-12-02 19:01:13 +01:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2017-04-02 22:14:03 +02:00
|
|
|
if(settings.multiboot)
|
|
|
|
{
|
|
|
|
dbg_assert(settings.multiboot.buffer);
|
|
|
|
this.load_multiboot(settings.multiboot.buffer);
|
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
if(DEBUG)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.debug.init();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-04-02 22:14:03 +02:00
|
|
|
CPU.prototype.load_multiboot = function(buffer)
|
|
|
|
{
|
|
|
|
// https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
|
|
|
|
|
|
|
|
dbg_log("Trying multiboot from buffer of size " + buffer.byteLength, LOG_CPU);
|
|
|
|
|
|
|
|
const MAGIC = 0x1BADB002;
|
|
|
|
const ELF_MAGIC = 0x464C457F;
|
|
|
|
const MULTIBOOT_HEADER_ADDRESS = 0x10000;
|
|
|
|
const MULTIBOOT_SEARCH_BYTES = 8192;
|
|
|
|
|
2017-05-06 18:37:23 +02:00
|
|
|
if(buffer.byteLength < MULTIBOOT_SEARCH_BYTES)
|
|
|
|
{
|
|
|
|
var buf32 = new Int32Array(MULTIBOOT_SEARCH_BYTES / 4);
|
|
|
|
new Uint8Array(buf32.buffer).set(new Uint8Array(buffer));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var buf32 = new Int32Array(buffer, 0, MULTIBOOT_SEARCH_BYTES / 4);
|
|
|
|
}
|
2017-04-02 22:14:03 +02:00
|
|
|
|
|
|
|
for(var offset = 0; offset < MULTIBOOT_SEARCH_BYTES; offset += 4)
|
|
|
|
{
|
|
|
|
if(buf32[offset >> 2] === MAGIC)
|
|
|
|
{
|
|
|
|
var flags = buf32[offset + 4 >> 2];
|
|
|
|
var checksum = buf32[offset + 8 >> 2];
|
|
|
|
var total = MAGIC + flags + checksum | 0;
|
|
|
|
|
|
|
|
if(total)
|
|
|
|
{
|
|
|
|
dbg_log("Multiboot checksum check failed", LOG_CPU);
|
2017-05-06 18:37:23 +02:00
|
|
|
continue;
|
2017-04-02 22:14:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_log("Multiboot magic found, flags: " + h(flags >>> 0, 8), LOG_CPU);
|
|
|
|
dbg_assert((flags & ~MULTIBOOT_HEADER_ADDRESS) === 0, "TODO");
|
|
|
|
|
|
|
|
this.reg32s[reg_eax] = 0x2BADB002;
|
|
|
|
|
2017-05-06 18:37:23 +02:00
|
|
|
let multiboot_info_addr = 0x7C00;
|
2017-04-02 22:14:03 +02:00
|
|
|
this.reg32s[reg_ebx] = multiboot_info_addr;
|
|
|
|
this.write32(multiboot_info_addr, 0);
|
|
|
|
|
|
|
|
this.cr[0] = 1;
|
|
|
|
this.protected_mode = true;
|
2017-05-06 18:37:23 +02:00
|
|
|
this.flags = flags_default;
|
2017-04-02 22:14:03 +02:00
|
|
|
this.update_cs_size(true);
|
|
|
|
this.stack_size_32 = true;
|
|
|
|
|
|
|
|
for(var i = 0; i < 6; i++)
|
|
|
|
{
|
|
|
|
this.segment_is_null[i] = 0;
|
|
|
|
this.segment_offsets[i] = 0;
|
|
|
|
this.segment_limits[i] = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
// Value doesn't matter, OS isn't allowed to reload without setting
|
|
|
|
// up a proper GDT
|
|
|
|
this.sreg[i] = 0xB002;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(flags & MULTIBOOT_HEADER_ADDRESS)
|
|
|
|
{
|
|
|
|
dbg_log("Multiboot specifies its own address table", LOG_CPU);
|
|
|
|
|
|
|
|
var header_addr = buf32[offset + 12 >> 2];
|
|
|
|
var load_addr = buf32[offset + 16 >> 2];
|
|
|
|
var load_end_addr = buf32[offset + 20 >> 2];
|
|
|
|
var bss_end_addr = buf32[offset + 24 >> 2];
|
|
|
|
var entry_addr = buf32[offset + 28 >> 2];
|
|
|
|
|
|
|
|
dbg_log("header=" + h(header_addr, 8) +
|
|
|
|
" load=" + h(load_addr, 8) +
|
|
|
|
" load_end=" + h(load_end_addr, 8) +
|
|
|
|
" bss_end=" + h(bss_end_addr, 8) +
|
2017-05-06 18:37:23 +02:00
|
|
|
" entry=" + h(entry_addr, 8));
|
2017-04-02 22:14:03 +02:00
|
|
|
|
|
|
|
dbg_assert(load_addr <= header_addr);
|
|
|
|
|
|
|
|
var file_start = offset - (header_addr - load_addr);
|
|
|
|
|
|
|
|
if(load_end_addr === 0)
|
|
|
|
{
|
|
|
|
var length = undefined;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_assert(load_end_addr >= load_addr);
|
|
|
|
var length = load_end_addr - load_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
let blob = new Uint8Array(buffer, file_start, length);
|
|
|
|
this.write_blob(blob, load_addr);
|
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + entry_addr | 0;
|
|
|
|
}
|
|
|
|
else if(buf32[0] === ELF_MAGIC)
|
|
|
|
{
|
|
|
|
dbg_log("Multiboot image is in elf format", LOG_CPU);
|
|
|
|
|
|
|
|
let elf = read_elf(buffer);
|
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + elf.header.entry | 0;
|
|
|
|
|
|
|
|
for(let program of elf.program_headers)
|
|
|
|
{
|
|
|
|
if(program.type === 0)
|
|
|
|
{
|
|
|
|
// null
|
|
|
|
}
|
|
|
|
else if(program.type === 1)
|
|
|
|
{
|
|
|
|
// load
|
|
|
|
|
|
|
|
// Since multiboot specifies that paging is disabled,
|
|
|
|
// virtual and physical address must be equal
|
|
|
|
dbg_assert(program.paddr === program.vaddr);
|
|
|
|
dbg_assert(program.filesz <= program.memsz);
|
|
|
|
|
|
|
|
let blob = new Uint8Array(buffer, program.offset, program.filesz);
|
|
|
|
this.write_blob(blob, program.paddr);
|
|
|
|
}
|
|
|
|
else if(program.type === 4 ||
|
|
|
|
program.type === 0x6474e550 ||
|
|
|
|
program.type === 0x6474e551)
|
|
|
|
{
|
|
|
|
// ignore for now
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_assert(false, "unimplemented elf section type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_assert(false, "Not a bootable multiboot format");
|
|
|
|
}
|
|
|
|
|
|
|
|
// only for kvm-unit-test
|
|
|
|
this.io.register_write_consecutive(0xF4, this,
|
|
|
|
function(value)
|
|
|
|
{
|
|
|
|
console.log("Test exited with code " + h(value, 2));
|
|
|
|
throw "HALT";
|
|
|
|
},
|
|
|
|
function() {},
|
|
|
|
function() {},
|
|
|
|
function() {});
|
|
|
|
|
|
|
|
// only for kvm-unit-test
|
|
|
|
for(let i = 0xE; i <= 0xF; i++)
|
|
|
|
{
|
|
|
|
this.io.register_write(0x2000 + i, this,
|
|
|
|
function(value)
|
|
|
|
{
|
|
|
|
dbg_log("kvm-unit-test: Set irq " + h(i) + " to " + h(value, 2));
|
|
|
|
if(value)
|
|
|
|
{
|
|
|
|
this.device_raise_irq(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.device_lower_irq(i);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_log("Starting multiboot kernel at:", LOG_CPU);
|
|
|
|
this.debug.dump_state();
|
|
|
|
this.debug.dump_regs();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-25 18:23:10 +01:00
|
|
|
CPU.prototype.fill_cmos = function(rtc, settings)
|
|
|
|
{
|
|
|
|
var boot_order = settings.boot_order || 0x213;
|
|
|
|
|
|
|
|
// Used by seabios to determine the boot order
|
|
|
|
// Nibble
|
2015-09-14 01:45:51 +02:00
|
|
|
// 1: FloppyPrio
|
|
|
|
// 2: HDPrio
|
|
|
|
// 3: CDPrio
|
|
|
|
// 4: BEVPrio
|
2015-02-25 18:23:10 +01:00
|
|
|
// bootflag 1, high nibble, lowest priority
|
|
|
|
// Low nibble: Disable floppy signature check (1)
|
2016-01-01 18:23:40 +01:00
|
|
|
rtc.cmos_write(CMOS_BIOS_BOOTFLAG1 , 1 | boot_order >> 4 & 0xF0);
|
2015-02-25 18:23:10 +01:00
|
|
|
|
|
|
|
// bootflag 2, both nibbles, high and middle priority
|
2016-01-01 18:23:40 +01:00
|
|
|
rtc.cmos_write(CMOS_BIOS_BOOTFLAG2, boot_order & 0xFF);
|
|
|
|
|
2016-01-03 02:30:36 +01:00
|
|
|
// 640k or less if less memory is used
|
|
|
|
rtc.cmos_write(CMOS_MEM_BASE_LOW, 640 & 0xFF);
|
|
|
|
rtc.cmos_write(CMOS_MEM_BASE_HIGH, 640 >> 8);
|
|
|
|
|
|
|
|
var memory_above_1m = 0; // in k
|
|
|
|
if(this.memory_size >= 1024 * 1024)
|
|
|
|
{
|
|
|
|
memory_above_1m = (this.memory_size - 1024 * 1024) >> 10;
|
|
|
|
memory_above_1m = Math.min(memory_above_1m, 0xFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
rtc.cmos_write(CMOS_MEM_OLD_EXT_LOW, memory_above_1m & 0xFF);
|
|
|
|
rtc.cmos_write(CMOS_MEM_OLD_EXT_HIGH, memory_above_1m >> 8 & 0xFF);
|
|
|
|
rtc.cmos_write(CMOS_MEM_EXTMEM_LOW, memory_above_1m & 0xFF);
|
|
|
|
rtc.cmos_write(CMOS_MEM_EXTMEM_HIGH, memory_above_1m >> 8 & 0xFF);
|
|
|
|
|
|
|
|
var memory_above_16m = 0; // in 64k blocks
|
|
|
|
if(this.memory_size >= 16 * 1024 * 1024)
|
|
|
|
{
|
|
|
|
memory_above_16m = (this.memory_size - 16 * 1024 * 1024) >> 16;
|
|
|
|
memory_above_16m = Math.min(memory_above_16m, 0xFFFF);
|
|
|
|
}
|
|
|
|
rtc.cmos_write(CMOS_MEM_EXTMEM2_LOW, memory_above_16m & 0xFF);
|
|
|
|
rtc.cmos_write(CMOS_MEM_EXTMEM2_HIGH, memory_above_16m >> 8 & 0xFF);
|
|
|
|
|
|
|
|
// memory above 4G (not supported by this emulator)
|
2016-01-01 18:23:40 +01:00
|
|
|
rtc.cmos_write(CMOS_MEM_HIGHMEM_LOW, 0);
|
|
|
|
rtc.cmos_write(CMOS_MEM_HIGHMEM_MID, 0);
|
|
|
|
rtc.cmos_write(CMOS_MEM_HIGHMEM_HIGH, 0);
|
2015-02-25 18:23:10 +01:00
|
|
|
|
2016-01-03 02:30:36 +01:00
|
|
|
rtc.cmos_write(CMOS_EQUIPMENT_INFO, 0x2F);
|
|
|
|
|
|
|
|
rtc.cmos_write(CMOS_BIOS_SMP_COUNT, 0);
|
2015-02-25 18:23:10 +01:00
|
|
|
};
|
|
|
|
|
2014-12-21 19:19:04 +01:00
|
|
|
CPU.prototype.load_bios = function()
|
|
|
|
{
|
|
|
|
var bios = this.bios.main;
|
|
|
|
var vga_bios = this.bios.vga;
|
|
|
|
|
|
|
|
if(!bios)
|
|
|
|
{
|
|
|
|
dbg_log("Warning: No BIOS");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// load bios
|
|
|
|
var data = new Uint8Array(bios),
|
|
|
|
start = 0x100000 - bios.byteLength;
|
|
|
|
|
2016-08-02 04:32:02 +02:00
|
|
|
this.write_blob(data, start);
|
2014-12-21 19:19:04 +01:00
|
|
|
|
|
|
|
if(vga_bios)
|
|
|
|
{
|
|
|
|
// load vga bios
|
2017-03-11 00:08:35 +01:00
|
|
|
var vga_bios8 = new Uint8Array(vga_bios);
|
|
|
|
|
|
|
|
// older versions of seabios
|
|
|
|
this.write_blob(vga_bios8, 0xC0000);
|
|
|
|
|
|
|
|
// newer versions of seabios (needs to match pci rom address)
|
|
|
|
this.io.mmap_register(0xFEB00000, 0x100000,
|
|
|
|
function(addr)
|
|
|
|
{
|
|
|
|
addr = (addr - 0xFEB00000) | 0;
|
|
|
|
if(addr < vga_bios8.length)
|
|
|
|
{
|
|
|
|
return vga_bios8[addr];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function(addr, value)
|
|
|
|
{
|
|
|
|
});
|
2014-12-21 19:19:04 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_log("Warning: No VGA BIOS");
|
|
|
|
}
|
|
|
|
|
|
|
|
// seabios expects the bios to be mapped to 0xFFF00000 also
|
2015-09-14 01:45:51 +02:00
|
|
|
this.io.mmap_register(0xFFF00000, 0x100000,
|
2014-12-21 19:19:04 +01:00
|
|
|
function(addr)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.mem8[addr];
|
2014-12-21 19:19:04 +01:00
|
|
|
}.bind(this),
|
|
|
|
function(addr, value)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
2016-08-02 04:15:24 +02:00
|
|
|
this.mem8[addr] = value;
|
2014-12-21 19:19:04 +01:00
|
|
|
}.bind(this));
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.do_run = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-08-02 02:41:30 +02:00
|
|
|
/** @type {number} */
|
|
|
|
var start = v86.microtick();
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
var now = start;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// outer loop:
|
|
|
|
// runs cycles + timers
|
2014-02-07 17:41:48 +01:00
|
|
|
for(; now - start < TIME_PER_FRAME;)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-04-01 01:12:02 +02:00
|
|
|
this.run_hardware_timers(now);
|
2014-07-12 22:20:57 +02:00
|
|
|
this.handle_irqs();
|
2017-04-01 01:12:02 +02:00
|
|
|
|
2016-08-02 02:41:30 +02:00
|
|
|
this.do_many_cycles();
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-08-04 18:21:41 +02:00
|
|
|
if(this.in_hlt)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-14 13:15:09 +01:00
|
|
|
now = v86.microtick();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-02 02:41:30 +02:00
|
|
|
CPU.prototype.do_many_cycles = function()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
this.do_many_cycles_unsafe();
|
|
|
|
}
|
|
|
|
catch(e)
|
|
|
|
{
|
|
|
|
this.exception_cleanup(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.do_many_cycles_unsafe = function()
|
|
|
|
{
|
|
|
|
// inner loop:
|
|
|
|
// runs only cycles
|
|
|
|
for(var k = LOOP_COUNTER; k--;)
|
|
|
|
{
|
2016-08-03 01:40:26 +02:00
|
|
|
this.cycle_internal();
|
2016-08-02 02:41:30 +02:00
|
|
|
}
|
2017-03-07 01:03:06 +01:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-02 02:41:30 +02:00
|
|
|
// Some functions must not be inlined, because then more code is in the
|
|
|
|
// deoptimized try-catch block.
|
2013-11-07 21:30:18 +01:00
|
|
|
// This trick is a bit ugly, but it works without further complication.
|
|
|
|
if(typeof window !== "undefined")
|
|
|
|
{
|
2016-08-02 02:41:30 +02:00
|
|
|
window["__no_inline_for_closure_compiler__"] = [
|
|
|
|
CPU.prototype.exception_cleanup,
|
|
|
|
CPU.prototype.do_many_cycles_unsafe,
|
|
|
|
CPU.prototype.do_many_cycles,
|
|
|
|
];
|
2017-03-07 01:03:06 +01:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-01 23:22:38 +02:00
|
|
|
/** @const */
|
|
|
|
var PROFILING = false;
|
|
|
|
|
|
|
|
if(PROFILING)
|
|
|
|
{
|
|
|
|
var instruction_total = new Float64Array(256);
|
2016-08-02 02:11:05 +02:00
|
|
|
var instruction_count = new Float64Array(256);
|
2016-08-01 23:22:38 +02:00
|
|
|
|
2016-08-02 02:11:05 +02:00
|
|
|
window["print_profiling"] = function print_profiling()
|
2016-08-01 23:22:38 +02:00
|
|
|
{
|
|
|
|
var prof_instructions = [];
|
|
|
|
for(var i = 0; i < 256; i++) prof_instructions[i] = {
|
|
|
|
n: h(i, 2),
|
|
|
|
total: instruction_total[i],
|
|
|
|
count: instruction_count[i],
|
|
|
|
per: (instruction_total[i] / instruction_count[i]) || 0,
|
2017-03-07 01:03:06 +01:00
|
|
|
};
|
2016-08-01 23:22:38 +02:00
|
|
|
|
|
|
|
console.log("count:");
|
|
|
|
console.table(prof_instructions.sort((p0, p1) => p1.count - p0.count));
|
|
|
|
|
|
|
|
console.log("time:");
|
|
|
|
console.table(prof_instructions.sort((p0, p1) => p1.total - p0.total));
|
|
|
|
|
|
|
|
console.log("time/count:");
|
|
|
|
console.table(prof_instructions.sort((p0, p1) => p1.per - p0.per));
|
2017-03-07 01:03:06 +01:00
|
|
|
};
|
2016-08-01 23:22:38 +02:00
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
/**
|
|
|
|
* execute a single instruction cycle on the cpu
|
|
|
|
* this includes reading all prefixes and the whole instruction
|
|
|
|
*/
|
2016-07-26 21:42:42 +02:00
|
|
|
CPU.prototype.cycle_internal = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.previous_ip = this.instruction_pointer;
|
2014-05-12 18:44:28 +02:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
this.timestamp_counter++;
|
|
|
|
|
2016-08-01 23:22:38 +02:00
|
|
|
if(PROFILING)
|
|
|
|
{
|
|
|
|
var start = performance.now();
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var opcode = this.read_imm8();
|
2016-08-01 23:22:38 +02:00
|
|
|
//this.translate_address_read(this.instruction_pointer + 15|0)
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
this.debug.logop(this.instruction_pointer - 1 >>> 0, opcode);
|
2014-12-06 23:20:54 +01:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// call the instruction
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table[opcode](this);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-01 23:22:38 +02:00
|
|
|
if(PROFILING)
|
|
|
|
{
|
|
|
|
var end = performance.now();
|
|
|
|
instruction_total[opcode] += end - start;
|
|
|
|
instruction_count[opcode]++;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.flags & flag_trap)
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
dbg_log("Trap flag: Ignored", LOG_CPU);
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-07-26 21:42:42 +02:00
|
|
|
/** @export */
|
|
|
|
CPU.prototype.cycle = function()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
this.cycle_internal();
|
|
|
|
}
|
|
|
|
catch(e)
|
|
|
|
{
|
|
|
|
this.exception_cleanup(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU.prototype.segment_prefix_op = function(sreg)
|
|
|
|
{
|
|
|
|
dbg_assert(sreg <= 5);
|
|
|
|
this.prefixes |= sreg + 1;
|
|
|
|
this.run_prefix_instruction();
|
|
|
|
this.prefixes = 0;
|
|
|
|
};
|
|
|
|
|
2016-08-03 01:40:26 +02:00
|
|
|
CPU.prototype.run_prefix_instruction = function()
|
2014-12-12 23:24:37 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_osize_32())
|
|
|
|
{
|
|
|
|
this.table32[this.read_imm8()](this);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.table16[this.read_imm8()](this);
|
|
|
|
}
|
2014-12-12 23:24:37 +01:00
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.hlt_loop = function()
|
2014-06-22 19:10:35 +02:00
|
|
|
{
|
2016-08-04 18:21:41 +02:00
|
|
|
dbg_assert(this.flags & flag_interrupt);
|
2014-06-29 22:28:55 +02:00
|
|
|
//dbg_log("In HLT loop", LOG_CPU);
|
|
|
|
|
2017-04-01 01:12:02 +02:00
|
|
|
this.run_hardware_timers(v86.microtick());
|
|
|
|
this.handle_irqs();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
2014-06-22 19:10:35 +02:00
|
|
|
|
2017-04-01 01:12:02 +02:00
|
|
|
CPU.prototype.run_hardware_timers = function(now)
|
|
|
|
{
|
2014-06-22 19:10:35 +02:00
|
|
|
if(ENABLE_HPET)
|
|
|
|
{
|
2015-01-14 02:43:09 +01:00
|
|
|
var pit_time = this.devices.pit.timer(now, this.devices.hpet.legacy_mode);
|
|
|
|
var rtc_time = this.devices.rtc.timer(now, this.devices.hpet.legacy_mode);
|
2014-06-22 19:10:35 +02:00
|
|
|
this.devices.hpet.timer(now);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-01-14 02:43:09 +01:00
|
|
|
var pit_time = this.devices.pit.timer(now, false);
|
|
|
|
var rtc_time = this.devices.rtc.timer(now, false);
|
2014-06-22 19:10:35 +02:00
|
|
|
}
|
|
|
|
|
2015-12-31 00:31:08 +01:00
|
|
|
if(ENABLE_ACPI)
|
|
|
|
{
|
2017-04-01 00:16:43 +02:00
|
|
|
this.devices.acpi.timer(now);
|
2015-12-31 00:31:08 +01:00
|
|
|
this.devices.apic.timer(now);
|
|
|
|
}
|
2014-06-22 19:10:35 +02:00
|
|
|
};
|
|
|
|
|
2015-04-28 11:12:40 +02:00
|
|
|
CPU.prototype.clear_prefixes = function()
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
this.prefixes = 0;
|
2015-04-28 11:12:40 +02:00
|
|
|
};
|
|
|
|
|
2017-03-16 02:26:31 +01:00
|
|
|
CPU.prototype.set_cr0 = function(cr0)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-04-12 20:16:26 +02:00
|
|
|
//dbg_log("cr0 = " + h(this.cr[0] >>> 0), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-03-16 02:26:31 +01:00
|
|
|
if((cr0 & (CR0_PE | CR0_PG)) === CR0_PG)
|
|
|
|
{
|
|
|
|
// cannot load PG without PE
|
|
|
|
throw this.debug.unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.cr[0] = cr0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.fpu)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-03 22:02:43 +01:00
|
|
|
// if there's no FPU, keep emulation set
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[0] |= CR0_EM;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[0] |= CR0_ET;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-03-16 02:26:31 +01:00
|
|
|
var new_paging = (this.cr[0] & CR0_PG) === CR0_PG;
|
2016-08-01 23:22:38 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
dbg_assert(typeof this.paging === "boolean");
|
|
|
|
if(new_paging !== this.paging)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.paging = new_paging;
|
|
|
|
this.full_clear_tlb();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2017-03-12 01:33:41 +01:00
|
|
|
|
|
|
|
this.protected_mode = (this.cr[0] & CR0_PE) === CR0_PE;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.cpl_changed = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.read_imm8 = function()
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
if((this.instruction_pointer & ~0xFFF) ^ this.last_virt_eip)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.eip_phys = this.translate_address_read(this.instruction_pointer) ^ this.instruction_pointer;
|
|
|
|
this.last_virt_eip = this.instruction_pointer & ~0xFFF;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var data8 = this.read8(this.eip_phys ^ this.instruction_pointer);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.instruction_pointer + 1 | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return data8;
|
2013-11-07 21:30:18 +01:00
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.read_imm8s = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.read_imm8() << 24 >> 24;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.read_imm16 = function()
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
// Two checks in one comparison:
|
|
|
|
// 1. Did the high 20 bits of eip change
|
|
|
|
// or 2. Are the low 12 bits of eip 0xFFF (and this read crosses a page boundary)
|
|
|
|
if(((this.instruction_pointer ^ this.last_virt_eip) >>> 0) > 0xFFE)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.read_imm8() | this.read_imm8() << 8;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var data16 = this.read16(this.eip_phys ^ this.instruction_pointer);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.instruction_pointer + 2 | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return data16;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.read_imm32s = function()
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
// Analogue to the above comment
|
|
|
|
if(((this.instruction_pointer ^ this.last_virt_eip) >>> 0) > 0xFFC)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.read_imm16() | this.read_imm16() << 16;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var data32 = this.read32s(this.eip_phys ^ this.instruction_pointer);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.instruction_pointer + 4 | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return data32;
|
2013-11-07 21:30:18 +01:00
|
|
|
};
|
|
|
|
|
2016-08-03 01:40:26 +02:00
|
|
|
CPU.prototype.read_modrm_byte = function()
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
2016-08-03 01:40:26 +02:00
|
|
|
this.modrm_byte = this.read_imm8();
|
2015-09-14 01:45:51 +02:00
|
|
|
};
|
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU.prototype.read_op0F = CPU.prototype.read_imm8;
|
2016-08-03 01:40:26 +02:00
|
|
|
CPU.prototype.read_sib = CPU.prototype.read_imm8;
|
|
|
|
CPU.prototype.read_op8 = CPU.prototype.read_imm8;
|
|
|
|
CPU.prototype.read_op8s = CPU.prototype.read_imm8s;
|
|
|
|
CPU.prototype.read_op16 = CPU.prototype.read_imm16;
|
|
|
|
CPU.prototype.read_op32s = CPU.prototype.read_imm32s;
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU.prototype.read_disp8 = CPU.prototype.read_imm8;
|
2016-08-03 01:40:26 +02:00
|
|
|
CPU.prototype.read_disp8s = CPU.prototype.read_imm8s;
|
|
|
|
CPU.prototype.read_disp16 = CPU.prototype.read_imm16;
|
|
|
|
CPU.prototype.read_disp32s = CPU.prototype.read_imm32s;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU.prototype.init2 = function () {};
|
|
|
|
CPU.prototype.branch_taken = function () {};
|
|
|
|
CPU.prototype.branch_not_taken = function () {};
|
|
|
|
CPU.prototype.diverged = function () {};
|
|
|
|
|
|
|
|
CPU.prototype.modrm_resolve = function(modrm_byte)
|
|
|
|
{
|
|
|
|
dbg_assert(modrm_byte < 0xC0);
|
|
|
|
|
|
|
|
return (this.is_asize_32() ? this.modrm_table32 : this.modrm_table16)[modrm_byte](this);
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.sib_resolve = function(mod)
|
|
|
|
{
|
|
|
|
return this.sib_table[this.read_sib()](this, mod);
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.clear_instruction_cache = function() {};
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
// read word from a page boundary, given 2 physical addresses
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.virt_boundary_read16 = function(low, high)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) === 0xFFF);
|
|
|
|
dbg_assert((high & 0xFFF) === 0);
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read8(low) | this.read8(high) << 8;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// read doubleword from a page boundary, given 2 addresses
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.virt_boundary_read32s = function(low, high)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) >= 0xFFD);
|
|
|
|
dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF));
|
|
|
|
|
2014-01-10 04:24:56 +01:00
|
|
|
var mid;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(low & 1)
|
|
|
|
{
|
|
|
|
if(low & 2)
|
|
|
|
{
|
|
|
|
// 0xFFF
|
2016-08-02 04:15:24 +02:00
|
|
|
mid = this.read_aligned16(high - 2 >> 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFD
|
2016-08-02 04:15:24 +02:00
|
|
|
mid = this.read_aligned16(low + 1 >> 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFE
|
2015-04-22 05:00:34 +02:00
|
|
|
mid = this.virt_boundary_read16(low + 1 | 0, high - 1 | 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
return this.read8(low) | mid << 8 | this.read8(high) << 24;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.virt_boundary_write16 = function(low, high, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) === 0xFFF);
|
|
|
|
dbg_assert((high & 0xFFF) === 0);
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(low, value);
|
|
|
|
this.write8(high, value >> 8);
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.virt_boundary_write32 = function(low, high, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert((low & 0xFFF) >= 0xFFD);
|
|
|
|
dbg_assert((high - 3 & 0xFFF) === (low & 0xFFF));
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(low, value);
|
|
|
|
this.write8(high, value >> 24);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(low & 1)
|
|
|
|
{
|
|
|
|
if(low & 2)
|
|
|
|
{
|
|
|
|
// 0xFFF
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(high - 2, value >> 8);
|
|
|
|
this.write8(high - 1, value >> 16);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFD
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(low + 1 | 0, value >> 8);
|
|
|
|
this.write8(low + 2 | 0, value >> 16);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 0xFFE
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(low + 1 | 0, value >> 8);
|
|
|
|
this.write8(high - 1, value >> 16);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// safe_read, safe_write
|
|
|
|
// read or write byte, word or dword to the given *virtual* address,
|
|
|
|
// and be safe on page boundaries
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.safe_read8 = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-02 01:05:49 +01:00
|
|
|
dbg_assert(addr < 0x80000000);
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read8(this.translate_address_read(addr));
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.safe_read16 = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging && (addr & 0xFFF) === 0xFFF)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-04-22 05:00:34 +02:00
|
|
|
return this.safe_read8(addr) | this.safe_read8(addr + 1 | 0) << 8;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read16(this.translate_address_read(addr));
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.safe_read32s = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging && (addr & 0xFFF) >= 0xFFD)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-04-22 05:00:34 +02:00
|
|
|
return this.safe_read16(addr) | this.safe_read16(addr + 2 | 0) << 16;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read32s(this.translate_address_read(addr));
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.safe_write8 = function(addr, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-01-02 01:05:49 +01:00
|
|
|
dbg_assert(addr < 0x80000000);
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(this.translate_address_write(addr), value);
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.safe_write16 = function(addr, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var phys_low = this.translate_address_write(addr);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if((addr & 0xFFF) === 0xFFF)
|
|
|
|
{
|
2015-04-22 05:00:34 +02:00
|
|
|
this.virt_boundary_write16(phys_low, this.translate_address_write(addr + 1 | 0), value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write16(phys_low, value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.safe_write32 = function(addr, value)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var phys_low = this.translate_address_write(addr);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if((addr & 0xFFF) >= 0xFFD)
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
// XXX
|
|
|
|
this.virt_boundary_write32(phys_low, this.translate_address_write(addr + 3 & ~3) | (addr + 3) & 3, value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write32(phys_low, value);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// read 2 or 4 byte from ip, depending on address size attribute
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.read_moffs = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_asize_32())
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-08-03 01:40:26 +02:00
|
|
|
return this.get_seg_prefix(reg_ds) + this.read_op32s() | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-03 01:40:26 +02:00
|
|
|
return this.get_seg_prefix(reg_ds) + this.read_op16() | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
CPU.prototype.getiopl = function()
|
2014-12-06 22:51:04 +01:00
|
|
|
{
|
|
|
|
return this.flags >> 12 & 3;
|
2014-12-06 23:20:54 +01:00
|
|
|
};
|
2014-12-06 22:51:04 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.vm86_mode = function()
|
2014-12-06 23:20:54 +01:00
|
|
|
{
|
|
|
|
return !!(this.flags & flag_vm);
|
|
|
|
};
|
2014-12-06 22:51:04 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_eflags = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
return (this.flags & ~flags_all) | !!this.getcf() | !!this.getpf() << 2 | !!this.getaf() << 4 |
|
2014-06-15 22:25:17 +02:00
|
|
|
!!this.getzf() << 6 | !!this.getsf() << 7 | !!this.getof() << 11;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
/**
|
|
|
|
* Update the flags register depending on iopl and cpl
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.update_eflags = function(new_flags)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-03-09 01:33:39 +01:00
|
|
|
var dont_update = flag_rf | flag_vm | flag_vip | flag_vif,
|
2014-06-15 22:25:17 +02:00
|
|
|
clear = ~flag_vip & ~flag_vif & flags_mask;
|
|
|
|
|
|
|
|
if(this.flags & flag_vm)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
// other case needs to be handled in popf or iret
|
2014-12-06 22:51:04 +01:00
|
|
|
dbg_assert(this.getiopl() === 3);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2015-03-09 01:33:39 +01:00
|
|
|
dont_update |= flag_iopl;
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2015-03-09 01:33:39 +01:00
|
|
|
// don't clear vip or vif
|
2014-06-15 22:25:17 +02:00
|
|
|
clear |= flag_vip | flag_vif;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
else
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.protected_mode) dbg_assert(this.cpl === 0);
|
|
|
|
|
|
|
|
if(this.cpl)
|
|
|
|
{
|
|
|
|
// cpl > 0
|
|
|
|
// cannot update iopl
|
2015-03-09 01:33:39 +01:00
|
|
|
dont_update |= flag_iopl;
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2014-12-06 22:51:04 +01:00
|
|
|
if(this.cpl > this.getiopl())
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
// cpl > iopl
|
2015-03-09 01:33:39 +01:00
|
|
|
// cannot update interrupt flag
|
|
|
|
dont_update |= flag_interrupt;
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2015-03-09 01:33:39 +01:00
|
|
|
this.flags = (new_flags ^ ((this.flags ^ new_flags) & dont_update)) & clear | flags_default;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.flags_changed = 0;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-12-30 19:57:20 +01:00
|
|
|
CPU.prototype.get_stack_reg = function()
|
|
|
|
{
|
|
|
|
if(this.stack_size_32)
|
|
|
|
{
|
|
|
|
return this.reg32s[reg_esp];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.reg16[reg_sp];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.set_stack_reg = function(value)
|
|
|
|
{
|
|
|
|
if(this.stack_size_32)
|
|
|
|
{
|
|
|
|
this.reg32s[reg_esp] = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.reg16[reg_sp] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.adjust_stack_reg = function(value)
|
|
|
|
{
|
|
|
|
if(this.stack_size_32)
|
|
|
|
{
|
|
|
|
this.reg32s[reg_esp] += value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.reg16[reg_sp] += value;
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_stack_pointer = function(mod)
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
2015-04-17 23:58:35 +02:00
|
|
|
if(this.stack_size_32)
|
|
|
|
{
|
2015-12-30 19:57:20 +01:00
|
|
|
return this.get_seg(reg_ss) + this.reg32s[reg_esp] + mod | 0;
|
2015-04-17 23:58:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-12-30 19:57:20 +01:00
|
|
|
return this.get_seg(reg_ss) + (this.reg16[reg_sp] + mod & 0xFFFF) | 0;
|
2015-04-17 23:58:35 +02:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/*
|
2015-09-14 01:45:51 +02:00
|
|
|
* returns the "real" instruction pointer,
|
2013-11-07 21:30:18 +01:00
|
|
|
* without segment offset
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_real_eip = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.instruction_pointer - this.get_seg(reg_cs) | 0;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.call_interrupt_vector = function(interrupt_nr, is_software_int, error_code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-15 00:50:35 +01:00
|
|
|
//dbg_log("int " + h(interrupt_nr, 2) + " (" + (is_software_int ? "soft" : "hard") + "ware)", LOG_CPU);
|
2017-04-02 18:03:50 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("int " + h(interrupt_nr) + " start" +
|
|
|
|
" (" + (is_software_int ? "soft" : "hard") + "ware)");
|
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_regs();
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
this.debug.debug_interrupt(interrupt_nr);
|
2014-06-24 16:10:40 +02:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_assert(error_code === false || typeof error_code === "number");
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
// we have to leave hlt_loop at some point, this is a
|
2014-06-22 19:10:35 +02:00
|
|
|
// good place to do it
|
2014-06-29 22:28:55 +02:00
|
|
|
//this.in_hlt && dbg_log("Leave HLT loop", LOG_CPU);
|
2014-06-22 19:10:35 +02:00
|
|
|
this.in_hlt = false;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.protected_mode)
|
|
|
|
{
|
2015-04-12 20:16:26 +02:00
|
|
|
if(this.vm86_mode() && (this.cr[4] & CR4_VME))
|
2014-01-03 22:02:43 +01:00
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("VME");
|
2014-01-03 22:02:43 +01:00
|
|
|
}
|
|
|
|
|
2014-12-06 23:20:54 +01:00
|
|
|
if(this.vm86_mode() && is_software_int && this.getiopl() < 3)
|
2013-12-09 12:30:24 +01:00
|
|
|
{
|
2015-12-31 00:31:08 +01:00
|
|
|
dbg_log("call_interrupt_vector #GP. vm86 && software int && iopl < 3", LOG_CPU);
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(0);
|
2013-12-09 12:30:24 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((interrupt_nr << 3 | 7) > this.idtr_size)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_log(interrupt_nr, LOG_CPU);
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var addr = this.idtr_offset + (interrupt_nr << 3) | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert((addr & 0xFFF) < 0xFF8);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
addr = this.translate_address_system_read(addr);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var base = this.read16(addr) | this.read16(addr + 6 | 0) << 16;
|
|
|
|
var selector = this.read16(addr + 2 | 0);
|
|
|
|
var access = this.read8(addr + 5 | 0);
|
2016-02-15 00:31:00 +01:00
|
|
|
var dpl = access >> 5 & 3;
|
|
|
|
var type = access & 31;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
if((access & 0x80) === 0)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// present bit not set
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#NP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(is_software_int && dpl < this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_log("#gp software interrupt (" + h(interrupt_nr, 2) + ") and dpl < cpl", LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(interrupt_nr << 3 | 2);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
if(type === 5)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
// task gate
|
|
|
|
dbg_log("interrupt to task gate: int=" + h(interrupt_nr, 2) + " sel=" + h(selector, 4) + " dpl=" + dpl, LOG_CPU);
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
this.do_task_switch(selector, error_code);
|
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("int end");
|
2015-09-14 01:45:51 +02:00
|
|
|
return;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-15 00:31:00 +01:00
|
|
|
|
|
|
|
if((type & ~1 & ~8) !== 6)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// invalid type
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_log("invalid type: " + h(type));
|
2015-03-09 01:53:38 +01:00
|
|
|
dbg_log(h(addr) + " " + h(base >>> 0) + " " + h(selector));
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
var is_trap = (type & 1) === 1;
|
|
|
|
var is_16 = (type & 8) === 0;
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_assert((base >>> 0) <= info.effective_limit);
|
|
|
|
dbg_assert(info.is_valid);
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("is null");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!info.is_executable || info.dpl > this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_log("not exec");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
2017-04-06 02:54:44 +02:00
|
|
|
// kvm-unit-test
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_log("not present");
|
2017-04-06 02:54:44 +02:00
|
|
|
this.trigger_np(interrupt_nr << 3 | 2);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2017-03-16 02:11:29 +01:00
|
|
|
var old_flags = this.get_eflags();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
//dbg_log("interrupt " + h(interrupt_nr, 2) + " (" + (is_software_int ? "soft" : "hard") + "ware) from cpl=" + this.cpl + " vm=" + (this.flags & flag_vm) + " cs:eip=" + h(this.sreg[reg_cs], 4) + ":" + h(this.get_real_eip(), 8) + " to cpl="
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!info.dc_bit && info.dpl < this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// inter privilege level interrupt
|
2013-12-09 12:30:24 +01:00
|
|
|
// interrupt from vm86 mode
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
//dbg_log("Inter privilege interrupt gate=" + h(selector, 4) + ":" + h(base >>> 0, 8) + " trap=" + is_trap + " 16bit=" + is_16, LOG_CPU);
|
2017-04-02 18:03:50 +02:00
|
|
|
//this.debug.dump_regs();
|
2016-08-01 22:26:10 +02:00
|
|
|
var tss_stack_addr = this.get_tss_stack_addr(info.dpl);
|
2016-02-15 00:31:00 +01:00
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
if(this.tss_size_32)
|
|
|
|
{
|
|
|
|
var new_esp = this.read32s(tss_stack_addr);
|
|
|
|
var new_ss = this.read16(tss_stack_addr + 4 | 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var new_esp = this.read16(tss_stack_addr);
|
|
|
|
var new_ss = this.read16(tss_stack_addr + 2 | 0);
|
|
|
|
}
|
2016-02-15 00:31:00 +01:00
|
|
|
var ss_info = this.lookup_segment_selector(new_ss);
|
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
// Disabled: Incorrect handling of direction bit
|
|
|
|
// See http://css.csail.mit.edu/6.858/2014/readings/i386/s06_03.htm
|
|
|
|
//if(!((new_esp >>> 0) <= ss_info.effective_limit))
|
|
|
|
// debugger;
|
|
|
|
//dbg_assert((new_esp >>> 0) <= ss_info.effective_limit);
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_assert(ss_info.is_valid && !ss_info.is_system && ss_info.is_writable);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
if(ss_info.is_null)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#TS handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-15 00:31:00 +01:00
|
|
|
if(ss_info.rpl !== info.dpl) // xxx: 0 in v86 mode
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#TS handler");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
if(ss_info.dpl !== info.dpl || !ss_info.rw_bit)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#TS handler");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
if(!ss_info.is_present)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#TS handler");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
var old_esp = this.reg32s[reg_esp];
|
|
|
|
var old_ss = this.sreg[reg_ss];
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
if(old_flags & flag_vm)
|
|
|
|
{
|
2015-03-09 01:53:38 +01:00
|
|
|
//dbg_log("return from vm86 mode");
|
2017-04-02 18:03:50 +02:00
|
|
|
//this.debug.dump_regs();
|
2015-03-09 01:53:38 +01:00
|
|
|
dbg_assert(info.dpl === 0, "switch to non-0 dpl from vm86 mode");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
var stack_space = (is_16 ? 2 : 4) * (5 + (error_code !== false) + 4 * ((old_flags & flag_vm) === flag_vm));
|
|
|
|
var new_stack_pointer = ss_info.base + (ss_info.size ? new_esp - stack_space : (new_esp - stack_space & 0xFFFF));
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
// XXX: with new cpl or with cpl 0?
|
|
|
|
this.translate_address_system_write(new_stack_pointer);
|
|
|
|
this.translate_address_system_write(ss_info.base + new_esp - 1);
|
|
|
|
|
|
|
|
// no exceptions below
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
this.cpl = info.dpl;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl_changed();
|
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(info.size);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
this.flags &= ~flag_vm & ~flag_rf;
|
|
|
|
|
|
|
|
this.switch_seg(reg_ss, new_ss);
|
2015-12-30 19:57:20 +01:00
|
|
|
this.set_stack_reg(new_esp);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
if(old_flags & flag_vm)
|
|
|
|
{
|
2015-03-09 01:53:38 +01:00
|
|
|
if(is_16)
|
|
|
|
{
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_assert(false);
|
2015-03-09 01:53:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.push32(this.sreg[reg_gs]);
|
|
|
|
this.push32(this.sreg[reg_fs]);
|
|
|
|
this.push32(this.sreg[reg_ds]);
|
|
|
|
this.push32(this.sreg[reg_es]);
|
|
|
|
}
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
this.push16(old_ss);
|
|
|
|
this.push16(old_esp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.push32(old_ss);
|
|
|
|
this.push32(old_esp);
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
else if(info.dc_bit || info.dpl === this.cpl)
|
|
|
|
{
|
2016-02-15 00:31:00 +01:00
|
|
|
// intra privilege level interrupt
|
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
//dbg_log("Intra privilege interrupt gate=" + h(selector, 4) + ":" + h(base >>> 0, 8) +
|
2015-03-09 01:53:38 +01:00
|
|
|
// " trap=" + is_trap + " 16bit=" + is_16 +
|
|
|
|
// " cpl=" + this.cpl + " dpl=" + info.dpl + " conforming=" + +info.dc_bit, LOG_CPU);
|
2017-04-02 18:03:50 +02:00
|
|
|
//this.debug.dump_regs();
|
2015-03-09 01:53:38 +01:00
|
|
|
|
2015-12-31 00:31:08 +01:00
|
|
|
if(this.flags & flag_vm)
|
|
|
|
{
|
2016-02-15 00:31:00 +01:00
|
|
|
dbg_assert(false, "check error code");
|
2015-12-31 00:31:08 +01:00
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
var stack_space = (is_16 ? 2 : 4) * (3 + (error_code !== false));
|
2015-03-09 01:53:38 +01:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
// XXX: with current cpl or with cpl 0?
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-stack_space), stack_space);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
// no exceptions below
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
this.push16(old_flags);
|
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
if(error_code !== false)
|
|
|
|
{
|
|
|
|
this.push16(error_code);
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
base &= 0xFFFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.push32(old_flags);
|
|
|
|
this.push32(this.sreg[reg_cs]);
|
|
|
|
this.push32(this.get_real_eip());
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
if(error_code !== false)
|
|
|
|
{
|
|
|
|
this.push32(error_code);
|
|
|
|
}
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
if(old_flags & flag_vm)
|
|
|
|
{
|
|
|
|
this.switch_seg(reg_gs, 0);
|
|
|
|
this.switch_seg(reg_fs, 0);
|
|
|
|
this.switch_seg(reg_ds, 0);
|
|
|
|
this.switch_seg(reg_es, 0);
|
|
|
|
}
|
|
|
|
|
2015-03-09 01:53:38 +01:00
|
|
|
this.sreg[reg_cs] = selector & ~3 | this.cpl;
|
2016-10-23 17:37:02 +02:00
|
|
|
dbg_assert((this.sreg[reg_cs] & 3) === this.cpl);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(info.size);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
this.segment_limits[reg_cs] = info.effective_limit;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + base | 0;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
this.flags &= ~flag_nt & ~flag_vm & ~flag_rf & ~flag_trap;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!is_trap)
|
|
|
|
{
|
|
|
|
// clear int flag for interrupt gates
|
|
|
|
this.flags &= ~flag_interrupt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-15 00:31:00 +01:00
|
|
|
if(!this.page_fault) // XXX
|
|
|
|
{
|
|
|
|
this.handle_irqs();
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// call 4 byte cs:ip interrupt vector from ivt at cpu.memory 0
|
2015-03-09 01:53:38 +01:00
|
|
|
|
|
|
|
var index = interrupt_nr << 2;
|
2016-08-02 04:15:24 +02:00
|
|
|
var new_ip = this.read16(index);
|
|
|
|
var new_cs = this.read16(index + 2 | 0);
|
2015-03-09 01:53:38 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// push flags, cs:ip
|
2017-03-16 02:11:29 +01:00
|
|
|
this.push16(this.get_eflags());
|
2014-06-15 22:25:17 +02:00
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
|
|
|
|
2016-02-15 00:31:00 +01:00
|
|
|
this.flags &= ~flag_interrupt;
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
this.switch_cs_real_mode(new_cs);
|
2015-03-09 01:53:38 +01:00
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + new_ip | 0;
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
2016-02-15 00:31:00 +01:00
|
|
|
|
|
|
|
//dbg_log("int to:", LOG_CPU);
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("int end");
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.iret16 = function()
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
2016-02-14 22:11:06 +01:00
|
|
|
this.iret(true);
|
2016-02-15 00:31:00 +01:00
|
|
|
};
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
CPU.prototype.iret32 = function()
|
|
|
|
{
|
|
|
|
this.iret(false);
|
2016-02-15 00:31:00 +01:00
|
|
|
};
|
2015-04-22 04:15:32 +02:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
CPU.prototype.iret = function(is_16)
|
|
|
|
{
|
|
|
|
//dbg_log("iret is_16=" + is_16, LOG_CPU);
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("iret" + (is_16 ? "16" : "32") + " start");
|
2017-04-02 18:03:50 +02:00
|
|
|
//this.debug.dump_regs();
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.vm86_mode() && this.getiopl() < 3)
|
|
|
|
{
|
|
|
|
// vm86 mode, iopl != 3
|
|
|
|
dbg_log("#gp iret vm86 mode, iopl != 3", LOG_CPU);
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
var new_eip = this.safe_read16(this.get_stack_pointer(0));
|
|
|
|
var new_cs = this.safe_read16(this.get_stack_pointer(2));
|
|
|
|
var new_flags = this.safe_read16(this.get_stack_pointer(4));
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
else
|
|
|
|
{
|
2016-02-14 22:11:06 +01:00
|
|
|
var new_eip = this.safe_read32s(this.get_stack_pointer(0));
|
|
|
|
var new_cs = this.safe_read16(this.get_stack_pointer(4));
|
|
|
|
var new_flags = this.safe_read32s(this.get_stack_pointer(8));
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
|
2014-12-06 23:20:54 +01:00
|
|
|
if(!this.protected_mode || (this.vm86_mode() && this.getiopl() === 3))
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
2016-02-14 22:11:06 +01:00
|
|
|
if(new_eip & 0xFFFF0000)
|
2015-04-22 04:15:32 +02:00
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
this.switch_cs_real_mode(new_cs);
|
2016-02-14 22:11:06 +01:00
|
|
|
this.instruction_pointer = new_eip + this.get_seg(reg_cs) | 0;
|
|
|
|
|
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
this.update_eflags(new_flags | this.flags & ~0xFFFF);
|
|
|
|
this.adjust_stack_reg(3 * 2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.update_eflags(new_flags);
|
|
|
|
this.adjust_stack_reg(3 * 4);
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
//dbg_log("iret32 to:", LOG_CPU);
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("iret end");
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
this.handle_irqs();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
dbg_assert(!this.vm86_mode());
|
2014-06-15 22:25:17 +02:00
|
|
|
|
|
|
|
if(this.flags & flag_nt)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
if(DEBUG) throw this.debug.unimpl("nt");
|
2016-02-14 22:11:06 +01:00
|
|
|
this.trigger_gp(0);
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(new_flags & flag_vm)
|
|
|
|
{
|
|
|
|
if(this.cpl === 0)
|
|
|
|
{
|
|
|
|
// return to virtual 8086 mode
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
// vm86 cannot be set in 16 bit flag
|
|
|
|
dbg_assert(!is_16);
|
|
|
|
|
|
|
|
dbg_assert((new_eip & ~0xFFFF) === 0);
|
2014-01-02 01:05:49 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
//dbg_log("in vm86 mode now " +
|
2016-02-14 22:11:06 +01:00
|
|
|
// " cs:eip=" + h(new_cs, 4) + ":" + h(this.instruction_pointer >>> 0, 8) +
|
|
|
|
// " iopl=" + this.getiopl() + " flags=" + h(new_flags, 8), LOG_CPU);
|
|
|
|
|
|
|
|
|
|
|
|
var temp_esp = this.safe_read32s(this.get_stack_pointer(12));
|
|
|
|
var temp_ss = this.safe_read16(this.get_stack_pointer(16));
|
|
|
|
|
|
|
|
var new_es = this.safe_read16(this.get_stack_pointer(20));
|
|
|
|
var new_ds = this.safe_read16(this.get_stack_pointer(24));
|
|
|
|
var new_fs = this.safe_read16(this.get_stack_pointer(28));
|
|
|
|
var new_gs = this.safe_read16(this.get_stack_pointer(32));
|
|
|
|
|
|
|
|
// no exceptions below
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
this.update_eflags(new_flags);
|
|
|
|
this.flags |= flag_vm;
|
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
this.switch_cs_real_mode(new_cs);
|
2016-02-14 22:11:06 +01:00
|
|
|
this.instruction_pointer = (new_eip & 0xFFFF) + this.get_seg(reg_cs) | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
this.switch_seg(reg_es, new_es);
|
|
|
|
this.switch_seg(reg_ds, new_ds);
|
|
|
|
this.switch_seg(reg_fs, new_fs);
|
|
|
|
this.switch_seg(reg_gs, new_gs);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
this.adjust_stack_reg(9 * 4); // 9 dwords: eip, cs, flags, esp, ss, es, ds, fs, gs
|
2013-12-28 23:21:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.reg32s[reg_esp] = temp_esp;
|
2016-02-14 22:11:06 +01:00
|
|
|
this.switch_seg(reg_ss, temp_ss);
|
2013-12-28 23:21:43 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl = 3;
|
2015-04-22 04:15:32 +02:00
|
|
|
this.cpl_changed();
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.update_cs_size(false);
|
2013-12-09 12:30:24 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
//dbg_log("iret32 to:", LOG_CPU);
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("iret end");
|
2017-04-02 18:03:50 +02:00
|
|
|
//this.debug.dump_regs();
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2013-12-28 23:21:43 +01:00
|
|
|
else
|
|
|
|
{
|
2016-07-28 00:15:23 +02:00
|
|
|
dbg_log("vm86 flag ignored because cpl != 0", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
new_flags &= ~flag_vm;
|
2013-12-28 23:21:43 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// protected mode return
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
var info = this.lookup_segment_selector(new_cs);
|
|
|
|
|
|
|
|
dbg_assert(info.is_valid);
|
|
|
|
dbg_assert((new_eip >>> 0) <= info.effective_limit);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(info.is_null)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("is null");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("not present");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
if(!info.is_executable)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("not exec");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
if(info.rpl < this.cpl)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("rpl < cpl");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
if(info.dc_bit && info.dpl > info.rpl)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("conforming and dpl > rpl");
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-11-28 01:45:36 +01:00
|
|
|
if(!info.dc_bit && info.rpl !== info.dpl)
|
|
|
|
{
|
|
|
|
dbg_log("#gp iret: non-conforming cs and rpl != dpl, dpl=" + info.dpl + " rpl=" + info.rpl, LOG_CPU);
|
2017-03-31 22:34:56 +02:00
|
|
|
this.trigger_gp(new_cs & ~3);
|
2016-11-28 01:45:36 +01:00
|
|
|
}
|
2016-02-14 22:11:06 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(info.rpl > this.cpl)
|
|
|
|
{
|
|
|
|
// outer privilege return
|
2016-02-14 22:11:06 +01:00
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
var temp_esp = this.safe_read16(this.get_stack_pointer(6));
|
|
|
|
var temp_ss = this.safe_read16(this.get_stack_pointer(8));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var temp_esp = this.safe_read32s(this.get_stack_pointer(12));
|
|
|
|
var temp_ss = this.safe_read16(this.get_stack_pointer(16));
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
var ss_info = this.lookup_segment_selector(temp_ss);
|
|
|
|
var new_cpl = info.rpl;
|
|
|
|
|
|
|
|
if(ss_info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("#GP for loading 0 in SS sel=" + h(temp_ss, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!ss_info.is_valid ||
|
|
|
|
ss_info.is_system ||
|
|
|
|
ss_info.rpl !== new_cpl ||
|
|
|
|
!ss_info.is_writable ||
|
|
|
|
ss_info.dpl !== new_cpl)
|
|
|
|
{
|
|
|
|
dbg_log("#GP for loading invalid in SS sel=" + h(temp_ss, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
|
|
|
this.trigger_gp(temp_ss & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!ss_info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#SS for loading non-present in SS sel=" + h(temp_ss, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
|
|
|
this.trigger_ss(temp_ss & ~3);
|
|
|
|
}
|
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
// no exceptions below
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
if(is_16)
|
2015-04-22 04:15:32 +02:00
|
|
|
{
|
2016-02-14 22:11:06 +01:00
|
|
|
this.update_eflags(new_flags | this.flags & ~0xFFFF);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.update_eflags(new_flags);
|
2015-04-22 04:15:32 +02:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.cpl = info.rpl;
|
2015-04-22 04:15:32 +02:00
|
|
|
this.cpl_changed();
|
|
|
|
|
2016-02-15 00:50:35 +01:00
|
|
|
//dbg_log("outer privilege return: from=" + this.cpl + " to=" + info.rpl + " ss:esp=" + h(temp_ss, 4) + ":" + h(temp_esp >>> 0, 8), LOG_CPU);
|
2016-02-14 22:11:06 +01:00
|
|
|
|
|
|
|
this.switch_seg(reg_ss, temp_ss);
|
|
|
|
|
|
|
|
this.set_stack_reg(temp_esp);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
if(this.cpl === 0)
|
|
|
|
{
|
|
|
|
this.flags = this.flags & ~flag_vif & ~flag_vip | (new_flags & (flag_vif | flag_vip));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// XXX: Set segment to 0 if it's not usable in the new cpl
|
|
|
|
// XXX: Use cached segment information
|
|
|
|
//var ds_info = this.lookup_segment_selector(this.sreg[reg_ds]);
|
|
|
|
//if(this.cpl > ds_info.dpl && (!ds_info.is_executable || !ds_info.dc_bit)) this.switch_seg(reg_ds, 0);
|
|
|
|
// ...
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-14 22:11:06 +01:00
|
|
|
else if(info.rpl === this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
// same privilege return
|
2016-02-14 22:11:06 +01:00
|
|
|
// no exceptions below
|
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
this.adjust_stack_reg(3 * 2);
|
|
|
|
this.update_eflags(new_flags | this.flags & ~0xFFFF);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.adjust_stack_reg(3 * 4);
|
|
|
|
this.update_eflags(new_flags);
|
|
|
|
}
|
2015-04-22 04:15:32 +02:00
|
|
|
|
|
|
|
// update vip and vif, which are not changed by update_eflags
|
2016-02-14 22:11:06 +01:00
|
|
|
if(this.cpl === 0)
|
2015-04-22 04:15:32 +02:00
|
|
|
{
|
|
|
|
this.flags = this.flags & ~flag_vif & ~flag_vip | (new_flags & (flag_vif | flag_vip));
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
2016-02-14 22:11:06 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.sreg[reg_cs] = new_cs;
|
2016-10-23 17:37:02 +02:00
|
|
|
dbg_assert((new_cs & 3) === this.cpl);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(info.size);
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
this.segment_limits[reg_cs] = info.effective_limit;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
|
2016-02-14 22:11:06 +01:00
|
|
|
this.instruction_pointer = new_eip + this.get_seg(reg_cs) | 0;
|
2014-06-15 22:25:17 +02:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("iret" + (is_16 ? "16" : "32") + " end");
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.handle_irqs();
|
|
|
|
};
|
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
CPU.prototype.switch_cs_real_mode = function(selector)
|
|
|
|
{
|
|
|
|
dbg_assert(!this.protected_mode || this.vm86_mode());
|
|
|
|
|
|
|
|
this.sreg[reg_cs] = selector;
|
|
|
|
this.segment_is_null[reg_cs] = 0;
|
|
|
|
this.segment_offsets[reg_cs] = selector << 4;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.far_return = function(eip, selector, stack_adjust)
|
|
|
|
{
|
|
|
|
dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
|
|
|
|
|
|
|
|
//dbg_log("far return eip=" + h(eip >>> 0, 8) + " cs=" + h(selector, 4) + " stack_adjust=" + h(stack_adjust), LOG_CPU);
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("far ret start");
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
if(!this.protected_mode)
|
|
|
|
{
|
|
|
|
dbg_assert(!this.is_32);
|
|
|
|
//dbg_assert(!this.stack_size_32);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!this.protected_mode || this.vm86_mode())
|
|
|
|
{
|
|
|
|
this.switch_cs_real_mode(selector);
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + eip | 0;
|
2017-03-07 01:03:06 +01:00
|
|
|
this.adjust_stack_reg(2 * (this.is_osize_32() ? 4 : 2) + stack_adjust);
|
2016-08-01 22:26:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var info = this.lookup_segment_selector(selector);
|
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("null cs", LOG_CPU);
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_valid)
|
|
|
|
{
|
|
|
|
dbg_log("invalid cs: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.is_system)
|
|
|
|
{
|
|
|
|
dbg_assert(false, "is system in far return");
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_executable)
|
|
|
|
{
|
|
|
|
dbg_log("non-executable cs: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.rpl < this.cpl)
|
|
|
|
{
|
|
|
|
dbg_log("cs rpl < cpl: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.dc_bit && info.dpl > info.rpl)
|
|
|
|
{
|
|
|
|
dbg_log("cs conforming and dpl > rpl: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.dc_bit && info.dpl !== info.rpl)
|
|
|
|
{
|
|
|
|
dbg_log("cs non-conforming and dpl != rpl: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#NP for loading not-present in cs sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
|
|
|
this.trigger_np(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.rpl > this.cpl)
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
dbg_log("far return privilege change cs: " + h(selector) + " from=" + this.cpl + " to=" + info.rpl + " is_16=" + this.is_osize_32(), LOG_CPU);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_osize_32())
|
2016-08-01 22:26:10 +02:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
//dbg_log("esp read from " + h(this.translate_address_system_read(this.get_stack_pointer(stack_adjust + 8))))
|
2016-08-01 22:26:10 +02:00
|
|
|
var temp_esp = this.safe_read32s(this.get_stack_pointer(stack_adjust + 8));
|
2017-03-07 01:03:06 +01:00
|
|
|
//dbg_log("esp=" + h(temp_esp));
|
2016-08-01 22:26:10 +02:00
|
|
|
var temp_ss = this.safe_read16(this.get_stack_pointer(stack_adjust + 12));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
//dbg_log("esp read from " + h(this.translate_address_system_read(this.get_stack_pointer(stack_adjust + 4))));
|
2016-08-01 22:26:10 +02:00
|
|
|
var temp_esp = this.safe_read16(this.get_stack_pointer(stack_adjust + 4));
|
2017-03-07 01:03:06 +01:00
|
|
|
//dbg_log("esp=" + h(temp_esp));
|
2016-08-01 22:26:10 +02:00
|
|
|
var temp_ss = this.safe_read16(this.get_stack_pointer(stack_adjust + 6));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.cpl = info.rpl;
|
|
|
|
this.cpl_changed();
|
|
|
|
|
|
|
|
// XXX: Can raise, conditions should be checked before side effects
|
|
|
|
this.switch_seg(reg_ss, temp_ss);
|
|
|
|
this.set_stack_reg(temp_esp + stack_adjust);
|
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
//if(this.is_osize_32())
|
2016-08-01 22:26:10 +02:00
|
|
|
//{
|
|
|
|
// this.adjust_stack_reg(2 * 4);
|
|
|
|
//}
|
|
|
|
//else
|
|
|
|
//{
|
|
|
|
// this.adjust_stack_reg(2 * 2);
|
|
|
|
//}
|
|
|
|
|
|
|
|
//throw this.debug.unimpl("privilege change");
|
|
|
|
|
|
|
|
//this.adjust_stack_reg(stack_adjust);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_osize_32())
|
2016-08-01 22:26:10 +02:00
|
|
|
{
|
|
|
|
this.adjust_stack_reg(2 * 4 + stack_adjust);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.adjust_stack_reg(2 * 2 + stack_adjust);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//dbg_assert(this.cpl === info.dpl);
|
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(info.size);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.segment_is_null[reg_cs] = 0;
|
|
|
|
this.segment_limits[reg_cs] = info.effective_limit;
|
|
|
|
//this.segment_infos[reg_cs] = 0; // TODO
|
|
|
|
|
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
this.sreg[reg_cs] = selector;
|
2017-04-02 18:03:50 +02:00
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
dbg_assert((selector & 3) === this.cpl);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + eip | 0;
|
|
|
|
|
|
|
|
//dbg_log("far return to:", LOG_CPU)
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("far ret end");
|
2016-08-01 22:26:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.far_jump = function(eip, selector, is_call)
|
|
|
|
{
|
|
|
|
dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
|
|
|
|
|
|
|
|
//dbg_log("far " + ["jump", "call"][+is_call] + " eip=" + h(eip >>> 0, 8) + " cs=" + h(selector, 4), LOG_CPU);
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("far " + ["jump", "call"][+is_call]);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
if(!this.protected_mode || this.vm86_mode())
|
|
|
|
{
|
|
|
|
if(is_call)
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_osize_32())
|
2016-08-01 22:26:10 +02:00
|
|
|
{
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-8), 8);
|
|
|
|
this.push32(this.sreg[reg_cs]);
|
|
|
|
this.push32(this.get_real_eip());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-4), 4);
|
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.switch_cs_real_mode(selector);
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + eip | 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var info = this.lookup_segment_selector(selector);
|
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("#gp null cs", LOG_CPU);
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_valid)
|
|
|
|
{
|
|
|
|
dbg_log("#gp invalid cs: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.is_system)
|
|
|
|
{
|
2017-05-09 19:11:20 +02:00
|
|
|
dbg_assert(is_call, "TODO: Jump");
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
dbg_log("system type cs: " + h(selector), LOG_CPU);
|
|
|
|
|
|
|
|
if(info.type === 0xC || info.type === 4)
|
|
|
|
{
|
|
|
|
// call gate
|
|
|
|
var is_16 = info.type === 4;
|
|
|
|
|
|
|
|
if(info.dpl < this.cpl || info.dpl < info.rpl)
|
|
|
|
{
|
|
|
|
dbg_log("#gp cs gate dpl < cpl or dpl < rpl: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#NP for loading not-present in gate cs sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
this.trigger_np(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
var cs_selector = info.raw0 >>> 16;
|
|
|
|
var cs_info = this.lookup_segment_selector(cs_selector);
|
|
|
|
|
|
|
|
if(cs_info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("#gp null cs", LOG_CPU);
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!cs_info.is_valid)
|
|
|
|
{
|
|
|
|
dbg_log("#gp invalid cs: " + h(cs_selector), LOG_CPU);
|
|
|
|
this.trigger_gp(cs_selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!cs_info.is_executable)
|
|
|
|
{
|
|
|
|
dbg_log("#gp non-executable cs: " + h(cs_selector), LOG_CPU);
|
|
|
|
this.trigger_gp(cs_selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(cs_info.dpl > this.cpl)
|
|
|
|
{
|
|
|
|
dbg_log("#gp dpl > cpl: " + h(cs_selector), LOG_CPU);
|
|
|
|
this.trigger_gp(cs_selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!cs_info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#NP for loading not-present in cs sel=" + h(cs_selector, 4), LOG_CPU);
|
|
|
|
this.trigger_np(cs_selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!cs_info.dc_bit && cs_info.dpl < this.cpl)
|
|
|
|
{
|
|
|
|
dbg_log("more privilege call gate is_16=" + is_16 + " from=" + this.cpl + " to=" + cs_info.dpl);
|
|
|
|
var tss_stack_addr = this.get_tss_stack_addr(cs_info.dpl);
|
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
if(this.tss_size_32)
|
|
|
|
{
|
|
|
|
var new_esp = this.read32s(tss_stack_addr);
|
|
|
|
var new_ss = this.read16(tss_stack_addr + 4 | 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var new_esp = this.read16(tss_stack_addr);
|
|
|
|
var new_ss = this.read16(tss_stack_addr + 2 | 0);
|
|
|
|
}
|
2016-08-01 22:26:10 +02:00
|
|
|
var ss_info = this.lookup_segment_selector(new_ss);
|
|
|
|
|
|
|
|
// Disabled: Incorrect handling of direction bit
|
|
|
|
// See http://css.csail.mit.edu/6.858/2014/readings/i386/s06_03.htm
|
|
|
|
//if(!((new_esp >>> 0) <= ss_info.effective_limit))
|
|
|
|
// debugger;
|
|
|
|
//dbg_assert((new_esp >>> 0) <= ss_info.effective_limit);
|
|
|
|
dbg_assert(ss_info.is_valid && !ss_info.is_system && ss_info.is_writable);
|
|
|
|
|
|
|
|
if(ss_info.is_null)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
if(ss_info.rpl !== cs_info.dpl) // xxx: 0 in v86 mode
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
if(ss_info.dpl !== cs_info.dpl || !ss_info.rw_bit)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
if(!ss_info.is_present)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#SS handler");
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
var parameter_count = info.raw1 & 0x1F;
|
|
|
|
var stack_space = is_16 ? 4 : 8;
|
|
|
|
if(is_call)
|
|
|
|
{
|
|
|
|
stack_space += is_16 ? 4 + 2 * parameter_count : 8 + 4 * parameter_count;
|
|
|
|
}
|
|
|
|
if(ss_info.size)
|
|
|
|
{
|
|
|
|
//try {
|
|
|
|
this.writable_or_pagefault(ss_info.base + new_esp - stack_space | 0, stack_space); // , cs_info.dpl
|
|
|
|
//} catch(e) { debugger; }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//try {
|
|
|
|
this.writable_or_pagefault(ss_info.base + (new_esp - stack_space & 0xFFFF) | 0, stack_space); // , cs_info.dpl
|
|
|
|
//} catch(e) { debugger; }
|
|
|
|
}
|
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
var old_esp = this.reg32s[reg_esp];
|
|
|
|
var old_ss = this.sreg[reg_ss];
|
|
|
|
var old_stack_pointer = this.get_stack_pointer(0);
|
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
//dbg_log("old_esp=" + h(old_esp));
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.cpl = cs_info.dpl;
|
|
|
|
this.cpl_changed();
|
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(cs_info.size);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.switch_seg(reg_ss, new_ss);
|
|
|
|
this.set_stack_reg(new_esp);
|
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
//dbg_log("parameter_count=" + parameter_count);
|
2016-08-01 22:26:10 +02:00
|
|
|
//dbg_assert(parameter_count === 0, "TODO");
|
|
|
|
|
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
this.push16(old_ss);
|
|
|
|
this.push16(old_esp);
|
2016-10-23 17:37:02 +02:00
|
|
|
//dbg_log("old esp written to " + h(this.translate_address_system_read(this.get_stack_pointer(0))));
|
2016-08-01 22:26:10 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.push32(old_ss);
|
|
|
|
this.push32(old_esp);
|
2016-10-23 17:37:02 +02:00
|
|
|
//dbg_log("old esp written to " + h(this.translate_address_system_read(this.get_stack_pointer(0))));
|
2016-08-01 22:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if(is_call)
|
|
|
|
{
|
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
for(var i = parameter_count - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
var parameter = this.safe_read16(old_stack_pointer + 2 * i);
|
|
|
|
this.push16(parameter);
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
//this.writable_or_pagefault(this.get_stack_pointer(-4), 4);
|
2016-08-01 22:26:10 +02:00
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(var i = parameter_count - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
var parameter = this.safe_read32s(old_stack_pointer + 4 * i);
|
|
|
|
this.push32(parameter);
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:37:02 +02:00
|
|
|
//this.writable_or_pagefault(this.get_stack_pointer(-8), 8);
|
2016-08-01 22:26:10 +02:00
|
|
|
this.push32(this.sreg[reg_cs]);
|
|
|
|
this.push32(this.get_real_eip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_log("same privilege call gate is_16=" + is_16 + " from=" + this.cpl + " to=" + cs_info.dpl + " conforming=" + cs_info.dc_bit);
|
|
|
|
// ok
|
|
|
|
|
|
|
|
if(is_call)
|
|
|
|
{
|
|
|
|
if(is_16)
|
|
|
|
{
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-4), 4);
|
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-8), 8);
|
|
|
|
this.push32(this.sreg[reg_cs]);
|
|
|
|
this.push32(this.get_real_eip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: eip from call is ignored
|
|
|
|
var new_eip = info.raw0 & 0xFFFF;
|
|
|
|
if(!is_16)
|
|
|
|
{
|
|
|
|
new_eip |= info.raw1 & 0xFFFF0000;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_log("call gate eip=" + h(new_eip >>> 0) + " cs=" + h(cs_selector) + " conforming=" + cs_info.dc_bit);
|
|
|
|
dbg_assert((new_eip >>> 0) <= cs_info.effective_limit, "todo: #gp");
|
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(cs_info.size);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.segment_is_null[reg_cs] = 0;
|
|
|
|
this.segment_limits[reg_cs] = cs_info.effective_limit;
|
|
|
|
//this.segment_infos[reg_cs] = 0; // TODO
|
|
|
|
this.segment_offsets[reg_cs] = cs_info.base;
|
|
|
|
this.sreg[reg_cs] = cs_selector & ~3 | this.cpl;
|
2016-10-23 17:37:02 +02:00
|
|
|
dbg_assert((this.sreg[reg_cs] & 3) === this.cpl);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + new_eip | 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var types = { 9: "Available 386 TSS", 0xb: "Busy 386 TSS", 4: "286 Call Gate", 0xc: "386 Call Gate" };
|
|
|
|
throw this.debug.unimpl("load system segment descriptor, type = " + (info.access & 15) + " (" + types[info.access & 15] + ")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(!info.is_executable)
|
|
|
|
{
|
|
|
|
dbg_log("#gp non-executable cs: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.dc_bit)
|
|
|
|
{
|
|
|
|
// conforming code segment
|
|
|
|
if(info.dpl > this.cpl)
|
|
|
|
{
|
|
|
|
dbg_log("#gp cs dpl > cpl: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// non-conforming code segment
|
|
|
|
|
|
|
|
if(info.rpl > this.cpl || info.dpl !== this.cpl)
|
|
|
|
{
|
|
|
|
dbg_log("#gp cs rpl > cpl or dpl != cpl: " + h(selector), LOG_CPU);
|
|
|
|
this.trigger_gp(selector & ~3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#NP for loading not-present in cs sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
|
|
|
this.trigger_np(selector & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(is_call)
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_osize_32())
|
2016-08-01 22:26:10 +02:00
|
|
|
{
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-8), 8);
|
|
|
|
this.push32(this.sreg[reg_cs]);
|
|
|
|
this.push32(this.get_real_eip());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.writable_or_pagefault(this.get_stack_pointer(-4), 4);
|
|
|
|
this.push16(this.sreg[reg_cs]);
|
|
|
|
this.push16(this.get_real_eip());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_assert((eip >>> 0) <= info.effective_limit, "todo: #gp");
|
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
this.update_cs_size(info.size);
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
this.segment_is_null[reg_cs] = 0;
|
|
|
|
this.segment_limits[reg_cs] = info.effective_limit;
|
|
|
|
//this.segment_infos[reg_cs] = 0; // TODO
|
|
|
|
|
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
this.sreg[reg_cs] = selector & ~3 | this.cpl;
|
|
|
|
|
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + eip | 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//dbg_log("far " + ["jump", "call"][+is_call] + " to:", LOG_CPU)
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state("far " + ["jump", "call"][+is_call] + " end");
|
2016-08-01 22:26:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.get_tss_stack_addr = function(dpl)
|
|
|
|
{
|
2017-05-09 19:11:20 +02:00
|
|
|
if(this.tss_size_32)
|
2016-08-01 22:26:10 +02:00
|
|
|
{
|
2017-05-09 19:11:20 +02:00
|
|
|
var tss_stack_addr = (dpl << 3) + 4 | 0;
|
|
|
|
|
|
|
|
if((tss_stack_addr + 5 | 0) > this.segment_limits[reg_tr])
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
tss_stack_addr = tss_stack_addr + this.segment_offsets[reg_tr] | 0;
|
|
|
|
|
|
|
|
dbg_assert((tss_stack_addr & 0xFFF) <= 0x1000 - 6);
|
2016-08-01 22:26:10 +02:00
|
|
|
}
|
2017-05-09 19:11:20 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
var tss_stack_addr = (dpl << 2) + 2 | 0;
|
2016-08-01 22:26:10 +02:00
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
if((tss_stack_addr + 5 | 0) > this.segment_limits[reg_tr])
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
tss_stack_addr = tss_stack_addr + this.segment_offsets[reg_tr] | 0;
|
|
|
|
dbg_assert((tss_stack_addr & 0xFFF) <= 0x1000 - 4);
|
|
|
|
}
|
2016-08-01 22:26:10 +02:00
|
|
|
|
|
|
|
if(this.paging)
|
|
|
|
{
|
|
|
|
tss_stack_addr = this.translate_address_system_read(tss_stack_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tss_stack_addr;
|
|
|
|
};
|
|
|
|
|
2017-04-06 03:34:26 +02:00
|
|
|
CPU.prototype.do_task_switch = function(selector, error_code)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
2017-06-02 18:00:35 +02:00
|
|
|
dbg_assert(this.tss_size_32, "TODO");
|
2017-05-09 19:11:20 +02:00
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
dbg_log("do_task_switch sel=" + h(selector), LOG_CPU);
|
2015-09-14 01:45:51 +02:00
|
|
|
var descriptor = this.lookup_segment_selector(selector);
|
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
dbg_assert((descriptor.type | 2) === 3 || (descriptor.type | 2) === 0xb);
|
|
|
|
var tss_is_16 = descriptor.type <= 3;
|
|
|
|
var tss_is_busy = (descriptor.type & 2) === 2;
|
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
if(!descriptor.is_valid || descriptor.is_null || !descriptor.from_gdt)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if((descriptor.access & 31) === 0xB)
|
|
|
|
{
|
|
|
|
// is busy
|
|
|
|
throw this.debug.unimpl("#GP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!descriptor.is_present)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#NP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(descriptor.effective_limit < 103)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#NP handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
var tsr_size = this.segment_limits[reg_tr];
|
|
|
|
var tsr_offset = this.segment_offsets[reg_tr];
|
|
|
|
|
|
|
|
var old_eflags = this.get_eflags();
|
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
if(tss_is_busy)
|
|
|
|
{
|
|
|
|
old_eflags &= ~flag_nt;
|
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
this.writable_or_pagefault(tsr_offset, 0x66);
|
|
|
|
|
|
|
|
//this.safe_write32(tsr_offset + TSR_CR3, this.cr[3]);
|
2016-08-01 23:22:38 +02:00
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
// TODO: Write 16 bit values if old tss is 16 bit
|
2015-09-14 01:45:51 +02:00
|
|
|
this.safe_write32(tsr_offset + TSR_EIP, this.get_real_eip());
|
|
|
|
this.safe_write32(tsr_offset + TSR_EFLAGS, old_eflags);
|
|
|
|
|
|
|
|
this.safe_write32(tsr_offset + TSR_EAX, this.reg32s[reg_eax]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_ECX, this.reg32s[reg_ecx]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_EDX, this.reg32s[reg_edx]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_EBX, this.reg32s[reg_ebx]);
|
|
|
|
|
|
|
|
this.safe_write32(tsr_offset + TSR_ESP, this.reg32s[reg_esp]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_EBP, this.reg32s[reg_ebp]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_ESI, this.reg32s[reg_esi]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_EDI, this.reg32s[reg_edi]);
|
|
|
|
|
|
|
|
this.safe_write32(tsr_offset + TSR_ES, this.sreg[reg_es]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_CS, this.sreg[reg_cs]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_SS, this.sreg[reg_ss]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_DS, this.sreg[reg_ds]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_FS, this.sreg[reg_fs]);
|
|
|
|
this.safe_write32(tsr_offset + TSR_GS, this.sreg[reg_gs]);
|
2017-04-06 03:07:09 +02:00
|
|
|
|
|
|
|
//this.safe_write32(tsr_offset + TSR_LDT, this.sreg[reg_ldtr]);
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
if(true /* is jump or call or int */)
|
|
|
|
{
|
|
|
|
// mark as busy
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(descriptor.table_offset + 5 | 0, this.read8(descriptor.table_offset + 5 | 0) | 2);
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//var new_tsr_size = descriptor.effective_limit;
|
|
|
|
var new_tsr_offset = descriptor.base;
|
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
dbg_assert(!tss_is_16, "unimplemented");
|
|
|
|
|
|
|
|
if(true /* is call or int */)
|
|
|
|
{
|
|
|
|
this.safe_write16(new_tsr_offset + TSR_BACKLINK, this.sreg[reg_tr]);
|
|
|
|
}
|
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
var new_cr3 = this.safe_read32s(new_tsr_offset + TSR_CR3);
|
|
|
|
|
|
|
|
this.flags &= ~flag_vm;
|
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
var new_eip = this.safe_read32s(new_tsr_offset + TSR_EIP);
|
|
|
|
var new_cs = this.safe_read16(new_tsr_offset + TSR_CS);
|
|
|
|
var info = this.lookup_segment_selector(new_cs);
|
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("null cs", LOG_CPU);
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_valid)
|
|
|
|
{
|
|
|
|
dbg_log("invalid cs: " + h(selector), LOG_CPU);
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.is_system)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_executable)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(info.dc_bit && info.dpl > info.rpl)
|
|
|
|
{
|
|
|
|
dbg_log("cs conforming and dpl > rpl: " + h(selector), LOG_CPU);
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.dc_bit && info.dpl !== info.rpl)
|
|
|
|
{
|
|
|
|
dbg_log("cs non-conforming and dpl != rpl: " + h(selector), LOG_CPU);
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#NP for loading not-present in cs sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
throw this.debug.unimpl("#TS handler");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.segment_is_null[reg_cs] = 0;
|
|
|
|
this.segment_limits[reg_cs] = info.effective_limit;
|
|
|
|
this.segment_offsets[reg_cs] = info.base;
|
|
|
|
this.sreg[reg_cs] = new_cs;
|
|
|
|
|
|
|
|
this.cpl = info.dpl;
|
|
|
|
this.cpl_changed();
|
|
|
|
|
|
|
|
dbg_assert((this.sreg[reg_cs] & 3) === this.cpl);
|
|
|
|
|
|
|
|
dbg_assert((new_eip >>> 0) <= info.effective_limit, "todo: #gp");
|
|
|
|
this.update_cs_size(info.size);
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
var new_eflags = this.safe_read32s(new_tsr_offset + TSR_EFLAGS);
|
|
|
|
|
|
|
|
if(true /* is call or int */)
|
|
|
|
{
|
|
|
|
this.safe_write32(tsr_offset + TSR_BACKLINK, selector);
|
|
|
|
new_eflags |= flag_nt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(new_eflags & flag_vm)
|
|
|
|
{
|
|
|
|
throw this.debug.unimpl("task switch to VM mode");
|
|
|
|
}
|
|
|
|
|
|
|
|
this.update_eflags(new_eflags);
|
2015-12-31 00:31:08 +01:00
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
if(true /* call or int */)
|
|
|
|
{
|
|
|
|
this.flags |= flag_nt;
|
|
|
|
}
|
|
|
|
|
2015-12-31 00:31:08 +01:00
|
|
|
var new_ldt = this.safe_read16(new_tsr_offset + TSR_LDT);
|
|
|
|
this.load_ldt(new_ldt);
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
this.reg32s[reg_eax] = this.safe_read32s(new_tsr_offset + TSR_EAX);
|
|
|
|
this.reg32s[reg_ecx] = this.safe_read32s(new_tsr_offset + TSR_ECX);
|
|
|
|
this.reg32s[reg_edx] = this.safe_read32s(new_tsr_offset + TSR_EDX);
|
|
|
|
this.reg32s[reg_ebx] = this.safe_read32s(new_tsr_offset + TSR_EBX);
|
|
|
|
|
|
|
|
this.reg32s[reg_esp] = this.safe_read32s(new_tsr_offset + TSR_ESP);
|
|
|
|
this.reg32s[reg_ebp] = this.safe_read32s(new_tsr_offset + TSR_EBP);
|
|
|
|
this.reg32s[reg_esi] = this.safe_read32s(new_tsr_offset + TSR_ESI);
|
|
|
|
this.reg32s[reg_edi] = this.safe_read32s(new_tsr_offset + TSR_EDI);
|
|
|
|
|
|
|
|
this.switch_seg(reg_es, this.safe_read16(new_tsr_offset + TSR_ES));
|
|
|
|
this.switch_seg(reg_ss, this.safe_read16(new_tsr_offset + TSR_SS));
|
|
|
|
this.switch_seg(reg_ds, this.safe_read16(new_tsr_offset + TSR_DS));
|
|
|
|
this.switch_seg(reg_fs, this.safe_read16(new_tsr_offset + TSR_FS));
|
|
|
|
this.switch_seg(reg_gs, this.safe_read16(new_tsr_offset + TSR_GS));
|
|
|
|
|
2017-04-06 03:07:09 +02:00
|
|
|
this.instruction_pointer = this.get_seg(reg_cs) + new_eip | 0;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
this.segment_offsets[reg_tr] = descriptor.base;
|
|
|
|
this.segment_limits[reg_tr] = descriptor.effective_limit;
|
|
|
|
this.sreg[reg_tr] = selector;
|
|
|
|
|
|
|
|
this.cr[3] = new_cr3;
|
|
|
|
dbg_assert((this.cr[3] & 0xFFF) === 0);
|
|
|
|
this.clear_tlb();
|
|
|
|
|
|
|
|
this.cr[0] |= CR0_TS;
|
2017-04-06 03:07:09 +02:00
|
|
|
|
|
|
|
if(error_code !== false)
|
|
|
|
{
|
|
|
|
if(tss_is_16)
|
|
|
|
{
|
|
|
|
this.push16(error_code & 0xFFFF);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.push32(error_code);
|
|
|
|
}
|
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.hlt_op = function()
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
if(this.cpl)
|
|
|
|
{
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((this.flags & flag_interrupt) === 0)
|
|
|
|
{
|
|
|
|
this.debug.show("cpu halted");
|
|
|
|
if(DEBUG) this.debug.dump_regs();
|
|
|
|
throw "HALT";
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
else
|
|
|
|
{
|
2014-06-22 19:10:35 +02:00
|
|
|
// get out of here and into hlt_loop
|
2014-06-15 22:25:17 +02:00
|
|
|
this.in_hlt = true;
|
2017-03-07 01:03:06 +01:00
|
|
|
|
2017-04-06 02:57:24 +02:00
|
|
|
//if(false) // possibly unsafe, test in safari
|
|
|
|
//{
|
|
|
|
// this.hlt_loop();
|
|
|
|
// this.diverged();
|
|
|
|
// if(this.in_hlt)
|
|
|
|
// {
|
|
|
|
// throw MAGIC_CPU_EXCEPTION;
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//else
|
2017-03-07 01:03:06 +01:00
|
|
|
{
|
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// assumes ip to point to the byte before the next instruction
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.raise_exception = function(interrupt_nr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-15 00:50:35 +01:00
|
|
|
//if(DEBUG && interrupt_nr !== 7)
|
|
|
|
//{
|
|
|
|
// // show interesting exceptions
|
|
|
|
// dbg_log("Exception " + h(interrupt_nr) + " at " + h(this.previous_ip >>> 0, 8) + " (cs=" + h(this.sreg[reg_cs], 4) + ")", LOG_CPU);
|
|
|
|
// dbg_trace(LOG_CPU);
|
2017-04-02 18:03:50 +02:00
|
|
|
// this.debug.dump_regs();
|
2016-02-15 00:50:35 +01:00
|
|
|
// this.debug.dump_state();
|
|
|
|
//}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.call_interrupt_vector(interrupt_nr, false, false);
|
2014-05-06 20:24:46 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.raise_exception_with_code = function(interrupt_nr, error_code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
dbg_assert(typeof error_code === "number");
|
|
|
|
|
2016-02-15 00:50:35 +01:00
|
|
|
//if(DEBUG)
|
|
|
|
//{
|
|
|
|
// dbg_log("Exception " + h(interrupt_nr) + " err=" + h(error_code) + " at " + h(this.previous_ip >>> 0, 8) + " (cs=" + h(this.sreg[reg_cs], 4) + ")", LOG_CPU);
|
|
|
|
// dbg_trace(LOG_CPU);
|
2017-04-02 18:03:50 +02:00
|
|
|
// this.debug.dump_regs();
|
2016-02-15 00:50:35 +01:00
|
|
|
//}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.call_interrupt_vector(interrupt_nr, false, error_code);
|
2014-05-06 20:24:46 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_de = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception(0);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_ud = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception(6);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_nm = function()
|
2014-01-03 22:02:43 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception(7);
|
|
|
|
};
|
2014-01-03 22:02:43 +01:00
|
|
|
|
2016-08-01 22:26:10 +02:00
|
|
|
CPU.prototype.trigger_ts = function(code)
|
|
|
|
{
|
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(10, code);
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_gp = function(code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(13, code);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_np = function(code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(11, code);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_ss = function(code)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.raise_exception_with_code(12, code);
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
// used before fpu instructions
|
|
|
|
CPU.prototype.task_switch_test = function()
|
|
|
|
{
|
|
|
|
if(this.cr[0] & (CR0_EM | CR0_TS))
|
|
|
|
{
|
|
|
|
this.trigger_nm();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.todo = function()
|
|
|
|
{
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
dbg_trace();
|
|
|
|
throw "TODO";
|
|
|
|
}
|
|
|
|
|
|
|
|
this.trigger_ud();
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.undefined_instruction = function()
|
|
|
|
{
|
|
|
|
if(DEBUG)
|
|
|
|
{
|
|
|
|
throw "Possible fault: undefined instruction";
|
|
|
|
}
|
|
|
|
|
|
|
|
this.trigger_ud();
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.unimplemented_sse = function()
|
|
|
|
{
|
|
|
|
dbg_log("No SSE", LOG_CPU);
|
|
|
|
this.trigger_ud();
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_seg_prefix_ds = function()
|
2014-11-01 13:59:32 +01:00
|
|
|
{
|
|
|
|
return this.get_seg_prefix(reg_ds);
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_seg_prefix_ss = function()
|
2014-11-01 13:59:32 +01:00
|
|
|
{
|
|
|
|
return this.get_seg_prefix(reg_ss);
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_seg_prefix_cs = function()
|
2014-11-01 13:59:32 +01:00
|
|
|
{
|
|
|
|
return this.get_seg_prefix(reg_cs);
|
|
|
|
};
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
/**
|
|
|
|
* Get segment base by prefix or default
|
|
|
|
* @param {number} default_segment
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_seg_prefix = function(default_segment /*, offset*/)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
var prefix = this.prefixes & PREFIX_MASK_SEGMENT;
|
|
|
|
|
|
|
|
if(prefix)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(prefix === SEG_PREFIX_ZERO)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.get_seg(prefix - 1 /*, offset*/);
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
return this.get_seg(default_segment /*, offset*/);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get segment base
|
|
|
|
* @param {number} segment
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.get_seg = function(segment /*, offset*/)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert(segment >= 0 && segment < 8);
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.protected_mode)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.segment_is_null[segment])
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-15 01:28:18 +01:00
|
|
|
dbg_assert(segment !== reg_cs && segment !== reg_ss);
|
2017-04-06 02:57:24 +02:00
|
|
|
dbg_trace();
|
2016-02-15 01:28:18 +01:00
|
|
|
dbg_log("#gp Use null segment: " + segment + " sel=" + h(this.sreg[segment], 4), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-15 01:28:18 +01:00
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
// TODO:
|
2013-11-07 21:30:18 +01:00
|
|
|
// - validate segment limits
|
|
|
|
// - validate if segment is writable
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.segment_offsets[segment];
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
CPU.prototype.read_e8 = function()
|
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
return this.safe_read8(this.modrm_resolve(this.modrm_byte));
|
|
|
|
} else {
|
|
|
|
return this.reg8[this.modrm_byte << 2 & 0xC | this.modrm_byte >> 2 & 1];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_e8s = function()
|
|
|
|
{
|
|
|
|
return this.read_e8() << 24 >> 24;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_e16 = function()
|
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
return this.safe_read16(this.modrm_resolve(this.modrm_byte));
|
|
|
|
} else {
|
|
|
|
return this.reg16[this.modrm_byte << 1 & 14];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_e16s = function()
|
|
|
|
{
|
|
|
|
return this.read_e16() << 16 >> 16;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_e32s = function()
|
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
return this.safe_read32s(this.modrm_resolve(this.modrm_byte));
|
|
|
|
} else {
|
|
|
|
return this.reg32s[this.modrm_byte & 7];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_e32 = function()
|
|
|
|
{
|
|
|
|
return this.read_e32s() >>> 0;
|
|
|
|
};
|
|
|
|
|
2016-03-26 15:26:08 +01:00
|
|
|
CPU.prototype.set_e8 = function(value)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
2016-03-26 15:26:08 +01:00
|
|
|
var addr = this.modrm_resolve(this.modrm_byte);
|
2015-09-14 01:45:51 +02:00
|
|
|
this.safe_write8(addr, value);
|
|
|
|
} else {
|
|
|
|
this.reg8[this.modrm_byte << 2 & 0xC | this.modrm_byte >> 2 & 1] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-26 15:26:08 +01:00
|
|
|
CPU.prototype.set_e16 = function(value)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
2016-03-26 15:26:08 +01:00
|
|
|
var addr = this.modrm_resolve(this.modrm_byte);
|
2015-09-14 01:45:51 +02:00
|
|
|
this.safe_write16(addr, value);
|
|
|
|
} else {
|
|
|
|
this.reg16[this.modrm_byte << 1 & 14] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-26 15:26:08 +01:00
|
|
|
CPU.prototype.set_e32 = function(value)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
2016-03-26 15:26:08 +01:00
|
|
|
var addr = this.modrm_resolve(this.modrm_byte);
|
2015-09-14 01:45:51 +02:00
|
|
|
this.safe_write32(addr, value);
|
|
|
|
} else {
|
|
|
|
this.reg32s[this.modrm_byte & 7] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
CPU.prototype.read_write_e8 = function()
|
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
var virt_addr = this.modrm_resolve(this.modrm_byte);
|
|
|
|
this.phys_addr = this.translate_address_write(virt_addr);
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read8(this.phys_addr);
|
2015-09-14 01:45:51 +02:00
|
|
|
} else {
|
|
|
|
return this.reg8[this.modrm_byte << 2 & 0xC | this.modrm_byte >> 2 & 1];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-31 01:28:53 +01:00
|
|
|
CPU.prototype.write_e8 = function(value)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(this.phys_addr, value);
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.reg8[this.modrm_byte << 2 & 0xC | this.modrm_byte >> 2 & 1] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_write_e16 = function()
|
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
var virt_addr = this.modrm_resolve(this.modrm_byte);
|
|
|
|
this.phys_addr = this.translate_address_write(virt_addr);
|
|
|
|
if(this.paging && (virt_addr & 0xFFF) === 0xFFF) {
|
|
|
|
this.phys_addr_high = this.translate_address_write(virt_addr + 1 | 0);
|
2016-08-01 23:22:38 +02:00
|
|
|
dbg_assert(this.phys_addr_high);
|
2015-09-14 01:45:51 +02:00
|
|
|
return this.virt_boundary_read16(this.phys_addr, this.phys_addr_high);
|
|
|
|
} else {
|
|
|
|
this.phys_addr_high = 0;
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read16(this.phys_addr);
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return this.reg16[this.modrm_byte << 1 & 14];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-31 01:28:53 +01:00
|
|
|
CPU.prototype.write_e16 = function(value)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
if(this.phys_addr_high) {
|
|
|
|
this.virt_boundary_write16(this.phys_addr, this.phys_addr_high, value);
|
|
|
|
} else {
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write16(this.phys_addr, value);
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.reg16[this.modrm_byte << 1 & 14] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_write_e32 = function()
|
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
var virt_addr = this.modrm_resolve(this.modrm_byte);
|
|
|
|
this.phys_addr = this.translate_address_write(virt_addr);
|
|
|
|
if(this.paging && (virt_addr & 0xFFF) >= 0xFFD) {
|
2017-03-07 01:03:06 +01:00
|
|
|
//this.phys_addr_high = this.translate_address_write(virt_addr + 3 | 0);
|
|
|
|
this.phys_addr_high = this.translate_address_write(virt_addr + 3 & ~3) | (virt_addr + 3) & 3;
|
2016-08-01 23:22:38 +02:00
|
|
|
dbg_assert(this.phys_addr_high);
|
2015-09-14 01:45:51 +02:00
|
|
|
return this.virt_boundary_read32s(this.phys_addr, this.phys_addr_high);
|
|
|
|
} else {
|
|
|
|
this.phys_addr_high = 0;
|
2016-08-02 04:15:24 +02:00
|
|
|
return this.read32s(this.phys_addr);
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return this.reg32s[this.modrm_byte & 7];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-31 01:28:53 +01:00
|
|
|
CPU.prototype.write_e32 = function(value)
|
2015-09-14 01:45:51 +02:00
|
|
|
{
|
|
|
|
if(this.modrm_byte < 0xC0) {
|
|
|
|
if(this.phys_addr_high) {
|
|
|
|
this.virt_boundary_write32(this.phys_addr, this.phys_addr_high, value);
|
|
|
|
} else {
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write32(this.phys_addr, value);
|
2015-09-14 01:45:51 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.reg32s[this.modrm_byte & 7] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_reg_e16 = function()
|
|
|
|
{
|
|
|
|
return this.reg16[this.modrm_byte << 1 & 14];
|
2017-05-09 19:11:20 +02:00
|
|
|
};
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
CPU.prototype.write_reg_e16 = function(value)
|
|
|
|
{
|
|
|
|
this.reg16[this.modrm_byte << 1 & 14] = value;
|
2017-05-09 19:11:20 +02:00
|
|
|
};
|
2015-09-14 01:45:51 +02:00
|
|
|
|
|
|
|
CPU.prototype.read_reg_e32s = function()
|
|
|
|
{
|
|
|
|
return this.reg32s[this.modrm_byte & 7];
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.write_reg_e32 = function(value)
|
|
|
|
{
|
|
|
|
this.reg32s[this.modrm_byte & 7] = value;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_g8 = function()
|
|
|
|
{
|
|
|
|
return this.reg8[this.modrm_byte >> 1 & 0xC | this.modrm_byte >> 5 & 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.write_g8 = function(value)
|
|
|
|
{
|
|
|
|
this.reg8[this.modrm_byte >> 1 & 0xC | this.modrm_byte >> 5 & 1] = value;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_g16 = function()
|
|
|
|
{
|
|
|
|
return this.reg16[this.modrm_byte >> 2 & 14];
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_g16s = function()
|
|
|
|
{
|
|
|
|
return this.reg16s[this.modrm_byte >> 2 & 14];
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.write_g16 = function(value)
|
|
|
|
{
|
|
|
|
this.reg16[this.modrm_byte >> 2 & 14] = value;
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.read_g32s = function()
|
|
|
|
{
|
|
|
|
return this.reg32s[this.modrm_byte >> 3 & 7];
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.write_g32 = function(value)
|
|
|
|
{
|
|
|
|
this.reg32[this.modrm_byte >> 3 & 7] = value;
|
|
|
|
};
|
|
|
|
|
2016-08-02 05:30:41 +02:00
|
|
|
CPU.prototype.pic_call_irq = function(int)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
this.previous_ip = this.instruction_pointer;
|
|
|
|
this.call_interrupt_vector(int, false, false);
|
|
|
|
}
|
|
|
|
catch(e)
|
|
|
|
{
|
|
|
|
this.exception_cleanup(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.handle_irqs = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-05-17 01:56:50 +02:00
|
|
|
dbg_assert(!this.page_fault);
|
2013-12-28 23:21:43 +01:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
this.diverged();
|
|
|
|
|
2015-05-17 01:56:50 +02:00
|
|
|
if((this.flags & flag_interrupt) && !this.page_fault)
|
|
|
|
{
|
|
|
|
if(this.devices.pic)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
this.devices.pic.acknowledge_irq();
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2015-05-17 01:56:50 +02:00
|
|
|
|
|
|
|
if(this.devices.apic)
|
|
|
|
{
|
2017-03-07 23:39:58 +01:00
|
|
|
this.devices.apic.acknowledge_irq();
|
2015-05-17 01:56:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.device_raise_irq = function(i)
|
|
|
|
{
|
|
|
|
dbg_assert(arguments.length === 1);
|
2016-01-04 04:32:03 +01:00
|
|
|
if(this.devices.pic)
|
|
|
|
{
|
|
|
|
this.devices.pic.set_irq(i);
|
|
|
|
}
|
|
|
|
|
2017-04-02 18:52:51 +02:00
|
|
|
if(this.devices.ioapic)
|
2016-01-04 04:32:03 +01:00
|
|
|
{
|
2017-04-02 18:52:51 +02:00
|
|
|
this.devices.ioapic.set_irq(i);
|
2016-01-04 04:32:03 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.device_lower_irq = function(i)
|
|
|
|
{
|
2015-05-17 01:56:50 +02:00
|
|
|
if(this.devices.pic)
|
|
|
|
{
|
2016-01-04 04:32:03 +01:00
|
|
|
this.devices.pic.clear_irq(i);
|
2015-05-17 01:56:50 +02:00
|
|
|
}
|
|
|
|
|
2017-04-02 18:52:51 +02:00
|
|
|
if(this.devices.ioapic)
|
2015-05-17 01:56:50 +02:00
|
|
|
{
|
2017-04-02 18:52:51 +02:00
|
|
|
this.devices.ioapic.clear_irq(i);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.test_privileges_for_io = function(port, size)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-12-06 22:51:04 +01:00
|
|
|
if(this.protected_mode && (this.cpl > this.getiopl() || (this.flags & flag_vm)))
|
2013-11-25 19:57:46 +01:00
|
|
|
{
|
2017-05-09 19:11:20 +02:00
|
|
|
if(!this.tss_size_32)
|
|
|
|
{
|
|
|
|
dbg_log("#GP for port io, 16-bit TSS port=" + h(port) + " size=" + size, LOG_CPU);
|
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state();
|
|
|
|
this.trigger_gp(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
var tsr_size = this.segment_limits[reg_tr];
|
|
|
|
var tsr_offset = this.segment_offsets[reg_tr];
|
2014-07-07 02:37:40 +02:00
|
|
|
|
|
|
|
if(tsr_size >= 0x67)
|
2013-12-09 12:30:24 +01:00
|
|
|
{
|
2016-08-01 23:22:38 +02:00
|
|
|
dbg_assert((tsr_offset + 0x64 + 2 & 0xFFF) < 0xFFF);
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var iomap_base = this.read16(this.translate_address_system_read(tsr_offset + 0x64 + 2 | 0)),
|
2015-04-22 05:00:34 +02:00
|
|
|
high_port = port + size - 1 | 0;
|
2013-12-09 12:30:24 +01:00
|
|
|
|
2015-04-22 05:00:34 +02:00
|
|
|
if(tsr_size >= (iomap_base + (high_port >> 3) | 0))
|
2013-12-09 12:30:24 +01:00
|
|
|
{
|
|
|
|
var mask = ((1 << size) - 1) << (port & 7),
|
2015-04-22 05:00:34 +02:00
|
|
|
addr = this.translate_address_system_read(tsr_offset + iomap_base + (port >> 3) | 0),
|
2015-09-14 01:45:51 +02:00
|
|
|
port_info = (mask & 0xFF00) ?
|
2016-08-02 04:15:24 +02:00
|
|
|
this.read16(addr) : this.read8(addr);
|
2013-12-09 12:30:24 +01:00
|
|
|
|
2016-08-01 23:22:38 +02:00
|
|
|
dbg_assert((addr & 0xFFF) < 0xFFF);
|
|
|
|
|
2013-12-09 12:30:24 +01:00
|
|
|
if(!(port_info & mask))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_log("#GP for port io port=" + h(port) + " size=" + size, LOG_CPU);
|
2016-10-23 17:37:02 +02:00
|
|
|
CPU_LOG_VERBOSE && this.debug.dump_state();
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(0);
|
2013-11-25 19:57:46 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.cpuid = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// cpuid
|
|
|
|
// TODO: Fill in with less bogus values
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
// http://lxr.linux.no/linux+%2a/arch/x86/include/asm/cpufeature.h
|
2014-01-08 03:22:30 +01:00
|
|
|
// http://www.sandpile.org/x86/cpuid.htm
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-07-27 00:14:23 +02:00
|
|
|
var eax = 0,
|
|
|
|
ecx = 0,
|
|
|
|
edx = 0,
|
|
|
|
ebx = 0;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2016-02-04 14:25:42 +01:00
|
|
|
var winnt_fix = false;
|
|
|
|
|
2014-07-27 00:14:23 +02:00
|
|
|
switch(this.reg32s[reg_eax])
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// maximum supported level
|
2016-02-04 14:25:42 +01:00
|
|
|
if(winnt_fix)
|
|
|
|
{
|
|
|
|
eax = 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eax = 5;
|
|
|
|
}
|
2014-07-27 00:14:23 +02:00
|
|
|
|
|
|
|
ebx = 0x756E6547|0; // Genu
|
|
|
|
edx = 0x49656E69|0; // ineI
|
|
|
|
ecx = 0x6C65746E|0; // ntel
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
// pentium
|
|
|
|
eax = 3 | 6 << 4 | 15 << 8;
|
2015-09-14 01:45:51 +02:00
|
|
|
ebx = 1 << 16 | 8 << 8; // cpu count, clflush size
|
2015-04-17 23:53:09 +02:00
|
|
|
ecx = 1 << 23 | 1 << 30; // popcnt, rdrand
|
2016-08-01 23:22:38 +02:00
|
|
|
var vme = 0 << 1;
|
2014-07-27 00:14:23 +02:00
|
|
|
edx = (this.fpu ? 1 : 0) | // fpu
|
2016-08-01 23:22:38 +02:00
|
|
|
vme | 1 << 3 | 1 << 4 | 1 << 5 | // vme, pse, tsc, msr
|
2014-07-27 00:14:23 +02:00
|
|
|
1 << 8 | 1 << 11 | 1 << 13 | 1 << 15; // cx8, sep, pge, cmov
|
2016-08-01 21:43:47 +02:00
|
|
|
|
2017-04-02 18:52:51 +02:00
|
|
|
if(ENABLE_ACPI && this.apic_enabled)
|
2016-08-01 21:43:47 +02:00
|
|
|
{
|
|
|
|
edx |= 1 << 9; // apic
|
|
|
|
}
|
2014-07-27 00:14:23 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
// Taken from http://siyobik.info.gf/main/reference/instruction/CPUID
|
|
|
|
eax = 0x665B5001|0;
|
|
|
|
ebx = 0;
|
|
|
|
ecx = 0;
|
|
|
|
edx = 0x007A7000;
|
|
|
|
break;
|
|
|
|
|
2014-12-20 21:36:39 +01:00
|
|
|
case 4:
|
|
|
|
// from my local machine
|
|
|
|
switch(this.reg32s[reg_ecx])
|
|
|
|
{
|
|
|
|
case 0:
|
2015-09-14 01:45:51 +02:00
|
|
|
eax = 0x00000121;
|
|
|
|
ebx = 0x01c0003f;
|
|
|
|
ecx = 0x0000003f;
|
2014-12-20 21:36:39 +01:00
|
|
|
edx = 0x00000001;
|
|
|
|
break;
|
|
|
|
case 1:
|
2015-09-14 01:45:51 +02:00
|
|
|
eax = 0x00000122;
|
|
|
|
ebx = 0x01c0003f;
|
|
|
|
ecx = 0x0000003f;
|
2014-12-20 21:36:39 +01:00
|
|
|
edx = 0x00000001;
|
2017-05-06 18:37:23 +02:00
|
|
|
break;
|
2014-12-20 21:36:39 +01:00
|
|
|
case 2:
|
2015-09-14 01:45:51 +02:00
|
|
|
eax = 0x00000143;
|
|
|
|
ebx = 0x05c0003f;
|
|
|
|
ecx = 0x00000fff;
|
2014-12-20 21:36:39 +01:00
|
|
|
edx = 0x00000001;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-04-06 03:07:22 +02:00
|
|
|
case 5:
|
|
|
|
// from my local machine
|
|
|
|
eax = 0x40;
|
|
|
|
ebx = 0x40;
|
|
|
|
ecx = 3;
|
|
|
|
edx = 0x00142120;
|
|
|
|
break;
|
|
|
|
|
2014-07-27 00:14:23 +02:00
|
|
|
case 0x80000000|0:
|
|
|
|
// maximum supported extended level
|
|
|
|
eax = 5;
|
|
|
|
// other registers are reserved
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
dbg_log("cpuid: unimplemented eax: " + h(this.reg32[reg_eax]), LOG_CPU);
|
|
|
|
}
|
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_log("cpuid: eax=" + h(this.reg32[reg_eax], 8) + " cl=" + h(this.reg8[reg_cl], 2), LOG_CPU);
|
2015-01-05 23:20:27 +01:00
|
|
|
|
2014-07-27 00:14:23 +02:00
|
|
|
this.reg32s[reg_eax] = eax;
|
|
|
|
this.reg32s[reg_ecx] = ecx;
|
|
|
|
this.reg32s[reg_edx] = edx;
|
|
|
|
this.reg32s[reg_ebx] = ebx;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.update_cs_size = function(new_size)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-18 01:03:56 +01:00
|
|
|
dbg_assert(typeof new_size === "boolean");
|
2014-01-03 22:02:43 +01:00
|
|
|
|
2017-03-18 01:03:56 +01:00
|
|
|
if(this.is_32 !== new_size)
|
|
|
|
{
|
|
|
|
this.clear_instruction_cache();
|
|
|
|
this.is_32 = new_size;
|
|
|
|
this.update_operand_size();
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.update_operand_size = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-03-07 01:03:06 +01:00
|
|
|
if(this.is_32)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table = this.table32;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.table = this.table16;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} selector
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.lookup_segment_selector = function(selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_assert(typeof selector === "number" && selector >= 0 && selector < 0x10000);
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
var is_gdt = (selector & 4) === 0,
|
|
|
|
selector_offset = selector & ~7,
|
|
|
|
info,
|
|
|
|
table_offset,
|
|
|
|
table_limit;
|
|
|
|
|
|
|
|
info = {
|
|
|
|
rpl: selector & 3,
|
|
|
|
from_gdt: is_gdt,
|
|
|
|
is_null: false,
|
|
|
|
is_valid: true,
|
2014-01-08 03:22:30 +01:00
|
|
|
|
|
|
|
base: 0,
|
|
|
|
access: 0,
|
|
|
|
flags: 0,
|
|
|
|
type: 0,
|
|
|
|
dpl: 0,
|
|
|
|
is_system: false,
|
|
|
|
is_present: false,
|
|
|
|
is_executable: false,
|
|
|
|
rw_bit: false,
|
|
|
|
dc_bit: false,
|
|
|
|
size: false,
|
2014-07-27 01:07:51 +02:00
|
|
|
|
2016-02-15 00:21:53 +01:00
|
|
|
is_conforming_executable: false,
|
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
// limit after applying granularity
|
|
|
|
effective_limit: 0,
|
|
|
|
|
2014-01-08 03:22:30 +01:00
|
|
|
is_writable: false,
|
|
|
|
is_readable: false,
|
2014-02-11 21:42:28 +01:00
|
|
|
table_offset: 0,
|
2016-02-07 17:45:55 +01:00
|
|
|
|
2016-08-01 22:30:38 +02:00
|
|
|
raw0: 0,
|
2016-02-07 17:45:55 +01:00
|
|
|
raw1: 0,
|
2013-11-07 21:30:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if(is_gdt)
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = this.gdtr_offset;
|
|
|
|
table_limit = this.gdtr_size;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-07-07 02:37:40 +02:00
|
|
|
table_offset = this.segment_offsets[reg_ldtr];
|
|
|
|
table_limit = this.segment_limits[reg_ldtr];
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2017-05-09 18:58:33 +02:00
|
|
|
if(is_gdt && selector_offset === 0)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
info.is_null = true;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
// limit is the number of entries in the table minus one
|
2014-07-27 01:07:51 +02:00
|
|
|
if((selector | 7) > table_limit)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2017-05-09 19:11:20 +02:00
|
|
|
dbg_log("Selector " + h(selector, 4) + " is outside of the " +
|
|
|
|
(is_gdt ? "g" : "l") + "dt limits", LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
info.is_valid = false;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = table_offset + selector_offset | 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.paging)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
table_offset = this.translate_address_system_read(table_offset);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-02-11 21:42:28 +01:00
|
|
|
info.table_offset = table_offset;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
info.base = this.read16(table_offset + 2 | 0) | this.read8(table_offset + 4 | 0) << 16 |
|
|
|
|
this.read8(table_offset + 7 | 0) << 24;
|
|
|
|
info.access = this.read8(table_offset + 5 | 0);
|
|
|
|
info.flags = this.read8(table_offset + 6 | 0) >> 4;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
info.raw0 = this.read32s(table_offset | 0);
|
|
|
|
info.raw1 = this.read32s(table_offset + 4 | 0);
|
2016-02-07 17:45:55 +01:00
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
//this.write8(table_offset + 5 | 0, info.access | 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
// used if system
|
|
|
|
info.type = info.access & 0xF;
|
|
|
|
|
|
|
|
info.dpl = info.access >> 5 & 3;
|
|
|
|
|
|
|
|
info.is_system = (info.access & 0x10) === 0;
|
|
|
|
info.is_present = (info.access & 0x80) === 0x80;
|
|
|
|
info.is_executable = (info.access & 8) === 8;
|
|
|
|
|
|
|
|
info.rw_bit = (info.access & 2) === 2;
|
|
|
|
info.dc_bit = (info.access & 4) === 4;
|
|
|
|
|
2016-02-15 00:21:53 +01:00
|
|
|
info.is_conforming_executable = info.dc_bit && info.is_executable;
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
info.size = (info.flags & 4) === 4;
|
|
|
|
|
2016-08-02 04:15:24 +02:00
|
|
|
var limit = this.read16(table_offset) |
|
|
|
|
(this.read8(table_offset + 6 | 0) & 0xF) << 16;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
if(info.flags & 8)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-07-27 01:07:51 +02:00
|
|
|
// granularity set
|
|
|
|
info.effective_limit = (limit << 12 | 0xFFF) >>> 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-07-27 01:07:51 +02:00
|
|
|
info.effective_limit = limit;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
info.is_writable = info.rw_bit && !info.is_executable;
|
|
|
|
info.is_readable = info.rw_bit || !info.is_executable;
|
|
|
|
|
|
|
|
return info;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {number} reg
|
|
|
|
* @param {number} selector
|
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.switch_seg = function(reg, selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
dbg_assert(reg >= 0 && reg <= 5);
|
|
|
|
dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
|
2014-11-01 13:59:32 +01:00
|
|
|
|
2014-12-06 23:20:54 +01:00
|
|
|
if(!this.protected_mode || this.vm86_mode())
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg[reg] = selector;
|
|
|
|
this.segment_is_null[reg] = 0;
|
|
|
|
this.segment_offsets[reg] = selector << 4;
|
2015-03-09 01:32:40 +01:00
|
|
|
|
2015-12-30 19:57:20 +01:00
|
|
|
if(reg === reg_ss)
|
2015-03-09 01:32:40 +01:00
|
|
|
{
|
|
|
|
this.stack_size_32 = false;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(reg === reg_ss)
|
|
|
|
{
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_log("#GP for loading 0 in SS sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(0);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-15 01:28:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
if(!info.is_valid ||
|
2016-02-15 01:28:18 +01:00
|
|
|
info.is_system ||
|
|
|
|
info.rpl !== this.cpl ||
|
|
|
|
!info.is_writable ||
|
|
|
|
info.dpl !== this.cpl)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_log("#GP for loading invalid in SS sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-15 01:28:18 +01:00
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
if(!info.is_present)
|
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_log("#SS for loading non-present in SS sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_ss(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.stack_size_32 = info.size;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else if(reg === reg_cs)
|
|
|
|
{
|
2016-08-01 22:26:10 +02:00
|
|
|
// handled by switch_cs_real_mode, far_return or far_jump
|
|
|
|
dbg_assert(false);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// es, ds, fs, gs
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
//dbg_log("0 loaded in seg=" + reg + " sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
//dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.sreg[reg] = selector;
|
|
|
|
this.segment_is_null[reg] = 1;
|
2015-09-14 01:45:51 +02:00
|
|
|
return;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-15 01:28:18 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
if(!info.is_valid ||
|
2016-02-15 01:28:18 +01:00
|
|
|
info.is_system ||
|
|
|
|
!info.is_readable ||
|
|
|
|
(!info.is_conforming_executable &&
|
|
|
|
(info.rpl > info.dpl || this.cpl > info.dpl))
|
|
|
|
) {
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_log("#GP for loading invalid in seg " + reg + " sel=" + h(selector, 4), LOG_CPU);
|
2016-08-01 23:22:38 +02:00
|
|
|
this.debug.dump_state();
|
2017-04-02 18:03:50 +02:00
|
|
|
this.debug.dump_regs();
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_gp(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2016-02-15 01:28:18 +01:00
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
if(!info.is_present)
|
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
dbg_log("#NP for loading not-present in seg " + reg + " sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
dbg_trace(LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_np(selector & ~3);
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_is_null[reg] = 0;
|
2014-07-27 01:07:51 +02:00
|
|
|
this.segment_limits[reg] = info.effective_limit;
|
|
|
|
//this.segment_infos[reg] = 0; // TODO
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.segment_offsets[reg] = info.base;
|
|
|
|
this.sreg[reg] = selector;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.load_tr = function(selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-15 00:50:35 +01:00
|
|
|
dbg_assert(info.is_valid);
|
2016-08-01 23:22:38 +02:00
|
|
|
//dbg_log("load tr: " + h(selector, 4) + " offset=" + h(info.base >>> 0, 8) + " limit=" + h(info.effective_limit >>> 0, 8), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(!info.from_gdt)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("TR can only be loaded from GDT");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
dbg_log("#GP(0) | tried to load null selector (ltr)");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_system)
|
|
|
|
{
|
|
|
|
dbg_log("#GP | ltr: not a system entry");
|
2017-04-06 02:57:24 +02:00
|
|
|
throw this.debug.unimpl("#GP handler (happens when running kvm-unit-test without ACPI)");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2016-02-15 00:50:35 +01:00
|
|
|
if(info.type !== 9 && info.type !== 1)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
// 0xB: busy 386 TSS (GP)
|
2017-04-06 02:57:24 +02:00
|
|
|
// 0x9: 386 TSS
|
2015-09-14 01:45:51 +02:00
|
|
|
// 0x3: busy 286 TSS (GP)
|
|
|
|
// 0x1: 286 TSS (??)
|
|
|
|
dbg_log("#GP | ltr: invalid type (type = " + h(info.type) + ")");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2017-04-06 02:57:24 +02:00
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("#NT | present bit not set (ltr)");
|
|
|
|
throw this.debug.unimpl("#NT handler");
|
|
|
|
}
|
|
|
|
|
2017-05-09 19:11:20 +02:00
|
|
|
this.tss_size_32 = info.type === 9;
|
2014-07-07 02:37:40 +02:00
|
|
|
this.segment_offsets[reg_tr] = info.base;
|
2014-07-27 01:07:51 +02:00
|
|
|
this.segment_limits[reg_tr] = info.effective_limit;
|
2014-07-07 02:37:40 +02:00
|
|
|
this.sreg[reg_tr] = selector;
|
2014-02-11 21:42:28 +01:00
|
|
|
|
2015-09-14 01:45:51 +02:00
|
|
|
// Mark task as busy
|
2016-08-02 04:15:24 +02:00
|
|
|
this.write8(info.table_offset + 5 | 0, this.read8(info.table_offset + 5 | 0) | 2);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-27 01:07:51 +02:00
|
|
|
//dbg_log("tsr at " + h(info.base) + "; (" + info.effective_limit + " bytes)");
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.load_ldt = function(selector)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
var info = this.lookup_segment_selector(selector);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
if(info.is_null)
|
|
|
|
{
|
|
|
|
// invalid
|
2014-07-07 02:37:40 +02:00
|
|
|
this.segment_offsets[reg_ldtr] = 0;
|
|
|
|
this.segment_limits[reg_ldtr] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-02-15 00:50:35 +01:00
|
|
|
dbg_assert(info.is_valid);
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
if(!info.from_gdt)
|
|
|
|
{
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("LDTR can only be loaded from GDT");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_present)
|
|
|
|
{
|
|
|
|
dbg_log("lldt: present bit not set");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!info.is_system)
|
|
|
|
{
|
|
|
|
dbg_log("lldt: not a system entry");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(info.type !== 2)
|
|
|
|
{
|
|
|
|
dbg_log("lldt: invalid type (" + info.type + ")");
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("#GP handler");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-07-07 02:37:40 +02:00
|
|
|
this.segment_offsets[reg_ldtr] = info.base;
|
2014-07-27 01:07:51 +02:00
|
|
|
this.segment_limits[reg_ldtr] = info.effective_limit;
|
2014-07-07 02:37:40 +02:00
|
|
|
this.sreg[reg_ldtr] = selector;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-08-01 23:22:38 +02:00
|
|
|
//dbg_log("ldt at " + h(info.base >>> 0) + "; (" + info.effective_limit + " bytes)", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.arpl = function(seg, r16)
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
this.flags_changed &= ~flag_zero;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if((seg & 3) < (this.reg16[r16] & 3))
|
|
|
|
{
|
|
|
|
this.flags |= flag_zero;
|
|
|
|
return seg & ~3 | this.reg16[r16] & 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.flags &= ~flag_zero;
|
|
|
|
return seg;
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-02-22 00:51:58 +01:00
|
|
|
CPU.prototype.lar = function(selector, original)
|
|
|
|
{
|
2017-04-06 02:57:24 +02:00
|
|
|
dbg_log("lar sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
|
2015-02-22 00:51:58 +01:00
|
|
|
/** @const */
|
2015-09-14 01:45:51 +02:00
|
|
|
var LAR_INVALID_TYPE = 1 << 0 | 1 << 6 | 1 << 7 | 1 << 8 | 1 << 0xA |
|
2015-02-22 00:51:58 +01:00
|
|
|
1 << 0xD | 1 << 0xE | 1 << 0xF;
|
|
|
|
|
|
|
|
var info = this.lookup_segment_selector(selector);
|
|
|
|
this.flags_changed &= ~flag_zero;
|
|
|
|
|
2016-02-07 17:45:55 +01:00
|
|
|
var dpl_bad = info.dpl < this.cpl || info.dpl < info.rpl;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2015-02-22 00:51:58 +01:00
|
|
|
if(info.is_null || !info.is_valid ||
|
2016-02-07 17:45:55 +01:00
|
|
|
(info.is_system ? (LAR_INVALID_TYPE >> info.type & 1) || dpl_bad :
|
2016-02-15 00:21:53 +01:00
|
|
|
!info.is_conforming_executable && dpl_bad)
|
2015-02-22 00:51:58 +01:00
|
|
|
) {
|
|
|
|
this.flags &= ~flag_zero;
|
2016-02-15 00:50:35 +01:00
|
|
|
dbg_log("lar: invalid selector=" + h(selector, 4) + " is_null=" + info.is_null, LOG_CPU);
|
2015-02-22 00:51:58 +01:00
|
|
|
return original;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.flags |= flag_zero;
|
2016-02-07 17:45:55 +01:00
|
|
|
return info.raw1 & 0x00FFFF00;
|
2015-02-22 00:51:58 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.lsl = function(selector, original)
|
|
|
|
{
|
2017-04-06 02:57:24 +02:00
|
|
|
dbg_log("lsl sel=" + h(selector, 4), LOG_CPU);
|
|
|
|
|
2015-02-22 00:51:58 +01:00
|
|
|
/** @const */
|
2015-09-14 01:45:51 +02:00
|
|
|
var LSL_INVALID_TYPE = 1 << 0 | 1 << 4 | 1 << 5 | 1 << 6 | 1 << 8 |
|
2015-02-22 00:51:58 +01:00
|
|
|
1 << 0xA | 1 << 0xC | 1 << 0xD | 1 << 0xE | 1 << 0xF;
|
|
|
|
|
|
|
|
var info = this.lookup_segment_selector(selector);
|
|
|
|
this.flags_changed &= ~flag_zero;
|
|
|
|
|
2016-02-07 17:45:55 +01:00
|
|
|
var dpl_bad = info.dpl < this.cpl || info.dpl < info.rpl;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2015-02-22 00:51:58 +01:00
|
|
|
if(info.is_null || !info.is_valid ||
|
2016-02-07 17:45:55 +01:00
|
|
|
(info.is_system ? (LSL_INVALID_TYPE >> info.type & 1) || dpl_bad :
|
2016-02-15 00:21:53 +01:00
|
|
|
!info.is_conforming_executable && dpl_bad)
|
2015-02-22 00:51:58 +01:00
|
|
|
) {
|
|
|
|
this.flags &= ~flag_zero;
|
2016-02-07 17:45:55 +01:00
|
|
|
dbg_log("lsl: invalid selector=" + h(selector, 4) + " is_null=" + info.is_null, LOG_CPU);
|
2015-02-22 00:51:58 +01:00
|
|
|
return original;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.flags |= flag_zero;
|
|
|
|
return info.effective_limit | 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2016-02-14 20:00:41 +01:00
|
|
|
CPU.prototype.verr = function(selector)
|
|
|
|
{
|
|
|
|
var info = this.lookup_segment_selector(selector);
|
|
|
|
this.flags_changed &= ~flag_zero;
|
|
|
|
|
|
|
|
if(info.is_null || !info.is_valid || info.is_system || !info.is_readable ||
|
|
|
|
(!info.is_conforming_executable && (info.dpl < this.cpl || info.dpl < info.rpl)))
|
|
|
|
{
|
2016-02-14 20:07:18 +01:00
|
|
|
dbg_log("verr -> invalid. selector=" + h(selector, 4), LOG_CPU);
|
2016-02-14 20:00:41 +01:00
|
|
|
this.flags &= ~flag_zero;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_log("verr -> valid. selector=" + h(selector, 4), LOG_CPU);
|
|
|
|
this.flags |= flag_zero;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.verw = function(selector)
|
|
|
|
{
|
|
|
|
var info = this.lookup_segment_selector(selector);
|
|
|
|
this.flags_changed &= ~flag_zero;
|
|
|
|
|
|
|
|
if(info.is_null || !info.is_valid || info.is_system || !info.is_writable ||
|
|
|
|
info.dpl < this.cpl || info.dpl < info.rpl)
|
|
|
|
{
|
|
|
|
dbg_log("verw invalid " + " " + h(selector) + " " + info.is_null + " " +
|
|
|
|
!info.is_valid + " " + info.is_system + " " + !info.is_writable + " " +
|
|
|
|
(info.dpl < this.cpl) + " " + (info.dpl < info.rpl) + " " + LOG_CPU);
|
|
|
|
this.flags &= ~flag_zero;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-04-06 02:57:24 +02:00
|
|
|
dbg_log("verw valid", LOG_CPU);
|
2016-02-14 20:00:41 +01:00
|
|
|
this.flags |= flag_zero;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.clear_tlb = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// clear tlb excluding global pages
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info.set(this.tlb_info_global);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
//dbg_log("page table loaded", LOG_CPU);
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.full_clear_tlb = function()
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
//dbg_log("TLB full clear", LOG_CPU);
|
2014-07-12 22:20:57 +02:00
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
// clear tlb including global pages
|
2014-07-13 00:02:17 +02:00
|
|
|
var buf32 = new Int32Array(this.tlb_info_global.buffer);
|
|
|
|
for(var i = 0; i < (1 << 18); )
|
|
|
|
{
|
|
|
|
buf32[i++] = buf32[i++] = buf32[i++] = buf32[i++] = 0;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.clear_tlb();
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.invlpg = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var page = addr >>> 12;
|
2014-07-12 22:20:57 +02:00
|
|
|
//dbg_log("invlpg: addr=" + h(addr >>> 0), LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info[page] = 0;
|
|
|
|
this.tlb_info_global[page] = 0;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.last_virt_eip = -1;
|
|
|
|
this.last_virt_esp = -1;
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.translate_address_read = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
if(this.cpl === 3)
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
return this.translate_address_user_read(addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.translate_address_system_read(addr);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.translate_address_write = function(addr)
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
if(this.cpl === 3)
|
2014-06-15 22:25:17 +02:00
|
|
|
{
|
|
|
|
return this.translate_address_user_write(addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.translate_address_system_write(addr);
|
|
|
|
}
|
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.translate_address_user_write = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-07 16:58:41 +01:00
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
var base = addr >>> 12;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_USER_WRITE)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-07-12 22:20:57 +02:00
|
|
|
return this.tlb_data[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 1, 1) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.translate_address_user_read = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-07 16:58:41 +01:00
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
var base = addr >>> 12;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_USER_READ)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-07-12 22:20:57 +02:00
|
|
|
return this.tlb_data[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 0, 1) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.translate_address_system_write = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-07 16:58:41 +01:00
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
var base = addr >>> 12;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_SYSTEM_WRITE)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-07-12 22:20:57 +02:00
|
|
|
return this.tlb_data[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 1, 0) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.translate_address_system_read = function(addr)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2016-02-07 16:58:41 +01:00
|
|
|
if(!this.paging)
|
|
|
|
{
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
var base = addr >>> 12;
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.tlb_info[base] & TLB_SYSTEM_READ)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-07-12 22:20:57 +02:00
|
|
|
return this.tlb_data[base] ^ addr;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-06-15 22:25:17 +02:00
|
|
|
return this.do_page_translation(addr, 0, 0) | addr & 0xFFF;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
/**
|
2015-09-14 01:45:51 +02:00
|
|
|
* @return {number}
|
2013-11-07 21:30:18 +01:00
|
|
|
*/
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.do_page_translation = function(addr, for_writing, user)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
var page = addr >>> 12,
|
2015-04-22 05:00:34 +02:00
|
|
|
page_dir_addr = (this.cr[3] >>> 2) + (page >> 10) | 0,
|
2016-08-02 04:15:24 +02:00
|
|
|
page_dir_entry = this.mem32s[page_dir_addr],
|
2013-11-07 21:30:18 +01:00
|
|
|
high,
|
|
|
|
can_write = true,
|
|
|
|
global,
|
|
|
|
cachable = true,
|
|
|
|
allow_user = true;
|
|
|
|
|
2014-01-05 03:17:40 +01:00
|
|
|
dbg_assert(addr < 0x80000000);
|
|
|
|
|
2013-11-07 21:30:18 +01:00
|
|
|
if(!(page_dir_entry & 1))
|
|
|
|
{
|
|
|
|
// to do at this place:
|
|
|
|
//
|
|
|
|
// - set cr2 = addr (which caused the page fault)
|
|
|
|
// - call_interrupt_vector with id 14, error code 0-7 (requires information if read or write)
|
|
|
|
// - prevent execution of the function that triggered this call
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not present", LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = addr;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_pagefault(for_writing, user, 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
// never reached as this.trigger_pagefault throws up
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_dir_entry & 2) === 0)
|
|
|
|
{
|
|
|
|
can_write = false;
|
|
|
|
|
2015-04-12 20:16:26 +02:00
|
|
|
if(for_writing && (user || (this.cr[0] & CR0_WP)))
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = addr;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_dir_entry & 4) === 0)
|
|
|
|
{
|
|
|
|
allow_user = false;
|
|
|
|
|
|
|
|
if(user)
|
|
|
|
{
|
|
|
|
// "Page Fault: page table accessed by non-supervisor";
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF supervisor", LOG_CPU);
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = addr;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(page_dir_entry & this.page_size_extensions)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
|
|
|
// size bit is set
|
|
|
|
|
|
|
|
// set the accessed and dirty bits
|
2016-08-02 04:15:24 +02:00
|
|
|
this.mem32s[page_dir_addr] = page_dir_entry | 0x20 | for_writing << 6;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
high = (page_dir_entry & 0xFFC00000) | (addr & 0x3FF000);
|
2013-11-07 21:30:18 +01:00
|
|
|
global = page_dir_entry & 0x100;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-04-22 05:00:34 +02:00
|
|
|
var page_table_addr = ((page_dir_entry & 0xFFFFF000) >>> 2) + (page & 0x3FF) | 0,
|
2016-08-02 04:15:24 +02:00
|
|
|
page_table_entry = this.mem32s[page_table_addr];
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
if((page_table_entry & 1) === 0)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not present table", LOG_CPU);
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = addr;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_pagefault(for_writing, user, 0);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_table_entry & 2) === 0)
|
|
|
|
{
|
|
|
|
can_write = false;
|
|
|
|
|
2015-04-12 20:16:26 +02:00
|
|
|
if(for_writing && (user || (this.cr[0] & CR0_WP)))
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not writable page", LOG_CPU);
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = addr;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((page_table_entry & 4) === 0)
|
|
|
|
{
|
|
|
|
allow_user = false;
|
|
|
|
|
|
|
|
if(user)
|
|
|
|
{
|
2013-12-01 23:36:37 +01:00
|
|
|
//dbg_log("#PF not supervisor page", LOG_CPU);
|
2015-04-12 20:16:26 +02:00
|
|
|
this.cr[2] = addr;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.trigger_pagefault(for_writing, user, 1);
|
2013-11-07 21:30:18 +01:00
|
|
|
dbg_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the accessed and dirty bits
|
2016-08-02 04:32:02 +02:00
|
|
|
this.write_aligned32(page_dir_addr, page_dir_entry | 0x20);
|
|
|
|
this.write_aligned32(page_table_addr, page_table_entry | 0x20 | for_writing << 6);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
|
|
|
high = page_table_entry & 0xFFFFF000;
|
|
|
|
global = page_table_entry & 0x100;
|
|
|
|
}
|
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
this.tlb_data[page] = high ^ page << 12;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
var allowed_flag;
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
if(allow_user)
|
|
|
|
{
|
|
|
|
if(can_write)
|
|
|
|
{
|
|
|
|
allowed_flag = TLB_SYSTEM_READ | TLB_SYSTEM_WRITE | TLB_USER_READ | TLB_USER_WRITE;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-07-12 22:20:57 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: Consider if cr0.wp is not set
|
|
|
|
allowed_flag = TLB_SYSTEM_READ | TLB_USER_READ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-11-07 21:30:18 +01:00
|
|
|
if(can_write)
|
|
|
|
{
|
2014-07-12 22:20:57 +02:00
|
|
|
allowed_flag = TLB_SYSTEM_READ | TLB_SYSTEM_WRITE;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
2014-07-12 22:20:57 +02:00
|
|
|
else
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2014-07-12 22:20:57 +02:00
|
|
|
allowed_flag = TLB_SYSTEM_READ;
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
}
|
2015-09-14 01:45:51 +02:00
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
this.tlb_info[page] = allowed_flag;
|
|
|
|
|
2015-04-12 20:16:26 +02:00
|
|
|
if(global && (this.cr[4] & CR4_PGE))
|
2014-07-12 22:20:57 +02:00
|
|
|
{
|
|
|
|
this.tlb_info_global[page] = allowed_flag;
|
|
|
|
}
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-02-22 22:15:41 +01:00
|
|
|
return high;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2014-02-22 22:15:41 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.writable_or_pagefault = function(addr, size)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
|
|
|
dbg_assert(size < 0x1000, "not supported yet");
|
|
|
|
dbg_assert(size > 0);
|
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(!this.paging)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-12 22:20:57 +02:00
|
|
|
var user = this.cpl === 3 ? 1 : 0,
|
|
|
|
mask = user ? TLB_USER_WRITE : TLB_SYSTEM_WRITE,
|
|
|
|
page = addr >>> 12;
|
|
|
|
|
|
|
|
if((this.tlb_info[page] & mask) === 0)
|
|
|
|
{
|
|
|
|
this.do_page_translation(addr, 1, user);
|
|
|
|
}
|
2014-02-22 22:15:41 +01:00
|
|
|
|
|
|
|
if((addr & 0xFFF) + size - 1 >= 0x1000)
|
|
|
|
{
|
2015-04-22 05:00:34 +02:00
|
|
|
if((this.tlb_info[page + 1 | 0] & mask) === 0)
|
2014-02-22 22:15:41 +01:00
|
|
|
{
|
2015-04-22 05:00:34 +02:00
|
|
|
this.do_page_translation(addr + size - 1 | 0, 1, user);
|
2014-02-22 22:15:41 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-12-21 18:11:22 +01:00
|
|
|
CPU.prototype.trigger_pagefault = function(write, user, present)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2015-09-14 01:45:51 +02:00
|
|
|
//dbg_log("page fault w=" + write + " u=" + user + " p=" + present +
|
2014-06-29 22:28:55 +02:00
|
|
|
// " eip=" + h(this.previous_ip >>> 0, 8) +
|
2015-04-12 20:16:26 +02:00
|
|
|
// " cr2=" + h(this.cr[2] >>> 0, 8), LOG_CPU);
|
2014-06-29 22:28:55 +02:00
|
|
|
//dbg_trace(LOG_CPU);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
if(this.page_fault)
|
2013-11-07 21:30:18 +01:00
|
|
|
{
|
2013-12-28 23:21:43 +01:00
|
|
|
dbg_trace(LOG_CPU);
|
2014-12-06 23:20:54 +01:00
|
|
|
throw this.debug.unimpl("Double fault");
|
2013-11-07 21:30:18 +01:00
|
|
|
}
|
|
|
|
|
2014-01-02 01:05:49 +01:00
|
|
|
// invalidate tlb entry
|
2015-04-12 20:16:26 +02:00
|
|
|
var page = this.cr[2] >>> 12;
|
2014-06-15 22:25:17 +02:00
|
|
|
this.tlb_info[page] = 0;
|
|
|
|
this.tlb_info_global[page] = 0;
|
2014-01-02 01:05:49 +01:00
|
|
|
|
2014-06-15 22:25:17 +02:00
|
|
|
this.instruction_pointer = this.previous_ip;
|
|
|
|
this.page_fault = true;
|
|
|
|
this.call_interrupt_vector(14, false, user << 2 | write << 1 | present);
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2014-05-06 20:24:46 +02:00
|
|
|
throw MAGIC_CPU_EXCEPTION;
|
2014-06-15 22:25:17 +02:00
|
|
|
};
|
2013-11-07 21:30:18 +01:00
|
|
|
|
2017-03-07 01:03:06 +01:00
|
|
|
CPU.prototype.is_osize_32 = function()
|
|
|
|
{
|
|
|
|
return this.is_32 !== ((this.prefixes & PREFIX_MASK_OPSIZE) === PREFIX_MASK_OPSIZE);
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.is_asize_32 = function()
|
|
|
|
{
|
|
|
|
return this.is_32 !== ((this.prefixes & PREFIX_MASK_ADDRSIZE) === PREFIX_MASK_ADDRSIZE);
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.get_reg_asize = function(reg)
|
|
|
|
{
|
|
|
|
dbg_assert(reg === reg_ecx || reg === reg_esi || reg === reg_edi);
|
|
|
|
var r = this.reg32s[reg];
|
|
|
|
|
|
|
|
if(this.is_asize_32())
|
|
|
|
{
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return r & 0xFFFF;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.set_ecx_asize = function(value)
|
|
|
|
{
|
|
|
|
if(this.is_asize_32())
|
|
|
|
{
|
|
|
|
this.reg32s[reg_ecx] = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.reg16[reg_cx] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.add_reg_asize = function(reg, value)
|
|
|
|
{
|
|
|
|
dbg_assert(reg === reg_ecx || reg === reg_esi || reg === reg_edi);
|
|
|
|
if(this.is_asize_32())
|
|
|
|
{
|
|
|
|
this.reg32s[reg] += value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.reg16[reg << 1] += value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CPU.prototype.decr_ecx_asize = function()
|
|
|
|
{
|
|
|
|
return this.is_asize_32() ? --this.reg32s[reg_ecx] : --this.reg16[reg_cx];
|
2017-04-06 02:57:24 +02:00
|
|
|
};
|
2016-08-01 21:28:19 +02:00
|
|
|
|
|
|
|
// Closure Compiler's way of exporting
|
|
|
|
if(typeof window !== "undefined")
|
|
|
|
{
|
|
|
|
window["CPU"] = CPU;
|
|
|
|
}
|
|
|
|
else if(typeof module !== "undefined" && typeof module.exports !== "undefined")
|
|
|
|
{
|
|
|
|
module.exports["CPU"] = CPU;
|
|
|
|
}
|
|
|
|
else if(typeof importScripts === "function")
|
|
|
|
{
|
|
|
|
self["CPU"] = CPU;
|
|
|
|
}
|