转自:http://blog.csdn.net/boymax2/article/details/52550197

  1. 版权声明:本文为博主原创文章,未经博主允许不得转载。
  2.  
  3. Magenta内核支持虚拟地址的配置,依赖于cpu内的mmu模块。
  4.  
  5. 下面会从以下几个方面对Magenta内核内存管理方面的代码进行分析:
  6.  
  7. mmu初始化,也就是硬件mmu的初始化,以底层寄存器操作为主,汇编
  8.  
  9. pmm初始化,也就是代码中物理内存结构的初始化
  10.  
  11. vmm初始化,也就是代码中虚拟内存结构的初始化
  12.  
  13. mmu初始化
  14.  
  15. mmu初始化的代码由汇编完成,其中主要涉及了以下几个结构
  16.  
  17. TLB:内存映射表,其定义位于c代码中
  18.  
  19. kernel/arch/arm/arm/mmu.c
  20.  
  21. [cpp] view plain copy
  22.  
  23. uint32_t arm_kernel_translation_table[TT_ENTRY_COUNT] __ALIGNED() __SECTION(".bss.prebss.translation_table");
  24.  
  25. 以及初始化的内存映射关系,以qemu-virt平台
  26.  
  27. kernel/platform/qemu-virt/platform.c
  28.  
  29. [cpp] view plain copy
  30.  
  31. struct mmu_initial_mapping mmu_initial_mappings[] = {
  32. /* all of memory */
  33. {
  34. .phys = MEMORY_BASE_PHYS, // 内存物理基地址
  35. .virt = KERNEL_BASE, // 内存虚拟基地址
  36. .size = MEMORY_APERTURE_SIZE,// 虚拟内存大小
  37. .flags = ,
  38. .name = "memory"
  39. },
  40.  
  41. /* 1GB of peripherals */
  42. {
  43. .phys = PERIPHERAL_BASE_PHYS, // 外设物理基地址
  44. .virt = PERIPHERAL_BASE_VIRT, // 外设虚拟基地址
  45. .size = PERIPHERAL_BASE_SIZE, // 虚拟内存大小
  46. .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
  47. .name = "peripherals"
  48. },
  49.  
  50. /* null entry to terminate the list */
  51. { }
  52. };
  53.  
  54. 这两个结构都会在之后的汇编代码中使用。
  55.  
  56. mmu初始化的汇编代码位于内核的启动文件中,以arm32为例
  57.  
  58. 自己对arm汇编不是很熟悉,在读汇编代码时花费了比较多的时间,希望有错误能指正出来
  59.  
  60. 启动文件中与mmu相关的代码已经提取出来
  61.  
  62. 在其中主要涉及到的操作为以下几个:
  63.  
  64. 、重置mmu相关寄存器
  65.  
  66. 、计算物理地址相对虚拟地址的偏移
  67.  
  68. 、将tlb地址指向空间清零
  69.  
  70. 、遍历mmu_initial_mappings结构,计算后写入tlb
  71.  
  72. 、设置mmu相关寄存器
  73.  
  74. 、跳转至c代码
  75.  
  76. kernel/arch/arm/arm/start.S
  77.  
  78. [plain] view plain copy
  79.  
  80. #include <asm.h>
  81. #include <arch/arm/cores.h>
  82. #include <arch/arm/mmu.h>
  83. #include <kernel/vm.h>
  84.  
  85. .section ".text.boot"
  86. .globl _start
  87. _start:
  88. b platform_reset
  89. b arm_undefined
  90. b arm_syscall
  91. b arm_prefetch_abort
  92. b arm_data_abort
  93. b arm_reserved
  94. b arm_irq
  95. b arm_fiq
  96. #if WITH_SMP
  97. b arm_reset
  98. #endif
  99.  
  100. .weak platform_reset
  101. platform_reset:
  102. /* Fall through for the weak symbol */
  103.  
  104. // arm复位处理程序
  105. .globl arm_reset
  106. arm_reset:
  107. /* do some early cpu setup */
  108. // 读SCTLR寄存器,手册P1711
  109. mrc p15, , r12, c1, c0,
  110. /* i/d cache disable, mmu disabled */
  111. // cache位与mmu位置0
  112. bic r12, #(<<)
  113. bic r12, #(<< | <<)
  114. #if WITH_KERNEL_VM
  115. /* enable caches so atomics and spinlocks work */
  116. // cache位与mmu位置1
  117. orr r12, r12, #(<<)
  118. orr r12, r12, #(<<)
  119. #endif // WITH_KERNEL_VM
  120. // 写SCTLR寄存器
  121. mcr p15, , r12, c1, c0,
  122.  
  123. /* calculate the physical offset from our eventual virtual location */
  124. // 计算物理地址相对虚拟地址的偏移,用于之后的转换
  125. .Lphys_offset:
  126. ldr r4, =.Lphys_offset
  127. adr r11, .Lphys_offset
  128. sub r11, r11, r4
  129.  
  130. ...
  131.  
  132. #if ARM_WITH_MMU
  133. .Lsetup_mmu:
  134.  
  135. /* set up the mmu according to mmu_initial_mappings */
  136.  
  137. /* load the base of the translation table and clear the table */
  138. // 获取转换表地址
  139. ldr r4, =arm_kernel_translation_table
  140. // 获取转换表物理地址
  141. add r4, r4, r11
  142. /* r4 = physical address of translation table */
  143.  
  144. mov r5, #
  145. mov r6, #
  146.  
  147. /* walk through all the entries in the translation table, setting them up */
  148. // 遍历转换表结构清零
  149. :
  150. str r5, [r4, r6, lsl #]
  151. add r6, #
  152. cmp r6, #
  153. bne 0b
  154.  
  155. /* load the address of the mmu_initial_mappings table and start processing */
  156. // 获取初始映射地址
  157. ldr r5, =mmu_initial_mappings
  158. // 获取初始映射物理地址
  159. add r5, r5, r11
  160. /* r5 = physical address of mmu initial mapping table */
  161.  
  162. // 初始映射遍历绑定至转换表
  163. // 转换表的绑定 转换表中元素的高12位为物理基地址下标,低20位为mmu相关flag
  164. .Linitial_mapping_loop:
  165. // 把结构体加载到各个通用寄存器中
  166. ldmia r5!, { r6-r10 }
  167. /* r6 = phys, r7 = virt, r8 = size, r9 = flags, r10 = name */
  168.  
  169. /* round size up to 1MB alignment */
  170. // 上调size对齐1MB
  171. ubfx r10, r6, #, #
  172. add r8, r8, r10
  173. add r8, r8, #( << )
  174. sub r8, r8, #
  175.  
  176. /* mask all the addresses and sizes to 1MB boundaries */
  177. // 物理地址 虚拟地址 大小 右移20位 取高12位
  178. lsr r6, # /* r6 = physical address / 1MB */
  179. lsr r7, # /* r7 = virtual address / 1MB */
  180. lsr r8, # /* r8 = size in 1MB chunks */
  181.  
  182. /* if size == 0, end of list */
  183. // 循环边界判断
  184. cmp r8, #
  185. beq .Linitial_mapping_done
  186.  
  187. /* set up the flags */
  188. // 设置mmu相关flag,放置在r10
  189. ldr r10, =MMU_KERNEL_L1_PTE_FLAGS
  190. teq r9, #MMU_INITIAL_MAPPING_FLAG_UNCACHED
  191. ldreq r10, =MMU_INITIAL_MAP_STRONGLY_ORDERED
  192. beq 0f
  193. teq r9, #MMU_INITIAL_MAPPING_FLAG_DEVICE
  194. ldreq r10, =MMU_INITIAL_MAP_DEVICE
  195. /* r10 = mmu entry flags */
  196.  
  197. :
  198. // 计算translation_table元素的值
  199. // r10:mmu相关flag r6:物理地址高12位
  200. // r12 = r10 | (r6 << 20)
  201. // 高20位为物理地址,低12位为mmu相关flag
  202. orr r12, r10, r6, lsl #
  203. /* r12 = phys addr | flags */
  204.  
  205. /* store into appropriate translation table entry */
  206. // r4:转换表物理基地址 r7:虚拟地址对应的section
  207. // r12 -> [r4 + r7 << 2]
  208. str r12, [r4, r7, lsl #]
  209.  
  210. /* loop until we're done */
  211. // 准备下一个转换表元素的填充
  212. add r6, #
  213. add r7, #
  214. subs r8, #
  215. bne 0b
  216.  
  217. b .Linitial_mapping_loop
  218.  
  219. .Linitial_mapping_done:
  220. ...
  221.  
  222. /* set up the mmu */
  223. bl .Lmmu_setup
  224. #endif // WITH_KERNEL_VM
  225.  
  226. ...
  227. // 跳转至c程序
  228. bl lk_main
  229. b .
  230.  
  231. #if WITH_KERNEL_VM
  232. /* per cpu mmu setup, shared between primary and secondary cpus
  233. args:
  234. r4 == translation table physical
  235. r8 == final translation table physical (if using trampoline)
  236. */
  237. // 设置mmu相关寄存器
  238. // r4:转换表物理基地址
  239. // mmu相关寄存器 手册P1724
  240. .Lmmu_setup:
  241. /* Invalidate TLB */
  242. mov r12, #
  243. mcr p15, , r12, c8, c7,
  244. isb
  245.  
  246. /* Write 0 to TTBCR */
  247. // ttbcr写0
  248. mcr p15, , r12, c2, c0,
  249. isb
  250.  
  251. /* Set cacheable attributes on translation walk */
  252. // 宏MMU_TTBRx_FLAGS为 (1 << 3) | (1 << 6)
  253. orr r12, r4, #MMU_TTBRx_FLAGS
  254.  
  255. /* Write ttbr with phys addr of the translation table */
  256. // 写入ttbr0
  257. mcr p15, , r12, c2, c0,
  258. isb
  259.  
  260. /* Write DACR */
  261. // 写DACR cache相关
  262. mov r12, #0x1
  263. mcr p15, , r12, c3, c0,
  264. isb
  265.  
  266. /* Read SCTLR into r12 */
  267. // 读SCTLR寄存器,手册P1711
  268. mrc p15, , r12, c1, c0,
  269.  
  270. /* Disable TRE/AFE */
  271. // 禁用TRE和AFE标志位
  272. bic r12, #(<< | <<)
  273.  
  274. /* Turn on the MMU */
  275. // MMU使能标志位
  276. orr r12, #0x1
  277.  
  278. /* Write back SCTLR */
  279. // 写入SCTLR
  280. // MMU打开
  281. mcr p15, , r12, c1, c0,
  282. isb
  283.  
  284. /* Jump to virtual code address */
  285. // 跳转
  286. ldr pc, =1f
  287. :
  288. ...
  289.  
  290. /* Invalidate TLB */
  291. mov r12, #
  292. mcr p15, , r12, c8, c7,
  293. isb
  294.  
  295. /* assume lr was in physical memory, adjust it before returning */
  296. // 计算跳转点的虚拟地址,跳转,之后会调用lk_main
  297. sub lr, r11
  298. bx lr
  299. #endif
  300.  
  301. ...
  302.  
  303. 硬件层的内存管理相关的初始化基本完成后,会跳转到c代码
  304.  
  305. 位于kernel/top/main.c
  306.  
  307. 其中有关内存管理的函数调用顺序为:
  308.  
  309. pmm_add_arena 将物理内存加入pmm结构
  310.  
  311. vm_init_preheap 堆初始化前的准备工作(钩子)
  312.  
  313. heap_init 堆的初始化
  314.  
  315. vm_init_postheap 堆初始化后的工作(钩子)
  316.  
  317. arm_mmu_init mmu相关的调整
  318.  
  319. 首先要完成pmm初始化工作
  320.  
  321. pmm初始化主要分为以下几步:
  322.  
  323. 、通过fdt库从bootloader中获取物理内存的长度
  324.  
  325. 、在pmm中加入物理内存
  326.  
  327. 、标记fdt结构的空间
  328.  
  329. 、标记bootloader相关的空间
  330.  
  331. pmm中比较重要的一个结构体,pmm_arena_t代表着一块物理内存的抽象
  332.  
  333. kernel/include/kernel/vm.h
  334.  
  335. [cpp] view plain copy
  336.  
  337. typedef struct pmm_arena {
  338. struct list_node node; // 节点,物理内存链表
  339. const char* name; // 名称
  340.  
  341. uint flags;
  342. uint priority;
  343.  
  344. paddr_t base; // 物理内存基地址
  345. size_t size; // 物理内存长度
  346.  
  347. size_t free_count; // 空闲的页数
  348.  
  349. struct vm_page* page_array; // 页结构数组
  350. struct list_node free_list; // 节点,该内存中空闲空间的链表
  351. } pmm_arena_t;
  352.  
  353. 接着以qemu-virtplatform为例,分析pmm初始化的过程
  354.  
  355. kernel/platform/qemu-virt.c
  356.  
  357. [cpp] view plain copy
  358.  
  359. // 全局物理内存结构体
  360. static pmm_arena_t arena = {
  361. .name = "ram",
  362. .base = MEMORY_BASE_PHYS,
  363. .size = DEFAULT_MEMORY_SIZE,
  364. .flags = PMM_ARENA_FLAG_KMAP,
  365. };
  366. ...
  367. // 该函数为平台的早期初始化,在内核启动时调用
  368. void platform_early_init(void)
  369. {
  370. ...
  371. /* look for a flattened device tree just before the kernel */
  372. // 获取fdt结构
  373. const void *fdt = (void *)KERNEL_BASE;
  374. int err = fdt_check_header(fdt);
  375. if (err >= ) {
  376. /* walk the nodes, looking for 'memory' and 'chosen' */
  377. int depth = ;
  378. int offset = ;
  379. for (;;) {
  380. offset = fdt_next_node(fdt, offset, &depth);
  381. if (offset < )
  382. break;
  383.  
  384. /* get the name */
  385. const char *name = fdt_get_name(fdt, offset, NULL);
  386. if (!name)
  387. continue;
  388.  
  389. /* look for the properties we care about */
  390. // 从fdt中查找到内存信息
  391. if (strcmp(name, "memory") == ) {
  392. int lenp;
  393. const void *prop_ptr = fdt_getprop(fdt, offset, "reg", &lenp);
  394. if (prop_ptr && lenp == 0x10) {
  395. /* we're looking at a memory descriptor */
  396. //uint64_t base = fdt64_to_cpu(*(uint64_t *)prop_ptr);
  397. // 获取内存长度
  398. uint64_t len = fdt64_to_cpu(*((const uint64_t *)prop_ptr + ));
  399.  
  400. /* trim size on certain platforms */
  401. #if ARCH_ARM
  402. // 如果是32位arm,只使用内存前1GB
  403. if (len > **1024U) {
  404. len = **; /* only use the first 1GB on ARM32 */
  405. printf("trimming memory to 1GB\n");
  406. }
  407. #endif
  408. /* set the size in the pmm arena */
  409. // 保存内存长度
  410. arena.size = len;
  411. }
  412. } else if (strcmp(name, "chosen") == ) {
  413. ...
  414. }
  415. }
  416. }
  417.  
  418. /* add the main memory arena */
  419. // 将改内存区域加入到pmm中
  420. pmm_add_arena(&arena);
  421.  
  422. /* reserve the first 64k of ram, which should be holding the fdt */
  423. // 标记fdt区域
  424. pmm_alloc_range(MEMBASE, 0x10000 / PAGE_SIZE, NULL);
  425.  
  426. // 标记bootloader_ramdisk区域
  427. platform_preserve_ramdisk();
  428. ...
  429. }
  430.  
  431. 内核在接下来初始化堆之前会在内存中构造出出一个VmAspace对象,其代表的是内核空间的抽象
  432.  
  433. kernel/kernel/vm/vm.cpp
  434.  
  435. [cpp] view plain copy
  436.  
  437. void vm_init_preheap(uint level) {
  438. LTRACE_ENTRY;
  439.  
  440. // allow the vmm a shot at initializing some of its data structures
  441. // 构造代表内核空间的VmAspace对象
  442. VmAspace::KernelAspaceInitPreHeap();
  443.  
  444. // mark all of the kernel pages in use
  445. LTRACEF("marking all kernel pages as used\n");
  446. // 标记内核代码所用内存
  447. mark_pages_in_use((vaddr_t)&_start, ((uintptr_t)&_end - (uintptr_t)&_start));
  448.  
  449. // mark the physical pages used by the boot time allocator
  450. // 标记boot time allocator代码所用内存
  451. if (boot_alloc_end != boot_alloc_start) {
  452. LTRACEF("marking boot alloc used from 0x%lx to 0x%lx\n", boot_alloc_start, boot_alloc_end);
  453.  
  454. mark_pages_in_use(boot_alloc_start, boot_alloc_end - boot_alloc_start);
  455. }
  456. }
  457.  
  458. kernel/kernel/vm/vm_aspace.cpp
  459.  
  460. [cpp] view plain copy
  461.  
  462. void VmAspace::KernelAspaceInitPreHeap() {
  463. // the singleton kernel address space
  464. // 构造一个内核空间单例,因为这个函数只会在启动时调用,所以是这个对象是单例
  465. static VmAspace _kernel_aspace(KERNEL_ASPACE_BASE, KERNEL_ASPACE_SIZE, VmAspace::TYPE_KERNEL,
  466. "kernel");
  467. // 初始化
  468. auto err = _kernel_aspace.Init();
  469. ASSERT(err >= );
  470.  
  471. // save a pointer to the singleton kernel address space
  472. // 保存单例指针
  473. VmAspace::kernel_aspace_ = &_kernel_aspace;
  474. }
  475.  
  476. VmAspace::VmAspace(vaddr_t base, size_t size, uint32_t flags, const char* name)
  477. : base_(base), size_(size), flags_(flags) {
  478.  
  479. DEBUG_ASSERT(size != );
  480. DEBUG_ASSERT(base + size - >= base);
  481.  
  482. Rename(name);
  483.  
  484. LTRACEF("%p '%s'\n", this, name_);
  485. }
  486.  
  487. status_t VmAspace::Init() {
  488. DEBUG_ASSERT(magic_ == MAGIC);
  489.  
  490. LTRACEF("%p '%s'\n", this, name_);
  491.  
  492. // intialize the architectually specific part
  493. // 标记为内核的空间
  494. bool is_high_kernel = (flags_ & TYPE_MASK) == TYPE_KERNEL;
  495. uint arch_aspace_flags = is_high_kernel ? ARCH_ASPACE_FLAG_KERNEL : ;
  496. // 调用mmu相关的函数
  497. return arch_mmu_init_aspace(&arch_aspace_, base_, size_, arch_aspace_flags);
  498. }
  499.  
  500. kernel/arch/arm/arm/mmu.c
  501.  
  502. [cpp] view plain copy
  503.  
  504. status_t arch_mmu_init_aspace(arch_aspace_t *aspace, vaddr_t base, size_t size, uint flags)
  505. {
  506. LTRACEF("aspace %p, base 0x%lx, size 0x%zx, flags 0x%x\n", aspace, base, size, flags);
  507.  
  508. DEBUG_ASSERT(aspace);
  509. DEBUG_ASSERT(aspace->magic != ARCH_ASPACE_MAGIC);
  510.  
  511. /* validate that the base + size is sane and doesn't wrap */
  512. DEBUG_ASSERT(size > PAGE_SIZE);
  513. DEBUG_ASSERT(base + size - > base);
  514.  
  515. // 初始化内核空间中页的链表
  516. list_initialize(&aspace->pt_page_list);
  517.  
  518. aspace->magic = ARCH_ASPACE_MAGIC;
  519. if (flags & ARCH_ASPACE_FLAG_KERNEL) {
  520. // 设置结构内相关参数,其中转换表的物理内存通过vaddr_to_paddr获取
  521. // 该函数不详细分析了,实质就是通过转换表进行查询得到的物理地址
  522. aspace->base = base;
  523. aspace->size = size;
  524. aspace->tt_virt = arm_kernel_translation_table;
  525. aspace->tt_phys = vaddr_to_paddr(aspace->tt_virt);
  526. } else {
  527. ...
  528. }
  529.  
  530. LTRACEF("tt_phys 0x%lx tt_virt %p\n", aspace->tt_phys, aspace->tt_virt);
  531.  
  532. return NO_ERROR;
  533. }
  534.  
  535. 到此内核空间的结构初始化完成
  536.  
  537. 接下来进行内核堆的初始化,Magenta内核中提供了两种堆的实现miniheap以及cmpctmalloc,用户可以自己进行配置。
  538.  
  539. 堆的具体实现方法会在之后进行具体的分析
  540.  
  541. 堆的初始化完成以后,会调用相应的钩子函数,该函数的主要的作用如下:
  542.  
  543. 、在vmm结构中标记内核已使用的虚拟地址
  544.  
  545. 、根据内核使用的地址的区域,分别设置内存的保护
  546.  
  547. [cpp] view plain copy
  548.  
  549. void vm_init_postheap(uint level) {
  550. LTRACE_ENTRY;
  551.  
  552. vmm_aspace_t* aspace = vmm_get_kernel_aspace();
  553.  
  554. // we expect the kernel to be in a temporary mapping, define permanent
  555. // regions for those now
  556. struct temp_region {
  557. const char* name;
  558. vaddr_t base;
  559. size_t size;
  560. uint arch_mmu_flags;
  561. } regions[] = {
  562. {
  563. .name = "kernel_code",
  564. .base = (vaddr_t)&__code_start,
  565. .size = ROUNDUP((size_t)&__code_end - (size_t)&__code_start, PAGE_SIZE),
  566. .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE,
  567. },
  568. {
  569. .name = "kernel_rodata",
  570. .base = (vaddr_t)&__rodata_start,
  571. .size = ROUNDUP((size_t)&__rodata_end - (size_t)&__rodata_start, PAGE_SIZE),
  572. .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ,
  573. },
  574. {
  575. .name = "kernel_data",
  576. .base = (vaddr_t)&__data_start,
  577. .size = ROUNDUP((size_t)&__data_end - (size_t)&__data_start, PAGE_SIZE),
  578. .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
  579. },
  580. {
  581. .name = "kernel_bss",
  582. .base = (vaddr_t)&__bss_start,
  583. .size = ROUNDUP((size_t)&__bss_end - (size_t)&__bss_start, PAGE_SIZE),
  584. .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
  585. },
  586. {
  587. .name = "kernel_bootalloc",
  588. .base = (vaddr_t)boot_alloc_start,
  589. .size = ROUNDUP(boot_alloc_end - boot_alloc_start, PAGE_SIZE),
  590. .arch_mmu_flags = ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE,
  591. },
  592. };
  593.  
  594. for (uint i = ; i < countof(regions); ++i) {
  595. temp_region* region = &regions[i];
  596. ASSERT(IS_PAGE_ALIGNED(region->base));
  597. status_t status = vmm_reserve_space(aspace, region->name, region->size, region->base);
  598. ASSERT(status == NO_ERROR);
  599. status = vmm_protect_region(aspace, region->base, region->arch_mmu_flags);
  600. ASSERT(status == NO_ERROR);
  601. }
  602.  
  603. // mmu_initial_mappings should reflect where we are now, use it to construct the actual
  604. // mappings. We will carve out the kernel code/data from any mappings and
  605. // unmap any temporary ones.
  606. const struct mmu_initial_mapping* map = mmu_initial_mappings;
  607. for (map = mmu_initial_mappings; map->size > ; ++map) {
  608. LTRACEF("looking at mapping %p (%s)\n", map, map->name);
  609. // Unmap temporary mappings except where they intersect with the
  610. // kernel code/data regions.
  611. vaddr_t vaddr = map->virt;
  612. LTRACEF("vaddr 0x%lx, virt + size 0x%lx\n", vaddr, map->virt + map->size);
  613. while (vaddr != map->virt + map->size) {
  614. vaddr_t next_kernel_region = map->virt + map->size;
  615. vaddr_t next_kernel_region_end = map->virt + map->size;
  616.  
  617. // Find the kernel code/data region with the lowest start address
  618. // that is within this mapping.
  619. for (uint i = ; i < countof(regions); ++i) {
  620. temp_region* region = &regions[i];
  621.  
  622. if (region->base >= vaddr && region->base < map->virt + map->size &&
  623. region->base < next_kernel_region) {
  624.  
  625. next_kernel_region = region->base;
  626. next_kernel_region_end = region->base + region->size;
  627. }
  628. }
  629.  
  630. // If vaddr isn't the start of a kernel code/data region, then we should make
  631. // a mapping between it and the next closest one.
  632. if (next_kernel_region != vaddr) {
  633. status_t status =
  634. vmm_reserve_space(aspace, map->name, next_kernel_region - vaddr, vaddr);
  635. ASSERT(status == NO_ERROR);
  636.  
  637. if (map->flags & MMU_INITIAL_MAPPING_TEMPORARY) {
  638. // If the region is part of a temporary mapping, immediately unmap it
  639. LTRACEF("Freeing region [%016lx, %016lx)\n", vaddr, next_kernel_region);
  640. status = vmm_free_region(aspace, vaddr);
  641. ASSERT(status == NO_ERROR);
  642. } else {
  643. // Otherwise, mark it no-exec since it's not explicitly code
  644. status = vmm_protect_region(
  645. aspace,
  646. vaddr,
  647. ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE);
  648. ASSERT(status == NO_ERROR);
  649. }
  650. }
  651. vaddr = next_kernel_region_end;
  652. }
  653. }
  654. }
  655.  
  656. 以上代码中涉及到的几个函数,只是做下简单的介绍,不具体分析:
  657.  
  658. vmm_reserve_space:在vmm中标记一块虚拟内存,这块虚拟内存抽象为VmRegion类,拥有自己的底层mmu相关的配置
  659.  
  660. vmm_protect_region:对某VmRegion对应的虚拟内存设置内存保护的相关参数
  661.  
  662. mmu相关的调整
  663. mmu相关的调整,由内核新建的bootstrap2线程进行调用arch_init完成
  664.  
  665. kernel/arch/arm/arm/arch.c
  666.  
  667. [cpp] view plain copy
  668.  
  669. void arch_init(void)
  670. {
  671. ...
  672. #if ARM_WITH_MMU
  673. /* finish intializing the mmu */
  674. arm_mmu_init();
  675. #endif
  676. }
  677.  
  678. kernel/arch/arm/arm/mmu.c
  679.  
  680. [cpp] view plain copy
  681.  
  682. void arm_mmu_init(void)
  683. {
  684. /* unmap the initial mapings that are marked temporary */
  685. // 解除具有MMU_INITIAL_MAPPING_TEMPORARY标志的内存映射
  686. struct mmu_initial_mapping *map = mmu_initial_mappings;
  687. while (map->size > ) {
  688. if (map->flags & MMU_INITIAL_MAPPING_TEMPORARY) {
  689. vaddr_t va = map->virt;
  690. size_t size = map->size;
  691.  
  692. DEBUG_ASSERT(IS_SECTION_ALIGNED(size));
  693.  
  694. while (size > ) {
  695. arm_mmu_unmap_l1_entry(arm_kernel_translation_table, va / SECTION_SIZE);
  696. va += MB;
  697. size -= MB;
  698. }
  699. }
  700. map++;
  701. }
  702. arm_after_invalidate_tlb_barrier();
  703.  
  704. #if KERNEL_ASPACE_BASE != 0
  705. /* bounce the ttbr over to ttbr1 and leave 0 unmapped */
  706. // 重新设置mmu相关的寄存器,禁用ttbcr0,将原先ttbr0的映射移动到ttbr1
  707. // ttbr1为内核空间使用的寄存器
  708. uint32_t n = __builtin_clz(KERNEL_ASPACE_BASE) + ;
  709. DEBUG_ASSERT(n <= );
  710.  
  711. uint32_t ttbcr = (<<) | n; /* disable TTBCR0 and set the split between TTBR0 and TTBR1 */
  712.  
  713. arm_write_ttbr1(arm_read_ttbr0());
  714. ISB;
  715. arm_write_ttbcr(ttbcr);
  716. ISB;
  717. arm_write_ttbr0();
  718. ISB;
  719. #endif
  720. }
  721.  
  722. 至此Magenta内核有关内存管理的初始化完成。

Magenta源代码笔记(3) —— 内存管理【转】的更多相关文章

  1. linux kernel学习笔记-5内存管理_转

    void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...

  2. XV6学习笔记(2) :内存管理

    XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...

  3. redis 源代码分析(一) 内存管理

    一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...

  4. redis源代码解读之内存管理————zmalloc文件

    本文章主要记录本人在看redis源代码的一些理解和想法.由于功力有限,肯定会出现故障,所以.希望高手给出指正. 第一篇就是内存相关的介绍.由于我喜欢先看一些组件的东西,再看总体的流程. 先上一下代码吧 ...

  5. Cocos2D-X2.2.3学习笔记3(内存管理)

    本章节介绍例如以下: 1.C/C++内存管理机制 2.引用计数机制 3.自己主动释放机制 1.C/C++内存管理机制 相信仅仅要懂oop的都知道NEW这个keyword吧,这个通俗点说事实上就是创建对 ...

  6. Linux内核设计笔记12——内存管理

    内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...

  7. COCOS学习笔记--Cocod2dx内存管理(三)-Coco2d-x内存执行原理

    通过上两篇博客.我们对Cocos引用计数和Ref类.PoolManager类以及AutoreleasePool类已有所了解,那么接下来就通过举栗子来进一步看看Coco2d-x内存执行原理是如何的. / ...

  8. cocos2d-x 源代码分析 : Ref (CCObject) 源代码分析 cocos2d-x内存管理策略

    从源代码版本号3.x.转载请注明 cocos2d-x 总的文件夹的源代码分析: http://blog.csdn.net/u011225840/article/details/31743129 1.R ...

  9. 《代码的未来》读书笔记:内存管理与GC那点事儿

    一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...

随机推荐

  1. Iframe父子间元素操作

    1.在父页面 获取iframe子页面的元素 (在同域的情况下 且在http://下测试,且最好在iframe onload加载完毕后 dosomething...) js写法 a.通过contentW ...

  2. HashMap原理以及自己实现HashMap

    1.HashMap是什么? HashMap是java常用来存储键值对的数据结构,它是以key/value的形式存储的,它不是线程安全的,Key可以为null值. 2.HashMap的实现原理 Hash ...

  3. docker安装后无法启动问题

    问题报错: Error starting daemon: Error initializing network controller: list bridge addresses failed: no ...

  4. 精读《Epitath 源码 - renderProps 新用法》

    1 引言 很高兴这一期的话题是由 epitath 的作者 grsabreu 提供的. 前端发展了 20 多年,随着发展中国家越来越多的互联网从业者涌入,现在前端知识玲琅满足,概念.库也越来越多.虽然内 ...

  5. JavaScript对象创建的九种方式

    1.标准创建对象模式 var person = new Object(); person.name = "Nicholas"; person.age = 29; person.jo ...

  6. javaWeb开发中常见的问题

    1.修改表单提交的时候不好使可能是因为没写对应隐藏域的ID 2.el表达式在js代码中要加“”,例如 "${}" 3.JavaScript中的函数也有重载的特性.如果两个input ...

  7. Java集合---简介

    概念 集合可以理解为一个动态的对象数组,不同的是集合中的对象内容可以任意扩充.Java最基本的集合接口:Collection接口 集合的特点 性能高 容易扩展和修改 Collection的常用子类 L ...

  8. loj2063 「HAOI2016」字符合并

    ref #include <iostream> #include <cstring> #include <cstdio> using namespace std; ...

  9. 【Restore IP Addresses 】cpp

    题目: Given a string containing only digits, restore it by returning all possible valid IP address com ...

  10. Bugku杂项-convert

    一进去就发现一堆二进制数,然后考虑怎么才能把这个和隐写扯上关系.首先,二进制我们肉眼就是看不懂再说什么的,这里就想到了转换,再联想上hex将原始数据转化为16进制.我们可以先把2进制转化为16进制,然 ...