Спасибо, Стивен, ваше исследование здесь было полезно для меня. У меня было несколько проблем, хотя, когда я пытался сделать это на ядре 2.6.32 и получить WARNING: at arch/x86/mm/pageattr.c:877 change_page_attr_set_clr+0x343/0x530() (Not tainted)
с последующим ООПС ядра о невозможности записи в адрес памяти.
Комментарий над указанной строкой гласит:
// People should not be passing in unaligned addresses
Работает следующий модифицированный код:
int set_page_rw(long unsigned int _addr)
{
return set_memory_rw(PAGE_ALIGN(_addr) - PAGE_SIZE, 1);
}
int set_page_ro(long unsigned int _addr)
{
return set_memory_ro(PAGE_ALIGN(_addr) - PAGE_SIZE, 1);
}
Обратите внимание, что в некоторых ситуациях это все еще не делает страницу доступной для чтения / записи. Функция static_protections()
, которая вызывается внутри set_memory_rw()
, удаляет флаг _PAGE_RW
, если:
- Это в области BIOS
- Адрес находится внутри .rodata
- CONFIG_DEBUG_RODATA установлен, а ядро настроено только для чтения
Я выяснил это после отладки, почему я все еще "не смог обработать запрос подкачки ядра" при попытке изменить адрес функций ядра. В конце концов я смог решить эту проблему, найдя запись таблицы страниц для адреса самостоятельно и вручную установив ее для записи. К счастью, функция lookup_address()
экспортирована в версии 2.6.26+. Вот код, который я написал для этого:
void set_addr_rw(unsigned long addr) {
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
}
void set_addr_ro(unsigned long addr) {
unsigned int level;
pte_t *pte = lookup_address(addr, &level);
pte->pte = pte->pte &~_PAGE_RW;
}
Наконец, хотя ответ Марка технически верен, при запуске внутри Xen возникнет проблема. Если вы хотите отключить защиту от записи, используйте функции чтения / записи cr0. Я макрос их так:
#define GPF_DISABLE write_cr0(read_cr0() & (~ 0x10000))
#define GPF_ENABLE write_cr0(read_cr0() | 0x10000)
Надеюсь, это поможет любому, кто наткнется на этот вопрос.