# HG changeset patch # Node ID 7f9e81f7cbbc31125e9cbf7012d59ecd77f683c3 # Parent d929135007adb8b7cc5ac0aa8b8172f2468d4bc5 # Add support for buffer-input (as opposed to file names) for xc_linux_build # add xc_vmx_build. In order to maintain the existing interfaces, add # new routines called xc_linux_build_mem and xc_vmx_build_mem. This was one # choice. Another was to change the existing interfaces. # # The buffer-based routines may take compressed and uncompressed buffers, # and use zlib to gunzip any buffer detected as being gzip compressed. # This matches the filename-based behavior. # # While here, add some more defensive code to guard against NULL input # arguments to a few routines. # # Signed-off-by: Ben Thomas diff -r d929135007ad -r 7f9e81f7cbbc tools/libxc/xc_linux_build.c --- a/tools/libxc/xc_linux_build.c Thu Jan 12 21:07:52 2006 +++ b/tools/libxc/xc_linux_build.c Mon Jan 16 20:39:54 2006 @@ -282,8 +282,9 @@ static int setup_guest(int xc_handle, uint32_t dom, - char *image, unsigned long image_size, + const unsigned char *image, unsigned long image_size, gzFile initrd_gfd, unsigned long initrd_len, + unsigned char *ramdisk_buffer, unsigned long ramdisk_size, unsigned long nr_pages, unsigned long *pvsi, unsigned long *pvke, unsigned long *pvss, vcpu_guest_context_t *ctxt, @@ -350,20 +351,79 @@ /* Load the initial ramdisk image. */ if ( initrd_len != 0 ) { - for ( i = (vinitrd_start - dsi.v_start); - i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE ) - { - char page[PAGE_SIZE]; - if ( gzread(initrd_gfd, page, PAGE_SIZE) == -1 ) + // If there's a file for input, use gzread to get the data + if (initrd_gfd != NULL) + { + for ( i = (vinitrd_start - dsi.v_start); + i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE ) + { + char page[PAGE_SIZE]; + if ( gzread(initrd_gfd, page, PAGE_SIZE) == -1 ) + { + PERROR("Error reading initrd image, could not"); + goto error_out; + } + xc_copy_to_domain_page(xc_handle, dom, + page_array[i>>PAGE_SHIFT], page); + } + } + + // Else, if there's a buffer, loop over it with inflate + + else if (ramdisk_size > 0) + { + z_stream zStream; + unsigned char page[PAGE_SIZE]; + int sts; + int remain; + + bzero(&zStream, sizeof(zStream)); + zStream.next_in = ramdisk_buffer; + zStream.avail_in = ramdisk_size; + zStream.next_out = page; + zStream.avail_out = sizeof(page); + sts = inflateInit2(&zStream, (MAX_WBITS+32)); // +32 means "handle gzip" + if (sts != Z_OK) { - PERROR("Error reading initrd image, could not"); + ERROR("inflateInit failed, sts %d\n", sts); goto error_out; } - xc_copy_to_domain_page(xc_handle, dom, - page_array[i>>PAGE_SHIFT], page); - } - } - + + // Pages are not contiguous, so do the inflation a page at a time + + for ( i = (vinitrd_start - dsi.v_start); + i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE ) + { + sts = inflate(&zStream, Z_SYNC_FLUSH); + if (sts < 0) + { + ERROR("Inflate failed, sts %d", sts); + goto error_out; + } + if (sts == Z_STREAM_END) + { + remain = sizeof(page) - zStream.avail_out; + bzero(&page[zStream.avail_out], remain); + xc_copy_to_domain_page(xc_handle, dom, + page_array[i>>PAGE_SHIFT], page); + break; + } + if (sts == Z_OK) + { + xc_copy_to_domain_page(xc_handle, dom, + page_array[i>>PAGE_SHIFT], page); + zStream.next_out = page; + zStream.avail_out = sizeof(page); + } + else + { + ERROR("Inflate error, sts %d", sts); + goto error_out; + } + } + } else + ERROR("initrd requested, but no data available"); + } *pvke = dsi.v_kernentry; @@ -419,8 +479,9 @@ #else /* x86 */ static int setup_guest(int xc_handle, uint32_t dom, - char *image, unsigned long image_size, + const unsigned char *image, unsigned long image_size, gzFile initrd_gfd, unsigned long initrd_len, + unsigned char *ramdisk_buffer, unsigned long ramdisk_size, unsigned long nr_pages, unsigned long *pvsi, unsigned long *pvke, unsigned long *pvss, vcpu_guest_context_t *ctxt, @@ -459,13 +520,13 @@ unsigned long vpt_end; unsigned long v_end; - rc = probeimageformat(image, image_size, &load_funcs); + rc = probeimageformat((char *)image, (unsigned long)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); + rc = (load_funcs.parseimage)((char *)image, (unsigned long)image_size, &dsi); if ( rc != 0 ) goto error_out; @@ -570,24 +631,84 @@ goto error_out; } - (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array, + (load_funcs.loadimage)((char *)image, image_size, xc_handle, dom, page_array, &dsi); /* Load the initial ramdisk image. */ if ( initrd_len != 0 ) { - for ( i = (vinitrd_start - dsi.v_start); - i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE ) - { - char page[PAGE_SIZE]; - if ( gzread(initrd_gfd, page, PAGE_SIZE) == -1 ) + // If there's a file for input, use gzread to get the data + if (initrd_gfd != NULL) + { + for ( i = (vinitrd_start - dsi.v_start); + i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE ) + { + char page[PAGE_SIZE]; + if ( gzread(initrd_gfd, page, PAGE_SIZE) == -1 ) + { + PERROR("Error reading initrd image, could not"); + goto error_out; + } + xc_copy_to_domain_page(xc_handle, dom, + page_array[i>>PAGE_SHIFT], page); + } + } + + // Else, if there's a buffer, loop over it with inflate + + else if (ramdisk_size > 0) + { + z_stream zStream; + unsigned char page[PAGE_SIZE]; + int sts; + int remain; + + bzero(&zStream, sizeof(zStream)); + zStream.next_in = ramdisk_buffer; + zStream.avail_in = ramdisk_size; + zStream.next_out = page; + zStream.avail_out = sizeof(page); + sts = inflateInit2(&zStream, (MAX_WBITS+32)); + if (sts != Z_OK) { - PERROR("Error reading initrd image, could not"); + ERROR("inflateInit failed, sts %d\n", sts); goto error_out; } - xc_copy_to_domain_page(xc_handle, dom, - page_array[i>>PAGE_SHIFT], page); - } + + // Pages are not contiguous, so do the inflation a page at a time + + for ( i = (vinitrd_start - dsi.v_start); + i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE ) + { + sts = inflate(&zStream, Z_SYNC_FLUSH); + if (sts < 0) + { + ERROR("Inflate failed, sts %d", sts); + goto error_out; + } + if (sts == Z_STREAM_END) + { // Done, but may have partial buffer + remain = sizeof(page) - zStream.avail_out; + bzero(&page[zStream.avail_out], remain); + xc_copy_to_domain_page(xc_handle, dom, + page_array[i>>PAGE_SHIFT], page); + break; + } + if (sts == Z_OK) + { + xc_copy_to_domain_page(xc_handle, dom, + page_array[i>>PAGE_SHIFT], page); + zStream.next_out = page; + zStream.avail_out = sizeof(page); + } + else + { + ERROR("Inflate error, sts %d", sts); + goto error_out; + } + } + } else + ERROR("initrd requested, but no data available"); } if ( (mmu = xc_init_mmu_updates(xc_handle, dom)) == NULL ) @@ -733,16 +854,28 @@ } #endif -int xc_linux_build(int xc_handle, - uint32_t domid, - const char *image_name, - const char *ramdisk_name, - const char *cmdline, - unsigned long flags, - unsigned int store_evtchn, - unsigned long *store_mfn, - unsigned int console_evtchn, - unsigned long *console_mfn) +/* xc_linux_build_internal + * + * Common routine to create a domain for a para-virtualized Linux. Always called with + * a buffer containing the uncompressed kernel. The RAMdisk is either a file, or a + * buffer with the potentially compressed RAMdisk. Figure out which, and make it all + * happen. + * + */ + +static int xc_linux_build_internal(int xc_handle, + uint32_t domid, + const unsigned char *image, + unsigned long image_size, + const char *ramdisk_name, + unsigned char *ramdisk_buffer, + unsigned long ramdisk_size, + const char *cmdline, + 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; @@ -751,8 +884,7 @@ int rc, i; vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt; unsigned long nr_pages; - char *image = NULL; - unsigned long image_size, initrd_size=0; + unsigned long initrd_size=0; unsigned long vstartinfo_start, vkern_entry, vstack_start; if ( (nr_pages = get_tot_pages(xc_handle, domid)) < 0 ) @@ -761,9 +893,10 @@ goto error_out; } - if ( (image = xc_read_kernel_image(image_name, &image_size)) == NULL ) - goto error_out; - + // The kernel image arrives as an uncompressed buffer at this point + // The ramdisk may be either a file name, or a buffer/size pair + // Determine which, and handle the processing + if ( (ramdisk_name != NULL) && (strlen(ramdisk_name) != 0) ) { if ( (initrd_fd = open(ramdisk_name, O_RDONLY)) < 0 ) @@ -779,6 +912,14 @@ PERROR("Could not allocate decompression state for initrd"); goto error_out; } + } else if ( (ramdisk_buffer != NULL) && (ramdisk_size > 16) ) { + if ( (ramdisk_buffer[0] == 0x1F) && (ramdisk_buffer[1] == 0x8B) ) { + initrd_size = ramdisk_buffer[ramdisk_size-4] + + (256 * (ramdisk_buffer[ramdisk_size-3] + + (256 * (ramdisk_buffer[ramdisk_size-2] + + (256 * ramdisk_buffer[ramdisk_size-1]))))); + } else + initrd_size = ramdisk_size; } #ifdef VALGRIND @@ -813,7 +954,8 @@ } if ( setup_guest(xc_handle, domid, image, image_size, - initrd_gfd, initrd_size, nr_pages, + initrd_gfd, initrd_size, ramdisk_buffer, ramdisk_size, + nr_pages, &vstartinfo_start, &vkern_entry, &vstack_start, ctxt, cmdline, op.u.getdomaininfo.shared_info_frame, @@ -828,7 +970,6 @@ close(initrd_fd); if ( initrd_gfd ) gzclose(initrd_gfd); - free(image); #ifdef __ia64__ /* based on new_thread in xen/arch/ia64/domain.c */ @@ -917,8 +1058,125 @@ gzclose(initrd_gfd); else if ( initrd_fd >= 0 ) close(initrd_fd); + return -1; +} + +/* xc_linux_build_mem + * + * Create a domain for a para-virtualized Linux, using buffers containing the kernel + * and, possibly, the ramdisk. This allows for use in situations where files may + * not be readily available. + * + */ + +int xc_linux_build_mem(int xc_handle, + uint32_t domid, + unsigned char *image_buffer, + unsigned long image_size, + unsigned char *ramdisk_buffer, + unsigned long ramdisk_size, + const char *cmdline, + unsigned long flags, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + int sts; + z_stream zStream; + unsigned long outbuf_length; + unsigned char *outBuf; + + // Validate that there is a kernel buffer + + if (image_buffer == NULL) { + ERROR("kernel image buffer not present"); + return EINVAL; + } + + // If it's gzipped, inflate it; otherwise, use as is + + if ( (image_buffer[0] == 0x1F) && (image_buffer[1] == 0x8B) ) { + outbuf_length = image_buffer[image_size-4] + + (256 * (image_buffer[image_size-3] + + (256 * (image_buffer[image_size-2] + + (256 * image_buffer[image_size-1]))))); + bzero(&zStream, sizeof(zStream)); + outBuf = malloc(outbuf_length + 16); // Leave a little extra space + if (outBuf == NULL) { + ERROR("Error mallocing buffer\n"); + return ENOMEM; + } + + zStream.next_in = image_buffer; + zStream.avail_in = image_size; + zStream.next_out = outBuf; + zStream.avail_out = outbuf_length+16; + sts = inflateInit2(&zStream, (MAX_WBITS+32)); // +32 means "handle gzip" + if (sts != Z_OK) { + ERROR("inflateInit failed, sts %d\n", sts); + free(outBuf); + return sts; + } + + // Inflate in one pass/call + + sts = inflate(&zStream, Z_FINISH); + if (sts != Z_STREAM_END) { + ERROR("inflate failed, sts %d\n", sts); + free(outBuf); + return sts; + } + sts = xc_linux_build_internal(xc_handle, domid, outBuf, outbuf_length, + NULL, ramdisk_buffer, ramdisk_size, + cmdline, flags, store_evtchn, + store_mfn, console_evtchn, console_mfn); + free(outBuf); + } else { + sts = xc_linux_build_internal(xc_handle, domid, image_buffer, image_size, + NULL, ramdisk_buffer, ramdisk_size, + cmdline, flags, store_evtchn, + store_mfn, console_evtchn, console_mfn); + } + + return sts; +} + +/* xc_linux_build + * + * Create a domain for a para-virtualized Linux, using files/filenames + * + */ + +int xc_linux_build(int xc_handle, + uint32_t domid, + const char *image_name, + const char *ramdisk_name, + const char *cmdline, + unsigned long flags, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn) +{ + unsigned char *image; + int sts; + long image_size; + + // Bring uncompressed image into memory + + if ( (image_name == NULL) || + ((image = (unsigned char *)xc_read_kernel_image(image_name, (unsigned long *)&image_size)) == NULL )) + return -1; + + // Then, call internal routine for processing (using ramdisk name, not buffer) + + sts = xc_linux_build_internal(xc_handle, domid, image, image_size, + ramdisk_name, NULL, 0, + cmdline, flags, store_evtchn, + store_mfn, console_evtchn, console_mfn); free(image); - return -1; + return sts; } /* diff -r d929135007ad -r 7f9e81f7cbbc tools/libxc/xc_vmx_build.c --- a/tools/libxc/xc_vmx_build.c Thu Jan 12 21:07:52 2006 +++ b/tools/libxc/xc_vmx_build.c Mon Jan 16 20:39:54 2006 @@ -595,24 +595,29 @@ return -1; } -int xc_vmx_build(int xc_handle, - uint32_t domid, - int memsize, - const char *image_name, - unsigned int control_evtchn, - unsigned int vcpus, - unsigned int acpi, - unsigned int apic, - unsigned int store_evtchn, - unsigned long *store_mfn) +static int xc_vmx_build_internal(int xc_handle, + uint32_t domid, + int memsize, + unsigned char *image, + unsigned long image_size, + unsigned int control_evtchn, + unsigned int vcpus, + unsigned int acpi, + unsigned int apic, + unsigned int store_evtchn, + unsigned long *store_mfn) { dom0_op_t launch_op, op; int rc, i; vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt; unsigned long nr_pages; - char *image = NULL; - unsigned long image_size; xen_capabilities_info_t xen_caps; + + if (image == NULL) + { + ERROR("Image required"); + goto error_out; + } if ( (rc = xc_version(xc_handle, XENVER_capabilities, &xen_caps)) != 0 ) { @@ -632,9 +637,6 @@ PERROR("Could not find total pages for domain"); goto error_out; } - - if ( (image = xc_read_kernel_image(image_name, &image_size)) == NULL ) - goto error_out; if ( mlock(&st_ctxt, sizeof(st_ctxt) ) ) { @@ -664,15 +666,13 @@ goto error_out; } - if ( setup_guest(xc_handle, domid, memsize, image, image_size, nr_pages, + if ( setup_guest(xc_handle, domid, memsize, (char *)image, image_size, nr_pages, ctxt, op.u.getdomaininfo.shared_info_frame, control_evtchn, vcpus, acpi, apic, store_evtchn, store_mfn) < 0) { ERROR("Error constructing guest OS"); goto error_out; } - - free(image); ctxt->flags = VGCF_VMX_GUEST; /* FPU is set up to default initial state. */ @@ -718,7 +718,6 @@ return rc; error_out: - free(image); return -1; } @@ -848,6 +847,121 @@ return 0; } +/* xc_vmx_build + * + * Create a domain for a virtualized Linux, using files/filenames + * + */ + +int xc_vmx_build(int xc_handle, + uint32_t domid, + int memsize, + const char *image_name, + unsigned int control_evtchn, + unsigned int vcpus, + unsigned int acpi, + unsigned int apic, + unsigned int store_evtchn, + unsigned long *store_mfn) +{ + unsigned char *image; + int sts; + long image_size; + + // Bring uncompressed image into memory + + if ( (image_name == NULL) || + ((image = (unsigned char *)xc_read_kernel_image(image_name, (unsigned long *)&image_size)) == NULL )) + return -1; + + // Then, call internal routine for processing (using ramdisk name, not buffer) + + sts = xc_vmx_build_internal(xc_handle, domid, memsize, + image, image_size, + control_evtchn, vcpus, acpi, + apic, store_evtchn, store_mfn); + free(image); + return sts; +} + +/* xc_vmx_build_mem + * + * Create a domain for a virtualized Linux, using buffers + * + */ + +int xc_vmx_build_mem(int xc_handle, + uint32_t domid, + int memsize, + unsigned char *image_buffer, + unsigned long image_size, + unsigned int control_evtchn, + unsigned int vcpus, + unsigned int acpi, + unsigned int apic, + unsigned int store_evtchn, + unsigned long *store_mfn) +{ + int sts; + z_stream zStream; + unsigned long outbuf_length; + unsigned char *outbuf; + + // Validate that there is a kernel buffer + + if (image_buffer == NULL) { + ERROR("kernel image buffer not present"); + return EINVAL; + } + + // If it's gzipped, inflate it; otherwise, use as is + + if ( (image_buffer[0] == 0x1F) && (image_buffer[1] == 0x8B) ) { + outbuf_length = image_buffer[image_size-4] + + (256 * (image_buffer[image_size-3] + + (256 * (image_buffer[image_size-2] + + (256 * image_buffer[image_size-1]))))); + bzero(&zStream, sizeof(zStream)); + outbuf = malloc(outbuf_length + 16); + if (outbuf == NULL) { + ERROR("Error mallocing buffer\n"); + return ENOMEM; + } + + zStream.next_in = image_buffer; + zStream.avail_in = image_size; + zStream.next_out = outbuf; + zStream.avail_out = outbuf_length+16; + sts = inflateInit2(&zStream, (MAX_WBITS+32)); // +32 means "handle gzip" + if (sts != Z_OK) { + ERROR("inflateInit failed, sts %d\n", sts); + free(outbuf); + return sts; + } + + // Inflate in one pass/call + + sts = inflate(&zStream, Z_FINISH); + if (sts != Z_STREAM_END) { + ERROR("inflate failed, sts %d\n", sts); + free(outbuf); + return sts; + } + sts = xc_vmx_build_internal(xc_handle, domid, memsize, + outbuf, outbuf_length, + control_evtchn, vcpus, acpi, + apic, store_evtchn, store_mfn); + free(outbuf); + } else { + sts = xc_vmx_build_internal(xc_handle, domid, memsize, + image_buffer, image_size, + control_evtchn, vcpus, acpi, + apic, store_evtchn, store_mfn); + } + + return sts; +} + /* * Local variables: * mode: C diff -r d929135007ad -r 7f9e81f7cbbc tools/libxc/xenguest.h --- a/tools/libxc/xenguest.h Thu Jan 12 21:07:52 2006 +++ b/tools/libxc/xenguest.h Mon Jan 16 20:39:54 2006 @@ -42,6 +42,22 @@ unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn); +/** + * This function will create a domain for a paravirtualized Linux + * using file names pointing to kernel and ramdisk + * + * @parm xc_handle a handle to an open hypervisor interface + * @parm domid the id of the domain + * @param image_name name of the kernel image file + * @param ramdisk_name name of the ramdisk image file + * @parm cmdline command line string + * @parm flags domain creation flags + * @parm store_evtchn the store event channel for this domain to use + * @parm store_mfn returned with the mfn of the store page + * @parm console_evtchn the console event channel for this domain to use + * @parm conole_mfn returned with the mfn of the console page + * @return 0 on success, -1 on failure + */ int xc_linux_build(int xc_handle, uint32_t domid, const char *image_name, @@ -52,6 +68,37 @@ unsigned long *store_mfn, unsigned int console_evtchn, unsigned long *console_mfn); + +/** + * This function will create a domain for a paravirtualized Linux + * using buffers for kernel and ramdisk + * + * @parm xc_handle a handle to an open hypervisor interface + * @parm domid the id of the domain + * @param image_buffer buffer containing kernel image + * @param image_size size of the kernel image buffer + * @param ramdisk_buffer name of the ramdisk image file + * @param ramdisk_size size of the ramdisk buffer + * @parm cmdline command line string + * @parm flags domain creation flags + * @parm store_evtchn the store event channel for this domain to use + * @parm store_mfn returned with the mfn of the store page + * @parm console_evtchn the console event channel for this domain to use + * @parm conole_mfn returned with the mfn of the console page + * @return 0 on success, -1 on failure + */ +int xc_linux_build_mem(int xc_handle, + uint32_t domid, + unsigned char *image_buffer, + unsigned long image_size, + unsigned char *ramdisk_buffer, + unsigned long ramdisk_size, + const char *cmdline, + unsigned long flags, + unsigned int store_evtchn, + unsigned long *store_mfn, + unsigned int console_evtchn, + unsigned long *console_mfn); int xc_vmx_build(int xc_handle, uint32_t domid, @@ -64,4 +111,16 @@ unsigned int store_evtchn, unsigned long *store_mfn); +int xc_vmx_build_mem(int xc_handle, + uint32_t domid, + int memsize, + unsigned char *image_buffer, + unsigned long image_size, + unsigned int control_evtchn, + unsigned int vcpus, + unsigned int acpi, + unsigned int apic, + unsigned int store_evtchn, + unsigned long *store_mfn); + #endif // XENGUEST_H diff -r d929135007ad -r 7f9e81f7cbbc tools/libxc/xg_private.c --- a/tools/libxc/xg_private.c Thu Jan 12 21:07:52 2006 +++ b/tools/libxc/xg_private.c Mon Jan 16 20:39:54 2006 @@ -17,8 +17,12 @@ char *image = NULL; unsigned int bytes; - if ( filename == NULL ) - goto out; + // Check for non-null input. We could handle a NULL size, but it's + // not clear what value it provides to not know the length on return + // Also, just return - we haven't done anything requiring cleanup + + if ( ( filename == NULL ) || (size == NULL) ) + return NULL; if ( (kernel_fd = open(filename, O_RDONLY)) < 0 ) { @@ -83,6 +87,11 @@ unsigned long *p = page; unsigned long long sum=0; + // Return 0 checksum for NULL input + + if (page == NULL) + return 0; + for ( i = 0; i < (PAGE_SIZE/sizeof(unsigned long)); i++ ) sum += p[i];