Little Kernel代码学习笔记

news2025/1/18 6:48:26

目录

  • 虚拟地址转换为物理地址
  • 内核启动
    • 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.S

real_start:
    cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
    jne 0f
    movl %ebx, PHYS(_multiboot_info)

0:
    /* load our new gdt by physical pointer */
    lgdt PHYS(_gdtr_phys)

    /* load our data selectors */
    movw $DATA_SELECTOR, %ax
    movw %ax, %ds
    movw %ax, %es
    movw %ax, %fs
    movw %ax, %ss
    movw %ax, %gs
    movw %ax, %ss

    /* load initial stack pointer */
    movl $PHYS(_kstack + 4096), %esp

    /* far jump to load the CS from our GDT */
    pushl $CODE_SELECTOR
    pushl $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, %ecx
    shrl $2, %ecx       /* convert to 32 bit words, since the bss is aligned anyway */
2:
    movl $0, (%edi)
    addl $4, %edi
    loop 2b

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

页表转换设置

CR4、CR3、EFER寄存器设置

//start.S

paging_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, %eax
    btsl $(5), %eax
    mov %eax, %cr4

    /* load the physical pointer to the top level page table */
    movl $PHYS(kernel_pml4), %eax
    mov %eax, %cr3

    /* Long Mode Enabled at this point*/
    movl $MSR_EFER ,%ecx
    rdmsr
    orl $EFER_LME,%eax
    wrmsr

将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), %eax
    orl  $X86_KERNEL_PD_FLAGS, %eax
    movl %eax, PHYS(kernel_pml4)

    /* Setting the First PDPTE with a Page table reference at index 0 */
    movl $PHYS(kernel_pte), %eax
    orl  $X86_KERNEL_PD_FLAGS, %eax
    movl %eax, PHYS(kernel_pdp)

    /* point the pml4e at the second high PDP (for -2GB mapping) at index 511 */
    movl $PHYS(kernel_pdp_high),   %eax
    orl  $X86_KERNEL_PD_FLAGS, %eax
    movl %eax, PHYS(kernel_pml4 + 8*511)

    /* point the second pdp at the same low level page table */
    movl $PHYS(kernel_pte), %eax
    orl  $X86_KERNEL_PD_FLAGS, %eax
    movl %eax, PHYS(kernel_pdp_high + 8*510)

    /* map the first 1GB in this table */
    movl $PHYS(kernel_pte), %esi
    movl $0x200, %ecx               /* 512 entries */
    xor  %eax, %eax                 /* start off at address 0 */

0:
    mov  %eax, %ebx
    shll $21, %ebx
    orl  $X86_KERNEL_PD_LP_FLAGS, %ebx
    movl %ebx, (%esi)
    addl $8,%esi
    inc  %eax
    loop 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), %esi
    movl $32768, %ecx
    xor  %eax, %eax

    /* loop across these page tables, incrementing the address by 2MB */
0:
    mov  %eax, %ebx
    shll $21, %ebx
    orl  $X86_KERNEL_PD_LP_FLAGS, %ebx    # lower word of the entry
    movl %ebx, (%esi)
    mov  %eax, %ebx
    shrl $11, %ebx      # upper word of the entry
    movl %ebx, 4(%esi)
    addl $8,%esi
    inc  %eax
    loop 0b

    /* point the high pdp at our linear mapping page tables */
    movl $PHYS(kernel_pdp_high), %esi
    movl $64, %ecx
    movl $PHYS(kernel_linear_map_pdp), %eax
    orl  $X86_KERNEL_PD_FLAGS, %eax

0:
    movl %eax, (%esi)
    add  $8, %esi
    addl $4096, %eax
    loop 0b

    /* Enabling Paging and from this point we are in 32 bit compatibility mode */
    mov %cr0,  %eax
    btsl $(31), %eax
    mov %eax,  %cr0

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

初始化IDT,执行lk_main

    /* Use a far jump to get into 64bit mode */
    pushl $CODE_64_SELECTOR
    pushl $PHYS(farjump64)
    lret

.align 8
.code64
farjump64:
    /* branch to our high address */
    mov  $highaddr, %rax
    jmp  *%rax

highaddr:
    /* 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_main

0:                          /* just sit around waiting for interrupts */
    hlt                     /* interrupts will unhalt the processor */
    pause
    jmp 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.coloradmin.cn/o/926288.html

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

相关文章

Docker去除sudo权限

Docker去除sudo权限 使用docker命令时,每次都要sudo提权,否则就会报错提示无权限。 1.查看docker用户组及成员 sudo cat /etc/group | grep docker2.添加docker用户组 sudo groupadd docker3.添加用户到docker组 sudo gpasswd -a ${USER} docker4.增…

“超级AI助手:全新提升!中文NLP训练框架,快速上手,海量训练数据,ChatGLM-v2、中文Bloom、Dolly_v2_3b助您实现更智能的应用!”

“超级AI助手:全新提升!中文NLP训练框架,快速上手,海量训练数据,ChatGLM-v2、中文Bloom、Dolly_v2_3b助您实现更智能的应用!” 1.简介 目标:基于pytorch、transformers做中文领域的nlp开箱即用…

【android12-linux-5.1】【ST芯片】驱动移植后编译不通过

ST传感器芯片驱动移植后,编译报错timespec_to_ns未定义,这应该是内核版本的差异引起的。驱动的适配版本是4.19y,我实际使用的内核linux版本是5.1。 处理方法是使用timespec64_to_ns,如下图: 新代码如下: s…

制造执行系统(MES)在汽车行业中的应用

汽车行业在不断发展中仍然面临一些挑战和痛点。以下是一些当前汽车行业可能面临的问题: 1.电动化和可持续性转型:汽车行业正逐渐向电动化和可持续性转型,但这需要投入大量资金和资源,包括电池技术、充电基础设施等,同时…

如何判断光模块的收发端

随着光纤通信技术的快速发展,光模块作为光纤通信系统中至关重要的组件,扮演着光信号收发转换器的关键角色,它能够实现光信号的发射和接收功能。为了正确使用光模块并满足通信需求,了解如何准确判断光模块的发射端和接收端显得十分…

SDMMC/SDIO的PCB设计要求

RK3588集成了1个SDMMC控制器和1个SDIO控制器,均可支持SDIO3.0协议, 以及MMC V4.51协议。4线的数据总线宽度;支持SDR104 模式,速率达到150MHz。 SD/MMC或者SDMMC是数字安全记忆卡(Secure Digital Memory Card&#xff…

员工矩阵号短视频saas管理系统---开发工具

一、短视频矩阵号系统源码开发层面如何来解决? 1.短视频矩阵号系统源码搭建中,首先开发者需要保证api接口的稳定性 ,保证权限应用场景满足官方平台的开发预期。api---待发布、用户管理与授权绑定、私信回复与评论管理等是非常重要的权限接口…

Hutool:WeakCache导致的内存泄漏

闲聊 感谢各位居然有生之年上了一次榜单。没想到一次bug定位这么火,身为电商网站的后台开发,别的不敢说,jvm调优啊,bug定位啊,sql调优啊简直是家(ri)常(chang)便&#xf…

LeetCode——二叉树篇(九)

刷题顺序及思路来源于代码随想录,网站地址:https://programmercarl.com 目录 669. 修剪二叉搜索树 108. 将有序数组转换为二叉搜索树 538. 把二叉搜索树转换为累加树 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root ,同时给定最小边界…

用好「留存」,闭环小程序运营链路

如何通过线上小程序获取用户线索,提高企业抗风险能力,建立有效的营销数字化系统一直是困扰每一个小程序开发者与运营者的问题。 当我们选择使用小程序设计自己的运营流程时,从「推广」到「转化」,再到最终的「留存」都是运营过程…

leetcode 1035. 不相交的线

2023.8.25 本题可以转化为&#xff1a;求两数组的最长公共子序列。 进而可以用dp算法解决。 方法类似于这题最长公共子序列 。 代码如下&#xff1a; class Solution { public:int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {vector<…

汽车电子笔记之:AUTOSA架构下的多核OS操作系统

目录 1、AUTOSAR多核操作系统 1.1、OS Application 1.2、多核OS的软件分区 1.3、任务调度 1.4、核间任务同步 1.5、计数器、报警器、调度表 1.6、自旋锁与共享资源 1.7、核间通信IOC 1.8、OS Object中元素交互 1.9、多核OS的启动与关闭 2、多核OS注意事项 2.1、最小…

Classic AUTOSAR专题| 诊断模块(上)

往期小怿向各位小伙伴介绍了Classic AUTOSAR专题之I/O模块&#xff0c;相信看过的小伙伴对Classic AUTOSAR的I/O模块已经有基本的认知了&#xff0c;本期为大家介绍《AUTOSAR模块之诊断模块》&#xff0c;内容超丰富&#xff0c;将分两期推出哦。 目录 1.概述 2.DCM 3.DEM …

upgrade pip报错:def read(rel_path: str) -> str: syntaxerror

命令行执行以下命令就可以大功告成! wget https://bootstrap.pypa.io/pip/2.7/get-pip.py python get-pip.py pip install --upgrade setuptools最后大功告成:

微信小程序基于移动端的个人博客系统的设计与实现

博客系统是能够让网民记录分享和学习的一个网站&#xff0c;在博客中我们可以发表文章对感兴趣的事情进行讨论。而基于移动端的个人博客系统的设计是就为了迎合广大用户需求创建的一个界面简洁、有定向内容、业务逻辑简单易操作的博客系统。本文以博客系统的设计与实现为例&…

【Axure教程】调用日期选择器并筛选中继器表格

今天教大家在Axure里怎么调用代码调用浏览器的日期选择器并对对中继器表格进行日期区间的筛选。调用浏览器日期选择器的好处是&#xff0c;可以选择真实的日期&#xff0c;包括某年某月某日是星期几&#xff0c;哪个二月是29天……都是真实的&#xff0c;那不同的浏览器日期选择…

腾讯云服务器多少钱一年?一个月或1小时费用价格明细表

腾讯云服务器租用费用表&#xff1a;轻量应用服务器2核2G4M带宽112元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、云服务器CVM S5实例2核2G配置280.8元一年、GPU服务器GN10Xp实例145元7天&#xff0c;腾讯云服务器网长期更新腾讯云轻量…

vs2017 错误 RC1015 cannot open include file ‘afxres.h‘.

问题场景&#xff1a; WINDOWS在VS2017环境下编译项目&#xff0c;报错vs2017 错误 RC1015 cannot open include file ‘afxres.h’. 问题排查&#xff1a; 首先&#xff0c;定位到include的文件目录&#xff0c;发现没有这个头文件&#xff1a; 解决方法&#xff1a; 第一种…

【java】【springboot】【idea】springboot项目pom.xml 灰色下划线

解决方案&#xff1a; 这里我们找到了原因&#xff0c;就是因为选择了Ignored Files导致pom.xml文件被设置在maven忽略文件清单中&#xff0c;所以我们将打勾的选项取消&#xff0c;点击Apply,然后点击OK

【精度丢失】后端接口返回的Long类型参数,不同浏览器解析出的结果不一样

1、业务背景 有个同事找我帮他看一个问题&#xff0c;他给前端提供了一个接口。 这个接口是用来反查id的&#xff0c;他这里这个参数正常的返回值应该是 283232039247028226。 但前端反馈他&#xff0c;前端在浏览器&#xff08;火狐&#xff09;获取的值是 283232039247028…