读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)结构及中断处理流程。通过实模式下的键盘、定时器等硬件示例,揭示操作系统如何通过中断实现硬件管理,并演示自定义中断服务程序的安装与正确调用方式,强调保护现场、读取扫描码、避免重入等关键细节,为理解操作系统底层交互提供坚实基础。

软件工程师文艺 337

linux内核分析

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

学无止境 1742

关于发那科机器人抓手及物料搬运阀配置

4:确保抓手阀岛和输入模块配网连接正常,MENU—SETUP—MH Valves—Details,详细界面进入到左上图所示,根据每组阀分配夹钳数量并应用。3:设置搬运有效,抓手是否带点焊可以自行选择,要设置应用选择,物料搬用工具数量,工具上阀岛的数量,夹钳存在数量,工具存在数量,以及真空吸盘,冷启动。​2:MENU—应用选择,将搬运置为有效,剩余其他项根据机器人实际用途进行选择。5:设置夹钳数量,检查打开夹钳,检查闭合夹钳,延迟时间要给选择配置应用。

dxs93的博客 4772

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

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

process的专栏 2092

均匀直线阵的常规波束形成matlab

常规波束形成概述 波束形成是将基阵各阵元输出进行一定的处理,从而形成基阵空间指向性的技术。一个波束形成器可以对空间某方位的信号有响应,而抑制其他方位的信号,所以又被称为空域滤波。一般分为常规波束形成(CBF)和自适应波束形成(ABF)。 常规波束形成较为基础,一般又可以根据实现的途径不同,分为时延波束形成和频移波束形成。 时延波束形成 在各阵元间添加时延使波束控制于不同方位的方法成为时延波束形成。设定一个目标方向,当信号来源方向在该目标方向时,响应最大;信号来源于其他方向时,响应较小。将这种响应大小

Xujing1143的博客 2万+

Aruba无线控制器实战:如何用Client Match优化企业办公区漫游(附SNR调优技巧)

本文深入解析了如何利用Aruba无线控制器的Client Match功能优化企业办公区无线漫游体验。通过实战配置,详细阐述了基于SNR(信噪比)的精细化调优策略,包括阈值设定、负载均衡与场景化参数调整,旨在解决终端粘滞、乒乓漫游等问题,实现智能、平滑的无缝漫游。

weixin_28318011的博客 225

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

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

xxxxxlllllxl的专栏 3277

跟着团子学SAP CO:基于成本的完工百分比结算:Cost-based POC(以项目为例)

本文以英国及北美会计准则下的基于成本的POC的销售型项目结算对PS模块中的完工百分比进行介绍

Lucas1211的博客 2万+

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

Unreal Engine开发:角色动画与状态机_动画混合:通过混合节点实现平滑动画过渡

动画混合是提升游戏动画质量的关键技术之一。Unreal Engine提供了多种混合节点,包括BlendSpace1D、Blend Two By Bool、Blend Two By Alpha和Sequencer,每种节点适用于不同的场景。通过合理使用这些节点,可以实现平滑、自然且高效的动画过渡效果。此外,遵循最佳实践和高级应用技巧,可以进一步优化动画质量和性能。希望本节内容对你在Unreal Engine中实现动画混合有所帮助,使你的游戏角色动作更加流畅和自然。

chenlz2007的博客 1245

[特殊字符] 2026终极实战:Python爬取得物App球鞋实时交易价格 —— 从签名破解到数据可视化全栈指南

价格敏感度:热门鞋款(如Air Jordan 1、Yeezy 350、Dunk SB)价格每分钟都在波动,短线套利空间巨大。稀缺性:限量发售、联名款、地区限定,导致供需关系实时变化。数据壁垒:得物App使用自研WMS签名算法设备指纹绑定动态请求头加密,是爬虫工程师的“练兵场”。本博客将手把手带你从零搭建一套生产级得物球鞋价格爬虫系统,涵盖:抓包与逆向分析(mitmproxy + Frida)签名算法逆向(Python复刻)设备模拟与心跳保活(ADB + uiautomator2)

2201_76125261的博客 1124

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

人工智能在文学领域的应用:创作无限可能的智能作家

人工智能在文学领域的应用:创作无限可能的智能作家 作者:禅与计算机程序设计艺术 1. 背景介绍 人工智能技术近年来飞速发展,在各个领域都有广泛应用。文学创作作为人类最高级的智力活动之一,也逐渐受到人工智能技术的影响和改变。随着自然语言处理、生成式模型等人工智能

AI天才研究院 1110

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

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

weixin_33851604的博客 305

迟滞比较器原理及计算

为什么要用迟滞比较器 在讲迟滞比较器之前,我们先看一个案例。 在产品设计中常常需要用到电机,而电机在从运转到停止过程中需要泄放能量,这种能量大部分以电压的形式反馈回电源母线(Vbus)导致Vbus母线电压升高,特别是功率大又转速快的电机需要急刹时,产生的反馈电压更高,严重时可能导致母线上其他器件的损坏。 为了防止将电机反馈的能量泄放掉,我们需要设计一个电路来检测母线电压,当母线电压高于某一安全阈值时,打开MOSFET Q1将母线上的能量通过电阻R5以热的形式消耗掉。 假如用一般的比较器,设计如下: Vbu

sinat_15677011的博客 2万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

linux setup运行不,Linux __setup解析

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

weixin_42570484的博客 585

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

linux内核启动之setup_arch

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

xiaofeimo1的专栏 792

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

setup.bin的诞生记

setup.bin的编译过程

RichardYSteven的专栏 2230

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

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

汇编调试实战:DEBUG、Turbo Debugger、GDB 与 IDA

本文系统讲解汇编调试技巧,涵盖 DEBUG.exe 基础使用(寄存器查看、单步执行、内存操作)、Turbo Debugger 图形化调试,以及 GDB(32位Linux)和逆向工具(IDA/Ghidra)的高级应用。重点解析寄存器管理、断点设置、栈平衡检查与常见错误排查,如死循环、内存越界、栈崩溃等,强调静态分析识别函数边界、动态分析验证逻辑流程。掌握这些方法可高效定位底层缺陷,提升汇编开发能力。

软件工程师文艺 232

10个完整汇编实例:排序、字符串与递归算法

本文提供10个16位实模式下可运行的汇编程序,涵盖冒泡排序、字符串反转、进制转换、阶乘与斐波那契计算等经典算法,附逐行解析。重点讲解寄存器使用、循环控制、栈操作、除法清零、符号比较、字符处理等核心技巧,揭示如PUSH/POP CX保全计数器、DIV前清DX、ASCII转换、溢出处理等关键细节。通过实践掌握底层逻辑,提升汇编编程能力。

软件工程师文艺 227

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

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

ccddvvv的博客 180

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

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

软件工程师文艺 228

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

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

qq_39121450的博客 231

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

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

嵌入式开发,ARM Linux开发 412
上一篇: 读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、付费专栏及课程。

余额充值