《操作系统》的实验代码。

99 lines
3.2 KiB

12 years ago
  1. #include <defs.h>
  2. #include <x86.h>
  3. #include <mmu.h>
  4. #include <memlayout.h>
  5. #include <pmm.h>
  6. /* *
  7. * Task State Segment:
  8. *
  9. * The TSS may reside anywhere in memory. A special segment register called
  10. * the Task Register (TR) holds a segment selector that points a valid TSS
  11. * segment descriptor which resides in the GDT. Therefore, to use a TSS
  12. * the following must be done in function gdt_init:
  13. * - create a TSS descriptor entry in GDT
  14. * - add enough information to the TSS in memory as needed
  15. * - load the TR register with a segment selector for that segment
  16. *
  17. * There are several fileds in TSS for specifying the new stack pointer when a
  18. * privilege level change happens. But only the fields SS0 and ESP0 are useful
  19. * in our os kernel.
  20. *
  21. * The field SS0 contains the stack segment selector for CPL = 0, and the ESP0
  22. * contains the new ESP value for CPL = 0. When an interrupt happens in protected
  23. * mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value
  24. * into SS and ESP respectively.
  25. * */
  26. static struct taskstate ts = {0};
  27. /* *
  28. * Global Descriptor Table:
  29. *
  30. * The kernel and user segments are identical (except for the DPL). To load
  31. * the %ss register, the CPL must equal the DPL. Thus, we must duplicate the
  32. * segments for the user and the kernel. Defined as follows:
  33. * - 0x0 : unused (always faults -- for trapping NULL far pointers)
  34. * - 0x8 : kernel code segment
  35. * - 0x10: kernel data segment
  36. * - 0x18: user code segment
  37. * - 0x20: user data segment
  38. * - 0x28: defined for tss, initialized in gdt_init
  39. * */
  40. static struct segdesc gdt[] = {
  41. SEG_NULL,
  42. [SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL),
  43. [SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL),
  44. [SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER),
  45. [SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER),
  46. [SEG_TSS] = SEG_NULL,
  47. };
  48. static struct pseudodesc gdt_pd = {
  49. sizeof(gdt) - 1, (uint32_t)gdt
  50. };
  51. /* *
  52. * lgdt - load the global descriptor table register and reset the
  53. * data/code segement registers for kernel.
  54. * */
  55. static inline void
  56. lgdt(struct pseudodesc *pd) {
  57. asm volatile ("lgdt (%0)" :: "r" (pd));
  58. asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
  59. asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
  60. asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
  61. asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
  62. asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
  63. // reload cs
  64. asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
  65. }
  66. /* temporary kernel stack */
  67. uint8_t stack0[1024];
  68. /* gdt_init - initialize the default GDT and TSS */
  69. static void
  70. gdt_init(void) {
  71. // Setup a TSS so that we can get the right stack when we trap from
  72. // user to the kernel. But not safe here, it's only a temporary value,
  73. // it will be set to KSTACKTOP in lab2.
  74. ts.ts_esp0 = (uint32_t)&stack0 + sizeof(stack0);
  75. ts.ts_ss0 = KERNEL_DS;
  76. // initialize the TSS filed of the gdt
  77. gdt[SEG_TSS] = SEG16(STS_T32A, (uint32_t)&ts, sizeof(ts), DPL_KERNEL);
  78. gdt[SEG_TSS].sd_s = 0;
  79. // reload all segment registers
  80. lgdt(&gdt_pd);
  81. // load the TSS
  82. ltr(GD_TSS);
  83. }
  84. /* pmm_init - initialize the physical memory management */
  85. void
  86. pmm_init(void) {
  87. gdt_init();
  88. }