mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-11-04 10:24:12 +00:00 
			
		
		
		
	(LOCAL(firstlist)): ... this. Move it before the firstlist and not after. All users updated.
		
			
				
	
	
		
			379 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 *  GRUB  --  GRand Unified Bootloader
 | 
						|
 *  Copyright (C) 1999,2000,2001,2002,2006,2007,2009,2010   Free Software Foundation, Inc.
 | 
						|
 *
 | 
						|
 *  GRUB is free software: you can redistribute it and/or modify
 | 
						|
 *  it under the terms of the GNU General Public License as published by
 | 
						|
 *  the Free Software Foundation, either version 3 of the License, or
 | 
						|
 *  (at your option) any later version.
 | 
						|
 *
 | 
						|
 *  GRUB is distributed in the hope that it will be useful,
 | 
						|
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 *  GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 *  You should have received a copy of the GNU General Public License
 | 
						|
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include <grub/symbol.h>
 | 
						|
#include <grub/machine/boot.h>
 | 
						|
 | 
						|
/*
 | 
						|
 *  defines for the code go here
 | 
						|
 */
 | 
						|
 | 
						|
#define MSG(x)	movw $x, %si; call LOCAL(message)
 | 
						|
 | 
						|
	.file	"diskboot.S"
 | 
						|
 | 
						|
	.text
 | 
						|
 | 
						|
	/* Tell GAS to generate 16-bit instructions so that this code works
 | 
						|
	   in real mode. */
 | 
						|
	.code16
 | 
						|
 | 
						|
	.globl	start, _start
 | 
						|
start:
 | 
						|
_start:
 | 
						|
	/*
 | 
						|
	 * _start is loaded at 0x2000 and is jumped to with
 | 
						|
	 * CS:IP 0:0x2000 in kernel.
 | 
						|
	 */
 | 
						|
 | 
						|
	/*
 | 
						|
	 * we continue to use the stack for boot.img and assume that
 | 
						|
	 * some registers are set to correct values. See boot.S
 | 
						|
	 * for more information.
 | 
						|
	 */
 | 
						|
 | 
						|
	/* save drive reference first thing! */
 | 
						|
	pushw	%dx
 | 
						|
 | 
						|
	/* print a notification message on the screen */
 | 
						|
	pushw	%si
 | 
						|
	MSG(notification_string)
 | 
						|
	popw	%si
 | 
						|
 | 
						|
	/* this sets up for the first run through "bootloop" */
 | 
						|
	movw	$LOCAL(firstlist), %di
 | 
						|
 | 
						|
	/* save the sector number of the second sector in %ebp */
 | 
						|
	movl	(%di), %ebp
 | 
						|
 | 
						|
        /* this is the loop for reading the rest of the kernel in */
 | 
						|
LOCAL(bootloop):
 | 
						|
 | 
						|
	/* check the number of sectors to read */
 | 
						|
	cmpw	$0, 8(%di)
 | 
						|
 | 
						|
	/* if zero, go to the start function */
 | 
						|
	je	LOCAL(bootit)
 | 
						|
 | 
						|
LOCAL(setup_sectors):
 | 
						|
	/* check if we use LBA or CHS */
 | 
						|
	cmpb	$0, -1(%si)
 | 
						|
 | 
						|
	/* use CHS if zero, LBA otherwise */
 | 
						|
	je	LOCAL(chs_mode)
 | 
						|
 | 
						|
	/* load logical sector start */
 | 
						|
	movl	(%di), %ebx
 | 
						|
	movl	4(%di), %ecx
 | 
						|
 | 
						|
	/* the maximum is limited to 0x7f because of Phoenix EDD */
 | 
						|
	xorl	%eax, %eax
 | 
						|
	movb	$0x7f, %al
 | 
						|
 | 
						|
	/* how many do we really want to read? */
 | 
						|
	cmpw	%ax, 8(%di)	/* compare against total number of sectors */
 | 
						|
 | 
						|
	/* which is greater? */
 | 
						|
	jg	1f
 | 
						|
 | 
						|
	/* if less than, set to total */
 | 
						|
	movw	8(%di), %ax
 | 
						|
 | 
						|
1:
 | 
						|
	/* subtract from total */
 | 
						|
	subw	%ax, 8(%di)
 | 
						|
 | 
						|
	/* add into logical sector start */
 | 
						|
	addl	%eax, (%di)
 | 
						|
	adcl	$0, 4(%di)
 | 
						|
 | 
						|
	/* set up disk address packet */
 | 
						|
 | 
						|
	/* the size and the reserved byte */
 | 
						|
	movw	$0x0010, (%si)
 | 
						|
 | 
						|
	/* the number of sectors */
 | 
						|
	movw	%ax, 2(%si)
 | 
						|
 | 
						|
	/* the absolute address */
 | 
						|
	movl	%ebx, 8(%si)
 | 
						|
	movl	%ecx, 12(%si)
 | 
						|
 | 
						|
	/* the segment of buffer address */
 | 
						|
	movw	$GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
 | 
						|
 | 
						|
	/* save %ax from destruction! */
 | 
						|
	pushw	%ax
 | 
						|
 | 
						|
	/* the offset of buffer address */
 | 
						|
	movw	$0, 4(%si)
 | 
						|
 | 
						|
/*
 | 
						|
 * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
 | 
						|
 *	Call with	%ah = 0x42
 | 
						|
 *			%dl = drive number
 | 
						|
 *			%ds:%si = segment:offset of disk address packet
 | 
						|
 *	Return:
 | 
						|
 *			%al = 0x0 on success; err code on failure
 | 
						|
 */
 | 
						|
 | 
						|
	movb	$0x42, %ah
 | 
						|
	int	$0x13
 | 
						|
 | 
						|
	jc	LOCAL(read_error)
 | 
						|
 | 
						|
	movw	$GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
 | 
						|
	jmp	LOCAL(copy_buffer)
 | 
						|
 | 
						|
LOCAL(chs_mode):
 | 
						|
	/* load logical sector start (top half) */
 | 
						|
	movl	4(%di), %eax
 | 
						|
	orl	%eax, %eax
 | 
						|
	jnz	LOCAL(geometry_error)
 | 
						|
 | 
						|
	/* load logical sector start (bottom half) */
 | 
						|
	movl	(%di), %eax
 | 
						|
 | 
						|
	/* zero %edx */
 | 
						|
	xorl	%edx, %edx
 | 
						|
 | 
						|
	/* divide by number of sectors */
 | 
						|
	divl	(%si)
 | 
						|
 | 
						|
	/* save sector start */
 | 
						|
	movb	%dl, 10(%si)
 | 
						|
 | 
						|
	xorl	%edx, %edx	/* zero %edx */
 | 
						|
	divl	4(%si)		/* divide by number of heads */
 | 
						|
 | 
						|
	/* save head start */
 | 
						|
	movb	%dl, 11(%si)
 | 
						|
 | 
						|
	/* save cylinder start */
 | 
						|
	movw	%ax, 12(%si)
 | 
						|
 | 
						|
	/* do we need too many cylinders? */
 | 
						|
	cmpw	8(%si), %ax
 | 
						|
	jge	LOCAL(geometry_error)
 | 
						|
 | 
						|
	/* determine the maximum sector length of this read */
 | 
						|
	movw	(%si), %ax	/* get number of sectors per track/head */
 | 
						|
 | 
						|
	/* subtract sector start */
 | 
						|
	subb	10(%si), %al
 | 
						|
 | 
						|
	/* how many do we really want to read? */
 | 
						|
	cmpw	%ax, 8(%di)	/* compare against total number of sectors */
 | 
						|
 | 
						|
 | 
						|
	/* which is greater? */
 | 
						|
	jg	2f
 | 
						|
 | 
						|
	/* if less than, set to total */
 | 
						|
	movw	8(%di), %ax
 | 
						|
 | 
						|
2:
 | 
						|
	/* subtract from total */
 | 
						|
	subw	%ax, 8(%di)
 | 
						|
 | 
						|
	/* add into logical sector start */
 | 
						|
	addl	%eax, (%di)
 | 
						|
	adcl	$0, 4(%di)
 | 
						|
 | 
						|
/*
 | 
						|
 *  This is the loop for taking care of BIOS geometry translation (ugh!)
 | 
						|
 */
 | 
						|
 | 
						|
	/* get high bits of cylinder */
 | 
						|
	movb	13(%si), %dl
 | 
						|
 | 
						|
	shlb	$6, %dl		/* shift left by 6 bits */
 | 
						|
	movb	10(%si), %cl	/* get sector */
 | 
						|
 | 
						|
	incb	%cl		/* normalize sector (sectors go
 | 
						|
					from 1-N, not 0-(N-1) ) */
 | 
						|
	orb	%dl, %cl	/* composite together */
 | 
						|
	movb	12(%si), %ch	/* sector+hcyl in cl, cylinder in ch */
 | 
						|
 | 
						|
	/* restore %dx */
 | 
						|
	popw	%dx
 | 
						|
	pushw	%dx
 | 
						|
 | 
						|
	/* head number */
 | 
						|
	movb	11(%si), %dh
 | 
						|
 | 
						|
	pushw	%ax	/* save %ax from destruction! */
 | 
						|
 | 
						|
/*
 | 
						|
 * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
 | 
						|
 *	Call with	%ah = 0x2
 | 
						|
 *			%al = number of sectors
 | 
						|
 *			%ch = cylinder
 | 
						|
 *			%cl = sector (bits 6-7 are high bits of "cylinder")
 | 
						|
 *			%dh = head
 | 
						|
 *			%dl = drive (0x80 for hard disk, 0x0 for floppy disk)
 | 
						|
 *			%es:%bx = segment:offset of buffer
 | 
						|
 *	Return:
 | 
						|
 *			%al = 0x0 on success; err code on failure
 | 
						|
 */
 | 
						|
 | 
						|
	movw	$GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
 | 
						|
	movw	%bx, %es	/* load %es segment with disk buffer */
 | 
						|
 | 
						|
	xorw	%bx, %bx	/* %bx = 0, put it at 0 in the segment */
 | 
						|
	movb	$0x2, %ah	/* function 2 */
 | 
						|
	int	$0x13
 | 
						|
 | 
						|
	jc	LOCAL(read_error)
 | 
						|
 | 
						|
	/* save source segment */
 | 
						|
	movw	%es, %bx
 | 
						|
 | 
						|
LOCAL(copy_buffer):
 | 
						|
 | 
						|
	/* load addresses for copy from disk buffer to destination */
 | 
						|
	movw	10(%di), %es	/* load destination segment */
 | 
						|
 | 
						|
	/* restore %ax */
 | 
						|
	popw	%ax
 | 
						|
 | 
						|
	/* determine the next possible destination address (presuming
 | 
						|
		512 byte sectors!) */
 | 
						|
	shlw	$5, %ax		/* shift %ax five bits to the left */
 | 
						|
	addw	%ax, 10(%di)	/* add the corrected value to the destination
 | 
						|
				   address for next time */
 | 
						|
 | 
						|
	/* save addressing regs */
 | 
						|
	pusha
 | 
						|
	pushw	%ds
 | 
						|
 | 
						|
	/* get the copy length */
 | 
						|
	shlw	$3, %ax
 | 
						|
	movw	%ax, %cx
 | 
						|
 | 
						|
	xorw	%di, %di	/* zero offset of destination addresses */
 | 
						|
	xorw	%si, %si	/* zero offset of source addresses */
 | 
						|
	movw	%bx, %ds	/* restore the source segment */
 | 
						|
 | 
						|
	cld		/* sets the copy direction to forward */
 | 
						|
 | 
						|
	/* perform copy */
 | 
						|
	rep		/* sets a repeat */
 | 
						|
	movsw		/* this runs the actual copy */
 | 
						|
 | 
						|
	/* restore addressing regs and print a dot with correct DS
 | 
						|
	   (MSG modifies SI, which is saved, and unused AX and BX) */
 | 
						|
	popw	%ds
 | 
						|
	MSG(notification_step)
 | 
						|
	popa
 | 
						|
 | 
						|
	/* check if finished with this dataset */
 | 
						|
	cmpw	$0, 8(%di)
 | 
						|
	jne	LOCAL(setup_sectors)
 | 
						|
 | 
						|
	/* update position to load from */
 | 
						|
	subw	$GRUB_BOOT_MACHINE_LIST_SIZE, %di
 | 
						|
 | 
						|
	/* jump to bootloop */
 | 
						|
	jmp	LOCAL(bootloop)
 | 
						|
 | 
						|
/* END OF MAIN LOOP */
 | 
						|
 | 
						|
LOCAL(bootit):
 | 
						|
	/* print a newline */
 | 
						|
	MSG(notification_done)
 | 
						|
	popw	%dx	/* this makes sure %dl is our "boot" drive */
 | 
						|
	ljmp	$0, $(GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200)
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * BIOS Geometry translation error (past the end of the disk geometry!).
 | 
						|
 */
 | 
						|
LOCAL(geometry_error):
 | 
						|
	MSG(geometry_error_string)
 | 
						|
	jmp	LOCAL(general_error)
 | 
						|
 | 
						|
/*
 | 
						|
 * Read error on the disk.
 | 
						|
 */
 | 
						|
LOCAL(read_error):
 | 
						|
	MSG(read_error_string)
 | 
						|
 | 
						|
LOCAL(general_error):
 | 
						|
	MSG(general_error_string)
 | 
						|
 | 
						|
/* go here when you need to stop the machine hard after an error condition */
 | 
						|
LOCAL(stop):	jmp	LOCAL(stop)
 | 
						|
 | 
						|
notification_string:	.asciz "loading"
 | 
						|
 | 
						|
notification_step:	.asciz "."
 | 
						|
notification_done:	.asciz "\r\n"
 | 
						|
 | 
						|
geometry_error_string:	.asciz "Geom"
 | 
						|
read_error_string:	.asciz "Read"
 | 
						|
general_error_string:	.asciz " Error"
 | 
						|
 | 
						|
/*
 | 
						|
 * message: write the string pointed to by %si
 | 
						|
 *
 | 
						|
 *   WARNING: trashes %si, %ax, and %bx
 | 
						|
 */
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
 | 
						|
	 *	%ah = 0xe	%al = character
 | 
						|
	 *	%bh = page	%bl = foreground color (graphics modes)
 | 
						|
	 */
 | 
						|
1:
 | 
						|
	movw	$0x0001, %bx
 | 
						|
	movb	$0xe, %ah
 | 
						|
	int	$0x10		/* display a byte */
 | 
						|
 | 
						|
	incw	%si
 | 
						|
LOCAL(message):
 | 
						|
	movb	(%si), %al
 | 
						|
	cmpb	$0, %al
 | 
						|
	jne	1b	/* if not end of string, jmp to display */
 | 
						|
	ret
 | 
						|
 | 
						|
/*
 | 
						|
 *  This area is an empty space between the main body of code below which
 | 
						|
 *  grows up (fixed after compilation, but between releases it may change
 | 
						|
 *  in size easily), and the lists of sectors to read, which grows down
 | 
						|
 *  from a fixed top location.
 | 
						|
 */
 | 
						|
 | 
						|
	.word 0
 | 
						|
	.word 0
 | 
						|
 | 
						|
	. = _start + 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE
 | 
						|
LOCAL(firstlist):	/* this label has to be before the first list entry!!! */
 | 
						|
        /* fill the first data listing with the default */
 | 
						|
blocklist_default_start:
 | 
						|
	/* this is the sector start parameter, in logical sectors from
 | 
						|
	   the start of the disk, sector 0 */
 | 
						|
	.long 2, 0
 | 
						|
blocklist_default_len:
 | 
						|
	/* this is the number of sectors to read.  grub-mkimage
 | 
						|
	   will fill this up */
 | 
						|
	.word 0
 | 
						|
blocklist_default_seg:
 | 
						|
	/* this is the segment of the starting address to load the data into */
 | 
						|
	.word (GRUB_BOOT_MACHINE_KERNEL_SEG + 0x20)
 |