学习笔记
使用教材(配书源码以及使用方法)
《一个64位操作系统的设计与实现》
http://www.ituring.com.cn/book/2450
https://www.jianshu.com/p/28f9713a9171
源码结构
- 配书代码包 :第8章 \ 程序 \ 程序8-2
实验操作
1、虚拟机:生成boot.bin 、loader.bin以及kernel.bin 并复制到U盘
[anno@localhost 32MB-bootloader]$
sudo dd if=boot.bin of=/dev/sdb bs=512 count=1 conv=notrunc
[anno@localhost 物理平台]$ cd kernel
[anno@localhost kernel]$ make
gcc -E head.S > head.s
as --64 -o head.o head.s
gcc -E entry.S > entry.s
as --64 -o entry.o entry.s
gcc -mcmodel=large -fno-builtin -m64 -c main.c
gcc -mcmodel=large -fno-builtin -m64 -c printk.c
gcc -mcmodel=large -fno-builtin -m64 -c trap.c
gcc -mcmodel=large -fno-builtin -m64 -c memory.c
gcc -mcmodel=large -fno-builtin -m64 -c interrupt.c
gcc -mcmodel=large -fno-builtin -m64 -c task.c
gcc -mcmodel=large -fno-builtin -m64 -c cpu.c
ld -b elf64-x86-64 -z muldefs -o system head.o entry.o main.o printk.o trap.o memory.o interrupt.o task.o cpu.o -T Kernel.lds
objcopy -I elf64-x86-64 -S -R ".eh_frame" -R ".comment" -O binary system kernel.bin
根据自己使用的U盘,修改bootloader部分源码的方法参见
[OS64][031]实验操作:程序7-3 移植到物理平台 U盘启动
https://www.jianshu.com/p/a84b45ae3219
- 其实程序8-2与程序7-3使用的
bootloader
代码是一致的,如果在程序7-3的实验中已经按照自己的U盘规格制作好了boot.bin
、loader.bin
,可以直接拿来用了
2、物理机器:插上U盘,显示运行画面
源码阅读
get_cpuid 用于获取处理器固件信息
CPUID
指令将通过EAX
寄存器输入查询的主功能号
,如果有需要,则再向ECX寄存器输入查询的子功能号。
当这条汇编指令执行结束后,查询的返回值
将保存在EAX、EBX、ECX和EDX寄存器
中
代码清单8-4 第8章\程序\程序8-2\物理平台\kernel\cpu.h
inline void get_cpuid(unsigned int Mop,
unsigned int Sop,
unsigned int * a,
unsigned int * b,
unsigned int * c,
unsigned int * d)
{
__asm__ __volatile__ ( "cpuid \n\t"
:"=a"(*a),"=b"(*b),"=c"(*c),"=d"(*d)
:"0"(Mop),"2"(Sop)
);
}
- 将
CPUID
汇编指令封装成get_cpuid
函数 -
Mop
和Sop
参数用于向CPUID
指令传递主功能号和子功能号 - 将查询的返回值保存到指针变量
a、b、c和d
指向的内存中
init_cpu() 中调用 get_cpuid(...)
void init_cpu(void)
{
int i,j;
unsigned int CpuFacName[4] = {0,0,0,0};
char FactoryName[17] = {0};
//vendor_string
get_cpuid(0,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
*(unsigned int*)&FactoryName[0] = CpuFacName[1];
*(unsigned int*)&FactoryName[4] = CpuFacName[3];
*(unsigned int*)&FactoryName[8] = CpuFacName[2];
FactoryName[12] = '\0';
color_printk(YELLOW,BLACK,"%s\t%#010x\t%#010x\t%#010x\n",FactoryName,CpuFacName[1],CpuFacName[3],CpuFacName[2]);
//brand_string
for(i = 0x80000002;i < 0x80000005;i++)
{
get_cpuid(i,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
*(unsigned int*)&FactoryName[0] = CpuFacName[0];
*(unsigned int*)&FactoryName[4] = CpuFacName[1];
*(unsigned int*)&FactoryName[8] = CpuFacName[2];
*(unsigned int*)&FactoryName[12] = CpuFacName[3];
FactoryName[16] = '\0';
color_printk(YELLOW,BLACK,"%s",FactoryName);
}
color_printk(YELLOW,BLACK,"\n");
//Version Informatin Type,Family,Model,and Stepping ID
get_cpuid(1,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
color_printk(YELLOW,BLACK,"Family Code:%#010x,Extended Family:%#010x,Model Number:%#010x,Extended Model:%#010x,Processor Type:%#010x,Stepping ID:%#010x\n",(CpuFacName[0] >> 8 & 0xf),(CpuFacName[0] >> 20 & 0xff),(CpuFacName[0] >> 4 & 0xf),(CpuFacName[0] >> 16 & 0xf),(CpuFacName[0] >> 12 & 0x3),(CpuFacName[0] & 0xf));
//get Linear/Physical Address size
get_cpuid(0x80000008,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
color_printk(YELLOW,BLACK,"Physical Address size:%08d,Linear Address size:%08d\n",(CpuFacName[0] & 0xff),(CpuFacName[0] >> 8 & 0xff));
//max cpuid operation code
get_cpuid(0,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
color_printk(WHITE,BLACK,"MAX Basic Operation Code :%#010x\t",CpuFacName[0]);
get_cpuid(0x80000000,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
color_printk(WHITE,BLACK,"MAX Extended Operation Code :%#010x\n",CpuFacName[0]);
}
功能号 0x00
获取处理器的供应商标识
get_cpuid(0,0,&CpuFacName[0],&CpuFacName[1],&CpuFacName[2],&CpuFacName[3]);
传入的功能号0x00
*(unsigned int*)&FactoryName[0] = CpuFacName[1];
其中 CpuFacName[1] = 0x756e6547 代表 "Genu"*(unsigned int*)&FactoryName[0]
先取FactoryName[0]的地址,然后就是将 0x756e6547 填入FactoryName[0] FactoryName[1] FactoryName[2] FactoryName[3]
color_printk(YELLOW,BLACK,"%s\t%#010x\t%#010x\t%#010x\n",FactoryName,CpuFacName[1],CpuFacName[3],CpuFacName[2]);
其中%s
把 0x756e6547 看做字符串 Genu输出,%#010x
直接输出数值0x756e6547
功能号0x80002~0x80004
获取处理器商标信息
for(i = 0x80000002;i < 0x80000005;i++) { }
init_cpu() 在何处被调用 ?
通常情况下,这些数据(处理器固件信息),应该最先被操作系统捕获,随后操作系统再根据处理器的固件信息来确定处理器支持的功能,以便进一步初始化处理器。
因此,将init_cpu
函数插入到系统异常处理功能的初始化函数( sys_vector_init();)之后会更妥当一些
代码清单8-6 第8章\程序\程序8-2\物理平台\kernel\main.c
void Start_Kernel(void)
{
……
sys_vector_init();
init_cpu();
……
}
参考资料
[ATT汇编]程序举例:xxx.S 编译、链接、运行、调试 (CPUID命令 显示处理器厂商信息)
https://www.jianshu.com/p/91506500523a
[内联汇编]扩展asm:格式、占位符、跳转、内联汇编宏函数
https://www.jianshu.com/p/76fda24ee7f7