Sharing memory between device driver and userspace

I'm experimenting with FreeBSD device drivers, and am attempting to share memory between kernel space and user space. Here's the overall flow I'm trying to accomplish:
  1. Driver is loaded.
  2. At load time, kernel memory is allocated and filled with the string "HELLO".
  3. At load time, a /dev/foo device is created.
  4. Userspace application runs, and performs an open("/dev/foo")
  5. After opening the device, it performs a mmap(), and attempts to puts() the returned address.
Everything seems to be sort of working. mmap() is called, and returns a valid address. However, the block of memory returned is filled with zeros.

The kernel module loader code, at MOD_LOAD, looks like:

Code:
my_dev = make_dev(&my_cdevsw, 0, UID_ROOT, 
    GID_WHEEL, 0666, "foo");

my_mem = kmem_alloc(kmem_map, PAGE_SIZE);

vm_map_lookup(&kmem_map, my_mem, VM_PROT_ALL, 
    &my_entry, &my_mem_obj, &my_index, &my_prot, &my_wired);
vm_map_lookup_done(kmem_map, my_entry);

char_ptr = (char*)my_mem;

char_ptr[0] = 'H';
char_ptr[1] = 'E';
char_ptr[2] = 'L';
char_ptr[3] = 'L';
char_ptr[4] = 'O';
char_ptr[5] = '\0';

To handle the mmap, I specify a d_mmap_single function as follows:

Code:
static int my_mmap_single (
    struct cdev* dev, vm_ooffset_t* offset, 
    vm_size_t size, struct vm_object** object, int prot)
{
    if(*offset != 0 || size != PAGE_SIZE)
        return ENOMEM;

    *object = my_mem_obj;

    uprintf("mmap kernel address, string: %p %s\n", (void*)my_mem, (char*)my_mem);
    
    return 0;
}

When the userspace application runs, I see the kernel address and string value printed out as expected. The userspace mmap() returns successfully, but the memory location does not contain the string specified. Where have I gone wrong?
 
I am by no means a kernel hacker or even a proper programmer, but I always understood that a user space application is given its own (virtual) address space.

So memory position 0x12345678 doesn't necessarily point to the same memory position 0x12345678 from the kernel's (or any other application's) point of view.
 
Yes, every user space process has its own virtual address space that does not have anything in common with how the kernel code sees the kernel address space. The user space process can practically pretend that it's alone in the machine and every addressable memory address is owned by it and can use it at will.
 
Thought I'd report back with a solution which may be useful to others. The loader remains pretty much the same:

Code:
case MOD_LOAD:
   my_dev = make_dev(&my_cdevsw, 0, UID_ROOT, 
       GID_WHEEL, 0666, "foo");
   
   my_mem = (vm_offset_t)malloc(PAGE_SIZE, M_MYMODULE, M_WAITOK);

   char_ptr = (char*)my_mem;
   
   char_ptr[0] = 'H';
   char_ptr[1] = 'E';
   char_ptr[2] = 'L';
   char_ptr[3] = 'L';
   char_ptr[4] = 'O';
   char_ptr[5] = '\0';

   break;

case MOD_UNLOAD:
   destroy_dev(my_dev);
   free((void*)my_mem, M_MYMODULE);

   break;


I then removed my d_mmap_single implementation and replaced it with just a d_mmap implementation:

Code:
static int my_mmap (struct cdev* dev, 
    vm_ooffset_t offset, vm_paddr_t* paddr, int nprot, vm_memattr_t* attr)
{
    *paddr = vtophys(my_mem+offset);

    return 0;
}

This seems to do the trick. I'm not 100% confident in this solution, so if anyone familiar with the kernel can confirm this is good practice I'd be very appreciative.
 
Back
Top