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

v86.c

Go to the documentation of this file.
00001 /*!     \file arch/i386/kernel/v86.c
00002  *      \brief Virtual 8086 mode.
00003  *      \author Andrea Righi <drizzt@inwind.it>
00004  *      \date Last update:\n
00005  *              2003-12-16 Andrea Righi:
00006  *                      Added temporary memory operators to get and
00007  *                      free the temporary mapped pages.\n
00008  *      \note Copyright (C) 2003 Andrea Righi
00009  */
00010 
00011 #include <const.h>
00012 #include <string.h>
00013 
00014 #include <arch/i386.h>
00015 #include <arch/interrupt.h>
00016 #include <arch/mem.h>
00017 #include <arch/paging.h>
00018 
00019 #include <kernel/console.h>
00020 #include <kernel/kmalloc.h>
00021 #include <kernel/queue.h>
00022 #include <kernel/kernel_map.h>
00023 #include <kernel/task.h>
00024 
00025 #include <arch/v86.h>
00026 
00027 // --- Virtual 8086 mode task creation -------------------------------- //
00028 
00029 extern int new_pid();
00030 extern queue_t *ready_queue;
00031 extern task_t *curr_task;
00032 
00033 //! The address where the task stack is placed.
00034 #define V86_TASK_STACK_START    0x90000
00035 
00036 //! \brief Create a new virtual-8086 mode process.
00037 //! \param address
00038 //!     The address of the starting point for the task.
00039 //! \param buffer
00040 //!     An address to a buffer where is placed the executable code.
00041 //! \param size
00042 //!     The size of the executable code buffer.
00043 //! \param v86name
00044 //!     The name of the new v86 task.
00045 //! \return A pointer to the new-created task structure.
00046 //! \exception NULL If an error occurs (in particular out-of-memory).
00047 /*!
00048  *      This routine initializes a new v86-task structure, copies
00049  *      the content of the \a buffer to the entry point \a address and
00050  *      then add the new task to the ready queue.
00051  */
00052 task_t *create_v86_process(void *address, void *buffer, size_t size, char *v86name)
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 }
00225 
00226 
00227 // --- Virtual 8086 monitor ------------------------------------------- //
00228 
00229 //! \brief The virtual-8086 monitor.
00230 void v86_monitor()
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:17 2004 for Minirighi by doxygen1.2.18