问题
The following code I have reads the input from user similar to gets
function in C
language.
section .text
global _start
_start:
mov eax, 3 ; Read user input into str
mov ebx, 0 ; |
mov ecx, str ; | <- destination
mov edx, 100 ; | <- length
int 80h ; \
mov eax, 1 ; Return
mov ebx, 0 ; | <- return code
int 80h ; \
section .data
str: times 100 db 0 ; Allocate buffer of 100 bytes
I am not sure how Linux is handling my code, but I am curious how this code is handled on an Intel machine natively (without any OS). As far as I know, interrupt 80h
is searched through an interrupt vector table and the related code function is called. But, going one step lower, how is that code written? I want to know how is the algorithm that handles such a functionality?
Can anyone please advise how to find the complete function code for handling user input with the lowest level on an actual machine? I am interested in both knowing the function and also finding the way to obtain such a code.
回答1:
At the lowest level (bare metal), there is no "function" you "call" with an int xx
instruction. int xx
can only invoke other code running on the CPU, not make something special happen.
Talking to hardware involves running code that uses loads or stores to generate PCI / PCIe transactions, or in
/out
instructions to access I/O space. (Or at least some access to special physical addresses; on older computers it wasn't necessarily PCI, and a few MMIO addresses on modern CPUs don't actually go off-chip so PCI isn't really involved.)
As far as I know, interrupt 80h is searched through an interrupt vector table and the related code function is called. But, going one step lower, how is that code written?
Yes, the Linux kernel (written in C and assembly) sets up the IDT so user-space int 80h
will enter the kernel at its int80
handler entry-point.
int 80h
/ eax=3
in user-space just dispatches to the sys_read
function in the Linux kernel, which implements the POSIX read()
function / system call. (See What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? for a bit about the kernel side of that dispatching to the table of system calls).
Think of int 0x80
(or the more efficient sysenter
and syscall
instructions) as ways to make a function call across a privilege boundary. All the magic is (as you guessed) in the implementation of that function inside the kernel, and the other functions it calls. (And the whole Unix model of "everything is a file"; this is the same system call you'd use to read a disk file.)
It takes a file-descriptor arg, in your case stdin
= 0. That's just a file, often a TTY or pseudo-tty connected to a terminal emulator, which talks to an X11 server to get keyboard events. You could be running on a Linux text console (ctrl+alt+f2) in which case the kernel is running a terminal emulator with keyboard input coming from whatever physical keyboard(s) is/are connected. Or you could have redirected input from any type of file.
If I redirected input from /dev/input/by-id/usb-Logitech_USB_Receiver-if02-event-kbd
on my system, I can get some raw keypress events but not in ASCII text format. (You can safely sudo cat
that file; input still goes to your X server as well so you can control-C it). That's a more direct way to talk to the keyboard driver, but you're still going through Linux's HID (Human Interface Device) and event subsystem before you actually get to code that accesses the USB host controller connected to your keyboard.
There's nothing remotely similar you can do on bare metal; you'd use in
/ out
or MMIO loads / stores to talk to a keyboard through a USB host control controller (e.g. eHCI or xHCI). https://wiki.osdev.org/Universal_Serial_Bus
(With BIOS emulation of a PS/2 keyboard, or on an old machine with a real PS/2 keyboard controller, you could talk to that much more easily. Back in early PC days, lots of different programs running in real mode would interact with hardware directly so there were simpler standards for how to access it, often with in
and out
instructions to well-known port numbers. No PCI bus enumeration or anything needed.
In modern PCs you can still do that, but mostly it's faked by software that traps the accesses. BIOS emulation is by definition not bare metal. Only motherboard firmware developers truly program bare metal. System Management Mode lets the firmware set up hooks that can run even after the OS boots. Fortunately most systems don't do much / any of that, although there are still ACPI tables that a kernel should read instead of probing hardware directly.)
If you boot a legacy bootloader, the firmware will switch back to real mode and set up a bunch of "BIOS services" which you can use via int 10h
and other interrupt numbers. Much like running under a Linux kernel, you're not remotely close to talking "directly" to real hardware; all device-driver details are hidden behind a standard API. https://wiki.osdev.org/BIOS
If you boot a modern UEFI bootloader, again you have a standard API for accessing screen / keyboard, with "driver" code provided by the firmware. It's like a minimal kernel. https://wiki.osdev.org/UEFI
Once a real kernel like Linux boots, it does have device drivers for the real USB controllers. If the BIOS had set up emulation of PS/2 hardware, the kernel replaces / disables that.
Like I said, this Linux kernel code is written in C, with some inline asm wrappers for a few things.
- https://github.com/torvalds/linux/tree/master/drivers/input/keyboard/ is a whole directory of keyboard drivers for various possible keyboard hardware. (I think separate from USB).
- https://github.com/torvalds/linux/tree/master/drivers/usb/host/ drivers for USB host controllers. (Also other directories under drivers/usb/, like
core/
, have essential code) - https://github.com/torvalds/linux/blob/master/drivers/hid/usbhid/usbkbd.c Linux's USB keyboard driver. (There's a standard generic protocol for USB HID devices including keyboards; that's why most keyboards and mice can "just work" without needing special drivers even on Windows where the base OS doesn't have drivers for everything already built / available.)
This is almost certainly more code than you want to wade through, but it is the answer to your question.
来源:https://stackoverflow.com/questions/58569102/the-lowest-level-function-to-handle-a-user-input