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
 B.Blunden 1...1367 
 Boot...1367 
 2.6-> L.Torvalds 19.05.20...1366 
 AT&T Tutorial...1366 
 Rust 6...1366 
 NASM...1366 
 Part 3...1366 
 Kernel 5.3...1366 
 C++ Templates 1...1366 
 Minix FS...1366 
 DevFS...1366 
 Alg1...1366 
 Go...1366 
 Ext4 FS...1366 
 Benchmark...1366 
 Сетунь...1366 
 Trees...1366 
 Httpd-> История Ap...1366 
 
  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);
 }
 
Оставьте свой комментарий !

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

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