summaryrefslogtreecommitdiff
path: root/src/kernel/mem.c
blob: d3baad3d90630f38ef60baa549feec6cf0bd6547 (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
#include <stdint.h>
#include "panic.h"
#include "mem.h"

#define DYNAMIC_START (0x10000000)
#define DYNAMIC_END   (DYNAMIC_START + 65536 * 4096)
#define PAGE_TO_ADDR(n) ((void *)(DYNAMIC_START | ((n) << 12)))
#define ADDR_TO_PAGE(n) (((uint32_t)(n) & ~DYNAMIC_START) >> 12)

#define MMAP_START (0x00010000)
#define PAGE_USED(n) ((*(uint8_t *)(MMAP_START + (n >> 3)) >> (n & 7)) & 1)
#define CLEAR_PAGE(n)  *(uint8_t *)(MMAP_START + (n >> 3)) &= ~(1 << (n & 7))
#define SET_PAGE(n)    *(uint8_t *)(MMAP_START + (n >> 3)) |=   1 << (n & 7)

extern const void kernel_bss_end;

uint16_t pages_left;

void init_mmap() {
  volatile uint8_t *end_ptr = (uint8_t *)(DYNAMIC_END - 1);
  uint8_t end_val = *end_ptr;
  *end_ptr = (uint8_t)~end_val;
  if (*end_ptr != (uint8_t)~end_val)
    panic("Not enough memory. Must have at least 512MiB.");

  for (uint32_t *m = (uint32_t *)MMAP_START; m < (uint32_t *)(MMAP_START + (65536 / 8)); ++m)
    *m = 0;

  uint16_t kernel_bss_pages = (((uint32_t)&kernel_bss_end - DYNAMIC_START - 1) >> 12) + 1;
  for (uint16_t i = 0; i < kernel_bss_pages; ++i)
    SET_PAGE(i);

  pages_left = 65536 - kernel_bss_pages;
}

//very inneficient algorithm, just returns first hole big enough.
//a smarter algorithm might pick the smallest one available,
//and go by bytes (or dwords) instead of bits where possible.
void *allocate_pages(uint16_t n) {
  uint16_t run = 0;
  
  for (uint32_t page = 0; page < 65536; ++page) {
    if (PAGE_USED(page))
      run = 0;
    else if (++run == n) {
      uint16_t start = page - run + 1;
      for (uint32_t i = start; i <= page; ++i)
        SET_PAGE(i);
      pages_left -= n;
      return PAGE_TO_ADDR(start);
    }
  }

  return 0;
}

//in the future, change this to go by bytes or dwords instead of bits.
void free_pages(const void *ptr, uint16_t n) {
  uint16_t page = ADDR_TO_PAGE(ptr);
  for (uint32_t i = page; i < page + n; ++i)
    CLEAR_PAGE(i);
  pages_left += n;
}