diff options
author | 2015-08-28 09:58:54 +0800 | |
---|---|---|
committer | 2015-09-01 12:44:00 +0800 | |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/pc-bios/optionrom | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/pc-bios/optionrom')
-rw-r--r-- | qemu/pc-bios/optionrom/Makefile | 33 | ||||
-rw-r--r-- | qemu/pc-bios/optionrom/kvmvapic.S | 335 | ||||
-rw-r--r-- | qemu/pc-bios/optionrom/linuxboot.S | 195 | ||||
-rw-r--r-- | qemu/pc-bios/optionrom/multiboot.S | 232 | ||||
-rw-r--r-- | qemu/pc-bios/optionrom/optionrom.h | 152 |
5 files changed, 947 insertions, 0 deletions
diff --git a/qemu/pc-bios/optionrom/Makefile b/qemu/pc-bios/optionrom/Makefile new file mode 100644 index 000000000..ce4852a4d --- /dev/null +++ b/qemu/pc-bios/optionrom/Makefile @@ -0,0 +1,33 @@ +all: build-all +# Dummy command so that make thinks it has done something + @true + +include ../../config-host.mak +include $(SRC_PATH)/rules.mak + +$(call set-vpath, $(SRC_PATH)/pc-bios/optionrom) + +.PHONY : all clean build-all + +CFLAGS := -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin +CFLAGS += -I$(SRC_PATH) +CFLAGS += $(call cc-option, $(CFLAGS), -fno-stack-protector) +CFLAGS += $(CFLAGS_NOPIE) +QEMU_CFLAGS = $(CFLAGS) + +build-all: multiboot.bin linuxboot.bin kvmvapic.bin + +# suppress auto-removal of intermediate files +.SECONDARY: + +%.img: %.o + $(call quiet-command,$(LD) $(LDFLAGS_NOPIE) -Ttext 0 -e _start -s -o $@ $<," Building $(TARGET_DIR)$@") + +%.raw: %.img + $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@") + +%.bin: %.raw + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/signrom.py $< $@," Signing $(TARGET_DIR)$@") + +clean: + rm -f *.o *.d *.raw *.img *.bin *~ diff --git a/qemu/pc-bios/optionrom/kvmvapic.S b/qemu/pc-bios/optionrom/kvmvapic.S new file mode 100644 index 000000000..aa17a402d --- /dev/null +++ b/qemu/pc-bios/optionrom/kvmvapic.S @@ -0,0 +1,335 @@ +# +# Local APIC acceleration for Windows XP and related guests +# +# Copyright 2011 Red Hat, Inc. and/or its affiliates +# +# Author: Avi Kivity <avi@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2, or (at your +# option) any later version. See the COPYING file in the top-level directory. +# + +#include "optionrom.h" + +OPTION_ROM_START + + # clear vapic area: firmware load using rep insb may cause + # stale tpr/isr/irr data to corrupt the vapic area. + push %es + push %cs + pop %es + xor %ax, %ax + mov $vapic_size/2, %cx + lea vapic, %di + cld + rep stosw + pop %es + + # announce presence to the hypervisor + mov $vapic_base, %ax + out %ax, $0x7e + + lret + + .code32 +vapic_size = 2*4096 + +.macro fixup delta=-4 +777: + .text 1 + .long 777b + \delta - vapic_base + .text 0 +.endm + +.macro reenable_vtpr + out %al, $0x7e +.endm + +.text 1 + fixup_start = . +.text 0 + +.align 16 + +vapic_base: + .ascii "kvm aPiC" + + /* relocation data */ + .long vapic_base ; fixup + .long fixup_start ; fixup + .long fixup_end ; fixup + + .long vapic ; fixup + .long vapic_size +vcpu_shift: + .long 0 +real_tpr: + .long 0 + .long up_set_tpr ; fixup + .long up_set_tpr_eax ; fixup + .long up_get_tpr_eax ; fixup + .long up_get_tpr_ecx ; fixup + .long up_get_tpr_edx ; fixup + .long up_get_tpr_ebx ; fixup + .long 0 /* esp. won't work. */ + .long up_get_tpr_ebp ; fixup + .long up_get_tpr_esi ; fixup + .long up_get_tpr_edi ; fixup + .long up_get_tpr_stack ; fixup + .long mp_set_tpr ; fixup + .long mp_set_tpr_eax ; fixup + .long mp_get_tpr_eax ; fixup + .long mp_get_tpr_ecx ; fixup + .long mp_get_tpr_edx ; fixup + .long mp_get_tpr_ebx ; fixup + .long 0 /* esp. won't work. */ + .long mp_get_tpr_ebp ; fixup + .long mp_get_tpr_esi ; fixup + .long mp_get_tpr_edi ; fixup + .long mp_get_tpr_stack ; fixup + +.macro kvm_hypercall + .byte 0x0f, 0x01, 0xc1 +.endm + +kvm_hypercall_vapic_poll_irq = 1 + +pcr_cpu = 0x51 + +.align 64 + +mp_get_tpr_eax: + pushf + cli + reenable_vtpr + push %ecx + + fs/movzbl pcr_cpu, %eax + + mov vcpu_shift, %ecx ; fixup + shl %cl, %eax + testb $1, vapic+4(%eax) ; fixup delta=-5 + jz mp_get_tpr_bad + movzbl vapic(%eax), %eax ; fixup + +mp_get_tpr_out: + pop %ecx + popf + ret + +mp_get_tpr_bad: + mov real_tpr, %eax ; fixup + mov (%eax), %eax + jmp mp_get_tpr_out + +mp_get_tpr_ebx: + mov %eax, %ebx + call mp_get_tpr_eax + xchg %eax, %ebx + ret + +mp_get_tpr_ecx: + mov %eax, %ecx + call mp_get_tpr_eax + xchg %eax, %ecx + ret + +mp_get_tpr_edx: + mov %eax, %edx + call mp_get_tpr_eax + xchg %eax, %edx + ret + +mp_get_tpr_esi: + mov %eax, %esi + call mp_get_tpr_eax + xchg %eax, %esi + ret + +mp_get_tpr_edi: + mov %eax, %edi + call mp_get_tpr_edi + xchg %eax, %edi + ret + +mp_get_tpr_ebp: + mov %eax, %ebp + call mp_get_tpr_eax + xchg %eax, %ebp + ret + +mp_get_tpr_stack: + call mp_get_tpr_eax + xchg %eax, 4(%esp) + ret + +mp_set_tpr_eax: + push %eax + call mp_set_tpr + ret + +mp_set_tpr: + pushf + push %eax + push %ecx + push %edx + push %ebx + cli + reenable_vtpr + +mp_set_tpr_failed: + fs/movzbl pcr_cpu, %edx + + mov vcpu_shift, %ecx ; fixup + shl %cl, %edx + + testb $1, vapic+4(%edx) ; fixup delta=-5 + jz mp_set_tpr_bad + + mov vapic(%edx), %eax ; fixup + + mov %eax, %ebx + mov 24(%esp), %bl + + /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */ + + lock cmpxchg %ebx, vapic(%edx) ; fixup + jnz mp_set_tpr_failed + + /* compute ppr */ + cmp %bh, %bl + jae mp_tpr_is_bigger +mp_isr_is_bigger: + mov %bh, %bl +mp_tpr_is_bigger: + /* %bl = ppr */ + rol $8, %ebx + /* now: %bl = irr, %bh = ppr */ + cmp %bh, %bl + ja mp_set_tpr_poll_irq + +mp_set_tpr_out: + pop %ebx + pop %edx + pop %ecx + pop %eax + popf + ret $4 + +mp_set_tpr_poll_irq: + mov $kvm_hypercall_vapic_poll_irq, %eax + kvm_hypercall + jmp mp_set_tpr_out + +mp_set_tpr_bad: + mov 24(%esp), %ecx + mov real_tpr, %eax ; fixup + mov %ecx, (%eax) + jmp mp_set_tpr_out + +up_get_tpr_eax: + reenable_vtpr + movzbl vapic, %eax ; fixup + ret + +up_get_tpr_ebx: + reenable_vtpr + movzbl vapic, %ebx ; fixup + ret + +up_get_tpr_ecx: + reenable_vtpr + movzbl vapic, %ecx ; fixup + ret + +up_get_tpr_edx: + reenable_vtpr + movzbl vapic, %edx ; fixup + ret + +up_get_tpr_esi: + reenable_vtpr + movzbl vapic, %esi ; fixup + ret + +up_get_tpr_edi: + reenable_vtpr + movzbl vapic, %edi ; fixup + ret + +up_get_tpr_ebp: + reenable_vtpr + movzbl vapic, %ebp ; fixup + ret + +up_get_tpr_stack: + reenable_vtpr + movzbl vapic, %eax ; fixup + xchg %eax, 4(%esp) + ret + +up_set_tpr_eax: + push %eax + call up_set_tpr + ret + +up_set_tpr: + pushf + push %eax + push %ebx + reenable_vtpr + +up_set_tpr_failed: + mov vapic, %eax ; fixup + + mov %eax, %ebx + mov 16(%esp), %bl + + /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */ + + lock cmpxchg %ebx, vapic ; fixup + jnz up_set_tpr_failed + + /* compute ppr */ + cmp %bh, %bl + jae up_tpr_is_bigger +up_isr_is_bigger: + mov %bh, %bl +up_tpr_is_bigger: + /* %bl = ppr */ + rol $8, %ebx + /* now: %bl = irr, %bh = ppr */ + cmp %bh, %bl + ja up_set_tpr_poll_irq + +up_set_tpr_out: + pop %ebx + pop %eax + popf + ret $4 + +up_set_tpr_poll_irq: + mov $kvm_hypercall_vapic_poll_irq, %eax + kvm_hypercall + jmp up_set_tpr_out + +.text 1 + fixup_end = . +.text 0 + +/* + * vapic format: + * per-vcpu records of size 2^vcpu shift. + * byte 0: tpr (r/w) + * byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero + * byte 2: zero (r/o) + * byte 3: highest pending interrupt (irr) (r/o) + */ +.text 2 + +.align 128 + +vapic: +. = . + vapic_size + +OPTION_ROM_END diff --git a/qemu/pc-bios/optionrom/linuxboot.S b/qemu/pc-bios/optionrom/linuxboot.S new file mode 100644 index 000000000..ba821ab92 --- /dev/null +++ b/qemu/pc-bios/optionrom/linuxboot.S @@ -0,0 +1,195 @@ +/* + * Linux Boot Option ROM + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, see <http://www.gnu.org/licenses/>. + * + * Copyright Novell Inc, 2009 + * Authors: Alexander Graf <agraf@suse.de> + * + * Based on code in hw/pc.c. + */ + +#include "optionrom.h" + +#define BOOT_ROM_PRODUCT "Linux loader" + +BOOT_ROM_START + +run_linuxboot: + + cli + cld + + jmp copy_kernel +boot_kernel: + + read_fw FW_CFG_SETUP_ADDR + + mov %eax, %ebx + shr $4, %ebx + + /* All segments contain real_addr */ + mov %bx, %ds + mov %bx, %es + mov %bx, %fs + mov %bx, %gs + mov %bx, %ss + + /* CX = CS we want to jump to */ + add $0x20, %bx + mov %bx, %cx + + /* SP = cmdline_addr-real_addr-16 */ + read_fw FW_CFG_CMDLINE_ADDR + mov %eax, %ebx + read_fw FW_CFG_SETUP_ADDR + sub %eax, %ebx + sub $16, %ebx + mov %ebx, %esp + + /* Build indirect lret descriptor */ + pushw %cx /* CS */ + xor %ax, %ax + pushw %ax /* IP = 0 */ + + /* Clear registers */ + xor %eax, %eax + xor %ebx, %ebx + xor %ecx, %ecx + xor %edx, %edx + xor %edi, %edi + xor %ebp, %ebp + + /* Jump to Linux */ + lret + + +copy_kernel: + /* Read info block in low memory (0x10000 or 0x90000) */ + read_fw FW_CFG_SETUP_ADDR + shr $4, %eax + mov %eax, %es + xor %edi, %edi + read_fw_blob_addr32_edi(FW_CFG_SETUP) + + cmpw $0x203, %es:0x206 // if protocol >= 0x203 + jae 1f // have initrd_max + movl $0x37ffffff, %es:0x22c // else assume 0x37ffffff +1: + + /* Check if using kernel-specified initrd address */ + read_fw FW_CFG_INITRD_ADDR + mov %eax, %edi // (load_kernel wants it in %edi) + read_fw FW_CFG_INITRD_SIZE // find end of initrd + add %edi, %eax + xor %es:0x22c, %eax // if it matches es:0x22c + and $-4096, %eax // (apart from padding for page) + jz load_kernel // then initrd is not at top + // of memory + + /* pc.c placed the initrd at end of memory. Compute a better + * initrd address based on e801 data. + */ + mov $0xe801, %ax + xor %cx, %cx + xor %dx, %dx + int $0x15 + + /* Output could be in AX/BX or CX/DX */ + or %cx, %cx + jnz 1f + or %dx, %dx + jnz 1f + mov %ax, %cx + mov %bx, %dx +1: + + or %dx, %dx + jnz 2f + addw $1024, %cx /* add 1 MB */ + movzwl %cx, %edi + shll $10, %edi /* convert to bytes */ + jmp 3f + +2: + addw $16777216 >> 16, %dx /* add 16 MB */ + movzwl %dx, %edi + shll $16, %edi /* convert to bytes */ + +3: + read_fw FW_CFG_INITRD_SIZE + subl %eax, %edi + andl $-4096, %edi /* EDI = start of initrd */ + movl %edi, %es:0x218 /* put it in the header */ + +load_kernel: + /* We need to load the kernel into memory we can't access in 16 bit + mode, so let's get into 32 bit mode, write the kernel and jump + back again. */ + + /* Reserve space on the stack for our GDT descriptor. */ + mov %esp, %ebp + sub $16, %esp + + /* Now create the GDT descriptor */ + movw $((3 * 8) - 1), -16(%bp) + mov %cs, %eax + movzwl %ax, %eax + shl $4, %eax + addl $gdt, %eax + movl %eax, -14(%bp) + + /* And load the GDT */ + data32 lgdt -16(%bp) + mov %ebp, %esp + + /* Get us to protected mode now */ + mov $1, %eax + mov %eax, %cr0 + + /* So we can set ES to a 32-bit segment */ + mov $0x10, %eax + mov %eax, %es + + /* We're now running in 16-bit CS, but 32-bit ES! */ + + /* Load kernel and initrd */ + read_fw_blob_addr32_edi(FW_CFG_INITRD) + read_fw_blob_addr32(FW_CFG_KERNEL) + read_fw_blob_addr32(FW_CFG_CMDLINE) + + /* And now jump into Linux! */ + mov $0, %eax + mov %eax, %cr0 + + /* ES = CS */ + mov %cs, %ax + mov %ax, %es + + jmp boot_kernel + +/* Variables */ + +.align 4, 0 +gdt: + /* 0x00 */ +.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 + + /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 + +BOOT_ROM_END diff --git a/qemu/pc-bios/optionrom/multiboot.S b/qemu/pc-bios/optionrom/multiboot.S new file mode 100644 index 000000000..b7efe4de3 --- /dev/null +++ b/qemu/pc-bios/optionrom/multiboot.S @@ -0,0 +1,232 @@ +/* + * Multiboot Option ROM + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, see <http://www.gnu.org/licenses/>. + * + * Copyright Novell Inc, 2009 + * Authors: Alexander Graf <agraf@suse.de> + */ + +#include "optionrom.h" + +#define BOOT_ROM_PRODUCT "multiboot loader" + +#define MULTIBOOT_MAGIC 0x2badb002 + +#define GS_PROT_JUMP 0 +#define GS_GDT_DESC 6 + + +BOOT_ROM_START + +run_multiboot: + + cli + cld + + mov %cs, %eax + shl $0x4, %eax + + /* set up a long jump descriptor that is PC relative */ + + /* move stack memory to %gs */ + mov %ss, %ecx + shl $0x4, %ecx + mov %esp, %ebx + add %ebx, %ecx + sub $0x20, %ecx + sub $0x30, %esp + shr $0x4, %ecx + mov %cx, %gs + + /* now push the indirect jump descriptor there */ + mov (prot_jump), %ebx + add %eax, %ebx + movl %ebx, %gs:GS_PROT_JUMP + mov $8, %bx + movw %bx, %gs:GS_PROT_JUMP + 4 + + /* fix the gdt descriptor to be PC relative */ + movw (gdt_desc), %bx + movw %bx, %gs:GS_GDT_DESC + movl (gdt_desc+2), %ebx + add %eax, %ebx + movl %ebx, %gs:GS_GDT_DESC + 2 + + xor %eax, %eax + mov %eax, %es + + /* Read the bootinfo struct into RAM */ + read_fw_blob(FW_CFG_INITRD) + + /* FS = bootinfo_struct */ + read_fw FW_CFG_INITRD_ADDR + shr $4, %eax + mov %ax, %fs + + /* Account for the EBDA in the multiboot structure's e801 + * map. + */ + int $0x12 + cwtl + movl %eax, %fs:4 + + /* ES = mmap_addr */ + mov %fs:48, %eax + shr $4, %eax + mov %ax, %es + + /* Initialize multiboot mmap structs using int 0x15(e820) */ + xor %ebx, %ebx + /* Start storing mmap data at %es:0 */ + xor %edi, %edi + +mmap_loop: + /* The multiboot entry size has offset -4, so leave some space */ + add $4, %di + /* entry size (mmap struct) & max buffer size (int15) */ + movl $20, %ecx + /* e820 */ + movl $0x0000e820, %eax + /* 'SMAP' magic */ + movl $0x534d4150, %edx + int $0x15 + +mmap_check_entry: + /* Error or last entry already done? */ + jb mmap_done + +mmap_store_entry: + /* store entry size */ + /* old as(1) doesn't like this insn so emit the bytes instead: + movl %ecx, %es:-4(%edi) + */ + .dc.b 0x26,0x67,0x66,0x89,0x4f,0xfc + + /* %edi += entry_size, store as mbs_mmap_length */ + add %ecx, %edi + movw %di, %fs:0x2c + + /* Continuation value 0 means last entry */ + test %ebx, %ebx + jnz mmap_loop + +mmap_done: + /* Calculate upper_mem field: The amount of memory between 1 MB and + the first upper memory hole. Get it from the mmap. */ + xor %di, %di + mov $0x100000, %edx +upper_mem_entry: + cmp %fs:0x2c, %di + je upper_mem_done + add $4, %di + + /* Skip if type != 1 */ + cmpl $1, %es:16(%di) + jne upper_mem_next + + /* Skip if > 4 GB */ + movl %es:4(%di), %eax + test %eax, %eax + jnz upper_mem_next + + /* Check for contiguous extension (base <= %edx < base + length) */ + movl %es:(%di), %eax + cmp %eax, %edx + jb upper_mem_next + addl %es:8(%di), %eax + cmp %eax, %edx + jae upper_mem_next + + /* If so, update %edx, and restart the search (mmap isn't ordered) */ + mov %eax, %edx + xor %di, %di + jmp upper_mem_entry + +upper_mem_next: + addl %es:-4(%di), %edi + jmp upper_mem_entry + +upper_mem_done: + sub $0x100000, %edx + shr $10, %edx + mov %edx, %fs:0x8 + +real_to_prot: + /* Load the GDT before going into protected mode */ +lgdt: + data32 lgdt %gs:GS_GDT_DESC + + /* get us to protected mode now */ + movl $1, %eax + movl %eax, %cr0 + + /* the LJMP sets CS for us and gets us to 32-bit */ +ljmp: + data32 ljmp *%gs:GS_PROT_JUMP + +prot_mode: +.code32 + + /* initialize all other segments */ + movl $0x10, %eax + movl %eax, %ss + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + + /* Read the kernel and modules into RAM */ + read_fw_blob(FW_CFG_KERNEL) + + /* Jump off to the kernel */ + read_fw FW_CFG_KERNEL_ENTRY + mov %eax, %ecx + + /* EBX contains a pointer to the bootinfo struct */ + read_fw FW_CFG_INITRD_ADDR + movl %eax, %ebx + + /* EAX has to contain the magic */ + movl $MULTIBOOT_MAGIC, %eax +ljmp2: + jmp *%ecx + +/* Variables */ +.align 4, 0 +prot_jump: .long prot_mode + .short 8 + +.align 4, 0 +gdt: + /* 0x00 */ +.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 + + /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 + + /* 0x18: code segment (base=0, limit=0x0ffff, type=16bit code exec/read/conf, DPL=0, 1b) */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00 + + /* 0x20: data segment (base=0, limit=0x0ffff, type=16bit data read/write, DPL=0, 1b) */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00 + +gdt_desc: +.short (5 * 8) - 1 +.long gdt + +BOOT_ROM_END diff --git a/qemu/pc-bios/optionrom/optionrom.h b/qemu/pc-bios/optionrom/optionrom.h new file mode 100644 index 000000000..f1a9021ec --- /dev/null +++ b/qemu/pc-bios/optionrom/optionrom.h @@ -0,0 +1,152 @@ +/* + * Common Option ROM Functions + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, see <http://www.gnu.org/licenses/>. + * + * Copyright Novell Inc, 2009 + * Authors: Alexander Graf <agraf@suse.de> + */ + + +#define NO_QEMU_PROTOS +#include "../../include/hw/nvram/fw_cfg.h" + +#define BIOS_CFG_IOPORT_CFG 0x510 +#define BIOS_CFG_IOPORT_DATA 0x511 + +/* Break the translation block flow so -d cpu shows us values */ +#define DEBUG_HERE \ + jmp 1f; \ + 1: + +/* + * Read a variable from the fw_cfg device. + * Clobbers: %edx + * Out: %eax + */ +.macro read_fw VAR + mov $\VAR, %ax + mov $BIOS_CFG_IOPORT_CFG, %dx + outw %ax, (%dx) + mov $BIOS_CFG_IOPORT_DATA, %dx + inb (%dx), %al + shl $8, %eax + inb (%dx), %al + shl $8, %eax + inb (%dx), %al + shl $8, %eax + inb (%dx), %al + bswap %eax +.endm + +#define read_fw_blob_pre(var) \ + read_fw var ## _SIZE; \ + mov %eax, %ecx; \ + mov $var ## _DATA, %ax; \ + mov $BIOS_CFG_IOPORT_CFG, %edx; \ + outw %ax, (%dx); \ + mov $BIOS_CFG_IOPORT_DATA, %dx; \ + cld + +/* + * Read a blob from the fw_cfg device. + * Requires _ADDR, _SIZE and _DATA values for the parameter. + * + * Clobbers: %eax, %edx, %es, %ecx, %edi + */ +#define read_fw_blob(var) \ + read_fw var ## _ADDR; \ + mov %eax, %edi; \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0xf3,0x6c + +/* + * Read a blob from the fw_cfg device in forced addr32 mode. + * Requires _ADDR, _SIZE and _DATA values for the parameter. + * + * Clobbers: %eax, %edx, %es, %ecx, %edi + */ +#define read_fw_blob_addr32(var) \ + read_fw var ## _ADDR; \ + mov %eax, %edi; \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + addr32 rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0x67,0xf3,0x6c + +/* + * Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi. + * Requires _SIZE and _DATA values for the parameter. + * + * Clobbers: %eax, %edx, %edi, %es, %ecx + */ +#define read_fw_blob_addr32_edi(var) \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + addr32 rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0x67,0xf3,0x6c + +#define OPTION_ROM_START \ + .code16; \ + .text; \ + .global _start; \ + _start:; \ + .short 0xaa55; \ + .byte (_end - _start) / 512; + +#define BOOT_ROM_START \ + OPTION_ROM_START \ + lret; \ + .org 0x18; \ + .short 0; \ + .short _pnph; \ + _pnph: \ + .ascii "$PnP"; \ + .byte 0x01; \ + .byte ( _pnph_len / 16 ); \ + .short 0x0000; \ + .byte 0x00; \ + .byte 0x00; \ + .long 0x00000000; \ + .short _manufacturer; \ + .short _product; \ + .long 0x00000000; \ + .short 0x0000; \ + .short 0x0000; \ + .short _bev; \ + .short 0x0000; \ + .short 0x0000; \ + .equ _pnph_len, . - _pnph; \ + _bev:; \ + /* DS = CS */ \ + movw %cs, %ax; \ + movw %ax, %ds; + +#define OPTION_ROM_END \ + .byte 0; \ + .align 512, 0; \ + _end: + +#define BOOT_ROM_END \ + _manufacturer:; \ + .asciz "QEMU"; \ + _product:; \ + .asciz BOOT_ROM_PRODUCT; \ + OPTION_ROM_END + |