2 * setup.c: Setup the world for vmxassist.
4 * Leendert van Doorn, leendert@watson.ibm.com
5 * Copyright (c) 2005, International Business Machines Corporation.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms and conditions of the GNU General Public License,
9 * version 2, as published by the Free Software Foundation.
11 * This program is distributed in the hope it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 * Place - Suite 330, Boston, MA 02111-1307 USA.
24 #if (VMXASSIST_BASE != TEXTADDR)
25 #error VMXAssist base mismatch
28 #define NR_PGD (PGSIZE / sizeof(unsigned))
30 #define min(a, b) ((a) > (b) ? (b) : (a))
32 /* Which CPU are we booting, and what is the initial CS segment? */
33 int booting_cpu, booting_vector;
35 unsigned long long gdt[] __attribute__ ((aligned(32))) = {
36 0x0000000000000000ULL, /* 0x00: reserved */
37 0x0000890000000000ULL, /* 0x08: 32-bit TSS */
38 0x00CF9A000000FFFFULL, /* 0x10: CS 32-bit */
39 0x00CF92000000FFFFULL, /* 0x18: DS 32-bit */
42 struct dtr gdtr = { sizeof(gdt)-1, (unsigned long) &gdt };
44 struct tss tss __attribute__ ((aligned(4)));
46 unsigned long long idt[NR_TRAPS] __attribute__ ((aligned(32)));
48 struct dtr idtr = { sizeof(idt)-1, (unsigned long) &idt };
50 struct vmx_assist_context oldctx;
51 struct vmx_assist_context newctx;
53 unsigned long memory_size;
54 int initialize_real_mode;
56 extern char stack_top[];
57 extern unsigned trap_handlers[];
62 printf("VMXAssist (%s)\n", __DATE__);
64 /* Bochs its way to convey memory size */
65 memory_size = ((get_cmos(0x35) << 8) | get_cmos(0x34)) << 6;
66 if (memory_size > 0x3bc000)
67 memory_size = 0x3bc000;
68 memory_size = (memory_size << 10) + 0xF00000;
69 if (memory_size <= 0xF00000)
71 (((get_cmos(0x31) << 8) | get_cmos(0x30)) + 0x400) << 10;
72 memory_size += 0x400 << 10; /* + 1MB */
74 printf("Memory size %ld MB\n", memory_size >> 20);
75 printf("E820 map:\n");
76 print_e820_map(HVM_E820, *HVM_E820_NR);
83 unsigned long long addr = (unsigned long long) &tss;
85 /* setup task state segment */
86 memset(&tss, 0, sizeof(tss));
87 tss.ss0 = DATA_SELECTOR;
88 tss.esp0 = (unsigned) stack_top;
89 tss.iomap_base = offsetof(struct tss, iomap);
90 tss.iomap[sizeof(tss.iomap)-1] = 0xff;
92 /* initialize gdt's tss selector */
93 gdt[TSS_SELECTOR / sizeof(gdt[0])] |=
94 ((addr & 0xFF000000) << (56-24)) |
95 ((addr & 0x00FF0000) << (32-16)) |
96 ((addr & 0x0000FFFF) << (16)) |
99 /* switch to our own gdt and set current tss */
100 __asm__ __volatile__ ("lgdt %0" : : "m" (gdtr));
101 __asm__ __volatile__ ("movl %%eax,%%ds;"
105 "movl %%eax,%%ss" : : "a" (DATA_SELECTOR));
107 __asm__ __volatile__ ("ljmp %0,$1f; 1:" : : "i" (CODE_SELECTOR));
109 __asm__ __volatile__ ("ltr %%ax" : : "a" (TSS_SELECTOR));
113 set_intr_gate(int i, unsigned handler)
115 unsigned long long addr = handler;
117 idt[i] = ((addr & 0xFFFF0000ULL) << 32) | (0x8E00ULL << 32) |
118 (addr & 0xFFFFULL) | (CODE_SELECTOR << 16);
126 for (i = 0; i < NR_TRAPS; i++)
127 set_intr_gate(i, trap_handlers[i]);
128 __asm__ __volatile__ ("lidt %0" : : "m" (idtr));
134 /* mask all interrupts */
135 outb(PIC_MASTER + PIC_IMR, 0xFF);
136 outb(PIC_SLAVE + PIC_IMR, 0xFF);
138 /* setup master PIC */
139 outb(PIC_MASTER + PIC_CMD, 0x11); /* edge triggered, cascade, ICW4 */
140 outb(PIC_MASTER + PIC_IMR, NR_EXCEPTION_HANDLER);
141 outb(PIC_MASTER + PIC_IMR, 1 << 2); /* slave on channel 2 */
142 outb(PIC_MASTER + PIC_IMR, 0x01);
144 /* setup slave PIC */
145 outb(PIC_SLAVE + PIC_CMD, 0x11); /* edge triggered, cascade, ICW4 */
146 outb(PIC_SLAVE + PIC_IMR, NR_EXCEPTION_HANDLER + 8);
147 outb(PIC_SLAVE + PIC_IMR, 0x02); /* slave identity is 2 */
148 outb(PIC_SLAVE + PIC_IMR, 0x01);
150 /* enable all interrupts */
151 outb(PIC_MASTER + PIC_IMR, 0);
152 outb(PIC_SLAVE + PIC_IMR, 0);
158 tss.iomap[port >> 3] |= 1 << (port & 7);
162 enter_real_mode(struct regs *regs)
164 /* mask off TSS busy bit */
165 gdt[TSS_SELECTOR / sizeof(gdt[0])] &= ~0x0000020000000000ULL;
167 /* start 8086 emulation of BIOS */
168 if (initialize_real_mode) {
169 initialize_real_mode = 0;
170 regs->eflags |= EFLAGS_VM | 0x02;
171 regs->ves = regs->vds = regs->vfs = regs->vgs = 0xF000;
172 if (booting_cpu == 0) {
173 regs->cs = 0xF000; /* ROM BIOS POST entry point */
176 regs->cs = booting_vector << 8; /* AP entry point */
180 regs->uesp = regs->uss = 0;
181 regs->eax = regs->ecx = regs->edx = regs->ebx = 0;
182 regs->esp = regs->ebp = regs->esi = regs->edi = 0;
184 /* intercept accesses to the PIC */
185 setiomap(PIC_MASTER+PIC_CMD);
186 setiomap(PIC_MASTER+PIC_IMR);
187 setiomap(PIC_SLAVE+PIC_CMD);
188 setiomap(PIC_SLAVE+PIC_IMR);
190 printf("Starting emulated 16-bit real-mode: ip=%04x:%04x\n",
191 regs->cs, regs->eip);
193 mode = VM86_REAL; /* becomes previous mode */
194 set_mode(regs, VM86_REAL);
196 /* this should get us into 16-bit mode */
200 /* go from protected to real mode */
201 set_mode(regs, VM86_PROTECTED_TO_REAL);
203 if (mode != VM86_REAL)
204 panic("failed to emulate between clear PE and long jump.\n");
208 * Setup the environment for VMX assist.
209 * This environment consists of flat segments (code and data),
210 * its own gdt, idt, and tr.
215 struct vmx_assist_context *c = &newctx;
217 memset(c, 0, sizeof(*c));
218 c->eip = (unsigned long) switch_to_real_mode;
219 c->esp = (unsigned) stack_top;
220 c->eflags = 0x2; /* no interrupts, please */
223 * Obviously, vmx assist is not running with CR0_PE disabled.
224 * The reason why the vmx assist cr0 has CR0.PE disabled is
225 * that a transtion to CR0.PE causes a world switch. It seems
226 * more natural to enable CR0.PE to cause a world switch to
227 * protected mode rather than disabling it.
229 c->cr0 = (get_cr0() | CR0_NE) & ~CR0_PE;
233 c->idtr_limit = sizeof(idt)-1;
234 c->idtr_base = (unsigned long) &idt;
236 c->gdtr_limit = sizeof(gdt)-1;
237 c->gdtr_base = (unsigned long) &gdt;
239 c->cs_sel = CODE_SELECTOR;
240 c->cs_limit = 0xFFFFFFFF;
242 c->cs_arbytes.fields.seg_type = 0xb;
243 c->cs_arbytes.fields.s = 1;
244 c->cs_arbytes.fields.dpl = 0;
245 c->cs_arbytes.fields.p = 1;
246 c->cs_arbytes.fields.avl = 0;
247 c->cs_arbytes.fields.default_ops_size = 1;
248 c->cs_arbytes.fields.g = 1;
250 c->ds_sel = DATA_SELECTOR;
251 c->ds_limit = 0xFFFFFFFF;
253 c->ds_arbytes = c->cs_arbytes;
254 c->ds_arbytes.fields.seg_type = 0x3;
256 c->es_sel = DATA_SELECTOR;
257 c->es_limit = 0xFFFFFFFF;
259 c->es_arbytes = c->ds_arbytes;
261 c->ss_sel = DATA_SELECTOR;
262 c->ss_limit = 0xFFFFFFFF;
264 c->ss_arbytes = c->ds_arbytes;
266 c->fs_sel = DATA_SELECTOR;
267 c->fs_limit = 0xFFFFFFFF;
269 c->fs_arbytes = c->ds_arbytes;
271 c->gs_sel = DATA_SELECTOR;
272 c->gs_limit = 0xFFFFFFFF;
274 c->gs_arbytes = c->ds_arbytes;
276 c->tr_sel = TSS_SELECTOR;
277 c->tr_limit = sizeof(tss) - 1;
278 c->tr_base = (unsigned long) &tss;
279 c->tr_arbytes.fields.seg_type = 0xb; /* 0x9 | 0x2 (busy) */
280 c->tr_arbytes.fields.s = 0;
281 c->tr_arbytes.fields.dpl = 0;
282 c->tr_arbytes.fields.p = 1;
283 c->tr_arbytes.fields.avl = 0;
284 c->tr_arbytes.fields.default_ops_size = 0;
285 c->tr_arbytes.fields.g = 0;
290 c->ldtr_arbytes = c->ds_arbytes;
291 c->ldtr_arbytes.fields.seg_type = 0x2;
292 c->ldtr_arbytes.fields.s = 0;
293 c->ldtr_arbytes.fields.dpl = 0;
294 c->ldtr_arbytes.fields.p = 1;
295 c->ldtr_arbytes.fields.avl = 0;
296 c->ldtr_arbytes.fields.default_ops_size = 0;
297 c->ldtr_arbytes.fields.g = 0;
301 * Start BIOS by causing a world switch to vmxassist, which causes
302 * VM8086 to be enabled and control is transfered to F000:FFF0.
307 if (booting_cpu == 0)
308 printf("Start BIOS ...\n");
310 printf("Start AP %d from %08x ...\n",
311 booting_cpu, booting_vector << 12);
313 initialize_real_mode = 1;
314 set_cr0(get_cr0() & ~CR0_PE);
315 panic("vmxassist returned"); /* "cannot happen" */
321 if (booting_cpu == 0)
327 set_cr4(get_cr4() | CR4_VME);
331 if (booting_cpu == 0)