Little Kernel代码学习笔记

目录

  • 虚拟地址转换为物理地址
  • 内核启动
    • Multiboot头部结构
    • 启动时的寄存器状态
    • real_start
    • 段选择子
    • 初始化BSS段
  • 页表转换设置
    • CR4、CR3、EFER寄存器设置
    • 页表映射
  • 初始化IDT,执行lk_main

虚拟地址转换为物理地址

// start.S#define PHYS_LOAD_ADDRESS (MEMBASE + KERNEL_LOAD_OFFSET)
#define PHYS_ADDR_DELTA (KERNEL_BASE + KERNEL_LOAD_OFFSET - PHYS_LOAD_ADDRESS)
#define PHYS(x) ((x) - PHYS_ADDR_DELTA)

PHYS(x) 将x转换为物理地址

内核启动

Multiboot头部结构

// start.S.section ".text.boot"
.code32
.global _start
_start:jmp real_start.align 8/* flags for multiboot header */
#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE)
//MULTIBOOT_PAGE_ALIGN   0x00000001      MULTIBOOT_MEMORY_INFO   0x00000002      MULTIBOOT_AOUT_KLUDGE      0x00010000
.type multiboot_header,STT_OBJECT
multiboot_header:/* magic */.int MULTIBOOT_HEADER_MAGIC/* flags */.int MULTIBOOT_HEADER_FLAGS/* checksum */.int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)/* header_addr */.int PHYS(multiboot_header)/* load_addr */.int PHYS(_start)/* load_end_addr */.int PHYS(__data_end)/* bss_end_addr */.int PHYS(__bss_end)/* entry_addr */.int PHYS(real_start)

刚启动时,使用32位指令集,MULTIBOOT_HEADER_FLAGS 指定启动加载程序的功能,此处设置了4K字节对齐、multiboot_info需要包含mem_*字段以及设Multiboot偏移12-28处的字段有效
图中代码在multiboot.h
multibool_info结构
图中代码在start.Smultiboot_header
Multiboot header地址含义可以参考Multiboot技术文档3.1.3小节,Multiboot_info可参考3.3小节

启动时的寄存器状态

Multiboot协议规定,EAX = 0x2BADB002(魔数) 表明操作系统是被符合Multiboot的加载程序进行加载的,此外Multiboot协议规定,EBX必须包含Multiboot_info的32位物理地址。有关机器启动时的状态可参考文档3.2小节。

real_start

// start.Sreal_start:cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eaxjne 0fmovl %ebx, PHYS(_multiboot_info)0:/* load our new gdt by physical pointer */lgdt PHYS(_gdtr_phys)/* load our data selectors */movw $DATA_SELECTOR, %axmovw %ax, %dsmovw %ax, %esmovw %ax, %fsmovw %ax, %ssmovw %ax, %gsmovw %ax, %ss/* load initial stack pointer */movl $PHYS(_kstack + 4096), %esp/* far jump to load the CS from our GDT */pushl $CODE_SELECTORpushl $PHYS(.Lfarjump)lret

在real_start开始部分,先检查EAX中的值是否等于Multiboot魔数,等于则将EBX的值加载到multiboot_info的物理地址,否则直接跳转到标号0处执行。

将全局描述符表gdt的物理地址加载到GDTR中,然后将段寄存器的值设置为DATA_SELECTOR = 0x10 = 0001 0000

段选择子

在这里插入图片描述
Requestor Privilege-Level (RPL)表示处理器正在运行的特权级别
Table Indicator (TI)表示选择哪个描述符表,TI=0使用GDT,TI=1使用LDT
Selector Index Field(SI)表示索引
因此DATA_SELECTOR = 0x10 = 0001 0000 表示CPL=0,即最高权限;使用GDT,Index为2,使用GDT中的第二个段描述符
图中代码在gdt.S
在这里插入图片描述


段寄存器设置好后,用一个4K的数组作为栈,数组末尾作为栈顶,然后将CODE_SELECTOR和.Lfarjump的物理地址压栈,再跳转到.Lfarjump处运行。
CODE_SELECTOR = 0x08 = 0000 1000 即选择GDT的第一个段描述符

初始化BSS段

//start.S.Lfarjump:/* zero the bss section */
bss_setup:movl $PHYS(__bss_start), %edi /* starting address of the bss */movl $PHYS(__bss_end), %ecx   /* find the length of the bss in bytes */subl %edi, %ecxshrl $2, %ecx       /* convert to 32 bit words, since the bss is aligned anyway */
2:movl $0, (%edi)addl $4, %ediloop 2b

初始化BSS段,其中_bss_start, _bss_end在kernel.ld文件中,在链接阶段分配地址

页表转换设置

CR4、CR3、EFER寄存器设置

//start.Spaging_setup:/* Preparing 64 bit paging. We will use 2MB pages covering 1GB* for initial bootstrap, this page table will be 1 to 1.*//* PAE bit must be enabled  for 64 bit paging*/mov %cr4, %eaxbtsl $(5), %eaxmov %eax, %cr4/* load the physical pointer to the top level page table */movl $PHYS(kernel_pml4), %eaxmov %eax, %cr3/* Long Mode Enabled at this point*/movl $MSR_EFER ,%ecxrdmsrorl $EFER_LME,%eaxwrmsr

将CR4位5置1,启用PAE(Physical-Address Extensions),并将PML4的地址存储在CR3中,然后设置MSR_EFER寄存器,启用长模式。

//start.S#define MSR_EFER 0xc0000080
#define EFER_LME 0x00000100

MSR_EFER = 0xc0000080 看似是一个宏定义,其实是EFER寄存器的地址(在AMD手册3.1.7中给出)
在这里插入图片描述

页表映射

//mmu.c/* top level kernel page tables, initialized in start.S */
map_addr_t kernel_pml4[NO_OF_PT_ENTRIES] __ALIGNED(PAGE_SIZE);
map_addr_t kernel_pdp[NO_OF_PT_ENTRIES] __ALIGNED(PAGE_SIZE); /* temporary */
map_addr_t kernel_pte[NO_OF_PT_ENTRIES] __ALIGNED(PAGE_SIZE);/* top level pdp needed to map the -512GB..0 space */
map_addr_t kernel_pdp_high[NO_OF_PT_ENTRIES] __ALIGNED(PAGE_SIZE);/* a big pile of page tables needed to map 64GB of memory into kernel space using 2MB pages */
map_addr_t kernel_linear_map_pdp[(64ULL*GB) / (2*MB)]; 

kernel_pml4、kernel_pdp、kernel_pte都是一个4K大小的数组,kernel_linear_map_pdp是一个4*64K大小的数组,在这里的作用是作为64个4K的kernel_pte

//start.S/* Setting the First PML4E with a PDP table reference at index 0 */movl $PHYS(kernel_pdp), %eaxorl  $X86_KERNEL_PD_FLAGS, %eaxmovl %eax, PHYS(kernel_pml4)/* Setting the First PDPTE with a Page table reference at index 0 */movl $PHYS(kernel_pte), %eaxorl  $X86_KERNEL_PD_FLAGS, %eaxmovl %eax, PHYS(kernel_pdp)/* point the pml4e at the second high PDP (for -2GB mapping) at index 511 */movl $PHYS(kernel_pdp_high),   %eaxorl  $X86_KERNEL_PD_FLAGS, %eaxmovl %eax, PHYS(kernel_pml4 + 8*511)/* point the second pdp at the same low level page table */movl $PHYS(kernel_pte), %eaxorl  $X86_KERNEL_PD_FLAGS, %eaxmovl %eax, PHYS(kernel_pdp_high + 8*510)/* map the first 1GB in this table */movl $PHYS(kernel_pte), %esimovl $0x200, %ecx               /* 512 entries */xor  %eax, %eax                 /* start off at address 0 */0:mov  %eax, %ebxshll $21, %ebxorl  $X86_KERNEL_PD_LP_FLAGS, %ebxmovl %ebx, (%esi)addl $8,%esiinc  %eaxloop 0b                         /* dec ecx and loop while > 0 */

使用的是2M的页表,实际上kernel_pte换成kernel_pde会更好,但只是个名字,并不影响实际运行。映射的结果如图:
在这里插入图片描述

    /* set up a linear map of the first 64GB at 0xffffff8000000000 */movl $PHYS(kernel_linear_map_pdp), %esimovl $32768, %ecxxor  %eax, %eax/* loop across these page tables, incrementing the address by 2MB */
0:mov  %eax, %ebxshll $21, %ebxorl  $X86_KERNEL_PD_LP_FLAGS, %ebx    # lower word of the entrymovl %ebx, (%esi)mov  %eax, %ebxshrl $11, %ebx      # upper word of the entrymovl %ebx, 4(%esi)addl $8,%esiinc  %eaxloop 0b/* point the high pdp at our linear mapping page tables */movl $PHYS(kernel_pdp_high), %esimovl $64, %ecxmovl $PHYS(kernel_linear_map_pdp), %eaxorl  $X86_KERNEL_PD_FLAGS, %eax0:movl %eax, (%esi)add  $8, %esiaddl $4096, %eaxloop 0b/* Enabling Paging and from this point we are in 32 bit compatibility mode */mov %cr0,  %eaxbtsl $(31), %eaxmov %eax,  %cr0

页表初始化完成后,通过CR0启动页表,映射的结果如下,其中PA大小是64G,内核区为1G。图中有个问题,PA前1G和内核是同一个区域,并不是分开的
在这里插入图片描述

初始化IDT,执行lk_main

    /* Use a far jump to get into 64bit mode */pushl $CODE_64_SELECTORpushl $PHYS(farjump64)lret.align 8
.code64
farjump64:/* branch to our high address */mov  $highaddr, %raxjmp  *%raxhighaddr:/* load the high kernel stack */mov  $(_kstack + 4096), %rsp/* reload the gdtr */lgdt _gdtr/* set up the idt */call setup_idt/* call the main module */call lk_main0:                          /* just sit around waiting for interrupts */hlt                     /* interrupts will unhalt the processor */pausejmp 0b                  /* so jump back to halt to conserve power */

最后将CODE_64_SELECTOR和farjump64物理地址压栈,CODE_64_SELECTOR = 0x28 = 0010 1000,选择GDT第5个段描述符。
重新初始化栈顶以及GDTR,并调用setup_idt初始化IDT,以及调用lk_main。
此时因为已经启用页表,所以不再使用物理地址,而是逻辑地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56623.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多功能租车平台微信小程序源码 汽车租赁平台源码 摩托车租车平台源码 汽车租赁小程序源码

多功能租车平台微信小程序源码是一款用于汽车租赁的平台程序源码。它提供了丰富的功能,可以用于租赁各种类型的车辆,包括汽车和摩托车。 这个小程序源码可以帮助用户方便地租赁车辆。用户可以通过小程序浏览车辆列表,查看车辆的详细信息&…

npm 卸载 vuecli后还是存在

运行了npm uninstall vue-cli -g,之后是up to date in,然后vue -V,版本号一直都在,说明没有卸载掉 1、执行全局卸载命令 npm uninstall vue-cli -g 2、删除vue原始文件 查看文件位置,找到文件删掉 where vue 3、再…

[Go版]算法通关村第十三关黄金——数字数学问题之数论问题(最大公约数、素数、埃氏筛、丑数)

目录 题目:辗转相除法(求最大公约数)思路分析:辗转相除法(也叫欧几里得算法)gcd(a,b) gcd(b,a mod b)复杂度:时间复杂度 O ( n l o g ( m a x ) ) O(nlog(max)) O(nlog(max))、空间复杂度 O (…

探索pytest:Python自动化测试的新境界

在当今的软件开发领域,测试已经不仅仅是一个简单的步骤,而是确保软件质量的核心环节。Python,作为全球最受欢迎的编程语言之一,拥有丰富的测试框架和工具。而在这其中,pytest无疑是最受欢迎和最具影响力的一个。本文将…

ICS PA1

ICS PA1 init.shmake 编译加速ISA计算机是个状态机程序是个状态机准备第一个客户程序parse_argsinit_randinit_loginit_meminit_isa load_img剩余的初始化工作运行第一个客户程序调试:零断点TUI 基础设施单步执行打印寄存器状态扫描内存 表达式求值词法分析递归求值…

Go的基础运行方式和打包

目录 基础运行方式导入路径 打包技巧相关知识点 基础运行方式 // 文件名可以不是main,但包名和入口函数比如是main // main.go package main // 导入包的时候可以直接导入,也可以导入后指定包名, import ("fmt"godemo "githu…

Android学习之路(10) setContentView详解

一、简介 setContentView我们在Activity中经常见到,它的作用就是把我们的布局文件放在Activity中显示,下面我们根据源码分析setContentView是如何做到的 二、源码分析 1.两种setContentView 注意Activity的setContentView和AppCompatActivity的setCo…

斯坦福人生设计课——简略笔记

来源:⽐尔博内特 戴夫伊万斯 著图书《人生设计课》 目录 一、认清当下的情况,从四个维度观察自己的人生 二、平衡人生,但不要走入误区 2.1 记录你的“美好时光日志”: 2.1.1 记录内容: 2.1.2 辅助反思的方法&…

WebRTC音视频通话-iOS端调用ossrs直播拉流

WebRTC音视频通话-iOS端调用ossrs直播拉流 之前实现iOS端调用ossrs服务,文中提到了推流。没有写拉流流程,所以会用到文中的WebRTCClient。请详细查看:https://blog.csdn.net/gloryFlow/article/details/132262724 一、iOS播放端拉流效果 二…

强训第40天

选择 A 在域名解析之前,需要先与访问域名服务器,与域名服务器之间的网络通信需要涉及相邻设备之间的数据传输。也就是ARP,所以ARP是第一个 B C D 可靠传输的基础是确认应答以及超时重传。这俩是通过序号和确认序号交付的 A B 对用户是不透明的…

Rust之自动化测试(一):如何编写测试

开发环境 Windows 10Rust 1.71.1 VS Code 1.81.1 项目工程 这里继续沿用上次工程rust-demo 编写自动化测试 Edsger W. Dijkstra在他1972年的文章《谦逊的程序员》中说,“程序测试可以是一种非常有效的方法来显示错误的存在,但它对于显示它们的不存在…

架构评估-架构师之路(十二)

软件系统质量属性 软件系统质量熟悉分为 开发期质量属性 和 运行期质量属性。 质量属性 性能:指 系统的响应能力,如 响应时间,吞吐率。 设计策略:优先级队列、增加计算资源、减少计算开销、引入并发机制、采用资源调度。 可靠…

【ArcGIS Pro二次开发】(62):复制字段

应网友需求,做了这么一个复制字段的小工具。 假定这样一个场景,手头有一个要素1,要素里有10个字段,另一个要素2,除了shape_area等图形字段外,没有其它字段。 现在的需求是,想把要素1中的8个字…

【跨域异常】

想在前端使用vue获取后端接口的数据,但是报了跨域异常,如下图所示。 一种解决的方式是,在后端Controller接口上加上CrossOrigin,从后端解决跨域问题。 还要注意前端请求的url要加上协议,比如http://

vue使用vant中的popup层,在popup层中加搜索功能后,input框获取焦点 ios机型的软键盘不会将popup顶起来的问题

1.使用vant的popup弹出层做了一个piker的选择器,用户需要在此基础上增加筛选功能。也就是输入框 2.可是在ios机型中,input框在获取焦点以后,ios的软键盘弹起会遮盖住我们的popup层,导致体验不是很好 3.在大佬的解答及帮助下,采用窗口滚动的方式解决此方法 <Popupv-model&q…

数据结构--树4.2(二叉树)

目录 一、二叉树的定义和特点 1、定义 2、特点 二、二叉树的基本形态 1、空二叉树 2、只有一个根结点 3、根结点只有左子树 4、根结点只有右子树 5、根结点既有左子树又有右子树 6、斜树 7、满二叉树 8、满二叉树和完全二叉树 三、二叉树的性质 一、二叉树的定义和…

Element——table排序,上移下移功能。及按钮上一条下一条功能

需求&#xff1a;table排序&#xff0c;可操作排序上移下移功能。判断第一行上移禁用和最后一行下移禁用&#xff0c;排序根据后端返回的字段 <el-table:data"tableData"style"width: 100%"><el-table-column type"index" label"序…

JVM虚拟机:定位对象的两种方式

定位对象的方式 1、句柄池 2、直接指针 ‘句柄池 直接指针 在Java中&#xff0c;可以使用两种方式来定位对象&#xff1a;句柄池和直接指针。 1. 句柄池&#xff1a;在Java的句柄池模型中&#xff0c;Java虚拟机&#xff08;JVM&#xff09;会为每个对象创建一个句柄&#xff…

软件工程(九) UML顺序-活动-状态-通信图

顺序图和后面的一些图,要求没有用例图和类图那么高,但仍然是比较重要的,我们也需要按程度去了解。 1、顺序图 顺序图(sequence diagram, 顺序图),顺序图是一种交互图(interaction diagram),它强调的是对象之间消息发送的顺序,同时显示对象之间的交互。 下面以一个简…

算法通关村第5关【白银】| 哈希和栈经典算法题

1.两个栈实现队列 思路&#xff1a;两个栈&#xff0c;一个输入栈&#xff0c;一个输出栈。 当需要输入的时候就往inStack中插入&#xff0c;需要输出就往outStack中输出&#xff0c;当输出栈是空就倒出输入栈的数据到输出栈中&#xff0c;这样就保证了后插入的数据从栈顶倒入…