spinup_ap.c revision 244167
1/*-
2 * Copyright (c) 2012 NetApp, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: projects/bhyve/usr.sbin/bhyve/spinup_ap.c 244167 2012-12-13 01:58:11Z grehan $
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: projects/bhyve/usr.sbin/bhyve/spinup_ap.c 244167 2012-12-13 01:58:11Z grehan $");
31
32#include <sys/param.h>
33#include <sys/types.h>
34
35#include <machine/vmm.h>
36#include <vmmapi.h>
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <assert.h>
41
42#include "bhyverun.h"
43#include "spinup_ap.h"
44
45/*
46 * Trampoline for hypervisor direct 64-bit jump.
47 *
48 *   0  - signature for guest->host verification
49 *   8  - kernel virtual address of trampoline
50 *  16  - instruction virtual address
51 *  24  - stack pointer virtual address
52 *  32  - CR3, physical address of kernel page table
53 *  40  - 24-byte area for null/code/data GDT entries
54 */
55#define MP_V64T_SIG	0xcafebabecafebabeULL
56struct mp_v64tramp {
57	uint64_t	mt_sig;
58	uint64_t	mt_virt;
59	uint64_t	mt_eip;
60	uint64_t	mt_rsp;
61	uint64_t	mt_cr3;
62	uint64_t	mt_gdtr[3];
63};
64
65static void
66spinup_ap_realmode(struct vmctx *ctx, int newcpu, uint64_t *rip)
67{
68	int vector, error;
69	uint16_t cs;
70	uint64_t desc_base;
71	uint32_t desc_limit, desc_access;
72
73	vector = *rip >> PAGE_SHIFT;
74	*rip = 0;
75
76	/*
77	 * Update the %cs and %rip of the guest so that it starts
78	 * executing real mode code at at 'vector << 12'.
79	 */
80	error = vm_set_register(ctx, newcpu, VM_REG_GUEST_RIP, *rip);
81	assert(error == 0);
82
83	error = vm_get_desc(ctx, newcpu, VM_REG_GUEST_CS, &desc_base,
84			    &desc_limit, &desc_access);
85	assert(error == 0);
86
87	desc_base = vector << PAGE_SHIFT;
88	error = vm_set_desc(ctx, newcpu, VM_REG_GUEST_CS,
89			    desc_base, desc_limit, desc_access);
90	assert(error == 0);
91
92	cs = (vector << PAGE_SHIFT) >> 4;
93	error = vm_set_register(ctx, newcpu, VM_REG_GUEST_CS, cs);
94	assert(error == 0);
95}
96
97static void
98spinup_ap_direct64(struct vmctx *ctx, int newcpu, uint64_t *rip)
99{
100	struct mp_v64tramp *mvt;
101	char *errstr;
102	int error;
103	uint64_t gdtbase;
104
105	mvt = paddr_guest2host(*rip);
106
107	assert(mvt->mt_sig == MP_V64T_SIG);
108
109	/*
110	 * Set up the 3-entry GDT using memory supplied in the
111	 * guest's trampoline structure.
112	 */
113	vm_setup_freebsd_gdt(mvt->mt_gdtr);
114
115#define  CHECK_ERROR(msg) \
116	if (error != 0) { \
117		errstr = msg; \
118		goto err_exit; \
119	}
120
121        /* entry point */
122	*rip = mvt->mt_eip;
123
124	/* Get the guest virtual address of the GDT */
125        gdtbase = mvt->mt_virt + __offsetof(struct mp_v64tramp, mt_gdtr);
126
127	error = vm_setup_freebsd_registers(ctx, newcpu, mvt->mt_eip,
128					   mvt->mt_cr3, gdtbase, mvt->mt_rsp);
129	CHECK_ERROR("vm_setup_freebsd_registers");
130
131	return;
132err_exit:
133	printf("spinup_ap_direct64: machine state error: %s", errstr);
134	exit(1);
135}
136
137int
138spinup_ap(struct vmctx *ctx, int vcpu, int newcpu, uint64_t rip)
139{
140	int error;
141
142	assert(newcpu != 0);
143	assert(newcpu < guest_ncpus);
144
145	error = vcpu_reset(ctx, newcpu);
146	assert(error == 0);
147
148	/* Set up capabilities */
149	if (fbsdrun_vmexit_on_hlt()) {
150		error = vm_set_capability(ctx, newcpu, VM_CAP_HALT_EXIT, 1);
151		assert(error == 0);
152	}
153
154	if (fbsdrun_vmexit_on_pause()) {
155		error = vm_set_capability(ctx, newcpu, VM_CAP_PAUSE_EXIT, 1);
156		assert(error == 0);
157	}
158
159	if (fbsdrun_disable_x2apic())
160		error = vm_set_x2apic_state(ctx, newcpu, X2APIC_DISABLED);
161	else
162		error = vm_set_x2apic_state(ctx, newcpu, X2APIC_ENABLED);
163	assert(error == 0);
164
165	/*
166	 * There are 2 startup modes possible here:
167	 *  - if the CPU supports 'unrestricted guest' mode, the spinup can
168	 *    set up the processor state in power-on 16-bit mode, with the CS:IP
169	 *    init'd to the specified low-mem 4K page.
170	 *  - if the guest has requested a 64-bit trampoline in the low-mem 4K
171	 *    page by placing in the specified signature, set up the register
172	 *    state using register state in the signature. Note that this
173	 *    requires accessing guest physical memory to read the signature
174	 *    while 'unrestricted mode' does not.
175	 */
176	error = vm_set_capability(ctx, newcpu, VM_CAP_UNRESTRICTED_GUEST, 1);
177	if (error) {
178		spinup_ap_direct64(ctx, newcpu, &rip);
179	} else {
180		spinup_ap_realmode(ctx, newcpu, &rip);
181	}
182
183	fbsdrun_addcpu(ctx, newcpu, rip);
184
185	return (newcpu);
186}
187