X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=lib%2Fvirt_mem.ml;h=22cbbc59a479d93ea63004657fc24ea44191ed52;hb=70d3dc854c18fb1148239eee305eebffdc81f521;hp=0a956f1257b8fd9bb6b45c5726a4f389978428b6;hpb=89d41f7614612cf991d6005e7c1e20cd502a5404;p=virt-mem.git diff --git a/lib/virt_mem.ml b/lib/virt_mem.ml index 0a956f1..22cbbc5 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. @@ -43,12 +42,18 @@ let max_memory_peek = 65536 (* XXX Use D.max_peek function *) let tools = ref [] (* Registration function used by the tools. *) -let register ?(external_cmd = true) ?(extra_args = []) - ?argcheck ?beforeksyms ?run +let register + ?(needs_ksyms = false) ?(needs_utsname = false) + ?(needs_tasks = false) ?(needs_everything = false) + ~run + ?(external_cmd = true) + ?(extra_args = []) + ?argcheck name summary description = tools := - (name, (name, summary, description, external_cmd, extra_args, - argcheck, beforeksyms, run)) + (name, (name, summary, description, + needs_ksyms, needs_utsname, needs_tasks, needs_everything, + run, external_cmd, extra_args, argcheck)) :: !tools (* Main program, called from mem/virt_mem_main.ml when all the @@ -106,7 +111,7 @@ let main () = match tool with | None -> (* Generic usage message. *) let tools = List.map ( - fun (name, (_, summary, _, external_cmd, _, _, _, _)) -> + fun (name, (_, summary, _, _, _, _, _, _, external_cmd, _, _)) -> if external_cmd then "virt-"^name, summary else "virt-mem "^name, summary ) tools in @@ -128,12 +133,12 @@ General usage is: [-options] [domains...] To display extra help for a single tool, do: - virt-mem help + virt-mem --help Options:") tools (* Tool-specific usage message. *) - | Some (name, summary, description, external_cmd, _, _, _, _) -> + | Some (name, summary, description, _, _, _, _, _, external_cmd, _, _) -> let cmd = if external_cmd then "virt-" ^ name else "virt-mem " ^ name in @@ -152,7 +157,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 +166,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 +177,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 +188,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 @@ -219,7 +248,7 @@ Options:") cmd summary description in let argspec = let extra_args = match tool with | None -> [] - | Some (_, _, _, _, extra_args, _, _, _) -> extra_args in + | Some (_, _, _, _, _, _, _, _, _, extra_args, _) -> extra_args in let argspec = [ "-A", Arg.String set_architecture, "arch " ^ s_"Set kernel architecture, endianness and word size"; @@ -270,7 +299,9 @@ Options:") cmd summary description in * or the user didn't give us a valid tool (eg. "virt-mem foobar"). * Detect that final case now and give an error. *) - let name, _, _, _, _, argcheck, beforeksyms, run = + let name, _, _, + needs_ksyms, needs_utsname, needs_tasks, needs_everything, + run, external_cmd, extra_args, argcheck = match tool with | Some t -> t | None -> @@ -350,22 +381,21 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)"); List.map ( fun (dom, _) -> - let id = D.get_id dom in - let name = D.get_name dom in + let domname = D.get_name dom in let wordsize = match !def_wordsize with | None -> failwith (sprintf (f_"%s: use -W to define word size for this image") - name); + domname); | Some ws -> ws in let endian = match !def_endian with | None -> failwith (sprintf (f_"%s: use -E to define endianness for this image") - name); + domname); | Some e -> e in let arch = @@ -373,28 +403,29 @@ Use 'virt-mem --help' for more help or read the manual page virt-mem(1)"); | Some I386 -> I386 | Some X86_64 -> X86_64 | _ -> failwith - (sprintf (f_"%s: use -A to define architecture (i386/x86-64 only) for this image") name) in + (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") name); + 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 @@ -402,14 +433,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 - ((Some id, name, arch, mem) : image0) ) xmls ) else ( (* One or more -t options passed. *) @@ -417,7 +442,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 ... *) @@ -457,58 +483,100 @@ 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 - ((None, filename, arch, mem) : image0) + { dom = None; domname = filename; mem = mem; arch = arch; + kernel_min = kernel_min; kernel_max = kernel_max } ) testimages ) in - (* Optional callback into the tool before we start looking for - * kernel symbols. - *) - (match beforeksyms with - | None -> () - | Some beforeksyms -> beforeksyms debug images - ); - - (* If there is no run function, then there is no point continuing - * with the rest of the program (kernel symbol analysis) ... - *) - if run = None then exit 0; - - (* Do the kernel symbol analysis. *) + (* Now build the kdata, depending on what the tool asked for. *) let images = List.map ( fun image -> - - (* Look for ordinary kernel symbols: *) - let image = Virt_mem_ksyms.find_kernel_symbols debug image in - (* Look for kallsyms: *) - let image = Virt_mem_kallsyms.find_kallsyms debug image in - - (* Finally, just wrap the lookup_ksym call in something - * which prints the query when debug is set. - *) - let image = - if debug then - let (domid, name, arch, mem, lookup_ksym) = image in - let lookup_ksym sym = - try - let value = lookup_ksym sym in - eprintf "lookup_ksym %S = %Lx\n%!" sym value; - value - with Not_found -> - eprintf "lookup_ksym %S failed\n%!" sym; - raise Not_found - in - (domid, name, arch, mem, lookup_ksym) - else - image in - - image + let kdata = { ksyms = None; utsname = None; tasks = None } in + image, kdata ) images in + (* Certain needs are dependent on others ... *) + let needs_ksyms = + if needs_utsname then true + else needs_ksyms in + let needs_ksyms, needs_utsname = + if needs_tasks then true, true + else needs_ksyms, needs_utsname in + let needs_ksyms, needs_utsname, needs_tasks = + if needs_everything then true, true, true + else needs_ksyms, needs_utsname, needs_tasks in + + (* Do the kernel symbol analysis. *) + let images = + if not needs_ksyms then images + else + List.map ( + fun (image, kdata) -> + (* Look for ordinary kernel symbols: *) + let image, ksyms = + Virt_mem_ksyms.find_kernel_symbols debug image in + + match ksyms with + | None -> image, kdata + | Some ksyms -> + (* Look for kallsyms: *) + let image, kallsyms = + Virt_mem_kallsyms.find_kallsyms debug image ksyms in + + let ksyms = + match kallsyms with + | None -> ksyms (* no kallsyms, just use module symbols *) + | Some kallsyms -> kallsyms (* ksyms + kallsyms *) in + + image, { kdata with ksyms = Some ksyms } + ) images in + + (* Get the kernel version (utsname analysis). *) + let images = + if not needs_utsname then images + else + List.map ( + fun (image, ({ ksyms = ksyms } as kdata)) -> + match ksyms with + | None -> image, kdata + | Some ksyms -> + let image, utsname = + Virt_mem_utsname.find_utsname debug image ksyms in + let kdata = { kdata with utsname = utsname } in + image, kdata + ) images in + + (* Get the tasks. *) + let images = + if not needs_tasks then images + else + List.map ( + fun (image, ({ ksyms = ksyms; utsname = utsname } as kdata)) -> + match ksyms, utsname with + | Some ksyms, Some { kernel_release = kversion } -> + let image, tasks = + Virt_mem_tasks.find_tasks debug image ksyms kversion in + let kdata = { kdata with tasks = tasks } in + image, kdata + | _, _ -> image, kdata + ) images in (* Run the tool's main function. *) - (match run with - | None -> () - | Some run -> - run debug images - ) + let errors = ref 0 in + List.iter ( + fun (image, kdata) -> + try + if not needs_everything then ( + if needs_ksyms && kdata.ksyms = None then + failwith (s_"could not read kernel symbols") + else if needs_utsname && kdata.utsname = None then + failwith (s_"could not read kernel version") + else if needs_tasks && kdata.tasks = None then + failwith (s_"could not read process table") + ); + run debug image kdata + with exn -> + eprintf "%s: %s\n" image.domname (Printexc.to_string exn); + incr errors + ) images; + exit (if !errors > 0 then 1 else 0)