Pular para o conteúdo

Real Time Clock (RTC/CMOS)

O Real Time Clock (RTC), frequentemente chamado de CMOS a partir da tecnologia de memória de baixo consumo com a qual seu banco de registradores compartilha chip, é o pedaço de hardware que guarda data e hora de calendário através de um reboot ou um ciclo completo de energia, apoiado por bateria especificamente para continuar rodando quando tudo mais na placa está desligado. Nenhum dos contadores que Timers já cobre, o PIT, o timer do Local APIC, o TSC, ou o HPET, resolve esse problema: cada um deles mede tempo decorrido a partir de um ponto de partida arbitrário que reseta para zero a cada boot, enquanto o RTC é especificamente o que um kernel lê uma vez para descobrir quais são a data e hora de relógio de parede reais agora mesmo.

Os registradores do RTC são alcançados indiretamente através de um par de portas de I/O em vez de serem endereçados individualmente: 0x70 é a porta de índice, selecionando a qual registrador o próximo acesso se refere, e 0x71 é a porta de dado, através da qual esse registrador selecionado é de fato lido ou escrito.

mov al, 0x00 ; seleciona o registrador 0 (segundos)
out 0x70, al
in al, 0x71 ; AL agora contém o valor de segundos

Os registradores 0 a 9 contêm os campos reais de hora e data, segundos, minutos, horas, dia da semana, dia do mês, mês, e um ano de dois dígitos, cada um em seu próprio byte; uma leitura completa de data precisa ler todos eles na mesma passada em vez de um de cada vez espalhados por chamadas separadas, já que o relógio continua avançando entre leituras e um valor de segundos lido antes da meia-noite combinado com uma leitura de data depois dela produziria silenciosamente um resultado errado e autoinconsistente.

Por padrão, e por compatibilidade retroativa com décadas de software escrito contra ele, os registradores de hora e data do RTC são codificados em binary-coded decimal: todo byte contém dois dígitos decimais, um empacotado em cada nibble, em vez da representação binária comum do valor, então o valor de segundos 42 é armazenado como o byte 0x42, não 0x2A. O Status Register B, no índice 0x0B, controla isso diretamente: o bit 2 limpo (o padrão) significa BCD, o bit 2 definido significa que os registradores em vez disso contêm valores binários comuns, um modo que algum firmware suporta e outro não, motivo pelo qual um driver lendo um bit 2 limpo precisa converter explicitamente em vez de presumir que o valor lido já é utilizável como um inteiro comum.

uint8_t bcd_to_binary(uint8_t bcd) {
return (bcd & 0x0F) + ((bcd >> 4) * 10);
}

O Registrador B também carrega a seleção de 12 ou 24 horas (bit 1) e, quando o modo de 12 horas está ativo, empacota o indicador AM/PM no próprio bit mais alto do byte de hora em vez de como um campo separado, um detalhe fácil de perder e que produz um valor de hora que parece plausível mas está silenciosamente errado em doze se um driver presumir formato de 24 horas sem de fato checar.

Os registradores do RTC são atualizados a partir de seu contador interno aproximadamente uma vez por segundo, e ler um registrador bem no meio dessa atualização pode retornar um valor transitório e inconsistente: nem a hora de antes, nem de forma limpa a hora de depois. O Status Register A, no índice 0x0A, expõe uma flag de Update In Progress no bit 7 exatamente para que o software consiga detectar essa janela e evitar ler durante ela: um driver sonda esse bit e só prossegue com a leitura depois que ele se limpa, ou, de forma mais robusta, lê a data completa duas vezes seguidas e descarta o resultado se as duas leituras não baterem, já que checar o bit uma única vez ainda deixa uma janela estreita entre a checagem e a leitura em si.

void wait_for_update_complete(void) {
outb(0x70, 0x0A);
while (inb(0x71) & 0x80) {
outb(0x70, 0x0A);
}
}

Um kernel tipicamente lê o RTC exatamente uma vez, cedo no boot, para estabelecer uma hora inicial de relógio de parede, e a partir desse ponto avança seu próprio relógio de software usando qualquer timer do qual já depende para escalonamento, em vez de sondar repetidamente o próprio RTC a cada consulta de hora subsequente. Essa divisão existe porque a resolução de um segundo do RTC e seu padrão de acesso via I/O de porta são ambos grosseiros e lentos demais para consultas frequentes, enquanto um timer já disparando numa granularidade muito mais fina para propósitos de escalonamento consegue rastrear tempo decorrido em subsegundos de forma bem mais barata, uma vez que tem um ponto de partida real a partir do qual contar.

Alguns sistemas adicionalmente expõem hora de calendário através dos campos relacionados a RTC da tabela FADT do ACPI (o local de seu registrador de século, por exemplo, já que o ano de dois dígitos do próprio RTC precisa de um valor de século vindo de algum lugar para se tornar um ano completo de quatro dígitos), o que um kernel já analisando tabelas ACPI por outros motivos pode ler em vez de adivinhar o local no espaço de portas do registrador de século, já que ele não é fixo identicamente em todos os chipsets da forma que os registradores primários de hora e data são. O Registrador C, no índice 0x0C, precisa ser lido (e é limpo como efeito colateral dessa leitura) depois de qualquer interrupção gerada pelo RTC que um kernel tenha habilitado, já que o RTC de outra forma para de levantar outras interrupções desse tipo até que o status pendente no Registrador C seja reconhecido dessa forma.

  1. ^ Motorola, datasheet MC146818A Real-Time Clock Plus RAM: a especificação original do chip RTC que a maioria das implementações compatíveis com PC ainda segue.
  • Timers: os contadores de tempo decorrido que o relógio de calendário deste artigo complementa em vez de competir.
  • ACPI: os campos da FADT que alguns sistemas usam para expor detalhes do RTC, como o registrador de século, ao lado das portas fixas do CMOS.