Archive for December, 2009

Drivers de Boot no Windows

17 de December de 2009

Tenho acompanhado o trabalho do meu amigo Lesma, que em seu blog tem descrito como o processo de boot transforma um apanhado de bytes no disco rígido em um sistema operacional vivo. Pegando carona nesse tema, vou aproveitar para comentar sobre a ordem de carga dos drivers durante este processo. Com isso posso tentar responder uma pergunta frequente dos leitores: “Como fazer para que meu driver seja o primeiro a ser carregado?”. Talvez este post possa clarear um pouco as coisas neste sentido, ou não.

Eu primeiro! Eu primeiro!

Um ponto importante a ser considerado no modelo Legacy quando escrevemos um driver é o referente ao momento no qual seu driver é carregado. Isso é configurado no valor “Start” na chave do driver no registro. Quatro valores configuram o momento da carga do seu driver, sendo eles:

  • Boot (0) – Drivers são carregados durante o boot, antes mesmo sistema operacional estar completamente pronto para execução.
  • System (1) – Drivers são carregados depois dos drivers de boot, quando o Kernel já está completamente funcional.
  • Automatic (2) – Neste grupo os drivers são carregados quando os subsistemas forem carregados. Basicamente junto com os serviços de User Mode.
  • Manual (3) – Nenhuma carga automática é realizada aqui, o driver é carregado somente quando alguém, ou algum componente, solicita sua carga.
  • Disabled (4)- Mesmo que o driver seja solicitado, sua carga é negada.


“Bom, então para meu driver ser o primeiro a ser carregado ele precisa ser iniciado como boot e pronto?”

Na verdade seu driver vai disputar um lugar na fila de drivers que querem ser iniciados no boot. Vários drivers estão configurados para ser iniciados nesse momento e o seu será apenas mais um. Mesmo entre os drivers de boot, uma ordem de carga precisa ser seguida para que certos drivers possam contar com os serviços de outros drivers. Por esse motivo, drivers se separam em grupos. Um grupo de cada vez vai sendo iniciado até que todos os drivers de boot passem por esse processo.

Drivers identificam seu grupo pelo valor “Group” encontrado em sua chave de registro. Esta chave deve conter o nome do grupo ao qual o driver pertence. Os nomes de todos os grupos podem ser encontrados na chave HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder. Nela existe um valor do tipo REG_MULTI_SZ chamado “List” que contém a lista de todos os grupos existentes dispostos em sua ordem de carga.


“Tudo bem, meu driver está configurado para ser iniciado em Boot e está configurado para iniciar com o primeiro grupo de drivers. Pronto agora?”

Quase. Quando falamos em iniciar grupos de drivers, já fica sub-entendido que mais de um driver será carregado. A ordem que tais drivers são carregados dentro de cada grupo também pode ser determinada.

A chave KHEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList mantém uma série de valores, cada um com o nome de um grupo. O valor é do tipo REG_BINARY e sua interpretação é um array de conjuntos de quatro bytes. O primeiro conjunto indica quantas tags estão contidas naquele buffer binário. Os demais conjuntos são as representações numéricas de cada tag. Dessa forma, a interpretação do buffer exibido na figura abaixo nos dá a informação de que temos seis tags, sendo elas: 1, 2, 3, 4, 5 e 6.


“Mas o que é uma tag?”

Uma tag é a identificação numérica de um driver dentro de um determinado grupo. Um driver se identifica pelo valor “Tag” que podemos encontrar na chave do driver no registro.

Apesar de o exemplo nos mostrar uma ordem crescente de tags, o valor da tag não determina a ordem de carga dos drivers. A ordem é determinada por sua posição dentro do buffer binário.

Ficou com nojinho de mexer em buffers binários? Você pode utilizar o OSR Driver Loader que configura tudo isso pra você quando utilizado para instalar um driver.


Colocar sua tag como primeiro na lista de tags classifica sua ordem de carga dentro de um determinado grupo, mas ainda não é fator determinante para ter seu driver carregado antes de todos os drivers do universo. Um novo grupo sempre pode ser criado e ter sua ordem de carga configurada para antes do seu grupo.

Todas essas regras sobre grupos, tags e afins não fazem sentido nos drivers gerenciados pelo Plug-And-Play (Pnp) Manager, já que a carga de tais drivers é solicitada quando o dispositivo ao qual seu driver está relacionado é detectado pelo driver de barramento.

“Aff! Fernando, pega leve e tenta explicar isso de novo.”

Tudo bem, vamos lá. Quando você instala um driver Pnp, você o associa a um determinado dispositivo. Apenas como exemplo, digamos que esse dispositivo seja um conversor USB/Serial. Seu driver será carregado automagicamente quando seu dispositivo for detectado e será descarregado quando o dispositivo for removido.

Para que ele seja detectado, outros dispositivos precisam ser detectados antes, tais como controladora PCI, controladora USB e hub USB. Essa lista de dependência cria a pilha de dispositivos USB.

A controladora PCI, ao ser detectada, tem seu driver carregado e este enumera seus dispositivos filhos, já que PCI é um barramento. Para cada disposivito detectado, esse driver utiliza o barramento para detectar a identidade de cada dispositivo e cria um Phisical Device Object (PDO) para cada um deles. O Pnp Manager carrega o driver de cada dispositivo atachado a esse barramento. Esse driver criará o Functional Device Object (FDO) do dispositivo, dando funcionalidade a ele.

Um desses dispositivos é a controladora do barramento USB. Seguindo o ritual, o driver de barramento USB enumera seus dispositivos filhos, criando novos PDOs. Assim, os hubs USB são detectados e seu driver será carregado. Este driver criará um novo FDO para cada hub. O driver de hub USB vai enumerar seus dispositivos filhos e é nesse momento que seu dispositivo é detectado. O driver que você escreveu será carregado e o Pnp Manager irá chamar sua rotina AddDevice, que receberá o PDO que o driver de hub criou referente ao seu dispositivo.

Ufa! Tudo bem, tenham calma. O assunto Plug-And-Play não é o foco deste post e já está na minha lista de posts futuros.

Toda essa atividade que age recursivamente serve para montar a árvore de dispositivos do sistema. Sabendo que esta árvore é formada pelos nossos drivers e seus devices, fica explícito aqui que no fundo “As árveres somos nozes”. A figura abaixo dá uma idéia de como a árvore de dispositivos é organizada.


Ainda falando sobre ordem de carga de drivers, não faria sentido ter seu driver carregado antes de todos os outros drivers, já que os componentes básicos para a comunicação com seu dispositivo ainda não foram carregados, e por isso, não possuem funcionalidade nenhuma. Além do mais, ter seu driver carregado muito cedo lhe trará problemas em lidar com outros componentes do sistema que ainda não estarão prontos para atender seu pedido. Mais detalhes neste post.

Depurando no Boot

Outro assunto curioso e que pode gerar alguma confusão é o referente ao debug de drivers que são carregados no boot. Apesar de a conexão de Debug usar o meio serial, firewire ou mesmo USB, os drivers referentes a estes meios não precisam estar carregados para que você possa realizar o debug do sistema. Em outras palavras, o driver de porta serial não é utilizado para fazer debug do sistema quando se usa o meio serial. Isso seria um problema se pensarmos que alguns drivers são carregados e inciados antes do driver de porta serial. Como estes drivers seriam depurados?

O fato é que o algoritmo que lida com os meios de depuração do sistema são definidos no próprio Kernel (mais precisamente no módulo ntoskrnl.exe e seus irmãos). Este módulo lida diretamente com o hardware responsável pelo meio utilizado. Essa é também a explicação para outra pergunta frequente: “Meu computador não tem porta serial. Posso usar um conversor USB/Serial do lado Target para fazer debug do sistema?”. Como acabamos de ver, um conversor USB/Serial depende de toda uma pilha de dispositivos para que a porta serial esteja disponível. Tal funcionalidade não está implementada no algoritmo de debug do sistema, e como comentei neste outro post, sistemas mais novos implementam novas funcionalidades de debug no Kernel.

Mesmo que seu driver seja de boot, ele ainda pode ser depurado. Este outro post mostra ainda como fazer mapeamento de um driver de boot pelo WinDbg. Não sabe do que estou falando? É sobre ter seu driver substituído por uma nova versão automaticamente no lado target quando este for carregado. Vale a pena dar uma olhada.

Have fun!