]> git.corax.cc Git - corax/commitdiff
Implement new frame map for 64bit physical address spaces
authorMatthias Kruk <m@m10k.eu>
Wed, 30 Dec 2020 01:57:58 +0000 (10:57 +0900)
committerMatthias Kruk <m@m10k.eu>
Wed, 30 Dec 2020 01:57:58 +0000 (10:57 +0900)
The current frame map implementation does not support physical
address spaces larger than 32 bits, which is necessary for PAE
support. This commit adds a new frame map that can be used with
64 bit address spaces.

kernel/arch/frame.c [new file with mode: 0644]
kernel/arch/frame.h [new file with mode: 0644]

diff --git a/kernel/arch/frame.c b/kernel/arch/frame.c
new file mode 100644 (file)
index 0000000..484eb10
--- /dev/null
@@ -0,0 +1,290 @@
+#include <config.h>
+#include <corax/types.h>
+#include <arch.h>
+#include <multiboot.h>
+#include <string.h>
+#include "frame.h"
+
+static u64_t *_frame_map;
+static u64_t _mem_end;
+
+#define FRAMES_PER_CHUNK      (sizeof(*_frame_map) * BITS_PER_BYTE)
+
+#define ADDR_TO_FRAME(addr)   ((addr) / FRAME_SIZE)
+#define FRAME_TO_CHUNK(frame) ((frame) / FRAMES_PER_CHUNK)
+#define FRAME_TO_INDEX(frame) ((frame) % FRAMES_PER_CHUNK)
+
+/*
+ * _frame_set - Mark a frame as in-use
+ *
+ * SYNOPSIS
+ *  void _frame_set(const u64_t addr)
+ *
+ * DESCRIPTION
+ *  The _frame_set() function marks the frame indicated by `addr' as in-use in the frame map.
+ *  `addr' may be any address contained within the frame to be marked.
+ *
+ * RETURN VALUE
+ *  void
+ *
+ * ERRORS
+ *  This function does not return information about any error conditions.
+ */
+static inline void _frame_set(const u64_t addr)
+{
+       u64_t frame;
+
+       frame = ADDR_TO_FRAME(addr);
+       _frame_map[FRAME_TO_CHUNK(frame)] |= (1 << FRAME_TO_INDEX(frame));
+
+       return;
+}
+
+/*
+ * _frame_clear - Mark a frame as not-in-use
+ *
+ * SYNOPSIS
+ *  void _frame_clear(const u64_t addr)
+ *
+ * DESCRIPTION
+ *  The _frame_clear() function marks the frame indicated by `addr' as not-in-use in the frame
+ *  map. `addr' may be any address contained within the frame to be marked.
+ *
+ * RETURN VALUE
+ *  void
+ *
+ * ERRORS
+ *  This function does not return information about any error conditions.
+ */
+static inline void _frame_clear(const u64_t addr)
+{
+       u64_t frame;
+
+       frame = ADDR_TO_FRAME(addr);
+       _frame_map[FRAME_TO_CHUNK(frame)] &= ~(1 << FRAME_TO_INDEX(frame));
+
+       return;
+}
+
+/*
+ * _frame_get - Return the state of a frame in the frame-map
+ *
+ * SYNOPSIS
+ *  u64_t _frame_get(const u64_t addr)
+ *
+ * DESCRIPTION
+ *  The _frame_get() function returns the state of the frame indicated by `addr' in the frame
+ *  map. `addr' may be any address within the frame to be inquired about.
+ *
+ * RETURN VALUE
+ *  Zero if the frame is not in use, or a non-zero value otherwise.
+ *
+ * ERRORS
+ *  This function does not return information about any error conditions.
+ */
+static inline u64_t _frame_get(const u64_t addr)
+{
+       u64_t frame;
+
+       frame = ADDR_TO_FRAME(addr);
+
+       return(_frame_map[FRAME_TO_CHUNK(frame)] & (1 << FRAME_TO_INDEX(frame)));
+}
+
+/*
+ * _clear_available_frames() - Mark available memory as unused in the frame map
+ *
+ * SYNOPSIS
+ *  void _clear_unused_frames(struct multiboot_info *info);
+ *
+ * DESCRIPTION
+ *  The _clear_available_frames() function will iterate over the memory map from the multiboot
+ *  information pointed to by `info', and mark all frames of memory regions that are available
+ *  for general use as not-in-use in the frame map.
+ *
+ * RETURN VALUE
+ *  void
+ *
+ * ERRORS
+ *  This function does not signal any errors.
+ */
+void _clear_unused_frames(struct multiboot_info *info)
+{
+        struct memory_map *mmap;
+
+        for(mmap = (struct memory_map*)info->mmap_addr;
+            (void*)mmap < (void*)(info->mmap_addr + info->mmap_length);
+            mmap = (struct memory_map*)((void*)mmap + mmap->size + sizeof(mmap->size))) {
+                u64_t addr;
+
+                if(mmap->type != MEM_AVAILABLE) {
+                        continue;
+                }
+
+                for(addr = mmap->addr; addr < (mmap->addr + mmap->len); addr += FRAME_SIZE) {
+                        _frame_clear(addr);
+                }
+        }
+
+        return;
+}
+
+/*
+ * _get_memory_end() - Get the upper end of usable memory
+ *
+ * SYNOPSIS
+ *  u64_t _get_memory_end(struct multiboot_info *info);
+ *
+ * DESCRIPTION
+ *  The _get_memory_end() function determines the upper end of the usable memory by inspecting \
+the
+ *  multiboot information pointed to by `info'. It iterates over the memory map provided by the
+ *  bootloader and simply computes the end of the highest memory region that the bootloader
+ *  recons to be available for use by the operating system.
+ *
+ * RETURN VALUE
+ *  Upon success, the first address after the highest usable memory region is returned. If the
+ *  address could not be determined, zero is returned.
+ *
+ * ERRORS
+ *  This function does not signal any errors.
+ */
+static u64_t _get_memory_end(struct multiboot_info *info)
+{
+        struct memory_map *mmap;
+        u64_t mem_end;
+
+        mem_end = 0;
+
+        for(mmap = (struct memory_map*)info->mmap_addr;
+            (void*)mmap < (void*)(info->mmap_addr + info->mmap_length);
+            mmap = (struct memory_map*)((void*)mmap + mmap->size + sizeof(mmap->size))) {
+                if(mmap->type != MEM_AVAILABLE) {
+                        continue;
+                }
+
+                if((mmap->addr + mmap->len) > mem_end) {
+                        mem_end = mmap->addr + mmap->len;
+                }
+        }
+
+        return(mem_end);
+}
+
+/*
+ * frame_init - Initialize the frame map
+ *
+ * SYNOPSIS
+ *  void frame_init(struct memory_map *mmap, const u32_t mmap_length)
+ *
+ * DESCRIPTION
+ *  The frame_init() function initializes the frame map that is used to remember which frames
+ *  in the systems are occupied or free. The size of the map is determined from the multiboot
+ *  memory map pointed to by `mmap', and the frame map is initialized according to the memory
+ *  regions in the memory map.
+ *
+ * RETURN VALUE
+ *  void
+ *
+ * ERRORS
+ *  This function does not return information about any error conditions.
+ */
+void frame_init(struct multiboot_info *info)
+{
+       extern u64_t _mem_start;
+       u64_t map_bytes;
+       u64_t num_frames;
+
+       _mem_end = _get_memory_end(info);
+
+       num_frames = _mem_end / FRAME_SIZE;
+       map_bytes = num_frames / BITS_PER_BYTE;
+
+       if(!ALIGNED(_mem_start, sizeof(*_frame_map))) {
+               _mem_start = ALIGN(_mem_start, sizeof(*_frame_map));
+       }
+       _frame_map = (u64_t*)((u32_t)_mem_start);
+
+       memset(_frame_map, 0xff, map_bytes);
+
+       dbg_printf("%lluKB of memory (%llu frames) to account for\n", _mem_end / 1024, num_frames);
+
+       _clear_unused_frames(info);
+
+       _mem_start += map_bytes;
+
+       return;
+}
+
+static int _n_free_frames_r(u64_t addr, unsigned n)
+{
+       while(n > 0) {
+               if(_frame_get(addr)) {
+                       return(0);
+               }
+
+               addr -= FRAME_SIZE;
+               n--;
+       }
+
+       return(1);
+}
+
+static u64_t _find_contiguous_frames_r(const unsigned num_frames, const u64_t addr)
+{
+       u64_t cur_addr;
+       u64_t tsize;
+
+       tsize = (u64_t)num_frames * (u64_t)FRAME_SIZE;
+
+       for(cur_addr = addr; cur_addr > tsize; cur_addr -= FRAME_SIZE) {
+               if(_n_free_frames_r(cur_addr, num_frames)) {
+                       return(cur_addr - tsize);
+               }
+       }
+
+       return(-1ULL);
+}
+
+u64_t frame_alloc(const u64_t size, const int flags)
+{
+       u64_t phys_size;
+       u64_t num_frames;
+       u64_t limit;
+       u64_t addr;
+
+       phys_size = ALIGN(size, FRAME_SIZE);
+       num_frames = phys_size / FRAME_SIZE;
+       limit = _mem_end;
+
+       if(flags & FRAME_ALLOC_4G && _mem_end >= 0x100000000LL) {
+               limit = 0xfffff000;
+       }
+
+//     dbg_printf("frame_alloc(0x%016llx, 0x%02x) limit = 0x%016llx\n",
+//                size, flags, limit);
+
+       addr = _find_contiguous_frames_r(num_frames, limit);
+
+//     dbg_printf("addr = 0x%016llx\n", addr);
+
+       if(addr != -1ULL) {
+               while(num_frames > 0) {
+                       _frame_set(addr + FRAME_SIZE * num_frames);
+                       num_frames--;
+               }
+       }
+
+       return(addr);
+}
+
+void frame_free(const u64_t addr, const u64_t size)
+{
+       u64_t cur_addr;
+
+       for(cur_addr = addr; cur_addr < addr + size; cur_addr += FRAME_SIZE) {
+               _frame_clear(cur_addr);
+       }
+
+       return;
+}
diff --git a/kernel/arch/frame.h b/kernel/arch/frame.h
new file mode 100644 (file)
index 0000000..93028e3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FRAME_H
+#define __FRAME_H
+
+#define FRAME_SIZE      PAGE_SIZE
+#define FRAME_ALLOC_4G  (1 << 0)
+
+struct multiboot_info;
+
+void  frame_init(struct multiboot_info *info);
+u64_t frame_alloc(const u64_t size, const int flags);
+void  frame_free(const u64_t addr, const u64_t size);
+
+#endif /* __FRAME_H */