Subject: Re: [Fwd: Re: Porting FreeBSD NDIS to
To: Bill Paul <wpaul@windriver.com>
From: Alan Ritter <rittera@cc.wwu.edu>
List: tech-kern
Date: 08/03/2005 17:03:04
Hi,

>> This is an excellent idea!  I just got started on it, now I just need to
>> go through, and print out each of the fields, I'm using a macro for this
>> of course but it's still tedious.  Here's what I've done to get it
>> started:
>>
>> bash-3.00$ ./test
>> sizeof(ndis_miniport_block) == 0x19c == 412
>> offset of nmb_signature: 0
>> offset of nmb_nextminiport: 4
>> offset of nmb_driverhandle: 8
>> offset of nmb_miniportadapterctx: 12
>
> According to a small program I just cooked up:
>
> [/sys/compat/ndis]:test{685}% ./a.out
> sizeof(nmb_status_block): 0x244 (580)
> nmb_status_func: 0x17c (380)
>
> So your structure size is definitely off.
>
> The program I wrote is:
>
> #include <sys/types.h>
> #include <sys/param.h>
> #include <sys/callout.h>
>
> #include <sys/kernel.h>
> #include <sys/module.h>
> #include <sys/kthread.h>
> #include <machine/bus.h>
> #include <machine/resource.h>
> #include <sys/bus.h>
>
> #include <sys/queue.h>
>
> #include "pe_var.h"
> #include "cfg_var.h"
> #include "resource_var.h"
> #include "ntoskrnl_var.h"
> #include "hal_var.h"
> #include "ndis_var.h"
>
> #include <stdio.h>
>
> main()
> {
>         printf("sizeof(nmb_status_block): 0x%x (%d)\n",
>             sizeof(ndis_miniport_block), sizeof(ndis_miniport_block));
>         printf("nmb_status_func: 0x%x (%d)\n",
>             offsetof(ndis_miniport_block, nmb_status_func),
>             offsetof(ndis_miniport_block, nmb_status_func));
>         exit(0);
> }

Sorry, I forgot to mention, I had commented out the BSD-specific part of
the ndis_miniport_block structure.  However I just uncommented it, and
fixed a few things to get it to compile as a user program and the
structure size seems to be 0x250 or 592 both on FreeBSD and NetBSD (both
outputs are identical).  anyway, the offset of the last Windows specific
function 408, and ndis_status_func is at 380:

offset of nmb_status_func: 380
offset of nmb_statusdone_func: 384
offset of nmb_tdcond_func: 388
offset of nmb_querydone_func: 392
offset of nmb_setdone_func: 396
offset of nmb_wantxdone_func: 400
offset of nmb_wanrx_func: 404
offset of nmb_wanrxdone_func: 408


>> Yes, this has definitely happened.  For instance there is an ndis_timer
>> field in the miniport_block, and the ndis_timer structure contains a
>> "struct ktimer" which I did the following to:
>>
>> struct ktimer {
>>    nt_dispatch_header      k_header;
>>    uint64_t                k_duetime;
>>    union {
>>            list_entry              k_timerlistentry;
>> #ifdef __FreeBSD__
>>            struct callout_handle   k_handle;
>> #else /* __NetBSD__ */
>>            struct callout          k_handle;
>> #endif
>>    } u;
>>    void                    *k_dpc;
>>    uint32_t                k_period;
>> };
>>
>> From looking at the definition of callout_handle it appears to only
>> contain a single pointer (4 bytes) whereas the NetBSD callout structure
>> seems to contain 24 bytes, but this is 20 bytes not 16.  I think there
>> must be somewhere else that I need to find.  Anyway just for fun I tried
>> changing it to a pointer like so:
>>
>>            struct callout          *k_handle;
>>
>> After recompiling it now blows up in KeInitializeTimerEx(), inside this
>> call
>>
>> callout_init(timer->k_handle);
>>
>> where timer->k_handle is null (I did malloc this in NdisAddDevice()).
>>
>> Anyway I'm pretty sure it's just an issue of the layout of the
>> ndis_miniport_block structure, so I'll finish writing that test program
>> to
>> print out all it's offsets, then try it out on FreeBSD, and see what
>> happens.
>
> A note about this: the size of struct ktimer is defined by Windows.
> In Windows, it's struct KTIMER and is defined in ntddk.h as:
>
> typedef struct _KTIMER {
>     DISPATCHER_HEADER Header;
>     ULARGE_INTEGER DueTime;
>     LIST_ENTRY TimerListEntry;
>     struct _KDPC *Dpc;
>     LONG Period;
> } KTIMER, *PKTIMER, *RESTRICTED_POINTER PRKTIMER;
>
> Now, drivers know about the size of KTIMER, and it's the caller of the
> Windows timer API that's responsible for allocating the KTIMER structures
> and passing them into the kernel, so Project Evil's definition must be the
> same size, or else driver binaries will break. Also, since drivers
> allocate
> KTIMERs using the generic ExAllocatePoolWithTag() API, you can't easily
> shadow the driver's KTIMER structures with your own that contain more
> data.
> (Unless you do something very unpleasant to the memory allocator, which I
> don't even want to think about.)
>
> Logically, I should be using FreeBSD's struct callout and the callout API
> for
> implementing timers, but I can't: struct callout in FreeBSD is too big for
> me
> to embed it inside struct KTIMER! Consequently, I just use the old style
> timeout()/untimeout() API, which requires just a callout_handle. This,
> as you saw, is just a pointer, so I can safely stick it in KTIMER and
> not have any problems. I used the union with list_entry to insure the
> KTIMER structure is still the same size.

I see, I thought that FreeBSD just stuck with the old timeout/untimeout
system, and only NetBSD had the callout facility.  This is why I earlier
converted everything from timeout to callout (hopefully I did this
correctly).  Anyway I don't think timeout() is still available in NetBSD
(man 9 timeout takes you to man 9 callout), but I should look into it.  It
would be better if I stuck with the original way it was written.

> So you can't just replace struct callout handle with struct callout
> here: you'll hose everything. There's a couple of solutions to this
> problem:
>
> - Clone the Windows timer API so you can use KTIMERs directly. I didn't
> use
>   this idea because I was too lazy to write my own timer library from
> scratch.
>
> - Allocate your own pool of callout structures that can be attached to
>   KTIMERs as needed, and use struct callout * in the KTIMER structure
>   instead directly embedding the callout in KTIMER. I punted on this too
>   because I decided I'd just be duplicating timeout()/untimeout() anyway.
>

Right now I'm using option 2, I just put a pointer to a callout structure
in the KTIMER.  I'm also just malloc'ing struct callouts in
NdisMInitializeTimer() as there needed like so:

#ifdef __NetBSD__
/* TODO: free this memory somewhere! */
	if(timer->nmt_ktimer.k_handle == NULL) {
		timer->nmt_ktimer.k_handle =
				malloc(sizeof(struct callout), M_DEVBUF, M_NOWAIT|M_ZERO);
	}
#endif

I guess this is a bad idea, I should set up a pool of callout structures
like you say if I can't just use the timeout() facility in NetBSD.

Thanks!