├── .gdbinit ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── ci.yaml │ ├── code-style.yml │ └── release.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── before_install.sh ├── emulator └── Makefile.mk ├── external └── src │ ├── Makefile.mk │ ├── mbr_builder.c │ └── mkfs_ffs.c ├── include └── fuzzy │ ├── drivers │ ├── display │ │ └── vga │ │ │ └── graphics.h │ ├── pic │ │ ├── pic.h │ │ └── pit.h │ ├── port.h │ └── ps2 │ │ ├── keyboard.h │ │ └── ps2.h │ ├── fs │ ├── ffs.h │ └── mbr.h │ ├── kernel │ ├── interrupts │ │ ├── exceptions.h │ │ ├── interrupts.h │ │ └── timer.h │ ├── panic.h │ ├── process │ │ └── process.h │ └── syscall │ │ ├── console.h │ │ ├── file_handler.h │ │ ├── graphics.h │ │ ├── keyboard.h │ │ └── syscall.h │ ├── memmgr │ ├── layout.asm │ ├── layout.h │ ├── stackguard │ │ └── stackguard.h │ └── tables │ │ ├── gdt.asm │ │ └── gdt.h │ └── real_mode │ └── client.h ├── linker.ld ├── memory_layout.md ├── requirements.txt ├── scripts ├── build_image.sh └── burn.sh ├── src ├── bootloader │ ├── Makefile.mk │ ├── constants.asm │ ├── disk.asm │ ├── io.asm │ ├── stage1.asm │ ├── stage2.asm │ └── stage2.c ├── drivers │ ├── disk │ │ ├── Makefile.mk │ │ ├── disk.c │ │ ├── disk.h │ │ ├── disk_16.asm │ │ └── disk_16.c │ ├── display │ │ ├── Makefile.mk │ │ ├── text_mode.h │ │ ├── text_mode_bios.asm │ │ ├── text_mode_bios.c │ │ ├── text_mode_vga.asm │ │ ├── text_mode_vga.c │ │ └── vga │ │ │ ├── Makefile.mk │ │ │ └── graphics.c │ ├── keyboard │ │ ├── Makefile.mk │ │ ├── keyboard.asm │ │ ├── keyboard.c │ │ ├── keyboard.h │ │ └── scancode_handler.c │ ├── pic │ │ ├── Makefile.mk │ │ ├── pic.c │ │ └── pit.c │ └── ps2 │ │ ├── Makefile.mk │ │ ├── keyboard.c │ │ ├── ps2.asm │ │ └── ps2.c ├── fs │ ├── Makefile.mk │ ├── ffs.c │ └── mbr.c ├── kernel │ ├── Makefile.mk │ ├── core.asm │ ├── core.c │ ├── interrupts │ │ ├── Makefile.mk │ │ ├── exceptions.asm │ │ ├── exceptions.c │ │ ├── interrupts.asm │ │ ├── interrupts.c │ │ ├── timer.asm │ │ └── timer.c │ ├── panic.asm │ ├── panic.c │ ├── process │ │ ├── Makefile.mk │ │ ├── allocation.c │ │ ├── process.asm │ │ ├── process.c │ │ └── scheduler.c │ └── syscall │ │ ├── Makefile.mk │ │ ├── console.c │ │ ├── file_handler.c │ │ ├── graphics.c │ │ ├── keyboard.c │ │ └── syscall.c ├── lib │ ├── app │ │ ├── Makefile.mk │ │ └── entry.asm │ ├── ds │ │ ├── Makefile.mk │ │ ├── queue.c │ │ └── queue.h │ └── utils │ │ ├── Makefile.mk │ │ ├── basic.asm │ │ ├── basic.c │ │ ├── basic.h │ │ ├── basic_16.asm │ │ ├── color.c │ │ ├── color.h │ │ ├── input.c │ │ ├── input.h │ │ ├── logging.c │ │ ├── logging.h │ │ ├── output.c │ │ ├── output.h │ │ ├── time.asm │ │ ├── time.c │ │ └── time.h ├── memmgr │ ├── stackguard │ │ ├── Makefile.mk │ │ ├── stackguard.asm │ │ └── stackguard.c │ └── tables │ │ ├── Makefile.mk │ │ └── gdt.c ├── real_mode │ ├── Makefile.mk │ ├── client.asm │ └── static_library.asm └── usr │ ├── include │ ├── Makefile.mk │ ├── conio.h │ ├── ctype.h │ ├── dirent.h │ ├── graphics.h │ ├── iostream.h │ ├── limits.h │ ├── math.h │ ├── new.h │ ├── process.h │ ├── stdarg.h │ ├── stddef.h │ ├── stdint.h │ ├── stdio.h │ ├── stdlib.h │ ├── string.h │ ├── string.tcc │ ├── sys │ │ └── syscall.h │ ├── testing.h │ ├── utility.h │ ├── utility.tcc │ ├── vector.h │ └── vector.tcc │ ├── lib │ ├── Makefile.mk │ ├── conio.c │ ├── ctype.c │ ├── dirent.c │ ├── graphics.c │ ├── iostream.cpp │ ├── math.c │ ├── new.cpp │ ├── process.c │ ├── stdio.c │ ├── stdlib.asm │ ├── stdlib.c │ ├── string.c │ └── sys │ │ └── syscall.asm │ └── local │ └── src │ ├── Makefile.mk │ ├── calc.c │ ├── cat.c │ ├── desktop.cpp │ ├── echo.c │ ├── forkbomb.c │ ├── logo.cpp │ ├── ls.c │ ├── more.c │ ├── multiprocessing.c │ ├── pingpong.c │ ├── sh.c │ ├── simplecpp.cpp │ ├── test-math.cpp │ ├── test-stdlib.cpp │ ├── test-string.cpp │ ├── test-vector.cpp │ └── tictactoe.c └── tests ├── __init__.py ├── app ├── cat_test.sh ├── libraries │ └── usr_lib_test.sh ├── ls_test.sh ├── multiprocessing_test.sh ├── shell_test.sh └── tictactoe_test.sh ├── app_ttt_test.sh ├── bootloader_stage1_test.sh ├── bootloader_stage2_test.sh ├── fuzzy_shell_test.sh ├── kernel_core_entry_test.sh ├── lib.sh ├── qemu ├── __init__.py ├── args.py ├── key.py └── monitor.py ├── qemu_monitor_expect.sh └── run.sh /.gdbinit: -------------------------------------------------------------------------------- 1 | set $CS_BASE = 0x30000 2 | set $DS_BASE = 0x30000 3 | set $SS_BASE = 0x30000 4 | 5 | # config 6 | set architecture i386:x86-64 7 | 8 | set pagination off 9 | set prompt \033[32mgdb$ \033[0m 10 | 11 | # layout 12 | define cb 13 | b *($CS_BASE+$arg0) 14 | end 15 | 16 | define cprintstr 17 | x/s $DS_BASE+$arg0 18 | end 19 | 20 | define cprintdata 21 | x/4w $DS_BASE+$arg0 22 | end 23 | 24 | define casm 25 | x/10i ($CS_BASE+$pc) 26 | end 27 | 28 | define ccode 29 | list *$pc 30 | end 31 | 32 | define cstack 33 | info register esp ebp 34 | x/24w $SS_BASE+$esp 35 | end 36 | 37 | define creg 38 | # Not needed for now: cr0 cr2 cr3 cr4 cr8 gs_base 39 | info register eax ebx ecx edx esi edi eflags cs ss ds es fs gs fs_base 40 | end 41 | 42 | define _clayout_title 43 | echo \033[33m---[ $arg0 ]---------------\033[0m\n 44 | end 45 | 46 | define clayout 47 | # clear screen 48 | echo \033c 49 | _clayout_title ASM 50 | casm 51 | _clayout_title Code 52 | ccode 53 | _clayout_title Register 54 | creg 55 | _clayout_title Stack 56 | cstack 57 | _clayout_title Command 58 | end 59 | 60 | define cc 61 | # shorthand for cresetscreen 62 | clayout 63 | end 64 | 65 | # mode 66 | define view_realmode 67 | set $CS_BASE = 0x0000 68 | set $DS_BASE = 0x0000 69 | set $SS_BASE = 0x0000 70 | symbol-file build/bootloader/stage2.elf 71 | clayout 72 | end 73 | 74 | define view_kernelmode 75 | set $CS_BASE = 0x0C000 76 | set $DS_BASE = 0x0C000 77 | set $SS_BASE = 0x0C000 78 | symbol-file build/kernel/core.elf 79 | clayout 80 | end 81 | 82 | define view_usermode 83 | set $CS_BASE = 0x30000 84 | set $DS_BASE = 0x30000 85 | set $SS_BASE = 0x30000 86 | symbol-file 87 | clayout 88 | end 89 | 90 | # override existing shorthand 91 | define si 92 | stepi 93 | cc 94 | end 95 | define ni 96 | nexti 97 | cc 98 | end 99 | define c 100 | continue 101 | cc 102 | end 103 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. ... 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Emulation (if applicable):** 24 | - Host OS: [e.g. iOS] 25 | - Emulator 26 | 27 | **Real machine (if applicable, difficult to test or fix):** 28 | - CPU: 29 | - BIOS: 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Prerequisite 13 | run: sudo bash before_install.sh 14 | 15 | - name: make 16 | run: make 17 | 18 | - uses: actions/upload-artifact@v2 19 | with: 20 | name: Raw Image 21 | path: build/FuzzyOS_raw.zip 22 | 23 | - uses: actions/upload-artifact@v2 24 | with: 25 | name: Virtual Box Image 26 | path: build/FuzzyOS.vdi 27 | 28 | - uses: actions/upload-artifact@v2 29 | with: 30 | name: VMWare Image 31 | path: build/FuzzyOS.vmdk 32 | 33 | - name: make test 34 | run: make test 35 | 36 | - uses: actions/upload-artifact@v2 37 | with: 38 | name: Test Screenshots 39 | path: /tmp/*_test.png 40 | -------------------------------------------------------------------------------- /.github/workflows/code-style.yml: -------------------------------------------------------------------------------- 1 | name: Code Style CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Install clang-format 17 | run: | 18 | sudo apt-get update 19 | sudo apt install clang-format 20 | - name: C/CPP code style check 21 | run: | 22 | find . -iname *.h -o -iname *.c -o -iname *.cpp -o -iname *.hpp -o -iname *.tcc | \ 23 | xargs clang-format --style="{BasedOnStyle: llvm, IndentWidth: 4}" --dry-run --Werror 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Prerequisite 15 | run: sudo bash before_install.sh 16 | 17 | - name: make 18 | run: make 19 | 20 | - name: make test 21 | run: make test 22 | 23 | - name: Get current time 24 | uses: 1466587594/get-current-time@v2 25 | id: current-time 26 | with: 27 | format: YYYY-MM-DD HH:mm 28 | utcOffset: "+00:00" 29 | 30 | - name: Create Release 31 | id: create_release 32 | uses: actions/create-release@v1 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | with: 36 | tag_name: ${{ github.ref }} 37 | release_name: Release ${{ steps.current-time.outputs.formattedTime }} UTC 38 | draft: false 39 | prerelease: false 40 | commitish: ${{ github.sha }} 41 | 42 | - name: Upload Raw Image 43 | uses: actions/upload-release-asset@v1 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | with: 47 | upload_url: ${{ steps.create_release.outputs.upload_url }} 48 | asset_path: build/FuzzyOS_raw.zip 49 | asset_name: FuzzyOS_raw.zip 50 | asset_content_type: application/zip 51 | 52 | - name: Upload Virtual Box Image 53 | uses: actions/upload-release-asset@v1 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | with: 57 | upload_url: ${{ steps.create_release.outputs.upload_url }} 58 | asset_path: build/FuzzyOS.vdi 59 | asset_name: FuzzyOS.vdi 60 | asset_content_type: application/x-virtualbox-vdi 61 | 62 | - name: Upload VMWare Image 63 | uses: actions/upload-release-asset@v1 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 66 | with: 67 | upload_url: ${{ steps.create_release.outputs.upload_url }} 68 | asset_path: build/FuzzyOS.vmdk 69 | asset_name: FuzzyOS.vmdk 70 | asset_content_type: application/x-vmdk 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.vmdk 2 | *.o 3 | local_notes/ 4 | build/ 5 | src_test/ 6 | build_test/ 7 | __pycache__/ 8 | .vscode/ -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute 2 | 3 | #### Why was FuzzyOS created? 4 | 5 | * It's primarily a Hobby Project with aim of learning some trivial challenges during OS development. 6 | 7 | #### Did you find a bug? 8 | 9 | * I won't be surprised. Feel free to [create a GitHub issue](https://github.com/scopeInfinity/FuzzyOS/issues/new) and raise a pull request to submit a patch. 10 | 11 | #### Did you write a patch that fixes a bug? 12 | 13 | * Please ensure code is formatted as per clang-format `{BasedOnStyle: llvm, IndentWidth: 4}` style. 14 | 15 | ``` 16 | find . -iname *.h -o -iname *.c -o -iname *.cpp -o -iname *.hpp -o -iname *.tcc | xargs clang-format --style="{BasedOnStyle: llvm, IndentWidth: 4}" -i 17 | ``` 18 | 19 | * Open a new GitHub pull request with the patch. 20 | 21 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 22 | 23 | #### **Do you intend to add a new feature or change an existing one?** 24 | 25 | * Suggest the feature or change by opening a [new GitHub issue](https://github.com/scopeInfinity/FuzzyOS/issues/new). After discussing it with the maintainer one can proceed with the patch. 26 | 27 | Thanks! 28 | -------------------------------------------------------------------------------- /before_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get update 3 | apt install -y make nasm gcc cpulimit qemu-system-x86 gocr expect gdb graphicsmagick-imagemagick-compat 4 | 5 | python3 -m pip install -r requirements.txt 6 | -------------------------------------------------------------------------------- /emulator/Makefile.mk: -------------------------------------------------------------------------------- 1 | QEMU_SHUT_FLAGS= -no-shutdown -no-reboot 2 | QEMU_EXTRA_FLAGS= 3 | QEMU_GDB_PORT=9000 4 | QEMU_MONITOR_PORT=55555 5 | GDB_EX=echo Use GDB_EX="" to execute command on connect.\n 6 | 7 | qemu: $(image_raw) 8 | qemu-system-x86_64 -smp 1 -m 128M -hda $< $(QEMU_SHUT_FLAGS) $(QEMU_EXTRA_FLAGS) 9 | 10 | qemu_vvv: $(image_raw) 11 | qemu-system-x86_64 -smp 1 -m 128M -hda $< $(QEMU_SHUT_FLAGS) $(QEMU_EXTRA_FLAGS) -d cpu,exec,in_asm 12 | 13 | qemu_monitor: $(image_raw) 14 | qemu-system-x86_64 -smp 1 -m 128M -hda $< $(QEMU_SHUT_FLAGS) $(QEMU_EXTRA_FLAGS) -monitor telnet:127.0.0.1:$(QEMU_MONITOR_PORT),server,nowait 15 | 16 | qemu_debug: $(image_raw) 17 | qemu-system-x86_64 -S -gdb tcp::$(QEMU_GDB_PORT) -smp 1 -m 128M -hda $< $(QEMU_SHUT_FLAGS) -d cpu,exec,in_asm 18 | 19 | qemu_debug_connect: 20 | gdb -x $(ROOT_DIR)/.gdbinit -ex "target remote :$(QEMU_GDB_PORT)" -ex '$(GDB_EX)' -ex 'continue' 21 | 22 | -------------------------------------------------------------------------------- /external/src/Makefile.mk: -------------------------------------------------------------------------------- 1 | external: $(BUILD_DIR)/external/bin/mkfs.ffs $(MINIMAL_DISK) 2 | 3 | $(BUILD_DIR)/external/bin/mkfs.ffs: external/src/mkfs_ffs.c 4 | mkdir -p $(dir $@) 5 | $(HOST_CC) -o $@ $< 6 | 7 | $(BUILD_DIR)/external/bin/mbr_builder: external/src/mbr_builder.c 8 | mkdir -p $(dir $@) 9 | $(HOST_CC) -o $@ $< 10 | 11 | $(BUILD_DIR)/external/out/fuzzy_mount: $(patsubst $(SRC_APP)/%.c,$(BUILD_APP)/%,$(shell find $(SRC_APP)/ -name '*.c')) \ 12 | $(patsubst $(SRC_APP)/%.cpp,$(BUILD_APP)/%,$(shell find $(SRC_APP)/ -name '*.cpp')) \ 13 | README.md \ 14 | .gdbinit 15 | # not a mount 16 | mkdir -p $@ 17 | cp -f -t $@ $^ 18 | 19 | $(MINIMAL_DISK): $(BUILD_DIR)/external/bin/mkfs.ffs $(BUILD_DIR)/external/out/fuzzy_mount 20 | mkdir -p $(dir $@) 21 | $< $(BUILD_DIR)/external/out/fuzzy_mount/ $@ 22 | -------------------------------------------------------------------------------- /external/src/mbr_builder.c: -------------------------------------------------------------------------------- 1 | // Classical generic MBR 2 | // https://en.wikipedia.org/wiki/Master_boot_record#PTE 3 | #include 4 | #include 5 | 6 | char DRIVE = 0x80; 7 | char DRIVE_INACTIVE = 0x00; 8 | // https://en.wikipedia.org/wiki/Partition_type 9 | char PARTITION_TYPE = 0x07; // Stealing exFAT 10 | 11 | // https://en.wikipedia.org/wiki/Cylinder-head-sector 12 | void lba_to_chs(int lba, char chs[3]) { 13 | // only lower 3 bytes of CHS are valid. 14 | // TODO: Not implemented 15 | 16 | // # Sector 17 | // START_CHS_HEAD= 18 | // START_CHS_CYLINDER= 19 | // START_CHS_SECTOR 20 | // START_CHS_ADDRESS= 21 | // END_CHS_HEAD= 22 | // END_CHS_CYLINDER= 23 | // END_CHS_SECTORA 24 | // END_CHS_ADDRESS= 25 | } 26 | 27 | void write_boot_signature(FILE *out) { 28 | fseek(out, 510, SEEK_SET); 29 | char boot_signature[2] = {0x55, 0xAA}; 30 | fwrite(boot_signature, 1, sizeof(boot_signature), out); 31 | } 32 | 33 | void write_partition_entry(FILE *out, int id, char drive, int lba, 34 | int sector_count) { 35 | struct PartitionEntry entry; 36 | entry.drive = drive; 37 | entry.partition_type = PARTITION_TYPE; 38 | entry.lba = lba; 39 | entry.sector_count = sector_count; 40 | // TODO: write lba using lba_to_chs too 41 | fseek(out, MBR_PARTITION_BEGIN + id * sizeof(struct PartitionEntry), 42 | SEEK_SET); 43 | fwrite(&entry, 1, sizeof(entry), out); 44 | printf("Added partition entry %d at lba: %d sector_count: %d\n", id, lba, 45 | sector_count); 46 | } 47 | 48 | void write_partition(FILE *out, FILE *in, int lba) { 49 | int count; 50 | char buffer[1024]; 51 | rewind(in); 52 | fseek(out, lba * 512, SEEK_SET); 53 | while ((count = fread(buffer, sizeof(char), sizeof(buffer), in)) > 0) { 54 | fwrite(buffer, sizeof(char), count, out); 55 | } 56 | } 57 | 58 | void populate_outfile_using_image_prefix(FILE *out, FILE *in) { 59 | char buffer[1024]; 60 | int count; 61 | rewind(in); 62 | rewind(out); 63 | while ((count = fread(buffer, sizeof(char), sizeof(buffer), in)) > 0) { 64 | fwrite(buffer, sizeof(char), count, out); 65 | } 66 | } 67 | 68 | int main(int argc, char *argv[]) { 69 | // Meant to create single parition entry within MBR. 70 | if (argc != 4) { 71 | printf("Usage: %s \n", argv[0]); 72 | return 0; 73 | } 74 | FILE *out = fopen(argv[1], "wb"); 75 | FILE *image_prefix = fopen(argv[2], "rb"); 76 | FILE *part1 = fopen(argv[3], "rb"); 77 | 78 | populate_outfile_using_image_prefix(out, image_prefix); 79 | 80 | int lba, sector_count; // 4 bytes 81 | { 82 | fseek(out, 0L, SEEK_END); 83 | int file_size = ftell(out); 84 | rewind(out); 85 | lba = (file_size + 511) / 512; 86 | } 87 | { 88 | fseek(part1, 0L, SEEK_END); 89 | int file_size = ftell(part1); 90 | sector_count = (file_size + 511) / 512; 91 | } 92 | 93 | write_boot_signature(out); 94 | write_partition_entry(out, 0, DRIVE, lba, sector_count); 95 | write_partition(out, part1, lba); 96 | write_partition_entry(out, 1, DRIVE_INACTIVE, 0, 0); 97 | write_partition_entry(out, 2, DRIVE_INACTIVE, 0, 0); 98 | write_partition_entry(out, 3, DRIVE_INACTIVE, 0, 0); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /external/src/mkfs_ffs.c: -------------------------------------------------------------------------------- 1 | /* 2 | mkfs.ffs binary implementation for Linux. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define FILEENTRY_LOCATION(file_id) \ 12 | (FS_FFS_FIRST_BLOCK_SIZE + FS_FFS_FILEENTRY_SIZE * (file_id)) 13 | 14 | void write_first_block(FILE *out) { 15 | union FFSMetaData block; 16 | strncpy(block.content.signature, FS_FFS_SIGNATURE, 17 | sizeof(block.content.signature)); 18 | 19 | rewind(out); 20 | fwrite(block.bytes, 1, sizeof(block.bytes), out); 21 | } 22 | 23 | void write_file(int file_id, FILE *outfile, int *outfile_nextdata_block, 24 | const char *filename, FILE *srcfile, int is_executable) { 25 | fseek(srcfile, 0L, SEEK_END); 26 | int file_size = ftell(srcfile); 27 | rewind(srcfile); 28 | 29 | union FFSFileEntry entry; 30 | strncpy(entry.content.filename, filename, sizeof(entry.content.filename)); 31 | entry.content.filesize = file_size; 32 | entry.content.start_block_id = (*outfile_nextdata_block); 33 | entry.content.flags = 0; 34 | if (is_executable) { 35 | entry.content.flags |= FFS_FILE_FLAG_EXECUTABLE; 36 | } 37 | 38 | // Write file entry 39 | fseek(outfile, FILEENTRY_LOCATION(file_id), SEEK_SET); 40 | fwrite(entry.bytes, 1, sizeof(entry.bytes), outfile); 41 | 42 | // Write file content 43 | char buffer[512]; 44 | size_t bytes_read; 45 | printf("Writting context at %d\n", ((*outfile_nextdata_block))); 46 | fseek(outfile, (*outfile_nextdata_block) * FS_BLOCK_SIZE, SEEK_SET); 47 | (*outfile_nextdata_block) += 48 | (file_size + FS_BLOCK_SIZE - 1) / FS_BLOCK_SIZE; 49 | 50 | while ((bytes_read = fread(buffer, 1, sizeof(buffer), srcfile)) > 0) { 51 | fwrite(buffer, 1, bytes_read, outfile); 52 | } 53 | 54 | printf("Added '%s' file\n", filename); 55 | } 56 | 57 | void write_nofile(int file_id, FILE *out) { 58 | union FFSFileEntry entry; 59 | entry.content.start_block_id = 0; 60 | fseek(out, FILEENTRY_LOCATION(file_id), SEEK_SET); 61 | fwrite(entry.bytes, 1, sizeof(entry.bytes), out); 62 | } 63 | 64 | int create_partition(char *src_dir, char *out_filepath) { 65 | printf("dir:%s partition: %s\n", src_dir, out_filepath); 66 | FILE *src = opendir(src_dir); 67 | FILE *out = fopen(out_filepath, "wb"); 68 | if (!out) { 69 | fprintf(stderr, "can't open output file."); 70 | return 1; 71 | } 72 | if (!src) { 73 | fprintf(stderr, "can't open src directory."); 74 | return 2; 75 | } 76 | // header 77 | write_first_block(out); 78 | // interate over files and write them in partition. 79 | struct dirent *de; 80 | int file_id = 0; 81 | int outfile_nextdata_block = FS_FFS_BLOCK_DATA_START; 82 | char buffer_filename[4096]; 83 | while ((de = readdir(src)) != NULL) { 84 | strncpy(buffer_filename, src_dir, sizeof(buffer_filename)); 85 | strncat(buffer_filename, "/", sizeof(buffer_filename)); 86 | strncat(buffer_filename, de->d_name, sizeof(buffer_filename)); 87 | if (file_id >= FS_FFS_FILEENTRY_COUNT) { 88 | fprintf("reached max supported files, ignoring file '%s'", 89 | buffer_filename); 90 | continue; 91 | } 92 | struct stat file_stat; 93 | stat(buffer_filename, &file_stat); 94 | if (!S_ISREG(file_stat.st_mode)) { 95 | fprintf("skipping non-regular file '%s': %d", buffer_filename, 96 | file_stat.st_mode); 97 | continue; 98 | } 99 | int is_executable = file_stat.st_mode & S_IXUSR; 100 | 101 | FILE *file_src = fopen(buffer_filename, "rb"); 102 | write_file(file_id++, out, &outfile_nextdata_block, de->d_name, 103 | file_src, is_executable); 104 | fclose(file_src); 105 | } 106 | while (file_id < FS_FFS_FILEENTRY_COUNT) { 107 | write_nofile(file_id++, out); 108 | } 109 | 110 | fclose(out); 111 | return 0; 112 | } 113 | 114 | int main(int argc, const char **argv) { 115 | if (argc != 3) { 116 | printf("Usage: %s \n", argv[0]); 117 | return 0; 118 | } 119 | char *src_dir = argv[1]; 120 | char *out_filepath = argv[2]; 121 | return create_partition(src_dir, out_filepath); 122 | } -------------------------------------------------------------------------------- /include/fuzzy/drivers/display/vga/graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int graphics_set_mode(uint8_t video_mode); 6 | uint8_t graphics_get_mode(); 7 | 8 | int graphics_write_320x200x256(int user_ds, uint8_t *__us_buffer); 9 | -------------------------------------------------------------------------------- /include/fuzzy/drivers/pic/pic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PIC_PIT_FREQ 1193182 // hz 6 | #define PIC_PIT_MAX_COUNTER 0xFFFF 7 | 8 | #define PIC_IRQ_PIT 0 9 | 10 | void pic_init(); 11 | 12 | void pic_writemask_new(uint16_t mask); 13 | uint16_t pic_readmask_new(); 14 | 15 | void pic_irq_enable(int irq); 16 | void pic_irq_disable(int irq); 17 | -------------------------------------------------------------------------------- /include/fuzzy/drivers/pic/pit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static uint16_t frequency_dividor; 6 | 7 | void pit_init(); 8 | 9 | void pit_set_counter(uint16_t counter); 10 | uint16_t pit_get_counter(); 11 | 12 | void pit_reload_counter(); 13 | void pit_reset(); 14 | -------------------------------------------------------------------------------- /include/fuzzy/drivers/port.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PORT_PIC1_CMD 0x20 6 | #define PORT_PIC1_DATA 0x21 7 | #define PORT_PIC2_CMD 0xA0 8 | #define PORT_PIC2_DATA 0xA1 9 | 10 | #define PORT_PIT_DATA0 0x40 11 | #define PORT_PIT_DATA1 0x41 12 | #define PORT_PIT_DATA2 0x42 13 | #define PORT_PIT_CMD 0x43 14 | 15 | #define PORT_PS2_DATA 0x60 16 | #define PORT_PS2_CMD 0x64 17 | #define PORT_PS2_STATUS 0x64 18 | 19 | static inline void outb(uint16_t port, uint8_t data) { 20 | __asm__ volatile("outb %0, %1 \n" 21 | : /* output */ 22 | : "a"(data), "ir"(port) /* input */ 23 | :); 24 | } 25 | 26 | static inline uint8_t inputb(uint16_t port) { 27 | uint8_t data; 28 | __asm__ volatile("inb %1, %0 \n" 29 | : "=a"(data) /* output */ 30 | : "ir"(port) /* input */ 31 | :); 32 | return data; 33 | } 34 | -------------------------------------------------------------------------------- /include/fuzzy/drivers/ps2/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void ps2_keyboard_init(); 4 | 5 | char ps2_keyboard_get_key_pressed_blocking(); 6 | char ps2_keyboard_get_key_pressed_poll(); 7 | int ps2_keyboard_get_kbhit(); 8 | -------------------------------------------------------------------------------- /include/fuzzy/drivers/ps2/ps2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void ps2_init(); 6 | 7 | uint8_t ps2_read_data(); 8 | void ps2_write_port1(uint8_t byte); 9 | void ps2_controller_wait_for_empty_input(); 10 | void ps2_controller_wait_for_full_output(); 11 | 12 | void interrupt_register_0x21_0x2C_irq1_ir12_keyboard_mouse(); 13 | void irq1_handler(); 14 | void irq12_handler(); 15 | -------------------------------------------------------------------------------- /include/fuzzy/fs/ffs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | Format 4 | metadata; 5 | fileentry0; 128 byte each 6 | fileentry1 7 | fileentry2 8 | fileentry3 9 | ... 10 | fileentry127; 16896 bytes or 33 block used. 11 | data... 12 | */ 13 | 14 | /* 15 | Number of file which can be stored: 64 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #define FS_FFS_FILENAME_LIMIT 100 // including NULL, same as dirent.h 22 | #define FFS_UNIQUE_PARITION_ID 0 // only paritition 0 is supported for now 23 | 24 | enum FFSFileFlagMask { FFS_FILE_FLAG_EXECUTABLE = 1 << 0 }; 25 | 26 | union FFSMetaData { 27 | struct { 28 | char signature[16]; 29 | } content; 30 | char bytes[512]; 31 | }; // size: 512 bytes 32 | 33 | union FFSFileEntry { 34 | struct { 35 | int32_t start_block_id; // 0 implies no file. 36 | int32_t filesize; 37 | char filename[FS_FFS_FILENAME_LIMIT]; 38 | uint32_t flags; 39 | } content; 40 | char bytes[128]; 41 | }; // size: 128 bytes 42 | 43 | #define FS_FFS_FIRST_BLOCK_SIZE sizeof(union FFSMetaData) 44 | #define FS_FFS_FILEENTRY_SIZE sizeof(union FFSFileEntry) 45 | #define FS_FFS_FILEENTRY_COUNT 128 46 | 47 | #define FS_BLOCK_SIZE 512 48 | #define FS_FFS_BLOCK_DATA_START \ 49 | ((FS_FFS_FIRST_BLOCK_SIZE + \ 50 | FS_FFS_FILEENTRY_SIZE * FS_FFS_FILEENTRY_COUNT) / \ 51 | FS_BLOCK_SIZE) 52 | 53 | #define FS_FFS_SIGNATURE "__FuzzyOS__FFS__" // 16 chars 54 | 55 | int resolve_abs_lba(int parition_id, int partition_relative_lba); 56 | 57 | int partition_read_block(int block_index, void *wr_buffer); 58 | 59 | int verify_partition(int partition_id); 60 | 61 | int fetch_file_entry(int partition_id, int entry_id, union FFSFileEntry *entry); 62 | 63 | int fetch_file_content(const int partition_id, const union FFSFileEntry *entry, 64 | char buffer[FS_BLOCK_SIZE], const int file_block_id); -------------------------------------------------------------------------------- /include/fuzzy/fs/mbr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MBR_PARTITION_BEGIN 446 4 | 5 | struct PartitionEntry { 6 | char drive; 7 | char start_chs[3]; 8 | char partition_type; 9 | char end_chs[3]; 10 | int lba; // 4 bytes 11 | int sector_count; // 4 bytes 12 | }; 13 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/interrupts/exceptions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void interrupt_register_0x00_0x1F_exceptions(); 4 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/interrupts/interrupts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Interrupt descriptive table 5 | * [00:32) ; [0x00:0x20) ; Exceptions: faults, traps, aborts 6 | * [32:48) ; [0x20:0x30) ; IRQ0-15 7 | * 80 ; 0x32 ; syscall 8 | */ 9 | #define IDT_SIZE 256 10 | #define IDT_IRQ_OFFSET 0x20 11 | 12 | // Used by /usr/lib/process.c 13 | #define IDT_IRQ0_PIC (IDT_IRQ_OFFSET + 0) 14 | #define IDT_IRQ1_KEYBOARD (IDT_IRQ_OFFSET + 1) 15 | #define IDT_IRQ12_MOUSE (IDT_IRQ_OFFSET + 12) 16 | // Used by /usr/lib/sys/syscall.asm 17 | #define IDT_SYSCALL 0x32 18 | 19 | extern void populate_idt_entry_32bit(int id, unsigned int address, 20 | unsigned char dpl, // 2-bit 21 | int is_trap); -------------------------------------------------------------------------------- /include/fuzzy/kernel/interrupts/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void interrupt_pit_enable(); 4 | 5 | void interrupt_register_0x20_irq0_pit(); 6 | 7 | // return new user_sp 8 | int create_infant_process_irq0_stack(int user_datasegment, int user_sp); 9 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PANIC(err, message) panic((err), (message), __FILE__, __LINE__) 6 | 7 | #define ASSERT(ok) (ok || panic(0, "Assert Failed: " #ok, __FILE__, __LINE__)) 8 | 9 | void panic_just_halt(); 10 | int panic(int err, const char *message, const char *src_file, 11 | unsigned int line_number); 12 | int panic_screen_init(); 13 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/process/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define MAX_PROCESS 127 6 | // each process will have CS and DS, and kernel 7 | // CS and DS is already included in GDT standard table. 8 | #define GDT_TABLE_SIZE ((MAX_PROCESS - 1) * 2 + GDT_STD_SIZE) 9 | 10 | typedef char ARGV[PROCESS_MAX_ARGC][PROCESS_MAX_ARG_LEN]; 11 | 12 | enum process_state { 13 | STATE_COLD = 0, // STATE_COLD must be 0 14 | STATE_LOADING, // resources allocated but not ready. 15 | STATE_READY, 16 | STATE_RUNNING, 17 | STATE_EXIT, // should be unallocated in next scheduling cycle 18 | STATE_BLOCK, // process is waiting on IO, process_wait, etc. 19 | }; 20 | 21 | enum process_state_block { 22 | STATE_BLOCK_PROCESS_WAIT, // waiting for another process termination. 23 | }; 24 | 25 | struct Process { 26 | enum process_state state; 27 | 28 | unsigned int ss, cs, sp, ip; 29 | unsigned int *e; 30 | 31 | int exit_code; 32 | 33 | unsigned int ppid; // parent pid, 0 to root under kernel_core 34 | 35 | enum process_state_block block_type; // valid, if state == BLOCK 36 | union { 37 | struct { 38 | unsigned int blocked_on_pid; 39 | unsigned int blocking_pid; // TODO: make list 40 | } process_wait; 41 | } block_data; // valid if state == BLOCK 42 | 43 | // schedule for IRQ0 44 | // 0 - no fork requested or last fork successful; can try fork again 45 | // 1 - fork requested; should NOT try fork for now. 46 | // -1 - last fork failed; can try fork again 47 | signed char flagirq0_fork_ready; 48 | int flagirq0_fork_newchild; // pid 49 | }; 50 | 51 | void process_scheduler_init(); 52 | 53 | int syscall_1_process(int operation, int a0, int a1, int a2, int a3, 54 | int user_ds); 55 | 56 | // getter 57 | struct Process *get_process(int pid); 58 | int get_gdt_number_from_entry_id(int id); 59 | 60 | // allocation 61 | int get_idt_cs_entry(int process_id); 62 | int get_idt_ds_entry(int process_id); 63 | int get_idt_reverse_pid_lookup_cs(int cs); 64 | int get_idt_reverse_pid_lookup_ds(int ds); 65 | 66 | // process create or kill 67 | int process_create(unsigned int ppid, int argc, char *argv[]); 68 | void process_kill(unsigned int pid, int status); 69 | 70 | // scheduler 71 | 72 | // on return process_scheduler, irq0 should execute from 73 | // cs:eip with ss:esp (updated or not). 74 | void process_scheduler(int *_e_ip, int *_e_cs, int *_e_sp, int *_e_ss); 75 | 76 | // user space <-> kernel space data transfer helper 77 | extern void syscall_strncpy_user_to_kernel(int user_ds, char *src_es_address, 78 | char *dest_ds_address, size_t size); 79 | extern void syscall_strncpy_kernel_to_user(int user_ds, char *dest_address, 80 | char *src_address, size_t size); 81 | // assumes size is multiple of 4 82 | extern void kernel_memncpy_absolute(int dst_ds, char *dst_address, int src_ds, 83 | char *src_address, size_t size); 84 | 85 | // operations 86 | int process_waitpid(unsigned int pid, unsigned int blocked_on_pid, 87 | int *exit_code); 88 | 89 | int process_fork_mark_ready(int pid); 90 | int process_fork_check_ready(int pid); 91 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/syscall/console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int syscall_3_console(int operation, int a1, int a2, int a3, int user_ds); 4 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/syscall/file_handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // return kernel_file_id if found. 4 | // if successful (non-negative return value), entry is updated. 5 | int file_handler_find(char *filename, union FFSFileEntry *entry); 6 | 7 | int syscall_2_file_handler(int operation, int a1, int a2, int a3); 8 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/syscall/graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int syscall_4_graphics(int operation, int a1, int a2, int a3, int user_ds); 4 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/syscall/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int syscall_0_keyboard(int operation, int a1, int a2, int a3, int user_ds); 4 | -------------------------------------------------------------------------------- /include/fuzzy/kernel/syscall/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void interrupt_register_0x32_syscall(); 4 | 5 | int interrupt_handler_0x32_syscall_handler(int id, int arg0, int arg1, int arg2, 6 | int arg3, int user_ds); 7 | -------------------------------------------------------------------------------- /include/fuzzy/memmgr/layout.asm: -------------------------------------------------------------------------------- 1 | ; constants only 2 | 3 | ; START_ENSURE_SAME_layout_h 4 | MEMORY_APP_SIZE EQU 0x20000 5 | MEMORY_KERNEL_SIZE EQU 0x64000 6 | ; END_ENSURE_SAME_layout_h 7 | 8 | ; Kernel core is app with pid 0 9 | ; Kernel event is trigged via IRQ0 or syscall, 10 | STACKINIT_APP EQU (MEMORY_APP_SIZE-4) 11 | STACKINIT_KERNEL_CORE EQU (MEMORY_KERNEL_SIZE/2) 12 | 13 | ; Should be able to store 14 | ; - syscall and all internal calls 15 | ; - ISRs and all internal calls 16 | STACKINIT_KERNEL_EVENT EQU (MEMORY_KERNEL_SIZE-4) 17 | 18 | -------------------------------------------------------------------------------- /include/fuzzy/memmgr/layout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // START_ENSURE_SAME_layout_asm 4 | #define MEMORY_APP_SIZE 0x200000 5 | #define STACKINIT_APP (MEMORY_APP_SIZE - 4) 6 | #define MEMORY_REALLIBRARY_DATA_ADDRESS 0x70000 7 | #define MEMORY_REALLIBRARY_DATA_SIZE 0x10000 8 | #define MEMORY_VGA_GRAPHICS_ADDRESS 0xA0000 9 | #define MEMORY_VGA_GRAPHICS_SIZE 0x10000 10 | 11 | // END_ENSURE_SAME_layout_asm 12 | 13 | // Most of the memory layout relies on the fact the kernel is first app 14 | // and kernel's pid is 0. 15 | #define PID_KERNEL 0 16 | 17 | /* 18 | * Bug: 19 | * Bootloader stage 2 is not able to load kernel if 20 | * memory address need 20 address lines (0x10000) in 21 | * our case. 22 | * It works as intended in QEMU but not in others. 23 | * Mitigation: 24 | * - Forcing MEMORY_KERNEL_LOCATION to be represented 25 | * by 16 address lines. For ex 0xF000. 26 | * Resolution: 27 | * - I'm not sure. 28 | */ 29 | #define MEMORY_APPBASE_LOCATION 0x100000 30 | 31 | // Keep in sync with memory_layout.md 32 | #define memmgr_app_abs_location(pid) \ 33 | ((pid == PID_KERNEL) ? 0x0C000 \ 34 | : (MEMORY_APPBASE_LOCATION + (pid)*MEMORY_APP_SIZE)) 35 | #define memmgr_app_size(pid) ((pid == PID_KERNEL) ? 0x64000 : MEMORY_APP_SIZE) 36 | 37 | #define MEMORY_KERNEL_LOCATION (memmgr_app_abs_location(PID_KERNEL)) 38 | #define MEMORY_KERNEL_SIZE (memmgr_app_size(PID_KERNEL)) 39 | -------------------------------------------------------------------------------- /include/fuzzy/memmgr/stackguard/stackguard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define __MACRO_TO_STRING_INTERNAL(x) #x 4 | #define MACRO_TO_STRING(x) __MACRO_TO_STRING_INTERNAL(x) 5 | #define __STR__LINE__ MACRO_TO_STRING(__LINE__) 6 | 7 | #define VERIFY_STACKGUARD() \ 8 | (verify_stack_guard(__FILE__ "[" __STR__LINE__ \ 9 | "]; verify_stack_guard failed")) 10 | 11 | void verify_stack_guard(char err_message[]); 12 | -------------------------------------------------------------------------------- /include/fuzzy/memmgr/tables/gdt.asm: -------------------------------------------------------------------------------- 1 | ; constants only 2 | 3 | ; START_ENSURE_SAME_gdt_h 4 | 5 | GDT_STD_SELECTOR_NULL EQU 0 6 | GDT_STD_SELECTOR_KERNEL_CS EQU 1 7 | GDT_STD_SELECTOR_KERNEL_DS EQU 2 8 | GDT_STD_SELECTOR_ABS16_CS EQU 3 9 | GDT_STD_SELECTOR_ABS16_DS EQU 4 10 | GDT_STD_SELECTOR_ABS32_DS EQU 6 11 | GDT_STD_SIZE EQU 7 12 | 13 | GDT_STD_SIZEOF_ENTRY EQU 8 14 | ; END_ENSURE_SAME_gdt_h 15 | 16 | GDT_NULL_CS EQU (GDT_STD_SELECTOR_NULL*GDT_STD_SIZEOF_ENTRY) 17 | GDT_KERNEL_CS EQU (GDT_STD_SELECTOR_KERNEL_CS*GDT_STD_SIZEOF_ENTRY) 18 | GDT_KERNEL_DS EQU (GDT_STD_SELECTOR_KERNEL_DS*GDT_STD_SIZEOF_ENTRY) 19 | GDT_ABS16_CS EQU (GDT_STD_SELECTOR_ABS16_CS*GDT_STD_SIZEOF_ENTRY) 20 | GDT_ABS16_DS EQU (GDT_STD_SELECTOR_ABS16_DS*GDT_STD_SIZEOF_ENTRY) 21 | GDT_ABS32_DS EQU (GDT_STD_SELECTOR_ABS32_DS*GDT_STD_SIZEOF_ENTRY) 22 | -------------------------------------------------------------------------------- /include/fuzzy/memmgr/tables/gdt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Min GDT_TABLE_SIZE: 5 4 | // Max GDT_TABLE_SIZE: 8192 5 | 6 | // START_ENSURE_SAME_gdt_asm 7 | #define GDT_STD_SELECTOR_NULL 0 8 | #define GDT_STD_SELECTOR_KERNEL_CS 1 9 | #define GDT_STD_SELECTOR_KERNEL_DS 2 10 | #define GDT_STD_SELECTOR_ABS16_CS 3 11 | #define GDT_STD_SELECTOR_ABS16_DS 4 12 | #define GDT_STD_SELECTOR_ABS32_DS 6 13 | #define GDT_STD_SIZE 7 14 | // Entries on and after GDT_STD_SIZE are reserved for applications 15 | // and are in CS, DS order for each app. 16 | // END_ENSURE_SAME_gdt_asm 17 | 18 | #pragma pack(push, 1) 19 | struct GDTReference { 20 | unsigned short size; 21 | unsigned int base_address; 22 | }; 23 | struct GDTEntry { 24 | unsigned short limit0; 25 | unsigned short base0; 26 | unsigned char base1; 27 | unsigned char access_byte; 28 | unsigned char flags_limit1; 29 | unsigned char base2; 30 | }; 31 | #pragma pack(pop) 32 | 33 | #define GDT_ENTRY_FLAG_32_BIT_SELECTOR 1 34 | 35 | #define GDT_NULL_CS (GDT_STD_SELECTOR_NULL * sizeof(struct GDTEntry)) 36 | #define GDT_KERNEL_CS (GDT_STD_SELECTOR_KERNEL_CS * sizeof(struct GDTEntry)) 37 | #define GDT_KERNEL_DS (GDT_STD_SELECTOR_KERNEL_DS * sizeof(struct GDTEntry)) 38 | #define GDT_ABS16_CS (GDT_STD_SELECTOR_ABS16_CS * sizeof(struct GDTEntry)) 39 | #define GDT_ABS16_DS (GDT_STD_SELECTOR_ABS16_DS * sizeof(struct GDTEntry)) 40 | #define GDT_ABS32_DS (GDT_STD_SELECTOR_ABS32_DS * sizeof(struct GDTEntry)) 41 | 42 | int get_gdt_baseaddress(struct GDTEntry gdt_table[], unsigned int table_size, 43 | int entry_id); 44 | 45 | void populate_gdt_entry(struct GDTEntry *entry, unsigned int base, 46 | unsigned int limit, // 32 bit with 4k granularity 47 | unsigned char access_byte, int is_32_bit_selector); -------------------------------------------------------------------------------- /include/fuzzy/real_mode/client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int real_mode_client(int int_num, int eax, int ebx, int ecx, int edx, int es); 4 | -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | SECTIONS 2 | { 3 | .text ALIGN(8) : { *(.text) } 4 | .data ALIGN(8) : { 5 | *(.data) 6 | } 7 | .bss ALIGN(8) : { 8 | *(.bss) 9 | } 10 | _heap_start = .; 11 | } -------------------------------------------------------------------------------- /memory_layout.md: -------------------------------------------------------------------------------- 1 | ### Memory Layout 2 | 3 | | From | To | Size | Usage | 4 | |--------- |-------- |-------- |-------------------------------------------- | 5 | | 0x00000 | 0x003FF | - | IVT (standard) | 6 | | 0x00400 | 0x07BFF | - | BIOS data area + reserve | 7 | | 0x07C00 | 0x07DFF | 512B | Bootloader Stage 1 | 8 | | 0x07E00 | 0x07FFF | 512B | STATIC CODE for real_mode library | 9 | | 0x08000 | 0x0BFFF | 16KB | Bootloader Stage 2 + own stack | 10 | | 0x0C000 | 0x6FFFF | 400KB | Kernel + (core, isr, syscall) stack | 11 | | 0x70000 | 0x7FFFF | 64KB | Extra space for real_mode library+client | 12 | | 0x80000 | 0x9FFFF | 128KB | Extended BIOS Data area | 13 | | 0xA0000 | 0xBFFFF | 128KB | Video memory | 14 | | 0xC0000 | 0xFFFFF | 256KB | BIOS stuff | 15 | | 0x100000 | 0x1FFFFF | 1MB | pid 0; Kernel future location (not used) | 16 | | 0x200000 | 0x2FFFFF | 1MB | App pid 1 | 17 | | 0x300000 | 0x3FFFFF | 1MB | App pid 2 | 18 | | ... | .... | ... | ... | 19 | | ... | .... | 1MB | App pid 126 | 20 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pexpect 2 | -------------------------------------------------------------------------------- /scripts/build_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # The scripts take essential subimages as argument merging them while verifying their size. 3 | 4 | set -e 5 | if [ $# -ne 9 ] ; then 6 | echo "Usage: build_image.sh " 7 | echo " is sector count of preceding subimage." 8 | exit 1 9 | fi 10 | 11 | IMAGE_FILE="$1" 12 | TMP_SUBIMAGE="/tmp/fuzzy_subimage" 13 | shift 14 | 15 | function verify_subimage_and_push() { 16 | subimage_name="${1:?}" 17 | subimage="${2:?}" 18 | min_sectors="${3:?}" 19 | max_sectors="${4:?}" 20 | 21 | echo "Verifying ${subimage_name:?}: ${subimage:?} min_sectors: ${min_sectors:?} max_sectors: ${max_sectors:?}" 22 | fsize=$(stat -c "%s" ${subimage:?}) 23 | 24 | if [ "$(( $fsize % 512 ))" -ne 0 ]; then 25 | echo "The ${subimage} size isn't divisible by 512" >&2 26 | exit 2 27 | fi 28 | if [ "$(( $fsize / 512 ))" -gt ${max_sectors:?} ]; then 29 | echo "The ${subimage} uses more sectors than ${max_sectors:?}" >&2 30 | exit 2 31 | fi 32 | 33 | cp -f ${subimage:?} ${TMP_SUBIMAGE:?} 34 | truncate --size=%"$(( $max_sectors * 512 ))" ${TMP_SUBIMAGE:?} 35 | cat ${TMP_SUBIMAGE:?} >> ${IMAGE_FILE:?} 36 | } 37 | 38 | echo "Using $IMAGE_FILE as image file" >&2 39 | rm "${IMAGE_FILE:?}" || echo "Can't remove existing image file, continuing..." >&2 40 | touch "${IMAGE_FILE}" 41 | 42 | # sum of all should not exceed 256 sectors 43 | verify_subimage_and_push "bt_stage1" $1 $2 $2 44 | verify_subimage_and_push "realmode_lib" $3 $4 $4 45 | verify_subimage_and_push "bt_stage2" $5 $6 $6 46 | verify_subimage_and_push "kernel" $7 $8 $8 47 | -------------------------------------------------------------------------------- /scripts/burn.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | IMAGE=${1?"path to image is missing: \$1"} 5 | DISK=${2?"path for disk for burning is missing: \$2"} 6 | 7 | echo "[Burn] Starting..." 8 | dd bs=512 if=${IMAGE:?} of=${DISK:?} 9 | echo "[Burn] Done." 10 | -------------------------------------------------------------------------------- /src/bootloader/Makefile.mk: -------------------------------------------------------------------------------- 1 | MEMORY_STATIC_LIBRARY = 0x7E00 2 | 3 | debug_stage1: $(bt_stage1) 4 | objdump -b binary -mi386 -Maddr16,data16 -D $< 5 | xxd $< 6 | 7 | debug_stage2: $(bt_stage2) 8 | objdump -b binary -mi386 -Maddr16,data16 -D $< 9 | xxd $< 10 | 11 | $(bt_stage1): $(SRC_BOOTLOADER)/stage1.asm $(SRC_BOOTLOADER)/constants.asm $(SRC_BOOTLOADER)/io.asm $(SRC_BOOTLOADER)/disk.asm 12 | mkdir -p $$(dirname $(bt_stage1)) 13 | nasm -o $@ -f bin -i $(SRC_BOOTLOADER)/ \ 14 | -D SECTOR_START_BT_STAGE2=$(shell printf "%02x" `expr 1 + $(SECTOR_START_BT_STAGE2)` ) \ 15 | -D SECTOR_COUNT_BT_STAGE2=$(shell printf "%02x" $(SECTOR_COUNT_BT_STAGE2) ) \ 16 | $< 17 | truncate --size=%512 $@ 18 | @echo $(SECTOR_COUNT_BT_STAGE1) 19 | 20 | 21 | $(bt_stage2).elf: $(SRC_BOOTLOADER)/stage2.asm $(SRC_BOOTLOADER)/stage2.c $(INCLUDE_DIR)/memmgr/tables/gdt.h $(SRC_MEMMGR)/tables/gdt.c $(SRC_BOOTLOADER)/io.asm $(SRC_BOOTLOADER)/constants.asm $(BUILD_LIB_UTILS)/libutils_16 $(BUILD_DRIVERS)/display/libtm_bios $(BUILD_DRIVERS)/disk/libdisk_16 22 | mkdir -p $$(dirname $(bt_stage2)) 23 | $(NASM) -o $(BUILD_BOOTLOADER)/stage2_asm.o -i $(SRC_BOOTLOADER)/ $(SRC_BOOTLOADER)/stage2.asm 24 | $(CC) -m16 -fno-pie -c -Isrc \ 25 | -Iinclude \ 26 | -D SECTOR_START_SHARED_LIBRARY=$(SECTOR_START_SHARED_LIBRARY) \ 27 | -D SECTOR_COUNT_SHARED_LIBRARY=$(SECTOR_COUNT_SHARED_LIBRARY) \ 28 | -D SECTOR_START_KERNEL=$(SECTOR_START_KERNEL) \ 29 | -D SECTOR_COUNT_KERNEL=$(SECTOR_COUNT_KERNEL) \ 30 | -D MEMORY_STATIC_LIBRARY=$(MEMORY_STATIC_LIBRARY) \ 31 | -o $(BUILD_BOOTLOADER)/stage2_c.o $(SRC_BOOTLOADER)/stage2.c 32 | $(LD) -m elf_i386 -Ttext 0x8000 -T linker.ld -o $@ $(BUILD_BOOTLOADER)/stage2_asm.o $(BUILD_BOOTLOADER)/stage2_c.o $(BUILD_LIB_UTILS)/libutils_16 $(BUILD_DRIVERS)/display/libtm_bios $(BUILD_DRIVERS)/disk/libdisk_16 33 | truncate --size=%512 $@ 34 | 35 | $(bt_stage2): $(bt_stage2).elf 36 | $(FLAT_FROM_ELF) $< $@ 37 | truncate --size=%512 $@ 38 | -------------------------------------------------------------------------------- /src/bootloader/constants.asm: -------------------------------------------------------------------------------- 1 | %define C_BLACK 0 2 | %define C_BLUE 1 3 | %define C_GREEN 2 4 | %define C_CYAN 3 5 | %define C_RED 4 6 | %define C_MAGENTA 5 7 | %define C_BROWN 6 8 | %define C_LIGHT_GRAY 7 9 | %define C_DARK_GRAY 8 10 | %define C_LIGHT_BLUE 9 11 | %define C_LIGHT_GREEN A 12 | %define C_LIGHT_CYAN B 13 | %define C_LIGHT_RED C 14 | %define C_LIGHT_MAGENTA D 15 | %define C_YELLOW E 16 | %define C_WHITE F -------------------------------------------------------------------------------- /src/bootloader/disk.asm: -------------------------------------------------------------------------------- 1 | ; Not: sector is 1 based indexing here. 2 | %macro disk_read 6 3 | ; Args: (sector_count, drive 8bit, cylinder 10bit, head 8bit, sector 6bit, write_add) 4 | ; check es 5 | mov ax, 0x02%1 ; (read sectors, sector count) 6 | mov cx, 0x%3%5 ; (cylinder 10bit, sector 6bit) 7 | mov dx, 0x%4%2 ; (head, drive index) 8 | mov bx, 0x%6 ; (es:bx as write address) 9 | int 0x13 10 | %endmacro 11 | 12 | %macro disk_write 6 13 | ; Args: (sector_count, drive 8bit, cylinder 10bit, head 8bit, sector 6bit, read_add) 14 | ; check es 15 | mov ax, 0x03%1 ; (write sectors, sector count) 16 | mov cx, 0x%3%5 ; (cylinder 10bit, sector 6bit) 17 | mov dx, 0x%4%2 ; (head, drive index) 18 | mov bx, 0x%6 ; (es:bx as write address) 19 | int 0x13 20 | %endmacro 21 | 22 | %macro disk_success 1 23 | ; Args: (drive index 8 bit) 24 | ; Result: (AH: status code, set CF on error) 25 | mov ah, 0x01 ; (get status of last drive operation) 26 | mov dl, 0x%1 ; (drive index) 27 | int 0x13 28 | %endmacro -------------------------------------------------------------------------------- /src/bootloader/io.asm: -------------------------------------------------------------------------------- 1 | %macro print_string 4 2 | ; Args: (str, len, x, y) 3 | ; check es, bx 4 | mov ax, 0x1301 ; (print string, update cursor) 5 | mov bp, %1 ; (es:bp string pointer) 6 | mov cx, %2 ; (string length) 7 | mov dx, 0x%4%3 ; (pos_y, pos_x) 8 | int 0x10 9 | %endmacro 10 | 11 | %macro print_string_ext 7 12 | ; Args: (str, len, x, y, fg_color, bg_color, page) 13 | ; check es 14 | mov bx, 0x%7%6%5 ; (pagenumber, attribute) 15 | print_string %1, %2, %3, %4 16 | %endmacro 17 | 18 | %macro put_chars 2 19 | ; Args: (char, count) 20 | mov ah, 0x09 ; (write) 21 | mov al, %1 ; (char) 22 | mov cx, %2 ; (count) 23 | int 0x10 24 | %endmacro 25 | 26 | %macro put_chars_ext 5 27 | ; Args: (char, count, fg_color, bg_color, page) 28 | mov bx, 0x%5%4%3 ; (page, attribute) 29 | put_chars %1, %2 30 | %endmacro 31 | 32 | %macro read_char 0 33 | ; Output: (ah: scan code, al: ascii code) 34 | mov ah, 0x00 ; (read character) 35 | int 0x16 36 | %endmacro 37 | 38 | %macro move_xy 3 39 | ; Args: (x, y, page) 40 | mov ah, 0x02 ; (set custor position) 41 | mov bh, 0x%3 ; (page number) 42 | mov dx, 0x%2%1 ; (pos_y, pos_x) 43 | int 0x10 44 | %endmacro 45 | 46 | %macro print_hex_string_ext 4 47 | ; Args: (str, len, fg_color, page) 48 | mov si, %1 ; (str) 49 | mov dx, %2 ; (len) 50 | mov bx, 0x%4%3 ; (page, text color) 51 | mov ah, 0x0e ; (write tty) 52 | mov cx, 1 ; (count) 53 | label_marker: 54 | push bx 55 | mov bl, [si] 56 | and bx, 0x00F0 57 | shr bx, 4 58 | add bx, digit_to_hex 59 | mov al, [bx] 60 | pop bx 61 | int 0x10 62 | 63 | push bx 64 | mov bl, [si] 65 | and bx, 0x000F 66 | add bx, digit_to_hex 67 | mov al, [bx] 68 | pop bx 69 | int 0x10 70 | 71 | inc si 72 | sub dx, 1 73 | jnc label_marker 74 | %endmacro 75 | 76 | %macro set_blinking 1 77 | ; Args: (should_blink) 78 | ; check es 79 | mov ax, 0x1003 80 | mov bx, 0x000%1 81 | int 0x10 82 | %endmacro 83 | 84 | %macro clear_screen 2 85 | ; Output: (fg_color, bg_color) 86 | mov ax, 0x0600 ; (scroll 0 lines) 87 | mov bh, 0x%2%1 ; (attribute) 88 | mov cx, 0x0000 ; (window top-left RC) 89 | mov dx, 0x184F ; (window bottom-right RC) 90 | int 0x10 91 | %endmacro 92 | 93 | [SECTION .data] 94 | digit_to_hex db "0123456789ABCDEF" 95 | -------------------------------------------------------------------------------- /src/bootloader/stage1.asm: -------------------------------------------------------------------------------- 1 | ; Fuzzy Bootloader Stage 1 2 | 3 | ; Assumptions: 4 | ; - Stage 2 starts from Sector 1 to Sector 1 5 | ; - Stage 2 has ORG 0x8000 6 | 7 | %include "constants.asm" 8 | %include "io.asm" 9 | %include "disk.asm" 10 | 11 | [ORG 0x7C00] 12 | [BITS 16] 13 | 14 | 15 | [SECTION .text] 16 | 17 | CLI 18 | MOV ax, 0x0000 19 | MOV es, ax ; es := 0 20 | mov sp, 0x7FF0 21 | 22 | set_blinking 0 23 | clear_screen C_WHITE, C_BLACK 24 | print_string_ext bl_welcome, bl_welcome_len, 02, 02, C_MAGENTA, C_WHITE, 0 25 | print_string_ext bls1, bls1_len, 04, 04, C_WHITE, C_BLACK, 0 26 | 27 | ; Attempt to load Bootloader Stage 2 in Memory 28 | disk_read SECTOR_COUNT_BT_STAGE2, 80, 00, 00, SECTOR_START_BT_STAGE2, 8000 29 | disk_success 80 30 | JNC label_bts2_loaded 31 | print_string_ext bls2_load_fail, bls2_load_fail_len, 06, 06, C_RED, C_BLACK, 0 32 | JMP label_exit 33 | 34 | label_bts2_loaded: 35 | print_string_ext bls2_loaded, bls2_loaded_len, 06, 07, C_WHITE, C_BLACK, 0 36 | print_hex_string_ext 0x8000, 10, C_WHITE, 0 37 | ; __TEST_INJECT_BT1__: mov eax, 0x9A11C824 38 | ; __TEST_INJECT_BT1__: HLT 39 | JMP 0x8000 40 | JMP label_exit 41 | 42 | label_exit: 43 | HLT 44 | JMP label_exit 45 | 46 | [SECTION .data] 47 | bl_welcome db " Fuzzy OS... (^_^) " 48 | bl_welcome_len equ ($-bl_welcome) 49 | bls1 db "Bootloader: Stage 1" 50 | bls1_len equ ($-bls1) 51 | bls2_loaded db "Stage 2 Loaded: " 52 | bls2_loaded_len equ ($-bls2_loaded) 53 | bls2_load_fail db "Stage 2 Load Failed!" 54 | bls2_load_fail_len equ ($-bls2_load_fail) 55 | -------------------------------------------------------------------------------- /src/bootloader/stage2.asm: -------------------------------------------------------------------------------- 1 | ; Fuzzy Bootloader Stage 2 2 | %include "constants.asm" 3 | %include "io.asm" 4 | %include "fuzzy/memmgr/tables/gdt.asm" 5 | 6 | [BITS 16] 7 | 8 | extern entry_stage 9 | extern gdtr 10 | global enter_protected_mode 11 | global label_exit 12 | global call_int_0x15 13 | 14 | [SECTION .text] 15 | mov eax, 0x0 16 | mov es, eax 17 | mov ss, eax 18 | mov ds, eax 19 | mov fs, eax 20 | mov gs, eax 21 | mov eax, 0xBFFC ; create stack 22 | mov esp, eax 23 | 24 | set_blinking 0 25 | print_string_ext bl_stage_2, bl_stage_2_len, 04, 09, C_WHITE, C_BLACK, 0 26 | call entry_stage 27 | 28 | enter_protected_mode: 29 | ; Never returns. 30 | 31 | ; Load GDT Table 32 | mov eax, gdtr 33 | lgdt [eax] 34 | 35 | ; Enter Protected mode 36 | mov eax, cr0 37 | or eax, 0x00000001 38 | mov cr0, eax 39 | 40 | ; __TEST_INJECT_BT2__: mov eax, 0x198A65C3 41 | ; __TEST_INJECT_BT2__: HLT 42 | jmp GDT_KERNEL_CS:0x0000 ; address of smart kernel init 43 | 44 | label_exit: 45 | HLT 46 | JMP label_exit 47 | 48 | call_int_0x15: 49 | push ebp 50 | mov ebp, esp 51 | 52 | mov ax, [ebp+0x08] 53 | int 0x15 54 | 55 | pop ebp 56 | ret 57 | 58 | 59 | [SECTION .data] 60 | bl_stage_2 db "Bootloader: Stage 2" 61 | bl_stage_2_len equ ($-bl_stage_2) -------------------------------------------------------------------------------- /src/bootloader/stage2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "memmgr/tables/gdt.c" 11 | 12 | struct GDTEntry gdt_table[GDT_STD_SIZE]; 13 | struct GDTReference gdtr; 14 | 15 | char DIGIT_TO_HEX[] = "0123456789ABCDEF"; 16 | 17 | extern void enter_protected_mode(); 18 | extern void label_exit(); 19 | extern unsigned short call_int_0x15(unsigned short ax); 20 | 21 | void enable_a20() { 22 | int ax; 23 | ax = call_int_0x15(0x2403); 24 | if (!ax) { 25 | print_log("BIOS A20-gate not supported"); 26 | return; 27 | } 28 | 29 | ax = call_int_0x15(0x2402); 30 | if (ax == 1) { 31 | print_log("BIOS A20-gate already enabled"); 32 | return; 33 | } 34 | 35 | ax = call_int_0x15(0x2401); 36 | if (!ax) { 37 | print_log("BIOS A20-gate enabling attempt failed"); 38 | label_exit(); 39 | } 40 | 41 | print_log("BIOS A20-gate enabled"); 42 | } 43 | 44 | char *get_memdump_8byte(void *address) { 45 | static char shared_memdump[17]; 46 | for (int i = 0; i < 8; i++) { 47 | unsigned char byte = *(char *)address; 48 | address++; 49 | shared_memdump[i << 1] = DIGIT_TO_HEX[byte / 16]; 50 | shared_memdump[(i << 1) | 1] = DIGIT_TO_HEX[byte % 16]; 51 | } 52 | shared_memdump[16] = '\0'; 53 | return shared_memdump; 54 | } 55 | 56 | void load_kernel() { 57 | print_log("Loading Kernel"); 58 | int err = load_sectors(MEMORY_KERNEL_LOCATION, 0x80, SECTOR_START_KERNEL, 59 | SECTOR_COUNT_KERNEL); 60 | if (err) { 61 | print_log("Failed to load kernel in memory: %d", err); 62 | label_exit(); 63 | } else { 64 | print_log("Kernel loaded at 0x%x: %s...", MEMORY_KERNEL_LOCATION, 65 | get_memdump_8byte(MEMORY_KERNEL_LOCATION)); 66 | } 67 | } 68 | 69 | void load_static_library() { 70 | print_log("Loading Static Library"); 71 | int err = 72 | load_sectors(MEMORY_STATIC_LIBRARY, 0x80, SECTOR_START_SHARED_LIBRARY, 73 | SECTOR_COUNT_SHARED_LIBRARY); 74 | if (err) { 75 | print_log("Failed to load static library in memory: %d", err); 76 | label_exit(); 77 | } else { 78 | print_log("Static library loaded at 0x%x: %s...", MEMORY_STATIC_LIBRARY, 79 | get_memdump_8byte(MEMORY_STATIC_LIBRARY)); 80 | } 81 | } 82 | 83 | void entry_stage() { 84 | move_xy(6, 11); 85 | set_color_bg(C_BLACK); 86 | set_color_fg(C_GREEN); 87 | print_line("C says 'Hello World'"); 88 | set_color_fg(C_WHITE); 89 | 90 | print_log(""); 91 | enable_a20(); 92 | load_static_library(); 93 | load_kernel(); 94 | 95 | populate_gdt_table(&gdtr, gdt_table, GDT_STD_SIZE, 0); 96 | 97 | // Enter_protected_mode never returns. 98 | print_log("Loading GDT Table and entering protected mode"); 99 | 100 | // move cursor to end of screen to not bother protected mode. 101 | set_display_text_xy(TEXT_WINDOW_WIDTH - 1, TEXT_WINDOW_HEIGHT - 1); 102 | enter_protected_mode(); 103 | // And thus PC should never reach here :) 104 | } -------------------------------------------------------------------------------- /src/drivers/disk/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(BUILD_DRIVERS)/disk/libdisk_16: $(SRC_DRIVERS)/disk/disk_16.c $(SRC_DRIVERS)/disk/disk_16.asm $(SRC_DRIVERS)/disk/disk.h 2 | mkdir -p $(BUILD_DRIVERS)/disk/ 3 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_DRIVERS)/disk/disk_16_c.o $(SRC_DRIVERS)/disk/disk_16.c 4 | nasm -o $(BUILD_DRIVERS)/disk/disk_16_asm.o -f elf32 $(SRC_DRIVERS)/disk/disk_16.asm 5 | ar rc $@ $(BUILD_DRIVERS)/disk/disk_16_c.o $(BUILD_DRIVERS)/disk/disk_16_asm.o 6 | 7 | $(BUILD_DRIVERS)/disk/libdisk: $(SRC_DRIVERS)/disk/disk.c $(SRC_DRIVERS)/disk/disk.h 8 | mkdir -p $(BUILD_DRIVERS)/disk/ 9 | $(KERNEL_CC) -c -o $(BUILD_DRIVERS)/disk/disk_c.o $(SRC_DRIVERS)/disk/disk.c 10 | ar rc $@ $(BUILD_DRIVERS)/disk/disk_c.o 11 | 12 | -------------------------------------------------------------------------------- /src/drivers/disk/disk.c: -------------------------------------------------------------------------------- 1 | // written for protected mode 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #define SECTOR_SIZE 512 10 | 11 | static int load_sector_via_reallibrary_data(unsigned int dst_address, 12 | unsigned char drive, 13 | unsigned int lba, 14 | unsigned char count) { 15 | // assumes count <= 128 16 | int es = (MEMORY_REALLIBRARY_DATA_ADDRESS & 0xFFFF0) >> 4; 17 | int es_address = MEMORY_REALLIBRARY_DATA_ADDRESS & 0x000F; 18 | int cylinder_head = (lba / 63); 19 | int sector_index = lba % 63 + 1; 20 | // https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=02h:_Read_Sectors_From_Drive 21 | real_mode_client(0x13, // interrupt number 22 | (0x02 << 8) | count, es_address, 23 | ((cylinder_head >> 2) & 0xFFC0) | (sector_index), 24 | ((cylinder_head << 8) & 0xFF00) | (drive), es); 25 | // https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=01h:_Get_Status_of_Last_Drive_Operation 26 | int eax = real_mode_client(0x13, // interrupt number 27 | (0x01 << 8), 0, 0, drive, 0); 28 | unsigned int err = eax >> 8; 29 | if (err) 30 | return err; 31 | 32 | // copy from temporary memory to real dst address. 33 | int dst_ds = GDT_ABS32_DS; 34 | int src_ds = GDT_ABS32_DS; 35 | 36 | kernel_memncpy_absolute(dst_ds, dst_address, src_ds, 37 | MEMORY_REALLIBRARY_DATA_ADDRESS, 38 | SECTOR_SIZE * (int)count); 39 | print_info("[load_sectors] mem %x:%x -> mem %x:%x, cnt: %d; err: %d", 40 | src_ds, MEMORY_REALLIBRARY_DATA_ADDRESS, dst_ds, dst_address, 41 | SECTOR_SIZE * (int)count, 0); 42 | 43 | return 0; 44 | }; 45 | 46 | int load_sectors(unsigned int dst_address, unsigned char drive, 47 | unsigned int lba, // 0-based 48 | unsigned int sector_count) { 49 | // load_sector_via_reallibrary_data supports upto 255 sector count 50 | // but the memory allocation for reallibrary data is 0x10000 which 51 | // amounts to 128 sectors. Please have a look at memory_layout.md 52 | const int OCC_MAX_SECTOR_COUNT = 128; 53 | 54 | int err = 0; 55 | while (!err && sector_count > 0) { 56 | int occ_read_sectors = min(sector_count, OCC_MAX_SECTOR_COUNT); 57 | err = load_sector_via_reallibrary_data(dst_address, drive, lba, 58 | occ_read_sectors); 59 | dst_address += SECTOR_SIZE * occ_read_sectors; 60 | lba += occ_read_sectors; 61 | sector_count -= occ_read_sectors; 62 | } 63 | return err; 64 | } 65 | -------------------------------------------------------------------------------- /src/drivers/disk/disk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int load_sectors(unsigned int full_address, unsigned char drive, 4 | unsigned int lba, // 0-based 5 | unsigned int count); -------------------------------------------------------------------------------- /src/drivers/disk/disk_16.asm: -------------------------------------------------------------------------------- 1 | [BITS 16] 2 | 3 | global _low_int_0x13 4 | 5 | [SECTION .text] 6 | _low_int_0x13: 7 | push ebp 8 | mov ebp, esp 9 | ; callee save register 10 | push ebx 11 | push esi 12 | push edi 13 | push es 14 | 15 | ; check es 16 | mov ax, [ebp + 0x18] ; (es) 17 | mov es, ax 18 | 19 | mov ax, [ebp + 0x08] 20 | mov bx, [ebp + 0x0C] 21 | mov cx, [ebp + 0x10] 22 | mov dx, [ebp + 0x14] 23 | int 0x13 24 | 25 | pop es 26 | pop edi 27 | pop esi 28 | pop ebx 29 | pop ebp 30 | ret 31 | -------------------------------------------------------------------------------- /src/drivers/disk/disk_16.c: -------------------------------------------------------------------------------- 1 | // written for real mode 2 | 3 | extern unsigned short _low_int_0x13(unsigned short ax, unsigned short bx, 4 | unsigned short cx, unsigned short dx, 5 | unsigned short es); 6 | 7 | int load_sectors(unsigned int full_address, unsigned char drive, 8 | unsigned int lba, // 0-based 9 | unsigned int count) { 10 | // we don't expect disk_16 (used by bootloader) to load more than 255 11 | // sectors. 12 | 13 | int es = (full_address & 0xFFFF0) >> 4; 14 | int es_address = full_address & 0x000F; 15 | int cylinder_head = (lba / 63); 16 | int sector_index = lba % 63 + 1; 17 | // https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=02h:_Read_Sectors_From_Drive 18 | _low_int_0x13((0x02 << 8) | count, es_address, 19 | ((cylinder_head >> 2) & 0xFFC0) | (sector_index), 20 | ((cylinder_head << 8) & 0xFF00) | (drive), es); 21 | // https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=01h:_Get_Status_of_Last_Drive_Operation 22 | unsigned short ax = _low_int_0x13((0x01 << 8), 0, 0, drive, 0); 23 | int status = ax >> 8; 24 | return status; 25 | } -------------------------------------------------------------------------------- /src/drivers/display/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(BUILD_DRIVERS)/display/libtm_bios: $(SRC_DRIVERS)/display/text_mode_bios.c $(SRC_DRIVERS)/display/text_mode_bios.asm $(SRC_DRIVERS)/display/text_mode.h 2 | # 16 bit mode 3 | mkdir -p $(BUILD_DRIVERS)/display/ 4 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_DRIVERS)/display/text_mode_bios_c.o $(SRC_DRIVERS)/display/text_mode_bios.c 5 | $(NASM) -o $(BUILD_DRIVERS)/display/text_mode_bios_asm.o $(SRC_DRIVERS)/display/text_mode_bios.asm 6 | ar rc $@ $(BUILD_DRIVERS)/display/text_mode_bios_c.o $(BUILD_DRIVERS)/display/text_mode_bios_asm.o 7 | 8 | $(BUILD_DRIVERS)/display/libtm_vga: $(SRC_DRIVERS)/display/text_mode_vga.c $(SRC_DRIVERS)/display/text_mode_vga.asm $(SRC_DRIVERS)/display/text_mode.h 9 | mkdir -p $(BUILD_DRIVERS)/display/ 10 | $(CC) -m32 -fno-pie -c -Isrc -o $(BUILD_DRIVERS)/display/text_mode_vga_c.o $(SRC_DRIVERS)/display/text_mode_vga.c 11 | $(NASM) -o $(BUILD_DRIVERS)/display/text_mode_vga_asm.o $(SRC_DRIVERS)/display/text_mode_vga.asm 12 | ar rc $@ $(BUILD_DRIVERS)/display/text_mode_vga_c.o $(BUILD_DRIVERS)/display/text_mode_vga_asm.o 13 | -------------------------------------------------------------------------------- /src/drivers/display/text_mode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TEXT_WINDOW_HEIGHT 0x19 4 | #define TEXT_WINDOW_WIDTH 0x50 5 | 6 | int get_display_text_x(); 7 | int get_display_text_y(); 8 | void set_display_text_x(int x); 9 | void set_display_text_y(int y); 10 | void set_display_text_xy(int x, int y); 11 | 12 | void io_low_scroll_screen(char count, unsigned char color, int x1, int y1, 13 | int x2, int y2); 14 | 15 | void io_low_put_char(char c, unsigned char color); 16 | 17 | void io_low_flush(); -------------------------------------------------------------------------------- /src/drivers/display/text_mode_bios.asm: -------------------------------------------------------------------------------- 1 | [BITS 16] 2 | 3 | global _low_put_chars 4 | global _low_move_xy 5 | global _low_scroll_screen 6 | global _low_read_char 7 | 8 | [SECTION .text] 9 | _low_put_chars: 10 | push ebp 11 | mov ebp, esp 12 | 13 | mov ah, 0x09 ; (write) 14 | mov al, [ebp + 0x8] ; (char) 15 | mov cx, [ebp + 0xc] ; (count) 16 | mov bh, 0x00 ; (pagenumber) 17 | mov bl, [ebp + 0x10] ; (attribute) 18 | int 0x10 19 | 20 | pop ebp 21 | ret 22 | 23 | _low_move_xy: 24 | push ebp 25 | mov ebp, esp 26 | 27 | mov ah, 0x02 ; (set custor position) 28 | mov dl, [ebp + 0x8] ; (pos_x) 29 | mov dh, [ebp + 0xc] ; (pos_y) 30 | mov bh, [ebp + 0x10] ; (page number) 31 | int 0x10 32 | 33 | pop ebp 34 | ret 35 | 36 | _low_scroll_screen: 37 | push ebp 38 | mov ebp, esp 39 | 40 | mov al, [ebp + 0x08] ; (line count) 41 | mov bh, [ebp + 0x0c] ; (attribute) 42 | mov cl, [ebp + 0x10] ; (window top-left x) 43 | mov ch, [ebp + 0x14] ; (window top-left y) 44 | mov dl, [ebp + 0x18] ; (window bottom-right x) 45 | mov dh, [ebp + 0x1c] ; (window bottom-right y) 46 | mov ah, [ebp + 0x20] ; (scroll up:0x06, down:0x07) 47 | int 0x10 48 | 49 | pop ebp 50 | ret -------------------------------------------------------------------------------- /src/drivers/display/text_mode_bios.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void _low_print(char str[], unsigned short n, unsigned char x, 4 | unsigned char y, unsigned char color); 5 | 6 | extern void _low_put_chars(char c, unsigned short count, unsigned char color); 7 | extern void _low_move_xy(unsigned char x, unsigned char y, unsigned char page); 8 | 9 | // _low_scroll_screen with count 0 implies fill. 10 | extern void _low_scroll_screen(unsigned char count, unsigned char color, 11 | unsigned char x1, unsigned char y1, 12 | unsigned char x2, unsigned char y2, 13 | unsigned char scroll_ah_value); 14 | extern char _low_read_char(); 15 | 16 | int IO_CURRENT_X = 0; 17 | int IO_CURRENT_Y = 0; 18 | 19 | int get_display_text_x() { return IO_CURRENT_X; } 20 | 21 | int get_display_text_y() { return IO_CURRENT_Y; } 22 | 23 | void set_display_text_x(int x) { 24 | IO_CURRENT_X = x; 25 | _low_move_xy(IO_CURRENT_X, IO_CURRENT_Y, 0); 26 | } 27 | 28 | void set_display_text_y(int y) { 29 | IO_CURRENT_Y = y; 30 | _low_move_xy(IO_CURRENT_X, IO_CURRENT_Y, 0); 31 | } 32 | 33 | void set_display_text_xy(int x, int y) { 34 | IO_CURRENT_X = x; 35 | IO_CURRENT_Y = y; 36 | _low_move_xy(IO_CURRENT_X, IO_CURRENT_Y, 0); 37 | } 38 | 39 | void io_low_scroll_screen(char count, unsigned char color, int x1, int y1, 40 | int x2, int y2) { 41 | if (count >= 0) { 42 | _low_scroll_screen(count, color, x1, y1, x2, y2, 0x06); 43 | } else { 44 | _low_scroll_screen(-count, color, x1, y1, x2, y2, 0x07); 45 | } 46 | } 47 | 48 | void io_low_put_char(char c, unsigned char color) { 49 | _low_put_chars(c, 1, color); 50 | } 51 | 52 | void io_low_flush() { 53 | // Not implemented, as it's not required. 54 | } -------------------------------------------------------------------------------- /src/drivers/display/text_mode_vga.asm: -------------------------------------------------------------------------------- 1 | %include "fuzzy/memmgr/tables/gdt.asm" 2 | 3 | [BITS 32] 4 | 5 | global _low_put_char 6 | global _low_vga_copy_step 7 | global _low_flush 8 | 9 | [SECTION .text] 10 | _low_put_char: 11 | push ebp 12 | mov ebp, esp 13 | ; callee save register 14 | push ebx 15 | ; push esi 16 | ; push edi 17 | 18 | push ds 19 | mov eax, GDT_ABS32_DS 20 | mov ds, eax ; Absolute memory address 21 | 22 | mov ebx,[ebp + 0x10] ; (ROW_WIDTH*y+x) 23 | shl ebx, 1 24 | add ebx, 0xb8000 25 | mov al, [ebp + 0x8] ; (char) 26 | mov ah, [ebp + 0xc] ; (color) 27 | mov [ebx], ax 28 | 29 | pop ds 30 | ; pop edi 31 | ; pop esi 32 | pop ebx 33 | pop ebp 34 | ret 35 | 36 | _low_vga_copy_step: 37 | push ebp 38 | mov ebp, esp 39 | ; callee save register 40 | push ebx 41 | ; push esi 42 | ; push edi 43 | push ds 44 | mov eax, GDT_ABS32_DS 45 | mov ds, eax ; Absolute memory address 46 | 47 | ; Copy char+colors in Row Order Format 48 | mov eax,[ebp + 0x8] ; (ROW_WIDTH*y1+x1) 49 | mov ebx,[ebp + 0xc] ; (ROW_WIDTH*y2+x2) 50 | mov ecx,[ebp + 0x10] ; (bytes_count/2) 51 | add eax,0xb8000 52 | add ebx,0xb8000 53 | 54 | _low_vga_copy_step_internal: 55 | mov dx, [eax] 56 | mov [ebx], dx 57 | add eax, 2 58 | add ebx, 2 59 | loop _low_vga_copy_step_internal 60 | 61 | pop ds 62 | ; pop edi 63 | ; pop esi 64 | pop ebx 65 | pop ebp 66 | ret 67 | 68 | _low_flush: 69 | push ebp 70 | mov ebp, esp 71 | ; callee save register 72 | ; push ebx 73 | push esi 74 | push edi 75 | push es 76 | mov eax, GDT_ABS32_DS 77 | mov es, eax ; Absolute memory address 78 | 79 | mov esi, [ebp + 0x8] ; (buffer) 80 | mov ecx, [ebp + 0xc] ; (count) 81 | mov edi, 0xb8000 82 | 83 | cld 84 | rep movsw 85 | 86 | pop es 87 | pop edi 88 | pop esi 89 | ; pop ebx 90 | pop ebp 91 | ret -------------------------------------------------------------------------------- /src/drivers/display/text_mode_vga.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void _low_put_char(char c, unsigned char color, unsigned int xy); 4 | 5 | extern void _low_vga_copy_step(unsigned int xy1, unsigned int xy2, 6 | unsigned int count); 7 | 8 | extern _low_flush(unsigned short *buffer, int count); 9 | 10 | static int location_xy = 0; 11 | 12 | int IO_CURRENT_X = 0; 13 | int IO_CURRENT_Y = 0; 14 | unsigned short buffer[TEXT_WINDOW_HEIGHT * TEXT_WINDOW_WIDTH] = {0}; 15 | 16 | int get_display_text_x() { return IO_CURRENT_X; } 17 | 18 | int get_display_text_y() { return IO_CURRENT_Y; } 19 | 20 | void set_display_text_x(int x) { 21 | IO_CURRENT_X = x; 22 | location_xy = IO_CURRENT_X + IO_CURRENT_Y * TEXT_WINDOW_WIDTH; 23 | } 24 | 25 | void set_display_text_y(int y) { 26 | IO_CURRENT_Y = y; 27 | location_xy = IO_CURRENT_X + IO_CURRENT_Y * TEXT_WINDOW_WIDTH; 28 | } 29 | 30 | void set_display_text_xy(int x, int y) { 31 | IO_CURRENT_X = x; 32 | IO_CURRENT_Y = y; 33 | location_xy = IO_CURRENT_X + IO_CURRENT_Y * TEXT_WINDOW_WIDTH; 34 | } 35 | 36 | void io_low_scroll_screen(char count, unsigned char color, int x1, int y1, 37 | int x2, int y2) { 38 | if (count == 0) { 39 | for (int r = y1; r <= y2; ++r) { 40 | int d_index = r * TEXT_WINDOW_WIDTH + x1; 41 | for (int c = x1; c <= x2; ++c) { 42 | buffer[d_index++] = (((unsigned short)color) << 8) | ' '; 43 | } 44 | } 45 | } else if (count > 0) { 46 | // scroll up 47 | int width = x2 - x1 + 1; 48 | for (int r_dst = y1, r_src = y1 + count; r_src <= y2; 49 | r_src++, r_dst++) { 50 | int s_index = r_src * TEXT_WINDOW_WIDTH + x1; 51 | int d_index = r_dst * TEXT_WINDOW_WIDTH + x1; 52 | for (int j = x1; j <= x2; ++j) { 53 | buffer[d_index++] = buffer[s_index++]; 54 | } 55 | } 56 | // clear bottom rows 57 | for (int r = y2 - count + 1; r <= y2; r++) { 58 | int x = r * TEXT_WINDOW_WIDTH + x1; 59 | for (int j = x1; j <= x2; ++j) { 60 | buffer[x++] = (((unsigned short)color) << 8) | ' '; 61 | } 62 | } 63 | } else { 64 | // scroll down 65 | int width = x2 - x1 + 1; 66 | for (int r_dst = y2, r_src = y2 + count; r_src >= y1; 67 | r_src--, r_dst--) { 68 | int s_index = r_src * TEXT_WINDOW_WIDTH + x1; 69 | int d_index = r_dst * TEXT_WINDOW_WIDTH + x1; 70 | for (int j = x1; j <= x2; ++j) { 71 | buffer[d_index++] = buffer[s_index++]; 72 | } 73 | } 74 | 75 | // clear top rows 76 | for (int r = y1; r <= y1 + count - 1; r++) { 77 | int x = r * TEXT_WINDOW_WIDTH + x1; 78 | for (int j = x1; j <= x2; ++j) { 79 | buffer[x++] = (((unsigned short)color) << 8) | ' '; 80 | } 81 | } 82 | } 83 | io_low_flush(); 84 | } 85 | 86 | void io_low_put_char(char c, unsigned char color) { 87 | _low_put_char(c, color, location_xy); 88 | buffer[location_xy] = (((unsigned short)color) << 8) | c; 89 | } 90 | 91 | void io_low_flush() { _low_flush(buffer, sizeof(buffer) / 2); } -------------------------------------------------------------------------------- /src/drivers/display/vga/Makefile.mk: -------------------------------------------------------------------------------- 1 | 2 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 3 | mkdir -p $(dir $@) 4 | $(KERNEL_CC) -c -o $@ \ 5 | $< 6 | 7 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 8 | mkdir -p $(dir $@) 9 | nasm -o $@ -f elf32 $< 10 | 11 | $(SELF_BUILD_DIR)/libvga_graphics: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 12 | ar rc $@ $^ 13 | 14 | -------------------------------------------------------------------------------- /src/drivers/display/vga/graphics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | int graphics_set_mode(uint8_t video_mode) { 10 | // https://en.wikipedia.org/wiki/INT_10H 11 | int eax = real_mode_client(0x10, // interrupt number 12 | (0x00 << 8) | video_mode, // set video mode 13 | 0, // do not care 14 | 0, // do not care 15 | 0, // do not care 16 | 0 // do not care 17 | ); 18 | return 0; 19 | } 20 | 21 | uint8_t graphics_get_mode() { 22 | // https://en.wikipedia.org/wiki/INT_10H 23 | int eax = real_mode_client(0x10, // interrupt number 24 | (0x0F << 8), // get video mode 25 | 0, // do not care 26 | 0, // do not care 27 | 0, // do not care 28 | 0 // do not care 29 | ); 30 | // active page ignored 31 | return eax & 0xFF; 32 | } 33 | 34 | int graphics_write_320x200x256(int user_ds, uint8_t *__us_buffer) { 35 | const int size = 320 * 200 * 1; 36 | kernel_memncpy_absolute(GDT_ABS32_DS, // dst ds 37 | MEMORY_VGA_GRAPHICS_ADDRESS, // dst address 38 | user_ds, // src ds 39 | __us_buffer, // src address 40 | size); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/drivers/keyboard/Makefile.mk: -------------------------------------------------------------------------------- 1 | 2 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 3 | mkdir -p $(dir $@) 4 | $(KERNEL_CC) -c -o $@ \ 5 | $< 6 | 7 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 8 | mkdir -p $(dir $@) 9 | nasm -o $@ -f elf32 $< 10 | 11 | $(SELF_BUILD_DIR)/libkeyboard: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 12 | ar rc $@ $^ 13 | 14 | -------------------------------------------------------------------------------- /src/drivers/keyboard/keyboard.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global port_write 4 | global port_read 5 | 6 | [SECTION .text] 7 | port_write: 8 | push ebp 9 | mov ebp, esp 10 | 11 | mov al, [ebp + 0xc] ; value 12 | mov dx, [ebp + 0x8] ; port 13 | out dx, al 14 | 15 | pop ebp 16 | ret 17 | 18 | port_read: 19 | push ebp 20 | mov ebp, esp 21 | 22 | mov dx, [ebp + 0x8] ; port 23 | in al, dx 24 | and eax, 0xFF 25 | 26 | pop ebp 27 | ret 28 | -------------------------------------------------------------------------------- /src/drivers/keyboard/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void keyboard_init(); 4 | 5 | char keyboard_get_key_pressed_blocking(); 6 | char keyboard_get_key_pressed_poll(); 7 | int keyboard_get_kbhit(); 8 | -------------------------------------------------------------------------------- /src/drivers/pic/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c -o $@ $< 4 | 5 | $(SELF_BUILD_DIR)/libpic: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 6 | ar rc $@ $^ 7 | -------------------------------------------------------------------------------- /src/drivers/pic/pic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void pic_init() { 7 | print_log("[driver][pic] init"); 8 | int idt_irq0_pic1 = IDT_IRQ_OFFSET; 9 | int idt_irq0_pic2 = IDT_IRQ_OFFSET + 8; 10 | 11 | outb(PORT_PIC1_CMD, 0x11); 12 | outb(PORT_PIC2_CMD, 0x11); 13 | 14 | // Remap IRQ 15 | // FuzzyOS won't be using IRQ0 in real mode. 16 | outb(PORT_PIC1_DATA, idt_irq0_pic1); 17 | outb(PORT_PIC2_DATA, idt_irq0_pic2); 18 | 19 | outb(PORT_PIC1_DATA, 4); 20 | outb(PORT_PIC2_DATA, 2); 21 | 22 | outb(PORT_PIC1_DATA, 1); 23 | outb(PORT_PIC2_DATA, 1); 24 | 25 | // disable all IRQs 26 | pic_writemask_new(0xFFFF); 27 | 28 | pit_init(); 29 | } 30 | 31 | void pic_writemask_new(uint16_t mask) { 32 | outb(PORT_PIC1_DATA, mask & 0xFF); 33 | outb(PORT_PIC2_DATA, mask >> 8); 34 | } 35 | 36 | uint16_t pic_readmask_new() { 37 | uint16_t mask = inputb(PORT_PIC1_DATA); 38 | mask |= inputb(PORT_PIC2_DATA) << 8; 39 | return mask; 40 | } 41 | 42 | void pic_irq_enable(int irq) { 43 | int mask = pic_readmask_new(); 44 | mask = mask & (~(1 << irq)); 45 | pic_writemask_new(mask); 46 | } 47 | 48 | void pic_irq_disable(int irq) { 49 | int mask = pic_readmask_new(); 50 | mask = mask | ((1 << irq)); 51 | pic_writemask_new(mask); 52 | } 53 | -------------------------------------------------------------------------------- /src/drivers/pic/pit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static uint16_t frequency_dividor; 5 | 6 | void pit_init() { 7 | // Channel 0 8 | // Low/high byte 9 | // Interrupt on terminal count 10 | outb(PORT_PIT_CMD, 0b00110000); 11 | } 12 | 13 | uint16_t pit_get_counter() { return frequency_dividor; } 14 | 15 | void pit_reload_counter() { 16 | uint16_t counter = pit_get_counter(); 17 | outb(PORT_PIT_DATA0, counter & 0xFF); 18 | outb(PORT_PIT_DATA0, counter >> 8); 19 | } 20 | 21 | void pit_reset() { 22 | outb(PORT_PIC1_CMD, 0x20); 23 | outb(PORT_PIC2_CMD, 0x20); 24 | pit_reload_counter(); 25 | } 26 | 27 | void pit_set_counter(uint16_t counter) { 28 | frequency_dividor = counter; 29 | pit_reload_counter(); 30 | } 31 | -------------------------------------------------------------------------------- /src/drivers/ps2/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c -o $@ \ 4 | $< 5 | 6 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 7 | mkdir -p $(dir $@) 8 | nasm -o $@ -f elf32 $< 9 | 10 | $(SELF_BUILD_DIR)/libps2: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 11 | ar rc $@ $^ 12 | 13 | -------------------------------------------------------------------------------- /src/drivers/ps2/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | void ps2_keyboard_init() { keyboard_init(); } 7 | 8 | char ps2_keyboard_get_key_pressed_blocking() { 9 | return keyboard_get_key_pressed_blocking(); 10 | } 11 | 12 | char ps2_keyboard_get_key_pressed_poll() { 13 | return keyboard_get_key_pressed_poll(); 14 | } 15 | 16 | int ps2_keyboard_get_kbhit() { return keyboard_get_kbhit(); } 17 | -------------------------------------------------------------------------------- /src/drivers/ps2/ps2.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global irq1_handler_low 4 | global irq12_handler_low 5 | 6 | [SECTION .text] 7 | 8 | irq1_handler_low: 9 | ; TODO: send EOI 10 | iret 11 | 12 | irq12_handler_low: 13 | ; TODO: send EOI 14 | iret 15 | -------------------------------------------------------------------------------- /src/fs/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(SELF_INCLUDE_DIR)/%.h 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c \ 4 | -o $@ $< 5 | 6 | $(SELF_BUILD_DIR)/libffs: $(patsubst $(SELF_SRC_DIR)/%.c,$(SELF_BUILD_DIR)/%.o,$(wildcard $(SELF_SRC_DIR)/*.c)) 7 | ar rc $@ $^ 8 | -------------------------------------------------------------------------------- /src/fs/ffs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // TODO: cache partition entry 9 | // TODO: verify partition metadata 10 | 11 | int partition_read_block(int lba, void *wr_buffer) { 12 | int memory_location = ((int)wr_buffer) + MEMORY_KERNEL_LOCATION; 13 | int err = load_sectors(memory_location, 0x80, lba, 1); 14 | return err; 15 | } 16 | 17 | int verify_partition(int partition_id) { 18 | // cache 19 | // -1: not yet verified 20 | // 0: verified with no error 21 | // 1: verified but failed 22 | static char verified_cache[4] = {-1, -1, -1, -1}; 23 | if (partition_id < 0 || partition_id >= 4) 24 | return 1; 25 | if (verified_cache[partition_id] != -1) 26 | return verified_cache[partition_id]; 27 | 28 | // fetch parition info 29 | struct PartitionEntry partition; 30 | read_partition_entry(partition_id, &partition); 31 | 32 | union FFSMetaData block; 33 | int err = partition_read_block(partition.lba, // FS metadata 34 | block.bytes); 35 | if (!err) { 36 | // verify signature 37 | err = strcmp(block.content.signature, FS_FFS_SIGNATURE); 38 | } 39 | if (err) { 40 | verified_cache[partition_id] = 1; // err 41 | print_log("[ffs] partition %d verification failed, err: %d", 42 | partition_id, err); 43 | return err; 44 | } 45 | verified_cache[partition_id] = 0; // no error 46 | return 0; 47 | } 48 | 49 | int resolve_abs_lba(int partition_id, int partition_relative_lba) { 50 | // fetch parition info 51 | struct PartitionEntry partition; 52 | read_partition_entry(partition_id, &partition); 53 | return partition.lba + partition_relative_lba; 54 | } 55 | 56 | int fetch_file_entry(int partition_id, int entry_id, 57 | union FFSFileEntry *entry) { 58 | int err = verify_partition(partition_id); 59 | if (err) { 60 | return err; 61 | } 62 | 63 | char buffer[FS_BLOCK_SIZE]; 64 | // fetch parition info 65 | struct PartitionEntry partition; 66 | read_partition_entry(partition_id, &partition); 67 | 68 | // fetch file entry 69 | const int FILE_ENTRY_PER_BLOCK = (FS_BLOCK_SIZE / FS_FFS_FILEENTRY_SIZE); 70 | err = partition_read_block(partition.lba + 1 + // FS metadata 71 | entry_id / FILE_ENTRY_PER_BLOCK, 72 | buffer); 73 | if (err) 74 | return err; 75 | 76 | memcpy(entry->bytes, 77 | buffer + (entry_id % FILE_ENTRY_PER_BLOCK) * FS_FFS_FILEENTRY_SIZE, 78 | sizeof(entry->bytes)); 79 | int file_not_exists = (entry->content.start_block_id == 0); 80 | err = file_not_exists; 81 | return err; 82 | } 83 | 84 | int fetch_file_content(const int partition_id, const union FFSFileEntry *entry, 85 | char buffer[FS_BLOCK_SIZE], const int file_block_id) { 86 | 87 | // fetch parition info 88 | struct PartitionEntry partition; 89 | read_partition_entry(partition_id, &partition); 90 | 91 | // fetch file entry 92 | int file_size = entry->content.filesize; 93 | int block_count = (file_size + FS_BLOCK_SIZE - 1) / FS_BLOCK_SIZE; 94 | if (file_block_id >= block_count) { 95 | // zero block read 96 | return 0; 97 | } 98 | 99 | int err = partition_read_block( 100 | partition.lba + entry->content.start_block_id + file_block_id, buffer); 101 | if (err) 102 | return -2; 103 | // returns content length in buffer. 104 | if (file_block_id + 1 == block_count) 105 | return file_size % FS_BLOCK_SIZE; 106 | return FS_BLOCK_SIZE; 107 | } 108 | -------------------------------------------------------------------------------- /src/fs/mbr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | char _cache_mbrblock[FS_BLOCK_SIZE]; 10 | int _cache_mbrblock_ready = 0; 11 | 12 | char *get_mbrblock() { 13 | if (!_cache_mbrblock_ready) { 14 | int memory_location = ((int)_cache_mbrblock) + MEMORY_KERNEL_LOCATION; 15 | int lba = 0; 16 | int err = load_sectors(memory_location, 0x80, lba, 1); 17 | if (err) { 18 | PANIC(err, "[mbr] Failed to read MBR block."); 19 | } 20 | _cache_mbrblock_ready = 1; 21 | } 22 | return _cache_mbrblock; 23 | } 24 | 25 | void read_partition_entry(int id, struct PartitionEntry *entry) { 26 | // TODO: Handle all partition id. 27 | char *mbr_block = get_mbrblock(); 28 | char *entry_str = (char *)entry; 29 | 30 | memcpy(entry_str, mbr_block + MBR_PARTITION_BEGIN, 31 | sizeof(struct PartitionEntry)); 32 | print_info("[mbr][p%d] lba: %d, sector_count: %d", id, entry->lba, 33 | entry->sector_count); 34 | } 35 | -------------------------------------------------------------------------------- /src/kernel/Makefile.mk: -------------------------------------------------------------------------------- 1 | debug_kernel: $(kernel_core) 2 | objdump -b binary -mi386 -Maddr32,data32 -D $< 3 | xxd $< 4 | 5 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 6 | mkdir -p $(dir $@) 7 | $(KERNEL_CC) -c -o $@ \ 8 | -D INIT_APPNAME=\"$(INIT_APPNAME)\" \ 9 | -D __SOURCE_SNAPSHOT__=$(SOURCE_SNAPSHOT) \ 10 | $< 11 | 12 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 13 | mkdir -p $(dir $@) 14 | $(NASM) -o $@ -i $(SRC_REALMODE)/ $< 15 | 16 | $(kernel_core).elf: $(SELF_BUILD_DIR)/core_asm.o \ 17 | $(SELF_BUILD_DIR)/panic_asm.o \ 18 | $(SELF_BUILD_ALL_C) \ 19 | $(BUILD_KERNEL)/interrupts/libinterrupts \ 20 | $(BUILD_KERNEL)/syscall/libsyscall \ 21 | $(BUILD_KERNEL)/process/libprocess \ 22 | $(BUILD_DIR)/memmgr/tables/libgdt \ 23 | $(BUILD_DIR)/fs/libffs \ 24 | $(BUILD_DRIVERS)/ps2/libps2 \ 25 | $(BUILD_DRIVERS)/keyboard/libkeyboard \ 26 | $(BUILD_DRIVERS)/pic/libpic \ 27 | $(BUILD_LIB_UTILS)/libutils \ 28 | $(BUILD_DRIVERS)/display/libtm_vga \ 29 | $(BUILD_DRIVERS)/display/vga/libvga_graphics \ 30 | $(BUILD_LIB_DS)/libds \ 31 | $(BUILD_DRIVERS)/disk/libdisk \ 32 | $(BUILD_DIR)/real_mode/librealmodeclient \ 33 | $(BUILD_USR_LIB)/libfuzzyc \ 34 | $(BUILD_DIR)/memmgr/stackguard/libstackguard # stackguard must be the last one 35 | 36 | mkdir -p $(dir $@) 37 | $(KERNEL_LD) -o $@ $^ 38 | 39 | $(kernel_core): $(kernel_core).elf 40 | $(FLAT_FROM_ELF) $< $@ 41 | truncate --size=%512 $@ 42 | -------------------------------------------------------------------------------- /src/kernel/core.asm: -------------------------------------------------------------------------------- 1 | %include "fuzzy/memmgr/layout.asm" 2 | %include "fuzzy/memmgr/tables/gdt.asm" 3 | 4 | [BITS 32] 5 | 6 | extern kernel_core_entry 7 | extern reload_idt_table 8 | global kernel_core_entry_asm 9 | 10 | [SECTION .text] 11 | ; protected mode real entry point. 12 | CLI 13 | mov ax, GDT_KERNEL_DS 14 | mov es, ax 15 | mov ss, ax 16 | mov ds, ax 17 | mov fs, ax 18 | mov gs, ax 19 | 20 | xor ebp, ebp ; origin of stack trace 21 | mov esp, STACKINIT_KERNEL_CORE ; init stack pointer 22 | jmp kernel_core_entry 23 | 24 | ; kernel_core_entry_asm currently exists only for tests. 25 | kernel_core_entry_asm: 26 | ; __TEST_INJECT_KERNEL_CORE_ENTRY__: mov eax, 0x922E52FF 27 | ; __TEST_INJECT_KERNEL_CORE_ENTRY__: HLT 28 | ret -------------------------------------------------------------------------------- /src/kernel/core.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | extern void kernel_enable_interrupts(); 26 | extern void kernel_core_entry_asm(); 27 | 28 | char command[30]; 29 | int need_to_clear_hack; 30 | int run; 31 | int lba_start, sector_count; 32 | extern void enable_timer_interrupt(); 33 | 34 | void kernel_core_entry() { 35 | set_color_bg(C_BLUE); 36 | set_color_fg(C_WHITE); 37 | print_rectangle(0, 0, TEXT_WINDOW_WIDTH - 1, TEXT_WINDOW_HEIGHT - 1); 38 | move_xy(0, 0); 39 | print_log("Initializing Kernel"); 40 | 41 | populate_and_load_idt_table(); 42 | 43 | kernel_core_entry_asm(); 44 | 45 | print_log("Kernel enabling interrupts"); 46 | 47 | ps2_init(); 48 | ps2_keyboard_init(); 49 | 50 | process_scheduler_init(); 51 | 52 | clrscr(); 53 | 54 | VERIFY_STACKGUARD(); 55 | int init_pid = spawnl(INIT_APPNAME, INIT_APPNAME, NULL); 56 | print_info("init process got created: %d", init_pid); 57 | 58 | kernel_enable_interrupts(); 59 | interrupt_pit_enable(); 60 | while (1) 61 | ; 62 | } -------------------------------------------------------------------------------- /src/kernel/interrupts/Makefile.mk: -------------------------------------------------------------------------------- 1 | 2 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 3 | mkdir -p $(dir $@) 4 | $(KERNEL_CC) -c -o $@ \ 5 | $< 6 | 7 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 8 | mkdir -p $(dir $@) 9 | $(NASM) -o $@ $< 10 | 11 | $(SELF_BUILD_DIR)/libinterrupts: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 12 | ar rc $@ $^ 13 | -------------------------------------------------------------------------------- /src/kernel/interrupts/interrupts.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | extern void interrupt_nohup(); 11 | extern void load_idt_table_low(unsigned int idtr_address); 12 | 13 | #pragma pack(push, 1) 14 | struct IDTEntry { 15 | unsigned short offset_0; 16 | unsigned short selector; 17 | unsigned char unused; 18 | unsigned char type_attr; 19 | unsigned short offset_1; 20 | }; 21 | 22 | struct IDTReference { 23 | unsigned short size; 24 | unsigned int base_address; 25 | }; 26 | #pragma pack(pop) 27 | 28 | struct IDTReference idtr; 29 | struct IDTEntry idt_table[IDT_SIZE]; 30 | 31 | void populate_idt_entry(int id, unsigned short selector, unsigned int address, 32 | unsigned char present, // 1-bit 33 | unsigned char dpl, // 2-bit 34 | unsigned char storage_segment, // 1-bit 35 | unsigned char gate_type // 4-bit 36 | ) { 37 | struct IDTEntry *entry = &idt_table[id]; 38 | entry->offset_0 = address & 0xFFFF; 39 | entry->offset_1 = (address >> 16) & 0xFFFF; 40 | entry->selector = selector; 41 | entry->unused = 0; 42 | entry->type_attr = 43 | (present << 7) | (dpl << 5) | (storage_segment << 4) | gate_type; 44 | } 45 | 46 | void populate_idt_entry_32bit(int id, unsigned int address, 47 | unsigned char dpl, // 2-bit 48 | int is_trap) { 49 | populate_idt_entry(id, GDT_KERNEL_CS, address, 1, dpl, 0, 50 | is_trap ? 0b1111 : 0b1110); 51 | } 52 | 53 | void populate_and_load_idt_table() { 54 | print_log("Populating IDT Table"); 55 | for (int i = 0; i < IDT_SIZE; ++i) { 56 | populate_idt_entry_32bit(i, (unsigned int)interrupt_nohup, 0, 1); 57 | } 58 | print_log(" Placed %d no-hub interrupts", IDT_SIZE); 59 | 60 | print_log(" Placed custom interrupts (if any)"); 61 | 62 | interrupt_register_0x00_0x1F_exceptions(); 63 | interrupt_register_0x20_irq0_pit(); 64 | interrupt_register_0x21_0x2C_irq1_ir12_keyboard_mouse(); 65 | interrupt_register_0x32_syscall(); 66 | 67 | idtr.size = sizeof(struct IDTEntry) * IDT_SIZE; 68 | idtr.base_address = ((int)idt_table + MEMORY_KERNEL_LOCATION); 69 | print_log("IDTR: 0x%x; base address: 0x%x, size: %d", (int)&idtr, 70 | idtr.base_address, idtr.size); 71 | reload_idt_table(); 72 | } 73 | 74 | void reload_idt_table() { 75 | int idtr_address = (int)&idtr; 76 | load_idt_table_low(idtr_address); 77 | } 78 | -------------------------------------------------------------------------------- /src/kernel/interrupts/timer.asm: -------------------------------------------------------------------------------- 1 | %include "fuzzy/memmgr/layout.asm" 2 | %include "fuzzy/memmgr/tables/gdt.asm" 3 | 4 | [BITS 32] 5 | 6 | global irq0_pit_handler_low 7 | extern irq0_pit_handler 8 | extern pit_reset 9 | 10 | global create_infant_process_irq0_stack 11 | 12 | %macro _int_irq0_start 0 13 | ; eflag, cs, ip should be at start: 0 bytes 14 | CLI ; should get restored on iret 15 | pushad ; ADD: 8*4 bytes; start: 12 bytes 16 | 17 | mov ecx, [esp+36] ; cs 18 | mov edi, [esp+32] ; ip 19 | 20 | ; segment register 21 | push ds ; ADD: 4 bytes; start: 44 bytes 22 | push es ; ADD: 4 bytes; start: 48 bytes 23 | push fs ; ADD: 4 bytes; start: 52 bytes 24 | push gs ; ADD: 4 bytes; start: 56 bytes 25 | ; ignore ss; will handle later 26 | 27 | mov edx, ss 28 | mov esi, esp 29 | 30 | ; new stack and segment area 31 | mov esp, STACKINIT_KERNEL_EVENT 32 | mov eax, GDT_KERNEL_DS 33 | mov ss, eax 34 | mov ds, eax 35 | mov es, eax 36 | mov fs, eax 37 | mov gs, eax 38 | 39 | push edx ; previous ss 40 | push esi ; previous esp 41 | push ecx ; previous cs 42 | push edi ; previous ip 43 | 44 | ; C code will be using DS to read stack 45 | mov eax, esp 46 | add eax, 12 47 | push eax ; arg3: &previous_ss 48 | sub eax, 4 49 | push eax ; arg2: &previous_esp 50 | sub eax, 4 51 | push eax ; arg1: &previous_cs 52 | sub eax, 4 53 | push eax ; arg0: &previous_ip 54 | %endmacro 55 | 56 | %macro _int_irq0_end 0 57 | add esp, 16 58 | ; meant to placed at end of irq handler 59 | pop edi ; new ip 60 | pop ecx ; new cs 61 | pop esi ; new esp 62 | pop edx ; new ss 63 | 64 | mov ss, edx 65 | mov esp, esi 66 | 67 | ; pop gs 68 | ; pop fs 69 | ; pop es 70 | ; pop ds 71 | add esp, 16 72 | mov eax, ss ; ss and ds should be same for MOST purpose. This will break something :( 73 | mov gs, eax 74 | mov fs, eax 75 | mov es, eax 76 | mov ds, eax 77 | 78 | 79 | mov [esp+36], ecx ; cs 80 | mov [esp+32], edi ; ip 81 | 82 | popad 83 | %endmacro 84 | 85 | [SECTION .text] 86 | 87 | irq0_pit_handler_low: 88 | _int_irq0_start 89 | call irq0_pit_handler 90 | call pit_reset 91 | _int_irq0_end 92 | iret 93 | 94 | create_infant_process_irq0_stack: 95 | ; keep in sync with _int_irq0_start 96 | ; return the esp for user stack 97 | push ebp 98 | mov ebp, esp 99 | 100 | ; callee save register 101 | push ebx 102 | push esi 103 | push edi 104 | 105 | push ds 106 | mov ecx, [ebp+0x08] ; arg0: ss 107 | mov eax, [ebp+0x0C] ; arg1; user stack esp 108 | 109 | mov ds, ecx 110 | sub eax, 4 111 | 112 | ; kernel offset 113 | xor ecx, ecx 114 | 115 | xor ecx, 1<<9 ; enable interrupt 116 | mov [eax-0], ecx ; user: eflag 117 | 118 | ; do_not_care: next two cs, ip 119 | ; CS and IP doesn't matter, as they are controlled by _int_irq0_end 120 | 121 | ; user: pushad 122 | mov ecx, eax 123 | sub ecx, 8 124 | mov [eax-28], ecx 125 | mov ecx, 0 126 | mov [eax-12], ecx ; user: eax 127 | mov [eax-16], ecx ; user: ecx 128 | mov [eax-20], ecx ; user: edx 129 | mov [eax-24], ecx ; user: ebx 130 | ; missing esp, pushed on top. 131 | mov [eax-32], ecx ; user: ebp==0, origin of stack trace. 132 | mov [eax-36], ecx ; user: esi 133 | mov [eax-40], ecx ; user: edi 134 | 135 | mov ecx, [ebp+0x08] ; arg0 136 | mov [eax-44], ecx ; user: ds 137 | mov [eax-48], ecx ; user: es 138 | mov [eax-52], ecx ; user: fs 139 | mov [eax-56], ecx ; user: gs 140 | 141 | sub eax, 56 ; user stack pointer 142 | 143 | pop ds 144 | 145 | pop edi 146 | pop esi 147 | pop ebx 148 | 149 | pop ebp 150 | ret 151 | -------------------------------------------------------------------------------- /src/kernel/interrupts/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define INT_MAX_VALUE 0x7FFFFFFF 10 | 11 | extern void irq0_pit_handler_low(); 12 | 13 | // ticks counter 14 | static unsigned int boot_ticks_0 = 0; 15 | static unsigned int boot_ticks_1 = 0; 16 | 17 | void timer_add_ticks(unsigned int inc) { 18 | int l0 = boot_ticks_0; 19 | boot_ticks_0 += inc; 20 | if (boot_ticks_0 < l0) { 21 | boot_ticks_1++; 22 | } 23 | } 24 | 25 | // absolute time 26 | int get_time_since_boot_ms() { 27 | // This isn't accurate. 28 | // TODO: use boot_ticks_1 29 | int cycle_per_ms = PIC_PIT_FREQ / 1000; 30 | int ms = boot_ticks_0 / cycle_per_ms; 31 | return ms; 32 | } 33 | 34 | void irq0_pit_handler(int *e_ip, int *e_cs, int *e_sp, int *e_ss) { 35 | // called every X milli seconds. 36 | // time period is defined by pic_pit_set_counter. 37 | unsigned short ticks_jumped = pit_get_counter(); 38 | 39 | int oldtime_ms = get_time_since_boot_ms(); 40 | timer_add_ticks(ticks_jumped); 41 | int newtime_ms = get_time_since_boot_ms(); 42 | 43 | // yield relies on int 0x20 only 44 | process_scheduler(e_ip, e_cs, e_sp, e_ss); 45 | VERIFY_STACKGUARD(); 46 | } 47 | 48 | void interrupt_pit_enable() { 49 | pit_set_counter(PIC_PIT_FREQ / 100); // 10ms 50 | pic_irq_enable(PIC_IRQ_PIT); 51 | } 52 | 53 | void interrupt_register_0x20_irq0_pit() { 54 | populate_idt_entry_32bit(IDT_IRQ0_PIC, (unsigned int)irq0_pit_handler_low, 55 | 0, 0); 56 | pic_init(); 57 | } -------------------------------------------------------------------------------- /src/kernel/panic.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global panic_just_halt 4 | 5 | [SECTION .text] 6 | panic_just_halt: 7 | HLT 8 | JMP panic_just_halt -------------------------------------------------------------------------------- /src/kernel/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void panic_just_halt(); 4 | 5 | int panic(int err, const char *message, const char *src_file, 6 | unsigned int line_number) { 7 | set_color_bg(C_WHITE); 8 | set_color_fg(C_RED); 9 | move_xy(0, 0); 10 | print_line(src_file); 11 | print_char(':'); 12 | print_int(line_number); 13 | print_char(','); 14 | print_line(__SOURCE_SNAPSHOT__); 15 | move_xy(0, 1); 16 | print_line("Panic"); 17 | if (err > 0) { 18 | print_char('['); 19 | print_int(err); 20 | print_char(']'); 21 | } 22 | print_line(": "); 23 | print_line(message); 24 | panic_just_halt(); 25 | } 26 | 27 | int panic_screen_init() { 28 | set_color_bg(C_RED); 29 | set_color_fg(C_WHITE); 30 | // first two lines are used by panic(...) 31 | move_xy(0, 2); 32 | // kernel can not use print_log(...) to write. 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /src/kernel/process/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c -o $@ \ 4 | $< 5 | 6 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 7 | mkdir -p $(dir $@) 8 | $(NASM) -o $@ $< 9 | 10 | $(SELF_BUILD_DIR)/libprocess: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 11 | ar rc $@ $^ 12 | -------------------------------------------------------------------------------- /src/kernel/process/process.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global syscall_strncpy_user_to_kernel 4 | global syscall_strncpy_kernel_to_user 5 | global kernel_memncpy_absolute 6 | 7 | [SECTION .text] 8 | 9 | syscall_strncpy_user_to_kernel: 10 | push ebp 11 | mov ebp, esp 12 | ; callee save register 13 | push ebx 14 | push esi 15 | push edi 16 | push ds 17 | 18 | mov eax, [ebp + 0x08] ; user_ds 19 | mov ds, eax 20 | 21 | ; strcpy 22 | mov esi, [ebp + 0x0C] ; ds:esi, char *src_es_address 23 | mov edi, [ebp + 0x10] ; es:edi, char *dest_ds_address 24 | mov ecx, [ebp + 0x14] ; size_t size 25 | rep movsb 26 | 27 | pop ds 28 | pop edi 29 | pop esi 30 | pop ebx 31 | 32 | pop ebp 33 | ret 34 | 35 | syscall_strncpy_kernel_to_user: 36 | push ebp 37 | mov ebp, esp 38 | ; callee save register 39 | push ebx 40 | push esi 41 | push edi 42 | push es 43 | 44 | mov eax, [ebp + 0x08] ; user_ds 45 | mov es, eax 46 | 47 | ; strcpy 48 | mov edi, [ebp + 0x0C] ; es:edi, char *dest_address 49 | mov esi, [ebp + 0x10] ; ds:esi, char *src_address 50 | mov ecx, [ebp + 0x14] ; size_t size 51 | rep movsb 52 | 53 | pop es 54 | pop edi 55 | pop esi 56 | pop ebx 57 | 58 | pop ebp 59 | ret 60 | 61 | kernel_memncpy_absolute: 62 | ; assumes count is multiple of 4 63 | push ebp 64 | mov ebp, esp 65 | ; callee save register 66 | push ebx 67 | push esi 68 | push edi 69 | push ds 70 | push es 71 | 72 | mov ecx, [ebp + 0x18] ; count 73 | shr ecx, 2 ; count/4 74 | mov esi, [ebp + 0x14] ; src_loc 75 | mov ds, [ebp + 0x10] ; src_ds 76 | mov edi, [ebp + 0x0C] ; dest_loc 77 | mov es, [ebp + 0x08] ; dest_ds 78 | 79 | ; strcpy 80 | rep movsd ; move double word 81 | 82 | pop es 83 | pop ds 84 | pop edi 85 | pop esi 86 | pop ebx 87 | 88 | pop ebp 89 | ret 90 | 91 | 92 | [SECTION .data] 93 | kernel_saved_stack_top db ' ' 94 | -------------------------------------------------------------------------------- /src/kernel/process/scheduler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int process_scheduler_rr(int lastpid) { 5 | // Round Robin Scheduler. 6 | int best_id = -1; // not found 7 | for (int i = 1; i <= MAX_PROCESS; ++i) { 8 | int nid = (lastpid + i) % MAX_PROCESS; 9 | struct Process *process = get_process(nid); 10 | if (process->state == STATE_READY) { 11 | best_id = nid; 12 | break; 13 | } 14 | } 15 | return best_id; 16 | } 17 | 18 | int process_scheduler_largestpid(int lastpid) { 19 | // Run largest READY pid 20 | int best_id = -1; // not found 21 | for (int nid = MAX_PROCESS - 1; nid >= 0; --nid) { 22 | struct Process *process = get_process(nid); 23 | 24 | if (process->state == STATE_READY) { 25 | best_id = nid; 26 | break; 27 | } 28 | } 29 | return best_id; 30 | } 31 | 32 | void process_scheduler_stash_state() { 33 | for (int id = 0; id < MAX_PROCESS; id++) { 34 | struct Process *process = get_process(id); 35 | 36 | if (process->state == STATE_EXIT) { 37 | // process unallocate ready to be killed 38 | process->state = STATE_COLD; 39 | // print_log("process_killed: %d", id); 40 | } else if (process->state == STATE_RUNNING) { 41 | // process move running process to ready 42 | process->state = STATE_READY; 43 | } 44 | } 45 | } 46 | 47 | int process_scheduler_get_next_pid(int lastpid) { 48 | // execute one of the scheduling algorithm 49 | return process_scheduler_rr(lastpid); 50 | } 51 | 52 | static void handle_fork(unsigned int ppid, struct Process *process) { 53 | if (process->flagirq0_fork_ready > 0) { 54 | int npid = process_fork(ppid); 55 | if (npid < 0) { 56 | process->flagirq0_fork_ready = -1; // request failed; 57 | } else { 58 | struct Process *nprocess = get_process(npid); 59 | process->flagirq0_fork_ready = 0; // request success; 60 | process->flagirq0_fork_newchild = npid; 61 | nprocess->flagirq0_fork_ready = 0; // request success; 62 | nprocess->flagirq0_fork_newchild = npid; 63 | } 64 | } 65 | } 66 | 67 | void process_scheduler(int *_e_ip, int *_e_cs, int *_e_sp, int *_e_ss) { 68 | int e_ip = *_e_ip; 69 | int e_cs = *_e_cs; 70 | int e_sp = *_e_sp; 71 | int e_ss = *_e_ss; 72 | print_info("[process_scheduler] cs:ip %x:%x, ss:sp %x:%x", e_cs, e_ip, 73 | e_ss, e_sp); 74 | 75 | int pid = get_idt_reverse_pid_lookup_cs(e_cs); 76 | 77 | struct Process *process = get_process(pid); 78 | process_scheduler_stash_state(); 79 | 80 | if (process->state != STATE_COLD) { 81 | // if last process is still alive 82 | process->cs = e_cs; 83 | process->ip = e_ip; 84 | process->ss = e_ss; 85 | process->sp = e_sp; 86 | handle_fork(pid, process); 87 | } 88 | 89 | int npid = process_scheduler_get_next_pid(pid); 90 | 91 | if (npid < 0) { 92 | PANIC(0, "[process_scheduler] no STATE_READY process alive"); 93 | } 94 | 95 | if (pid != npid) { 96 | print_info("[process_scheduler] pid: %d -> %d", pid, npid); 97 | } 98 | 99 | struct Process *nprocess = get_process(npid); 100 | nprocess->state = STATE_RUNNING; 101 | e_cs = nprocess->cs; 102 | e_ip = nprocess->ip; 103 | e_ss = nprocess->ss; 104 | e_sp = nprocess->sp; 105 | 106 | *_e_ip = e_ip; 107 | *_e_cs = e_cs; 108 | *_e_sp = e_sp; 109 | *_e_ss = e_ss; 110 | } 111 | 112 | int process_waitpid(unsigned int pid, unsigned int blocked_on_pid, 113 | int *exit_code) { 114 | // It currently allows blocking on any pid, and rely on syscall client 115 | // for yield. 116 | struct Process *other_process = get_process(blocked_on_pid); 117 | if (other_process == NULL) { 118 | return -1; // err 119 | } 120 | if (other_process->state == STATE_COLD) { 121 | *exit_code = other_process->exit_code; 122 | return 0; // no error, wait over 123 | } 124 | return 1; // no error, keep waiting 125 | } 126 | -------------------------------------------------------------------------------- /src/kernel/syscall/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c -o $@ $< 4 | 5 | $(SELF_BUILD_DIR)/libsyscall: $(SELF_BUILD_ALL_C) 6 | ar rc $@ $^ 7 | -------------------------------------------------------------------------------- /src/kernel/syscall/console.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | #define CONSOLE_PUTS_BUFFER_SIZE 128 8 | 9 | static int _console_putchar(char c) { 10 | print_char(c); 11 | return 0; 12 | } 13 | 14 | static int _console_puts_buffer(int user_ds, const char *__us_str, int len) { 15 | char str[CONSOLE_PUTS_BUFFER_SIZE] = {0}; 16 | while (len > 0) { 17 | int sub_len = min(CONSOLE_PUTS_BUFFER_SIZE, len); 18 | syscall_strncpy_user_to_kernel(user_ds, __us_str, str, sub_len); 19 | 20 | for (int i = 0; i < sub_len; i++) { 21 | print_char(str[i]); 22 | } 23 | 24 | len -= sub_len; 25 | __us_str += sub_len; 26 | } 27 | return 0; 28 | } 29 | 30 | static int _console_clrscr() { 31 | print_rectangle(0, 0, TEXT_WINDOW_WIDTH - 1, TEXT_WINDOW_HEIGHT - 1); 32 | move_xy(0, 0); 33 | } 34 | 35 | int syscall_3_console(int operation, int a1, int a2, int a3, int user_ds) { 36 | switch (operation) { 37 | case SYSCALL_CONSOLE_SUB_CLRSCR: 38 | return _console_clrscr(); 39 | case SYSCALL_CONSOLE_SUB_PUTCHAR: 40 | return _console_putchar((char)a1); 41 | case SYSCALL_CONSOLE_SUB_PUTS_BUFFER: 42 | return _console_puts_buffer(user_ds, (const char *)a1, a2); 43 | } 44 | return -1; 45 | } 46 | 47 | #undef CONSOLE_PUTS_BUFFER_SIZE 48 | -------------------------------------------------------------------------------- /src/kernel/syscall/file_handler.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int fh_switch(int operation) { return 0; } 11 | 12 | int file_handler_find(char *filename, union FFSFileEntry *entry) { 13 | // search for filename case insensitive as keyboard driver currently 14 | // doesn't support shift or caps lock. 15 | int file_id = 0; 16 | while (file_id < FS_FFS_FILEENTRY_COUNT) { 17 | int err = fetch_file_entry(FFS_UNIQUE_PARITION_ID, file_id, entry); 18 | if (!err && strcmpi(filename, entry->content.filename) == 0) { 19 | // match 20 | return file_id; 21 | } 22 | file_id++; 23 | } 24 | return -1; // no file found 25 | } 26 | 27 | int _file_handler_open(int user_ds, char *_us_filename) { 28 | char filename[FS_FFS_FILENAME_LIMIT]; 29 | syscall_strncpy_user_to_kernel(user_ds, _us_filename, filename, 30 | sizeof(filename)); 31 | 32 | union FFSFileEntry entry; 33 | return file_handler_find(filename, &entry); 34 | } 35 | 36 | int _file_handler_read(int user_ds, int file_id, 37 | char _us_buffer[FILEIO_BUFFER_SIZE], size_t byte_index) { 38 | union FFSFileEntry entry; 39 | int err = fetch_file_entry(FFS_UNIQUE_PARITION_ID, file_id, &entry); 40 | if (err) { 41 | return -2; 42 | } 43 | 44 | int block_id = byte_index / FS_BLOCK_SIZE; 45 | int byte_index_block = byte_index % FS_BLOCK_SIZE; 46 | char block[FS_BLOCK_SIZE]; 47 | 48 | int len = 49 | fetch_file_content(FFS_UNIQUE_PARITION_ID, &entry, block, block_id); 50 | 51 | if (len < 0) { 52 | return len; // err 53 | } 54 | if (len == 0) { 55 | return 0; 56 | } 57 | len = min(len, FILEIO_BUFFER_SIZE); // guard for _us_buffer 58 | len = min(len, 59 | FS_BLOCK_SIZE - 60 | byte_index_block); // skip already read content of the block 61 | syscall_strncpy_kernel_to_user(user_ds, _us_buffer, 62 | block + byte_index_block, len); 63 | return len; 64 | } 65 | 66 | int _file_handler_read_dir(int user_ds, int last_file_id, 67 | struct dirent *_us_dirent) { 68 | // char *_us_filename 69 | union FFSFileEntry entry; 70 | int file_id; 71 | if (last_file_id < 0) { 72 | file_id = 0; 73 | } else { 74 | file_id = last_file_id + 1; 75 | } 76 | while (file_id < FS_FFS_FILEENTRY_COUNT) { 77 | int err = fetch_file_entry(FFS_UNIQUE_PARITION_ID, file_id, &entry); 78 | if (err) { 79 | file_id++; 80 | continue; 81 | } 82 | // next file 83 | struct dirent centry = {0}; 84 | memcpy(centry.d_name, entry.content.filename, sizeof(centry.d_name)); 85 | centry.size = entry.content.filesize; 86 | if (entry.content.flags & FFS_FILE_FLAG_EXECUTABLE) { 87 | centry.flag |= DIRENT_EXECUTABLE; 88 | } 89 | 90 | syscall_strncpy_kernel_to_user(user_ds, _us_dirent, ¢ry, 91 | sizeof(struct dirent)); 92 | return file_id; 93 | } 94 | return -1; // there is no next file 95 | } 96 | 97 | int syscall_2_file_handler(int operation, int a1, int a2, int a3, int user_ds) { 98 | switch (operation) { 99 | case SYSCALL_FILE_SUB_OPEN: 100 | return _file_handler_open(user_ds, (char *)a1); 101 | case SYSCALL_FILE_SUB_READBUFFER: 102 | return _file_handler_read(user_ds, a1, (char *)a2, a3); 103 | case SYSCALL_FILE_SUB_READ_DIR: 104 | return _file_handler_read_dir(user_ds, a1, (struct dirent *)a2); 105 | } 106 | return -1; 107 | } 108 | -------------------------------------------------------------------------------- /src/kernel/syscall/graphics.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static int _graphics_prev_mode = -1; 7 | 8 | static int _graphics_init() { 9 | if (_graphics_prev_mode != -1) { 10 | // already in graphics mode 11 | return -1; 12 | } 13 | _graphics_prev_mode = graphics_get_mode(); 14 | int err = graphics_set_mode(GRAPHIC_MODE_320x200x256); 15 | return err; 16 | } 17 | 18 | static int _graphics_close() { 19 | if (_graphics_prev_mode == -1) { 20 | // not in graphics mode 21 | return -1; 22 | } 23 | int err = graphics_set_mode(_graphics_prev_mode); 24 | // flush text screen 25 | flush_screen(); 26 | _graphics_prev_mode = -1; 27 | return err; 28 | } 29 | 30 | static int _graphics_copybuffer(int user_ds, void *_us_buffer) { 31 | uint8_t ARR[10][10]; 32 | for (int i = 0; i < 10; i++) 33 | for (int j = 0; j < 10; j++) { 34 | ARR[i][j] = WHITE; 35 | } 36 | return graphics_write_320x200x256(user_ds, _us_buffer); 37 | } 38 | 39 | int syscall_4_graphics(int operation, int a1, int a2, int a3, int user_ds) { 40 | switch (operation) { 41 | case SYSCALL_GRAPHICS_INITGRAPH: 42 | return _graphics_init(); 43 | case SYSCALL_GRAPHICS_CLOSEGRAPH: 44 | return _graphics_close(); 45 | case SYSCALL_GRAPHICS_COPYBUFFER: 46 | return _graphics_copybuffer(user_ds, (void *)a1); 47 | } 48 | return -1; 49 | } -------------------------------------------------------------------------------- /src/kernel/syscall/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | static int _keyboard_getch() { return ps2_keyboard_get_key_pressed_poll(); } 7 | 8 | static int _keyboard_kbhit() { return ps2_keyboard_get_kbhit(); } 9 | 10 | int syscall_0_keyboard(int operation, int a1, int a2, int a3, int user_ds) { 11 | switch (operation) { 12 | case SYSCALL_KEYBOARD_SUB_GETCH: 13 | return _keyboard_getch(); 14 | case SYSCALL_KEYBOARD_SUB_KBHIT: 15 | return _keyboard_kbhit(); 16 | } 17 | return -1; 18 | } -------------------------------------------------------------------------------- /src/kernel/syscall/syscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #define SYSCALL_SIZE 30 15 | 16 | int SYSCALL_TABLE[SYSCALL_SIZE]; 17 | 18 | extern int interrupt_handler_0x32_syscall_handler(); 19 | 20 | void interrupt_register_0x32_syscall() { 21 | print_log("Registering syscalls."); 22 | populate_idt_entry_32bit( 23 | IDT_SYSCALL, (unsigned int)interrupt_handler_0x32_syscall_handler, 0, 24 | 1); 25 | SYSCALL_TABLE[SYSCALL_KEYBOARD] = syscall_0_keyboard; 26 | SYSCALL_TABLE[SYSCALL_PROCESS] = syscall_1_process; 27 | SYSCALL_TABLE[SYSCALL_FILE_OP] = syscall_2_file_handler; 28 | SYSCALL_TABLE[SYSCALL_CONSOLE] = syscall_3_console; 29 | SYSCALL_TABLE[SYSCALL_GRAPHICS] = syscall_4_graphics; 30 | } 31 | 32 | int syscall_selector(int id, int arg0, int arg1, int arg2, int arg3, 33 | int user_ds) { 34 | return ((int (*)(int, int, int, int, int))(SYSCALL_TABLE[id]))( 35 | arg0, arg1, arg2, arg3, user_ds); 36 | } 37 | -------------------------------------------------------------------------------- /src/lib/app/Makefile.mk: -------------------------------------------------------------------------------- 1 | BUILD_LIB_APP_ENTRY = $(addprefix $(BUILD_LIB)/app/,entry_asm.o) 2 | 3 | $(BUILD_LIB)/app/%_asm.o: $(SRC_LIB)/app/%.asm 4 | mkdir -p $(BUILD_LIB)/app 5 | nasm -o $@ -f elf32 $< 6 | -------------------------------------------------------------------------------- /src/lib/app/entry.asm: -------------------------------------------------------------------------------- 1 | ; All user application entry point 2 | [BITS 32] 3 | 4 | extern main 5 | extern exit 6 | 7 | [SECTION .text] 8 | _start: 9 | call main ; return value should be in eax 10 | push eax 11 | call exit ; process should be unallocated before returning from exit 12 | -------------------------------------------------------------------------------- /src/lib/ds/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(BUILD_LIB_DS)/libds: $(SRC_LIB_DS)/queue.h $(SRC_LIB_DS)/queue.c 2 | mkdir -p $(BUILD_LIB_DS)/ 3 | $(CC) -m32 -fno-pie -c -Isrc -o $(BUILD_LIB_DS)/queue.o $(SRC_LIB_DS)/queue.c 4 | ar rc $@ $(BUILD_LIB_DS)/queue.o -------------------------------------------------------------------------------- /src/lib/ds/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int queue_init(int container[], int size) { 4 | container[2] = size; 5 | if (size < 4) 6 | return 0; 7 | int *back = &container[0]; 8 | int *front = &container[1]; 9 | *back = *front = -1; 10 | return 1; 11 | } 12 | 13 | void queue_clear(int container[]) { 14 | int *back = &container[0]; 15 | int *front = &container[1]; 16 | *back = *front = -1; 17 | } 18 | 19 | int queue_push(int container[], int data) { 20 | int *back = &container[0]; 21 | int *front = &container[1]; 22 | int size = container[2]; 23 | if ((*back) == -1) { 24 | *back = 3; 25 | *front = 3; 26 | } else { 27 | (*back)++; 28 | if ((*back) >= size) { 29 | (*back) = 3; 30 | } 31 | if ((*back) == (*front)) { 32 | return 0; 33 | } 34 | } 35 | container[*back] = data; 36 | return 1; 37 | } 38 | 39 | int queue_push_front(int container[], int data) { 40 | int *back = &container[0]; 41 | int *front = &container[1]; 42 | int size = container[2]; 43 | if ((*back) == -1) { 44 | *back = 3; 45 | *front = 3; 46 | } else { 47 | (*front)--; 48 | if ((*front) < 3) { 49 | (*front) = size - 1; 50 | } 51 | if ((*back) == (*front)) { 52 | return 0; 53 | } 54 | } 55 | container[*front] = data; 56 | return 1; 57 | } 58 | 59 | int queue_front(int container[]) { 60 | int *front = &container[0]; 61 | return container[*front]; 62 | } 63 | 64 | int queue_pop(int container[]) { 65 | int *back = &container[0]; 66 | int *front = &container[1]; 67 | int size = container[2]; 68 | int top = container[*front]; 69 | if ((*front) == (*back)) { 70 | *front = *back = -1; 71 | return 0; 72 | } else { 73 | (*front)++; 74 | if ((*front) >= size) { 75 | (*front) = 3; 76 | } 77 | return 1; 78 | } 79 | } 80 | 81 | int queue_size(int container[]) { 82 | int *back = &container[0]; 83 | int *front = &container[1]; 84 | if ((*back) < 0) 85 | return 0; 86 | if ((*back) >= (*front)) { 87 | return (*back) - (*front) + 1; 88 | } 89 | int size = container[2]; 90 | return size - (*front) + (*back) - 1; 91 | } 92 | 93 | int queue_capacity(int container[]) { 94 | int size = container[2]; 95 | return size; 96 | } -------------------------------------------------------------------------------- /src/lib/ds/queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Note: Container expects size to be atleast 3 more than then 3 | // the effective_size. 4 | 5 | // Returns if the queue init is successful. 6 | int queue_init(int container[], int effective_size); 7 | // Clears the queue. 8 | void queue_clear(int container[]); 9 | // Returns if the push is successful. 10 | int queue_push(int container[], int data); 11 | // Returns if the push is successful. 12 | int queue_push_front(int container[], int data); 13 | // Returns the front of the queue. 14 | int queue_front(int container[]); 15 | // Returns if the pop is successful. 16 | int queue_pop(int container[]); 17 | // Returns the number of elements in the queue. 18 | int queue_size(int container[]); 19 | // Returns the queue capacity. 20 | int queue_capacity(int container[]); -------------------------------------------------------------------------------- /src/lib/utils/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(BUILD_LIB_UTILS)/libutils_16: $(SRC_LIB_UTILS)/basic.c $(SRC_LIB_UTILS)/basic_16.asm $(SRC_LIB_UTILS)/logging.c $(SRC_LIB_UTILS)/output.c $(SRC_LIB_UTILS)/output.h $(SRC_LIB_UTILS)/time.c $(SRC_LIB_UTILS)/time.h $(SRC_LIB_UTILS)/time.asm $(SRC_LIB_UTILS)/color.c $(SRC_LIB_UTILS)/color.h 2 | mkdir -p $(BUILD_LIB_UTILS)/ 3 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_LIB_UTILS)/basic_16_c.o $(SRC_LIB_UTILS)/basic.c 4 | nasm -o $(BUILD_LIB_UTILS)/basic_16_asm.o -f elf32 $(SRC_LIB_UTILS)/basic_16.asm 5 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_LIB_UTILS)/logging_16.o $(SRC_LIB_UTILS)/logging.c 6 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_LIB_UTILS)/output_16.o $(SRC_LIB_UTILS)/output.c 7 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_LIB_UTILS)/color_16.o $(SRC_LIB_UTILS)/color.c 8 | $(CC) -m16 -fno-pie -c -Isrc -o $(BUILD_LIB_UTILS)/time_16_c.o $(SRC_LIB_UTILS)/time.c 9 | nasm -o $(BUILD_LIB_UTILS)/time_16_asm.o -f elf32 $(SRC_LIB_UTILS)/time.asm 10 | ar rc $@ $(BUILD_LIB_UTILS)/basic_16_asm.o $(BUILD_LIB_UTILS)/basic_16_c.o $(BUILD_LIB_UTILS)/logging_16.o $(BUILD_LIB_UTILS)/output_16.o $(BUILD_LIB_UTILS)/color_16.o $(BUILD_LIB_UTILS)/time_16_c.o $(BUILD_LIB_UTILS)/time_16_asm.o 11 | 12 | $(BUILD_LIB_UTILS)/libutils: $(BUILD_USR_INCLUDE_ALL) $(SRC_LIB_UTILS)/basic.c $(SRC_LIB_UTILS)/basic.asm $(SRC_LIB_UTILS)/logging.c $(SRC_LIB_UTILS)/output.c $(SRC_LIB_UTILS)/output.h $(SRC_LIB_UTILS)/input.c $(SRC_LIB_UTILS)/input.h $(SRC_LIB_UTILS)/time.c $(SRC_LIB_UTILS)/time.h $(SRC_LIB_UTILS)/time.asm $(SRC_LIB_UTILS)/color.c $(SRC_LIB_UTILS)/color.h 13 | mkdir -p $(BUILD_LIB_UTILS)/ 14 | $(KERNEL_CC) -c -o $(BUILD_LIB_UTILS)/basic_c.o $(SRC_LIB_UTILS)/basic.c 15 | nasm -o $(BUILD_LIB_UTILS)/basic_asm.o -f elf32 $(SRC_LIB_UTILS)/basic.asm 16 | $(KERNEL_CC) -c -o $(BUILD_LIB_UTILS)/logging.o $(SRC_LIB_UTILS)/logging.c 17 | $(KERNEL_CC) -c -o $(BUILD_LIB_UTILS)/output.o $(SRC_LIB_UTILS)/output.c 18 | $(KERNEL_CC) -c -o $(BUILD_LIB_UTILS)/input.o $(SRC_LIB_UTILS)/input.c 19 | $(KERNEL_CC) -c -o $(BUILD_LIB_UTILS)/color.o $(SRC_LIB_UTILS)/color.c 20 | $(KERNEL_CC) -c -o $(BUILD_LIB_UTILS)/time_c.o $(SRC_LIB_UTILS)/time.c 21 | nasm -o $(BUILD_LIB_UTILS)/time_asm.o -f elf32 $(SRC_LIB_UTILS)/time.asm 22 | ar rc $@ $(BUILD_LIB_UTILS)/basic_asm.o $(BUILD_LIB_UTILS)/basic_c.o $(BUILD_LIB_UTILS)/logging.o $(BUILD_LIB_UTILS)/output.o $(BUILD_LIB_UTILS)/input.o $(BUILD_LIB_UTILS)/color.o $(BUILD_LIB_UTILS)/time_c.o $(BUILD_LIB_UTILS)/time_asm.o 23 | -------------------------------------------------------------------------------- /src/lib/utils/basic.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global va_args_first_adress 4 | 5 | [SECTION .text] 6 | 7 | va_args_first_adress: 8 | 9 | mov eax, ebp 10 | add eax, 8 11 | ret 12 | -------------------------------------------------------------------------------- /src/lib/utils/basic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int *va_args_first_adress(); 4 | -------------------------------------------------------------------------------- /src/lib/utils/basic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CLOCKS_PER_MS 56230 // 5% of Host CPU 4 | #define INT_MAX 0xFFFFFFFF 5 | 6 | // Returns address of the first variable argument of caller function. 7 | int *va_args_first_adress(); 8 | // Returns i-th variable argument given the address of first VA. 9 | #define get_va_arg(base, type, index) (*((type *)(((int *)base) + index))) -------------------------------------------------------------------------------- /src/lib/utils/basic_16.asm: -------------------------------------------------------------------------------- 1 | [BITS 16] 2 | 3 | global va_args_first_adress 4 | 5 | [SECTION .text] 6 | 7 | va_args_first_adress: 8 | 9 | mov eax, ebp 10 | add eax, 8 11 | ret 12 | -------------------------------------------------------------------------------- /src/lib/utils/color.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned char IO_CURRENT_FG = 0; 4 | unsigned char IO_CURRENT_BG = 0; 5 | unsigned char IO_CURRENT_COLOR = 0; 6 | 7 | void _compute_color() { 8 | IO_CURRENT_COLOR = make_color(IO_CURRENT_FG, IO_CURRENT_BG); 9 | } 10 | 11 | unsigned char get_color_fgbg() { return IO_CURRENT_COLOR; } 12 | 13 | void set_color_fgbg(unsigned char color) { IO_CURRENT_COLOR = color; } 14 | 15 | void set_color_fg(unsigned char color) { 16 | IO_CURRENT_FG = color; 17 | _compute_color(); 18 | } 19 | 20 | void set_color_bg(unsigned char color) { 21 | IO_CURRENT_BG = color; 22 | _compute_color(); 23 | } -------------------------------------------------------------------------------- /src/lib/utils/color.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define C_BLACK 0x0 4 | #define C_BLUE 0x1 5 | #define C_GREEN 0x2 6 | #define C_CYAN 0x3 7 | #define C_RED 0x4 8 | #define C_MAGENTA 0x5 9 | #define C_BROWN 0x6 10 | #define C_LIGHT_GRAY 0x7 11 | #define C_DARK_GRAY 0x8 12 | #define C_LIGHT_BLUE 0x9 13 | #define C_LIGHT_GREEN 0xA 14 | #define C_LIGHT_CYAN 0xB 15 | #define C_LIGHT_RED 0xC 16 | #define C_LIGHT_MAGENTA 0xD 17 | #define C_YELLOW 0xE 18 | #define C_WHITE 0xF 19 | 20 | #define make_color(fg, bg) ((unsigned char)((fg) + ((bg) << 4))) 21 | 22 | unsigned char get_color_fgbg(); 23 | void set_color_fgbg(unsigned char color); 24 | void set_color_fg(unsigned char color); 25 | void set_color_bg(unsigned char color); 26 | -------------------------------------------------------------------------------- /src/lib/utils/input.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | static char buffer_num[20]; 7 | 8 | extern int getch_low(); 9 | 10 | char getch_in() { return SYSCALL_A0(SYSCALL_KEYBOARD); } 11 | 12 | // TODO(scopeinfinity): Fix local variables with syscall(maybe?) 13 | int i = 0; 14 | void read_line(char *str) { 15 | i = 0; 16 | while (1) { 17 | // Bug: Wierd hack to mitigate another hack. 18 | // Using following instead of str[i]=getch_in(); 19 | char z = getch_in(); 20 | str[i] = z; 21 | 22 | if (str[i] == '\r' || str[i] == '\n') { 23 | str[i] = '\0'; 24 | print_char('\n'); 25 | break; 26 | } 27 | print_char(str[i]); 28 | i++; 29 | } 30 | } 31 | 32 | int read_int() { 33 | read_line(buffer_num); 34 | return atoi(buffer_num); 35 | } -------------------------------------------------------------------------------- /src/lib/utils/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __cplusplus 3 | extern "C" { 4 | #endif 5 | 6 | char getch_in(); 7 | void read_line(char *str); 8 | int read_int(); 9 | 10 | #ifdef __cplusplus 11 | } 12 | #endif -------------------------------------------------------------------------------- /src/lib/utils/logging.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // User should override LOG_PREFIX is required. 5 | #define LOG_PREFIX "" 6 | 7 | void print_log(const char *strfmt, ...) { 8 | int *va_base = va_args_first_adress(); 9 | print_line(LOG_PREFIX); 10 | printf_low(strfmt, va_base + 1); 11 | print_char('\n'); 12 | } 13 | 14 | void print_info(const char *strfmt, ...) { 15 | // Do nothing for now. 16 | // TODO: Log appropriately 17 | } -------------------------------------------------------------------------------- /src/lib/utils/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LOG_VVV 0 4 | void print_log(const char *strfmt, ...); 5 | void print_info(const char *strfmt, ...); -------------------------------------------------------------------------------- /src/lib/utils/output.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void flush_screen() { io_low_flush(); } 7 | 8 | void move_x(unsigned char x) { set_display_text_x(x); } 9 | 10 | void move_y(unsigned char y) { set_display_text_y(y); } 11 | 12 | void move_xy(unsigned char x, unsigned char y) { set_display_text_xy(x, y); } 13 | 14 | void move_x_diff(char dx) { set_display_text_x(get_display_text_x() + dx); } 15 | 16 | void move_y_diff(char dy) { set_display_text_y(get_display_text_y() + dy); } 17 | 18 | void move_xy_diff(char dx, char dy) { 19 | int x = get_display_text_x() + dx; 20 | int y = get_display_text_y() + dy; 21 | set_display_text_xy(x, y); 22 | } 23 | 24 | void print_rectangle(unsigned char x1, unsigned char y1, unsigned char x2, 25 | unsigned char y2) { 26 | io_low_scroll_screen(0, get_color_fgbg(), x1, y1, x2, y2); 27 | } 28 | 29 | void scroll(char count, unsigned char x1, unsigned char y1, unsigned char x2, 30 | unsigned char y2) { 31 | io_low_scroll_screen(count, get_color_fgbg(), x1, y1, x2, y2); 32 | } 33 | 34 | void move_to_next_line() { 35 | if (get_display_text_y() >= TEXT_WINDOW_HEIGHT - 1) { 36 | scroll(1, 0, 0, TEXT_WINDOW_WIDTH - 1, TEXT_WINDOW_HEIGHT - 1); 37 | } else { 38 | move_y_diff(1); 39 | } 40 | move_x(0); 41 | } 42 | 43 | void print_char(char c) { 44 | // TODO: Think about handling ANSI escape sequence. 45 | switch (c) { 46 | case '\n': 47 | move_to_next_line(); 48 | break; 49 | default: 50 | if (get_display_text_x() == TEXT_WINDOW_WIDTH) { 51 | // wrap around 52 | move_to_next_line(); 53 | } 54 | io_low_put_char(c, get_color_fgbg()); 55 | move_xy_diff(1, 0); 56 | } 57 | } 58 | char digit_to_hex[] = "0123456789ABCDEF"; 59 | 60 | void print_hex_nibble(unsigned char x) { 61 | if (x < 10) { 62 | print_char((char)(x + '0')); 63 | } else { 64 | print_char((char)(x + ('A' - 10))); 65 | } 66 | } 67 | 68 | void print_hex_byte(unsigned char x) { 69 | print_hex_nibble((x >> 4) & 0xF); 70 | print_hex_nibble(x & 0xF); 71 | } 72 | 73 | void print_hex_short(unsigned short x) { 74 | print_hex_byte(x >> 8); 75 | print_hex_byte(x & 0xFF); 76 | } 77 | 78 | void print_hex_int(unsigned int x) { 79 | print_hex_short(x >> 16); 80 | print_hex_short(x & 0xFFFF); 81 | } 82 | 83 | void print_line(const char *str) { 84 | while ((*str) != '\0') { 85 | print_char(*str); 86 | str++; 87 | } 88 | } 89 | 90 | void print_memory_hex(const char *str, unsigned short count) { 91 | while (count) { 92 | print_hex_byte(*str); 93 | str++; 94 | count--; 95 | } 96 | } 97 | 98 | void print_int(int x) { 99 | int is_negative = 0; 100 | int tailing_zero = 0; 101 | if (x < 0) { 102 | is_negative = 1; 103 | x = -x; 104 | } 105 | int rev = 0; 106 | while (x && x % 10 == 0) { 107 | x /= 10; 108 | tailing_zero++; 109 | } 110 | if (x == 0) { 111 | // If the initial number is zero, add one here. 112 | tailing_zero++; 113 | } 114 | while (x) { 115 | rev = rev * 10 + x % 10; 116 | x /= 10; 117 | } 118 | if (is_negative) { 119 | print_char('-'); 120 | } 121 | while (rev) { 122 | print_char((char)(rev % 10 + '0')); 123 | rev /= 10; 124 | } 125 | while (tailing_zero) { 126 | print_char('0'); 127 | tailing_zero--; 128 | } 129 | } 130 | 131 | void printf_low(const char *fmt, int *va_base) { 132 | int va_index = 0; 133 | for (; (*fmt) != '\0'; fmt++) { 134 | if ((*fmt) == '%') { 135 | fmt++; 136 | if ((*fmt) == '\0') 137 | break; 138 | switch (*fmt) { 139 | case '%': 140 | print_char('%'); 141 | break; 142 | case 's': 143 | print_line(get_va_arg(va_base, const char *, va_index++)); 144 | break; 145 | case 'd': 146 | print_int(get_va_arg(va_base, const int, va_index++)); 147 | break; 148 | case 'x': 149 | print_hex_int(get_va_arg(va_base, const int, va_index++)); 150 | break; 151 | default: 152 | break; 153 | } 154 | } else { 155 | print_char(*fmt); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/lib/utils/output.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __cplusplus 3 | extern "C" { 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | void flush_screen(); 11 | void move_x(unsigned char x); 12 | void move_y(unsigned char y); 13 | void move_xy(unsigned char x, unsigned char y); 14 | void move_x_diff(char dx); 15 | void move_y_diff(char dy); 16 | void move_xy_diff(char dx, char dy); 17 | 18 | void print_rectangle(unsigned char x1, unsigned char y1, unsigned char x2, 19 | unsigned char y2); 20 | 21 | void scroll(char count, unsigned char x1, unsigned char y1, unsigned char x2, 22 | unsigned char y2); 23 | 24 | void print_char(char c); 25 | void print_hex_nibble(unsigned char x); 26 | void print_hex_byte(unsigned char x); 27 | void print_hex_short(unsigned short x); 28 | void print_hex_int(unsigned int x); 29 | void print_line(const char *str); 30 | void print_memory_hex(const char *str, unsigned short count); 31 | void print_int(int x); 32 | 33 | void printf_low(const char *strfmt, int *va_base); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif -------------------------------------------------------------------------------- /src/lib/utils/time.asm: -------------------------------------------------------------------------------- 1 | [BITS 16] 2 | 3 | global _low_sleep 4 | 5 | [SECTION .text] 6 | _low_sleep: 7 | push ebp 8 | mov ebp, esp 9 | ; callee save register 10 | push ebx 11 | ; push esi 12 | ; push edi 13 | 14 | mov ebx, [ebp + 0x08] ; (cycles) 15 | _low_sleep_internal: 16 | sub ebx, 1 17 | jnc _low_sleep_internal 18 | 19 | ; pop edi 20 | ; pop esi 21 | pop ebx 22 | pop ebp 23 | ret -------------------------------------------------------------------------------- /src/lib/utils/time.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void _low_sleep(unsigned int half_instructions_count); 4 | 5 | void sleep(unsigned int ms) { 6 | while (ms > 0) { 7 | unsigned int fms = ms > SLEEP_BURST_MS ? SLEEP_BURST_MS : ms; 8 | unsigned int cycles = 9 | CLOCKS_PER_MS * fms / SLEEP_FACTOR_DENO * SLEEP_FACTOR_NUM; 10 | _low_sleep(cycles); 11 | ms -= fms; 12 | } 13 | } -------------------------------------------------------------------------------- /src/lib/utils/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #define SLEEP_FACTOR_NUM 3 // Possiblity of improvement. 5 | #define SLEEP_FACTOR_DENO 2 6 | #define SLEEP_BURST_MS \ 7 | INT_MAX / CLOCKS_PER_MS / SLEEP_FACTOR_NUM *SLEEP_FACTOR_DENO 8 | 9 | void sleep(unsigned int ms); -------------------------------------------------------------------------------- /src/memmgr/stackguard/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(BUILD_USR_INCLUDE_ALL) 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c -o $@ $< 4 | 5 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm 6 | mkdir -p $(dir $@) 7 | nasm -o $@ -f elf32 $< 8 | 9 | $(SELF_BUILD_DIR)/libstackguard: $(SELF_BUILD_ALL_C) $(SELF_BUILD_ALL_ASM) 10 | ar rc $@ $^ 11 | -------------------------------------------------------------------------------- /src/memmgr/stackguard/stackguard.asm: -------------------------------------------------------------------------------- 1 | ; Corresponding object file should be linked at the tail of the 2 | ; instructions. 3 | ; Assumes .data gets linked after .text 4 | 5 | [BITS 32] 6 | 7 | global check_stack_guard 8 | 9 | ; it would be nice if magic number is present at head of 10 | ; instruction but this should be enough for now. 11 | STACK_GUARD_MAGIC_NUMBER EQU 0x4C10A5C7 12 | 13 | [SECTION .text] 14 | check_stack_guard: 15 | ; return 0 is stack is good 16 | push ebp 17 | mov ebp, esp 18 | 19 | mov eax, [stack_guard_pointer] 20 | xor eax, STACK_GUARD_MAGIC_NUMBER 21 | 22 | pop ebp 23 | ret 24 | 25 | [SECTION .data] 26 | stack_guard_pointer dd STACK_GUARD_MAGIC_NUMBER -------------------------------------------------------------------------------- /src/memmgr/stackguard/stackguard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern int check_stack_guard(); 5 | 6 | void verify_stack_guard(char err_message[]) { 7 | int err = check_stack_guard(); 8 | if (err != 0) { 9 | PANIC(0, err_message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/memmgr/tables/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(SELF_BUILD_DIR)/%.o: $(SELF_SRC_DIR)/%.c $(SELF_INCLUDE_DIR)/%.h 2 | mkdir -p $(dir $@) 3 | $(KERNEL_CC) -c -o $@ \ 4 | $< 5 | 6 | $(SELF_BUILD_DIR)/libgdt: $(SELF_BUILD_ALL_C) 7 | ar rc $@ $^ 8 | -------------------------------------------------------------------------------- /src/memmgr/tables/gdt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int get_gdt_baseaddress(struct GDTEntry gdt_table[], unsigned int table_size, 7 | int entry_id) { 8 | if (entry_id >= 0 && entry_id < table_size) { 9 | unsigned int base0 = gdt_table[entry_id].base0; 10 | unsigned int base1 = gdt_table[entry_id].base1; 11 | base1 <<= 16; 12 | unsigned int base2 = gdt_table[entry_id].base2; 13 | base2 <<= 24; 14 | return base2 | base1 | base0; 15 | } 16 | return -1; 17 | } 18 | 19 | void populate_gdt_entry(struct GDTEntry *entry, unsigned int base, 20 | unsigned int limit, // 32 bit with 4k granularity 21 | unsigned char access_byte, int is_32_bit_selector) { 22 | const int is_4k_granularity = 1; 23 | 24 | char flags = 0b0000; // 4 bits 25 | if (is_4k_granularity) 26 | flags |= 0b1000; 27 | if (is_32_bit_selector) 28 | flags |= 0b0100; 29 | 30 | if (is_4k_granularity) { 31 | limit >>= 12; 32 | } 33 | 34 | entry->base0 = (base & 0x0000FFFF); 35 | entry->base1 = (base & 0x00FF0000) >> 16; 36 | entry->base2 = (base & 0xFF000000) >> 24; 37 | entry->limit0 = (limit & 0x0FFFF); 38 | entry->flags_limit1 = (flags << 4) | ((limit & 0xF0000) >> 16); 39 | entry->access_byte = access_byte; 40 | } 41 | 42 | void populate_gdt_table(struct GDTReference *gdtr, struct GDTEntry gdt_table[], 43 | int entries_count, int ds_fix) { 44 | // Populate the allocated GDT Table and GDT Reference. 45 | // gdtr : Reference to the GDT Table to be consumed by lgdt. 46 | // gdt_table : GDT Table. 47 | // entries_count : Allocated number of entries for GDT Table. 48 | // ds_fix : Data Segment fix to translate gdt_table relative 49 | // address to absolute address. 50 | 51 | print_log("Populating GDT Table at 0x%d", gdt_table); 52 | 53 | // NULL selector 54 | populate_gdt_entry(&gdt_table[GDT_STD_SELECTOR_NULL], 0, 0, 0, 55 | !GDT_ENTRY_FLAG_32_BIT_SELECTOR); 56 | // Kernel Code Segment Selector 57 | populate_gdt_entry(&gdt_table[GDT_STD_SELECTOR_KERNEL_CS], 58 | MEMORY_KERNEL_LOCATION, MEMORY_KERNEL_SIZE - 1, 0x9a, 59 | GDT_ENTRY_FLAG_32_BIT_SELECTOR // 32-bit protected mode 60 | ); 61 | // Kernel Data Segment Selector 62 | populate_gdt_entry(&gdt_table[GDT_STD_SELECTOR_KERNEL_DS], 63 | MEMORY_KERNEL_LOCATION, MEMORY_KERNEL_SIZE - 1, 0x92, 64 | GDT_ENTRY_FLAG_32_BIT_SELECTOR // 32-bit protected mode 65 | ); 66 | // Absolute Code Segment Selector 67 | populate_gdt_entry(&gdt_table[GDT_STD_SELECTOR_ABS16_CS], 0, 0xfffff, 0x9a, 68 | !GDT_ENTRY_FLAG_32_BIT_SELECTOR // 16-bit protected mode 69 | ); 70 | // Absolute Data Segment Selector 71 | populate_gdt_entry(&gdt_table[GDT_STD_SELECTOR_ABS16_DS], 0, 0xfffff, 0x92, 72 | !GDT_ENTRY_FLAG_32_BIT_SELECTOR // 16-bit protected mode 73 | ); 74 | // NOT USING &gdt_table[5] for now. 75 | // Absolute Data Segment Selector 76 | populate_gdt_entry(&gdt_table[GDT_STD_SELECTOR_ABS32_DS], 0, 0xffffffff, 77 | 0x92, 78 | GDT_ENTRY_FLAG_32_BIT_SELECTOR // 32-bit protected mode 79 | ); 80 | // Ensure GDT_STD_SIZE = last sector entry+1 81 | 82 | gdtr->base_address = ds_fix + (int)gdt_table; 83 | gdtr->size = (entries_count * sizeof(struct GDTEntry)); 84 | 85 | // Log table and table address. 86 | print_log("GDTR: 0x%x; base address: 0x%x, size: %d", (int)gdtr, 87 | gdtr->base_address, gdtr->size); 88 | 89 | if (LOG_VVV) { 90 | int gdt_entries = gdtr->size / sizeof(struct GDTEntry); 91 | for (int i = 0; i < gdt_entries; i++) { 92 | print_log(" GDT Entry %d: %x%x", i, 93 | *(int *)(8 * i + (int)gdt_table), 94 | *(int *)(8 * i + 4 + (int)gdt_table)); 95 | } 96 | } 97 | } 98 | 99 | void load_gdt_table(struct GDTReference *gdtr) { 100 | print_log("Loading GDT Table: 0x%x; base address: 0x%x, size: %d", 101 | (int)gdtr, gdtr->base_address, gdtr->size); 102 | __asm__("mov %0, %%eax\n\t" 103 | "lgdt (%%eax)" 104 | : 105 | : "r"(gdtr)); 106 | } -------------------------------------------------------------------------------- /src/real_mode/Makefile.mk: -------------------------------------------------------------------------------- 1 | $(BUILD_REALMODE)/static_library: $(SRC_REALMODE)/static_library.asm 2 | mkdir -p $(BUILD_REALMODE)/ 3 | nasm -o $@ -i include/ -f bin $(SRC_REALMODE)/static_library.asm 4 | truncate --size=%512 $@ 5 | 6 | $(SELF_BUILD_DIR)/%_asm.o: $(SELF_SRC_DIR)/%.asm $(SELF_INCLUDE_DIR)/%.h 7 | mkdir -p $(dir $@) 8 | $(NASM) -o $@ $< 9 | 10 | $(SELF_BUILD_DIR)/librealmodeclient: $(SELF_BUILD_DIR)/client_asm.o 11 | ar rc $@ $^ 12 | 13 | -------------------------------------------------------------------------------- /src/real_mode/client.asm: -------------------------------------------------------------------------------- 1 | %include "fuzzy/memmgr/tables/gdt.asm" 2 | 3 | [BITS 32] 4 | 5 | extern reload_idt_table 6 | global real_mode_client 7 | 8 | [SECTION .text] 9 | real_mode_client: 10 | push ebp 11 | mov ebp, esp 12 | ; callee save register 13 | push ebx 14 | push esi 15 | push edi 16 | 17 | ; Assumption: SS == DS 18 | ; DS will be changed. 19 | ; DO_NOT_USE stack here. 20 | pushf 21 | push ds 22 | push es 23 | push fs 24 | push gs 25 | CLI 26 | ; reset all segment register. 27 | 28 | mov eax, ss 29 | mov ds, eax ; use old stack using ds 30 | ; create stack for real mode here. 31 | mov ebx, ss 32 | mov eax, GDT_ABS16_DS ; Absolute Data Segment Selector 33 | mov ss, eax 34 | mov eax, esp 35 | mov esp, 0x7DFC 36 | push ebx ; push old ss to new stack 37 | push eax ; push old esp in new stack 38 | 39 | 40 | ; push all value to be poped in real mode 41 | mov ecx, 0 42 | mov esi, ebp 43 | mov ebx, [ds:esi + 28] ; (arg5:es) 44 | 45 | push ecx ; realmode: ds 46 | push ebx ; realmode: es 47 | push ecx ; realmode: fs 48 | push ecx ; realmode: gs 49 | 50 | mov eax, [ds:esi + 12] ; (arg1:eax) 51 | mov ebx, [ds:esi + 16] ; (arg2:ebx) 52 | mov ecx, [ds:esi + 20] ; (arg3:ecx) 53 | mov edx, [ds:esi + 24] ; (arg4:edx) 54 | 55 | push eax ; realmode: ax 56 | push ebx ; realmode: bx 57 | push ecx ; realmode: cx 58 | push edx ; realmode: dx 59 | 60 | mov eax, [ds:esi + 8] ; (arg0:interrupt) 61 | push eax 62 | 63 | ; Note: Make sure these line have IP representable 64 | ; in 2 bytes. 65 | call GDT_ABS16_CS:0x7E00 ; execute_0x13_enter_real_mode 66 | add esp, 36 67 | 68 | ; revert back to original stack 69 | pop ebx 70 | pop ecx 71 | mov esp, ebx 72 | mov ss, ecx 73 | 74 | pop gs 75 | pop fs 76 | pop es 77 | pop ds 78 | push eax 79 | call reload_idt_table 80 | pop eax 81 | popf 82 | 83 | pop edi 84 | pop esi 85 | pop ebx 86 | 87 | pop ebp 88 | ret -------------------------------------------------------------------------------- /src/real_mode/static_library.asm: -------------------------------------------------------------------------------- 1 | %include "fuzzy/memmgr/tables/gdt.asm" 2 | 3 | ; Code must be within 512 bytes. 4 | [ORG 0x7E00] 5 | [BITS 16] 6 | 7 | [SECTION .text] 8 | ; convert 4+4 byte CS:IP address into 9 | ; 2+2 byte CS:IP address 10 | pop eax 11 | pop ebx 12 | push bx 13 | push ax 14 | jmp execute_int_enter_real_mode 15 | 16 | execute_int_enter_real_mode: 17 | ; interrupts should already be disabled 18 | ; meant to be executed serially. 19 | ; mov ss, 0 ; Hack: Prerequisite 20 | 21 | ; enter real mode 22 | mov ebp, esp 23 | push ss ; save old absolute stack 24 | mov eax, cr0 25 | and eax, 0xFFFFFFFE 26 | mov cr0, eax 27 | jmp GDT_NULL_CS:execute_int_from_real_mode 28 | 29 | execute_int_from_real_mode: 30 | ; fix stack 31 | mov eax, 0 32 | mov ss, ax 33 | mov ds, ax ; for loading IDT table 34 | lidt [idt_table_real_mode] 35 | 36 | ; minimal setup for real mode as required. 37 | mov ds, [ebp+36] 38 | mov es, [ebp+32] 39 | mov fs, [ebp+28] 40 | mov gs, [ebp+24] 41 | 42 | mov eax, [ebp+20] 43 | mov ebx, [ebp+16] 44 | mov ecx, [ebp+12] 45 | mov edx, [ebp+8] 46 | 47 | ; interrupt number 48 | mov esi, [ebp+4] ; 4 byte for far call 49 | 50 | ; check for some interrupt numbers only 51 | cmp esi, 0x10 52 | je _int_0x10 53 | cmp esi, 0x13 54 | je _int_0x13 55 | 56 | jmp _int_end ; found no match 57 | 58 | _int_0x10: 59 | int 0x10 60 | jmp _int_end 61 | 62 | _int_0x13: 63 | int 0x13 64 | jmp _int_end 65 | 66 | _int_end: 67 | push eax 68 | mov eax, cr0 69 | or eax, 0x00000001 70 | mov cr0, eax 71 | pop eax 72 | 73 | pop ss; restore absolute stack 74 | 75 | jmp GDT_ABS16_CS:execute_int_leave_real_mode ; Absolute Code Segment Selector 76 | 77 | execute_int_leave_real_mode: 78 | retf 79 | 80 | [SECTION .data] 81 | idt_table_real_mode dw 0x3ff 82 | db 0 -------------------------------------------------------------------------------- /src/usr/include/Makefile.mk: -------------------------------------------------------------------------------- 1 | SRC_USR_INCLUDE=$(SRC_DIR)/usr/include 2 | BUILD_USR_INCLUDE=$(BUILD_DIR)/usr/include 3 | BUILD_USR_INCLUDE_ALL = $(patsubst $(SRC_USR_INCLUDE)/%.h,$(BUILD_USR_INCLUDE)/%.h,$(shell find $(SRC_USR_INCLUDE) -name '*.h')) 4 | 5 | $(BUILD_USR_INCLUDE)/%.h: $(SRC_USR_INCLUDE)/%.h 6 | mkdir -p $(dir $@) 7 | cp $< $@ 8 | 9 | -------------------------------------------------------------------------------- /src/usr/include/conio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | #endif 7 | 8 | #define SYSCALL_KEYBOARD_SUB_GETCH 0 9 | #define SYSCALL_KEYBOARD_SUB_KBHIT 1 10 | 11 | int getch(); 12 | int kbhit(); 13 | 14 | void clrscr(); 15 | 16 | #ifdef __cplusplus 17 | } // namespace std end 18 | } // extern C end 19 | #endif 20 | -------------------------------------------------------------------------------- /src/usr/include/ctype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | #endif 7 | 8 | int tolower(int c); 9 | 10 | #ifdef __cplusplus 11 | } // namespace std end 12 | } // extern C end 13 | #endif 14 | -------------------------------------------------------------------------------- /src/usr/include/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | namespace std { 8 | #endif 9 | 10 | #define FILENAME_LIMIT 100 // same as fuzzy/fs/ffs.h 11 | 12 | enum dirent_flag { DIRENT_EXECUTABLE = 1 << 0 }; 13 | 14 | struct dirent { 15 | // In FFS the only file types are only normal file. 16 | char d_name[FILENAME_LIMIT]; 17 | uint32_t size; 18 | uint32_t flag; 19 | }; 20 | 21 | struct DIR { 22 | struct dirent entry; 23 | int kernel_file_id; // iterator 24 | }; 25 | 26 | // There is only one global directory in FFS. 27 | void opendir(struct DIR *dirp); 28 | struct dirent *readdir(struct DIR *dirp); 29 | 30 | #ifdef __cplusplus 31 | } // namespace std end 32 | } // extern C end 33 | #endif 34 | -------------------------------------------------------------------------------- /src/usr/include/graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | namespace graphics { 7 | #endif 8 | 9 | #define GRAPHICS_MAX_WIDTH 320 10 | #define GRAPHICS_MAX_HEIGHT 200 11 | 12 | #define DETECT 0 13 | #define GRAPHIC_DRIVER_VGA 1 14 | 15 | #define GRAPHIC_MODE_320x200x256 0x13 16 | 17 | #define BLACK 0x0 18 | #define BLUE 0x1 19 | #define GREEN 0x2 20 | #define CYAN 0x3 21 | #define RED 0x4 22 | #define MAGENTA 0x5 23 | #define BROWN 0x6 24 | #define LIGHT_GRAY 0x7 25 | #define DARK_GRAY 0x8 26 | #define LIGHT_BLUE 0x9 27 | #define LIGHT_GREEN 0xA 28 | #define LIGHT_CYAN 0xB 29 | #define LIGHT_RED 0xC 30 | #define LIGHT_MAGENTA 0xD 31 | #define YELLOW 0xE 32 | #define WHITE 0xF 33 | 34 | #define SYSCALL_GRAPHICS_INITGRAPH 0 35 | #define SYSCALL_GRAPHICS_CLOSEGRAPH 1 36 | #define SYSCALL_GRAPHICS_COPYBUFFER 2 37 | 38 | void initgraph(int *graphdetect, int *graphmode, char *not_used); 39 | int graphresult(); 40 | void closegraph(); 41 | void cleardevice(); 42 | 43 | void graphautoflush_enable(); 44 | void graphautoflush_disable(); 45 | int graphflush(); 46 | 47 | void setcolor(int color); 48 | int getcolor(); 49 | void setbkcolor(int color); 50 | int getbkcolor(); 51 | 52 | void putpixel(int x, int y, int color); 53 | 54 | void line(int x1, int y1, int x2, int y2); 55 | void rectangle(int left, int top, int right, int bottom); 56 | void bar(int left, int top, int right, int bottom); 57 | void fillellipse(int xcenter, int ycenter, int x_radius, int y_radius); 58 | 59 | int textheight(const char *str); 60 | int textwidth(const char *str); 61 | void outtext(const char *str); 62 | int outtextxy(int x, int y, const char *str); 63 | void moveto(int x, int y); 64 | int getx(); 65 | int gety(); 66 | 67 | #ifdef __cplusplus 68 | } // namespace graphics end 69 | } // namespace std end 70 | } // extern C end 71 | #endif 72 | -------------------------------------------------------------------------------- /src/usr/include/iostream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std { 6 | 7 | // treat endl as character 8 | extern const char endl; 9 | 10 | class ostream { 11 | public: 12 | ostream &operator<<(const char c); 13 | ostream &operator<<(const int num); 14 | ostream &operator<<(const double num); 15 | ostream &operator<<(const std::string &str); 16 | ostream &operator<<(const char *str); 17 | }; 18 | 19 | class istream { 20 | public: 21 | istream &operator>>(char &c); 22 | istream &operator>>(char *str); 23 | istream &operator>>(std::string &str); 24 | istream &operator>>(int &x); 25 | 26 | istream &get(char &c); 27 | istream &get_line(char *str, std::size_t n, char delim = '\n'); 28 | }; 29 | 30 | extern ostream cout; 31 | extern istream cin; 32 | 33 | std::istream &get_line(std::istream &i, std::string &str); 34 | 35 | } // namespace std -------------------------------------------------------------------------------- /src/usr/include/limits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LLONG_MAX 9223372036854775807LL 4 | #define LLONG_MIN (-LLONG_MAX - 1) 5 | #define ULLONG_MAX ((unsigned long)(((unsigned long)LLONG_MAX) * 2ULL + 1)) 6 | 7 | #define INT_MAX 2147483647 8 | #define INT_MIN (-INT_MAX - 1) 9 | #define UINT_MAX ((unsigned int)(((unsigned int)INT_MAX) * 2U + 1)) 10 | -------------------------------------------------------------------------------- /src/usr/include/math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define M_PI 3.14159265358979323846 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | namespace std { 8 | #endif 9 | 10 | int isnan(double x); 11 | double floor(double x); 12 | double round(double x); 13 | double fmod(double x, double y); 14 | double fabs(double x); 15 | 16 | double sin(double x); 17 | double cos(double x); 18 | 19 | #ifdef __cplusplus 20 | } // namespace std end 21 | } // extern C end 22 | #endif 23 | -------------------------------------------------------------------------------- /src/usr/include/new.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void *operator new(std::size_t size); 6 | void *operator new[](std::size_t size); 7 | void operator delete(void *ptr); 8 | void operator delete[](void *ptr); 9 | 10 | void *operator new(std::size_t size, void *); 11 | -------------------------------------------------------------------------------- /src/usr/include/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | #endif 7 | 8 | #define SYSCALL_PROCESS_SUB_EXIT 0 9 | #define SYSCALL_PROCESS_SUB_WAIT 1 10 | #define SYSCALL_PROCESS_SUB_FORK 2 11 | #define SYSCALL_PROCESS_SUB_SPAWN_FNAME 3 12 | #define SYSCALL_PROCESS_SUB_GET 4 13 | 14 | #define PROCESS_MAX_ARGC 6 15 | #define PROCESS_MAX_ARG_LEN 32 16 | 17 | #define SYSCALL_PROCESS_SUB_FORK_MARK_READY 0 18 | #define SYSCALL_PROCESS_SUB_FORK_CHECK_READY 1 19 | 20 | #define SYSCALL_PROCESS_SUB_GET_PID 0 21 | 22 | // if non-negative value is returned 23 | // then it's the pid of the new process 24 | // otherwise it's some error code 25 | int spawnl(const char *path, const char *arg0, ...); 26 | int spawnv(const char *path, const char *argv[]); 27 | 28 | int getpid(); 29 | 30 | int fork(); 31 | void yield(); 32 | int waitpid(unsigned int blocked_on_pid, int *exit_code); 33 | 34 | #ifdef __cplusplus 35 | } // namespace std end 36 | } // extern C end 37 | #endif 38 | -------------------------------------------------------------------------------- /src/usr/include/stdarg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | #endif 7 | 8 | typedef struct va_list { 9 | void *base; 10 | } va_list; 11 | 12 | #define va_start(list, last) ((list).base = ((void *)(&(last) + 1))) 13 | #define va_end(list) (list).base = NULL 14 | #define va_arg(list, type) \ 15 | (*(type *)((list).base += sizeof(type), \ 16 | (list) \ 17 | .base - \ 18 | sizeof(type))) 19 | #define va_copy(dst, src) (dst).base = (src).base 20 | 21 | #ifdef __cplusplus 22 | } // namespace std end 23 | } // extern C end 24 | #endif 25 | -------------------------------------------------------------------------------- /src/usr/include/stddef.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | #endif 7 | 8 | #ifndef NULL 9 | #define NULL 0 10 | #endif 11 | 12 | typedef char int8_t; 13 | typedef short int16_t; 14 | typedef int int32_t; 15 | 16 | typedef unsigned char uint8_t; 17 | typedef unsigned short uint16_t; 18 | typedef unsigned int uint32_t; 19 | 20 | typedef uint32_t size_t; 21 | 22 | #ifdef __cplusplus 23 | } // namespace std end 24 | } // extern C end 25 | #endif 26 | -------------------------------------------------------------------------------- /src/usr/include/stdint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | namespace std { 6 | #endif 7 | 8 | typedef int int32_t; 9 | 10 | #ifdef __cplusplus 11 | } // namespace std end 12 | } // extern C end 13 | #endif 14 | -------------------------------------------------------------------------------- /src/usr/include/stdio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | namespace std { 8 | #endif 9 | 10 | #define SYSCALL_CONSOLE_SUB_CLRSCR 0 11 | #define SYSCALL_CONSOLE_SUB_PUTCHAR 1 12 | #define SYSCALL_CONSOLE_SUB_PUTS_BUFFER 2 13 | 14 | int putchar(int c); 15 | int puts(const char *s); 16 | 17 | int snprintf(char *s, size_t n, const char *fmt, ...); 18 | int printf(const char *strfmt, ...); 19 | 20 | char *gets(char *s); 21 | 22 | typedef struct FILE { 23 | // in user space. 24 | int file_handler_id; 25 | // in kernel/filesystem space 26 | int file_id; 27 | 28 | // 0 => start of file 29 | int cursor; 30 | 31 | // store last error code 32 | int err; 33 | } FILE; 34 | 35 | // TODO: Add API for reading list of files. 36 | // Only readonly mode is supported. 37 | #define SYSCALL_FILE_SUB_OPEN 0 38 | #define SYSCALL_FILE_SUB_READBUFFER 1 39 | #define SYSCALL_FILE_SUB_READ_DIR 2 40 | 41 | // We are allocating FILE handler as global because we 42 | // don't have heap memory manager yet. 43 | #define LIMIT_MAX_FILE_OPEN 10 44 | // same as sector size for better performance 45 | #define FILEIO_BUFFER_SIZE 512 46 | 47 | #define EOF (-1) 48 | 49 | FILE *fopen(char *filename, char *mode); 50 | int fgetc(FILE *file); 51 | char *fgets(char *buf, size_t n, FILE *file); 52 | int fclose(FILE *file); 53 | 54 | int ferror(FILE *file); 55 | void clearerror(FILE *file); 56 | 57 | #ifdef __cplusplus 58 | } // namespace std end 59 | } // extern C end 60 | #endif 61 | -------------------------------------------------------------------------------- /src/usr/include/stdlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | namespace std { 8 | #endif 9 | 10 | int atoi(const char *s); 11 | 12 | // only base 2, 8, 10 and 16 are supported 13 | // for others the result can be undefined. 14 | // And only base 10 is considered as signed int. 15 | void itoa(int num, char *s, int base); 16 | void ftoa(double num, char *s, int afterpoint); 17 | int min(int, int); 18 | int max(int, int); 19 | int abs(int a); 20 | 21 | int benchmark_get_heap_usage(); 22 | int benchmark_get_heap_area(); 23 | void *malloc(size_t size); 24 | void *realloc(void *ptr, size_t size); 25 | void free(void *ptr); 26 | 27 | void atexit(void (*)(void)); 28 | void exit(int status); 29 | 30 | #ifdef __cplusplus 31 | } // namespace std end 32 | } // extern C end 33 | #endif 34 | -------------------------------------------------------------------------------- /src/usr/include/sys/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SYSCALL_KEYBOARD 0 4 | #define SYSCALL_PROCESS 1 5 | #define SYSCALL_FILE_OP 2 6 | #define SYSCALL_CONSOLE 3 7 | #define SYSCALL_GRAPHICS 4 8 | 9 | #define SYSCALL_A0(id) syscall((id), 0, 0, 0, 0) 10 | #define SYSCALL_A1(id, a0) syscall((id), (a0), 0, 0, 0) 11 | #define SYSCALL_A2(id, a0, a1) syscall((id), (a0), (a1), 0, 0) 12 | #define SYSCALL_A3(id, a0, a1, a2) syscall((id), (a0), (a1), (a2), 0) 13 | #define SYSCALL_A4(id, a0, a1, a2, a3) syscall((id), (a0), (a1), (a2), (a3)) 14 | 15 | extern int syscall(int id, int arg0, int arg1, int arg2, int arg3); 16 | -------------------------------------------------------------------------------- /src/usr/include/testing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | namespace { 7 | #endif 8 | 9 | // these tests are meant to be executed sequentially. 10 | 11 | static int test_task_failed; 12 | static int test_count_success; 13 | static int test_count_failure; 14 | 15 | #define __ASSERT(msg, x) \ 16 | do { \ 17 | if (!(x)) { \ 18 | test_task_failed = 1; \ 19 | std::printf("[%d]: %s failed.\n", __LINE__, (msg)); \ 20 | } \ 21 | } while (0); 22 | #define ASSERT_TRUE(x) __ASSERT("ASSERT_TRUE(" #x ")", (x)) 23 | #define ASSERT_FALSE(x) __ASSERT("ASSERT_FALSE(" #x ")", (!(x))) 24 | #define ASSERT_EQ(x, y) __ASSERT("ASSERT_EQ(" #x "," #y ")", (x) == (y)) 25 | #define ASSERT_NE(x, y) __ASSERT("ASSERT_NE(" #x "," #y ")", (x) != (y)) 26 | 27 | #define RUN_TEST(test_name) \ 28 | do { \ 29 | test_task_failed = 0; \ 30 | test_name(); \ 31 | if (test_task_failed) { \ 32 | test_count_failure++; \ 33 | std::puts("Test failed: " #test_name "\n"); \ 34 | } else { \ 35 | test_count_success++; \ 36 | std::puts("Test passed: " #test_name "\n"); \ 37 | } \ 38 | } while (0) 39 | 40 | #define TEST_INIT() \ 41 | do { \ 42 | test_count_success = 0; \ 43 | test_count_failure = 0; \ 44 | } while (0) 45 | #define TEST_SUMMARY() \ 46 | do { \ 47 | std::printf("[%s] TEST_SUCCESS_COUNT: %d\n", __FILE__, \ 48 | test_count_success); \ 49 | std::printf("[%s] TEST_FAILURE_COUNT: %d\n", __FILE__, \ 50 | test_count_failure); \ 51 | } while (0) 52 | 53 | #ifdef __cplusplus 54 | } // namespace end 55 | #endif 56 | -------------------------------------------------------------------------------- /src/usr/include/utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace std { 4 | 5 | template class pair { 6 | public: 7 | T1 first; 8 | T2 second; 9 | pair(); 10 | pair(const T1 &first, const T2 &second); 11 | }; 12 | 13 | template 14 | inline pair make_pair(const T1 &a, const T2 &b) { 15 | return pair(a, b); 16 | } 17 | 18 | } // namespace std 19 | 20 | #include 21 | -------------------------------------------------------------------------------- /src/usr/include/utility.tcc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This is internal headerfile to be included by utility.h only 4 | 5 | #include 6 | 7 | namespace std { 8 | 9 | template pair::pair() {} 10 | 11 | template 12 | pair::pair(const T1 &first, const T2 &second) 13 | : first(first), second(second) {} 14 | 15 | } // namespace std 16 | -------------------------------------------------------------------------------- /src/usr/include/vector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std { 6 | 7 | template class vector { 8 | std::size_t _capacity, _size; 9 | T *_data; 10 | 11 | public: 12 | typedef T *iterator; 13 | typedef const T *const_iterator; 14 | 15 | vector(); 16 | vector(std::size_t size); 17 | vector(std::size_t size, const T &_default); 18 | vector(const vector &o); 19 | ~vector(); 20 | 21 | vector &operator=(const vector &o); 22 | 23 | void reserve(std::size_t n); 24 | void resize(std::size_t new_size); 25 | void resize(std::size_t new_size, const T &_default); 26 | void push_back(const T &val); 27 | void pop_back(); 28 | 29 | void clear(); 30 | 31 | iterator begin(); 32 | iterator end(); 33 | const_iterator begin() const; 34 | const_iterator end() const; 35 | 36 | bool empty() const; 37 | std::size_t size() const; 38 | 39 | T &back(); 40 | T &front(); 41 | T &at(std::size_t pos); 42 | T &operator[](std::size_t pos); 43 | const T &operator[](std::size_t pos) const; 44 | }; 45 | 46 | } // namespace std 47 | 48 | template 49 | inline bool operator!=(const std::vector &a, const std::vector b) { 50 | if (a.size() != b.size()) 51 | return true; 52 | for (std::size_t i = 0; i < a.size(); i++) { 53 | if (a[i] != b[i]) 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | template 60 | inline bool operator==(const std::vector &a, const std::vector b) { 61 | return !(a != b); 62 | } 63 | 64 | #include -------------------------------------------------------------------------------- /src/usr/lib/Makefile.mk: -------------------------------------------------------------------------------- 1 | SRC_USR_LIB=$(SRC_DIR)/usr/lib 2 | BUILD_USR_LIB=$(BUILD_DIR)/usr/lib 3 | 4 | $(BUILD_USR_LIB)/%.o: $(SRC_USR_LIB)/%.c $(BUILD_USR_INCLUDE_ALL) 5 | mkdir -p $(dir $@) 6 | $(KERNEL_CC) -c -o $@ $< 7 | 8 | $(BUILD_USR_LIB)/%.o: $(SRC_USR_LIB)/%.cpp $(BUILD_USR_INCLUDE_ALL) 9 | mkdir -p $(dir $@) 10 | $(KERNEL_CPP) -c -o $@ $< 11 | 12 | $(BUILD_USR_LIB)/%_asm.o: $(SRC_USR_LIB)/%.asm 13 | mkdir -p $(dir $@) 14 | nasm -o $@ -f elf32 $< 15 | 16 | $(BUILD_USR_LIB)/libfuzzyc: $(patsubst $(SRC_USR_LIB)/%.c,$(BUILD_USR_LIB)/%.o,$(shell find $(SRC_USR_LIB) -name '*.c')) \ 17 | $(patsubst $(SRC_USR_LIB)/%.asm,$(BUILD_USR_LIB)/%_asm.o,$(shell find $(SRC_USR_LIB) -name '*.asm')) 18 | ar rc $@ $^ 19 | 20 | $(BUILD_USR_LIB)/libfuzzycpp: $(patsubst $(SRC_USR_LIB)/%.cpp,$(BUILD_USR_LIB)/%.o,$(shell find $(SRC_USR_LIB) -name '*.cpp')) 21 | ar rc $@ $^ 22 | -------------------------------------------------------------------------------- /src/usr/lib/conio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int getch() { 6 | // syscall is irq blocking, 7 | // so try to keep them as short as possible. 8 | while (1) { 9 | char c = SYSCALL_A1(SYSCALL_KEYBOARD, SYSCALL_KEYBOARD_SUB_GETCH); 10 | if (c) 11 | return c; 12 | yield(); 13 | } 14 | } 15 | 16 | int kbhit() { return SYSCALL_A1(SYSCALL_KEYBOARD, SYSCALL_KEYBOARD_SUB_KBHIT); } 17 | 18 | void clrscr() { SYSCALL_A1(SYSCALL_CONSOLE, SYSCALL_CONSOLE_SUB_CLRSCR); } 19 | -------------------------------------------------------------------------------- /src/usr/lib/ctype.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int tolower(int x) { 4 | if (x >= 'A' && x <= 'Z') { 5 | return x - 'A' + 'a'; 6 | } 7 | return x; 8 | } -------------------------------------------------------------------------------- /src/usr/lib/dirent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void opendir(struct DIR *dirp) { dirp->kernel_file_id = -1; } 6 | 7 | struct dirent *readdir(struct DIR *dirp) { 8 | dirp->kernel_file_id = 9 | SYSCALL_A3(SYSCALL_FILE_OP, SYSCALL_FILE_SUB_READ_DIR, 10 | dirp->kernel_file_id, &dirp->entry); 11 | if (dirp->kernel_file_id == -1) { 12 | return NULL; 13 | } 14 | return &dirp->entry; 15 | } 16 | -------------------------------------------------------------------------------- /src/usr/lib/iostream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace std { 6 | #define __IS_WHITESPACE(ch) ((ch) == ' ' || (ch) == '\n' || (ch) == '\t') 7 | 8 | const char endl = '\n'; 9 | ostream cout; 10 | istream cin; 11 | 12 | ostream &ostream::operator<<(const char c) { 13 | putchar(c); 14 | return *this; 15 | } 16 | ostream &ostream::operator<<(const int num) { 17 | printf("%d", num); 18 | return *this; 19 | } 20 | ostream &ostream::operator<<(const double num) { 21 | printf("%f", num); 22 | return *this; 23 | } 24 | ostream &ostream::operator<<(const char *str) { 25 | puts(str); 26 | return *this; 27 | } 28 | ostream &ostream::operator<<(const std::string &str) { 29 | puts(str.c_str()); 30 | return *this; 31 | } 32 | 33 | istream &istream::operator>>(char &c) { 34 | do { 35 | this->get(c); 36 | } while (__IS_WHITESPACE(c)); 37 | return *this; 38 | } 39 | 40 | istream &istream::operator>>(char *str) { 41 | char c; 42 | bool leading_whitespace = 1; 43 | while (1) { 44 | this->get(c); 45 | if (leading_whitespace) { 46 | if (__IS_WHITESPACE(c)) 47 | continue; 48 | leading_whitespace = 0; 49 | } else { 50 | // from second non-whitespace character 51 | if (__IS_WHITESPACE(c)) 52 | break; 53 | } 54 | *(str++) = c; 55 | }; 56 | *(str++) = '\0'; 57 | return *this; 58 | } 59 | 60 | istream &istream::operator>>(std::string &str) { 61 | str.clear(); 62 | char c; 63 | bool leading_whitespace = 1; 64 | while (1) { 65 | this->get(c); 66 | if (leading_whitespace) { 67 | if (__IS_WHITESPACE(c)) 68 | continue; 69 | leading_whitespace = 0; 70 | } else { 71 | // from second non-whitespace character 72 | if (__IS_WHITESPACE(c)) 73 | break; 74 | } 75 | str += c; 76 | }; 77 | return *this; 78 | } 79 | 80 | istream &istream::operator>>(int &x) { 81 | std::string s; 82 | (*this) >> s; 83 | x = std::atoi(s.c_str()); 84 | return *this; 85 | } 86 | 87 | istream &istream::get(char &c) { 88 | c = getch(); 89 | putchar(c); 90 | return *this; 91 | } 92 | 93 | istream &istream::get_line(char *str, std::size_t n, char delim) { 94 | char c; 95 | while (n-- > 0) { 96 | this->get(c); 97 | if (c == delim) 98 | break; 99 | *(str++) = c; 100 | } 101 | *(str) = '\0'; 102 | return *this; 103 | } 104 | 105 | std::istream &get_line(std::istream &i, std::string &str) { 106 | char ch; 107 | str.clear(); 108 | while (1) { 109 | i.get(ch); 110 | if (ch == '\n') 111 | break; 112 | str += ch; 113 | } 114 | } 115 | 116 | #undef __IS_WHITESPACE 117 | } // namespace std 118 | -------------------------------------------------------------------------------- /src/usr/lib/math.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int isnan(double x) { return x != x; } 5 | 6 | double floor(double x) { 7 | if (x >= LLONG_MAX || x <= LLONG_MIN || isnan(x)) { 8 | // x doesn't have any fractional digits in significant part 9 | return x; 10 | } 11 | long long xl = ((long long)x); 12 | if (x >= 0) { 13 | return xl; 14 | } else { 15 | if (x == xl) { 16 | return xl; 17 | } else { 18 | return xl - 1; 19 | } 20 | } 21 | } 22 | 23 | double round(double x) { return floor(x + 0.5); } 24 | 25 | double fmod(double x, double y) { return x - floor(x / y) * y; } 26 | 27 | double fabs(double x) { return (x >= 0) ? x : -x; } 28 | 29 | double sin(double x) { 30 | const int terms = 32; 31 | const double x2 = x * x; 32 | double r = x; // first term short-circuited 33 | double term = x; 34 | for (int i = 1, deno = 2; i < terms; i++, deno += 2) { 35 | term = (-term * x2) / (deno * (deno + 1)); 36 | r += term; 37 | } 38 | return r; 39 | } 40 | 41 | double cos(double x) { return sin(M_PI / 2 - x); } 42 | -------------------------------------------------------------------------------- /src/usr/lib/new.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void *operator new(std::size_t size) { return std::malloc(size); } 6 | 7 | void *operator new[](std::size_t size) { return ::operator new(size); } 8 | 9 | void operator delete(void *ptr) { std::free(ptr); } 10 | 11 | void operator delete[](void *ptr) { ::operator delete(ptr); } 12 | 13 | void *operator new(std::size_t s, void *ptr) { return ptr; } 14 | -------------------------------------------------------------------------------- /src/usr/lib/process.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int spawnl(const char *file_path, const char *arg0, ...) { 7 | va_list args; 8 | va_start(args, arg0); 9 | const char *argv[PROCESS_MAX_ARGC] = {NULL}; 10 | 11 | // prepare argv 12 | { 13 | argv[0] = arg0; 14 | int i = 1; 15 | while (i < PROCESS_MAX_ARGC - 2) { 16 | char *arg = va_arg(args, char *); 17 | if (arg == NULL) 18 | break; 19 | argv[i] = arg; 20 | i++; 21 | } 22 | } 23 | 24 | int rv = _spawnv_syscall(file_path, argv); 25 | va_end(args); 26 | return rv; 27 | } 28 | 29 | int spawnv(const char *file_path, const char *argv[]) { 30 | // kernel expects argv size must be PROCESS_MAX_ARGC 31 | const char *argv_resized[PROCESS_MAX_ARGC] = {NULL}; 32 | for (int i = 0; i < PROCESS_MAX_ARGC - 1; i++) { 33 | if (argv[i] == NULL) 34 | break; 35 | argv_resized[i] = argv[i]; 36 | } 37 | 38 | return _spawnv_syscall(file_path, argv_resized); 39 | } 40 | 41 | int _spawnv_syscall(const char *file_path, const char *argv[]) { 42 | // kernel expects argv size must be PROCESS_MAX_ARGC 43 | int pid = SYSCALL_A3(SYSCALL_PROCESS, SYSCALL_PROCESS_SUB_SPAWN_FNAME, 44 | file_path, argv); 45 | return pid; 46 | } 47 | 48 | void yield() { __asm__("int $0x20"); } 49 | 50 | int waitpid(unsigned int blocked_on_pid, int *exit_code) { 51 | while (1) { 52 | int status = SYSCALL_A3(SYSCALL_PROCESS, SYSCALL_PROCESS_SUB_WAIT, 53 | blocked_on_pid, exit_code); 54 | if (status < 0) { 55 | // err 56 | return status; 57 | } 58 | if (status == 0) { 59 | // wait over 60 | return 0; 61 | } 62 | // keep waiting 63 | yield(); 64 | } 65 | } 66 | 67 | int getpid() { 68 | return SYSCALL_A2(SYSCALL_PROCESS, SYSCALL_PROCESS_SUB_GET, 69 | SYSCALL_PROCESS_SUB_GET_PID); 70 | } 71 | 72 | int fork() { 73 | int mark_it = SYSCALL_A2(SYSCALL_PROCESS, SYSCALL_PROCESS_SUB_FORK, 74 | SYSCALL_PROCESS_SUB_FORK_MARK_READY); 75 | if (mark_it != 0) { 76 | // failed to mark process as fork ready, maybe it's already marked. 77 | return mark_it; 78 | } 79 | // fork requested. 80 | // let's process scheduler fork the job. 81 | yield(); 82 | 83 | // fork request should be complete by now. 84 | int status = SYSCALL_A2(SYSCALL_PROCESS, SYSCALL_PROCESS_SUB_FORK, 85 | SYSCALL_PROCESS_SUB_FORK_CHECK_READY); 86 | if (status < 0) { 87 | // request either failed 88 | // or still waiting: it's a bad state, but we are going to let that 89 | // slide. 90 | return status; 91 | } 92 | // fork successful. 93 | int child_pid = status; 94 | int my_pid = getpid(); 95 | if (my_pid == child_pid) { 96 | // child process 97 | return 0; 98 | } 99 | // parent process 100 | return child_pid; 101 | } -------------------------------------------------------------------------------- /src/usr/lib/stdlib.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global get_current_esp 4 | 5 | [SECTION .text] 6 | 7 | get_current_esp: 8 | mov eax, esp 9 | ret 10 | -------------------------------------------------------------------------------- /src/usr/lib/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int strcmp(const char *l, const char *r) { 6 | int i = 0, j = 0; 7 | while (l[i] != '\0' && r[j] != '\0') { 8 | if (l[i] != r[j]) { 9 | return l[i] - r[j]; 10 | } 11 | i++; 12 | j++; 13 | } 14 | if (l[i] == '\0' && r[j] == '\0') { 15 | return 0; 16 | } 17 | if (l[i] == '\0') { 18 | return -1; 19 | } 20 | return 1; 21 | } 22 | 23 | int strcmpi(const char *l, const char *r) { 24 | int i = 0, j = 0; 25 | while (l[i] != '\0' && r[j] != '\0') { 26 | char a = tolower(l[i]); 27 | char b = tolower(r[j]); 28 | if (a != b) { 29 | return a - b; 30 | } 31 | i++; 32 | j++; 33 | } 34 | if (l[i] == '\0' && r[j] == '\0') { 35 | return 0; 36 | } 37 | if (l[i] == '\0') { 38 | return -1; 39 | } 40 | return 1; 41 | } 42 | 43 | int strlen(const char *str) { 44 | int n = 0; 45 | while ((*str) != '\0') { 46 | n++; 47 | str++; 48 | } 49 | return n; 50 | } 51 | 52 | char *strcpy(char *dst, const char *src) { 53 | void *og_dst = dst; 54 | while ((*src) != NULL) { 55 | *(dst++) = *(src++); 56 | } 57 | *(dst++) = NULL; 58 | return og_dst; 59 | } 60 | 61 | char *strncpy(char *dst, const char *src, size_t n) { 62 | void *og_dst = dst; 63 | while ((*src) != NULL && n > 0) { 64 | *(dst++) = *(src++); 65 | n--; 66 | } 67 | return og_dst; 68 | } 69 | 70 | void *memcpy(void *dst, const void *src, size_t n) { 71 | void *og_dst = dst; 72 | while (n--) { 73 | *(char *)(dst++) = *(char *)(src++); 74 | } 75 | return og_dst; 76 | } 77 | 78 | void *memset(void *dst, const unsigned char c, size_t n) { 79 | void *og_dst = dst; 80 | while (n--) { 81 | *(char *)(dst++) = c; 82 | } 83 | return og_dst; 84 | } 85 | 86 | char *strchr(const char *str, char ch) { 87 | while ((*str) != NULL) { 88 | if ((*str) == ch) { 89 | return str; 90 | } 91 | str++; 92 | } 93 | return NULL; 94 | } 95 | 96 | char *strtok(char *str, const char *delim) { 97 | static char *last_it = NULL; 98 | static char last_char_stash; 99 | char *it; 100 | if (str == NULL) { 101 | it = last_it; 102 | *(it) = last_char_stash; 103 | if ((*it) == NULL) { 104 | // no more token left 105 | return NULL; 106 | } 107 | it++; 108 | } else { 109 | it = str; 110 | if ((*it) == NULL) { 111 | // empty string 112 | return NULL; 113 | } 114 | } 115 | 116 | char *end = it; 117 | while ((*end) != NULL && strchr(delim, *end) == NULL) { 118 | end++; 119 | } 120 | 121 | last_char_stash = *end; 122 | last_it = end; 123 | *end = NULL; 124 | return it; 125 | } -------------------------------------------------------------------------------- /src/usr/lib/sys/syscall.asm: -------------------------------------------------------------------------------- 1 | [BITS 32] 2 | 3 | global syscall 4 | 5 | [SECTION .text] 6 | 7 | syscall: 8 | push ebp 9 | mov ebp, esp 10 | ; callee save register 11 | push ebx 12 | push esi 13 | push edi 14 | 15 | mov ebx, [ebp + 0x08] ; (id) 16 | mov ecx, [ebp + 0x0c] ; (arg0) 17 | mov edx, [ebp + 0x10] ; (arg1) 18 | mov esi, [ebp + 0x14] ; (arg2) 19 | mov edi, [ebp + 0x18] ; (arg3) 20 | 21 | pushf 22 | push edi 23 | push esi 24 | push edx 25 | push ecx 26 | push ebx 27 | ; CLI hack is introduced to avoid race condition when 28 | ; just after int 0x32 and before CLI (inside interrupt handle) 29 | ; if the IRQ0 get triggered, it will fetch kernel code segment 30 | ; and use it to determine invalid PID. 31 | ; Also, user application should not have previlage to execute CLI. 32 | ; It should not be that complicated to fix, I hope. 33 | CLI 34 | int 0x32 35 | add esp, 20 36 | 37 | popf 38 | pop edi 39 | pop esi 40 | pop ebx 41 | pop ebp 42 | ret 43 | -------------------------------------------------------------------------------- /src/usr/local/src/Makefile.mk: -------------------------------------------------------------------------------- 1 | # User Applications 2 | $(BUILD_APP)/%_c.o: $(SRC_APP)/%.c $(BUILD_USR_INCLUDE_ALL) 3 | mkdir -p $(BUILD_APP) 4 | $(USER_CC) -c -o $@ $< 5 | 6 | $(BUILD_APP)/%_cpp.o: $(SRC_APP)/%.cpp $(BUILD_USR_INCLUDE_ALL) 7 | mkdir -p $(BUILD_APP) 8 | $(USER_CPP) -c -o $@ $< 9 | 10 | $(BUILD_APP)/%.elf: $(BUILD_LIB_APP_ENTRY) $(BUILD_APP)/%_c.o $(BUILD_USR_LIB)/libfuzzyc 11 | $(USER_LD) -o $@ $^ 12 | 13 | $(BUILD_APP)/%.elf: $(BUILD_LIB_APP_ENTRY) $(BUILD_APP)/%_cpp.o $(BUILD_USR_LIB)/libfuzzycpp $(BUILD_USR_LIB)/libfuzzyc 14 | $(USER_LD) -o $@ $^ 15 | 16 | $(BUILD_APP)/%: $(BUILD_APP)/%.elf 17 | $(FLAT_FROM_ELF) $< $@ 18 | truncate --size=%512 $@ 19 | -------------------------------------------------------------------------------- /src/usr/local/src/calc.c: -------------------------------------------------------------------------------- 1 | // Simple Calculator 2 | #include 3 | #include 4 | #include 5 | 6 | int err; 7 | char tokens[2][20]; 8 | int a, b; 9 | char op; 10 | int t; 11 | int tl; 12 | 13 | int solve(char s[]) { 14 | t = 0; 15 | tl = 0; 16 | op = 0; 17 | for (int i = 0; s[i] != '\0'; i++) { 18 | if (s[i] >= '0' && s[i] <= '9') { 19 | if (t >= 2) { 20 | err = 1; 21 | return 0; 22 | } 23 | tokens[t][tl] = s[i]; 24 | tl++; 25 | } else { 26 | if (op != 0) { 27 | err = 2; 28 | return 0; 29 | } 30 | op = s[i]; 31 | tokens[t][tl] = '\0'; 32 | tl = 0; 33 | t++; 34 | } 35 | } 36 | tokens[t][tl] = '\0'; 37 | tl = 0; 38 | t++; 39 | if (t != 2) { 40 | err = 3; 41 | return 0; 42 | } 43 | a = atoi(tokens[0]); 44 | b = atoi(tokens[1]); 45 | switch (op) { 46 | case '+': 47 | return a + b; 48 | case '-': 49 | return a - b; 50 | case '*': 51 | return a * b; 52 | case '/': 53 | return a / b; 54 | default: 55 | err = 4; 56 | } 57 | return 0; 58 | } 59 | 60 | void show_usage() { 61 | puts(" Usage: \n"); 62 | puts(" HELP - Show Usage\n"); 63 | puts(" EXIT - To quit Program\n"); 64 | puts("\n"); 65 | puts(" to calculate expression.\n"); 66 | puts(" Where can be one of +-*/.\n"); 67 | } 68 | char expr[100]; 69 | int result; 70 | 71 | int handle_expression(char str[]) { 72 | if (strcmpi(str, "HELP") == 0) { 73 | show_usage(); 74 | return 1; 75 | } 76 | if (strcmpi(str, "EXIT") == 0) { 77 | return 0; 78 | } 79 | result = solve(str); 80 | if (err) { 81 | printf(" Error[%d]: Only ' [+-*/] ' syntax supported!\n", 82 | err); 83 | printf(" Type 'HELP' for instructions!\n\n"); 84 | } else { 85 | printf(" Result: %d\n\n", result); 86 | } 87 | return 1; 88 | } 89 | 90 | int main(int argc, char *argv[]) { 91 | puts("Simple Calculator\n"); 92 | puts("-----------------\n"); 93 | puts("\n"); 94 | show_usage(); 95 | while (1) { 96 | err = 0; 97 | puts("Expression: "); 98 | gets(expr); 99 | 100 | if (!handle_expression(expr)) { 101 | break; 102 | } 103 | } 104 | return 0; 105 | } -------------------------------------------------------------------------------- /src/usr/local/src/cat.c: -------------------------------------------------------------------------------- 1 | // print content of file 2 | #include 3 | #include 4 | 5 | void process(char filename[]) { 6 | FILE *handler = fopen(filename, "r"); 7 | if (handler == NULL) { 8 | printf("failed to open '%s' file.\n", filename); 9 | return; 10 | } 11 | char buffer[80]; 12 | while (1) { 13 | if (!fgets(buffer, sizeof(buffer), handler)) { 14 | int err = ferror(handler); 15 | if (err) { 16 | printf("Error: failed to read next chunk, code %d\n", err); 17 | exit(err); 18 | } 19 | break; 20 | } 21 | puts(buffer); 22 | } 23 | fclose(handler); 24 | } 25 | 26 | int print_usage() { 27 | printf("Usage: cat \n"); 28 | return 0; 29 | } 30 | 31 | int main(int argc, char *argv[]) { 32 | if (argc != 2) { 33 | return print_usage(); 34 | } 35 | char *filename = argv[1]; 36 | process(filename); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /src/usr/local/src/echo.c: -------------------------------------------------------------------------------- 1 | // echo 2 | #include 3 | 4 | int main(int argc, char *argv[]) { 5 | for (size_t i = 1; i < argc; i++) { 6 | printf("%s", argv[i]); 7 | if (i != argc - 1) 8 | putchar(' '); 9 | } 10 | putchar('\n'); 11 | return 0; 12 | } -------------------------------------------------------------------------------- /src/usr/local/src/forkbomb.c: -------------------------------------------------------------------------------- 1 | // multiprocessing example 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | while (1) { 7 | int potential_parent_pid = getpid(); 8 | int pid = fork(); 9 | if (pid < 0) { 10 | printf("fork bomb pid: %d says it can't fork anymore\n", 11 | potential_parent_pid); 12 | } else if (pid > 0) { 13 | printf("new fork bomb pid:%d says thanks to pid:%d\n", pid, 14 | potential_parent_pid); 15 | } 16 | } 17 | return 0; 18 | } -------------------------------------------------------------------------------- /src/usr/local/src/ls.c: -------------------------------------------------------------------------------- 1 | // list directory 2 | #include 3 | #include 4 | 5 | void process(int is_long_listing) { 6 | struct DIR dir; 7 | opendir(&dir); 8 | struct dirent *dp; 9 | 10 | while ((dp = readdir(&dir)) != NULL) { 11 | if (is_long_listing) { 12 | printf("r-%c %d %s\n", (dp->flag & DIRENT_EXECUTABLE) ? 'x' : '-', 13 | dp->size, dp->d_name); 14 | } else { 15 | printf("%s\n", dp->d_name); 16 | } 17 | } 18 | } 19 | 20 | int print_usage() { 21 | printf("Usage: ls [-h] [-l]\n"); 22 | return 0; 23 | } 24 | 25 | int main(int argc, char *argv[]) { 26 | int is_long_listing = 0; 27 | 28 | // command line argument parsing 29 | for (size_t i = 1; i < argc; i++) { 30 | if (strcmp(argv[i], "-h") == 0) { 31 | print_usage(); 32 | return 0; 33 | } else if (strcmp(argv[i], "-l") == 0) { 34 | is_long_listing = 1; 35 | } else { 36 | printf("unrecognized flag: '%s'\n", argv[i]); 37 | print_usage(); 38 | return 1; 39 | } 40 | } 41 | 42 | process(is_long_listing); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /src/usr/local/src/more.c: -------------------------------------------------------------------------------- 1 | // buffered print content of file 2 | #include 3 | #include 4 | 5 | // assumption 6 | #define SCREEN_WIDTH 0x50 7 | #define SCREEN_HEIGHT 0x19 8 | 9 | void process(char filename[]) { 10 | FILE *handler = fopen(filename, "r"); 11 | if (handler == NULL) { 12 | printf("failed to open '%s' file.\n", filename); 13 | return; 14 | } 15 | char buffer[SCREEN_WIDTH + 1]; 16 | int count_before_buffer = SCREEN_HEIGHT - 2; 17 | while (1) { 18 | if (!fgets(buffer, sizeof(buffer), handler)) { 19 | int err = ferror(handler); 20 | if (err) { 21 | printf("Error: failed to read next chunk, code %d\n", err); 22 | exit(err); 23 | } 24 | break; 25 | } 26 | puts(buffer); 27 | if (count_before_buffer > 0) { 28 | count_before_buffer--; 29 | } else { 30 | // buffer 31 | char c = getch(); 32 | if (c == 'q' || c == 'Q') { 33 | exit(0); 34 | } 35 | // continue 36 | } 37 | } 38 | fclose(handler); 39 | } 40 | 41 | int print_usage() { 42 | printf("Usage: more \n"); 43 | printf(" > buffer will start when output takes over the whole screen\n"); 44 | printf(" > press 'q' to exit\n"); 45 | printf(" > press any other key to scroll down\n"); 46 | return 0; 47 | } 48 | 49 | int main(int argc, char *argv[]) { 50 | if (argc != 2) { 51 | return print_usage(); 52 | } 53 | char *filename = argv[1]; 54 | process(filename); 55 | return 0; 56 | } -------------------------------------------------------------------------------- /src/usr/local/src/multiprocessing.c: -------------------------------------------------------------------------------- 1 | // multiprocessing example 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | printf("main process, executing fork()\n"); 7 | int pid = fork(); 8 | printf("fork()=%d\n", pid); 9 | int am_i_child; 10 | if (pid == 0) { 11 | // child process 12 | am_i_child = 1; 13 | printf("child process: says Hi\n"); 14 | yield(); // force process scheduler 15 | } else { 16 | am_i_child = 0; 17 | // parent process 18 | printf("parent process: says Hi\n"); 19 | printf("will for child process to exit\n"); 20 | waitpid(pid, NULL); 21 | } 22 | printf("process exiting. Is current process the child: %s\n", 23 | (am_i_child) ? "Yes" : "No"); 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /src/usr/local/src/sh.c: -------------------------------------------------------------------------------- 1 | // simple shell 2 | #include 3 | #include 4 | #include 5 | 6 | const int COMMAND_SIZE = 64; 7 | const char COMMAND_DELIM[] = " "; 8 | int last_status_code = 0; 9 | 10 | void banner() { 11 | puts("Simple Shell\n"); 12 | puts("------------\n"); 13 | } 14 | 15 | int cmd_help() { 16 | printf("commands\n"); 17 | printf(" > help print the available commands\n"); 18 | printf(" > exit kill the current shell\n"); 19 | printf(" > [arg1]... execute executable program\n"); 20 | printf(" > ls -h example to show ls usage\n"); 21 | last_status_code = 0; 22 | return 0; 23 | } 24 | 25 | static char *copy_arg(char *dst, char *src) { 26 | int slen = strlen(src); 27 | if (slen >= PROCESS_MAX_ARG_LEN) { 28 | slen = PROCESS_MAX_ARG_LEN - 1; 29 | } 30 | memcpy(dst, src, slen); 31 | dst[slen] = NULL; 32 | return dst; 33 | } 34 | 35 | int cmd_run(char *cmd) { 36 | if (cmd == NULL) { 37 | // no command entered 38 | last_status_code = 0; 39 | return 0; 40 | } 41 | char *argv[PROCESS_MAX_ARGC]; 42 | char argv_data[PROCESS_MAX_ARGC][PROCESS_MAX_ARG_LEN] = {0}; 43 | 44 | char *token; 45 | int argc = 0; 46 | argv[argc] = copy_arg(argv_data[argc], cmd); // executable filename 47 | argc++; 48 | while ((token = strtok(NULL, COMMAND_DELIM)) != NULL && 49 | argc < PROCESS_MAX_ARGC - 1) { 50 | argv[argc] = copy_arg(argv_data[argc], token); 51 | argc++; 52 | } 53 | argv[argc] = NULL; 54 | 55 | char *filename = argv[0]; 56 | int pid = spawnv(filename, argv); 57 | if (pid < 0) { 58 | // failed 59 | return pid; 60 | } 61 | waitpid(pid, &last_status_code); 62 | return 0; 63 | } 64 | 65 | int cmd_exit() { 66 | exit(0); 67 | return 0; 68 | } 69 | 70 | void handle_command(char *full_cmd) { 71 | // syntax: [arg0]... 72 | // - handle locally if is builtin 73 | // - update to err status code 74 | 75 | char *cmd = strtok(full_cmd, COMMAND_DELIM); 76 | 77 | if (strcmp(cmd, "help") == 0) { 78 | cmd_help(); 79 | } else if (strcmp(cmd, "exit") == 0) { 80 | cmd_exit(); 81 | } else { 82 | if (cmd_run(cmd) < 0) { 83 | printf("failed to run '%s' command\n", cmd); 84 | } 85 | } 86 | } 87 | 88 | int main(int argc, char *argv[]) { 89 | char command[COMMAND_SIZE]; 90 | banner(); 91 | cmd_help(); 92 | int c = 0; 93 | while (1) { 94 | printf("[%d] $ ", last_status_code); 95 | gets(command); 96 | handle_command(command); 97 | } 98 | return 0; 99 | } -------------------------------------------------------------------------------- /src/usr/local/src/simplecpp.cpp: -------------------------------------------------------------------------------- 1 | // Simple C++ program 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace geometry { 8 | 9 | class Shape { 10 | char name[64]; 11 | 12 | protected: 13 | Shape(char *name) { 14 | std::snprintf(this->name, sizeof(this->name), name); 15 | std::cout << "Shape [" << this->name << "] created" << std::endl; 16 | } 17 | ~Shape() { 18 | std::cout << "Shape [" << this->name << "] destroyed" << std::endl; 19 | } 20 | 21 | public: 22 | // non-pure virtual function. 23 | virtual double getArea() { 24 | std::cout << "I SHOULD NOT BE CALLED" << std::endl; 25 | } 26 | char *getName() { return this->name; } 27 | }; 28 | 29 | class Rectangle : public Shape { 30 | int length, width; 31 | 32 | public: 33 | Rectangle(char name[], int length, int width) 34 | : Shape(name), length(length), width(width) { 35 | std::cout << "[" << name << "] is " << length << "x" << width 36 | << " rectangle" << std::endl; 37 | } 38 | double getArea() { return this->length * this->width; } 39 | }; 40 | 41 | class Square : public Rectangle { 42 | public: 43 | Square(char name[], int side) : Rectangle(name, side, side) { 44 | std::cout << "[" << name << "] is side " << side << " square" 45 | << std::endl; 46 | } 47 | }; 48 | 49 | } // namespace geometry 50 | 51 | void printShapeDetails(geometry::Shape *shape) { 52 | std::cout << "Shape Details" << std::endl; 53 | std::cout << " - Name: " << shape->getName() << std::endl; 54 | std::cout << " - Area: " << (int)shape->getArea() << std::endl; 55 | } 56 | #include 57 | int main(int argc, char *argv[]) { 58 | // shapes 59 | 60 | geometry::Rectangle r1("R1", 10, 20); 61 | geometry::Square s1("S1", 50); 62 | 63 | printShapeDetails(&r1); 64 | printShapeDetails(&s1); 65 | 66 | // new, delete and std::string 67 | char *name = new char[1024]; 68 | std::cout << "Enter your name: "; 69 | std::cin >> name; 70 | std::string cool_name; 71 | cool_name += '['; 72 | cool_name += name; 73 | cool_name += ']'; 74 | std::cout << "Your name is: " << cool_name << std::endl; 75 | delete[] name; 76 | 77 | // vector 78 | std::vector list(4, 100); 79 | list[0] = 0; 80 | for (std::size_t i = 0; i < list.size(); i++) { 81 | std::cout << "list[" << (int)i << "] = " << list[i] << std::endl; 82 | } 83 | 84 | // trigonometry 85 | int angle; 86 | std::cout << "Enter angle in degree: "; 87 | std::cin >> angle; 88 | std::cout << "sin(" << angle << "): " << std::sin(angle * M_PI / 180) 89 | << std::endl; 90 | std::cout << "cos(" << angle << "): " << std::cos(angle * M_PI / 180) 91 | << std::endl; 92 | std::cout << "floor(17.6): " << std::floor(17.6) << std::endl; 93 | std::cout << "round(17.6): " << std::round(17.6) << std::endl; 94 | std::cout << "fmod(17.6, 5.0): " << std::fmod(17.6, 5.0) << std::endl; 95 | std::cout << "fmod(-17.6, 5.0): " << std::fmod(-17.6, 5.0) << std::endl; 96 | std::cout << "fmod(17.6, -5.0): " << std::fmod(17.6, -5.0) << std::endl; 97 | std::cout << "fmod(-17.6, -5.0): " << std::fmod(-17.6, -5.0) << std::endl; 98 | 99 | std::cout << "Exiting..." << std::endl; 100 | return 0; 101 | } -------------------------------------------------------------------------------- /src/usr/local/src/test-math.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | double EPSILON = 1e-9; 6 | 7 | double check_double_equal(double x, double y) { 8 | return std::fabs(x - y) < EPSILON; 9 | } 10 | 11 | double to_radian(double x) { return x * (M_PI / 180); } 12 | 13 | void test_isnan() { 14 | ASSERT_TRUE(std::isnan(16.0) == 0); 15 | ASSERT_TRUE(std::isnan(-16.0) == 0); 16 | ASSERT_TRUE(std::isnan(0.0 / 0.0) != 0); 17 | ASSERT_TRUE(std::isnan(1.0 / 0.0) == 0); 18 | } 19 | 20 | void test_floor() { 21 | ASSERT_TRUE(check_double_equal(std::floor(16.0), 16.0)); 22 | ASSERT_TRUE(check_double_equal(std::floor(16.6), 16.0)); 23 | ASSERT_TRUE(check_double_equal(std::floor(-16.0), -16.0)); 24 | ASSERT_TRUE(check_double_equal(std::floor(-16.9), -17)); 25 | } 26 | 27 | void test_round() { 28 | ASSERT_TRUE(check_double_equal(std::round(16.3), 16.0)); 29 | ASSERT_TRUE(check_double_equal(std::round(16.6), 17.0)); 30 | ASSERT_TRUE(check_double_equal(std::round(-16.0), -16.0)); 31 | ASSERT_TRUE(check_double_equal(std::round(-16.9), -17)); 32 | } 33 | 34 | void test_fmod() { 35 | ASSERT_TRUE(check_double_equal(std::fmod(1.6, 1.2), 0.4)); 36 | ASSERT_TRUE(check_double_equal(std::fmod(7.0, 5.0), 2.0)); 37 | } 38 | 39 | void test_fabs() { 40 | ASSERT_TRUE(check_double_equal(std::fabs(1.6), 1.6)); 41 | ASSERT_TRUE(check_double_equal(std::fabs(-7.7), 7.7)); 42 | ASSERT_TRUE(check_double_equal(std::fabs(0.0), 0.0)); 43 | } 44 | 45 | void test_sin() { 46 | ASSERT_TRUE(check_double_equal(std::sin(to_radian(45.0)), 0.7071067812)); 47 | ASSERT_TRUE(check_double_equal(std::sin(to_radian(90.0)), 1.0)); 48 | } 49 | 50 | void test_cos() { 51 | ASSERT_TRUE(check_double_equal(std::cos(to_radian(60.0)), 0.5)); 52 | ASSERT_TRUE(check_double_equal(std::cos(to_radian(90.0)), 0.0)); 53 | } 54 | 55 | int main(int argc, char *argv[]) { 56 | TEST_INIT(); 57 | 58 | RUN_TEST(test_isnan); 59 | RUN_TEST(test_floor); 60 | RUN_TEST(test_round); 61 | RUN_TEST(test_fmod); 62 | RUN_TEST(test_fabs); 63 | RUN_TEST(test_sin); 64 | RUN_TEST(test_cos); 65 | 66 | TEST_SUMMARY(); 67 | return 0; 68 | } -------------------------------------------------------------------------------- /src/usr/local/src/test-stdlib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void test_min() { 8 | ASSERT_TRUE(std::min(3, 6) == 3); 9 | ASSERT_TRUE(std::min(6, 4) == 4); 10 | ASSERT_TRUE(std::min(300, 6) == 6); 11 | } 12 | 13 | void test_max() { 14 | ASSERT_TRUE(std::max(3, 6) == 6); 15 | ASSERT_TRUE(std::max(8, 4) == 8); 16 | ASSERT_TRUE(std::max(3, 600) == 600); 17 | } 18 | 19 | void test_abs() { 20 | ASSERT_TRUE(std::abs(0) == 0); 21 | ASSERT_TRUE(std::abs(3) == 3); 22 | ASSERT_TRUE(std::abs(-3) == 3); 23 | } 24 | 25 | void test_atoi() { ASSERT_TRUE(std::atoi("36") == 36); } 26 | 27 | void test_itoa() { 28 | char buffer[20]; 29 | std::itoa(54325, buffer, 2); 30 | ASSERT_TRUE(std::strcmp(buffer, "1101010000110101") == 0); 31 | std::itoa(54325, buffer, 10); 32 | ASSERT_TRUE(std::strcmp(buffer, "54325") == 0); 33 | std::itoa(54325, buffer, 16); 34 | ASSERT_TRUE(std::strcmp(buffer, "D435") == 0); 35 | } 36 | 37 | void test_ftoa() { 38 | char buffer[20]; 39 | std::ftoa(1.555, buffer, 1); 40 | ASSERT_TRUE(std::strcmp(buffer, "0.1e+1") == 0); 41 | std::ftoa(1.555, buffer, 2); 42 | ASSERT_TRUE(std::strcmp(buffer, "0.15e+1") == 0); 43 | std::ftoa(-1.555, buffer, 2); 44 | ASSERT_TRUE(std::strcmp(buffer, "-0.15e+1") == 0); 45 | std::ftoa(0.01555, buffer, 2); 46 | ASSERT_TRUE(std::strcmp(buffer, "0.15e-1") == 0); 47 | std::ftoa(0.1555, buffer, 2); 48 | ASSERT_TRUE(std::strcmp(buffer, "0.15") == 0); 49 | } 50 | 51 | void test_malloc_free() { 52 | // we don't want to test to be dependent on the internal 53 | // implementation of malloc and free. So, we are ensuring 54 | // no overlapping memory is being allocated. 55 | 56 | const int test_order[] = { 57 | 64, // [0] allocate 64 bytes 58 | 128, // [1] allocate 128 bytes 59 | -0, // [2] test memory and deallocate 64 bytes 60 | 1024, // [3] allocate 1024 bytes 61 | -1, // [4] test memory and deallocate 128 bytes 62 | 100, // [5] allocate 100 bytes, it could get allocated in freed space 63 | -5, // [6] test memory and deallocate 100 bytes 64 | -3 // [7] test memory and deallocate 1024 bytes 65 | }; 66 | std::uint8_t *memory_track[sizeof(test_order) / sizeof(test_order[0])]; 67 | 68 | for (std::uint8_t i = 0; i < sizeof(test_order) / sizeof(test_order[0]); 69 | i++) { 70 | int op = test_order[i]; 71 | if (op > 0) { 72 | // allocate memory 73 | memory_track[i] = (std::uint8_t *)std::malloc(op); 74 | 75 | // put data bytes marker... in allocated memory 76 | const std::uint8_t marker = i; 77 | std::memset(memory_track[i], marker, test_order[i]); 78 | } else { 79 | int track_id = -op; 80 | 81 | // test memory 82 | const std::uint8_t marker = track_id; 83 | bool is_track_memory_good = true; 84 | for (int j = 0; j < test_order[track_id]; j++) { 85 | if (memory_track[track_id][j] != marker) { 86 | is_track_memory_good = false; 87 | break; 88 | } 89 | } 90 | ASSERT_TRUE(is_track_memory_good); 91 | 92 | // free memory 93 | std::free(memory_track[track_id]); 94 | } 95 | } 96 | } 97 | 98 | void test_realloc() { 99 | // allocate memory 100 | std::uint8_t *data = 101 | (std::uint8_t *)std::realloc(NULL, sizeof(std::uint8_t) * 2); 102 | ASSERT_TRUE(data); 103 | data[0] = 100; 104 | data[1] = 101; 105 | 106 | // upsize memory 107 | data = (std::uint8_t *)std::realloc(data, sizeof(std::uint8_t) * 4); 108 | ASSERT_TRUE(data); 109 | ASSERT_EQ(data[0], 100); 110 | ASSERT_EQ(data[1], 101); 111 | data[2] = 102; 112 | data[3] = 103; 113 | 114 | // downsize memory 115 | data = (std::uint8_t *)std::realloc(data, sizeof(std::uint8_t) * 3); 116 | ASSERT_TRUE(data); 117 | ASSERT_EQ(data[0], 100); 118 | ASSERT_EQ(data[1], 101); 119 | ASSERT_EQ(data[2], 102); 120 | 121 | // free memory 122 | data = (std::uint8_t *)std::realloc(data, 0); 123 | ASSERT_FALSE(data); 124 | } 125 | 126 | int main(int argc, char *argv[]) { 127 | TEST_INIT(); 128 | 129 | RUN_TEST(test_min); 130 | RUN_TEST(test_max); 131 | RUN_TEST(test_abs); 132 | RUN_TEST(test_atoi); 133 | RUN_TEST(test_itoa); 134 | RUN_TEST(test_ftoa); 135 | RUN_TEST(test_malloc_free); 136 | RUN_TEST(test_realloc); 137 | 138 | TEST_SUMMARY(); 139 | return 0; 140 | } -------------------------------------------------------------------------------- /src/usr/local/src/test-string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void test_strcmp() { 6 | ASSERT_TRUE(std::strcmp("hello", "hello") == 0); 7 | ASSERT_TRUE(std::strcmp("hell", "hello") < 0); 8 | ASSERT_TRUE(std::strcmp("hello", "hell") > 0); 9 | 10 | ASSERT_TRUE(std::strcmp("hello", "Hello") != 0); 11 | 12 | ASSERT_TRUE(std::strcmp("abc", "def") < 0); 13 | ASSERT_TRUE(std::strcmp("def", "abc") > 0); 14 | ASSERT_TRUE(std::strcmp("abcdef", "abcdef") == 0); 15 | } 16 | 17 | void test_strcmpi() { 18 | ASSERT_TRUE(std::strcmpi("hello", "hello") == 0); 19 | ASSERT_TRUE(std::strcmpi("hell", "hello") < 0); 20 | ASSERT_TRUE(std::strcmpi("hello", "hell") > 0); 21 | 22 | ASSERT_TRUE(std::strcmpi("hellO", "Hello") == 0); 23 | 24 | ASSERT_TRUE(std::strcmpi("ABC", "def") < 0); 25 | ASSERT_TRUE(std::strcmpi("DEF", "abc") > 0); 26 | ASSERT_TRUE(std::strcmpi("ABCDEF", "abcdef") == 0); 27 | } 28 | 29 | void test_strlen() { 30 | ASSERT_EQ(std::strlen(""), 0); 31 | ASSERT_EQ(std::strlen("hi"), 2); 32 | ASSERT_EQ(std::strlen("hello"), 5); 33 | } 34 | 35 | void test_strcpy() { 36 | char buffer[64]; 37 | 38 | ASSERT_EQ(std::strcmp(std::strcpy(buffer, "hi"), "hi"), 0); 39 | ASSERT_NE(std::strcmp(std::strcpy(buffer, "hello"), "hi"), 0); 40 | ASSERT_EQ(std::strcmp(buffer, "hello"), 0); 41 | } 42 | 43 | void test_strncpy() { 44 | char buffer[64]; 45 | 46 | std::strncpy(buffer, "hello", 2); 47 | ASSERT_TRUE(buffer[0] == 'h' && buffer[1] == 'e'); 48 | } 49 | 50 | void test_memcpy() { 51 | char buffer[64]; 52 | 53 | std::memcpy(buffer, "hello", 6); 54 | ASSERT_EQ(std::strcmp(buffer, "hello"), 0); 55 | } 56 | 57 | void test_memset() { 58 | char buffer[64]; 59 | 60 | buffer[5] = '\0'; 61 | std::memset(buffer, 'a', 5); 62 | ASSERT_EQ(std::strcmp(buffer, "aaaaa"), 0); 63 | } 64 | 65 | void test_strchr() { 66 | char buffer[] = "hello"; 67 | char *occ; 68 | occ = std::strchr(buffer, 'l'); 69 | ASSERT_EQ(occ, buffer + 2); 70 | occ = std::strchr(occ + 1, 'l'); 71 | ASSERT_EQ(occ, buffer + 3); 72 | occ = std::strchr(occ + 1, 'l'); 73 | ASSERT_EQ(occ, NULL); 74 | } 75 | 76 | void test_strtok() { 77 | const char delim[] = " ,"; 78 | char buffer[] = "This world is amazing,is it not?"; 79 | char *tok; 80 | 81 | ASSERT_EQ(std::strcmp((tok = std::strtok(buffer, delim)), "This"), 0); 82 | ASSERT_EQ(std::strcmp((tok = std::strtok(NULL, delim)), "world"), 0); 83 | ASSERT_EQ(std::strcmp((tok = std::strtok(NULL, delim)), "is"), 0); 84 | ASSERT_EQ(std::strcmp((tok = std::strtok(NULL, delim)), "amazing"), 0); 85 | ASSERT_EQ(std::strcmp((tok = std::strtok(NULL, delim)), "is"), 0); 86 | ASSERT_EQ(std::strcmp((tok = std::strtok(NULL, delim)), "it"), 0); 87 | ASSERT_EQ(std::strcmp((tok = std::strtok(NULL, delim)), "not?"), 0); 88 | ASSERT_EQ((tok = std::strtok(NULL, delim)), NULL); 89 | } 90 | 91 | int main(int argc, char *argv[]) { 92 | TEST_INIT(); 93 | 94 | RUN_TEST(test_strcmp); 95 | RUN_TEST(test_strcmpi); 96 | RUN_TEST(test_strlen); 97 | RUN_TEST(test_strcpy); 98 | RUN_TEST(test_strncpy); 99 | RUN_TEST(test_memcpy); 100 | RUN_TEST(test_memset); 101 | RUN_TEST(test_strchr); 102 | RUN_TEST(test_strtok); 103 | 104 | TEST_SUMMARY(); 105 | return 0; 106 | } -------------------------------------------------------------------------------- /src/usr/local/src/tictactoe.c: -------------------------------------------------------------------------------- 1 | // Simple Calculator 2 | #include 3 | #include 4 | #include 5 | 6 | struct State { 7 | unsigned char turn; // 0 or 1 8 | unsigned char mat[3][3]; 9 | }; 10 | 11 | void reset(struct State *s) { 12 | s->turn = 0; 13 | for (int i = 0; i < 3; ++i) 14 | for (int j = 0; j < 3; ++j) 15 | s->mat[i][j] = ' '; 16 | } 17 | 18 | void print_board(struct State *s) { 19 | for (int i = 0; i < 3; ++i) { 20 | printf(" %c|%c|%c\n", s->mat[i][0], s->mat[i][1], s->mat[i][2]); 21 | if (i < 2) { 22 | printf(" -----\n"); 23 | } 24 | } 25 | } 26 | 27 | void redraw(struct State *s) { 28 | clrscr(); 29 | puts("TicTacToe\n"); 30 | puts("---------\n"); 31 | puts("\n"); 32 | puts("Player 1 : X\n"); 33 | puts("Player 2 : O\n"); 34 | puts("\n"); 35 | printf("Turn: Player %d", s->turn + 1); 36 | puts("\n"); 37 | puts("Controls: Use 1-9 in numpad keys pattern\n"); 38 | puts(" : R to reset game\n"); 39 | puts(" : Q to quit game\n\n"); 40 | print_board(s); 41 | 42 | // __TEST_INJECT_APP_TTT_ENTRY__: __asm__("hlt"); 43 | } 44 | 45 | void play_move(struct State *s, unsigned char r, unsigned char c) { 46 | if (s->mat[r][c] != ' ') 47 | return; 48 | char mark = 'X'; 49 | if (s->turn == 1) { 50 | mark = 'O'; 51 | } 52 | s->mat[r][c] = mark; 53 | s->turn = 1 - (s->turn); 54 | } 55 | 56 | struct State s; 57 | unsigned char quit; 58 | unsigned char greset; 59 | int main(int argc, char *argv[]) { 60 | greset = 1; 61 | quit = 0; 62 | while (1) { 63 | if (quit) 64 | break; 65 | if (greset) { 66 | reset(&s); 67 | greset = 0; 68 | } 69 | redraw(&s); 70 | while (1) { 71 | char c = getch(); 72 | if (c == 'r' || c == 'R') { 73 | greset = 1; 74 | break; 75 | } 76 | if (c == 'q' || c == 'Q') { 77 | quit = 1; 78 | break; 79 | } 80 | char row = -1, col = -1; 81 | if (c == '1' || c == '2' || c == '3') 82 | row = 2; 83 | if (c == '4' || c == '5' || c == '6') 84 | row = 1; 85 | if (c == '7' || c == '8' || c == '9') 86 | row = 0; 87 | if (c == '1' || c == '4' || c == '7') 88 | col = 0; 89 | if (c == '2' || c == '5' || c == '8') 90 | col = 1; 91 | if (c == '3' || c == '6' || c == '9') 92 | col = 2; 93 | if (row >= 0) { 94 | play_move(&s, row, col); 95 | break; 96 | } else { 97 | printf("KeyPressed: %d\n", c); 98 | } 99 | } 100 | } 101 | return 0; 102 | } -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scopeInfinity/FuzzyOS/95a3d5c472c9f94bfd8a66d0d53ed0788dfa9dc2/tests/__init__.py -------------------------------------------------------------------------------- /tests/app/cat_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test is supposed to be sourced from shell_test.sh 4 | 5 | QEMU_SCREENSHOT_NAME="cat_test.ppm" 6 | 7 | python3 -m tests.qemu.monitor -p ${MONITOR_PORT:?} -sc cat readme.md 8 | 9 | test_create_screen_dump 10 | test_screen_content $LINENO "Execute QEMU in debug mode and setup GDB server" 11 | -------------------------------------------------------------------------------- /tests/app/libraries/usr_lib_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test is supposed to be sourced from shell_test.sh 4 | 5 | function add_library_unit_test { 6 | QEMU_SCREENSHOT_NAME="usr_lib_${1:?}.ppm" 7 | 8 | python3 -m tests.qemu.monitor -p ${MONITOR_PORT:?} -sc ${1:?} 9 | 10 | test_create_screen_dump 11 | test_screen_content $LINENO "\[.*/usr/local/src/${1:?}.cpp\] TEST_FAILURE_COUNT: 0" 12 | } 13 | 14 | add_library_unit_test "test-string" 15 | add_library_unit_test "test-stdlib" 16 | add_library_unit_test "test-vector" 17 | add_library_unit_test "test-math" 18 | -------------------------------------------------------------------------------- /tests/app/ls_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test is supposed to be sourced from shell_test.sh 4 | 5 | QEMU_SCREENSHOT_NAME="ls_test.ppm" 6 | 7 | python3 -m tests.qemu.monitor -p ${MONITOR_PORT:?} -sc "ls -l" 8 | 9 | test_create_screen_dump 10 | test_screen_content $LINENO "r-x.*cat" 11 | test_screen_content $LINENO "r-x.*ls" 12 | test_screen_content $LINENO "r-x.*calc" 13 | test_screen_content $LINENO "r-x.*tictactoe" 14 | test_screen_content $LINENO "r--.*README.md" 15 | -------------------------------------------------------------------------------- /tests/app/multiprocessing_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test is supposed to be sourced from shell_test.sh 4 | 5 | QEMU_SCREENSHOT_NAME="multiprocessing_test.ppm" 6 | 7 | python3 -m tests.qemu.monitor -p ${MONITOR_PORT:?} -sc "multiprocessing" 8 | 9 | test_create_screen_dump 10 | test_screen_content $LINENO "child process: says.*" 11 | test_screen_content $LINENO "parent process: says.*" 12 | test_screen_content $LINENO "Is current process the child:.*Yes" 13 | test_screen_content $LINENO "Is current process the child:.*No" 14 | -------------------------------------------------------------------------------- /tests/app/shell_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test is supposed to be sourced from fuzzy_app_test.sh 4 | 5 | test_screen_content $LINENO "Simple Shell" 6 | 7 | # Test applications 8 | source tests/app/ls_test.sh 9 | source tests/app/tictactoe_test.sh 10 | source tests/app/cat_test.sh 11 | source tests/app/multiprocessing_test.sh 12 | source tests/app/libraries/usr_lib_test.sh 13 | -------------------------------------------------------------------------------- /tests/app/tictactoe_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Test is supposed to be sourced from shell_test.sh 4 | 5 | QEMU_SCREENSHOT_NAME="tictactoe_test.ppm" 6 | 7 | python3 -m tests.qemu.monitor -p ${MONITOR_PORT:?} -sc tictactoe 8 | 9 | test_create_screen_dump 10 | test_screen_content $LINENO "TicTacToe" 11 | test_screen_content $LINENO "Q to quit game" 12 | 13 | python3 -m tests.qemu.monitor -p ${MONITOR_PORT:?} -sc 'q' 14 | -------------------------------------------------------------------------------- /tests/app_ttt_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source tests/lib.sh 4 | 5 | TEST_INJECT_WORD="__TEST_INJECT_APP_TTT_ENTRY__" 6 | TEST_MAGIC_WANT="${MAGIC_WORD_SLEEP:?}" 7 | 8 | os_test_up "${TEST_MAGIC_WANT:?}" "${TEST_INJECT_WORD:?}" INIT_APPNAME="tictactoe" || exit -1 9 | 10 | # Test 11 | set -e 12 | test_screen_content $LINENO "TicTacToe" 13 | 14 | python -m tests.qemu.monitor --quit 15 | wait ${QEMU_PID:?} 16 | echo "$0 passed!!!" -------------------------------------------------------------------------------- /tests/bootloader_stage1_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_INJECT_WORD="__TEST_INJECT_BT1__" 4 | TEST_MAGIC_WANT="9A11C824" 5 | 6 | source tests/lib.sh 7 | 8 | os_test_up "${TEST_MAGIC_WANT:?}" "${TEST_INJECT_WORD:?}" || exit -1 9 | 10 | # Test 11 | set -e 12 | set -x 13 | 14 | test_screen_content $LINENO "Bootloader: Stage 1" 15 | test_screen_content $LINENO "Stage 2 Loaded: `build_8hexbyte bootloader/stage2`" 16 | 17 | python -m tests.qemu.monitor --quit 18 | wait ${QEMU_PID:?} 19 | echo "$0 passed!!!" -------------------------------------------------------------------------------- /tests/bootloader_stage2_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_INJECT_WORD="__TEST_INJECT_BT2__" 4 | TEST_MAGIC_WANT="198A65C3" 5 | 6 | source tests/lib.sh 7 | 8 | os_test_up "${TEST_MAGIC_WANT:?}" "${TEST_INJECT_WORD:?}" || exit -1 9 | 10 | # Test 11 | set -e 12 | 13 | test_screen_content $LINENO "Bootloader: Stage 2" 14 | test_screen_content $LINENO "Static library loaded at 0x00007E00: `build_8hexbyte real_mode/static_library`" 15 | test_screen_content $LINENO "Kernel loaded at 0x[0-9A-Fa-f]\+: `build_8hexbyte kernel/core`" 16 | test_screen_content $LINENO "Loading GDT Table and entering protected mode" 17 | 18 | python -m tests.qemu.monitor --quit 19 | wait ${QEMU_PID:?} 20 | echo "$0 passed!!!" -------------------------------------------------------------------------------- /tests/fuzzy_shell_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source tests/lib.sh 4 | 5 | TEST_INJECT_WORD="${MAGIC_WORD_NO_TEST_INJECT:?}" 6 | TEST_MAGIC_WANT="${MAGIC_WORD_SLEEP:?}" 7 | 8 | os_test_up "${TEST_MAGIC_WANT:?}" "${TEST_INJECT_WORD:?}" || exit -1 9 | 10 | source tests/app/shell_test.sh 11 | 12 | python -m tests.qemu.monitor --quit 13 | wait ${QEMU_PID:?} 14 | echo "$0 passed!!!" -------------------------------------------------------------------------------- /tests/kernel_core_entry_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEST_INJECT_WORD="__TEST_INJECT_KERNEL_CORE_ENTRY__" 4 | TEST_MAGIC_WANT="922E52FF" 5 | 6 | source tests/lib.sh 7 | 8 | os_test_up "${TEST_MAGIC_WANT:?}" "${TEST_INJECT_WORD:?}" || exit -1 9 | 10 | # Test 11 | set -e 12 | test_screen_content $LINENO "Initializing Kernel" 13 | test_screen_content $LINENO "Registering syscalls" 14 | 15 | 16 | python -m tests.qemu.monitor --quit 17 | wait ${QEMU_PID:?} 18 | echo "$0 passed!!!" -------------------------------------------------------------------------------- /tests/qemu/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scopeInfinity/FuzzyOS/95a3d5c472c9f94bfd8a66d0d53ed0788dfa9dc2/tests/qemu/__init__.py -------------------------------------------------------------------------------- /tests/qemu/args.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def get_args(): 4 | parser = argparse.ArgumentParser(description="Fuzzy OS QEMU monitor client") 5 | parser.add_argument("-p", "--port", 6 | type=int, 7 | default=55555, 8 | help="qemu monitor port") 9 | parser.add_argument('-qc', '--qemu-cmd', 10 | metavar="qemu-token", 11 | type=str, 12 | nargs='+', 13 | help="command to execute in qemu monitor") 14 | parser.add_argument('-sc', '--fuzzy-shell-cmd', 15 | metavar="fuzzy-token", 16 | type=str, 17 | nargs='+', 18 | help="command to execute in Fuzzy OS shell") 19 | parser.add_argument('-q', '--quit', 20 | action='store_true', 21 | help="kill qemu before leaving") 22 | args = parser.parse_args() 23 | return args 24 | -------------------------------------------------------------------------------- /tests/qemu/key.py: -------------------------------------------------------------------------------- 1 | QEMU_CODES = { 2 | " " : "spc", 3 | "\n": "ret", 4 | "." : "dot", 5 | "," : "comma", 6 | "-" : "minus", 7 | "=" : "equal", 8 | "*" : "asterisk" 9 | } 10 | 11 | def char_to_key(char): 12 | if char in QEMU_CODES: 13 | return QEMU_CODES[char] 14 | 15 | if isinstance(char, str) and len(char) == 1 and char.isalnum(): 16 | # single isalnum char 17 | return char 18 | raise Exception(f"char_to_key can't handle '{char}'") 19 | -------------------------------------------------------------------------------- /tests/qemu/monitor.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import pexpect 3 | import time 4 | 5 | from tests.qemu import args 6 | from tests.qemu import key 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | QEMU_PROMPT = "(qemu)" 11 | 12 | 13 | class Monitor: 14 | 15 | def __init__(self, port): 16 | logger.info("Connecting to qemu, waiting for prompt") 17 | self.process = pexpect.spawn(f"telnet 127.0.0.1 {port}") 18 | self.wait_for_prompt() 19 | 20 | def wait_for_prompt(self): 21 | try: 22 | self.process.expect(QEMU_PROMPT, timeout=2) 23 | except pexpect.TIMEOUT as e: 24 | logger.error("[pexpect] Qemu prompt wasn't seen within timeout") 25 | raise e 26 | 27 | def send_qemu_command(self, cmd, wait_prompt=True): 28 | self.process.sendline(cmd) 29 | if wait_prompt: 30 | self.wait_for_prompt() 31 | 32 | def send_key(self, key): 33 | """Send a key to qemu.""" 34 | self.process.sendline(f"sendkey {key}") 35 | self.wait_for_prompt() 36 | 37 | def type_string(self, msg): 38 | """Type string in qemu.""" 39 | logger.info(f"[to_qemu] typing string: {repr(msg)}") 40 | for char in msg: 41 | self.send_key(key.char_to_key(char)) 42 | 43 | def handle_qemu_cmd(monitor, args): 44 | if not args.qemu_cmd: 45 | return 46 | logger.info("Handle: fuzzy_shell_cmd") 47 | cmd = " ".join(args.qemu_cmd) + "\n" 48 | monitor.send_qemu_command(cmd) 49 | 50 | def handle_fuzzy_shell_cmd(monitor, args): 51 | if not args.fuzzy_shell_cmd: 52 | return 53 | logger.info("Handle: fuzzy_shell_cmd") 54 | cmd = " ".join(args.fuzzy_shell_cmd) + "\n" 55 | monitor.type_string(cmd) 56 | 57 | def handle_qemu_quit(monitor, args): 58 | if not args.quit: 59 | return 60 | logger.info("Handle: qemu quit") 61 | monitor.send_qemu_command("quit\n", wait_prompt=False) 62 | time.sleep(1) 63 | 64 | def main(): 65 | logging.basicConfig(level=logging.INFO) 66 | parsed_args = args.get_args() 67 | 68 | monitor = Monitor(parsed_args.port) 69 | 70 | handle_fuzzy_shell_cmd(monitor, parsed_args) 71 | handle_qemu_cmd(monitor, parsed_args) 72 | handle_qemu_quit(monitor, parsed_args) 73 | 74 | logger.info("graceful exit") 75 | 76 | if __name__ == '__main__': 77 | main() 78 | -------------------------------------------------------------------------------- /tests/qemu_monitor_expect.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | set timeout 10 3 | 4 | set port [lindex $argv 0] 5 | set cmd [lindex $argv 1] 6 | 7 | spawn telnet 127.0.0.1 $port 8 | 9 | expect "(qemu) " 10 | 11 | send -- "$cmd\r" 12 | 13 | expect { 14 | "(qemu) " {} 15 | "closed" {} 16 | } -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Execute Tests 5 | timeout 2m bash tests/bootloader_stage1_test.sh && 6 | timeout 2m bash tests/bootloader_stage2_test.sh && 7 | timeout 2m bash tests/kernel_core_entry_test.sh && 8 | timeout 2m bash tests/fuzzy_shell_test.sh 9 | 10 | # Done 11 | echo "All tests passed!" 12 | --------------------------------------------------------------------------------