~cypheon/xos

xos/paging.c -rw-r--r-- 3.4 KiB
567db604 — Johann Rudloff Implement scanning and allocation of physical memory. 7 years ago
                                                                                
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
#include <sys/memory.h>
#include <sys/paging.h>
#include <sys/types.h>
#include <stdlib.h>

#define PD_SIZE (4 * 1024)
#define PT_SIZE (4 * 1024)

#define PD_ENTRY(pt_addr, flags) ((((uint32_t)(pt_addr)) & 0xfffff000) | (flags))

#define PT_ENTRY(phys_addr, flags) ((((uint32_t)(phys_addr)) & 0xfffff000) | (flags))

static uint32_t pt_init(void);
static void pd_proto_init(void);

pd_t kernel_paging_directory;

/* Contains page mappings shared by all page directories. */
static pd_t pd_proto;

#define KERNEL_MEM_START 0x100000
#define KERNEL_MEM_END 0x400000

#define VIDEO_MEM_START 0xB8000
#define VIDEO_MEM_SIZE 4000

#define KMEM_MAX (256 * 1024 * 1024)

static inline void
pd_switch(pd_t pd) {
  asm volatile ("movl %0, %%cr3\n"
      "movl %%cr0, %%eax\n"
      "or $0x80000000, %%eax\n"
      "movl %%eax, %%cr0\n"
      :
      : "r" (pd)
      : "memory", "eax");
}

void
paging_init(void) {
  pd_proto_init();

  pd_t paging_directory = pd_create();
  kernel_paging_directory = paging_directory;

  pd_switch(kernel_paging_directory);
}

void
pd_proto_init(void) {
  pd_proto = kmalloc_align(PAGE_SIZE, PD_SIZE);
  memset(pd_proto, 0, PD_SIZE);

  void *sentry_page_table = kmalloc_align(PAGE_SIZE, PT_SIZE);
  memset(sentry_page_table, 0, PT_SIZE);

  for(int i=0; i < 1024; ++i) {
    pd_proto[i] = PD_ENTRY(sentry_page_table, 0);
  }

  int video_pages = (VIDEO_MEM_SIZE + PAGE_SIZE - 1) / PAGE_SIZE;
  pd_map_area(pd_proto, (void *)VIDEO_MEM_START,
      (void *)VIDEO_MEM_START, video_pages, 0);

  int kernel_pages = KMEM_MAX / PAGE_SIZE;
  pd_map_area(pd_proto, (void *)KERNEL_MEM_START,
      (void *)KERNEL_MEM_START, kernel_pages, 0);
}

pd_t
pd_create(void) {
  pd_t pd = kmalloc_align(PAGE_SIZE, PD_SIZE);
  memcpy(pd, pd_proto, PD_SIZE);
  return pd;
}

void
pd_destroy(pd_t pd) {
  // TODO: free allocated page tables
  kfree(pd);
}

void pd_map_area(pd_t pd_base, void *virt_addr_p, void *phys_addr_p, int num_pages, uint32_t flags) {
  for (int i=0; i<num_pages; ++i) {
    pd_map_page(pd_base, virt_addr_p, phys_addr_p, flags);
    virt_addr_p += PAGE_SIZE;
    phys_addr_p += PAGE_SIZE;
  }
}

void pd_map_page(pd_t pd_base, void *virt_addr_p, void *phys_addr_p, uint32_t flags) {
  uint32_t virt_addr = (uint32_t)virt_addr_p;
  uint32_t phys_addr = (uint32_t)phys_addr_p;
  uint32_t pd_offset = virt_addr >> (PAGE_SIZE_SHIFT + 10);
  uint32_t pd_entry = pd_base[pd_offset];

  if (!(pd_entry & PD_FLAG_PRESENT)) {
    pd_entry = pd_base[pd_offset] = pt_init();
  }

  uint32_t *pt_base = (uint32_t *)(pd_entry & 0xfffff000);
  uint32_t pt_offset = (virt_addr >> PAGE_SIZE_SHIFT) & 0x3ff;
  pt_base[pt_offset] = PT_ENTRY(phys_addr, flags | PT_FLAG_PRESENT);

  asm volatile ("invlpg (%0)" : : "r" (virt_addr) : "memory");
}

static uint32_t pt_init(void) {
  void *pt_addr = kmalloc_align(PAGE_SIZE, PT_SIZE);
  memset(pt_addr, 0, PT_SIZE);

  return PD_ENTRY(pt_addr, PD_FLAG_PRESENT | PD_FLAG_USER | PD_FLAG_WRITABLE);
}

uint32_t
pd_get_phys(pd_t pd_base, void *virt_addr_p) {
  uint32_t virt_addr = (uint32_t)virt_addr_p;
  uint32_t pd_offset = virt_addr >> (PAGE_SIZE_SHIFT + 10);
  uint32_t pd_entry = pd_base[pd_offset];

  if (!(pd_entry & PD_FLAG_PRESENT)) {
    return 0;
  }

  uint32_t *pt_base = (uint32_t *)(pd_entry & 0xfffff000);
  uint32_t pt_offset = (virt_addr >> PAGE_SIZE_SHIFT) & 0x3ff;
  uint32_t phys_addr_base = pt_base[pt_offset] & 0xfffff000;

  return phys_addr_base | (virt_addr & 0xfff);
}