Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

v86.c File Reference

Virtual 8086 mode. More...

#include <const.h>
#include <string.h>
#include <arch/i386.h>
#include <arch/interrupt.h>
#include <arch/mem.h>
#include <arch/paging.h>
#include <kernel/console.h>
#include <kernel/kmalloc.h>
#include <kernel/queue.h>
#include <kernel/kernel_map.h>
#include <kernel/task.h>
#include <arch/v86.h>

Go to the source code of this file.

Defines

#define V86_TASK_STACK_START   0x90000
 The address where the task stack is placed.


Functions

int new_pid ()
 Create a new pid.
Returns:
The new pid created.


task_tcreate_v86_process (void *address, void *buffer, size_t size, char *v86name)
 Create a new virtual-8086 mode process.
Parameters:
address  The address of the starting point for the task.
buffer  An address to a buffer where is placed the executable code.
size  The size of the executable code buffer.
v86name  The name of the new v86 task.
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).


void v86_monitor ()
 The virtual-8086 monitor.


Variables

queue_tready_queue
 Ready-queue: tasks are ready for the CPU.

task_tcurr_task
 Current running task.


Detailed Description

Virtual 8086 mode.

Author:
Andrea Righi <drizzt@inwind.it>
Date:
Last update:
2003-12-16 Andrea Righi: Added temporary memory operators to get and free the temporary mapped pages.
Note:
Copyright (C) 2003 Andrea Righi

Definition in file v86.c.


Define Documentation

#define V86_TASK_STACK_START   0x90000
 

The address where the task stack is placed.

Definition at line 34 of file v86.c.


Function Documentation

task_t* create_v86_process void *    address,
void *    buffer,
size_t    size,
char *    v86name
 

Create a new virtual-8086 mode process.

Parameters:
address  The address of the starting point for the task.
buffer  An address to a buffer where is placed the executable code.
size  The size of the executable code buffer.
v86name  The name of the new v86 task.
Returns:
A pointer to the new-created task structure.
Exceptions:
NULL  If an error occurs (in particular out-of-memory).

This routine initializes a new v86-task structure, copies the content of the buffer to the entry point address and then add the new task to the ready queue.

Definition at line 52 of file v86.c.

00053 {
00054         extern dword K_PDBR[PAGE_SIZE/sizeof(dword)];
00055         task_t *new_task;
00056         byte *pl0_stack;
00057         dword *PDBR;
00058         dword cr3, PDBR_frame, i;
00059 
00060         sched_enter_critical_region();
00061 
00062         // --- Create the task structure --- //
00063         new_task = kmemalign(PAGE_SIZE, sizeof(task_t));
00064         if (!new_task)
00065         {
00066                 // Out of virtual memory!!! //
00067                 set_color(LIGHT_RED);
00068                 kprintf("\n\rOut of virtual memory!!! Cannot create v86task [%s].", v86name);
00069                 set_color(DEFAULT_COLOR);
00070 
00071                 sched_leave_critical_region();
00072                 return(NULL);
00073         }
00074         memset(new_task, 0, sizeof(task_t));
00075 
00076         // --- Create the pl0-stack --- //
00077         pl0_stack = kmalloc(STACK_SIZE);
00078         if (!pl0_stack)
00079         {
00080                 // Out of virtual memory!!! //
00081                 set_color(LIGHT_RED);
00082                 kprintf("\n\rOut of virtual memory!!! Cannot create v86task [%s].", v86name);
00083                 set_color(DEFAULT_COLOR);
00084                 // Free the previous allocated space //
00085                 kfree((void *)new_task);
00086 
00087                 sched_leave_critical_region();
00088                 return(NULL);
00089         }
00090         // Null the stack to enforce the page mapping //
00091         memset(pl0_stack, 0, STACK_SIZE);
00092         // Setup the pl0-stack //
00093         new_task->tss.ss0 = KERNEL_STACK;
00094         new_task->tss.esp0 = new_task->pl0_stack = (dword)(pl0_stack+STACK_SIZE-sizeof(uint32_t));
00095 
00096         // --- Setup the TSS --- //
00097         new_task->tss_sel = setup_GDT_entry(sizeof(tss_IO_t), (dword)&(new_task->tss), TSS_SEG, 0);
00098 
00099         // --- Create the virtual space of the task ------------------- //
00100         PDBR = (dword *)get_temp_page();
00101         if ( PDBR == NULL )
00102         {
00103                 // Out of memory!!! Free the previous allocated memory. //
00104                 set_color(LIGHT_RED);
00105                 kprintf("\n\rOut of physical memory!!! Cannot create v86task [%s].", v86name);
00106                 set_color(DEFAULT_COLOR);
00107                 // Free the previous allocated space //
00108                 kfree((void *)(new_task->pl0_stack-STACK_SIZE));
00109                 kfree((void *)new_task);
00110                 // Destroy the TSS //
00111                 remove_GDT_entry(new_task->tss_sel);
00112 
00113                 sched_leave_critical_region();
00114                 return(NULL);
00115         }
00116         PDBR_frame = vir_to_phys( (dword)PDBR );
00117 
00118         // Initialize PDBR //
00119         memset(PDBR, 0, PAGE_SIZE);
00120         for (i=K_VIR_START/(PAGE_SIZE*1024); i<1024; i++)
00121                 PDBR[i] = K_PDBR[i];
00122 
00123         // Map page directory into itself //
00124         PDBR[1023] = PDBR_frame | P_PRESENT | P_WRITE;
00125 
00126         // Unmap the temporary page, without free the physical frame    //
00127         free_temp_page(PDBR, FALSE);
00128 
00129         // Temporary switch to the new address space                    //
00130         //                                                              //
00131         // QUESTION: cr3 is in the stack... if I switch into the new    //
00132         // address space can I ever see the old cr3 value???            //
00133         //                                                              //
00134         // RE: Yes, but only if cr3 is stored into a register...        //
00135         // And if the value should be stored into the stack, here we    //
00136         // are at CPL0, so we're using the 0-privileged stack, so,      //
00137         // since the 0-privileged stack space is shared between every   //
00138         // task and also the kernel, we're ever able to see the stored  //
00139         // cr3 value...                                                 //
00140         __asm__ __volatile__ ("movl %%cr3, %0" : "=r"(cr3) : );
00141         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(PDBR_frame));
00142 
00143         // Map the IVT (Interrupt Vector Table) //
00144         for (i=BIOS_IVT_START; i<BIOS_IVT_END; i+=PAGE_SIZE)
00145                 map_page(i, i, P_PRESENT | P_WRITE | P_USER);
00146         // Map the VIDEO BUFFER //
00147         for (i=VIDEO_BUF_START; i<VIDEO_BUF_END; i+=PAGE_SIZE)
00148                 map_page(i, i, P_PRESENT | P_WRITE | P_USER);
00149         // Map the BIOS ROM memory //
00150         for (i=BIOS_ROM_START; i<BIOS_ROM_END; i+=PAGE_SIZE)
00151                 map_page(i, i, P_PRESENT | P_WRITE | P_USER);
00152 
00153         // --- Create the user space --- //
00154 
00155         // Create the stack //
00156 
00157         // It is not necessary to null the stack to enforce page        //
00158         // mapping, because V86-mode tasks run permanently at CPL3.     //
00159         // So a stack-fault is managed like a normal page-fault.        //
00160         new_task->tss.ss = SEGMENT((dword)V86_TASK_STACK_START-sizeof(uint32_t));
00161         new_task->tss.esp = OFFSET((dword)(V86_TASK_STACK_START-sizeof(uint32_t)));
00162 
00163         // Map the user code and data //
00164         if (address != buffer)
00165         {
00166                 memcpy(address, buffer, size);
00167         }
00168 
00169         // Restore the old address space //
00170         __asm__ __volatile__ ("movl %0, %%cr3" : : "r"(cr3));
00171 
00172         // Setup the v86 task PDBR //
00173         new_task->tss.cr3 = PDBR_frame;
00174 
00175         // Setup the IO port mapping //
00176         new_task->tss.io_map_addr = sizeof(tss_t);
00177         // for (i=0; i<8192; i++) new_task->tss.io_map[i] = 0;
00178 
00179         // Initialize segment registers //
00180         new_task->tss.ds = new_task->tss.es =
00181         new_task->tss.fs = new_task->tss.gs = SEGMENT((dword)address);
00182 
00183         // Initialize general purpose registers //
00184         new_task->tss.eax = new_task->tss.ebx =
00185         new_task->tss.ecx = new_task->tss.edx =
00186         new_task->tss.esi = new_task->tss.edi = 0;
00187 
00188         // Virtual 8086 mode task - IOPL = 0 //
00189         // IOPL < 3 => all IOPL-sensitive instructions trap to the kernel with a GPF //
00190         new_task->tss.eflags = EFLAGS_IOPL0 | EFLAGS_VM | EFLAGS_IF | 0x02;
00191 
00192         // Initialize LDTR (Local Descriptor Table Register) //
00193         // No LDTs for now...
00194         new_task->tss.ldtr = 0;
00195 
00196         // Initialize debug trap //
00197         // If set to 1 the processor generate a debug exception when a task switch to this task occurs...
00198         new_task->tss.trace = 0;
00199 
00200         // Setup program counter //
00201         new_task->tss.cs = SEGMENT((dword)address);
00202         new_task->tss.eip = OFFSET((dword)address);
00203 
00204         // --- Get a pid --- //
00205         new_task->pid = new_pid();
00206 
00207         // --- Store the name --- //
00208         // The last character must be ever '\0' (end of string) //
00209         strncpy(new_task->name, v86name, sizeof(new_task->name)-2);
00210 
00211         // --- Set the type --- //
00212         new_task->type = PROCESS_T;
00213 
00214         // --- Set the parent process --- //
00215         new_task->father = curr_task;
00216 
00217         // --- Insert the task into the ready queue --- //
00218         new_task->state = READY;
00219         add_queue(&ready_queue, new_task);
00220 
00221         sched_leave_critical_region();
00222 
00223         return (new_task);
00224 }

void v86_monitor  
 

The virtual-8086 monitor.

Definition at line 230 of file v86.c.

00231 {
00232         v86_context *context = (v86_context *)(curr_task->pl0_stack-sizeof(v86_context));
00233         byte *ip;
00234         word *stack, *ivt;
00235 
00236         ip = (byte *) LINEAR(context->cs, context->ip);
00237         stack = (word *) LINEAR(context->ss, context->sp);
00238         ivt = (word *) BIOS_IVT_START;
00239 
00240         while(TRUE)
00241         {
00242                 switch (ip[0])
00243                 {
00244                 case 0x9C:      // PUSHF //
00245                         context->sp = ((context->sp & 0xFFFF) - 2) & 0xFFFF;
00246                         stack--;
00247                         stack[0] = (word) context->eflags;
00248                         context->ip = (word)(context->ip + 1);
00249                         return;
00250 
00251                 case 0x9D:      // POPF //
00252                         context->eflags = stack[0] | EFLAGS_VM;
00253                         context->sp = ((context->sp & 0xFFFF) + 2) & 0xFFFF;
00254                         context->ip = (word)(context->ip + 1);
00255                         return;
00256 
00257                 case 0xCD:      // INT n //
00258                         if (ip[1]==MINIRIGHI_INT)
00259                                 // Exit to system (syscalls are reserved for 32-bit tasks!!!) //
00260                                 auto_kill( 1 );
00261                         else
00262                         {
00263                                 if (ip[1]==DOS_INT)
00264                                 {
00265                                         // Emulate DOS 0x20 interrupt... not yet implemented! //
00266                                         // If you like to do it... here are the source... good luck! :)
00267                                         kprintf("\n\rV86 monitor -> INT 0x20 emulation not yet implemented @CS=%#06x,IP=%#06x",
00268                                                 context->cs, context->ip);
00269                                         kprintf("\n\rKilling task: [%s] (%i)\n\r", get_pname(), get_pid());
00270                                         auto_kill( 1 );
00271                                 }
00272                                 stack -= 3;
00273                                 context->sp = ((context->sp & 0xFFFF) - 6) & 0xFFFF;
00274                                 stack[0] = (word) (context->ip + 2);
00275                                 stack[1] = (word) context->cs;
00276                                 stack[2] = (word) context->eflags;
00277 
00278                                 context->cs = ivt[ip[1] * 2 + 1];
00279                                 context->ip = ivt[ip[1] * 2];
00280                                 return;
00281                         }
00282 
00283                 case 0xCF:      // IRET //
00284                         context->ip = stack[0];
00285                         context->cs = stack[1];
00286                         context->eflags = stack[2] | EFLAGS_VM;
00287                         context->sp = ((context->sp & 0xFFFF) + 6) & 0xFFFF;
00288                         return;
00289 
00290                 case 0xFA:      // CLI //
00291                         context->eflags = (context->eflags & (~EFLAGS_IF));
00292                         context->ip = (word)(context->ip + 1);
00293                         return;
00294 
00295                 case 0xFB:      // STI //
00296                         context->eflags = (context->eflags | EFLAGS_IF);
00297                         context->ip = (word)(context->ip + 1);
00298                         return;
00299 
00300                 case 0xF4:      // HLT //
00301                         // If interrupts are disabled the current task  //
00302                         // wants to disable the CPU, so kill this task! //
00303                         if ((context->eflags & EFLAGS_IF) != EFLAGS_IF)
00304                         {
00305                                 kprintf("\n\rAttempt to disable the CPU.");
00306                                 kprintf("\n\rKilling task: [%s] (%i)\n\r", get_pname(), get_pid());
00307                                 auto_kill( 1 );
00308                         }
00309                         // Wait for an interrupt //
00310                         context->ip = (word)(context->ip + 1);
00311                         enable();
00312                         __asm__("hlt");
00313                         return;
00314 
00315                 default:
00316                         kprintf("\n\rV86 monitor -> %#04x Unknown instruction@CS=%#06x,IP=%#06x",
00317                                 ip[0], context->cs, context->ip);
00318                         context->ip = (word)(context->ip + 1);
00319                         kprintf("\n\rKilling task: [%s] (%i)\n\r", get_pname(), get_pid());
00320                         auto_kill( 1 );
00321                         return;
00322                 }
00323         }
00324 }


Generated on Fri Feb 20 15:32:21 2004 for Minirighi by doxygen1.2.18