Fork me on GitHub

root/mm/fault.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. send_sigsegv
  2. page_protection_violation
  3. page_not_present
  4. do_page_fault

   1 /*
   2  * fiwix/mm/fault.c
   3  *
   4  * Copyright 2018-2021, Jordi Sanfeliu. All rights reserved.
   5  * Distributed under the terms of the Fiwix License.
   6  */
   7 
   8 #include <fiwix/kernel.h>
   9 #include <fiwix/sigcontext.h>
  10 #include <fiwix/asm.h>
  11 #include <fiwix/mm.h>
  12 #include <fiwix/process.h>
  13 #include <fiwix/traps.h>
  14 #include <fiwix/sched.h>
  15 #include <fiwix/fs.h>
  16 #include <fiwix/mman.h>
  17 #include <fiwix/errno.h>
  18 #include <fiwix/stdio.h>
  19 #include <fiwix/string.h>
  20 
  21 /* send the SIGSEGV signal to the ofending process */
  22 static void send_sigsegv(struct sigcontext *sc)
  23 {
  24 #if defined(CONFIG_VERBOSE_SEGFAULTS)
  25         dump_registers(14, sc);
  26         printk("Memory map:\n");
  27         show_vma_regions(current);
  28 #endif
  29         send_sig(current, SIGSEGV);
  30 }
  31 
  32 static int page_protection_violation(struct vma *vma, unsigned int cr2, struct sigcontext *sc)
  33 {
  34         unsigned int *pgdir;
  35         unsigned int *pgtbl;
  36         unsigned int pde, pte, addr;
  37         struct page *pg;
  38         int page;
  39 
  40         pde = GET_PGDIR(cr2);
  41         pte = GET_PGTBL(cr2);
  42         pgdir = (unsigned int *)P2V(current->tss.cr3);
  43         pgtbl = (unsigned int *)P2V((pgdir[pde] & PAGE_MASK));
  44         page = (pgtbl[pte] & PAGE_MASK) >> PAGE_SHIFT;
  45 
  46         pg = &page_table[page];
  47 
  48         /* Copy On Write feature */
  49         if(pg->count > 1) {
  50                 /* a page not marked as copy-on-write means it's read-only */
  51                 if(!(pg->flags & PAGE_COW)) {
  52                         printk("Oops!, page %d NOT marked for CoW.\n", pg->page);
  53                         send_sigsegv(sc);
  54                         return 0;
  55                 }
  56                 if(!(addr = kmalloc())) {
  57                         printk("%s(): not enough memory!\n", __FUNCTION__);
  58                         return 1;
  59                 }
  60                 current->rss++;
  61                 memcpy_b((void *)addr, (void *)P2V((page << PAGE_SHIFT)), PAGE_SIZE);
  62                 pgtbl[pte] = V2P(addr) | PAGE_PRESENT | PAGE_RW | PAGE_USER;
  63                 kfree(P2V((page << PAGE_SHIFT)));
  64                 current->rss--;
  65                 invalidate_tlb();
  66                 return 0;
  67         } else {
  68                 /* last page of Copy On Write procedure */
  69                 if(pg->count == 1) {
  70                         /* a page not marked as copy-on-write means it's read-only */
  71                         if(!(pg->flags & PAGE_COW)) {
  72                                 printk("Oops!, last page %d NOT marked for CoW.\n", pg->page);
  73                                 send_sigsegv(sc);
  74                                 return 0;
  75                         }
  76                         pgtbl[pte] = (page << PAGE_SHIFT) | PAGE_PRESENT | PAGE_RW | PAGE_USER;
  77                         invalidate_tlb();
  78                         return 0;
  79                 }
  80         }
  81         printk("WARNING: %s(): page %d with pg->count = 0!\n", __FUNCTION__, pg->page);
  82         return 1;
  83 }
  84 
  85 static int page_not_present(struct vma *vma, unsigned int cr2, struct sigcontext *sc)
  86 {
  87         unsigned int addr, file_offset;
  88         struct page *pg;
  89 
  90         if(!vma) {
  91                 if(cr2 >= (sc->oldesp - 32) && cr2 < KERNEL_BASE_ADDR) {
  92                         if(!(vma = find_vma_region(KERNEL_BASE_ADDR - 1))) {
  93                                 printk("WARNING: %s(): process %d doesn't have an stack region in vma!\n", __FUNCTION__, current->pid);
  94                                 send_sigsegv(sc);
  95                                 return 0;
  96                         } else {
  97                                 /* assuming stack will never reach heap */
  98                                 vma->start = cr2;
  99                                 vma->start = vma->start & PAGE_MASK;
 100                         }
 101                 }
 102         }
 103 
 104         /* if still a non-valid vma is found then kill the process! */
 105         if(!vma || vma->prot == PROT_NONE) {
 106                 send_sigsegv(sc);
 107                 return 0;
 108         }
 109 
 110         /* fill the page with its corresponding file content */
 111         if(vma->inode) {
 112                 file_offset = (cr2 & PAGE_MASK) - vma->start + vma->offset;
 113                 file_offset &= PAGE_MASK;
 114                 pg = NULL;
 115 
 116                 if(!(vma->prot & PROT_WRITE) || vma->flags & MAP_SHARED) {
 117                         /* check if it's already in cache */
 118                         if((pg = search_page_hash(vma->inode, file_offset))) {
 119                                 if(!map_page(current, cr2, (unsigned int)V2P(pg->data), vma->prot)) {
 120                                         printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__);
 121                                         return 1;
 122                                 }
 123                                 addr = (unsigned int)pg->data;
 124                         }
 125                 }
 126                 if(!pg) {
 127                         if(!(addr = map_page(current, cr2, 0, vma->prot))) {
 128                                 printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__);
 129                                 return 1;
 130                         }
 131                         pg = &page_table[V2P(addr) >> PAGE_SHIFT];
 132                         if(bread_page(pg, vma->inode, file_offset, vma->prot, vma->flags)) {
 133                                 unmap_page(cr2);
 134                                 return 1;
 135                         }
 136                         current->usage.ru_majflt++;
 137                 }
 138         } else {
 139                 current->usage.ru_minflt++;
 140                 addr = 0;
 141         }
 142 
 143         if(vma->flags & ZERO_PAGE) {
 144                 if(!addr) {
 145                         if(!(addr = map_page(current, cr2, 0, vma->prot))) {
 146                                 printk("%s(): Oops, map_page() returned 0!\n", __FUNCTION__);
 147                                 return 1;
 148                         }
 149                 }
 150                 memset_b((void *)(addr & PAGE_MASK), NULL, PAGE_SIZE);
 151         }
 152 
 153         return 0;
 154 }
 155 
 156 /*
 157  * Exception 0xE: Page Fault
 158  *
 159  *               +------+------+------+------+------+------+
 160  *               | user |kernel|  PV  |  PF  | read |write |
 161  * +-------------+------+------+------+------+------+------+
 162  * |the page     | U1   |    K1| U1 K1|      | U1 K1|    K1|
 163  * |has          | U2   |    K2| U2   |    K2|    K2| U2 K2|
 164  * |a vma region | U3   |      |      | U3   | U3   | U3   |
 165  * +-------------+------+------+------+------+------+------+
 166  * |the page     | U1   |    K1| U1 K1|    K1| U1 K1| U1 K1|
 167  * |doesn't have | U2   |      |      | U2   | U2   | U2   |
 168  * |a vma region |      |      |      |      |      |      |
 169  * +-------------+------+------+------+------+------+------+
 170  *
 171  * U1 - vma + user + PV + read
 172  *      (vma page in user-mode, page-violation during read)
 173  *      U1.1) if flags match                    -> Demand paging
 174  *      U1.2) if flags don't match              -> SIGSEV
 175  *
 176  * U2 - vma + user + PV + write
 177  *      (vma page in user-mode, page-violation during write)
 178  *      U2.1) if flags match                    -> Copy-On-Write
 179  *      U2.2) if flags don't match              -> SIGSEGV
 180  *
 181  * U3 - vma + user + PF + (read | write)        -> Demand paging
 182  *      (vma page in user-mode, page-fault during read or write)
 183  *
 184  * K1 - vma + kernel + PV + (read | write)      -> PANIC
 185  *      (vma page in kernel-mode, page-violation during read or write)
 186  * K2 - vma + kernel + PF + (read | write)      -> Demand paging (mmap)
 187  *      (vma page in kernel-mode, page-fault during read or write)
 188  *
 189  * ----------------------------------------------------------------------------
 190  *
 191  * U1 - !vma + user + PV + (read | write)       -> SIGSEGV
 192  *      (!vma page in user-mode, page-violation during read or write)
 193  * U2 - !vma + user + PF + (read | write)       -> STACK grows
 194  *      (!vma page in user-mode, page-fault during read or write)
 195  *
 196  * K1 - !vma + kernel + (PV | PF) + (read | write)      -> PANIC
 197  *      (!vma page in kernel-mode, page-fault or page-violation during read
 198  *      or write)
 199  */
 200 void do_page_fault(unsigned int trap, struct sigcontext *sc)
 201 {
 202         unsigned int cr2;
 203         struct vma *vma;
 204 
 205         GET_CR2(cr2);
 206         if((vma = find_vma_region(cr2))) {
 207 
 208                 /* in user mode */
 209                 if(sc->err & PFAULT_U) {
 210                         if(sc->err & PFAULT_V) {        /* violation */
 211                                 if(sc->err & PFAULT_W) {
 212                                         if((page_protection_violation(vma, cr2, sc))) {
 213                                                 send_sig(current, SIGKILL);
 214                                         }
 215                                         return;
 216                                 }
 217                                 send_sigsegv(sc);
 218                         } else {                        /* page not present */
 219                                 if((page_not_present(vma, cr2, sc))) {
 220                                         send_sig(current, SIGKILL);
 221                                 }
 222                         }
 223                         return;
 224 
 225                 /* in kernel mode */
 226                 } else {
 227                         /* 
 228                          * WP bit marks the order: first check if the page is
 229                          * present, then check for protection violation.
 230                          */
 231                         if(!(sc->err & PFAULT_V)) {     /* page not present */
 232                                 if((page_not_present(vma, cr2, sc))) {
 233                                         send_sig(current, SIGKILL);
 234                                         printk("%s(): kernel was unable to read a page of process '%s' (pid %d).\n", __FUNCTION__, current->argv0, current->pid);
 235                                 }
 236                                 return;
 237                         }
 238                         if(sc->err & PFAULT_W) {        /* copy-on-write? */
 239                                 if((page_protection_violation(vma, cr2, sc))) {
 240                                         send_sig(current, SIGKILL);
 241                                         printk("%s(): kernel was unable to write a page of process '%s' (pid %d).\n", __FUNCTION__, current->argv0, current->pid);
 242                                 }
 243                                 return;
 244                         }
 245                 }
 246         } else {
 247                 /* in user mode */
 248                 if(sc->err & PFAULT_U) {
 249                         if(sc->err & PFAULT_V) {        /* violation */
 250                                 send_sigsegv(sc);
 251                         } else {                        /* stack? */
 252                                 if((page_not_present(vma, cr2, sc))) {
 253                                         send_sig(current, SIGKILL);
 254                                 }
 255                         }
 256                         return;
 257                 }
 258         }
 259 
 260         dump_registers(trap, sc);
 261         show_vma_regions(current);
 262         PANIC("\n");
 263 }

/* [previous][next][first][last][top][bottom][index][help] */