IBNOS
gdt.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, Michael Müller
3  * Copyright (c) 2014, Sebastian Lackner
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #include <hardware/gdt.h>
29 #include <memory/paging.h>
30 #include <memory/physmem.h>
31 #include <console/console.h>
32 #include <interrupt/interrupt.h>
33 #include <process/thread.h>
34 
41 /* kernelstack */
43 
44 /* gdt */
45 static struct GDTTable gdtTable;
46 static struct GDTEntry *gdtTableEntries;
47 
48 /* idt */
49 static struct IDTTable idtTable;
50 static struct IDTEntry *idtTableEntries;
51 
52 /* intjmp */
55 
56 /* task */
57 static struct taskContext *taskTable;
58 
59 static struct taskContext *TSS_kernel;
60 static struct taskContext *TSS_user;
61 
62 /* code and data segments */
67 
68 /* task segments */
71 
72 #define INTJMP_ENTRY_SIZE 8
73 #define INTJMP_ENTRY_MASK 7
74 #define INTJMP_ENTRY_BITS 3
75 
76 /* used to determine the memory locations of the idle part */
77 extern uint32_t __kernelIdleBegin;
78 extern uint32_t __kernelIdleEnd;
79 #define KERNEL_IDLE_BEGIN ((uint32_t)&__kernelIdleBegin)
80 #define KERNEL_IDLE_END ((uint32_t)&__kernelIdleEnd)
81 
82 static const char *error_outOfGDTEntries[] =
83 {
84  " GDT ERROR ",
85  " No more GDT entries left!",
86  NULL
87 };
88 
89 static const char *error_unhandledKernelInterrupt[] =
90 {
91  " KERNEL INTERRUPT ",
92  " Unable to handle kernel interrupt! ",
93  NULL
94 };
95 
96 static const char *error_usermodeInterruptInvalid[] =
97 {
98  " USERMODE INTERRUPT ",
99  " Unable to recover from usermode interrupt! ",
100  NULL
101 };
102 
103 void __attribute__((cdecl)) __setGDT(const struct GDTTable *table);
104 asm(".text\n.align 4\n"
105 "__setGDT:\n"
106 " movl 4(%esp), %eax\n"
107 " lgdt (%eax)\n"
108 " ret\n"
109 );
110 
111 void __attribute__((cdecl)) __setSegments(uint32_t code, uint32_t data);
112 asm(".text\n.align 4\n"
113 "__setSegments:\n"
114 " movw 8(%esp), %ax\n"
115 " movw %ax, %ds\n"
116 " movw %ax, %es\n"
117 " movw %ax, %fs\n"
118 " movw %ax, %gs\n"
119 " movw %ax, %ss\n"
120 " popl %eax\n"
121 " pushl (%esp)\n"
122 " pushl %eax\n"
123 " retf\n"
124 );
125 
126 void __attribute__((cdecl)) __loadTSS(uint16_t value);
127 asm(".text\n.align 4\n"
128 "__loadTSS:\n"
129 " movw 4(%esp), %ax\n"
130 " ltr %ax\n"
131 " ret\n"
132 );
133 
134 uint32_t __attribute__((cdecl)) __runUserModeTask(uint16_t task);
135 asm(".text\n.align 4\n"
136 "__runUserModeTask:\n"
137 " pushw 4(%esp)\n"
138 " pushl $0\n"
139 " ljmp *(%esp)\n"
140 " add $6, %esp\n"
141 " ret\n"
142 );
143 
144 void __attribute__((cdecl)) __setIDT(const struct IDTTable *table);
145 asm(".text\n.align 4\n"
146 "__setIDT:\n"
147 " movl 4(%esp), %eax\n"
148 " lidt (%eax)\n"
149 " ret\n"
150 );
151 
152 #define __isErrorCodeInterrupt(i) \
153  ((i) == 8 || ((i) >= 10 && (i) <= 14) || (i) == 17)
154 
162 static __attribute__((cdecl)) void __dispatchKernelInterrupt(uint32_t interrupt, uint32_t error, struct taskContext *context)
163 {
164  uint32_t status = dispatchInterrupt(interrupt, error, NULL);
165 
166  /* unable to process kernel interrupt - show bluescreen */
167  if (status != INTERRUPT_CONTINUE_EXECUTION && status != INTERRUPT_YIELD)
168  {
169  uint32_t cr2;
170  asm volatile("mov %%cr2, %0" : "=r" (cr2));
171  uint32_t args[] = {interrupt, error, status, cr2};
172  consoleSystemFailure(error_unhandledKernelInterrupt, sizeof(args)/sizeof(args[0]), args, context);
173  }
174 
175  /* kernel was idling */
176  if (context->eip >= KERNEL_IDLE_BEGIN && context->eip < KERNEL_IDLE_END)
177  {
178  context->eflags &= ~(1 << 9); /* disable interrupts again */
179  context->eip = KERNEL_IDLE_END;
180  }
181 }
182 
190 static void __initBasicGDT()
191 {
192  codeRing0 = gdtGetFreeEntry();
193  gdtEntrySetAddress(codeRing0, 0);
194  gdtEntrySetLimit(codeRing0, 0x100000000);
195  codeRing0->accessBits.accessed = 0;
196  codeRing0->accessBits.readWrite = 1;
197  codeRing0->accessBits.dc = 0;
198  codeRing0->accessBits.execute = 1;
199  codeRing0->accessBits.isSystem = 1;
200  codeRing0->accessBits.privlevel = GDT_CPL_RING0;
201  codeRing0->accessBits.present = 1;
202  codeRing0->user = 0;
203  codeRing0->reserved = 0;
204  codeRing0->is32bit = 1;
205 
206  dataRing0 = gdtGetFreeEntry();
207  gdtEntrySetAddress(dataRing0, 0);
208  gdtEntrySetLimit(dataRing0, 0x100000000);
209  dataRing0->accessBits.accessed = 0;
210  dataRing0->accessBits.readWrite = 1;
211  dataRing0->accessBits.dc = 0;
212  dataRing0->accessBits.execute = 0;
213  dataRing0->accessBits.isSystem = 1;
214  dataRing0->accessBits.privlevel = GDT_CPL_RING0;
215  dataRing0->accessBits.present = 1;
216  dataRing0->user = 0;
217  dataRing0->reserved = 0;
218  dataRing0->is32bit = 1;
219 
220  codeRing3 = gdtGetFreeEntry();
221  gdtEntrySetAddress(codeRing3, 0);
222  gdtEntrySetLimit(codeRing3, 0x100000000);
223  codeRing3->accessBits.accessed = 0;
224  codeRing3->accessBits.readWrite = 1;
225  codeRing3->accessBits.dc = 0;
226  codeRing3->accessBits.execute = 1;
227  codeRing3->accessBits.isSystem = 1;
228  codeRing3->accessBits.privlevel = GDT_CPL_RING3;
229  codeRing3->accessBits.present = 1;
230  codeRing3->user = 0;
231  codeRing3->reserved = 0;
232  codeRing3->is32bit = 1;
233 
234  dataRing3 = gdtGetFreeEntry();
235  gdtEntrySetAddress(dataRing3, 0);
236  gdtEntrySetLimit(dataRing3, 0x100000000);
237  dataRing3->accessBits.accessed = 0;
238  dataRing3->accessBits.readWrite = 1;
239  dataRing3->accessBits.dc = 0;
240  dataRing3->accessBits.execute = 0;
241  dataRing3->accessBits.isSystem = 1;
242  dataRing3->accessBits.privlevel = GDT_CPL_RING3;
243  dataRing3->accessBits.present = 1;
244  dataRing3->user = 0;
245  dataRing3->reserved = 0;
246  dataRing3->is32bit = 1;
247 }
248 
255 static void __initBasicTask()
256 {
257  struct taskContext *task = taskTable;
258  assert(taskTable);
259 
260  kernelTask = gdtGetFreeEntry();
261  gdtEntrySetAddress(kernelTask, (uint32_t)task);
262  gdtEntrySetLimit(kernelTask, sizeof(*task));
263  kernelTask->accessBits.accessed = 1;
264  kernelTask->accessBits.readWrite = 0; /* busy flag for tasks */
265  kernelTask->accessBits.dc = 0;
266  kernelTask->accessBits.execute = 1;
267  kernelTask->accessBits.isSystem = 0;
268  kernelTask->accessBits.privlevel = GDT_CPL_RING0;
269  kernelTask->accessBits.present = 1;
270  kernelTask->user = 0;
271  kernelTask->reserved = 0;
272  kernelTask->is32bit = 0; /* not used for tasks */
273 
274  TSS_kernel = task;
275  memset(task, 0, sizeof(*task));
276  task->ldt = 0;
277  task->iomap = sizeof(*task);
278  task++;
279 
280  usermodeTask = gdtGetFreeEntry();
281  gdtEntrySetAddress(usermodeTask, (uint32_t)task);
282  gdtEntrySetLimit(usermodeTask, sizeof(*task));
283  usermodeTask->accessBits.accessed = 1;
284  usermodeTask->accessBits.readWrite = 0; /* busy flag for tasks */
285  usermodeTask->accessBits.dc = 0;
286  usermodeTask->accessBits.execute = 1;
287  usermodeTask->accessBits.isSystem = 0;
288  usermodeTask->accessBits.privlevel = GDT_CPL_RING0;
289  usermodeTask->accessBits.present = 1;
290  usermodeTask->user = 0;
291  usermodeTask->reserved = 0;
292  usermodeTask->is32bit = 0; /* not used for tasks */
293 
294  TSS_user = task;
295  memset(task, 0, sizeof(*task));
296  task->ldt = 0;
297  task->iomap = sizeof(*task);
298  task++;
299 
300  /* ensure that all pointers are within the single task page */
301  assert((uint32_t)task - (uint32_t)taskTable <= PAGE_SIZE);
302 }
303 
311 static void __generateIntJmpTables()
312 {
313  uint32_t i;
314  uint8_t *cur, *dispatcher;
315 
318  assert(kernelTask);
319 
320  /*
321  * Kernel mode interrupt table
322  */
323 
324  dispatcher = (uint8_t *)intJmpTable_kernel + INTJMP_ENTRY_SIZE * IDT_MAX_COUNT;
325 
326  for (i = 0; i < IDT_MAX_COUNT; i++)
327  {
328  cur = (uint8_t *)intJmpTable_kernel + INTJMP_ENTRY_SIZE * i;
329 
330  if (!__isErrorCodeInterrupt(i))
331  {
332  *cur++ = 0x83; *cur++ = 0xEC; *cur++ = 0x6C; /* sub esp, 0x6C */
333  }
334  else
335  {
336  *cur++ = 0x83; *cur++ = 0xEC; *cur++ = 0x68; /* sub esp, 0x68 */
337  }
338 
339  *cur++ = 0xE8; /* call <dispatcher> */
340  *(uint32_t *)cur = dispatcher - (cur + 4); cur += 4;
341  }
342 
343  /* STACK LAYOUT:
344  *
345  * esp + 0x00: local call (used to determine interrupt number)
346  * esp + 0x04: context (length: 0x68)
347  * esp + 0x6C: errorcode or undefined
348  * esp + 0x70: eip
349  * esp + 0x74: cs
350  * esp + 0x78: eflags
351  *
352  */
353 
354  cur = dispatcher; /* <dispatcher>: */
355  *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x2C; /* mov DWORD PTR [esp+0x2C],eax */
356  *cur++ = 0x89; *cur++ = 0x4C; *cur++ = 0x24; *cur++ = 0x30; /* mov DWORD PTR [esp+0x30],ecx */
357  *cur++ = 0x89; *cur++ = 0x54; *cur++ = 0x24; *cur++ = 0x34; /* mov DWORD PTR [esp+0x34],edx */
358  *cur++ = 0x89; *cur++ = 0x5C; *cur++ = 0x24; *cur++ = 0x38; /* mov DWORD PTR [esp+0x38],ebx */
359  *cur++ = 0x8D; *cur++ = 0x84; *cur++ = 0x24; *cur++ = 0x80; /* lea eax,[esp+0x80] */
360  *cur++ = 0x00; *cur++ = 0x00; *cur++ = 0x00;
361  *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x3C; /* mov DWORD PTR [esp+0x3C],eax */
362  *cur++ = 0x89; *cur++ = 0x6C; *cur++ = 0x24; *cur++ = 0x40; /* mov DWORD PTR [esp+0x40],ebp */
363  *cur++ = 0x89; *cur++ = 0x74; *cur++ = 0x24; *cur++ = 0x44; /* mov DWORD PTR [esp+0x44],esi */
364  *cur++ = 0x89; *cur++ = 0x7C; *cur++ = 0x24; *cur++ = 0x48; /* mov DWORD PTR [esp+0x48],edi */
365  *cur++ = 0x8C; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x4C; /* mov WORD PTR [esp+0x4C],es */
366  *cur++ = 0x66; *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; /* mov ax,WORD PTR [esp+0x74] */
367  *cur++ = 0x74;
368  *cur++ = 0x66; *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; /* mov WORD PTR [esp+0x50],ax */
369  *cur++ = 0x50;
370  *cur++ = 0x8C; *cur++ = 0x54; *cur++ = 0x24; *cur++ = 0x54; /* mov WORD PTR [esp+0x54],ss */
371  *cur++ = 0x8C; *cur++ = 0x5C; *cur++ = 0x24; *cur++ = 0x58; /* mov WORD PTR [esp+0x58],ds */
372  *cur++ = 0x8C; *cur++ = 0x64; *cur++ = 0x24; *cur++ = 0x5C; /* mov WORD PTR [esp+0x5C],fs */
373  *cur++ = 0x8C; *cur++ = 0x6C; *cur++ = 0x24; *cur++ = 0x60; /* mov WORD PTR [esp+0x60],gs */
374  *cur++ = 0x0F; *cur++ = 0x20; *cur++ = 0xD8; /* mov eax,cr3 */
375  *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x20; /* mov DWORD PTR [esp+0x20],eax */
376  *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x70; /* mov eax,DWORD PTR [esp+0x70] */
377  *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x24; /* mov DWORD PTR [esp+0x24],eax */
378  *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x78; /* mov eax,DWORD PTR [esp+0x78] */
379  *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x28; /* mov DWORD PTR [esp+0x28],eax */
380  *cur++ = 0x58; /* pop eax (interrupt addr) */
381  *cur++ = 0x2D; /* sub eax, <offset> */
382  *(uint32_t *)cur = (USERMODE_INTJMP_ADDRESS + 8); cur += 4;
383  *cur++ = 0xC1; *cur++ = 0xE8; *cur++ = 0x03; /* shr eax, 0x3 */
384  *cur++ = 0x54; /* push esp (context) */
385  *cur++ = 0xFF; *cur++ = 0x74; *cur++ = 0x24; *cur++ = 0x6C; /* push DWORD PTR [esp+0x6c] (error code) */
386  *cur++ = 0x50; /* push eax (interrupt nr) */
387  *cur++ = 0xE8; /* call <address> */
388  *(uint32_t *)cur = (uint8_t *)&__dispatchKernelInterrupt - (cur + 4); cur += 4;
389  *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x2C; /* mov eax,DWORD PTR [esp+0x2C] */
390  *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x78; /* mov DWORD PTR [esp+0x78],eax */
391  *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x58; /* mov eax,DWORD PTR [esp+0x58] */
392  *cur++ = 0x66; *cur++ = 0x89; *cur++ = 0x44; *cur++ = 0x24; /* mov WORD PTR [esp+0x7C],ax */
393  *cur++ = 0x7C;
394  *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x30; /* mov eax,DWORD PTR [esp+0x30] */
395  *cur++ = 0x89; *cur++ = 0x84; *cur++ = 0x24; *cur++ = 0x80; /* mov DWORD PTR [esp+0x80],eax */
396  *cur++ = 0x00; *cur++ = 0x00; *cur++ = 0x00;
397  *cur++ = 0x8B; *cur++ = 0x7C; *cur++ = 0x24; *cur++ = 0x50; /* mov edi,DWORD PTR [esp+0x50] */
398  *cur++ = 0x8B; *cur++ = 0x74; *cur++ = 0x24; *cur++ = 0x4C; /* mov esi,DWORD PTR [esp+0x4C] */
399  *cur++ = 0x8B; *cur++ = 0x6C; *cur++ = 0x24; *cur++ = 0x48; /* mov ebp,DWORD PTR [esp+0x48] */
400  *cur++ = 0x8B; *cur++ = 0x5C; *cur++ = 0x24; *cur++ = 0x40; /* mov ebx,DWORD PTR [esp+0x40] */
401  *cur++ = 0x8B; *cur++ = 0x54; *cur++ = 0x24; *cur++ = 0x3C; /* mov edx,DWORD PTR [esp+0x3C] */
402  *cur++ = 0x8B; *cur++ = 0x4C; *cur++ = 0x24; *cur++ = 0x38; /* mov ecx,DWORD PTR [esp+0x38] */
403  *cur++ = 0x8B; *cur++ = 0x44; *cur++ = 0x24; *cur++ = 0x34; /* mov eax,DWORD PTR [esp+0x34] */
404  *cur++ = 0x83; *cur++ = 0xC4; *cur++ = 0x78; /* add esp, 0x78 */
405  *cur++ = 0xCF; /* iret */
406  assert(cur <= (uint8_t *)intJmpTable_kernel + PAGE_SIZE);
407 
408  /*
409  * Usermode interrupt table
410  */
411 
412  for (i = 0; i < IDT_MAX_COUNT; i++)
413  {
414  cur = (uint8_t *)intJmpTable_user + INTJMP_ENTRY_SIZE * i;
415 
416  *cur++ = 0xEA; *(uint32_t *)cur = 0; cur += 4; /* jmp seg:addr */
417  *(uint16_t *)cur = gdtGetEntryOffset(kernelTask, GDT_CPL_RING0); cur += 2;
418  *cur++ = 0xCC; /* int 3 */
419  }
420 
422 
423  cur = dispatcher; /* <enable_fpu>: */
424  *cur++ = 0x0F; *cur++ = 0x06; /* clts */
425  *cur++ = 0xCF; /* iret */
426  assert(cur <= (uint8_t *)intJmpTable_user + PAGE_SIZE);
427 }
428 
432 void gdtInit()
433 {
434  uint32_t i;
435 
436  assert(!gdtTableEntries);
437  assert(!idtTableEntries);
438  assert(!taskTable);
440 
441  assert(GDT_MAX_COUNT * sizeof(struct GDTEntry) == GDT_MAX_SIZE);
443  assert(IDT_MAX_COUNT * sizeof(struct IDTEntry) <= PAGE_SIZE);
444  assert(2 * sizeof(struct taskContext) <= PAGE_SIZE);
445 
446  kernelStack = pagingAllocatePhysMemUnpageable(NULL, 1, true, false);
448 
449  gdtTableEntries = (struct GDTEntry *)pagingAllocatePhysMemFixedUnpageable(NULL, (void *)USERMODE_GDT_ADDRESS, GDT_MAX_PAGES, true, false);
450  memset(gdtTableEntries, 0, sizeof(struct GDTEntry) * GDT_MAX_COUNT);
451 
452  idtTableEntries = (struct IDTEntry *)pagingAllocatePhysMemFixedUnpageable(NULL, (void *)USERMODE_IDT_ADDRESS, 1, true, false);
453  memset(idtTableEntries, 0, PAGE_SIZE);
454 
455  taskTable = (struct taskContext *)pagingAllocatePhysMemFixedUnpageable(NULL, (void *)USERMODE_TASK_ADDRESS, 1, true, false);
456  memset(taskTable, 0, PAGE_SIZE);
457 
460 
461  intJmpTable_user = pagingAllocatePhysMemUnpageable(NULL, 1, true, false);
463 
464  /* gdt */
465  __initBasicGDT();
466  gdtTable.limit = GDT_MAX_SIZE - 1;
467  gdtTable.address = (uint32_t)gdtTableEntries;
468  __setGDT(&gdtTable);
469  __setSegments(gdtGetEntryOffset(codeRing0, GDT_CPL_RING0), gdtGetEntryOffset(dataRing0, GDT_CPL_RING0));
470 
471  /* task */
472  __initBasicTask();
473  debugCaptureCpuContext(TSS_kernel);
474  __loadTSS(gdtGetEntryOffset(kernelTask, GDT_CPL_RING0));
475 
476  /* idt */
477  __generateIntJmpTables();
478 
479  for (i = 0; i < IDT_MAX_COUNT; i++)
480  {
481  struct IDTEntry *entry = &idtTableEntries[i];
482  uint32_t address = USERMODE_INTJMP_ADDRESS + INTJMP_ENTRY_SIZE * i;
483 
484  entry->addressLow = address & 0x0000FFFF;
485  entry->csSelector = gdtGetEntryOffset(codeRing0, GDT_CPL_RING0);
486  entry->zero = 0;
487  entry->typeBits.type = INT_TYPE_INT32;
488  entry->typeBits.storageSegment = 0;
489  entry->typeBits.dpl = (i == 0x80) ? GDT_CPL_RING3 : GDT_CPL_RING0; /* FIXME: don't hardcode the syscall interrupt */
490  entry->typeBits.present = 1;
491  entry->addressHigh = address >> 16;
492  }
493 
494  idtTable.limit = IDT_MAX_COUNT * sizeof(struct IDTEntry) - 1;
495  idtTable.address = (uint32_t)idtTableEntries;
496  __setIDT(&idtTable);
497 }
498 
504 {
505  uint32_t i;
506 
507  assert(gdtTableEntries);
508 
509  /* we skip the first entry, the so called null descriptor */
510  for (i = 1; i < GDT_MAX_COUNT; i++)
511  {
512  if (!gdtTableEntries[i].accessBits.present)
513  {
514  gdtTableEntries[i].accessBits.present = 1;
515  return &gdtTableEntries[i];
516  }
517  }
518 
519  SYSTEM_FAILURE(error_outOfGDTEntries);
520 }
521 
537 uint32_t gdtGetEntryOffset(struct GDTEntry* entry, uint32_t ring)
538 {
539  uint32_t offset;
540  assert(gdtTableEntries && entry >= gdtTableEntries && entry < gdtTableEntries + GDT_MAX_COUNT);
541 
542  offset = (uint32_t)entry - (uint32_t)gdtTableEntries;
543  assert((offset & 7) == 0);
544 
545  return offset | ring;
546 }
547 
557 void gdtEntrySetAddress(struct GDTEntry* entry, uint32_t address)
558 {
559  assert(entry);
560 
561  entry->address1 = address & 0x0000FFFF;
562  entry->address2 = (address & 0x00FF0000) >> 16;
563  entry->address3 = (address & 0xFF000000) >> 24;
564 }
565 
582 void gdtEntrySetLimit(struct GDTEntry* entry, uint64_t length)
583 {
584  assert(entry);
585  assert(length <= 0x100000000);
586  assert(length > 0);
587 
588  if (length > 0x100000)
589  {
590  assert((length & PAGE_MASK) == 0);
591  length >>= PAGE_BITS;
592  entry->granularity = 1; /* 4 KB blocks */
593  }
594  else
595  {
596  entry->granularity = 0; /* 1 byte blocks */
597  }
598 
599  length--;
600 
601  entry->limit1 = length & 0x0000FFFF;
602  entry->limit2 = (length >> 16) & 0xF;
603 }
604 
612 void gdtReleaseEntry(struct GDTEntry* entry)
613 {
614  assert(entry);
615 
616  /* reset all flags including present */
617  memset(entry, 0, sizeof(struct GDTEntry));
618 }
619 
628 uint32_t tssRunUsermodeThread(struct thread *t)
629 {
630  uint32_t interrupt, error = 0;
631  uint32_t *args;
632 
633  assert(t);
634 
635  /* initialize user context with provided context */
636  *TSS_user = t->task;
637 
638  /* if FPU is enabled, we have to return in ring0, then clear the TS bit,
639  * and afterwards switch back to the original ring3 code. */
640  if (t == lastFPUthread)
641  {
642  args = (uint32_t *)((uint32_t)kernelStack + PAGE_SIZE - 5 * sizeof(uint32_t));
643  args[0] = TSS_user->eip;
644  args[1] = TSS_user->cs;
645  args[2] = TSS_user->eflags;
646  args[3] = TSS_user->esp;
647  args[4] = TSS_user->ss;
648 
649  TSS_user->eip = USERMODE_INTJMP_ENABLE_FPU;
650  TSS_user->cs = gdtGetEntryOffset(codeRing0, GDT_CPL_RING0);
651  TSS_user->eflags = 0; /* real eflags will be restored later, it is important that interrupts are still disabled */
652  TSS_user->esp = USERMODE_KERNELSTACK_LIMIT - 5 * sizeof(uint32_t);
653  TSS_user->ss = gdtGetEntryOffset(dataRing0, GDT_CPL_RING0);
654  }
655 
656  __runUserModeTask(gdtGetEntryOffset(usermodeTask, GDT_CPL_RING0));
657  t->task = *TSS_user;
658 
659  assert(TSS_user->ss == t->task.ss0);
660 
661  /* determine which interrupt it was */
662  interrupt = (t->task.eip - (USERMODE_INTJMP_ADDRESS + 7));
663  assert((interrupt & INTJMP_ENTRY_MASK) == 0);
664  interrupt >>= INTJMP_ENTRY_BITS;
665  assert((interrupt & ~255) == 0);
666 
667  /* read args from kernel stack in the user process */
668  args = (uint32_t *)((uint32_t)kernelStack + TSS_user->esp - USERMODE_KERNELSTACK_ADDRESS);
669  if (TSS_user->esp == USERMODE_KERNELSTACK_LIMIT - 6 * sizeof(uint32_t) /* && __isErrorCodeInterrupt(interrupt) */)
670  {
671  error = args[0];
672  t->task.eip = args[1];
673  t->task.cs = args[2];
674  t->task.eflags = args[3];
675  t->task.esp = args[4];
676  t->task.ss = args[5];
677  }
678  else if (TSS_user->esp == USERMODE_KERNELSTACK_LIMIT - 5 * sizeof(uint32_t))
679  {
680  t->task.eip = args[0];
681  t->task.cs = args[1];
682  t->task.eflags = args[2];
683  t->task.esp = args[3];
684  t->task.ss = args[4];
685  }
686  else
687  consoleSystemFailure(error_usermodeInterruptInvalid, 0, NULL, &t->task);
688 
689  /* we should now be again in usermode */
692 
693  return dispatchInterrupt(interrupt, error, t);
694 }
695 
701 void __attribute__((cdecl)) tssKernelIdle();
702 asm(".text\n.align 4\n"
703 ".globl tssKernelIdle\n"
704 "tssKernelIdle:\n"
705 "__kernelIdleBegin:\n"
706 " sti\n"
707 ".loop:\n"
708 " hlt\n"
709 " jmp .loop\n"
710 "__kernelIdleEnd:\n"
711 " ret\n"
712 );
713 
#define USERMODE_KERNELSTACK_LIMIT
Definition: gdt.h:99
#define USERMODE_IDT_ADDRESS
Definition: gdt.h:95
uint32_t address
Definition: gdt.h:118
uint16_t cs
Definition: context.h:63
#define KERNEL_IDLE_END
Definition: gdt.c:80
#define PAGE_MASK
Definition: physmem.h:36
#define INTJMP_ENTRY_MASK
Definition: gdt.c:73
#define assert(ex)
Definition: util.h:61
uint32_t eflags
Definition: context.h:50
#define INT_TYPE_INT32
Definition: gdt.h:58
#define GDT_MAX_SIZE
Definition: gdt.h:72
void * pagingAllocatePhysMemFixedUnpageable(struct process *p, void *addr, uint32_t length, bool rw, bool user)
Allocates several pages of unpageable physical memory at a fixed virtual address in a process...
Definition: paging.c:804
#define GDT_CPL_RING3
Definition: gdt.h:47
void * memset(void *ptr, int value, size_t num)
Fills a memory region with some specific byte value.
Definition: util.c:123
void * pagingAllocatePhysMemUnpageable(struct process *p, uint32_t length, bool rw, bool user)
Allocates several pages of unpageable physical memory in a process.
Definition: paging.c:686
uint32_t __kernelIdleBegin
void gdtEntrySetAddress(struct GDTEntry *entry, uint32_t address)
Helper function to set the address inside a GDTEntry.
Definition: gdt.c:557
uint16_t iomap
Definition: context.h:77
#define USERMODE_GDT_ADDRESS
Definition: gdt.h:94
#define PAGE_SIZE
Definition: physmem.h:35
struct GDTEntry * gdtGetFreeEntry()
Get a free entry in the GDT.
Definition: gdt.c:503
void gdtReleaseEntry(struct GDTEntry *entry)
Mark a GDTEntry as free.
Definition: gdt.c:612
uint32_t address
Definition: gdt.h:156
uint8_t address3
Definition: gdt.h:150
struct thread * lastFPUthread
Definition: thread.c:45
#define PAGE_BITS
Definition: physmem.h:37
#define GDT_CPL_MASK
Definition: gdt.h:45
#define INTERRUPT_YIELD
Definition: interrupt.h:50
uint8_t reserved
Definition: gdt.h:147
void * intJmpTable_user
Definition: gdt.c:54
uint16_t addressLow
Definition: gdt.h:161
uint16_t addressHigh
Definition: gdt.h:176
uint8_t address2
Definition: gdt.h:132
Definition: thread.h:47
void debugCaptureCpuContext(struct taskContext *context)
#define USERMODE_INTJMP_ENABLE_FPU
Definition: gdt.h:102
Definition: gdt.h:122
struct IDTEntry::@7::@9 typeBits
#define USERMODE_KERNELSTACK_ADDRESS
Definition: gdt.h:93
struct GDTEntry * codeRing3
Definition: gdt.c:65
Definition: gdt.h:153
struct @2::@4 accessBits
struct GDTEntry * dataRing0
Definition: gdt.c:64
#define INTJMP_ENTRY_SIZE
Definition: gdt.c:72
void * kernelStack
Definition: gdt.c:42
uint32_t __kernelIdleEnd
struct GDTEntry::@0::@1 accessBits
uint8_t is32bit
Definition: gdt.h:148
uint32_t gdtGetEntryOffset(struct GDTEntry *entry, uint32_t ring)
Determines the offset of a GDT entry.
Definition: gdt.c:537
uint32_t tssRunUsermodeThread(struct thread *t)
Run a thread.
Definition: gdt.c:628
uint32_t eip
Definition: context.h:49
#define __isErrorCodeInterrupt(i)
Definition: gdt.h:128
#define SYSTEM_FAILURE(lines,...)
Definition: util.h:71
struct GDTEntry * dataRing3
Definition: gdt.c:66
void consoleSystemFailure(const char **lines, uint32_t numArgs, uint32_t *args, struct taskContext *context)
Print a system failure message and halts the system.
Definition: console.c:589
uint8_t zero
Definition: gdt.h:166
#define GDT_MAX_COUNT
Definition: gdt.h:76
uint16_t limit1
Definition: gdt.h:130
void __attribute__((cdecl))
Definition: gdt.c:103
#define KERNEL_IDLE_BEGIN
Definition: gdt.c:79
uint32_t address
Definition: gdt.h:125
uint16_t ss0
Definition: context.h:37
uint16_t address1
Definition: gdt.h:131
uint16_t ldt
Definition: context.h:74
#define GDT_CPL_RING0
Definition: gdt.h:46
uint8_t limit2
Definition: gdt.h:145
#define INTERRUPT_CONTINUE_EXECUTION
Definition: interrupt.h:48
#define INTJMP_ENTRY_BITS
Definition: gdt.c:74
uint8_t user
Definition: gdt.h:146
void * intJmpTable_kernel
Definition: gdt.c:53
struct GDTEntry * usermodeTask
Definition: gdt.c:70
void gdtEntrySetLimit(struct GDTEntry *entry, uint64_t length)
Helper function to set the length inside a GDTEntry.
Definition: gdt.c:582
uint32_t dispatchInterrupt(uint32_t interrupt, uint32_t error, struct thread *t)
Handle an incoming interrupt.
Definition: interrupt.c:83
#define IDT_MAX_COUNT
Definition: gdt.h:86
#define GDT_MAX_PAGES
Definition: gdt.h:74
uint16_t limit
Definition: gdt.h:124
struct GDTEntry * kernelTask
Definition: gdt.c:69
uint8_t granularity
Definition: gdt.h:149
uint16_t limit
Definition: gdt.h:155
uint16_t ss
Definition: context.h:65
struct GDTEntry * codeRing0
Definition: gdt.c:63
struct taskContext task
Definition: thread.h:69
void gdtInit()
Initializes the GDT, task registers, and sets up everything required for multiprocessing.
Definition: gdt.c:432
uint32_t value
Definition: paging.h:55
Definition: gdt.h:159
#define USERMODE_TASK_ADDRESS
Definition: gdt.h:97
#define USERMODE_INTJMP_ADDRESS
Definition: gdt.h:96
uint32_t esp
Definition: context.h:56
uint16_t csSelector
Definition: gdt.h:163