I have created an operating system from scratch, which accepts interruptions from the keyboard. However, when the keyboard is pressed, the kernel stops working. Any advice is appreciated.
Here's the code:
BootLoader.asm
[BITS 16]
[ORG 0x7C00]
READSECTOR EQU 0x64
READMEMORY EQU 0x7E00
Init:
CLI
XOR AX, AX
MOV DS, AX
MOV ES, AX
MOV SS, AX
MOV SP, 0x7C00
MOV BP, SP
STI
Main:
CALL LoadKernel
CALL SetVBE
CALL DisableInt
CALL EnableA20
CALL SetupGDT
CALL EnterProtectedMode
LoadKernel:
MOV BYTE[BootDrive], DL
MOV BX, 0x02
MOV CX, READSECTOR
MOV DX, READMEMORY
MOV AH, 0x02
MOV AL, CL
MOV CL, BL
MOV BX, DX
MOV CH, 0x00
MOV DH, 0x00
MOV DL, BYTE[BootDrive]
INT 0x13
JC LoadFailed
RET
LoadFailed:
MOV SI, MsgBootFailed
CALL Print
CLI
HLT
Print:
MOV AH, 0x0e
MOV BL, 0x07
MOV BH, 0x00
PrintLoop:
LODSB
TEST AL, AL
JZ PrintEnd
INT 0x10
JMP PrintLoop
PrintEnd:
RET
SetVBE:
MOV AL, 0x13
MOV AH, 0x00
INT 0x10
RET
DisableInt:
MOV AL, 0xff
OUT 0x21, AL
NOP
OUT 0xa1, AL
CLI
RET
EnableA20:
IN AL, 0x92
OR AL, 2
OUT 0x92, AL
RET
SetupGDT:
CLI
PUSHA
LGDT [GDT_TOC]
STI
POPA
RET
GDT_TOC:
DW 8 * 3
DW GDT, 0x0000
GDT:
DW 0x0000, 0x0000, 0x0000, 0x0000
DW 0xFFFF, 0x0000, 0x9A00, 0x00CF
DW 0xFFFF, 0x0000, 0x9200, 0x00CF
EnterProtectedMode:
MOV EAX, CR0
AND EAX, 0x7fffffff
OR EAX, 1
MOV CR0, EAX
JMP 08h:Main32
[BITS 32]
Main32:
MOV AX, 0x10
MOV DS, AX
MOV ES, AX
MOV FS, AX
MOV GS, AX
MOV SS, AX
MOV ESP, 0xffff
CALL RelocKernel
JMP ExecuteKernel
RelocKernel:
MOV ESI, READMEMORY
MOV EDI, 0x200000
MOV ECX, 512 * READSECTOR
CALL memcpy
RET
memcpy:
MOV EAX, [ESI]
ADD ESI, 4
MOV [EDI], EAX
ADD EDI, 4
SUB ECX, 1
JNZ memcpy
RET
ExecuteKernel:
CLI
MOV EBP, 0x200000
;ADD EBP, [EBP + 0x18]
CALL EBP
HLT
MsgBootFailed: DB "Kernel Load Failed.", 0x0D, 0x0A, 0x00
BootDrive: DB 0
TIMES 510 - ($ - $$) DB 0
DW 0xAA55
KernelMain.c
#include "KernelMain.h"
int main()
{
InitGDTIDT();
InitPIC();
ASM_STI();
IoWrite8(0x0021, 0xf9);
IoWrite8(0x00a1, 0xef);
for (;;)
{
ASM_HLT();
}
return 0;
}
KernelMain.h
typedef unsigned long size_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned long long uint64_t;
#define ADR_IDT 0x0010f800
#define LIMIT_IDT 0x000007ff
#define ADR_GDT 0x00110000
#define LIMIT_GDT 0x0000ffff
#define ADR_KERNEL 0x00200000
#define LIMIT_KERNEL 0x0007ffff
#define AR_DATA32_RW 0x4092
#define AR_CODE32_ER 0x409a
#define AR_INTGATE32 0x008e
typedef struct
{
uint16_t LimitLow;
uint16_t BaseLow;
uint8_t BaseMid;
uint8_t AccessRight;
uint8_t LimitHigh;
uint8_t BaseHigh;
} SEGMENT_DESCRIPTOR;
typedef struct
{
uint16_t OffsetLow;
uint16_t Selector;
uint8_t DwCount;
uint8_t AccessRight;
uint16_t OffsetHigh;
} GATE_DESCRIPTOR;
void InitGDTIDT();
void SetSegmentDescriptor(SEGMENT_DESCRIPTOR *sd, uint32_t limit, int base, int ar);
void SetGateDescriptor(GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
void InitPIC();
void _inthandler21(int *esp);
void _inthandler27(int *esp);
void _inthandler2c(int *esp);
void _asm_inthandler21(void);
void _asm_inthandler27(void);
void _asm_inthandler2c(void);
Segment.c
#include "KernelMain.h"
void InitGDTIDT()
{
SEGMENT_DESCRIPTOR *gdt = (SEGMENT_DESCRIPTOR *)ADR_GDT;
GATE_DESCRIPTOR *idt = (GATE_DESCRIPTOR *)ADR_IDT;
int i;
for (i = 0; i <= LIMIT_GDT / 8; i++)
{
SetSegmentDescriptor(gdt + i, 0, 0, 0);
}
SetSegmentDescriptor(gdt + 1, 0xffffffff, 0x00000000, AR_DATA32_RW);
SetSegmentDescriptor(gdt + 2, LIMIT_KERNEL, ADR_KERNEL, AR_CODE32_ER);
LoadGDT(LIMIT_GDT, ADR_GDT);
for (i = 0; i <= LIMIT_IDT / 8; i++)
{
SetGateDescriptor(idt + i, 0, 0, 0);
}
LoadIDT(LIMIT_IDT, ADR_IDT);
SetGateDescriptor(idt + 0x21, (int)_asm_inthandler21, 2 * 8, AR_INTGATE32);
SetGateDescriptor(idt + 0x27, (int)_asm_inthandler27, 2 * 8, AR_INTGATE32);
SetGateDescriptor(idt + 0x2c, (int)_asm_inthandler2c, 2 * 8, AR_INTGATE32);
}
void SetSegmentDescriptor(SEGMENT_DESCRIPTOR *sd, uint32_t limit, int base, int ar)
{
if (limit > 0xfffff)
{
ar |= 0x8000;
limit /= 0x1000;
}
sd->LimitLow = limit & 0xffff;
sd->BaseLow = base & 0xffff;
sd->BaseMid = (base >> 16) & 0xff;
sd->AccessRight = ar & 0xff;
sd->LimitHigh = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
sd->BaseHigh = (base >> 24) & 0xff;
}
void SetGateDescriptor(GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
gd->OffsetLow = offset & 0xffff;
gd->Selector = selector;
gd->DwCount = (ar >> 8) & 0xff;
gd->AccessRight = ar & 0xff;
gd->OffsetHigh = (offset >> 16) & 0xffff;
}
Interrupts.asm
[BITS 32]
GLOBAL _asm_inthandler21, _asm_inthandler27, _asm_inthandler2c
EXTERN _inthandler21, _inthandler27, _inthandler2c
[SECTION .text]
_asm_inthandler21:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler21
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler27:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler27
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler2c:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler2c
POP EAX
POPAD
POP DS
POP ES
IRETD
Interrupts.c
#include "KernelMain.h"
#define PIC0_ICW1 0x0020
#define PIC0_OCW2 0x0020
#define PIC0_IMR 0x0021
#define PIC0_ICW2 0x0021
#define PIC0_ICW3 0x0021
#define PIC0_ICW4 0x0021
#define PIC1_ICW1 0x00a0
#define PIC1_OCW2 0x00a0
#define PIC1_IMR 0x00a1
#define PIC1_ICW2 0x00a1
#define PIC1_ICW3 0x00a1
#define PIC1_ICW4 0x00a1
void InitPIC()
{
IoWrite8(PIC0_IMR, 0xff);
IoWrite8(PIC1_IMR, 0xff);
IoWrite8(PIC0_ICW1, 0x11);
IoWrite8(PIC0_ICW2, 0x20);
IoWrite8(PIC0_ICW3, 1 << 2);
IoWrite8(PIC0_ICW4, 0x01);
IoWrite8(PIC1_ICW1, 0x11);
IoWrite8(PIC1_ICW2, 0x28);
IoWrite8(PIC1_ICW3, 2);
IoWrite8(PIC1_ICW4, 0x01);
IoWrite8(PIC0_IMR, 0xfb);
IoWrite8(PIC1_IMR, 0xff);
return;
}
void _inthandler21(int *esp)
{
for (int i = 0; i < 320 * 200; i++)
{
*((uint8_t *)i) = 1;
}
for (;;)
{
ASM_HLT();
}
}
void _inthandler2c(int *esp)
{
for (int i = 0; i < 320 * 200; i++)
{
*((uint8_t *)i) = 2;
}
for (;;)
{
ASM_HLT();
}
}
void _inthandler27(int *esp)
{
for (int i = 0; i < 320 * 200; i++)
{
*((uint8_t *)i) = 3;
}
IoWrite8(PIC0_OCW2, 0x67);
return;
}
asm.c
#include "KernelMain.h"
void IoWrite8(uint32_t port, uint8_t data)
{
asm volatile("outb %b0, %w1"
:
: "a"(data), "Nd"(port));
}
uint8_t IoRead8(uint32_t port)
{
uint8_t data;
asm volatile("inb %w1, %b0"
: "=a"(data)
: "Nd"(port));
return data;
}
void LoadGDT(int Limit, int Addr)
{
asm volatile("mov 0x4(%esp),%ax");
asm volatile("mov %ax,0x6(%esp)");
asm volatile("lgdtl 0x6(%esp)");
return;
}
void LoadIDT(int Limit, int Addr)
{
asm volatile("mov 0x4(%esp),%ax");
asm volatile("mov %ax,0x6(%esp)");
asm volatile("lidtl 0x6(%esp)");
return;
}
void ASM_HLT()
{
asm volatile("HLT");
}
void ASM_CLI()
{
asm volatile("CLI");
}
void ASM_STI()
{
asm volatile("STI");
}
After trying for a while, I found that _asm_inthandler21 is probably not called when the keyboard is pressed.
Whether the below answer solves 'the keyboard problem' is unsure, but the following issues are not inviting to keep searching for more...
Your kernel stretches from 0x00007E00 to 0x00014600.
Because of MOV ESP, 0xffff
, the immediately following CALL RelocKernel
overwrites part of the kernel (with the return address)!
You could relocate the kernel with ESP pointing at 0x7C00 like it was initially, and then once relocation to 0x200000 finished, set ESP equal to 0x10000. Don't set ESP=0xFFFF, always pick an aligned address.
memcpy: MOV EAX, [ESI] ADD ESI, 4 MOV [EDI], EAX ADD EDI, 4 SUB ECX, 1 JNZ memcpy RET
The memcpy routine is fed a count expressed as bytes (512 * READSECTOR
), yet the code transfers dwords and decrements the counter as if it were bytes! You need to use sub ecx, 4
. Or else express the argument as dwords (512 / 4 * READSECTOR
).
GDT_TOC: DW 8 * 3 DW GDT, 0x0000
The first word is a limit (not a size) and should read 23 (not 24).
You are asking BIOS to load (all at once) 51200 bytes at address 0x7E00. This crosses a 64KB boundary. Check out these answers: