Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Stupid OS

Данный проект является примером того , как трудозатраты на низкоуровневое программирование свести к минимуму. Чистого ассемблера здесь раз-два и обчелся : маленький загрузчик и немного кода для доступа к портам. Хотя совсем от ассемблера отказаться невозможно , поэтому он перекочевывает в инлайн.

Последовательность событий :
1. Инициализация GDT
2. Инициализация IDT
3. Инициализация PIC
4. Инициализация клавиатуры
5. Инициализвация таймера

GDT

8-байтовый дескриптор описывается с помощью структуры gdt_str_tag :

 typedef struct gdt_str_tag
 {
 	unsigned short int seg_limit_low;       //segment limit - 2 байта
 	unsigned short int base_low;		//Base Address - 2 байта
 	unsigned char base_mid;			//Base Address - 1 байт
 	seg_t seg_type:4;			//Segment Type - 4 бита
 	seg_desc_t seg_desc_type:1;		//Descriptor type (1 бит, 0 = system, 1 = code/data)
 	dpl_t dpl:2;				//Descriptor Privilege level (2 бита)
 	present_t present:1;			//Segment present (1 бит)
 	unsigned char seg_limit_high:4;		//segment limit 16:19 (4 бита)
 	unsigned char gran_def_op:4;		//avl (1 бит)
 						//reserved = 0 (1 бит)
 						//D/B(default operation size / big) (1 бит)
 						//Granularity (1 бит)
 						//Для защищенного режима = 0xC 
 	unsigned char base_high;		//Base Address - 1 байт
 }gdt_st_t;
 
Заполняем поля :

 	gdt_temp.seg_limit_low 		= 0x0FFF;	//Code Descriptor	
 	gdt_temp.base_low 		= 0x0000;
 	gdt_temp.base_mid 		= 0x00;
 	gdt_temp.seg_type		= 0xA;
 	gdt_temp.seg_desc_type 		= CODE_DATA;
 	gdt_temp.dpl 			= RING_0;
 	gdt_temp.present 		= PRESENT;
 	gdt_temp.seg_limit_high 	= 0x0;
 	gdt_temp.gran_def_op 		= GDO;
 	gdt_temp.base_high 		= 0x00;
 
Для дескриптора данных все то же самое , кроме :

 	gdt_temp.seg_type		= 0x2;
 
Адрес таблицы GDT - GDT_BASE=0x6000. Таблица состоит из 256 дескрипторов , первый - нулевой , второй - кодовый , третий-данные, остальные - также нулевые.

Для загрузки GDT в регистр GDTR создадим структуру :


 typedef struct gdtr_str_tag
 {
 	unsigned short int gdt_length;
 	unsigned short int gdt_base_low;
 	unsigned short int gdt_base_high;
 }gdtr_st_t;
 
Загрузка :

 	gdtr.gdt_length = (unsigned short int) 256 * 8;
 	gdtr.gdt_base_low = (unsigned short int) GDT_BASE_LOW;
 	gdtr.gdt_base_high = (unsigned short int) GDT_BASE_HIGH;
 	asm("lgdt (%0)": :"p" (&gdtr));
 

IDT

Опишем исключение с помощью структуры и создадим массив из 32 таких структур :

 typedef struct exceptiont_st_tag
 {
 	sint number;
 	int (* handler)();
 }exception_st_t;
 
 extern exception_st_t exceptions[32];
 
Массив exception_st_t описан в файле handler.c:

 exception_st_t exceptions[32]=
 {
         {0, ÷_error},
         {1, &debug},
         {2, &nmi_interrupt},
 		..........
         {30, &reserved},
         {31, &reserved}
 };
 
При возникновении исключения указатель exception_st_t->handler приводит куда надо - а именно в функию типа :

 int divide_error()
 {
 	kprintf("divide error exception\n", 0xF);
 	while(1);
 	return 0;
 }
 
Произведем загрузку IDT начиная с адреса IDTBASE 0x6800:

 	for(lv0 = 0; lv0 <= 31; lv0++)
 	{
 	load_idt_entry(exceptions[lv0].number,
 					(uint)exceptions[lv0].handler,
 					CS_SELECTOR,
 					TRAP_GATE|PRESENT|RING_0|BITS_32);
 	}
 
 
Внутренний дескриптор IDT опишем с помощью 6-байтовой структуры

 typedef struct idt_st_tag
 {
 	usint loffset;		// 2 байта
 	usint selector;		// 2 байта
 	byte unused;		// 1 байт
 	byte options;		
 				/*	constant:5;	 5 бит
 					 dpl:2;		 2 бит
 					 present:1;	 1 бит		*/
 
 	usint uoffset; 		// 2 bytes	
 }idt_st_t;
 
Загрузка регистра IDTR :

 	idtr.limit = (usint) (34 * 8);
 	idtr.lowbase = (usint) IDT_LOWBASE;
 	idtr.highbase = (usint) IDT_HIGHBASE;
 	asm("lidt (%0) ": :"p" (idtr));
 

PIC

Инициализация программируемого контроллера необходима для обслуживания хардварных прерываний. Определены хардварные хэндлеры в порядке приоритета :

 #define TIMER 0x0       	- системный таймер
 #define KEYBOARD 0x1    	- клавиатура
 #define SLAVE 0x2       	- вторичный PIC 
 #define TTY1 0x3        	- COM2 
 #define TTY2 0x4        	- COM1
 #define XT_WINCHESTER 0x5   	- LPT2
 #define FLOPPY 0x6		- floppy
 #define PRINTER 0x7		- printer
В последующем будут проинициализированы клавиатура с таймером. Для их инициализации будет вызвана функция enable_irq(byte irq_no), где в качестве параметра будет стоять нужный хэндлер.

Клавиатура

Для загрузки клавиатуры нужно загрузить дескриптор в таблице IDT со следующими параметрами :

 	load_idt_entry(33,(uint)&keyboard_handler,CS_SELECTOR,INT_GATE|PRESENT|BITS_32);
 
после чего разрешить прерывание :

 	enable_irq(KEYBOARD);
 
В качестве одного из параметров этой загрузки выступает функция keyboard_handler() - фактически это обработчик нажимаемых клавиш.

Таймер

Для инициализации таймера нужно загрузить дескриптор в таблице IDT со следующими параметрами :

      load_idt_entry(32,(uint)&timer_handler,CS_SELECTOR,TRAP_GATE|PRESENT|RING_0|BITS_32);
 
после чего разрешить прерывание :

 	 enable_irq(TIMER);
 
На системный таймер повешен хэндлер - timer_handler(). В нем обрабатывается событие - переполнение переменной , при котором на экране вы увидите появление очередного символа - "точки".

Загрузчик работает из-под граб-а.

Исходники лежат тут.

Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье