24 #include <boot/multiboot.h> 33 #define CODE_ADDRESS ((void*) 0x500) 36 #define IVT_ADDRESS ((void*) 0) 37 #define OPERAND_SIZE 0x66 38 #define OPCODE_PUSHF 0x9C 39 #define OPCODE_POPF 0x9D 40 #define OPCODE_INT_3 0xCC 41 #define OPCODE_INT 0xCD 42 #define OPCODE_IRET 0xCF 43 #define OPCODE_CLI 0xFA 44 #define OPCODE_STI 0xFB 54 #define CASE_IN(opcode, in_func, operand, inc, type) \ 56 *(type*) &cpu->r.eax = (in_func)(operand); \ 57 vm86_increment_eip(cpu, (inc)); \ 67 #define CASE_OUT(opcode, out_func, operand, inc) \ 69 (out_func)((operand), cpu->r.eax); \ 70 vm86_increment_eip(cpu, (inc)); \ 92 if ((uintptr_t) addr >= MULTIBOOT_LOWER_MEMORY) {
93 println(
"%4aAddress %08x too large for VM86 mode%a", addr);
94 vm86_farptr_t farptr = {.offset = 0, .segment = 0};
103 uint16_t offset = (uintptr_t) addr & 0xFFFF;
104 vm86_farptr_t farptr = {
107 .segment = ((uintptr_t) addr - offset) >> 4
149 logln(
"VM86",
"Creating VM86 task with %dKB kernel and %dKB user stack",
150 kernel_stack_len, user_stack_len);
159 vmm_map_range(0, 0, MULTIBOOT_LOWER_MEMORY, VMM_USER | VMM_WRITABLE);
161 uint32_t code_length = (uintptr_t) code_end - (uintptr_t) code_start + 1;
168 task->
state = TASK_RUNNING;
188 ((uint32_t) task->
user_stack + user_stack_len - 1));
207 uint8_t* operand = opcode + 1;
209 println(
"%4aVM86 BIOS handler corrupted%a");
212 *operand = interrupt;
238 uint16_t value = *esp;
280 println(
"BIOS call returned EAX=%08x, EBX=%08x, ECX=%08x, EDX=%08x",
285 logln(
"VM86",
"Emulating INT %02x", eip[1]);
298 logln(
"VM86",
"Emulating IRET");
305 #pragma GCC diagnostic push 306 #pragma GCC diagnostic ignored "-Wstrict-aliasing" 308 CASE_IN (0xE4, inb, eip[1], 2, uint8_t);
309 CASE_IN (0xE5, inw, eip[1], 2, uint16_t);
310 CASE_IN (0x66E5, inl, eip[2], 3, uint32_t);
320 #pragma GCC diagnostic pop 326 panic(
"VM86 opcode %02x unhandled (CS:IP=%04x:%04x)",
327 opcode, cpu->
cs, cpu->
eip);
339 panic(
"%4aEX%02x (EIP=%08x)", cpu->
intr, cpu->
eip);
#define GDT_RING3_DATA_SEG
user data segment index
#define _4KB
4KB are 4096 bytes, often used for stacks
#define CASE_IN(opcode, in_func, operand, inc, type)
Emulates an IN instruction.
page_directory_t * vmm_create_page_directory()
Creates an empty page directory.
uint8_t task_get_vm86(task_pid_t pid)
Returns whether a task is a VM86 task.
#define OPERAND_SIZE
opcode for overriding operand size
uint8_t isr_enable_interrupts(uint8_t enable)
Enables or disables interrupts.
general purpose registers
void vmm_enable_domain_check(uint8_t enable)
Enables or disables domain checking.
uint16_t gs
GS segment register.
task_state_t state
whether the task is running or stopped
task_pid_t schedule_get_current_task()
Returns the current task's PID.
task_stack_t * kernel_stack
stack for handling interrupts
void vm86_init()
Initializes VM86 mode.
static vm86_farptr_t vm86_get_farptr(void *addr)
Translate an address into a far pointer.
uint32_t edx
data register
static page_directory_t * page_directory
the current page directory
const void vm86_interrupt_hook
location of INT in the BIOS call code
#define OPCODE_PUSHF
PUSHF triggers a GPF inside VM86 mode.
void vmm_modified_page_directory()
Ends a page directory modification.
The CPU's state when an interrupt occurs.
uint16_t vm86_ds
DS register (only pushed and popped in VM86 mode)
void * vmm_alloc(size_t len, vmm_flags_t flags)
Marks some page(s) as used and maps them somewhere into memory.
static uint16_t vm86_pop(cpu_state_t *cpu)
Emulates a POP instruction.
static void vm86_write_farptr(uint16_t *segment, uint16_t *offset, vm86_farptr_t farptr)
Writes a far pointer into another location.
void vmm_map_range(void *vaddr, void *paddr, size_t len, vmm_flags_t flags)
Maps the given page(s) into memory.
static void vm86_push(cpu_state_t *cpu, uint16_t value)
Emulates a PUSH instruction.
task_pid_t task_add(task_t *task)
Adds a new task to the task list and associates a PID.
uint16_t vm86_gs
GS register (only pushed and popped in VM86 mode)
cpu_state_t * cpu
saved CPU state when entering/leaving interrupts
size_t kernel_stack_len
kernel stack length
static vm86_farptr_t * ivt
IVT needed to do BIOS calls.
size_t user_stack_len
user stack length
static uint8_t vm86_monitor(cpu_state_t *cpu)
Monitors a VM86 task by emulating sensitive instructions.
static uint8_t old_interrupts
for temporary modifications
uint16_t offset
offset inside the segment
uint32_t user_ss
stack segment (only pushed and popped in user space)
#define IVT_ADDRESS
The real mode IVT (= Interrupt Vector Table) lies at the start of memory.
#define CASE_OUT(opcode, out_func, operand, inc)
Emulates an OUT instruction.
uint8_t task_stack_t
stacks are measured in bytes
#define OPCODE_STI
STI triggers a GPF inside VM86 mode.
uint8_t _if
interrupt flag
page_directory_t * page_directory
this task's virtual memory map
uint32_t eip
the instruction to return to after the interrupt
uint32_t task_pid_t
unique process ID
#define ISR_EXCEPTION(ex)
the interrupt vector for an exception
internal representation of a task
#define CODE_ADDRESS
Where the assembly code will be located.
uint16_t gdt_get_selector(size_t entry)
Returns a selector ready to be loaded in a segment register.
task_pid_t vm86_create_task(void *code_start, void *code_end, page_directory_t *page_directory, size_t kernel_stack_len, size_t user_stack_len, isr_registers_t *registers)
Creates a VM86 task.
void isr_register_handler(size_t intr, isr_handler_t handler)
Registers a handler to call whenever a given interrupt is fired.
uint16_t es
ES segment register.
uint8_t vm86
whether this task is running in Virtual 8086 mode
uint32_t dword
useful for casting
static void vm86_increment_eip(cpu_state_t *cpu, size_t inc)
Emulates the completion of an instruction.
uint8_t vm
virtual 8086 mode
uint16_t vm86_es
ES register (only pushed and popped in VM86 mode)
task_stack_t * user_stack
stack for the actual task's code
static void * vm86_get_address(vm86_farptr_t farptr)
Translate a far pointer into an address.
An entry in a page directory.
uint16_t fs
FS segment register.
uint8_t reserved
always one
#define OPCODE_IRET
IRET triggers a GPF inside VM86 mode.
void vmm_modify_page_directory(page_directory_t *new_directory)
Loads a page directory for temporary modification.
struct isr_eflags_t::@8 bits
bit field
uint32_t eax
accumulator register
uint16_t cs
the code segment to return to after the interrupt
uint16_t isr_eflags_t eflags
the EFLAGS register before the interrupt was fired
uint16_t vm86_fs
FS register (only pushed and popped in VM86 mode)
#define OPCODE_INT_3
INT $3 triggers a GPF inside VM86 mode.
uint32_t ebx
base register
#define OPCODE_CLI
CLI triggers a GPF inside VM86 mode.
void vm86_call_bios(uint8_t interrupt, isr_registers_t *registers)
Calls a BIOS interrupt.
static cpu_state_t * vm86_handle_gpf(cpu_state_t *cpu)
Handles general protection faults.
uint16_t segment
blocks of 16 byte
uint32_t user_esp
stack pointer (only pushed and popped in user space)
const void vm86_call_bios_end
end of the BIOS call code
uint16_t ds
DS segment register.
uint32_t ecx
counter register
const void vm86_call_bios_start
start of the BIOS call code
uint32_t intr
the interrupt vector of the fired interrupt
#define OPCODE_POPF
POPF triggers a GPF inside VM86 mode.
#define OPCODE_INT
INT triggers a GPF inside VM86 mode.
uint16_t isr_registers_t r
The general purpose registers.