From a7c685d05be45f0f04e09650aa7f1e314783a342 Mon Sep 17 00:00:00 2001
From: Oliver Ford <oliford@NervousEnergy.(none)>
Date: Sun, 20 Apr 2008 19:16:20 +0100
Subject: [PATCH] Resume pointer style resume hooking:
  Set ResumePtr to where windows stores the resume address. This address
  is watched and when windows writes its own address there, resume_handler's
  address is written and the one windows wrote is kept to jump to after
  resume_handler();

  r0 is saved during call to resume_handler.
---
 include/irq.h        |    7 ++++
 include/winvectors.h |   15 +++++++-
 src/irq.cpp          |   29 ++++++++++++++--
 src/linboot.cpp      |    4 +-
 src/wince/asmstuff.S |   22 ++++++++++++
 src/winvectors.cpp   |   93 +++++++++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 159 insertions(+), 11 deletions(-)

diff --git a/include/irq.h b/include/irq.h
index f27b2f6..6a00ff9 100644
--- a/include/irq.h
+++ b/include/irq.h
@@ -90,6 +90,13 @@ struct irqData {
     uint32 traceForWatch;
 
     //
+    // Resume pointer hook tracing (winvectors.cpp) 
+    //
+    uint32 resumeCodePtr; //address of our resume code (Phys)
+    uint32 *resumePtrPtr; //address at which windows will store its resume address (MVA)
+    uint32 *resumeJReturnPtr; //address at which we store where to go when we're done (MVA)
+
+    //
     // Trace buffer.
     //
     uint32 overflows, errors;
diff --git a/include/winvectors.h b/include/winvectors.h
index 48e5e32..768a22d 100644
--- a/include/winvectors.h
+++ b/include/winvectors.h
@@ -15,6 +15,17 @@ struct stackJumper_s {
 };
 extern struct stackJumper_s stackJumper;
 
+struct resumeJumper_s {
+    uint32 stack;
+    uint32 data;
+    uint32 execCode;
+    uint32 returnCode;
+    uint32 r0Store;
+    uint32 in[5];
+    uint32 out[2];
+};
+extern struct resumeJumper_s resumeJumper;
+
 uint32 *findWinCEirq(uint32 offset);
-int hookResume(uint32 handler, uint32 stack, uint32 data, int complain=1);
-void unhookResume();
+int hookResume(struct irqData *dataVA, uint32 handler, uint32 stack, uint32 data, int complain=1);
+void unhookResume(struct irqData *dataVA);
diff --git a/src/irq.cpp b/src/irq.cpp
index e294518..62ffd62 100644
--- a/src/irq.cpp
+++ b/src/irq.cpp
@@ -76,11 +76,31 @@ postPoll(struct irqData *data, int isPXA) {
         set_DBCON(data->dbcon);
 }
 
-
 /****************************************************************
  * C part of exception handlers
  ****************************************************************/
 
+//Resume pointer hooking (winvectors.cpp)
+static void
+report_resumeChanged(irqData *data, const char *header, traceitem *item)
+{
+    uint32 newAddr=item->d0;
+    Output("%s Resume pointer changed to %08x, using as resume addr.",header,newAddr);
+}
+
+static void __irq checkResumePtr(struct irqData *data){
+    if( !data->resumePtrPtr )return;
+    
+    uint32 newResumeAddr = *data->resumePtrPtr; //get it once
+    if( newResumeAddr != data->resumeCodePtr){ //windows has overwritten the resume pointer
+
+         *data->resumeJReturnPtr = newResumeAddr;	//set into pointer we run when resume_handler() is done
+         *data->resumePtrPtr = data->resumeCodePtr;	//make sure we'll still get control
+
+        add_trace(data, report_resumeChanged, newResumeAddr); // and report the change
+    }
+}
+
 static void
 report_memPoll(irqData *data, const char *header, traceitem *item)
 {
@@ -133,6 +153,7 @@ irq_handler(struct irqData *data, struct irqregs *regs)
     prePoll(data, isPXA);
     checkPolls(data, &data->irqpoll, faultaddr);
     checkPolls(data, &data->tracepoll, faultaddr);
+    checkResumePtr(data);
     checkMMUMerge(data);
     postPoll(data, isPXA);
 }
@@ -153,6 +174,7 @@ abort_handler(struct irqData *data, struct irqregs *regs)
     // Trace time memory polling.
     prePoll(data, isPXA);
     checkPolls(data, &data->tracepoll, faultaddr);
+    checkResumePtr(data);
     checkMMUMerge(data);
     postPoll(data, isPXA);
 
@@ -175,6 +197,7 @@ prefetch_handler(struct irqData *data, struct irqregs *regs)
     // Trace time memory polling.
     prePoll(data, isPXA);
     checkPolls(data, &data->tracepoll, faultaddr);
+    checkResumePtr(data);
     checkMMUMerge(data);
     postPoll(data, isPXA);
 
@@ -452,7 +475,7 @@ cmd_wirq(const char *cmd, const char *args)
     if (ret)
         goto abort;
 
-    ret = hookResume(
+    ret = hookResume(data,
         memVirtToPhys((uint32)&code->cCode[offset_cResumeHandler()])
         , memVirtToPhys((uint32)data)
         , memVirtToPhys((uint32)data)
@@ -489,7 +512,7 @@ cmd_wirq(const char *cmd, const char *args)
     return_control();
     Output("Finished restoring windows exception handlers.");
 
-    unhookResume();
+    unhookResume(data);
 
     dumpMMUMerge(data);
     postLoop(data);
diff --git a/src/linboot.cpp b/src/linboot.cpp
index d21fe10..9e455d1 100644
--- a/src/linboot.cpp
+++ b/src/linboot.cpp
@@ -723,7 +723,7 @@ static void
 resumeIntoBoot(struct bootmem *bm)
 {
     // Overwrite the resume vector.
-    int ret = hookResume(bm->physExec, 0, 0);
+    int ret = hookResume(NULL,bm->physExec, 0, 0);
     if (ret) {
         Output("Failed to hook resume vector");
         return;
@@ -736,7 +736,7 @@ resumeIntoBoot(struct bootmem *bm)
 
     // Cleanup (if boot failed somehow).
     Output("Timeout. Restoring original resume vector");
-    unhookResume();
+    unhookResume(NULL);
 }
 
 
diff --git a/src/wince/asmstuff.S b/src/wince/asmstuff.S
index 076c6ae..7f7e297 100644
--- a/src/wince/asmstuff.S
+++ b/src/wince/asmstuff.S
@@ -137,6 +137,28 @@ asm_handler:
         ldr     lr, [pc, #(returnCode - . - 8)]
         ldr     pc, [pc, #(execCode - . - 8)]
 
+@ Version of the stackJumper(above) that saves r0 before calling the C 
+@   function, and restores it before jumping to the return code.
+@ TODO: (future-proofing) Use stmia/ldmia to save all the registers
+        .section .text.preload
+        .global resumeJumper
+resumeJumper:
+resumeStack:          .long 0
+resumeData:           .long 0
+resumeExecCode:       .long 0
+resumeReturnCode:     .long 0
+resumeR0Store:	      .long 0
+resumeIn:
+	str	r0, [pc, #(resumeR0Store - . - 8)]
+        ldr     r0, [pc, #(resumeData - . - 8)]
+        ldr     sp, [pc, #(resumeStack - . - 8)]	
+	add	lr, pc, #(resumeOut - . - 8)
+        ldr     pc, [pc, #(resumeExecCode - . - 8)]
+resumeOut:
+	ldr	r0, [pc, #(resumeR0Store - . - 8)]
+	ldr	pc, [pc, #(resumeReturnCode - . - 8)]	
+
+
 @ Code that can disable the MMU and jump to a function.
 @ In:   r0 = Physical address of this function
 @       r1 = Virtual address of MMU in non-cached ram.
diff --git a/src/winvectors.cpp b/src/winvectors.cpp
index d3cd186..c6ba454 100644
--- a/src/winvectors.cpp
+++ b/src/winvectors.cpp
@@ -14,6 +14,7 @@
 #include "cpu.h" // take_control
 #include "machines.h" // Mach
 #include "winvectors.h"
+#include "irq.h"
 
 static const uint32 VADDR_IRQTABLE=0xffff0000;
 
@@ -43,14 +44,28 @@ REG_VAR_INT(0, "RESUMEADDR", winceResumeAddr
 static uint32 *ResumePtr, OldResume[2];
 static struct stackJumper_s *ResumeSJ;
 
+// vars for resume ptr type hooking
+static uint32 winceResumePtr = 0xffffffff;
+REG_VAR_INT(0, "RESUMEPTR", winceResumePtr
+            , "Phys address where windows will put the resume location.")
+static struct resumeJumper_s *ResumeJ;
+static uint32 *ResumePtrPtr, WinResumePtr;
+int hookResumePtr(struct irqData *dataVA, uint32 handler, uint32 stack, uint32 data, int complain);
+void unhookResumePtr(struct irqData *data);
+
 // Setup wince to resume into a haret handler.
 int
-hookResume(uint32 handler, uint32 stack, uint32 data, int complain)
+hookResume(struct irqData *dataVA, uint32 handler, uint32 stack, uint32 data, int complain)
 {
-    if (winceResumeAddr == 0xffffffff) {
+    if( (winceResumeAddr == 0xffffffff) && (winceResumePtr != 0xffffffff) )
+        return hookResumePtr(dataVA,handler,stack,data,complain); // resume ptr type hooking
+
+    else if(  ((winceResumeAddr == 0xffffffff) && (winceResumePtr == 0xffffffff))  ||
+              ((winceResumeAddr != 0xffffffff) && (winceResumePtr != 0xffffffff))    ){
         if (! complain)
             return 0;
-        Output(C_ERROR "Please specify WinCE physical resume address");
+        Output(C_ERROR "Please specify either WinCE physical resume address RESUMEADDR"
+                         "or resume pointer address RESUMEPTR - not both.");
         return -1;
     }
 
@@ -102,8 +117,9 @@ fail:
 }
 
 void
-unhookResume()
+unhookResume(struct irqData *data)
 {
+    if( ResumePtrPtr ){ unhookResumePtr(data); return; } //resume ptr type hooking
     if (!ResumePtr)
         // Nothing to do.
         return;
@@ -119,3 +135,72 @@ unhookResume()
     ResumeSJ = NULL;
     ResumePtr = NULL;
 }
+
+
+//Resume pointer hooking
+int hookResumePtr(struct irqData *dataVA, uint32 handler, uint32 stack, uint32 data, int complain)
+{
+    if( !dataVA ){ Output("ResumeIntoBoot not yet supported for ResumePtr type hooking."); return -1; }
+
+    // Map the pointer address.
+    ResumePtrPtr = (uint32*)memPhysMap(winceResumePtr);
+    if (!ResumePtrPtr) {
+        Output(C_ERROR "Could not map resume pointer addr %08x", winceResumePtr);
+        return -1;
+    }
+    
+    //store what there at the moment, it may be left over from a previous suspend
+    //or may be junk. We'll fix this in trapResumeWrite()
+    WinResumePtr = *ResumePtrPtr;
+
+    // Allocate and setup C code trampoline - we need the resumeJumper type to save
+    // r0 while we get called
+    ResumeJ = (resumeJumper_s*)malloc(sizeof(*ResumeJ)*2);
+    if (! ResumeJ) {
+        Output("Unable to allocate memory for trampoline");
+        ResumeJ = NULL;
+        ResumePtrPtr = NULL;
+        return -1;
+    }
+    if (PAGE_ALIGN((uint32)ResumeJ) != PAGE_ALIGN((uint32)&ResumeJ[1]))
+        // Spans a page - move it to start of page.
+        ResumeJ = (resumeJumper_s*)PAGE_ALIGN((uint32)ResumeJ);
+    memcpy(ResumeJ, &resumeJumper, sizeof(*ResumeJ));
+    ResumeJ->stack = stack;
+    ResumeJ->data = data;
+    ResumeJ->execCode = handler;
+    ResumeJ->returnCode = WinResumePtr; // where to go after we're done, we'll probably
+                                        // overwrite this later in checkResumePtr()
+
+    handler = retryVirtToPhys((uint32)ResumeJ->in); //get phys addr of jumper code
+    Output("Initially setting resume  address @(%p) to %08x and returning to %08x",
+               ResumePtrPtr, handler, WinResumePtr);
+
+    // Overwrite the resume pointer now, we'll need todo this again if windows overwrites it
+    take_control();
+    Mach->flushCache();
+    *ResumePtrPtr = handler;	//now points directly at our ResumeJ->in
+    return_control();
+
+    //checkResumePtr() will require ...
+    dataVA->resumeCodePtr = handler;		//where our resume handler is (phys)
+    dataVA->resumePtrPtr = ResumePtrPtr;	//where it can access the resume ptr (MVA)
+    dataVA->resumeJReturnPtr = (uint32*)cachedMVA(&ResumeJ->returnCode);  //where it has to store the windows resume address, when it gets it (MVA)
+
+    return 0;
+}
+
+void unhookResumePtr(struct irqData *data)
+{
+    uint32 lastWritten = *data->resumeJReturnPtr;
+    Output("Restoring resume ptr (%p) to the last thing windows tried to change it to (%08x)"
+           , ResumePtrPtr, lastWritten );
+    take_control();
+    Mach->flushCache();
+    *ResumePtrPtr = lastWritten;
+    return_control();
+
+    free(ResumeJ);
+    ResumeJ = NULL;
+    ResumePtrPtr = NULL;
+}
-- 
1.5.2.5

