手搓操作系统(一)

写在前面:根据视频一步步走的
自己动手写一个操作系统【中英字幕】_哔哩哔哩_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) || truevirtualbox --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也关了,就是不行。网上说让我关内核隔离,关了就不弹报错了,还真用上了,离谱……

关于“ VMware Workstation 16 此平台不支持虚拟化的Intel VT-x/EPT. 不使用虚拟化的Intel VT-x/EPT,是否继续?”的有关问题的总结解答_vmware16不支持嵌套虚拟化_林麦安的博客-CSDN博客

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]]

(1条消息) x86 - 描述符详解:存储/系统段描述符、门描述符_描述符系统_嗷大墨的博客-CSDN博客

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.binmake 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);

暂时收一个尾咯,后面慢慢补

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值