Cliente de IRC embutido na placa-mãe
(axleos.com)- UEFIRC é um cliente gráfico de IRC que roda no ambiente de pré-boot UEFI do firmware da placa-mãe antes de o sistema operacional iniciar, mostrando que mesmo em um ambiente voltado a bootloaders é possível implementar uma UI e recursos de rede próximos aos de um app comum
- A implementação aproveita os drivers de NIC e a pilha TCP que a UEFI oferece para boot via rede, e o backend de rede
vmnetpara QEMU viabiliza o desenvolvimento - O ponto mais complicado foi lidar com o protocolo TCP da UEFI em Rust, com estado global, callbacks reentrantes, buffers scatter-gather e uma mistura complexa de eventos, tokens, handles e protocolos
- A GUI é uma adaptação para UEFI do toolkit gráfico em Rust e do renderizador TrueType do axle, e também houve melhorias no libgui para entrada de mouse, barra de rolagem e renderização de texto em scroll view
- O resultado final está mais para um projeto de piada sofisticado do que para um cliente de IRC prático, mas serve como ferramenta para reclamar da pilha TCP/IP da UEFI de dentro da própria UEFI
O que o UEFIRC faz
- UEFIRC é um cliente gráfico de IRC que roda em UEFI
- Foi escrito em Rust e usa o toolkit de GUI e o renderizador TrueType criados para o espaço de usuário do axle
- É possível conectar a um servidor IRC, conversar e ler mensagens
- No desenvolvimento foi usado o backend de rede
vmnetpara QEMU
A UEFI como ambiente de execução
- O bootloader do sistema operacional é carregado com a ajuda do firmware armazenado na ROM da placa-mãe
- No passado, a BIOS tinha várias limitações, e o padrão UEFI foi criado para substituí-la
- A BIOS exigia que o bootloader começasse em modo 16-bit
- Havia também a exigência de que o loader do primeiro estágio coubesse em 512 bytes
- A UEFI coloca o bootloader desde o início em um ambiente 64-bit e fornece APIs para troca de resolução de vídeo via VESA, alocação de memória e acesso ao sistema de arquivos EFI
- É um grande avanço em relação à BIOS, mas também é vista por alguns como excessivamente projetada
Reaproveitando o boot por rede para IRC
- Alguns bootloaders podem carregar o sistema operacional pela rede em vez de usar um dispositivo de bloco local
- Para dar suporte a esse caso de uso, o firmware UEFI precisa incluir uma pilha de rede
- Driver de NIC
- Implementação de TCP
- API para que aplicações executadas no ambiente de pré-boot acessem essa pilha
- Como um bootloader não precisa necessariamente carregar um sistema operacional, também é possível rodar um cliente de IRC nesse mesmo ambiente
A dificuldade de lidar com TCP da UEFI em Rust
- A parte mais difícil do projeto foi implementar em Rust um cliente do protocolo TCP da UEFI
- O protocolo TCP da UEFI exige tempos de vida de dados e interações difíceis de descrever em Rust
- Estado global
- Callbacks reentrantes
- Buffers scatter-gather
- Eventos, tokens, handles e protocolos
- O código em Rust foi testado por vários dias para eliminar vazamentos de memória e use-after-free nos buffers de recepção TCP
A confusão entre NOTIFY_SIGNAL e NOTIFY_WAIT
- Só pelos nomes, a API de eventos da UEFI já é difícil de prever como funciona
- Ao especificar
NOTIFY_SIGNAL, o callback é chamado quando o evento ocorre, e usarwait()vira erro - Ao especificar
NOTIFY_WAITe chamarwait(), a UEFI pode chamar o callback várias vezes antes de o evento ocorrer, e quando ele ocorre owait()é liberado - Os dois modos dão ao mesmo callback significados completamente diferentes
NOTIFY_SIGNAL: o evento ocorreu, então é hora de fazer a próxima etapaNOTIFY_WAIT: o evento ainda não ocorreu, então é hora de estimular o progresso
- Para fazer buffering assíncrono dos dados de pacotes recebidos, no fim foi usado um loop com
NOTIFY_WAITjunto com um timer de timeout curto
Suporte a mouse e cursor
- Um mouse não é essencial para um cliente de IRC, mas faz o app parecer mais interativo
- O Simple Pointer Protocol da UEFI foi usado para ler movimento do mouse e entrada de botões, além de fornecer feedback de posição do cursor na GUI
- O Simple Pointer Protocol não oferece suporte à roda de rolagem
- No UEFIRC, é preciso usar as setas do teclado ou arrastar a barra de rolagem com o cursor
- No firmware UEFI OVMF padrão não era possível obter eventos de mouse, então foi compilado um firmware UEFI customizado com os drivers e protocolos necessários, como
UsbMouseDxe - Para permitir testar o UEFIRC no QEMU, esse firmware UEFI também foi enviado para as releases
Escalonamento do movimento do mouse
- O driver do mouse informa variações de posição, não coordenadas absolutas
- Somar linearmente
delta_xedelta_ydá uma sensação de resposta lenta - Sistemas operacionais usam um tipo de escalonamento mais próximo de permitir ao mesmo tempo movimentos rápidos e ajustes finos
- No exemplo de implementação, o deslocamento do cursor é ampliado multiplicando pela aplicação de
log2()sobre a soma dos valores absolutos do movimento - Um cursor com movimento linear tende a passar a sensação de que todo o ambiente é lento e pouco responsivo
Modelando mensagens IRC
- A modelagem das mensagens IRC foi relativamente simples e agradável
- O IRC usa um formato de linhas baseado em texto, o que facilita o parsing
- Ainda assim, décadas de extensões deixaram uma certa complexidade, com apenas parte disso padronizada
Usando libgui em UEFI
- O toolkit de GUI em Rust do axle já havia sido bastante trabalhado para poder ser usado fora do contexto do axle, então executá-lo em UEFI não foi especialmente difícil
- O trabalho principal foi fornecer uma implementação de
AwmWindowque pudesse ser usada dentro da UEFI - Depois disso, foi possível aproveitar diretamente vários recursos do libgui
- Gerenciamento de eventos
- Renderização de fontes
- Composição de camadas
- Decoração de views
- Componentes mais complexos, como scroll view
Barra de rolagem e renderização de texto em scroll view
- O libgui em C do axle já tinha funcionalidade de barra de rolagem, mas a versão em Rust ainda não tinha alguns recursos
- Como a principal interação no UEFIRC acontece em uma scroll view cheia de texto, a funcionalidade de barra de rolagem foi reimplementada no libgui em Rust
- Uma scroll view tem custo de renderização em pixels maior do que uma view de tamanho fixo
- Em uma view de tamanho fixo, basta pensar em um buffer RGB de tamanho
width * height - Em uma scroll view, é preciso lidar com uma tela potencialmente infinita
- Em uma view de tamanho fixo, basta pensar em um buffer RGB de tamanho
- O toolkit de GUI em Rust do axle trata a scroll view de forma baseada em tiles
- Cada tile é um buffer de pixels quadrado com algumas centenas de pixels de largura
- Só são alocados os tiles necessários para a área em que o conteúdo realmente será renderizado
- Os tiles visíveis são calculados e então costurados na imagem final
- Quando o renderizador TrueType chama
putpixel()para cada pixel de um glifo, a scroll view não sabe antecipadamente toda a área de renderização, o que é ineficiente - Para resolver isso, a polygon stack passou a incluir primitivas básicas de desenho como linhas, círculos e retângulos
- Assim, a scroll view consegue saber de antemão que um polígono grande será desenhado e alocar os tiles necessários
- Ter preenchimento arbitrário de polígonos como primitiva básica não agrada muito, mas na prática funciona bem
Melhorias no libgui surgidas ao criar o UEFIRC
- Sempre que se cria uma nova aplicação gráfica, pequenas limitações e incômodos do toolkit de GUI, do IPC, das interfaces de driver e dos recursos do kernel acabam aparecendo
- Durante o desenvolvimento do UEFIRC, várias mudanças foram feitas no libgui
- Evento de liberação do clique esquerdo
- Suporte a redimensionamento dinâmico de
Label Labelrenderizando fontes TrueType em vez das fontes bitmap anterioresButtonrenderizando fontes TrueType em vez das fontes bitmap anteriores- O renderizador TrueType passou a fornecer mais informações de layout ao renderizar glifos
- O parser de TrueType passou a modelar e expor mais métricas de fonte
- Correções de bugs sutis de layout e espaçamento no renderizador TrueType
TextInputViewpassou a oferecer suporte a callbacks de entrada de teclado fornecidos pelo usuário
Um resultado totalmente desnecessário
- O cliente de IRC em si é mais um projeto de piada sofisticado do que algo realmente útil no dia a dia
- Mas ele pode servir como ferramenta para reclamar da pilha TCP/IP da UEFI quando ela estiver te irritando
- Por fim, houve até um acesso ao canal IRC
#edk2de desenvolvimento da UEFI, diretamente de dentro da própria UEFI, para deixar um cumprimento
1 comentários
Opiniões no Hacker News
Fiz, de brincadeira, um cliente IRC gráfico que roda apenas no ambiente pré-boot UEFI, e ainda coloquei recursos exagerados como fontes TrueType, cursor e decorações de GUI
Originalmente era um projeto para tentar fazer algo rápido e leve, depois de ficar cansado de um receptor GPS feito do zero, mas, como sempre, levou muito mais tempo do que o esperado
Também passei bastante tempo nas visualizações do artigo que mostram como modelei a visualização rolável e a renderizei em um viewport estático; espero que vocês gostem
No começo, com a ideia de “enfiar no UEFI algo que não deveria estar lá”, pensei em um cliente do Twitter, mas alguém já tinha feito um muito bom usando o protocolo HTTP do UEFI, então decidi evitar HTTP
Por isso escolhi IRC, que roda sobre TCP e também tem um ar de mídia social que não combina nada com um ambiente pré-boot
Claro que é brincadeira. Até certo ponto
Sou minimalista, então nem preciso de GUI ou mouse, e o UEFI já parece ter mais do que eu preciso
O cliente de Twitter mencionado está aqui: https://github.com/arata-nvm/mitnal
Parece que poderia ser uma forma bem interessante, talvez segura, de permitir a recuperação automática de sistemas remotos cuja instalação quebrou completamente e não consegue inicializar normalmente
Muito bom. Também mostra bem que, por baixo do sistema em que a maioria pensa, há um software mais complexo e poderoso do que se imagina
As pessoas costumam achar, equivocadamente, que o sistema operacional é a “camada mais baixa” da pilha de software, mas na verdade existe código com cara de firmware que é quem realmente possui o sistema
Às vezes ele desaparece depois de cumprir sua função; outras vezes permanece durante todo o tempo em que o sistema está ligado, de um modo que até o sistema operacional percebe como transparente
Existe a atitude de que “é só código de baixo nível para acionar dispositivos, nada sério vai acontecer ali”, mas, se dá para colocar até um cliente IRC lá embaixo, fica fácil imaginar outras coisas maliciosas
“Por quê?” Que tipo de pergunta é “por quê”, afinal? Venho ao HN para ver esse espírito
“Veio a percepção mais assustadora. Não havia motivo algum para o que eu tinha feito. Eu sabia por que tinha feito. Fiz porque parecia que seria divertido. Mas eles perguntariam ‘por que diabos você fez isso?’ e, se eu não tivesse uma razão suficientemente plausível, parecia que iam me enfiar num hospital psiquiátrico.” — Boyd Rice
Não precisa se subestimar. Aqui temos um projeto de cliente de comando e controle de botnet
A UI é meio engraçada
Muito legal. Eu não sabia que a API do UEFI era tão acessível e bem documentada assim
Fiquei curioso sobre como foi o ciclo de desenvolvimento. Imagino que tenha rodado em uma VM, mas queria saber se era preciso “dar boot” toda vez que fosse executar o cliente
O script principal de execução recriava um sistema de arquivos EFI contendo a nova build do UEFIRC e o passava para o QEMU
Mas, ao desenvolver a GUI, esse overhead ficou bem inconveniente, então configurei o app para ser compilado tanto para UEFI puro quanto para um ambiente host que roda no Mac
Ao mudar uma flag de build, o toolkit de GUI desenha diretamente no framebuffer fornecido pelo UEFI ou se conecta ao sistema de janelas do Mac para trocar eventos
O overhead dessa abordagem de dois alvos também pode ser visto no ponto de entrada: https://github.com/codyd51/uefirc/blob/main/src/main.rs
O parsing de mensagens IRC não exigia nenhum enfeite, então foi desenvolvido como um conjunto de testes unitários que rodam diretamente no Mac; alguns estão aqui: https://github.com/codyd51/uefirc/blob/main/src/irc/response...
Um dia quero terminar de escrever o sistema operacional para o meu bot de IRC que ainda está rodando
Talvez seja a coisa mais inútil a dizer, mas movimento não linear do mouse, ou seja, aceleração, é a primeira configuração que desligo quando inicializo um sistema operacional novo. Estranhamente, minha mão realmente dói
Por exemplo, no Mac existe o linearmouse, que é gratuito; no Windows basta desligar a aceleração. No Linux, claro, é fácil
Com aceleração do mouse, fica difícil aprender a sensação de mapear a distância que o mouse se move para a distância percorrida na tela, e acho que, no longo prazo, usar sem aceleração é mais eficiente
Aprendi isso com gamers, e acho que eles ainda têm bons motivos para agir assim
Acho que a sensação acaba ficando familiar de qualquer forma. Assim como o pedal do acelerador de um carro normalmente não é mapeado diretamente para a velocidade
Se a pergunta é “por quê?”, é porque, quando o UEFI foi apresentado, esse tipo de aplicação de baixo nível foi prometido
Quem criou o UEFI também sonhava em substituir até aqueles mini sistemas operacionais só para internet, baseados em Linux, que alguns fornecedores permitiam acessar durante o boot pressionando uma tecla específica. Não lembro o nome
Vi um vídeo no YouTube que explorava isso a fundo; pelo que lembro, no começo era um Linux reduzido ou outro sistema operacional customizado, depois migrou para apps UEFI e acabou saindo de moda
Bom artigo. Lembrou-me a pegadinha de 1º de abril do bootloader barebox de dois anos atrás. Se todos os outros destinos de boot falhassem, ele conectava ao #barebox[1]
O foco deles era adicionar suporte a TCP ao barebox, sem elementos de GUI legais como aqui
A interface era só linha de comando e, se o barebox fosse compilado como payload EFI, ele conseguia desenhar sobre o EFI GOP
[1]: https://lore.barebox.org/barebox/20220401145902.GF4351@telli...
Pensei imediatamente em um vídeo recente do Cathode Ray Dude. Ele tratava do QuickLook da HP, um “cliente de e-mail” que na prática era um plugin do Outlook, e que também foi implementado e lançado como produto desse jeito: https://www.youtube.com/watch?v=ssob-7sGVWs
O vídeo também mostra outras coisas ainda mais estranhas que a HP fez. Só que este projeto conseguiu fazer até a parte difícil que o QuickLook evitava: rede
As visualizações do artigo são surpreendentemente boas e impressionantes