Skip to content

Burgundy-Red/toySys

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bootloader

1. MBR 只有512B,放不下所有代码

2. boot -> loader -> kernel

硬盘读取:
    - 硬盘控制端口
    | Primary 通道            | Secondary 通道 | in 操作      | out 操作     |
    | ----------------------- | -------------- | ------------ | ------------ |
    | 0x1F0                   | 0x170          | Data         | Data         |
    | 0x1F1                   | 0x171          | Error        | Features     |
    | 0x1F2                   | 0x172          | Sector count | Sector count |
    | 0x1F3                   | 0x173          | LBA low      | LBA low      |
    | 0x1F4                   | 0x174          | LBA mid      | LBA mid      |
    | 0x1F5                   | 0x175          | LBA high     | LBA high     |
    | 0x1F6                   | 0x176          | Device       | Device       |
    | 0x1F7                   | 0x177          | Status       | Command      |

内存检测

BIOS 0x15 0xe820

注意:将ards_buffer放到最后,因为不知道会有多长

实模式到保护模式

1. 实模式缺点: 
    - 实模式下操作系统和用户程序属于同一特权级
    - 用户程序所引用的地址都是指向真实的物理地址
    - 用户程序可以自由修改段基址,不受阻碍地访问所有内存
    - 访问超过 64KB 的内存区域时要切换段基址
2. 保护模式如何解决:
    - 地址转换:在开启分页机制后,程序引用的地址、CPU执行机器指令时处理的地址都变成了虚拟地址,
      需要转化为物理地址后再去访问,地址转换由 处理器 和 操作系统 共同完成。处理器提供硬件,操
      作系统提供页表。
    - 兼容实模式:实模式是 32位CPU 运行在 16位模式 下的状态,此时 CPU 相当于更加强大的16位CPU,
      可以处理32位的操作数。
    - 段保护:在实模式下,程序访问内存段时,CPU 会完成对用户的权限检查,防止出现错误
3. 需要哪些步骤:
    - 打开A20地址线:通过0x92端口
    - 加载GDT:OS 会在 loader 代码中定义GDT,然后更新 gdtr 指向这一段内存,CPU 通过段寄存器存储段选择子,
      访问地址首先通过段描述符表获得段描述符,然后通过段描述符得到段基址去访问,在这一过程,加载段选择子
      和获得访问地址的过程中都会进行内存保护,如果保护失败就抛出异常。
    - 将 cr0 的pe置为0

注意:一个nasm文件里,同时会处理16或者32位,例如在写实模式跳转到保护模式,在初始化保护模式中最后一句跳转指令所跳到
    的标号地址一定再[bits 32]下面。(16位代码段和32位代码段的主要区别是,在16位代码段中,跳转目标的偏移用16位表示,
    而在32位代码段中,跳转目标的偏移用32位表示。)

内核全局描述符

将loader中保存的全局描述符表和指针保存在内核中
- lgdt [gdt_ptr]; 加载 gdt
- sgdt [gdt_ptr]; 保存 gdt

注意:selector
```c++
    typedef struct selector_t
    {
        u8 RPL : 2;
        u8 TI : 1;
        u16 index : 13;
    } selector_t;
    // 代码段 1 << 3 
```

中断

- 实模式下:
    中断向量表(0x000 ~ 0x3ff)中保存256个中断函数指针(4字节, 段地址<<4+偏移地址, 低位偏移地址,高位段地址)
    
    1. 注册中断向量表
    2. 调用方式:
        - int / iret: 保存 [错误码], eip, cs, eflags 值
        - int nr ; 中断函数编号
    3. 异常中断一般在异常处理函数中结束程序

- 保护模式下:
    中断描述符表可放在内核任意位置
    需要先注册中断处理函数
    
- 引发中断的方式有三种: 外中断 异常 软中断

    1. 外中断就是由外部中断控制器通知 CPU 需要执行的,CPU 在当前指令执行完成之后,回去检测是否有中断产生,
       如果有,并且 IF 位有效,也就是允许中断,那么就会执行中断处理函数,这种方式直接的体验就是,CPU 可以
       在任意两个指令间插入一个中断函数调用,中断函数调用与普通函数调用稍有区别,在调用时栈中多压了一些数
       据,在中断返回时,会弹出;
    
    - 异常和软中断统称为 内中断,也就是这个中断时 CPU 和 软件内部产生的,与外部硬件无关;

    2. 异常是 CPU 在执行过程中,执行不下去了,引发的中断调用,比如 除零异常,缺页异常,一般保护错误,有一
       些异常在处理后程序是可以继续执行的,比如缺页异常,而有一些异常就不行了,比如一般保护,这种情况下一
       般是软件访问了不该访问的内存或者寄存器,自己没有权限,于是CPU会调用一般保护异常函数,这个函数中,一
       般会终止该进程的执行,试图访问自己没有权限的内容,应该是危险的程序,可能是恶意程序,或者是程序有漏洞;

    3. 软中断,可以认为是应用程序和操作系统沟通的一种方式,应用程序运行在较低的特权级,一般来说没有直接访
       问硬件的权限,当应用程序想要访问硬件的时候,比如典型的读写文件,就需要调用系统调用,系统调用就是用
       软中断实现的,也就是应用程序调用软中断函数来请求操作系统,以访问硬件,访问硬件的函数是操作系统实现
       的,于是被认为是安全的。

注意:有的中断会压入错误码,可以压入一个随机数,保持后续操作一致
错误码

- 外中断原理
    指令执行流程:
        取指:将 eip 指向的指令读入处理器
        译码:将指令的微程序写入流水线 (多级 cache)
        执行:执行指令
        中断:处理中断
    
    IF: Interrupt Flag 外中断允许标志,CPU 外中断的总开关
    
    sti; 设置 IF 位 (set interrupt),允许外中断
    cli; 清除 IF 位 (clear interrupt),禁止外中断

    pushf; 将 elags 压入栈中
    popf; 将栈顶弹出到 eflags
    
    检测IF位(是否开启中断),检测INTR引脚(级联8259a芯片),得到中断编号,执行

    利用外中断(时钟中断)进行任务切换:
    
![](./images/interrupt_context.drawio.svg)

TODO: 初始化外中断步骤

内存管理

1. 初步
    - 得到loader中内存检查结果(将储存结果的地址压栈,当做函数参数)
    - 找到可用内存(type=1,基址1M 0x100000)
    
2. 物理内存管理
    - 可用内存开始位置一些页用于管理物理内存
    - 每一个物理页用一个字节表示引用数量
    - get_page(), put_page();
    
3. 分页机制(平坦模型)
    - 虚拟地址 -> 页目录(cr3寄存器) -> 页表 -> 页 + 偏移
      4M = 4K * 1024,也就是需要1024个页存储;
      用页目录表示这1024个页,用到了哪些页,一页页表4B,总共4KB。
    - 使用
      (1) 按照地址结构将逻辑地址拆成三个部分(10,10,12)。
      (2) 从PCB中读取页目录起始地址,再根据一级页号查页目录表,找到下一级页表在内存中存放位置。
      (3) 根据二级页号查表,找到最终想要访问的内存块号。
      (4) 结合页内偏移量得到物理地址。
    
4. 启用分页
    - 首先准备一个页目录,若干页表
    - 将映射的地址写入页表,将页表写入页目录
    - 将页目录写入 cr3 寄存器
    - 将 cr0 最高位 (PG) 置为 1,启用分页机制

硬盘异步PIO 参考

与同步区别就是在磁盘准备数据的时候是否忙等待(异步:阻塞,中断唤醒)

PIO方式读命令的执行过程如下:
    1) 根据要读的扇区位置,向控制寄存器1F2H~1F6H发命令参数,等驱动器的状态寄存
    器1F7H的DRDY置位后进入下一步;
    2) 主机向驱动器命令控制器1F7H发送读命令20H;
    3) 驱动器设置状态寄存器1F7H中的BSY位,并把数据发送到硬盘缓冲区;
    4) 驱动器读取一个扇区后,自动设置状态寄存器1F7H的DRQ数据请求位,并清除BSY
    位忙信号。DRQ位通知主机现在可以从缓冲区中读取512字节的数据,同时向主机发
    INTRQ中断请求信号;
    5) 主机响应中断请求,开始读取状态寄存器1F7H,以判断读命令执行的情况,同时
    驱动器清除INTRQ中断请求信号;
    6) 根据状态寄存器,如果读取的数据命令执行正常,进入7)
    7) 主机通过数据寄存器1F0H读取硬盘缓冲区中的数据到主机缓冲区中,当一个扇区
    数据被读完,扇区计数器−1,如果扇区计数器不为0,进入3),否则进入8);
    1) 当所有的请求扇区的数据被读取后,命令执行结束。

文件系统

![参考](https://silverrainz.me/blog/minix-v1-file-system.html) 

实现 minix 文件系统
Minix v1 file system structure

1 zone = 2 block = 1024 byte
| bootsector | superblock | inode bitmap ... | zone bitmap ... | inodes zone | data zone |
| ......     | ......     | ......           | ......          | ......      | ......    |
| 主引导扇区 | 超级块     | inode位图        | 数据块位图      | inode块     | 数据块    |


- 系统层次:
    整个文件系统的实现被分为五个层次

    1. 磁盘驱动层:这一层通过 ins outs 指令, 负责从磁盘读取扇区到高速缓冲区 
        buf。
        ide_ctrl_init(控制器初始化,识别硬盘,硬盘分区检测)
    
    2. 块缓冲层:维护了一个高速缓冲的hashtable(拉链), 为上层提供bread bwrite
        函数, 而getblk则用来分配缓冲区,brelse用来释放缓冲块

    3. inode节点层:这一层开始和文件系统密切相关, inode节点层为使用中的磁盘中
        的inode节点(inode_descriptor_t) 提供了内存中的拷贝(inode_t), 可以类比
        块缓冲和虚拟块的关系.

    4. 目录层

    5. 文件和系统调用

DEBUG

vsprintf
sprintf
printk
显示驱动

解释

- 映射: 所谓的映射就是访问某个地址空间中的内容时,就会自动定位到被映射的目标
    物理设备中进行访问,这是由硬件来保证的。

- BIOS 所做的事情包括:
    侦测硬件设备:系统中有哪些硬件设备,工作状态是什么;
    对硬件设备进行初始化:比如最初始的中断向量表;
    侦测操作系统启动设备:选择好一个系统盘之后,把系统盘中主引导扇区中的引导程
                          序读取到内存中;
                          
- 特权级相关:
    不论是访问数据,还是跳转到代码,特权级检查仅发生在 重新加载选择子 时,而不是每条指令都检查一遍。
    对于 访问数据 来说,只能高特权级的指令访问地特权级的数据
    对于 跳转到代码 来说,只能平级跳转,如果想从低特权级跳到高特权级需要通过 门,如果想从高特权级跳到低特权级需要通过 返回指令
    
    | 门     | type值 | 存在位置      | 用法                                                                     |
    | ----   | ----   | ----          | ----                                                                     |
    | 任务门 | 0101   | GDT、LDT、IDT | 与TSS配合实现任务切换,不过大多数操作系统都不这么玩                      |
    | 中断门 | 1110   | IDT           | 进入中断后屏蔽中断(eflags的IF位置0),linux利用此实现系统调用,int 0x80 |
    | 陷阱门 | 1111   | IDT           | 进入中断后不屏蔽中断                                                     |
    | 调用门 | 1100   | GDT、LDT      | 用户用call或jmp指令从用户进程进入0特权级                                 |

- 

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published