Hello World OS : Boot
Исходники лежат тут
Для начала давайте напишем простой загрузчик .
Пусть он выведет нам на экран "Hello world".
В проекте 3 файла :
start.S
kernel-boot.c
mkbootdisk.c
С помощью команды make будет создан образ kernel.img .
Особенность сборки в том , что 2 обьектных файла - start.o и boot.o -
линкуются в kernel-bootsector , после чего запускается утилита mkbootdisk ,
которая лепит к kernel-bootsector 2 магических байтика .
У меня kernel.img работает на bochs (2.2.1) , vmware 5.0 .
С помощью команды :
dd if=kernel.img of=/dev/fd0
его можно скопировать на дискету и загрузить уже с нее компьютер .
Надеюсь , вы представляете , как работают загрузчики :
БИОС читает 512 байт с образа , копирует их в память по адресу начиная с 0x7c00 ,
после чего передает туда управление .
Файл start.S :
.set PROT_MODE_CSEG,0x8 # селектор кодового сегмента
.set PROT_MODE_DSEG,0x10 # селектор сегмента данных
.set CR0_PE_ON,0x1 # флаг защищенного режима
.globl start
start: .code16 # Старт в режиме real mode
cli # Disable interrupts
cld # String operations increment
# Поскольку при загрузке вопрос о содержимом
# интеловских регистров - вопрос темный ,
# проинициализируем их (DS, ES, SS).
#
xorw %ax,%ax # Просто обнуляем их
movw %ax,%ds # -> Data Segment
movw %ax,%es # -> Extra Segment
movw %ax,%ss # -> Stack Segment
# в стек помещаем загрузочный адрес памяти - 0x7c00.
movw $start,%sp # Stack Pointer
# Enable A20:
# Модели PC-шек серии 8086 имели 1 MB физичесой памяти
# В более поздних моделях 80286 для 'совместимости' отключили
# 20-ю шину данных при загрузке
# Для получения доступа к памяти свыше одного метра
# следующий код позволяет получить доступ к памяти свыше 1 MB :
seta20.1: inb $0x64,%al # читаем порт $0x64
testb $0x2,%al # Занят ?
jnz seta20.1 # опять читаем
movb $0xd1,%al # значение $0xd1
outb %al,$0x64 # пишем в порт $0x64
seta20.2: inb $0x64,%al # читаем порт $0x64
testb $0x2,%al # Занят ?
jnz seta20.2 # читаем , пока не прочитаем
movb $0xdf,%al # наконец прочитали
outb %al,$0x60 # включаем A20
# Переключаемся из real в protected
# В чем вообще разница между ними ?
# До сего момента процессор понятия не имеет о том ,
# есть ли у него право на чтение , запись или выполнение кода .
# Пока мы ему об этом говорим :-)
# После переключения в защищенный режим он будет сам знать о том ,
# где у него лежат данные , где исполняемый код , и т.д.
# Для этого нужно проинициализировать таблицы 'gdt' и 'gdtdesc'.
real_to_prot: cli # Отключаем прерывания
lgdt gdtdesc # в регистр LGDTR загрузим
# адрес таблицы gdtdesc
movl %cr0, %eax # переключаемся в защищенный режим
orl $CR0_PE_ON, %eax
movl %eax, %cr0
# следующий финт не просто позволяет перейти
# на следующую команду , но и загружает регистр
# CS значением $PROT_MODE_CSEG.
ljmp $PROT_MODE_CSEG, $protcseg
.code32 # запускаем 32-битный защищенный режим
# Инициализируем data segment registers
protcseg: movw $PROT_MODE_DSEG, %ax # data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
movw %ax, %ss # -> SS: Stack Segment
call bootmain # загрузка закончена
spinloop: jmp spinloop
# Segment descriptors
# макросы для инициализации сегментных дескрипторов
#define SEG_NULL \
.word 0, 0; \
.byte 0, 0, 0, 0
#define SEG(type,base,lim) \
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable segments)
#define STA_R 0x2 // Readable (executable segments)
.p2align 2 # force 4 byte alignment
gdt: SEG_NULL # null seg
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG(STA_W, 0x0, 0xffffffff) # data seg
gdtdesc: .word 0x17 # sizeof(gdt) - 1
.long gdt # address gdt
Файл kernel-boot.c :
void bootmain(void)
{
int i;
// console - переменная , указывающая на консольную видеопамять
char *console = (char *) 0xB8000;
// чистим экран
for (i = 0; i < 80 * 50; i++) console[i] = ' ';
// выводим мессагу
console[0] = 'H';
console[1] = 0x07;
console[2] = 'e';
console[3] = 0x07;
console[4] = 'l';
console[5] = 0x07;
console[6] = 'l';
console[7] = 0x07;
console[8] = '0';
console[9] = 0x07;
console[10] = ' ';
console[11] = 0x07;
console[12] = 'w';
console[13] = 0x07;
console[14] = 'o';
console[15] = 0x07;
console[16] = 'r';
console[17] = 0x07;
console[18] = 'l';
console[19] = 0x07;
console[20] = 'd';
console[21] = 0x07;
while (1);
}
|