From 64a86b092fc85d1ca0e1b44f5abe6ad6bd76831f Mon Sep 17 00:00:00 2001 From: Keir Fraser Date: Thu, 21 Aug 2008 15:18:05 +0100 Subject: [PATCH] hvmloader: Debug build incorporates HVM environment tests which must pass for the HVM guest to successfully boot. Currently only one test (REP INS across page boundaries) but more will be added in future. Signed-off-by: Keir Fraser --- tools/firmware/Rules.mk | 4 + tools/firmware/hvmloader/Makefile | 3 + tools/firmware/hvmloader/hvmloader.c | 2 + tools/firmware/hvmloader/tests.c | 164 +++++++++++++++++++++++++++ tools/firmware/hvmloader/util.h | 6 + 5 files changed, 179 insertions(+) create mode 100644 tools/firmware/hvmloader/tests.c diff --git a/tools/firmware/Rules.mk b/tools/firmware/Rules.mk index 47db139592..6cf0dfa2ac 100644 --- a/tools/firmware/Rules.mk +++ b/tools/firmware/Rules.mk @@ -6,6 +6,10 @@ CFLAGS := include $(XEN_ROOT)/tools/Rules.mk +ifneq ($(debug),y) +CFLAGS += -DNDEBUG +endif + CFLAGS += -Werror # Disable PIE/SSP if GCC supports them. They can break us. diff --git a/tools/firmware/hvmloader/Makefile b/tools/firmware/hvmloader/Makefile index 5988da6816..3d5f91f037 100644 --- a/tools/firmware/hvmloader/Makefile +++ b/tools/firmware/hvmloader/Makefile @@ -30,6 +30,9 @@ CFLAGS += $(CFLAGS_include) -I. SRCS = hvmloader.c mp_tables.c util.c smbios.c SRCS += 32bitbios_support.c smp.c cacheattr.c +ifeq ($(debug),y) +SRCS += tests.c +endif OBJS = $(patsubst %.c,%.o,$(SRCS)) .PHONY: all diff --git a/tools/firmware/hvmloader/hvmloader.c b/tools/firmware/hvmloader/hvmloader.c index f0e5816caa..decb1d2188 100644 --- a/tools/firmware/hvmloader/hvmloader.c +++ b/tools/firmware/hvmloader/hvmloader.c @@ -460,6 +460,8 @@ int main(void) smp_initialise(); + perform_tests(); + printf("Writing SMBIOS tables ...\n"); smbios_sz = hvm_write_smbios_tables(); diff --git a/tools/firmware/hvmloader/tests.c b/tools/firmware/hvmloader/tests.c new file mode 100644 index 0000000000..d3ee9cfa98 --- /dev/null +++ b/tools/firmware/hvmloader/tests.c @@ -0,0 +1,164 @@ +/* + * tests.c: HVM environment tests. + * + * Copyright (c) 2008, Citrix Systems, Inc. + * + * Authors: + * Keir Fraser + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "util.h" + +/* + * Memory layout during tests: + * 4MB to 8MB is cleared. + * Page directory resides at 8MB. + * 4 page table pages reside at 8MB+4kB to 8MB+20kB. + * Pagetables identity-map 0-16MB, except 4kB at va 6MB maps to pa 5MB. + */ +#define PD_START (8ul << 20) +#define PT_START (PD_START + 4096) + +static void setup_paging(void) +{ + uint32_t *pd = (uint32_t *)PD_START; + uint32_t *pt = (uint32_t *)PT_START; + uint32_t i; + + /* Identity map 0-16MB. */ + for ( i = 0; i < 4; i++ ) + pd[i] = (unsigned long)pt + (i<<12) + 3; + for ( i = 0; i < (4*1024); i++ ) + pt[i] = (i << 12) + 3; + + /* Page at virtual 6MB maps to physical 5MB. */ + pt[6u<<8] -= 0x100000u; +} + +static void start_paging(void) +{ + asm volatile ( + "mov %%eax,%%cr3; mov %%cr0,%%eax; " + "orl $0x80000000,%%eax; mov %%eax,%%cr0; " + "jmp 1f; 1:" + : : "a" (PD_START) : "memory" ); +} + +static void stop_paging(void) +{ + asm volatile ( + "mov %%cr0,%%eax; andl $0x7fffffff,%%eax; mov %%eax,%%cr0; " + "jmp 1f; 1:" + : : : "eax", "memory" ); +} + +/* + * rep_io_test: Tests REP INSB both forwards and backwards (EF.DF={0,1}) across + * a discontiguous page boundary. + */ +static int rep_io_test(void) +{ + uint32_t *p; + uint32_t i, p0, p1, p2; + int okay = 1; + + static const struct { + unsigned long addr; + uint32_t expected; + } check[] = { + { 0x00500000, 0x987654ff }, + { 0x00500ffc, 0xff000000 }, + { 0x005ffffc, 0xff000000 }, + { 0x00601000, 0x000000ff }, + { 0, 0 } + }; + + start_paging(); + + /* Phys 5MB = 0xdeadbeef */ + *(uint32_t *)0x500000ul = 0xdeadbeef; + + /* Phys 5MB = 0x98765432 */ + *(uint32_t *)0x600000ul = 0x98765432; + + /* Phys 0x5fffff = Phys 0x500000 = 0xff (byte) */ + asm volatile ( + "rep insb" + : "=d" (p0), "=c" (p1), "=D" (p2) + : "0" (0x5f), "1" (2), "2" (0x5ffffful) : "memory" ); + + /* Phys 0x500fff = Phys 0x601000 = 0xff (byte) */ + asm volatile ( + "std ; rep insb ; cld" + : "=d" (p0), "=c" (p1), "=D" (p2) + : "0" (0x5f), "1" (2), "2" (0x601000ul) : "memory" ); + + stop_paging(); + + i = 0; + for ( p = (uint32_t *)0x400000ul; p < (uint32_t *)0x700000ul; p++ ) + { + uint32_t expected = 0; + if ( check[i].addr == (unsigned long)p ) + { + expected = check[i].expected; + i++; + } + if ( *p != expected ) + { + printf("Bad value at 0x%08lx: saw %08x expected %08x\n", + (unsigned long)p, *p, expected); + okay = 0; + } + } + + return okay; +} + +void perform_tests(void) +{ + int i, passed; + + static struct { + int (* const test)(void); + const char *description; + } tests[] = { + { rep_io_test, "REP INSB across page boundaries" }, + { NULL, NULL } + }; + + printf("Testing HVM environment:\n"); + + passed = 0; + for ( i = 0; tests[i].test; i++ ) + { + printf(" - %s ... ", tests[i].description); + memset((char *)(4ul << 20), 0, 4ul << 20); + setup_paging(); + if ( (*tests[i].test)() ) + { + printf("passed\n"); + passed++; + } + else + { + printf("failed\n"); + } + } + + printf("Passed %d/%d tests\n", passed, i); + BUG_ON(passed != i); +} diff --git a/tools/firmware/hvmloader/util.h b/tools/firmware/hvmloader/util.h index 4d85e2cef9..957f25350e 100644 --- a/tools/firmware/hvmloader/util.h +++ b/tools/firmware/hvmloader/util.h @@ -143,6 +143,12 @@ void create_mp_tables(void); int hvm_write_smbios_tables(void); void smp_initialise(void); +#ifndef NDEBUG +void perform_tests(void); +#else +#define perform_tests() ((void)0) +#endif + #define isdigit(c) ((c) >= '0' && (c) <= '9') extern char _start[], _end[]; -- 2.30.2