qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] KQEMU Darwin port status?


From: Mike Kronenberg
Subject: Re: [Qemu-devel] KQEMU Darwin port status?
Date: Tue, 20 Mar 2007 17:26:10 +0100

You may want to hit me with a brick :)

here is a draft not thested or whatever of kqemu-darwin.cpp before I have to leave...
...just for some ideads.

/note to self
-open a folder in kju svn tonight, so that we don't have to spam the list to much.

Mike

On 20.03.2007, at 12:58, Mike Kronenberg wrote:

Here we go:
www.kronenberg.org/qemu/qemu-devel-darwin-kqemu-20mar07.tar.bz2

- Made a new IOKit kext proj to match all the kqemu name requirements
- moved kext header inside kqemu-darwin.cpp to lessen stray darwin related files
- added kqemu-kernel.h to the target
- added kqemu.h to the target
- added stubs from kqemu-kernel.h
- moved the test cases to the "playground"

The kqemuClient is updated to work with the kext and its playground.

Next I will start filling in the stubs and kqemu_open/close and kqemu_ioctl based on Phils prototype.
- Wondering how to convert a physical address to a page index on...

feel free to be faster :)
Mike


On 20.03.2007, at 09:02, Mike Kronenberg wrote:

Cool :)

just dlded your tarball, and things work well.

I'm about to add the code to the "genuine" kqemu environment. With kqemu Variables and function stubs.
I hope to post back soon.

Mike


On 20.03.2007, at 03:18, Philip Boulain wrote:

Mike Kronenberg wrote:
> So any suggestions on how to lock user pages in Darwin would be very welcome.

Philip Boulain wrote:
Thanks; looking at this post, I'm probably barking up the right tree

Right. I've cobbled up the aformentioned prototype, and it working insofar that the modified and now-leaky version of Mike's test client is allocating sequential lumps of memory when you bash the EXEC button, and I'm getting plausible sequential physical addresses out; I can also read/write to the memory via the IOMemoryDescriptor (the client initialises the first uint8 of the 'lump' to 23, which will appear in the system log/console; the kext writes 42 to it, which will appear in the client).

http://www.ecs.soton.ac.uk/~prb/junk/qemu-devel-darwin- kqemu-19mar07.tar.bz2 (1934KB)

Summary of changes to Mike's earlier example:
- 'transporter' struct now has a 'uint8_t* lump_of_ram;' member, to act as some application-allocated memory (it mallocs (and never frees, ahem) 2K each EXEC ioctl). - I dropped Mike's KEXT code into the HelloIOKit example, so that it does all the IO KEXT initialisation. I'm not convinced that this is actually necessary. A side effect is that 'kqemu.ext' is now called 'HelloIOKit.ext'. Please bear with the quick and nasty prototype. ;)
 - The KQEMU_EXEC ioctl handling case now:
- Constructs a IOGeneralMemoryDescriptor and initialises it to point to the lump_of_ram in the transporter struct - 'Prepares' the Descriptor. I believe that this is performing the required locking: "This involves paging in the memory, if necessary, and wiring it down for the duration of the transfer." [1] - Prints the physical address to the system log, and does the aformentioned read/write
   - 'Completes' the Descriptor (unlocking)
- Destructs (pedantically: unreferences) the IOGeneralMemoryDescriptor

[De]constructing a fresh IOGMD each time is rough-prototype-code garbage. One can be recycled by just calling initWithAddress() again on it---at a _glance_, it looks like the kqemu_instance struct would be a sensible place to put it.

Phil
1. http://developer.apple.com/documentation/Darwin/Reference/ KernelIOKitFramework/IOMemoryDescriptor/Classes/ IOMemoryDescriptor/index.html



_______________________________________________
Qemu-devel mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/qemu-devel



_______________________________________________
Qemu-devel mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/qemu-devel



_______________________________________________
Qemu-devel mailing list
address@hidden
http://lists.nongnu.org/mailman/listinfo/qemu-devel



//header start - included to generate less clutter in the kqemu repository :)
#include <IOKit/IOService.h>

class org_qemu_driver_kqemu : public IOService {
        OSDeclareDefaultStructors(org_qemu_driver_kqemu)
public:
        virtual bool init(OSDictionary* dictionary = 0);
        virtual void free(void);
        virtual IOService* probe(IOService* provider, SInt32* score);
        virtual bool start(IOService* provider);
        virtual void stop(IOService* provider);
};
//header end



#include <IOKit/IOLib.h>

#include <mach/mach_types.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <miscfs/devfs/devfs.h>
#include <sys/ioctl.h>

#import <libkern/OSMalloc.h>

//#include "kqemu-darwin.h" //we included the kext header

extern "C" {
        #include <pexpert/pexpert.h> /* Tutorial says for debug only */
}

#define super IOService

// Second argument must be literal (preprocessor limitation?)
OSDefineMetaClassAndStructors(org_qemu_driver_kqemu, IOService)



/*
* kqemu stuff below ***********************************************************
 */

//#include <sys/proc.h>

#import "kqemu-kernel.h" //this is the kqemu project header
int kqemu_debug;

static struct kqemu_global_state *kqemu_gs = NULL;

struct kqemu_global_state * CDECL kqemu_global_init(int max_locked_pages)
{
    return NULL;
}

void CDECL kqemu_global_delete(struct kqemu_global_state *g)
{
}

struct kqemu_state * CDECL kqemu_init(struct kqemu_init *d,
                                      struct kqemu_global_state *g)
{
    return NULL;
}

struct kqemu_cpu_state * CDECL kqemu_get_cpu_state(struct kqemu_state *s)
{
    return NULL;
}

long CDECL kqemu_exec(struct kqemu_state *s)
{
    return NULL;
}

void CDECL kqemu_delete(struct kqemu_state *s)
{
}


/* Lock the page at virtual address 'user_addr' and return its
   physical address (page index). Return a host OS private user page
   identifier or NULL if error */
struct kqemu_user_page *CDECL kqemu_lock_user_page(unsigned long *ppage_index, unsigned long user_addr)
{
    return NULL;
}

void CDECL kqemu_unlock_user_page(struct kqemu_user_page *page)
{
}


/* Allocate a new page and return its physical address (page
   index). Return a host OS private page identifier or NULL if
   error */
struct kqemu_page *CDECL kqemu_alloc_zeroed_page(unsigned long *ppage_index)
{
    return NULL;
}

void CDECL kqemu_free_page(struct kqemu_page *page)
{
}


/* Return a host kernel address of the physical page whose private
   identifier is 'page1' */
void * CDECL kqemu_page_kaddr(struct kqemu_page *page)
{
    return NULL;
}


/* Allocate 'size' bytes of memory in host kernel address space (size
   is a multiple of 4 KB) and return the address or NULL if error. The
   allocated memory must be marked as executable by the host kernel
   and must be page aligned. On i386 with PAE (but not on x86_64), it
   must be allocated in the first 4 GB of physical memory. */
void * CDECL kqemu_vmalloc(unsigned int size)
{
    return NULL;
}

void CDECL kqemu_vfree(void *ptr)
{
}


/* Convert a page aligned address inside a memory area allocated by
   kqemu_vmalloc() to a physical address (page index) */
unsigned long CDECL kqemu_vmalloc_to_phys(const void *vaddr)
{
vm_offset_t pa = ml_vtophys((vm_offset_t)vaddr); //vm_paddr_t pa = vtophys(vaddr);
    if (pa == 0) {
        kqemu_log("kqemu_vmalloc_to_phys(%p)->error\n", vaddr);
        return -1;
    }
    if (kqemu_debug > 0)
        kqemu_log("kqemu_vmalloc_to_phys(%p)->%08x\n", vaddr, pa);
    return pa >> PAGE_SHIFT;
}


/* Map a IO area in the kernel address space and return its
   address. Return NULL if error or not implemented. This function is
   only used if an APIC is detected on the host CPU. */
void * CDECL kqemu_io_map(unsigned long page_index, unsigned int size)
{

// from machine_routines.h:
///* Map memory map IO space */
//vm_offset_t ml_io_map(
//      vm_offset_t phys_addr,
//      vm_size_t size);

    return NULL;
}

void CDECL kqemu_io_unmap(void *ptr, unsigned int size)
{
}


/* return TRUE if a signal is pending (i.e. the guest must stop
   execution) */
int CDECL kqemu_schedule(void)
{
        return proc_issignal(proc_selfpid(), (sigset_t)-1);
}

static char log_buf[4096];

void CDECL kqemu_log(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(log_buf, sizeof(log_buf), fmt, ap);
    printf("kqemu: %s", log_buf);
    va_end(ap);
}

/*
* darwin specific stuff *******************************************************
 */
struct kqemu_instance {
//#if __FreeBSD_version >= 500000
//    TAILQ_ENTRY(kqemu_instance) kqemu_ent;
//    struct cdev *kqemu_dev;
//#endif
    /*    struct semaphore sem;  */
    struct kqemu_state *state;
};


/*
   workaround for darwin not having dev->si_drv1
   found in qvm86-osx.c by Sandro Tolaini, txs */
#define MAX_MAP_ENTRIES 8
struct proc *procs[MAX_MAP_ENTRIES];
kqemu_instance *instances[MAX_MAP_ENTRIES];

void instance_map_init()
{
        int i;

        for (i = 0; i < MAX_MAP_ENTRIES; ++i) {
                procs[i] = NULL;
                instances[i] = NULL;
        }
}

kqemu_instance *instance_map_find(struct proc *p)
{
        int i;
        
        for (i = 0; i < MAX_MAP_ENTRIES; ++i) {
                if (procs[i] == p)
                        return instances[i];
        }
        
        return NULL;
}

int instance_map_add(struct proc *p, kqemu_instance *ks)
{
        int i;
        
        for (i = 0; i < MAX_MAP_ENTRIES; ++i) {
                if (procs[i] == NULL) {
                        procs[i] = p;
                        instances[i] = ks;
                        return 0;
                }
        }
        
        return -1;
}

void instance_map_remove(struct proc *p)
{
        int i;
        
        for (i = 0; i < MAX_MAP_ENTRIES; ++i) {
                if (procs[i] == p) {
                        procs[i] = NULL;
                        instances[i] = NULL;
                }
        }
}


/*
* kext stuff below ***********************************************************
 */
OSMallocTag kqemu_malloc_tag;
static int major = -1;
static void *devfshandle = 0;

static int kqemu_open(dev_t dev, int flags, int devtype, struct proc *p) {
        struct kqemu_instance *ks;

ks = (kqemu_instance *)OSMalloc(sizeof(struct kqemu_instance), kqemu_malloc_tag);
    if (ks == NULL) {
        kqemu_log("malloc failed\n");
        return ENOMEM;
    }
//    init_MUTEX(&ks->sem); do we need this semaphore?
    memset(ks, 0, sizeof *ks);
    instance_map_add(p, ks);
    if (kqemu_debug > 0)
        kqemu_log("opened by pid=%d\n", proc_pid(p));

    return 0;
}

static int kqemu_close(dev_t dev, int flags, int devtype, struct proc *p) {
        kqemu_instance *ks = (kqemu_instance *)instance_map_find(p);

//    down(&ks->sem);
    if (ks->state) {
        kqemu_delete(ks->state);
        ks->state = NULL;
    }
//    up(&ks->sem);
    OSFree(ks, sizeof(struct kqemu_instance), kqemu_malloc_tag);
    instance_map_remove(p);

    return 0;
}

/* playground start - this is for debigging/testing only */
#define KQEMU_TEST_1          _IOWR('q', 5, u_int32_t)
#define KQEMU_TEST_2          _IOWR('q', 6, transporter)
#define KQEMU_TEST_3          _IOWR('q', 7, int)

typedef struct transporter {
        uint8_t* lump_of_ram;
} transporter;
/* playground end */

static int kqemu_ioctl(dev_t dev, u_long cmd, caddr_t addr, int fflag, struct proc *p) {
    int error = 0;
    kqemu_instance *ks = (kqemu_instance *)instance_map_find(p);
    struct kqemu_state *s = ks->state;

    switch(cmd) {
        case KQEMU_INIT: {
            if (addr) {

                struct kqemu_init d1, *d = &d1;
                if (s != NULL) {
                    error = EIO;
                    break;
                }
                d1 = *(struct kqemu_init *)addr;
                if (kqemu_debug > 0)
kqemu_log("ram_base=%p ram_size=%ld\n", d1.ram_base, d1.ram_size);
                s = kqemu_init(d, kqemu_gs);
                if (s == NULL) {
                    error = ENOMEM;
                    break;
                }
                ks->state = s;

                break;
            } else {
                printf("kqemu KEXT: KQEMU_INIT: no addr\n");
                error = EINVAL;
                break;
            }
        }
                        
        case KQEMU_EXEC: {
            if (addr) {
                struct kqemu_cpu_state *ctx;
                if (s != NULL) {
                    error = EIO;
                    break;
                }
                ctx = kqemu_get_cpu_state(s);
                *ctx = *(struct kqemu_cpu_state *)addr;
                //do we need a lock here?
                error = kqemu_exec(s);
                //do we need a unlock here?
                *(struct kqemu_cpu_state *)addr = *ctx;
                break;
            } else {
                printf("kqemu KEXT: KQEMU_EXEC no addr\n");
            }
            break;
        }
                        
        case KQEMU_GET_VERSION: {
            if (addr) {
                                *(int *)addr = KQEMU_VERSION;
                                printf("kqemu KEXT: KQEMU_VERSION: %08x\n", 
*(int *)addr);
            } else {
                printf("kqemu KEXT: KQEMU_VERSION: no addr\n");
            }
            break;
        }

        /* playground start - this is for debigging/testing only */
        case KQEMU_TEST_1: {
            if (addr) {
                *(u_int32_t *)addr = 102;
                printf("kqemu KEXT: KQEMU_TEST_1: %d\n", *addr);
                break;
            } else {
                printf("kqemu KEXT: KQEMU_TEST_1: no addr\n");
            }
        }
                        
        case KQEMU_TEST_2: { // Actually just a test case
            if (addr) {
                                IOGeneralMemoryDescriptor* iomd = new 
IOGeneralMemoryDescriptor();
                                
                transporter *tTr = (transporter *)addr;
printf("kqemu KEXT: KQEMU_TEST_2: Ooh, a lump of RAM, with user address %p!\n", tTr->lump_of_ram);
                                
if(!iomd->initWithAddress((vm_address_t) tTr->lump_of_ram, 256, kIODirectionOutIn, current_task())) {
                                        // Initialisation failed---drop the 
instance and bail.
                                        printf("kqemu KEXT: KQEMU_TEST_2: 
initWithAddress failed!\n");
                                        iomd->release();
                                        break;
                                } else {
printf("kqemu KEXT: KQEMU_TEST_2: initWithAddress succeeded, hurrah.\n");
                                }
                                if(iomd->prepare() == kIOReturnSuccess) {
                                        uint8_t value;
                                        uint8_t* ram_ptr = (uint8_t*) 
iomd->getPhysicalAddress();
printf("kqemu KEXT: KQEMU_TEST_2: It's physical address is %p. \n", (void*) ram_ptr);
                                        
// Unsuprisingly, poking with random physical addresses in a kernel context causes explosions. // Uncommenting these lines will almost certainly panic OS X beautifully. YHBW. //printf("kqemu KEXT: KQEMU_TEST_2: And the first value there is %u.\n", (unsigned int) *ram_ptr);
                                        //*ram_ptr = 42;
                                        
// For the sake of testing, let's do this with the accessors/ mutators
                                        iomd->readBytes(0, &value, 
sizeof(uint8_t));
printf("kqemu KEXT: KQEMU_TEST_2: And the first value there is % u.\n", (unsigned int) value);
                                        value = 42;
                                        iomd->writeBytes(0, &value, 
sizeof(uint8_t));
                                                
                                        iomd->complete();
                                } else {
                                        printf("kqemu KEXT: KQEMU_TEST_2: prepare 
failed!\n");
                                }
                                
                                iomd->release();
            } else {
                printf("kqemu KEXT: KQEMU_TEST_2 no addr\n");
            }
            break;
        }
                        
        case KQEMU_TEST_3: {
            if (addr) {
                                *(int *)addr = KQEMU_VERSION;
                                printf("kqemu KEXT: KQEMU_TEST_3: %08x\n", 
*(int *)addr);
            } else {
                printf("kqemu KEXT: KQEMU_TEST_3: no addr\n");
            }
            break;
        }
        /* playground end */
                        
        default:
            error = EINVAL;
    }

    return error;
}

// Character device switch table
static struct cdevsw chardev = {
        kqemu_open, /* open */
        kqemu_close, /* close */
        eno_rdwrt, /* read */
        eno_rdwrt, /* write */
        kqemu_ioctl, /* ioctl */
        eno_stop, /* stop*/
        eno_reset, /* reset */
        0, /* ttys */
        eno_select, /* select */
        eno_mmap, /* mmap */
        eno_strat, /* strategy */
        eno_getc, /* getc */
        eno_putc, /* putc */
        0 /* type */
};

// Object initialiser
bool org_qemu_driver_kqemu::init(OSDictionary* dict) {
        bool result = super::init(dict);
        IOLog("kqemu KEXT: initializing...\n");
        return result;
}

// Object destructor
void org_qemu_driver_kqemu::free(void) {
        IOLog("kqemu KEXT: freeing...\n");
        super::free();
}

// Hardware compatability test
IOService* org_qemu_driver_kqemu::probe(IOService* provider, SInt32* score) {
        IOService* result = super::probe(provider, score);
        IOLog("kqemu KEXT: probing...\n");
        return result;
}

// Class initialiser
bool org_qemu_driver_kqemu::start(IOService* provider) {
        bool result = super::start(provider);

        instance_map_init();
    kqemu_malloc_tag = OSMalloc_Tagalloc("kqemu", OSMT_DEFAULT);
        
        // Register device in /dev
    major = cdevsw_add(major, &chardev);
    if (major == -1) {
        printf("kqemu KEXT: error registering cdevsw\n");
    }
devfshandle = devfs_make_node(makedev(major, 0), DEVFS_CHAR, UID_ROOT, GID_WHEEL, 0666, "kqemu");
    if (!devfshandle) {
        printf("kqemu KEXT: error creating device node\n");
    }
        
        IOLog("kqemu KEXT: starting...\n");
        return result;
}

// Class destructor
void org_qemu_driver_kqemu::stop(IOService* provider) {
    // remove device from /dev
        devfs_remove(devfshandle);
        cdevsw_remove(major, &chardev);
        
        IOLog("kqemu KEXT: stopping...\n");
        super::stop(provider);
    OSMalloc_Tagfree(kqemu_malloc_tag);
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]