读Kernel感悟-Linux内核启动-setup辅助程序

Linux kernel 分析之四:setup辅助程序 我们发现,在起点与终点之间,还有几个中转站。最近的一站叫作MBR。BIOS,带你到MBR后,说:“对不起,只能送你到这里了。”     那其它几个中转站是什么呢?我们知道,在x86上,保护模式有两种,32位页式寻址的保护模式和32位段式寻址的保护模式。显然,32位页式寻址的保护模式要求系统中已经构造好页表。从16位实地址模式直接到32位页式寻址的保护模式是很有难度的,因为你要在16位实地址模式下 阅读详情

文章来源:http://www.top-e.org/jiaoshi/class/

 

我们发现,在起点与终点之间,还有几个中转站。最近的一站叫作MBR。BIOS,带你到MBR后,说:“对不起,只能送你到这里了。”

那其它几个中转站是什么呢?我们知道,在x86上,保护模式有两种,32位页式寻址的保护模式和32位段式寻址的保护模式。显然,32位页式寻址的保护模式要求系统中已经构造好页表。从16位实地址模式直接到32位页式寻址的保护模式是很有难度的,因为你要在16位实地址模式下构造页表。所以不妨三级跳,先从16位实地址模式跳到32位段式寻址的保护模式,再从32位段式寻址的保护模式跳到32位页式寻址的保护模式。

我们需要这样一个程序,负责从16位实地址模式跳到32位段式寻址的保护模式,然后设置eip,启动内核。这个程序的确存在,就是arch/i386/boot/setup.S。最后汇编成setup程序。

事实上,平时所见的压缩内核映象bzImage,其实由三部分组成。这可以从arch/i386/boot/tools/build.c中看出来。build.c是用户态工具build的源代码,后者用来把bootsect(MBR),setup(辅助程序)和vmlinux.bin(压缩过的内核)拼接成bzImage。

setup有了,它可以启动内核,然而新的问题来了。谁来启动setup呢?第一个想到的是MBR。可惜,MBR只有512个字节,而且有64个字节来存放主分区表。这样看来,MBR的功能就很有限了。所以,最好在setup和MBR之间再架一座桥梁。引导程序,对,用它来引导setup再合适不过了。现有的引导程序如grub,lilo不仅功能强大,而且还提供了人机交互的功能。再合适不过了。

所以,我们理清了大概思路,查阅相关资料可知:

1.CPU加电,从0xffff0处,执行BIOS(可以理解为“硬件”引导BIOS)

2.BIOS执行扫描检测设备的操作,然后将MBR读到物理地址为0x7c00处。然后从MBR头部开始执行(可以理解为BIOS引导MBR)

3.MBR上的代码跳转到引导程序,开始执行引导程序的代码,例如grub(引以理解为BIOS引导boot loader)。

4.引导程序把内核映象(包括bootsect,setup,vmlinux)读到内存中,其中setup位于0x90200处,如果是zImage,则vmlinux.bin位于0x10000(64K)处。如果是bzImage,则vmlinux.bin位于0x100000(1M)处。然后执行setup(可以理解为boot loader引导setup)

5.setup负责引导linux内核vmlinux.bin

现在,让我们看看setup做了些什么。

首先,运行一下file setup

它报告说这是一个MS DOS的可执行程序。看来,file也有不正常工作的时候。不过有一点是肯定的。setup不是普通的elf可执行程序。事实上,gcc可以编译出各种格式的程序,具体可以运行objdump -i,查看ld(gcc的链接器)支持的输出格式。其中有一个是binary格式。

从start开始,setup做了很多操作,例如,如果是zImage,则把它从0x10000拷贝到0x1000,调用bios功能,查询硬件信息,然后放在内存中供将来的内核使用,然后建立临时的idt和gdt,负责把16位实地址模式转化为32位段式寻址的保护模式。前面这些我们不关心。我们关心的是后者:

_

832         movw    $1, %ax                         # protected mode (PE) bit

833         lmsw    %ax                             # This is it!

正是以下指令开启了保护模式的大门。

最后,再临门一脚

对bzImage来说:

jmpi    0x100000,__BOOT_CS

对zImage来说:

jmpi    0x1000,__BOOT_CS

就大功告成了。

不过,且慢,由于当时CS寄存器还没有设置为__BOOT_CS,所以,尽管在保护模式下,也仍然不能用通常的方式访问大于1M的内存。

不过,x86处理器提供了特殊的手段来访问大于1M的内存,那就是在指令前加前缀0x66。由于跳转地址与内核大小相关(zImage和bzImage不一样)所以用一个小技巧,即把该指令当作数据处理。在计算机看来,指令和数据是没什么区别的,只要ip寄存器指向内存中某地址,计算机就把地址中的数据当作指令来看待。

854         .byte 0x66, 0xea                        # prefix + jmpi-opcode

855 code32: .long   0x1000                          # will be set to 0x100000

856                                                 # for big kernels

857         .word   __BOOT_CS

我不仅发出感慨:看setup.S的代码真是一种痛苦的体验,并且有以下感悟:

1.为什么不用C写呢?

原因很简单,因为setup要尽量短小精悍,由于种种原因,它的大小不能超过63.5K.启动时内存分配如下:

0x00000~0x00400 BIOS中断向量表

0x00400~0x01000

0x01000~0x10000

0x10000~0x8f000 用来存放zImage所以最多508K

0x8f000~0x90000 引导程序的命令行参数以及bios查询的信息

0x90000~0x90200 MBR

0x90200~0xA0000 setup

0xA0000~0x100000 映射到BIOS和外设硬件等

0x100000~ 存放bzImage(如果大于508K)

2.如何引用变量?

在setup.S中,定义了不少全局变量。在编译生成setup文件时,链接参数

LDFLAGS_setup    := -Ttext 0x0 -s --oformat binary -e begtext

意为text段的地址为0,输出格式为binary(而不是默认的elf32-i386),入口地址begtext。

由于基本上处于16位实模式下,所以只要设置好CS等段寄存器就可以正确地寻址了。

现在我们跳到vmlinux.bin的开头(位于0x1000或者0x100000),也就是startup_32(),执行相应代码。

3.helloworld,vmlinux,arch/i386/boot/compressed/vmlinux,setup,bootsect的区别与联系。

这几个可执行文件都是由gcc编译生成。只是格式不一样。

其中,helloworld,vmlinux,arch/i386/boot/compressed/vmlinux都是elf32_i386格式的可执行文件。setup,bootsect是binary格式的可执行文件,它们的区别在于

1).helloworld是普通的elf32_i386可执行文件。它的入口是_start。运行在用户态空间。变量的地址都是32位页式寻址的保护模式的地址,在用户态空间。由shell负责装载。

2).vmlinux是未压缩的内核,它的入口是startup_32(0x100000,线性地址),运行在内核态空间,变量的地址是32位页式寻址的保护模式的地址,在内核态空间。由内核自解压后启动运行。

3).arch/i386/boot/compressed/vmlinux是压缩后的内核,它的入口地址是startup_32(0x100000,线性地址).运行在32位段式寻址的保护模式下,变量的地址是32位段式寻址的保护模式的地址。由setup启动运行。

4).setup是装载内核的binary格式的辅助程序。它的入口地址是:begtext(偏移地址为0。运行时需要把cs段寄存器设置为0x9020)。运行在16位实地址模式下。变量的地址等于相对于代码段起始地址的偏移地址。由boot loader启动运行。

5).bootsect是MBR上的引导程序,也为binary格式。它的入口地址是_start(),由于装载到0x7c00处,运行时需要把cs段寄存器设置为0x7c0。运行在16位实地址模式下。变量地址等于相对于代码段起始地址的偏移地址。由BIOS启动运行。

问题出现了。startup_32有两个,分别在这两个文件中:

arch/i386/boot/compressed/head.S

arch/i386/kernel/head.S

那究竟执行的是哪一个呢?难道编译时不会报错么?

学习linux0.11内核代码——引导启动程序setup.s 一、功能描述 setup.s是一个操作系统加载程序,主要作用是 1)利用ROS BIOS中断读取机器系统数据,并将这些数据保存到0x90000开始的位置(覆盖了bootsect程序所在地方),所取得的参数和保留的内存位置如下表: 2)将setup程序将system模块从0x10000-0x8ffff整块向下移动到内存绝对地址0x00000处; 3)加载中断描述符表寄存器(idt... 阅读详情

相关推荐

汇编 I/O 与中断:端口读写、中断向量表与 ISR

本文详解x86架构下I/O端口与中断机制,涵盖端口映射、IN/OUT指令、中断向量表(IVT)结构及中断处理流程。通过实模式下的键盘、定时器等硬件示例,揭示操作系统如何通过中断实现硬件管理,并演示自定义中断服务程序的安装与正确调用方式,强调保护现场、读取扫描码、避免重入等关键细节,为理解操作系统底层交互提供坚实基础。

软件工程师文艺 344

linux内核分析

启动   当PC启动时,Intel系列的CPU首先进入的是实模式,并开始执行位于地址0xFFFF0处 的代码,也就是ROM-BIOS起始位置的代码。BIOS先进行一系列的系统自检,然后初始化位 于地址0的中断向量表。最后BIOS将启动盘的第一个扇区装入到0x7C00,并开始执行此处 的代码。这就是对内核初始化过程的一个最简单的描述。   最初,linux核心的最开始部分是用8086汇编语

学无止境 1742

C 与汇编混合编程:符号链接、调用约定与参数传递

本文详解汇编与C语言混合编程的实现原理,涵盖PUBLIC与EXTERN指令的使用、32位调用约定(参数从右往左压栈、调用者清栈)、栈帧布局及寄存器保护规则。通过实例展示C调用汇编函数与汇编调用C库(如printf)的写法,并对比32位与64位(Windows x64)在参数传递、返回值和栈对齐上的差异。强调边界清晰的重要性,确保跨模块调用稳定可靠,适用于性能优化、硬件操作及学习底层机制场景。

软件工程师文艺 347

linux启动过程(参考内核0.11)

当PC启动时,Intel系列的CPU首先进入的是实模式,并开始执行位于地址0xFFFF0处的代码,也就是ROM-BIOS起始位置的代码。BIOS先进行一系列的系统自检,然后初始化位于地址0的中断向量表。最后BIOS将启动盘的第一个扇区装入到0x7C00,并开始执行此处的代码.这就

process的专栏 2092

实战指南:将MediaPipe封装为Windows DLL,实现C++/C#高效调用

在计算机视觉和多媒体处理领域,跨平台框架为开发者提供了强大的算法模型和计算能力。其核心原理在于通过预训练的机器学习模型和优化的计算图,实现对图像、视频流中特定目标(如人脸、手势、姿态)的实时检测与分析。这项技术的价值在于能够将复杂的AI能力模块化,降低应用开发门槛。在实际工程实践中,为了突破特定语言生态的限制并优化部署流程,开发者常需将核心功能封装成独立的库。例如,将Google的MediaPipe框架封装为Windows动态链接库(DLL),就是一个典型的工程实践。此举能有效解决语言壁垒和部署简化问题,使

weixin_28691741的博客 360

2023东华大学OJ机试题22-70

东华大学计算机考研复试机试22 约瑟夫环223、整除的尾数24、回文质数25、汽水瓶26、阶乘最后的非0位27、算菜价28、 水果价格29、求奇数的乘积30、求最晚和最早日期31、素数32、计算e33 数字之和34 繁殖问题35 奇妙的数字36 整除的尾数37 黑色星期五38 树39 约瑟夫环40 最大与最小41 环42 求数列项43 最高频率44 三艘船45 回文数46 特殊四位数47 最大值48 数列149 修理牛棚50 按要求输出序列51 部落人乘法52 序列53 双重回文数54 等差数列55 人见人

m0_49263811的博客 2599

Linux内核构建之setup.bin

转载于 https://book.2cto.com/201309/33427.html 构建setup.bin的规则也在/arch/x86/boot目录下的Makefile中: /arch/x86/boot/Makefile: setup-y += a20.o bioscall.o cmdline.o copy.o cpu.o cpuflags.o cpucheck.o setup-y +...

一点点 1061

跟着Synopsys报告,一步步拆解车规芯片FMEDA计算(含公式与避坑点)

本文详细解析了车规芯片FMEDA计算的全流程,基于Synopsys报告拆解实战指南,涵盖基础失效率建模、安全机制效能评估及安全指标合成等关键步骤。通过公式解析、数据准备清单和避坑点分享,帮助工程师掌握ISO26262标准下的硬件量化分析技术,提升车载系统设计可靠性。

weixin_30460489的博客 195

Boot——自己编写简单的bootloader

bootlaoder目标:启动内核 1.从FLASH上把内核读入内存; 需要可以读写FLASH;初始化时钟,内存,其他; 2.启动 设置参数;跳转执行; 最简单bootloader的编写步骤: 1.初始化硬件:关看门狗(防止自动复位整个开发板);设置时钟,SDRAM,NAND FALSH; 2.如果bootloader比较大需要将其重定位至SDRAM; 3.把内核从NAND FLASH读到SDRAM中; 4.设置需要传给内核的启动参数; 5.跳转执行内核; 假设是NOR启动: 将代码直接从NOR FALSH

没实力的码农写的渣渣博客 1695

STM32L476低功耗实战:LPTIM精准定时唤醒STOP2模式与系统级功耗优化

本文深入探讨了STM32L476低功耗实战技巧,重点解析了LPTIM精准定时唤醒STOP2模式的配置与优化。通过详细的步骤指南和实战经验分享,帮助开发者实现系统级功耗优化,特别适合电池供电设备开发。文章涵盖LPTIM1定时器配置、STOP2模式进入流程及常见问题排查,助力将整机功耗降至8.2uA级别。

weixin_33686714的博客 435

Arduino ESP32S3 基础入门教学大纲

ESP32环境、Arduino

追梦 3547

setup.bin的诞生记

setup.bin的编译过程

RichardYSteven的专栏 2230

【51单片机仿真】基于51单片机设计的多功能电子时钟日历(实时时钟闹钟秒表温度检测)系统__资料编号5

该项目是基于单片机的多功能电子时钟系统,集成了时间显示、闹钟设置、秒表功能以及温度显示等功能。主要硬件包括1602液晶显示屏、DS1302时钟芯片、DS18B20温度传感器和蜂鸣器。系统通过按键进行时间和闹钟的设置,并实时显示当前时间和温度。

嵌入式阿齐Archie 2万+

RTX A4000部署glm-4v-9b:CUDA版本冲突与bitsandbytes报错排查指南

在多模态大模型部署中,CUDA生态的版本兼容性一直是工程实践的核心难题。显卡驱动、CUDA Toolkit、PyTorch预编译包与量化库bitsandbytes四者之间,构成了环环相扣的依赖链。任何一层版本错位,都可能导致torch.cuda.is_available()返回True,却在加载4bit量化模型时报出“CUDA Setup failed”的经典错误。本文从GPU硬件算力(如sm_86架构)出发,拆解驱动向下兼容、PyTorch wheel内置CUDA运行时、以及bitsandbytes动态链

weixin_34168880的博客 373

【AI工具】Browser use搭建及使用的保姆级教程:让AI帮你自动操作浏览器

【AI工具】Browser use搭建及使用的保姆级教程:让AI帮你自动操作浏览器

funfan的博客 2535

以阿克曼转向车为例对URDF和XACRO文件进行解读

   最近我在古月学院购买了在Gazebo中实现阿克曼转向车的仿真课程,课程资料的阿克曼小车的XACRO文件在Gazebo里加载的时候报了三个错误,在这之前,我没接触过URDF或XACRO文件,本篇文章就以这个阿克曼小车的XACRO文件为例,对URDF或XACRO文件的格式和内容进行解读,期望经过解读和学习,找到错误原因及所在,并解决这三个错误    先来放一下上面右图所示的模型的完整的XACRO文件: <?xml version="1.0"?> <!-- racecar.urdf.x

慕羽★的博客 3701

RK3568适配OV13850摄像头的七层调试方法论

MIPI CSI摄像头是嵌入式视觉系统的核心组件,其稳定运行依赖于硬件链路、时序约束与软件抽象的深度协同。理解CSI物理层(PHY)的电压摆幅、阻抗匹配与clock jitter容限,是解决花屏、丢帧等现象的基础;掌握V4L2子系统中media controller的拓扑绑定逻辑与pipeline协商机制,则是打通从sensor probe到video node出图的关键路径。本文聚焦瑞芯微RK3568平台与OV13850高分辨率图像传感器的适配实践,系统拆解设备树七层嵌套语义、I2C初始化握手时序、MIP

weixin_33851604的博客 306

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

linux内核启动之setup_arch

start_kernel ()          --> setup_arch ()                    --> paging_init ()                             --> bootmem_init ()                             --> alloc_bootmem_low_p

xiaofeimo1的专栏 792

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

linux内核完全剖析—第四章 80x86保护模式及其编程—编译代码和代码详细解读

一、搭建第四章编译和调试 也就是搭建linux-0.00(简单多任务内核实例剖析)的环境和编译 下面我的是ubuntu12.04 x32编译 1.下载代码 下载http://www.oldlinux.org/Linux.old/bochs/linux-0.11-devel-050518.zip这也就是linux-0.00第四章末尾的程序   2.安装as86和ld86 其中boot.

雪夜 1371

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

linux-3.2.36内核启动1-启动参数(arm平台 启动参数的获取和处理,分析setup_arch)

最近公司要求调试一个内核,启动时有问题,所以就花了一点时间看看内核启动。 看的过程中总结了一点东西,希望可以帮助大家调试内核。 当我开始看的时候,第一件事是从网上搜集资料,不看不知道,一看吓一跳!牛人太多了,像这种内核启动的上古代码早就被人分析的彻彻底底。这注定我写的只能是烂微博了。 为了此微博有存在的必要,我会显示内核启动打印的代码位置(用绿色表示)及出现错误打印的原因(用红色表示),同时

xxxxxlllllxl的专栏 3277

linux setup运行不,Linux __setup解析

现在不难想像内核启动时怎么处理启动参数的了:通过__setup宏定义obs_kernel_param结构变量都被放入.init.setup段中,这样一来实际是使.init.setup段变成一张表,Kernel在处理每一个启动参数时,都会来查找这张表,与每一个数据项中的成员str进行比较,如果完全相同,就会调用该数据项的函数指针成员setup_func所指向的函数(该函数是在使用__setup宏定义...

weixin_42570484的博客 585

ARM 汇编调试入门:MOV、LDR 和 STR

掌握,最重要的是看清数据移动的方向。

ccddvvv的博客 194

汇编子程序设计:参数传递、局部变量与调用约定

汇编代码模块化核心在于子程序设计。通过PROC/ENDP定义函数,结合栈帧管理(PUSH BP/MOV BP, SP)实现局部变量隔离与递归支持。参数传递可选寄存器(高效)或栈(多参适用),配合调用约定(如C、Pascal)规范参数入栈顺序与栈清理责任。掌握这些模式,便能编写结构清晰、可复用的汇编代码,迈向工程化开发。

软件工程师文艺 234

四级 Linux 应用与开发工程师笔记:8086 汇编、存储器、接口芯片和操作系统计算题整理

计算机四级 Linux 应用与开发工程师备考笔记:这门考的不是 Linux 命令,而是操作系统原理和计算机组成与接口两门课。组成与接口部分重点整理 8086 寻址方式、常考汇编指令和读程序题、存储器位扩展和字扩展、流水线时空图,接口芯片和中断按做题方法整理;操作系统部分整理高响应比优先、Clock 置换、两级页表、生产者消费者等考点,配真题讲解。

qq_39121450的博客 249

C 语言:编译 → 汇编 → 链接 → 加载运行

C程序从源码到执行经历预处理、编译、汇编、链接四阶段,生成ELF可执行文件,由操作系统加载至内存并由CPU执行。关键点包括:交叉编译生成ARM指令;链接分静态与动态;入口为_start而非main;动态库需-fPIC保证位置无关性;嵌入式环境下可能跳过OS直接运行。

嵌入式开发,ARM Linux开发 437

汇编分支与循环:JMP/LOOP/条件跳转的结构化编程

本文详解如何用汇编的JMP与条件跳转实现if/else、for、while等高级控制流。通过循环计数、嵌套循环及复杂判断的典型模式,展示底层指令如何构建逻辑结构。掌握LOOP指令与跳转组合,即可在无高级语法环境下实现高效、精准的程序控制,体现汇编语言的简洁与性能优势。

软件工程师文艺 261
上一篇: 读Kernel感悟-Linux内核启动-BIOS
下一篇: 读Kernel感悟-Linux内核启动-内核解压缩
TopEmbedded
博客等级 码龄18年 58粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值