Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 OS
 osjournal 
 Protected Mode 
 Hardware 
 Kernels
  Dark Fiber
  BOS
  QNX
  OS Dev
  Lecture notes
  MINIX
  OS
  Solaris
  История UNIX
  История FreeBSD
  Сетунь
  Эльбрус
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...5172 
 Trees...943 
 Максвелл 3...874 
 Go Web ...826 
 William Gropp...809 
 Ethreal 3...790 
 Gary V.Vaughan-> Libtool...778 
 Ethreal 4...774 
 Rodriguez 6...769 
 Steve Pate 1...758 
 Ext4 FS...758 
 Clickhouse...757 
 Ethreal 1...745 
 Secure Programming for Li...734 
 C++ Patterns 3...721 
 Ulrich Drepper...701 
 Assembler...697 
 DevFS...668 
 Стивенс 9...655 
 MySQL & PosgreSQL...635 
 
  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);
 }
 
Оставьте свой комментарий !

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

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