Reputation: 11561
I was trying to add keyboard interaction to code from this example. Consider the following files:
Cargo.toml
[package]
name = "kernelhello"
version = "0.0.1"
[dependencies]
bootloader = "0.3.12"
[package.metadata.bootimage]
default-target = "build.json"
build.json
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
src/main.rs
// src/main.rs
#![feature(asm)]
#![no_std] // don't link the Rust standard library
#![no_main] // disable all Rust-level entry points
use core::panic::PanicInfo;
/// This function is called on panic.
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub extern "C" fn _start() -> ! {
let mut HELLO: &mut [u8] = &mut b"Hello World!".clone();
let vga_buffer = 0xb8000 as *mut u8;
let mut z = 0;
loop {
for (i, byte) in HELLO.iter_mut().enumerate() {
unsafe {
z += 14;
z %= 4000;
*vga_buffer.offset(z + i as isize * 2) = *byte;
*vga_buffer.offset(z + i as isize * 2 + 1) = 0xa;
asm!("mov $$0, %ah\nint $$0x16");
}
}
}
}
Unfortunately, an attempt to do bootimage run
ends with an image that is stuck in a reboot loop - which doesn't happen if I comment out the asm!
call. Here's a disassembly:
➜ rust-kernel-hello objdump -D -b binary -Mintel,x86-64 -m i386 target/build/debug/bootimage-kernelhello.bin | grep -C5 'ef01'
eef0: 00
eef1: 48 8b 84 24 d0 00 00 mov rax,QWORD PTR [rsp+0xd0]
eef8: 00
eef9: 48 89 84 24 00 01 00 mov QWORD PTR [rsp+0x100],rax
ef00: 00
ef01: 48 8d bc 24 f0 00 00 lea rdi,[rsp+0xf0]
ef08: 00
ef09: e8 72 fe ff ff call 0xed80
ef0e: 48 89 94 24 20 01 00 mov QWORD PTR [rsp+0x120],rdx
ef15: 00
ef16: 48 89 84 24 18 01 00 mov QWORD PTR [rsp+0x118],rax
--
f119: 48 89 04 24 mov QWORD PTR [rsp],rax
f11d: 48 8b 04 24 mov rax,QWORD PTR [rsp]
f121: c6 00 0a mov BYTE PTR [rax],0xa
f124: b4 00 mov ah,0x0
f126: cd 16 int 0x16
f128: e9 d4 fd ff ff jmp 0xef01
f12d: 48 8d 3d cc 0a 00 00 lea rdi,[rip+0xacc] # 0xfc00
f134: e8 07 04 00 00 call 0xf540
f139: 0f 0b ud2
f13b: 48 8d 3d e6 0a 00 00 lea rdi,[rip+0xae6] # 0xfc28
f142: e8 f9 03 00 00 call 0xf540
What am I doing wrong?
Upvotes: 4
Views: 168
Reputation: 18503
I'd like to explain zx485's comment in more detail:
I've often seen that assembly language beginners are confused about what instructions like int
(x86), syscall
(MIPS) or SWI
(ARM) actually do.
Actually, these instructions are a special form of call
instruction: They call some sub-routine which is typically located in the operating system.
64-bit x86 CPUs have different operating modes. One of them is named "real mode". In this mode the CPU can only execute 16-bit code.
The BIOS sub-routine that can be called using int 0x16
only works when the PC is operating in "real mode".
The fact that your program is a 64-bit program (registers like rax
are used) tells us that your CPU is not running in real mode.
If you are writing your own operating system, you can define own sub-routines that are called by certain int
instructions:
You can define a sub-routine that is called by int 0x16
which reads the keyboard and another one called by int 0x10
writing to the screen.
However, you are also free to define that int 0x16
is used for writing to the screen and int 0x10
is used for hard disk access in your operating system.
And in every case you'll have to write the sub-routines yourself because the existing sub-routines in the BIOS cannot be used in any other operating mode than "real mode". (This is what Ross Ridge is indicating in his comment.)
Upvotes: 4