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

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);
 }
 
Оставьте свой комментарий !

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

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