1 /*
2  * tests.c: HVM environment tests.
3  *
4  * Copyright (c) 2008, Citrix Systems, Inc.
5  *
6  * Authors:
7  *    Keir Fraser <keir@xen.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms and conditions of the GNU General Public License,
11  * version 2, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program; If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 #include "util.h"
24 #include <xen/arch-x86/hvm/start_info.h>
25 
26 #define TEST_FAIL 0
27 #define TEST_PASS 1
28 #define TEST_SKIP 2
29 
30 /*
31  * Memory layout during tests:
32  *  The 4MB block at test_mem_base is cleared.
33  *  Page directory resides at test_mem_base.
34  *  2 page table pages reside at test_mem_base+4kB to test_mem_base+12kB.
35  *  Pagetables identity-map 0-4MB and test_mem_base-test_mem_base+4MB,
36  *  except 4kB at va test_mem_base+2MB maps to pa test_mem_base+1MB.
37  */
38 static unsigned long test_mem_base;
39 #define TEST_MEM_SIZE (4ul << 20)
40 #define PD_START test_mem_base
41 #define PT_START (PD_START + 4096)
42 
setup_paging(void)43 static void setup_paging(void)
44 {
45     uint32_t *pd = (uint32_t *)PD_START;
46     uint32_t *pt = (uint32_t *)PT_START;
47     uint32_t i;
48 
49     /* Identity map [0,_end). */
50     for ( i = 0; i <= (unsigned long)(_end - 1) >> (PAGE_SHIFT + 10); ++i )
51     {
52         unsigned int j;
53 
54         pd[i] = (unsigned long)pt + 3;
55         for ( j = 0; j < PAGE_SIZE / sizeof(*pt); ++j )
56             *pt++ = (i << (PAGE_SHIFT + 10)) + (j << PAGE_SHIFT) + 3;
57     }
58 
59     /* Identity map TEST_MEM_SIZE @ test_mem_base. */
60     for ( i = 0; i < (TEST_MEM_SIZE >> (PAGE_SHIFT + 10)); ++i )
61         pd[i + (test_mem_base >> (PAGE_SHIFT + 10))] =
62             (unsigned long)pt + (i << PAGE_SHIFT) + 3;
63     for ( i = 0; i < (TEST_MEM_SIZE >> PAGE_SHIFT); ++i )
64         pt[i] = test_mem_base + (i << PAGE_SHIFT) + 3;
65 
66     /* Page at virtual test_mem_base+2MB maps physical test_mem_base+1MB. */
67     pt[0x200000 >> PAGE_SHIFT] -= 0x100000;
68 }
69 
start_paging(void)70 static void start_paging(void)
71 {
72     asm volatile (
73         "mov %%eax,%%cr3; mov %%cr0,%%eax; "
74         "orl $0x80000000,%%eax; mov %%eax,%%cr0; "
75         "jmp 1f; 1:"
76         : : "a" (PD_START) : "memory" );
77 }
78 
stop_paging(void)79 static void stop_paging(void)
80 {
81     asm volatile (
82         "mov %%cr0,%%eax; andl $0x7fffffff,%%eax; mov %%eax,%%cr0; "
83         "jmp 1f; 1:"
84         : : : "eax", "memory" );
85 }
86 
87 /*
88  * rep_insb_test: Tests REP INSB both forwards and backwards (EF.DF={0,1}) across
89  * a discontiguous page boundary.
90  */
rep_insb_test(void)91 static int rep_insb_test(void)
92 {
93     uint32_t *p;
94     uint32_t i, p0, p1, p2;
95     int okay = TEST_PASS;
96     const struct {
97         unsigned long addr;
98         uint32_t expected;
99     } check[] = {
100         { test_mem_base + 0x00100000, 0x987654ff },
101         { test_mem_base + 0x00100ffc, 0xff000000 },
102         { test_mem_base + 0x001ffffc, 0xff000000 },
103         { test_mem_base + 0x00201000, 0x000000ff },
104         { 0, 0 }
105     };
106 
107     start_paging();
108 
109     /* Phys 5MB = 0xdeadbeef */
110     *(uint32_t *)(test_mem_base + 0x100000) = 0xdeadbeef;
111 
112     /* Phys 6MB = 0x98765432 */
113     *(uint32_t *)(test_mem_base + 0x200000) = 0x98765432;
114 
115     /* Phys 0x5fffff = Phys 0x500000 = 0xff (byte) */
116     asm volatile (
117         "rep insb"
118         : "=d" (p0), "=c" (p1), "=D" (p2)
119         : "0" (0x5f), "1" (2), "2" (test_mem_base + 0x1fffff) : "memory" );
120 
121     /* Phys 0x500fff = Phys 0x601000 = 0xff (byte) */
122     asm volatile (
123         "std ; rep insb ; cld"
124         : "=d" (p0), "=c" (p1), "=D" (p2)
125         : "0" (0x5f), "1" (2), "2" (test_mem_base + 0x201000) : "memory" );
126 
127     stop_paging();
128 
129     i = 0;
130     for ( p = (uint32_t *)(test_mem_base + 0x0ff000);
131           p < (uint32_t *)(test_mem_base + 0x202000); p++ )
132     {
133         uint32_t expected = 0;
134         if ( check[i].addr == (unsigned long)p )
135         {
136             expected = check[i].expected;
137             i++;
138         }
139         if ( *p != expected )
140         {
141             printf("Bad value at 0x%08lx: saw %08x expected %08x\n",
142                    (unsigned long)p, *p, expected);
143             okay = TEST_FAIL;
144         }
145     }
146 
147     return okay;
148 }
149 
150 /*
151  * rep_insw_test: Tests REP INSW both forwards and backwards (EF.DF={0,1}) across
152  * a discontiguous page boundary.
153  */
rep_insw_test(void)154 static int rep_insw_test(void)
155 {
156     uint32_t *p;
157     unsigned int i;
158     int okay = TEST_PASS;
159     const struct {
160         unsigned long addr;
161         uint32_t expected;
162     } check[] = {
163         { test_mem_base + 0x00100000, 0x98ffffff },
164         { test_mem_base + 0x00100ffc, 0xffffff00 },
165         { test_mem_base + 0x001ffffc, 0xffffff00 },
166         { test_mem_base + 0x00201000, 0x00ffffff },
167         { 0, 0 }
168     };
169 
170     start_paging();
171 
172     /* Phys 5MB = 0xdeadbeef */
173     *(uint32_t *)(test_mem_base + 0x100000) = 0xdeadbeef;
174 
175     /* Phys 6MB = 0x98765432 */
176     *(uint32_t *)(test_mem_base + 0x200000) = 0x98765432;
177 
178     /* Phys 0x5ffffd...0x5fffff = Phys 0x500000...0x500002 = 0xff */
179     asm volatile (
180         "rep insw"
181         : "=c" (i), "=D" (i)
182         : "d" (0x5e), "0" (3), "1" (test_mem_base + 0x1ffffd) : "memory" );
183 
184     /* Phys 0x500ffd...0x500fff = Phys 0x601000...0x601002 = 0xff */
185     asm volatile (
186         "std ; rep insw ; cld"
187         : "=c" (i), "=D" (i)
188         : "d" (0x5e), "0" (3), "1" (test_mem_base + 0x201001) : "memory" );
189 
190     stop_paging();
191 
192     i = 0;
193     for ( p = (uint32_t *)(test_mem_base + 0x0ff000);
194           p < (uint32_t *)(test_mem_base + 0x202000); p++ )
195     {
196         uint32_t expected = 0;
197 
198         if ( check[i].addr == (unsigned long)p )
199         {
200             expected = check[i].expected;
201             i++;
202         }
203         if ( *p != expected )
204         {
205             printf("Bad value at 0x%08lx: saw %08x expected %08x\n",
206                    (unsigned long)p, *p, expected);
207             okay = TEST_FAIL;
208         }
209     }
210 
211     return okay;
212 }
213 
shadow_gs_test(void)214 static int shadow_gs_test(void)
215 {
216     uint64_t *pd = (uint64_t *)PD_START;
217     uint32_t i, eax, ebx, ecx, edx;
218 
219     /* Skip this test if the CPU does not support long mode. */
220     cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
221     if ( eax < 0x80000001 )
222         return TEST_SKIP;
223     cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
224     if ( !(edx & (1u<<29)) )
225         return TEST_SKIP;
226 
227     /* Long mode pagetable setup: Identity map [0,_end) with 2MB mappings. */
228     *pd = (unsigned long)pd + 0x1007; /* Level 4 */
229     pd += 512;
230     *pd = (unsigned long)pd + 0x1007; /* Level 3 */
231     pd += 512;
232     /* Level 2: */
233     for ( i = 0; i <= (unsigned long)(_end - 1) >> (PAGE_SHIFT + 9); i++ )
234         *pd++ = (i << (PAGE_SHIFT + 9)) + 0x1e3;
235 
236     asm volatile (
237         /* CR4.PAE=1 */
238         "mov $0x20,%%ebx; "
239         "mov %%ebx,%%cr4; "
240         /* CR3 */
241         "mov %%eax,%%cr3; "
242         /* EFER.LME=1 */
243         "mov $0xc0000080,%%ecx; rdmsr; btsl $8,%%eax; wrmsr; "
244         /* CR0.PG=1 */
245         "mov %%cr0,%%eax; btsl $31,%%eax; mov %%eax,%%cr0; "
246         "jmp 1f; 1: "
247         /* GS_BASE=2; SHADOW_GS_BASE=3 */
248         "mov $0xc0000101,%%ecx; xor %%edx,%%edx; mov $2,%%eax; wrmsr; "
249         "mov $0xc0000102,%%ecx; xor %%edx,%%edx; mov $3,%%eax; wrmsr; "
250         /* Push LRETQ stack frame. */
251         "pushl $0; pushl $"STR(SEL_CODE32)"; pushl $0; pushl $2f; "
252         /* Jump to 64-bit mode. */
253         "ljmp $"STR(SEL_CODE64)",$1f; 1: "
254         /* Swap GS_BASE and SHADOW_GS_BASE */
255         ".byte 0x0f,0x01,0xf8; " /* SWAPGS */
256         /* Jump to 32-bit mode. */
257         ".byte 0x89, 0xe4; "     /* MOV ESP,ESP */
258         ".byte 0x48, 0xcb; 2: "  /* LRETQ */
259         /* Read SHADOW_GS_BASE: should now contain 2 */
260         "mov $0xc0000102,%%ecx; rdmsr; mov %%eax,%%ebx; "
261         /* CR0.PG=0 */
262         "mov %%cr0,%%eax; btcl $31,%%eax; mov %%eax,%%cr0; "
263         "jmp 1f; 1:"
264         /* EFER.LME=0 */
265         "mov $0xc0000080,%%ecx; rdmsr; btcl $8,%%eax; wrmsr; "
266         /* CR4.PAE=0 */
267         "xor %%eax,%%eax; mov %%eax,%%cr4; "
268         : "=b" (ebx) : "a" (PD_START) : "ecx", "edx", "memory" );
269 
270     return (ebx == 2) ? TEST_PASS : TEST_FAIL;
271 }
272 
perform_tests(void)273 void perform_tests(void)
274 {
275     unsigned int i, passed, skipped;
276     static struct {
277         int (* const test)(void);
278         const char *description;
279     } tests[] = {
280         { rep_insb_test, "REP INSB across page boundaries" },
281         { rep_insw_test, "REP INSW across page boundaries" },
282         { shadow_gs_test, "GS base MSRs and SWAPGS" },
283         { NULL, NULL }
284     };
285 
286     printf("Testing HVM environment:\n");
287 
288     BUILD_BUG_ON(SCRATCH_PHYSICAL_ADDRESS > HVMLOADER_PHYSICAL_ADDRESS);
289 
290     if ( hvm_start_info->rsdp_paddr )
291     {
292         printf("Skipping tests due to non-zero RSDP address\n");
293         return;
294     }
295 
296     for ( ; ; test_mem_base += TEST_MEM_SIZE )
297     {
298         if ( hvm_info->low_mem_pgend <
299              ((test_mem_base + TEST_MEM_SIZE) >> PAGE_SHIFT) )
300         {
301             printf("Skipping tests due to insufficient memory (<%luMB)\n",
302                    (test_mem_base + TEST_MEM_SIZE) >> 20);
303             return;
304         }
305 
306         if ( (unsigned long)_end > test_mem_base )
307             continue;
308 
309         if ( hvm_start_info->cmdline_paddr &&
310              hvm_start_info->cmdline_paddr < test_mem_base + TEST_MEM_SIZE &&
311              ((hvm_start_info->cmdline_paddr +
312                strlen((char *)(uintptr_t)hvm_start_info->cmdline_paddr)) >=
313               test_mem_base) )
314             continue;
315 
316         if ( hvm_start_info->nr_modules )
317         {
318             const struct hvm_modlist_entry *modlist =
319                 (void *)(uintptr_t)hvm_start_info->modlist_paddr;
320 
321             if ( hvm_start_info->modlist_paddr > UINTPTR_MAX ||
322                  ((UINTPTR_MAX - (uintptr_t)modlist) / sizeof(*modlist) <
323                   hvm_start_info->nr_modules) )
324                 continue;
325 
326             if ( test_mem_base < (uintptr_t)(modlist +
327                                              hvm_start_info->nr_modules) &&
328                  (uintptr_t)modlist < test_mem_base + TEST_MEM_SIZE )
329                 continue;
330 
331             for ( i = 0; i < hvm_start_info->nr_modules; ++i )
332             {
333                 if ( test_mem_base < modlist[i].paddr + modlist[i].size &&
334                      modlist[i].paddr < test_mem_base + TEST_MEM_SIZE )
335                     break;
336 
337                 if ( modlist[i].cmdline_paddr &&
338                      modlist[i].cmdline_paddr < test_mem_base + TEST_MEM_SIZE &&
339                      ((modlist[i].cmdline_paddr +
340                        strlen((char *)(uintptr_t)modlist[i].cmdline_paddr)) >=
341                       test_mem_base) )
342                     break;
343             }
344             if ( i < hvm_start_info->nr_modules )
345                 continue;
346         }
347 
348         printf("Using scratch memory at %lx\n", test_mem_base);
349         break;
350     }
351 
352     passed = skipped = 0;
353     for ( i = 0; tests[i].test; i++ )
354     {
355         printf(" - %s ... ", tests[i].description);
356         memset((char *)test_mem_base, 0, TEST_MEM_SIZE);
357         setup_paging();
358         switch ( (*tests[i].test)() )
359         {
360         case TEST_PASS:
361             printf("passed\n");
362             passed++;
363             break;
364         case TEST_FAIL:
365             printf("failed\n");
366             break;
367         case TEST_SKIP:
368             printf("skipped\n");
369             skipped++;
370             break;
371         }
372     }
373 
374     printf("Passed %d of %d tests\n", passed, i);
375     if ( skipped != 0 )
376         printf("Skipped %d of %d tests\n", skipped, i);
377     if ( (passed + skipped) != i )
378     {
379         printf("FAILED %d of %d tests\n", i - passed - skipped, i);
380         BUG();
381     }
382 }
383 
384 /*
385  * Local variables:
386  * mode: C
387  * c-file-style: "BSD"
388  * c-basic-offset: 4
389  * tab-width: 4
390  * indent-tabs-mode: nil
391  * End:
392  */
393