Avoids nehalem keyboard issues, better monitors
[akaros.git] / kern / arch / i686 / env.c
1 /* See COPYRIGHT for copyright information. */
2 #ifdef __SHARC__
3 #pragma nosharc
4 #endif
5
6 #include <arch/trap.h>
7 #include <env.h>
8 #include <assert.h>
9 #include <pmap.h>
10 #include <smp.h>
11
12 //
13 // This exits the kernel and starts executing some environment's code.
14 // This function does not return.
15 // Uses 'iret' or 'sysexit' depending on CS.
16 //
17 void env_pop_tf(trapframe_t *tf)
18 {
19         /* Bug with this whole idea (TODO: (TLSV))*/
20         /* Load the LDT for this process.  Slightly ghetto doing it here. */
21         /* copy-in and check the LDT location.  the segmentation hardware write the
22          * accessed bit, so we want the memory to be in the user-writeable area. */
23         segdesc_t *ldt = current->procdata->ldt;
24         ldt = (segdesc_t*)MIN((uintptr_t)ldt, UTOP - LDT_SIZE);
25         /* Only set up the ldt if a pointer to the ldt actually exists */
26         if(ldt != NULL) {
27                 segdesc_t *my_gdt = per_cpu_info[core_id()].gdt;
28                 segdesc_t ldt_temp = SEG_SYS(STS_LDT, (uint32_t)ldt, LDT_SIZE, 3);
29                 my_gdt[GD_LDT >> 3] = ldt_temp;
30                 asm volatile("lldt %%ax" :: "a"(GD_LDT));
31         }
32
33         /* In case they are enabled elsewhere.  We can't take an interrupt in these
34          * routines, due to how they play with the kernel stack pointer. */
35         disable_irq();
36         /*
37          * If the process entered the kernel via sysenter, we need to leave via
38          * sysexit.  sysenter trapframes have 0 for a CS, which is pushed in
39          * sysenter_handler.
40          */
41         if(tf->tf_cs) {
42                 /*
43                  * Restores the register values in the Trapframe with the 'iret'
44                  * instruction.  This exits the kernel and starts executing some
45                  * environment's code.  This function does not return.
46                  */
47                 asm volatile ("movl %0,%%esp;           "
48                               "popal;                   "
49                               "popl %%gs;               "
50                               "popl %%fs;               "
51                               "popl %%es;               "
52                               "popl %%ds;               "
53                               "addl $0x8,%%esp;         "
54                               "iret                     "
55                               : : "g" (tf) : "memory");
56                 panic("iret failed");  /* mostly to placate the compiler */
57         } else {
58                 /* Return path of sysexit.  See sysenter_handler's asm for details.
59                  * One difference is that this tf could be somewhere other than a stack
60                  * (like in a struct proc).  We need to make sure esp is valid once
61                  * interrupts are turned on (which would happen on popfl normally), so
62                  * we need to save and restore a decent esp (the current one).  We need
63                  * a place to save it that is accessible after we change the stack
64                  * pointer to the tf *and* that is specific to this core/instance of
65                  * sysexit.  The simplest and nicest is to use the tf_esp, which we
66                  * can just pop.  Incidentally, the value in oesp would work too.
67                  * To prevent popfl from turning interrupts on, we hack the tf's eflags
68                  * so that we have a chance to change esp to a good value before
69                  * interrupts are enabled.  The other option would be to throw away the
70                  * eflags, but that's less desirable. */
71                 tf->tf_eflags &= !FL_IF;
72                 tf->tf_esp = read_esp();
73                 asm volatile ("movl %0,%%esp;           "
74                               "popal;                   "
75                               "popl %%gs;               "
76                               "popl %%fs;               "
77                               "popl %%es;               "
78                               "popl %%ds;               "
79                               "addl $0x10,%%esp;        "
80                               "popfl;                   "
81                               "movl %%ebp,%%ecx;        "
82                               "popl %%esp;              "
83                               "sti;                     "
84                               "sysexit                  "
85                               : : "g" (tf) : "memory");
86                 panic("sysexit failed");  /* mostly to placate your mom */
87         }
88 }
89
90 /* Walks len bytes from start, executing 'callback' on every PTE, passing it a
91  * specific VA and whatever arg is passed in.  Note, this cannot handle jumbo
92  * pages. */
93 int env_user_mem_walk(env_t* e, void* start, size_t len,
94                       mem_walk_callback_t callback, void* arg)
95 {
96         pte_t *pt;
97         uint32_t pdeno, pteno;
98         physaddr_t pa;
99
100         assert((uintptr_t)start % PGSIZE == 0 && len % PGSIZE == 0);
101         void* end = (char*)start+len;
102         uint32_t pdeno_start = PDX(start);
103         uint32_t pdeno_end = PDX(ROUNDUP(end,PTSIZE));
104         /* concerned about overflow.  this should catch it for now, given the above
105          * assert. */
106         assert((len == 0) || (pdeno_start < pdeno_end));
107
108         for (pdeno = pdeno_start; pdeno < pdeno_end; pdeno++) {
109                 if (!(e->env_pgdir[pdeno] & PTE_P))
110                         continue;
111                 /* find the pa and a pointer to the page table */
112                 pa = PTE_ADDR(e->env_pgdir[pdeno]);
113                 pt = (pte_t*COUNT(NPTENTRIES)) KADDR(pa);
114                 /* figure out where we start and end within the page table */
115                 uint32_t pteno_start = (pdeno == pdeno_start ? PTX(start) : 0);
116                 uint32_t pteno_end = (pdeno == pdeno_end - 1 && PTX(end) != 0 ?
117                                       PTX(end) : NPTENTRIES );
118                 int ret;
119                 for (pteno = pteno_start; pteno < pteno_end; pteno++) {
120                         if (!PAGE_UNMAPPED(pt[pteno]))
121                                 if((ret = callback(e, &pt[pteno], PGADDR(pdeno, pteno, 0), arg)))
122                                         return ret;
123                 }
124         }
125         return 0;
126 }
127
128 /* Frees (decrefs) all pages of the process's page table, including the page
129  * directory.  Does not free the memory that is actually mapped. */
130 void env_pagetable_free(env_t* e)
131 {
132         static_assert(UVPT % PTSIZE == 0);
133         assert(e->env_cr3 != rcr3());
134         for(uint32_t pdeno = 0; pdeno < PDX(UVPT); pdeno++)
135         {
136                 // only look at mapped page tables
137                 if (!(e->env_pgdir[pdeno] & PTE_P))
138                         continue;
139
140                 // find the pa and va of the page table
141                 physaddr_t pa = PTE_ADDR(e->env_pgdir[pdeno]);
142
143                 // free the page table itself
144                 e->env_pgdir[pdeno] = 0;
145                 page_decref(pa2page(pa));
146         }
147
148         // free the page directory
149         physaddr_t pa = e->env_cr3;
150         e->env_cr3 = 0;
151         page_decref(pa2page(pa));
152         tlbflush();
153 }
154