From 56e9db683be09f411dec3d0fe4f0afb433be709b Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:12:52 +0000 Subject: [PATCH 1/6] [HVM] qemu: Add guest address-space mapping cache. On IA32 host or IA32 PAE host, at present, generally, we can't create an HVM guest with more than 2G memory, because generally it's almost impossible for Qemu to find a large enough and consecutive virtual address space to map an HVM guest's whole physical address space. The attached patch fixes this issue using dynamic mapping based on little blocks of memory. Signed-off-by: Jun Nakajima Signed-off-by: Dexuan Cui Signed-off-by: Keir Fraser --- tools/ioemu/target-i386-dm/cpu.h | 3 +- tools/ioemu/target-i386-dm/exec-dm.c | 17 ++- tools/ioemu/vl.c | 154 ++++++++++++++++++++------- tools/ioemu/vl.h | 20 ++++ 4 files changed, 151 insertions(+), 43 deletions(-) diff --git a/tools/ioemu/target-i386-dm/cpu.h b/tools/ioemu/target-i386-dm/cpu.h index dc016edd3e..017b8c021b 100644 --- a/tools/ioemu/target-i386-dm/cpu.h +++ b/tools/ioemu/target-i386-dm/cpu.h @@ -25,7 +25,8 @@ #ifdef TARGET_X86_64 #define TARGET_LONG_BITS 64 #else -#define TARGET_LONG_BITS 32 +/* #define TARGET_LONG_BITS 32 */ +#define TARGET_LONG_BITS 64 /* for Qemu map cache */ #endif /* target supports implicit self modifying code */ diff --git a/tools/ioemu/target-i386-dm/exec-dm.c b/tools/ioemu/target-i386-dm/exec-dm.c index be8b280dda..755d547772 100644 --- a/tools/ioemu/target-i386-dm/exec-dm.c +++ b/tools/ioemu/target-i386-dm/exec-dm.c @@ -36,6 +36,7 @@ #include "cpu.h" #include "exec-all.h" +#include "vl.h" //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH @@ -426,6 +427,12 @@ static inline int paddr_is_ram(target_phys_addr_t addr) #endif } +#if defined(__i386__) || defined(__x86_64__) +#define phys_ram_addr(x) (qemu_map_cache(x)) +#elif defined(__ia64__) +#define phys_ram_addr(x) (phys_ram_base + (x)) +#endif + void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, int len, int is_write) { @@ -438,7 +445,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, l = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); if (l > len) l = len; - + io_index = iomem_index(addr); if (is_write) { if (io_index) { @@ -460,9 +467,10 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, } } else if (paddr_is_ram(addr)) { /* Reading from RAM */ - memcpy(phys_ram_base + addr, buf, l); + ptr = phys_ram_addr(addr); + memcpy(ptr, buf, l); #ifdef __ia64__ - sync_icache((unsigned long)(phys_ram_base + addr), l); + sync_icache(ptr, l); #endif } } else { @@ -485,7 +493,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, } } else if (paddr_is_ram(addr)) { /* Reading from RAM */ - memcpy(buf, phys_ram_base + addr, l); + ptr = phys_ram_addr(addr); + memcpy(buf, ptr, l); } else { /* Neither RAM nor known MMIO space */ memset(buf, 0xff, len); diff --git a/tools/ioemu/vl.c b/tools/ioemu/vl.c index a2700ef313..b65f61d8df 100644 --- a/tools/ioemu/vl.c +++ b/tools/ioemu/vl.c @@ -5808,6 +5808,92 @@ int set_mm_mapping(int xc_handle, uint32_t domid, return 0; } +#if defined(__i386__) || defined(__x86_64__) +static struct map_cache *mapcache_entry; +static unsigned long nr_buckets; + +static int qemu_map_cache_init(unsigned long nr_pages) +{ + unsigned long max_pages = MAX_MCACHE_SIZE >> PAGE_SHIFT; + int i; + + if (nr_pages < max_pages) + max_pages = nr_pages; + + nr_buckets = (max_pages << PAGE_SHIFT) >> MCACHE_BUCKET_SHIFT; + + fprintf(logfile, "qemu_map_cache_init nr_buckets = %lx\n", nr_buckets); + + mapcache_entry = malloc(nr_buckets * sizeof(struct map_cache)); + if (mapcache_entry == NULL) { + errno = ENOMEM; + return -1; + } + + memset(mapcache_entry, 0, nr_buckets * sizeof(struct map_cache)); + + /* + * To avoid ENOMEM from xc_map_foreign_batch() at runtime, we + * pre-fill all the map caches in advance. + */ + for (i = 0; i < nr_buckets; i++) + (void)qemu_map_cache(((target_phys_addr_t)i) << MCACHE_BUCKET_SHIFT); + + return 0; +} + +uint8_t *qemu_map_cache(target_phys_addr_t phys_addr) +{ + struct map_cache *entry; + unsigned long address_index = phys_addr >> MCACHE_BUCKET_SHIFT; + unsigned long address_offset = phys_addr & (MCACHE_BUCKET_SIZE-1); + + /* For most cases (>99.9%), the page address is the same. */ + static unsigned long last_address_index = ~0UL; + static uint8_t *last_address_vaddr; + + if (address_index == last_address_index) + return last_address_vaddr + address_offset; + + entry = &mapcache_entry[address_index % nr_buckets]; + + if (entry->vaddr_base == NULL || entry->paddr_index != address_index) + { + /* We need to remap a bucket. */ + uint8_t *vaddr_base; + unsigned long pfns[MCACHE_BUCKET_SIZE >> PAGE_SHIFT]; + unsigned int i; + + if (entry->vaddr_base != NULL) { + errno = munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE); + if (errno) { + fprintf(logfile, "unmap fails %d\n", errno); + exit(-1); + } + } + + for (i = 0; i < MCACHE_BUCKET_SIZE >> PAGE_SHIFT; i++) + pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-PAGE_SHIFT)) + i; + + vaddr_base = xc_map_foreign_batch( + xc_handle, domid, PROT_READ|PROT_WRITE, + pfns, MCACHE_BUCKET_SIZE >> PAGE_SHIFT); + if (vaddr_base == NULL) { + fprintf(logfile, "xc_map_foreign_batch error %d\n", errno); + exit(-1); + } + + entry->vaddr_base = vaddr_base; + entry->paddr_index = address_index;; + } + + last_address_index = address_index; + last_address_vaddr = entry->vaddr_base; + + return last_address_vaddr + address_offset; +} +#endif + int main(int argc, char **argv) { #ifdef CONFIG_GDBSTUB @@ -6130,6 +6216,7 @@ int main(int argc, char **argv) break; case QEMU_OPTION_m: ram_size = atol(optarg) * 1024 * 1024; + ram_size = (uint64_t)atol(optarg) * 1024 * 1024; if (ram_size <= 0) help(); #ifndef CONFIG_DM @@ -6400,50 +6487,41 @@ int main(int argc, char **argv) shared_page_nr = nr_pages - 1; #endif +#if defined(__i386__) || defined(__x86_64__) + + if ( qemu_map_cache_init(tmp_nr_pages) ) + { + fprintf(logfile, "qemu_map_cache_init returned: error %d\n", errno); + exit(-1); + } + + shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ|PROT_WRITE, shared_page_nr); + if (shared_page == NULL) { + fprintf(logfile, "map shared IO page returned error %d\n", errno); + exit(-1); + } + + fprintf(logfile, "shared page at pfn:%lx\n", shared_page_nr); + + buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ|PROT_WRITE, + shared_page_nr - 2); + if (buffered_io_page == NULL) { + fprintf(logfile, "map buffered IO page returned error %d\n", errno); + exit(-1); + } + + fprintf(logfile, "buffered io page at pfn:%lx\n", shared_page_nr - 2); + +#elif defined(__ia64__) + page_array = (xen_pfn_t *)malloc(tmp_nr_pages * sizeof(xen_pfn_t)); if (page_array == NULL) { fprintf(logfile, "malloc returned error %d\n", errno); exit(-1); } -#if defined(__i386__) || defined(__x86_64__) - for ( i = 0; i < tmp_nr_pages; i++) - page_array[i] = i; - - phys_ram_base = xc_map_foreign_batch(xc_handle, domid, - PROT_READ|PROT_WRITE, page_array, - tmp_nr_pages); - if (phys_ram_base == NULL) { - fprintf(logfile, "batch map guest memory returned error %d\n", errno); - exit(-1); - } - - shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, - PROT_READ|PROT_WRITE, - page_array[shared_page_nr]); - if (shared_page == NULL) { - fprintf(logfile, "map shared IO page returned error %d\n", errno); - exit(-1); - } - - fprintf(logfile, "shared page at pfn:%lx, mfn: %"PRIx64"\n", - shared_page_nr, (uint64_t)(page_array[shared_page_nr])); - - buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, - PROT_READ|PROT_WRITE, - page_array[shared_page_nr - 2]); - if (buffered_io_page == NULL) { - fprintf(logfile, "map buffered IO page returned error %d\n", errno); - exit(-1); - } - - fprintf(logfile, "buffered io page at pfn:%lx, mfn: %"PRIx64"\n", - shared_page_nr - 2, (uint64_t)(page_array[shared_page_nr - 2])); - - free(page_array); - -#elif defined(__ia64__) - if (xc_ia64_get_pfn_list(xc_handle, domid, page_array, IO_PAGE_START >> PAGE_SHIFT, 3) != 3) { fprintf(logfile, "xc_ia64_get_pfn_list returned error %d\n", errno); diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h index 3df963241c..9e4d92a17b 100644 --- a/tools/ioemu/vl.h +++ b/tools/ioemu/vl.h @@ -156,6 +156,26 @@ extern void *shared_vram; extern FILE *logfile; + +#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) +#define MAX_MCACHE_SIZE 0x40000000 /* 1GB max for x86 */ +#define MCACHE_BUCKET_SHIFT 16 +#elif defined(__x86_64__) +#define MAX_MCACHE_SIZE 0x1000000000 /* 64GB max for x86_64 */ +#define MCACHE_BUCKET_SHIFT 20 +#endif + +#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) + +struct map_cache { + unsigned long paddr_index; + uint8_t *vaddr_base; +}; + +uint8_t *qemu_map_cache(target_phys_addr_t phys_addr); +#endif + extern int xc_handle; extern int domid; From a490953a599fdf14f3e1a9fed85949ab0c1e0b1c Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:16:17 +0000 Subject: [PATCH 2/6] [HVM] Fix reboot failure when configured 'cdrom', i.e. bug 832. Signed-off-by : Xinmei Huang --- tools/python/xen/xend/server/blkif.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index 16e2c5052d..bba11ebe1f 100644 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -124,6 +124,8 @@ class BlkifController(DevController): config['dev'] = dev if typ and params: config['uname'] = typ +':' + params + else: + config['uname'] = None if mode: config['mode'] = mode if uuid: From 03b88d57897af9feaada5f5b35dec0369ce217db Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:17:28 +0000 Subject: [PATCH 3/6] [VTPM][HVM] Transmit the locality in which the command was sent in the highest 3 bits of the 4 bytes prepended to each packet. Signed-off-by: Stefan Berger --- tools/ioemu/hw/tpm_tis.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/ioemu/hw/tpm_tis.c b/tools/ioemu/hw/tpm_tis.c index a4d07bc67f..ace469f1ec 100644 --- a/tools/ioemu/hw/tpm_tis.c +++ b/tools/ioemu/hw/tpm_tis.c @@ -132,7 +132,7 @@ typedef struct TPMState { /* local prototypes */ -static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg); +static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg); static int TPM_Receive(tpmState *s, tpmBuffer *buffer); static uint32_t vtpm_instance_from_xenstore(void); static void tis_poll_timer(void *opaque); @@ -271,6 +271,8 @@ static int create_local_socket(tpmState *s, uint32_t vtpm_instance) /* * the 'write' method for sending requests to the vTPM * four bytes with the vTPM instance number are prepended to each request + * the locality in which the command was sent is transmitted in the + * highest 3 bits */ static int write_local_socket(tpmState *s, const tpmBuffer *buffer) { @@ -608,7 +610,7 @@ static void tis_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } if (val & STS_TPM_GO) { - n = TPM_Send(s, &s->buffer,"tpm_data_write"); + n = TPM_Send(s, &s->buffer, locty, "tpm_data_write"); if (n > 0) { /* sending of data was successful */ s->offset = 0; @@ -915,7 +917,7 @@ const static unsigned char tpm_failure[] = { /* * Send a TPM request. */ -static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg) +static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg) { int len; uint32_t size = tpm_get_size_from_buffer(buffer->buf); @@ -945,6 +947,10 @@ static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg) showBuff(buffer->buf, "To TPM"); #endif + /* transmit the locality in the highest 3 bits */ + buffer->instance[0] &= 0x1f; + buffer->instance[0] |= (locty << 5); + len = vTPMTransmit[s->Transmitlayer].write(s, buffer); if (len < 0) { s->Transmitlayer = -1; From 40574f04b42731e24cb00237b7c29bc8a5f45eab Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:18:38 +0000 Subject: [PATCH 4/6] VMXAssist cleanups Signed-off-by: Xin Li --- tools/firmware/vmxassist/head.S | 2 +- tools/firmware/vmxassist/setup.c | 4 +- tools/firmware/vmxassist/vm86.c | 203 ++++++++++++++----------------- 3 files changed, 94 insertions(+), 115 deletions(-) diff --git a/tools/firmware/vmxassist/head.S b/tools/firmware/vmxassist/head.S index a4cb614c68..7cb4ec1069 100644 --- a/tools/firmware/vmxassist/head.S +++ b/tools/firmware/vmxassist/head.S @@ -130,7 +130,7 @@ _start: clts /* setup my own stack */ - movl $stack_top - 4*4, %esp + movl $stack_top, %esp movl %esp, %ebp /* go ... */ diff --git a/tools/firmware/vmxassist/setup.c b/tools/firmware/vmxassist/setup.c index d1c47b2325..9f8928ff0d 100644 --- a/tools/firmware/vmxassist/setup.c +++ b/tools/firmware/vmxassist/setup.c @@ -125,7 +125,7 @@ setup_gdt(void) /* setup task state segment */ memset(&tss, 0, sizeof(tss)); tss.ss0 = DATA_SELECTOR; - tss.esp0 = (unsigned) stack_top - 4*4; + tss.esp0 = (unsigned) stack_top; tss.iomap_base = offsetof(struct tss, iomap); /* initialize gdt's tss selector */ @@ -258,7 +258,7 @@ setup_ctx(void) memset(c, 0, sizeof(*c)); c->eip = (unsigned long) switch_to_real_mode; - c->esp = (unsigned) stack_top - 4*4; + c->esp = (unsigned) stack_top; c->eflags = 0x2; /* no interrupts, please */ /* diff --git a/tools/firmware/vmxassist/vm86.c b/tools/firmware/vmxassist/vm86.c index 7e37de63b8..7a76ad8b15 100644 --- a/tools/firmware/vmxassist/vm86.c +++ b/tools/firmware/vmxassist/vm86.c @@ -1,6 +1,6 @@ /* * vm86.c: A vm86 emulator. The main purpose of this emulator is to do as - * little work as possible. + * little work as possible. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005-2006, International Business Machines Corporation. @@ -52,8 +52,8 @@ char *states[] = { static char *rnames[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di" }; #endif /* DEBUG */ -#define PDE_PS (1 << 7) -#define PT_ENTRY_PRESENT 0x1 +#define PDE_PS (1 << 7) +#define PT_ENTRY_PRESENT 0x1 /* We only support access to <=4G physical memory due to 1:1 mapping */ static uint64_t @@ -136,7 +136,7 @@ address(struct regs *regs, unsigned seg, unsigned off) } if (mode == VM86_REAL || seg > oldctx.gdtr_limit || - (mode == VM86_REAL_TO_PROTECTED && regs->cs == seg)) + (mode == VM86_REAL_TO_PROTECTED && regs->cs == seg)) return ((seg & 0xFFFF) << 4) + off; gdt_phys_base = guest_linear_to_phys(oldctx.gdtr_base); @@ -153,13 +153,13 @@ address(struct regs *regs, unsigned seg, unsigned off) seg_limit = (entry_high & 0xF0000) | (entry_low & 0xFFFF); if (entry_high & 0x8000 && - ((entry_high & 0x800000 && off >> 12 <= seg_limit) || - (!(entry_high & 0x800000) && off <= seg_limit))) + ((entry_high & 0x800000 && off >> 12 <= seg_limit) || + (!(entry_high & 0x800000) && off <= seg_limit))) return seg_base + off; panic("should never reach here in function address():\n\t" - "entry=0x%08x%08x, mode=%d, seg=0x%08x, offset=0x%08x\n", - entry_high, entry_low, mode, seg, off); + "entry=0x%08x%08x, mode=%d, seg=0x%08x, offset=0x%08x\n", + entry_high, entry_low, mode, seg, off); return 0; } @@ -172,7 +172,7 @@ trace(struct regs *regs, int adjust, char *fmt, ...) va_list ap; if ((traceset & (1 << mode)) && - (mode == VM86_REAL_TO_PROTECTED || mode == VM86_REAL)) { + (mode == VM86_REAL_TO_PROTECTED || mode == VM86_REAL)) { /* 16-bit, seg:off addressing */ unsigned addr = address(regs, regs->cs, off); printf("0x%08x: 0x%x:0x%04x ", addr, regs->cs, off); @@ -183,7 +183,7 @@ trace(struct regs *regs, int adjust, char *fmt, ...) printf("\n"); } if ((traceset & (1 << mode)) && - (mode == VM86_PROTECTED_TO_REAL || mode == VM86_PROTECTED)) { + (mode == VM86_PROTECTED_TO_REAL || mode == VM86_PROTECTED)) { /* 16-bit, gdt addressing */ unsigned addr = address(regs, regs->cs, off); printf("0x%08x: 0x%x:0x%08x ", addr, regs->cs, off); @@ -430,7 +430,7 @@ operand(unsigned prefix, struct regs *regs, unsigned modrm) case 2: return address(regs, seg, regs->edx); case 3: return address(regs, seg, regs->ebx); case 4: return address(regs, seg, - sib(regs, mod, fetch8(regs))); + sib(regs, mod, fetch8(regs))); case 5: return address(regs, seg, fetch32(regs)); case 6: return address(regs, seg, regs->esi); case 7: return address(regs, seg, regs->edi); @@ -450,7 +450,7 @@ operand(unsigned prefix, struct regs *regs, unsigned modrm) case 2: return address(regs, seg, regs->edx + disp); case 3: return address(regs, seg, regs->ebx + disp); case 4: return address(regs, seg, - sib(regs, mod, fetch8(regs))); + sib(regs, mod, fetch8(regs))); case 5: return address(regs, seg, regs->ebp + disp); case 6: return address(regs, seg, regs->esi + disp); case 7: return address(regs, seg, regs->edi + disp); @@ -507,7 +507,7 @@ operand(unsigned prefix, struct regs *regs, unsigned modrm) } } - return 0; + return 0; } /* @@ -859,7 +859,7 @@ mov_to_seg(struct regs *regs, unsigned prefix, unsigned opc) fail: printf("%s:%d: missed opcode %02x %02x\n", - __FUNCTION__, __LINE__, opc, modrm); + __FUNCTION__, __LINE__, opc, modrm); return 0; } @@ -896,11 +896,11 @@ load_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes ((entry >> (32-16)) & 0x00FF0000) | ((entry >> ( 16)) & 0x0000FFFF)); *limit = (((entry >> (48-16)) & 0x000F0000) | - ((entry ) & 0x0000FFFF)); + (entry & 0x0000FFFF)); arbytes->bytes = 0; arbytes->fields.seg_type = (entry >> (8+32)) & 0xF; /* TYPE */ - arbytes->fields.s = (entry >> (12+32)) & 0x1; /* S */ + arbytes->fields.s = (entry >> (12+32)) & 0x1; /* S */ if (arbytes->fields.s) arbytes->fields.seg_type |= 1; /* accessed */ arbytes->fields.dpl = (entry >> (13+32)) & 0x3; /* DPL */ @@ -924,7 +924,7 @@ static void load_or_clear_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes) { if (!load_seg(sel, base, limit, arbytes)) - load_seg(0, base, limit, arbytes); + load_seg(0, base, limit, arbytes); } @@ -988,21 +988,21 @@ real_mode(struct regs *regs) panic("%%ss 0x%lx higher than 1MB", regs->uss); regs->uss = address(regs, regs->uss, 0) >> 4; } else { - regs->uss = saved_rm_regs.uss; + regs->uss = saved_rm_regs.uss; } if (regs->vds != 0) { if (regs->vds >= HIGHMEM) panic("%%ds 0x%lx higher than 1MB", regs->vds); regs->vds = address(regs, regs->vds, 0) >> 4; } else { - regs->vds = saved_rm_regs.vds; + regs->vds = saved_rm_regs.vds; } if (regs->ves != 0) { if (regs->ves >= HIGHMEM) panic("%%es 0x%lx higher than 1MB", regs->ves); regs->ves = address(regs, regs->ves, 0) >> 4; } else { - regs->ves = saved_rm_regs.ves; + regs->ves = saved_rm_regs.ves; } /* this should get us into 16-bit mode */ @@ -1029,10 +1029,7 @@ set_mode(struct regs *regs, enum vm86_mode newmode) (mode == VM86_REAL_TO_PROTECTED)) { regs->eflags &= ~EFLAGS_TF; real_mode(regs); - break; - } else if (mode == VM86_REAL) { - break; - } else + } else if (mode != VM86_REAL) panic("unexpected real mode transition"); break; @@ -1049,25 +1046,19 @@ set_mode(struct regs *regs, enum vm86_mode newmode) oldctx.fs_sel = 0; oldctx.gs_sel = 0; oldctx.ss_sel = 0; - break; - } else if (mode == VM86_REAL_TO_PROTECTED) { - break; - } else + } else if (mode != VM86_REAL_TO_PROTECTED) panic("unexpected real-to-protected mode transition"); break; case VM86_PROTECTED_TO_REAL: - if (mode == VM86_PROTECTED) { - break; - } else + if (mode != VM86_PROTECTED) panic("unexpected protected-to-real mode transition"); break; case VM86_PROTECTED: - if (mode == VM86_REAL_TO_PROTECTED) { - protected_mode(regs); - } else + if (mode != VM86_REAL_TO_PROTECTED) panic("unexpected protected mode transition"); + protected_mode(regs); break; } @@ -1081,25 +1072,19 @@ jmpl(struct regs *regs, int prefix) unsigned n = regs->eip; unsigned cs, eip; - if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */ - eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); - cs = fetch16(regs); + eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); + cs = fetch16(regs); - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); + TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - regs->cs = cs; - regs->eip = eip; + regs->cs = cs; + regs->eip = eip; + + if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); - } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */ - eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); - cs = fetch16(regs); - - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - - regs->cs = cs; - regs->eip = eip; + else if (mode == VM86_PROTECTED_TO_REAL)/* jump to real mode */ set_mode(regs, VM86_REAL); - } else + else panic("jmpl"); } @@ -1110,29 +1095,22 @@ jmpl_indirect(struct regs *regs, int prefix, unsigned modrm) unsigned cs, eip; unsigned addr; - addr = operand(prefix, regs, modrm); + addr = operand(prefix, regs, modrm); - if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */ - eip = (prefix & DATA32) ? read32(addr) : read16(addr); - addr += (prefix & DATA32) ? 4 : 2; - cs = read16(addr); + eip = (prefix & DATA32) ? read32(addr) : read16(addr); + addr += (prefix & DATA32) ? 4 : 2; + cs = read16(addr); - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); + TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - regs->cs = cs; - regs->eip = eip; + regs->cs = cs; + regs->eip = eip; + + if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); - } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */ - eip = (prefix & DATA32) ? read32(addr) : read16(addr); - addr += (prefix & DATA32) ? 4 : 2; - cs = read16(addr); - - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - - regs->cs = cs; - regs->eip = eip; + else if (mode == VM86_PROTECTED_TO_REAL)/* jump to real mode */ set_mode(regs, VM86_REAL); - } else + else panic("jmpl"); } @@ -1151,15 +1129,14 @@ retl(struct regs *regs, int prefix) TRACE((regs, 1, "retl (to 0x%x:0x%x)", cs, eip)); - if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */ - regs->cs = cs; - regs->eip = eip; + regs->cs = cs; + regs->eip = eip; + + if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); - } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */ - regs->cs = cs; - regs->eip = eip; + else if (mode == VM86_PROTECTED_TO_REAL)/* jump to real mode */ set_mode(regs, VM86_REAL); - } else + else panic("retl"); } @@ -1259,8 +1236,8 @@ pushrm(struct regs *regs, int prefix, unsigned modrm) unsigned addr; unsigned data; - addr = operand(prefix, regs, modrm); - + addr = operand(prefix, regs, modrm); + if (prefix & DATA32) { data = read32(addr); push32(regs, data); @@ -1386,11 +1363,11 @@ opcode(struct regs *regs) case 0x3B: /* addr32 cmp r/m16, r16 */ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED) goto invalid; - if ((prefix & ADDR32) == 0) - goto invalid; - if (!cmp(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!cmp(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x3E: TRACE((regs, regs->eip - eip, "%%ds:")); @@ -1412,7 +1389,7 @@ opcode(struct regs *regs) prefix |= DATA32; continue; - case 0x67: + case 0x67: TRACE((regs, regs->eip - eip, "addr32")); prefix |= ADDR32; continue; @@ -1421,18 +1398,18 @@ opcode(struct regs *regs) case 0x8A: /* addr32 mov r/m8, r8 */ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED) goto invalid; - if ((prefix & ADDR32) == 0) - goto invalid; - if (!movr(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!movr(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x89: /* addr32 mov r16, r/m16 */ if (mode == VM86_PROTECTED_TO_REAL) { unsigned modrm = fetch8(regs); unsigned addr = operand(prefix, regs, modrm); unsigned val, r = (modrm >> 3) & 7; - + if (prefix & DATA32) { val = getreg16(regs, r); write32(addr, val); @@ -1447,11 +1424,11 @@ opcode(struct regs *regs) case 0x8B: /* addr32 mov r/m16, r16 */ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED) goto invalid; - if ((prefix & ADDR32) == 0) - goto invalid; - if (!movr(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!movr(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x8E: /* mov r16, sreg */ if (!mov_to_seg(regs, prefix, opc)) @@ -1459,11 +1436,11 @@ opcode(struct regs *regs) return OPC_EMULATED; case 0x8F: /* addr32 pop r/m16 */ - if ((prefix & ADDR32) == 0) - goto invalid; - if (!pop(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!pop(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x90: /* nop */ TRACE((regs, regs->eip - eip, "nop")); @@ -1487,7 +1464,7 @@ opcode(struct regs *regs) regs->eflags |= EFLAGS_VM; return OPC_EMULATED; - case 0xA1: /* mov ax, r/m16 */ + case 0xA1: /* mov ax, r/m16 */ { int addr, data; int seg = segment(prefix, regs, regs->vds); @@ -1521,15 +1498,15 @@ opcode(struct regs *regs) return OPC_EMULATED; case 0xC6: /* addr32 movb $imm, r/m8 */ - if ((prefix & ADDR32) == 0) - goto invalid; - if (!movr(regs, prefix, opc)) - goto invalid; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!movr(regs, prefix, opc)) + goto invalid; return OPC_EMULATED; case 0xCB: /* retl */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { retl(regs, prefix); return OPC_INVALID; } @@ -1567,7 +1544,7 @@ opcode(struct regs *regs) case 0xEA: /* jmpl */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { jmpl(regs, prefix); return OPC_INVALID; } @@ -1579,7 +1556,7 @@ opcode(struct regs *regs) switch((modrm >> 3) & 7) { case 5: /* jmpl (indirect) */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { jmpl_indirect(regs, prefix, modrm); return OPC_INVALID; } @@ -1596,7 +1573,7 @@ opcode(struct regs *regs) case 0xEB: /* short jump */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { disp = (char) fetch8(regs); TRACE((regs, 2, "jmp 0x%x", regs->eip + disp)); regs->eip += disp; @@ -1619,10 +1596,10 @@ opcode(struct regs *regs) continue; case 0xF6: /* addr32 testb $imm, r/m8 */ - if ((prefix & ADDR32) == 0) - goto invalid; - if (!test(regs, prefix, opc)) - goto invalid; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!test(regs, prefix, opc)) + goto invalid; return OPC_EMULATED; case 0xFA: /* cli */ @@ -1682,6 +1659,8 @@ trap(int trapno, int errno, struct regs *regs) case 1: /* Debug */ if (regs->eflags & EFLAGS_VM) { /* emulate any 8086 instructions */ + if (mode == VM86_REAL) + return; if (mode != VM86_REAL_TO_PROTECTED) panic("not in real-to-protected mode"); emulate(regs); @@ -1702,7 +1681,7 @@ trap(int trapno, int errno, struct regs *regs) default: invalid: printf("Trap (0x%x) while in %s mode\n", - trapno, regs->eflags & EFLAGS_VM ? "real" : "protected"); + trapno, regs->eflags & EFLAGS_VM ? "real" : "protected"); if (trapno == 14) printf("Page fault address 0x%x\n", get_cr2()); dump_regs(regs); From d3f023e9ade9d28b3af1e346c63f95e73ca345c0 Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:22:26 +0000 Subject: [PATCH 5/6] [XEN] Make multiboot-related code more readable. Tested on i386 and x86_64 with GRUB and PXELINUX bootmanagers. From: Christoph Egger Signed-off-by: Keir Fraser --- xen/arch/x86/boot/x86_32.S | 9 ++++++--- xen/arch/x86/boot/x86_64.S | 9 ++++++--- xen/include/xen/multiboot.h | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/xen/arch/x86/boot/x86_32.S b/xen/arch/x86/boot/x86_32.S index e0f2e09643..c3466dee5f 100644 --- a/xen/arch/x86/boot/x86_32.S +++ b/xen/arch/x86/boot/x86_32.S @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -17,12 +18,14 @@ ENTRY(_stext) .align 4 /*** MULTIBOOT HEADER ****/ +#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_HEADER_MODS_ALIGNED | \ + MULTIBOOT_HEADER_WANT_MEMORY) /* Magic number indicating a Multiboot header. */ - .long 0x1BADB002 + .long MULTIBOOT_HEADER_MAGIC /* Flags to bootloader (see Multiboot spec). */ - .long 0x00000003 + .long MULTIBOOT_HEADER_FLAGS /* Checksum: must be the negated sum of the first two fields. */ - .long -0x1BADB005 + .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) not_multiboot_msg: .asciz "ERR: Not a Multiboot bootloader!" diff --git a/xen/arch/x86/boot/x86_64.S b/xen/arch/x86/boot/x86_64.S index 5a8a111b59..ce0e42987c 100644 --- a/xen/arch/x86/boot/x86_64.S +++ b/xen/arch/x86/boot/x86_64.S @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,12 +20,14 @@ ENTRY(_stext) .org 0x004 /*** MULTIBOOT HEADER ****/ +#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_HEADER_MODS_ALIGNED | \ + MULTIBOOT_HEADER_WANT_MEMORY) /* Magic number indicating a Multiboot header. */ - .long 0x1BADB002 + .long MULTIBOOT_HEADER_MAGIC /* Flags to bootloader (see Multiboot spec). */ - .long 0x00000003 + .long MULTIBOOT_HEADER_FLAGS /* Checksum: must be the negated sum of the first two fields. */ - .long -0x1BADB005 + .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) .Lbad_cpu_msg: .asciz "ERR: Not a 64-bit CPU!" .Lbad_ldr_msg: .asciz "ERR: Not a Multiboot bootloader!" diff --git a/xen/include/xen/multiboot.h b/xen/include/xen/multiboot.h index 9473404775..86b59db1fe 100644 --- a/xen/include/xen/multiboot.h +++ b/xen/include/xen/multiboot.h @@ -18,16 +18,30 @@ #ifndef __MULTIBOOT_H__ #define __MULTIBOOT_H__ + +/* + * Multiboot header structure. + */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 +#define MULTIBOOT_HEADER_MODS_ALIGNED 0x00000001 +#define MULTIBOOT_HEADER_WANT_MEMORY 0x00000002 +#define MULTIBOOT_HEADER_HAS_VBE 0x00000004 +#define MULTIBOOT_HEADER_HAS_ADDR 0x00010000 + /* The magic number passed by a Multiboot-compliant boot loader. */ -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 #define MBI_MEMLIMITS (1<<0) #define MBI_DRIVES (1<<1) #define MBI_CMDLINE (1<<2) #define MBI_MODULES (1<<3) +#define MBI_AOUT_SYMS (1<<4) +#define MBI_ELF_SYMS (1<<5) #define MBI_MEMMAP (1<<6) #define MBI_LOADERNAME (1<<9) +#ifndef __ASSEMBLY__ + /* The symbol table for a.out. */ typedef struct { u32 tabsize; @@ -47,16 +61,28 @@ typedef struct { /* The Multiboot information. */ typedef struct { u32 flags; + + /* Valid if flags sets MBI_MEMLIMITS */ u32 mem_lower; u32 mem_upper; + + /* Valid if flags sets MBI_DRIVES */ u32 boot_device; + + /* Valid if flags sets MBI_CMDLINE */ u32 cmdline; + + /* Valid if flags sets MBI_MODULES */ u32 mods_count; u32 mods_addr; + + /* Valid if flags sets ... */ union { - aout_symbol_table_t aout_sym; - elf_section_header_table_t elf_sec; + aout_symbol_table_t aout_sym; /* ... MBI_AOUT_SYMS */ + elf_section_header_table_t elf_sec; /* ... MBI_ELF_SYMS */ } u; + + /* Valid if flags sets MBI_MEMMAP */ u32 mmap_length; u32 mmap_addr; } multiboot_info_t; @@ -80,4 +106,7 @@ typedef struct { u32 type; } memory_map_t; + +#endif /* __ASSEMBLY__ */ + #endif /* __MULTIBOOT_H__ */ From b07ced9c1fa55ffab47e3789af1a9f9ba672ef39 Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 7 Dec 2006 11:36:26 +0000 Subject: [PATCH 6/6] [LIBXC] Add an error reporting API to the libxc library. - An 'xc_error' struct is used to pass around error details. Currently contains two members 'code' an enumeration of error types, and 'message' a free text description of the specific problem. - The xc_get_last_error() method returns a const pointer to the internal instance of this struct manged by libxc. By returning a const pointer we can add extra members to the end of the struct at any time without worrying about ABI of callers. This will let us provide more fine-grained info if needed in the future. - The xc_error instance is statically defined inside libxc and marked __thread. This ensures that errors are recorded per-thread, and that when dealing with errors we never need to call malloc - all storage needed is statically allocated. - The xc_clear_last_error() method resets any currently recorded error details - The xc_error_code_to_desc() method converts the integer error code into a generic user facing messsage. eg "Invalid kernel". Together with the 'message' field from xc_error, this provides the user visible feedback. eg "Invalid kernel: Non PAE-kernel on PAE host." - A callback can be registered with xc_set_error_handler to receive notification whenever an error is recorded, rather than querying for error details after the fact with xc_get_last_error - If built with -DDEBUG set, a default error handler will be registered which calls fprintf(stderr), thus maintaining current behaviour of logging errors to stderr during developer builds. - The python binding for libxc is updated to use xc_get_last_error to pull out error details whenever appropriate, instead of returning info based on 'errno' - The xc_set_error method is private to libxc internals, and is used for setting error details - The ERROR and PERROR macros have been updated to call xc_set_error automatically specifying XC_INTERNAL_ERROR as the error code. This gives a generic error report for all current failure points - Some uses of the ERROR macro have been replaced with explicit calls to xc_set_error to enable finer grained error reporting. In particular the code dealing with invalid kernel types uses this to report about PAE/architecture/wordsize mismatches The patch has been tested by calling xm create against a varietry of config files defining invalid kernels of various kinds. It has also been tested with libvirt talking to xend. In both cases the error messages were propagated all the way back up the stack. There is only one place where I need to do further work. The suspend & restore APIs in Xend invoke external helper programs rather than calling libxc directly. This means that error details are essentially lost. Since there is already code in XenD which scans STDERR from these programs I will investigate adapting this to extract actual error messages from these helpers. Signed-off-by: Daniel P. Berrange --- tools/libxc/xc_hvm_build.c | 16 +++-- tools/libxc/xc_linux_build.c | 12 ++-- tools/libxc/xc_load_elf.c | 95 +++++++++++++++++++++------ tools/libxc/xc_private.c | 76 ++++++++++++++++++++++ tools/libxc/xc_private.h | 19 ++---- tools/libxc/xenctrl.h | 42 ++++++++++++ tools/python/xen/lowlevel/xc/xc.c | 104 +++++++++++++++++------------- 7 files changed, 275 insertions(+), 89 deletions(-) diff --git a/tools/libxc/xc_hvm_build.c b/tools/libxc/xc_hvm_build.c index 1c038755cd..21e20ffaf9 100644 --- a/tools/libxc/xc_hvm_build.c +++ b/tools/libxc/xc_hvm_build.c @@ -285,7 +285,6 @@ static int xc_hvm_build_internal(int xc_handle, if ( setup_guest(xc_handle, domid, memsize, image, image_size, &ctxt) < 0 ) { - ERROR("Error constructing guest OS"); goto error_out; } @@ -329,26 +328,30 @@ static int parseelfimage(char *elfbase, if ( !IS_ELF(*ehdr) ) { - ERROR("Kernel image does not have an ELF header."); + xc_set_error(XC_INVALID_KERNEL, + "Kernel image does not have an ELF header."); return -EINVAL; } if ( (ehdr->e_phoff + (ehdr->e_phnum * ehdr->e_phentsize)) > elfsize ) { - ERROR("ELF program headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF program headers extend beyond end of image."); return -EINVAL; } if ( (ehdr->e_shoff + (ehdr->e_shnum * ehdr->e_shentsize)) > elfsize ) { - ERROR("ELF section headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF section headers extend beyond end of image."); return -EINVAL; } /* Find the section-header strings table. */ if ( ehdr->e_shstrndx == SHN_UNDEF ) { - ERROR("ELF image has no section-header strings table (shstrtab)."); + xc_set_error(XC_INVALID_KERNEL, + "ELF image has no section-header strings table (shstrtab)."); return -EINVAL; } shdr = (Elf32_Shdr *)(elfbase + ehdr->e_shoff + @@ -370,7 +373,8 @@ static int parseelfimage(char *elfbase, (ehdr->e_entry < kernstart) || (ehdr->e_entry > kernend) ) { - ERROR("Malformed ELF image."); + xc_set_error(XC_INVALID_KERNEL, + "Malformed ELF image."); return -EINVAL; } diff --git a/tools/libxc/xc_linux_build.c b/tools/libxc/xc_linux_build.c index 9bd4a5e3f4..edf95c0fc0 100644 --- a/tools/libxc/xc_linux_build.c +++ b/tools/libxc/xc_linux_build.c @@ -120,7 +120,7 @@ static int probeimageformat(const char *image, if ( probe_elf(image, image_size, load_funcs) && probe_bin(image, image_size, load_funcs) ) { - ERROR( "Unrecognized image format" ); + xc_set_error(XC_INVALID_KERNEL, "Not a valid ELF or raw kernel image"); return -EINVAL; } @@ -618,17 +618,20 @@ static int compat_check(int xc_handle, struct domain_setup_info *dsi) xen_capabilities_info_t xen_caps = ""; if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) { - ERROR("Cannot determine host capabilities."); + xc_set_error(XC_INVALID_KERNEL, + "Cannot determine host capabilities."); return 0; } if (strstr(xen_caps, "xen-3.0-x86_32p")) { if (dsi->pae_kernel == PAEKERN_no) { - ERROR("Non PAE-kernel on PAE host."); + xc_set_error(XC_INVALID_KERNEL, + "Non PAE-kernel on PAE host."); return 0; } } else if (dsi->pae_kernel != PAEKERN_no) { - ERROR("PAE-kernel on non-PAE host."); + xc_set_error(XC_INVALID_KERNEL, + "PAE-kernel on non-PAE host."); return 0; } @@ -1141,7 +1144,6 @@ static int xc_linux_build_internal(int xc_handle, console_evtchn, console_mfn, features_bitmap) < 0 ) { - ERROR("Error constructing guest OS"); goto error_out; } diff --git a/tools/libxc/xc_load_elf.c b/tools/libxc/xc_load_elf.c index 9ca9759398..f8a28fb83c 100644 --- a/tools/libxc/xc_load_elf.c +++ b/tools/libxc/xc_load_elf.c @@ -29,20 +29,46 @@ loadelfsymtab( */ #if defined(__ia64__) #define ELFCLASS ELFCLASS64 +#define ELFCLASS_DESC "64-bit" + #define ELFDATA ELFDATA2LSB +#define ELFDATA_DESC "Little-Endian" + #define ELFMACHINE EM_IA_64 +#define ELFMACHINE_DESC "ia64" + + #elif defined(__i386__) #define ELFCLASS ELFCLASS32 +#define ELFCLASS_DESC "32-bit" + #define ELFDATA ELFDATA2LSB +#define ELFDATA_DESC "Little-Endian" + #define ELFMACHINE EM_386 +#define ELFMACHINE_DESC "i386" + + #elif defined(__x86_64__) #define ELFCLASS ELFCLASS64 +#define ELFCLASS_DESC "64-bit" + #define ELFDATA ELFDATA2LSB +#define ELFDATA_DESC "Little-Endian" + #define ELFMACHINE EM_X86_64 +#define ELFMACHINE_DESC "x86_64" + + #elif defined(__powerpc__) #define ELFCLASS ELFCLASS64 +#define ELFCLASS_DESC "64-bit" + #define ELFDATA ELFDATA2MSB +#define ELFDATA_DESC "Big-Endian" + #define ELFMACHINE EM_PPC64 +#define ELFMACHINE_DESC "ppc64" #endif int probe_elf(const char *image, @@ -231,7 +257,8 @@ unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi, *defined = 1; return *(uint64_t*)ELFNOTE_DESC(note); default: - ERROR("elfnotes: unknown data size %#x for numeric type note %#x\n", + xc_set_error(XC_INVALID_KERNEL, + "elfnotes: unknown data size %#x for numeric type note %#x\n", note->descsz, type); return 0; } @@ -250,35 +277,59 @@ static int parseelfimage(const char *image, if ( !IS_ELF(*ehdr) ) { - ERROR("Kernel image does not have an ELF header."); + xc_set_error(XC_INVALID_KERNEL, + "Kernel image does not have an ELF header."); return -EINVAL; } - if ( (ehdr->e_ident[EI_CLASS] != ELFCLASS) || - (ehdr->e_machine != ELFMACHINE) || - (ehdr->e_ident[EI_DATA] != ELFDATA) || - (ehdr->e_type != ET_EXEC) ) + if (ehdr->e_machine != ELFMACHINE) { - ERROR("Kernel not a Xen-compatible Elf image."); + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF architecture '%d' does not match Xen architecture '%d' (%s)", + ehdr->e_machine, ELFMACHINE, ELFMACHINE_DESC); + return -EINVAL; + } + if (ehdr->e_ident[EI_CLASS] != ELFCLASS) + { + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF wordsize '%d' does not match Xen wordsize '%d' (%s)", + ehdr->e_ident[EI_CLASS], ELFCLASS, ELFCLASS_DESC); + return -EINVAL; + } + if (ehdr->e_ident[EI_DATA] != ELFDATA) + { + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF endianness '%d' does not match Xen endianness '%d' (%s)", + ehdr->e_ident[EI_DATA], ELFDATA, ELFDATA_DESC); + return -EINVAL; + } + if (ehdr->e_type != ET_EXEC) + { + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF type '%d' does not match Xen type '%d'", + ehdr->e_type, ET_EXEC); return -EINVAL; } if ( (ehdr->e_phoff + (ehdr->e_phnum*ehdr->e_phentsize)) > image_len ) { - ERROR("ELF program headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF program headers extend beyond end of image."); return -EINVAL; } if ( (ehdr->e_shoff + (ehdr->e_shnum*ehdr->e_shentsize)) > image_len ) { - ERROR("ELF section headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF section headers extend beyond end of image."); return -EINVAL; } /* Find the section-header strings table. */ if ( ehdr->e_shstrndx == SHN_UNDEF ) { - ERROR("ELF image has no section-header strings table (shstrtab)."); + xc_set_error(XC_INVALID_KERNEL, + "ELF image has no section-header strings table (shstrtab)."); return -EINVAL; } shdr = (Elf_Shdr *)(image + ehdr->e_shoff + @@ -325,22 +376,25 @@ static int parseelfimage(const char *image, if ( ( loader == NULL || strncmp(loader, "generic", 7) ) && ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) ) { - ERROR("Will only load images built for the generic loader " - "or Linux images"); + xc_set_error(XC_INVALID_KERNEL, + "Will only load images built for the generic loader " + "or Linux images"); return -EINVAL; } if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) ) { - ERROR("Will only load images built for Xen v3.0"); + xc_set_error(XC_INVALID_KERNEL, + "Will only load images built for Xen v3.0"); return -EINVAL; } } else { #if defined(__x86_64__) || defined(__i386__) - ERROR("Not a Xen-ELF image: " - "No ELF notes or '__xen_guest' section found."); + xc_set_error(XC_INVALID_KERNEL, + "Not a Xen-ELF image: " + "No ELF notes or '__xen_guest' section found."); return -EINVAL; #endif } @@ -396,8 +450,9 @@ static int parseelfimage(const char *image, if ( elf_pa_off_defined && !virt_base_defined ) { - ERROR("Neither ELF_PADDR_OFFSET nor VIRT_BASE found in ELF " - " notes or __xen_guest section."); + xc_set_error(XC_INVALID_KERNEL, + "Neither ELF_PADDR_OFFSET nor VIRT_BASE found in ELF " + " notes or __xen_guest section."); return -EINVAL; } @@ -409,7 +464,8 @@ static int parseelfimage(const char *image, vaddr = phdr->p_paddr - dsi->elf_paddr_offset + dsi->v_start; if ( (vaddr + phdr->p_memsz) < vaddr ) { - ERROR("ELF program header %d is too large.", h); + xc_set_error(XC_INVALID_KERNEL, + "ELF program header %d is too large.", h); return -EINVAL; } @@ -431,7 +487,8 @@ static int parseelfimage(const char *image, (dsi->v_kernentry > kernend) || (dsi->v_start > kernstart) ) { - ERROR("ELF start or entries are out of bounds."); + xc_set_error(XC_INVALID_KERNEL, + "ELF start or entries are out of bounds."); return -EINVAL; } diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c index d8dbb5e0dd..3ff0b791d0 100644 --- a/tools/libxc/xc_private.c +++ b/tools/libxc/xc_private.c @@ -8,6 +8,82 @@ #include "xc_private.h" #include "xg_private.h" +#include + +static __thread xc_error last_error = { XC_ERROR_NONE, ""}; +#if DEBUG +static xc_error_handler error_handler = xc_default_error_handler; +#else +static xc_error_handler error_handler = NULL; +#endif + +void xc_default_error_handler(const xc_error const *err) +{ + const char *desc = xc_error_code_to_desc(err->code); + fprintf(stderr, "ERROR %s: %s\n", desc, err->message); +} + +const xc_error const *xc_get_last_error(void) +{ + return &last_error; +} + +void xc_clear_last_error(void) +{ + last_error.code = XC_ERROR_NONE; + last_error.message[0] = '\0'; +} + +const char *xc_error_code_to_desc(int code) +{ + /* Sync to members of xc_error_code enumeration in xenctrl.h */ + switch ( code ) + { + case XC_ERROR_NONE: + return "No error details"; + case XC_INTERNAL_ERROR: + return "Internal error"; + case XC_INVALID_KERNEL: + return "Invalid kernel"; + } + + return "Unknown error code"; +} + +xc_error_handler xc_set_error_handler(xc_error_handler handler) +{ + xc_error_handler old = error_handler; + error_handler = handler; + return old; +} + + +static void _xc_set_error(int code, const char *msg) +{ + last_error.code = code; + strncpy(last_error.message, msg, XC_MAX_ERROR_MSG_LEN - 1); + last_error.message[XC_MAX_ERROR_MSG_LEN-1] = '\0'; +} + +void xc_set_error(int code, const char *fmt, ...) +{ + int saved_errno = errno; + char msg[XC_MAX_ERROR_MSG_LEN]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, XC_MAX_ERROR_MSG_LEN-1, fmt, args); + msg[XC_MAX_ERROR_MSG_LEN-1] = '\0'; + va_end(args); + + _xc_set_error(code, msg); + + errno = saved_errno; + + if ( error_handler != NULL ) + error_handler(&last_error); +} + int lock_pages(void *addr, size_t len) { int e = 0; diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h index 20f7a9b445..2ddec3f988 100644 --- a/tools/libxc/xc_private.h +++ b/tools/libxc/xc_private.h @@ -59,24 +59,15 @@ #define PPRINTF(_f, _a...) #endif -#define ERROR(_m, _a...) \ -do { \ - int __saved_errno = errno; \ - DPRINTF("ERROR: " _m "\n" , ## _a ); \ - errno = __saved_errno; \ -} while (0) +void xc_set_error(int code, const char *fmt, ...); + +#define ERROR(_m, _a...) xc_set_error(XC_INTERNAL_ERROR, _m , ## _a ) +#define PERROR(_m, _a...) xc_set_error(XC_INTERNAL_ERROR, _m " (%d = %s)", \ + _m , ## _a , errno, strerror(errno)) int lock_pages(void *addr, size_t len); void unlock_pages(void *addr, size_t len); -#define PERROR(_m, _a...) \ -do { \ - int __saved_errno = errno; \ - DPRINTF("ERROR: " _m " (%d = %s)\n" , ## _a , \ - __saved_errno, strerror(__saved_errno)); \ - errno = __saved_errno; \ -} while (0) - static inline void safe_munlock(const void *addr, size_t len) { int saved_errno = errno; diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index 3904cede84..d0a9f5db97 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -682,4 +682,46 @@ int xc_hvm_set_isa_irq_level( int xc_hvm_set_pci_link_route( int xc_handle, domid_t dom, uint8_t link, uint8_t isa_irq); + +typedef enum { + XC_ERROR_NONE = 0, + XC_INTERNAL_ERROR = 1, + XC_INVALID_KERNEL = 2, +} xc_error_code; + +#define XC_MAX_ERROR_MSG_LEN 1024 +typedef struct { + int code; + char message[XC_MAX_ERROR_MSG_LEN]; +} xc_error; + +/* + * Return a pointer to the last error. This pointer and the + * data pointed to are only valid until the next call to + * libxc. + */ +const xc_error const *xc_get_last_error(void); + +/* + * Clear the last error + */ +void xc_clear_last_error(void); + +typedef void (*xc_error_handler)(const xc_error const* err); + +/* + * The default error handler which prints to stderr + */ +void xc_default_error_handler(const xc_error const* err); + +/* + * Convert an error code into a text description + */ +const char *xc_error_code_to_desc(int code); + +/* + * Registers a callback to handle errors + */ +xc_error_handler xc_set_error_handler(xc_error_handler handler); + #endif diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 2eb9376426..be02bf8ed8 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -29,7 +29,7 @@ #define PKG "xen.lowlevel.xc" #define CLS "xc" -static PyObject *xc_error, *zero; +static PyObject *xc_error_obj, *zero; typedef struct { PyObject_HEAD; @@ -40,6 +40,23 @@ typedef struct { static PyObject *dom_op(XcObject *self, PyObject *args, int (*fn)(int, uint32_t)); +static PyObject *pyxc_error_to_exception(void) +{ + PyObject *pyerr; + const xc_error const *err = xc_get_last_error(); + const char *desc = xc_error_code_to_desc(err->code); + + if (err->message[1]) + pyerr = Py_BuildValue("(iss)", err->code, desc, err->message); + else + pyerr = Py_BuildValue("(is)", err->code, desc); + + xc_clear_last_error(); + + PyErr_SetObject(xc_error_obj, pyerr); + + return NULL; +} static PyObject *pyxc_domain_dumpcore(XcObject *self, PyObject *args) { @@ -53,7 +70,7 @@ static PyObject *pyxc_domain_dumpcore(XcObject *self, PyObject *args) return NULL; if (xc_domain_dumpcore(self->xc_handle, dom, corefile) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -101,13 +118,13 @@ static PyObject *pyxc_domain_create(XcObject *self, if ( (ret = xc_domain_create(self->xc_handle, ssidref, handle, flags, &dom)) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return PyInt_FromLong(dom); out_exception: errno = EINVAL; - PyErr_SetFromErrno(xc_error); + PyErr_SetFromErrno(xc_error_obj); return NULL; } @@ -119,7 +136,7 @@ static PyObject *pyxc_domain_max_vcpus(XcObject *self, PyObject *args) return NULL; if (xc_domain_max_vcpus(self->xc_handle, dom, max) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -164,7 +181,7 @@ static PyObject *pyxc_vcpu_setaffinity(XcObject *self, } if ( xc_vcpu_setaffinity(self->xc_handle, dom, vcpu, cpumap) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -184,7 +201,7 @@ static PyObject *pyxc_domain_setcpuweight(XcObject *self, return NULL; if ( xc_domain_setcpuweight(self->xc_handle, dom, cpuweight) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -215,14 +232,13 @@ static PyObject *pyxc_domain_sethandle(XcObject *self, PyObject *args) } if (xc_domain_sethandle(self->xc_handle, dom, handle) < 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; out_exception: - errno = EINVAL; - PyErr_SetFromErrno(xc_error); + PyErr_SetFromErrno(xc_error_obj); return NULL; } @@ -251,7 +267,7 @@ static PyObject *pyxc_domain_getinfo(XcObject *self, if (nr_doms < 0) { free(info); - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); } list = PyList_New(nr_doms); @@ -306,10 +322,10 @@ static PyObject *pyxc_vcpu_getinfo(XcObject *self, rc = xc_vcpu_getinfo(self->xc_handle, dom, vcpu, &info); if ( rc < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); rc = xc_vcpu_getaffinity(self->xc_handle, dom, vcpu, &cpumap); if ( rc < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); info_dict = Py_BuildValue("{s:i,s:i,s:i,s:L,s:i}", "online", info.online, @@ -360,9 +376,7 @@ static PyObject *pyxc_linux_build(XcObject *self, ramdisk, cmdline, features, flags, store_evtchn, &store_mfn, console_evtchn, &console_mfn) != 0 ) { - if (!errno) - errno = EINVAL; - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); } return Py_BuildValue("{s:i,s:i}", "store_mfn", store_mfn, @@ -389,14 +403,14 @@ static PyObject *pyxc_hvm_build(XcObject *self, return NULL; if ( xc_hvm_build(self->xc_handle, dom, memsize, image) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); /* Set up the HVM info table. */ va_map = xc_map_foreign_range(self->xc_handle, dom, XC_PAGE_SIZE, PROT_READ | PROT_WRITE, HVM_INFO_PFN); if ( va_map == NULL ) - return PyErr_SetFromErrno(xc_error); + return PyErr_SetFromErrno(xc_error_obj); va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET); memset(va_hvm, 0, sizeof(*va_hvm)); strncpy(va_hvm->signature, "HVM INFO", 8); @@ -431,7 +445,7 @@ static PyObject *pyxc_evtchn_alloc_unbound(XcObject *self, return NULL; if ( (port = xc_evtchn_alloc_unbound(self->xc_handle, dom, remote_dom)) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return PyInt_FromLong(port); } @@ -452,7 +466,7 @@ static PyObject *pyxc_physdev_pci_access_modify(XcObject *self, ret = xc_physdev_pci_access_modify( self->xc_handle, dom, bus, dev, func, enable); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -474,7 +488,7 @@ static PyObject *pyxc_readconsolering(XcObject *self, ret = xc_readconsolering(self->xc_handle, &str, &count, clear); if ( ret < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return PyString_FromStringAndSize(str, count); } @@ -504,7 +518,7 @@ static PyObject *pyxc_physinfo(XcObject *self) int i; if ( xc_physinfo(self->xc_handle, &info) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); *q=0; for(i=0;ixc_handle, XENVER_version, NULL); if ( xc_version(self->xc_handle, XENVER_extraversion, &xen_extra) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_compile_info, &xen_cc) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_changeset, &xen_chgset) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_capabilities, &xen_caps) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_platform_parameters, &p_parms) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); sprintf(str, "virt_start=0x%lx", p_parms.virt_start); xen_pagesize = xc_version(self->xc_handle, XENVER_pagesize, NULL); if (xen_pagesize < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return Py_BuildValue("{s:i,s:i,s:s,s:s,s:i,s:s,s:s,s:s,s:s,s:s,s:s}", "xen_major", xen_version >> 16, @@ -593,7 +607,7 @@ static PyObject *pyxc_sedf_domain_set(XcObject *self, return NULL; if ( xc_sedf_domain_set(self->xc_handle, domid, period, slice, latency, extratime,weight) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -610,7 +624,7 @@ static PyObject *pyxc_sedf_domain_get(XcObject *self, PyObject *args) if (xc_sedf_domain_get(self->xc_handle, domid, &period, &slice,&latency,&extratime,&weight)) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i,s:i}", "domid", domid, @@ -638,7 +652,7 @@ static PyObject *pyxc_shadow_control(PyObject *self, if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, NULL, 0, NULL) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -668,7 +682,7 @@ static PyObject *pyxc_shadow_mem_control(PyObject *self, op = XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION; } if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, &mb, 0, NULL) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); mbarg = mb; return Py_BuildValue("i", mbarg); @@ -678,7 +692,7 @@ static PyObject *pyxc_sched_id_get(XcObject *self) { int sched_id; if (xc_sched_id(self->xc_handle, &sched_id) != 0) - return PyErr_SetFromErrno(xc_error); + return PyErr_SetFromErrno(xc_error_obj); return Py_BuildValue("i", sched_id); } @@ -704,7 +718,7 @@ static PyObject *pyxc_sched_credit_domain_set(XcObject *self, sdom.cap = cap; if ( xc_sched_credit_domain_set(self->xc_handle, domid, &sdom) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -719,7 +733,7 @@ static PyObject *pyxc_sched_credit_domain_get(XcObject *self, PyObject *args) return NULL; if ( xc_sched_credit_domain_get(self->xc_handle, domid, &sdom) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return Py_BuildValue("{s:H,s:H}", "weight", sdom.weight, @@ -735,7 +749,7 @@ static PyObject *pyxc_domain_setmaxmem(XcObject *self, PyObject *args) return NULL; if (xc_domain_setmaxmem(self->xc_handle, dom, maxmem_kb) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -762,7 +776,7 @@ static PyObject *pyxc_domain_memory_increase_reservation(XcObject *self, if ( xc_domain_memory_increase_reservation(self->xc_handle, dom, nr_extents, extent_order, address_bits, NULL) ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -784,7 +798,7 @@ static PyObject *pyxc_domain_ioport_permission(XcObject *self, ret = xc_domain_ioport_permission( self->xc_handle, dom, first_port, nr_ports, allow_access); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -807,7 +821,7 @@ static PyObject *pyxc_domain_irq_permission(PyObject *self, ret = xc_domain_irq_permission( xc->xc_handle, dom, pirq, allow_access); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -830,7 +844,7 @@ static PyObject *pyxc_domain_iomem_permission(PyObject *self, ret = xc_domain_iomem_permission( xc->xc_handle, dom, first_pfn, nr_pfns, allow_access); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -870,7 +884,7 @@ static PyObject *dom_op(XcObject *self, PyObject *args, return NULL; if (fn(self->xc_handle, dom) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -1199,7 +1213,7 @@ static int PyXc_init(XcObject *self, PyObject *args, PyObject *kwds) { if ((self->xc_handle = xc_interface_open()) == -1) { - PyErr_SetFromErrno(xc_error); + pyxc_error_to_exception(); return -1; } @@ -1272,7 +1286,7 @@ PyMODINIT_FUNC initxc(void) if (m == NULL) return; - xc_error = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); + xc_error_obj = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); zero = PyInt_FromLong(0); /* KAF: This ensures that we get debug output in a timely manner. */ @@ -1282,8 +1296,8 @@ PyMODINIT_FUNC initxc(void) Py_INCREF(&PyXcType); PyModule_AddObject(m, CLS, (PyObject *)&PyXcType); - Py_INCREF(xc_error); - PyModule_AddObject(m, "Error", xc_error); + Py_INCREF(xc_error_obj); + PyModule_AddObject(m, "Error", xc_error_obj); /* Expose some libxc constants to Python */ PyModule_AddIntConstant(m, "XEN_SCHEDULER_SEDF", XEN_SCHEDULER_SEDF);