Subject: Re: kqmemu: uvm(9) questions
To: None <tech-kern@netbsd.org>
From: Tobias Nygren <tnn@NetBSD.org>
List: tech-kern
Date: 04/25/2007 09:45:56
Oliver Gould wrote:
> Hello-
>
> More questions.. this time about uvm(9)
>
> KQEMU wants to allocate kernel memory, and then be able to wire and
> unwire it.
>
> FreeBSD's implementation looks something like:
>
> struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index)
> {
> pmap_t pmap;
> vm_offset_t va;
> vm_paddr_t pa;
>
> va = kmem_alloc(kernel_map, PAGE_SIZE);
> if (va == 0) {
> kqemu_log("kqemu_alloc_zeroed_page: NULL\n");
> return NULL;
> }
> pmap = vm_map_pmap(kernel_map);
> pa = pmap_extract(pmap, va);
> /* kqemu_log("kqemu_alloc_zeroed_page: %08x\n", pa); */
> *ppage_index = pa >> PAGE_SHIFT;
> return (struct kqemu_page *)va;
> }
>
> struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index,
> unsigned long user_addr)
> {
> struct vmspace *vm = curproc->p_vmspace;
> vm_offset_t va = user_addr;
> vm_paddr_t pa = 0;
> int ret;
> pmap_t pmap;
> #if __FreeBSD_version >= 500000
> ret = vm_map_wire(&vm->vm_map, va, va+PAGE_SIZE, VM_MAP_WIRE_USER);
> #else
> ret = vm_map_user_pageable(&vm->vm_map, va, va+PAGE_SIZE, FALSE);
> #endif
> if (ret != KERN_SUCCESS) {
> kqemu_log("kqemu_lock_user_page(%08lx) failed, ret=%d\n", user_addr, ret);
> return NULL;
> }
> pmap = vm_map_pmap(&vm->vm_map);
> pa = pmap_extract(pmap, va);
> *ppage_index = pa >> PAGE_SHIFT;
> return (struct kqemu_user_page *)va;
> }
>
> My (thusfar disfunctional) port looks like:
>
> struct kqemu_page *
> kqemu_alloc_zeroed_page(unsigned long *ppage_index)
> {
> extern struct vm_map *kernel_map;
> pmap_t pmap;
> vaddr_t va;
> paddr_t pa;
>
> pa = 0;
> va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_WIRED|UVM_KMF_ZERO);
> if (va == 0) {
> kqemu_log(...);
> return NULL;
> }
> pmap = vm_map_pmap(kernel_map);
> if (pmap_extract(pmap, va, &pa) == FALSE) {
> kqemu_log(...);
> return NULL;
> }
> if (kqemu_debug > 0)
> kqemu_log("kqemu_alloc_zeroed_page: va=%08x pa=%08x\n",
> va, pa);
>
> *ppage_index = pa >> PAGE_SHIFT;
> return (struct kqemu_page *)va;
> }
>
> struct kqemu_user_page *
> kqemu_lock_user_page(unsigned long *ppage_index, unsigned long user_addr)
> {
> struct vmspace *vm;
> vaddr_t va;
> paddr_t pa;
> pmap_t pmap;
>
> vm = curproc->p_vmspace;
> va = (vaddr_t)user_addr;
> pa = 0;
> /* FIXME - fails */
> if (uvm_map_pageable(&vm->vm_map, va, va+PAGE_SIZE,
> FALSE, 0) == FALSE) {
> kqemu_log("kqemu_lock_user_page(%08lx) failed\n", va);
> return NULL;
> }
> pmap = vm_map_pmap(&vm->vm_map);
> if (pmap_extract(pmap, va, &pa) == FALSE) {
> kqemu_log(...);
> return NULL;
> }
> *ppage_index = pa >> PAGE_SHIFT;
> return (struct kqemu_user_page *)va;
> }
>
> The issue is that uvm_map_pageable(9) fails in kqemu_lock_user_page():
>
> Apr 24 18:58:12 isla /netbsd: kqemu: kqemu_alloc_zeroed_page:
> va=d49e0000 pa=24c95000
> Apr 24 18:58:12 isla /netbsd: kqemu: kqemu_alloc_zeroed_page:
> va=d49e1000 pa=260d6000
> Apr 24 18:58:12 isla /netbsd: kqemu: kqemu_lock_user_page(b305d000) failed
>
> If I put an equivalent statement in kqemu_alloc_zeroed_page() directly
> following uvm_km_alloc(), it succeeds.
>
> My current thought is that the vm_map is the culprit here. From a
> recent thread, I gather that 'kernel_map' should not be used (my use of
> it is inherited from FreeBSD's code). Does it seem likely that this is
> the issue? What else should I consider?
>
> Also, is there any documentation that describes, in a high-level way,
> how uvm(9) works? The Internals Guide is woefully incomplete, and I
> would imagine that would be the proper place for such documentation.
>
> And, on a procedural note, is it bothersome for me to post this much
> code in email? I'd hate to put anyone off.
>
> Many thanks,
> - Oliver
>
Not sure why it fails, but I had this wiring stuff working awhile ago on
3.99.x, see:
http://cvsweb.netbsd.se/cgi-bin/bsdweb.cgi/~checkout~/wip/qemu-qvm86/files/Attic/qvm86-netbsd.c?rev=1.1.1.1;content-type=text%2Fplain
-Tobias