{"id":276,"date":"2009-08-10T21:47:12","date_gmt":"2009-08-11T05:47:12","guid":{"rendered":"http:\/\/www.pagetable.com\/?p=276"},"modified":"2009-08-10T21:47:12","modified_gmt":"2009-08-11T05:47:12","slug":"minimizing-the-assembly-needed-for-machine-initialization","status":"publish","type":"post","link":"https:\/\/www.pagetable.com\/?p=276","title":{"rendered":"Minimizing the Assembly needed for Machine Initialization"},"content":{"rendered":"<p>In many operating systems, I have seen overly complicated startup code. Too much is done in assembly, and <a href=\"http:\/\/www.pagetable.com\/?p=298\">printf()<\/a> and framebuffer access is only available very late. In the next three blog posts, I will show how this can be avoided.<\/p>\n<p>In this post, I am showing how little assembly code is needed for startup. Minimizing the assembly makes your code significantly more maintainable. Everything that really needs to be done is setting up the CPU state to support 64 bit (or 32 bit) C code running at physical addresses, and everything else, including setting up the final machine state, can be done in C with a little inline assembly. The following example switches from 16 bit (real mode) or 32 bit mode (flat protected mode) into 64 bit mode on an x86_64 CPU (NASM syntax):<\/p>\n<pre>\nPAGE_SIZE\tequ\t0x1000\nSTACK_SIZE\tequ\t16*1024\n\nPML2\t\tequ\t0x1000\nPML3\t\tequ\tPML2+PAGE_SIZE\nPML4\t\tequ\tPML3+PAGE_SIZE\n\nSTACK_BOTTOM\tequ\tPML4+PAGE_SIZE\nSTACK_TOP\tequ\tSTACK_BOTTOM+STACK_SIZE\n\nCODE_START\tequ\tSTACK_TOP\n\n\torg CODE_START\n\n\t[BITS 16]\n\tcli\n\n\t; clear 3 pages of pagetables\n\tmov edi, PML2\n\txor eax, eax\n\tmov ecx, 3*4096\/4\n\trep stosd\n\n\t; set up pagetables\n\tmov dword [PML2], 0x87\t\t; single 4 MB id mapping PML2\n\tmov dword [PML3], PML2 | 7\t; single entry at PML3\n\tmov dword [PML4], PML3 | 7\t; single entry at PML4\n\n\t; load the GDT\n\tlgdt [gdt_desc]\n\n\t; set PSE,  PAE\n\tmov eax, 0x30\n\tmov cr4, eax\n\n\t; long mode\n\tmov ecx, 0xc0000080\n\trdmsr\n\tor eax, 0x100\n\twrmsr\n\n\t; enable pagetables\n\tmov eax, PML4\n\tmov cr3, eax\n\n\t; turn on long mode and paging\n\tmov eax, 0x80010001\n\tmov cr0, eax\n\n\tjmp SEL_CS:code64\n\ncode64:\n\t[BITS 64]\n\tmov ax, SEL_DS\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tmov ss, ax\n\n\tmov sp, STACK_TOP\n\tcall start\n\ninf:\tjmp inf\n\ngdt_desc:\n\tdw  GDT_LEN-1\n\tdd  gdt\n\talign 8\ngdt\tdb 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 0x00 dummy\ngdt_cs\tdb 0xff, 0xff, 0x00, 0x00, 0x00, 0x9b, 0xaf, 0x00 ; 0x08 code64\ngdt_ds\tdb 0xff, 0xff, 0x00, 0x00, 0x00, 0x93, 0xaf, 0x00 ; 0x18 data64\n\nGDT_LEN equ $ - gdt\nSEL_CS equ gdt_cs - gdt\nSEL_DS equ gdt_ds - gdt\n<\/pre>\n<p>While switching into 32 bit flat mode is trivial, switching into 64 bit mode requires setting up pagetables. This code sets up 4 MB of identity-mapped memory starting at address 0.<\/p>\n<p>The code is designed to switch from 16 bit mode into 64 bit mode, but since 16 and 32 bit flat mode on i386 are assembly source compatible, you can replace &#8220;[BITS 16]&#8221; with &#8220;[BITS 32]&#8221;, and the code will switch from 32 bit to 64 bit mode. (Yes, it is possible to switch from real mode directly into 64 bit mode: <a href=\"http:\/\/forum.osdev.org\/viewtopic.php?t=11093\">osdev.org Forums<\/a>)<\/p>\n<p>If you use this code, be careful about the memory layout. The example leaves the first page in memory untouched (in case you need the original real mode BIOS IDT for something later), and occupies the next few pages for the pagetables and the stack. Your code should be above that, but, on a BIOS system, not be between 640 KB and 1 MB (this might be device memory and ROM), and also not above 1 MB before you have enabled the A20 gate.<\/p>\n<p>While this code is enough to support 64 bit C code, this is not enough to set up the machine to support all aspects of an operating system. You probably want to set up your own GDT that has entries for 32 bit code and data too, you want to set up an IDT in order to be able to catch exceptions and interrupts, and you will need real pagetables to support virtual memory. Also, you will have to move your stack pointer once you have your final memory layout.<\/p>\n<p>But it is possible to construct the overly complicated GDT, IDT and pagetable structures using readable C code, and the &#8220;lidt&#8221;, &#8220;lgdt&#8221; etc. instructions can be done in inline assembly. While this is not portable C code, it is possible to reuse large parts of the machine initialization for a 32 bit (i386) and a 64 bit (x86_64) version of the operating system, which is not as easy to get right in assembly.<\/p>\n<p>In my next post, I am going to show how easy it is to get printf() working as soon as you reach C, so you don&#8217;t have to mess around with puts()- and print_hex()-like functions in early machine initialization.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In many operating systems, I have seen overly complicated startup code. Too much is done in assembly, and printf() and framebuffer access is only available very late. In the next three blog posts, I will show how this can be avoided. In this post, I am showing how little assembly code is needed for startup. &#8230; <a title=\"Minimizing the Assembly needed for Machine Initialization\" class=\"read-more\" href=\"https:\/\/www.pagetable.com\/?p=276\" aria-label=\"Read more about Minimizing the Assembly needed for Machine Initialization\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,32,38],"tags":[],"class_list":["post-276","post","type-post","status-publish","format-standard","hentry","category-operating-systems","category-tricks","category-x86"],"_links":{"self":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts\/276","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=276"}],"version-history":[{"count":0,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts\/276\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=276"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=276"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}