Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
include config.mak
SRC_DIR := $(TOPSRC_DIR)/pc-bios/optionrom
VPATH = $(SRC_DIR)
all: multiboot_dma.bin linuxboot_dma.bin kvmvapic.bin pvh.bin
# Dummy command so that make thinks it has done something
@true
CFLAGS = -O2 -g
NULL :=
SPACE := $(NULL) #
TARGET_PREFIX := $(patsubst %/,%:$(SPACE),$(TARGET_DIR))
quiet-@ = $(if $(V),,@$(if $1,printf "%s\n" "$(TARGET_PREFIX)$1" && ))
quiet-command = $(call quiet-@,$2 $@)$1
# Flags for dependency generation
override CPPFLAGS += -MMD -MP -MT $@ -MF $(@D)/$(*F).d
override CFLAGS += -march=i486 -Wall $(EXTRA_CFLAGS) -m16
override CFLAGS += -ffreestanding -I$(TOPSRC_DIR)/include
cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null
cc-option = if $(call cc-test, $1); then \
echo "$(TARGET_PREFIX)$1 detected" && echo "override CFLAGS += $1" >&3; else \
echo "$(TARGET_PREFIX)$1 not detected" $(if $2,&& echo "override CFLAGS += $2" >&3); fi
# If -fcf-protection is enabled in flags or compiler defaults that will
# conflict with -march=i486
config-cc.mak: Makefile
$(quiet-@)($(call cc-option,-fcf-protection=none); \
$(call cc-option,-fno-pie); \
$(call cc-option,-no-pie); \
$(call cc-option,-fno-stack-protector); \
$(call cc-option,-Wno-array-bounds)) 3> config-cc.mak
-include config-cc.mak
override LDFLAGS = -nostdlib -Wl,--build-id=none,-T,$(SRC_DIR)/flat.lds,-m,elf_i386
pvh.img: pvh.o pvh_main.o
%.o: %.S
$(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<,Assembling)
%.o: %.c
$(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@,Compiling)
%.img: %.o
$(call quiet-command,$(CC) $(CFLAGS) $(LDFLAGS) -s -o $@ $^,Linking)
%.raw: %.img
$(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@,Extracting raw object)
%.bin: %.raw
$(call quiet-command,$(PYTHON) $(TOPSRC_DIR)/scripts/signrom.py $< $@,Computing checksum into)
include $(wildcard *.d)
clean:
rm -f *.o *.d *.raw *.img *.bin *~
distclean:
rm -f config-cc.mak
# suppress auto-removal of intermediate files
.SECONDARY:
.PHONY: all clean distclean
+6
View File
@@ -0,0 +1,6 @@
SECTIONS
{
. = 0;
.text : { *(.text) *(.text.$) }
}
ENTRY(_start)
+335
View File
@@ -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
+212
View File
@@ -0,0 +1,212 @@
/*
* Linux Boot Option ROM for fw_cfg DMA
*
* 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 (c) 2015-2016 Red Hat Inc.
* Authors:
* Marc Marí <[email protected]>
* Richard W.M. Jones <[email protected]>
*/
asm(
".text\n"
".global _start\n"
"_start:\n"
" .short 0xaa55\n"
" .byte 3\n" /* desired size in 512 units; signrom.py adds padding */
" .byte 0xcb\n" /* far return without prefix */
" .org 0x18\n"
" .short 0\n"
" .short _pnph\n"
"_pnph:\n"
" .ascii \"$PnP\"\n"
" .byte 0x01\n"
" .byte (_pnph_len / 16)\n"
" .short 0x0000\n"
" .byte 0x00\n"
" .byte 0x00\n"
" .long 0x00000000\n"
" .short _manufacturer\n"
" .short _product\n"
" .long 0x00000000\n"
" .short 0x0000\n"
" .short 0x0000\n"
" .short _bev\n"
" .short 0x0000\n"
" .short 0x0000\n"
" .equ _pnph_len, . - _pnph\n"
"_manufacturer:\n"
" .asciz \"QEMU\"\n"
"_product:\n"
" .asciz \"Linux loader DMA\"\n"
" .align 4, 0\n"
"_bev:\n"
" cli\n"
" cld\n"
" jmp load_kernel\n"
);
/*
* The includes of C headers must be after the asm block to avoid compiler
* errors.
*/
#include <stdint.h>
#include "optrom.h"
#include "optrom_fw_cfg.h"
static inline void set_es(void *addr)
{
uint32_t seg = (uint32_t)addr >> 4;
asm("movl %0, %%es" : : "r"(seg));
}
static inline uint16_t readw_es(uint16_t offset)
{
uint16_t val;
asm(ADDR32 "movw %%es:(%1), %0" : "=r"(val) : "r"((uint32_t)offset));
barrier();
return val;
}
static inline uint32_t readl_es(uint16_t offset)
{
uint32_t val;
asm(ADDR32 "movl %%es:(%1), %0" : "=r"(val) : "r"((uint32_t)offset));
barrier();
return val;
}
static inline void writel_es(uint16_t offset, uint32_t val)
{
barrier();
asm(ADDR32 "movl %0, %%es:(%1)" : : "r"(val), "r"((uint32_t)offset));
}
/* Return top of memory using BIOS function E801. */
static uint32_t get_e801_addr(void)
{
uint16_t ax, bx, cx, dx;
uint32_t ret;
asm("int $0x15\n"
: "=a"(ax), "=b"(bx), "=c"(cx), "=d"(dx)
: "a"(0xe801), "b"(0), "c"(0), "d"(0));
/* Not SeaBIOS, but in theory a BIOS could return CX=DX=0 in which
* case we need to use the result from AX & BX instead.
*/
if (cx == 0 && dx == 0) {
cx = ax;
dx = bx;
}
if (dx) {
/* DX = extended memory above 16M, in 64K units.
* Convert it to bytes and return.
*/
ret = ((uint32_t)dx + 256 /* 16M in 64K units */) << 16;
} else {
/* This is a fallback path for machines with <= 16MB of RAM,
* which probably would never be the case, but deal with it
* anyway.
*
* CX = extended memory between 1M and 16M, in kilobytes
* Convert it to bytes and return.
*/
ret = ((uint32_t)cx + 1024 /* 1M in K */) << 10;
}
return ret;
}
/* Force the asm name without leading underscore, even on Win32. */
extern void load_kernel(void) asm("load_kernel");
void load_kernel(void)
{
void *setup_addr;
void *initrd_addr;
void *kernel_addr;
void *cmdline_addr;
uint32_t setup_size;
uint32_t initrd_size;
uint32_t kernel_size;
uint32_t cmdline_size;
uint32_t initrd_end_page, max_allowed_page;
uint32_t segment_addr, stack_addr;
bios_cfg_read_entry_dma(&setup_addr, FW_CFG_SETUP_ADDR, 4);
bios_cfg_read_entry_dma(&setup_size, FW_CFG_SETUP_SIZE, 4);
bios_cfg_read_entry_dma(setup_addr, FW_CFG_SETUP_DATA, setup_size);
set_es(setup_addr);
/* For protocol < 0x203 we don't have initrd_max ... */
if (readw_es(0x206) < 0x203) {
/* ... so we assume initrd_max = 0x37ffffff. */
writel_es(0x22c, 0x37ffffff);
}
bios_cfg_read_entry_dma(&initrd_addr, FW_CFG_INITRD_ADDR, 4);
bios_cfg_read_entry_dma(&initrd_size, FW_CFG_INITRD_SIZE, 4);
initrd_end_page = ((uint32_t)(initrd_addr + initrd_size) & -4096);
max_allowed_page = (readl_es(0x22c) & -4096);
if (initrd_end_page != 0 && max_allowed_page != 0 &&
initrd_end_page != max_allowed_page) {
/* Initrd at the end of memory. Compute better initrd address
* based on e801 data
*/
initrd_addr = (void *)((get_e801_addr() - initrd_size) & -4096);
writel_es(0x218, (uint32_t)initrd_addr);
}
bios_cfg_read_entry_dma(initrd_addr, FW_CFG_INITRD_DATA, initrd_size);
bios_cfg_read_entry_dma(&kernel_addr, FW_CFG_KERNEL_ADDR, 4);
bios_cfg_read_entry_dma(&kernel_size, FW_CFG_KERNEL_SIZE, 4);
bios_cfg_read_entry_dma(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size);
bios_cfg_read_entry_dma(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4);
bios_cfg_read_entry_dma(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4);
bios_cfg_read_entry_dma(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size);
/* Boot linux */
segment_addr = ((uint32_t)setup_addr >> 4);
stack_addr = (uint32_t)(cmdline_addr - setup_addr - 16);
/* As we are changing critical registers, we cannot leave freedom to the
* compiler.
*/
asm("movw %%ax, %%ds\n"
"movw %%ax, %%es\n"
"movw %%ax, %%fs\n"
"movw %%ax, %%gs\n"
"movw %%ax, %%ss\n"
"movl %%ebx, %%esp\n"
"addw $0x20, %%ax\n"
"pushw %%ax\n" /* CS */
"pushw $0\n" /* IP */
/* Clear registers and jump to Linux */
"xor %%ebx, %%ebx\n"
"xor %%ecx, %%ecx\n"
"xor %%edx, %%edx\n"
"xor %%edi, %%edi\n"
"xor %%ebp, %%ebp\n"
"lretw\n"
: : "a"(segment_addr), "b"(stack_addr));
}
+232
View File
@@ -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_dma(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_dma(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 8, 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
+226
View File
@@ -0,0 +1,226 @@
/*
* 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 <[email protected]>
*/
#define FW_CFG_KERNEL_ADDR 0x07
#define FW_CFG_KERNEL_SIZE 0x08
#define FW_CFG_KERNEL_CMDLINE 0x09
#define FW_CFG_INITRD_ADDR 0x0a
#define FW_CFG_INITRD_SIZE 0x0b
#define FW_CFG_KERNEL_ENTRY 0x10
#define FW_CFG_KERNEL_DATA 0x11
#define FW_CFG_INITRD_DATA 0x12
#define FW_CFG_CMDLINE_ADDR 0x13
#define FW_CFG_CMDLINE_SIZE 0x14
#define FW_CFG_CMDLINE_DATA 0x15
#define FW_CFG_SETUP_ADDR 0x16
#define FW_CFG_SETUP_SIZE 0x17
#define FW_CFG_SETUP_DATA 0x18
#define BIOS_CFG_IOPORT_CFG 0x510
#define BIOS_CFG_IOPORT_DATA 0x511
#define FW_CFG_DMA_CTL_ERROR 0x01
#define FW_CFG_DMA_CTL_READ 0x02
#define FW_CFG_DMA_CTL_SKIP 0x04
#define FW_CFG_DMA_CTL_SELECT 0x08
#define FW_CFG_DMA_CTL_WRITE 0x10
#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */
#define BIOS_CFG_DMA_ADDR_HIGH 0x514
#define BIOS_CFG_DMA_ADDR_LOW 0x518
/* 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
/*
* Read data from the fw_cfg device using DMA.
* Clobbers: %edx, %eax, ADDR, SIZE, memory[%esp-16] to memory[%esp]
*/
.macro read_fw_dma VAR, SIZE, ADDR
/* Address */
bswapl \ADDR
pushl \ADDR
/* We only support 32 bit target addresses */
xorl %eax, %eax
pushl %eax
mov $BIOS_CFG_DMA_ADDR_HIGH, %dx
outl %eax, (%dx)
/* Size */
bswapl \SIZE
pushl \SIZE
/* Control */
movl $(\VAR << 16) | (FW_CFG_DMA_CTL_READ | FW_CFG_DMA_CTL_SELECT), %eax
bswapl %eax
pushl %eax
movl %esp, %eax /* Address of the struct we generated */
bswapl %eax
mov $BIOS_CFG_DMA_ADDR_LOW, %dx
outl %eax, (%dx) /* Initiate DMA */
1: mov (%esp), %eax /* Wait for completion */
bswapl %eax
testl $~FW_CFG_DMA_CTL_ERROR, %eax
jnz 1b
addl $16, %esp
.endm
/*
* Read a blob from the fw_cfg device using DMA
* Requires _ADDR, _SIZE and _DATA values for the parameter.
*
* Clobbers: %eax, %edx, %es, %ecx, %edi and adresses %esp-20 to %esp
*/
#define read_fw_blob_dma(var) \
read_fw var ## _SIZE; \
mov %eax, %ecx; \
read_fw var ## _ADDR; \
mov %eax, %edi ; \
read_fw_dma var ## _DATA, %ecx, %edi
#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
+110
View File
@@ -0,0 +1,110 @@
/*
* Common Option ROM Functions for C code
*
* 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 (c) 2015-2019 Red Hat Inc.
* Authors:
* Marc Marí <[email protected]>
* Richard W.M. Jones <[email protected]>
* Stefano Garzarella <[email protected]>
*/
#ifndef OPTROM_H
#define OPTROM_H
#include <stdint.h>
#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
#define barrier() asm("" : : : "memory")
#ifdef __clang__
#define ADDR32
#else
#define ADDR32 "addr32 "
#endif
static inline void outb(uint8_t value, uint16_t port)
{
asm volatile("outb %0, %w1" : : "a"(value), "Nd"(port));
}
static inline void outw(uint16_t value, uint16_t port)
{
asm volatile("outw %0, %w1" : : "a"(value), "Nd"(port));
}
static inline void outl(uint32_t value, uint16_t port)
{
asm volatile("outl %0, %w1" : : "a"(value), "Nd"(port));
}
static inline uint8_t inb(uint16_t port)
{
uint8_t value;
asm volatile("inb %w1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline uint16_t inw(uint16_t port)
{
uint16_t value;
asm volatile("inw %w1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline uint32_t inl(uint16_t port)
{
uint32_t value;
asm volatile("inl %w1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void insb(uint16_t port, uint8_t *buf, uint32_t len)
{
asm volatile("rep insb %%dx, %%es:(%%edi)"
: "+c"(len), "+D"(buf) : "d"(port) : "memory");
}
static inline uint32_t bswap32(uint32_t x)
{
asm("bswapl %0" : "=r" (x) : "0" (x));
return x;
}
static inline uint64_t bswap64(uint64_t x)
{
asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x));
return x;
}
static inline uint64_t cpu_to_be64(uint64_t x)
{
return bswap64(x);
}
static inline uint32_t cpu_to_be32(uint32_t x)
{
return bswap32(x);
}
static inline uint32_t be32_to_cpu(uint32_t x)
{
return bswap32(x);
}
#endif /* OPTROM_H */
+92
View File
@@ -0,0 +1,92 @@
/*
* Common Option ROM Functions for fw_cfg
*
* 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 (c) 2015-2019 Red Hat Inc.
* Authors:
* Marc Marí <[email protected]>
* Richard W.M. Jones <[email protected]>
* Stefano Garzarella <[email protected]>
*/
#ifndef OPTROM_FW_CFG_H
#define OPTROM_FW_CFG_H
#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
#define BIOS_CFG_IOPORT_CFG 0x510
#define BIOS_CFG_IOPORT_DATA 0x511
#define BIOS_CFG_DMA_ADDR_HIGH 0x514
#define BIOS_CFG_DMA_ADDR_LOW 0x518
static __attribute__((unused))
void bios_cfg_select(uint16_t key)
{
outw(key, BIOS_CFG_IOPORT_CFG);
}
static __attribute__((unused))
void bios_cfg_read_entry_io(void *buf, uint16_t entry, uint32_t len)
{
bios_cfg_select(entry);
insb(BIOS_CFG_IOPORT_DATA, buf, len);
}
/*
* clang is happy to inline this function, and bloats the
* ROM.
*/
static __attribute__((__noinline__)) __attribute__((unused))
void bios_cfg_read_entry_dma(void *buf, uint16_t entry, uint32_t len)
{
struct fw_cfg_dma_access access;
uint32_t control = (entry << 16) | FW_CFG_DMA_CTL_SELECT
| FW_CFG_DMA_CTL_READ;
access.address = cpu_to_be64((uint64_t)(uint32_t)buf);
access.length = cpu_to_be32(len);
access.control = cpu_to_be32(control);
barrier();
outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW);
while (be32_to_cpu(access.control) & ~FW_CFG_DMA_CTL_ERROR) {
barrier();
}
}
static __attribute__((unused))
void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len,
uint32_t version)
{
if (version & FW_CFG_VERSION_DMA) {
bios_cfg_read_entry_dma(buf, entry, len);
} else {
bios_cfg_read_entry_io(buf, entry, len);
}
}
static __attribute__((unused))
uint32_t bios_cfg_version(void)
{
uint32_t version;
bios_cfg_read_entry_io(&version, FW_CFG_ID, sizeof(version));
return version;
}
#endif /* OPTROM_FW_CFG_H */
+200
View File
@@ -0,0 +1,200 @@
/*
* PVH 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>
*
* Copyright (c) 2019 Red Hat Inc.
* Authors: Stefano Garzarella <sgarzare@redhat.com>
*/
#include "optionrom.h"
#define BOOT_ROM_PRODUCT "PVH loader"
#define GS_PROT_JUMP 0
#define GS_GDT_DESC 6
#ifdef OPTION_ROM_START
#undef OPTION_ROM_START
#endif
#ifdef OPTION_ROM_END
#undef OPTION_ROM_END
#endif
/*
* Redefine OPTION_ROM_START and OPTION_ROM_END, because this rom is produced
* linking multiple objects.
* signrom.py will add padding.
*/
#define OPTION_ROM_START \
.code16; \
.text; \
.global _start; \
_start:; \
.short 0xaa55; \
.byte 3; /* desired size in 512 units */
#define OPTION_ROM_END \
_end:
BOOT_ROM_START
run_pvhboot:
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
/* initialize HVM memmap table using int 0x15(e820) */
/* ES = pvh_e820 struct */
mov $pvh_e820, %eax
shr $4, %eax
mov %ax, %es
/* start storing memmap table at %es:8 (pvh_e820.table) */
mov $8,%edi
xor %ebx, %ebx
jmp memmap_loop
memmap_loop_check:
/* pvh_e820 can contains up to 128 entries */
cmp $128, %ebx
je memmap_done
memmap_loop:
/* entry size (hvm_memmap_table_entry) & max buffer size (int15) */
movl $24, %ecx
/* e820 */
movl $0x0000e820, %eax
/* 'SMAP' magic */
movl $0x534d4150, %edx
/* store counter value at %es:0 (pvh_e820.entries) */
movl %ebx, %es:0
int $0x15
/* error or last entry already done? */
jb memmap_err
/* %edi += entry size (hvm_memmap_table_entry) */
add $24, %edi
/* continuation value 0 means last entry */
test %ebx, %ebx
jnz memmap_loop_check
/* increase pvh_e820.entries to save the last entry */
movl %es:0, %ebx
inc %ebx
memmap_done:
movl %ebx, %es:0
memmap_err:
/* 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
jmp pvh_load_kernel
/* 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
+133
View File
@@ -0,0 +1,133 @@
/*
* PVH Option ROM for fw_cfg DMA
*
* 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 (c) 2019 Red Hat Inc.
* Authors:
* Stefano Garzarella <[email protected]>
*/
asm (".code32"); /* this code will be executed in protected mode */
#include <stddef.h>
#include <stdint.h>
#include "optrom.h"
#include "optrom_fw_cfg.h"
#include "../../include/hw/xen/start_info.h"
#define RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */
#define RSDP_AREA_ADDR 0x000E0000
#define RSDP_AREA_SIZE 0x00020000
#define EBDA_BASE_ADDR 0x0000040E
#define EBDA_SIZE 1024
#define E820_MAXENTRIES 128
#define CMDLINE_BUFSIZE 4096
/* e820 table filled in pvh.S using int 0x15 */
struct pvh_e820_table {
uint32_t entries;
uint32_t reserved;
struct hvm_memmap_table_entry table[E820_MAXENTRIES];
};
struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned));
static struct hvm_start_info start_info;
static struct hvm_modlist_entry ramdisk_mod;
static uint8_t cmdline_buffer[CMDLINE_BUFSIZE];
/* Search RSDP signature. */
static uintptr_t search_rsdp(uint32_t start_addr, uint32_t end_addr)
{
uint64_t *rsdp_p;
/* RSDP signature is always on a 16 byte boundary */
for (rsdp_p = (uint64_t *)start_addr; rsdp_p < (uint64_t *)end_addr;
rsdp_p += 2) {
if (*rsdp_p == RSDP_SIGNATURE) {
return (uintptr_t)rsdp_p;
}
}
return 0;
}
/* Force the asm name without leading underscore, even on Win32. */
extern void pvh_load_kernel(void) asm("pvh_load_kernel");
void pvh_load_kernel(void)
{
void *cmdline_addr = &cmdline_buffer;
void *kernel_entry, *initrd_addr;
uint32_t cmdline_size, initrd_size, fw_cfg_version = bios_cfg_version();
start_info.magic = XEN_HVM_START_MAGIC_VALUE;
start_info.version = 1;
/*
* pvh_e820 is filled in the pvh.S before to switch in protected mode,
* because we can use int 0x15 only in real mode.
*/
start_info.memmap_entries = pvh_e820.entries;
start_info.memmap_paddr = (uintptr_t)pvh_e820.table;
/*
* Search RSDP in the main BIOS area below 1 MB.
* SeaBIOS store the RSDP in this area, so we try it first.
*/
start_info.rsdp_paddr = search_rsdp(RSDP_AREA_ADDR,
RSDP_AREA_ADDR + RSDP_AREA_SIZE);
/* Search RSDP in the EBDA if it is not found */
if (!start_info.rsdp_paddr) {
/*
* Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes
* segment pointer to EBDA, so we must convert it to a linear address.
*/
uint32_t ebda_paddr = ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR)) << 4;
if (ebda_paddr > 0x400) {
uint32_t *ebda = (uint32_t *)ebda_paddr;
start_info.rsdp_paddr = search_rsdp(*ebda, *ebda + EBDA_SIZE);
}
}
bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4, fw_cfg_version);
bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size,
fw_cfg_version);
start_info.cmdline_paddr = (uintptr_t)cmdline_addr;
/* Check if we have the initrd to load */
bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4, fw_cfg_version);
if (initrd_size) {
bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4,
fw_cfg_version);
bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size,
fw_cfg_version);
ramdisk_mod.paddr = (uintptr_t)initrd_addr;
ramdisk_mod.size = initrd_size;
/* The first module is always ramdisk. */
start_info.modlist_paddr = (uintptr_t)&ramdisk_mod;
start_info.nr_modules = 1;
}
bios_cfg_read_entry(&kernel_entry, FW_CFG_KERNEL_ENTRY, 4, fw_cfg_version);
asm volatile("jmp *%1" : : "b"(&start_info), "c"(kernel_entry));
}