Back to home page

Quest Cross Reference

 
 

    


0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /*
0019  *
0020  * Interrupt handler code: interrupt_handler.c
0021  *
0022  */
0023 
0024 #include "arch/i386.h"
0025 #include "arch/i386-percpu.h"
0026 #include "arch/i386-measure.h"
0027 #include "kernel.h"
0028 #include "mem/mem.h"
0029 #include "util/elf.h"
0030 #include "fs/filesys.h"
0031 #include "smp/smp.h"
0032 #include "smp/apic.h"
0033 #include "util/printf.h"
0034 #include "util/screen.h"
0035 #include "util/debug.h"
0036 #include "drivers/input/keymap.h"
0037 #include "drivers/input/keyboard.h"
0038 #include "sched/sched.h"
0039 #include "sched/vcpu.h"
0040 
0041 //#define DEBUG_SYSCALL
0042 //#define DEBUG_PIT
0043 
0044 static char kernel_ver[] = "0.1a";
0045 char *kernel_version = kernel_ver;
0046 uint32 tick;                    /* Software clock tick */
0047 
0048 extern uint32 ul_tss[][1024];
0049 
0050 /* Table of functions handling interrupt vectors. */
0051 static vector_handler vector_handlers[256];
0052 
0053 /* Default function filling above table. */
0054 static uint32
0055 default_vector_handler (uint8 vec)
0056 {
0057   return 0;
0058 }
0059 
0060 /* Program an entry in the table. */
0061 void
0062 set_vector_handler (uint8 vec, vector_handler func)
0063 {
0064   vector_handlers[vec] = func;
0065 }
0066 
0067 /* Reset an entry to the default. */
0068 void
0069 clr_vector_handler (uint8 vec)
0070 {
0071   vector_handlers[vec] = default_vector_handler;
0072 }
0073 
0074 /* Obtain a pointer to the handler. */
0075 vector_handler
0076 get_vector_handler (uint8 vec)
0077 {
0078   if (vector_handlers[vec])
0079     return vector_handlers[vec];
0080   else
0081     return default_vector_handler;
0082 }
0083 
0084 /* This is the function invoked by the CPU which then dispatches to
0085  * the handler in the table. */
0086 uint32
0087 dispatch_vector (uint32 vec)
0088 {
0089   vector_handler func = vector_handlers[(uint8) vec];
0090   uint32 v;
0091   //com1_printf ("dispatching vec=0x%x\n", vec);
0092 
0093   if (func)
0094     v = func (vec);
0095   else
0096     v = 0;
0097 
0098   if (!mp_apic_mode && PIC2_BASE_IRQ <= vec && vec < (PIC2_BASE_IRQ + 8))
0099     outb (0x20, 0xA0);          /* send to 8259A slave PIC too */
0100   send_eoi ();
0101   return v;
0102 }
0103 
0104 u8
0105 find_unused_vector (u8 min_prio)
0106 {
0107   u8 i;
0108   if (min_prio < MINIMUM_VECTOR_PRIORITY || min_prio > 0xF)
0109     return 0;
0110   for (i=(min_prio << 4); i < 0xFF; i++) {
0111     if (vector_handlers[i] == default_vector_handler)
0112       return i;
0113   }
0114   return (vector_handlers[i] == default_vector_handler ? i : 0);
0115 }
0116 
0117 /* Duplicate parent TSS -- used with fork */
0118 uint16
0119 duplicate_TSS (uint32 ebp,
0120                uint32 *esp,
0121                uint32 child_eip,
0122                uint32 child_ebp,
0123                uint32 child_esp,
0124                uint32 child_eflags, 
0125                uint32 child_directory)
0126 {
0127 
0128   int i;
0129   descriptor *ad = (descriptor *) KERN_GDT;
0130   quest_tss *pTSS;
0131   uint32 pa;
0132 
0133   pa = alloc_phys_frame (); /* --??-- For now, whole page per tss */
0134   /* --??-- Error checking in the future */
0135 
0136   /* Establish space for a new TSS: +3 declares present and r/w */
0137 
0138   /* Note, we rely on page being initialised to 0 since EAX contains
0139    * return value for child
0140    */
0141 
0142   pTSS = map_virtual_page (pa + 3);
0143 
0144   /* Clear virtual page before use. */
0145   memset (pTSS, 0, 4096);
0146 
0147   /* Search 2KB GDT for first free entry */
0148   for (i = 1; i < 256; i++)
0149     if (!(ad[i].fPresent))
0150       break;
0151 
0152   if (i == 256)
0153     panic ("No free selector for TSS");
0154 
0155   logger_printf ("duplicate_TSS: pTSS=%p i=0x%x esp=%p ebp=%p\n",
0156                  pTSS, i << 3,
0157                  child_esp, child_ebp);
0158 
0159   /* See pp 6-7 in IA-32 vol 3 docs for meanings of these assignments */
0160   ad[i].uLimit0 = 0xFFF;        /* --??-- Right now, a page per TSS */
0161   ad[i].uLimit1 = 0;
0162   ad[i].pBase0 = (u32) pTSS & 0xFFFF;
0163   ad[i].pBase1 = ((u32) pTSS >> 16) & 0xFF;
0164   ad[i].pBase2 = (u32) pTSS >> 24;
0165   ad[i].uType = 0x09;           /* 32-bit tss */
0166   ad[i].uDPL = 0;               /* Only let kernel perform task-switching */
0167   ad[i].fPresent = 1;
0168   ad[i].f0 = 0;
0169   ad[i].fX = 0;
0170   ad[i].fGranularity = 0;       /* Set granularity of tss in bytes */
0171 
0172   pTSS->CR3 = (u32) child_directory;
0173 
0174   /* The child will begin running at the specified EIP */
0175   pTSS->initial_EIP = child_eip;
0176 
0177   /* modify stack in child space */
0178   linear_address_t esp_la; esp_la.raw = child_esp;
0179   pgdir_t child_pgdir;
0180   child_pgdir.dir_pa = child_directory;
0181   child_pgdir.dir_va = map_virtual_page (child_directory | 3);
0182   if (child_pgdir.dir_va == NULL)
0183     panic ("child_pgdir: out of memory");
0184   frame_t esp_frame = pgdir_get_frame (child_pgdir, (void *) (child_esp & (~0xFFF)));
0185   u32 *esp_virt = map_virtual_page (esp_frame | 3);
0186   if (esp_virt == NULL)
0187     panic ("esp_virt: out of memory");
0188   esp_virt[esp_la.offset >> 2] = pTSS->initial_EIP;
0189   unmap_virtual_page (child_pgdir.dir_va);
0190   unmap_virtual_page (esp_virt);
0191 
0192   pTSS->EFLAGS = child_eflags & 0xFFFFBFFF;   /* Disable NT flag */
0193   pTSS->ESP = child_esp;
0194   pTSS->EBP = child_ebp;
0195 
0196   semaphore_init (&pTSS->Msem, 1, 0);
0197 
0198   pTSS->cpu = 0xFF;
0199 
0200   /* Return the index into the GDT for the segment */
0201   return i << 3;
0202 }
0203 
0204 char *exception_messages[] = {
0205   "Division Error",
0206   "Debug",
0207   "Non Maskable Interrupt",
0208   "Breakpoint",
0209   "Into Detected Overflow",
0210   "Out of Bounds",
0211   "Invalid Opcode",
0212   "No Coprocessor",
0213   "Double Fault",
0214   "Coprocessor Segment Overrun",
0215   "Bad TSS",
0216   "Segment Not Present",
0217   "Stack Fault",
0218   "General Protection Fault",
0219   "Page Fault",
0220   "Unknown Interrupt",
0221   "Coprocessor Fault",
0222   "Alignment Check",
0223   "Machine Check",
0224   "Reserved",
0225   "Reserved",
0226   "Reserved",
0227   "Reserved",
0228   "Reserved",
0229   "Reserved",
0230   "Reserved",
0231   "Reserved",
0232   "Reserved",
0233   "Reserved",
0234   "Reserved",
0235   "Reserved",
0236   "Reserved"
0237 };
0238 
0239 /* Generic CPU fault exception handler -- dumps some info to the
0240  * screen and serial port, then goes into debugger. */
0241 extern void
0242 handle_interrupt (u32 edi, u32 esi, u32 ebp, u32 _esp, u32 ebx, u32 edx, u32 ecx, u32 eax,
0243                   u32 fs_gs, u32 ds_es, u32 ulInt, u32 ulCode,
0244                   u32 eip, u32 cs, u32 eflags, u32 esp, u32 ss)
0245 {
0246   u32 cr0, cr2, cr3;
0247   u16 tr, fs, ds;
0248 
0249   asm volatile ("movl %%cr0, %%eax\n"
0250                 "movl %%eax, %0\n"
0251                 "movl %%cr2, %%eax\n"
0252                 "movl %%eax, %1\n"
0253                 "movl %%cr3, %%eax\n"
0254                 "movl %%eax, %2\n"
0255                 "xorl %%eax, %%eax\n"
0256                 "str  %%ax\n"
0257                 "movw %%ax, %3\n"
0258                 "movw %%fs, %%ax\n"
0259                 "movw %%ax, %4\n"
0260                 "movw %%ds, %%ax\n"
0261                 "movw %%ax, %5\n"
0262                 :"=m" (cr0), "=m" (cr2), "=m" (cr3),
0263                  "=m" (tr), "=m" (fs), "=m" (ds):);
0264 
0265   if ((cs & 0x3) == 0) {
0266     /* same priv level: ESP and SS were not pushed onto stack by interrupt transfer */
0267     asm volatile ("movl %%ss, %0":"=r" (ss));
0268     esp = _esp;
0269   }
0270 
0271   spinlock_lock (&screen_lock);
0272   _putchar ('I');
0273   _putx (ulInt);
0274   _putchar (' ');
0275   _putchar ('c');
0276   _putx (ulCode);
0277   _putchar (' ');
0278   if (ulInt < 32)
0279     _print (exception_messages[ulInt]);
0280   _putchar ('\n');
0281 
0282 #ifndef ENABLE_GDBSTUB
0283 #define _putchar com1_putc
0284 #define _putx com1_putx
0285 #define _print com1_puts
0286 #define _printf com1_printf
0287 #endif
0288 
0289   _printf ("INT=%.2X CODE=%.8X %s\n", 
0290            ulInt, ulCode, exception_messages[ulInt]);
0291   _printf ("EAX=%.8X ESI=%.8X\n", eax, esi);
0292   _printf ("EBX=%.8X EDI=%.8X\n", ebx, edi);
0293   _printf ("ECX=%.8X EBP=%.8X\n", ecx, ebp);
0294   _printf ("EDX=%.8X ESP=%.8X\n", edx, esp);
0295   _printf ("EFL=%.8X EIP=%.8X\n", eflags, eip);
0296   _printf ("CR0=%.8X CR2=%.8X\nCR3=%.8X TR=%.4X\n", cr0, cr2, cr3, tr);
0297   _printf (" CS=%.4X SS=%.4X DS=%.4X FS=%.4X\n", cs, ss, ds, fs);
0298   _printf ("CURRENT=0x%X\n", percpu_read (current_task));
0299   stacktrace_frame (esp, ebp);
0300 
0301 #ifndef ENABLE_GDBSTUB
0302 #undef _putx
0303 #undef _putchar
0304 #undef _print
0305 #undef _printf
0306 #endif
0307 
0308   spinlock_unlock (&screen_lock);
0309 
0310   if (ulInt < 0x20)
0311     /* unhandled exception - die */
0312     crash_debug ("Unhandled exception");
0313 
0314   send_eoi ();
0315 }
0316 
0317 static int
0318 user_putchar (int ch, int attribute)
0319 {
0320   static int x, y;
0321 
0322   if (ch == '\n') {
0323     x = 0;
0324     y++;
0325 
0326     if (y > 24) {
0327       memcpy (pchVideo, pchVideo + 160, 24 * 160);
0328       memset (pchVideo + (24 * 160), 0, 160);
0329       y = 24;
0330     }
0331     return (int) (unsigned char) ch;
0332   }
0333 
0334   if (y * 160 + x * 2 >= 0x1000) return ch;
0335 
0336   pchVideo[y * 160 + x * 2] = ch;
0337   pchVideo[y * 160 + x * 2 + 1] = attribute;
0338   x++;
0339 
0340   if (y * 160 + x * 2 >= 0x1000) return ch;
0341 
0342   pchVideo[y * 160 + x * 2] = ' ';
0343   pchVideo[y * 160 + x * 2 + 1] = attribute;
0344 
0345   /* Move cursor */
0346   outb (0x0E, 0x3D4);           /* CRTC Cursor location high index */
0347   outb ((y * 80 + x) >> 8, 0x3D5);      /* CRTC Cursor location high data */
0348   outb (0x0F, 0x3D4);           /* CRTC Cursor location low index */
0349   outb ((y * 80 + x) & 0xFF, 0x3D5);    /* CRTC Cursor location low data */
0350 
0351   return (int) (unsigned char) ch;
0352 }
0353 
0354 static void
0355 _user_putchar_attr_4 (char c)
0356 {
0357   user_putchar (c, 4);
0358 }
0359 
0360 static void
0361 splash_screen (void)
0362 {
0363   int _uname (char *);
0364   u32 _meminfo (u32, u32);
0365   u32 free = _meminfo (0, 0);
0366   char vers[80];
0367 
0368   _uname (vers);
0369 
0370   fun_printf (_user_putchar_attr_4,
0371               "**** Quest kernel version: %s *****"
0372               "   //---\\ \\\\  \\ //-- //--\\ \\\\---\\ \n",
0373               vers);
0374 
0375   fun_printf (_user_putchar_attr_4,
0376               "* Copyright Boston University, 2010 *"
0377               "   ||   | ||  | ||-- \\\\--\\   || \n");
0378 
0379   fun_printf (_user_putchar_attr_4,
0380               "******** 0x%.08X bytes free ******"
0381               "   \\\\__\\_  \\\\_/ \\\\__ \\\\__/   || \n",
0382               free);
0383 }
0384 
0385 /* Syscalls */
0386 static u32
0387 syscall_putchar (u32 eax, u32 ebx)
0388 {
0389   static bool first = TRUE;
0390 
0391   if (first) { splash_screen (); first = FALSE; }
0392   user_putchar (ebx, 7);
0393   return 0;
0394 }
0395 
0396 static u32
0397 syscall_usleep (u32 eax, u32 ebx)
0398 {
0399   sched_usleep (ebx);
0400   return ebx;
0401 }
0402 
0403 struct syscall {
0404   u32 (*func) (u32, u32);
0405 };
0406 struct syscall syscall_table[] = {
0407   { .func = syscall_putchar },
0408   { .func = syscall_usleep },
0409 };
0410 #define NUM_SYSCALLS (sizeof (syscall_table) / sizeof (struct syscall))
0411 
0412 u32
0413 handle_syscall0 (u32 eax, u32 ebx)
0414 {
0415   u32 res;
0416   lock_kernel ();
0417   if (eax < NUM_SYSCALLS)
0418     res = syscall_table[eax].func (eax, ebx);
0419   else
0420     res = 0;
0421   unlock_kernel ();
0422   return res;
0423 }
0424 
0425 
0426 /* Syscall: fork
0427  *
0428  * esp argument used to find info about parent's eip and other
0429  * registers inherited by child
0430  */
0431 task_id
0432 _fork (uint32 ebp, uint32 *esp)
0433 {
0434 
0435   uint16 child_gdt_index;
0436   void *phys_addr;
0437   uint32 *virt_addr;
0438   uint32 priority;
0439   uint32 eflags, eip, this_esp, this_ebp;
0440 
0441 #ifdef DEBUG_SYSCALL
0442   com1_printf ("_fork (%X, %p)\n", ebp, esp);
0443 #endif
0444   lock_kernel ();
0445 
0446   /* 
0447    * This ugly bit of assembly is designed to obtain the value of EIP
0448    * in the parent and return from the `call 1f' in the child.
0449    */
0450 
0451   asm volatile ("call 1f\n"
0452                 "movl $0, %0\n"
0453                 "jmp 2f\n"
0454                 "1:\n"
0455                 "movl (%%esp), %0\n"
0456                 "addl $4, %%esp\n"
0457                 "2:\n":"=r" (eip):);
0458 
0459   if (eip == 0) {
0460     /* We are in the child process now */
0461     unlock_kernel ();
0462     /* don't need to reload per-CPU segment here because we are going
0463      * straight to userspace */
0464     return 0;
0465   }
0466 
0467   asm volatile ("movl %%ebp, %0\n"
0468                 "movl %%esp, %1\n"
0469                 "pushfl\n"
0470                 "pop %2\n":"=r" (this_ebp), "=r" (this_esp), "=r" (eflags):);
0471 
0472   /* Create a new address space cloned from this one */
0473 
0474   phys_addr = get_pdbr ();      /* Parent page dir base address */
0475   virt_addr = map_virtual_page ((uint32) phys_addr | 3);        /* Temporary virtual address */
0476 
0477   if (virt_addr == NULL)
0478     panic ("_fork: virt_addr: out of memory");
0479 
0480   pgdir_t parentpgd = { .dir_pa = (frame_t) phys_addr,
0481                         .dir_va = (pgdir_entry_t *) virt_addr };
0482 
0483   pgdir_t childpgd = clone_page_directory (parentpgd);
0484   if (childpgd.dir_pa == -1)
0485     panic ("_fork: clone_page_directory: failed");
0486 
0487   unmap_virtual_page (parentpgd.dir_va);
0488   unmap_virtual_page (childpgd.dir_va);
0489 
0490   /* Create a child task which is the same as this task except that it will
0491    * begin running at the program point after `call 1f' in the above inline asm. */
0492 
0493   child_gdt_index =
0494     duplicate_TSS (ebp, esp, eip, this_ebp, this_esp, eflags, childpgd.dir_pa);
0495 
0496   /* Inherit priority from parent */
0497   priority = lookup_TSS (child_gdt_index)->priority =
0498     lookup_TSS (str ())->priority;
0499 
0500   wakeup (child_gdt_index);
0501 
0502   /* --??-- Duplicate any other parent resources as necessary */
0503 
0504   unlock_kernel ();
0505 
0506   return child_gdt_index;       /* Use this index for child ID for now */
0507 }
0508 
0509 
0510 char *
0511 strncpy (char *s1, const char *s2, int length)
0512 {
0513 
0514   while ((length--) && (*s1++ = *s2++));
0515 
0516   if (length < 0)
0517     *(s1 - 1) = '\0';
0518 
0519   return s1;
0520 }
0521 
0522 
0523 /* --??-- TODO: Rewrite _exec to create a temporary new address space
0524  before overwriting the old one in case of errors */
0525 
0526 /* Syscall: _exec: replace address space of caller with new memory areas, in part
0527  * populated by program image on disk
0528  */
0529 int
0530 _exec (char *filename, char *argv[], uint32 *curr_stack)
0531 {
0532 
0533   uint32 *plPageDirectory = map_virtual_page ((uint32) get_pdbr () | 3);
0534   uint32 *plPageTable;
0535   uint32 pStack;
0536   Elf32_Ehdr *pe = (Elf32_Ehdr *) 0xFF400000;   /* 4MB below KERN_STK virt address */
0537   Elf32_Phdr *pph;
0538   void *pEntry;
0539   int filesize, orig_filesize;
0540   /* Temporary storage for frame pointers for a file image up to 4MB
0541      discounting bss */
0542   uint32 phys_addr = alloc_phys_frame () | 3;
0543   /* frame_map is a 1024 bit bitmap to mark frames not needed for 
0544      file of specific size when not all sections need loading into RAM */
0545   uint32 frame_map[32];
0546   uint32 *frame_ptr = map_virtual_page (phys_addr);
0547   uint32 *tmp_page;
0548   int i, j, c;
0549   char command_args[80];
0550   char filename_bak[256];
0551 
0552   if (!argv || !argv[0]) {
0553     BITMAP_SET (mm_table, phys_addr >> 12);
0554     unmap_virtual_page (plPageDirectory);
0555     unmap_virtual_page (frame_ptr);
0556     return -1;
0557   }
0558 
0559   lock_kernel ();
0560 #ifdef DEBUG_SYSCALL
0561   com1_printf ("_exec (%s, [%s,...], %p)\n", filename, argv[0], curr_stack);
0562 #endif
0563 
0564   /* --??-- Checks should be added here for valid argv[0] etc...
0565      Allocate space for argument vector passed via exec call.
0566      Right now, assume max size for prog name and arguments.
0567    */
0568   strncpy (command_args, argv[0], 80);
0569 
0570   /*
0571    * This is a bug fix. We have to backup the file name before
0572    * erasing the old address space because it will be used later
0573    * and will already be gone with the old stack at that time.
0574    */
0575   strncpy (filename_bak, filename, 256);
0576 
0577 #ifdef DEBUG_SYSCALL
0578   com1_printf ("_exec: vfs_dir\n");
0579 #endif
0580   /* Find file on disk -- essentially a basic open call */
0581   if ((filesize = vfs_dir (filename)) < 0) {    /* Error */
0582     BITMAP_SET (mm_table, phys_addr >> 12);
0583     unmap_virtual_page (plPageDirectory);
0584     unmap_virtual_page (frame_ptr);
0585     unlock_kernel ();
0586     return -1;
0587   }
0588 
0589   /* Free frames used for old address space before _exec was called
0590    *
0591    * Reuse page directory
0592    */
0593 #ifdef DEBUG_SYSCALL
0594   com1_printf ("_exec: setup page directory\n");
0595 #endif
0596   for (i = 0; i < 1019; i++) {  /* Skip freeing kernel pg table mapping and
0597                                    kernel stack space. */
0598     if (plPageDirectory[i]) {   /* Present in currrent address space */
0599       tmp_page = map_virtual_page (plPageDirectory[i] | 3);
0600       for (j = 0; j < 1024; j++) {
0601         if (tmp_page[j]) {      /* Present in current address space */
0602           if ((j < 0x200) || (j > 0x20F) || i) {        /* --??-- Don't free
0603                                                            temp video memory */
0604             BITMAP_SET (mm_table, tmp_page[j] >> 12);   /* Free frame */
0605             tmp_page[j] = 0;
0606           }
0607         }
0608       }
0609       unmap_virtual_page (tmp_page);
0610       BITMAP_SET (mm_table, plPageDirectory[i] >> 12);
0611       plPageDirectory[i] = 0;
0612     }
0613   }
0614 
0615   /* Allocate space for new page table */
0616   plPageDirectory[0] = alloc_phys_frame () | 7;
0617   plPageTable = map_virtual_page (plPageDirectory[0]);
0618   memset (plPageTable, 0, 0x1000);
0619 
0620   for (i = 0; i < filesize; i += 4096) {
0621     frame_ptr[i >> 12] = alloc_phys_frame () | 3;
0622   }
0623 
0624   /* Temporary dir entry for mapping file image into virtual address space */
0625   plPageDirectory[(uint32) pe >> 22] = phys_addr;
0626 
0627   flush_tlb_all ();
0628 
0629 #ifdef DEBUG_SYSCALL
0630   com1_printf ("_exec: vfs read\n");
0631 #endif
0632   /* Read into virtual address corresponding to plPageDirectory[1021] */
0633   orig_filesize = filesize;
0634   filesize = vfs_read (filename_bak, (void *) pe, orig_filesize);
0635   if (filesize != orig_filesize) {
0636     printf ("expected filesize=%d got filesize=%d\n", orig_filesize, filesize);
0637     panic ("File size mismatch on read");
0638   }
0639 
0640   pph = (void *) pe + pe->e_phoff;
0641   pEntry = (void *) pe->e_entry;
0642 
0643   memset (frame_map, 0, 32 * sizeof (uint32));
0644 
0645 #ifdef DEBUG_SYSCALL
0646   com1_printf ("_exec: walk ELF header\n");
0647 #endif
0648   /* Walk ELF header */
0649   for (i = 0; i < pe->e_phnum; i++) {
0650     if (pph->p_type == PT_LOAD) {
0651       if ((pph->p_offset & 0xFFF) != (pph->p_vaddr & 0xFFF))
0652         panic ("Misalignment in program header");
0653 
0654       /* map pages loaded from file */
0655       c = ((pph->p_offset + pph->p_filesz - 1) >> 12) - (pph->p_offset >> 12) + 1;      /* #pages to load for module */
0656 
0657       for (j = 0; j < c; j++) {
0658         if (j == c - 1) {
0659           /* Page is the last of this header, and needs to be
0660              zero-padded, but unfortunately the page may be
0661              shared with the next phdr.  We copy it to avoid any
0662              conflicts. */
0663           uint32 frame = alloc_phys_frame ();
0664           char *buf = map_virtual_page (frame | 3);
0665           int partial = (pph->p_offset + pph->p_filesz) & 0xFFF;
0666 
0667           memcpy (buf, (char *) pe + (pph->p_offset & ~0xFFF) +
0668                   (j << 12), partial);
0669           memset (buf + partial, 0, 0x1000 - partial);
0670 
0671           unmap_virtual_page (buf);
0672 
0673           plPageTable[((uint32) pph->p_vaddr >> 12) + j] = frame | 7;
0674         } else {
0675           BITMAP_SET (frame_map, j + (pph->p_offset >> 12));
0676           plPageTable[((uint32) pph->p_vaddr >> 12) + j] =
0677             frame_ptr[j + (pph->p_offset >> 12)] | 7;
0678         }
0679       }
0680 
0681       /* map additional zeroed pages */
0682       c = ((pph->p_offset + pph->p_memsz - 1) >> 12) - (pph->p_offset >> 12) + 1;       /* page limit to clear for module */
0683 
0684       /* Allocate space for bss section.  Use temporary virtual memory for
0685        * memset call to clear physical frame(s)
0686        */
0687       for (; j < c; j++) {
0688         uint32 page_frame = (uint32) alloc_phys_frame ();
0689         void *virt_page = map_virtual_page (page_frame | 3);
0690         plPageTable[((uint32) pph->p_vaddr >> 12) + j] = page_frame | 7;
0691         memset (virt_page, 0, 0x1000);
0692         unmap_virtual_page (virt_page);
0693       }
0694     }
0695 
0696     pph = (void *) pph + pe->e_phentsize;
0697   }
0698 
0699   /* Deallocate unsued frames for file that were not loaded with contents */
0700   for (i = 0; i < filesize; i += 4096) {
0701     if (!BITMAP_TST (frame_map, i >> 12))
0702       BITMAP_SET (mm_table, frame_ptr[i >> 12] >> 12);
0703   }
0704 
0705   /* --??-- temporarily map video memory into exec()ed process */
0706   for (i = 0; i < 16; i++)
0707     plPageTable[0x200 + i] = 0xA0000 | (i << 12) | 7;
0708 
0709   /* map stack and clear its contents -- Here, setup 16 pages for stack */
0710   for (i = 0; i < 16; i++) {
0711     pStack = alloc_phys_frame ();
0712     plPageTable[1023 - i] = pStack | 7;
0713     invalidate_page ((void *) ((1023 - i) << 12));
0714   }
0715   memset ((void *) 0x3F0000, 0, 0x10000);       /* Clear 16 page stack */
0716 
0717   plPageDirectory[1021] = 0;
0718   unmap_virtual_page (plPageDirectory);
0719   unmap_virtual_page (plPageTable);
0720   unmap_virtual_page (frame_ptr);
0721   BITMAP_SET (mm_table, phys_addr >> 12);
0722 
0723   flush_tlb_all ();
0724 
0725   /* Copy command-line arguments to top of new stack */
0726   memcpy ((void *) (0x400000 - 80), command_args, 80);
0727 
0728   /* Push onto stack argument vector for when we call _start in our "libc"
0729      library. Here, we work with user-level virtual addresses for when we
0730      return to user. */
0731   *(uint32 *) (0x400000 - 84) = 0;    /* argv[1] -- not used right now */
0732   *(uint32 *) (0x400000 - 88) = 0x400000 - 80;        /* argv[0] */
0733   *(uint32 *) (0x400000 - 92) = 0x400000 - 88;        /* argv */
0734   *(uint32 *) (0x400000 - 96) = 1;    /* argc -- hard-coded right now */
0735 
0736   /* Dummy return address placed here for the simulated "call" to our
0737      library */
0738   *(uint32 *) (0x400000 - 100) = 0;   /* NULL return address -- never
0739                                            used */
0740 
0741   /* Patch up kernel stack with new values so that we can start new program
0742      on return to user-level  */
0743   curr_stack[0] = 0x00230023;   /* fs/gs selectors */
0744   curr_stack[1] = 0x00230023;   /* ds/es selectors */
0745   curr_stack[2] = (uint32) pEntry;        /* eip */
0746   curr_stack[3] = 0x1B;         /* cs selector */
0747   /* --??-- Temporarily set IOPL 3 in exec()ed program for VGA/keyboard testing */
0748   curr_stack[4] = F_1 | F_IF | 0x3000;  /* EFLAGS */
0749   curr_stack[5] = 0x400000 - 100;       /* -100 after pushing command-line args */
0750   curr_stack[6] = 0x23;         /* ss selector */
0751 
0752   unlock_kernel ();
0753   return 0;
0754 }
0755 
0756 /* Syscall: getchar / getcode */
0757 uint
0758 _getchar (uint ebx)
0759 {
0760 
0761   uint c = 0;
0762 
0763   lock_kernel ();
0764 
0765   if (ebx == 0)
0766     c = keymap_getchar ();
0767   else {
0768     key_event e;
0769     uint i;
0770 
0771     keyboard_8042_next (&e);
0772     for (i=0; i<KEY_EVENT_MAX; i++) {
0773       if (e.keys[i].latest) {
0774         c = e.keys[i].scancode;
0775         if (e.keys[i].release)
0776           c |= 0x80;            /* restore "break" code */
0777         break;
0778       }
0779     }
0780   }
0781   
0782   unlock_kernel ();
0783 
0784   return c;
0785 }
0786 
0787 /* Syscall: switch to other task -- deprecated */
0788 void
0789 _switch_to (uint32 pid)
0790 {
0791 
0792   /* This would cause a #PF in SMP situation, usually: */
0793   /*******************
0794    * jmp_gate (pid); *
0795    *******************/
0796   
0797   /* Stick to this */
0798   uint32 _waitpid(task_id);
0799   _waitpid(pid);
0800 }
0801 
0802 
0803 /* Syscall: open --??-- Flags not used for now...a crude open call as
0804  * it stands  */
0805 int
0806 _open (char *pathname, int flags)
0807 {
0808   lock_kernel ();
0809   //logger_printf ("_open (\"%s\", 0x%x)\n", pathname, flags);
0810   int res = vfs_dir (pathname);
0811   unlock_kernel ();
0812   return res;
0813 }
0814 
0815 /* Syscall: read --??-- proess-global file handle */
0816 int
0817 _read (char *pathname, void *buf, int count)
0818 {
0819   lock_kernel ();
0820   //logger_printf ("_read (\"%s\", %p, 0x%x)\n", pathname, buf, count);
0821   int res = vfs_read (pathname, buf, count);
0822   unlock_kernel ();
0823   return res;
0824 }
0825 
0826 /* Syscall: uname */
0827 int
0828 _uname (char *name)
0829 {
0830 
0831   /* --??-- Error check in the future */
0832   memcpy (name, kernel_version, sizeof (kernel_ver));
0833 
0834   return 0;
0835 }
0836 
0837 /* Syscalls: meminfo, shared mem alloc, attach, detach, and free. */
0838 uint32
0839 _meminfo (uint32 eax, uint32 edx)
0840 {
0841 
0842   int i, j = 0;
0843 
0844   uint32 frame;
0845   uint32 pgd;
0846   uint32 *pgd_virt, *ptab1_virt;
0847   uint32 addr;
0848 
0849   switch (eax) {
0850   case 0:
0851     for (i = 0; i < mm_limit; i++)
0852       if (BITMAP_TST (mm_table, i))
0853         j++;
0854 
0855     return j << 12;
0856   case 1:{
0857       void *virt;
0858       /* shared_mem_alloc() */
0859       frame = alloc_phys_frame ();
0860       if (frame < 0)
0861         return -1;
0862       /* use 'frame' as identifier of shared memory region
0863        * obvious security flaw -- but its just for testing, atm */
0864       virt = map_virtual_page (frame | 3);
0865       memset ((void *) virt, 0, 0x1000);
0866       unmap_virtual_page (virt);
0867       return frame;
0868     }
0869   case 2:{
0870       /* shared_mem_attach() */
0871       frame = edx;
0872       if ((frame >> 12) >= mm_limit)
0873         /* invalid frame */
0874         return -1;
0875       if (BITMAP_TST (mm_table, frame >> 12))
0876         /* unallocated frame */
0877         return -1;
0878       /* Now find a userspace page to map to this frame */
0879       pgd = (uint32) get_pdbr ();
0880       pgd_virt = map_virtual_page (pgd | 3);
0881       ptab1_virt = map_virtual_page (pgd_virt[0] | 3);
0882       /* Going to assume I can just use the first page table for this */
0883       addr = -1;
0884       for (i = 1; i < 1024; i++) {
0885         if ((ptab1_virt[i] & 0x1) == 0) {
0886           /* found empty entry */
0887           ptab1_virt[i] = frame | 7;
0888           addr = i << 12;
0889           break;
0890         }
0891       }
0892       unmap_virtual_page (ptab1_virt);
0893       unmap_virtual_page (pgd_virt);
0894       return addr;
0895     }
0896   case 3:{
0897       /* shared_mem_detach() */
0898       i = (edx >> 12) & 0x3FF;  /* index into page table */
0899       pgd = (uint32) get_pdbr ();
0900       pgd_virt = map_virtual_page (pgd | 3);
0901       ptab1_virt = map_virtual_page (pgd_virt[0] | 3);
0902       ptab1_virt[i] = 0;
0903       unmap_virtual_page (ptab1_virt);
0904       unmap_virtual_page (pgd_virt);
0905       return 0;
0906     }
0907   case 4:{
0908       /* shared_mem_free() */
0909       frame = edx;
0910       /* again, this is insecure atm */
0911       BITMAP_SET (mm_table, frame >> 12);
0912       return 0;
0913     }
0914   default:
0915     return -1;
0916   }
0917 }
0918 
0919 /* Syscall: time */
0920 uint32
0921 _time (void)
0922 {
0923 
0924   return tick;
0925 }
0926 
0927 /* ACPI System Control Interrupt -- IRQ 9 usually */
0928 extern uint32
0929 _interrupt29 (void)
0930 {
0931   extern ACPI_OSD_HANDLER acpi_service_routine;
0932   extern void *acpi_service_routine_context;
0933   if (acpi_service_routine)
0934     return acpi_service_routine (acpi_service_routine_context);
0935   else
0936     return 0;
0937 }
0938 
0939 /* LAPIC timer handler -- used to implement scheduler quantum on a
0940  * per-CPU basis. */
0941 extern void
0942 _interrupt3e (void)
0943 {
0944   uint8 phys_id = get_pcpu_id ();
0945   send_eoi ();
0946   LAPIC_start_timer (cpu_bus_freq / QUANTUM_HZ); /* setup next tick */
0947 
0948   lock_kernel ();
0949 
0950   if (str () != idleTSS_selector[phys_id]) {
0951     /* CPU was not idling */
0952     /* add the current task to the back of the run queue */
0953     wakeup (str ());
0954   }
0955 
0956   /* with kernel locked, go ahead and schedule */
0957   schedule ();
0958   unlock_kernel ();
0959 }
0960 
0961 /* IRQ0 system timer interrupt handler: simply updates the system clock
0962    tick for now */
0963 void
0964 _timer (void)
0965 {
0966   void begin_kernel_threads (void);
0967   extern volatile bool mp_enabled;
0968   extern bool mp_ISA_PC;
0969   void net_tmr_process (void);
0970 
0971 #ifdef DEBUG_PIT
0972   com1_printf ("tick: %u\n", tick);
0973 #endif
0974 
0975   tick++;
0976 
0977   /* Need to issue an EOI "end of interrupt" to be ready for further
0978      interrupts */
0979   send_eoi ();
0980 
0981   if (mp_enabled) {
0982     lock_kernel ();
0983 
0984     /* check sleeping processes */
0985     process_sleepqueue ();
0986 
0987 #if 1
0988     extern void vcpu_dump_stats (void);
0989     if ((tick & 0x1FF) == 0)
0990       vcpu_dump_stats ();
0991 #endif
0992 
0993     unlock_kernel ();
0994 
0995 #ifdef GDBSTUB_TCP
0996     { 
0997       extern bool break_requested; 
0998       if (break_requested) {
0999         break_requested = FALSE;
1000         BREAKPOINT ();
1001       }
1002     }      
1003 #endif
1004 #if 0
1005     {
1006       void umsc_tmr_test (void);
1007       umsc_tmr_test ();
1008     }
1009 #endif
1010   }
1011 
1012   if (!mp_ISA_PC) {
1013     if (!mp_enabled)
1014       com1_printf ("timer: enabling scheduling\n");
1015     mp_enabled = 1;
1016     begin_kernel_threads ();
1017   } else {
1018     begin_kernel_threads ();    /* has internal flag */
1019     /* On an ISA PC, must use PIT IRQ for scheduling */
1020     if (str () == idleTSS_selector[0]) {
1021       /* CPU was idling */
1022       schedule ();
1023       /* if returned, go back to idling */
1024       return;
1025     } else {
1026       /* add the current task to the back of the run queue */
1027       wakeup (str ());
1028       /* with kernel locked, go ahead and schedule */
1029       schedule ();
1030     }
1031   }
1032 }
1033 
1034 /* Syscall: _exit */
1035 void
1036 __exit (int status)
1037 {
1038 
1039   void *phys_addr;
1040   uint32 *virt_addr;
1041   uint32 *tmp_page;
1042   int i, j;
1043   task_id tss;
1044   descriptor *ad = (descriptor *) KERN_GDT;
1045   uint32 *kern_page_table = (uint32 *) KERN_PGT;
1046   quest_tss *ptss;
1047   int waiter;
1048 
1049   lock_kernel ();
1050 
1051   /* For now, simply free up memory used by calling process address
1052      space.  We will pass the exit status to the parent process in the
1053      future. */
1054 
1055   phys_addr = get_pdbr ();
1056   virt_addr = map_virtual_page ((uint32) phys_addr | 3);
1057 
1058   /* Free user-level virtual address space */
1059   for (i = 0; i < 1023; i++) {
1060     if (virt_addr[i]            /* Free page directory entry */
1061         &&!(virt_addr[i] & 0x80)) {     /* and not 4MB page */
1062       tmp_page = map_virtual_page (virt_addr[i] | 3);
1063       for (j = 0; j < 1024; j++) {
1064         if (tmp_page[j]) {      /* Free frame */
1065           if ((j < 0x200) || (j > 0x20F) || i) {        /* --??-- Skip releasing
1066                                                            video memory */
1067             BITMAP_SET (mm_table, tmp_page[j] >> 12);
1068           }
1069         }
1070       }
1071       unmap_virtual_page (tmp_page);
1072       BITMAP_SET (mm_table, virt_addr[i] >> 12);
1073     }
1074   }
1075   BITMAP_SET (mm_table, (uint32) phys_addr >> 12);    /* Free up page for page directory */
1076   unmap_virtual_page (virt_addr);
1077 
1078   /* Destroyed current page directory, so everything that happens
1079    * until the next task switch must work within the current TLB. */
1080 
1081   /* --??-- Need to release TSS used by exiting process. Here, we need a way
1082      to index GDT based on current PID returned from original fork call.
1083 
1084      NOTE: Here' we shouldn't really release the TSS until the parent has
1085      been able to check the status of the child... */
1086 
1087   tss = str ();
1088   ltr (0);
1089 
1090   /* Remove space for tss -- but first we need to construct the linear
1091      address of where it is in memory from the TSS descriptor */
1092   ptss = lookup_TSS (tss);
1093 
1094   /* All tasks waiting for us now belong on the runqueue. */
1095   while ((waiter = queue_remove_head (&ptss->waitqueue)))
1096     wakeup (waiter);
1097 
1098   BITMAP_SET (mm_table,
1099               kern_page_table[((uint32) ptss >> 12) & 0x3FF] >> 12);
1100 
1101   /* Remove tss descriptor entry in GDT */
1102   memset (ad + (tss >> 3), 0, sizeof (descriptor));
1103 
1104   unmap_virtual_page (ptss);
1105 
1106   schedule ();
1107   /* never return */
1108   panic ("__exit: unreachable");
1109 }
1110 
1111 /* Syscall: waitpid */
1112 extern uint32
1113 _waitpid (task_id pid)
1114 {
1115 
1116   quest_tss *ptss;
1117 
1118   lock_kernel ();
1119 
1120   ptss = lookup_TSS (pid);
1121 
1122   if (ptss) {
1123     /* Destination task exists.  Add ourselves to the queue of tasks
1124        waiting for it. */
1125     queue_append (&ptss->waitqueue, str ());
1126     /* We have to go to sleep now -- find another task. */
1127     schedule ();
1128     unlock_kernel ();
1129     /* We have been woken up (see __exit).  Return successfully. */
1130     return 0;
1131   } else {
1132     unlock_kernel ();
1133     /* Destination task does not exist.  Return an error. */
1134     return -1;
1135   }
1136 }
1137 
1138 
1139 extern int
1140 _sched_setparam (task_id pid, const struct sched_param *p)
1141 {
1142 
1143   quest_tss *ptss;
1144 
1145   lock_kernel ();
1146 
1147   ptss = lookup_TSS (pid);
1148 
1149   if (ptss) {
1150     if (p->sched_priority == -1)        /* Assume window-constrained task */
1151       ptss->priority = (p->k * p->T) / p->m;
1152     else
1153       ptss->priority = p->sched_priority;
1154 
1155     wakeup (str ());
1156 
1157     schedule ();
1158     unlock_kernel ();
1159 
1160     return 0;
1161 
1162   } else
1163     unlock_kernel ();
1164   /* Destination task does not exist.  Return an error. */
1165   return -1;
1166 }
1167 
1168 #if 0
1169 static void *tlb_shootdown_page = NULL;
1170 static uint32 tlb_shootdown_count = 0;
1171 static spinlock tlb_shootdown_lock = SPINLOCK_INIT;
1172 
1173 extern void
1174 invlpg_shootdown (void *va)
1175 {
1176 
1177   invalidate_page (va);
1178   send_ipi (0xFF,
1179             LAPIC_ICR_LEVELASSERT |
1180             LAPIC_ICR_DS_ALLEX | LAPIC_ICR_DM_LOGICAL | 0xfd);
1181 
1182 }
1183 
1184 extern uint32
1185 invlpg_handler (uint8 vec)
1186 {
1187   asm volatile ("invlpg %0"::"m" (*(char *) tlb_shootdown_page));
1188   asm volatile ("lock decl %0"::"m" (tlb_shootdown_count));
1189   return 0;
1190 }
1191 
1192 extern uint32
1193 flush_tlb_handler (uint8 vec)
1194 {
1195   asm volatile ("movl %%cr3, %%eax\n"
1196                 "movl %%eax, %%cr3\n"
1197                 "lock decl %0"::"m" (tlb_shootdown_count));
1198   return 0;
1199 }
1200 
1201 #endif
1202 
1203 /* Initialize the vector handling table. */
1204 extern void
1205 init_interrupt_handlers (void)
1206 {
1207   int i;
1208   for (i = 0; i < 256; i++)
1209     vector_handlers[i] = default_vector_handler;
1210   /************************************************
1211    * set_vector_handler(0xfd, invlpg_handler);    *
1212    * set_vector_handler(0xfe, flush_tlb_handler); *
1213    ************************************************/
1214 }
1215 
1216 /* 
1217  * Local Variables:
1218  * indent-tabs-mode: nil
1219  * mode: C
1220  * c-file-style: "gnu"
1221  * c-basic-offset: 2
1222  * End: 
1223  */
1224 
1225 /* vi: set et sw=2 sts=2: */