// kern/init/init.cintkern_init(void) {externchar edata[], end[];memset(edata,0, end - edata);cons_init(); // init the consoleconstchar*message ="(THU.CST) os is loading ...\0";cputs(message);print_kerninfo();idt_init(); // init interrupt descriptor tablepmm_init(); // 新东西!clock_init(); // init clock interruptintr_enable(); // enable irq interrupt /* do nothing */while (1) ;}// kern/mm/pmm.c/* pmm_init - initialize the physical memory management */voidpmm_init(void) {// We need to alloc/free the physical memory (granularity is 4KB or other size).// So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h// First we should init a physical memory manager(pmm) based on the framework.// Then pmm can alloc/free the physical memory.init_pmm_manager();// detect physical memory space, reserve already used memory,// then use pmm->init_memmap to create free page listpage_init();// use pmm->check to verify the correctness of the alloc/free function in a pmmcheck_alloc_page();externchar boot_page_table_sv39[]; //我们把汇编里定义的页表所在位置的符号声明进来 satp_virtual = (pte_t*)boot_page_table_sv39; satp_physical =PADDR(satp_virtual);//然后输出页表所在的地址cprintf("satp virtual address: 0x%016lx\nsatp physical address: 0x%016lx\n", satp_virtual, satp_physical);}
// libs/defs.h/* Return the offset of 'member' relative to the beginning of a struct type */#defineoffsetof(type, member) \ ((size_t)(&((type *)0)->member))/* * * to_struct - get the struct from a ptr * @ptr: a struct pointer of member * @type: the type of the struct this is embedded in * @member: the name of the member within the struct * */#defineto_struct(ptr, type, member) \ ((type *)((char*)(ptr) -offsetof(type, member)))// kern/mm/memlayout.h/* * * struct Page - Page descriptor structures. Each Page describes one * physical page. In kern/mm/pmm.h, you can find lots of useful functions * that convert Page to other data types, such as physical address. * */struct Page {int ref; // page frame's reference counteruint64_t flags; // array of flags that describe the status of the page frameunsignedint property; // the num of free block, used in first fit pm managerlist_entry_t page_link; // free list link};/* Flags describing the status of a page frame */#define PG_reserved 0 // if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0
#define PG_property 1 // if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page.
//这几个对page操作的宏用到了atomic.h的原子操作#defineSetPageReserved(page) set_bit(PG_reserved,&((page)->flags))#defineClearPageReserved(page) clear_bit(PG_reserved,&((page)->flags))#definePageReserved(page) test_bit(PG_reserved,&((page)->flags))#defineSetPageProperty(page) set_bit(PG_property,&((page)->flags))#defineClearPageProperty(page) clear_bit(PG_property,&((page)->flags))#definePageProperty(page) test_bit(PG_property,&((page)->flags))// convert list entry to page#definele2page(le, member) \to_struct((le),struct Page, member)/* free_area_t - maintains a doubly linked list to record free (unused) pages */typedefstruct {list_entry_t free_list; // the list headerunsignedint nr_free; // # of free pages in this free list} free_area_t;
// kern/mm/pmm.h/* * * PADDR - takes a kernel virtual address (an address that points above * KERNBASE), * where the machine's maximum 256MB of physical memory is mapped and returns * the * corresponding physical address. It panics if you pass it a non-kernel * virtual address. * */#definePADDR(kva) \ ({ \uintptr_t __m_kva = (uintptr_t)(kva); \if (__m_kva < KERNBASE) { \panic("PADDR called with invalid kva %08lx", __m_kva); \ } \ __m_kva - va_pa_offset; \ })/* * * KADDR - takes a physical address and returns the corresponding kernel virtual * address. It panics if you pass an invalid physical address. * *//*#define KADDR(pa) \ ({ \ uintptr_t __m_pa = (pa); \ size_t __m_ppn = PPN(__m_pa); \ if (__m_ppn >= npage) { \ panic("KADDR called with invalid pa %08lx", __m_pa); \ } \ (void *)(__m_pa + va_pa_offset); \ })*/externstruct Page *pages;externsize_t npage;// kern/mm/pmm.c// pages指针保存的是第一个Page结构体所在的位置,也可以认为是Page结构体组成的数组的开头// 由于C语言的特性,可以把pages作为数组名使用,pages[i]表示顺序排列的第i个结构体struct Page *pages;size_t npage =0;uint64_t va_pa_offset;// memory starts at 0x80000000 in RISC-Vconstsize_t nbase = DRAM_BASE / PGSIZE;//(npage - nbase)表示物理内存的页数staticvoidpage_init(void) { va_pa_offset = PHYSICAL_MEMORY_OFFSET; //硬编码 0xFFFFFFFF40000000uint64_t mem_begin = KERNEL_BEGIN_PADDR;//硬编码 0x80200000uint64_t mem_size = PHYSICAL_MEMORY_END - KERNEL_BEGIN_PADDR;uint64_t mem_end = PHYSICAL_MEMORY_END; //硬编码 0x88000000cprintf("physcial memory map:\n");cprintf(" memory: 0x%016lx, [0x%016lx, 0x%016lx].\n", mem_size, mem_begin, mem_end -1);uint64_t maxpa = mem_end;if (maxpa > KERNTOP) { maxpa = KERNTOP; } npage = maxpa / PGSIZE;externchar end[]; pages = (struct Page *)ROUNDUP((void*)end, PGSIZE);//把pages指针指向内核所占内存空间结束后的第一页//一开始把所有页面都设置为保留给内核使用的,之后再设置哪些页面可以分配给其他程序for (size_t i =0; i < npage - nbase; i++) {SetPageReserved(pages + i);//记得吗?在kern/mm/memlayout.h定义的 }//从这个地方开始才是我们可以自由使用的物理内存uintptr_t freemem =PADDR((uintptr_t)pages +sizeof(struct Page) * (npage - nbase));//按照页面大小PGSIZE进行对齐, ROUNDUP, ROUNDDOWN是在libs/defs.h定义的 mem_begin =ROUNDUP(freemem, PGSIZE); mem_end =ROUNDDOWN(mem_end, PGSIZE);if (freemem < mem_end) {//初始化我们可以自由使用的物理内存init_memmap(pa2page(mem_begin), (mem_end - mem_begin) / PGSIZE); }}
page_init()的代码里,我们调用了一个函数init_memmap(), 这和我们的另一个结构体pmm_manager有关。虽然C语言基本上不支持面向对象,但我们可以用类似面向对象的思路,把”物理内存管理“的功能集中给一个结构体。我们甚至可以让函数指针作为结构体的成员,强行在C语言里支持了”成员函数“。可以看到,我们调用的init_memmap()实际上又调用了pmm_manager的一个”成员函数“。如果你不熟悉函数指针的用法,可以读一读《The C Programming Language》的相关章节(待补充:第几章第几节?)。
// kern/mm/pmm.c// physical memory managementconststruct pmm_manager *pmm_manager;// init_memmap - call pmm->init_memmap to build Page struct for free memorystaticvoidinit_memmap(struct Page *base,size_t n) {pmm_manager->init_memmap(base, n);}// kern/mm/pmm.h#ifndef__KERN_MM_PMM_H__#define__KERN_MM_PMM_H__#include<assert.h>#include<atomic.h>#include<defs.h>#include<memlayout.h>#include<mmu.h>#include<riscv.h>// pmm_manager is a physical memory management class. A special pmm manager -// XXX_pmm_manager// only needs to implement the methods in pmm_manager class, then// XXX_pmm_manager can be used// by ucore to manage the total physical memory space.struct pmm_manager {constchar*name; // XXX_pmm_manager's namevoid (*init)(void); // 初始化XXX_pmm_manager内部的数据结构(如空闲页面的链表)void (*init_memmap)(struct Page *base,size_t n); //知道了可用的物理页面数目之后,进行更详细的初始化struct Page *(*alloc_pages)(size_t n); // 分配至少n个物理页面, 根据分配算法可能返回不同的结果void (*free_pages)(struct Page *base,size_t n); // free >=n pages with// "base" addr of Page// descriptor// structures(memlayout.h)size_t (*nr_free_pages)(void); // 返回空闲物理页面的数目void (*check)(void); // 测试正确性};externconststruct pmm_manager *pmm_manager;voidpmm_init(void);struct Page *alloc_pages(size_t n);voidfree_pages(struct Page *base,size_t n);size_tnr_free_pages(void); // number of free pages#definealloc_page() alloc_pages(1)#definefree_page(page) free_pages(page,1)