From: Matthias Kruk Date: Wed, 30 Dec 2020 01:57:58 +0000 (+0900) Subject: Implement new frame map for 64bit physical address spaces X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=b96dc099e023b7071be9b8320a3383ddfb97bed7;p=corax Implement new frame map for 64bit physical address spaces 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. --- diff --git a/kernel/arch/frame.c b/kernel/arch/frame.c new file mode 100644 index 0000000..484eb10 --- /dev/null +++ b/kernel/arch/frame.c @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include +#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 index 0000000..93028e3 --- /dev/null +++ b/kernel/arch/frame.h @@ -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 */