Search code examples
assemblyioportinteraction

How to interact with x86 I/O device from assembly bootloader?


The primitive goal of this question is

  • Is a device connected?
  • If something is connected, what is the device?
  • What is the device information?

So far I've understood is (x86 intel architecture Z370 chipset)

  • I/O devices can be interacted with IN / OUT instructions.
  • Modern computer takes PCI Express bus architecture. Devices connected can be found by enumerating PCI Configuration Space.
  • This will cover devices at least Graphic Card, Disk Drives, Keyboard, and Mouse.

What I thought of doing is

  • Iterate all I/O ports
  • Check device existance / connection
  • Check device info
  • Move on to enumerating PCI Express Configuration Space

However, very few articles dealing with I/O ports are found, and they aren't enough.

Q1. Is there some article or book that extensively explains I/O port interaction using IN and OUT instruction?

The articles found were

Brokenthorn

http://www.brokenthorn.com/Resources/OSDev7.html

http://www.brokenthorn.com/Resources/OSDev9.html

OS Dev

https://wiki.osdev.org/I/O_Ports

https://wiki.osdev.org/%228042%22_PS/2_Controller

The reason why I need more detailed article is that I've got questions like if 2 USB keyboards are plugged, if one is accessed by 0x60, then how can other be accessed? Device enabled can be checked by command, but isn't device existance / connection check has to be preceded?

Q2. Also, it seems these ports does not have a standard like PCI Configuration Header, then how can other ports be manipulated?

Even small piece of kind answer will be greatly appreciated.

P.S. Perhaps what I am trying to do is writing a logic that resides in the stage between I/O Port (=I/O Device Controller) <-> I/O Device Driver. And this job seems done by ACPI, ACPICA. But I wish to do them by myself in assembly.


Solution

  • Nope, it doesn't work like that.

    You are in the mindset of enumerating all the devices, instead you must get into enumerating all the busses.

    As you know, the central bus in modern x86s is the PCI express.
    It is backward compatible with legacy PCI, which is simpler. Using the legacy PCI will get you far away but not all the way to the end.

    Legacy devices, like the 8042 keyboard controller or the 8259A PIC, are not found on the PCI bus.
    They existed before it.
    Some devices are in both the PCI bus and the legacy port address because these devices still expose the legacy interface along with a PCI BAR (Base address register, it tells where to put this interface in the IO address space).
    This is the case for PCI, obsolete, ATA controllers, for example.

    There is no way to know the device behind a port in general, you can check (probe) for a device if the device has a recognisable behaviour.
    But that's not 100% safe.
    Simply reading from all the 64KiB ports won't get you anywhere, the return value of in is total, meaning that there is no special value it returns in case of error (for legacy reason, it returns all ones for non existent devices but that's a legal value that an existent device could send).
    That's why there are no books.

    USB is the other big bus used today, again your approach should be bus-centric.
    Enumerate all the PCI devices, including USB adapter, enumerate all the USB devices in a bus and so on.
    In general, start from a central bus, enumerate all the devices, find other bus adaptors (I2C, LPC, SPI, USB, ...) and handle that bus accordingly.

    Alas, you must also deal with the legacy devices that elide this enumeration process.
    In general, my advice with legacy device is: handle a device as soon as needed but not sooner.
    Ralf Brown is known for its interrupt list but he also have a very useful port files (A, B, C, D, ...).
    The problem is that it includes a lot of x86 variants and it is not easy to read at first but its very detailed.
    As fuz commented, bochs also have a list of devices it emulates.

    Everybody likes to write software that manages all the hardware but the truth is that we can only write a software that manage the hardware we know about.
    When you feel the need for a device, document your self about it and find out how it is connected and what is its interface.

    Another good approach is to at least read the datasheet of your CPU and chipset (e.g. the PCH for Intel).
    Laptops and similar also have an Embedded Controller, while desktops have Super IO chips.
    Find their datasheet too, if publicly available.
    To find the ICs part numbers of those components and of all the other components you probably need to open your computer.
    On some sites you may find the schematics of your motherboard but your still need the part number of it, which is not its model name+number.

    Programming the hardware is fun, don't give up!