diff --git a/.travis.yml b/.travis.yml index 54850e1..18bfbac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ addons: # example-os build dependencies: - nasm - mtools + - genisoimage matrix: include: @@ -35,7 +36,15 @@ matrix: - qemu-system-arm - bochs-sdl - dosbox - - env: MODE=qemu-system-i386 + - env: MODE=qemu-system-i386-floppy + sudo: false + addons: + apt: + packages: + - *common_apt_packages + - qemu + - qemu-system-x86 + - env: MODE=qemu-system-i386-cdrom sudo: false addons: apt: diff --git a/README.md b/README.md index f20e05b..d8501d7 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,13 @@ This Travis configuration runs an operating system in various emulators. It can Below are screenshots of an example operating system. This example merely displays a gradient and does not process any user input. -## QEMU +## QEMU (floppy disk) -![Latest screenshot of the operating system running in QEMU](https://raw.githubusercontent.com/jsmaniac/travis-os-deploy-artifacts/screenshots-master-qemu-system-i386/qemu-system-i386.png) +![Latest screenshot of the operating system running in QEMU, booted as a floppy disk](https://raw.githubusercontent.com/jsmaniac/travis-os-deploy-artifacts/screenshots-master-qemu-system-i386/qemu-system-i386-floppy.png) + +## QEMU (CD-ROM) + +![Latest screenshot of the operating system running in QEMU, booted as a CD-ROM](https://raw.githubusercontent.com/jsmaniac/travis-os-deploy-artifacts/screenshots-master-qemu-system-i386/qemu-system-i386-cdrom.png) ## VirtualBox diff --git a/example-os/.gitignore b/example-os/.gitignore index 6dc21ac..ac9590e 100644 --- a/example-os/.gitignore +++ b/example-os/.gitignore @@ -7,3 +7,10 @@ /os.fdisk /os.arm.disasm /os.zip +/os.zip.adjusted +/os.iso +/os.32k +/os.fat12 +/os.offsets +/iso_files/os.zip +/iso_files/boot/iso_boot.sys diff --git a/example-os/Makefile b/example-os/Makefile index 01c0f61..83e1ee2 100644 --- a/example-os/Makefile +++ b/example-os/Makefile @@ -1,5 +1,7 @@ os_filename = os.bat -tests = test/qemu-system-i386 test/qemu-system-arm test/virtualbox test/bochs test/gui-sh test/dosbox +tests = test/qemu-system-i386-floppy test/qemu-system-i386-cdrom test/qemu-system-arm test/virtualbox test/bochs test/gui-sh test/dosbox +built_files = $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.reasm.disasm os.file os.fdisk os.arm.disasm os.zip os.zip.adjusted os.iso os.32k os.fat12 os.offsets iso_files/os.zip iso_files/boot/iso_boot.sys +built_directories = iso_files/boot iso_files os_image_size_kb = 1440 os_partition_start_sectors = 3 @@ -10,24 +12,105 @@ os_floppy_chs_s = 9 .PHONY: all # all: os.arm.disasm -all: $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.file os.fdisk .gitignore Makefile +all: $(os_filename) os.ndisasm.disasm os.reasm.asm os.file os.fdisk os.offsets .gitignore Makefile ../deploy-screenshots: Makefile mkdir -p $@ touch $@ -$(os_filename): os.asm ../deploy-screenshots Makefile - rm -f $@ +# 32k header of the ISO9660 image +os.32k: os.asm Makefile nasm -o $@ $< - mformat -v "Example OS" \ - -T $$(($(os_partition_size_sectors))) \ + +os.iso: iso_files/os.zip iso_files/boot/iso_boot.sys Makefile + mkisofs \ + --input-charset utf-8 \ + -rock \ + -joliet \ + -eltorito-catalog boot/boot.cat \ + -eltorito-boot boot/iso_boot.sys \ + -no-emul-boot \ + -boot-load-size 4 \ + -pad \ + -output os.iso \ + ./iso_files/ + +sector_size = 512 +# should be exact +os_total_size_sectors = ( $(os_image_size_kb)*1024 / $(sector_size) ) +os_total_size_tracks = ( $(os_total_size_sectors) / $(os_floppy_chs_s) ) +# round up +iso_size_sectors = ( ( $$(wc -c os.iso | cut -d ' ' -f 1) + $(sector_size) - 1 ) / $(sector_size) ) +iso_size_tracks = ( ( $(iso_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) ) +# round up +zip_size_sectors = ( ( $$(wc -c os.zip | cut -d ' ' -f 1) + $(sector_size) - 1 ) / $(sector_size) ) +zip_size_tracks = ( ( $(zip_size_sectors) + $(os_floppy_chs_s) - 1 ) / $(os_floppy_chs_s) ) +# allocate the remaining sectors, aligned on tracks +fat12_size_tracks = ( ( $(os_total_size_tracks) - $(iso_size_tracks) - $(zip_size_tracks) ) ) +fat12_size_sectors = ( $(fat12_size_tracks) * $(os_floppy_chs_s) ) +# zip should probably have its end aligned, not its start +space_before_zip_bytes = ( $(os_image_size_kb)*1024 - $$(wc -c os.zip | cut -d ' ' -f 1) ) + +mbr_start = 0 +mbr_end = 512 +header_32k_start = 0 +header_32k_end = ( 32 * 1024 ) +header_32k_size = ( $(header_32k_end) - $(header_32k_start) ) +iso_start = ( 32 * 1024 ) +iso_end = ( $(iso_size_sectors) * $(sector_size) ) +fat12_start = ( $(iso_size_tracks) * $(os_floppy_chs_s) * $(sector_size) ) +fat12_size = ( $(fat12_size_sectors) * $(sector_size) ) +fat12_end = ( $(fat12_start) + $(fat12_size) ) +zip_start = $(space_before_zip_bytes) +zip_end = ( $(os_total_size_sectors) * $(sector_size) ) + +os_fat12_partition = "$@@@$$(( $(fat12_start) ))" +os.fat12: os.zip os.iso Makefile + set -x; dd if=/dev/zero bs=$(sector_size) count=$$(( $(os_total_size_sectors) )) of=$@ + set -x; mformat -v "Example OS" \ + -T $$(( $(fat12_size_sectors) )) \ -h $(os_floppy_chs_h) \ -s $(os_floppy_chs_s) \ - -i "$@@@$$(($(os_partition_start_sectors)*512))" -# mcopy … "$@@@$$(($(os_partition_start_sectors)*512))" - zip os.zip $@ - dd seek=$$(($(os_image_size_kb)*1024-$$(wc -c os.zip | cut -d ' ' -f 1))) bs=1 if=os.zip of=$@ + -i $(os_fat12_partition) + set -x; mcopy -i $(os_fat12_partition) os.zip "::os.zip" + +iso_files: Makefile + mkdir -p $@ + +iso_files/boot: iso_files Makefile + mkdir -p $@ + +iso_files/os.zip: os.zip iso_files Makefile +# TODO: make it so that the various file formats are mutual quines: +# * the ISO should contain the original file +# * the ZIP should contain the original file +# * the FAT12 should contain the original file + cp $< $@ + +# 4 sectors loaded when booting from optical media (CD-ROM, …): +iso_files/boot/iso_boot.sys: os.32k iso_files/boot Makefile + dd if=$< bs=512 count=4 of=$@ + +os.zip: os.32k Makefile + zip $@ $< + +os.zip.adjusted: os.zip Makefile + set -x; dd if=/dev/zero bs=1 count=$$(( $(space_before_zip_bytes) )) of=$@ + cat $< >> $@ zip --adjust-sfx $@ + +$(os_filename): os.32k os.iso os.fat12 os.zip os.zip.adjusted ../deploy-screenshots Makefile + rm -f $@ +# start with the .iso + cp os.iso $@ +# splice in the first 32k (bootsector and partition table) + set -x; dd skip=$$(( $(header_32k_start) )) seek=$$(( $(header_32k_start) )) bs=1 count=$$(( $(header_32k_size) )) conv=notrunc if=os.32k of=$@ +# splice in fat12 + set -x; dd skip=$$(( $(fat12_start) )) seek=$$(( $(fat12_start) )) bs=1 count=$$(( $(fat12_size) )) conv=notrunc if=os.fat12 of=$@ +# splice in zip at the end + set -x; dd skip=$$(( $(space_before_zip_bytes) )) seek=$$(( $(space_before_zip_bytes) )) bs=1 conv=notrunc if=os.zip.adjusted of=$@ +# patch the partition table + printf "p\nd\nn\np\n1\n$$(( $(fat12_start) / $(sector_size) ))\n$$(( $(fat12_size_sectors) ))\nt\n01\na\n1\np\nw\nq\n" | fdisk $@ chmod a+x-w $@ os.file: $(os_filename) Makefile @@ -36,7 +119,20 @@ os.file: $(os_filename) Makefile os.fdisk: $(os_filename) Makefile fdisk -l $< > $@ -os.ndisasm.disasm: $(os_filename) Makefile +os.offsets: $(os_filename) os.32k os.iso os.fat12 os.zip Makefile + printf 'total_size = 0x%08x\n' $$(( $(os_total_size_sectors) * $(sector_size) )) > $@ + printf 'mbr_start = 0x%08x\n' $$(( $(mbr_start) )) >> $@ + printf 'mbr_end = 0x%08x\n' $$(( $(mbr_end) )) >> $@ + printf '32k_start = 0x%08x\n' $$(( $(header_32k_start) )) >> $@ + printf '32k_end = 0x%08x\n' $$(( $(header_32k_end) )) >> $@ + printf 'iso_start = 0x%08x\n' $$(( $(iso_start) )) >> $@ + printf 'iso_end = 0x%08x\n' $$(( $(iso_end) )) >> $@ + printf 'fat12_start = 0x%08x\n' $$(( $(fat12_start) )) >> $@ + printf 'fat12_end = 0x%08x\n' $$(( $(fat12_end) )) >> $@ + printf 'zip_start = 0x%08x\n' $$(( $(zip_start) )) >> $@ + printf 'zip_end = 0x%08x\n' $$(( $(zip_end) )) >> $@ + +os.ndisasm.disasm: $(os_filename) Makefile ../utils/compact-ndisasm.sh ../utils/compact-ndisasm.sh $< $@ os.reasm.asm: os.ndisasm.disasm Makefile @@ -49,33 +145,38 @@ os.reasm: os.reasm.asm $(os_filename) Makefile && echo "Re-assembled file is identical to $(os_filename)" \ || (../utils/compact-ndisasm.sh $@ os.reasm.disasm; \ echo "Re-assembled file is different from $(os_filename). Use meld os.ndisasm.disasm os.reasm.disasm to see differences."; \ - exit 1) + exit 0) # For now ignore this error, since we cannot have a reliable re-assembly of arbitrary data. #os.arm.disasm: $(os_filename) Makefile # arm-none-eabi-objdump --endian=little -marm -b binary -D --adjust-vma=0x8000 $< > $@ .PHONY: clean clean: Makefile - rm -f $(os_filename) os.ndisasm.disasm os.reasm.asm os.reasm os.file os.arm.disasm os.zip + rm -f $(built_files) + for d in $(built_directories); do if test -e "$$d"; then rmdir "$$d"; fi; done .gitignore: Makefile - echo /$(os_filename) > $@ - echo /os.ndisasm.disasm >> $@ - echo /os.reasm.asm >> $@ - echo /os.reasm >> $@ - echo /os.reasm.disasm >> $@ - echo /os.file >> $@ - echo /os.fdisk >> $@ - echo /os.arm.disasm >> $@ - echo /os.zip >> $@ + for f in $(built_files); do echo "/$$f"; done > $@ .PHONY: test -test: $(tests) test/zip all Makefile +test: $(tests) test/noemu all Makefile .PHONY: $(tests) $(tests): $(os_filename) ../deploy-screenshots Makefile cd .. && ./utils/gui-wrapper.sh 800x600x24 ./$@.sh example-os/$< +.PHONY: test/noemu +test/noemu: test/zip os.reasm test/sizes test/fat12_contents Makefile + .PHONY: test/zip -test/zip: - unzip -t os.zip +test/zip: $(os_filename) Makefile + unzip -t $(os_filename) + +.PHONY: test/sizes +test/sizes: os.32k $(os_filename) Makefile + test "$$(wc -c os.32k)" = "$$((32*1024)) os.32k" + test "$$(wc -c $(os_filename))" = "$$((1440*1024)) $(os_filename)" + +# check that the fat filesystem has the correct contents +test/fat12_contents: $(os_filename) + mdir -i "$<@@$$(( $(fat12_start) ))" :: | grep -E "^os\s+zip\s+" diff --git a/example-os/os.asm b/example-os/os.asm index 85b77cf..3840971 100644 --- a/example-os/os.asm +++ b/example-os/os.asm @@ -45,7 +45,7 @@ end: ;;; Fill the remaining bytes with 0 and write a partition table times 0x1b8-($-$$) db 0 -db "ExOSxx" ;; 0x1b8 unique disk ID (4 bytes, can be any value) +db "ExOSxx" ;; 0x1b8 unique disk ID (4-6 bytes? Can be any value) ;;; Partition table entries follow this format: ;;; 1 byte Bootable flag (0x00 = no, 0x80 = yes) ;;; 3 bytes Start Head Sector Cylinder (8 bits + 6 bits + 10 bits) @@ -53,7 +53,9 @@ db "ExOSxx" ;; 0x1b8 unique disk ID (4 bytes, can be any value) ;;; 3 bytes End Head Sector Cylinder (8 bits + 6 bits + 10 bits) ;;; 4 bytes LBA offset ;;; 4 bytes LBA length -db 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x0b, 0x00, 0x00, 0x3d, 0x0b, 0x00, 0x00 ;; 0x1be p1 +;;;0x1be 0x1c1 0x1c2 0x1c5 0x1c6 0x1c9 0x1ca 0x1cd +;;; This is filled with dummy values, and later patched with fdisk. +db 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x9b, 0x05, 0x00, 0x00 ;; 0x1be p1 db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;; 0x1ce p2 db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;; 0x1de p3 db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ;; 0x1ee p4 @@ -80,5 +82,5 @@ db `exit\n` db `:loop\n` db `GOTO loop\n` -;;; Fill up to 1.44M with 0 -times (1440*1024)-($-$$) db 0 +;;; Fill up to 32k with 0. This constitutes the first 32k of the ISO image. +times (32*1024)-($-$$) db 0 diff --git a/test/qemu-system-i386-cdrom.sh b/test/qemu-system-i386-cdrom.sh new file mode 100755 index 0000000..0ac9ea4 --- /dev/null +++ b/test/qemu-system-i386-cdrom.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -e + +if test $# -ne 1 || test "$1" = '-h' -o "$1" = '--help'; then + echo "Usage: $0 operating_system_file" +fi +os_filename="$1" + +qemu-system-i386 -drive format=raw,readonly,file=${os_filename},index=0,if=ide,index=1,media=cdrom -boot d & +pid=$! +runsikulix -r test/check-gradient.sikuli && exitcode=$? || exitcode=$? + +./utils/take-screenshots.sh "./deploy-screenshots/$(basename "$0" .sh).png" + +kill $pid + +exit $exitcode diff --git a/test/qemu-system-i386.sh b/test/qemu-system-i386-floppy.sh similarity index 100% rename from test/qemu-system-i386.sh rename to test/qemu-system-i386-floppy.sh diff --git a/utils/compact-ndisasm.sh b/utils/compact-ndisasm.sh index dcb52e4..33e2de1 100755 --- a/utils/compact-ndisasm.sh +++ b/utils/compact-ndisasm.sh @@ -1,14 +1,18 @@ +#!/bin/sh + +set -e + in=$1 out=$2 # Add -s 0x1234 to te ndisasm command-line to specify that there is an # instruction starting at 0x1234. Use this when ndisasm misinterprets # some instructions due to db. -skip="" +skip="-s 0x16F40C -s 0x16F415 -s 0x16FBD0" for i in `seq 0 255`; do skip="$skip -s 0x7e$(printf %02x $i)"; done (echo "[BITS 16]"; \ echo "[ORG 0x7c00]"; \ - ndisasm -s 0x7c78 $skip -o 0x7C00 "$in" \ + ndisasm -s 0x7c78 $skip -o 0x7C00 -b 16 "$in" \ | uniq -s 8 -c \ | sed -r -e 's/^\s*1 //; t; s/^\s*([0-9]+) ([^ ]+\s+[^ ]+\s+)/\2times \1 /' \ | sed -r -e 's/([^ ]+\s+[0-9A-F]{4}\s+j[^ ]+ )0x/\1short 0x/') > "$out"