bkerndev - boot
Bran's Kernel Development
A tutorial on writing kernels
Version 1.0 (Feb 6th, 2005)
By: Brandon Friesen
Разработка ядра - нетривиальная задача .
Это как проверка на умение создать софт и интерфейс для управления железом .
Ядро - это центральная часть операционных систем - центр управления ресурсами компьютера .
Одним из важнейших ресурсов является процессор - на нем выделяется время для операций ,
прерываний , задач и т.д. Через него реализуется многозадачность , которая подразумевает
наличие шедулятора (sheduler) , например шедулятор под названием 'Round Robin' -
простейший вариант , когда имеется список процессов .
Более сложные шедуляторы имеют приоритеты , от которых зависит время выполнения процесса .
Другим важнейшим ресурсом является память . Для эффективной работы процессор должен уметь
использовать память для хранения промежуточных данных .
Следующим важнейшим ресурсом являются прерывания - Interrupt Requests (IRQs),
которые представляют из себя специальные сигналы , например от клавиатуры или харда ,
посланные процессору для того , чтобы тот приготовился от них читать данные .
Следующим ресурсом являются шины данных - Direct Memory Access (DMA) channels.
Они управляют потоками данных памяти . DMA позволяют переводить данные без использования
ЦПУ
Еще одним ресурсом являются порты ввода-вывода . Устройство может быть прочитано
с помощью таких портов .
В данном руководстве будет показано , как создать простейшее ядро , которое может быть
загружено с помощью GRUB . Будут проинициализированы Global Descriptor Table (GDT) ,
Interrupt Descriptor Table (IDT) . Будут созданы процедуры , обслуживающие прерывания -
Interrupt Service Routines (ISRs) . Будет проинициализирована клавиатура .
Автор все тестировал на следующей конфигурации : NASM + DJGPP + Windows .
Я переписал загрузчик на GNU ASM под 3-й федорой .
Вам может пригодиться утилита intel2gas , которая перводит формат INTEL ASM в гну-шный формат AT .
Автор предлагает иметь в наличие второй компьютер для тестирования .
Существуют более оптимальные варианты отладки - например , я отлаживался на VMWARE 5.0 .
После того как произошла первичная загрузка , загрузчик обычно передает управление
стартовой функции ядра (main()) . Эта часть , равно как и инициализация стека , загрузка GDT, IDT ,
как правило делается на ассемблере . Все остальное пишется на си .
В ассемблерном файле создается стек размером 8 КБ . Он используется си-шными функциями
для передачи параметров . Он также используется для хранения локальных переменных
внутри этих функций . Глобальные переменные хранятся в секторе данных .
В самом начале ассемблерного файла также необходимо проинициализировать глобальные физические адреса
для линковочного скрипта .
.code32
.globl start
start:
mov $(Stack + 8192), %esp
jmp stublet
# Эта часть должна быть выравнена на 4 byte
.align 4
mboot:
# Multiboot macros
.set MULTIBOOT_PAGE_ALIGN, 1<<0
.set MULTIBOOT_MEMORY_INFO, 1<<1
.set MULTIBOOT_AOUT_KLUDGE, 1<<16
.set MULTIBOOT_HEADER_MAGIC, 0x1BADB002
.set MULTIBOOT_HEADER_FLAGS , MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO
| MULTIBOOT_AOUT_KLUDGE
.set MULTIBOOT_CHECKSUM , -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
.extern code, bss, end
# GRUB Multiboot header
.long MULTIBOOT_HEADER_MAGIC
.long MULTIBOOT_HEADER_FLAGS
.long MULTIBOOT_CHECKSUM
# глобальные физические адреса для линковочного скрипта
.long mboot
.long code
.long bss
.long end
.long start
stublet:
.extern main
call main
right:
jmp right
.....
.section .bss
.lcomm Stack, 8192 # Резервируем 8 КБ под стек
Линкер - инструмент , который берет скомпилированные обьектные файла и получает из них
собственно ядро - бинарный файл kernel.img . В таком бинарном файле всегда существуют
3 секции :
TEXT
DATA
BSS
TEXT - хранится код . DATA - инициализированные данные . BSS - неинициализированные данные .
BSS - это виртуальная секция : в самом бинарнике ее нет , она создается в момент загрузки ядра .
Для линковки линкер использует специальный скрипт . Скрипт имеет 3 главных ключевых слова :
OUTPUT_FORMAT - тип файла - бинарный
ENTRY - указывает обьектный файл , который в списке линковки идет первым .
phys - это не ключевое слово , это просто переменная , которая указывает , по какому
адресу памяти будет загружено наше ядро - в данном случае 1 МБ
SECTIONS - определяет секции , которые выравниваются по 4 КБ - размер интеловской страницы по умолчанию
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
Makefile для получения ядра :
CFLAGS = -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions
-nostdinc -fno-builtin -I./include -c
LDFLAGS = -T link.ld
OBJS = obj/start.o obj/main.o obj/scrn.o obj/gdt.o obj/idt.o obj/isrs.o obj/irq.o
obj/timer.o obj/kb.o
CC = gcc -pipe
#default: clean all
all: kernel.img
obj/start.o: boot/start.s
as -o obj/start.o boot/start.s
obj/main.o: kernel/main.c
${CC} -o obj/main.o -c kernel/main.c ${CFLAGS}
obj/scrn.o: kernel/scrn.c
${CC} -o obj/scrn.o -c kernel/scrn.c ${CFLAGS}
obj/gdt.o: kernel/gdt.c
${CC} -o obj/gdt.o -c kernel/gdt.c ${CFLAGS}
obj/idt.o: kernel/idt.c
${CC} -o obj/idt.o -c kernel/idt.c ${CFLAGS}
obj/isrs.o: kernel/isrs.c
${CC} -o obj/isrs.o -c kernel/isrs.c ${CFLAGS}
obj/irq.o: kernel/irq.c
${CC} -o obj/irq.o -c kernel/irq.c ${CFLAGS}
obj/timer.o: kernel/timer.c
${CC} -o obj/timer.o -c kernel/timer.c ${CFLAGS}
obj/kb.o: kernel/kb.c
${CC} -o obj/kb.o -c kernel/kb.c ${CFLAGS}
kernel.img: ${OBJS}
ld ${LDFLAGS} -o build/kernel.img ${OBJS}
clean:
rm -f -v obj/*.o *.img
rm build/kernel.img
Архив исходников лежит тут (GNU ASM)
Архив исходников лежит тут (NASM)
Функция main() - стартовая точка ядра , она находится в main.c .
Имеется хидер - system.h - в котором прописаны прототипы глобальных функций .
Функция main() ничего не возвращает , она оканчивается как правило бесконечным циклом .
#include < system.h>
void *memcpy(void *dest, const void *src, size_t count)
{
const char *sp = (const char *)src;
char *dp = (char *)dest;
for(; count != 0; count--) *dp++ = *sp++;
return dest;
}
void *memset(void *dest, char val, size_t count)
{
char *temp = (char *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}
unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count)
{
unsigned short *temp = (unsigned short *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}
size_t strlen(const char *str)
{
size_t retval;
for(retval = 0; *str != '\0'; str++) retval++;
return retval;
}
unsigned char inportb (unsigned short _port)
{
unsigned char rv;
__asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port));
return rv;
}
void outportb (unsigned short _port, unsigned char _data)
{
__asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data));
}
void main()
{
gdt_install();
idt_install();
isrs_install();
irq_install();
init_video();
timer_install();
keyboard_install();
__asm__ __volatile__ ("sti");
........
for (;;);
}
|
Anton | На вашем сайте я узнал ряд интересных подробностей о программирование
в защищенном режиме. Спасибо. 2005-12-20 17:59:43 | Антон ( ино� | Very Good
Спасибо што подсказали, что есть порт LD под Windu!!!
Не знал. Очень пригодится. Исходники ld можно где-нибудь отыскать (на си)?
ant-str@yandex.ru 2006-01-26 02:47:41 | Яковлев Се� | Если имеется ввиду линуксовый линкер ,то он входит в состав пакета binutils
binutils версии 2.15 например весит 11 метров в архиве
Там кроме ld входят также :
ar
as
gprof
objdump
и т.д. 2006-01-26 18:57:37 | Антон | Thanx 2006-04-11 03:16:14 | |
|