И так! Продолжаем описание.

WinCE, как и любая винда, работает с виртуальными адресами. Т.е. приложение не напрямую обращается к памяти, а через виртуализатор(у процессора есть специальный HW блок который это делает автоматом, но нам это знать вообще не нужно). Нужно просто знать, что процессор может автоматом конвертить виртуальный адрес A в физический B. Сделано это для защиты. Т.е. если мы попробуем доступиться к адресу 0xс4000000, или любому другому физическому адресу, приложение выдаст нам ошибку и закроется.
Верно и обратное. Если мы считываем данные из памяти с адресом 0x1223, то это не физический адрес DDR, а виртуальный. Где реально находится эта память - ХЗ.
Для того чтобы обратиться к физическому адресу, нам надо получить его виртуальный адрес. Но сделать это можно только в режиме kernel (режим ядра, но правельней наверное режим драйвера).
Приложение работает только в обычном режиме, поэтому получить виртуальный адрес физической памяти для него невозможно.
Важно отметить: режимы user и kernel - это не физические режимы, т.е. нельзя переключить режим вызовом специальной ф-и! Это очень тонкий момент, и я на нем остановлюсь поподробнее.
Как винда переключает режимы (для меня это было не очивидно из описаний).
На этапе когда запускается ф-я, винда разрещает user или kernel режим для этой ф-и в зависимости от того в каком участке памяти эта ф-я расположена. Т.е. у винды есть таблица, где храниться соответствие адреса и режима. По умолчанию все ф-и всех приложений расположены в памяти помеченной как user.
Если мы загружаем в приложении DLL, то ф-и этой DLL будут располагаться в памяти user.
Для того чтобы ф-я и все что в ней запускалась в режиме kernel, DLL должна быть загружина специальной ф-ей, которая помечает ее память как kernel.
В общем случае, сделать это можно, если загружать DLL как dll драйвера на этапе загрузки виндов. Т.е. если мы скажем винде загрузить нашу DLL как драйвер в режиме kernel, то сможем внутри ф-й этой DLL работать в режиме ядра.
Как загрузить в режиме ядра, чуть позже...