xref: /openbmc/qemu/hw/i386/vapic.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
111118c72SPhilippe Mathieu-Daudé /*
211118c72SPhilippe Mathieu-Daudé  * TPR optimization for 32-bit Windows guests (XP and Server 2003)
311118c72SPhilippe Mathieu-Daudé  *
411118c72SPhilippe Mathieu-Daudé  * Copyright (C) 2007-2008 Qumranet Technologies
511118c72SPhilippe Mathieu-Daudé  * Copyright (C) 2012      Jan Kiszka, Siemens AG
611118c72SPhilippe Mathieu-Daudé  *
711118c72SPhilippe Mathieu-Daudé  * This work is licensed under the terms of the GNU GPL version 2, or
811118c72SPhilippe Mathieu-Daudé  * (at your option) any later version. See the COPYING file in the
911118c72SPhilippe Mathieu-Daudé  * top-level directory.
1011118c72SPhilippe Mathieu-Daudé  */
1111118c72SPhilippe Mathieu-Daudé 
1211118c72SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
1311118c72SPhilippe Mathieu-Daudé #include "qemu/module.h"
1411118c72SPhilippe Mathieu-Daudé #include "sysemu/sysemu.h"
1511118c72SPhilippe Mathieu-Daudé #include "sysemu/cpus.h"
1611118c72SPhilippe Mathieu-Daudé #include "sysemu/hw_accel.h"
1711118c72SPhilippe Mathieu-Daudé #include "sysemu/kvm.h"
1811118c72SPhilippe Mathieu-Daudé #include "sysemu/runstate.h"
1911118c72SPhilippe Mathieu-Daudé #include "exec/address-spaces.h"
2011118c72SPhilippe Mathieu-Daudé #include "hw/i386/apic_internal.h"
2111118c72SPhilippe Mathieu-Daudé #include "hw/sysbus.h"
2211118c72SPhilippe Mathieu-Daudé #include "hw/boards.h"
2311118c72SPhilippe Mathieu-Daudé #include "migration/vmstate.h"
2411118c72SPhilippe Mathieu-Daudé #include "qom/object.h"
2511118c72SPhilippe Mathieu-Daudé 
2611118c72SPhilippe Mathieu-Daudé #define VAPIC_IO_PORT           0x7e
2711118c72SPhilippe Mathieu-Daudé 
2811118c72SPhilippe Mathieu-Daudé #define VAPIC_CPU_SHIFT         7
2911118c72SPhilippe Mathieu-Daudé 
3011118c72SPhilippe Mathieu-Daudé #define ROM_BLOCK_SIZE          512
3111118c72SPhilippe Mathieu-Daudé #define ROM_BLOCK_MASK          (~(ROM_BLOCK_SIZE - 1))
3211118c72SPhilippe Mathieu-Daudé 
3311118c72SPhilippe Mathieu-Daudé typedef enum VAPICMode {
3411118c72SPhilippe Mathieu-Daudé     VAPIC_INACTIVE = 0,
3511118c72SPhilippe Mathieu-Daudé     VAPIC_ACTIVE   = 1,
3611118c72SPhilippe Mathieu-Daudé     VAPIC_STANDBY  = 2,
3711118c72SPhilippe Mathieu-Daudé } VAPICMode;
3811118c72SPhilippe Mathieu-Daudé 
3911118c72SPhilippe Mathieu-Daudé typedef struct VAPICHandlers {
4011118c72SPhilippe Mathieu-Daudé     uint32_t set_tpr;
4111118c72SPhilippe Mathieu-Daudé     uint32_t set_tpr_eax;
4211118c72SPhilippe Mathieu-Daudé     uint32_t get_tpr[8];
4311118c72SPhilippe Mathieu-Daudé     uint32_t get_tpr_stack;
4411118c72SPhilippe Mathieu-Daudé } QEMU_PACKED VAPICHandlers;
4511118c72SPhilippe Mathieu-Daudé 
4611118c72SPhilippe Mathieu-Daudé typedef struct GuestROMState {
4711118c72SPhilippe Mathieu-Daudé     char signature[8];
4811118c72SPhilippe Mathieu-Daudé     uint32_t vaddr;
4911118c72SPhilippe Mathieu-Daudé     uint32_t fixup_start;
5011118c72SPhilippe Mathieu-Daudé     uint32_t fixup_end;
5111118c72SPhilippe Mathieu-Daudé     uint32_t vapic_vaddr;
5211118c72SPhilippe Mathieu-Daudé     uint32_t vapic_size;
5311118c72SPhilippe Mathieu-Daudé     uint32_t vcpu_shift;
5411118c72SPhilippe Mathieu-Daudé     uint32_t real_tpr_addr;
5511118c72SPhilippe Mathieu-Daudé     VAPICHandlers up;
5611118c72SPhilippe Mathieu-Daudé     VAPICHandlers mp;
5711118c72SPhilippe Mathieu-Daudé } QEMU_PACKED GuestROMState;
5811118c72SPhilippe Mathieu-Daudé 
5911118c72SPhilippe Mathieu-Daudé struct VAPICROMState {
6011118c72SPhilippe Mathieu-Daudé     SysBusDevice busdev;
6111118c72SPhilippe Mathieu-Daudé 
6211118c72SPhilippe Mathieu-Daudé     MemoryRegion io;
6311118c72SPhilippe Mathieu-Daudé     MemoryRegion rom;
6411118c72SPhilippe Mathieu-Daudé     uint32_t state;
6511118c72SPhilippe Mathieu-Daudé     uint32_t rom_state_paddr;
6611118c72SPhilippe Mathieu-Daudé     uint32_t rom_state_vaddr;
6711118c72SPhilippe Mathieu-Daudé     uint32_t vapic_paddr;
6811118c72SPhilippe Mathieu-Daudé     uint32_t real_tpr_addr;
6911118c72SPhilippe Mathieu-Daudé     GuestROMState rom_state;
7011118c72SPhilippe Mathieu-Daudé     size_t rom_size;
7111118c72SPhilippe Mathieu-Daudé     bool rom_mapped_writable;
7211118c72SPhilippe Mathieu-Daudé     VMChangeStateEntry *vmsentry;
7311118c72SPhilippe Mathieu-Daudé };
7411118c72SPhilippe Mathieu-Daudé 
7511118c72SPhilippe Mathieu-Daudé #define TYPE_VAPIC "kvmvapic"
7611118c72SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(VAPICROMState, VAPIC)
7711118c72SPhilippe Mathieu-Daudé 
7811118c72SPhilippe Mathieu-Daudé #define TPR_INSTR_ABS_MODRM             0x1
7911118c72SPhilippe Mathieu-Daudé #define TPR_INSTR_MATCH_MODRM_REG       0x2
8011118c72SPhilippe Mathieu-Daudé 
8111118c72SPhilippe Mathieu-Daudé typedef struct TPRInstruction {
8211118c72SPhilippe Mathieu-Daudé     uint8_t opcode;
8311118c72SPhilippe Mathieu-Daudé     uint8_t modrm_reg;
8411118c72SPhilippe Mathieu-Daudé     unsigned int flags;
8511118c72SPhilippe Mathieu-Daudé     TPRAccess access;
8611118c72SPhilippe Mathieu-Daudé     size_t length;
8711118c72SPhilippe Mathieu-Daudé     off_t addr_offset;
8811118c72SPhilippe Mathieu-Daudé } TPRInstruction;
8911118c72SPhilippe Mathieu-Daudé 
9011118c72SPhilippe Mathieu-Daudé /* must be sorted by length, shortest first */
9111118c72SPhilippe Mathieu-Daudé static const TPRInstruction tpr_instr[] = {
9211118c72SPhilippe Mathieu-Daudé     { /* mov abs to eax */
9311118c72SPhilippe Mathieu-Daudé         .opcode = 0xa1,
9411118c72SPhilippe Mathieu-Daudé         .access = TPR_ACCESS_READ,
9511118c72SPhilippe Mathieu-Daudé         .length = 5,
9611118c72SPhilippe Mathieu-Daudé         .addr_offset = 1,
9711118c72SPhilippe Mathieu-Daudé     },
9811118c72SPhilippe Mathieu-Daudé     { /* mov eax to abs */
9911118c72SPhilippe Mathieu-Daudé         .opcode = 0xa3,
10011118c72SPhilippe Mathieu-Daudé         .access = TPR_ACCESS_WRITE,
10111118c72SPhilippe Mathieu-Daudé         .length = 5,
10211118c72SPhilippe Mathieu-Daudé         .addr_offset = 1,
10311118c72SPhilippe Mathieu-Daudé     },
10411118c72SPhilippe Mathieu-Daudé     { /* mov r32 to r/m32 */
10511118c72SPhilippe Mathieu-Daudé         .opcode = 0x89,
10611118c72SPhilippe Mathieu-Daudé         .flags = TPR_INSTR_ABS_MODRM,
10711118c72SPhilippe Mathieu-Daudé         .access = TPR_ACCESS_WRITE,
10811118c72SPhilippe Mathieu-Daudé         .length = 6,
10911118c72SPhilippe Mathieu-Daudé         .addr_offset = 2,
11011118c72SPhilippe Mathieu-Daudé     },
11111118c72SPhilippe Mathieu-Daudé     { /* mov r/m32 to r32 */
11211118c72SPhilippe Mathieu-Daudé         .opcode = 0x8b,
11311118c72SPhilippe Mathieu-Daudé         .flags = TPR_INSTR_ABS_MODRM,
11411118c72SPhilippe Mathieu-Daudé         .access = TPR_ACCESS_READ,
11511118c72SPhilippe Mathieu-Daudé         .length = 6,
11611118c72SPhilippe Mathieu-Daudé         .addr_offset = 2,
11711118c72SPhilippe Mathieu-Daudé     },
11811118c72SPhilippe Mathieu-Daudé     { /* push r/m32 */
11911118c72SPhilippe Mathieu-Daudé         .opcode = 0xff,
12011118c72SPhilippe Mathieu-Daudé         .modrm_reg = 6,
12111118c72SPhilippe Mathieu-Daudé         .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG,
12211118c72SPhilippe Mathieu-Daudé         .access = TPR_ACCESS_READ,
12311118c72SPhilippe Mathieu-Daudé         .length = 6,
12411118c72SPhilippe Mathieu-Daudé         .addr_offset = 2,
12511118c72SPhilippe Mathieu-Daudé     },
12611118c72SPhilippe Mathieu-Daudé     { /* mov imm32, r/m32 (c7/0) */
12711118c72SPhilippe Mathieu-Daudé         .opcode = 0xc7,
12811118c72SPhilippe Mathieu-Daudé         .modrm_reg = 0,
12911118c72SPhilippe Mathieu-Daudé         .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG,
13011118c72SPhilippe Mathieu-Daudé         .access = TPR_ACCESS_WRITE,
13111118c72SPhilippe Mathieu-Daudé         .length = 10,
13211118c72SPhilippe Mathieu-Daudé         .addr_offset = 2,
13311118c72SPhilippe Mathieu-Daudé     },
13411118c72SPhilippe Mathieu-Daudé };
13511118c72SPhilippe Mathieu-Daudé 
read_guest_rom_state(VAPICROMState * s)13611118c72SPhilippe Mathieu-Daudé static void read_guest_rom_state(VAPICROMState *s)
13711118c72SPhilippe Mathieu-Daudé {
13811118c72SPhilippe Mathieu-Daudé     cpu_physical_memory_read(s->rom_state_paddr, &s->rom_state,
13911118c72SPhilippe Mathieu-Daudé                              sizeof(GuestROMState));
14011118c72SPhilippe Mathieu-Daudé }
14111118c72SPhilippe Mathieu-Daudé 
write_guest_rom_state(VAPICROMState * s)14211118c72SPhilippe Mathieu-Daudé static void write_guest_rom_state(VAPICROMState *s)
14311118c72SPhilippe Mathieu-Daudé {
14411118c72SPhilippe Mathieu-Daudé     cpu_physical_memory_write(s->rom_state_paddr, &s->rom_state,
14511118c72SPhilippe Mathieu-Daudé                               sizeof(GuestROMState));
14611118c72SPhilippe Mathieu-Daudé }
14711118c72SPhilippe Mathieu-Daudé 
update_guest_rom_state(VAPICROMState * s)14811118c72SPhilippe Mathieu-Daudé static void update_guest_rom_state(VAPICROMState *s)
14911118c72SPhilippe Mathieu-Daudé {
15011118c72SPhilippe Mathieu-Daudé     read_guest_rom_state(s);
15111118c72SPhilippe Mathieu-Daudé 
15211118c72SPhilippe Mathieu-Daudé     s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr);
15311118c72SPhilippe Mathieu-Daudé     s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT);
15411118c72SPhilippe Mathieu-Daudé 
15511118c72SPhilippe Mathieu-Daudé     write_guest_rom_state(s);
15611118c72SPhilippe Mathieu-Daudé }
15711118c72SPhilippe Mathieu-Daudé 
find_real_tpr_addr(VAPICROMState * s,CPUX86State * env)15811118c72SPhilippe Mathieu-Daudé static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env)
15911118c72SPhilippe Mathieu-Daudé {
16011118c72SPhilippe Mathieu-Daudé     CPUState *cs = env_cpu(env);
16111118c72SPhilippe Mathieu-Daudé     hwaddr paddr;
16211118c72SPhilippe Mathieu-Daudé     target_ulong addr;
16311118c72SPhilippe Mathieu-Daudé 
16411118c72SPhilippe Mathieu-Daudé     if (s->state == VAPIC_ACTIVE) {
16511118c72SPhilippe Mathieu-Daudé         return 0;
16611118c72SPhilippe Mathieu-Daudé     }
16711118c72SPhilippe Mathieu-Daudé     /*
16811118c72SPhilippe Mathieu-Daudé      * If there is no prior TPR access instruction we could analyze (which is
16911118c72SPhilippe Mathieu-Daudé      * the case after resume from hibernation), we need to scan the possible
17011118c72SPhilippe Mathieu-Daudé      * virtual address space for the APIC mapping.
17111118c72SPhilippe Mathieu-Daudé      */
17211118c72SPhilippe Mathieu-Daudé     for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) {
17311118c72SPhilippe Mathieu-Daudé         paddr = cpu_get_phys_page_debug(cs, addr);
17411118c72SPhilippe Mathieu-Daudé         if (paddr != APIC_DEFAULT_ADDRESS) {
17511118c72SPhilippe Mathieu-Daudé             continue;
17611118c72SPhilippe Mathieu-Daudé         }
17711118c72SPhilippe Mathieu-Daudé         s->real_tpr_addr = addr + 0x80;
17811118c72SPhilippe Mathieu-Daudé         update_guest_rom_state(s);
17911118c72SPhilippe Mathieu-Daudé         return 0;
18011118c72SPhilippe Mathieu-Daudé     }
18111118c72SPhilippe Mathieu-Daudé     return -1;
18211118c72SPhilippe Mathieu-Daudé }
18311118c72SPhilippe Mathieu-Daudé 
modrm_reg(uint8_t modrm)18411118c72SPhilippe Mathieu-Daudé static uint8_t modrm_reg(uint8_t modrm)
18511118c72SPhilippe Mathieu-Daudé {
18611118c72SPhilippe Mathieu-Daudé     return (modrm >> 3) & 7;
18711118c72SPhilippe Mathieu-Daudé }
18811118c72SPhilippe Mathieu-Daudé 
is_abs_modrm(uint8_t modrm)18911118c72SPhilippe Mathieu-Daudé static bool is_abs_modrm(uint8_t modrm)
19011118c72SPhilippe Mathieu-Daudé {
19111118c72SPhilippe Mathieu-Daudé     return (modrm & 0xc7) == 0x05;
19211118c72SPhilippe Mathieu-Daudé }
19311118c72SPhilippe Mathieu-Daudé 
opcode_matches(uint8_t * opcode,const TPRInstruction * instr)19411118c72SPhilippe Mathieu-Daudé static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr)
19511118c72SPhilippe Mathieu-Daudé {
19611118c72SPhilippe Mathieu-Daudé     return opcode[0] == instr->opcode &&
19711118c72SPhilippe Mathieu-Daudé         (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) &&
19811118c72SPhilippe Mathieu-Daudé         (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) ||
19911118c72SPhilippe Mathieu-Daudé          modrm_reg(opcode[1]) == instr->modrm_reg);
20011118c72SPhilippe Mathieu-Daudé }
20111118c72SPhilippe Mathieu-Daudé 
evaluate_tpr_instruction(VAPICROMState * s,X86CPU * cpu,target_ulong * pip,TPRAccess access)20211118c72SPhilippe Mathieu-Daudé static int evaluate_tpr_instruction(VAPICROMState *s, X86CPU *cpu,
20311118c72SPhilippe Mathieu-Daudé                                     target_ulong *pip, TPRAccess access)
20411118c72SPhilippe Mathieu-Daudé {
20511118c72SPhilippe Mathieu-Daudé     CPUState *cs = CPU(cpu);
20611118c72SPhilippe Mathieu-Daudé     const TPRInstruction *instr;
20711118c72SPhilippe Mathieu-Daudé     target_ulong ip = *pip;
20811118c72SPhilippe Mathieu-Daudé     uint8_t opcode[2];
20911118c72SPhilippe Mathieu-Daudé     uint32_t real_tpr_addr;
21011118c72SPhilippe Mathieu-Daudé     int i;
21111118c72SPhilippe Mathieu-Daudé 
21211118c72SPhilippe Mathieu-Daudé     if ((ip & 0xf0000000ULL) != 0x80000000ULL &&
21311118c72SPhilippe Mathieu-Daudé         (ip & 0xf0000000ULL) != 0xe0000000ULL) {
21411118c72SPhilippe Mathieu-Daudé         return -1;
21511118c72SPhilippe Mathieu-Daudé     }
21611118c72SPhilippe Mathieu-Daudé 
21711118c72SPhilippe Mathieu-Daudé     /*
21811118c72SPhilippe Mathieu-Daudé      * Early Windows 2003 SMP initialization contains a
21911118c72SPhilippe Mathieu-Daudé      *
22011118c72SPhilippe Mathieu-Daudé      *   mov imm32, r/m32
22111118c72SPhilippe Mathieu-Daudé      *
22211118c72SPhilippe Mathieu-Daudé      * instruction that is patched by TPR optimization. The problem is that
22311118c72SPhilippe Mathieu-Daudé      * RSP, used by the patched instruction, is zero, so the guest gets a
22411118c72SPhilippe Mathieu-Daudé      * double fault and dies.
22511118c72SPhilippe Mathieu-Daudé      */
22611118c72SPhilippe Mathieu-Daudé     if (cpu->env.regs[R_ESP] == 0) {
22711118c72SPhilippe Mathieu-Daudé         return -1;
22811118c72SPhilippe Mathieu-Daudé     }
22911118c72SPhilippe Mathieu-Daudé 
23011118c72SPhilippe Mathieu-Daudé     if (kvm_enabled() && !kvm_irqchip_in_kernel()) {
23111118c72SPhilippe Mathieu-Daudé         /*
23211118c72SPhilippe Mathieu-Daudé          * KVM without kernel-based TPR access reporting will pass an IP that
23311118c72SPhilippe Mathieu-Daudé          * points after the accessing instruction. So we need to look backward
23411118c72SPhilippe Mathieu-Daudé          * to find the reason.
23511118c72SPhilippe Mathieu-Daudé          */
23611118c72SPhilippe Mathieu-Daudé         for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) {
23711118c72SPhilippe Mathieu-Daudé             instr = &tpr_instr[i];
23811118c72SPhilippe Mathieu-Daudé             if (instr->access != access) {
23911118c72SPhilippe Mathieu-Daudé                 continue;
24011118c72SPhilippe Mathieu-Daudé             }
24111118c72SPhilippe Mathieu-Daudé             if (cpu_memory_rw_debug(cs, ip - instr->length, opcode,
24211118c72SPhilippe Mathieu-Daudé                                     sizeof(opcode), 0) < 0) {
24311118c72SPhilippe Mathieu-Daudé                 return -1;
24411118c72SPhilippe Mathieu-Daudé             }
24511118c72SPhilippe Mathieu-Daudé             if (opcode_matches(opcode, instr)) {
24611118c72SPhilippe Mathieu-Daudé                 ip -= instr->length;
24711118c72SPhilippe Mathieu-Daudé                 goto instruction_ok;
24811118c72SPhilippe Mathieu-Daudé             }
24911118c72SPhilippe Mathieu-Daudé         }
25011118c72SPhilippe Mathieu-Daudé         return -1;
25111118c72SPhilippe Mathieu-Daudé     } else {
25211118c72SPhilippe Mathieu-Daudé         if (cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0) < 0) {
25311118c72SPhilippe Mathieu-Daudé             return -1;
25411118c72SPhilippe Mathieu-Daudé         }
25511118c72SPhilippe Mathieu-Daudé         for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) {
25611118c72SPhilippe Mathieu-Daudé             instr = &tpr_instr[i];
25711118c72SPhilippe Mathieu-Daudé             if (opcode_matches(opcode, instr)) {
25811118c72SPhilippe Mathieu-Daudé                 goto instruction_ok;
25911118c72SPhilippe Mathieu-Daudé             }
26011118c72SPhilippe Mathieu-Daudé         }
26111118c72SPhilippe Mathieu-Daudé         return -1;
26211118c72SPhilippe Mathieu-Daudé     }
26311118c72SPhilippe Mathieu-Daudé 
26411118c72SPhilippe Mathieu-Daudé instruction_ok:
26511118c72SPhilippe Mathieu-Daudé     /*
26611118c72SPhilippe Mathieu-Daudé      * Grab the virtual TPR address from the instruction
26711118c72SPhilippe Mathieu-Daudé      * and update the cached values.
26811118c72SPhilippe Mathieu-Daudé      */
26911118c72SPhilippe Mathieu-Daudé     if (cpu_memory_rw_debug(cs, ip + instr->addr_offset,
27011118c72SPhilippe Mathieu-Daudé                             (void *)&real_tpr_addr,
27111118c72SPhilippe Mathieu-Daudé                             sizeof(real_tpr_addr), 0) < 0) {
27211118c72SPhilippe Mathieu-Daudé         return -1;
27311118c72SPhilippe Mathieu-Daudé     }
27411118c72SPhilippe Mathieu-Daudé     real_tpr_addr = le32_to_cpu(real_tpr_addr);
27511118c72SPhilippe Mathieu-Daudé     if ((real_tpr_addr & 0xfff) != 0x80) {
27611118c72SPhilippe Mathieu-Daudé         return -1;
27711118c72SPhilippe Mathieu-Daudé     }
27811118c72SPhilippe Mathieu-Daudé     s->real_tpr_addr = real_tpr_addr;
27911118c72SPhilippe Mathieu-Daudé     update_guest_rom_state(s);
28011118c72SPhilippe Mathieu-Daudé 
28111118c72SPhilippe Mathieu-Daudé     *pip = ip;
28211118c72SPhilippe Mathieu-Daudé     return 0;
28311118c72SPhilippe Mathieu-Daudé }
28411118c72SPhilippe Mathieu-Daudé 
update_rom_mapping(VAPICROMState * s,CPUX86State * env,target_ulong ip)28511118c72SPhilippe Mathieu-Daudé static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip)
28611118c72SPhilippe Mathieu-Daudé {
28711118c72SPhilippe Mathieu-Daudé     CPUState *cs = env_cpu(env);
28811118c72SPhilippe Mathieu-Daudé     hwaddr paddr;
28911118c72SPhilippe Mathieu-Daudé     uint32_t rom_state_vaddr;
29011118c72SPhilippe Mathieu-Daudé     uint32_t pos, patch, offset;
29111118c72SPhilippe Mathieu-Daudé 
29211118c72SPhilippe Mathieu-Daudé     /* nothing to do if already activated */
29311118c72SPhilippe Mathieu-Daudé     if (s->state == VAPIC_ACTIVE) {
29411118c72SPhilippe Mathieu-Daudé         return 0;
29511118c72SPhilippe Mathieu-Daudé     }
29611118c72SPhilippe Mathieu-Daudé 
29711118c72SPhilippe Mathieu-Daudé     /* bail out if ROM init code was not executed (missing ROM?) */
29811118c72SPhilippe Mathieu-Daudé     if (s->state == VAPIC_INACTIVE) {
29911118c72SPhilippe Mathieu-Daudé         return -1;
30011118c72SPhilippe Mathieu-Daudé     }
30111118c72SPhilippe Mathieu-Daudé 
30211118c72SPhilippe Mathieu-Daudé     /* find out virtual address of the ROM */
30311118c72SPhilippe Mathieu-Daudé     rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000);
30411118c72SPhilippe Mathieu-Daudé     paddr = cpu_get_phys_page_debug(cs, rom_state_vaddr);
30511118c72SPhilippe Mathieu-Daudé     if (paddr == -1) {
30611118c72SPhilippe Mathieu-Daudé         return -1;
30711118c72SPhilippe Mathieu-Daudé     }
30811118c72SPhilippe Mathieu-Daudé     paddr += rom_state_vaddr & ~TARGET_PAGE_MASK;
30911118c72SPhilippe Mathieu-Daudé     if (paddr != s->rom_state_paddr) {
31011118c72SPhilippe Mathieu-Daudé         return -1;
31111118c72SPhilippe Mathieu-Daudé     }
31211118c72SPhilippe Mathieu-Daudé     read_guest_rom_state(s);
31311118c72SPhilippe Mathieu-Daudé     if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) {
31411118c72SPhilippe Mathieu-Daudé         return -1;
31511118c72SPhilippe Mathieu-Daudé     }
31611118c72SPhilippe Mathieu-Daudé     s->rom_state_vaddr = rom_state_vaddr;
31711118c72SPhilippe Mathieu-Daudé 
31811118c72SPhilippe Mathieu-Daudé     /* fixup addresses in ROM if needed */
31911118c72SPhilippe Mathieu-Daudé     if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) {
32011118c72SPhilippe Mathieu-Daudé         return 0;
32111118c72SPhilippe Mathieu-Daudé     }
32211118c72SPhilippe Mathieu-Daudé     for (pos = le32_to_cpu(s->rom_state.fixup_start);
32311118c72SPhilippe Mathieu-Daudé          pos < le32_to_cpu(s->rom_state.fixup_end);
32411118c72SPhilippe Mathieu-Daudé          pos += 4) {
32511118c72SPhilippe Mathieu-Daudé         cpu_physical_memory_read(paddr + pos - s->rom_state.vaddr,
32611118c72SPhilippe Mathieu-Daudé                                  &offset, sizeof(offset));
32711118c72SPhilippe Mathieu-Daudé         offset = le32_to_cpu(offset);
32811118c72SPhilippe Mathieu-Daudé         cpu_physical_memory_read(paddr + offset, &patch, sizeof(patch));
32911118c72SPhilippe Mathieu-Daudé         patch = le32_to_cpu(patch);
33011118c72SPhilippe Mathieu-Daudé         patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr);
33111118c72SPhilippe Mathieu-Daudé         patch = cpu_to_le32(patch);
33211118c72SPhilippe Mathieu-Daudé         cpu_physical_memory_write(paddr + offset, &patch, sizeof(patch));
33311118c72SPhilippe Mathieu-Daudé     }
33411118c72SPhilippe Mathieu-Daudé     read_guest_rom_state(s);
33511118c72SPhilippe Mathieu-Daudé     s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) -
33611118c72SPhilippe Mathieu-Daudé         le32_to_cpu(s->rom_state.vaddr);
33711118c72SPhilippe Mathieu-Daudé 
33811118c72SPhilippe Mathieu-Daudé     return 0;
33911118c72SPhilippe Mathieu-Daudé }
34011118c72SPhilippe Mathieu-Daudé 
34111118c72SPhilippe Mathieu-Daudé /*
34211118c72SPhilippe Mathieu-Daudé  * Tries to read the unique processor number from the Kernel Processor Control
34311118c72SPhilippe Mathieu-Daudé  * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR
34411118c72SPhilippe Mathieu-Daudé  * cannot be accessed or is considered invalid. This also ensures that we are
34511118c72SPhilippe Mathieu-Daudé  * not patching the wrong guest.
34611118c72SPhilippe Mathieu-Daudé  */
get_kpcr_number(X86CPU * cpu)34711118c72SPhilippe Mathieu-Daudé static int get_kpcr_number(X86CPU *cpu)
34811118c72SPhilippe Mathieu-Daudé {
34911118c72SPhilippe Mathieu-Daudé     CPUX86State *env = &cpu->env;
35011118c72SPhilippe Mathieu-Daudé     struct kpcr {
35111118c72SPhilippe Mathieu-Daudé         uint8_t  fill1[0x1c];
35211118c72SPhilippe Mathieu-Daudé         uint32_t self;
35311118c72SPhilippe Mathieu-Daudé         uint8_t  fill2[0x31];
35411118c72SPhilippe Mathieu-Daudé         uint8_t  number;
35511118c72SPhilippe Mathieu-Daudé     } QEMU_PACKED kpcr;
35611118c72SPhilippe Mathieu-Daudé 
35711118c72SPhilippe Mathieu-Daudé     if (cpu_memory_rw_debug(CPU(cpu), env->segs[R_FS].base,
35811118c72SPhilippe Mathieu-Daudé                             (void *)&kpcr, sizeof(kpcr), 0) < 0 ||
35911118c72SPhilippe Mathieu-Daudé         kpcr.self != env->segs[R_FS].base) {
36011118c72SPhilippe Mathieu-Daudé         return -1;
36111118c72SPhilippe Mathieu-Daudé     }
36211118c72SPhilippe Mathieu-Daudé     return kpcr.number;
36311118c72SPhilippe Mathieu-Daudé }
36411118c72SPhilippe Mathieu-Daudé 
vapic_enable(VAPICROMState * s,X86CPU * cpu)36511118c72SPhilippe Mathieu-Daudé static int vapic_enable(VAPICROMState *s, X86CPU *cpu)
36611118c72SPhilippe Mathieu-Daudé {
36711118c72SPhilippe Mathieu-Daudé     int cpu_number = get_kpcr_number(cpu);
36811118c72SPhilippe Mathieu-Daudé     hwaddr vapic_paddr;
36911118c72SPhilippe Mathieu-Daudé     static const uint8_t enabled = 1;
37011118c72SPhilippe Mathieu-Daudé 
37111118c72SPhilippe Mathieu-Daudé     if (cpu_number < 0) {
37211118c72SPhilippe Mathieu-Daudé         return -1;
37311118c72SPhilippe Mathieu-Daudé     }
37411118c72SPhilippe Mathieu-Daudé     vapic_paddr = s->vapic_paddr +
37511118c72SPhilippe Mathieu-Daudé         (((hwaddr)cpu_number) << VAPIC_CPU_SHIFT);
37611118c72SPhilippe Mathieu-Daudé     cpu_physical_memory_write(vapic_paddr + offsetof(VAPICState, enabled),
37711118c72SPhilippe Mathieu-Daudé                               &enabled, sizeof(enabled));
37811118c72SPhilippe Mathieu-Daudé     apic_enable_vapic(cpu->apic_state, vapic_paddr);
37911118c72SPhilippe Mathieu-Daudé 
38011118c72SPhilippe Mathieu-Daudé     s->state = VAPIC_ACTIVE;
38111118c72SPhilippe Mathieu-Daudé 
38211118c72SPhilippe Mathieu-Daudé     return 0;
38311118c72SPhilippe Mathieu-Daudé }
38411118c72SPhilippe Mathieu-Daudé 
patch_byte(X86CPU * cpu,target_ulong addr,uint8_t byte)38511118c72SPhilippe Mathieu-Daudé static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte)
38611118c72SPhilippe Mathieu-Daudé {
38711118c72SPhilippe Mathieu-Daudé     cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1);
38811118c72SPhilippe Mathieu-Daudé }
38911118c72SPhilippe Mathieu-Daudé 
patch_call(X86CPU * cpu,target_ulong ip,uint32_t target)39011118c72SPhilippe Mathieu-Daudé static void patch_call(X86CPU *cpu, target_ulong ip, uint32_t target)
39111118c72SPhilippe Mathieu-Daudé {
39211118c72SPhilippe Mathieu-Daudé     uint32_t offset;
39311118c72SPhilippe Mathieu-Daudé 
39411118c72SPhilippe Mathieu-Daudé     offset = cpu_to_le32(target - ip - 5);
39511118c72SPhilippe Mathieu-Daudé     patch_byte(cpu, ip, 0xe8); /* call near */
39611118c72SPhilippe Mathieu-Daudé     cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1);
39711118c72SPhilippe Mathieu-Daudé }
39811118c72SPhilippe Mathieu-Daudé 
39911118c72SPhilippe Mathieu-Daudé typedef struct PatchInfo {
40011118c72SPhilippe Mathieu-Daudé     VAPICHandlers *handler;
40111118c72SPhilippe Mathieu-Daudé     target_ulong ip;
40211118c72SPhilippe Mathieu-Daudé } PatchInfo;
40311118c72SPhilippe Mathieu-Daudé 
do_patch_instruction(CPUState * cs,run_on_cpu_data data)40411118c72SPhilippe Mathieu-Daudé static void do_patch_instruction(CPUState *cs, run_on_cpu_data data)
40511118c72SPhilippe Mathieu-Daudé {
40611118c72SPhilippe Mathieu-Daudé     X86CPU *x86_cpu = X86_CPU(cs);
40711118c72SPhilippe Mathieu-Daudé     PatchInfo *info = (PatchInfo *) data.host_ptr;
40811118c72SPhilippe Mathieu-Daudé     VAPICHandlers *handlers = info->handler;
40911118c72SPhilippe Mathieu-Daudé     target_ulong ip = info->ip;
41011118c72SPhilippe Mathieu-Daudé     uint8_t opcode[2];
41111118c72SPhilippe Mathieu-Daudé     uint32_t imm32 = 0;
41211118c72SPhilippe Mathieu-Daudé 
41311118c72SPhilippe Mathieu-Daudé     cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0);
41411118c72SPhilippe Mathieu-Daudé 
41511118c72SPhilippe Mathieu-Daudé     switch (opcode[0]) {
41611118c72SPhilippe Mathieu-Daudé     case 0x89: /* mov r32 to r/m32 */
41711118c72SPhilippe Mathieu-Daudé         patch_byte(x86_cpu, ip, 0x50 + modrm_reg(opcode[1]));  /* push reg */
41811118c72SPhilippe Mathieu-Daudé         patch_call(x86_cpu, ip + 1, handlers->set_tpr);
41911118c72SPhilippe Mathieu-Daudé         break;
42011118c72SPhilippe Mathieu-Daudé     case 0x8b: /* mov r/m32 to r32 */
42111118c72SPhilippe Mathieu-Daudé         patch_byte(x86_cpu, ip, 0x90);
42211118c72SPhilippe Mathieu-Daudé         patch_call(x86_cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]);
42311118c72SPhilippe Mathieu-Daudé         break;
42411118c72SPhilippe Mathieu-Daudé     case 0xa1: /* mov abs to eax */
42511118c72SPhilippe Mathieu-Daudé         patch_call(x86_cpu, ip, handlers->get_tpr[0]);
42611118c72SPhilippe Mathieu-Daudé         break;
42711118c72SPhilippe Mathieu-Daudé     case 0xa3: /* mov eax to abs */
42811118c72SPhilippe Mathieu-Daudé         patch_call(x86_cpu, ip, handlers->set_tpr_eax);
42911118c72SPhilippe Mathieu-Daudé         break;
43011118c72SPhilippe Mathieu-Daudé     case 0xc7: /* mov imm32, r/m32 (c7/0) */
43111118c72SPhilippe Mathieu-Daudé         patch_byte(x86_cpu, ip, 0x68);  /* push imm32 */
43211118c72SPhilippe Mathieu-Daudé         cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0);
43311118c72SPhilippe Mathieu-Daudé         cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1);
43411118c72SPhilippe Mathieu-Daudé         patch_call(x86_cpu, ip + 5, handlers->set_tpr);
43511118c72SPhilippe Mathieu-Daudé         break;
43611118c72SPhilippe Mathieu-Daudé     case 0xff: /* push r/m32 */
43711118c72SPhilippe Mathieu-Daudé         patch_byte(x86_cpu, ip, 0x50); /* push eax */
43811118c72SPhilippe Mathieu-Daudé         patch_call(x86_cpu, ip + 1, handlers->get_tpr_stack);
43911118c72SPhilippe Mathieu-Daudé         break;
44011118c72SPhilippe Mathieu-Daudé     default:
44111118c72SPhilippe Mathieu-Daudé         abort();
44211118c72SPhilippe Mathieu-Daudé     }
44311118c72SPhilippe Mathieu-Daudé 
44411118c72SPhilippe Mathieu-Daudé     g_free(info);
44511118c72SPhilippe Mathieu-Daudé }
44611118c72SPhilippe Mathieu-Daudé 
patch_instruction(VAPICROMState * s,X86CPU * cpu,target_ulong ip)44711118c72SPhilippe Mathieu-Daudé static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
44811118c72SPhilippe Mathieu-Daudé {
44911118c72SPhilippe Mathieu-Daudé     MachineState *ms = MACHINE(qdev_get_machine());
45011118c72SPhilippe Mathieu-Daudé     CPUState *cs = CPU(cpu);
45111118c72SPhilippe Mathieu-Daudé     VAPICHandlers *handlers;
45211118c72SPhilippe Mathieu-Daudé     PatchInfo *info;
45311118c72SPhilippe Mathieu-Daudé 
45411118c72SPhilippe Mathieu-Daudé     if (ms->smp.cpus == 1) {
45511118c72SPhilippe Mathieu-Daudé         handlers = &s->rom_state.up;
45611118c72SPhilippe Mathieu-Daudé     } else {
45711118c72SPhilippe Mathieu-Daudé         handlers = &s->rom_state.mp;
45811118c72SPhilippe Mathieu-Daudé     }
45911118c72SPhilippe Mathieu-Daudé 
46011118c72SPhilippe Mathieu-Daudé     info  = g_new(PatchInfo, 1);
46111118c72SPhilippe Mathieu-Daudé     info->handler = handlers;
46211118c72SPhilippe Mathieu-Daudé     info->ip = ip;
46311118c72SPhilippe Mathieu-Daudé 
46411118c72SPhilippe Mathieu-Daudé     async_safe_run_on_cpu(cs, do_patch_instruction, RUN_ON_CPU_HOST_PTR(info));
46511118c72SPhilippe Mathieu-Daudé }
46611118c72SPhilippe Mathieu-Daudé 
vapic_report_tpr_access(DeviceState * dev,CPUState * cs,target_ulong ip,TPRAccess access)46711118c72SPhilippe Mathieu-Daudé void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip,
46811118c72SPhilippe Mathieu-Daudé                              TPRAccess access)
46911118c72SPhilippe Mathieu-Daudé {
47011118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = VAPIC(dev);
47111118c72SPhilippe Mathieu-Daudé     X86CPU *cpu = X86_CPU(cs);
47211118c72SPhilippe Mathieu-Daudé     CPUX86State *env = &cpu->env;
47311118c72SPhilippe Mathieu-Daudé 
47411118c72SPhilippe Mathieu-Daudé     cpu_synchronize_state(cs);
47511118c72SPhilippe Mathieu-Daudé 
47611118c72SPhilippe Mathieu-Daudé     if (evaluate_tpr_instruction(s, cpu, &ip, access) < 0) {
47711118c72SPhilippe Mathieu-Daudé         if (s->state == VAPIC_ACTIVE) {
47811118c72SPhilippe Mathieu-Daudé             vapic_enable(s, cpu);
47911118c72SPhilippe Mathieu-Daudé         }
48011118c72SPhilippe Mathieu-Daudé         return;
48111118c72SPhilippe Mathieu-Daudé     }
48211118c72SPhilippe Mathieu-Daudé     if (update_rom_mapping(s, env, ip) < 0) {
48311118c72SPhilippe Mathieu-Daudé         return;
48411118c72SPhilippe Mathieu-Daudé     }
48511118c72SPhilippe Mathieu-Daudé     if (vapic_enable(s, cpu) < 0) {
48611118c72SPhilippe Mathieu-Daudé         return;
48711118c72SPhilippe Mathieu-Daudé     }
48811118c72SPhilippe Mathieu-Daudé     patch_instruction(s, cpu, ip);
48911118c72SPhilippe Mathieu-Daudé }
49011118c72SPhilippe Mathieu-Daudé 
49111118c72SPhilippe Mathieu-Daudé typedef struct VAPICEnableTPRReporting {
49211118c72SPhilippe Mathieu-Daudé     DeviceState *apic;
49311118c72SPhilippe Mathieu-Daudé     bool enable;
49411118c72SPhilippe Mathieu-Daudé } VAPICEnableTPRReporting;
49511118c72SPhilippe Mathieu-Daudé 
vapic_do_enable_tpr_reporting(CPUState * cpu,run_on_cpu_data data)49611118c72SPhilippe Mathieu-Daudé static void vapic_do_enable_tpr_reporting(CPUState *cpu, run_on_cpu_data data)
49711118c72SPhilippe Mathieu-Daudé {
49811118c72SPhilippe Mathieu-Daudé     VAPICEnableTPRReporting *info = data.host_ptr;
49911118c72SPhilippe Mathieu-Daudé     apic_enable_tpr_access_reporting(info->apic, info->enable);
50011118c72SPhilippe Mathieu-Daudé }
50111118c72SPhilippe Mathieu-Daudé 
vapic_enable_tpr_reporting(bool enable)50211118c72SPhilippe Mathieu-Daudé static void vapic_enable_tpr_reporting(bool enable)
50311118c72SPhilippe Mathieu-Daudé {
50411118c72SPhilippe Mathieu-Daudé     VAPICEnableTPRReporting info = {
50511118c72SPhilippe Mathieu-Daudé         .enable = enable,
50611118c72SPhilippe Mathieu-Daudé     };
50711118c72SPhilippe Mathieu-Daudé     CPUState *cs;
50811118c72SPhilippe Mathieu-Daudé     X86CPU *cpu;
50911118c72SPhilippe Mathieu-Daudé 
51011118c72SPhilippe Mathieu-Daudé     CPU_FOREACH(cs) {
51111118c72SPhilippe Mathieu-Daudé         cpu = X86_CPU(cs);
51211118c72SPhilippe Mathieu-Daudé         info.apic = cpu->apic_state;
51311118c72SPhilippe Mathieu-Daudé         run_on_cpu(cs, vapic_do_enable_tpr_reporting, RUN_ON_CPU_HOST_PTR(&info));
51411118c72SPhilippe Mathieu-Daudé     }
51511118c72SPhilippe Mathieu-Daudé }
51611118c72SPhilippe Mathieu-Daudé 
vapic_reset(DeviceState * dev)51711118c72SPhilippe Mathieu-Daudé static void vapic_reset(DeviceState *dev)
51811118c72SPhilippe Mathieu-Daudé {
51911118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = VAPIC(dev);
52011118c72SPhilippe Mathieu-Daudé 
52111118c72SPhilippe Mathieu-Daudé     s->state = VAPIC_INACTIVE;
52211118c72SPhilippe Mathieu-Daudé     s->rom_state_paddr = 0;
52311118c72SPhilippe Mathieu-Daudé     vapic_enable_tpr_reporting(false);
52411118c72SPhilippe Mathieu-Daudé }
52511118c72SPhilippe Mathieu-Daudé 
52611118c72SPhilippe Mathieu-Daudé /*
52711118c72SPhilippe Mathieu-Daudé  * Set the IRQ polling hypercalls to the supported variant:
52811118c72SPhilippe Mathieu-Daudé  *  - vmcall if using KVM in-kernel irqchip
52911118c72SPhilippe Mathieu-Daudé  *  - 32-bit VAPIC port write otherwise
53011118c72SPhilippe Mathieu-Daudé  */
patch_hypercalls(VAPICROMState * s)53111118c72SPhilippe Mathieu-Daudé static int patch_hypercalls(VAPICROMState *s)
53211118c72SPhilippe Mathieu-Daudé {
53311118c72SPhilippe Mathieu-Daudé     hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK;
53411118c72SPhilippe Mathieu-Daudé     static const uint8_t vmcall_pattern[] = { /* vmcall */
53511118c72SPhilippe Mathieu-Daudé         0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1
53611118c72SPhilippe Mathieu-Daudé     };
53711118c72SPhilippe Mathieu-Daudé     static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */
53811118c72SPhilippe Mathieu-Daudé         0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e
53911118c72SPhilippe Mathieu-Daudé     };
54011118c72SPhilippe Mathieu-Daudé     uint8_t alternates[2];
54111118c72SPhilippe Mathieu-Daudé     const uint8_t *pattern;
54211118c72SPhilippe Mathieu-Daudé     const uint8_t *patch;
54311118c72SPhilippe Mathieu-Daudé     off_t pos;
54411118c72SPhilippe Mathieu-Daudé     uint8_t *rom;
54511118c72SPhilippe Mathieu-Daudé 
54611118c72SPhilippe Mathieu-Daudé     rom = g_malloc(s->rom_size);
54711118c72SPhilippe Mathieu-Daudé     cpu_physical_memory_read(rom_paddr, rom, s->rom_size);
54811118c72SPhilippe Mathieu-Daudé 
54911118c72SPhilippe Mathieu-Daudé     for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) {
55011118c72SPhilippe Mathieu-Daudé         if (kvm_irqchip_in_kernel()) {
55111118c72SPhilippe Mathieu-Daudé             pattern = outl_pattern;
55211118c72SPhilippe Mathieu-Daudé             alternates[0] = outl_pattern[7];
55311118c72SPhilippe Mathieu-Daudé             alternates[1] = outl_pattern[7];
55411118c72SPhilippe Mathieu-Daudé             patch = &vmcall_pattern[5];
55511118c72SPhilippe Mathieu-Daudé         } else {
55611118c72SPhilippe Mathieu-Daudé             pattern = vmcall_pattern;
55711118c72SPhilippe Mathieu-Daudé             alternates[0] = vmcall_pattern[7];
55811118c72SPhilippe Mathieu-Daudé             alternates[1] = 0xd9; /* AMD's VMMCALL */
55911118c72SPhilippe Mathieu-Daudé             patch = &outl_pattern[5];
56011118c72SPhilippe Mathieu-Daudé         }
56111118c72SPhilippe Mathieu-Daudé         if (memcmp(rom + pos, pattern, 7) == 0 &&
56211118c72SPhilippe Mathieu-Daudé             (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) {
56311118c72SPhilippe Mathieu-Daudé             cpu_physical_memory_write(rom_paddr + pos + 5, patch, 3);
56411118c72SPhilippe Mathieu-Daudé             /*
56511118c72SPhilippe Mathieu-Daudé              * Don't flush the tb here. Under ordinary conditions, the patched
56611118c72SPhilippe Mathieu-Daudé              * calls are miles away from the current IP. Under malicious
56711118c72SPhilippe Mathieu-Daudé              * conditions, the guest could trick us to crash.
56811118c72SPhilippe Mathieu-Daudé              */
56911118c72SPhilippe Mathieu-Daudé         }
57011118c72SPhilippe Mathieu-Daudé     }
57111118c72SPhilippe Mathieu-Daudé 
57211118c72SPhilippe Mathieu-Daudé     g_free(rom);
57311118c72SPhilippe Mathieu-Daudé     return 0;
57411118c72SPhilippe Mathieu-Daudé }
57511118c72SPhilippe Mathieu-Daudé 
57611118c72SPhilippe Mathieu-Daudé /*
57711118c72SPhilippe Mathieu-Daudé  * For TCG mode or the time KVM honors read-only memory regions, we need to
57811118c72SPhilippe Mathieu-Daudé  * enable write access to the option ROM so that variables can be updated by
57911118c72SPhilippe Mathieu-Daudé  * the guest.
58011118c72SPhilippe Mathieu-Daudé  */
vapic_map_rom_writable(VAPICROMState * s)58111118c72SPhilippe Mathieu-Daudé static int vapic_map_rom_writable(VAPICROMState *s)
58211118c72SPhilippe Mathieu-Daudé {
58311118c72SPhilippe Mathieu-Daudé     hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK;
58411118c72SPhilippe Mathieu-Daudé     MemoryRegionSection section;
58511118c72SPhilippe Mathieu-Daudé     MemoryRegion *mr = get_system_memory();
58611118c72SPhilippe Mathieu-Daudé     size_t rom_size;
58711118c72SPhilippe Mathieu-Daudé     uint8_t *ram;
58811118c72SPhilippe Mathieu-Daudé 
58911118c72SPhilippe Mathieu-Daudé     if (s->rom_mapped_writable) {
59011118c72SPhilippe Mathieu-Daudé         memory_region_del_subregion(mr, &s->rom);
59111118c72SPhilippe Mathieu-Daudé         object_unparent(OBJECT(&s->rom));
59211118c72SPhilippe Mathieu-Daudé     }
59311118c72SPhilippe Mathieu-Daudé 
59411118c72SPhilippe Mathieu-Daudé     /* grab RAM memory region (region @rom_paddr may still be pc.rom) */
59511118c72SPhilippe Mathieu-Daudé     section = memory_region_find(mr, 0, 1);
59611118c72SPhilippe Mathieu-Daudé 
59711118c72SPhilippe Mathieu-Daudé     /* read ROM size from RAM region */
59811118c72SPhilippe Mathieu-Daudé     if (rom_paddr + 2 >= memory_region_size(section.mr)) {
59911118c72SPhilippe Mathieu-Daudé         return -1;
60011118c72SPhilippe Mathieu-Daudé     }
60111118c72SPhilippe Mathieu-Daudé     ram = memory_region_get_ram_ptr(section.mr);
60211118c72SPhilippe Mathieu-Daudé     rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE;
60311118c72SPhilippe Mathieu-Daudé     if (rom_size == 0) {
60411118c72SPhilippe Mathieu-Daudé         return -1;
60511118c72SPhilippe Mathieu-Daudé     }
60611118c72SPhilippe Mathieu-Daudé     s->rom_size = rom_size;
60711118c72SPhilippe Mathieu-Daudé 
60811118c72SPhilippe Mathieu-Daudé     /* We need to round to avoid creating subpages
60911118c72SPhilippe Mathieu-Daudé      * from which we cannot run code. */
61011118c72SPhilippe Mathieu-Daudé     rom_size += rom_paddr & ~TARGET_PAGE_MASK;
61111118c72SPhilippe Mathieu-Daudé     rom_paddr &= TARGET_PAGE_MASK;
61211118c72SPhilippe Mathieu-Daudé     rom_size = TARGET_PAGE_ALIGN(rom_size);
61311118c72SPhilippe Mathieu-Daudé 
61411118c72SPhilippe Mathieu-Daudé     memory_region_init_alias(&s->rom, OBJECT(s), "kvmvapic-rom", section.mr,
61511118c72SPhilippe Mathieu-Daudé                              rom_paddr, rom_size);
61611118c72SPhilippe Mathieu-Daudé     memory_region_add_subregion_overlap(mr, rom_paddr, &s->rom, 1000);
61711118c72SPhilippe Mathieu-Daudé     s->rom_mapped_writable = true;
61811118c72SPhilippe Mathieu-Daudé     memory_region_unref(section.mr);
61911118c72SPhilippe Mathieu-Daudé 
62011118c72SPhilippe Mathieu-Daudé     return 0;
62111118c72SPhilippe Mathieu-Daudé }
62211118c72SPhilippe Mathieu-Daudé 
vapic_prepare(VAPICROMState * s)62311118c72SPhilippe Mathieu-Daudé static int vapic_prepare(VAPICROMState *s)
62411118c72SPhilippe Mathieu-Daudé {
62511118c72SPhilippe Mathieu-Daudé     if (vapic_map_rom_writable(s) < 0) {
62611118c72SPhilippe Mathieu-Daudé         return -1;
62711118c72SPhilippe Mathieu-Daudé     }
62811118c72SPhilippe Mathieu-Daudé 
62911118c72SPhilippe Mathieu-Daudé     if (patch_hypercalls(s) < 0) {
63011118c72SPhilippe Mathieu-Daudé         return -1;
63111118c72SPhilippe Mathieu-Daudé     }
63211118c72SPhilippe Mathieu-Daudé 
63311118c72SPhilippe Mathieu-Daudé     vapic_enable_tpr_reporting(true);
63411118c72SPhilippe Mathieu-Daudé 
63511118c72SPhilippe Mathieu-Daudé     return 0;
63611118c72SPhilippe Mathieu-Daudé }
63711118c72SPhilippe Mathieu-Daudé 
vapic_write(void * opaque,hwaddr addr,uint64_t data,unsigned int size)63811118c72SPhilippe Mathieu-Daudé static void vapic_write(void *opaque, hwaddr addr, uint64_t data,
63911118c72SPhilippe Mathieu-Daudé                         unsigned int size)
64011118c72SPhilippe Mathieu-Daudé {
64111118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = opaque;
64211118c72SPhilippe Mathieu-Daudé     X86CPU *cpu;
64311118c72SPhilippe Mathieu-Daudé     CPUX86State *env;
64411118c72SPhilippe Mathieu-Daudé     hwaddr rom_paddr;
64511118c72SPhilippe Mathieu-Daudé 
64611118c72SPhilippe Mathieu-Daudé     if (!current_cpu) {
64711118c72SPhilippe Mathieu-Daudé         return;
64811118c72SPhilippe Mathieu-Daudé     }
64911118c72SPhilippe Mathieu-Daudé 
65011118c72SPhilippe Mathieu-Daudé     cpu_synchronize_state(current_cpu);
65111118c72SPhilippe Mathieu-Daudé     cpu = X86_CPU(current_cpu);
65211118c72SPhilippe Mathieu-Daudé     env = &cpu->env;
65311118c72SPhilippe Mathieu-Daudé 
65411118c72SPhilippe Mathieu-Daudé     /*
65511118c72SPhilippe Mathieu-Daudé      * The VAPIC supports two PIO-based hypercalls, both via port 0x7E.
65611118c72SPhilippe Mathieu-Daudé      *  o 16-bit write access:
65711118c72SPhilippe Mathieu-Daudé      *    Reports the option ROM initialization to the hypervisor. Written
65811118c72SPhilippe Mathieu-Daudé      *    value is the offset of the state structure in the ROM.
65911118c72SPhilippe Mathieu-Daudé      *  o 8-bit write access:
66011118c72SPhilippe Mathieu-Daudé      *    Reactivates the VAPIC after a guest hibernation, i.e. after the
66111118c72SPhilippe Mathieu-Daudé      *    option ROM content has been re-initialized by a guest power cycle.
66211118c72SPhilippe Mathieu-Daudé      *  o 32-bit write access:
66311118c72SPhilippe Mathieu-Daudé      *    Poll for pending IRQs, considering the current VAPIC state.
66411118c72SPhilippe Mathieu-Daudé      */
66511118c72SPhilippe Mathieu-Daudé     switch (size) {
66611118c72SPhilippe Mathieu-Daudé     case 2:
66711118c72SPhilippe Mathieu-Daudé         if (s->state == VAPIC_INACTIVE) {
66811118c72SPhilippe Mathieu-Daudé             rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK;
66911118c72SPhilippe Mathieu-Daudé             s->rom_state_paddr = rom_paddr + data;
67011118c72SPhilippe Mathieu-Daudé 
67111118c72SPhilippe Mathieu-Daudé             s->state = VAPIC_STANDBY;
67211118c72SPhilippe Mathieu-Daudé         }
67311118c72SPhilippe Mathieu-Daudé         if (vapic_prepare(s) < 0) {
67411118c72SPhilippe Mathieu-Daudé             s->state = VAPIC_INACTIVE;
67511118c72SPhilippe Mathieu-Daudé             s->rom_state_paddr = 0;
67611118c72SPhilippe Mathieu-Daudé             break;
67711118c72SPhilippe Mathieu-Daudé         }
67811118c72SPhilippe Mathieu-Daudé         break;
67911118c72SPhilippe Mathieu-Daudé     case 1:
68011118c72SPhilippe Mathieu-Daudé         if (kvm_enabled()) {
68111118c72SPhilippe Mathieu-Daudé             /*
68211118c72SPhilippe Mathieu-Daudé              * Disable triggering instruction in ROM by writing a NOP.
68311118c72SPhilippe Mathieu-Daudé              *
68411118c72SPhilippe Mathieu-Daudé              * We cannot do this in TCG mode as the reported IP is not
68511118c72SPhilippe Mathieu-Daudé              * accurate.
68611118c72SPhilippe Mathieu-Daudé              */
68711118c72SPhilippe Mathieu-Daudé             pause_all_vcpus();
68811118c72SPhilippe Mathieu-Daudé             patch_byte(cpu, env->eip - 2, 0x66);
68911118c72SPhilippe Mathieu-Daudé             patch_byte(cpu, env->eip - 1, 0x90);
69011118c72SPhilippe Mathieu-Daudé             resume_all_vcpus();
69111118c72SPhilippe Mathieu-Daudé         }
69211118c72SPhilippe Mathieu-Daudé 
69311118c72SPhilippe Mathieu-Daudé         if (s->state == VAPIC_ACTIVE) {
69411118c72SPhilippe Mathieu-Daudé             break;
69511118c72SPhilippe Mathieu-Daudé         }
69611118c72SPhilippe Mathieu-Daudé         if (update_rom_mapping(s, env, env->eip) < 0) {
69711118c72SPhilippe Mathieu-Daudé             break;
69811118c72SPhilippe Mathieu-Daudé         }
69911118c72SPhilippe Mathieu-Daudé         if (find_real_tpr_addr(s, env) < 0) {
70011118c72SPhilippe Mathieu-Daudé             break;
70111118c72SPhilippe Mathieu-Daudé         }
70211118c72SPhilippe Mathieu-Daudé         vapic_enable(s, cpu);
70311118c72SPhilippe Mathieu-Daudé         break;
70411118c72SPhilippe Mathieu-Daudé     default:
70511118c72SPhilippe Mathieu-Daudé     case 4:
70611118c72SPhilippe Mathieu-Daudé         if (!kvm_irqchip_in_kernel()) {
70711118c72SPhilippe Mathieu-Daudé             apic_poll_irq(cpu->apic_state);
70811118c72SPhilippe Mathieu-Daudé         }
70911118c72SPhilippe Mathieu-Daudé         break;
71011118c72SPhilippe Mathieu-Daudé     }
71111118c72SPhilippe Mathieu-Daudé }
71211118c72SPhilippe Mathieu-Daudé 
vapic_read(void * opaque,hwaddr addr,unsigned size)71311118c72SPhilippe Mathieu-Daudé static uint64_t vapic_read(void *opaque, hwaddr addr, unsigned size)
71411118c72SPhilippe Mathieu-Daudé {
71511118c72SPhilippe Mathieu-Daudé     return 0xffffffff;
71611118c72SPhilippe Mathieu-Daudé }
71711118c72SPhilippe Mathieu-Daudé 
71811118c72SPhilippe Mathieu-Daudé static const MemoryRegionOps vapic_ops = {
71911118c72SPhilippe Mathieu-Daudé     .write = vapic_write,
72011118c72SPhilippe Mathieu-Daudé     .read = vapic_read,
72111118c72SPhilippe Mathieu-Daudé     .endianness = DEVICE_NATIVE_ENDIAN,
72211118c72SPhilippe Mathieu-Daudé };
72311118c72SPhilippe Mathieu-Daudé 
vapic_realize(DeviceState * dev,Error ** errp)72411118c72SPhilippe Mathieu-Daudé static void vapic_realize(DeviceState *dev, Error **errp)
72511118c72SPhilippe Mathieu-Daudé {
72611118c72SPhilippe Mathieu-Daudé     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
72711118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = VAPIC(dev);
72811118c72SPhilippe Mathieu-Daudé 
72911118c72SPhilippe Mathieu-Daudé     memory_region_init_io(&s->io, OBJECT(s), &vapic_ops, s, "kvmvapic", 2);
73011118c72SPhilippe Mathieu-Daudé     memory_region_add_subregion(get_system_io(), VAPIC_IO_PORT, &s->io);
73111118c72SPhilippe Mathieu-Daudé     sysbus_init_ioports(sbd, VAPIC_IO_PORT, 2);
73211118c72SPhilippe Mathieu-Daudé 
73311118c72SPhilippe Mathieu-Daudé     option_rom[nb_option_roms].name = "kvmvapic.bin";
73411118c72SPhilippe Mathieu-Daudé     option_rom[nb_option_roms].bootindex = -1;
73511118c72SPhilippe Mathieu-Daudé     nb_option_roms++;
73611118c72SPhilippe Mathieu-Daudé }
73711118c72SPhilippe Mathieu-Daudé 
do_vapic_enable(CPUState * cs,run_on_cpu_data data)73811118c72SPhilippe Mathieu-Daudé static void do_vapic_enable(CPUState *cs, run_on_cpu_data data)
73911118c72SPhilippe Mathieu-Daudé {
74011118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = data.host_ptr;
74111118c72SPhilippe Mathieu-Daudé     X86CPU *cpu = X86_CPU(cs);
74211118c72SPhilippe Mathieu-Daudé 
74311118c72SPhilippe Mathieu-Daudé     static const uint8_t enabled = 1;
74411118c72SPhilippe Mathieu-Daudé     cpu_physical_memory_write(s->vapic_paddr + offsetof(VAPICState, enabled),
74511118c72SPhilippe Mathieu-Daudé                               &enabled, sizeof(enabled));
74611118c72SPhilippe Mathieu-Daudé     apic_enable_vapic(cpu->apic_state, s->vapic_paddr);
74711118c72SPhilippe Mathieu-Daudé     s->state = VAPIC_ACTIVE;
74811118c72SPhilippe Mathieu-Daudé }
74911118c72SPhilippe Mathieu-Daudé 
vapic_vm_state_change(void * opaque,bool running,RunState state)75011118c72SPhilippe Mathieu-Daudé static void vapic_vm_state_change(void *opaque, bool running, RunState state)
75111118c72SPhilippe Mathieu-Daudé {
75211118c72SPhilippe Mathieu-Daudé     MachineState *ms = MACHINE(qdev_get_machine());
75311118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = opaque;
75411118c72SPhilippe Mathieu-Daudé     uint8_t *zero;
75511118c72SPhilippe Mathieu-Daudé 
75611118c72SPhilippe Mathieu-Daudé     if (!running) {
75711118c72SPhilippe Mathieu-Daudé         return;
75811118c72SPhilippe Mathieu-Daudé     }
75911118c72SPhilippe Mathieu-Daudé 
76011118c72SPhilippe Mathieu-Daudé     if (s->state == VAPIC_ACTIVE) {
76111118c72SPhilippe Mathieu-Daudé         if (ms->smp.cpus == 1) {
76211118c72SPhilippe Mathieu-Daudé             run_on_cpu(first_cpu, do_vapic_enable, RUN_ON_CPU_HOST_PTR(s));
76311118c72SPhilippe Mathieu-Daudé         } else {
76411118c72SPhilippe Mathieu-Daudé             zero = g_malloc0(s->rom_state.vapic_size);
76511118c72SPhilippe Mathieu-Daudé             cpu_physical_memory_write(s->vapic_paddr, zero,
76611118c72SPhilippe Mathieu-Daudé                                       s->rom_state.vapic_size);
76711118c72SPhilippe Mathieu-Daudé             g_free(zero);
76811118c72SPhilippe Mathieu-Daudé         }
76911118c72SPhilippe Mathieu-Daudé     }
77011118c72SPhilippe Mathieu-Daudé 
77111118c72SPhilippe Mathieu-Daudé     qemu_del_vm_change_state_handler(s->vmsentry);
77211118c72SPhilippe Mathieu-Daudé     s->vmsentry = NULL;
77311118c72SPhilippe Mathieu-Daudé }
77411118c72SPhilippe Mathieu-Daudé 
vapic_post_load(void * opaque,int version_id)77511118c72SPhilippe Mathieu-Daudé static int vapic_post_load(void *opaque, int version_id)
77611118c72SPhilippe Mathieu-Daudé {
77711118c72SPhilippe Mathieu-Daudé     VAPICROMState *s = opaque;
77811118c72SPhilippe Mathieu-Daudé 
77911118c72SPhilippe Mathieu-Daudé     /*
78011118c72SPhilippe Mathieu-Daudé      * The old implementation of qemu-kvm did not provide the state
78111118c72SPhilippe Mathieu-Daudé      * VAPIC_STANDBY. Reconstruct it.
78211118c72SPhilippe Mathieu-Daudé      */
78311118c72SPhilippe Mathieu-Daudé     if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) {
78411118c72SPhilippe Mathieu-Daudé         s->state = VAPIC_STANDBY;
78511118c72SPhilippe Mathieu-Daudé     }
78611118c72SPhilippe Mathieu-Daudé 
78711118c72SPhilippe Mathieu-Daudé     if (s->state != VAPIC_INACTIVE) {
78811118c72SPhilippe Mathieu-Daudé         if (vapic_prepare(s) < 0) {
78911118c72SPhilippe Mathieu-Daudé             return -1;
79011118c72SPhilippe Mathieu-Daudé         }
79111118c72SPhilippe Mathieu-Daudé     }
79211118c72SPhilippe Mathieu-Daudé 
79311118c72SPhilippe Mathieu-Daudé     if (!s->vmsentry) {
79411118c72SPhilippe Mathieu-Daudé         s->vmsentry =
79511118c72SPhilippe Mathieu-Daudé             qemu_add_vm_change_state_handler(vapic_vm_state_change, s);
79611118c72SPhilippe Mathieu-Daudé     }
79711118c72SPhilippe Mathieu-Daudé     return 0;
79811118c72SPhilippe Mathieu-Daudé }
79911118c72SPhilippe Mathieu-Daudé 
80011118c72SPhilippe Mathieu-Daudé static const VMStateDescription vmstate_handlers = {
80111118c72SPhilippe Mathieu-Daudé     .name = "kvmvapic-handlers",
80211118c72SPhilippe Mathieu-Daudé     .version_id = 1,
80311118c72SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
80411118c72SPhilippe Mathieu-Daudé     .fields = (const VMStateField[]) {
80511118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(set_tpr, VAPICHandlers),
80611118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(set_tpr_eax, VAPICHandlers),
80711118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8),
80811118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(get_tpr_stack, VAPICHandlers),
80911118c72SPhilippe Mathieu-Daudé         VMSTATE_END_OF_LIST()
81011118c72SPhilippe Mathieu-Daudé     }
81111118c72SPhilippe Mathieu-Daudé };
81211118c72SPhilippe Mathieu-Daudé 
81311118c72SPhilippe Mathieu-Daudé static const VMStateDescription vmstate_guest_rom = {
81411118c72SPhilippe Mathieu-Daudé     .name = "kvmvapic-guest-rom",
81511118c72SPhilippe Mathieu-Daudé     .version_id = 1,
81611118c72SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
81711118c72SPhilippe Mathieu-Daudé     .fields = (const VMStateField[]) {
81811118c72SPhilippe Mathieu-Daudé         VMSTATE_UNUSED(8),     /* signature */
81911118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(vaddr, GuestROMState),
82011118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(fixup_start, GuestROMState),
82111118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(fixup_end, GuestROMState),
82211118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(vapic_vaddr, GuestROMState),
82311118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(vapic_size, GuestROMState),
82411118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(vcpu_shift, GuestROMState),
82511118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(real_tpr_addr, GuestROMState),
82611118c72SPhilippe Mathieu-Daudé         VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
82711118c72SPhilippe Mathieu-Daudé         VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers),
82811118c72SPhilippe Mathieu-Daudé         VMSTATE_END_OF_LIST()
82911118c72SPhilippe Mathieu-Daudé     }
83011118c72SPhilippe Mathieu-Daudé };
83111118c72SPhilippe Mathieu-Daudé 
83211118c72SPhilippe Mathieu-Daudé static const VMStateDescription vmstate_vapic = {
83311118c72SPhilippe Mathieu-Daudé     .name = "kvm-tpr-opt",      /* compatible with qemu-kvm VAPIC */
83411118c72SPhilippe Mathieu-Daudé     .version_id = 1,
83511118c72SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
83611118c72SPhilippe Mathieu-Daudé     .post_load = vapic_post_load,
83711118c72SPhilippe Mathieu-Daudé     .fields = (const VMStateField[]) {
83811118c72SPhilippe Mathieu-Daudé         VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom,
83911118c72SPhilippe Mathieu-Daudé                        GuestROMState),
84011118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(state, VAPICROMState),
84111118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(real_tpr_addr, VAPICROMState),
84211118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(rom_state_vaddr, VAPICROMState),
84311118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(vapic_paddr, VAPICROMState),
84411118c72SPhilippe Mathieu-Daudé         VMSTATE_UINT32(rom_state_paddr, VAPICROMState),
84511118c72SPhilippe Mathieu-Daudé         VMSTATE_END_OF_LIST()
84611118c72SPhilippe Mathieu-Daudé     }
84711118c72SPhilippe Mathieu-Daudé };
84811118c72SPhilippe Mathieu-Daudé 
vapic_class_init(ObjectClass * klass,void * data)84911118c72SPhilippe Mathieu-Daudé static void vapic_class_init(ObjectClass *klass, void *data)
85011118c72SPhilippe Mathieu-Daudé {
85111118c72SPhilippe Mathieu-Daudé     DeviceClass *dc = DEVICE_CLASS(klass);
85211118c72SPhilippe Mathieu-Daudé 
853*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, vapic_reset);
85411118c72SPhilippe Mathieu-Daudé     dc->vmsd    = &vmstate_vapic;
85511118c72SPhilippe Mathieu-Daudé     dc->realize = vapic_realize;
85611118c72SPhilippe Mathieu-Daudé }
85711118c72SPhilippe Mathieu-Daudé 
85811118c72SPhilippe Mathieu-Daudé static const TypeInfo vapic_type = {
85911118c72SPhilippe Mathieu-Daudé     .name          = TYPE_VAPIC,
86011118c72SPhilippe Mathieu-Daudé     .parent        = TYPE_SYS_BUS_DEVICE,
86111118c72SPhilippe Mathieu-Daudé     .instance_size = sizeof(VAPICROMState),
86211118c72SPhilippe Mathieu-Daudé     .class_init    = vapic_class_init,
86311118c72SPhilippe Mathieu-Daudé };
86411118c72SPhilippe Mathieu-Daudé 
vapic_register(void)86511118c72SPhilippe Mathieu-Daudé static void vapic_register(void)
86611118c72SPhilippe Mathieu-Daudé {
86711118c72SPhilippe Mathieu-Daudé     type_register_static(&vapic_type);
86811118c72SPhilippe Mathieu-Daudé }
86911118c72SPhilippe Mathieu-Daudé 
87011118c72SPhilippe Mathieu-Daudé type_init(vapic_register);
871