WinDriver/win31drv/thunk.h
2026-02-21 18:01:54 -06:00

135 lines
5.8 KiB
C

#ifndef THUNK_H
#define THUNK_H
#include <stdint.h>
#include <stdbool.h>
#include "wintypes.h"
// ============================================================================
// 32-to-16 bit thunking layer
//
// Provides a mechanism for 32-bit DJGPP code to call into 16-bit Windows
// driver code running in 16-bit protected mode segments.
//
// Architecture:
// - A small 16-bit relay thunk is placed in a 16-bit code segment
// - Parameters are pre-built on a dedicated 16-bit stack
// - The relay handles 32/16-bit return address translation
// - Driver functions use Pascal calling convention (callee cleans stack)
//
// The relay thunk code:
// 1. Pops the 32-bit return address (8 bytes: 32-bit CS + 32-bit EIP)
// 2. Pushes parameters from the shared data area onto the 16-bit stack
// 3. Does a 16-bit far call to the target driver function
// 4. Saves the return value (DX:AX)
// 5. Pushes the 32-bit return address back
// 6. Does a 32-bit far return (o32 retf)
// ============================================================================
// ============================================================================
// Thunk shared data area (in 16-bit addressable memory)
//
// The 32-bit side writes target address and parameters here before
// calling the relay. The relay reads from here.
// ============================================================================
#define THUNK_MAX_PARAMS 32 // Max 16-bit words of parameters per call
typedef struct __attribute__((packed)) {
uint16_t targetOff; // 0x00: target function offset
uint16_t targetSeg; // 0x02: target function segment (selector)
uint16_t paramCount; // 0x04: number of 16-bit parameter words
uint16_t params[THUNK_MAX_PARAMS]; // 0x06: parameter words
} ThunkDataT;
// ============================================================================
// Thunk context (initialized once, used for all calls)
// ============================================================================
typedef struct {
// 16-bit relay code segment
uint16_t relayCodeSel; // Selector for relay code segment
uint32_t relayCodeBase; // Linear base address of relay code
uint16_t relayCodeSize; // Size of relay code
// 16-bit data segment (for ThunkDataT)
uint16_t dataSegSel; // Selector for shared data segment
uint32_t dataSegBase; // Linear base address of data segment
uint16_t dataSegSize; // Size of data segment
// 16-bit stack segment
uint16_t stackSel; // Selector for 16-bit stack
uint32_t stackBase; // Linear base address of stack
uint16_t stackSize; // Size of stack
// DOS memory for all 16-bit segments (single allocation)
int dosMemSeg; // Real-mode segment
int dosMemSel; // PM selector from DOS alloc
uint32_t dosMemSize; // Total bytes allocated
// Driver's auto data segment (DGROUP) selector.
// Set this before calling thunkCall16 so the relay loads DS correctly.
uint16_t dgroupSel;
bool initialized;
} ThunkContextT;
// ============================================================================
// Thunk layer functions
// ============================================================================
// Initialize the thunking infrastructure.
// Allocates DOS memory, creates 16-bit segments, installs relay code.
bool thunkInit(ThunkContextT *ctx);
// Enable or disable verbose callback tracing.
void thunkSetDebug(bool debug);
// Set a watchpoint on 3 bytes at sel:off. Logs any changes during callbacks.
void thunkSetWatch(uint16_t sel, uint32_t off);
// Shut down the thunking infrastructure and free resources.
void thunkShutdown(ThunkContextT *ctx);
// Call a 16-bit function via the thunk.
// targetSel:targetOff - far address of the 16-bit function
// params - array of 16-bit parameter words in Pascal order:
// params[0] = leftmost parameter (pushed first, deepest)
// params[N-1] = rightmost parameter (pushed last, top)
// paramCount - number of 16-bit words in params
//
// Returns DX:AX combined as a uint32_t (AX in low 16, DX in high 16).
// For functions returning WORD, just use the low 16 bits.
uint32_t thunkCall16(ThunkContextT *ctx, uint16_t targetSel, uint16_t targetOff, const uint16_t *params, uint16_t paramCount);
// Convenience: call with individual parameters (up to 12 words).
uint32_t thunkCall16v(ThunkContextT *ctx, uint16_t targetSel, uint16_t targetOff, uint16_t paramCount, ...);
// ============================================================================
// 16-bit stub generation
//
// For Windows API stubs that the driver calls back into, we need 16-bit
// entry points that thunk UP to 32-bit code.
// ============================================================================
// Callback function type for 16-to-32 callbacks.
// Receives the parameters as an array of 16-bit words.
// Returns DX:AX as uint32_t.
typedef uint32_t (*ThunkCallbackT)(uint16_t *params, uint16_t paramCount);
// Maximum number of registered callbacks
#define THUNK_MAX_CALLBACKS 128
// Register a callback and get a 16-bit far pointer (sel:off) that,
// when called from 16-bit code, will invoke the callback in 32-bit.
// paramWords = number of 16-bit parameter words the function expects
// (used by the stub to clean the stack with retf N).
// Returns true on success.
bool thunkRegisterCallback(ThunkContextT *ctx, ThunkCallbackT callback, uint16_t paramWords, FarPtr16T *result);
// Sanitize the callback frame after freeing a selector.
// If the saved ES or DS in the callback interrupt frame matches freedSel,
// zero it out so the IRET doesn't try to load a freed descriptor.
void thunkSanitizeCbFrame(uint16_t freedSel);
#endif // THUNK_H