<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Kernel - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Kernel - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 22 May 2026 17:40:12 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/kernel/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Retrieve System Information Using The CPUID Instruction ]]>
                </title>
                <description>
                    <![CDATA[ When developing a bootloader/kernel, understanding the underlying architecture is crucial for optimizing performance and compatibility between software and hardware. One important yet sometimes overlooked tool available to engineers for querying and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/retrieve-system-information-using-cpuid/</link>
                <guid isPermaLink="false">66fe6e2eac038fabde9a34bd</guid>
                
                    <category>
                        <![CDATA[ Kernel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ operating system ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cpu ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nikolaos Panagopoulos ]]>
                </dc:creator>
                <pubDate>Thu, 03 Oct 2024 10:13:02 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/JMwCe3w7qKk/upload/bb94515f8210b64d35039199912a3b6c.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When developing a bootloader/kernel, understanding the underlying architecture is crucial for optimizing performance and compatibility between software and hardware.</p>
<p>One important yet sometimes overlooked tool available to engineers for querying and retrieving system information is the CPUID instruction.</p>
<h3 id="heading-what-is-the-cpuid-instruction">What is the CPUID Instruction?</h3>
<p>The CPUID instruction is a low level instruction, inside the heart of every modern x86 and x86-64 processor that allows the software to query the CPU for information about the processor and its supported features.</p>
<p>By invoking this instruction, you can gather information such as the processor’s model, family, internal cache sizes, and supported features like <a target="_blank" href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">SIMD</a> or hardware virtualization. This can help you optimize performance and dynamically enable or disable supported features.</p>
<p>For bootloader or kernel developers, understanding what features a processor supports—such as hardware virtualization, cache sizes, or SIMD instructions—can ensure that the system runs efficiently and that the code you write is compatible across different CPUs. By utilizing the CPUID instruction, you can dynamically adjust your kernel’s behavior based on the specific processor it is running on.</p>
<p>In this article you will learn how to check if the CPUID instruction is available for your system, how it works and what information you can get from using it.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>Some knowledge of assembly language (for this example I use FASM)</p>
</li>
<li><p>Some knowledge of operating systems/kernels</p>
</li>
<li><p>Access to low-level debugging tools (for example, GDB) or hardware emulators like QEMU to test your bootloader/kernel on various platforms.</p>
</li>
</ul>
<h2 id="heading-step-1-check-for-cpuid-availability">Step 1: Check for CPUID Availability</h2>
<p>Before executing the CPUID instruction, it's important to determine whether the processor supports it, as not all CPUs are guaranteed to have this functionality. The following code checks the availability of the CPUID instruction by modifying and testing the ID bit (bit 21) in the EFLAGS register.</p>
<p>Here’s a picture from <a target="_blank" href="https://wiki.osdev.org/Expanded_Main_Page">wiki.osdev.org</a> that shows each bit of the EFLAGS register:</p>
<p><a target="_blank" href="https://wiki.osdev.org/CPU_Registers_x86"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727637307676/82ad4bf5-3906-49a3-a12a-6cb83cc852db.png" alt="82ad4bf5-3906-49a3-a12a-6cb83cc852db" class="image--center mx-auto" width="478" height="632" loading="lazy"></a></p>
<p>If the processor allows this bit to be toggled, CPUID is supported; otherwise, it is not. Here's how the detection process works:</p>
<p>(most people think that in Real mode 32 registers are not accessible. That is not true. All 32bit registers are usable)</p>
<pre><code class="lang-plaintext">cpuid_check:
    pusha                                ; save state
    pushfd                               ; Save EFLAGS
    pushfd                               ; Store EFLAGS
    xor dword [esp],0x00200000           ; Invert the ID bit in stored EFLAGS
    popfd                                ; Load stored EFLAGS (with ID bit inverted)
    pushfd                               ; Store EFLAGS again (ID bit may or may not be inverted)
    pop eax                              ; eax = modified EFLAGS (ID bit may or may not be inverted)
    xor eax,[esp]                        ; eax = whichever bits were changed
    popfd                                ; Restore original EFLAGS
    and eax,0x00200000                   ; eax = zero if ID bit can't be changed, else non-zero
    cmp eax,0x00
    je .cpuid_instruction_not_is_available
.cpuid_instruction_is_available:
    ;handle CPUID exists
.cpuid_instruction_not_is_available:
    ;handle CPUID isn't supported
.cpuid_check_end:
    popa                                  ; restore state
    ret
</code></pre>
<p><code>pusha</code>: Saves all the general purpose registers to ensure the original state can be restored at the end.</p>
<p><code>pushfd</code>: Saves the current EFLAGS register.</p>
<p><code>pushfd</code>: Stores a copy of the EFLAGS.</p>
<p><code>xor dword [esp], 0x00200000</code>: The code flips the ID bit (21) of the EFLAGS using the XOR operator.</p>
<p><code>popfd</code>: Restores the modified EFLAGS with the ID bit inverted.</p>
<p><code>pushfd</code>: Pushes the modified EFLAGS back to the stack.</p>
<p><code>pop eax</code>: Puts the modified EFLAGS (ID bit may or may not be inverted) in the EAX register.</p>
<p><code>xor eax, [esp]</code>: After the XOR operation, the EAX will contain the bits that were changed.</p>
<p><code>popfd</code>: Restores the original EFLAGS.</p>
<p><code>and eax, 0x00200000</code>: The <code>and</code> operation isolates the 21st bit (ID bit) by masking all other bits. After this operation the EAX register will contain either 0x00200000 (if 21 bit was changed which means CPUID is supported) or 0×00 (21 bit hasn’t changed, CPUID not supported).</p>
<p><code>cmp eax, 0x00</code>: The CMP instruction checks the result of the previous operation. If EAX equals 0×00, it means that the ID bit cannot be modified and the processor doesn’t support the CPUID instruction. If it is not zero, it means that the ID bit was flipped and your processor supports the CPUID instruction.</p>
<h2 id="heading-step2-how-to-use-the-cpuid-instruction">Step2: How to Use The CPUID Instruction</h2>
<h3 id="heading-get-cpu-features">Get CPU Features</h3>
<p>The CPUID instruction will return different information with different values in the EAX register.</p>
<pre><code class="lang-plaintext">mov eax, 0x1
cpuid
</code></pre>
<p>With EAX set to 1, the CPUID will return a bitfield in EDX, which will contain the following values. Different brands may give different meaning to these (source <a target="_blank" href="https://wiki.osdev.org/CPUID">https://wiki.osdev.org/CPUID</a>)</p>
<pre><code class="lang-c"><span class="hljs-keyword">enum</span> {
    CPUID_FEAT_ECX_SSE3         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">0</span>,
    CPUID_FEAT_ECX_PCLMUL       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">1</span>,
    CPUID_FEAT_ECX_DTES64       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">2</span>,
    CPUID_FEAT_ECX_MONITOR      = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">3</span>,
    CPUID_FEAT_ECX_DS_CPL       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">4</span>,
    CPUID_FEAT_ECX_VMX          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">5</span>,
    CPUID_FEAT_ECX_SMX          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">6</span>,
    CPUID_FEAT_ECX_EST          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">7</span>,
    CPUID_FEAT_ECX_TM2          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">8</span>,
    CPUID_FEAT_ECX_SSSE3        = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">9</span>,
    CPUID_FEAT_ECX_CID          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">10</span>,
    CPUID_FEAT_ECX_SDBG         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">11</span>,
    CPUID_FEAT_ECX_FMA          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">12</span>,
    CPUID_FEAT_ECX_CX16         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">13</span>,
    CPUID_FEAT_ECX_XTPR         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">14</span>,
    CPUID_FEAT_ECX_PDCM         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">15</span>,
    CPUID_FEAT_ECX_PCID         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">17</span>,
    CPUID_FEAT_ECX_DCA          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">18</span>,
    CPUID_FEAT_ECX_SSE4_1       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">19</span>,
    CPUID_FEAT_ECX_SSE4_2       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">20</span>,
    CPUID_FEAT_ECX_X2APIC       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">21</span>,
    CPUID_FEAT_ECX_MOVBE        = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">22</span>,
    CPUID_FEAT_ECX_POPCNT       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">23</span>,
    CPUID_FEAT_ECX_TSC          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">24</span>,
    CPUID_FEAT_ECX_AES          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">25</span>,
    CPUID_FEAT_ECX_XSAVE        = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">26</span>,
    CPUID_FEAT_ECX_OSXSAVE      = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">27</span>,
    CPUID_FEAT_ECX_AVX          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">28</span>,
    CPUID_FEAT_ECX_F16C         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">29</span>,
    CPUID_FEAT_ECX_RDRAND       = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">30</span>,
    CPUID_FEAT_ECX_HYPERVISOR   = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">31</span>,

    CPUID_FEAT_EDX_FPU          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">0</span>,
    CPUID_FEAT_EDX_VME          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">1</span>,
    CPUID_FEAT_EDX_DE           = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">2</span>,
    CPUID_FEAT_EDX_PSE          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">3</span>,
    CPUID_FEAT_EDX_TSC          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">4</span>,
    CPUID_FEAT_EDX_MSR          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">5</span>,
    CPUID_FEAT_EDX_PAE          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">6</span>,
    CPUID_FEAT_EDX_MCE          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">7</span>,
    CPUID_FEAT_EDX_CX8          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">8</span>,
    CPUID_FEAT_EDX_APIC         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">9</span>,
    CPUID_FEAT_EDX_SEP          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">11</span>,
    CPUID_FEAT_EDX_MTRR         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">12</span>,
    CPUID_FEAT_EDX_PGE          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">13</span>,
    CPUID_FEAT_EDX_MCA          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">14</span>,
    CPUID_FEAT_EDX_CMOV         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">15</span>,
    CPUID_FEAT_EDX_PAT          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">16</span>,
    CPUID_FEAT_EDX_PSE36        = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">17</span>,
    CPUID_FEAT_EDX_PSN          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">18</span>,
    CPUID_FEAT_EDX_CLFLUSH      = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">19</span>,
    CPUID_FEAT_EDX_DS           = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">21</span>,
    CPUID_FEAT_EDX_ACPI         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">22</span>,
    CPUID_FEAT_EDX_MMX          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">23</span>,
    CPUID_FEAT_EDX_FXSR         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">24</span>,
    CPUID_FEAT_EDX_SSE          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">25</span>,
    CPUID_FEAT_EDX_SSE2         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">26</span>,
    CPUID_FEAT_EDX_SS           = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">27</span>,
    CPUID_FEAT_EDX_HTT          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">28</span>,
    CPUID_FEAT_EDX_TM           = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">29</span>,
    CPUID_FEAT_EDX_IA64         = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">30</span>,
    CPUID_FEAT_EDX_PBE          = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">31</span>
};
</code></pre>
<p>A brief explanation of the CPU features above:</p>
<ul>
<li><p><code>PCLMUL, AES</code>: Cryptographic instruction sets for fast encryption and decryption.</p>
</li>
<li><p><code>VMX, SMX</code>: Virtualization support for running virtual machines.</p>
</li>
<li><p><code>SSE3, SSSE3, SSE4.1, SSE4.2, AVX</code>: SIMD instruction sets for faster multimedia, math, and vector processing.</p>
</li>
<li><p><code>FMA</code>: Fused Multiply-Add, improves performance in floating-point calculations.</p>
</li>
<li><p><code>RDRAND</code>: Random number generator.</p>
</li>
<li><p><code>X2APIC</code>: Advanced interrupt handling in multiprocessor systems.</p>
</li>
<li><p><code>PCID</code>: Optimizes memory management during context switches.</p>
</li>
<li><p><code>FPU</code>: Hardware floating-point unit for faster math operations.</p>
</li>
<li><p><code>PAE</code>: Physical Address Extension, allows addressing more than 4 GB of memory.</p>
</li>
<li><p><code>HTT</code>: Allows a single CPU core to handle multiple threads.</p>
</li>
<li><p><code>PAT, PGE</code>: Memory management features for controlling caching and page mapping.</p>
</li>
<li><p><code>MMX, SSE, SSE2</code>: Older SIMD instruction sets for multimedia processing.</p>
</li>
</ul>
<h3 id="heading-get-cpu-vendor-string">Get CPU Vendor String</h3>
<p>If you want to get the CPU vendor string, EAX should be set to 0×0 before invoking the CPUID instruction.</p>
<pre><code class="lang-plaintext">mov eax, 0x0
cpuid
</code></pre>
<p>The vendor string is a unique identifier that CPU vendors like AMD and Intel use. Examples are: GenuineIntel (for Intel processors) or AuthenticAMD (for AMD processors). It basically specifies the manufacturer of the CPU.</p>
<p>The vendor string allows the kernel to identify the CPU manufacturer which is very useful because different manufacturers implement certain features differently. Also, software or drivers can interact differently based on the CPU manufacturer to ensure compatibility.</p>
<p>When used like this, the vendor id string will be returned in EBX, EDX, ECX registers. You can write them to a buffer and get the full 12 character string.</p>
<p>Example code:</p>
<h3 id="heading-step-1-the-buffer">Step 1: The Buffer</h3>
<p>Create a buffer that can hold 12 bytes:</p>
<pre><code class="lang-plaintext">buffer: db 12 dup(0), 0xA, 0xD, 0
</code></pre>
<h3 id="heading-step-2-print-the-buffer">Step 2: Print the Buffer</h3>
<p>We start by creating a string printing function.</p>
<p>This assembly code reads a string character by character and prints it to the screen using BIOS interrupt 0x10. The <code>print</code> function loops through the string and uses the <code>lodsb</code> instruction to load each character in the <code>al</code> register.</p>
<p>Then the <code>print_char</code> function uses the interrupt 0×10 to print it on the screen. When the code reaches the end of the string (null terminator), the loop ends.</p>
<pre><code class="lang-plaintext">print_string:
    call print
    ret
print:
.loop:  
    lodsb   ;read character to al and then increment
    cmp al ,0 ;check if we reached the end
    je .done  ;we reached null terminator, finish
    call print_char ;print character
    jmp .loop   ;jump back into the loop
.done:
    ret
print_char:
    mov ah, 0eh
    int 0x10
    ret
</code></pre>
<h3 id="heading-step-3-fill-the-buffer-and-print-it">Step 3: Fill the Buffer and Print it</h3>
<p>Here, after saving the current state using the <code>pusha</code> instruction and calling <code>cpuid</code> with 0×0 passed in the EAX register, we can store the contents of <code>ebx</code>, <code>edx</code>, <code>ecx</code> to the buffer. Then we call <code>print_string</code> to print it.</p>
<pre><code class="lang-plaintext">get_cpu_vendor:
    pusha
    mov eax, 0x0
    cpuid
    mov [buffer], ebx
    mov [buffer + 4], edx
    mov [buffer + 8], ecx
    mov si, buffer 
    call print_string
    popa
    ret
</code></pre>
<p>A video from my YouTube channel where I implement and explain the code above in detail</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/K0Rxq2AIMmo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>More information about what information CPUID instruction can give you according to the value passed in the EAX register, can be found here: <a target="_blank" href="https://gitlab.com/x86-cpuid.org/x86-cpuid-db">https://gitlab.com/x86-cpuid.org/x86-cpuid-db</a></p>
<h3 id="heading-epilogue">Epilogue</h3>
<p>By understanding and using the CPUID instruction, you can make your bootloader/kernel more adaptable to a wide range of processors. Knowing how to detect the instruction's availability and retrieve crucial system information—such as CPU features, cache sizes, and supported technologies—can significantly enhance performance and compatibility.</p>
<p>After reading this article, you should have the tools and knowledge to start exploring the CPUID instruction and how you can use it in your own project!</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Get a Memory Map of Your System using BIOS Interrupts ]]>
                </title>
                <description>
                    <![CDATA[ When you are developing a kernel, one of the most important things is memory. The kernel must know how much memory is available and where it's located to avoid overwriting crucial system resources. But not all memory is freely available for use. Some... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-a-memory-map-of-your-system-using-bios-interrupts/</link>
                <guid isPermaLink="false">66f177c1aa7c0509267cf26e</guid>
                
                    <category>
                        <![CDATA[ Kernel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory-management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nikolaos Panagopoulos ]]>
                </dc:creator>
                <pubDate>Mon, 23 Sep 2024 14:14:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/iar-afB0QQw/upload/7b7f724f7260216b7427408112d5f8c4.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you are developing a kernel, one of the most important things is memory. The kernel must know how much memory is available and where it's located to avoid overwriting crucial system resources.</p>
<p>But not all memory is freely available for use. Some memory sections are reserved for system functions and others may be occupied by hardware devices. That’s why it is very important to get the system’s memory map.</p>
<h3 id="heading-what-is-a-memory-map">What is a Memory Map?</h3>
<p>But what is a memory map? A memory map is a representation (think about it like a table) that shows how physical memory is organized in your system. It shows the address of each memory region, it’s length and it’s type.</p>
<p>Type 1 means that the region is available for you to use freely and type 2 means that it is reserved by your system. Type 3 means that the region is reserved for the Advanced configuration and power interface (ACPI 3.x). While a type 3 region might not be used by the system, it can be reclaimed later.</p>
<p>Using a memory map will allow you to manage memory resources successfully without any issues such as crashes or system instability.</p>
<p>There are some ways you can detect your system’s available memory. One is by using the BIOS and interrupt 15h. Another one is by doing memory probing.</p>
<p>In this article you will learn which tools are available to help you get a memory map of your system, which ones you should use, and which ones you should avoid and why. Then finally, you will see some assembly code that you can use in your own bootloader / kernel.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>if you want to follow along with the code shown in this article, you’ll need:</p>
<ul>
<li><p>A Linux operating system</p>
</li>
<li><p>Some knowledge of assembly language</p>
</li>
<li><p>A text editor of your choice</p>
</li>
<li><p>An emulator installed. For this example I use QEMU.</p>
</li>
<li><p>FASM assembler installed</p>
</li>
<li><p>Git to be able to clone the repository (<a target="_blank" href="https://github.com/nikolaospanagopoulos/memoryMapBoot">https://github.com/nikolaospanagopoulos/memoryMapBoot</a>)</p>
</li>
</ul>
<h3 id="heading-a-few-words-about-bios-int-15h">A Few Words about BIOS int 15h</h3>
<p>In Real mode, the BIOS offers many interrupts that interact with the hardware and can give you information.</p>
<p>There are some interrupts that can help with getting a memory map, but the most powerful one is int15h with E820h function (hexadecimal numbers! very important to remember. Decimal numbers will not work). This method offers a detailed memory map that you can use to safely determine which areas of memory can be used for vital tasks like setting up paging, memory allocation, and more.</p>
<p>In this article you will see how you can use this interrupt to get a detailed memory map of your system.</p>
<p>Now, before we go deeper, I would like to add a few things about memory probing and why you should avoid it.</p>
<h3 id="heading-memory-probing-and-why-you-should-avoid-it">Memory probing and why you should avoid it</h3>
<p>Memory probing is the process of manually accessing physical memory and determining whether it is available or not. The issue is that not all memory is designed to be accessed directly.</p>
<p>Accessing parts of memory that you shouldn’t can cause unpredictable behavior like:</p>
<ul>
<li><p><strong>System Crashes:</strong> some memory is reserved for BIOS structures, hardware devices etc. Accessing those areas can lead to system crashes or system instability.</p>
</li>
<li><p><strong>Memory Corruption:</strong> accessing reserved memory areas can lead to corruption of those areas. This can cause again crashes, instability, malfunctions etc</p>
</li>
</ul>
<p>So, you should avoid memory probing because it’s an unnecessary risk to your kernel development process.</p>
<h2 id="heading-the-code">The Code</h2>
<h3 id="heading-step-1-prepare-to-call-int-15h">Step 1: Prepare to Call int 15h</h3>
<p>In this part, you will basically setup the environment needed to invoke int 15h. The general purpose registers need to be stored so that no important data on them is lost during the interrupt invocation. Then the registers <code>bp</code>, <code>ebx</code> are cleared so that they can be set to their initial values.</p>
<p>The “SMAP” value is stored in the <code>edx</code> register to ensure the correct format that the BIOS will return. Finally, we setup the <code>0xe820</code> function and request memory map data.</p>
<pre><code class="lang-plaintext">pusha
mov di, 0x0504        ; Set DI register for memory storage
xor ebx, ebx          ; EBX must be 0
xor bp, bp            ; BP must be 0 (to keep an entry count)
mov edx, 0x534D4150   ; Place "SMAP" into edx | The "SMAP" signature ensures that the BIOS provides the correct memory map format
mov eax, 0xe820       ; Function 0xE820 to get memory map
mov dword [es:di + 20], 1 ; force a valid ACPI 3.X entry | allows us to get additional information (extended attributes)
mov ecx, 24           ; Request 24 bytes of data
</code></pre>
<ul>
<li><p>The <code>pusha</code> command pushed all general purpose registers to the stack to save their values during the interrupt call. They can be restored after the interrupt call to avoid corruption of other areas.</p>
</li>
<li><p>The <code>mov di, 0x0504</code> instruction sets the di register to 0×0504 (where the memory map entries will be stored).</p>
</li>
<li><p><code>xor ebx, ebx</code> the xor instruction uses the xor operator to clear the ebx register. It must be set to 0 to start retrieving entries.</p>
</li>
<li><p><code>xor bp, bp</code> use of the same xor operator here to set bp to 0. This will keep track of your memory entries.</p>
</li>
<li><p><code>mov edx, 0x534D4150</code> this instruction will store <code>0x534D4150</code> (ASCII string “SMAP”) into the edx register. It makes certain that the BIOS will return the correct format for your memory map.</p>
</li>
<li><p><code>mov eax, 0xe820</code> this instruction sets the function 0xe280 which will get the memory map along with int15h.</p>
</li>
<li><p><code>mov dword [es:di + 20], 1</code> this instruction forces a valid ACPI (Advanced Configuration and Power Interface) 3.x entry. This way the BIOS provides extra information in the form of extra attributes.</p>
</li>
<li><p><code>mov ecx, 24</code> this instruction asks the BIOS for 24 bytes of memory data. This is the size that ACPI 3.x entries need to include extra information.</p>
</li>
</ul>
<h3 id="heading-step-2-call-int15h">Step 2: Call int15h</h3>
<p>Here, you can finally invoke the interrupt to fetch the memory map. You need to check that the function is supported by the BIOS and that valid data is being fetched. You also need to ensure that the correct format is being fetched by setting again the “SMAP” into the <code>edx</code> register.</p>
<pre><code class="lang-plaintext">    int 0x15                 ; using interrupt
    jc short .failed         ; carry set on first call means "unsupported function"
    mov edx, 0x534D4150      ; Some BIOSes apparently trash this register? lets set it again
    cmp eax, edx             ; on success, eax must have been reset to "SMAP"
    jne short .failed
    test ebx, ebx            ; ebx = 0 implies list is only 1 entry long (worthless)
    je short .failed
</code></pre>
<ul>
<li><p><code>int 0x15</code> this instruction invokes the interrupt 0×15.</p>
</li>
<li><p><code>jc short .failed</code> is the carry flag that is set. It means the function is unsupported and the call has failed. It jumps to our error handler.</p>
</li>
<li><p><code>mov edx, 0x534D4150</code> set again the “SMAP” because some BIOSes corrupt this register after the call.</p>
</li>
<li><p><code>cmp eax, edx</code> if the call is successfull, on success the BIOS will return the “SMAP” value in eax.</p>
</li>
<li><p><code>jne short .failed</code> if it doesn’t, it means the call has failed and it jumps to our error handling label.</p>
</li>
<li><p><code>test ebx, ebx</code> this instruction checks if ebx is 0 after the first call. This means that the memory map only contains one entry. This entry is probably invalid, so it jumps to the error handling label.</p>
</li>
</ul>
<h3 id="heading-step-3-loop-through-memory-entries">Step 3: Loop Through Memory Entries</h3>
<p>After a successful first invocation, you need to loop through each entry of the memory map.</p>
<p>In the loop, you will invoke again int 15h to get all subsequent memory entries while checking each entry’s length and other attributes. If it meets the criteria, you increment the counter and you store the entry. This continues until there are no entries left to process.</p>
<pre><code class="lang-plaintext">    jmp short .jmpin
.e820lp:
    mov eax, 0xe820          ; eax, ecx get trashed on every int 0x15 call
    mov dword [es:di + 20], 1 ; force a valid ACPI 3.X entry
    mov ecx, 24              ; ask for 24 bytes again
    int 0x15
    jc short .e820f          ; carry set means "end of list already reached"
    mov edx, 0x534D4150      ; repair potentially trashed register
.jmpin:
    jcxz .skipent            ; skip any 0 length entries (If ecx is zero, skip this entry (indicates an invalid entry length))
    cmp cl, 20               ; got a 24 byte ACPI 3.X response?
    jbe short .notext
    test byte [es:di + 20], 1 ;if bit 0 is clear, the entry should be ignored
    je short .skipent         ; jump if bit 0 is clear 
.notext:
    mov eax, [es:di + 8]     ; get lower uint32_t of memory region length
    or eax, [es:di + 12]     ; "or" it with upper uint32_t to test for zero and form 64 bits (little endian)
    jz .skipent              ; if length uint64_t is 0, skip entry
    inc bp                   ; got a good entry: ++count, move to next storage spot
    add di, 24               ; move next entry into buffer
.skipent:
    test ebx, ebx            ; if ebx resets to 0, list is complete
    jne short .e820lp
</code></pre>
<ul>
<li><code>.e820lp</code> is a label for looping through each memory map entry.</li>
</ul>
<p>The next lines are used to call int15h to get the next memory entry:</p>
<ul>
<li><p><code>jc short .e820f</code> if the carry flag is set, it means that we have reached the end of the list.</p>
</li>
<li><p><code>jcxz .skipent</code> if ecx register is 0, it means the length of the memory entry is invalid. So the code skips it.</p>
</li>
<li><p><code>cmp cl, 20</code> checks if the memory entry is a valid ACPI 3.x entry. (It would be 24 bytes long). If it is not, the code jumps to <code>.notext</code>.</p>
</li>
<li><p><code>test byte [es:di + 20], 1</code> checks if bit 0 is set in the memory entry's extended attributes, indicating a valid entry. If it's clear, the entry is skipped.</p>
</li>
<li><p><code>mov eax, [es:di + 8]</code> gets the lower 32 bits of the memory region length and then we combine it using the or operator, with the upper 32 bits. If the total length is 0, then the entry is skipped.</p>
</li>
<li><p><code>inc bp</code> increments entry count.</p>
</li>
<li><p><code>add di, 24</code> moves the pointer di forward to the next memory entry. Each entry is 24 bytes long.</p>
</li>
</ul>
<h3 id="heading-step-4-end-of-memory-entries-handling">Step 4: End of Memory Entries Handling</h3>
<p>Finally, you can store the entry count. And by using the <code>popa</code> instruction, you will restore all general purpose registers to their previous values. If an error occurs during the process, the code jumps to <code>.failed</code> label which is our error handling function.</p>
<pre><code class="lang-plaintext">.e820f:
    mov [mmap_ent], bp       ; store the entry count
    clc                      ; there is "jc" on end of list to this point, so the carry must be cleared

    popa
    ret
.failed:
    stc                      ; "function unsupported" error exit
    ret
</code></pre>
<ul>
<li><p><code>mov [mmap_ent], bp</code> stores the entry count.</p>
</li>
<li><p><code>clc</code> clears the carry flag because it is already set.</p>
</li>
<li><p><code>popa</code> pops all general purpose registers back from the stack.</p>
</li>
<li><p><code>.failed</code> we use this label for error handling.</p>
</li>
</ul>
<p>Here is a video from my YouTube account where I implement and explain the above code:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/WW3pduHMWkc" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-epilogue">Epilogue</h3>
<p>In kernel development, one of the most important tasks is managing memory. The above is a reliable way to detect your system’s memory layout information. This means that you can make safe decisions when allocating resources, implementing paging, and so on.</p>
<p>It might appear to be complex and it maybe is, but if you follow the code line by line you will be able to understand it. These techniques will allow you to build a robust kernel capable of running on different hardware configurations.</p>
<p>Keep Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
