x86_64-中断,异常与syscall
介绍本文主要介绍x86_64中的中断,异常与sys call,以及他们的基本处理。x86_64中的中断处理与syscall需要使用一系列的寄存器以及其他字段,如GDT,IDT等,这些在下文会中途介绍。
中断与异常中断和异常其实差别不是很大,一般来讲异常会触发中断,比如一个page fault异常会触发它对应的中断,因此在下面统一将异常归于中断中。中断比较好理解,CPU在执行指令的时候突然发生了一系列它不得不暂时放弃正在执行的指令,转而去执行中断处理程序来解决问题,放在现实生活中就好比在读书,打游戏时突然被喊吃饭了,先去吃饭。读书打游戏就相当于CPU正在执行的指令,去吃饭就相当于中断处理程序,不同的中断当然也需要不同的处理程序,吃饭和拿快递显然行动不一样。
那么下面说明一下中断的分类,中断主要分为外部中断和内部中断,内部中断一般指的是CPU在执行指令时出发的一些异常,或者指令自己触发的中断(int xxx)等。外部中断主要代表的是外设通过一些设备比如APIC等出发的CPU外部中断,它们会带有一个特定的vector,在这里称之为中断号,让CPU跳转到对应的中断处理程序。
syscallsy ...
Virtio文档阅读笔记(三)具体设备
以下设备的顺序与文档顺序不太一样, 或者不全, 日后会补, 遗留的特征等不会写上来
块设备什么是块设备在文档中的定义如下: Virtio块设备是一个简单的虚拟块设备, 比如磁盘等, 读取和写入请求被放入在了队列中, 并由设备处理(可能不是顺序处理), 块设备的Device ID为2
块设备的特征bits
bit
特证名
解释
1
VIRTIO_BLK_F_SIZE_MAX
最大的任何segment的大小放在了size_max
2
VIRTIO_BLK_F_SEG_MAX
一个request的最大数量segment放在了seg_max
4
VIRTIO_BLK_F_GEOMETRY
Disk-style geometry放在了geometry
5
VIRTIO_BLK_F_RO
设备是只读
6
VIRTIO_BLK_F_BLK_SIZE
磁盘的块大小放在了blk_size
9
VIRTIO_BLK_F_FLUSH
支持缓存刷新命令
10
VIRTIO_BLK_F_TOPOLOGY
设备利用最佳I/O对齐(optimal I/O ali ...
PCI+Express体系结构导读笔记(二)-MSI及MSI-X
MSI及MSI-X中断机制为什么提出这两个概念在这一篇中讲的原因为: MSI/MSI-X机制的引入解决了传统Line-based Interrupt机制的限制, 包括:
无需经过I/O APIC转发中断,直接通过PCI/PCIe Memory Write Transaction向CPU发送中断,效率更高
每个PCI Function可以支持分配多个中断向量,满足同一个设备有多个不同中断请求的需要
当分配多个中断向量给同1个PCI Function时,提供按中断向量进行屏蔽的功能,更为灵活
在PCI中, MSI/MSI-X为可选功能, PCIe必须要修支持64位的MSI或者MSI-X, 一个PCIe可以同时支持 INT#x/MSI/MSI-X, 但驱动只用启动其中一个, MSI/MSI-X以Capability的形式存在于PCIe中
MSI/MSI-X Capability结构MSI结构MSI的结构可以由四种方式组成, 分别为:
32位的Message结构
64位的Message结构
32位带中断的Ma ...
PCI+Express体系结构导读笔记(一)-基础介绍
备注只注重架构以及软件的实现, 其余内部的具体信号还有其他的引脚不会记录
PCI的结构
如图所示, PCI总线的相关模块包括:
Host主桥
PCI总线
PCI桥
PCI设备
整个PCI架构以一个树状结构呈现, Host主桥用于连接CPU与PCI设备, PCI桥用于拓展PCI设备(树中的非叶子节点), PCI总线代表树中的枝, PCI设备则代表树中的所有节点(下面的定义中, PCI桥也是PCI设备的一部分,只是他是用于拓展其他PCI设备用的).
Host主桥Host主桥的主要功能有:
隔离处理器系统的存储器域与PCI总线域, 一个Host主桥对应一个PCI总线域
管理PCI总线域
完成处理器与PCI设备之间的数据交换
PCI总线PCI总线由HOST主桥以及PCI桥进行管理, 用来连接各种设备
PCI设备PCI总线中一共有三种设备:
PCI主设备, 可以通过总线仲裁获得PCI总线的使用权, 主动发出请求
PCI从设备, 只能被动接受来自HOST主桥或者其他PCI设备的读写请求
桥设备, 用于管理下游的PCI总线, 即为PCI桥
PCI总线规范中将PCI主从设备统称为PCI A ...
Virtio文档阅读笔记(二)传输形式
Virtio的传输形式Virtio可以使用各种不同的总线, 因此标准分为Virtio通用部分和总线专用部分
基于PCI总线的Virtio发现PCI设备任何PCI供应商ID为0x1AF4且PCI设备ID为0x1000到0x107F(包括1000和107F)的PCI设备都是virtio设备, 这个范围内(0x1000-0x107F)的值标识是哪个virtio设备, PCI设备ID与Virtio设备计算方式为PCI设备ID = 0x1040+Virtio设备ID, 此外, 根据设备类型, 设备可能使用过渡的PCI设备ID范围0x1000-0x103F. PCI的子设备供应商ID和PCI子设备ID可以反应PCI供应商和设备ID(毕竟原来的被占用了)
暂时PCI设备ID
Virtio设备类型
0x1000
网络设备
0x1001
块设备
0x1002
内存膨胀(memory ballooning)
0x1003
控制台
0x1004
SCSI host
0x1005
entropy source
0x1009
9P transport
一个例子: ...
Virtio文档阅读笔记(一)基础设备
备注
文档中的特殊说明比如驱动程序必须要干啥, 设备必须要干啥没总结了, 按着规矩来就好, 如果发现出问题了可以上文档里翻翻
有一些自认为多余的没加上来, 防止看的有点绕.
Virtio基本设施每个virtio设备通过一个特定的方法被发现(具体为PCI, MMIO, 通道IO), 每个设备包含以下的部分
设备状态字段(Device status field)
特征位 (Feature bits)
通知 (Notification)
设备配置空间 (Device Configuration space)
一个或多个虚拟队列 (one or more virtqueues)
设备状态字段包含该设备的状态:
ACKNOWLEDGE(1): 操作系统发现了这个设备, 认为这是个有效的virtio设备
DRIVER(2): 操作系统知道怎么驱动该设备
FAILED(128): 由于某种原因, 操作系统放弃驱动该设备
FEATURES_OK(8): 驱动程序确认了它理解的所有特征, 特征协商达成一致
DRIVER_OK(4): 驱动程序加载完成, 可以驱动该设备了
DEVICE_NEEDS ...
用Rust改写riscv-pk总结
闲话 最近闲来无事, 便想总结一下暑假进行的用Rust改写riscv-pk这个练手项目, 主要是为了熟悉一下rCore以及Rust, 也学会了用objdump来反汇编一个二进制文件进行debug, 目前看来除了rCore的文件系统以及一些驱动基本都熟悉了一遍, 因为riscv-pk的文件系统是直接交由宿主机完成, 它只是一个代理作用, 熟悉rCore的同时也了解了riscv-pk的pk部分, 与现在在看的x86_64的内存空间分布感觉差不多.
总的来讲改写起来还是蛮有趣的, 主要有成就感的点有几个, 一个是当os输出hello world的时候(蛮简单的), 以及虚拟内存成功跑起来, 还有一个就是跑user-mode的elf文件, 并且syscall陷入trap, 最后一个点成就感比较明显. 但这一次改写只是搭建了一个框架, 实现了user-mode程序的hello world, 没有实现其他的syscall, 这一部分还是需要未来慢慢熟悉, 慢慢实现.
最后补一句: rCore无论是教程还是代码都蛮不错, 建议去看看, 附:
rCore教程地址
rCore GitHub地址
...
x86_64-寄存器详解
说明这一篇的寄存器来自于Rust-x86_64库总结,讲解在registers下的一些寄存器, 随时更新
寄存器通用寄存器
寄存器名
作用
谁保存
%rax
返回值
-
%rbx
被调用者
%rcx
参数4
调用者
%rdx
参数3
调用者
%rsi
参数2
调用者
%rdi
参数1
调用者
%rbp
被调用者
%rsp
栈顶(指针)
-
%r8
参数5
调用者
%r9
参数6
调用者
%r10
调用者
%r11
调用者
%r12
被调用者
%r13
被调用者
%r14
被调用者
%r15
被调用者
还有一个指令寄存器RIP, 不知道放哪就放这了
Control Registers各个寄存器大致的说明:
CR0: 当前处理器运行的控制标志
CR2: 发生页面错误时的线性地址
CR3: 最高级页表的地址(与RISCV中的satp作用差不多)
CR4: 包含处理器扩展功能的标志位
图片:
CR0
位名
全称
作用(是否的话除特殊说明否则视为bool)
PE
Protected mo ...
Rust-x86_64库
原因最近在学x86_64的操作系统, blog os中用到了x86_64这个库, 看了一下里面蛮多东西都已经写好了, 可以直接用, 所以总结一下(由于里面的寄存器或者指令等可能不会用到,所以这篇会跟RISCV CSV详解那一张一样随时更新), 总结的同时也能了解一下x86的一些东西
首先列出来每个rs文件的作用, 之后会细讲里面的一些东西:
根目录
文件名
作用
备注
addr.rs
包含物理地址以及虚拟地址的结构体
lib.rs
库的lib, 包含一个特权级的enum
structures
文件名
作用
备注
gdt.rs
二级中断表, 指定每个中断入口函数后load启用
GlobalDescriptorTable
idt.rs
一级中断表, 指定每个中断入口函数后load启用
InterruptDescriptorTable
mod.rs
mod
port.rs
两个trait, 用于读写IO端口
提供给instructions包用的
tss.rs
用于描述一个TaskStateSegment
64位与32位有点区别,参见下面的表格 ...
Rust-C-汇编之间的调用
前言 最近在尝试Rust与C之间的混合编译, 在里面还夹杂着汇编文件来写特定的入口函数, 所以写这一篇记录Rust,C与汇编他们之间如何互相调用
首先来看C语言的源文件是如何变成可执行文件的
预处理(hello.c -> hello.i) 将宏#define等替换
编译(hello.i -> hello.s) 将预处理后的文件编译成汇编代码
汇编(hello.s -> hello.o) 根据汇编生成目标文件(二进制)
链接(hello.o -> hello.exe/out) 将多个目标文件链接生成可执行代码
在这个过程中可以看见直接写汇编语言缺少了前面两项, 而C语言还是要生成汇编文件, 因此只要汇编和C语言都遵循一个规则它们就可以互相调用了
在不同的机器上参数传递标准似乎不太一样, 比如x86中似乎是直接将参数入栈以从右向左的方式来传递, 而在x86-64和RISCV等架构用一些专门的寄存器来传参数, 超过多少个参数才会使用栈来传递参数, 对于他们之间的具体差异可以见Linux X86架构参数传递规则, 本篇文章里面使用x86-64为例, 因为 ...