Para que servem as instruções IN & OUT no x86?

Eu encontrei essas instruções para IN & OUT ao ler o livro “Understanding Linux Kernel”. Eu olhei para o manual de referência.

5.1.9 Instruções de E / S

Essas instruções movem dados entre as portas de E / S do processador e um registro ou memory.

IN Read from a port OUT Write to a port INS/INSB Input string from port/Input byte string from port INS/INSW Input string from port/Input word string from port INS/INSD Input string from port/Input doubleword string from port OUTS/OUTSB Output string to port/Output byte string to port OUTS/OUTSW Output string to port/Output word string to port OUTS/OUTSD Output string to port/Output doubleword string to port 

Eu não recebi poucas coisas:

  1. “portas de E / S do processador”. O que eles são? Por que queremos ler e escrever “strings” para e a partir dessas portas?
  2. Eu nunca encontrei um scenerio onde eu precise usar essas instruções. Quando eu precisaria disso?
  3. Dê alguns exemplos práticos.

Você sabe como funciona o endereçamento de memory? Há um barramento de endereços, um barramento de dados e algumas linhas de controle. A CPU coloca o endereço de um byte (ou um byte inicial) de memory no barramento de endereços, então aumenta o sinal de LEITURA, e algum chip de RAM retorna o conteúdo da memory naquele endereço, aumentando ou diminuindo as linhas individuais (correspondentes aos bits). no (s) byte (s)) no barramento de dados. Isso funciona para RAM e ROM.

Mas também há dispositivos I / O: portas serial e paralela, o driver para o minúsculo alto-falante interno de um PC, controladores de disco, chips de som e assim por diante. E esses dispositivos também são lidos e gravados. Eles também precisam ser endereçados para que a CPU acesse o dispositivo correto e (geralmente) a localização correta dos dados dentro de um determinado dispositivo.

Para alguns modelos de CPU, incluindo a série xxx86, encontrada na maioria dos PCs “modernos”, os dispositivos de E / S compartilham o espaço de endereço com a memory. Ambos os dispositivos RAM / ROM e IO estão conectados ao mesmo endereço, dados e linhas de controle. Por exemplo, a porta serial para COM1 é endereçada a partir de (hex) 03F8. Mas quase certamente há memory no mesmo endereço.

Aqui está um diagrama bem simples:

[ https://qph.ec.quoracdn.net/main-qimg-e510d81162f562d8f671d5900da84d68-c?convert_to_webp=true ]

Claramente, a CPU precisa falar com a memory ou com o dispositivo de E / S, nunca com os dois. Para distinguir entre os dois, uma das linhas de controle chamada “M / # IO” afirma se a CPU quer falar com a memory (line = high) ou um dispositivo de I / O (line = low).

A instrução IN lê de um dispositivo de E / S, gravações de saída. Quando você usa as instruções IN ou OUT, o M / # IO não é declarado (mantido baixo), então a memory não responde e o chip de I / O faz. Para as instruções orientadas à memory, M / # IO é declarado, de forma que o CPU se comunica com a RAM e os dispositivos IO ficam fora da comunicação.

Sob certas condições, os dispositivos IO podem conduzir as linhas de dados e a RAM pode lê-los ao mesmo tempo. E vice versa. É chamado DMA.

Tradicionalmente, as portas seriais e de impressora, bem como o teclado, o mouse, os sensores de temperatura e assim por diante, eram dispositivos de E / S. Discos eram meio que intermediários; as transferências de dados seriam iniciadas por comandos de E / S, mas o controlador de disco normalmente depositaria diretamente seus dados na memory do sistema.

Nos sistemas operacionais modernos, como o Windows ou o Linux, o access a portas de E / S é escondido dos programas de usuário “normais”, e há camadas de software, instruções privilegiadas e drivers para lidar com o hardware. Portanto, neste século, a maioria dos programadores não lida com essas instruções.

Comece com algo assim:

http://www.cpu-world.com/info/Pinouts/8088.html

Você está aprendendo instruções para um chip / arquitetura de tecnologia muito antiga. Quando tudo, menos o núcleo do processador, estava fora do chip. Veja as linhas de endereço e as linhas de dados e há uma linha de leitura RD e linha de gravação WR e linha IO / M?

Havia dois tipos de instruções baseadas em memory e baseadas em E / S porque havia espaços endereçáveis, facilmente decodificados pelo IO / M IO ou Memory.

Lembre-se que você tinha lógica de cola 74LSxx, muitos fios e muitos chips para conectar uma memory ao processador. E a memory era apenas aquela memory, grandes chips caros. Se você tivesse um periférico que precisasse fazer algo útil, você também tinha registros de controle, a memory poderia ser dados de pixel, mas em algum lugar você precisava definir os limites de varredura horizontais e verticais, eles podem ser latches individuais, NÃO memorys, / O mapeou a E / S salva na lógica de cola e fez muito sentido a partir de uma perspectiva de programador que também evitou mudar seus registradores de segmento para apontar sua janela de memory de 64K, etc. O espaço de endereço de memory era um recurso sagrado, especialmente quando você queria limitar sua decodificação de endereço a alguns bits, porque cada poucos bits lhe custam vários chips e fios.

Como mapeamento de memory big and little endian mapeado E / S versus E / S mapeada E / S era uma guerra religiosa. E algumas das respostas que você vai ver para sua pergunta vão refletir as opiniões fortes que ainda estão por aí hoje nas pessoas que a vivenciaram. A realidade é que cada chip no mercado hoje tem vários busess para várias coisas, você não pendura seu relógio de tempo real fora do barramento de memory ddr com um decodificador de endereço. Alguns ainda têm barramentos de dados e instruções completamente separados. Em certo sentido, a Intel venceu a guerra pelo conceito de espaços de endereçamento separados para diferentes classs de coisas, embora o termo porta de E / S seja ruim e ruim e não deva ser dito por mais de 20 a 30 anos. Você precisa de pessoas da minha idade que viviam para se aposentar ou partir antes que a guerra acabasse. Até mesmo o termo memory mapeada E / S é uma coisa do passado.

Isso é realmente tudo o que sempre foi, um único bit de decodificação de endereço do lado de fora do chip Intel que era controlado pelo uso de instruções específicas. Use um conjunto de instruções em que o bit estava usando um conjunto de instruções que o bit estava desligado. Deseja ver algo interessante ir olhar para o conjunto de instruções para os processadores xmos xcore eles têm muitas coisas que são instruções ao invés de registradores mapeados na memory, ele leva essa coisa de E / S mapeada de E / S para um nível totalmente novo.

Onde foi usado é como eu descrevi acima, você colocaria coisas que faziam sentido e você poderia arcar com espaço de endereço de memory para pixels de vídeo, memory de pacote de rede (talvez), memory de placa de som (bem, não, mas você poderia ter ), etc. E os registros de controle, o espaço de endereçamento relativo aos dados era muito pequeno, talvez apenas alguns registradores, foram decodificados e utilizados no espaço de I / O. as mais óbvias são / eram portas seriais e portas paralelas que tinham pouco ou nenhum armazenamento, você poderia ter um pequeno fifo na porta serial se tivesse alguma coisa.

Como o espaço de endereços era escasso, não era incomum e ainda hoje se vê a memory escondida atrás de dois registradores, um registro de endereços e um registrador de dados, essa memory só está disponível por meio desses dois registros, não é mapeada na memory. assim, você escreve o deslocamento nessa memory oculta no registro de endereços e lê ou grava o registro de dados para acessar o conteúdo da memory. Agora, porque a Intel tinha a instrução rep e você podia combiná-la com insb / w outsb / w, o decodificador de hardware (se você tivesse um pessoal amigável / amigável trabalhando com você) aumentaria automaticamente o endereço sempre que fizesse um ciclo de I / O. Assim, você poderia escrever o endereço inicial no registrador de endereços e fazer um representante fora e sem gravar os ciclos de busca e decodificação no processador e no barramento de memory você poderia mover dados rapidamente para dentro ou para fora do periférico. Esse tipo de coisa é agora considerado uma falha de design graças aos modernos processadores super escalares com buscas baseadas na previsão de ramificação, seu hardware pode ter leituras a qualquer momento que não têm nada a ver com a execução de código, como resultado você NUNCA deve incrementar automaticamente endereçar ou limpar bits em um registrador de status ou modificar qualquer coisa como resultado de uma leitura para um endereço.

Os mecanismos de proteção embutidos no 386 e no presente, na verdade, facilitam muito o access a E / S do espaço do usuário. Dependendo do que você faz para viver, o que sua empresa produz, etc. Você pode definitivamente usar a família in e out de instruções do espaço do usuário (programas aplicativos no windows e linux, etc) ou espaço kernel / driver, é o seu escolha. Você também pode fazer coisas divertidas, como aproveitar a máquina virtual e usar instruções de E / S para conversar com os drivers, mas isso provavelmente irritaria as pessoas nos mundos Windows e Linux, que o driver / aplicativo não iria muito longe. Os outros pôsteres estão corretos, pois provavelmente você nunca precisará usar essas instruções, a menos que esteja gravando drivers, e provavelmente nunca escreverá drivers para dispositivos usando E / S mapeada de E / S porque sabe … drivers para esses dispositivos legados já foram escritos. Design moderno definitivamente tem E / S, mas é toda a memory mapeada (de uma perspectiva de programadores) e usa instruções de memory não instruções de E / S. Agora, o outro lado, se este for o DOS, definitivamente não está morto, dependendo de onde você esteja construindo máquinas de votação, bombas de gasolina, checkboxs registradoras ou uma longa lista de equipamentos baseados em DOS. Na verdade, se você trabalha em algum lugar que constrói PCs ou periféricos ou placas-mãe baseados em PC, as ferramentas baseadas em DOS ainda são amplamente usadas para testar e distribuir atualizações de BIOS e outras coisas semelhantes. Eu ainda me deparo com situações em que tenho que tomar código de um programa de teste atual para escrever um driver Linux. Assim como nem todo mundo que pode jogar ou pegar uma bola de futebol joga na NFL, em termos percentuais muito poucos fazem o trabalho de software que envolve esse tipo de coisa. Portanto, ainda é seguro dizer que essas instruções que você encontrou provavelmente não serão mais para você do que uma lição de história.

Dê alguns exemplos práticos.

Primeiro aprenda como:

  • crie um sistema operacional de bootloader mínimo e execute-o no QEMU e no hardware real, conforme explicado aqui: https://stackoverflow.com/a/32483545/895245
  • fazer algumas chamadas do BIOS para fazer um IO rápido e sujo

Então:

  1. Controlador PS / 2 : obtenha o ID scancode do último caractere typescript no teclado para al :

     in $0x60, %al 

    Exemplo mínimo

  2. Relógio de Tempo Real (RTC) : obtenha o tempo de parede com a definição de segundos:

     .equ RTCaddress, 0x70 .equ RTCdata, 0x71 /* al contains seconds. */ mov $0, %al out %al, $RTCaddress in $RTCdata, %al /* al contains minutes. */ mov $0x02, %al out %al, $RTCaddress in $RTCdata, %al /* al contains hour. */ mov $0x04, %al out %al, $RTCaddress 

    Exemplo mínimo

  3. Temporizador de Intervalo Programável (PIT) : gera um número de interrupção 8 a cada 0x1234 / 1193181 segundos:

     mov $0b00110100, %al outb %al, $0x43 mov $0xFF, %al out %al, $0x34 out %al, $0x12 

    Exemplo mínimo

    Um uso do kernel Linux 4.2 . Há outros.

Testado em: QEMU 2.0.0 Ubuntu 14.04 e hardware real Lenovo ThinkPad T400.

Como encontrar números de portas: Existe uma especificação de atribuição de portas de E / S x86?

https://github.com/torvalds/linux/blob/v4.2/arch/x86/kernel/setup.c#L646 tem uma lista de muitas portas usadas pelo kernel do Linux.

Outras arquiteturas

Nem todas as arquiteturas têm essas instruções dedicadas de IO.

No ARM, por exemplo, o IO é feito simplesmente escrevendo para endereços de memory definidos por hardware mágico.

Acho que isso é o que significa https://stackoverflow.com/a/3221839/895245 por “E / S mapeada em memory versus E / S mapeada em E / S”.

Do ponto de vista do programador, eu prefiro o modo ARM, já que as instruções de IO já precisam de endereços mágicos para operar, e nós temos enormes espaços de endereços não utilizados no endereçamento de 64 bits.

Veja https://stackoverflow.com/a/40063032/895245 para um exemplo concreto do ARM.

Se você não estiver escrevendo um sistema operacional, nunca usará essas instruções.

As máquinas baseadas em x86 têm dois espaços de endereço independentes – o espaço de endereço de memory com o qual você está familiarizado e, em seguida, o espaço de endereço de E / S. Endereços de portas de E / S têm apenas 16 bits de largura e registradores de baixo nível de referência e outros widgets de baixo nível que fazem parte de um dispositivo de E / S – algo como uma porta serial ou paralela, controlador de disco, etc.

Não há exemplos práticos, porque eles são usados ​​apenas por drivers de dispositivos e sistemas operacionais.

No nível do hardware, a maioria dos microprocessadores tem pouca ou nenhuma capacidade de E / S incorporada. Alguns processadores têm um ou mais pinos que podem ser ligados e desligados usando instruções especiais e / ou um ou mais pinos que podem ser testados instruções de ramificação, mas esses resources são raros. Em vez disso, a E / S geralmente é manipulada conectando o sistema de forma que os accesss a um intervalo de endereços de memory acionem algum efeito ou incluindo instruções “in” e “out” que se comportam como operações de armazenamento / armazenamento de memory, exceto que um sinal especial é a saída dizendo “Esta é uma operação de E / S em vez de uma operação de memory.” Nos dias de processadores de 16 bits, costumava haver algumas vantagens reais em ter instruções de input / saída especializadas. Hoje em dia, tais vantagens são amplamente discutíveis, uma vez que se poderia simplesmente alocar uma grande parte do espaço de endereços de uma pessoa para a E / S e ainda ter muito espaço para a memory.

Como um programa pode causar danos consideráveis ​​em um sistema ao executar inadequadamente instruções de E / S (por exemplo, tais instruções podem realizar accesss arbitrários ao disco), todos os sistemas operacionais modernos proíbem o uso de tais instruções no código de nível de usuário. Alguns sistemas podem permitir que tais instruções sejam virtualizadas; Se o código do usuário tentar gravar nas portas de E / S 0x3D4 e 0x3D5, por exemplo, um sistema operacional pode interpretar isso como uma tentativa de definir alguns registros de controle de controle de vídeo para mover o cursor intermitente. Cada vez que o programa do usuário executou a instrução OUT, o sistema operacional assumiria o controle, veria o que o programa do usuário estava tentando fazer e agiria apropriadamente.

Na grande maioria dos casos, mesmo que o sistema operacional traduzisse uma instrução IN ou OUT em algo adequado, seria mais eficiente solicitar diretamente a ação apropriada do sistema operacional.

Há um pouco mais de truques do que isso. Ele não multiplexa apenas um espaço de endereçamento separado de 64kb nos mesmos fios com um ‘pino de seleção de chip / barramento de endereço extra’. Intel 8086 e 8088 e seus clones também multiplexam o barramento de dados e o barramento de endereços; tudo muito incomum em CPUs. As folhas de dados estão cheias de material de configuração ‘mínimo / máximo’ e todos os registros de trava que você precisa conectar a ele para fazer com que ele se comporte ‘normalmente’. Por outro lado, ele salva uma carga de e portas e ‘ou’ portas na decodificação de endereço e 64kb deve ser ‘portas de E / S suficientes para todos’: P.

Além disso, para todas as pessoas ‘apenas desenvolvedoras de drivers’, observe: além das pessoas que usam chips compatíveis com Intel em outros hardwares que não apenas PCs (eles nunca foram realmente destinados ao IBM PC em primeiro lugar – a IBM apenas os utilizou porque eram baratos e já estavam no mercado), a Intel também vende microcontroladores com o mesmo conjunto de instruções (Intel Quark) e há muitos “sistemas em um chip” de outros fornecedores com o mesmo conjunto de instruções. Eu não acho que você conseguirá empinar qualquer coisa com ‘kernel’ e ‘drivers’ separados em ‘user space’ em 32kb :). Para a maioria das coisas, tais sistemas operacionais complexos não são ótimos nem desejados. Formando alguns pacotes UDP em RAM e, em seguida, colocando-os em algum buffer de anel e fazendo alguns relés, clique em click não requer um kernel de 30mb e 10 segundos de tempo de carregamento, você sabe. É basicamente a melhor escolha no caso de um microcontrolador PIC não ser suficiente, mas você não quer um PC industrial inteiro. Portanto, as instruções de porta E / S são muito usadas e não apenas por ‘desenvolvedores de drivers’ para sistemas operacionais maiores.

CPU conectada a alguns controladores externos através de portas io. no pc x86 velho eu trabalho com drive de disquete usando portas de E / S. Se você sabe quais comandos aceitam o controlador de dispositivo, você pode programá-lo através de suas portas.

No mundo moderno, você nunca usará instruções de portas. Exceção se você é (ou será) desenvolvedor do driver.

há informações mais detalhadas sobre as portas de E / S http://webster.cs.ucr.edu/AoA/DOS/ch03/CH03-6.html#HEADING6-1