diff --git a/README.md b/README.md new file mode 100644 index 0000000..abc9e66 --- /dev/null +++ b/README.md @@ -0,0 +1,128 @@ +# mikanos-build + +このリポジトリは uchan が開発している教育用 OS [MikanOS](https://github.com/uchan-nos/mikanos) をビルドする手順およびツールを収録しています。 +Ubuntu 18.04 で動作を確認しています。 + +MikanOS のビルド手順は大きく次の 4 段階です。 + +1. ビルド環境の構築 +2. MikanOS のソースコードの入手 +3. ブートローダーのビルド +4. MikanOS のビルド + +## ビルド環境の構築 + +ブートローダーおよび MikanOS 本体のビルドに必要なツールやファイルを揃えます。 + +### リポジトリのダウンロード + +まずは Git をインストールして,mikanos-build リポジトリをダウンロードします。 + + $ sudo apt update + $ sudo apt install git + $ mkdir $HOME + $ git clone https://github.com/uchan-nos/mikanos-build.git osbook + +### 開発ツールの導入 + +次に Clang,Nasm といった開発ツールや,EDK IIのセットアップを行います。 +`ansible_provision.yml` に必要なツールが記載されています。 +Ansible を使ってセットアップを行うと楽です。 + + $ sudo apt install ansible + $ cd $HOME/osbook/devenv + $ ansible-playbook -K -i ansible_inventory ansible_provision.yml + +セットアップが上手くいけば `iasl` というコマンドがインストールされ,`$HOME/edk2` というディレクトリが生成されているはずです。 +これらがなければセットアップが失敗しています。 + + $ iasl -v + $ ls $HOME/edk2 + +EDK II に含まれる細かいツールをビルドしておきます。 + + $ make -C $HOME/edk2/BaseTools/Source/C + +### 標準ライブラリの入手 + +ビルド済みの標準ライブラリをダウンロードし展開します。 + + $ cd $HOME/osbook/devenv + $ wget https://github.com/uchan-nos/mikanos-build/releases/download/v1.0/x86_64-elf.tar.gz + $ tar xf x86_64-elf.tar.gz + +`x86_64-elf.tar.gz` に含まれるファイルは [Newlib](https://sourceware.org/newlib/) と [libc++](https://libcxx.llvm.org/) をビルドしたものです。 +それらのライセンスは Newlib や libc++ のライセンスに従います。 +MikanOS や mikanos-build リポジトリのライセンスとは異なりますので注意してください。 + +次のファイル群は Newlib 由来です。ライセンスは `x86_64-elf/LICENSE.newlib` を参照してください。 + + x86_64-elf/lib/ + libc.a + libg.a + libm.a + x86_64-elf/include/ + c++/ を除くすべて + +次のファイル群は libc++ 由来です。ライセンスは `x86_64-elf/LICENSE.libcxx` を参照してください。 + + x86_64-elf/lib/ + libc++.a + libc++abi.a + libc++experimental.a + x86_64-elf/include/c++/v1/ + すべて + +## MikanOS のソースコードの入手 + +Git で入手できます。 + + $ git clone https://github.com/uchan-nos/mikanos.git + +## ブートローダーのビルド + +EDK II のディレクトリに MikanOS ブートローダーのディレクトリをリンクします。 + + $ cd $HOME/edk2 + $ ln -s /path/to/mikanos/MikanLoaderPkg ./ + +ブートローダーのソースコードが正しく見えればリンク成功です。 + + $ ls MikanLoaderPkg/Main.c + +次に,`edksetup.sh` を読み込むことで EDK II のビルドに必要な環境変数を設定します。 + + $ source edksetup.sh + +`edksetup.sh` ファイルを読み込むと,環境変数が設定される他に `Conf/target.txt` が自動的に生成されます。 +このファイルをエディタで開き,次の項目を修正します。 + +| 設定項目 | 設定値 | +|-----------------|-----------------------------------| +| ACTIVE_PLATFORM | MikanLoaderPkg/MikanLoaderPkg.dsc | +| TARGET | DEBUG | +| TARGET_ARCH | X64 | +| TOOL_CHAIN_TAG | CLANG38 | + +設定が終わったらブートローダーをビルドします。 + + $ build + +Loader.efi ファイルが出力されていればビルド成功です。 + + $ ls Build/MikanLoaderX64/DEBUG_CLANG38/X64/Loader.efi + +## MikanOS のビルド + +ビルドに必要な環境変数を読み込みます。 + + $ source $HOME/osbook/devenv/buildenv.sh + +ビルドします。 + + $ cd /path/to/mikanos/kernel + $ ./build.sh + +QEMU で起動するには `./build.sh` に `run` オプションを指定します。 + + $ ./build.sh run diff --git a/devenv/.gitignore b/devenv/.gitignore new file mode 100644 index 0000000..5d3b71c --- /dev/null +++ b/devenv/.gitignore @@ -0,0 +1,3 @@ +.vagrant +*.retry +*.log diff --git a/devenv/OVMF_CODE.fd b/devenv/OVMF_CODE.fd new file mode 100644 index 0000000..169c5e6 Binary files /dev/null and b/devenv/OVMF_CODE.fd differ diff --git a/devenv/OVMF_VARS.fd b/devenv/OVMF_VARS.fd new file mode 100644 index 0000000..6ab720a Binary files /dev/null and b/devenv/OVMF_VARS.fd differ diff --git a/devenv/Vagrantfile b/devenv/Vagrantfile new file mode 100644 index 0000000..12c1d9d --- /dev/null +++ b/devenv/Vagrantfile @@ -0,0 +1,77 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://vagrantcloud.com/search. + config.vm.box = "bento/ubuntu-18.04" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # NOTE: This will enable public access to the opened port + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine and only allow access + # via 127.0.0.1 to disable public access + # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + config.vm.synced_folder "..", "/home/vagrant/osbook_ro", :mount_options => ["ro"] + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + # config.vm.provision "shell", inline: <<-SHELL + # apt-get update + # apt-get install -y apache2 + # SHELL + + config.vm.provision "ansible_local" do |ansible| + ansible.playbook = "ansible_provision.yml" + ansible.inventory_path = "ansible_inventory" + end + + config.ssh.forward_x11 = true +end diff --git a/devenv/ansible_inventory b/devenv/ansible_inventory new file mode 100644 index 0000000..b8a3276 --- /dev/null +++ b/devenv/ansible_inventory @@ -0,0 +1,2 @@ +[default] +localhost ansible_connection=local diff --git a/devenv/ansible_provision.yml b/devenv/ansible_provision.yml new file mode 100644 index 0000000..907dc13 --- /dev/null +++ b/devenv/ansible_provision.yml @@ -0,0 +1,82 @@ +--- +- hosts: all + tasks: + - name: ensure development tools are at the latest version + become: yes + apt: name={{ item }} state=latest install_recommends=no + with_items: + - make + - llvm-7-dev + - lld-7 + - clang-7 + - nasm + - acpica-tools + - uuid-dev + - qemu + - xauth + - unzip + + - name: set llvm 7 as default + become: yes + alternatives: + name: "{{ item }}" + link: "/usr/bin/{{ item }}" + path: "/usr/bin/{{ item }}-7" + with_items: + - llvm-PerfectShuffle + - llvm-ar + - llvm-as + - llvm-bcanalyzer + - llvm-cat + - llvm-cfi-verify + - llvm-config + - llvm-cov + - llvm-c-test + - llvm-cvtres + - llvm-cxxdump + - llvm-cxxfilt + - llvm-diff + - llvm-dis + - llvm-dlltool + - llvm-dwarfdump + - llvm-dwp + - llvm-exegesis + - llvm-extract + - llvm-lib + - llvm-link + - llvm-lto + - llvm-lto2 + - llvm-mc + - llvm-mca + - llvm-modextract + - llvm-mt + - llvm-nm + - llvm-objcopy + - llvm-objdump + - llvm-opt-report + - llvm-pdbutil + - llvm-profdata + - llvm-ranlib + - llvm-rc + - llvm-readelf + - llvm-readobj + - llvm-rtdyld + - llvm-size + - llvm-split + - llvm-stress + - llvm-strings + - llvm-strip + - llvm-symbolizer + - llvm-tblgen + - llvm-undname + - llvm-xray + - ld.lld + - lld-link + - clang + - clang++ + - clang-cpp + + - name: clone EDK II repository + git: + repo: "https://github.com/tianocore/edk2.git" + dest: "{{ ansible_env.HOME }}/edk2" diff --git a/devenv/build-edk2.sh b/devenv/build-edk2.sh new file mode 100755 index 0000000..21d0a91 --- /dev/null +++ b/devenv/build-edk2.sh @@ -0,0 +1,21 @@ +#!/bin/bash -ex + +cd $HOME +if [ ! -d edk2 ] +then + git clone https://github.com/tianocore/edk2.git +fi + +cd edk2 + +make -C /home/vagrant/edk2/BaseTools/Source/C + +source ./edksetup.sh + +sed -i '/ACTIVE_PLATFORM/ s:= .*$:= OvmfPkg/OvmfPkgX64.dsc:' Conf/target.txt +sed -i '/TARGET_ARCH/ s:= .*$:= X64:' Conf/target.txt +sed -i '/TOOL_CHAIN_TAG/ s:= .*$:= CLANG38:' Conf/target.txt + +sed -i '/CLANG38/ s/-flto//' Conf/tools_def.txt + +build diff --git a/devenv/buildenv.sh b/devenv/buildenv.sh new file mode 100644 index 0000000..10cc7ac --- /dev/null +++ b/devenv/buildenv.sh @@ -0,0 +1,5 @@ +# Usage: source buildenv.sh + +BASEDIR="$HOME/osbook/devenv/x86_64-elf" +export CPPFLAGS="-I$BASEDIR/include/c++/v1 -I$BASEDIR/include -nostdlibinc -D__ELF__ -D_LDBL_EQ_DBL -D_GNU_SOURCE -D_POSIX_TIMERS" +export LDFLAGS="-L$BASEDIR/lib" diff --git a/devenv/make_image.sh b/devenv/make_image.sh new file mode 100755 index 0000000..1f99933 --- /dev/null +++ b/devenv/make_image.sh @@ -0,0 +1,33 @@ +#!/bin/sh -ex + +if [ $# -lt 3 ] +then + echo "Usage: $0 <.efi file> [another file]" + exit 1 +fi + +DEVENV_DIR=$(dirname "$0") +DISK_IMG=$1 +MOUNT_POINT=$2 +EFI_FILE=$3 +ANOTHER_FILE=$4 + +if [ ! -f $EFI_FILE ] +then + echo "No such file: $EFI_FILE" + exit 1 +fi + +rm -f $DISK_IMG +qemu-img create -f raw $DISK_IMG 200M +mkfs.fat -n 'MIKAN OS' -s 2 -f 2 -R 32 -F 32 $DISK_IMG + +$DEVENV_DIR/mount_image.sh $DISK_IMG $MOUNT_POINT +sudo mkdir -p $MOUNT_POINT/EFI/BOOT +sudo cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI +if [ "$ANOTHER_FILE" != "" ] +then + sudo cp $ANOTHER_FILE $MOUNT_POINT/ +fi +sleep 0.5 +sudo umount $MOUNT_POINT diff --git a/devenv/mount_image.sh b/devenv/mount_image.sh new file mode 100755 index 0000000..ba8233e --- /dev/null +++ b/devenv/mount_image.sh @@ -0,0 +1,20 @@ +#!/bin/sh -ex + +if [ $# -lt 2 ] +then + echo "Usage: $0 " + exit 1 +fi + +DEVENV_DIR=$(dirname "$0") +DISK_IMG=$1 +MOUNT_POINT=$2 + +if [ ! -f $DISK_IMG ] +then + echo "No such file: $DISK_IMG" + exit 1 +fi + +mkdir -p $MOUNT_POINT +sudo mount -o loop $DISK_IMG $MOUNT_POINT diff --git a/devenv/run_image.sh b/devenv/run_image.sh new file mode 100755 index 0000000..33358b7 --- /dev/null +++ b/devenv/run_image.sh @@ -0,0 +1,26 @@ +#!/bin/sh -ex + +if [ $# -lt 1 ] +then + echo "Usage: $0 " + exit 1 +fi + +DEVENV_DIR=$(dirname "$0") +DISK_IMG=$1 + +if [ ! -f $DISK_IMG ] +then + echo "No such file: $DISK_IMG" + exit 1 +fi + +qemu-system-x86_64 \ + -m 1G \ + -drive if=pflash,format=raw,readonly,file=$DEVENV_DIR/OVMF_CODE.fd \ + -drive if=pflash,format=raw,file=$DEVENV_DIR/OVMF_VARS.fd \ + -drive if=ide,index=0,media=disk,format=raw,file=$DISK_IMG \ + -device nec-usb-xhci,id=xhci \ + -device usb-mouse -device usb-kbd \ + -monitor stdio \ + $QEMU_OPTS diff --git a/devenv/run_mikanos.sh b/devenv/run_mikanos.sh new file mode 100755 index 0000000..25d1f7a --- /dev/null +++ b/devenv/run_mikanos.sh @@ -0,0 +1,34 @@ +#!/bin/sh -ex + +DEVENV_DIR=$(dirname "$0") +DISK_IMG=./disk.img +MOUNT_POINT=./mnt + +if [ "$MIKANOS_DIR" = "" ] +then + if [ $# -lt 1 ] + then + echo "Usage: $0 " + exit 1 + fi + MIKANOS_DIR="$HOME/osbook/$1" +fi + +LOADER_EFI="$HOME/edk2/Build/MikanLoaderX64/DEBUG_CLANG38/X64/Loader.efi" +KERNEL_ELF="$MIKANOS_DIR/kernel/kernel.elf" + +$DEVENV_DIR/make_image.sh $DISK_IMG $MOUNT_POINT $LOADER_EFI $KERNEL_ELF +$DEVENV_DIR/mount_image.sh $DISK_IMG $MOUNT_POINT + +for APP in $(ls "$MIKANOS_DIR/apps") +do + if [ -f $MIKANOS_DIR/apps/$APP/$APP ] + then + sudo cp "$MIKANOS_DIR/apps/$APP/$APP" $MOUNT_POINT/ + fi +done + +sleep 0.5 +sudo umount $MOUNT_POINT + +$DEVENV_DIR/run_image.sh $DISK_IMG diff --git a/devenv/run_qemu.sh b/devenv/run_qemu.sh new file mode 100755 index 0000000..24b5e42 --- /dev/null +++ b/devenv/run_qemu.sh @@ -0,0 +1,16 @@ +#!/bin/sh -ex + +if [ $# -lt 1 ] +then + echo "Usage: $0 <.efi file> [another file]" + exit 1 +fi + +DEVENV_DIR=$(dirname "$0") +EFI_FILE=$1 +ANOTHER_FILE=$2 +DISK_IMG=./disk.img +MOUNT_POINT=./mnt + +$DEVENV_DIR/make_image.sh $DISK_IMG $MOUNT_POINT $EFI_FILE $ANOTHER_FILE +$DEVENV_DIR/run_image.sh $DISK_IMG diff --git a/devenv/vagrant-build-edk2.sh b/devenv/vagrant-build-edk2.sh new file mode 100755 index 0000000..7ac5548 --- /dev/null +++ b/devenv/vagrant-build-edk2.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +dd if=build-edk2.sh | vagrant ssh -c 'dd of=build-edk2.sh; chmod +x build-edk2.sh; ./build-edk2.sh' diff --git a/devenv_src/stdlib/Dockerfile b/devenv_src/stdlib/Dockerfile new file mode 100644 index 0000000..572fd8f --- /dev/null +++ b/devenv_src/stdlib/Dockerfile @@ -0,0 +1,9 @@ +FROM debian:stable + +WORKDIR /usr/local/src + +ADD . /usr/local/src + +RUN apt-get update && apt-get install -y --no-install-recommends git-core subversion make cmake clang python ca-certificates + +CMD ["/usr/local/src/build-stdlib.sh"] diff --git a/devenv_src/stdlib/build-stdlib.sh b/devenv_src/stdlib/build-stdlib.sh new file mode 100755 index 0000000..0c9e34d --- /dev/null +++ b/devenv_src/stdlib/build-stdlib.sh @@ -0,0 +1,75 @@ +#!/bin/sh -eux + +BASEDIR=/usr/local/src +PREFIX=/usr/local/x86_64-elf +COMMON_CFLAGS="-nostdlibinc -O2 -D__ELF__ -D_LDBL_EQ_DBL -D_GNU_SOURCE -D_POSIX_TIMERS" +CC=clang +CXX=clang++ +TARGET_TRIPLE=x86_64-elf + +cd $BASEDIR +git clone --depth 1 --branch fix-build https://github.com/uchan-nos/newlib-cygwin.git + +cd $BASEDIR +mkdir build_newlib +cd build_newlib +../newlib-cygwin/newlib/configure \ + CC=$CC \ + CC_FOR_BUILD=$CC \ + CFLAGS="-fPIC $COMMON_CFLAGS" \ + --target=$TARGET_TRIPLE --prefix=$PREFIX --disable-multilib --disable-newlib-multithread +make -j 4 +make install + +cd $BASEDIR +git clone --depth 1 --branch llvmorg-8.0.1 https://github.com/llvm/llvm-project.git + +cd $BASEDIR +mkdir build_libcxxabi +cd build_libcxxabi +cmake -G "Unix Makefiles" \ + -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DCMAKE_CXX_COMPILER=$CXX \ + -DCMAKE_CXX_FLAGS="-I$PREFIX/include $COMMON_CFLAGS -D_LIBCPP_HAS_NO_THREADS" \ + -DCMAKE_C_COMPILER=$CC \ + -DCMAKE_C_FLAGS="-I$PREFIX/include $COMMON_CFLAGS -D_LIBCPP_HAS_NO_THREADS" \ + -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ + -DCMAKE_BUILD_TYPE=Release \ + -DLIBCXXABI_LIBCXX_INCLUDES="$BASEDIR/llvm-project/libcxx/include" \ + -DLIBCXXABI_ENABLE_EXCEPTIONS=False \ + -DLIBCXXABI_ENABLE_THREADS=False \ + -DLIBCXXABI_TARGET_TRIPLE=$TARGET_TRIPLE \ + -DLIBCXXABI_ENABLE_SHARED=False \ + -DLIBCXXABI_ENABLE_STATIC=True \ + $BASEDIR/llvm-project/libcxxabi + +make -j4 +make install + +cd $BASEDIR +mkdir build_libcxx + +cmake -G "Unix Makefiles" \ + -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DCMAKE_CXX_COMPILER=$CXX \ + -DCMAKE_CXX_FLAGS="-I$PREFIX/include $COMMON_CFLAGS" \ + -DCMAKE_CXX_COMPILER_TARGET=$TARGET_TRIPLE \ + -DCMAKE_C_COMPILER=$CC \ + -DCMAKE_C_FLAGS="-I$PREFIX/include $COMMON_CFLAGS" \ + -DCMAKE_C_COMPILER_TARGET=$TARGET_TRIPLE \ + -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \ + -DCMAKE_BUILD_TYPE=Release \ + -DLIBCXX_CXX_ABI=libcxxabi \ + -DLIBCXX_CXX_ABI_INCLUDE_PATHS="$BASEDIR/llvm-project/libcxxabi/include" \ + -DLIBCXX_CXX_ABI_LIBRARY_PATH="$PREFIX/lib" \ + -DLIBCXX_ENABLE_EXCEPTIONS=False \ + -DLIBCXX_ENABLE_FILESYSTEM=False \ + -DLIBCXX_ENABLE_MONOTONIC_CLOCK=False \ + -DLIBCXX_ENABLE_RTTI=False \ + -DLIBCXX_ENABLE_THREADS=False \ + -DLIBCXX_ENABLE_SHARED=False \ + -DLIBCXX_ENABLE_STATIC=True \ + $BASEDIR/llvm-project/libcxx + +make -j4 +make install diff --git a/devenv_src/stdlib/build.sh b/devenv_src/stdlib/build.sh new file mode 100755 index 0000000..0fca68a --- /dev/null +++ b/devenv_src/stdlib/build.sh @@ -0,0 +1,30 @@ +#!/bin/sh -eu + +exe() { + echo "$@" + "$@" +} + +echo "===========================================" +echo "Preparing build environment." +echo "===========================================" +IIDFILE=./docker-image-id +exe docker build --iidfile="${IIDFILE}" --tag stdlib-builder . +IID=$(cat ${IIDFILE}) +exe rm ${IIDFILE} + +echo "===========================================" +echo "Building standard libraries." +echo "===========================================" +CIDFILE=./docker-container-id +exe docker run --cidfile="${CIDFILE}" ${IID} +CID=$(cat ${CIDFILE}) +exe rm ${CIDFILE} + +echo "===========================================" +echo "Copying standard libraries." +echo "===========================================" +exe docker cp ${CID}:/usr/local/x86_64-elf ${HOME}/osbook/devenv + +echo "" +echo "Done. Standard libraries at ${HOME}/osbook/devenv/x86_64-elf"