summaryrefslogtreecommitdiff
path: root/src/kernel/paging.c
blob: 0432ab691cc104d19624539b84ab1583f67f0f65 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <stdbool.h>
#include <stdint.h>
#include "pmap.h"
#include "paging.h"
#include "boot.h"
#include "panic.h"
#include "task.h"

enum {
  PE_ADDR_MASK = 0xfffff000,

  PT_GLOBAL   = 0x100,
  PD_LARGE    = 0x080,
  PT_DIRTY    = 0x040,
  PE_ACCESSED = 0x020,
  PE_NO_CACHE = 0x010,
  PE_WRTHCH   = 0x008,
  PE_USER     = 0x004,
  PE_WRITABLE = 0x002,
  PE_PRESENT  = 0x001
};

#define KERNEL_END (0x08000000)

static void pd_map(void *pd, uint32_t physical_addr, uint32_t virtual_addr, bool writable) {
  uint32_t *ptp = (uint32_t *)pd + (virtual_addr >> 22);
  if (!(*ptp & PE_PRESENT)) {
    uint32_t *new_pt = allocate_kernel_pages(1);
    for (uint16_t i = 0; i < 1024; ++i)
      new_pt[i] = 0;
    *ptp = (uint32_t)new_pt | PE_USER | PE_WRITABLE | PE_PRESENT;
  }
  ((uint32_t *)(*ptp & PE_ADDR_MASK))[(virtual_addr >> 12) % 1024] = physical_addr | PE_USER | PE_PRESENT | (PE_WRITABLE * writable);
}

__attribute__ ((pure))
bool pd_is_mapped(const void *pd, uint32_t vma) {
  uint32_t pde = ((uint32_t *)pd)[vma >> 22];
  return (pde & PE_PRESENT) && (((uint32_t *)(pde & PE_ADDR_MASK))[(vma >> 12) % 1024] & PE_PRESENT);
}

void free_task_pd(void *pd) {
  uint32_t *pd_32 = pd;
  for (uint16_t i = KERNEL_END / 4096 / 1024; i < 1024; ++i)
    if (pd_32[i] & PE_PRESENT) {
      uint32_t *pt_32 = (uint32_t *)(pd_32[i] & PE_ADDR_MASK);
      for (uint16_t j = 0; j < 1024; ++j)
        if (pt_32[j] & PE_PRESENT)
          free_pages((void *)(pt_32[j] & PE_ADDR_MASK), 1);
      free_pages(pt_32, 1);
    }
  free_pages(pd, 1);
}

#define kmap ((uint32_t *)0x00060000)

void *new_task_pd() {
  uint32_t *pd = allocate_kernel_pages(1);
  for (uint8_t i = 0; i < KERNEL_END / 4096 / 1024; ++i)
    pd[i] = (uint32_t)(kmap + i * 1024) | PE_USER | PE_PRESENT;
  for (uint16_t i = KERNEL_END / 4096 / 1024; i < 1024; ++i)
    pd[i] = 0;
  return pd;
}

void *pd_user_allocate(void *pd, uint32_t vma, uint32_t pages, bool writable) {
  void *pma = allocate_user_pages(pages);
  if (!pma)
    PANIC("Could not allocate user pages.");
  for (uint32_t i = 0; i < pages; ++i)
    pd_map(pd, (uint32_t)pma + (i << 12), vma + (i << 12), writable);
  return pma;
}

__attribute__ ((pure))
static void *find_user_vma_run(void *pd, uint32_t pages) {
  uint32_t run = 0;
  for (void *vma = (void *)KERNEL_END; vma; vma += 4096) {
    if (pd_is_mapped(pd, (uint32_t)vma))
      run = 0;
    else if (++run == pages)
      return vma - (pages - 1) * 4096;
  }
  return 0;
}

void *pd_user_allocate_anywhere_writable(void *pd, uint32_t pages) {
  void *vma = find_user_vma_run(pd, pages);
  if (!vma)
    return 0;
  for (uint32_t i = 0; i < pages; ++i)
    pd_map(pd, (uint32_t)allocate_user_pages(1), (uint32_t)vma + 4096 * i, true);
  return vma;
}

void user_allocate_anywhere_readonly_together(void *pd, uint32_t pages, void **vma_out, void **pma_out) {
  *vma_out = find_user_vma_run(pd, pages);
  if (!*vma_out) {
    *pma_out = 0;
    return;
  }
  *pma_out = allocate_user_pages(pages);
  if (!*pma_out) {
    *vma_out = 0;
    return;
  }
  pd_map(pd, (uint32_t)*pma_out, (uint32_t)*vma_out, false);
}

#define KPAGE_DIR     ((uint32_t *)0x00005000)
#define KPAGE_TABLE_0 ((uint32_t *)0x00400000)

void init_paging() {
  //TODO: use PAE if possible

  for (uint32_t i = 0; i < 1048576; ++i)
    KPAGE_TABLE_0[i] = (i * 4096) | PE_WRITABLE | PE_PRESENT;
  for (uint16_t i = 0; i < 1024; ++i)
    KPAGE_DIR[i] = (uint32_t)(KPAGE_TABLE_0 + i * 1024) | PE_WRITABLE | PE_PRESENT;

  for (uint16_t i = 0; i < KERNEL_END / 4096; ++i)
    kmap[i] = (i * 4096) | PE_USER | PE_PRESENT;

  asm volatile (
    "mov $0x00005000, %%eax\n"
    "mov %%eax, %%cr3\n"
    "mov %%cr0, %%eax\n"
    "or $0x80000000, %%eax\n"
    "mov %%eax, %%cr0"
  : : : "eax");
}

__attribute__ ((pure))
void *vma_to_pma(void *pd, const void *vma) {
  //this is maybe not good - in the case where pd is zero, vma is returned unconsted. hopefully this doesn't become a problem.
  return pd ? (void *)(((uint32_t *)(((uint32_t *)pd)[(uint32_t)vma >> 22] & PE_ADDR_MASK))[((uint32_t)vma >> 12) % 1024] & PE_ADDR_MASK) + (uint32_t)vma % 4096 : (void *)(uint32_t)vma;
}

void switch_to_kernel_cr3() {
  asm volatile (
    "mov $0x00005000, %%eax\n"
    "mov %%eax, %%cr3"
  : : : "eax");
}

void switch_to_task_cr3() {
  if (active_task->page_directory)
    asm volatile (
      "mov %0, %%cr3"
    : : "a" (active_task->page_directory));
}