How to get keyboard input with x86 bare metal assembly? - c

I'm in the process of trying to hack together the first bits of a kernel. I currently have the entire kernel compiled down as C code, and I've managed to get it displaying text in the console window and all of that fine goodness. Now, I want to start accepting keyboard input so I can actually make some use of the thing and get going on process management.
I'm using DJGPP to compile, and loading with GRUB. I'm also using a small bit of assembly which basically jumps directly into my compiled C code and I'm happy from there.
All the research I've done seems to point to an ISR at $0x16 to read in the next character from the keyboard buffer. From what I can tell, this is supposed to store the ASCII value in ah, and the keycode in al, or something to that effect. I'm attempting to code this using the following routine in inline assembly:
char getc(void)
{
int output = 0;
//CRAZY VOODOO CODE
asm("xor %%ah, %%ah\n\t"
"int $0x16"
: "=a" (output)
: "a" (output)
:
);
return (char)output;
}
When this code is called, the core immediately crashes. (I'm running it on VirtualBox, I didn't feel the need to try something this basic on real hardware.)
Now I have actually a couple of questions. No one has been able to tell me if (since my code was launched from GRUB) I'm running in real mode or protected mode at the moment. I haven't made the jump one way or another, I was planning on running in real mode until I got a process handler set up.
So, assuming that I'm running in real mode, what am I doing wrong, and how do I fix it? I just need a basic getc routine, preferably non-blocking, but I'll be darned if google is helping on this one at all. Once I can do that, I can do the rest from there.
I guess what I'm asking here is, am I anywhere near the right track? How does one generally go about getting keyboard input on this level?
EDIT: OOhh... so I'm running in protected mode. This certainly explains the crash trying to access real mode functions then.
So then I guess I'm looking for how to access the keyboard IO from protected mode. I might be able to find that on my own, but if anyone happens to know feel free. Thanks again.

If you are compiling with gcc, unless you are using the crazy ".code16gcc" trick the linux kernel uses (which I very much doubt), you cannot be in real mode. If you are using the GRUB multiboot specification, GRUB itself is switching to protected mode for you. So, as others pointed out, you will have to talk to the 8042-compatible keyboard/mouse controller directly. Unless it's a USB keyboard/mouse and 8042 emulation is disabled, where you would need a USB stack (but you can use the "boot" protocol for the keyboard/mouse, which is simpler).
Nobody said writing an OS kernel was simple.

The code you've got there is trying to access a real mode BIOS service. If you're running in protected mode, which is likely considering that you're writing a kernel, then the interrupt won't work. You will need to do one of the following:
Thunk the CPU into real mode, making sure the interrupt vector table is correct, and use the real mode code you have or
Write your own protected mode keyboard handler (i.e. use the in/out instructions).
The first solution is going to involve a runtime performance overhead whist the second will require some information about keyboard IO.

I've a piece of GeekOS that seems to do
In_Byte(KB_CMD);
and then
In_Byte(KB_DATA);
to fetch a scancode. I put it up: keyboard.c and keyboard.h. KB_CMD and KB_DATA being 0x64 and 0x60 respectively. I could perhaps also point out that this is done in an interrupt handler for intr:1.

You're doing the right thing, but I seem to recall that djgpp only generates protected mode output, which you can't call interrupts from. Can you drop to real mode like others have suggested, or would you prefer to address the hardware directly?

For the purposes of explanation, let's suppose you were writing everything in assembly language yourself, boot loader and kernel (*cough* I've done this).
In real mode, you can make use of the interrupt routines that come from the BIOS. You can also replace the interrupt vectors with your own. However all code is 16-bit code, which is not binary compatible with 32-bit code.
When you jump through a few burning hoops to get to protected mode (including reprogramming the interrupt controller, to get around the fact that IBM used Intel-reserved interrupts in the PC), you have the opportunity to set up 16- and 32-bit code segments. This can be used to run 16-bit code. So you can use this to access the getchar interrupt!
... not quite. For this interrupt to work, you actually need data in a keyboard buffer that was put there by a different ISR - the one that is triggered by the keyboard when a key is pressed. There are various issues which pretty much prevent you using BIOS ISRs as actual hardware ISRs in protected mode. So, the BIOS keyboard routines are useless.
BIOS video calls, on the other hand, are fine, because there's no hardware-triggered component. You do have to prepare a 16-bit code segment but if that's under control then you can switch video modes and that sort of thing by using BIOS interrupts.
Back to the keyboard: what you need (again assuming that YOU'RE writing all the code) is to write a keyboard driver. Unless you're a masochist (I'm one) then don't go there.
A suggestion: try writing a multitasking kernel in Real mode. (That's 16-bit mode.) You can use all the BIOS interrupts! You don't get memory protection but you can still get pre-emptive multitasking by hooking the timer interrupt.

Just an idea: looking at GRUB for DOS source (asm.s), the console_checkkey function is using BIOS INT 16H Function 01, and not function 00, as you are trying to do. Maybe you'd want to check if a key is waiting to be input.
The console_checkkey code is setting the CPU to real mode in order to use the BIOS, as #skizz suggested.
You can also try using GRUB functions directly (if still mapped in real mode).
A note on reading assembly source: in this version
movb $0x1, %ah
means move constant byte (0x1) to register %ah
The console_checkkey from GRUB asm.s:
/*
* int console_checkkey (void)
* if there is a character pending, return it; otherwise return -1
* BIOS call "INT 16H Function 01H" to check whether a character is pending
* Call with %ah = 0x1
* Return:
* If key waiting to be input:
* %ah = keyboard scan code
* %al = ASCII character
* Zero flag = clear
* else
* Zero flag = set
*/
ENTRY(console_checkkey)
push %ebp
xorl %edx, %edx
call EXT_C(prot_to_real) /* enter real mode */
.code16
sti /* checkkey needs interrupt on */
movb $0x1, %ah
int $0x16
DATA32 jz notpending
movw %ax, %dx
//call translate_keycode
call remap_ascii_char
DATA32 jmp pending
notpending:
movl $0xFFFFFFFF, %edx
pending:
DATA32 call EXT_C(real_to_prot)
.code32
mov %edx, %eax
pop %ebp
ret

Example for polling the keyboard controller:
Start:
cli
mov al,2 ; dissable IRQ 1
out 21h,al
sti
;--------------------------------------
; Main-Routine
AGAIN:
in al,64h ; get the status
test al,1 ; check output buffer
jz short NOKEY
test al,20h ; check if it is a PS2Mouse-byte
jnz short NOKEY
in al,60h ; get the key
; insert your code here (maybe for converting into ASCII...)
NOKEY:
jmp AGAIN
;--------------------------------------
; At the end
cli
xor al,al ; enable IRQ 1
out 21h,al
sti

Related

Enabling interrupts on 8052 causes lock-up

The problem I have currently is that ever since I enabled interrupts, the program is stuck in an endless loop. If I disable the interrupts then the program executes normally.
I even made sure that I protected the registers (variables) in the functions by pushing them onto the stack and popping them off upon exit of the function, and that did not help.
I even took the step to even replace the function names with only iret (to exit from interrupt), and I still face the same problem.
The only way for me to solve the problem right now is to disable the interrupts by not executing mov TCON,#50h. This makes me think that the interrupt vector addresses published on the internet are not correct, and that in reality, screwed-up code is being executed instead.
My microcontroller I'm using is AT89S52.
Am I off with my vector addressing here? I need some advice because the code I currently use below is currently not working when timer interrupts are enabled.
org 000h
;entry point when program first runs
ljmp sysinit ;seems to execute
sjmp $
org 00Bh
;Timer 0 interrupt executes every 65536 machine cycles even if timer 1 interrupt executes
ljmp crit
sjmp $
org 01Bh
;Timer 1 interrupt executes every 256 machine cycles
ljmp processkey
sjmp $
org 030h
start:
;rest of program goes here.
sysinit:
mov TL0,#0h
mov TH0,#0h
mov TH1,#0h
mov TL1,#0h
mov PSW,#0h
mov R0,#7Fh
;make all ram addresses 0h to 7Fh contain value 0
sysreset:
CLR A
mov #R0,A
djnz R0,sysreset
mov T2CON,#0h ;8052 register
mov AUXR,#0h ;8052 register
mov AUXR1,#0h ;8052 register
mov PCON,#80h ;Make baud divisor=192
mov TMOD,#21h ;Timer1=0-255,Timer2=0-65535
mov IP,#02h ;priority to timer 0
mov TCON,#50h ;timers on
mov SP,#050h ;stack=50h
mov IE,#8Ah ;ints enabled
ljmp start
Are you sure that the interrupt service routines actually clear the interrupts? If not, the core will continuously try to serve them hence the apparent lockup.
Check out the datasheet pg. 17:
Timer 2 interrupt is generated by the logical OR of bits TF2 and EXF2 in register T2CON. Nei-
ther of these flags is cleared by hardware when the service routine is vectored to. In fact, the
service routine may have to determine whether it was TF2 or EXF2 that generated the interrupt,
and that bit will have to be cleared in software.
So your interrupt will keep firing if the flags aren't reset.
Why are you writing your code in assembly anyway? Surely, for this functionality you could use plain C instead.

Is there any way to make a call to linux kernel with my own softirq

Similar to how system call works on int 0x80, is it possible to implement my own ISR inside kernel so that on softirq assume int 0x120 or with any other softirq Program Counter can jump from user space to kernel space?
Is entering kernel in privileged mode is associated only with int 0x80, or with any softirq implementation I can enter privileged mode automatically or for disabling the protected mode and entering into privileged mode we have to do manually by writing its associated flag?
and one more thing, if it is possible to implement this type of ISR, is the best possible way for data exchange is with registers EBX, ECX, EDX, ESI, EDI and EBP or any other way is still there?
I already saw How to define and trigger my own new softirq in linux kernel? but didn't got the solution I was looking for.
I'll make it some more clear, why i need this
I had implemented few kernel functions, which are directly talking to hardware peripherals, I want them to trigger from user space using software interrupt. can't use system calls with available driver architecture because i need to reduce execution time.
First, software interrupts and softirq are completely different:
software interrupt is the assembly instruction to switch from user mode to privilege mode and this is what you're looking for
softirq is a mechanism to split hardware interrupt handler to top,bottom halfs
For your question - you'll need to write assembly code and modify platform specific code
You need to define the int number in Linux arch/x86/include/asm/irq_vectors.h:
#define MY_SYSCALL_VECTOR 0x120
Change the function trap_init in Linux arch/x86/kernel/traps.c:
set_system_trap_gate(MY_SYSCALL_VECTOR, entry_INT120_32);
Now you need to write the assembly function entry_INT120_32. you can see an example in the file: arch/x86/entry/entry_32.S starting at ENTRY(entry_INT80_32).
You'll need to take care of the CPU registers as documented at the beginning of entry_32.S file.

Does int 80h interrupt a kernel process?

First some background knowledge, this is from the book: Linux System Programming: Talking Directly to the Kernel and C Library
Signals are a mechanism for one-way asynchronous notifications. A
signal may be sent from the kernel to a process, from a process to
another process, or from a process to itself.
The Linux kernel implements about 30 signals.
Signals interrupt an executing process, causing it to stop whatever it
is doing and immediately perform a predetermined action.
Ok moving further, from here I will quote this part:
On the Intel family of microprocessors, such as the Pentium, int 80h
is the assembly language op code for interrupt 80h. This is the
syscall interrupt on a typical Intel-based Unix system, such as
FreeBSD. It allows application programmers to obtain system services
from the Unix kernel.
I can not quite make the connection in my head really. So when I for example use the
write
method defined in Posix, and when it is compiled into assembly, and then further assembled into an object code and linked to an executable in a given architecture that runs Linux.... a system call is made right?
I am assuming the compiled code would look something like this:
mov eax,4 ;code for system_write
mov ebx,1 ;standard output
mov ecx, memlocation; memlocation is just some location where a number of ascii is present
mov edx, 20; 20 bytes will be written
int 80h;
Ok my question is exactly at this point. Will int 80h send a signal to kernel / interrupt the kernel? Is kernel just one process? (Is it the init process?) When the cpu executes the int 80h , what exactly happens? The registers are full of information already, (eax, ebx, ecx and edx in my example..), but how is this information used?
I can not quite make the connection between the CPU - the kernel and what exactly the CPU does as it executes the int 80h.
I can imagine some code resides somewhere in the memory that actually sends the required information to the device driver but to which process does this code belong to? (I am assuming kernel but is kernel just one process?) And how does int 80h instruction jump to that code? Is it something that Linux has to implement somehow?
Is kernel just one process? (Is it the init process?)
The kernel is a magic beast. It's not a process. The kernel doesn't have a PID you can refer to.
First, it's worth stating (even though it's obvious) that instructions runs on the processor: Therefore, int 80h is executed by the processor.
There is something called Interrupt Request Handler. They are somehow similar to a function pointer. The processor has a table of interrupt handler. This table is called the Interrupt Descriptor Table (aka IDT) and is system wide (ie, not every process has it's own table).
I believe that this table is populated by the kernel when it first boot.
So, what happens when the int 80h is executed?
The processor was running in ring 3 protection level (the normal level for a process). For more info on ring level, see this.
The processor will switch to ring 0, aka kernel mode. In this mode, hardware protection are disabled. This mean that the code that will be executed from now on can do whatever it wants. Write everywhere in physical memory, rewrite the interrupt descriptor table, etc.
The processor will jump to the code located in the Interrupt Descriptor Table for the 80h interrupt. The space available for each interruption in the IDT is very small. This is why this code will generally jump again somewhere else.
The previous jump lend the processor in the kernel routine dedicated to handling int 80h. The processor is no longer running your process' code, but it is now running kernel code.
The kernel can check the registers and memory and determine why the interrupt was triggered. It will understand that you wanted to execute the system call write.
The kernel code will jump again, this time in the routine that handle write. The kernel will runs its code for write.
The kernel is done running its code. It tells the processor to go back to ring 3 protection level, and resume your process.
Userspace process (aka your process) resumes.
When the CPU executes an INT 80h instruction the currently running process on that CPU is an ordinary user process. As a result of processing this instruction the CPU switches from user-mode to kernel-mode. The process doesn't change. The current process is still an ordinary user process, it's just now executing in kernel-mode. Being in kernel-mode gives the system call permission to do things that the program can't do itself. The kernel code then does whatever is necessary to implement system call and executes an IRET instruction. The causes the CPU to switch back to user-mode and start executing code following INT 80h instruction.
Note if the kernel mode code takes long enough to execute, in particular if it blocks, then the scheduler may kick in and switch the CPU to running a different process. In this case the kernel mode code has to wait for an opportunity to finish its job.
Most of the CPU time spent in the kernel is like this, executing system calls in the context of the process that made the system call. Most of the rest of the time spent in the kernel is handling hardware interrupts. (Note that INT 80h is a software interrupt.) In that case the interrupt runs in the context of whatever process happens to be running at the time. The interrupt routine does whatever is necessary to service the hardware device that generated the interrupt and then returns.
While the kernel creates some special processes for itself, these processes have very specialized tasks. There's no main kernel process. The init process in particular isn't a kernel process, it's just an ordinary user process.
your questions are answered as asked. I recommend to consult the book linux programming interface page 44. howerever short answers are as follows.
Ok my question is exactly at this point. Will int 80h send a signal to kernel / interrupt the kernel?
No int 80h does not raise any signal to kernel instead it is an entry in a table of interrupts
Is kernel just one process? (Is it the init process?)
No. Now unix kernel is set of threats (called native threads) which can have 3 different types of process-kernel mappings.
When the cpu executes the int 80h , what exactly happens? The registers are full of information already, (eax, ebx, ecx and edx in my example..), but how is this information used?
int 80h is a trap instruction which transition the environment from user to kernel mode %eax contains the system call number for write to be run in kernel mode. contents of all other registers are stored in memory to be stored on return to user mode
I can not quite make the connection between the CPU - the kernel and what exactly the CPU does as it executes the int 80h.
80h is a trap for CPU which changes the environment from user to kernel and save registers to memory. it means CPU helps the kernel for doing somthing useful with efficiency.
I can imagine some code resides somewhere in the memory that actually sends the required information to the device driver but to which process does this code belong to? (I am assuming kernel but is kernel just one process?) And how does int 80h instruction jump to that code? Is it something that Linux has to implement somehow?
here you are asking about device drivers. drivers functionality is different from syscall handling. int 80h does not work with drivers.
The answer by #Xaqq is perfect but in addition to that understand this.
A CPU is just silicon Slab with Billions of transistors which are used in CPU to Hard Code the most basic Routines in it. It is done using Logic Gates [AND, OR, XOR] etc. This Landscape of Transistors is designed in such a way that when you put :
4 in EAX
1 in EBX
Addr in ECX
Length in EDX
And then call the int 80h which is basically a routine which understands above values and what to do with them. It handles the execution to the CPU and just by magic these transistors flip flop to bring about the action you intended which is to print the message at addr to console.

Why do I need an infinite loop in STM32 programming?

I'm programing a STM32F4 in C (gcc), it's a ARM Cortex M4, I see all examples finish their main() function with an infinite loop, even when the rest of the program will be executed from interruptions. If I try to remove the loop from my program, the interruptions stop being fired too.
Why can't I just remove this loop and exit the main thread?
here is the assembly (I guess it's thumb, but I can't read that, even with the doc):
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call the application's entry point.*/
bl main
bx lr
.size Reset_Handler, .-Reset_Handler
Take a look at the setup code that runs before main in your project. It might be some slim assembly code or something more complicated, but in general it's pretty close to the bare minimum amount of processor setup required to initialize a stack and get the C runtime going.
If you were return from main, what is your processor supposed to do? Reset? Hang? There's no one good answer, so you'll have to look at the runtime support code being linked with your program to see what its designers decided. In your case, it sounds like they didn't make any allowances for main to return, so the processor just crashes/takes an exception and your program stops working.
Edit: It seems like what you're actually looking for is a way to enter a low power state during the idle loop. That's certainly possible - since your processor is an ARM Cortex-M4, there's a simple instruction to do just that:
while (1)
{
asm("wfi");
}
If you're using CMSIS (and it looks like you are, given your use of SystemInit), the assembly is probably already done for you:
while(1)
{
__WFI();
}
More details at this link.
you are not running on an operating system. main() is just a function like any other, it returns from where it was called. With a bare metal system like this though that is not an operating system. So if your software is all interrupt driven and main() is simply for setup, then you need to keep the processor in a controlled state either in an infinite loop or in a low power mode. you can do that at the end of main() or whatever your setup function is called, or the assembly that calls main:
bl main
b . ;# an infinite loop
or if you want wfi in there:
bl main
xyz:
wfi
b xyz
i guess you haven't defined a interrupt handler which your program need.

What happens in an interrupt service routine?

Can someone please explain to me what happens inside an interrupt service routine (although it depends upon specific routine, a general explanation is enough)? This always used be a black box for me.
There is a good wikipedia page on interrupt handlers.
"An interrupt handler, also known as an interrupt service routine (ISR), is a callback subroutine in an operating system or device driver whose execution is triggered by the reception of an interrupt. Interrupt handlers have a multitude of functions, which vary based on the reason the interrupt was generated and the speed at which the Interrupt Handler completes its task."
Basically when a piece of hardware (a hardware interrupt) or some OS task (software interrupt) needs to run it triggers an interrupt. If these interrupts aren't masked (ignored) the OS will stop what it's doing and call some special code to handle this new event.
One good example is reading from a hard drive. The drive is slow and you don't want your OS to wait for the data to come back; you want the OS to go and do other things. So you set up the system so that when the disk has the data requested, it raises an interrupt. In the interrupt service routine for the disk the CPU will take the data that is now ready and will return it to the requester.
ISRs often need to happen quickly as the hardware can have a limited buffer, which will be overwritten by new data if the older data is not pulled off quickly enough.
It's also important to have your ISR complete quickly as while the CPU is servicing one ISR other interrupts will be masked, which means if the CPU can't get to them quickly enough data can be lost.
Minimal 16-bit example
The best way to understand is to make some minimal examples yourself.
First learn how to create a minimal bootloader OS and run it on QEMU and real hardware as I've explained here: https://stackoverflow.com/a/32483545/895245
Now you can run in 16-bit real mode:
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
This would do in order:
Do 0.
Do 1.
hlt: stop executing
Note how the processor looks for the first handler at address 0, and the second one at 4: that is a table of handlers called the IVT, and each entry has 4 bytes.
Minimal example that does some IO to make handlers visible.
Protected mode
Modern operating systems run in the so called protected mode.
The handling has more options in this mode, so it is more complex, but the spirit is the same.
Minimal example
See also
Related question: What does "int 0x80" mean in assembly code?
While the 8086 is executing a program an interrupt breaks the normal sequence of execution of instruction, divert its execution to some other program called interrupt service Routine (ISR). after executing, control return the back again to the main program.
An interrupt is used to cause a temporary halt in the execution of program. Microprocessor responds to the interrupt service routine, which is short program or subroutine that instruct the microprocessor on how to handle the interrupt.

Resources