Tuesday, February 18, 2025

Let us print text from the UEFI boot loader in assembly

1. Raam Raam ji (greeting with god's name :) ). This is our second blog post in the operating system development series. If you haven't, you can read the first blog post about creating our first UEFI boot loader in assembly here.

Today, we will code and run what the title of the post says.




struc UINT32 {
	align 4	; ensure 4-byte alignment
	. dd ?	; reserve 4 bytes (32-bit) of uninitialized space
}

struc void {
	align 8
    	. dq ?	; reserve 8 bytes (64-bit) of uninitialized space
}

macro struct name {
	virtual at 0
		name name
	end virtual
}

struc EFI_TABLE_HEADER {
    .Signature		void
    .Revision		UINT32
    .HeaderSize		UINT32
    .CRC32    		UINT32
    .Reserved		UINT32
}
struct EFI_TABLE_HEADER

struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
    .Reset            		void
    .OutputString   		void
    .TestString      		void
    .QueryMode          	void
    .SetMode            	void
    .SetAttribute       	void
    .ClearScreen        	void
    .SetCursorPosition  	void
    .EnableCursor       	void
    .Mode             		void
}
struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL

struc EFI_SYSTEM_TABLE {
    .Hdr                    EFI_TABLE_HEADER
    .FirmwareVendor         void
    .FirmwareRevision       UINT32
    .ConsoleInHandle        void
    .ConIn                  void
    .ConsoleOutHandle       void
    .ConOut                 void
    .StandardErrorHandle    void
    .StdErr                 void
    .RuntimeServices        void
    .BootServices           void
    .NumberOfTableEntries   void
    .ConfigurationTable     void
}
struct EFI_SYSTEM_TABLE

format pe64 efi

entry start

section '.text' code executable readable

start:
	; store the image handle and the system table pointer passed by the
	; firmware
	mov [image_handle], rcx
	mov [system_table], rdx
        
	push rbp
	sub rsp,0x20	; allocate stack space
        
	mov rax,[system_table]
	mov rdx,test_ok_text
	mov rcx,[rax+EFI_SYSTEM_TABLE.ConOut]
	mov rcx,[rcx+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString]
	call rcx
  
	add rsp,0x20	; restore stack

	; hang here
	jmp $

section '.data' data readable writeable

image_handle   dq ?
system_table   dq ?
test_ok_text:  du 'Test OK',0

Save the above program in a file named `boot.asm` and assemble it as:
    `fasm boot.asm BOOTx64.EFI`

2. Now prepare a pen drive with the boot loader image and test it on your real machine by following the procedure given in the point #2 from the previous blog post.
Here is the output:


3. Now let us understand the above code a little bit:


struc UINT32 {
	align 4	; ensure 4-byte alignment
	. dd ?	; reserve 4 bytes (32-bit) of uninitialized space
}

struc void {
	align 8
    	. dq ?	; reserve 8 bytes (64-bit) of uninitialized space
}

These two structure definitions create standard data types with proper alignment. These structures are used when defining UEFI system tables and structures where proper alignment is necessary.


macro struct name {
	virtual at 0
		name name
	end virtual
}

This macro defines a struct-like alias, allowing us to create named structures without allocating memory immediately. It allows a named structure to be used as a structure reference.

The next three structures above define key UEFI system tables used for interacting with the firmware. You can check the UEFI specification document for the structures of these tables.
  • EFI_TABLE_HEADER – A common header used in UEFI tables, containing metadata like a signature, version, and checksum.
  • EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL – Defines function pointers for text output operations (e.g., printing text to the screen, clearing the screen, setting the cursor position).
  • EFI_SYSTEM_TABLE – The main UEFI system table, providing access to firmware services, console input/output, and runtime/boot services.
  • These structures allow the boot loader to interface with UEFI firmware, enabling operations like printing text on the screen.


    Now beginning from our entry point, first we store the image handle and the system table pointer passed by the firmware in the `rcx` and `rdx` registers respectively.


    The subsequent code prints a test message using UEFI’s text output protocol and goes into an infinite loop. First, we setup the stack by saving the base pointer (`rbp`) and allocating stack space (0x20 bytes) for function calls. Then we get UEFI conout (console output interface) interface and finally get `OutputString` function and print text by loading the string address into `rdx`.


    Then we restore stack and execute the infinite loop.


    Please try the above code and let me know if you have any comments or suggestions :)



    Thursday, January 30, 2025

    Raam kernel version 0.01 release _/\_

    Raam Raam ji _/\_ _/\_ _/\_ (greeting with god's name :) )

    Raam is a 64-bit Unix-like kernel for x86-64 based PC.

    I have been creating it since the beginning of this year. It is a proprietary software and I'm happy to share that I'm releasing its first version for free! "Release early" is my mantra! The aim of this release is to create the "core infrastructure" needed to enable the process management and the filesystem management in the kernel.

    What have I implemented?


    1. A UEFI boot loader
    2. GDT (global descriptor table)
    3. IDT (interrupt descriptor table)
    4. interrupt handling
    5. tty (output) driver initialization
    6. printk implementation
    7. acpi and pcie initializations for NVMe driver
    8. NVMe driver
    9. PIT timer driver
    10. PIC driver
    11. handler for timer interrupts

    Please Note:

    Running the kernel executable on your 64-bit intel machine may lead to data loss or corruption. It may also make your system unstable as I can't test the binary on all the possible machines. It works fine on my and my sister's machine though. Please continue at your own risk.


    What do you need to test it?

    1. The kernel executable file. Please download it from here and get the `BOOTx64.EFI` file.
    2. A laptop or desktop PC with 64-bit intel cpu supporting UEFI booting, ACPI version 2, NVMe over PCIe SSD, PIT and PIC devices (available on most of the modern systems).
    3. A pen drive formatted as GPT with FAT filesystem. Create two directories viz. `/EFI/BOOT`. Copy the kernel file `BOOTx64.EFI` to `/EFI/BOOT/`.

    Reboot your machine and on boot, press the key required to get the boot menu (just google it for your model) and select your usb thumb drive from it. Voila! It should run and you will get a screen filled with various initializations and timer fires.

    That's it.

    If you like, please post the screenshot in the comments by uploading it somewhere.

    It is the passion and the hobby :)

    Note: The kernel works on my HP laptop and my elder sister's Dell laptop.

    Let us create our first UEFI boot loader in assembly

    Raam Raam ji (greeting with god's name :) ). Hello world. This is our first blog post in the operating system development series.

    Today, we will try to address this fundamental question - "How does an OS (Operating System) boot? What happens first?"

    Let's get our hands dirty. Let us learn by doing. So it is the "firmware", the UEFI firmware on modern systems that is responsible for loading and calling your OS boot loader's entry point.

    If you don't know what is UEFI, it stands for "Unified Extensible Firmware Interface". You don't need to know what is it. Just know that it is both different and newer than BIOS (Basic Input/Output System). It's a firmware that loads your OS's boot loader into RAM and calls its entry point. Simple!

    Now what is a boot loader? It is a boot program that performs some initialization tasks, loads the kernel file into memory, and provides the kernel with the information it needs to work correctly.

    So the first system program that runs after booting by the firmware is your boot loader which in turns loads and jumps to your kernel's entry point. A kernel is the core and central component of an OS. The userspace program like your shell runs later.

    1. Enough theory! Now let's create a simple boot loader that does nothing but goes into an infinite loop and you will test it on your real machine:

    
    format pe64 efi
    
    entry start
    
    section '.text' code executable readable
    
    start:
    	; infinite loop
    	jmp $
    

    Open your favorite text editor (I like `vim`) and paste the above code. Save it as boot.asm and then on a 64-bit Ubuntu like machine, install fasm assembler with apt install fasm. Then run the following command:
    
    fasm boot.asm BOOTx64.EFI

    On successful passes, you will get the output file `BOOTx64.EFI`.


    2. Now you need to format a pen drive in order to make it bootable and then we will copy this boot loader program into it.

    Open `Disks` program on your Ubuntu-like machine and select your pen drive from the leftmost column. Now click on Drive Options (upper three dots) in the right and then on Format Disk.... Now select the options as below:



    click Format.... Now click on the plus button (+) below the Volumes label, click Next, choose FAT and then finally click Create. Now mount this partition and create two directories viz. EFI/BOOT on it. Now copy the above boot loader executable to <mount point>/EFI/BOOT/BOOTx64.EFI .

    Reboot.

    Now when your machine powers on, press the key to select the boot options (just google for it), select your USB thumb drive, and voila... we got a steady blank screen because of the infinite loop!

    The code that you had just typed and assembled is now working!

    3. Now let us understand the above code a bit:

    `format pe64 efi` tells your assembler that it is going to be a 64-bit Portable Executable output format, with UEFI as the target subsystem.

    `entry start` tells that the entry point for this loader application is the `start` label.

    `section '.text' code executable readable` tells the assembler about the text section where the instructions of the loader program goes and this section is both executable and readable by the processor.

    `start:` is our start label (a label in assembly is a mnemonic for an address). It can be any name.

    `; infinite loop` is a comment telling that the following line will execute an infinite loop.

    Finally, `jmp $` tells the program control to jump to the present address (which is this line itself) and so it goes into an infinite loop.

    Thursday, June 13, 2024

    || चालोनी रे हंसा अपना राम जना रा देश ||

    Raam Raam sa🌹🌹🌹🙏🙏🙏






     



    Raamdwara Shahpura || Raamcharan Jī Mahārāja || Raamsnehi

     Raam Raam sa 🌹🌹🌹🙏🙏🙏

    Raamdwara Shahpura's video (short film) created by me:



    || परमपस्तावा ||

    || राम ||

    || RAAM ||


    || THE ULTIMATE REGRET ||


    Humans in the world of death (Mrityuloka) and the deities in the world of

    deities (Devaloka) have forgotten to chant (bhaj) the name of "GOD" who created

    them and the Honakal, which includes the Parbrahm, the divine feminine energy

    (Shakti), Brahma, Shiva, Vishnu, the world of deities (Devaloka), the world of

    death (Mrityuloka, including birds, animals, worms, insects, trees, and plants),

    and even hell (Narakaloka). Instead of chanting (bhajana) the name of "GOD", we

    have entangled ourselves in worldly deeds and relationships, illusion (Māyā),

    and in other lords and deities. This is the ultimate regret!!!


    In a nutshell, we have entangled ourselves in "others" and have forgotten to

    chant (bhaj) the name of "GOD" who created us and others. This is the ultimate regret!!!


    🙏 🌹 Raam Raam sa 🌹 🙏

    Let us print text from the UEFI boot loader in assembly

    1. Raam Raam ji (greeting with god's name :) ). This is our second blog post in the operating system development series. If yo...