как избежать кеширования при записи в регистры mmio?

Я пишу пользовательскую ОС в виртуальном боксе, и у меня возникают проблемы с записью и чтением из регистров IOAPIC mmio. т. е. кажется, что запись индексного регистра игнорируется. После загрузки R8 с базовым адресом IOAPIC (определенным из перечисления ACPI как 0xFEC00000) я использую следующие процедуры для чтения/записи:

; -----------------------------------------------------------------------------
; IN :  RAX = ioapic address, EBX = index register
; OUT:  ECX = return value
ioapic_read:
    mov [r8], ebx
    mov ecx, [r8 + 0x10]
    ret
; -----------------------------------------------------------------------------
; IN :  RAX = ioapic address, EBX = index register, ECX = value
; OUT:  -
ioapic_write:
    mov [r8], ebx
    mov [r8 + 0x10], ecx
    ret        

Но ioapic_read всегда будет возвращать последнее записанное значение (с помощью ioapic_write) независимо от используемого индекса. У меня есть настройка пейджинга для использования 0x9B, что, я думаю, должно отключить кеширование.

Я пытался использовать pause после каждого из mov. Не помогло. Пробовал mfences между movs. Не помогло.

Я подтвердил, что адрес 0xFEC00000 успешно сопоставлен.

Похоже, кэширование все еще происходит. Что мне не хватает?

РЕДАКТИРОВАТЬ

Я обнаружил, что это не проблема кэширования, а что-то очень странное - по крайней мере, для моего невежественного мозга. Моя подкачка удостоверений работает по запросу, так что ошибка страницы создает правильную физическую страницу в таблицах.

Кажется, это работает, но в случае регистров IOAPIC mmio мне нужно вызвать ошибку страницы, выполнив фиктивное чтение или запись по адресу 0xFEC00000, прежде чем пытаться его использовать. Еще более странно то, что мне нужно сделать этот манекен, предварительно прочитав достаточно инструкций, иначе он не сработает. например

Это работает!

 mov eax, [os_IOAPICAddress]
 mov dword[rax], 0
 mov r8, rax
 .
 .
 .
 call ioapic_read

... НЕТ!

 mov eax, [os_IOAPICAddress]
 mov r8, rax
 mov dword[rax], 0
 .
 .
 .
 call ioapic_read

Я подозреваю проблему с конвейерной/сериализацией, но мне бы очень хотелось узнать, почему мне нужно загружать адрес в таблицы, прежде чем использовать его в регистре MMIO, и почему мне нужно сделать это достаточно заранее. В последнем случае, как это исправить, чтобы он был сериализован, чтобы мне не нужно было об этом беспокоиться.

Моя процедура подкачки личности:

pageFault_identity_0x0E:
    pop r8
    push rsi rdi rax rcx rdx r9

    test r8, 1
    jnz exception_gate_14
    mov rdx, cr2                                   ; faulting address
    shr rdx, 39
    and rdx, 0x1FF                                 ; get 9 bit index      

    mov rdi, cr3
    lea rsi, [rdi + rdx*8]
    mov rdi, [rsi]
    test rdi, 1
    jnz @f
    call set_new_page_table                                               
@@:
    shr rdi, 12                                     ; get rid of flags
    shl rdi, 12

    mov rdx, cr2
    shr rdx, 30                                     ; get 9 bit index    
    and rdx, 0x1FF

    lea rsi, [rdi + rdx*8]
    mov rdi, [rsi]
    test rdi, 1
    jnz @f
    call set_new_page_table                                               
@@:
    shr rdi, 12                                     ; get rid of flags
    shl rdi, 12

    mov rdx, cr2
    shr rdx, 21
    mov rax, rdx
    and rdx, 0x1FF                                  ; get 9 bit index    
    lea rsi, [rdi + rdx*8]

    shl rax, 21
    or rax, 0x83
    mov [rsi], rax

    shr rax, 21
    shl rax, 21

    pop r9 rdx rcx rax rdi rsi
    iretq
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;
; IN:   rsi = address of blank entry
; OUT:  rdi = base address of new table, changes rax & rcx
;
set_new_page_table:                                ; make table, get it, zero it, insert base into previous table
    movzx rdi, [page_table_count]
    shl rdi, 12
    add rdi, NEW_PAGE_TABLES

    CLEAR_BLOCK rdi, 0x200                     ; clears 4096 bytes in rdi, returns rdi + 4096

    sub rdi, 0x1000
    lea rax, [rdi + 0x3]                              ; table base address
    mov [rsi], rax
    inc [page_table_count]
    ret

person poby    schedule 02.10.2016    source источник
comment
Эти функции ioapic_read и ioapic_write вызываются из C?   -  person Michael Petch    schedule 03.10.2016
comment
Нет, все в сборе   -  person poby    schedule 03.10.2016
comment
Область памяти помечена как некэшируемая? stackoverflow.com/questions/90204/   -  person stark    schedule 03.10.2016
comment
Он находится в таблице страниц. Но есть ли какое-то другое место, где мне нужно установить регион как некэшируемый?   -  person poby    schedule 03.10.2016
comment
@ Поби, ты не должен. ЦП использует наиболее строгий набор кэширования для доступной области памяти (и, похоже, вы использовали UC- в PTE). Тем не менее, вы должны исключить любую проблему с кешем, попробуйте полностью отключить кеширование (установив CD и NW для CR0 и очистив кеш) или попробуйте использовать переменную MTRRs для сопоставления IOAPIC MMIO как UC-.   -  person Margaret Bloom    schedule 03.10.2016
comment
Вы действительно убедились, что r8 имеет то значение, которое, по вашему мнению, имеет значение? Вы случайно не прошли через отладчик? Я заметил в коде, который вы не разместили здесь, что у вас есть инструкция mov r8, [IOAPICAddress] . Как вы определили IOAPICAddress?   -  person Michael Petch    schedule 03.10.2016
comment
@Margaret: Пробовал настраивать CD и NW, не помогло. Итак, если это не проблема кэширования, что еще может быть?   -  person poby    schedule 03.10.2016
comment
@MichaelPetch: я полностью подтвердил, что R8 содержит то, что должен. IOAPICADDRESS содержит 0xFEC00000, который я извлек из таблиц ACPI.   -  person poby    schedule 03.10.2016
comment
Если вы читаете регистры перед их записью, вы получаете разумные значения? Большинство регистров IOAPIC полностью доступны для записи. Пробовали ли вы писать регистры RO, такие как IOAPICVER или IOAPICARB?   -  person Margaret Bloom    schedule 03.10.2016
comment
Кажется, не имеет значения, какое значение я выбираю для индекса. Если я не напишу сначала, я верну 0x8E000008. После того, как я напишу, я верну все, что написал, независимо от используемого индексного регистра.   -  person poby    schedule 03.10.2016
comment
Хорошо, этот 0x8E000008 выглядит подозрительно знакомым. Я почти уверен, что теперь моя идентификационная страница - кактус (ошибка).   -  person poby    schedule 03.10.2016
comment
Находится ли 0x8E000008 в середине вашего IDT?   -  person Michael Petch    schedule 03.10.2016
comment
Да, это так! Однако таблицы страниц работают... см. редактирование вопроса.   -  person poby    schedule 03.10.2016
comment
Делаете ли вы страницу недействительной (сбрасываете для нее записи TLB) при изменении битов таблицы страниц в обработчике ошибок страниц?   -  person Michael Petch    schedule 03.10.2016
comment
Я пробовал это, но это не имело никакого значения. Это необходимо? Я думал, что сбрасывать записи TLB не нужно, если страницы все равно нет в таблице.   -  person poby    schedule 03.10.2016
comment
Я не знаю, как работает ваш обработчик ошибок страниц и отображение памяти, поэтому я просто спросил.   -  person Michael Petch    schedule 03.10.2016
comment
Насколько я помню, вы можете практически изменить все биты в записи таблицы страниц, пока текущий флаг остается сброшенным (0). Но если вы меняете текущий бит (от 1 до 0), я считаю, что вам нужно сбросить записи TLB, связанные с этой страницей. Если текущий бит уже установлен в записи, вы можете изменить доступный бит, не беспокоясь о сбросе записи TLB. Но если текущий бит уже включен, и вы меняете любой из битов, кроме доступного бита, вам необходимо сбросить записи TLB.   -  person Michael Petch    schedule 03.10.2016
comment
@RossRidge Я вернулся, чтобы пересмотреть свой комментарий здесь, и я вижу, что вы сделали комментарий относительно той же ошибки. Ты совершенно прав.   -  person Michael Petch    schedule 03.10.2016
comment
Я не знаю, кажется, может происходить кеширование. В качестве эксперимента просто в ioapic_read между двумя инструкциями перемещения поместите инструкцию sfence.   -  person Michael Petch    schedule 04.10.2016
comment
Вы сказали в своем вопросе 0x9B, что, я думаю, должно отключить кеширование . Я предполагаю, что вы на самом деле используете пейджинг 4 МБ? Если этот 0x9B используется в качестве флагов для записи в таблице страниц, то я думаю, что у нас проблема в Хьюстоне. Если бы это было для записи каталога страниц, это имело бы некоторый смысл, но это означало бы, что вы используете пейджинг › 4kb.   -  person Michael Petch    schedule 04.10.2016
comment
@MichaelPetch: я использую страницы размером 2 МБ в длинном режиме. 0x9B предназначен для записи каталога страниц. Сначала я пытался использовать sfence, как вы предложили, но это не имело никакого значения.   -  person poby    schedule 04.10.2016
comment
Я знал, что у вас либо ошибка, либо вы используете что-то большее, чем 4 КБ. В вашем вопросе не было сказано, что он просто сказал 0x9b без реального контекста.   -  person Michael Petch    schedule 04.10.2016
comment
Я не уверен, что вы получите ответ, кроме людей, предлагающих способы отследить проблему. Это не минимальный полный проверяемый пример. Что произойдет, если вы сделаете тот же код с отключенным кэшированием, как предлагала Маргарет гораздо раньше? Если бы вы предоставили свой полный код, я уверен, что кто-то мог бы понять это. Могут быть проблемы с вашими таблицами страниц, вашим обработчиком ошибок, кэшированием в целом или чем-то еще.   -  person Michael Petch    schedule 05.10.2016
comment
Кэширование отключено. Это подтверждено.   -  person poby    schedule 05.10.2016
comment
Глядя на обработчик ошибок вашей страницы. Я ошибаюсь, но вы уничтожаете содержимое R8, не восстанавливая его? Вы вставляете код ошибки в R8, но это означает, что предыдущее значение R8 было уничтожено. Когда ваш обработчик ошибок завершает работу, R8 скорее всего больше не содержит исходного значения.   -  person Michael Petch    schedule 05.10.2016
comment
СПАСИБО! Вот оно! Я не знаю, как я это пропустил, просматривал этот код сотни раз. Такая глупая маленькая ошибка причинила мне столько горя! Пожалуйста, напишите это как ответ, чтобы я мог с радостью наградить вас баллами   -  person poby    schedule 05.10.2016


Ответы (2)


Учитывая исходный код, это выглядело так, как будто вы правильно устанавливали биты записи каталога страниц, чтобы пометить область MMIO как некэшируемую. Я был уверен, что проблема в другом. С вашим последующим редактированием вы показали нам свой обработчик ошибок страницы pageFault_identity_0x0:

pageFault_identity_0x0E:
    pop r8
    push rsi rdi rax rcx rdx r9

Когда процессор передает управление этому обработчику исключений сбоя страницы, он передает в качестве параметра код ошибки на вершине стека. Проблема в том, что вы заменяете содержимое R8 номером ошибки без сохранения и последующего восстановления реестра.

Вам придется изменить обработчик исключений, чтобы сохранить R8, переместить содержимое из правильного смещения стека, где находится номер ошибки, в R8. Просто не забудьте убедиться, что номер ошибки больше не находится на вершине стека до IRETQ.

Вероятно, странное поведение, с которым вы столкнулись, напрямую связано с тем, что R8 не восстанавливается должным образом после возврата из-за сбоя страницы.


Решение, которое может сработать:

pageFault_identity_0x0E:
    push rsi
    push rdi
    push rax
    push rcx
    push rdx
    push r9
    push r8

    mov r8, [rsp+7*8]    ; Error Code is at offset RSP+7*8 after all the pushes
    ; Do exception handling work here

    pop r8
    pop r9
    pop rdx
    pop rcx
    pop rax
    pop rdi
    pop rsi

    add rsp, 8           ; Remove the error code
    iretq
person Michael Petch    schedule 05.10.2016
comment
Я потратил 2 дня своей жизни, пытаясь найти это. Я так тщательно отладил эту подпрограмму подкачки, что был уверен, что она не виновата. Но как-то я пропустил тот поп R8. Это нагрузка на мой взгляд :) - person poby; 05.10.2016
comment
Не проблема, я очень рад, что вы добавили код обработчика ошибок страницы. У меня было подозрение, что дело в нем. Рад, что это работает для вас сейчас. Разработка и отладка ядра могут быть сложным делом. Возможно, вам просто нужен был свежий взгляд, чтобы взглянуть на это. - person Michael Petch; 05.10.2016

Майкл решил это, но в интересах полноты я опубликую свою окончательную реализацию.

pageFault_identity_0x0E:
    test qword[rsp], 1
    jnz exception_gate_14
    add rsp, 8

    push rsi rdi rax rcx rdx
    mov rdx, cr2                                   ; faulting address
    .
    .
    .
    pop rdx rcx rax rdi rsi
    iretq

РЕДАКТИРОВАТЬ: отредактировали, чтобы удалить файл xchg.

person poby    schedule 11.10.2016
comment
Я избегал использования XCHG с операндом в памяти главным образом потому, что это приведет к неявной блокировке, которая приведет к монопольному владению соответствующей строкой кэша и снижению производительности. XCHG с двумя регистрами не имеет этой проблемы. В руководстве по оптимизации Intel это предложение сводит к минимуму использование инструкций xchg в ячейках памяти. - person Michael Petch; 11.10.2016
comment
Ваше здоровье. Я этого не знал. Исправили. - person poby; 12.10.2016