linux-0.11分析:boot文件 setup.s 第二篇随笔

boot文件 setup.s 第二篇随笔

参考 [github这个博主的][ https://github.com/sunym1993/flash-linux0.11-talk ]

  1. 中断获取光标的位置,和从磁盘读取一些东西

    entry start
    start:
    ; ok, the read went well so we get current cursor position and save it for
    ; posterity.
    	mov	ax,#INITSEG	; this is done in bootsect already, but... INITSEG = 0x9000
    	mov	ds,ax
    	mov	ah,#0x03	; read cursor pos
    	xor	bh,bh
    	int	0x10		; save it in known place, con_init fetches
    	mov	[0],dx		; it from 0x90000.
    

    读取光标的位置在dx中,dh(高8位)存储的是行号,dl(第8位)存储的是列号.

    然后把dx存入偏移地址为[0],段地址为0x9000中:0x9000:0x0000

  2. 获取一些其他信息,内存大小、显卡位置、一些配置参数存入到内存中

    ; 获取内存大小(扩展内存,kB)
    	mov	ah,#0x88
    	int	0x15
    	mov	[2],ax
    
    ; 获取显示卡数据:
    	mov	ah,#0x0f
    	int	0x10
    	mov	[4],bx		; bh = 显示页面
    	mov	[6],ax		; al = 显示模式模式,ah=窗口宽度
    
    ; 检查EGA/VGA和一些配置参数
    	mov	ah,#0x12
    	mov	bl,#0x10
    	int	0x10
    	mov	[8],ax
    	mov	[10],bx
    	mov	[12],cx
    
    • int 0x15中断获取一些内存信息,存储到0x9000:0x0002中,也就是0x90002
    • int 0x10中断获取其他信息,显示页面存储到0x9000:0x0004中,也就是0x90004;显示模式和窗口宽度在ax中,然存储到0x9000:0x0006中,也就是0x90006,al = 显示模式模式,ah = 窗口宽度
    • int 0x10中断获取一些配置参数的信息,分别存储到0x90008,0x9000A,0x9000C中
  3. 接下来又是利用:rep movsb 复制字节来读取硬盘信息

    ; Get hd0(硬盘) data
    	mov	ax,#0x0000
    	mov	ds,ax
    	lds	si,[4*0x41]
    	mov	ax,#INITSEG
    	mov	es,ax
    	mov	di,#0x0080
    	mov	cx,#0x10
    	rep
    	movsb
    
    ; Get hd1(硬盘) data
    	mov	ax,#0x0000
    	mov	ds,ax
    	lds	si,[4*0x46]
    	mov	ax,#INITSEG
    	mov	es,ax
    	mov	di,#0x0090
    	mov	cx,#0x10
    	rep
    	movsb
    
    lds si,[4*0x41] ;也就是把这个地址的低位给si
    

    hd0是从 es:si = ds:di 读取16个字节:也就是从

    0x0000:0x0004 ==> 0x9000:0x0080

    hd0是从 es:si = ds:di 读取16个字节:也就是从

    0x0000:0x0018 ==> 0x9000:0x0090

setup经过上面的操作:

地址 字节 存储的东西
0x90000 1 光标列号
0x90001 1 光标行号
0x90002 2 内存大小
0x90004 2 显示页面
0x90006 1 模式模式
0x90007 1 窗口宽度
0x90008 2 配置参数
0x9000A 2 配置参数
0x9000C 2 配置参数
0x90080 16 hd0硬盘信息
0x90010 16 hd1硬盘信息
  1. 接下来这一段不是很重要在判断hd1是否存在信息

    ; Check that there IS a hd1 :-)
    	mov	ax,#0x01500
    	mov	dl,#0x81
    	int	0x13
    	jc	no_disk1
    	cmp	ah,#3
    	je	is_disk1
    no_disk1:			;没有就再移动一次
    	mov	ax,#INITSEG	;INITSEG=0x9000
    	mov	es,ax
    	mov	di,#0x0090
    	mov	cx,#0x10
    	mov	ax,#0x00
    	rep
    	stosb
    
  2. 存储完毕就转到保护模式并且结束中断

    is_disk1:
    ; 现在我们要转到保护模式 ...
    	cli			; 不允许中断
    
  3. 接下下来又是一段内存数据的复制操作:

    mov	ax,#0x0000
    	cld			; 'direction'=0, movs moves forward
    do_move:
    	mov	es,ax		; destination segment	es = 0x0000
    	add	ax,#0x1000
    	cmp	ax,#0x9000		;控制循环8次
    	jz	end_move
    	mov	ds,ax		; source segment		ds = 0x1000
    	sub	di,di
    	sub	si,si
    	mov cx,#0x8000	;每次移动 0x8000次
    	rep
    	movsw			;一次移动 2个字节
    	jmp	do_move
    
    	cmp	ax,#0x9000
    	jz	end_move	;当ax为等于0x9000时就跳转,一直循环移动的8个来回,正好可以跳转
    

    这里也是 es:si = ds:di :

    0x10000开始的0x80000个字节移动到  ==>  0x00000开始位置后面的80000个字节
    0x10000-0x90000   ==>  0x00000-0x80000
    

    经过上面这一段存储信息,移动内存等等操作现在重新看看内存表

  1. 接下来就是从实模式到保护模式的操作了

    end_move:				;这也就是上面复制系统代码结束后来到的地方
    	mov	ax,#SETUPSEG	; right, forgot this at first. didn't work :-)
    	mov	ds,ax
    	lidt	idt_48		; load idt with 0,0
    	lgdt	gdt_48		; load gdt with whatever appropriate
    
    ......
    
    idt_48:
    	.word	0			; idt limit=0
    	.word	0,0			; idt base=0L
    
    gdt_48:
    	.word	0x800		; gdt limit=2048, 256 GDT entries
    	.word	512+gdt,0x9	; gdt base = 0X9xxxx	
    

    实模式和保护模式第一个区别就是寻址方式的改变,

    在实模式下是 段地址:偏移地址

    在保护模式下:

    • ds 寄存器里存储的值,在实模式下叫做段基址,在保护模式下叫段选择子 ; 段选择子里存储着段描述符的索引

    • 通过段描述符索引,可以从全局描述符表 GDT 中找到一个段描述符,段描述符里存储着段基址。

    • 段基址取出来,再和偏移地址相加,就得到了物理地址,整个过程如下。

      段基址:也就是段描述符基地址

    • 然后经过下面的操作:就可以得到物理地址

    • GDT 表在哪,在内存中:

      lgdt    gdt_48 	;通过这条指令来存储这个表的
      

      看看 gdt_48这个表:

      gdt_48:
      	.word	0x800		; gdt limit=2048, 256 GDT entries
      	.word	512+gdt,0x9	; gdt base = 0X9xxxx
      

      gdt 的内存地址:0x90200 + gdt

      这就是gdt存储的数据,页数这个表

      gdt:
      	.word	0,0,0,0		; dummy
      
      	.word	0x07FF		; 8Mb - limit=2047 (2048*4096=8Mb)
      	.word	0x0000		; base address=0
      	.word	0x9A00		; code read/exec
      	.word	0x00C0		; granularity=4096, 386
      
      	.word	0x07FF		; 8Mb - limit=2047 (2048*4096=8Mb)
      	.word	0x0000		; base address=0
      	.word	0x9200		; data read/write
      	.word	0x00C0		; granularity=4096, 386
      

      最终的内存结构:( idtr 寄存器 == > 中断描述符表 )

  2. 接下来这段代码就是打开20A的地址总线

    call	empty_8042
    mov	al,#0xD1		; command write
    out	#0x64,al
    call	empty_8042
    mov	al,#0xDF		; A20 on
    out	#0x60,al
    

    也就是当CUP升级到了32位时,由于物理地址总线还是20位的,所以只能使用20位的寻址方式,使用需要手动打开20位的地址总线

  3. 接下来这段代码不是很重要

    ; 嗯,我希望一切顺利。现在我们必须重新编程中断:
    ; 我们把它们放在intel保留硬件中断之后
    ; int 0x20-0x2F。在那里,他们不会搞砸任何事情。可悲的是,IBM真的
    ; 把这件事和原来的电脑搞砸了,他们还没能做到
    ; 事后纠正。因此,bios在0x08-0x0f处设置中断,
    ; 它也用于内部硬件中断。我们只是
    ; 必须重新编程8259,这一点都不有趣。
    
    mov	al,#0x11		; initialization sequence
    out	#0x20,al		; send it to 8259A-1
    .word	0x00eb,0x00eb		; jmp $+2, jmp $+2
    out	#0xA0,al		; and to 8259A-2
    .word	0x00eb,0x00eb
    mov	al,#0x20		; start of hardware int's (0x20)
    out	#0x21,al
    .word	0x00eb,0x00eb
    mov	al,#0x28		; start of hardware int's 2 (0x28)
    out	#0xA1,al
    .word	0x00eb,0x00eb
    mov	al,#0x04		; 8259-1 is master
    out	#0x21,al
    .word	0x00eb,0x00eb
    mov	al,#0x02		; 8259-2 is slave
    out	#0xA1,al
    .word	0x00eb,0x00eb
    mov	al,#0x01		; 8086 mode for both
    out	#0x21,al
    .word	0x00eb,0x00eb
    out	#0xA1,al
    .word	0x00eb,0x00eb
    mov	al,#0xFF		; mask off all interrupts for now
    out	#0x21,al
    .word	0x00eb,0x00eb
    out	#0xA1,al
    

    不是很重要,这是之后中断信息表

    PIC 请求号 中断号 用途
    IRQ0 0x20 时钟中断
    IRQ1 0x21 键盘中断
    IRQ2 0x22 接连从芯片
    IRQ3 0x23 串口2
    IRQ4 0x24 串口1
    IRQ5 0x25 并口2
    IRQ6 0x26 软盘驱动器
    IRQ7 0x27 并口1
    IRQ8 0x28 实时钟中断
    IRQ9 0x29 保留
    IRQ10 0x2a 保留
    IRQ11 0x2b 保留
    IRQ12 0x2c 鼠标中断
    IRQ13 0x2d 数学协处理器
    IRQ14 0x2e 硬盘中断
    IRQ15 0x2f 保留
  4. 下面是从实模式切换到保护模式的最后步骤

    mov	ax,#0x0001	; 保护模式(PE)位
    lmsw	ax		; This is it;
    jmpi	0,8		; 第8段(cs)的jmp偏移0
    

    前两行,将 cr0 这个寄存器的位 0 置 1,模式就从实模式切换到保护模式了。

    jmpi 0,8 就是把段寄存器和偏移地址改变,在保护模式下的寻址方式变了

    然后在最后的 计算下 最后执行的位置为0x00000处

    这个位置就是最后操作系统的System代码在内存中的位置

  5. System这段代码的文件就是通过Makefile这个执行把head.smain.c以及其他的文件整合起来一起导入,接下就看看head.s代码把

posted @ 2022-07-28 21:17  水三丫  阅读(206)  评论(0编辑  收藏  举报