188 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
		
		
			
		
	
	
			188 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
|  | # Copyright (C) 2015 The University of Notre Dame | ||
|  | # This software is distributed under the GNU General Public License. | ||
|  | # See the file LICENSE for details. | ||
|  | 
 | ||
|  | # This is the raw bootblock code, a 512-byte chunk of assembly | ||
|  | # found on the first sector of the boot disk.  The BIOS is responsible | ||
|  | # for starting the machine, loading this sector into memory, | ||
|  | # and then transferring control here.  The bootblock must call | ||
|  | # to the BIOS to load the remaining sectors containing the | ||
|  | # kernel code, and then jump there. | ||
|  | 
 | ||
|  | # Constants describing our basic memory layout are in this | ||
|  | # header file, which is shared between C and assembly modules: | ||
|  | 	 | ||
|  | #include "memorylayout.h" | ||
|  | 	 | ||
|  | # When we receive control from the BIOS, the following are set: | ||
|  | # %dl - the device number this block was loaded from | ||
|  | # %es:%si - the partition table entry we were loaded from | ||
|  | 
 | ||
|  | # To set the code segment appropriately, the first thing we | ||
|  | # do is a long jump to _start2, which sets cs=BOOTBLOCK_SEGMENT | ||
|  | 	 | ||
|  | .code16 | ||
|  | .text | ||
|  | .global _start
 | ||
|  | _start: | ||
|  | 	ljmp	$BOOTBLOCK_SEGMENT,$_start2 | ||
|  | 
 | ||
|  | # Now we begin setting up the execution environment | ||
|  | # for loading the rest of the kernel. | ||
|  | 
 | ||
|  | _start2:	 | ||
|  | 	sti				# disable interrupts | ||
|  | 	cld				# clear the direction flag | ||
|  | 	mov	%cs, %ax		# set all segments to code | ||
|  | 	mov	%ax, %ds | ||
|  | 	mov	%ax, %es | ||
|  | 	mov	$INTERRUPT_STACK_SEGMENT, %ax     # set up the stack | ||
|  | 	mov	%ax, %ss | ||
|  | 	mov	$INTERRUPT_STACK_OFFSET, %sp | ||
|  | 
 | ||
|  | 	mov	%dl, (disk_number)	# save the disk number | ||
|  | 	mov	partition_status,%di	# set the partition table as dest | ||
|  | 	mov	$12, %cx		# copy 12 bytes from si to di | ||
|  | 	rep	movsb | ||
|  | 		 | ||
|  | 	mov	$(loadmsg),%si		# print initial message | ||
|  |         call	bios_putstring | ||
|  | 
 | ||
|  | 	mov	$0,%ah			# reset the disk system | ||
|  | 	int	$0x13 | ||
|  | 
 | ||
|  | 	mov	$0x08, %ah		# get the drive geometry | ||
|  | 	int	$0x13 | ||
|  | 	and	$0x3f, %cl		# mask off high tracks | ||
|  | 	mov	%cl, (disk_sectors) | ||
|  | 	mov	%ch, (disk_cylinders) | ||
|  | 	mov	%dh, (disk_heads) | ||
|  | 	 | ||
|  | 	mov	$KERNEL_SEGMENT,%ax	# load happens at es:bx | ||
|  | 	mov	%ax, %es		# which we set to | ||
|  | 	mov	$KERNEL_OFFSET,%bx	# KERNEL_SEGMENT:KERNEL_OFFSET | ||
|  | 
 | ||
|  | 					# disk parameters: | ||
|  | 	mov	(disk_number), %dl	#	device | ||
|  | 	mov	$0,%ch			#	cylinder 0 | ||
|  | 	mov	$0,%dh			#	head 0 | ||
|  | 	mov	$2,%cl			#	sector 2 | ||
|  | 
 | ||
|  | loadsector: | ||
|  | 	mov	$1,%al			# load 1 sector | ||
|  | 	mov	$0x02, %ah		# load command | ||
|  | 	int	$0x13			# execute load | ||
|  | 	 | ||
|  | 	mov	$'.', %al		# display a dot | ||
|  | 	call	bios_putchar		# for each sector loaded | ||
|  | 	 | ||
|  | 	mov	(sectors_left),%ax	# how many sectors left? | ||
|  | 	cmp	$0xffff, %ax		# has it been initialized? | ||
|  | 	jne	gotsectors		# yes - use the value | ||
|  | 	mov	%es:(KERNEL_SIZE_OFFSET),%eax	# no - get size of kernel | ||
|  | 	shr	$9, %eax		# convert into blocks | ||
|  | 	inc	%eax			# add one for good measure | ||
|  | 	 | ||
|  | gotsectors:		 | ||
|  | 	dec	%ax			# remove one block | ||
|  | 	mov	%ax,(sectors_left)	# store the value | ||
|  | 	cmp	$0, %ax			# are we done? | ||
|  | 	je	loaddone		# yes - jump to bottom | ||
|  | 			 | ||
|  | checksegment: | ||
|  | 	add	$512,%bx		# move data pointer by 512 bytes | ||
|  | 	cmp	$0, %bx			# did we reach segment end? | ||
|  | 	jnz	nextsector		# no - find next sector | ||
|  | 	mov	%es, %ax		# yes - retrieve seg register | ||
|  | 	add	$0x1000, %ax		# move to next 64k block | ||
|  | 	mov	%ax, %es		# store segment register | ||
|  | 
 | ||
|  | nextsector: | ||
|  | 	inc	%cl			# advance by one sector | ||
|  | 	mov	(disk_sectors),%al	# what is the maximum sector? | ||
|  | 	cmp	%al, %cl		# is this the last sector? | ||
|  | 	jle	loadsector		# no - load the next sector | ||
|  | 	mov	$1,%cl			# yes - go to sector zero.. | ||
|  | 
 | ||
|  | 	inc	%dh			# advance to next head | ||
|  | 	mov	(disk_heads), %al	# what is the maximum head? | ||
|  | 	cmp	%al, %dh		# is this the last head? | ||
|  | 	jle	loadsector		# no - read the next sector | ||
|  | 	mov	$0,%dh			# yes - go to head zero | ||
|  | 
 | ||
|  | 	inc	%ch			# advance to next cylinder | ||
|  | 	mov	(disk_cylinders), %al	# what is the maximum cylinder? | ||
|  | 	cmp	%al, %ch		# is this the last cylinder? | ||
|  | 	jle	loadsector		# no - read the next sector | ||
|  | 					# yes - fall through here | ||
|  | loaddone: | ||
|  | 	mov	$0,%ah			# reset the disk system | ||
|  | 	int	$0x13 | ||
|  | 	 | ||
|  | 	mov	$(bootmsg),%si		# print boot message | ||
|  |         call	bios_putstring | ||
|  | 	 | ||
|  | 	mov	$KERNEL_SEGMENT, %ax    # jump to the kernel code | ||
|  | 	mov	%ax, %ds | ||
|  | 	ljmp	$KERNEL_SEGMENT, $KERNEL_OFFSET | ||
|  | 	 | ||
|  | bios_putstring:				# routine to print an entire string | ||
|  | 	mov	(%si), %al | ||
|  | 	cmp	$0, %al | ||
|  | 	jz	bios_putstring_done | ||
|  |         call	bios_putchar | ||
|  | 	inc	%si | ||
|  | 	jmp	bios_putstring | ||
|  | bios_putstring_done: | ||
|  |         ret | ||
|  | 
 | ||
|  | bios_putchar:				# routine to print a single char | ||
|  | 	push	%ax | ||
|  | 	push	%bx | ||
|  |         mov	$14,%ah | ||
|  |         mov	$1,%bl | ||
|  |         int	$0x10 | ||
|  | 	pop	%bx | ||
|  | 	pop	%ax | ||
|  | 	ret | ||
|  | 
 | ||
|  | loadmsg: | ||
|  |         .asciz "bootblock: loading kernel...\r\n" | ||
|  | bootmsg: | ||
|  | 	.asciz "\r\nbootblock: booting kernel...\r\n" | ||
|  | 
 | ||
|  | disk_number: | ||
|  | 	.byte 0
 | ||
|  | disk_cylinders: | ||
|  | 	.byte 0
 | ||
|  | disk_heads: | ||
|  | 	.byte 0
 | ||
|  | disk_sectors: | ||
|  | 	.byte 0
 | ||
|  | sectors_left:	 | ||
|  | 	.word 0xffff
 | ||
|  | 
 | ||
|  | partition_status: | ||
|  | 	.byte 0
 | ||
|  | partition_start_chs:	 | ||
|  | 	.byte 0
 | ||
|  | 	.byte 0
 | ||
|  | 	.byte 0
 | ||
|  | partition_type:	 | ||
|  | 	.byte 0
 | ||
|  | partition_stop_chs:	 | ||
|  | 	.byte 0
 | ||
|  | 	.byte 0
 | ||
|  | 	.byte 0
 | ||
|  | partition_start_lba: | ||
|  | 	.long 0
 | ||
|  | partition_length: | ||
|  | 	.long 0
 | ||
|  | 				 | ||
|  | # A bootblock must have 0xaa55 in its two final bytes. | ||
|  | # The .org directive forces this data to that point. | ||
|  | 
 | ||
|  | .org 510
 | ||
|  | bootflag: | ||
|  | 	.word 0xaa55
 |