Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
      Languages 
      Kernels 
      Packages 
      Books 
      Tests 
      OS 
      Forum 
      Математика 
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...3041 
 Trees...1500 
 William Gropp...1452 
 Ethreal 3...1423 
 C++ Patterns 3...1410 
 Ethreal 4...1406 
 Rodriguez 6...1400 
 Максвелл 3...1394 
 Httpd-> История Ap...1391 
 Go Web ...1387 
 Kamran Husain...1387 
 Максвелл 5...1386 
 Ext4 FS...1386 
 Robert Love 5...1383 
 Стивенс 9...1382 
 K&R 1...1381 
 OS ->Intel Manual 1...1379 
 Rubni-Corbet -> Глав...1377 
 Perl OOP...1375 
 Erlang...1373 
 
  01.01.2024 : 3621733 посещений 

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

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

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