* Re: [PATCH] Move assembly code out of the kernel
@ 2008-07-31 13:55 chaac
2008-08-17 11:31 ` Vesa Jääskeläinen
0 siblings, 1 reply; 8+ messages in thread
From: chaac @ 2008-07-31 13:55 UTC (permalink / raw)
To: The development of GRUB 2
Bean [bean123ch@gmail.com] kirjoitti:
> Hi,
>
> Currently, most assembly code are in startup.S. This is normally used
> to ensure that the function address are below 1m, which is required if
> it would switch to real mode and call bios services. However, this
> make the kernel larger. For example, the biosdisk functions are only
> used by biodisk module, they should not be placed inside the kernel.
>
> This patch support splitting such code from startup.S. For example, we
> create a new module biosdisk_stub.mod for assembly code of biosdisk.
> Instead of call prot_to_real and real_to_prot, we call
> grub_call_real_stub to enter real mode. grub_call_real_stub would copy
> the code to real mode and do the mode switch.
>
> To avoid unnecessary memory transfer, grub_call_real_stub would not
> erase the real mode stub when it's done, so that it can be used
> directly next time. When the stub area is full, it zero it out and
> start anew. The area uses a simple verification method so that the old
> mapping is invalidated, the code would need be copied again on their
> next use.
>
> The patch shows how to do it for the biosdisk module, here is the new
> grub_biosdisk_rw_int13_extensions function.
>
> REAL_STUB_START(grub_biosdisk_rw_int13_extensions)
> movb %dh, %ah
> movw %cx, %ds
> int $0x13 /* do the operation */
> movb %ah, %dl /* save return value */
> lret
> REAL_STUB_END(grub_biosdisk_rw_int13_extensions)
>
> FUNCTION(grub_biosdisk_rw_int13_extensions)
> pushl %ebp
> pushl %esi
>
> /* compute the address of disk_address_packet */
> movw %cx, %si
> xorw %cx, %cx
> shrl $4, %ecx /* save the segment to cx */
>
> /* ah */
> movb %al, %dh
>
> leal grub_biosdisk_rw_int13_extensions_stub, %eax
> call EXT_C(grub_call_real_stub)
>
> movb %dl, %al /* return value in %eax */
>
> popl %esi
> popl %ebp
>
> ret
>
> Real mode code is enclosed between REAL_STUB_START and REAL_STUB_END,
> no need to use .code16 and .code32 as it's handled by the macro. In
> the main function, use
>
> leal grub_biosdisk_rw_int13_extensions_stub, %eax
> call EXT_C(grub_call_real_stub)
>
> to invoke grub_call_real_stub. grub_biosdisk_rw_int13_extensions_stub
> is defined in the REAL_STUB_START macro.
>
> This same method can be applied to loaders, vbe, etc. In fact, almost
> all function behind grub_call_real_stub can be moved out of startup.S.
I have made generic function that does basically the same thing for bios service 0x10 (video). In that modification you prepare registers structure that will be configured during real mode switching. I am yet to commit it for review, but I think it would be more generic way to do this. When I come back from my holiday I will commit the code for review.
So please wait a bit before committing this :)
Thanks,
Vesa Jääskeläinen
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] Move assembly code out of the kernel
2008-07-31 13:55 [PATCH] Move assembly code out of the kernel chaac
@ 2008-08-17 11:31 ` Vesa Jääskeläinen
2008-08-17 16:55 ` Bean
0 siblings, 1 reply; 8+ messages in thread
From: Vesa Jääskeläinen @ 2008-08-17 11:31 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 479 bytes --]
chaac@nic.fi wrote:
> I have made generic function that does basically the same thing for bios
> service 0x10 (video). In that modification you prepare registers
> structure that will be configured during real mode switching. I am yet
> to commit it for review, but I think it would be more generic way to do
> this. When I come back from my holiday I will commit the code for review.
>
> So please wait a bit before committing this :)
And here it is :)
Comments are welcome.
[-- Attachment #2: move_vbe_calls_from_kernel_to_vbe_mod.diff --]
[-- Type: text/plain, Size: 29912 bytes --]
Index: conf/i386-pc.rmk
===================================================================
--- conf/i386-pc.rmk (revision 1815)
+++ conf/i386-pc.rmk (working copy)
@@ -55,7 +55,7 @@
partition.h pc_partition.h rescue.h symbol.h term.h time.h types.h \
machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \
machine/memory.h machine/loader.h machine/vga.h machine/vbe.h \
- machine/kernel.h machine/pxe.h
+ machine/kernel.h machine/pxe.h machine/bioscall.h
kernel_img_CFLAGS = $(COMMON_CFLAGS)
kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
kernel_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS) -Wl,-Ttext,$(GRUB_MEMORY_MACHINE_LINK_ADDR) $(COMMON_CFLAGS)
@@ -231,8 +231,9 @@
multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For vbe.mod.
-vbe_mod_SOURCES = video/i386/pc/vbe.c video/i386/pc/vbeblit.c \
- video/i386/pc/vbefill.c video/i386/pc/vbeutil.c
+vbe_mod_SOURCES = video/i386/pc/vbe.c video/i386/pc/vbebios.c \
+ video/i386/pc/vbeblit.c video/i386/pc/vbefill.c \
+ video/i386/pc/vbeutil.c
vbe_mod_CFLAGS = $(COMMON_CFLAGS)
vbe_mod_LDFLAGS = $(COMMON_LDFLAGS)
Index: kern/i386/pc/startup.S
===================================================================
--- kern/i386/pc/startup.S (revision 1815)
+++ kern/i386/pc/startup.S (working copy)
@@ -1625,437 +1625,6 @@
popl %ebp
ret
-/*
- * grub_vbe_bios_status_t grub_vbe_get_controller_info (struct grub_vbe_info_block *controller_info)
- *
- * Register allocations for parameters:
- * %eax *controller_info
- */
-FUNCTION(grub_vbe_bios_get_controller_info)
- pushl %ebp
- pushl %edi
- pushl %edx
-
- movw %ax, %di /* Store *controller_info to %edx:%di. */
- xorw %ax, %ax
- shrl $4, %eax
- mov %eax, %edx /* prot_to_real destroys %eax. */
-
- call prot_to_real
- .code16
-
- pushw %es
-
- movw %dx, %es /* *controller_info is now on %es:%di. */
- movw $0x4f00, %ax
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- popw %es
-
- DATA32 call real_to_prot
- .code32
-
- movl %edx, %eax
- andl $0x0FFFF, %eax /* Return value in %eax. */
-
- pop %edx
- popl %edi
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_get_mode_info (grub_uint32_t mode,
- * struct grub_vbe_mode_info_block *mode_info)
- *
- * Register allocations for parameters:
- * %eax mode
- * %edx *mode_info
- */
-FUNCTION(grub_vbe_bios_get_mode_info)
- pushl %ebp
- pushl %edi
-
- movl %eax, %ecx /* Store mode number to %ecx. */
-
- movw %dx, %di /* Store *mode_info to %edx:%di. */
- xorw %dx, %dx
- shrl $4, %edx
-
- call prot_to_real
- .code16
-
- pushw %es
-
- movw %dx, %es /* *mode_info is now on %es:%di. */
- movw $0x4f01, %ax
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- popw %es
-
- DATA32 call real_to_prot
- .code32
-
- movl %edx, %eax
- andl $0x0FFFF, %eax /* Return value in %eax. */
-
- popl %edi
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_set_mode (grub_uint32_t mode,
- * struct grub_vbe_crtc_info_block *crtc_info)
- *
- * Register allocations for parameters:
- * %eax mode
- * %edx *crtc_info
- */
-FUNCTION(grub_vbe_bios_set_mode)
- pushl %ebp
- pushl %ebx
- pushl %edi
-
- movl %eax, %ebx /* Store mode in %ebx. */
-
- movw %dx, %di /* Store *crtc_info to %edx:%di. */
- xorw %dx, %dx
- shrl $4, %edx
-
- call prot_to_real
- .code16
-
- pushw %es
-
- movw %dx, %es /* *crtc_info is now on %es:%di. */
-
- movw $0x4f02, %ax
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- popw %es
-
- DATA32 call real_to_prot
- .code32
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edi
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_get_mode (grub_uint32_t *mode)
- *
- * Register allocations for parameters:
- * %eax *mode
- */
-FUNCTION(grub_vbe_bios_get_mode)
- pushl %ebp
- pushl %ebx
- pushl %edi
- pushl %edx
- pushl %eax /* Push *mode to stack. */
-
- call prot_to_real
- .code16
-
- movw $0x4f03, %ax
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- popl %edi /* Pops *mode from stack to %edi. */
- andl $0xFFFF, %ebx
- movl %ebx, (%edi)
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edx
- popl %edi
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_set_memory_window (grub_uint32_t window,
- * grub_uint32_t position);
- *
- * Register allocations for parameters:
- * %eax window
- * %edx position
- */
-FUNCTION(grub_vbe_bios_set_memory_window)
- pushl %ebp
- pushl %ebx
-
- movl %eax, %ebx
-
- call prot_to_real
- .code16
-
- movw $0x4f05, %ax
- andw $0x00ff, %bx /* BL = window, BH = 0, Set memory window. */
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_get_memory_window (grub_uint32_t window,
- * grub_uint32_t *position);
- *
- * Register allocations for parameters:
- * %eax window
- * %edx *position
- */
-FUNCTION(grub_vbe_bios_get_memory_window)
- pushl %ebp
- pushl %ebx
- pushl %edi
- pushl %edx /* Push *position to stack. */
-
- movl %eax, %ebx /* Store window in %ebx. */
-
- call prot_to_real
- .code16
-
- movw $0x4f05, %ax
- andw $0x00ff, %bx /* BL = window. */
- orw $0x0100, %bx /* BH = 1, Get memory window. */
- int $0x10
-
- movw %ax, %bx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- popl %edi /* pops *position from stack to %edi. */
- andl $0xFFFF, %edx
- movl %edx, (%edi) /* Return position to caller. */
-
- movw %bx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edi
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_set_scanline_length (grub_uint32_t length)
- *
- * Register allocations for parameters:
- * %eax length
- */
-FUNCTION(grub_vbe_bios_set_scanline_length)
- pushl %ebp
- pushl %ebx
- pushl %edx
-
- movl %eax, %ecx /* Store length in %ecx. */
-
- call prot_to_real
- .code16
-
- movw $0x4f06, %ax
- movw $0x0002, %bx /* BL = 2, Set Scan Line in Bytes. */
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edx
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_get_scanline_length (grub_uint32_t *length)
- *
- * Register allocations for parameters:
- * %eax *length
- */
-FUNCTION(grub_vbe_bios_get_scanline_length)
- pushl %ebp
- pushl %ebx
- pushl %edi
- pushl %edx /* Push *length to stack. */
-
- call prot_to_real
- .code16
-
- movw $0x4f06, %ax
- movw $0x0001, %bx /* BL = 1, Get Scan Line Length (in bytes). */
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- popl %edi /* Pops *length from stack to %edi. */
- andl $0xFFFF, %ebx
- movl %ebx, (%edi) /* Return length to caller. */
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edi
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_set_display_start (grub_uint32_t x,
- * grub_uint32_t y)
- *
- * Register allocations for parameters:
- * %eax x
- * %edx y
- */
-FUNCTION(grub_vbe_bios_set_display_start)
- pushl %ebp
- pushl %ebx
-
- movl %eax, %ecx /* Store x in %ecx. */
-
- call prot_to_real
- .code16
-
- movw $0x4f07, %ax
- movw $0x0080, %bx /* BL = 80h, Set Display Start
- during Vertical Retrace. */
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_get_display_start (grub_uint32_t *x,
- * grub_uint32_t *y)
- *
- * Register allocations for parameters:
- * %eax *x
- * %edx *y
- */
-FUNCTION(grub_vbe_bios_get_display_start)
- pushl %ebp
- pushl %ebx
- pushl %edi
- pushl %eax /* Push *x to stack. */
- pushl %edx /* Push *y to stack. */
-
- call prot_to_real
- .code16
-
- movw $0x4f07, %ax
- movw $0x0001, %bx /* BL = 1, Get Display Start. */
- int $0x10
-
- movw %ax, %bx /* real_to_prot destroys %eax. */
-
- DATA32 call real_to_prot
- .code32
-
- popl %edi /* Pops *y from stack to %edi. */
- andl $0xFFFF, %edx
- movl %edx, (%edi) /* Return y-position to caller. */
-
- popl %edi /* Pops *x from stack to %edi. */
- andl $0xFFFF, %ecx
- movl %ecx, (%edi) /* Return x-position to caller. */
-
- movw %bx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edi
- popl %ebx
- popl %ebp
- ret
-
-/*
- * grub_vbe_status_t grub_vbe_bios_set_palette_data (grub_uint32_t color_count,
- * grub_uint32_t start_index,
- * struct grub_vbe_palette_data *palette_data)
- *
- * Register allocations for parameters:
- * %eax color_count
- * %edx start_index
- * %ecx *palette_data
- */
-FUNCTION(grub_vbe_bios_set_palette_data)
- pushl %ebp
- pushl %ebx
- pushl %edi
-
- movl %eax, %ebx /* Store color_count in %ebx. */
-
- movw %cx, %di /* Store *palette_data to %ecx:%di. */
- xorw %cx, %cx
- shrl $4, %ecx
-
- call prot_to_real
- .code16
-
- pushw %es
-
- movw %cx, %es /* *palette_data is now on %es:%di. */
- movw %bx, %cx /* color_count is now on %cx. */
-
- movw $0x4f09, %ax
- xorw %bx, %bx /* BL = 0, Set Palette Data. */
- int $0x10
-
- movw %ax, %dx /* real_to_prot destroys %eax. */
-
- popw %es
-
- DATA32 call real_to_prot
- .code32
-
- movw %dx, %ax
- andl $0xFFFF, %eax /* Return value in %eax. */
-
- popl %edi
- popl %ebx
- popl %ebp
- ret
-
-
pxe_rm_entry:
.long 0
@@ -2153,3 +1722,170 @@
popl %esi
popl %ebp
ret
+
+/*
+ * grub_uint32_t grub_bioscall_int10h (struct grub_bioscall_regs *regs,
+ * struct grub_bioscall_regs *result_regs)
+ *
+ * Register allocations for parameters:
+ * %eax *regs
+ * %edx *result_regs
+ */
+FUNCTION(grub_bioscall_int10h)
+ pushl %ebp
+ pushl %ecx
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+
+ /* save address to result register structure */
+ pushl %edx
+
+ /* just in case, set GDT */
+ lgdt gdtdesc
+
+ /* setup %esi for bioscall registers copy operation */
+ movl %eax, %esi
+
+ /* save the protected mode stack */
+ movl %esp, %eax
+ movl %eax, protstack
+
+ /* copy bios call registers to realmode stack */
+ xorl %eax,%eax
+ movw %ax,%ds
+ movw %ax,%es
+ movl $(GRUB_MEMORY_MACHINE_REAL_STACK-16), %eax
+
+ /* register order: ax, cx, dx, bx, si, di, es, ds */
+ /* note: %esi is loaded above. */
+ movl $16, %ecx
+ movl %eax, %edi
+ cld
+ rep
+ movsb
+
+ /* set up new stack */
+ movl $GRUB_MEMORY_MACHINE_REAL_STACK, %eax
+ movl %eax, %ebp
+
+ /* adjust stack for copied registers */
+ subl $16, %eax
+ movl %eax, %esp
+
+ /* set up segment limits */
+ movw $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* this might be an extra step */
+ /* jump to a 16 bit segment */
+ ljmp $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $grub_bioscall_int10h_tmpcseg
+
+grub_bioscall_int10h_tmpcseg:
+ .code16
+
+ /* clear the PE bit of CR0 */
+ movl %cr0, %eax
+ andl $(~GRUB_MEMORY_MACHINE_CR0_PE_ON), %eax
+ movl %eax, %cr0
+
+ /* flush prefetch queue, reload %cs */
+ DATA32 ljmp $0, $grub_bioscall_int10h_realcseg
+
+grub_bioscall_int10h_realcseg:
+ /* we are in real mode now
+ * set up the real mode segment registers : DS, SS, ES
+ */
+ /* zero %eax */
+ xorl %eax, %eax
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ xorl %edx, %edx
+
+ /* setup registers from stack */
+ popw %ax
+ popw %cx
+ popw %dx
+ popw %bx
+ popw %si
+ popw %di
+ popw %es
+ popw %ds
+
+ /* note: bp should equal to sp */
+
+ /* restore interrupts */
+ sti
+
+ /* call BIOS interrupt service function */
+ int $0x10
+
+ /* disable interrupts */
+ cli
+
+ /* save registers to stack */
+ pushw %ds
+ pushw %es
+ pushw %di
+ pushw %si
+ pushw %bx
+ pushw %dx
+ pushw %cx
+ pushw %ax
+
+ /* just in case... load the GDT register */
+ DATA32 ADDR32 lgdt %cs:gdtdesc
+
+ /* turn on protected mode */
+ movl %cr0, %eax
+ orl $GRUB_MEMORY_MACHINE_CR0_PE_ON, %eax
+ movl %eax, %cr0
+
+ /* jump to relocation, flush prefetch queue, and reload %cs */
+ DATA32 ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $grub_bioscall_int10h_protcseg
+
+ .code32
+grub_bioscall_int10h_protcseg:
+ /* reload other segment registers */
+ movw $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+
+ /* get protected mode stack */
+ movl protstack, %eax
+ movl %eax, %esp
+ movl %eax, %ebp
+
+ /* get result register structure address from stack */
+ popl %edi
+
+ /* register order: ax, cx, dx, bx, si, di, es, ds */
+ /* note: %edi is setup above */
+ movl $16, %ecx
+ movl $(GRUB_MEMORY_MACHINE_REAL_STACK-16), %esi
+ cld
+ rep
+ movsb
+
+ /* zero %eax */
+ xorl %eax, %eax
+
+ /* copy %ax from result registers */
+ movw GRUB_MEMORY_MACHINE_REAL_STACK-16, %ax
+
+ /* Restore modified registers */
+ popl %esi
+ popl %edi
+ popl %ebx
+ popl %ecx
+ popl %ebp
+
+ ret
Index: video/i386/pc/vbebios.c
===================================================================
--- video/i386/pc/vbebios.c (revision 0)
+++ video/i386/pc/vbebios.c (revision 0)
@@ -0,0 +1,251 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 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/machine/bioscall.h>
+#include <grub/machine/vbe.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+
+/* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */
+grub_vbe_status_t
+grub_vbe_bios_get_controller_info (struct grub_vbe_info_block *ci)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t addr = (grub_uint32_t)ci;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.es = addr >> 4;
+ regs.di = addr & 0x0F;
+ regs.ax = 0x4F00;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
+
+/* Call VESA BIOS 0x4f01 to get VBE Mode Information, return status. */
+grub_vbe_status_t
+grub_vbe_bios_get_mode_info (grub_uint32_t mode,
+ struct grub_vbe_mode_info_block *mode_info)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t addr = (grub_uint32_t)mode_info;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.cx = mode & 0xFFFF;
+ regs.es = addr >> 4;
+ regs.di = addr & 0x0F;
+ regs.ax = 0x4F01;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
+
+/* Call VESA BIOS 0x4f02 to set video mode, return status. */
+grub_vbe_status_t
+grub_vbe_bios_set_mode (grub_uint32_t mode,
+ struct grub_vbe_crtc_info_block *crtc_info)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t addr = (grub_uint32_t)crtc_info;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.bx = mode & 0xFFFF;
+ regs.es = addr >> 4;
+ regs.di = addr & 0x0F;
+ regs.ax = 0x4F02;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
+
+/* Call VESA BIOS 0x4f03 to return current VBE Mode, return status. */
+grub_vbe_status_t grub_vbe_bios_get_mode (grub_uint32_t *mode)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t rc;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.ax = 0x4F03;
+
+ rc = grub_bioscall_int10h(®s, &result_regs);
+
+ /* Save results. */
+ *mode = result_regs.bx;
+
+ return rc;
+}
+
+/* Call VESA BIOS 0x4f05 to set memory window, return status. */
+grub_vbe_status_t grub_vbe_bios_set_memory_window (grub_uint32_t window,
+ grub_uint32_t position)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.bx = window & 0x00FF;
+ regs.dx = position & 0xFFFF;
+ regs.ax = 0x4F05;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
+
+/* Call VESA BIOS 0x4f05 to return memory window, return status. */
+grub_vbe_status_t grub_vbe_bios_get_memory_window (grub_uint32_t window,
+ grub_uint32_t *position)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t rc;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.bx = (window & 0x00FF) | 0x0100;
+ regs.ax = 0x4F05;
+
+ rc = grub_bioscall_int10h(®s, &result_regs);
+
+ /* Save results. */
+ *position = result_regs.dx;
+
+ return rc;
+}
+
+/* Call VESA BIOS 0x4f06 to set scanline length (in bytes), return status. */
+grub_vbe_status_t grub_vbe_bios_set_scanline_length (grub_uint32_t length)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.bx = 0x0002;
+ regs.cx = length & 0xFFFF;
+ regs.ax = 0x4F06;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
+
+/* Call VESA BIOS 0x4f06 to return scanline length (in bytes, return status. */
+grub_vbe_status_t grub_vbe_bios_get_scanline_length (grub_uint32_t *length)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t rc;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.bx = 0x0001;
+ regs.ax = 0x4F06;
+
+ rc = grub_bioscall_int10h(®s, &result_regs);
+
+ /* Save results. */
+ *length = result_regs.bx;
+
+ return rc;
+}
+
+/* Call VESA BIOS 0x4f07 to set display start, return status. */
+grub_vbe_status_t grub_vbe_bios_set_display_start (grub_uint32_t x,
+ grub_uint32_t y)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+
+ /* BL = 80h, Set Display Start during Vertical Retrace. */
+ regs.bx = 0x0080;
+ regs.cx = x & 0xFFFF;
+ regs.dx = y & 0xFFFF;
+ regs.ax = 0x4F07;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
+
+/* Call VESA BIOS 0x4f07 to get display start, return status. */
+grub_vbe_status_t grub_vbe_bios_get_display_start (grub_uint32_t *x,
+ grub_uint32_t *y)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t rc;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.bx = 0x0001;
+ regs.ax = 0x4F07;
+
+ rc = grub_bioscall_int10h(®s, &result_regs);
+
+ /* Save results. */
+ *x = result_regs.cx;
+ *y = result_regs.dx;
+
+ return rc;
+}
+
+/* Call VESA BIOS 0x4f09 to set palette data, return status. */
+grub_vbe_status_t grub_vbe_bios_set_palette_data (grub_uint32_t color_count,
+ grub_uint32_t start_index,
+ struct grub_vbe_palette_data *palette_data)
+{
+ struct grub_bioscall_regs regs;
+ struct grub_bioscall_regs result_regs;
+ grub_uint32_t addr = (grub_uint32_t)palette_data;
+
+ /* Zero registers. */
+ grub_memset(®s, 0, sizeof(regs));
+
+ /* Fill registers. */
+ regs.dx = start_index & 0xFFFF;
+ regs.cx = color_count & 0xFFFF;
+ regs.es = addr >> 4;
+ regs.di = addr & 0x0F;
+ regs.ax = 0x4F09;
+
+ return grub_bioscall_int10h(®s, &result_regs);
+}
Property changes on: video/i386/pc/vbebios.c
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Index: include/grub/i386/pc/vbe.h
===================================================================
--- include/grub/i386/pc/vbe.h (revision 1815)
+++ include/grub/i386/pc/vbe.h (working copy)
@@ -151,48 +151,46 @@
grub_uint8_t alignment;
} __attribute__ ((packed));
-/* Prototypes for kernel real mode thunks. */
-
/* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_get_controller_info) (struct grub_vbe_info_block *controller_info);
+grub_vbe_status_t grub_vbe_bios_get_controller_info (struct grub_vbe_info_block *controller_info);
/* Call VESA BIOS 0x4f01 to get VBE Mode Information, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_get_mode_info) (grub_uint32_t mode,
- struct grub_vbe_mode_info_block *mode_info);
+grub_vbe_status_t grub_vbe_bios_get_mode_info (grub_uint32_t mode,
+ struct grub_vbe_mode_info_block *mode_info);
/* Call VESA BIOS 0x4f02 to set video mode, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_set_mode) (grub_uint32_t mode,
- struct grub_vbe_crtc_info_block *crtc_info);
+grub_vbe_status_t grub_vbe_bios_set_mode (grub_uint32_t mode,
+ struct grub_vbe_crtc_info_block *crtc_info);
/* Call VESA BIOS 0x4f03 to return current VBE Mode, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_get_mode) (grub_uint32_t *mode);
+grub_vbe_status_t grub_vbe_bios_get_mode (grub_uint32_t *mode);
/* Call VESA BIOS 0x4f05 to set memory window, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_set_memory_window) (grub_uint32_t window,
- grub_uint32_t position);
+grub_vbe_status_t grub_vbe_bios_set_memory_window (grub_uint32_t window,
+ grub_uint32_t position);
/* Call VESA BIOS 0x4f05 to return memory window, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_get_memory_window) (grub_uint32_t window,
- grub_uint32_t *position);
+grub_vbe_status_t grub_vbe_bios_get_memory_window (grub_uint32_t window,
+ grub_uint32_t *position);
/* Call VESA BIOS 0x4f06 to set scanline length (in bytes), return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_set_scanline_length) (grub_uint32_t length);
+grub_vbe_status_t grub_vbe_bios_set_scanline_length (grub_uint32_t length);
-/* Call VESA BIOS 0x4f06 to return scanline length (in bytes), return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_get_scanline_length) (grub_uint32_t *length);
+/* Call VESA BIOS 0x4f06 to return scanline length (in bytes, return status. */
+grub_vbe_status_t grub_vbe_bios_get_scanline_length (grub_uint32_t *length);
/* Call VESA BIOS 0x4f07 to set display start, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_set_display_start) (grub_uint32_t x,
- grub_uint32_t y);
+grub_vbe_status_t grub_vbe_bios_set_display_start (grub_uint32_t x,
+ grub_uint32_t y);
/* Call VESA BIOS 0x4f07 to get display start, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_get_display_start) (grub_uint32_t *x,
- grub_uint32_t *y);
+grub_vbe_status_t grub_vbe_bios_get_display_start (grub_uint32_t *x,
+ grub_uint32_t *y);
/* Call VESA BIOS 0x4f09 to set palette data, return status. */
-grub_vbe_status_t EXPORT_FUNC(grub_vbe_bios_set_palette_data) (grub_uint32_t color_count,
- grub_uint32_t start_index,
- struct grub_vbe_palette_data *palette_data);
+grub_vbe_status_t grub_vbe_bios_set_palette_data (grub_uint32_t color_count,
+ grub_uint32_t start_index,
+ struct grub_vbe_palette_data *palette_data);
/* Prototypes for helper functions. */
Index: include/grub/i386/pc/bioscall.h
===================================================================
--- include/grub/i386/pc/bioscall.h (revision 0)
+++ include/grub/i386/pc/bioscall.h (revision 0)
@@ -0,0 +1,44 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 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/>.
+ */
+
+#ifndef GRUB_BIOSCALL_MACHINE_HEADER
+#define GRUB_BIOSCALL_MACHINE_HEADER 1
+
+#include <grub/symbol.h>
+#include <grub/types.h>
+
+/* Registers for real mode BIOS call. */
+struct grub_bioscall_regs
+{
+ grub_uint16_t ax;
+ grub_uint16_t cx;
+ grub_uint16_t dx;
+ grub_uint16_t bx;
+
+ grub_uint16_t si;
+ grub_uint16_t di;
+
+ grub_uint16_t es;
+ grub_uint16_t ds;
+} __attribute__ ((packed));
+
+/* Prototypes for kernel real mode helpers. */
+grub_uint32_t EXPORT_FUNC(grub_bioscall_int10h) (struct grub_bioscall_regs *regs,
+ struct grub_bioscall_regs *result_regs);
+
+#endif /* ! GRUB_BIOSCALL_MACHINE_HEADER */
Property changes on: include/grub/i386/pc/bioscall.h
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Index: ChangeLog
===================================================================
--- ChangeLog (revision 1815)
+++ ChangeLog (working copy)
@@ -1,3 +1,40 @@
+2008-08-17 Vesa Jääskeläinen <chaac@nic.fi>
+
+ * conf/i386-pc.rmk (kernel_img_HEADERS): Add machine/bioscall.h.
+ (vbe_mod_SOURCES): Add video/i386/pc/vbebios.c.
+
+ * include/grub/i386/pc/bioscall.h: New file.
+
+ * include/grub/i386/pc/vbe.h (grub_vbe_bios_get_controller_info):
+ Moved from kernel to vbe.mod.
+ (grub_vbe_bios_get_mode_info): Likewise.
+ (grub_vbe_bios_set_mode): Likewise.
+ (grub_vbe_bios_get_mode): Likewise.
+ (grub_vbe_bios_set_memory_window): Likewise.
+ (grub_vbe_bios_get_memory_window): Likewise.
+ (grub_vbe_bios_set_scanline_length): Likewise.
+ (grub_vbe_bios_get_scanline_length): Likewise.
+ (grub_vbe_bios_set_display_start): Likewise.
+ (grub_vbe_bios_get_display_start): Likewise.
+ (grub_vbe_bios_set_palette_data): Likewise.
+
+ * kern/i386/pc/startup.s (grub_vbe_bios_get_controller_info):
+ Removed.
+ (grub_vbe_bios_get_controller_info): Likewise.
+ (grub_vbe_bios_get_mode_info): Likewise.
+ (grub_vbe_bios_set_mode): Likewise.
+ (grub_vbe_bios_get_mode): Likewise.
+ (grub_vbe_bios_set_memory_window): Likewise.
+ (grub_vbe_bios_get_memory_window): Likewise.
+ (grub_vbe_bios_set_scanline_length): Likewise.
+ (grub_vbe_bios_get_scanline_length): Likewise.
+ (grub_vbe_bios_set_display_start): Likewise.
+ (grub_vbe_bios_get_display_start): Likewise.
+ (grub_vbe_bios_set_palette_data): Likewise.
+ (grub_bioscall_int10h): New function.
+
+ * video/i386/pc/vbebios.c: New file.
+
2008-08-17 Carles Pina i Estany <carles@pina.cat>
* menu/normal.c (run_menu): Add Home and End keys in grub-menu.
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH] Move assembly code out of the kernel
2008-08-17 11:31 ` Vesa Jääskeläinen
@ 2008-08-17 16:55 ` Bean
2008-08-24 9:37 ` Vesa Jääskeläinen
0 siblings, 1 reply; 8+ messages in thread
From: Bean @ 2008-08-17 16:55 UTC (permalink / raw)
To: The development of GRUB 2
On Sun, Aug 17, 2008 at 7:31 PM, Vesa Jääskeläinen <chaac@nic.fi> wrote:
> chaac@nic.fi wrote:
>> I have made generic function that does basically the same thing for bios
>> service 0x10 (video). In that modification you prepare registers
>> structure that will be configured during real mode switching. I am yet
>> to commit it for review, but I think it would be more generic way to do
>> this. When I come back from my holiday I will commit the code for review.
>>
>> So please wait a bit before committing this :)
Hi,
I'm thinking perhaps we can make it more generic and support multiple
INT in one function. One way to do this is not use INT, but retrieve
the address from IVT and do a far call. On return, pop the flag on
stack. This would also make it easily to support far call, In that
case, we just don't pop the flag.
I also notice that you do the protected/real mode switch in
grub_bioscall_int10h, why not use the prot_to_real and real_to_prot ?
If you need to keep the value of %eax, you can store it in a structure
and pass it using other register like %ecx.
--
Bean
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] Move assembly code out of the kernel
2008-08-17 16:55 ` Bean
@ 2008-08-24 9:37 ` Vesa Jääskeläinen
0 siblings, 0 replies; 8+ messages in thread
From: Vesa Jääskeläinen @ 2008-08-24 9:37 UTC (permalink / raw)
To: The development of GRUB 2
Bean wrote:
> On Sun, Aug 17, 2008 at 7:31 PM, Vesa Jääskeläinen <chaac@nic.fi> wrote:
>> chaac@nic.fi wrote:
>>> I have made generic function that does basically the same thing for bios
>>> service 0x10 (video). In that modification you prepare registers
>>> structure that will be configured during real mode switching. I am yet
>>> to commit it for review, but I think it would be more generic way to do
>>> this. When I come back from my holiday I will commit the code for review.
>>>
>>> So please wait a bit before committing this :)
>
> Hi,
>
> I'm thinking perhaps we can make it more generic and support multiple
> INT in one function. One way to do this is not use INT, but retrieve
> the address from IVT and do a far call. On return, pop the flag on
> stack. This would also make it easily to support far call, In that
> case, we just don't pop the flag.
Hmm. I have faint memory that this has been done previously somewhere
:)... need to think this a bit more.
> I also notice that you do the protected/real mode switch in
> grub_bioscall_int10h, why not use the prot_to_real and real_to_prot ?
> If you need to keep the value of %eax, you can store it in a structure
> and pass it using other register like %ecx.
I wanted it to be easily manageable so I can alter everything in order
to get it working at the first :)
But basically the points were:
- I needed to put registers to the stack and thus need to differentiate
esp and ebp.
- When returning I need to store those registers to structure provided
by caller.
- There was no need to "corrupt" stack area with return address, and I
needed stack for different purpose
Thanks,
Vesa Jääskeläinen
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH] Move assembly code out of the kernel
@ 2008-07-30 21:20 Bean
2008-07-31 7:46 ` Bean
0 siblings, 1 reply; 8+ messages in thread
From: Bean @ 2008-07-30 21:20 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 2228 bytes --]
Hi,
Currently, most assembly code are in startup.S. This is normally used
to ensure that the function address are below 1m, which is required if
it would switch to real mode and call bios services. However, this
make the kernel larger. For example, the biosdisk functions are only
used by biodisk module, they should not be placed inside the kernel.
This patch support splitting such code from startup.S. For example, we
create a new module biosdisk_stub.mod for assembly code of biosdisk.
Instead of call prot_to_real and real_to_prot, we call
grub_call_real_stub to enter real mode. grub_call_real_stub would copy
the code to real mode and do the mode switch.
To avoid unnecessary memory transfer, grub_call_real_stub would not
erase the real mode stub when it's done, so that it can be used
directly next time. When the stub area is full, it zero it out and
start anew. The area uses a simple verification method so that the old
mapping is invalidated, the code would need be copied again on their
next use.
The patch shows how to do it for the biosdisk module, here is the new
grub_biosdisk_rw_int13_extensions function.
REAL_STUB_START(grub_biosdisk_rw_int13_extensions)
movb %dh, %ah
movw %cx, %ds
int $0x13 /* do the operation */
movb %ah, %dl /* save return value */
lret
REAL_STUB_END(grub_biosdisk_rw_int13_extensions)
FUNCTION(grub_biosdisk_rw_int13_extensions)
pushl %ebp
pushl %esi
/* compute the address of disk_address_packet */
movw %cx, %si
xorw %cx, %cx
shrl $4, %ecx /* save the segment to cx */
/* ah */
movb %al, %dh
leal grub_biosdisk_rw_int13_extensions_stub, %eax
call EXT_C(grub_call_real_stub)
movb %dl, %al /* return value in %eax */
popl %esi
popl %ebp
ret
Real mode code is enclosed between REAL_STUB_START and REAL_STUB_END,
no need to use .code16 and .code32 as it's handled by the macro. In
the main function, use
leal grub_biosdisk_rw_int13_extensions_stub, %eax
call EXT_C(grub_call_real_stub)
to invoke grub_call_real_stub. grub_biosdisk_rw_int13_extensions_stub
is defined in the REAL_STUB_START macro.
This same method can be applied to loaders, vbe, etc. In fact, almost
all function behind grub_call_real_stub can be moved out of startup.S.
--
Bean
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: stub.diff --]
[-- Type: text/x-diff; name=stub.diff, Size: 21852 bytes --]
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 59fc6a3..fd58e17 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -154,7 +154,7 @@ grub_install_SOURCES = util/i386/pc/grub-install.in
grub_mkrescue_SOURCES = util/i386/pc/grub-mkrescue.in
# Modules.
-pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
+pkglib_MODULES = biosdisk.mod biosdisk_stub.mod _chain.mod _linux.mod linux.mod normal.mod \
_multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \
vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod \
@@ -166,6 +166,16 @@ biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
biosdisk_mod_CFLAGS = $(COMMON_CFLAGS)
biosdisk_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For biosdisk_stub.mod.
+biosdisk_stub_mod_SOURCES = disk/i386/pc/biosdisk_stub.S
+biosdisk_stub_mod_CFLAGS = $(COMMON_CFLAGS)
+biosdisk_stub_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For biosdiskstub.mod.
+biosdiskstub_mod_SOURCES = disk/i386/pc/biosdisk.c
+biosdiskstub_mod_CFLAGS = $(COMMON_CFLAGS)
+biosdisk_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
# For _chain.mod.
_chain_mod_SOURCES = loader/i386/pc/chainloader.c
_chain_mod_CFLAGS = $(COMMON_CFLAGS)
diff --git a/disk/i386/pc/biosdisk_stub.S b/disk/i386/pc/biosdisk_stub.S
new file mode 100755
index 0000000..05536c6
--- /dev/null
+++ b/disk/i386/pc/biosdisk_stub.S
@@ -0,0 +1,344 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 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/>.
+ */
+
+#define ASM_FILE 1
+
+#include <grub/symbol.h>
+#include <grub/machine/memory.h>
+
+/*
+ * int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
+ *
+ * Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
+ * is passed for disk address packet. If an error occurs, return
+ * non-zero, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_rw_int13_extensions)
+ movb %dh, %ah
+ movw %cx, %ds
+ int $0x13 /* do the operation */
+ movb %ah, %dl /* save return value */
+ lret
+REAL_STUB_END(grub_biosdisk_rw_int13_extensions)
+
+FUNCTION(grub_biosdisk_rw_int13_extensions)
+ pushl %ebp
+ pushl %esi
+
+ /* compute the address of disk_address_packet */
+ movw %cx, %si
+ xorw %cx, %cx
+ shrl $4, %ecx /* save the segment to cx */
+
+ /* ah */
+ movb %al, %dh
+
+ leal grub_biosdisk_rw_int13_extensions_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %dl, %al /* return value in %eax */
+
+ popl %esi
+ popl %ebp
+
+ ret
+
+/*
+ * int grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
+ * int soff, int nsec, int segment)
+ *
+ * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
+ * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
+ * return non-zero, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_rw_standard)
+ movw %bx, %es
+ xorw %bx, %bx
+ movw $3, %si /* attempt at least three times */
+
+1:
+ movw %di, %ax
+ int $0x13 /* do the operation */
+ jnc 2f /* check if successful */
+
+ movb %ah, %bl /* save return value */
+ /* if fail, reset the disk system */
+ xorw %ax, %ax
+ int $0x13
+
+ decw %si
+ cmpw $0, %si
+ je 2f
+ xorb %bl, %bl
+ jmp 1b /* retry */
+2:
+ lret
+REAL_STUB_END(grub_biosdisk_rw_standard)
+
+FUNCTION(grub_biosdisk_rw_standard)
+ pushl %ebp
+ movl %esp, %ebp
+
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+
+ /* set up CHS information */
+
+ /* set %ch to low eight bits of cylinder */
+ xchgb %cl, %ch
+ /* set bits 6-7 of %cl to high two bits of cylinder */
+ shlb $6, %cl
+ /* set bits 0-5 of %cl to sector */
+ addb 0xc(%ebp), %cl
+ /* set %dh to head */
+ movb 0x8(%ebp), %dh
+ /* set %ah to AH */
+ movb %al, %ah
+ /* set %al to NSEC */
+ movb 0x10(%ebp), %al
+ /* save %ax in %di */
+ movw %ax, %di
+ /* save SEGMENT in %bx */
+ movw 0x14(%ebp), %bx
+
+ leal grub_biosdisk_rw_standard_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %bl, %al /* return value in %eax */
+
+ popl %esi
+ popl %edi
+ popl %ebx
+ popl %ebp
+
+ ret $(4 * 4)
+
+
+/*
+ * int grub_biosdisk_check_int13_extensions (int drive)
+ *
+ * Check if LBA is supported for DRIVE. If it is supported, then return
+ * the major version of extensions, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_check_int13_extensions)
+ movb $0x41, %ah
+ movw $0x55aa, %bx
+ int $0x13 /* do the operation */
+
+ /* check the result */
+ jc 1f
+ cmpw $0xaa55, %bx
+ jne 1f
+
+ movb %ah, %bl /* save the major version into %bl */
+
+ /* check if AH=0x42 is supported */
+ andw $1, %cx
+ jnz 2f
+
+1:
+ xorb %bl, %bl
+2:
+ lret
+REAL_STUB_END(grub_biosdisk_check_int13_extensions)
+
+FUNCTION(grub_biosdisk_check_int13_extensions)
+ pushl %ebp
+ pushl %ebx
+
+ /* drive */
+ movb %al, %dl
+
+ leal grub_biosdisk_check_int13_extensions_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %bl, %al /* return value in %eax */
+
+ popl %ebx
+ popl %ebp
+
+ ret
+
+
+/*
+ * int grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
+ *
+ * Return the cdrom information of DRIVE in CDRP. If an error occurs,
+ * then return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_get_cdinfo_int13_extensions)
+ movw $0x4B01, %cx
+ jmp 1f
+
+/*
+ * int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
+ *
+ * Return the geometry of DRIVE in a drive parameters, DRP. If an error
+ * occurs, then return non-zero, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_get_diskinfo_int13_extensions)
+ movw %cx, %ax
+ movw %bx, %ds
+ int $0x13 /* do the operation */
+ movb %ah, %bl /* save return value in %bl */
+ lret
+REAL_STUB_END(grub_biosdisk_get_diskinfo_int13_extensions)
+
+FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
+ movb $0x48, %ch
+1:
+ pushl %ebp
+ pushl %ebx
+ pushl %esi
+
+ /* compute the address of drive parameters */
+ movw %dx, %si
+ andl $0xf, %esi
+ shrl $4, %edx
+ movw %dx, %bx /* save the segment into %bx */
+ /* drive */
+ movb %al, %dl
+
+ leal grub_biosdisk_get_diskinfo_int13_extensions_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %bl, %al /* return value in %eax */
+
+ popl %esi
+ popl %ebx
+ popl %ebp
+
+ ret
+
+
+/*
+ * int grub_biosdisk_get_diskinfo_standard (int drive,
+ * unsigned long *cylinders,
+ * unsigned long *heads,
+ * unsigned long *sectors)
+ *
+ * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
+ * error occurs, then return non-zero, otherwise zero.
+ */
+REAL_STUB_START(grub_biosdisk_get_diskinfo_standard)
+ movb $0x8, %ah
+ int $0x13 /* do the operation */
+ /* check if successful */
+ testb %ah, %ah
+ jnz 1f
+ /* bogus BIOSes may not return an error number */
+ testb $0x3f, %cl /* 0 sectors means no disk */
+ jnz 1f /* if non-zero, then succeed */
+ /* XXX 0x60 is one of the unused error numbers */
+ movb $0x60, %ah
+1:
+ movb %ah, %bl /* save return value in %bl */
+ lret
+REAL_STUB_END(grub_biosdisk_get_diskinfo_standard)
+
+FUNCTION(grub_biosdisk_get_diskinfo_standard)
+ pushl %ebp
+ pushl %ebx
+ pushl %edi
+
+ /* push CYLINDERS */
+ pushl %edx
+ /* push HEADS */
+ pushl %ecx
+ /* SECTORS is on the stack */
+
+ /* drive */
+ movb %al, %dl
+
+ leal grub_biosdisk_get_diskinfo_standard_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ /* pop HEADS */
+ popl %edi
+ movb %dh, %al
+ incl %eax /* the number of heads is counted from zero */
+ movl %eax, (%edi)
+
+ /* pop CYLINDERS */
+ popl %edi
+ movb %ch, %al
+ movb %cl, %ah
+ shrb $6, %ah /* the number of cylinders is counted from zero */
+ incl %eax
+ movl %eax, (%edi)
+
+ /* SECTORS */
+ movl 0x10(%esp), %edi
+ andb $0x3f, %cl
+ movzbl %cl, %eax
+ movl %eax, (%edi)
+
+ xorl %eax, %eax
+ movb %bl, %al /* return value in %eax */
+
+ popl %edi
+ popl %ebx
+ popl %ebp
+
+ ret $4
+
+
+/*
+ * int grub_biosdisk_get_num_floppies (void)
+ */
+
+REAL_STUB_START(grub_biosdisk_get_num_floppies)
+ /* reset the disk system first */
+ int $0x13
+1:
+ stc
+
+ /* call GET DISK TYPE */
+ movb $0x15, %ah
+ int $0x13
+
+ jc 2f
+
+ /* check if this drive exists */
+ testb $0x3, %ah
+ jz 2f
+
+ incb %dl
+ cmpb $2, %dl
+ jne 1b
+2:
+ lret
+REAL_STUB_END(grub_biosdisk_get_num_floppies)
+
+FUNCTION(grub_biosdisk_get_num_floppies)
+ pushl %ebp
+
+ xorl %edx, %edx
+
+ leal grub_biosdisk_get_num_floppies_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movl %edx, %eax
+ popl %ebp
+ ret
diff --git a/include/grub/i386/pc/biosdisk.h b/include/grub/i386/pc/biosdisk.h
index 05d5dc5..da82116 100644
--- a/include/grub/i386/pc/biosdisk.h
+++ b/include/grub/i386/pc/biosdisk.h
@@ -106,19 +106,19 @@ struct grub_biosdisk_dap
grub_uint64_t block;
} __attribute__ ((packed));
-int EXPORT_FUNC(grub_biosdisk_rw_int13_extensions) (int ah, int drive, void *dap);
-int EXPORT_FUNC(grub_biosdisk_rw_standard) (int ah, int drive, int coff, int hoff,
+int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap);
+int grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
int soff, int nsec, int segment);
-int EXPORT_FUNC(grub_biosdisk_check_int13_extensions) (int drive);
-int EXPORT_FUNC(grub_biosdisk_get_diskinfo_int13_extensions) (int drive,
+int grub_biosdisk_check_int13_extensions (int drive);
+int grub_biosdisk_get_diskinfo_int13_extensions (int drive,
void *drp);
-int EXPORT_FUNC(grub_biosdisk_get_cdinfo_int13_extensions) (int drive,
+int grub_biosdisk_get_cdinfo_int13_extensions (int drive,
void *cdrp);
-int EXPORT_FUNC(grub_biosdisk_get_diskinfo_standard) (int drive,
+int grub_biosdisk_get_diskinfo_standard (int drive,
unsigned long *cylinders,
unsigned long *heads,
unsigned long *sectors);
-int EXPORT_FUNC(grub_biosdisk_get_num_floppies) (void);
+int grub_biosdisk_get_num_floppies (void);
void grub_biosdisk_init (void);
void grub_biosdisk_fini (void);
diff --git a/include/grub/i386/pc/kernel.h b/include/grub/i386/pc/kernel.h
index 402faa3..3c205ac 100644
--- a/include/grub/i386/pc/kernel.h
+++ b/include/grub/i386/pc/kernel.h
@@ -86,6 +86,8 @@ extern grub_addr_t grub_end_addr;
extern grub_addr_t EXPORT_FUNC(grub_arch_memdisk_addr) (void);
extern grub_off_t EXPORT_FUNC(grub_arch_memdisk_size) (void);
+void EXPORT_FUNC(grub_call_real_stub) (void);
+
#endif /* ! ASM_FILE */
#endif /* ! KERNEL_MACHINE_HEADER */
diff --git a/include/grub/i386/pc/memory.h b/include/grub/i386/pc/memory.h
index a3e3ed7..55d5dcb 100644
--- a/include/grub/i386/pc/memory.h
+++ b/include/grub/i386/pc/memory.h
@@ -45,12 +45,17 @@
(GRUB_MEMORY_MACHINE_SCRATCH_ADDR + GRUB_MEMORY_MACHINE_SCRATCH_SIZE \
+ GRUB_MEMORY_MACHINE_PROT_STACK_SIZE - 0x10)
+#define GRUB_MEMORY_MACHINE_REAL_STUB_START (GRUB_MEMORY_MACHINE_PROT_STACK + 0x10)
+#define GRUB_MEMORY_MACHINE_REAL_STUB_SIZE 0x8000
+#define GRUB_MEMORY_MACHINE_REAL_STUB_END \
+ (GRUB_MEMORY_MACHINE_REAL_STUB_START + GRUB_MEMORY_MACHINE_REAL_STUB_SIZE)
+
/* The memory area where GRUB uses its own purpose. This part is not added
into free memory for dynamic allocations. */
#define GRUB_MEMORY_MACHINE_RESERVED_START \
GRUB_MEMORY_MACHINE_SCRATCH_ADDR
#define GRUB_MEMORY_MACHINE_RESERVED_END \
- (GRUB_MEMORY_MACHINE_PROT_STACK + 0x10)
+ GRUB_MEMORY_MACHINE_REAL_STUB_END
/* The area where GRUB is decompressed at early startup. */
#define GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR 0x100000
diff --git a/include/grub/symbol.h b/include/grub/symbol.h
index e951490..6fd161a 100644
--- a/include/grub/symbol.h
+++ b/include/grub/symbol.h
@@ -43,4 +43,14 @@
# define EXPORT_VAR(x) x
#endif /* ! GRUB_SYMBOL_GENERATOR */
+#define REAL_STUB_START(x) x ## _stub: ; \
+ .long x ## _start ; \
+ .long x ## _end - x ## _start ; \
+ .long 0 ; \
+x ## _start: ; \
+ .code16
+
+#define REAL_STUB_END(x) .code32 ; \
+x ## _end:
+
#endif /* ! GRUB_SYMBOL_HEADER */
diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S
index 679ad1f..5b1a2a1 100644
--- a/kern/i386/pc/startup.S
+++ b/kern/i386/pc/startup.S
@@ -618,357 +618,134 @@ FUNCTION(grub_halt)
jmp EXT_C(grub_hard_stop)
.code32
+real_stub_addr:
+ .long GRUB_MEMORY_MACHINE_REAL_STUB_START
/*
- * void grub_chainloader_real_boot (int drive, void *part_addr)
+ * Copy code to address below 1M, switch to real mode and execute.
*
- * This starts another boot loader.
- */
-
-FUNCTION(grub_chainloader_real_boot)
- pushl %edx
- pushl %eax
-
- call EXT_C(grub_dl_unload_all)
-
- /* Turn off Gate A20 */
- xorl %eax, %eax
- call EXT_C(grub_gate_a20)
-
- /* set up to pass boot drive */
- popl %edx
-
- /* ESI must point to a partition table entry */
- popl %esi
-
- call prot_to_real
- .code16
- ljmp $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
- .code32
-
-#include "../loader.S"
-
-/*
- * int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
+ * On entry, %eax should points to a structure that specifies the code to be
+ * copied:
+ *
+ * offset 0: address of code
+ * offset 4: size of code
+ * offset 8: mapped address
+ *
+ * To avoid unnecessary memory transfer, it replaces the code address with
+ * mapped address, so that it can be used directly the next time.
+ *
+ * The real mode stub also holds the address of the structure directly before
+ * its code. This is used for verification. When the stub area is full, we
+ * zero it and start again. This would invalidate all mapped block so that
+ * they must be copied again when used.
*
- * Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
- * is passed for disk address packet. If an error occurs, return
- * non-zero, otherwise zero.
*/
-FUNCTION(grub_biosdisk_rw_int13_extensions)
- pushl %ebp
- pushl %esi
-
- /* compute the address of disk_address_packet */
- movw %cx, %si
- xorw %cx, %cx
- shrl $4, %ecx /* save the segment to cx */
-
- /* ah */
- movb %al, %dh
- /* enter real mode */
- call prot_to_real
+FUNCTION(grub_call_real_stub)
+ pushl %edi
- .code16
- movb %dh, %ah
- movw %cx, %ds
- int $0x13 /* do the operation */
- movb %ah, %dl /* save return value */
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
+ /* Test if we need to map it. */
+ movl (%eax), %edi
+ cmpl $GRUB_MEMORY_MACHINE_UPPER, %edi
+ jb 3f
- movb %dl, %al /* return value in %eax */
+ /* Test if we already map it. */
+ movl 8(%eax), %edi
+ orl %edi, %edi
+ jz 1f
- popl %esi
- popl %ebp
+ /* Test if the mapped block is valid. */
+ cmpl %eax, -4(%edi)
+ jz 3f
- ret
+1:
+ pushl %esi
+ pushl %ecx
-/*
- * int grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
- * int soff, int nsec, int segment)
- *
- * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
- * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
- * return non-zero, otherwise zero.
- */
+ cld
-FUNCTION(grub_biosdisk_rw_standard)
- pushl %ebp
- movl %esp, %ebp
+ movl 4(%eax), %ecx
+ movl real_stub_addr, %edi
+ leal 4(%edi, %ecx), %esi
+ cmpl $GRUB_MEMORY_MACHINE_REAL_STUB_END, %esi
+ jbe 2f
- pushl %ebx
+ /* The stub area is full, zero it and start again. */
+ pushl %ecx
+ movl $(GRUB_MEMORY_MACHINE_REAL_STUB_SIZE >> 2), %ecx
+ movl $GRUB_MEMORY_MACHINE_REAL_STUB_START, %edi
pushl %edi
- pushl %esi
+ pushl %eax
- /* set up CHS information */
-
- /* set %ch to low eight bits of cylinder */
- xchgb %cl, %ch
- /* set bits 6-7 of %cl to high two bits of cylinder */
- shlb $6, %cl
- /* set bits 0-5 of %cl to sector */
- addb 0xc(%ebp), %cl
- /* set %dh to head */
- movb 0x8(%ebp), %dh
- /* set %ah to AH */
- movb %al, %ah
- /* set %al to NSEC */
- movb 0x10(%ebp), %al
- /* save %ax in %di */
- movw %ax, %di
- /* save SEGMENT in %bx */
- movw 0x14(%ebp), %bx
-
- /* enter real mode */
- call prot_to_real
+ xorl %eax, %eax
+ rep stosl
- .code16
- movw %bx, %es
- xorw %bx, %bx
- movw $3, %si /* attempt at least three times */
+ popl %edi
+ popl %eax
+ popl %ecx
-1:
- movw %di, %ax
- int $0x13 /* do the operation */
- jnc 2f /* check if successful */
+2:
+ stosl
- movb %ah, %bl /* save return value */
- /* if fail, reset the disk system */
- xorw %ax, %ax
- int $0x13
+ pushl %edi
+ movl %edi, 8(%eax)
- decw %si
- cmpw $0, %si
- je 2f
- xorb %bl, %bl
- jmp 1b /* retry */
-2:
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
+ movl (%eax), %esi
+ rep movsb
- movb %bl, %al /* return value in %eax */
+ movl %edi, real_stub_addr
- popl %esi
popl %edi
- popl %ebx
- popl %ebp
- ret $(4 * 4)
+ popl %ecx
+ popl %esi
+3:
-/*
- * int grub_biosdisk_check_int13_extensions (int drive)
- *
- * Check if LBA is supported for DRIVE. If it is supported, then return
- * the major version of extensions, otherwise zero.
- */
+ shll $12, %edi
+ shrw $12, %di
-FUNCTION(grub_biosdisk_check_int13_extensions)
- pushl %ebp
- pushl %ebx
+ movl %edi, GRUB_MEMORY_MACHINE_REAL_STACK + 0x10
+ popl %edi
- /* drive */
- movb %al, %dl
- /* enter real mode */
call prot_to_real
-
.code16
- movb $0x41, %ah
- movw $0x55aa, %bx
- int $0x13 /* do the operation */
- /* check the result */
- jc 1f
- cmpw $0xaa55, %bx
- jne 1f
+ lcall *(GRUB_MEMORY_MACHINE_REAL_STACK + 0x10)
- movb %ah, %bl /* save the major version into %bl */
-
- /* check if AH=0x42 is supported */
- andw $1, %cx
- jnz 2f
-
-1:
- xorb %bl, %bl
-2:
- /* back to protected mode */
DATA32 call real_to_prot
.code32
- movb %bl, %al /* return value in %eax */
-
- popl %ebx
- popl %ebp
-
ret
-
-/*
- * int grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
- *
- * Return the cdrom information of DRIVE in CDRP. If an error occurs,
- * then return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_get_cdinfo_int13_extensions)
- movw $0x4B01, %cx
- jmp 1f
-
/*
- * int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
- *
- * Return the geometry of DRIVE in a drive parameters, DRP. If an error
- * occurs, then return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
- movb $0x48, %ch
-1:
- pushl %ebp
- pushl %ebx
- pushl %esi
-
- /* compute the address of drive parameters */
- movw %dx, %si
- andl $0xf, %esi
- shrl $4, %edx
- movw %dx, %bx /* save the segment into %bx */
- /* drive */
- movb %al, %dl
- /* enter real mode */
- call prot_to_real
-
- .code16
- movw %cx, %ax
- movw %bx, %ds
- int $0x13 /* do the operation */
- movb %ah, %bl /* save return value in %bl */
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
-
- movb %bl, %al /* return value in %eax */
-
- popl %esi
- popl %ebx
- popl %ebp
-
- ret
-
-
-/*
- * int grub_biosdisk_get_diskinfo_standard (int drive,
- * unsigned long *cylinders,
- * unsigned long *heads,
- * unsigned long *sectors)
+ * void grub_chainloader_real_boot (int drive, void *part_addr)
*
- * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
- * error occurs, then return non-zero, otherwise zero.
+ * This starts another boot loader.
*/
-FUNCTION(grub_biosdisk_get_diskinfo_standard)
- pushl %ebp
- pushl %ebx
- pushl %edi
-
- /* push CYLINDERS */
+FUNCTION(grub_chainloader_real_boot)
pushl %edx
- /* push HEADS */
- pushl %ecx
- /* SECTORS is on the stack */
-
- /* drive */
- movb %al, %dl
- /* enter real mode */
- call prot_to_real
-
- .code16
- movb $0x8, %ah
- int $0x13 /* do the operation */
- /* check if successful */
- testb %ah, %ah
- jnz 1f
- /* bogus BIOSes may not return an error number */
- testb $0x3f, %cl /* 0 sectors means no disk */
- jnz 1f /* if non-zero, then succeed */
- /* XXX 0x60 is one of the unused error numbers */
- movb $0x60, %ah
-1:
- movb %ah, %bl /* save return value in %bl */
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
-
- /* pop HEADS */
- popl %edi
- movb %dh, %al
- incl %eax /* the number of heads is counted from zero */
- movl %eax, (%edi)
-
- /* pop CYLINDERS */
- popl %edi
- movb %ch, %al
- movb %cl, %ah
- shrb $6, %ah /* the number of cylinders is counted from zero */
- incl %eax
- movl %eax, (%edi)
+ pushl %eax
- /* SECTORS */
- movl 0x10(%esp), %edi
- andb $0x3f, %cl
- movzbl %cl, %eax
- movl %eax, (%edi)
+ call EXT_C(grub_dl_unload_all)
+ /* Turn off Gate A20 */
xorl %eax, %eax
- movb %bl, %al /* return value in %eax */
-
- popl %edi
- popl %ebx
- popl %ebp
-
- ret $4
+ call EXT_C(grub_gate_a20)
+ /* set up to pass boot drive */
+ popl %edx
-/*
- * int grub_biosdisk_get_num_floppies (void)
- */
-FUNCTION(grub_biosdisk_get_num_floppies)
- pushl %ebp
+ /* ESI must point to a partition table entry */
+ popl %esi
- xorl %edx, %edx
call prot_to_real
-
.code16
- /* reset the disk system first */
- int $0x13
-1:
- stc
-
- /* call GET DISK TYPE */
- movb $0x15, %ah
- int $0x13
-
- jc 2f
-
- /* check if this drive exists */
- testb $0x3, %ah
- jz 2f
-
- incb %dl
- cmpb $2, %dl
- jne 1b
-2:
- DATA32 call real_to_prot
+ ljmp $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
.code32
- movl %edx, %eax
- popl %ebp
- ret
-
+#include "../loader.S"
/*
*
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] Move assembly code out of the kernel
2008-07-30 21:20 Bean
@ 2008-07-31 7:46 ` Bean
2008-07-31 11:22 ` Robert Millan
0 siblings, 1 reply; 8+ messages in thread
From: Bean @ 2008-07-31 7:46 UTC (permalink / raw)
To: The development of GRUB 2
[-- Attachment #1: Type: text/plain, Size: 5328 bytes --]
On Thu, Jul 31, 2008 at 5:20 AM, Bean <bean123ch@gmail.com> wrote:
> Hi,
>
> Currently, most assembly code are in startup.S. This is normally used
> to ensure that the function address are below 1m, which is required if
> it would switch to real mode and call bios services. However, this
> make the kernel larger. For example, the biosdisk functions are only
> used by biodisk module, they should not be placed inside the kernel.
>
> This patch support splitting such code from startup.S. For example, we
> create a new module biosdisk_stub.mod for assembly code of biosdisk.
> Instead of call prot_to_real and real_to_prot, we call
> grub_call_real_stub to enter real mode. grub_call_real_stub would copy
> the code to real mode and do the mode switch.
>
> To avoid unnecessary memory transfer, grub_call_real_stub would not
> erase the real mode stub when it's done, so that it can be used
> directly next time. When the stub area is full, it zero it out and
> start anew. The area uses a simple verification method so that the old
> mapping is invalidated, the code would need be copied again on their
> next use.
>
> The patch shows how to do it for the biosdisk module, here is the new
> grub_biosdisk_rw_int13_extensions function.
>
> REAL_STUB_START(grub_biosdisk_rw_int13_extensions)
> movb %dh, %ah
> movw %cx, %ds
> int $0x13 /* do the operation */
> movb %ah, %dl /* save return value */
> lret
> REAL_STUB_END(grub_biosdisk_rw_int13_extensions)
>
> FUNCTION(grub_biosdisk_rw_int13_extensions)
> pushl %ebp
> pushl %esi
>
> /* compute the address of disk_address_packet */
> movw %cx, %si
> xorw %cx, %cx
> shrl $4, %ecx /* save the segment to cx */
>
> /* ah */
> movb %al, %dh
>
> leal grub_biosdisk_rw_int13_extensions_stub, %eax
> call EXT_C(grub_call_real_stub)
>
> movb %dl, %al /* return value in %eax */
>
> popl %esi
> popl %ebp
>
> ret
>
> Real mode code is enclosed between REAL_STUB_START and REAL_STUB_END,
> no need to use .code16 and .code32 as it's handled by the macro. In
> the main function, use
>
> leal grub_biosdisk_rw_int13_extensions_stub, %eax
> call EXT_C(grub_call_real_stub)
>
> to invoke grub_call_real_stub. grub_biosdisk_rw_int13_extensions_stub
> is defined in the REAL_STUB_START macro.
>
> This same method can be applied to loaders, vbe, etc. In fact, almost
> all function behind grub_call_real_stub can be moved out of startup.S.
Hi,
I make some adjustment for the patch. First, I don't try to remap
blocks anymore, it's not so reliable. and anyway, the real stub area
is 0x8000 long, which is more than enough for the assembly code. If
the area would ever overflow, I just halt grub2. This makes the code
much simpler, and also, there is no need for an extra field to store
the mapped address, we just replace the original address with the
mapped one.
I also add a grub_alloc_real_stub function, which can be used to
allocate memory from the stub area that can be used to to communicate
with real mode service. Please note that once allocated, the space
can't be released, so remember to save the pointer for later use.
Some real mode service need some kind of alignment for the input data,
so I add two more function grub_call_real_stub_align and
grub_alloc_real_stub_align, which have a alignment parameter. 1 means
no alignment, 2 word alignment, 4 dword alignment, etc. Don't use 0,
it will cause problem for the function.
2008-07-31 Bean <bean123ch@gmail.com>
* conf/i386-pc.rmk (biosdisk_mod_SOURCES): Add
disk/i386/pc/biosdisk_stub.S.
* disk/i386/pc/biosdisk_stub.S: New file.
* include/grub/i386/pc/biosdisk.h (grub_biosdisk_rw_int13_extensions):
Remove EXPORT_FUNC, as we have moved it out of the kernel.
(grub_biosdisk_rw_standard): Likewise.
(grub_biosdisk_check_int13_extensions): Likewise.
(grub_biosdisk_get_diskinfo_int13_extensions): Likewise.
(grub_biosdisk_get_cdinfo_int13_extensions): Likewise.
(grub_biosdisk_get_diskinfo_standard): Likewise.
(grub_biosdisk_get_num_floppies): Likewise.
* include/grub/i386/pc/kernel.h (grub_real_stub): New structure.
(grub_alloc_real_stub): New function.
(grub_alloc_real_stub_align): Likewise.
(grub_call_real_stub): Likewise.
(grub_call_real_stub_align): Likewise.
* include/grub/i386/pc/memory.h (GRUB_MEMORY_MACHINE_REAL_STUB_START):
New macro.
(GRUB_MEMORY_MACHINE_REAL_STUB_SIZE): Likewise.
(GRUB_MEMORY_MACHINE_REAL_STUB_END): Likewise.
(GRUB_MEMORY_MACHINE_RESERVED_END): Change value.
* include/symbol.h (REAL_STUB_START): New macro.
(REAL_STUB_END): Likewise.
* kern/i386/pc/startup.S (real_stub_addr): New variable.
(grub_alloc_real_stub): New function.
(grub_alloc_real_stub_align): Likewise.
(grub_call_real_stub): Likewise.
(grub_call_real_stub_align): Likewise.
(grub_biosdisk_rw_int13_extensions): Moved to biosdisk_stub.S.
(grub_biosdisk_rw_standard): Likewise.
(grub_biosdisk_check_int13_extensions): Likewise.
(grub_biosdisk_get_diskinfo_int13_extensions): Likewise.
(grub_biosdisk_get_cdinfo_int13_extensions): Likewise.
(grub_biosdisk_get_diskinfo_standard): Likewise.
(grub_biosdisk_get_num_floppies): Likewise.
--
Bean
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: stub_2.diff --]
[-- Type: text/x-diff; name=stub_2.diff, Size: 21970 bytes --]
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index 59fc6a3..f779414 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -162,7 +162,7 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod
# For biosdisk.mod.
-biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
+biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c disk/i386/pc/biosdisk_stub.S
biosdisk_mod_CFLAGS = $(COMMON_CFLAGS)
biosdisk_mod_LDFLAGS = $(COMMON_LDFLAGS)
diff --git a/disk/i386/pc/biosdisk_stub.S b/disk/i386/pc/biosdisk_stub.S
new file mode 100644
index 0000000..05536c6
--- /dev/null
+++ b/disk/i386/pc/biosdisk_stub.S
@@ -0,0 +1,344 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2008 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/>.
+ */
+
+#define ASM_FILE 1
+
+#include <grub/symbol.h>
+#include <grub/machine/memory.h>
+
+/*
+ * int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
+ *
+ * Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
+ * is passed for disk address packet. If an error occurs, return
+ * non-zero, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_rw_int13_extensions)
+ movb %dh, %ah
+ movw %cx, %ds
+ int $0x13 /* do the operation */
+ movb %ah, %dl /* save return value */
+ lret
+REAL_STUB_END(grub_biosdisk_rw_int13_extensions)
+
+FUNCTION(grub_biosdisk_rw_int13_extensions)
+ pushl %ebp
+ pushl %esi
+
+ /* compute the address of disk_address_packet */
+ movw %cx, %si
+ xorw %cx, %cx
+ shrl $4, %ecx /* save the segment to cx */
+
+ /* ah */
+ movb %al, %dh
+
+ leal grub_biosdisk_rw_int13_extensions_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %dl, %al /* return value in %eax */
+
+ popl %esi
+ popl %ebp
+
+ ret
+
+/*
+ * int grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
+ * int soff, int nsec, int segment)
+ *
+ * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
+ * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
+ * return non-zero, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_rw_standard)
+ movw %bx, %es
+ xorw %bx, %bx
+ movw $3, %si /* attempt at least three times */
+
+1:
+ movw %di, %ax
+ int $0x13 /* do the operation */
+ jnc 2f /* check if successful */
+
+ movb %ah, %bl /* save return value */
+ /* if fail, reset the disk system */
+ xorw %ax, %ax
+ int $0x13
+
+ decw %si
+ cmpw $0, %si
+ je 2f
+ xorb %bl, %bl
+ jmp 1b /* retry */
+2:
+ lret
+REAL_STUB_END(grub_biosdisk_rw_standard)
+
+FUNCTION(grub_biosdisk_rw_standard)
+ pushl %ebp
+ movl %esp, %ebp
+
+ pushl %ebx
+ pushl %edi
+ pushl %esi
+
+ /* set up CHS information */
+
+ /* set %ch to low eight bits of cylinder */
+ xchgb %cl, %ch
+ /* set bits 6-7 of %cl to high two bits of cylinder */
+ shlb $6, %cl
+ /* set bits 0-5 of %cl to sector */
+ addb 0xc(%ebp), %cl
+ /* set %dh to head */
+ movb 0x8(%ebp), %dh
+ /* set %ah to AH */
+ movb %al, %ah
+ /* set %al to NSEC */
+ movb 0x10(%ebp), %al
+ /* save %ax in %di */
+ movw %ax, %di
+ /* save SEGMENT in %bx */
+ movw 0x14(%ebp), %bx
+
+ leal grub_biosdisk_rw_standard_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %bl, %al /* return value in %eax */
+
+ popl %esi
+ popl %edi
+ popl %ebx
+ popl %ebp
+
+ ret $(4 * 4)
+
+
+/*
+ * int grub_biosdisk_check_int13_extensions (int drive)
+ *
+ * Check if LBA is supported for DRIVE. If it is supported, then return
+ * the major version of extensions, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_check_int13_extensions)
+ movb $0x41, %ah
+ movw $0x55aa, %bx
+ int $0x13 /* do the operation */
+
+ /* check the result */
+ jc 1f
+ cmpw $0xaa55, %bx
+ jne 1f
+
+ movb %ah, %bl /* save the major version into %bl */
+
+ /* check if AH=0x42 is supported */
+ andw $1, %cx
+ jnz 2f
+
+1:
+ xorb %bl, %bl
+2:
+ lret
+REAL_STUB_END(grub_biosdisk_check_int13_extensions)
+
+FUNCTION(grub_biosdisk_check_int13_extensions)
+ pushl %ebp
+ pushl %ebx
+
+ /* drive */
+ movb %al, %dl
+
+ leal grub_biosdisk_check_int13_extensions_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %bl, %al /* return value in %eax */
+
+ popl %ebx
+ popl %ebp
+
+ ret
+
+
+/*
+ * int grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
+ *
+ * Return the cdrom information of DRIVE in CDRP. If an error occurs,
+ * then return non-zero, otherwise zero.
+ */
+
+FUNCTION(grub_biosdisk_get_cdinfo_int13_extensions)
+ movw $0x4B01, %cx
+ jmp 1f
+
+/*
+ * int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
+ *
+ * Return the geometry of DRIVE in a drive parameters, DRP. If an error
+ * occurs, then return non-zero, otherwise zero.
+ */
+
+REAL_STUB_START(grub_biosdisk_get_diskinfo_int13_extensions)
+ movw %cx, %ax
+ movw %bx, %ds
+ int $0x13 /* do the operation */
+ movb %ah, %bl /* save return value in %bl */
+ lret
+REAL_STUB_END(grub_biosdisk_get_diskinfo_int13_extensions)
+
+FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
+ movb $0x48, %ch
+1:
+ pushl %ebp
+ pushl %ebx
+ pushl %esi
+
+ /* compute the address of drive parameters */
+ movw %dx, %si
+ andl $0xf, %esi
+ shrl $4, %edx
+ movw %dx, %bx /* save the segment into %bx */
+ /* drive */
+ movb %al, %dl
+
+ leal grub_biosdisk_get_diskinfo_int13_extensions_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movb %bl, %al /* return value in %eax */
+
+ popl %esi
+ popl %ebx
+ popl %ebp
+
+ ret
+
+
+/*
+ * int grub_biosdisk_get_diskinfo_standard (int drive,
+ * unsigned long *cylinders,
+ * unsigned long *heads,
+ * unsigned long *sectors)
+ *
+ * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
+ * error occurs, then return non-zero, otherwise zero.
+ */
+REAL_STUB_START(grub_biosdisk_get_diskinfo_standard)
+ movb $0x8, %ah
+ int $0x13 /* do the operation */
+ /* check if successful */
+ testb %ah, %ah
+ jnz 1f
+ /* bogus BIOSes may not return an error number */
+ testb $0x3f, %cl /* 0 sectors means no disk */
+ jnz 1f /* if non-zero, then succeed */
+ /* XXX 0x60 is one of the unused error numbers */
+ movb $0x60, %ah
+1:
+ movb %ah, %bl /* save return value in %bl */
+ lret
+REAL_STUB_END(grub_biosdisk_get_diskinfo_standard)
+
+FUNCTION(grub_biosdisk_get_diskinfo_standard)
+ pushl %ebp
+ pushl %ebx
+ pushl %edi
+
+ /* push CYLINDERS */
+ pushl %edx
+ /* push HEADS */
+ pushl %ecx
+ /* SECTORS is on the stack */
+
+ /* drive */
+ movb %al, %dl
+
+ leal grub_biosdisk_get_diskinfo_standard_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ /* pop HEADS */
+ popl %edi
+ movb %dh, %al
+ incl %eax /* the number of heads is counted from zero */
+ movl %eax, (%edi)
+
+ /* pop CYLINDERS */
+ popl %edi
+ movb %ch, %al
+ movb %cl, %ah
+ shrb $6, %ah /* the number of cylinders is counted from zero */
+ incl %eax
+ movl %eax, (%edi)
+
+ /* SECTORS */
+ movl 0x10(%esp), %edi
+ andb $0x3f, %cl
+ movzbl %cl, %eax
+ movl %eax, (%edi)
+
+ xorl %eax, %eax
+ movb %bl, %al /* return value in %eax */
+
+ popl %edi
+ popl %ebx
+ popl %ebp
+
+ ret $4
+
+
+/*
+ * int grub_biosdisk_get_num_floppies (void)
+ */
+
+REAL_STUB_START(grub_biosdisk_get_num_floppies)
+ /* reset the disk system first */
+ int $0x13
+1:
+ stc
+
+ /* call GET DISK TYPE */
+ movb $0x15, %ah
+ int $0x13
+
+ jc 2f
+
+ /* check if this drive exists */
+ testb $0x3, %ah
+ jz 2f
+
+ incb %dl
+ cmpb $2, %dl
+ jne 1b
+2:
+ lret
+REAL_STUB_END(grub_biosdisk_get_num_floppies)
+
+FUNCTION(grub_biosdisk_get_num_floppies)
+ pushl %ebp
+
+ xorl %edx, %edx
+
+ leal grub_biosdisk_get_num_floppies_stub, %eax
+ call EXT_C(grub_call_real_stub)
+
+ movl %edx, %eax
+ popl %ebp
+ ret
diff --git a/include/grub/i386/pc/biosdisk.h b/include/grub/i386/pc/biosdisk.h
index 05d5dc5..da82116 100644
--- a/include/grub/i386/pc/biosdisk.h
+++ b/include/grub/i386/pc/biosdisk.h
@@ -106,19 +106,19 @@ struct grub_biosdisk_dap
grub_uint64_t block;
} __attribute__ ((packed));
-int EXPORT_FUNC(grub_biosdisk_rw_int13_extensions) (int ah, int drive, void *dap);
-int EXPORT_FUNC(grub_biosdisk_rw_standard) (int ah, int drive, int coff, int hoff,
+int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap);
+int grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
int soff, int nsec, int segment);
-int EXPORT_FUNC(grub_biosdisk_check_int13_extensions) (int drive);
-int EXPORT_FUNC(grub_biosdisk_get_diskinfo_int13_extensions) (int drive,
+int grub_biosdisk_check_int13_extensions (int drive);
+int grub_biosdisk_get_diskinfo_int13_extensions (int drive,
void *drp);
-int EXPORT_FUNC(grub_biosdisk_get_cdinfo_int13_extensions) (int drive,
+int grub_biosdisk_get_cdinfo_int13_extensions (int drive,
void *cdrp);
-int EXPORT_FUNC(grub_biosdisk_get_diskinfo_standard) (int drive,
+int grub_biosdisk_get_diskinfo_standard (int drive,
unsigned long *cylinders,
unsigned long *heads,
unsigned long *sectors);
-int EXPORT_FUNC(grub_biosdisk_get_num_floppies) (void);
+int grub_biosdisk_get_num_floppies (void);
void grub_biosdisk_init (void);
void grub_biosdisk_fini (void);
diff --git a/include/grub/i386/pc/kernel.h b/include/grub/i386/pc/kernel.h
index a00117b..fde9525 100644
--- a/include/grub/i386/pc/kernel.h
+++ b/include/grub/i386/pc/kernel.h
@@ -86,6 +86,19 @@ extern grub_addr_t grub_end_addr;
extern grub_addr_t EXPORT_FUNC(grub_arch_memdisk_addr) (void);
extern grub_off_t EXPORT_FUNC(grub_arch_memdisk_size) (void);
+struct grub_real_stub
+{
+ void *address;
+ grub_uint32_t length;
+};
+
+void* EXPORT_FUNC(grub_alloc_real_stub) (int size);
+void* EXPORT_FUNC(grub_alloc_real_stub_align) (int size, int align);
+
+void EXPORT_FUNC(grub_call_real_stub) (struct grub_real_stub *stub);
+void EXPORT_FUNC(grub_call_real_stub_align) (struct grub_real_stub *stub,
+ int align);
+
#endif /* ! ASM_FILE */
#endif /* ! KERNEL_MACHINE_HEADER */
diff --git a/include/grub/i386/pc/memory.h b/include/grub/i386/pc/memory.h
index a3e3ed7..55d5dcb 100644
--- a/include/grub/i386/pc/memory.h
+++ b/include/grub/i386/pc/memory.h
@@ -45,12 +45,17 @@
(GRUB_MEMORY_MACHINE_SCRATCH_ADDR + GRUB_MEMORY_MACHINE_SCRATCH_SIZE \
+ GRUB_MEMORY_MACHINE_PROT_STACK_SIZE - 0x10)
+#define GRUB_MEMORY_MACHINE_REAL_STUB_START (GRUB_MEMORY_MACHINE_PROT_STACK + 0x10)
+#define GRUB_MEMORY_MACHINE_REAL_STUB_SIZE 0x8000
+#define GRUB_MEMORY_MACHINE_REAL_STUB_END \
+ (GRUB_MEMORY_MACHINE_REAL_STUB_START + GRUB_MEMORY_MACHINE_REAL_STUB_SIZE)
+
/* The memory area where GRUB uses its own purpose. This part is not added
into free memory for dynamic allocations. */
#define GRUB_MEMORY_MACHINE_RESERVED_START \
GRUB_MEMORY_MACHINE_SCRATCH_ADDR
#define GRUB_MEMORY_MACHINE_RESERVED_END \
- (GRUB_MEMORY_MACHINE_PROT_STACK + 0x10)
+ GRUB_MEMORY_MACHINE_REAL_STUB_END
/* The area where GRUB is decompressed at early startup. */
#define GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR 0x100000
diff --git a/include/grub/symbol.h b/include/grub/symbol.h
index e951490..eef966f 100644
--- a/include/grub/symbol.h
+++ b/include/grub/symbol.h
@@ -43,4 +43,13 @@
# define EXPORT_VAR(x) x
#endif /* ! GRUB_SYMBOL_GENERATOR */
+#define REAL_STUB_START(x) x ## _stub: ; \
+ .long x ## _start ; \
+ .long x ## _end - x ## _start ; \
+x ## _start: ; \
+ .code16
+
+#define REAL_STUB_END(x) .code32 ; \
+x ## _end:
+
#endif /* ! GRUB_SYMBOL_HEADER */
diff --git a/kern/i386/pc/startup.S b/kern/i386/pc/startup.S
index 679ad1f..8e7c68d 100644
--- a/kern/i386/pc/startup.S
+++ b/kern/i386/pc/startup.S
@@ -618,357 +618,157 @@ FUNCTION(grub_halt)
jmp EXT_C(grub_hard_stop)
.code32
+real_stub_addr:
+ .long GRUB_MEMORY_MACHINE_REAL_STUB_START
/*
- * void grub_chainloader_real_boot (int drive, void *part_addr)
+ * Copy code/data to address below 1M.
*
- * This starts another boot loader.
+ * On entry, %eax should points to a structure that specifies the code/data to
+ * be copied:
+ *
+ * offset 0: address of code/data
+ * offset 4: size of code/data
+ *
+ * grub_copy_real_stub fetches the address from offset 0, if it's not below 1M or
+ * properly aligned, it copies it to a reserved area and updates the address at
+ * offset 0, so that it don't need to do it again the next time.
+ *
+ * Currently, the reserved area for stub code/data is 0x80000 - 0x88000. It
+ * should be big enough for assembly code.
*/
-FUNCTION(grub_chainloader_real_boot)
- pushl %edx
- pushl %eax
-
- call EXT_C(grub_dl_unload_all)
-
- /* Turn off Gate A20 */
- xorl %eax, %eax
- call EXT_C(grub_gate_a20)
-
- /* set up to pass boot drive */
- popl %edx
-
- /* ESI must point to a partition table entry */
- popl %esi
-
- call prot_to_real
- .code16
- ljmp $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
- .code32
-
-#include "../loader.S"
-
/*
- * int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
+ * Allocate memory from the stub area.
*
- * Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
- * is passed for disk address packet. If an error occurs, return
- * non-zero, otherwise zero.
+ * void* grub_alloc_real_stub (int size);
+ * void* grub_alloc_real_stub_align (int size, int align);
*/
-FUNCTION(grub_biosdisk_rw_int13_extensions)
- pushl %ebp
- pushl %esi
+FUNCTION(grub_alloc_real_stub)
+ movl $1, %edx
- /* compute the address of disk_address_packet */
- movw %cx, %si
- xorw %cx, %cx
- shrl $4, %ecx /* save the segment to cx */
+FUNCTION(grub_alloc_real_stub_align)
+ decl %edx
- /* ah */
- movb %al, %dh
- /* enter real mode */
- call prot_to_real
+grub_alloc_real_stub_1:
+ movl %eax, %ecx
+ movl real_stub_addr, %eax
- .code16
- movb %dh, %ah
- movw %cx, %ds
- int $0x13 /* do the operation */
- movb %ah, %dl /* save return value */
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
+ addl %edx, %eax
+ notl %edx
+ andl %edx, %eax
- movb %dl, %al /* return value in %eax */
+ leal (%eax, %ecx), %edx
+ cmpl $GRUB_MEMORY_MACHINE_REAL_STUB_END, %edx
+ ja EXT_C(grub_stop)
- popl %esi
- popl %ebp
+ movl %edx, real_stub_addr
ret
/*
- * int grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
- * int soff, int nsec, int segment)
+ * Test the address in stub, if it's above 1M or not properly aligned,
+ * allocate memory from the stub area and copy the code. Then, it switches
+ * to real mode and execute.
*
- * Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
- * NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
- * return non-zero, otherwise zero.
+ * void grub_call_real_stub (struct grub_real_stub *stub);
+ * void grub_call_real_stub_align (struct grub_real_stub *stub,
+ * int align);
*/
-FUNCTION(grub_biosdisk_rw_standard)
- pushl %ebp
- movl %esp, %ebp
-
- pushl %ebx
- pushl %edi
- pushl %esi
-
- /* set up CHS information */
-
- /* set %ch to low eight bits of cylinder */
- xchgb %cl, %ch
- /* set bits 6-7 of %cl to high two bits of cylinder */
- shlb $6, %cl
- /* set bits 0-5 of %cl to sector */
- addb 0xc(%ebp), %cl
- /* set %dh to head */
- movb 0x8(%ebp), %dh
- /* set %ah to AH */
- movb %al, %ah
- /* set %al to NSEC */
- movb 0x10(%ebp), %al
- /* save %ax in %di */
- movw %ax, %di
- /* save SEGMENT in %bx */
- movw 0x14(%ebp), %bx
-
- /* enter real mode */
- call prot_to_real
+FUNCTION(grub_call_real_stub)
+ pushl %edx
+ xorl %edx, %edx
+ jmp 1f
- .code16
- movw %bx, %es
- xorw %bx, %bx
- movw $3, %si /* attempt at least three times */
+FUNCTION(grub_call_real_stub_align)
+ pushl %edx
+ decl %edx
1:
- movw %di, %ax
- int $0x13 /* do the operation */
- jnc 2f /* check if successful */
-
- movb %ah, %bl /* save return value */
- /* if fail, reset the disk system */
- xorw %ax, %ax
- int $0x13
-
- decw %si
- cmpw $0, %si
- je 2f
- xorb %bl, %bl
- jmp 1b /* retry */
-2:
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
-
- movb %bl, %al /* return value in %eax */
-
- popl %esi
- popl %edi
- popl %ebx
- popl %ebp
+ pushl %ecx
- ret $(4 * 4)
+ /* Test if it's below 1M. */
+ movl (%eax), %ecx
+ cmpl $GRUB_MEMORY_MACHINE_UPPER, %ecx
+ jae 1f
+ /* Test if it's aligned properly. */
+ testl %edx, %ecx
+ jz 2f
-/*
- * int grub_biosdisk_check_int13_extensions (int drive)
- *
- * Check if LBA is supported for DRIVE. If it is supported, then return
- * the major version of extensions, otherwise zero.
- */
+1:
+ pushl %esi
+ pushl %edi
+ pushl %eax
-FUNCTION(grub_biosdisk_check_int13_extensions)
- pushl %ebp
- pushl %ebx
+ movl %ecx, %esi
+ movl 4(%eax), %eax
- /* drive */
- movb %al, %dl
- /* enter real mode */
- call prot_to_real
+ call grub_alloc_real_stub_1
- .code16
- movb $0x41, %ah
- movw $0x55aa, %bx
- int $0x13 /* do the operation */
+ /* On return, %ecx will contain the length. */
- /* check the result */
- jc 1f
- cmpw $0xaa55, %bx
- jne 1f
+ movl %eax, %edi
+ xchgl %eax, (%esp)
+ movl %edi, (%eax)
- movb %ah, %bl /* save the major version into %bl */
+ cld
+ rep movsb
- /* check if AH=0x42 is supported */
- andw $1, %cx
- jnz 2f
+ popl %ecx
+ popl %edi
+ popl %esi
-1:
- xorb %bl, %bl
2:
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
+ shll $12, %ecx
+ shrw $12, %cx
- movb %bl, %al /* return value in %eax */
+ movl %ecx, GRUB_MEMORY_MACHINE_REAL_STACK + 0x10
- popl %ebx
- popl %ebp
-
- ret
-
-
-/*
- * int grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
- *
- * Return the cdrom information of DRIVE in CDRP. If an error occurs,
- * then return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_get_cdinfo_int13_extensions)
- movw $0x4B01, %cx
- jmp 1f
-
-/*
- * int grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
- *
- * Return the geometry of DRIVE in a drive parameters, DRP. If an error
- * occurs, then return non-zero, otherwise zero.
- */
-
-FUNCTION(grub_biosdisk_get_diskinfo_int13_extensions)
- movb $0x48, %ch
-1:
- pushl %ebp
- pushl %ebx
- pushl %esi
+ popl %ecx
+ popl %edx
- /* compute the address of drive parameters */
- movw %dx, %si
- andl $0xf, %esi
- shrl $4, %edx
- movw %dx, %bx /* save the segment into %bx */
- /* drive */
- movb %al, %dl
- /* enter real mode */
call prot_to_real
-
.code16
- movw %cx, %ax
- movw %bx, %ds
- int $0x13 /* do the operation */
- movb %ah, %bl /* save return value in %bl */
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
- movb %bl, %al /* return value in %eax */
+ lcall *(GRUB_MEMORY_MACHINE_REAL_STACK + 0x10)
- popl %esi
- popl %ebx
- popl %ebp
+ DATA32 call real_to_prot
+ .code32
ret
/*
- * int grub_biosdisk_get_diskinfo_standard (int drive,
- * unsigned long *cylinders,
- * unsigned long *heads,
- * unsigned long *sectors)
+ * void grub_chainloader_real_boot (int drive, void *part_addr)
*
- * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
- * error occurs, then return non-zero, otherwise zero.
+ * This starts another boot loader.
*/
-FUNCTION(grub_biosdisk_get_diskinfo_standard)
- pushl %ebp
- pushl %ebx
- pushl %edi
-
- /* push CYLINDERS */
+FUNCTION(grub_chainloader_real_boot)
pushl %edx
- /* push HEADS */
- pushl %ecx
- /* SECTORS is on the stack */
-
- /* drive */
- movb %al, %dl
- /* enter real mode */
- call prot_to_real
-
- .code16
- movb $0x8, %ah
- int $0x13 /* do the operation */
- /* check if successful */
- testb %ah, %ah
- jnz 1f
- /* bogus BIOSes may not return an error number */
- testb $0x3f, %cl /* 0 sectors means no disk */
- jnz 1f /* if non-zero, then succeed */
- /* XXX 0x60 is one of the unused error numbers */
- movb $0x60, %ah
-1:
- movb %ah, %bl /* save return value in %bl */
- /* back to protected mode */
- DATA32 call real_to_prot
- .code32
-
- /* pop HEADS */
- popl %edi
- movb %dh, %al
- incl %eax /* the number of heads is counted from zero */
- movl %eax, (%edi)
-
- /* pop CYLINDERS */
- popl %edi
- movb %ch, %al
- movb %cl, %ah
- shrb $6, %ah /* the number of cylinders is counted from zero */
- incl %eax
- movl %eax, (%edi)
+ pushl %eax
- /* SECTORS */
- movl 0x10(%esp), %edi
- andb $0x3f, %cl
- movzbl %cl, %eax
- movl %eax, (%edi)
+ call EXT_C(grub_dl_unload_all)
+ /* Turn off Gate A20 */
xorl %eax, %eax
- movb %bl, %al /* return value in %eax */
-
- popl %edi
- popl %ebx
- popl %ebp
-
- ret $4
+ call EXT_C(grub_gate_a20)
+ /* set up to pass boot drive */
+ popl %edx
-/*
- * int grub_biosdisk_get_num_floppies (void)
- */
-FUNCTION(grub_biosdisk_get_num_floppies)
- pushl %ebp
+ /* ESI must point to a partition table entry */
+ popl %esi
- xorl %edx, %edx
call prot_to_real
-
.code16
- /* reset the disk system first */
- int $0x13
-1:
- stc
-
- /* call GET DISK TYPE */
- movb $0x15, %ah
- int $0x13
-
- jc 2f
-
- /* check if this drive exists */
- testb $0x3, %ah
- jz 2f
-
- incb %dl
- cmpb $2, %dl
- jne 1b
-2:
- DATA32 call real_to_prot
+ ljmp $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR
.code32
- movl %edx, %eax
- popl %ebp
- ret
-
+#include "../loader.S"
/*
*
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] Move assembly code out of the kernel
2008-07-31 7:46 ` Bean
@ 2008-07-31 11:22 ` Robert Millan
2008-07-31 13:56 ` Bean
0 siblings, 1 reply; 8+ messages in thread
From: Robert Millan @ 2008-07-31 11:22 UTC (permalink / raw)
To: The development of GRUB 2
On Thu, Jul 31, 2008 at 03:46:47PM +0800, Bean wrote:
> diff --git a/include/grub/symbol.h b/include/grub/symbol.h
> index e951490..eef966f 100644
> --- a/include/grub/symbol.h
> +++ b/include/grub/symbol.h
> @@ -43,4 +43,13 @@
> # define EXPORT_VAR(x) x
> #endif /* ! GRUB_SYMBOL_GENERATOR */
>
> +#define REAL_STUB_START(x) x ## _stub: ; \
> + .long x ## _start ; \
> + .long x ## _end - x ## _start ; \
> +x ## _start: ; \
> + .code16
> +
> +#define REAL_STUB_END(x) .code32 ; \
> +x ## _end:
Shouldn't this be in an i386/pc/ subdir?
> + * On entry, %eax should points to a structure that specifies the code/data to
> + * be copied:
> + *
> + * offset 0: address of code/data
> + * offset 4: size of code/data
> + *
> + * grub_copy_real_stub fetches the address from offset 0, if it's not below 1M or
> + * properly aligned, it copies it to a reserved area and updates the address at
> + * offset 0, so that it don't need to do it again the next time.
> + *
> + * Currently, the reserved area for stub code/data is 0x80000 - 0x88000. It
> + * should be big enough for assembly code.
I wonder if this could be made simpler. For example, by having a function
that just calls "int" in real mode. Then callers could use inline assembly
to put parameters in appropiate registers and reading results from appropiate
registers. Would this be faster than copiing hooks?
--
Robert Millan
The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
how) you may access your data; but nobody's threatening your freedom: we
still allow you to remove your data and not access it at all."
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] Move assembly code out of the kernel
2008-07-31 11:22 ` Robert Millan
@ 2008-07-31 13:56 ` Bean
0 siblings, 0 replies; 8+ messages in thread
From: Bean @ 2008-07-31 13:56 UTC (permalink / raw)
To: The development of GRUB 2
On Thu, Jul 31, 2008 at 7:22 PM, Robert Millan <rmh@aybabtu.com> wrote:
> On Thu, Jul 31, 2008 at 03:46:47PM +0800, Bean wrote:
>> diff --git a/include/grub/symbol.h b/include/grub/symbol.h
>> index e951490..eef966f 100644
>> --- a/include/grub/symbol.h
>> +++ b/include/grub/symbol.h
>> @@ -43,4 +43,13 @@
>> # define EXPORT_VAR(x) x
>> #endif /* ! GRUB_SYMBOL_GENERATOR */
>>
>> +#define REAL_STUB_START(x) x ## _stub: ; \
>> + .long x ## _start ; \
>> + .long x ## _end - x ## _start ; \
>> +x ## _start: ; \
>> + .code16
>> +
>> +#define REAL_STUB_END(x) .code32 ; \
>> +x ## _end:
>
> Shouldn't this be in an i386/pc/ subdir?
Oh, perhaps I can move them to include/i386/pc/kernel.h
>
>> + * On entry, %eax should points to a structure that specifies the code/data to
>> + * be copied:
>> + *
>> + * offset 0: address of code/data
>> + * offset 4: size of code/data
>> + *
>> + * grub_copy_real_stub fetches the address from offset 0, if it's not below 1M or
>> + * properly aligned, it copies it to a reserved area and updates the address at
>> + * offset 0, so that it don't need to do it again the next time.
>> + *
>> + * Currently, the reserved area for stub code/data is 0x80000 - 0x88000. It
>> + * should be big enough for assembly code.
>
> I wonder if this could be made simpler. For example, by having a function
> that just calls "int" in real mode. Then callers could use inline assembly
> to put parameters in appropiate registers and reading results from appropiate
> registers. Would this be faster than copiing hooks?
INT sounds good, but it can't cover all cases. For example, it can't
pass parameter using stack, or call a far pointer. Besides, we still
need the stub area to store data.
--
Bean
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2008-08-24 9:37 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-31 13:55 [PATCH] Move assembly code out of the kernel chaac
2008-08-17 11:31 ` Vesa Jääskeläinen
2008-08-17 16:55 ` Bean
2008-08-24 9:37 ` Vesa Jääskeläinen
-- strict thread matches above, loose matches on Subject: below --
2008-07-30 21:20 Bean
2008-07-31 7:46 ` Bean
2008-07-31 11:22 ` Robert Millan
2008-07-31 13:56 ` Bean
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.