Задать вопрос

Не получается релоцировать local APIC?

Уже несколько дней безуспешно пытаюсь изменить адрес local APIC с начального 0xFEE00000 на нужный мне 0x105000. После записи в APIC BASE MSR (0x1B) адрес изменяется, но регистры APIC не проецируются (соотв. память обнулена вместо того, чтобы содержать начальные значания, описанные в спецификации). Поэтому не получается активировать прерывания по таймеру. Думал, дело в кешировании (ведь в спецификациях указанно, что странница с проецированными регистрами local APIC должна быть помечена как strong uncachable), выставлял для неё pwt = pcd = 1, но это не помогло. Может, я не учитываю специфику x86-64?

проблемный код.
#include "apic.h"
#include "display.h"
#include "mem_map.h"
#include "interrupt.h"
#include "util.h"

#define APIC_BASE_MSR 0x1B

#define APIC_SPURIOUS_REG 0x0F0
#define APIC_TIMER_REG 0x320
#define APIC_TIMER_INITIAL_REG 0x380
#define APIC_TIMER_CURRENT_REG 0x390
#define APIC_TIMER_DIVIDE_REG 0x3E0

#define APIC_ENABLE (1 << 11)
#define APIC_LOCAL_ENABLE (1 << 8)
#define APIC_TIMER_MASKED (1 << 16)
#define APIC_TIMER_PERIODIC (1 << 17)

#define APIC_TIMER_DIVIDE_BY_1 0b1011
#define APIC_TIMER_DIVIDE_BY_2 0b0000
#define APIC_TIMER_DIVIDE_BY_4 0b0001
#define APIC_TIMER_DIVIDE_BY_8 0b0010
#define APIC_TIMER_DIVIDE_BY_16 0b0011
#define APIC_TIMER_DIVIDE_BY_32 0b1000
#define APIC_TIMER_DIVIDE_BY_64 0b1001
#define APIC_TIMER_DIVIDE_BY_128 0b1010

ISR_DEFINE(spurious, 0) {
  printf("#SPURIOUS\n");
}

ISR_DEFINE(timer, 0) {
  printf("#TIMER\n");
}

static inline void reg_write(uint64_t reg, uint64_t value) {
  printf("reg %X (prev: %X, curr: %X)\n",
         reg, *(uint64_t*)(APIC_BASE_ADDR + reg), value);
  *(uint64_t*)(APIC_BASE_ADDR + reg) = value;
}

void init_apic(void) {
  uint64_t prev = rdmsr(APIC_BASE_MSR);
  wrmsr(APIC_BASE_MSR, APIC_BASE_ADDR | APIC_ENABLE);
  printf("msr %X (prev: %X, curr: %X)\n",
         APIC_BASE_MSR, prev, rdmsr(APIC_BASE_MSR));

  reg_write(0x30, 0);

  set_isr(INT_VECTOR_SPURIOUS, spurious_isr_getter());
  reg_write(APIC_SPURIOUS_REG, APIC_LOCAL_ENABLE | INT_VECTOR_SPURIOUS);

  set_isr(INT_VECTOR_TIMER, timer_isr_getter());
  reg_write(APIC_TIMER_REG, APIC_TIMER_PERIODIC | INT_VECTOR_TIMER);
  reg_write(APIC_TIMER_INITIAL_REG, 12345);
  reg_write(APIC_TIMER_DIVIDE_REG, APIC_TIMER_DIVIDE_BY_4);
}




Полный код лежит здесь. Буду признателен, если укажете, где я ошибаюсь. Спасибо.
  • Вопрос задан
  • 3695 просмотров
Подписаться 2 Оценить Комментировать
Решения вопроса 1
ababo
@ababo Автор вопроса
Причина была банальной — загрузчик не активировал A20 gate. Нашёл причину случайно, заметив, что второй мегабайт физической памяти фактически совпадает с первым.

Теперь не знаю, что делать с qemu/kvm, ведь они действительно не поддерживают релокацию локального APIC.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
ntkt
@ntkt
Потомственный рыцарь клавиатуры и паяльника
В интеловских мануалах пишут:
Table 9-1 shows how the APIC registers are mapped into the 4-KByte APIC register
space. Registers are 32 bits, 64 bits, or 256 bits in width; all are aligned on 128-bit
boundaries. All 32-bit registers should be accessed using 128-bit aligned 32-bit loads
or stores.


У Вас в коде я этого не увидел.
Ответ написан
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
Я вижу два странных момента.
Один у вас: доступ к 32-битным регистрам APIC не может выполняться как к 64-битной памяти. Об этом говорит intel developer's manual vol3, p 10.4.1:
All 32-bit registers should be accessed using 128-bit aligned 32-bit loads or stores.


Второй — в qemu. Там (смотрел текущую голову git) нет кода, который бы перемещал регистры при изменении базового адреса APIC.
На чём вы тестируете своё ядро?
Ответ написан
ababo
@ababo Автор вопроса
перенёс
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы