diff --git a/kernel/common/glib.h b/kernel/common/glib.h index e6e7b425..8d6d027d 100644 --- a/kernel/common/glib.h +++ b/kernel/common/glib.h @@ -114,8 +114,9 @@ static inline int strlen(char *s) -inline void *memset(void *dst, unsigned char C, ul Count) +void *memset(void *dst, unsigned char C, ul Count) { + int d0, d1; unsigned long tmp = C * 0x0101010101010101UL; __asm__ __volatile__("cld \n\t" @@ -136,3 +137,10 @@ inline void *memset(void *dst, unsigned char C, ul Count) : "memory"); return dst; } +void *memset_c(void *dst, unsigned char c, ul n) +{ + unsigned char *s = (unsigned char *)dst; + for(int i=0;iBaseAddrH, ards_ptr->BaseAddrL, ards_ptr->LengthH, ards_ptr->LengthL, ards_ptr->type); + //printk("Addr = %#18lx\tLength = %#18lx\tType = %#10lx\n", + // ards_ptr->BaseAddr, ards_ptr->Length, ards_ptr->type); //可用的内存 if (ards_ptr->type == 1) - { - Total_Memory += ards_ptr->LengthL; - Total_Memory += ((ul)(ards_ptr->LengthH)) << 32; - } + Total_Memory += ards_ptr->Length; + + // 保存信息到mms + memory_management_struct.e820[i].BaseAddr = ards_ptr->BaseAddr; + memory_management_struct.e820[i].Length = ards_ptr->Length; + memory_management_struct.e820[i].type = ards_ptr->type; + memory_management_struct.len_e820 = i; ++ards_ptr; // 脏数据 - if (ards_ptr->type > 4) + if (ards_ptr->type > 4 || ards_ptr->Length == 0 || ards_ptr->type < 1) break; } - printk_color(ORANGE, BLACK, "Total amount of RAM DragonOS can use: %ld bytes\n", Total_Memory); + printk("[ INFO ] Total amounts of RAM : %ld bytes\n", Total_Memory); + + // 计算有效内存页数 + + for (int i = 0; i < memory_management_struct.len_e820; ++i) + { + if (memory_management_struct.e820[i].type != 1) + continue; + + // 将内存段的起始物理地址按照2M进行对齐 + ul addr_start = PAGE_2M_ALIGN(memory_management_struct.e820[i].BaseAddr); + // 将内存段的终止物理地址的低2M区域清空,以实现对齐 + ul addr_end = ((memory_management_struct.e820[i].BaseAddr + memory_management_struct.e820[i].Length) & PAGE_2M_MASK); + + // 内存段不可用 + if (addr_end <= addr_start) + continue; + + total_2M_pages += ((addr_end - addr_start) >> PAGE_2M_SHIFT); + } + printk("[ INFO ] Total amounts of 2M pages : %ld.\n", total_2M_pages); + + // 设置内核程序不同部分的起止地址 + memory_management_struct.kernel_code_start = (ul)&_text; + memory_management_struct.kernel_code_end = (ul)&_etext; + memory_management_struct.kernel_data_end = (ul)&_edata; + memory_management_struct.kernel_end = (ul)&_end; + + // 物理地址空间的最大地址(包含了物理内存、内存空洞、ROM等) + ul max_addr = memory_management_struct.e820[memory_management_struct.len_e820].BaseAddr + memory_management_struct.e820[memory_management_struct.len_e820].Length; + // 初始化mms的bitmap + // bmp的指针指向截止位置的4k对齐的上边界(防止修改了别的数据) + memory_management_struct.bmp = (unsigned long *)((memory_management_struct.kernel_end + PAGE_4K_SIZE - 1) & PAGE_4K_MASK); + memory_management_struct.bits_size = max_addr >> PAGE_2M_SHIFT; // 物理地址空间的最大页面数 + memory_management_struct.bmp_len = ((unsigned long)((max_addr >> PAGE_2M_SHIFT) + sizeof(unsigned long) * 8 - 1) / 8) & (~(sizeof(unsigned long) - 1)); // bmp由多少个unsigned long变量组成 + + + // 初始化bitmap, 先将整个bmp空间全部置位。稍后再将可用物理内存页复位。 + memset(memory_management_struct.bmp, 0xff, memory_management_struct.bmp_len); + + // 初始化内存页结构 + // 将页结构映射于bmp之后 + + memory_management_struct.pages_struct = (struct Page *)(((unsigned long)memory_management_struct.bmp + memory_management_struct.bmp_len + PAGE_4K_SIZE - 1) & PAGE_4K_MASK); + + memory_management_struct.count_pages = max_addr >> PAGE_2M_SHIFT; + memory_management_struct.pages_struct_len = ((max_addr >> PAGE_2M_SHIFT) * sizeof(struct Page) + sizeof(long) - 1) & (~(sizeof(long) - 1)); + // 将pages_struct全部清空,以备后续初始化 + memset(memory_management_struct.pages_struct, 0x00, memory_management_struct.pages_struct_len); //init pages memory + + // 初始化内存区域 + memory_management_struct.zones_struct = (struct Zone *)(((ul)memory_management_struct.pages_struct + memory_management_struct.pages_struct_len + PAGE_4K_SIZE - 1) & PAGE_4K_MASK); + // 由于暂时无法计算zone结构体的数量,因此先将其设为0 + memory_management_struct.count_zones = 0; + // zones-struct 成员变量暂时按照5个来计算 + memory_management_struct.zones_struct_len = (5 * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1)); + memset(memory_management_struct.zones_struct, 0x00, memory_management_struct.zones_struct_len); + + // ==== 遍历e820数组,完成成员变量初始化工作 === + + for (int i = 0; i < memory_management_struct.len_e820; ++i) + { + if (memory_management_struct.e820[i].type != 1) // 不是操作系统可以使用的物理内存 + continue; + ul addr_start = PAGE_2M_ALIGN(memory_management_struct.e820[i].BaseAddr); + ul addr_end = (memory_management_struct.e820[i].BaseAddr + memory_management_struct.e820[i].Length) & PAGE_2M_MASK; + + if (addr_end <= addr_start) + continue; + + // zone init + struct Zone *z = memory_management_struct.zones_struct + memory_management_struct.count_zones; + ++memory_management_struct.count_zones; + + z->zone_addr_start = addr_start; + z->zone_addr_end = addr_end; + z->zone_length = addr_end - addr_start; + + z->count_pages_using = 0; + z->count_pages_free = (addr_end - addr_start) >> PAGE_2M_SHIFT; + z->total_pages_link = 0; + + z->attr = 0; + z->gmd_struct = &memory_management_struct; + + z->count_pages = (addr_end - addr_start) >> PAGE_2M_SHIFT; + z->pages_group = (struct Page *)(memory_management_struct.pages_struct + (addr_start >> PAGE_2M_SHIFT)); + + // 初始化页 + struct Page *p = z->pages_group; + + for (int j = 0; j < z->count_pages; ++j, ++p) + { + p->zone = z; + p->addr_phys = addr_start + PAGE_2M_SIZE * j; + p->attr = 0; + + p->ref_counts = 0; + p->age = 0; + + // 将bmp中对应的位 复位 + *(memory_management_struct.bmp + ((p->addr_phys >> PAGE_2M_SHIFT) >> 6)) ^= (1UL << ((p->addr_phys >> PAGE_2M_SHIFT) % 64)); + } + } + + // 初始化0~2MB的物理页 + // 由于这个区间的内存由多个内存段组成,因此不会被以上代码初始化,需要我们手动配置page[0]。 + + memory_management_struct.pages_struct->zone = memory_management_struct.zones_struct; + memory_management_struct.pages_struct->addr_phys = 0UL; + memory_management_struct.pages_struct->attr = 0; + memory_management_struct.pages_struct->ref_counts = 0; + memory_management_struct.pages_struct->age = 0; + + // 计算zone结构体的总长度(按照64位对齐) + memory_management_struct.zones_struct_len = (memory_management_struct.count_zones * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1)); + + printk_color(ORANGE, BLACK, "bmp:%#18lx, bmp_len:%#18lx, bits_size:%#18lx\n", memory_management_struct.bmp, memory_management_struct.bmp_len, memory_management_struct.bits_size); + + printk_color(ORANGE, BLACK, "pages_struct:%#18lx, count_pages:%#18lx, pages_struct_len:%#18lx\n", memory_management_struct.pages_struct, memory_management_struct.count_pages, memory_management_struct.pages_struct_len); + + printk_color(ORANGE, BLACK, "zones_struct:%#18lx, count_zones:%#18lx, zones_struct_len:%#18lx\n", memory_management_struct.zones_struct, memory_management_struct.count_zones, memory_management_struct.zones_struct_len); + + ZONE_DMA_INDEX = 0; //need rewrite in the future + ZONE_NORMAL_INDEX = 0; //need rewrite in the future + + for (int i = 0; i < memory_management_struct.count_zones; ++i) //need rewrite in the future + { + struct Zone *z = memory_management_struct.zones_struct + i; + printk_color(ORANGE, BLACK, "zone_addr_start:%#18lx, zone_addr_end:%#18lx, zone_length:%#18lx, pages_group:%#18lx, count_pages:%#18lx\n", + z->zone_addr_start, z->zone_addr_end, z->zone_length, z->pages_group, z->count_pages); + + // 1GB以上的内存空间不做映射 + if (z->zone_addr_start == 0x100000000) + ZONE_UNMAPED_INDEX = i; + } + // 设置内存页管理结构的地址,预留了一段空间,防止内存越界。 + memory_management_struct.end_of_struct = (ul)((ul)memory_management_struct.zones_struct + memory_management_struct.zones_struct_len + sizeof(long) * 32) & (~(sizeof(long) - 1)); + + printk_color(ORANGE, BLACK, "code_start:%#18lx, code_end:%#18lx, data_end:%#18lx, kernel_end:%#18lx, end_of_struct:%#18lx\n", + memory_management_struct.kernel_code_start, memory_management_struct.kernel_code_end, memory_management_struct.kernel_data_end, memory_management_struct.kernel_end, memory_management_struct.end_of_struct); + + // 初始化内存管理单元结构所占的物理页的结构体 + + ul mms_max_page = (virt_2_phys(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT); // 内存管理单元所占据的序号最大的物理页 + + for (ul j = 0; j <= mms_max_page; ++j) + { + page_init(memory_management_struct.pages_struct+j, PAGE_PGT_MAPPED|PAGE_KERNEL|PAGE_KERNEL_INIT|PAGE_ACTIVE); + } + + ul* cr3 = get_CR3(); + + printk_color(INDIGO, BLACK, "cr3:\t%#018lx\n", cr3); + printk_color(INDIGO, BLACK, "*cr3:\t%#018lx\n", *(phys_2_virt(cr3))&(~0xff)); + printk_color(INDIGO, BLACK, "**cr3:\t%#018lx\n", *phys_2_virt(*(phys_2_virt(cr3))&(~0xff))&(~0xff)); + + // 消除一致性页表映射,将页目录(PML4E)的前10项清空 + for(int i=0;i<10;++i) + *(phys_2_virt(cr3)+i) = 0UL; + + flush_tlb(); + + printk("[ INFO ] Memory management unit initialized.\n"); + +} + +/** + * @brief 初始化内存页 + * + * @param page 内存页结构体 + * @param flags 标志位 + * 对于新页面: 初始化struct page + * 对于当前页面属性/flags中含有引用属性或共享属性时,则只增加struct page和struct zone的被引用计数。否则就只是添加页表属性,并置位bmp的相应位。 + * @return unsigned long + */ +unsigned long page_init(struct Page *page, ul flags) +{ + // 全新的页面 + if (!page->attr) + { + // 将bmp对应的标志位置位 + *(memory_management_struct.bmp + ((page->addr_phys >> PAGE_2M_SHIFT) >> 6)) |= (1UL << ((page->addr_phys >> PAGE_2M_SHIFT) % 64)); + + page->attr = flags; + ++(page->ref_counts); + ++(page->zone->count_pages_using); + --(page->zone->count_pages_free); + ++(page->zone->total_pages_link); + } + // 不是全新的页面,而是含有引用属性/共享属性 + else if ((page->attr & PAGE_REFERENCED) || (page->attr & PAGE_K_SHARE_TO_U) || (flags & PAGE_REFERENCED) || (flags & PAGE_K_SHARE_TO_U)) + { + page->attr |= flags; + ++(page->ref_counts); + ++(page->zone->total_pages_link); + } + else + { + // 将bmp对应的标志位置位 + *(memory_management_struct.bmp + ((page->addr_phys >> PAGE_2M_SHIFT) >> 6)) |= (1UL << ((page->addr_phys >> PAGE_2M_SHIFT) % 64)); + page->attr |= flags; + } + return 0; } \ No newline at end of file diff --git a/kernel/mm/mm.h b/kernel/mm/mm.h index 0411eaed..e1147777 100644 --- a/kernel/mm/mm.h +++ b/kernel/mm/mm.h @@ -1,21 +1,183 @@ #pragma once -#include"../common/glib.h" +#include "../common/glib.h" + +// 每个页表的项数 +// 64位下,每个页表4k,每条页表项8B,故一个页表有512条 +#define PTRS_PER_PGT 512 + +// 内核层的起始地址 +#define KERNEL_BASE_ADDR 0xffff800000000000 + +#define PAGE_4K_SHIFT 12 +#define PAGE_2M_SHIFT 21 +#define PAGE_1G_SHIFT 30 + +// 不同大小的页的容量 +#define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT) +#define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT) +#define PAGE_1G_SIZE (1UL << PAGE_1G_SHIFT) + +// 屏蔽低于x的数值 +#define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1)) +#define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1)) + +// 将addr按照x的上边界对齐 +#define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK) +#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK) + +// 虚拟地址与物理地址转换 +#define virt_2_phys(addr) ((unsigned long)(addr)-KERNEL_BASE_ADDR) +#define phys_2_virt(addr) ((unsigned long *)((unsigned long)(addr) + KERNEL_BASE_ADDR)) + +// ===== 页面属性 ===== +// 页面在页表中已被映射 +#define PAGE_PGT_MAPPED (1 << 0) +// 内核初始化程序的页 +#define PAGE_KERNEL_INIT (1 << 1) +// 引用的页 +#define PAGE_REFERENCED (1 << 2) +// 脏页 +#define PAGE_DIRTY (1 << 3) +// 使用中的页 +#define PAGE_ACTIVE (1 << 4) +// 过时的页 +#define PAGE_UP_TO_DATE (1 << 5) +// 设备对应的页 +#define PAGE_DEVICE (1 << 6) +// 内核层页 +#define PAGE_KERNEL (1 << 7) +// 内核共享给用户态程序的页面 +#define PAGE_K_SHARE_TO_U (1 << 8) +// slab内存分配器的页 +#define PAGE_SLAB (1 << 9) // Address Range Descriptor Structure 地址范围描述符 struct ARDS { - unsigned int BaseAddrL; // 基地址低32位 - unsigned int BaseAddrH; // 基地址高32位 - unsigned int LengthL; // 内存长度低32位 以字节为单位 - unsigned int LengthH; // 内存长度高32位 - unsigned int type; // 本段内存的类型 - // type=1 表示可以被操作系统使用 - // type=2 ARR - 内存使用中或被保留,操作系统不能使用 - // 其他 未定义,操作系统需要将其视为ARR + ul BaseAddr; // 基地址 + ul Length; // 内存长度 以字节为单位 + unsigned int type; // 本段内存的类型 + // type=1 表示可以被操作系统使用 + // type=2 ARR - 内存使用中或被保留,操作系统不能使用 + // 其他 未定义,操作系统需要将其视为ARR +} __attribute__((packed)); // 修饰该结构体不会生成对齐空间,改用紧凑格式 + +struct memory_desc +{ + + struct ARDS e820[32]; // 物理内存段结构数组 + ul len_e820; // 物理内存段长度 + + ul *bmp; // 物理空间页映射位图 + ul bmp_len; // bmp的长度 + ul bits_size; // 物理地址空间页数量 + + struct Page *pages_struct; + ul count_pages; // struct page结构体的总数 + ul pages_struct_len; // pages_struct链表的长度 + + struct Zone *zones_struct; + ul count_zones; // zone结构体的数量 + ul zones_struct_len; // zones_struct列表的长度 + + ul kernel_code_start, kernel_code_end; // 内核程序代码段起始地址、结束地址 + ul kernel_data_end, kernel_end; // 内核程序数据段结束地址、 内核程序结束地址 + + ul end_of_struct; // 内存页管理结构的结束地址 }; +struct Zone +{ + // 指向内存页的指针 + struct Page *pages_group; + ul count_pages; // 本区域的struct page结构体总数 + // 本内存区域的起始、结束的页对齐地址 + ul zone_addr_start; + ul zone_addr_end; + ul zone_length; // 区域长度 + // 本区域空间的属性 + ul attr; -void mm_init(); \ No newline at end of file + struct memory_desc *gmd_struct; + + // 本区域正在使用中和空闲中的物理页面数量 + ul count_pages_using; + ul count_pages_free; + + // 物理页被引用次数 + ul total_pages_link; +}; + +struct Page +{ + // 本页所属的内存域结构体 + struct Zone *zone; + // 本页对应的物理地址 + ul addr_phys; + // 页面属性 + ul attr; + // 页面被引用的次数 + ul ref_counts; + // 本页的创建时间 + ul age; +}; + +extern struct memory_desc memory_management_struct; + +// 导出内核程序的几个段的起止地址 +extern char _text; +extern char _etext; +extern char _edata; +extern char _end; + +// 每个区域的索引 + +int ZONE_DMA_INDEX = 0; +int ZONE_NORMAL_INDEX = 0; //low 1GB RAM ,was mapped in pagetable +int ZONE_UNMAPED_INDEX = 0; //above 1GB RAM,unmapped in pagetable + +// 初始化内存管理单元 +void mm_init(); + +/** + * @brief 初始化内存页 + * + * @param page 内存页结构体 + * @param flags 标志位 + * 对于新页面: 初始化struct page + * 对于当前页面属性/flags中含有引用属性或共享属性时,则只增加struct page和struct zone的被引用计数。否则就只是添加页表属性,并置位bmp的相应位。 + * @return unsigned long + */ +unsigned long page_init(struct Page *page, ul flags); + +/** + * @brief 读取CR3寄存器的值(存储了页目录的基地址) + * + * @return unsigned* cr3的值的指针 + */ +unsigned long *get_CR3() +{ + ul *tmp; + __asm__ __volatile__( + "movq %%cr3, %0\n\t" + : "=r"(tmp)::"memory"); + return tmp; +} + +/** + * @brief 刷新TLB的宏定义 + * 由于任何写入cr3的操作都会刷新TLB,因此这个宏定义可以刷新TLB + */ +#define flush_tlb() \ + do \ + { \ + ul tmp; \ + __asm__ __volatile__( \ + "movq %%cr3, %0\n\t" \ + "movq %0, %%cr3\n\t" \ + : "=r"(tmp)::"memory"); \ + \ + } while (0); diff --git a/run_in_bochs.sh b/run_in_bochs.sh index c9362ba0..16342f49 100644 --- a/run_in_bochs.sh +++ b/run_in_bochs.sh @@ -8,7 +8,7 @@ fi # 第一个参数如果是--notbuild 那就不构建,直接运行 if [ ! "$1" == "--nobuild" ]; then echo "开始构建..." - make all + make all -j 16 make clean fi