From b8aef502747b039b20877f99f3d8986b6a9329d2 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 1 Jan 1970 00:00:00 +0000 Subject: [PATCH] Factor out the kernel loading function. Add kernel_min, kernel_max addresses. --- lib/virt_mem.ml | 90 ++++++++++++++++++++++++++++++-------------------- lib/virt_mem_mmap.ml | 23 +++++++++++-- lib/virt_mem_mmap.mli | 6 +++- lib/virt_mem_types.ml | 67 +++++++++++++++++++++++++++++++++++++ lib/virt_mem_types.mli | 38 +++++++++++++++++++++ virt-mem.1 | 19 ++++++----- virt-mem.pod | 15 +++++---- virt-mem.txt | 15 +++++---- 8 files changed, 214 insertions(+), 59 deletions(-) diff --git a/lib/virt_mem.ml b/lib/virt_mem.ml index 9280a69..69a0821 100644 --- a/lib/virt_mem.ml +++ b/lib/virt_mem.ml @@ -35,7 +35,6 @@ open Virt_mem_types let kernel_size = if Sys.word_size = 32 then Sys.max_string_length else 0x100_0000 -let max_memory_peek = 65536 (* XXX Use D.max_peek function *) (* When tools register themselves, they are added to this list. * Later, we will alphabetize the list. @@ -152,7 +151,7 @@ Options:") cmd summary description in let uri = ref "" in let anon_args = ref [] in - (* Default wordsize. *) + (* Default wordsize (-W). *) let def_wordsize = ref None in let set_wordsize = function | "32" -> def_wordsize := Some W32 @@ -161,7 +160,7 @@ Options:") cmd summary description in | str -> failwith (sprintf (f_"set_wordsize: %s: unknown wordsize") str) in - (* Default endianness. *) + (* Default endianness (-E). *) let def_endian = ref None in let set_endian = function | "auto" -> def_endian := None @@ -172,7 +171,7 @@ Options:") cmd summary description in | str -> failwith (sprintf (f_"set_endian: %s: unknown endianness") str) in - (* Default architecture. *) + (* Default architecture (-A). *) let def_architecture = ref None in let set_architecture = function | "auto" -> def_architecture := None @@ -183,19 +182,43 @@ Options:") cmd summary description in def_wordsize := Some (wordsize_of_architecture arch) in - (* Default text address. *) + (* Default text address (-T). *) let def_text_addr = ref 0L (* 0 = auto-detect *) in + let def_kernel_min = ref 0L in + let def_kernel_max = ref 0L in let set_text_addr = function | "auto" -> def_text_addr := 0L - | "i386" -> def_text_addr := 0xc010_0000_L (* common for x86 *) - | "x86-64"|"x86_64" -> def_text_addr := 0xffffffff_81000000_L (* x86-64? *) - | str -> def_text_addr := Int64.of_string str + | "i386" -> + (* common for x86, but we should be able to try a selection *) + def_text_addr := 0xc010_0000_L; + def_kernel_min := 0xc010_0000_L; + def_kernel_max := 0xffff_ffff_L + | "x86-64"|"x86_64" -> + def_text_addr := 0xffffffff_81000000_L; + def_kernel_min := 0xffffffff_81000000_L; + def_kernel_max := 0xffffffff_ffffffff_L; + | str -> + let strs = String.nsplit str "," in + match strs with + | [str] -> + def_text_addr := Int64.of_string str; + def_kernel_min := !def_text_addr; + def_kernel_max := + if !def_text_addr < 0x1_0000_0000_L + then 0xffff_ffff_L + else 0xffffffff_ffffffff_L + | [str1;str2;str3] -> + def_text_addr := Int64.of_string str1; + def_kernel_min := Int64.of_string str2; + def_kernel_max := Int64.of_string str3 + | _ -> failwith (sprintf (f_"set_text_addr: %s: incorrect number of parameters to -T option") str) in (* Handle -t option. *) let memory_image filename = testimages := - (!def_wordsize, !def_endian, !def_architecture, !def_text_addr, filename) + (!def_wordsize, !def_endian, !def_architecture, + !def_text_addr, !def_kernel_min, !def_kernel_max, filename) :: !testimages in @@ -374,26 +397,27 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)"); failwith (sprintf (f_"%s: use -A to define architecture (i386/x86-64 only) for this image") domname) in - if !def_text_addr = 0L then - failwith - (sprintf (f_"%s: use -T to define kernel load address for this image") domname); + if !def_text_addr = 0L || + !def_kernel_min = 0L || + !def_kernel_max = 0L then + failwith + (sprintf (f_"%s: use -T to define kernel load address for this image") domname); + (* Download the static part of the kernel. *) let start_t = gettimeofday () in - (* Read the kernel memory. - * Maximum 64K can be read over remote connections. - *) - let str = String.create kernel_size in - let rec loop i = - let remaining = kernel_size - i in - if remaining > 0 then ( - let size = min remaining max_memory_peek in - D.memory_peek dom [D.Virtual] - (!def_text_addr +^ Int64.of_int i) size str i; - loop (i + size) - ) - in - loop 0; + let image = + try + load_static_memory ~dom ~domname ~arch + ~wordsize ~endian + ~kernel_min:!def_kernel_min ~kernel_max:!def_kernel_max + !def_text_addr kernel_size + with + | LoadMemoryError (AddressOutOfRange, _) -> + prerr_endline (s_"virt-mem: error loading kernel memory: address out of range +Possibly the '-T' command line parameter was used inconsistently."); + exit 1 + (* Allow any other exceptions to escape & kill the program. *) in if debug then ( let end_t = gettimeofday () in @@ -401,14 +425,8 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)"); (end_t -. start_t) ); - (* Map the virtual memory. *) - let mem = Virt_mem_mmap.of_string str !def_text_addr in - - (* Force the wordsize and endianness. *) - let mem = Virt_mem_mmap.set_wordsize mem wordsize in - let mem = Virt_mem_mmap.set_endian mem endian in + image - { dom = Some dom; domname = domname; mem = mem; arch = arch } ) xmls ) else ( (* One or more -t options passed. *) @@ -416,7 +434,8 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)"); failwith (s_"virt-mem: if -t given on command line, then no domain arguments should be listed"); List.map ( - fun (wordsize, endian, arch, text_addr, filename) -> + fun (wordsize, endian, arch, + text_addr, kernel_min, kernel_max, filename) -> (* Quite a lot of limitations on the kernel images we can * handle at the moment ... *) @@ -456,7 +475,8 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)"); let mem = Virt_mem_mmap.set_wordsize mem wordsize in let mem = Virt_mem_mmap.set_endian mem endian in - { dom = None; domname = filename; mem = mem; arch = arch } + { dom = None; domname = filename; mem = mem; arch = arch; + kernel_min = kernel_min; kernel_max = kernel_max } ) testimages ) in diff --git a/lib/virt_mem_mmap.ml b/lib/virt_mem_mmap.ml index bf048f7..c323c1a 100644 --- a/lib/virt_mem_mmap.ml +++ b/lib/virt_mem_mmap.ml @@ -309,9 +309,13 @@ let rec get_mapping addr = function (* Use the tree to quickly check if an address is mapped (returns false * if it's a hole). *) -let is_mapped { tree = tree } addr = - let _, mapping = get_mapping addr tree in - mapping <> None +let is_mapped { mappings = mappings; tree = tree } addr = + (* NB: No [`HasMapping] in the type so we have to check mappings <> []. *) + match mappings with + | [] -> false + | _ -> + let _, mapping = get_mapping addr tree in + mapping <> None (* Get a single byte. *) let get_byte { tree = tree } addr = @@ -410,6 +414,19 @@ let dowhile { tree = tree } addr cond = in loop addr +let is_mapped_range ({ mappings = mappings } as t) addr size = + match mappings with + (* NB: No [`HasMapping] in the type so we have to check mappings <> []. *) + | [] -> false + | _ -> + (* Quick and dirty. It's possible to make a much faster + * implementation of this which doesn't call the closure for every + * byte. + *) + let size = ref size in + try dowhile t addr (fun _ _ -> decr size; !size > 0); true + with Invalid_argument "dowhile" -> false + (* Get a string, ending at ASCII NUL character. *) let get_string t addr = let chars = ref [] in diff --git a/lib/virt_mem_mmap.mli b/lib/virt_mem_mmap.mli index c66ba79..0294efd 100644 --- a/lib/virt_mem_mmap.mli +++ b/lib/virt_mem_mmap.mli @@ -221,9 +221,13 @@ val is_C_identifier : ('ws, 'e, [`HasMapping]) t -> addr -> bool (** Return true or false if the address contains a NUL-terminated C identifier. *) -val is_mapped : ('ws, 'e, [`HasMapping]) t -> addr -> bool +val is_mapped : ('ws, 'e, 'hm) t -> addr -> bool (** Return true if the single address [addr] is mapped. *) +val is_mapped_range : ('ws, 'e, 'hm) t -> addr -> int -> bool +(** Return true if all addresses in the range [addr] to [addr+size-1] + are mapped. *) + val follow_pointer : ([`Wordsize], [`Endian], [`HasMapping]) t -> addr -> addr (** Follow (dereference) the pointer at [addr] and return the address pointed to. *) diff --git a/lib/virt_mem_types.ml b/lib/virt_mem_types.ml index 071f6de..c3da932 100644 --- a/lib/virt_mem_types.ml +++ b/lib/virt_mem_types.ml @@ -22,6 +22,7 @@ module D = Libvirt.Domain +open Virt_mem_utils open Virt_mem_mmap type ksym = string @@ -33,6 +34,8 @@ type image0 = { domname : string; arch : Virt_mem_utils.architecture; mem : ([`Wordsize], [`Endian], [`HasMapping]) Virt_mem_mmap.t; + kernel_min : addr; + kernel_max : addr; } type image1 = @@ -52,3 +55,67 @@ and utsname = { machine : string; domainname : string; } + +(* This is the maximum we can download in one go over the libvirt + * remote connection. + * + * XXX Should have a 'D.max_peek' function. + *) +let max_memory_peek = 65536 + +type load_memory_error = + | AddressOutOfRange + | DomIsNull + +exception LoadMemoryError of load_memory_error * string + +let _load_memory mem dom start size = + let str = String.create size in + let rec loop i = + let remaining = size - i in + if remaining > 0 then ( + let size = min remaining max_memory_peek in + D.memory_peek dom [D.Virtual] (start +^ Int64.of_int i) size str i; + loop (i + size) + ) + in + loop 0; + + add_string mem str start + +let load_static_memory ~dom ~domname ~arch ~wordsize ~endian + ~kernel_min ~kernel_max start size = + if start < kernel_min then + raise (LoadMemoryError (AddressOutOfRange, + "load_memory: start < kernel_min")) + else if start +^ Int64.of_int size > kernel_max then + raise (LoadMemoryError (AddressOutOfRange, + "load_memory: start+size > kernel_max")) + else ( + let mem = Virt_mem_mmap.create () in + let mem = Virt_mem_mmap.set_wordsize mem wordsize in + let mem = Virt_mem_mmap.set_endian mem endian in + + let mem = _load_memory mem dom start size in + + { dom = Some dom; domname = domname; mem = mem; arch = arch; + kernel_min = kernel_min; kernel_max = kernel_max } + ) + +let load_memory ({ dom = dom; mem = mem; kernel_min = kernel_min; + kernel_max = kernel_max } as image) start size = + if start < kernel_min then + raise (LoadMemoryError (AddressOutOfRange, + "load_memory: start < kernel_min")) + else if start +^ Int64.of_int size > kernel_max then + raise (LoadMemoryError (AddressOutOfRange, + "load_memory: start+size > kernel_max")) + else if is_mapped_range mem start size then image + else ( + match dom with + | None -> + raise (LoadMemoryError (DomIsNull, "load_memory: dom = None")) + | Some dom -> + let mem = _load_memory mem dom start size in + { image with mem = mem } + ) diff --git a/lib/virt_mem_types.mli b/lib/virt_mem_types.mli index 24ce3e7..f537664 100644 --- a/lib/virt_mem_types.mli +++ b/lib/virt_mem_types.mli @@ -20,6 +20,8 @@ Common types. *) +(** {2 Kernel symbols} *) + type ksym = string (** A kernel symbol. *) @@ -41,6 +43,8 @@ module Ksymmap : sig end (** A map of kernel symbols to addresses. *) +(** {2 Kernel images and associated data} *) + type utsname = { kernel_name : string; nodename : string; @@ -57,6 +61,8 @@ type image0 = { arch : Virt_mem_utils.architecture; (** Architecture, eg. i386. *) mem : ([`Wordsize], [`Endian], [`HasMapping]) Virt_mem_mmap.t; (** Memory map. *) + kernel_min : Virt_mem_mmap.addr; (** Minimum addr of kernel pointers. *) + kernel_max : Virt_mem_mmap.addr; (** Maximum addr of kernel pointers. *) } (** A basic kernel image. *) @@ -70,3 +76,35 @@ type image2 = * Virt_mem_mmap.addr Ksymmap.t (* Kernel symbol map. *) * utsname option (* Kernel version, etc., if found. *) (** A kernel image, after finding kernel version (like 'uname'). *) + +(** {2 Load kernel memory} *) + +type load_memory_error = + | AddressOutOfRange (** Address not in [kernel_min..kernel_max] *) + | DomIsNull (** image.dom = None *) + +exception LoadMemoryError of load_memory_error * string + +val load_static_memory : dom:Libvirt.ro Libvirt.Domain.t -> + domname:string -> + arch:Virt_mem_utils.architecture -> + wordsize:Virt_mem_utils.wordsize -> endian:Bitstring.endian -> + kernel_min:Virt_mem_mmap.addr -> kernel_max:Virt_mem_mmap.addr -> + Virt_mem_mmap.addr -> int -> image0 + (** [load_static_memory ~dom (*...*) start size] creates an [image0] + object, and initializes it with static kernel memory loaded + from the [start] address and [size] of [dom]. + + See also {!load_memory} for exceptions this can raise. *) + +val load_memory : image0 -> Virt_mem_mmap.addr -> int -> image0 + (** [load_memory img start size] tries to load [size] bytes from + the start address into the memory map. If the memory was loaded + previously, then it is not requested again. + + Note that the memory map may be updated by this, so a modified + image structure is returned. + + This function can raise many different sorts of exceptions and + the caller is advised to catch any exceptions and deal with them + appropriately. *) diff --git a/virt-mem.1 b/virt-mem.1 index 53f61a1..94be74d 100644 --- a/virt-mem.1 +++ b/virt-mem.1 @@ -132,7 +132,7 @@ .\" ======================================================================== .\" .IX Title "VIRT-MEM 1" -.TH VIRT-MEM 1 "2008-07-14" "virt-mem-0.2.4" "Virtualization Support" +.TH VIRT-MEM 1 "2008-07-24" "virt-mem-0.2.7" "Virtualization Support" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -224,8 +224,8 @@ Display version and exit. .IP "\fB\-E auto|littleendian|bigendian\fR" 4 .IX Item "-E auto|littleendian|bigendian" .PD 0 -.IP "\fB\-T auto|i386|x86\-64|\f(BIaddress\fB\fR" 4 -.IX Item "-T auto|i386|x86-64|address" +.IP "\fB\-T auto|i386|x86\-64|\f(BIaddress\fB|\f(BIaddress,min,max\fB\fR" 4 +.IX Item "-T auto|i386|x86-64|address|address,min,max" .IP "\fB\-W auto|32|64\fR" 4 .IX Item "-W auto|32|64" .PD @@ -243,11 +243,14 @@ so on. \fI\-E littleendian\fR is the endianness used on Intel i386, x86\-64 and (usually) \s-1IA64\s0. \fI\-E bigendian\fR is the endianness used on many \s-1RISC\s0 chips such as \s-1SPARC\s0 and PowerPC. .Sp -Text address (\fI\-T\fR) sets the base address of the kernel image. \fI\-T -i386\fR means to try some common addresses for i386\-based kernels. \fI\-T -x86\-64\fR means to try some common addresses for x86\-64\-based kernels. -\&\fI\-T \fIaddress\fI\fR sets the address specifically (\fI0x\fR prefix is used -to specify hex addresses). +Text address (\fI\-T\fR) sets the base address and optionally min and max +addresses of the kernel image. \fI\-T i386\fR means to try some common +addresses for i386\-based kernels. \fI\-T x86\-64\fR means to try some +common addresses for x86\-64\-based kernels. +.Sp +\&\fI\-T address\fR sets the kernel base address specifically (\fI0x\fR prefix +is used to specify hex addresses). \fI\-T address,min,max\fR sets the +kernel base address, minimum address and maximum address. .Sp Word size (\fI\-W\fR) sets the word size, 32 or 64 bits. .IP "\fB\-A auto|i386|x86\-64|...\fR" 4 diff --git a/virt-mem.pod b/virt-mem.pod index cbfd0bb..9a4fd32 100644 --- a/virt-mem.pod +++ b/virt-mem.pod @@ -97,7 +97,7 @@ Display version and exit. =item B<-E auto|littleendian|bigendian> -=item B<-T auto|i386|x86-64|I
> +=item B<-T auto|i386|x86-64|I
|I> =item B<-W auto|32|64> @@ -115,11 +115,14 @@ so on. I<-E littleendian> is the endianness used on Intel i386, x86-64 and (usually) IA64. I<-E bigendian> is the endianness used on many RISC chips such as SPARC and PowerPC. -Text address (I<-T>) sets the base address of the kernel image. I<-T -i386> means to try some common addresses for i386-based kernels. I<-T -x86-64> means to try some common addresses for x86-64-based kernels. -I<-T I
> sets the address specifically (I<0x> prefix is used -to specify hex addresses). +Text address (I<-T>) sets the base address and optionally min and max +addresses of the kernel image. I<-T i386> means to try some common +addresses for i386-based kernels. I<-T x86-64> means to try some +common addresses for x86-64-based kernels. + +I<-T address> sets the kernel base address specifically (I<0x> prefix +is used to specify hex addresses). I<-T address,min,max> sets the +kernel base address, minimum address and maximum address. Word size (I<-W>) sets the word size, 32 or 64 bits. diff --git a/virt-mem.txt b/virt-mem.txt index 9c00d79..1b36816 100644 --- a/virt-mem.txt +++ b/virt-mem.txt @@ -85,7 +85,7 @@ COMMON OPTIONS Display version and exit. -E auto|littleendian|bigendian - -T auto|i386|x86-64|*address* + -T auto|i386|x86-64|*address*|*address,min,max* -W auto|32|64 These options force the endianness, text address, and word size for the subsequent domains listed on the command line (or if no domains @@ -101,11 +101,14 @@ COMMON OPTIONS x86-64 and (usually) IA64. *-E bigendian* is the endianness used on many RISC chips such as SPARC and PowerPC. - Text address (*-T*) sets the base address of the kernel image. *-T - i386* means to try some common addresses for i386-based kernels. *-T - x86-64* means to try some common addresses for x86-64-based kernels. - *-T *address** sets the address specifically (*0x* prefix is used to - specify hex addresses). + Text address (*-T*) sets the base address and optionally min and max + addresses of the kernel image. *-T i386* means to try some common + addresses for i386-based kernels. *-T x86-64* means to try some + common addresses for x86-64-based kernels. + + *-T address* sets the kernel base address specifically (*0x* prefix + is used to specify hex addresses). *-T address,min,max* sets the + kernel base address, minimum address and maximum address. Word size (*-W*) sets the word size, 32 or 64 bits. -- 1.8.3.1