写在前面:根据视频一步步走的
自己动手写一个操作系统【中英字幕】_哔哩哔哩_bilibili
使用c++编写操作系统_哔哩哔哩_bilibili
纯代码向记录!!!后续会慢慢补完整文档
环境准备
Ubuntu 20.04,VS code,VM或Virtual Box
ubuntu ssh
如何在 Ubuntu 20.04 上安装启用 SSH - 知乎 (zhihu.com)
sudo apt-get install openssh-server
sudo systemctl status ssh #查看状态,安装后默认开启
sudo ufw allow ssh #若是防火墙拦了就开规则
VS Code
配置C++环境:随便写个C文件,VSCode自动检测到后右下角弹安装C/C++,ctrl+shift+P快速搜到C/C++:Edit Configurations(UI)
去改里面的编译器,编译器路径找g++.exe,IntelliSense 模式选gcc-x64
配置ssh:VSCode远程连接ubuntu服务器_vscode连接ubuntu_风筝_的博客-CSDN博客
下ssh插件,亲测Remote development有用,改config。网上说安过Anconda等电脑一般不止一个ssh,容易报错,手动加上路径C:\Users\Lyris.ssh\config
hello OS
VV的操作系统笔记(一)操作系统I SeeYou!!! - 腾讯云开发者社区-腾讯云 (tencent.com)
编写make
Makefile
GPPPARAMS = -m32 -fno-use-cxa-atexit -fleading-underscore -fno-exceptions -fno-builtin -nostdlib -fno-rtti -fno-pie
ASPARAMS = -32 #有的版本是--32
LDPARAMS = -melf_i386 -no-pie
objects = loader.o kernel.o #objects变量包含两个文件
%.o: %.cpp #.cpp→.o
g++ ${GPPPARAMS} -o $@ -c $< #$@表示目标文件名即.o文件,$<第一个依赖项即.cpp文件
%.o: %.s
as ${ASPARAMS} -o $@ $<
mykernel.bin: linker.ld ${objects} #生成mykernel.bin需要linker.ld链接脚本和${objects}变量中指定的所有目标文件
ld ${LDPARAMS} -T $< -o $@ ${objects} #ld ${LDPARAMS} -T linker.ld -o mykernel.bin loader.o kernel.o
install: mykernel.bin #安装mykernel.bin需要先生成mykernel.bin文件
sudo cp $< /boot/mykernel.bin #sudo cp mykernel.bin /boot/mykernel.bin
- 写makefile的时候,tab键开,不要用空格挨个敲
kernel.cpp
void printf(char* str){
unsigned short* VideoMemory = (unsigned short*)0xb8000;
for(int i=0;str[i]!='\0';i++)
VideoMemory[i]=(VideoMemory[i] & 0xFF00) | str[i];
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors(){
for(constructor* i=&start_ctors;i!=end_ctors;i++)
(*i)();
}
extern "C" void kernelMain(void* multiboot_structure, unsigned int magicnumber){#C++编译时会替换名字,用C编
printf("hello world");
while(1);
}
loader.s
.code32
.set MAGIC, 0x1badb002
.set FLAGS, (1<<0 | 1<<1)
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multboot
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .text
.extern _kernelMain ;编译时kernelMain其实是编译成_kernelMain
.extern _callConstructors
.global loader
loader:
mov $kernel_stack, %esp
call _callConstructors
push %eax
push %ebx
call _kernelMain
_stop:
cli
hlt
jmp _stop
.section .bss
.space 2*1024*1024
kernel_stack:
;文件末尾需要一个换行符
loeder.ld
ENTRY(loader) //程序入口地址
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS //链接脚本的主体
{
. = 0x0100000; //.表示当前地址
.text :
{
*(.multiboot)
*(.text*)
*(.rodata)
}
.data :
{
_start_ctors = .;
KEEP(*(.init_arry));
KEEP(*(SORT_BY_INIT_PRIORITY(.init_arry.*)));
_end_ctors = .;
*(.data)
}
.bss :
{
*(.bss)
}
/DISCARD/ : { *(.fini_array*) *(.comment) } //丢弃该段
}
然后执行make
make loader.o #得到loader.o文件
make mykernel.bin #得到mykernel.bin文件
- 编译完一堆被加下划线的导致报错,对应到文件里挨个改吧。上面的代码是已经下过下划线的
引导项
可以看一眼grub里是啥
vim /boot/grub/grub.cfg #记得开root
利用grub文件 和kernel文件拉起来一 个镜像
在grub.config最后添加
### my os ###
menuentry "my os"{
multiboot /boot/mykernel.bin
boot
}
### my os ###
printf 改进
printf 打印时,并不会像平时写C++一样一行一行打,如果直接写两行 printf 而是后面的printf会覆盖前面的printf打印的内容(前面 kernel.cpp 里的 printf 多写一行就能看出效果)
一行80×25行,在内存中是连续的内存,打印第二行的时候考虑到前面一整行的空
修改kernel.cpp的 printf
函数
#include "types.h"
#include "gdt.h"
void printf(const char* str){
static uint16_t* VideoMemory = (uint16_t*)0xb8000;
static uint8_t x=0, y=0;
for(int i=0;str[i]!='\0';i++){
switch(str[i]){
case '\n':
y++;
x=0;
break;
default:
VideoMemory[80*y+x]=(VideoMemory[80*y+x] & 0xFF00) | str[i];
x++;
}
if(x>=80){ //一行写满了
x=0;
y++;
}
if(y>=25){//整个屏幕写满
for(y=0;y<25;y++){
for(x=0;x<80;x++){
VideoMemory[80*y+x]=(VideoMemory[80*y+x] & 0xFF00) | ' '; //清空
}
}
x=0;y=0;
}
}
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors(){
for(constructor* i=&start_ctors;i!=&end_ctors;i++)
(*i)();
}
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber){
printf("hello world\n");
printf("hi");
GlobalDescriptorTable gdt;
while(1);
}
准备镜像
安装
网上找怎么安virtual box,然后再安俩东西
sudo apt install xorriso
sudo apt install grub-efi-amd64
随时间这些包可能迭代过程中会改名等问题,具体情况具体搜解决办法
开始搞iso,继续改makefile
GPPPARAMS = -m32 -fno-use-cxa-atexit -fleading-underscore -fno-exceptions -fno-builtin -nostdlib -fno-rtti -fno-pie
ASPARAMS = -32
LDPARAMS = -melf_i386 -no-pie
objects = loader.o kernel.o
%.o: %.cpp
g++ ${GPPPARAMS} -o $@ -c $<
%.o: %.s
as ${ASPARAMS} -o $@ $<
mykernel.bin: linker.ld ${objects}
ld ${LDPARAMS} -T $< -o $@ ${objects}
install: mykernel.bin
sudo cp $< /boot/mykernel.bin
mykernel.iso: mykernel.bin
mkdir iso
mkdir iso/boot
mkdir iso/boot/grub
cp $< iso/boot/
echo 'set timeout=0' > iso/boot/grub/grub.cfg #一个>可以创建文件
echo 'set default=0' >> iso/boot/grub/grub.cfg
echo '' >> iso/boot/grub/grub.cfg
echo 'menuentry "my os" {' >> iso/boot/grub/grub.cfg
echo ' multiboot /boot/mykernel.bin' >> iso/boot/grub/grub.cfg
echo ' boot' >> iso/boot/grub/grub.cfg
echo '}' >> iso/boot/grub/grub.cfg
grub-mkrescue --output=$@ iso #制作镜像
rm -rf iso
- 然后就得到了mykernel.iso文件
求求了记住别把grub打成gurb了T_T
准备装虚拟机,我用的VirtualBox:
- 最上面一栏控制→自己起个虚拟机名字,类型选other→开机看看能不能开机
- 开机后最上面一栏设备→分配光驱→选择虚拟盘,找到从ubuntu里导出的mykernel.iso选它→重启→开屏hello world就没问题了
自动化
在ubuntu虚拟机里起一个虚拟机,步骤同上。启动时应该会遇到”Failed to open a session for the virtual machine myos.“报错,用的virtualbox还是vnware就去搜对应的怎么开虚拟机嵌套
哈,踩坑了,怎么看网上说vmware不支持硬件虚拟化而virtualbox支持(之前搞hyper-V时候vm也是出问题),上网找找vm机子导入vbox换工具继续开发吧
改Makefile,最下面加上run内容
GPPPARAMS = -m32 -fno-use-cxa-atexit -fleading-underscore -fno-exceptions -fno-builtin -nostdlib -fno-rtti -fno-pie
ASPARAMS = -32
LDPARAMS = -melf_i386 -no-pie
objects = loader.o kernel.o
%.o: %.cpp
g++ ${GPPPARAMS} -o $@ -c $<
%.o: %.s
as ${ASPARAMS} -o $@ $<
mykernel.bin: linker.ld ${objects}
ld ${LDPARAMS} -T $< -o $@ ${objects}
install: mykernel.bin
sudo cp $< /boot/mykernel.bin
mykernel.iso: mykernel.bin
mkdir iso
mkdir iso/boot
mkdir iso/boot/grub
cp $< iso/boot/
echo 'set timeout=0' > iso/boot/grub/grub.cfg #一个>可以创建文件
echo 'set default=0' >> iso/boot/grub/grub.cfg
echo '' >> iso/boot/grub/grub.cfg
echo 'menuentry "my os" {' >> iso/boot/grub/grub.cfg
echo ' multiboot /boot/mykernel.bin' >> iso/boot/grub/grub.cfg
echo ' boot' >> iso/boot/grub/grub.cfg
echo '}' >> iso/boot/grub/grub.cfg
grub-mkrescue --output=$@ iso #制作镜像
rm -rf iso
run: mykernel.iso
(killall VBoxManage && sleep 1) || true
VBoxManage startvm "my os" & #自动开启"my os"这台机子
- vbox5用
(killall virtualboxvm && sleep 1) || true
和virtualbox --startvm "my os" &
,我是vbox6所以用VBoxManage
make run #看到机子开启就可以了
环境结束,后面开始处理与硬件交互
切换vbox遇到的问题
怎么从vm→vbox:vm导出选导出为OVF,然后vbox里导入选这个就行
安装增强功能:设置里加上磁盘,再去虚拟机里安,外面加上了理论上虚拟机会直接弹窗让你安,然后进虚拟机找这个光盘,记得挂载好共享文件后,再点右上角run software,这样就可以拖拽文件了
后续拷贝,虚拟机里将文件拷贝到你挂载的地方,对应的主机的另一个挂载点应该会出现。(这两个挂载点的文件完全共享)
sudo cp mykernel.iso /mnt/code/
ssh连接问题:NAT模式下,ifconfig会变成10.0.2.15,需要去设置里找网络,做端口转发。或者直接换桥接模式。记得重启机子
Windows无法ssh连接Virtualbox虚拟机怎么办_virtual box ssh连不进去_传说中的暗暗的博客-CSDN博客
嵌套虚拟机的问题:先关机,设置→系统→高级,如果启用嵌套是灰色的,要用命令开启(不过先去看看物理机Hyper-V开没开),关PAE。进入到安装VirtualBox的目录里输入命令
D:\vmware\VirtualBox>VBoxManage.exe list vms
"myos" {b33f5def-41cb-4f70-aa69-00607f95d0f9}
"vm" {0b6bd16f-b622-420d-8643-c328872ad70e}
D:\vmware\VirtualBox>VBoxManage.exe modifyvm "vm" --nested-hw-virt on
- 需要先在BIOS中启用vt-x,一般默认是禁用的
vmware的问题
BIOS开了vt-x了,hyper-v也关了,就是不行。网上说让我关内核隔离,关了就不弹报错了,还真用上了,离谱……
vbox也是,关了内核隔离就没问题了,我哭死。我实在适应不了vbox,又回vm里写代码了
ubuntu的网络突然出问题了,只剩下lo网卡了
ifconfig -a #先找所有网卡
ifconfig ens33 up #开开
sudo dhclient ens33 #获取ip
GDT
types.h
#ifndef __TYPES_H
#define __TYPES_H
typedef char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef long long int int64_t;
typedef unsigned long long int uint64_t;
#endif
修改kernel.cpp:引入types.h然后改数据类型名
#include "types.h"
void printf(char* str){
static uint16_t* VideoMemory = (uint16_t*)0xb8000;
for(int i=0;str[i]!='\0';i++)
VideoMemory[i]=(VideoMemory[i] & 0xFF00) | str[i];
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors(){
for(constructor* i=&start_ctors;i!=&end_ctors;i++)
(*i)();
}
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber){
printf((char *)"hello world");
while(1);
}
make run #成功即可继续
![[Pasted image 20230308134618.png | 500]]
gdt.h
#endif
#ifndef __GDT_H
#define __GDT_H
#include "types.h"
class GlobalDescriptorTable {
public:
class SegmentDescriptor {
public:
SegmentDescriptor(uint32_t base, uint32_t limit, uint8_t type);
uint32_t Base();
uint32_t Limit();
private:
uint16_t limit_lo;
uint16_t base_lo;
uint8_t base_hi;
uint8_t type;
uint8_t flags_limit_hi;
uint8_t base_vhi;
} __attribute__((packed)); //不进行内存对齐
SegmentDescriptor nullSegmentDescriptor;
SegmentDescriptor unusedSegmentDescriptor;
SegmentDescriptor codeSegmentDescriptor;
SegmentDescriptor dataSegmentDescriptor;
public:
GlobalDescriptorTable();
~GlobalDescriptorTable();
uint16_t CodeSegmentDescriptor();
uint16_t DataSegmentDescriptor();
};
#endif
gdt.cpp
#include "gdt.h"
GlobalDescriptorTable::GlobalDescriptorTable()
: nullSegmentDescriptor(0,0,0),
unusedSegmentDescriptor(0,0,0),
codeSegmentDescriptor(0,64*1024*1024,0x9a), //64*1024*1024是内存大小
dataSegmentDescriptor(0,64*1024*1024,0x92) {
uint32_t i[2];
i[1] = (uint32_t)this;
i[0] = sizeof(GlobalDescriptorTable) << 16;
asm volatile("lgdt (%0)": :"p" (((uint8_t*)i) + 2)); //内联汇编,防止拆分;“p”加载地址
}
GlobalDescriptorTable::~GlobalDescriptorTable(){}
uint16_t GlobalDescriptorTable::DataSegmentDescriptor(){
return (uint8_t*)&dataSegmentDescriptor - (uint8_t*)this; //获得段地址偏移
}
uint16_t GlobalDescriptorTable::CodeSegmentDescriptor(){
return (uint8_t*)&codeSegmentDescriptor - (uint8_t*)this;
}
GlobalDescriptorTable::SegmentDescriptor::SegmentDescriptor(uint32_t base, uint32_t limit, uint8_t type){
uint8_t* target = (uint8_t*)this;
if(limit < 1048576){ //2^20,原版65536
target[6] = 0x40; //原0x80
}else{
if((limit & 0xfff) != 0xfff){
limit = (limit >> 12) -1;
}else{
limit = limit >> 12;
}
target[6] = 0xC0; //D/B位
}
target[0] = limit & 0xff;
target[1] = (limit >> 8) & 0xff;
target[6] |= (limit >> 16) & 0xf;
target[2] = base & 0xff;
target[3] = (base >> 8) & 0xff;
target[4] = (base >>16) & 0xff;
target[7] = (base >> 24) & 0xff;
target[5] = type;
}
uint32_t GlobalDescriptorTable::SegmentDescriptor::Base(){
uint8_t* target = (uint8_t*)this;
uint32_t result = target[7];
result = (result << 8) +target[4];
result = (result << 8) +target[3];
result = (result << 8) +target[2];
return result;
}
uint32_t GlobalDescriptorTable::SegmentDescriptor::Limit(){
uint8_t* target = (uint8_t*)this;
uint32_t result = target[6] & 0xf;
result = (result << 8) +target[1];
result = (result << 8) +target[0];
if((target[6] & 0xC0) == 0xC0)
result = (result << 12) | 0xfff;
return result;
}
修改kernel.cpp
#include "types.h"
#include "gdt.h"
void printf(char* str){
static uint16_t* VideoMemory = (uint16_t*)0xb8000;
for(int i=0;str[i]!='\0';i++)
VideoMemory[i]=(VideoMemory[i] & 0xFF00) | str[i];
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors(){
for(constructor* i=&start_ctors;i!=&end_ctors;i++)
(*i)();
}
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber){
printf((char *)"hello world");
GlobalDescriptorTable gdt;
while(1);
}
修改Makefile
objects = loader.o kernel.o gdt.o #这一行加上“gdt.o”
make mykernel.bin #检查一下有没有错误
I/O操作
关于汇编扩展:GCC 扩展内联汇编简介 - thammer - 博客园 (cnblogs.com)
makefile这行最后加上这回写的产生的 port.o
objects = loader.o kernel.o gdt.o port.o
port.h
#ifndef __PORT_H
#define __PORT_H
#include "types.h"
class Port{
protected:
uint16_t portnumber;
Port(uint16_t portnumber);
~Port();
};
class Port8bit : public Port{
public:
Port8bit(uint16_t portnumber);
~Port8bit();
virtual void write(uint8_t data);
virtual uint8_t read();
};
class Port8bitSlow : public Port8bit{
public:
Port8bitSlow(uint16_t portnumber);
~Port8bitSlow();
virtual void write(uint8_t data);
};
class Port16bit : public Port{
public:
Port16bit(uint16_t portnumber);
~Port16bit();
virtual void write(uint16_t data);
virtual uint16_t read();
};
class Port32bit : public Port{
public:
Port32bit(uint16_t portnumber);
~Port32bit();
virtual void write(uint32_t data);
virtual uint32_t read();
};
#endif
port.cpp
#include "port.h"
Port::Port(uint16_t portnumber)
: portnumber(portnumber){}
Port::~Port(){}
Port8bit::Port8bit(uint16_t portnumber)
:Port(portnumber){}
Port8bit::~Port8bit(){}
void Port8bit::write(uint8_t data){
__asm__ volatile("outb %0, %1" : "=a" (data) : "Nd" (portnumber));
}
uint8_t Port8bit::read(){
uint8_t result;
__asm__ volatile("inb %1, %0" : "=a" (result) : "Nd" (portnumber));
return result; //从端口读取一个字节放入result
}
Port8bitSlow::Port8bitSlow(uint16_t portnumber)
:Port8bit(portnumber){}
Port8bitSlow::~Port8bitSlow(){}
void Port8bitSlow::write(uint8_t data){ //只有写操作,"缓慢的写入"
__asm__ volatile("outb %0, %1\njmp 1f\n1: jmp 1f\n1:" : "=a" (data) : "Nd" (portnumber));
}
Port16bit::Port16bit(uint16_t portnumber)
:Port(portnumber){}
Port16bit::~Port16bit(){}
void Port16bit::write(uint16_t data){
__asm__ volatile("outw %0, %1" : "=a" (data) : "Nd" (portnumber));
}
uint16_t Port16bit::read(){
uint16_t result;
__asm__ volatile("inw %1, %0" : "=a" (result) : "Nd" (portnumber));
return result;
}
Port32bit::Port32bit(uint16_t portnumber)
:Port(portnumber){}
Port32bit::~Port32bit(){}
void Port32bit::write(uint32_t data){
__asm__ volatile("outl %0, %1" : "=a" (data) : "Nd" (portnumber));
}
uint32_t Port32bit::read(){
uint32_t result;
__asm__ volatile("inl %1, %0" : "=a" (result) : "Nd" (portnumber));
return result;
}
运行 make mykernel.bin
后 make run
机子没问题就行
makefile最后加几行清理文件的命令,中间文件可以使用 make clean
删掉
.PHONY: class
clean:
rm -rf ${objects} mykernel.bin mykernel.iso
实现中断
一
因为要开 vm 里的 vbox 虚拟机我需要关内核隔离,临时的调试就不开自动化了,把 makefile中 make run 相关的先注释掉,后面执行
make mykernel.iso
后手动拖过来改内核
需要实现20个异常中断和17个外部中断
makefile先加上去这次的文件
objects = loader.o kernel.o gdt.o port.o interrupts.o interruptstubs
C++编译时函数名会变,所以在写汇编之前先看看变成啥了。先写出 InterruptManager 这个类,cpp文件中随便写点引用它
interrupts.h
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
#include "types.h"
#include "port.h"
class InterruptManager{
public:
static uint32_t handleInterrupt(uint8_t interruptNumber, uint32_t esp);
};
#endif
interrupts.cpp
#include "interrupts.h"
void printf(const char*);
uint32_t InterruptManager::handleInterrupt(uint8_t interruptNumber, uint32_t esp){
printf("interrupt");
return esp;
}
然后运行 makefile 就得到了interupts.o
objdump -d interrupts.o #反汇编查看函数
得到函数名变成了 __ZN16InterruptManager15handleInterruptEhj
,所以后面写汇编的时候不要再写 InterruptManager 了,要写一长串这个
再写 HandleException 时同理
interrupts.cpp
#include "interrupts.h"
void printf(const char*);
uint32_t InterruptManager::handleInterrupt(uint8_t interruptNumber, uint32_t esp){
printf("interrupt");
return esp;
}
void InterruptManager::HandleInterruptRequest0x00(){}
void InterruptManager::HandleException0x00(){}
interrupts.h
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
#include "types.h"
#include "port.h"
class InterruptManager{
public:
static uint32_t handleInterrupt(uint8_t interruptNumber, uint32_t esp);
static void HandleInterruptRequest0x00();
static void HandleInterruptRequest0x01();
static void HandleInterruptRequest0x02();
static void HandleInterruptRequest0x03();
static void HandleInterruptRequest0x04();
static void HandleInterruptRequest0x05();
static void HandleInterruptRequest0x06();
static void HandleInterruptRequest0x07();
static void HandleInterruptRequest0x08();
static void HandleInterruptRequest0x09();
static void HandleInterruptRequest0x0A();
static void HandleInterruptRequest0x0B();
static void HandleInterruptRequest0x0C();
static void HandleInterruptRequest0x0D();
static void HandleInterruptRequest0x0E();
static void HandleInterruptRequest0x0F();
static void HandleInterruptRequest0x31();
static void HandleException0x00();
static void HandleException0x01();
static void HandleException0x02();
static void HandleException0x03();
static void HandleException0x04();
static void HandleException0x05();
static void HandleException0x06();
static void HandleException0x07();
static void HandleException0x08();
static void HandleException0x09();
static void HandleException0x0A();
static void HandleException0x0B();
static void HandleException0x0C();
static void HandleException0x0D();
static void HandleException0x0E();
static void HandleException0x0F();
static void HandleException0x10();
static void HandleException0x11();
static void HandleException0x12();
static void HandleException0x13();
};
#endif
interruptstubs.s
.section .text
.extern __ZN16InterruptManager15handleInterruptEhj
.macro HandleInetrruptRequest num ;宏定义,避免重复写17个中断
.global __ZN16InterruptManager26HandleInterruptRequest\num\()Ev
movb $\num, (interruptnumber)
jmp int_bottom
.endm
.macro HandleException num
.global __ZN16InterruptManager19HandleException\num\()Ev
movb $\num, (interruptnumber)
jmp int_bottom
.endm
HandleInetrruptRequest 0x00
HandleInetrruptRequest 0x01
HandleInetrruptRequest 0x02
HandleInetrruptRequest 0x03
HandleInetrruptRequest 0x04
HandleInetrruptRequest 0x05
HandleInetrruptRequest 0x06
HandleInetrruptRequest 0x07
HandleInetrruptRequest 0x08
HandleInetrruptRequest 0x09
HandleInetrruptRequest 0x0A
HandleInetrruptRequest 0x0B
HandleInetrruptRequest 0x0C
HandleInetrruptRequest 0x0D
HandleInetrruptRequest 0x0E
HandleInetrruptRequest 0x0F
HandleInetrruptRequest 0x31
HandleException 0x00
HandleException 0x01
HandleException 0x02
HandleException 0x03
HandleException 0x04
HandleException 0x05
HandleException 0x06
HandleException 0x07
HandleException 0x08
HandleException 0x09
HandleException 0x0A
HandleException 0x0B
HandleException 0x0C
HandleException 0x0D
HandleException 0x0E
HandleException 0x0F
HandleException 0x10
HandleException 0x11
HandleException 0x12
HandleException 0x13
int_bottom:
pusha ;常用寄存器进行压栈
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %esp
push(interruptnumber)
call __ZN16InterruptManager15handleInterruptEhj
movl %eax,%esp
popl %gs
popl %fs
popl %es
popl %ds
popa
iret
.data
interruptnumber: .byte 0;初始化
二
IDT表中有三个门描述符:中断门描述符、陷阱门描述符、任务门描述符
interrupts.cpp
#include "interrupts.h"
void printf(const char*);
InterruptManager::GateDescriptor InterruptManager::interruptDescriptorTable[256];
void InterruptManager::SetInterruptDescriptorTableEntry(
uint8_t interruptNumber,
uint16_t codeSegmentSelectorOffset,
void (*handler)(),
uint8_t DescriptorPrivilegelLevel,
uint8_t DescriptorType) {
const uint8_t IDT_DESC_PRESENT = 0x80;
interruptDescriptorTable[interruptNumber].handlerAddressLowBits = ((uint32_t)handler) & 0xffff;
interruptDescriptorTable[interruptNumber].handlerAddressHighBits = ((uint32_t)handler >> 16) & 0xffff;
interruptDescriptorTable[interruptNumber].gdt_codeSegmentSelector = codeSegmentSelectorOffset;
interruptDescriptorTable[interruptNumber].access = IDT_DESC_PRESENT | ((DescriptorPrivilegelLevel & 3) << 5) | DescriptorType;
interruptDescriptorTable[interruptNumber].reserved = 0;
}
InterruptManager::InterruptManager(uint16_t handwareInterruptOffset, GlobalDescriptorTable* gdt){
this->handwareInterruptOffset = handwareInterruptOffset;
uint16_t CodeSegment = gdt->CodeSegmentSelector();
const uint8_t IDT_INTERUPT_GATE = 0xe;
for(uint16_t i = 0; i < 256; i++){
SetInterruptDescriptorTableEntry(i, CodeSegment, &InterruptIgnore, 0, IDT_INTERUPT_GATE);
}
SetInterruptDescriptorTableEntry(0x00, CodeSegment, &HandleException0x00, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x01, CodeSegment, &HandleException0x01, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x02, CodeSegment, &HandleException0x02, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x03, CodeSegment, &HandleException0x03, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x04, CodeSegment, &HandleException0x04, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x05, CodeSegment, &HandleException0x05, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x06, CodeSegment, &HandleException0x06, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x07, CodeSegment, &HandleException0x07, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x08, CodeSegment, &HandleException0x08, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x09, CodeSegment, &HandleException0x09, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x0A, CodeSegment, &HandleException0x0A, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x0B, CodeSegment, &HandleException0x0B, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x0C, CodeSegment, &HandleException0x0C, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x0D, CodeSegment, &HandleException0x0D, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x0E, CodeSegment, &HandleException0x0E, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x0F, CodeSegment, &HandleException0x0F, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x10, CodeSegment, &HandleException0x10, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x11, CodeSegment, &HandleException0x11, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x12, CodeSegment, &HandleException0x12, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(0x13, CodeSegment, &HandleException0x13, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x09, CodeSegment, &HandleInterruptRequest0x00, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x00, CodeSegment, &HandleInterruptRequest0x01, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x01, CodeSegment, &HandleInterruptRequest0x02, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x02, CodeSegment, &HandleInterruptRequest0x03, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x03, CodeSegment, &HandleInterruptRequest0x04, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x04, CodeSegment, &HandleInterruptRequest0x05, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x05, CodeSegment, &HandleInterruptRequest0x06, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x06, CodeSegment, &HandleInterruptRequest0x07, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x07, CodeSegment, &HandleInterruptRequest0x08, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x08, CodeSegment, &HandleInterruptRequest0x09, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x0A, CodeSegment, &HandleInterruptRequest0x0A, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x0B, CodeSegment, &HandleInterruptRequest0x0B, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x0C, CodeSegment, &HandleInterruptRequest0x0C, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x0D, CodeSegment, &HandleInterruptRequest0x0D, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x0E, CodeSegment, &HandleInterruptRequest0x0E, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x0F, CodeSegment, &HandleInterruptRequest0x0F, 0, IDT_INTERUPT_GATE);
SetInterruptDescriptorTableEntry(handwareInterruptOffset + 0x31, CodeSegment, &HandleInterruptRequest0x31, 0, IDT_INTERUPT_GATE);
}
uint32_t InterruptManager::handleInterrupt(uint8_t interruptNumber, uint32_t esp){
printf("interrupt");
return esp;
}
interrupts.h
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
#include "types.h"
#include "port.h"
#include "gdt.h"
class InterruptManager{
public:
InterruptManager(uint16_t handwareInterruptOffset, GlobalDescriptorTable* gdt);
~InterruptManager();
protected:
struct GateDescriptor{
uint16_t handlerAddressLowBits;
uint16_t gdt_codeSegmentSelector;
uint8_t reserved;
uint8_t access;
uint16_t handlerAddressHighBits;
} __attribute__((packed));
static GateDescriptor interruptDescriptorTable[256];
static void SetInterruptDescriptorTableEntry(
uint8_t interruptNumber,
uint16_t codeSegmentSelectorOffset,
void (*handler)(),
uint8_t DescriptorPrivilegelLevel, //DPL 权限
uint8_t DescriptorType
);
static void InterruptIgnore();
uint16_t handwareInterruptOffset;
static uint32_t handleInterrupt(uint8_t interruptNumber, uint32_t esp);
static void HandleInterruptRequest0x00();
static void HandleInterruptRequest0x01();
static void HandleInterruptRequest0x02();
static void HandleInterruptRequest0x03();
static void HandleInterruptRequest0x04();
static void HandleInterruptRequest0x05();
static void HandleInterruptRequest0x06();
static void HandleInterruptRequest0x07();
static void HandleInterruptRequest0x08();
static void HandleInterruptRequest0x09();
static void HandleInterruptRequest0x0A();
static void HandleInterruptRequest0x0B();
static void HandleInterruptRequest0x0C();
static void HandleInterruptRequest0x0D();
static void HandleInterruptRequest0x0E();
static void HandleInterruptRequest0x0F();
static void HandleInterruptRequest0x31();
static void HandleException0x00();
static void HandleException0x01();
static void HandleException0x02();
static void HandleException0x03();
static void HandleException0x04();
static void HandleException0x05();
static void HandleException0x06();
static void HandleException0x07();
static void HandleException0x08();
static void HandleException0x09();
static void HandleException0x0A();
static void HandleException0x0B();
static void HandleException0x0C();
static void HandleException0x0D();
static void HandleException0x0E();
static void HandleException0x0F();
static void HandleException0x10();
static void HandleException0x11();
static void HandleException0x12();
static void HandleException0x13();
};
#endif
interruptstubs.s
.section .text
.extern __ZN16InterruptManager15handleInterruptEhj
.macro HandleInetrruptRequest num ;宏定义,避免重复写17个中断
.global __ZN16InterruptManager26HandleInterruptRequest\num\()Ev
movb $\num, (interruptnumber)
jmp int_bottom
.endm
.macro HandleException num
.global __ZN16InterruptManager19HandleException\num\()Ev
movb $\num, (interruptnumber)
jmp int_bottom
.endm
HandleInetrruptRequest 0x00
HandleInetrruptRequest 0x01
HandleInetrruptRequest 0x02
HandleInetrruptRequest 0x03
HandleInetrruptRequest 0x04
HandleInetrruptRequest 0x05
HandleInetrruptRequest 0x06
HandleInetrruptRequest 0x07
HandleInetrruptRequest 0x08
HandleInetrruptRequest 0x09
HandleInetrruptRequest 0x0A
HandleInetrruptRequest 0x0B
HandleInetrruptRequest 0x0C
HandleInetrruptRequest 0x0D
HandleInetrruptRequest 0x0E
HandleInetrruptRequest 0x0F
HandleInetrruptRequest 0x31
HandleException 0x00
HandleException 0x01
HandleException 0x02
HandleException 0x03
HandleException 0x04
HandleException 0x05
HandleException 0x06
HandleException 0x07
HandleException 0x08
HandleException 0x09
HandleException 0x0A
HandleException 0x0B
HandleException 0x0C
HandleException 0x0D
HandleException 0x0E
HandleException 0x0F
HandleException 0x10
HandleException 0x11
HandleException 0x12
HandleException 0x13
int_bottom:
pusha ;常用寄存器进行压栈
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %esp
push (interruptnumber)
call __ZN16InterruptManager15handleInterruptEhj
movl %eax,%esp
popl %gs
popl %fs
popl %es
popl %ds
popa
.global __ZN16InterruptManager15InterruptIgnoreEv
__ZN16InterruptManager15InterruptIgnoreEv:
iret
.data
interruptnumber: .byte 0;初始化
Makefile
objects = loader.o kernel.o gdt.o port.o interrupts.o interruptstubs.o
三
控制8259A芯片
之前好像哪里出了点bug,这次改动量有点杂乱我也记不清了,索性一次性贴其目前为止的所有代码。输出效果应该是在原本的基础上如果有键鼠操作会 printf
目前为止的部分code
interrupts.h
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
#include "types.h"
#include "port.h"
#include "gdt.h"
class InterruptManager{
public:
InterruptManager(uint16_t hardwareInterruptOffset, GlobalDescriptorTable* gdt);
~InterruptManager();
void Activate();
protected:
struct GateDescriptor{
uint16_t handlerAddressLowBits;
uint16_t gdt_codeSegmentSelector;
uint8_t reserved;
uint8_t access;
uint16_t handlerAddressHighBits;
} __attribute__((packed));
static GateDescriptor interruptDescriptorTable[256];
struct InterruptDescriptorTablePointer{
uint16_t size;
uint32_t base;
} __attribute__((packed));
static void SetInterruptDescriptorTableEntry(
uint8_t interruptNumber,
uint16_t codeSegmentSelectorOffset,
void (*handler)(),
uint8_t DescriptorPrivilegelLevel, //DPL 权限
uint8_t DescriptorType
);
uint16_t hardwareInterruptOffset;
static void InterruptIgnore();
static uint32_t handleInterrupt(uint8_t interruptNumber, uint32_t esp);
static void HandleInterruptRequest0x00();
static void HandleInterruptRequest0x01();
static void HandleInterruptRequest0x02();
static void HandleInterruptRequest0x03();
static void HandleInterruptRequest0x04();
static void HandleInterruptRequest0x05();
static void HandleInterruptRequest0x06();
static void HandleInterruptRequest0x07();
static void HandleInterruptRequest0x08();
static void HandleInterruptRequest0x09();
static void HandleInterruptRequest0x0A();
static void HandleInterruptRequest0x0B();
static void HandleInterruptRequest0x0C();
static void HandleInterruptRequest0x0D();
static void HandleInterruptRequest0x0E();
static void HandleInterruptRequest0x0F();
static void HandleInterruptRequest0x31();
static void HandleException0x00();
static void HandleException0x01();
static void HandleException0x02();
static void HandleException0x03();
static void HandleException0x04();
static void HandleException0x05();
static void HandleException0x06();
static void HandleException0x07();
static void HandleException0x08();
static void HandleException0x09();
static void HandleException0x0A();
static void HandleException0x0B();
static void HandleException0x0C();
static void HandleException0x0D();
static void HandleException0x0E();
static void HandleException0x0F();
static void HandleException0x10();
static void HandleException0x11();
static void HandleException0x12();
static void HandleException0x13();
Port8bitSlow picMasterCommand;
Port8bitSlow picMasterData;
Port8bitSlow picSlaveCommand;
Port8bitSlow picSlaveData;
};
#endif
interrupts.cpp
#include "interrupts.h"
void printf(const char*);
InterruptManager::GateDescriptor InterruptManager::interruptDescriptorTable[256];
void InterruptManager::SetInterruptDescriptorTableEntry(
uint8_t interruptNumber,
uint16_t codeSegmentSelectorOffset,
void (*handler)(),
uint8_t DescriptorPrivilegelLevel,
uint8_t DescriptorType) {
const uint8_t IDT_DESC_PRESENT = 0x80;
interruptDescriptorTable[interruptNumber].handlerAddressLowBits = ((uint32_t)handler) & 0xffff;
interruptDescriptorTable[interruptNumber].handlerAddressHighBits = ((uint32_t)handler >> 16) & 0xffff;
interruptDescriptorTable[interruptNumber].gdt_codeSegmentSelector = codeSegmentSelectorOffset;
interruptDescriptorTable[interruptNumber].access = IDT_DESC_PRESENT | ((DescriptorPrivilegelLevel & 3) << 5) | DescriptorType;
interruptDescriptorTable[interruptNumber].reserved = 0;
}
InterruptManager::InterruptManager(uint16_t hardwareInterruptOffset, GlobalDescriptorTable* gdt)
:picMasterCommand(0x20),
picMasterData(0x21),
picSlaveCommand(0xA0),
picSlaveData(0xA1) {
this->hardwareInterruptOffset = hardwareInterruptOffset;
uint16_t CodeSegment = gdt->CodeSegmentSelector(); //原本有>>3但是我报错
const uint8_t IDT_INTERUPT_GATE = 0xe;
for(uint16_t i = 0; i < 256; i++){
SetInterruptDescriptorTableEntry(i, CodeSegment, &InterruptIgnore, 0, IDT_INTERUPT_GATE);
}
SetInterruptDescriptorTableEntry(0x00, CodeSegment, &HandleException0x00, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x01, CodeSegment, &HandleException0x01, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x02, CodeSegment, &HandleException0x02, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x03, CodeSegment, &HandleException0x03, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x04, CodeSegment, &HandleException0x04, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x05, CodeSegment, &HandleException0x05, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x06, CodeSegment, &HandleException0x06, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x07, CodeSegment, &HandleException0x07, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x08, CodeSegment, &HandleException0x08, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x09, CodeSegment, &HandleException0x09, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x0A, CodeSegment, &HandleException0x0A, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x0B, CodeSegment, &HandleException0x0B, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x0C, CodeSegment, &HandleException0x0C, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x0D, CodeSegment, &HandleException0x0D, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x0E, CodeSegment, &HandleException0x0E, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x0F, CodeSegment, &HandleException0x0F, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x10, CodeSegment, &HandleException0x10, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x11, CodeSegment, &HandleException0x11, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x12, CodeSegment, &HandleException0x12, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(0x13, CodeSegment, &HandleException0x13, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x00, CodeSegment, &HandleInterruptRequest0x00, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x01, CodeSegment, &HandleInterruptRequest0x01, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x02, CodeSegment, &HandleInterruptRequest0x02, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x03, CodeSegment, &HandleInterruptRequest0x03, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x04, CodeSegment, &HandleInterruptRequest0x04, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x05, CodeSegment, &HandleInterruptRequest0x05, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x06, CodeSegment, &HandleInterruptRequest0x06, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x07, CodeSegment, &HandleInterruptRequest0x07, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x08, CodeSegment, &HandleInterruptRequest0x08, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x09, CodeSegment, &HandleInterruptRequest0x09, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x0A, CodeSegment, &HandleInterruptRequest0x0A, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x0B, CodeSegment, &HandleInterruptRequest0x0B, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x0C, CodeSegment, &HandleInterruptRequest0x0C, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x0D, CodeSegment, &HandleInterruptRequest0x0D, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x0E, CodeSegment, &HandleInterruptRequest0x0E, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x0F, CodeSegment, &HandleInterruptRequest0x0F, 0, IDT_INTERRUPT_GATE);
SetInterruptDescriptorTableEntry(hardwareInterruptOffset + 0x31, CodeSegment, &HandleInterruptRequest0x31, 0, IDT_INTERRUPT_GATE);
picMasterCommand.write(0x11);
picSlaveCommand.write(0x11);
picMasterData.write(hardwareInterruptOffset);
picSlaveData.write(hardwareInterruptOffset+8);
picMasterData.write(0x04);
picSlaveData.write(0x02);
picMasterData.write(0x01);
picSlaveData.write(0x01);
picMasterData.write(0x00);
picSlaveData.write(0x00);
InterruptDescriptorTablePointer idt;
idt.size = 256 * sizeof(GateDescriptor) -1;
idt.base = (uint32_t)interruptDescriptorTable;
asm volatile("lidt %0": :"m" (idt));
}
InterruptManager::~InterruptManager(){}
void InterruptManager::Activate(){
asm("sti");
}
uint32_t InterruptManager::handleInterrupt(uint8_t interruptNumber, uint32_t esp){
printf("interrupt");
return esp;
}
interruptstubs.s
.set IRQ_BASE, 0x20
.section .text
.extern __ZN16InterruptManager15handleInterruptEhj
.macro HandleInterruptRequest num
.global __ZN16InterruptManager26HandleInterruptRequest\num\()Ev
__ZN16InterruptManager26HandleInterruptRequest\num\()Ev:
movb $\num + IRQ_BASE, (interruptnumber)
jmp int_bottom
.endm
.macro HandleException num
.global __ZN16InterruptManager19HandleException\num\()Ev
__ZN16InterruptManager19HandleException\num\()Ev:
movb $\num, (interruptnumber)
jmp int_bottom
.endm
HandleInterruptRequest 0x00
HandleInterruptRequest 0x01
HandleInterruptRequest 0x02
HandleInterruptRequest 0x03
HandleInterruptRequest 0x04
HandleInterruptRequest 0x05
HandleInterruptRequest 0x06
HandleInterruptRequest 0x07
HandleInterruptRequest 0x08
HandleInterruptRequest 0x09
HandleInterruptRequest 0x0A
HandleInterruptRequest 0x0B
HandleInterruptRequest 0x0C
HandleInterruptRequest 0x0D
HandleInterruptRequest 0x0E
HandleInterruptRequest 0x0F
HandleInterruptRequest 0x31
HandleException 0x00
HandleException 0x01
HandleException 0x02
HandleException 0x03
HandleException 0x04
HandleException 0x05
HandleException 0x06
HandleException 0x07
HandleException 0x08
HandleException 0x09
HandleException 0x0A
HandleException 0x0B
HandleException 0x0C
HandleException 0x0D
HandleException 0x0E
HandleException 0x0F
HandleException 0x10
HandleException 0x11
HandleException 0x12
HandleException 0x13
int_bottom:
pusha
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushl %esp
push (interruptnumber)
call __ZN16InterruptManager15handleInterruptEhj
movl %eax,%esp
popl %gs
popl %fs
popl %es
popl %ds
popa
.global __ZN16InterruptManager15InterruptIgnoreEv
__ZN16InterruptManager15InterruptIgnoreEv:
iret
.data
interruptnumber: .byte 0
kernel.cpp 最下面 kernelMain 加上了新写的相关调用
#include "types.h"
#include "gdt.h"
#include "interrupts.h"
void printf(const char* str){
static uint16_t* VideoMemory = (uint16_t*)0xb8000;
static uint8_t x=0, y=0;
for(int i=0;str[i]!='\0';i++){
switch(str[i]){
case '\n':
y++;
x=0;
break;
default:
VideoMemory[80*y+x]=(VideoMemory[80*y+x] & 0xFF00) | str[i];
x++;
break;
}
if(x>=80){
x=0;
y++;
}
if(y>=25){
for(y=0;y<25;y++){
for(x=0;x<80;x++){
VideoMemory[80*y+x]=(VideoMemory[80*y+x] & 0xFF00) | ' ';
}
}
x=0;y=0;
}
}
}
typedef void (*constructor)();
extern "C" constructor start_ctors;
extern "C" constructor end_ctors;
extern "C" void callConstructors(){
for(constructor* i=&start_ctors;i!=&end_ctors;i++){
(*i)();
}
}
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber){
printf("hello world\n");
printf("aha");
GlobalDescriptorTable gdt;
InterruptManager interrupts(0x20, &gdt);
interrupts.Activate();
while(1);
}
Makefile 应该只动了objects
GPPPARAMS = -m32 -fno-use-cxa-atexit -fleading-underscore -fno-exceptions -fno-builtin -nostdlib -fno-rtti -fno-pie
ASPARAMS = -32
LDPARAMS = -melf_i386 -no-pie
objects = loader.o kernel.o gdt.o port.o interrupts.o interruptstubs.o
%.o: %.cpp
g++ ${GPPPARAMS} -o $@ -c $<
%.o: %.s
as ${ASPARAMS} -o $@ $<
mykernel.bin: linker.ld ${objects}
ld ${LDPARAMS} -T $< -o $@ ${objects}
install: mykernel.bin
sudo cp $< /boot/mykernel.bin
mykernel.iso: mykernel.bin
mkdir iso
mkdir iso/boot
mkdir iso/boot/grub
cp $< iso/boot/
echo 'set timeout=0' > iso/boot/grub/grub.cfg
echo 'set default=0' >> iso/boot/grub/grub.cfg
echo '' >> iso/boot/grub/grub.cfg
echo 'menuentry "my os" {' >> iso/boot/grub/grub.cfg
echo ' multiboot /boot/mykernel.bin' >> iso/boot/grub/grub.cfg
echo ' boot' >> iso/boot/grub/grub.cfg
echo '}' >> iso/boot/grub/grub.cfg
grub-mkrescue --output=$@ iso
rm -rf iso
run: mykernel.iso
(killall VBoxManage && sleep 1) || true
VBoxManage startvm "my os" &
.PHONY: class
clean:
rm -rf ${objects} mykernel.bin mykernel.iso
一些忠告
代码顺序别轻易改,我出现了 error : no mu ltiboot header found .error : youneed to load the kerne l f irst .
的报错,因为我的 interrupts.cpp 文件中写那一堆中断的时候,有两个顺序弄反了于是一直卡住……
四
增加了获取并打印中断号
interrupts.cpp
#include "interrupts.h"
void printf(const char*);
InterruptHandler::InterruptHandler(uint8_t interruptNumber, InterruptManager* interruptManager) {
this->interruptNumber = interruptNumber;
this->interruptManager = interruptManager;
interruptManager->handlers[interruptNumber] = this;
}
InterruptHandler::~InterruptHandler(){
if(interruptManager->handlers[interruptNumber] == this){
interruptManager->handlers[interruptNumber] = 0;
}
}
uint32_t InterruptHandler::HandleInterrupt(uint32_t esp){
return esp;
}
InterruptManager::GateDescriptor InterruptManager::interruptDescriptorTable[256];
InterruptManager* InterruptManager::ActiveInterruptManager = 0;
void InterruptManager::SetInterruptDescriptorTableEntry(...) {...}
InterruptManager::InterruptManager(uint16_t hardwareInterruptOffset, GlobalDescriptorTable* gdt):... {...}
InterruptManager::~InterruptManager(){}
void InterruptManager::Activate(){
if(ActiveInterruptManager != 0){
ActiveInterruptManager->Deactivate();
}
ActiveInterruptManager = this;
asm("sti");
}
void InterruptManager::Deactivate(){
if(ActiveInterruptManager != 0){
ActiveInterruptManager->Deactivate();
asm("sti");
}
}
uint32_t InterruptManager::handleInterrupt(uint8_t interruptNumber, uint32_t esp){
if(ActiveInterruptManager != 0){
return ActiveInterruptManager->DoHandleInterrupt(interruptNumber,esp);
}
return esp;
}
uint32_t InterruptManager::DoHandleInterrupt(uint8_t interruptNumber, uint32_t esp){
if(handlers[interruptNumber] != 0){
esp = handlers[interruptNumber]->HandleInterrupt(esp);
}else if(interruptNumber != hardwareInterruptOffset) {
char* foo = (char*)"UNHANDLED INTERRUPT 0x00";
char* hex = (char*)"0123456789ABCDEF";
foo[22] = hex[(interruptNumber >> 4) & 0x0F];
foo[23] = hex[interruptNumber & 0x0f];
printf((const char*)foo);
}
if(hardwareInterruptOffset <= interruptNumber && interruptNumber < hardwareInterruptOffset + 16){
picMasterCommand.Write(0x20);
if(hardwareInterruptOffset + 8 <= interruptNumber){
picSlaveCommand.Write(0x20);
}
}
return esp;
}
interrupts.h
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H
#include "types.h"
#include "port.h"
#include "gdt.h"
class InterruptManager;
class InterruptHandler{
public:
uint32_t HandleInterrupt(uint32_t esp);
protected:
InterruptHandler(uint8_t interruptNumber, InterruptManager* interruptManager);
~InterruptHandler();
uint8_t interruptNumber;
InterruptManager* interruptManager;
};
class InterruptManager{
friend class InterruptHandler;
public:
InterruptManager(uint16_t hardwareInterruptOffset, GlobalDescriptorTable* gdt);
~InterruptManager();
void Activate();
void Deactivate();
protected:
static InterruptManager* ActiveInterruptManager;
InterruptHandler* handlers[256];
struct GateDescriptor{...} __attribute__((packed));
static GateDescriptor interruptDescriptorTable[256];
struct InterruptDescriptorTablePointer{...} __attribute__((packed));
static void SetInterruptDescriptorTableEntry(...);
uint16_t hardwareInterruptOffset;
static void InterruptIgnore();
static uint32_t handleInterrupt(uint8_t interruptNumber, uint32_t esp);
uint32_t DoHandleInterrupt(uint8_t interruptNumber, uint32_t esp);
...
};
#endif
后续补充一下,前面部分地方改一下,handleInterrupt改成大写(为了规范)HandleInterrupt。然后移位问题,gdt.cpp中添加上左移
uint16_t GlobalDescriptorTable::DataSegmentSelector(){
return (uint8_t*)&dataSegmentDescriptor - (uint8_t*)this << 3;
}
uint16_t GlobalDescriptorTable::CodeSegmentSelector(){
return (uint8_t*)&codeSegmentDescriptor - (uint8_t*)this << 3;
}
然后前面说的这里 uint16_t CodeSegment = (gdt->CodeSegmentSelector()) >> 3; //原本有>>3但是我报错
右移就可以添上了
键鼠操作
一
kernel.cpp改一下,引入一下写的库,然后kernelMain里加一个keyboard的相关调用
#include "keyboard.h"
...
extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber){
printf("hello world\n");
printf("aha\n");
GlobalDescriptorTable gdt;
InterruptManager interrupts(0x20, &gdt);
KeyBoardDriver keyboard(&interrupts);
interrupts.Activate();
while(1);
}
makefile老规矩加上keyboard.o
为了方便调用,interrupts.h和.cpp都要改一下,.h的InterruptManager的public下加一行uint16_t HardwareInterruptOffset();
(因为hhardwareInterruptOffset私有的keyboard没法调用,需要加个公用方法调取),.cpp中加个调取的函数
uint16_t InterruptManager::HardwareInterruptOffset(){
return hardwareInterruptOffset;
}
keyboard.h
#ifndef __KEY_BOARD_H
#define __KEY_BOARD_H
#include "types.h"
#include "port.h"
#include "interrupts.h"
class KeyBoardDriver : public InterruptHandler {
public:
KeyBoardDriver(InterruptManager* manager);
~KeyBoardDriver();
virtual uint32_t HandleInterrupt(uint32_t esp);
private:
Port8bit dataport;
Port8bit commandport;
};
#endif
keyboard.cp
#include "keyboard.h"
KeyBoardDriver::KeyBoardDriver(InterruptManager* manager)
:InterruptHandler(0x01 + manager->HardwareInterruptOffset(), manager),
dataport(0x60),
commandport(0x64) {
while (commandport.Read() & 0x1){
dataport.Read();
}
commandport.Write(0xae);
commandport.Write(0x20);
uint8_t status = (dataport.Read() | 1) & ~0x10;
commandport.Write(0x60);
dataport.Write(status);
dataport.Write(0xf4);
}
KeyBoardDriver::~KeyBoardDriver(){}
void printf(const char*);
uint32_t KeyBoardDriver::HandleInterrupt(uint32_t esp){
uint8_t key = dataport.Read();
char* foo = (char*)"UNHANDLED INTERRUPT 0x00";
char* hex = (char*)"0123456789ABCDEF";
foo[22] = hex[(key >> 4) & 0x0F];
foo[23] = hex[key & 0x0f];
printf((const char*)foo);
return esp;
}
二
但是之前的操作并不能相应键鼠,改一点代码,一个是interrupts.h最开是InterruptHandler类里的最开始一行加上virtual virtual uint32_t HandleInterrupt(uint32_t esp);
。然后就是port.cpp里面所有的汇编的out都要改一下,改成两个冒号的形式
__asm__ volatile("outb %0, %1" : : "a" (data) , "Nd" (portnumber));
__asm__ volatile("outb %0, %1\njmp 1f\n1: jmp 1f\n1:" : : "a" (data) , "Nd" (portnumber));
鼠标操作
鼠标流三个信息:第一个是x、y坐标,第二个是x移动距离,第三个是y移动距离
鼠标和键盘使用的是相同的控制器,用的芯片实际是8042芯片
mouse.h
#ifndef __MOUSE_H
#define __MOUSE_H
#include "types.h"
#include "port.h"
#include "interrupts.h"
class MouseDriver : public InterruptHandler {
public:
MouseDriver(InterruptManager* manager);
~MouseDriver();
virtual uint32_t HandleInterrupt(uint32_t esp);
private:
Port8bit dataport;
Port8bit commandport;
uint8_t buffer[3];
uint8_t offset;
uint8_t buttons;
int8_t x,y;
};
#endif
mosue.cpp
#include "mouse.h"
MouseDriver::MouseDriver(InterruptManager* manager)
: InterruptHandler(0x0C + manager->HardwareInterruptOffset(), manager),
dataport(0x60),
commandport(0x64),
offset(0),
buttons(0),
x(40),
y(12) {
uint16_t* VideoMemory = (uint16_t*)0xb8000;
VideoMemory[y*80+x] = ((VideoMemory[y*80+x] & 0xf000) >> 4) |
((VideoMemory[y*80+x] & 0x0f00) << 4) |
(VideoMemory[y*80+x] & 0x00ff);
commandport.Write(0xa8);
commandport.Write(0x20);
uint8_t status = (dataport.Read() | 2) & ~0x20;
commandport.Write(0x60);
dataport.Write(status);
dataport.Write(0xf4);
commandport.Write(0xd4);
dataport.Write(0xf4);
dataport.Read();
}
MouseDriver::~MouseDriver(){}
void printf(const char*);
uint32_t MouseDriver::HandleInterrupt(uint32_t esp){
uint8_t status = dataport.Read();
if(!(status & 0x20)) return esp;
buffer[offset] = dataport.Read();
offset = (offset +1) %3;
if(offset == 0){
uint16_t* VideoMemory = (uint16_t*)0xb8000;
VideoMemory[y*80+x] = ((VideoMemory[y*80+x] & 0xf000) >> 4) |
((VideoMemory[y*80+x] & 0x0f00) << 4) |
(VideoMemory[y*80+x] & 0x00ff);
x += buffer[1];
if (x < 0) x=0;
else if (x >= 80) x=79;
y -= buffer[2];
if(y < 0) y=0;
else if (y >= 25) y=24;
VideoMemory[y*80+x] = ((VideoMemory[y*80+x] & 0xf000) >> 4) |
((VideoMemory[y*80+x] & 0x0f00) << 4) |
(VideoMemory[y*80+x] & 0x00ff);
for (uint8_t i = 0; i < 3; i++) {
if ((buffer[0] & (1 << i)) != (buttons & (1 << i))) {
VideoMemory[y*80+x] = ((VideoMemory[y*80+x] & 0xf000) >> 4) |
((VideoMemory[y*80+x] & 0x0f00) << 4) |
(VideoMemory[y*80+x] & 0x00ff);
}
}
buttons = buffer[0];
}
return esp;
}
Makefile里加上.o。然后kernelMain里加上
#include "mouse.h"
...
MouseDriver mouse(&interrupts);
暂时收一个尾咯,后面慢慢补