[
  {
    "path": ".gitignore",
    "content": "multiboot_header\nboot\n*.o\n*.bin\n*.iso\n.vagrant\ntarget\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"my_os\"\nversion = \"1.0.0\"\nauthors = [\"ag_dubs\"]\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\nrlibc = \"0.1.4\"\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 ashley williams\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "arch ?= x86_64\nkernel := build/kernel-$(arch).bin\niso := build/os-$(arch).iso\ntarget ?= $(arch)-unknown-linux-gnu\nrust_os := target/$(target)/debug/libmy_os.a\n\nlinker_script := src/arch/$(arch)/linker.ld\ngrub_cfg := src/arch/$(arch)/grub.cfg\nassembly_source_files := $(wildcard src/arch/$(arch)/*.asm)\nassembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \\\n\tbuild/arch/$(arch)/%.o, $(assembly_source_files))\n\n.PHONY: all clean run iso\n\nall: $(kernel)\n\nclean:\n\t@rm -r build\n\nrun: $(iso)\n\t@qemu-system-x86_64 -hda $(iso)\n\niso: $(iso)\n\n$(iso): $(kernel) $(grub_cfg)\n\t@mkdir -p build/isofiles/boot/grub\n\t@cp $(kernel) build/isofiles/boot/kernel.bin\n\t@cp $(grub_cfg) build/isofiles/boot/grub\n\t@grub-mkrescue -o $(iso) build/isofiles 2> /dev/null\n\t@rm -r build/isofiles\n\n$(kernel): cargo $(rust_os) $(assembly_object_files) $(linker_script)\n\t@ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os)\n\ncargo:\n\t@cargo rustc --target $(target) -- -Z no-landing-pads\n\nbuild/arch/$(arch)/%.o: src/arch/$(arch)/%.asm\n\t@mkdir -p $(shell dirname $@)\n\t@nasm -felf64 $< -o $@\n"
  },
  {
    "path": "README.md",
    "content": "# x86 kernel\n> a simple x86 kernel, extended with Rust \n\nthis is my work following along with a [@phil-opp][2]'s blog post series [\"A minimal x86 kernel\"][1]\n\n## prerequisites\n\n### virtualization\n> (if you are on OSX, ChromeOS, Windows, etc)\n\n- [Vagrant]: development environment manager\n- [VirtualBox]: virtualizer\n- [XQuartz]: X11 Graphics\n\n### linux dependencies\n- `nasm`: assembler (assembly -> binary)\n- `ld`: linker (makes binary out of other files)\n- `grub`: creates the bootable iso\n- `xorriso`: req'd by grub, filesystem manipulator\n- `QEMU`: fake-computer emulator\n\n### utilities\nyou don't need these, but they are nice for viewing\ngenerated code.\n\n- `hexdump`: allows you to view generated binary\n- `objdump`: a nicer viewer for .o files\n\n## up and running\n\n1. fork and clone this repository\n2. navigate into the repo directory: `cd x86-kernel`\n3. `$ vagrant up`\n4. `$ vagrant ssh -- -Y`\n    `-- -Y` forwards graphics\n5. `$ multirust default nightly-2015-11-19`\n    \n    Sets your default rust to a stable nightly. \n    The features needed to do OS work in Rust are\n    not yet in a stable release, so you must use\n    a nightly build.\n\n6. `$ cd /vagrant`\n\n    The `/vagrant` directory is the virtualized directory\n    that is synced with the `/` directory on your HD.\n\n7. `$ make run`\n\n[Vagrant]: https://www.vagrantup.com/\n[VirtualBox]: https://www.virtualbox.org/\n[XQuartz]: http://www.xquartz.org/\n[1]: http://blog.phil-opp.com/rust-os/multiboot-kernel.html\n[2]: https://github.com/phil-opp\n"
  },
  {
    "path": "Vagrantfile",
    "content": "# -*- mode: ruby -*-\n# vi: set ft=ruby :\n\n# All Vagrant configuration is done below. The \"2\" in Vagrant.configure\n# configures the configuration version (we support older styles for\n# backwards compatibility). Please don't change it unless you know what\n# you're doing.\nVagrant.configure(2) do |config|\n  # The most common configuration options are documented and commented below.\n  # For a complete reference, please see the online documentation at\n  # https://docs.vagrantup.com.\n\n  # Every Vagrant development environment requires a box. You can search for\n  # boxes at https://atlas.hashicorp.com/search.\n  config.vm.box = \"debian/jessie64\"\n\n  # Disable automatic box update checking. If you disable this, then\n  # boxes will only be checked for updates when the user runs\n  # `vagrant box outdated`. This is not recommended.\n  # config.vm.box_check_update = false\n\n  # Create a forwarded port mapping which allows access to a specific port\n  # within the machine from a port on the host machine. In the example below,\n  # accessing \"localhost:8080\" will access port 80 on the guest machine.\n  # config.vm.network \"forwarded_port\", guest: 80, host: 8080\n\n  # Create a private network, which allows host-only access to the machine\n  # using a specific IP.\n  # config.vm.network \"private_network\", ip: \"192.168.33.10\"\n\n  # Create a public network, which generally matched to bridged network.\n  # Bridged networks make the machine appear as another physical device on\n  # your network.\n  # config.vm.network \"public_network\"\n\n  # Share an additional folder to the guest VM. The first argument is\n  # the path on the host to the actual folder. The second argument is\n  # the path on the guest to mount the folder. And the optional third\n  # argument is a set of non-required options.\n  # config.vm.synced_folder \"../data\", \"/vagrant_data\"\n\n  # Provider-specific configuration so you can fine-tune various\n  # backing providers for Vagrant. These expose provider-specific options.\n  # Example for VirtualBox:\n  #\n  # config.vm.provider \"virtualbox\" do |vb|\n  #   # Display the VirtualBox GUI when booting the machine\n  #   vb.gui = true\n  #\n  #   # Customize the amount of memory on the VM:\n  #   vb.memory = \"1024\"\n  # end\n  #\n  # View the documentation for the provider you are using for more\n  # information on available options.\n\n  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies\n  # such as FTP and Heroku are also available. See the documentation at\n  # https://docs.vagrantup.com/v2/push/atlas.html for more information.\n  # config.push.define \"atlas\" do |push|\n  #   push.app = \"YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME\"\n  # end\n\n  # Enable provisioning with a shell script. Additional provisioners such as\n  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the\n  # documentation for more information about their specific syntax and use.\n   config.vm.provision \"shell\", inline: <<-SHELL\n      sudo apt-get update\n      sudo apt-get install nasm -y\n      sudo apt-get install xorriso -y\n      sudo apt-get install git -y\n      sudo apt-get install vim -y\n      sudo apt-get install -y qemu\n      curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh -s -- --yes\n      multirust default nightly-2015-11-19 \n  SHELL\n\n  config.ssh.forward_x11 = true\nend\n"
  },
  {
    "path": "src/arch/x86_64/boot.asm",
    "content": "global start\nextern long_mode_start\n\nsection .text\nbits 32\nstart:\n  mov esp, stack_top \n\n  ; tests\n  call test_multiboot\n  call test_cpuid\n  call test_long_mode\n\n  ; paging\n  call setup_page_tables\n  call enable_paging\n\n  lgdt [gdt64.pointer]\n\n  ; update selectors\n  mov ax, gdt64.data\n  mov ss, ax  ; stack selector\n  mov ds, ax  ; data selector\n  mov es, ax  ; extra selector\n\n  jmp gdt64.code:long_mode_start ; \"trampoline\"\n\n  hlt\n\n; prints `ERR: ` + error code \n; parameter: error code (in ascii) in al\nerror:\n  mov dword [0xb8000], 0x4f524f45\n  mov dword [0xb8004], 0x4f3a4f52\n  mov dword [0xb8008], 0xff204f20\n  mov byte [0xb800a], al\n  hlt\n\ntest_multiboot:\n  cmp eax, 0x36d76289\n  jne .no_multiboot\n  ret\n.no_multiboot:\n  mov al, \"0\"\n  jmp error\n\ntest_cpuid:\n    pushfd               ; Store the FLAGS-register.\n    pop eax              ; Restore the A-register.\n    mov ecx, eax         ; Set the C-register to the A-register.\n    xor eax, 1 << 21     ; Flip the ID-bit, which is bit 21.\n    push eax             ; Store the A-register.\n    popfd                ; Restore the FLAGS-register.\n    pushfd               ; Store the FLAGS-register.\n    pop eax              ; Restore the A-register.\n    push ecx             ; Store the C-register.\n    popfd                ; Restore the FLAGS-register.\n    xor eax, ecx         ; Do a XOR-operation on the A-register and the C-register.\n    jz .no_cpuid         ; The zero flag is set, no CPUID.\n    ret                  ; CPUID is available for use.\n.no_cpuid:\n    mov al, \"1\"\n    jmp error\n\ntest_long_mode:\n    mov eax, 0x80000000    ; Set the A-register to 0x80000000.\n    cpuid                  ; CPU identification.\n    cmp eax, 0x80000001    ; Compare the A-register with 0x80000001.\n    jb .no_long_mode       ; It is less, there is no long mode.\n    mov eax, 0x80000001    ; Set the A-register to 0x80000001.\n    cpuid                  ; CPU identification.\n    test edx, 1 << 29      ; Test if the LM-bit, which is bit 29, is set in the D-register.\n    jz .no_long_mode       ; They aren't, there is no long mode.\n    ret\n.no_long_mode:\n    mov al, \"2\"\n    jmp error\n\nsetup_page_tables:\n  ; map p4 to p3\n  mov eax, p3_table\n  or eax, 0b11\n  mov [p4_table], eax\n\n  ; map p3 to p2\n  mov eax, p2_table\n  or eax, 0b11\n  mov [p3_table], eax\n\n  mov ecx, 0\n\n.map_p2_table:\n  mov eax, 0x200000\n  mul ecx\n  or eax, 0b10000011\n  mov [p2_table + ecx * 8], eax\n\n  ; for loop, increment, compare(=512)\n  inc ecx\n  cmp ecx, 512\n  jne .map_p2_table\n\n  ret\n\nenable_paging:\n  ; load P4 to cr3 register (cpu uses this to access the P4 table)\n  mov eax, p4_table\n  mov cr3, eax\n\n  ; enable PAE-flag in cr4 (Physical Address Extension)\n  mov eax, cr4\n  or eax, 1 << 5\n  mov cr4, eax\n\n  ; set the long mode bit in the EFER MSR (model specific register)\n  mov ecx, 0xC0000080\n  rdmsr\n  or eax, 1 << 8\n  wrmsr\n\n  ; enable paging in the cr0 register\n  mov eax, cr0\n  or eax, 1 << 31\n  mov cr0, eax\n\n  ret\n\nsection .bss\nalign 4096\np4_table:\n  resb 4096\np3_table:\n  resb 4096\np2_table:\n  resb 4096\nstack_bottom:\n  resb 64\nstack_top:\n\nsection .rodata\ngdt64:\n  dq 0 ; zero entry\n.code: equ $ - gdt64\n  dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment\n.data: equ $ - gdt64\n  dq (1<<44) | (1<<47) | (1<<41) ; data segment\n.pointer:\n  dw $ - gdt64 - 1\n  dq gdt64\n"
  },
  {
    "path": "src/arch/x86_64/grub.cfg",
    "content": "set timeout=0\nset default=0\n\nmenuentry \"my os\" {\n  multiboot2 /boot/kernel.bin\n  boot\n}\n"
  },
  {
    "path": "src/arch/x86_64/linker.ld",
    "content": "ENTRY(start)\n\nSECTIONS {\n  /* sets load address to MiB (convention) */\n  . = 1M;\n\n  .boot : {\n    /* put the header at the beginning */\n    KEEP(*(.multiboot_header))\n  }\n\n  .text : {\n    *(.text)\n  }\n}\n"
  },
  {
    "path": "src/arch/x86_64/long_mode_init.asm",
    "content": "global long_mode_start\n\nsection .text\nbits 64\nlong_mode_start:\n  extern rust_main\n  call setup_SSE\n  call rust_main \n\n.os_returned:\n    ; rust main returned, print `OS returned!`\n    mov rax, 0x4f724f204f534f4f\n    mov [0xb8000], rax\n    mov rax, 0x4f724f754f744f65\n    mov [0xb8008], rax\n    mov rax, 0x4f214f644f654f6e\n    mov [0xb8010], rax\n    hlt\n\nerror:\n    mov rbx, 0x4f4f4f524f524f45\n    mov [0xb8000], rbx\n    mov rbx, 0x4f204f204f3a4f52\n    mov [0xb8008], rbx\n    mov byte [0xb800e], al\n    hlt\n    jmp error\n\nsetup_SSE:\n    ; check for SSE\n    mov rax, 0x1\n    cpuid\n    test edx, 1<<25\n    jz .no_SSE\n\n    ; enable SSE\n    mov rax, cr0\n    and ax, 0xFFFB      ; clear coprocessor emulation CR0.EM\n    or ax, 0x2          ; set coprocessor monitoring  CR0.MP\n    mov cr0, rax\n    mov rax, cr4\n    or ax, 3 << 9       ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time\n    mov cr4, rax\n\n    ret\n.no_SSE:\n    mov al, \"a\"\n    jmp error\n"
  },
  {
    "path": "src/arch/x86_64/multiboot_header.asm",
    "content": "section .multiboot_header\nheader_start:\n  dd 0xe85250d6                 ; magic number (multiboot 2)\n  dd 0                          ; architecture 0 (protected mode i386)\n  dd header_end - header_start  ; header length\n\n  ; checksum (magic number + architecture + header length)\n  ; we subtract from 0x1... to account for signedness \n  dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start))\n\n  ; required end tag (u16, u16, u32)\n  dw 0                          ; type\n  dw 0                          ; flags\n  dd 8                          ; size\nheader_end:\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#![feature(no_std)]\n#![feature(lang_items)]\n#![no_std]\n\nextern crate rlibc;\n\n#[no_mangle]\npub extern fn rust_main() {\n  let hello = b\"Hello World!\";\n  let color_byte = 0x1f;\n\n  let mut hello_colored = [color_byte; 24];\n  for (i, char_byte) in hello.into_iter().enumerate() {\n    hello_colored[i * 2] = *char_byte;\n  }\n\n  let buffer_ptr = (0xb8000  +1988) as *mut _;\n  unsafe { *buffer_ptr = hello_colored };\n\n  loop{}\n}\n\n#[lang = \"eh_personality\"] extern fn eh_personality() {}\n#[lang = \"panic_fmt\"] extern fn panic_fmt() -> ! { loop{} }\n"
  }
]