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
Последние статьи :
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
  SQL 30.07   
  JFS 10.06   
  B-trees 01.06   
 
TOP 20
 Go Web ...825 
 Httpd-> История Ap...517 
 2.0-> Linux IP Networking...438 
 MySQL & PosgreSQL...380 
 Trees...378 
 Secure Programming for Li...369 
 Assembler...304 
 TCP 2...279 
 Secure Programming for Li...270 
 Steve Pate 3...269 
 Mod_perl 1...263 
 Cluster 3...262 
 Ethreal 1...256 
 Максвелл 3...254 
 Advanced Bash Scripting G...244 
 Python...236 
 Daniel Bovet 5...231 
 Linux Inline Assembly...231 
 Rubni-Corbet -> Глав...230 
 Steve Pate 1...229 
 
  01.06.2017 : 2215600 посещений 

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

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

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