/****************************************************************************** * xc_linux_build.c */ #include "xg_private.h" #include "xc_private.h" #include #if defined(__i386__) #define ELFSIZE 32 #endif #if defined(__x86_64__) || defined(__ia64__) #define ELFSIZE 64 #endif #include "xc_elf.h" #include "xc_aout9.h" #include #include #include #if defined(__i386__) #define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED) #define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) #define L3_PROT (_PAGE_PRESENT) #endif #if defined(__x86_64__) #define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_USER) #define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) #define L3_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) #define L4_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER) #endif #ifdef __ia64__ #define get_tot_pages xc_get_max_pages #else #define get_tot_pages xc_get_tot_pages #endif #define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK) #define round_pgdown(_p) ((_p)&PAGE_MASK) #ifdef __ia64__ #define probe_aout9(image,image_size,load_funcs) 1 #endif struct initrd_info { enum { INITRD_none, INITRD_file, INITRD_mem } type; unsigned long len; union { gzFile file_handle; char *mem_addr; } u; }; static const char *feature_names[XENFEAT_NR_SUBMAPS*32] = { [XENFEAT_writable_page_tables] = "writable_page_tables", [XENFEAT_writable_descriptor_tables] = "writable_descriptor_tables", [XENFEAT_auto_translated_physmap] = "auto_translated_physmap", [XENFEAT_supervisor_mode_kernel] = "supervisor_mode_kernel", [XENFEAT_pae_pgdir_above_4gb] = "pae_pgdir_above_4gb" }; static inline void set_feature_bit (int nr, uint32_t *addr) { addr[nr>>5] |= (1<<(nr&31)); } static inline int test_feature_bit(int nr, uint32_t *addr) { return !!(addr[nr>>5] & (1<<(nr&31))); } static int parse_features( const char *feats, uint32_t supported[XENFEAT_NR_SUBMAPS], uint32_t required[XENFEAT_NR_SUBMAPS]) { const char *end, *p; int i, req; if ( (end = strchr(feats, ',')) == NULL ) end = feats + strlen(feats); while ( feats < end ) { p = strchr(feats, '|'); if ( (p == NULL) || (p > end) ) p = end; req = (*feats == '!'); if ( req ) feats++; for ( i = 0; i < XENFEAT_NR_SUBMAPS*32; i++ ) { if ( feature_names[i] == NULL ) continue; if ( strncmp(feature_names[i], feats, p-feats) == 0 ) { set_feature_bit(i, supported); if ( required && req ) set_feature_bit(i, required); break; } } if ( i == XENFEAT_NR_SUBMAPS*32 ) { ERROR("Unknown feature \"%.*s\".", (int)(p-feats), feats); if ( req ) { ERROR("Kernel requires an unknown hypervisor feature."); return -EINVAL; } } feats = p; if ( *feats == '|' ) feats++; } return -EINVAL; } static int probeimageformat(const char *image, unsigned long image_size, struct load_funcs *load_funcs) { if ( probe_elf(image, image_size, load_funcs) && probe_bin(image, image_size, load_funcs) && probe_aout9(image, image_size, load_funcs) ) { ERROR( "Unrecognized image format" ); return -EINVAL; } return 0; } int load_initrd(int xc_handle, domid_t dom, struct initrd_info *initrd, unsigned long physbase, unsigned long *phys_to_mach) { char page[PAGE_SIZE]; unsigned long pfn_start, pfn, nr_pages; if ( initrd->type == INITRD_none ) return 0; pfn_start = physbase >> PAGE_SHIFT; nr_pages = (initrd->len + PAGE_SIZE - 1) >> PAGE_SHIFT; for ( pfn = pfn_start; pfn < (pfn_start + nr_pages); pfn++ ) { if ( initrd->type == INITRD_mem ) { xc_copy_to_domain_page( xc_handle, dom, phys_to_mach[pfn], &initrd->u.mem_addr[(pfn - pfn_start) << PAGE_SHIFT]); } else { if ( gzread(initrd->u.file_handle, page, PAGE_SIZE) == -1 ) { PERROR("Error reading initrd image, could not"); return -EINVAL; } xc_copy_to_domain_page(xc_handle, dom, phys_to_mach[pfn], page); } } return 0; } #define alloc_pt(ltab, vltab, pltab) \ do { \ pltab = ppt_alloc++; \ ltab = (uint64_t)page_array[pltab] << PAGE_SHIFT; \ pltab <<= PAGE_SHIFT; \ if ( vltab != NULL ) \ munmap(vltab, PAGE_SIZE); \ if ( (vltab = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, \ PROT_READ|PROT_WRITE, \ ltab >> PAGE_SHIFT)) == NULL ) \ goto error_out; \ memset(vltab, 0x0, PAGE_SIZE); \ } while ( 0 ) #if defined(__i386__) static int setup_pg_tables(int xc_handle, uint32_t dom, vcpu_guest_context_t *ctxt, unsigned long dsi_v_start, unsigned long v_end, unsigned long *page_array, unsigned long vpt_start, unsigned long vpt_end, unsigned shadow_mode_enabled) { l1_pgentry_t *vl1tab=NULL, *vl1e=NULL; l2_pgentry_t *vl2tab=NULL, *vl2e=NULL; unsigned long l1tab = 0, pl1tab; unsigned long l2tab = 0, pl2tab; unsigned long ppt_alloc; unsigned long count; ppt_alloc = (vpt_start - dsi_v_start) >> PAGE_SHIFT; alloc_pt(l2tab, vl2tab, pl2tab); vl2e = &vl2tab[l2_table_offset(dsi_v_start)]; if (shadow_mode_enabled) ctxt->ctrlreg[3] = pl2tab; else ctxt->ctrlreg[3] = l2tab; for ( count = 0; count < ((v_end - dsi_v_start) >> PAGE_SHIFT); count++ ) { if ( ((unsigned long)vl1e & (PAGE_SIZE-1)) == 0 ) { alloc_pt(l1tab, vl1tab, pl1tab); vl1e = &vl1tab[l1_table_offset(dsi_v_start + (count<= ((vpt_start-dsi_v_start)>>PAGE_SHIFT)) && (count < ((vpt_end -dsi_v_start)>>PAGE_SHIFT)) ) *vl1e &= ~_PAGE_RW; } vl1e++; } munmap(vl1tab, PAGE_SIZE); munmap(vl2tab, PAGE_SIZE); return 0; error_out: if (vl1tab) munmap(vl1tab, PAGE_SIZE); if (vl2tab) munmap(vl2tab, PAGE_SIZE); return -1; } static int setup_pg_tables_pae(int xc_handle, uint32_t dom, vcpu_guest_context_t *ctxt, unsigned long dsi_v_start, unsigned long v_end, unsigned long *page_array, unsigned long vpt_start, unsigned long vpt_end, unsigned shadow_mode_enabled) { l1_pgentry_64_t *vl1tab = NULL, *vl1e = NULL; l2_pgentry_64_t *vl2tab = NULL, *vl2e = NULL; l3_pgentry_64_t *vl3tab = NULL, *vl3e = NULL; uint64_t l1tab, l2tab, l3tab, pl1tab, pl2tab, pl3tab; unsigned long ppt_alloc, count, nmfn; /* First allocate page for page dir. */ ppt_alloc = (vpt_start - dsi_v_start) >> PAGE_SHIFT; if ( page_array[ppt_alloc] > 0xfffff ) { nmfn = xc_make_page_below_4G(xc_handle, dom, page_array[ppt_alloc]); if ( nmfn == 0 ) { fprintf(stderr, "Couldn't get a page below 4GB :-(\n"); goto error_out; } page_array[ppt_alloc] = nmfn; } alloc_pt(l3tab, vl3tab, pl3tab); vl3e = &vl3tab[l3_table_offset_pae(dsi_v_start)]; if (shadow_mode_enabled) ctxt->ctrlreg[3] = pl3tab; else ctxt->ctrlreg[3] = l3tab; for ( count = 0; count < ((v_end - dsi_v_start) >> PAGE_SHIFT); count++) { if ( !((unsigned long)vl1e & (PAGE_SIZE-1)) ) { if ( !((unsigned long)vl2e & (PAGE_SIZE-1)) ) { alloc_pt(l2tab, vl2tab, pl2tab); vl2e = &vl2tab[l2_table_offset_pae( dsi_v_start + (count << PAGE_SHIFT))]; if (shadow_mode_enabled) *vl3e = pl2tab | L3_PROT; else *vl3e++ = l2tab | L3_PROT; } alloc_pt(l1tab, vl1tab, pl1tab); vl1e = &vl1tab[l1_table_offset_pae( dsi_v_start + (count << PAGE_SHIFT))]; if (shadow_mode_enabled) *vl2e = pl1tab | L2_PROT; else *vl2e++ = l1tab | L2_PROT; } if ( shadow_mode_enabled ) { *vl1e = (count << PAGE_SHIFT) | L1_PROT; } else { *vl1e = ((uint64_t)page_array[count] << PAGE_SHIFT) | L1_PROT; if ( (count >= ((vpt_start-dsi_v_start)>>PAGE_SHIFT)) && (count < ((vpt_end -dsi_v_start)>>PAGE_SHIFT)) ) *vl1e &= ~_PAGE_RW; } vl1e++; } munmap(vl1tab, PAGE_SIZE); munmap(vl2tab, PAGE_SIZE); munmap(vl3tab, PAGE_SIZE); return 0; error_out: if (vl1tab) munmap(vl1tab, PAGE_SIZE); if (vl2tab) munmap(vl2tab, PAGE_SIZE); if (vl3tab) munmap(vl3tab, PAGE_SIZE); return -1; } #endif #if defined(__x86_64__) static int setup_pg_tables_64(int xc_handle, uint32_t dom, vcpu_guest_context_t *ctxt, unsigned long dsi_v_start, unsigned long v_end, unsigned long *page_array, unsigned long vpt_start, unsigned long vpt_end, int shadow_mode_enabled) { l1_pgentry_t *vl1tab=NULL, *vl1e=NULL; l2_pgentry_t *vl2tab=NULL, *vl2e=NULL; l3_pgentry_t *vl3tab=NULL, *vl3e=NULL; l4_pgentry_t *vl4tab=NULL, *vl4e=NULL; unsigned long l2tab = 0, pl2tab; unsigned long l1tab = 0, pl1tab; unsigned long l3tab = 0, pl3tab; unsigned long l4tab = 0, pl4tab; unsigned long ppt_alloc; unsigned long count; /* First allocate page for page dir. */ ppt_alloc = (vpt_start - dsi_v_start) >> PAGE_SHIFT; alloc_pt(l4tab, vl4tab, pl4tab); vl4e = &vl4tab[l4_table_offset(dsi_v_start)]; if (shadow_mode_enabled) ctxt->ctrlreg[3] = pl4tab; else ctxt->ctrlreg[3] = l4tab; for ( count = 0; count < ((v_end-dsi_v_start)>>PAGE_SHIFT); count++) { if ( !((unsigned long)vl1e & (PAGE_SIZE-1)) ) { alloc_pt(l1tab, vl1tab, pl1tab); if ( !((unsigned long)vl2e & (PAGE_SIZE-1)) ) { alloc_pt(l2tab, vl2tab, pl2tab); if ( !((unsigned long)vl3e & (PAGE_SIZE-1)) ) { alloc_pt(l3tab, vl3tab, pl3tab); vl3e = &vl3tab[l3_table_offset(dsi_v_start + (count<= ((vpt_start-dsi_v_start)>>PAGE_SHIFT)) && (count < ((vpt_end -dsi_v_start)>>PAGE_SHIFT)) ) { *vl1e &= ~_PAGE_RW; } } vl1e++; } munmap(vl1tab, PAGE_SIZE); munmap(vl2tab, PAGE_SIZE); munmap(vl3tab, PAGE_SIZE); munmap(vl4tab, PAGE_SIZE); return 0; error_out: if (vl1tab) munmap(vl1tab, PAGE_SIZE); if (vl2tab) munmap(vl2tab, PAGE_SIZE); if (vl3tab) munmap(vl3tab, PAGE_SIZE); if (vl4tab) munmap(vl4tab, PAGE_SIZE); return -1; } #endif #ifdef __ia64__ extern unsigned long xc_ia64_fpsr_default(void); static int setup_guest(int xc_handle, uint32_t dom, const char *image, unsigned long image_size, struct initrd_info *initrd, unsigned long nr_pages, unsigned long *pvsi, unsigned long *pvke, unsigned long *pvss, vcpu_guest_context_t *ctxt, const char *cmdline, unsigned long shared_info_frame, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn, uint32_t required_features[XENFEAT_NR_SUBMAPS]) { unsigned long *page_array = NULL; struct load_funcs load_funcs; struct domain_setup_info dsi; unsigned long vinitrd_start; unsigned long vinitrd_end; unsigned long v_end; unsigned long start_page, pgnr; start_info_t *start_info; int rc; rc = probeimageformat(image, image_size, &load_funcs); if ( rc != 0 ) goto error_out; memset(&dsi, 0, sizeof(struct domain_setup_info)); rc = (load_funcs.parseimage)(image, image_size, &dsi); if ( rc != 0 ) goto error_out; dsi.v_start = round_pgdown(dsi.v_start); vinitrd_start = round_pgup(dsi.v_end); vinitrd_end = vinitrd_start + initrd->len; v_end = round_pgup(vinitrd_end); start_page = dsi.v_start >> PAGE_SHIFT; pgnr = (v_end - dsi.v_start) >> PAGE_SHIFT; if ( (page_array = malloc(pgnr * sizeof(unsigned long))) == NULL ) { PERROR("Could not allocate memory"); goto error_out; } if ( xc_ia64_get_pfn_list(xc_handle, dom, page_array, start_page, pgnr) != pgnr ) { PERROR("Could not get the page frame list"); goto error_out; } #define _p(a) ((void *) (a)) printf("VIRTUAL MEMORY ARRANGEMENT:\n" " Loaded kernel: %p->%p\n" " Init. ramdisk: %p->%p\n" " TOTAL: %p->%p\n", _p(dsi.v_kernstart), _p(dsi.v_kernend), _p(vinitrd_start), _p(vinitrd_end), _p(dsi.v_start), _p(v_end)); printf(" ENTRY ADDRESS: %p\n", _p(dsi.v_kernentry)); (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array, &dsi); if ( load_initrd(xc_handle, dom, initrd, vinitrd_start - dsi.v_start, page_array) ) goto error_out; *pvke = dsi.v_kernentry; /* Now need to retrieve machine pfn for system pages: * start_info/store/console */ pgnr = 3; if ( xc_ia64_get_pfn_list(xc_handle, dom, page_array, nr_pages - 3, pgnr) != pgnr ) { PERROR("Could not get page frame for xenstore"); goto error_out; } *store_mfn = page_array[1]; *console_mfn = page_array[2]; printf("start_info: 0x%lx at 0x%lx, " "store_mfn: 0x%lx at 0x%lx, " "console_mfn: 0x%lx at 0x%lx\n", page_array[0], nr_pages, *store_mfn, nr_pages - 2, *console_mfn, nr_pages - 1); start_info = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[0]); memset(start_info, 0, sizeof(*start_info)); rc = xc_version(xc_handle, XENVER_version, NULL); sprintf(start_info->magic, "xen-%i.%i-ia64", rc >> 16, rc & (0xFFFF)); start_info->flags = flags; start_info->store_mfn = nr_pages - 2; start_info->store_evtchn = store_evtchn; start_info->console_mfn = nr_pages - 1; start_info->console_evtchn = console_evtchn; start_info->nr_pages = nr_pages; // FIXME?: nr_pages - 2 ???? if ( initrd->len != 0 ) { ctxt->initrd.start = vinitrd_start; ctxt->initrd.size = initrd->len; } else { ctxt->initrd.start = 0; ctxt->initrd.size = 0; } if ( cmdline != NULL ) { strncpy((char *)ctxt->cmdline, cmdline, IA64_COMMAND_LINE_SIZE); ctxt->cmdline[IA64_COMMAND_LINE_SIZE-1] = '\0'; } munmap(start_info, PAGE_SIZE); free(page_array); return 0; error_out: free(page_array); return -1; } #else /* x86 */ /* Check if the platform supports the guest kernel format */ 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."); return 0; } if (strstr(xen_caps, "xen-3.0-x86_32p")) { if (!dsi->pae_kernel) { ERROR("Non PAE-kernel on PAE host."); return 0; } } else if (dsi->pae_kernel) { ERROR("PAE-kernel on non-PAE host."); return 0; } return 1; } static int setup_guest(int xc_handle, uint32_t dom, const char *image, unsigned long image_size, struct initrd_info *initrd, unsigned long nr_pages, unsigned long *pvsi, unsigned long *pvke, unsigned long *pvss, vcpu_guest_context_t *ctxt, const char *cmdline, unsigned long shared_info_frame, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn, uint32_t required_features[XENFEAT_NR_SUBMAPS]) { unsigned long *page_array = NULL; unsigned long count, i, hypercall_pfn; start_info_t *start_info; shared_info_t *shared_info; xc_mmu_t *mmu = NULL; char *p; DECLARE_DOM0_OP; int rc; unsigned long nr_pt_pages; unsigned long physmap_pfn; unsigned long *physmap, *physmap_e; struct load_funcs load_funcs; struct domain_setup_info dsi; unsigned long vinitrd_start; unsigned long vphysmap_start; unsigned long vstartinfo_start; unsigned long vstoreinfo_start; unsigned long vconsole_start; unsigned long vsharedinfo_start = 0; /* XXX gcc */ unsigned long vstack_start; unsigned long vstack_end; unsigned long vpt_start; unsigned long vpt_end; unsigned long v_end; unsigned long guest_store_mfn, guest_console_mfn, guest_shared_info_mfn; unsigned long shadow_mode_enabled; uint32_t supported_features[XENFEAT_NR_SUBMAPS] = { 0, }; rc = probeimageformat(image, image_size, &load_funcs); if ( rc != 0 ) goto error_out; memset(&dsi, 0, sizeof(struct domain_setup_info)); rc = (load_funcs.parseimage)(image, image_size, &dsi); if ( rc != 0 ) goto error_out; if ( (dsi.v_start & (PAGE_SIZE-1)) != 0 ) { PERROR("Guest OS must load to a page boundary."); goto error_out; } if (!compat_check(xc_handle, &dsi)) goto error_out; /* Parse and validate kernel features. */ p = strstr(dsi.xen_guest_string, "FEATURES="); if ( p != NULL ) { if ( !parse_features(p + strlen("FEATURES="), supported_features, required_features) ) { ERROR("Failed to parse guest kernel features."); goto error_out; } printf("Supported features = { %08x }.\n", supported_features[0]); printf("Required features = { %08x }.\n", required_features[0]); } for ( i = 0; i < XENFEAT_NR_SUBMAPS; i++ ) { if ( (supported_features[i]&required_features[i]) != required_features[i] ) { ERROR("Guest kernel does not support a required feature."); goto error_out; } } shadow_mode_enabled = test_feature_bit(XENFEAT_auto_translated_physmap, required_features); /* * Why do we need this? The number of page-table frames depends on the * size of the bootstrap address space. But the size of the address space * depends on the number of page-table frames (since each one is mapped * read-only). We have a pair of simultaneous equations in two unknowns, * which we solve by exhaustive search. */ v_end = round_pgup(dsi.v_end); vinitrd_start = v_end; v_end += round_pgup(initrd->len); vphysmap_start = v_end; v_end += round_pgup(nr_pages * sizeof(unsigned long)); vstartinfo_start = v_end; v_end += PAGE_SIZE; vstoreinfo_start = v_end; v_end += PAGE_SIZE; vconsole_start = v_end; v_end += PAGE_SIZE; if ( shadow_mode_enabled ) { vsharedinfo_start = v_end; v_end += PAGE_SIZE; } vpt_start = v_end; for ( nr_pt_pages = 2; ; nr_pt_pages++ ) { vpt_end = vpt_start + (nr_pt_pages * PAGE_SIZE); vstack_start = vpt_end; vstack_end = vstack_start + PAGE_SIZE; v_end = (vstack_end + (1UL<<22)-1) & ~((1UL<<22)-1); if ( (v_end - vstack_end) < (512UL << 10) ) v_end += 1UL << 22; /* Add extra 4MB to get >= 512kB padding. */ #if defined(__i386__) if ( dsi.pae_kernel ) { /* FIXME: assumes one L2 pgtable @ 0xc0000000 */ if ( (((v_end - dsi.v_start + ((1<> L2_PAGETABLE_SHIFT_PAE) + 2) <= nr_pt_pages ) break; } else { if ( (((v_end - dsi.v_start + ((1<> L2_PAGETABLE_SHIFT) + 1) <= nr_pt_pages ) break; } #endif #if defined(__x86_64__) #define NR(_l,_h,_s) \ (((((_h) + ((1UL<<(_s))-1)) & ~((1UL<<(_s))-1)) - \ ((_l) & ~((1UL<<(_s))-1))) >> (_s)) if ( (1 + /* # L4 */ NR(dsi.v_start, v_end, L4_PAGETABLE_SHIFT) + /* # L3 */ NR(dsi.v_start, v_end, L3_PAGETABLE_SHIFT) + /* # L2 */ NR(dsi.v_start, v_end, L2_PAGETABLE_SHIFT)) /* # L1 */ <= nr_pt_pages ) break; #endif } #define _p(a) ((void *) (a)) printf("VIRTUAL MEMORY ARRANGEMENT:\n"); printf(" Loaded kernel: %p->%p\n", _p(dsi.v_kernstart), _p(dsi.v_kernend)); if ( initrd->len ) printf(" Initial ramdisk: %p->%p\n", _p(vinitrd_start), _p(vinitrd_start + initrd->len)); printf(" Phys-Mach map: %p\n", _p(vphysmap_start)); printf(" Start info: %p\n", _p(vstartinfo_start)); printf(" Store page: %p\n", _p(vstoreinfo_start)); printf(" Console page: %p\n", _p(vconsole_start)); if ( shadow_mode_enabled ) printf(" Shared Info page: %p\n", _p(vsharedinfo_start)); printf(" Page tables: %p\n", _p(vpt_start)); printf(" Boot stack: %p\n", _p(vstack_start)); printf(" TOTAL: %p->%p\n", _p(dsi.v_start), _p(v_end)); printf(" ENTRY ADDRESS: %p\n", _p(dsi.v_kernentry)); if ( ((v_end - dsi.v_start)>>PAGE_SHIFT) > nr_pages ) { PERROR("Initial guest OS requires too much space\n" "(%luMB is greater than %luMB limit)\n", (v_end-dsi.v_start)>>20, nr_pages>>(20-PAGE_SHIFT)); goto error_out; } if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL ) { PERROR("Could not allocate memory"); goto error_out; } if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages ) { PERROR("Could not get the page frame list"); goto error_out; } (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array, &dsi); if ( load_initrd(xc_handle, dom, initrd, vinitrd_start - dsi.v_start, page_array) ) goto error_out; /* setup page tables */ #if defined(__i386__) if (dsi.pae_kernel) rc = setup_pg_tables_pae(xc_handle, dom, ctxt, dsi.v_start, v_end, page_array, vpt_start, vpt_end, shadow_mode_enabled); else rc = setup_pg_tables(xc_handle, dom, ctxt, dsi.v_start, v_end, page_array, vpt_start, vpt_end, shadow_mode_enabled); #endif #if defined(__x86_64__) rc = setup_pg_tables_64(xc_handle, dom, ctxt, dsi.v_start, v_end, page_array, vpt_start, vpt_end, shadow_mode_enabled); #endif if (0 != rc) goto error_out; #if defined(__i386__) /* * Pin down l2tab addr as page dir page - causes hypervisor to provide * correct protection for the page */ if ( !shadow_mode_enabled ) { if ( dsi.pae_kernel ) { if ( pin_table(xc_handle, MMUEXT_PIN_L3_TABLE, ctxt->ctrlreg[3] >> PAGE_SHIFT, dom) ) goto error_out; } else { if ( pin_table(xc_handle, MMUEXT_PIN_L2_TABLE, ctxt->ctrlreg[3] >> PAGE_SHIFT, dom) ) goto error_out; } } #endif #if defined(__x86_64__) /* * Pin down l4tab addr as page dir page - causes hypervisor to provide * correct protection for the page */ if ( pin_table(xc_handle, MMUEXT_PIN_L4_TABLE, ctxt->ctrlreg[3] >> PAGE_SHIFT, dom) ) goto error_out; #endif if ( (mmu = xc_init_mmu_updates(xc_handle, dom)) == NULL ) goto error_out; /* Write the phys->machine and machine->phys table entries. */ physmap_pfn = (vphysmap_start - dsi.v_start) >> PAGE_SHIFT; physmap = physmap_e = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[physmap_pfn++]); for ( count = 0; count < nr_pages; count++ ) { if ( xc_add_mmu_update( xc_handle, mmu, ((uint64_t)page_array[count] << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE, count) ) { fprintf(stderr,"m2p update failure p=%lx m=%lx\n", count, page_array[count]); munmap(physmap, PAGE_SIZE); goto error_out; } *physmap_e++ = page_array[count]; if ( ((unsigned long)physmap_e & (PAGE_SIZE-1)) == 0 ) { munmap(physmap, PAGE_SIZE); physmap = physmap_e = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[physmap_pfn++]); } } munmap(physmap, PAGE_SIZE); /* Send the page update requests down to the hypervisor. */ if ( xc_finish_mmu_updates(xc_handle, mmu) ) goto error_out; if ( shadow_mode_enabled ) { struct xen_add_to_physmap xatp; /* Enable shadow translate mode */ if ( xc_shadow_control(xc_handle, dom, DOM0_SHADOW_CONTROL_OP_ENABLE_TRANSLATE, NULL, 0, NULL) < 0 ) { PERROR("Could not enable translation mode"); goto error_out; } guest_shared_info_mfn = (vsharedinfo_start-dsi.v_start) >> PAGE_SHIFT; /* Map shared info frame into guest physmap. */ xatp.domid = dom; xatp.space = XENMAPSPACE_shared_info; xatp.idx = 0; xatp.gpfn = guest_shared_info_mfn; rc = xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); if ( rc != 0 ) { PERROR("Cannot map shared info pfn"); goto error_out; } /* Map grant table frames into guest physmap. */ for ( i = 0; ; i++ ) { xatp.domid = dom; xatp.space = XENMAPSPACE_grant_table; xatp.idx = i; xatp.gpfn = nr_pages + i; rc = xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); if ( rc != 0 ) { if ( errno == EINVAL ) break; /* done all grant tables */ PERROR("Cannot map grant table pfn"); goto error_out; } } } else { guest_shared_info_mfn = shared_info_frame; } *store_mfn = page_array[(vstoreinfo_start-dsi.v_start) >> PAGE_SHIFT]; *console_mfn = page_array[(vconsole_start-dsi.v_start) >> PAGE_SHIFT]; if ( xc_clear_domain_page(xc_handle, dom, *store_mfn) || xc_clear_domain_page(xc_handle, dom, *console_mfn) ) goto error_out; if ( shadow_mode_enabled ) { guest_store_mfn = (vstoreinfo_start-dsi.v_start) >> PAGE_SHIFT; guest_console_mfn = (vconsole_start-dsi.v_start) >> PAGE_SHIFT; } else { guest_store_mfn = *store_mfn; guest_console_mfn = *console_mfn; } start_info = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[(vstartinfo_start-dsi.v_start)>>PAGE_SHIFT]); /*shared_info, start_info */ memset(start_info, 0, sizeof(*start_info)); rc = xc_version(xc_handle, XENVER_version, NULL); sprintf(start_info->magic, "xen-%i.%i-x86_%d%s", rc >> 16, rc & (0xFFFF), (unsigned int)sizeof(long)*8, dsi.pae_kernel ? "p" : ""); start_info->nr_pages = nr_pages; start_info->shared_info = guest_shared_info_mfn << PAGE_SHIFT; start_info->flags = flags; start_info->pt_base = vpt_start; start_info->nr_pt_frames = nr_pt_pages; start_info->mfn_list = vphysmap_start; start_info->store_mfn = guest_store_mfn; start_info->store_evtchn = store_evtchn; start_info->console_mfn = guest_console_mfn; start_info->console_evtchn = console_evtchn; if ( initrd->len != 0 ) { start_info->mod_start = vinitrd_start; start_info->mod_len = initrd->len; } if ( cmdline != NULL ) { strncpy((char *)start_info->cmd_line, cmdline, MAX_GUEST_CMDLINE); start_info->cmd_line[MAX_GUEST_CMDLINE-1] = '\0'; } munmap(start_info, PAGE_SIZE); /* shared_info page starts its life empty. */ shared_info = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, shared_info_frame); memset(shared_info, 0, sizeof(shared_info_t)); /* Mask all upcalls... */ for ( i = 0; i < MAX_VIRT_CPUS; i++ ) shared_info->vcpu_info[i].evtchn_upcall_mask = 1; munmap(shared_info, PAGE_SIZE); /* Send the page update requests down to the hypervisor. */ if ( xc_finish_mmu_updates(xc_handle, mmu) ) goto error_out; p = strstr(dsi.xen_guest_string, "HYPERCALL_PAGE="); if ( p != NULL ) { p += strlen("HYPERCALL_PAGE="); hypercall_pfn = strtoul(p, NULL, 16); if ( hypercall_pfn >= nr_pages ) goto error_out; op.u.hypercall_init.domain = (domid_t)dom; op.u.hypercall_init.mfn = page_array[hypercall_pfn]; op.cmd = DOM0_HYPERCALL_INIT; if ( xc_dom0_op(xc_handle, &op) ) goto error_out; } free(mmu); free(page_array); *pvsi = vstartinfo_start; *pvss = vstack_start; *pvke = dsi.v_kernentry; return 0; error_out: free(mmu); free(page_array); return -1; } #endif static int xc_linux_build_internal(int xc_handle, uint32_t domid, char *image, unsigned long image_size, struct initrd_info *initrd, const char *cmdline, const char *features, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn) { dom0_op_t launch_op; DECLARE_DOM0_OP; int rc, i; vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt; unsigned long nr_pages; unsigned long vstartinfo_start, vkern_entry, vstack_start; uint32_t features_bitmap[XENFEAT_NR_SUBMAPS] = { 0, }; if ( features != NULL ) { if ( !parse_features(features, features_bitmap, NULL) ) { PERROR("Failed to parse configured features\n"); goto error_out; } } if ( (nr_pages = get_tot_pages(xc_handle, domid)) < 0 ) { PERROR("Could not find total pages for domain"); goto error_out; } #ifdef VALGRIND memset(&st_ctxt, 0, sizeof(st_ctxt)); #endif if ( mlock(&st_ctxt, sizeof(st_ctxt) ) ) { PERROR("%s: ctxt mlock failed", __func__); return 1; } op.cmd = DOM0_GETDOMAININFO; op.u.getdomaininfo.domain = (domid_t)domid; if ( (xc_dom0_op(xc_handle, &op) < 0) || ((uint16_t)op.u.getdomaininfo.domain != domid) ) { PERROR("Could not get info on domain"); goto error_out; } memset(ctxt, 0, sizeof(*ctxt)); if ( setup_guest(xc_handle, domid, image, image_size, initrd, nr_pages, &vstartinfo_start, &vkern_entry, &vstack_start, ctxt, cmdline, op.u.getdomaininfo.shared_info_frame, flags, store_evtchn, store_mfn, console_evtchn, console_mfn, features_bitmap) < 0 ) { ERROR("Error constructing guest OS"); goto error_out; } #ifdef __ia64__ /* based on new_thread in xen/arch/ia64/domain.c */ ctxt->flags = 0; ctxt->shared.flags = flags; ctxt->shared.start_info_pfn = nr_pages - 3; /* metaphysical */ ctxt->regs.cr_ipsr = 0; /* all necessary bits filled by hypervisor */ ctxt->regs.cr_iip = vkern_entry; ctxt->regs.cr_ifs = 1UL << 63; ctxt->regs.ar_fpsr = xc_ia64_fpsr_default(); /* currently done by hypervisor, should move here */ /* ctxt->regs.r28 = dom_fw_setup(); */ ctxt->vcpu.privregs = 0; ctxt->sys_pgnr = 3; i = 0; /* silence unused variable warning */ #else /* x86 */ /* * Initial register values: * DS,ES,FS,GS = FLAT_KERNEL_DS * CS:EIP = FLAT_KERNEL_CS:start_pc * SS:ESP = FLAT_KERNEL_DS:start_stack * ESI = start_info * [EAX,EBX,ECX,EDX,EDI,EBP are zero] * EFLAGS = IF | 2 (bit 1 is reserved and should always be 1) */ ctxt->user_regs.ds = FLAT_KERNEL_DS; ctxt->user_regs.es = FLAT_KERNEL_DS; ctxt->user_regs.fs = FLAT_KERNEL_DS; ctxt->user_regs.gs = FLAT_KERNEL_DS; ctxt->user_regs.ss = FLAT_KERNEL_SS; ctxt->user_regs.cs = FLAT_KERNEL_CS; ctxt->user_regs.eip = vkern_entry; ctxt->user_regs.esp = vstack_start + PAGE_SIZE; ctxt->user_regs.esi = vstartinfo_start; ctxt->user_regs.eflags = 1 << 9; /* Interrupt Enable */ ctxt->flags = VGCF_IN_KERNEL; /* FPU is set up to default initial state. */ memset(&ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt)); /* Virtual IDT is empty at start-of-day. */ for ( i = 0; i < 256; i++ ) { ctxt->trap_ctxt[i].vector = i; ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS; } /* No LDT. */ ctxt->ldt_ents = 0; /* Use the default Xen-provided GDT. */ ctxt->gdt_ents = 0; /* Ring 1 stack is the initial stack. */ ctxt->kernel_ss = FLAT_KERNEL_SS; ctxt->kernel_sp = vstack_start + PAGE_SIZE; /* No debugging. */ memset(ctxt->debugreg, 0, sizeof(ctxt->debugreg)); /* No callback handlers. */ #if defined(__i386__) ctxt->event_callback_cs = FLAT_KERNEL_CS; ctxt->event_callback_eip = 0; ctxt->failsafe_callback_cs = FLAT_KERNEL_CS; ctxt->failsafe_callback_eip = 0; #elif defined(__x86_64__) ctxt->event_callback_eip = 0; ctxt->failsafe_callback_eip = 0; ctxt->syscall_callback_eip = 0; #endif #endif /* x86 */ memset( &launch_op, 0, sizeof(launch_op) ); launch_op.u.setvcpucontext.domain = (domid_t)domid; launch_op.u.setvcpucontext.vcpu = 0; launch_op.u.setvcpucontext.ctxt = ctxt; launch_op.cmd = DOM0_SETVCPUCONTEXT; rc = xc_dom0_op(xc_handle, &launch_op); return rc; error_out: return -1; } int xc_linux_build_mem(int xc_handle, uint32_t domid, const char *image_buffer, unsigned long image_size, const char *initrd, unsigned long initrd_len, const char *cmdline, const char *features, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn) { int sts; char *img_buf; unsigned long img_len; struct initrd_info initrd_info = { .type = INITRD_none }; /* A kernel buffer is required */ if ( (image_buffer == NULL) || (image_size == 0) ) { ERROR("kernel image buffer not present"); return -1; } /* If it's gzipped, inflate it; otherwise, use as is */ /* xc_inflate_buffer may return the same buffer pointer if */ /* the buffer is already inflated */ img_buf = xc_inflate_buffer(image_buffer, image_size, &img_len); if ( img_buf == NULL ) { ERROR("unable to inflate kernel image buffer"); return -1; } /* RAM disks are optional; if we get one, inflate it */ if ( initrd != NULL ) { initrd_info.type = INITRD_mem; initrd_info.u.mem_addr = xc_inflate_buffer( initrd, initrd_len, &initrd_info.len); if ( initrd_info.u.mem_addr == NULL ) { ERROR("unable to inflate ram disk buffer"); sts = -1; goto out; } } sts = xc_linux_build_internal(xc_handle, domid, img_buf, img_len, &initrd_info, cmdline, features, flags, store_evtchn, store_mfn, console_evtchn, console_mfn); out: /* The inflation routines may pass back the same buffer so be */ /* sure that we have a buffer and that it's not the one passed in. */ /* Don't unnecessarily annoy/surprise/confound the caller */ if ( (img_buf != NULL) && (img_buf != image_buffer) ) free(img_buf); if ( (initrd_info.u.mem_addr != NULL) && (initrd_info.u.mem_addr != initrd) ) free(initrd_info.u.mem_addr); return sts; } int xc_linux_build(int xc_handle, uint32_t domid, const char *image_name, const char *initrd_name, const char *cmdline, const char *features, unsigned long flags, unsigned int store_evtchn, unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn) { char *image = NULL; unsigned long image_size; struct initrd_info initrd_info = { .type = INITRD_none }; int fd = -1, sts = -1; if ( (image_name == NULL) || ((image = xc_read_image(image_name, &image_size)) == NULL )) return -1; if ( (initrd_name != NULL) && (strlen(initrd_name) != 0) ) { initrd_info.type = INITRD_file; if ( (fd = open(initrd_name, O_RDONLY)) < 0 ) { PERROR("Could not open the initial ramdisk image"); goto error_out; } initrd_info.len = xc_get_filesz(fd); if ( (initrd_info.u.file_handle = gzdopen(fd, "rb")) == NULL ) { PERROR("Could not allocate decompression state for initrd"); goto error_out; } } sts = xc_linux_build_internal(xc_handle, domid, image, image_size, &initrd_info, cmdline, features, flags, store_evtchn, store_mfn, console_evtchn, console_mfn); error_out: free(image); if ( fd >= 0 ) close(fd); if ( initrd_info.u.file_handle ) gzclose(initrd_info.u.file_handle); return sts; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */