├── .gitignore ├── IPA_Font_License_Agreement_v1.0.txt ├── LICENSE ├── MikanLoaderPkg ├── Loader.inf ├── Main.c ├── MikanLoaderPkg.dec ├── MikanLoaderPkg.dsc ├── elf.hpp ├── frame_buffer_config.hpp └── memory_map.hpp ├── NOTICE ├── README.md ├── apps ├── .gitignore ├── Makefile.elfapp ├── blocks │ ├── .gitignore │ ├── Makefile │ └── blocks.cpp ├── cp │ ├── .gitignore │ ├── Makefile │ └── cp.cpp ├── cube │ ├── .gitignore │ ├── Makefile │ └── cube.cpp ├── dpage │ ├── .gitignore │ ├── Makefile │ └── dpage.cpp ├── eye │ ├── .gitignore │ ├── Makefile │ └── eye.cpp ├── fault │ ├── .gitignore │ ├── Makefile │ └── fault.cpp ├── grep │ ├── .gitignore │ ├── Makefile │ └── grep.cpp ├── gview │ ├── .gitignore │ ├── Makefile │ ├── gview.cpp │ └── stb_image.h ├── hex2bin │ ├── .gitignore │ ├── Makefile │ ├── hex2bin.cpp │ └── test.sh ├── large │ ├── .gitignore │ ├── Makefile │ └── large.cpp ├── library.c ├── lines │ ├── .gitignore │ ├── Makefile │ └── lines.cpp ├── mandel │ ├── .gitignore │ ├── Makefile │ └── mandel.cpp ├── mmap │ ├── .gitignore │ ├── Makefile │ └── mmap.cpp ├── more │ ├── .gitignore │ ├── Makefile │ └── more.cpp ├── newlib_support.c ├── onlyhlt │ ├── .gitignore │ ├── Makefile │ └── onlyhlt.asm ├── paint │ ├── .gitignore │ ├── Makefile │ └── paint.cpp ├── readfile │ ├── .gitignore │ ├── Makefile │ └── readfile.cpp ├── rpn │ ├── .gitignore │ ├── Makefile │ └── rpn.cpp ├── sort │ ├── .gitignore │ ├── Makefile │ └── sort.cpp ├── stars │ ├── .gitignore │ ├── Makefile │ └── stars.cpp ├── syscall.asm ├── syscall.h ├── tedit │ ├── .gitignore │ ├── Makefile │ └── tedit.cpp ├── timer │ ├── .gitignore │ ├── Makefile │ └── timer.cpp ├── tview │ ├── .gitignore │ ├── Makefile │ └── tview.cpp ├── winhello │ ├── .gitignore │ ├── Makefile │ └── winhello.cpp └── winjpn │ ├── .gitignore │ ├── Makefile │ └── winjpn.cpp ├── build.sh ├── docs ├── how-to-connect-USB-device-to-MikanOS.md ├── how-to-send-pull-request.md └── images │ ├── github-clone-repository.png │ ├── github-create-pull-request-button.png │ ├── github-describe-pull-request.png │ ├── github-fork-button.png │ ├── github-fork-progress.png │ ├── github-forked-from.png │ └── github-pull-request-suggestion.png ├── kernel ├── .gitignore ├── Doxyfile ├── Makefile ├── acpi.cpp ├── acpi.hpp ├── app_event.hpp ├── asmfunc.asm ├── asmfunc.h ├── compile_flags.txt ├── console.cpp ├── console.hpp ├── elf.hpp ├── error.hpp ├── fat.cpp ├── fat.hpp ├── file.cpp ├── file.hpp ├── font.cpp ├── font.hpp ├── frame_buffer.cpp ├── frame_buffer.hpp ├── frame_buffer_config.hpp ├── graphics.cpp ├── graphics.hpp ├── hankaku.txt ├── interrupt.cpp ├── interrupt.hpp ├── keyboard.cpp ├── keyboard.hpp ├── layer.cpp ├── layer.hpp ├── libcxx_support.cpp ├── logger.cpp ├── logger.hpp ├── main.cpp ├── memory_manager.cpp ├── memory_manager.hpp ├── memory_map.hpp ├── message.hpp ├── mouse.cpp ├── mouse.hpp ├── msr.hpp ├── newlib_support.c ├── paging.cpp ├── paging.hpp ├── pci.cpp ├── pci.hpp ├── register.hpp ├── segment.cpp ├── segment.hpp ├── syscall.cpp ├── syscall.hpp ├── task.cpp ├── task.hpp ├── terminal.cpp ├── terminal.hpp ├── timer.cpp ├── timer.hpp ├── uefi.hpp ├── usb │ ├── arraymap.hpp │ ├── classdriver │ │ ├── base.cpp │ │ ├── base.hpp │ │ ├── cdc.cpp │ │ ├── cdc.hpp │ │ ├── hid.cpp │ │ ├── hid.hpp │ │ ├── keyboard.cpp │ │ ├── keyboard.hpp │ │ ├── mouse.cpp │ │ └── mouse.hpp │ ├── descriptor.hpp │ ├── device.cpp │ ├── device.hpp │ ├── endpoint.hpp │ ├── memory.cpp │ ├── memory.hpp │ ├── setupdata.hpp │ └── xhci │ │ ├── context.hpp │ │ ├── device.cpp │ │ ├── device.hpp │ │ ├── devmgr.cpp │ │ ├── devmgr.hpp │ │ ├── port.cpp │ │ ├── port.hpp │ │ ├── registers.cpp │ │ ├── registers.hpp │ │ ├── ring.cpp │ │ ├── ring.hpp │ │ ├── speed.hpp │ │ ├── trb.cpp │ │ ├── trb.hpp │ │ ├── xhci.cpp │ │ └── xhci.hpp ├── window.cpp ├── window.hpp └── x86_descriptor.hpp ├── mikanos-after30-photo.png ├── release_pack ├── .gitignore ├── README.md ├── install.sh └── make_release.sh ├── resource ├── fujisan.jpg ├── jpn.txt ├── mikanos.txt ├── night.bmp ├── nihongo.ttf └── tokyost.jpg ├── run.sh └── tools ├── attach_usbdev.sh └── makefont.py /.gitignore: -------------------------------------------------------------------------------- 1 | .*.un~ 2 | *.img 3 | -------------------------------------------------------------------------------- /MikanLoaderPkg/Loader.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 0x00010006 3 | BASE_NAME = Loader 4 | FILE_GUID = c9d0d202-71e9-11e8-9e52-cfbfd0063fbf 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 0.1 7 | ENTRY_POINT = UefiMain 8 | 9 | # VALID_ARCHITECTURES = X64 10 | 11 | [Sources] 12 | Main.c 13 | 14 | [Packages] 15 | MdePkg/MdePkg.dec 16 | 17 | [LibraryClasses] 18 | UefiLib 19 | UefiApplicationEntryPoint 20 | 21 | [Guids] 22 | gEfiFileInfoGuid 23 | gEfiAcpiTableGuid 24 | 25 | [Protocols] 26 | gEfiLoadedImageProtocolGuid 27 | gEfiLoadFileProtocolGuid 28 | gEfiSimpleFileSystemProtocolGuid 29 | gEfiBlockIoProtocolGuid 30 | -------------------------------------------------------------------------------- /MikanLoaderPkg/MikanLoaderPkg.dec: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DEC_SPECIFICATION = 0x00010005 3 | PACKAGE_NAME = MikanLoaderPkg 4 | PACKAGE_GUID = 452eae8e-71e9-11e8-a243-df3f1ffdebe1 5 | PACKAGE_VERSION = 0.1 6 | -------------------------------------------------------------------------------- /MikanLoaderPkg/MikanLoaderPkg.dsc: -------------------------------------------------------------------------------- 1 | [Defines] 2 | PLATFORM_NAME = MikanLoaderPkg 3 | PLATFORM_GUID = d3f11f4e-71e9-11e8-a7e1-33fd4f7d5a3e 4 | PLATFORM_VERSION = 0.1 5 | DSC_SPECIFICATION = 0x00010005 6 | OUTPUT_DIRECTORY = Build/MikanLoader$(ARCH) 7 | SUPPORTED_ARCHITECTURES = X64 8 | BUILD_TARGETS = DEBUG|RELEASE|NOOPT 9 | 10 | [LibraryClasses] 11 | UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf 12 | UefiLib|MdePkg/Library/UefiLib/UefiLib.inf 13 | 14 | BaseLib|MdePkg/Library/BaseLib/BaseLib.inf 15 | BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf 16 | DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf 17 | DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf 18 | MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf 19 | PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf 20 | PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf 21 | RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf 22 | UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf 23 | UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf 24 | 25 | [Components] 26 | MikanLoaderPkg/Loader.inf 27 | -------------------------------------------------------------------------------- /MikanLoaderPkg/elf.hpp: -------------------------------------------------------------------------------- 1 | ../kernel/elf.hpp -------------------------------------------------------------------------------- /MikanLoaderPkg/frame_buffer_config.hpp: -------------------------------------------------------------------------------- 1 | ../kernel/frame_buffer_config.hpp -------------------------------------------------------------------------------- /MikanLoaderPkg/memory_map.hpp: -------------------------------------------------------------------------------- 1 | ../kernel/memory_map.hpp -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2018-2022 Kota Uchida 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MikanOS 2 | 3 | MikanOS はレガシーフリーなアーキテクチャ(UEFI BIOS、Intel 64 モード)で動作する教育用オペレーティングシステムです。 4 | 5 | ## ファイル構成 6 | 7 | - MikanLoaderPkg 8 | - UEFI アプリとして構成したブートローダ 9 | - kernel 10 | - MikanOS のカーネル 11 | - resource/nihongo.ttf 12 | - IPA ゴシックのフォントファイル 13 | - IPA_Font_License_Agreement_v1.0.txt 14 | - IPA フォントのライセンス文書 15 | 16 | ## ビルド方法 17 | 18 | [mikanos-build リポジトリ](https://github.com/uchan-nos/mikanos-build/) に MikanOS をビルドするためのスクリプトがあります。 19 | mikanos-build の手順に沿って開発ツールを導入した後、devenv/buildenv.sh を読み込むことでビルド可能です。 20 | (devenv/buildenv.sh により環境変数 CPPFLAGS などが適切に設定されます。) 21 | 22 | MikanOS の最新版をビルドするためには mikanos-build の最新版が必要です。 23 | 24 | ## 教科書 25 | 26 | MikanOS の作り方を説明した教科書があります。 27 | [ゼロからのOS自作入門](https://zero.osdev.jp/) 28 | 29 | ## スクリーンショット 30 | 31 | 「ゼロからのOS自作入門」の最終章を終えたときの姿 32 | ![30章後の姿](mikanos-after30-photo.png) 33 | 34 | ## 開発への参加 35 | 36 | MikanOS への機能追加、バグ修正の提案は Pull Request にてお願いします。 37 | Pull Request の出し方はこちらで説明しています。 [プルリクエストの送り方](https://github.com/uchan-nos/mikanos/blob/master/docs/how-to-send-pull-request.md) 38 | 39 | 実装が伴わない「単なる要望」は基本的に受け付けません。 40 | 実装をきちんと作ってから Pull Request を提出してください。 41 | 42 | もし、実装したいけど力が不足して実装できない、という場合はお気軽に Issues でご連絡ください。 43 | 実装ができるようになるように、できるだけご協力いたします。 44 | -------------------------------------------------------------------------------- /apps/.gitignore: -------------------------------------------------------------------------------- 1 | /*.o 2 | -------------------------------------------------------------------------------- /apps/Makefile.elfapp: -------------------------------------------------------------------------------- 1 | CPPFLAGS += -I. -D__SCLE 2 | CFLAGS += -O2 -Wall -g --target=x86_64-elf -mcmodel=large 3 | CXXFLAGS += -O2 -Wall -g --target=x86_64-elf -mcmodel=large \ 4 | -fno-exceptions -fno-rtti -std=c++17 5 | LDFLAGS += -z norelro --image-base 0xffff800000000000 --static 6 | 7 | OBJS += ../syscall.o ../newlib_support.o ../library.o 8 | 9 | .PHONY: all 10 | all: $(TARGET) 11 | 12 | $(TARGET): $(OBJS) Makefile 13 | ld.lld $(LDFLAGS) -o $@ $(OBJS) -lc -lc++ -lc++abi -lm 14 | 15 | %.o: %.c Makefile 16 | clang $(CPPFLAGS) $(CFLAGS) -c $< -o $@ 17 | 18 | %.o: %.cpp Makefile 19 | clang++ $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ 20 | 21 | %.o: %.asm Makefile 22 | nasm -f elf64 -o $@ $< 23 | -------------------------------------------------------------------------------- /apps/blocks/.gitignore: -------------------------------------------------------------------------------- 1 | /blocks 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/blocks/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = blocks 2 | OBJS = blocks.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/cp/.gitignore: -------------------------------------------------------------------------------- 1 | /cp 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/cp/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = cp 2 | OBJS = cp.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/cp/cp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | if (argc < 3) { 5 | printf("Usage: %s \n", argv[0]); 6 | return 1; 7 | } 8 | 9 | FILE* fp_src = fopen(argv[1], "r"); 10 | if (fp_src == nullptr) { 11 | printf("failed to open for read: %s\n", argv[1]); 12 | return 1; 13 | } 14 | 15 | FILE* fp_dest = fopen(argv[2], "w"); 16 | if (fp_dest == nullptr) { 17 | printf("failed to open for write: %s\n", argv[2]); 18 | return 1; 19 | } 20 | 21 | char buf[256]; 22 | size_t bytes; 23 | while ((bytes = fread(buf, 1, sizeof(buf), fp_src)) > 0) { 24 | const size_t written = fwrite(buf, 1, bytes, fp_dest); 25 | if (bytes != written) { 26 | printf("failed to write to %s\n", argv[2]); 27 | return 1; 28 | } 29 | } 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /apps/cube/.gitignore: -------------------------------------------------------------------------------- 1 | /cube 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/cube/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = cube 2 | OBJS = cube.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/dpage/.gitignore: -------------------------------------------------------------------------------- 1 | /dpage 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/dpage/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = dpage 2 | OBJS = dpage.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/dpage/dpage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | int main(int argc, char** argv) { 6 | const char* filename = "/memmap"; 7 | int ch = '\n'; 8 | if (argc >= 3) { 9 | filename = argv[1]; 10 | ch = atoi(argv[2]); 11 | } 12 | FILE* fp = fopen(filename, "r"); 13 | if (!fp) { 14 | printf("failed to open %s\n", filename); 15 | return 1; 16 | } 17 | 18 | SyscallResult res = SyscallDemandPages(1, 0); 19 | if (res.error) { 20 | return 1; 21 | } 22 | char* buf = reinterpret_cast(res.value); 23 | char* buf0 = buf; 24 | 25 | size_t total = 0; 26 | size_t n; 27 | while ((n = fread(buf, 1, 4096, fp)) == 4096) { 28 | total += n; 29 | if (res = SyscallDemandPages(1, 0); res.error) { 30 | return 1; 31 | } 32 | buf += 4096; 33 | } 34 | total += n; 35 | printf("size of %s = %lu bytes\n", filename, total); 36 | 37 | size_t num = 0; 38 | for (int i = 0; i < total; ++i) { 39 | if (buf0[i] == ch) { 40 | ++num; 41 | } 42 | } 43 | printf("the number of '%c' (0x%02x) = %lu\n", ch, ch, num); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /apps/eye/.gitignore: -------------------------------------------------------------------------------- 1 | /eye 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/eye/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = eye 2 | OBJS = eye.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/eye/eye.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../syscall.h" 5 | 6 | static const int kCanvasSize = 100, kEyeSize = 10; 7 | 8 | void DrawEye(uint64_t layer_id_flags, 9 | int mouse_x, int mouse_y, uint32_t color) { 10 | const double center_x = mouse_x - kCanvasSize/2 - 4; 11 | const double center_y = mouse_y - kCanvasSize/2 - 24; 12 | 13 | const double direction = atan2(center_y, center_x); 14 | double distance = sqrt(pow(center_x, 2) + pow(center_y, 2)); 15 | distance = std::min(distance, kCanvasSize/2 - kEyeSize/2); 16 | 17 | const double eye_center_x = cos(direction) * distance; 18 | const double eye_center_y = sin(direction) * distance; 19 | const int eye_x = static_cast(eye_center_x) + kCanvasSize/2 + 4; 20 | const int eye_y = static_cast(eye_center_y) + kCanvasSize/2 + 24; 21 | 22 | SyscallWinFillRectangle(layer_id_flags, eye_x - kEyeSize/2, eye_y - kEyeSize/2, kEyeSize, kEyeSize, color); 23 | } 24 | 25 | int main(int argc, char** argv) { 26 | auto [layer_id, err_openwin] 27 | = SyscallOpenWindow(kCanvasSize + 8, kCanvasSize + 28, 10, 10, "eye"); 28 | if (err_openwin) { 29 | return err_openwin; 30 | } 31 | 32 | SyscallWinFillRectangle(layer_id, 4, 24, kCanvasSize, kCanvasSize, 0xffffff); 33 | 34 | AppEvent events[1]; 35 | while (true) { 36 | auto [ n, err ] = SyscallReadEvent(events, 1); 37 | if (err) { 38 | printf("ReadEvent failed: %s\n", strerror(err)); 39 | break; 40 | } 41 | if (events[0].type == AppEvent::kQuit) { 42 | break; 43 | } else if (events[0].type == AppEvent::kMouseMove) { 44 | auto& arg = events[0].arg.mouse_move; 45 | SyscallWinFillRectangle(layer_id | LAYER_NO_REDRAW, 46 | 4, 24, kCanvasSize, kCanvasSize, 0xffffff); 47 | DrawEye(layer_id, arg.x, arg.y, 0x000000); 48 | } else { 49 | printf("unknown event: type = %d\n", events[0].type); 50 | } 51 | } 52 | SyscallCloseWindow(layer_id); 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /apps/fault/.gitignore: -------------------------------------------------------------------------------- 1 | /fault 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/fault/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = fault 2 | OBJS = fault.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/fault/fault.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | int main(int argc, char** argv) { 6 | const char* cmd = "hlt"; 7 | if (argc >= 2) { 8 | cmd = argv[1]; 9 | } 10 | 11 | if (strcmp(cmd, "hlt") == 0) { 12 | __asm__("hlt"); 13 | } else if (strcmp(cmd, "wr_kernel") == 0) { 14 | int* p = reinterpret_cast(0x100); 15 | *p = 42; 16 | } else if (strcmp(cmd, "wr_app") == 0) { 17 | int* p = reinterpret_cast(0xffff8000ffff0000); 18 | *p = 123; 19 | } else if (strcmp(cmd, "zero") == 0) { 20 | volatile int z = 0; 21 | printf("100/%d = %d\n", z, 100/z); 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /apps/grep/.gitignore: -------------------------------------------------------------------------------- 1 | /grep 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/grep/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = grep 2 | OBJS = grep.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/grep/grep.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char** argv) { 6 | if (argc < 2) { 7 | fprintf(stderr, "Usage: %s []\n", argv[0]); 8 | return 1; 9 | } 10 | 11 | std::regex pattern{argv[1]}; 12 | 13 | FILE* fp = stdin; 14 | if (argc >= 3 && (fp = fopen(argv[2], "r")) == nullptr) { 15 | fprintf(stderr, "failed to open: %s\n", argv[2]); 16 | return 1; 17 | } 18 | 19 | char line[256]; 20 | const bool is_term = isatty(STDOUT_FILENO); 21 | while (fgets(line, sizeof(line), fp)) { 22 | std::cmatch m; 23 | if (std::regex_search(line, m, pattern)) { 24 | if (is_term) { 25 | printf("%.*s\033[91m%.*s\033[0m%s", 26 | static_cast(m.prefix().length()), m.prefix().first, 27 | static_cast(m[0].length()), m[0].first, 28 | m.suffix().first); 29 | } else { 30 | printf("%s", line); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/gview/.gitignore: -------------------------------------------------------------------------------- 1 | /gview 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/gview/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = gview 2 | OBJS = gview.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/gview/gview.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../syscall.h" 7 | 8 | #define STBI_NO_THREAD_LOCALS 9 | #define STB_IMAGE_IMPLEMENTATION 10 | #define STBI_NO_STDIO 11 | #include "stb_image.h" 12 | 13 | std::tuple MapFile(const char* filepath) { 14 | SyscallResult res = SyscallOpenFile(filepath, O_RDONLY); 15 | if (res.error) { 16 | fprintf(stderr, "%s: %s\n", strerror(res.error), filepath); 17 | exit(1); 18 | } 19 | 20 | const int fd = res.value; 21 | size_t filesize; 22 | res = SyscallMapFile(fd, &filesize, 0); 23 | if (res.error) { 24 | fprintf(stderr, "%s\n", strerror(res.error)); 25 | exit(1); 26 | } 27 | 28 | return {fd, reinterpret_cast(res.value), filesize}; 29 | } 30 | 31 | void WaitEvent() { 32 | AppEvent events[1]; 33 | while (true) { 34 | auto [ n, err ] = SyscallReadEvent(events, 1); 35 | if (err) { 36 | fprintf(stderr, "ReadEvent failed: %s\n", strerror(err)); 37 | return; 38 | } 39 | if (events[0].type == AppEvent::kQuit) { 40 | return; 41 | } 42 | } 43 | } 44 | 45 | uint32_t GetColorRGB(unsigned char* image_data) { 46 | return static_cast(image_data[0]) << 16 | 47 | static_cast(image_data[1]) << 8 | 48 | static_cast(image_data[2]); 49 | } 50 | 51 | uint32_t GetColorGray(unsigned char* image_data) { 52 | const uint32_t gray = image_data[0]; 53 | return gray << 16 | gray << 8 | gray; 54 | } 55 | 56 | int main(int argc, char** argv) { 57 | if (argc < 2) { 58 | fprintf(stderr, "Usage: %s \n", argv[0]); 59 | return 1; 60 | } 61 | 62 | int width, height, bytes_per_pixel; 63 | const char* filepath = argv[1]; 64 | const auto [ fd, content, filesize ] = MapFile(filepath); 65 | 66 | unsigned char* image_data = stbi_load_from_memory( 67 | content, filesize, &width, &height, &bytes_per_pixel, 0); 68 | if (image_data == nullptr) { 69 | fprintf(stderr, "failed to load image: %s\n", stbi_failure_reason()); 70 | return 1; 71 | } 72 | 73 | fprintf(stderr, "%dx%d, %d bytes/pixel\n", width, height, bytes_per_pixel); 74 | auto get_color = GetColorRGB; 75 | if (bytes_per_pixel <= 2) { 76 | get_color = GetColorGray; 77 | } 78 | 79 | const char* last_slash = strrchr(filepath, '/'); 80 | const char* filename = last_slash ? &last_slash[1] : filepath; 81 | SyscallResult window = 82 | SyscallOpenWindow(8 + width, 28 + height, 10, 10, filename); 83 | if (window.error) { 84 | fprintf(stderr, "%s\n", strerror(window.error)); 85 | return 1; 86 | } 87 | const uint64_t layer_id = window.value; 88 | 89 | for (int y = 0; y < height; ++y) { 90 | for (int x = 0; x < width; ++x) { 91 | uint32_t c = get_color(&image_data[bytes_per_pixel * (y * width + x)]); 92 | SyscallWinFillRectangle(layer_id | LAYER_NO_REDRAW, 93 | 4 + x, 24 + y, 1, 1, c); 94 | } 95 | } 96 | 97 | SyscallWinRedraw(layer_id); 98 | WaitEvent(); 99 | 100 | SyscallCloseWindow(layer_id); 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /apps/hex2bin/.gitignore: -------------------------------------------------------------------------------- 1 | /hex2bin 2 | /hex2bin-linux 3 | /*.o 4 | -------------------------------------------------------------------------------- /apps/hex2bin/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = hex2bin 2 | OBJS = hex2bin.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/hex2bin/hex2bin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | bool reverse = false; 7 | int size = 1; 8 | bool little_endian = false; 9 | 10 | // オプションを解析し、最初に見つけた非オプションの位置を返す 11 | int ParseArgs(int argc, char** argv) { 12 | for (int i = 1; i < argc; i++) { 13 | if (strcmp(argv[i], "-h") == 0) { 14 | printf("Usage: %s [options] [in-file]\n", argv[0]); 15 | printf("16 進数値をバイナリに変換し、標準出力へ表示する\n" 16 | " -h: このヘルプを表示する\n" 17 | " -s : 処理の単位となるバイト数\n" 18 | " size = 1 (default), 2, 4, 8\n" 19 | " -l: バイナリをリトルエンディアンとして扱う\n" 20 | " -r: 入出力を反転し、バイナリを 16 進ダンプする\n" 21 | ); 22 | return -1; 23 | } 24 | 25 | if (strcmp(argv[i], "-s") == 0) { 26 | i++; 27 | size = atoi(argv[i]); 28 | if (size != 1 && size != 2 && size != 4 && size != 8) { 29 | fprintf(stderr, "size が不正: %d\n", size); 30 | return -1; 31 | } 32 | } else if (strcmp(argv[i], "-l") == 0) { 33 | little_endian = true; 34 | } else if (strcmp(argv[i], "-r") == 0) { 35 | reverse = true; 36 | } else { 37 | return i; 38 | } 39 | } 40 | return 0; 41 | } 42 | 43 | // stdin から最大 n バイトを v に読み込み、読んだバイト数を返す 44 | int ReadBytes(uint8_t *v, int n, FILE* in) { 45 | for (int i = 0; i < n; i++) { 46 | int c = getc(in); 47 | if (c == EOF) { 48 | return i; 49 | } 50 | v[i] = c; 51 | } 52 | return n; 53 | } 54 | 55 | int main(int argc, char** argv) { 56 | FILE* in_file; 57 | 58 | int non_opt_index = ParseArgs(argc, argv); 59 | if (non_opt_index < 0) { 60 | return -non_opt_index; 61 | } else if (non_opt_index == 0) { 62 | in_file = stdin; 63 | } else { 64 | in_file = fopen(argv[non_opt_index], "rb"); 65 | if (in_file == NULL) { 66 | perror("failed to open in-file"); 67 | return 1; 68 | } 69 | } 70 | 71 | if (reverse) { 72 | int line_bytes = 0; 73 | for (;;) { 74 | uint8_t v[8] = {}; 75 | if (ReadBytes(v, size, in_file) == 0) { 76 | break; 77 | } 78 | if (line_bytes > 0) { 79 | putchar(' '); 80 | } 81 | for (int i = 0; i < size; i++) { 82 | printf("%02x", v[little_endian ? size - 1 - i : i]); 83 | } 84 | line_bytes += size; 85 | if (line_bytes >= 8) { 86 | putchar('\n'); 87 | line_bytes = 0; 88 | } 89 | } 90 | } else { 91 | uint64_t v; 92 | while (fscanf(in_file, "%lx", &v) == 1) { 93 | for (int i = 0; i < size; i++) { 94 | if (little_endian) { 95 | putchar(v & 0xff); 96 | v >>= 8; 97 | } else { 98 | putchar((v >> 8*(size - 1)) & 0xff); 99 | v <<= 8; 100 | } 101 | } 102 | } 103 | } 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /apps/hex2bin/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | clang++ -o hex2bin-linux hex2bin.cpp 4 | 5 | function hex2bin() { 6 | src="$1" 7 | want="$2" 8 | opt="${3:-}" 9 | got=$(echo $src | ./hex2bin-linux $opt | hexdump -v -e '/1 "%02x "') 10 | 11 | if [ "$want" = "$got" ] 12 | then 13 | echo "[ OK ]: ($opt) $src -> '$got'" 14 | else 15 | echo "[FAILED]: ($opt) $src -> '$got', want '$want'" 16 | fi 17 | } 18 | 19 | function bin2hex() { 20 | src="$1" 21 | want="$2" 22 | opt="${3:-}" 23 | got=$(echo $src | xxd -r -p | ./hex2bin-linux -r $opt) 24 | 25 | if [ "$want" = "$got" ] 26 | then 27 | echo "[ OK ]: ($opt) $src -> '$got'" 28 | else 29 | echo "[FAILED]: ($opt) $src -> '$got', want '$want'" 30 | fi 31 | } 32 | 33 | hex2bin "42 61 64" "42 61 64 " 34 | hex2bin "42 61 64" "42 61 64 " "-l" 35 | hex2bin "42 61 64" "42 00 61 00 64 00 " "-l -s 2" 36 | hex2bin "42 61 64" "00 42 00 61 00 64 " "-s 2" 37 | hex2bin "4261 64" "42 61 00 64 " "-s 2" 38 | hex2bin "f0420123" "f0 42 01 23 " "-s 4" 39 | hex2bin "f0420123" "23 01 " "-l -s 2" 40 | 41 | bin2hex "61 42 00 f0" "61 42 00 f0" 42 | bin2hex "61 42 00 f0" "6142 00f0" "-s 2" 43 | bin2hex "61 42 00 f0" "4261 f000" "-l -s 2" 44 | bin2hex "01 02 03 04" "0102 0304" "-s 2" 45 | bin2hex "01 02 03" "0102 0300" "-s 2" 46 | -------------------------------------------------------------------------------- /apps/large/.gitignore: -------------------------------------------------------------------------------- 1 | /large 2 | /large.o 3 | -------------------------------------------------------------------------------- /apps/large/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = large 2 | OBJS = large.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/large/large.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../syscall.h" 3 | 4 | char table[3 * 1024 * 1024]; 5 | 6 | int main(int argc, char** argv) { 7 | return atoi(argv[1]); 8 | } 9 | -------------------------------------------------------------------------------- /apps/library.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | extern int main(int, char**); 4 | 5 | void _start(int argc, char** argv) { 6 | SyscallExit(main(argc, argv)); 7 | } 8 | -------------------------------------------------------------------------------- /apps/lines/.gitignore: -------------------------------------------------------------------------------- 1 | /lines 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/lines/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = lines 2 | OBJS = lines.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/lines/lines.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | static constexpr int kRadius = 90; 6 | 7 | constexpr uint32_t Color(int deg) { 8 | if (deg <= 30) { 9 | return (255 * deg / 30 << 8) | 0xff0000; 10 | } else if (deg <= 60) { 11 | return (255 * (60 - deg) / 30) << 16 | 0x00ff00; 12 | } else if (deg <= 90) { 13 | return (255 * (deg - 60) / 30) | 0x00ff00; 14 | } else if (deg <= 120) { 15 | return (255 * (120 - deg) / 30) << 8 | 0x0000ff; 16 | } else if (deg <= 150) { 17 | return (255 * (deg - 120) / 30) << 16 | 0x0000ff; 18 | } else { 19 | return (255 * (180 - deg) / 30) | 0xff0000; 20 | } 21 | }; 22 | 23 | int main(int argc, char** argv) { 24 | auto [layer_id, err_openwin] 25 | = SyscallOpenWindow(kRadius * 2 + 10 + 8, kRadius + 28, 10, 10, "lines"); 26 | if (err_openwin) { 27 | return err_openwin; 28 | } 29 | 30 | const int x0 = 4, y0 = 24, x1 = 4 + kRadius + 10, y1 = 24 + kRadius; 31 | for (int deg = 0; deg <= 90; deg += 5) { 32 | const int x = kRadius * cos(M_PI * deg / 180.0); 33 | const int y = kRadius * sin(M_PI * deg / 180.0); 34 | SyscallWinDrawLine(layer_id, x0, y0, x0 + x, y0 + y, Color(deg)); 35 | SyscallWinDrawLine(layer_id, x1, y1, x1 + x, y1 - y, Color(deg + 90)); 36 | } 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /apps/mandel/.gitignore: -------------------------------------------------------------------------------- 1 | /mandel 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/mandel/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = mandel 2 | OBJS = mandel.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/mandel/mandel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../syscall.h" 7 | 8 | struct RGBColor { 9 | double r, g, b; 10 | }; 11 | 12 | constexpr std::array rgb_table{{ 13 | {0.06076 ,0.00000 ,0.11058}, // 波長: 380 nm 14 | {0.08700 ,0.00000 ,0.16790}, // 390 15 | {0.13772 ,0.00000 ,0.26354}, // 400 16 | {0.20707 ,0.00000 ,0.39852}, // 410 17 | {0.31129 ,0.00000 ,0.60684}, // 420 18 | {0.39930 ,0.00000 ,0.80505}, // 430 19 | {0.40542 ,0.00000 ,0.87684}, // 440 20 | {0.34444 ,0.00000 ,0.88080}, // 450 21 | {0.11139 ,0.00000 ,0.86037}, // 460 22 | {0.00000 ,0.15233 ,0.77928}, // 470 23 | {0.00000 ,0.38550 ,0.65217}, // 480 24 | {0.00000 ,0.49412 ,0.51919}, // 490 25 | {0.00000 ,0.59271 ,0.40008}, // 500 26 | {0.00000 ,0.69549 ,0.25749}, // 510 27 | {0.00000 ,0.77773 ,0.00000}, // 520 28 | {0.00000 ,0.81692 ,0.00000}, // 530 29 | {0.00000 ,0.82625 ,0.00000}, // 540 30 | {0.00000 ,0.81204 ,0.00000}, // 550 31 | {0.47369 ,0.77626 ,0.00000}, // 560 32 | {0.70174 ,0.71523 ,0.00000}, // 570 33 | {0.84922 ,0.62468 ,0.00000}, // 580 34 | {0.94726 ,0.49713 ,0.00000}, // 590 35 | {0.99803 ,0.31072 ,0.00000}, // 600 36 | {1.00000 ,0.00000 ,0.00000}, // 610 37 | {0.95520 ,0.00000 ,0.00000}, // 620 38 | {0.86620 ,0.00000 ,0.00000}, // 630 39 | {0.76170 ,0.00000 ,0.00000}, // 640 40 | {0.64495 ,0.00000 ,0.00000}, // 650 41 | {0.52857 ,0.00000 ,0.00000}, // 660 42 | {0.41817 ,0.00000 ,0.00000}, // 670 43 | {0.33202 ,0.00000 ,0.00000}, // 680 44 | {0.25409 ,0.00000 ,0.00000}, // 690 45 | {0.19695 ,0.00000 ,0.00000}, // 700 46 | {0.15326 ,0.00000 ,0.00000}, // 710 47 | {0.11902 ,0.00000 ,0.00000}, // 720 48 | {0.09063 ,0.00000 ,0.00000}, // 730 49 | {0.06898 ,0.00000 ,0.00000}, // 740 50 | {0.05150 ,0.00000 ,0.00000}, // 750 51 | {0.04264 ,0.00000 ,0.00000}, // 760 52 | {0.03666 ,0.00000 ,0.00794}, // 770 53 | {0.00000 ,0.00000 ,0.00000}, // 780 54 | }}; 55 | 56 | constexpr int kWaveLenStep = 10, kWaveLenMin = 380, kWaveLenMax = 780; 57 | 58 | // 光の波長を RGB 値に変換する関数 59 | // 参考: http://k-hiura.cocolog-nifty.com/blog/2009/06/post-9c5b.html 60 | uint32_t WaveLenToColor(int wlen) { 61 | auto conv_to_uint32_t = [](RGBColor color) { 62 | return ((uint32_t)(color.r * 0xff) << 16) | 63 | ((uint32_t)(color.g * 0xff) << 8) | 64 | (uint32_t)(color.b * 0xff); 65 | }; 66 | 67 | wlen = std::max(kWaveLenMin, wlen); 68 | wlen = std::min(kWaveLenMax, wlen); 69 | 70 | // 与えられた周波数が10の倍数の場合、対応するRGB値を rgb_table から直接取ってくる 71 | const int widx = (wlen - kWaveLenMin) / kWaveLenStep; 72 | const int wlen_sub = (wlen - kWaveLenMin) - (widx * kWaveLenStep); 73 | if (wlen_sub == 0) { 74 | return conv_to_uint32_t(rgb_table[widx]); 75 | } 76 | 77 | // 周波数が10の倍数でない場合、rgb_table から前後の周波数に対応するRGB値を取得して 78 | // 線形補間する 79 | RGBColor ret{ 80 | rgb_table[widx].r, 81 | rgb_table[widx].g, 82 | rgb_table[widx].b 83 | }; 84 | const double rate = (double)wlen_sub / kWaveLenStep; 85 | ret.r += (rgb_table[widx+1].r - rgb_table[widx].r) * rate; 86 | ret.g += (rgb_table[widx+1].g - rgb_table[widx].g) * rate; 87 | ret.b += (rgb_table[widx+1].b - rgb_table[widx].b) * rate; 88 | 89 | return conv_to_uint32_t(ret); 90 | } 91 | 92 | int MandelConverge(std::complex z) { 93 | const auto c = z; 94 | int n = 0; 95 | const int nmax = 100; 96 | 97 | for (; n < nmax && std::norm(z) <= 4; ++n) { 98 | z = z*z + c; 99 | } 100 | 101 | return n; 102 | } 103 | 104 | void WaitEvent() { 105 | AppEvent events[1]; 106 | while (true) { 107 | auto [ n, err ] = SyscallReadEvent(events, 1); 108 | if (err) { 109 | fprintf(stderr, "ReadEvent failed: %s\n", strerror(err)); 110 | return; 111 | } 112 | if (events[0].type == AppEvent::kQuit) { 113 | return; 114 | } 115 | } 116 | } 117 | 118 | constexpr int kWidth = 78*4, kHeight = 52*4; 119 | 120 | int main(int argc, char** argv) { 121 | auto [layer_id, err_openwin] 122 | = SyscallOpenWindow(kWidth + 8, kHeight + 28, 10, 10, "mandel"); 123 | if (err_openwin) { 124 | return err_openwin; 125 | } 126 | 127 | // xmin: 実部の最小値, ymin: 虚部の最小値 128 | const double xmin = -2.3, ymin = -1.3; 129 | const double xstep = 0.0125, ystep = 0.0125; 130 | 131 | for (int y = 0; y < kHeight; ++y) { 132 | for (int x = 0; x < kWidth; ++x) { 133 | // 漸化式の計算が収束するまでの再帰回数 depth (100 を上限とする) を得る 134 | int depth = MandelConverge({xmin + xstep*x, ymin + ystep*y}); 135 | 136 | SyscallWinFillRectangle( 137 | layer_id | LAYER_NO_REDRAW, 138 | 4+x, 24+y, 1, 1, 139 | WaveLenToColor(depth * 4 + kWaveLenMin) 140 | ); 141 | } 142 | } 143 | SyscallWinRedraw(layer_id); 144 | 145 | WaitEvent(); 146 | SyscallCloseWindow(layer_id); 147 | 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /apps/mmap/.gitignore: -------------------------------------------------------------------------------- 1 | /mmap 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/mmap/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = mmap 2 | OBJS = mmap.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/mmap/mmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | int main(int argc, char** argv) { 6 | SyscallResult res = SyscallOpenFile("/memmap", O_RDONLY); 7 | if (res.error) { 8 | return res.error; 9 | } 10 | const int fd = res.value; 11 | size_t file_size; 12 | res = SyscallMapFile(fd, &file_size, 0); 13 | if (res.error) { 14 | return res.error; 15 | } 16 | 17 | char* p = reinterpret_cast(res.value); 18 | 19 | for (size_t i = 0; i < file_size; ++i) { 20 | printf("%c", p[i]); 21 | } 22 | printf("\nread from mapped file (%lu bytes)\n", file_size); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /apps/more/.gitignore: -------------------------------------------------------------------------------- 1 | /more 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/more/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = more 2 | OBJS = more.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/more/more.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../syscall.h" 7 | 8 | AppEvent WaitKey() { 9 | AppEvent events[1]; 10 | while (true) { 11 | auto [ n, err ] = SyscallReadEvent(events, 1); 12 | if (err) { 13 | fprintf(stderr, "ReadEvent failed: %s\n", strerror(err)); 14 | exit(1); 15 | } 16 | 17 | if (events[0].type == AppEvent::kQuit) { 18 | exit(0); 19 | } 20 | if (events[0].type == AppEvent::kKeyPush && 21 | events[0].arg.keypush.press) { 22 | return events[0]; 23 | } 24 | } 25 | } 26 | 27 | int main(int argc, char** argv) { 28 | int page_size = 10; 29 | int arg_file = 1; 30 | if (argc >= 2 && argv[1][0] == '-' && isdigit(argv[1][1])) { 31 | page_size = atoi(&argv[1][1]); 32 | ++arg_file; 33 | } 34 | 35 | FILE* fp = stdin; 36 | if (argc > arg_file) { 37 | fp = fopen(argv[arg_file], "r"); 38 | if (fp == nullptr) { 39 | fprintf(stderr, "failed to open '%s'\n", argv[arg_file]); 40 | return 1; 41 | } 42 | } 43 | 44 | std::vector lines{}; 45 | char line[256]; 46 | while (fgets(line, sizeof(line), fp)) { 47 | lines.emplace_back(line); 48 | } 49 | 50 | for (int i = 0; i < lines.size(); ++i) { 51 | if (i > 0 && (i % page_size) == 0) { 52 | fputs("---more---\n", stderr); 53 | WaitKey(); 54 | } 55 | fputs(lines[i].c_str(), stdout); 56 | } 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /apps/newlib_support.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "syscall.h" 9 | 10 | int close(int fd) { 11 | errno = EBADF; 12 | return -1; 13 | } 14 | 15 | int fstat(int fd, struct stat* buf) { 16 | errno = EBADF; 17 | return -1; 18 | } 19 | 20 | pid_t getpid(void) { 21 | return 0; 22 | } 23 | 24 | int isatty(int fd) { 25 | struct SyscallResult res = SyscallIsTerminal(fd); 26 | if (res.error == 0) { 27 | return res.value; 28 | } 29 | errno = res.error; 30 | return -1; 31 | } 32 | 33 | int kill(pid_t pid, int sig) { 34 | errno = EPERM; 35 | return -1; 36 | } 37 | 38 | off_t lseek(int fd, off_t offset, int whence) { 39 | errno = EBADF; 40 | return -1; 41 | } 42 | 43 | int open(const char* path, int flags) { 44 | struct SyscallResult res = SyscallOpenFile(path, flags); 45 | if (res.error == 0) { 46 | return res.value; 47 | } 48 | errno = res.error; 49 | return -1; 50 | } 51 | 52 | int posix_memalign(void** memptr, size_t alignment, size_t size) { 53 | void* p = malloc(size + alignment - 1); 54 | if (!p) { 55 | return ENOMEM; 56 | } 57 | uintptr_t addr = (uintptr_t)p; 58 | *memptr = (void*)((addr + alignment - 1) & ~(uintptr_t)(alignment - 1)); 59 | return 0; 60 | } 61 | 62 | ssize_t read(int fd, void* buf, size_t count) { 63 | struct SyscallResult res = SyscallReadFile(fd, buf, count); 64 | if (res.error == 0) { 65 | return res.value; 66 | } 67 | errno = res.error; 68 | return -1; 69 | } 70 | 71 | caddr_t sbrk(int incr) { 72 | static uint64_t dpage_end = 0; 73 | static uint64_t program_break = 0; 74 | 75 | if (dpage_end == 0 || dpage_end < program_break + incr) { 76 | int num_pages = (incr + 4095) / 4096; 77 | struct SyscallResult res = SyscallDemandPages(num_pages, 0); 78 | if (res.error) { 79 | errno = ENOMEM; 80 | return (caddr_t)-1; 81 | } 82 | program_break = res.value; 83 | dpage_end = res.value + 4096 * num_pages; 84 | } 85 | 86 | const uint64_t prev_break = program_break; 87 | program_break += incr; 88 | return (caddr_t)prev_break; 89 | } 90 | 91 | ssize_t write(int fd, const void* buf, size_t count) { 92 | struct SyscallResult res = SyscallPutString(fd, buf, count); 93 | if (res.error == 0) { 94 | return res.value; 95 | } 96 | errno = res.error; 97 | return -1; 98 | } 99 | 100 | void _exit(int status) { 101 | SyscallExit(status); 102 | } 103 | -------------------------------------------------------------------------------- /apps/onlyhlt/.gitignore: -------------------------------------------------------------------------------- 1 | /onlyhlt 2 | -------------------------------------------------------------------------------- /apps/onlyhlt/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = onlyhlt 2 | 3 | .PHONY: all 4 | all: $(TARGET) 5 | 6 | onlyhlt: onlyhlt.asm Makefile 7 | nasm -f bin -o $@ $< 8 | -------------------------------------------------------------------------------- /apps/onlyhlt/onlyhlt.asm: -------------------------------------------------------------------------------- 1 | bits 64 2 | section .text 3 | 4 | loop: 5 | hlt 6 | jmp loop 7 | -------------------------------------------------------------------------------- /apps/paint/.gitignore: -------------------------------------------------------------------------------- 1 | /paint 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/paint/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = paint 2 | OBJS = paint.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/paint/paint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | static const int kWidth = 200, kHeight = 130; 6 | 7 | bool IsInside(int x, int y) { 8 | return 4 <= x && x < 4 + kWidth && 24 <= y && y < 24 + kHeight; 9 | } 10 | 11 | int main(int argc, char** argv) { 12 | auto [layer_id, err_openwin] 13 | = SyscallOpenWindow(kWidth + 8, kHeight + 28, 10, 10, "paint"); 14 | if (err_openwin) { 15 | return err_openwin; 16 | } 17 | 18 | AppEvent events[1]; 19 | bool press = false; 20 | while (true) { 21 | auto [ n, err ] = SyscallReadEvent(events, 1); 22 | if (err) { 23 | printf("ReadEvent failed: %s\n", strerror(err)); 24 | break; 25 | } 26 | if (events[0].type == AppEvent::kQuit) { 27 | break; 28 | } else if (events[0].type == AppEvent::kMouseMove) { 29 | auto& arg = events[0].arg.mouse_move; 30 | const auto prev_x = arg.x - arg.dx, prev_y = arg.y - arg.dy; 31 | if (press && IsInside(prev_x, prev_y) && IsInside(arg.x, arg.y)) { 32 | SyscallWinDrawLine(layer_id, prev_x, prev_y, arg.x, arg.y, 0x000000); 33 | } 34 | } else if (events[0].type == AppEvent::kMouseButton) { 35 | auto& arg = events[0].arg.mouse_button; 36 | if (arg.button == 0) { 37 | press = arg.press; 38 | SyscallWinFillRectangle(layer_id, arg.x, arg.y, 1, 1, 0x000000); 39 | } 40 | } else { 41 | printf("unknown event: type = %d\n", events[0].type); 42 | } 43 | } 44 | 45 | SyscallCloseWindow(layer_id); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /apps/readfile/.gitignore: -------------------------------------------------------------------------------- 1 | /readfile 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/readfile/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = readfile 2 | OBJS = readfile.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/readfile/readfile.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | const char* path = "/memmap"; 5 | if (argc >= 2) { 6 | path = argv[1]; 7 | } 8 | 9 | FILE* fp = fopen(path, "r"); 10 | if (fp == nullptr) { 11 | printf("failed to open: %s\n", path); 12 | return 1; 13 | } 14 | 15 | char line[256]; 16 | for (int i = 0; i < 3; ++i) { 17 | if (fgets(line, sizeof(line), fp) == nullptr) { 18 | printf("failed to get a line\n"); 19 | return 1; 20 | } 21 | printf("%s", line); 22 | } 23 | printf("----\n"); 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /apps/rpn/.gitignore: -------------------------------------------------------------------------------- 1 | /rpn 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/rpn/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = rpn 2 | OBJS = rpn.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/rpn/rpn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../syscall.h" 5 | 6 | int stack_ptr; 7 | long stack[100]; 8 | 9 | long Pop() { 10 | long value = stack[stack_ptr]; 11 | --stack_ptr; 12 | return value; 13 | } 14 | 15 | void Push(long value) { 16 | ++stack_ptr; 17 | stack[stack_ptr] = value; 18 | } 19 | 20 | int main(int argc, char** argv) { 21 | stack_ptr = -1; 22 | 23 | for (int i = 1; i < argc; ++i) { 24 | if (strcmp(argv[i], "+") == 0) { 25 | long b = Pop(); 26 | long a = Pop(); 27 | Push(a + b); 28 | } else if (strcmp(argv[i], "-") == 0) { 29 | long b = Pop(); 30 | long a = Pop(); 31 | Push(a - b); 32 | } else { 33 | long a = atol(argv[i]); 34 | Push(a); 35 | } 36 | } 37 | long result = 0; 38 | if (stack_ptr >= 0) { 39 | result = Pop(); 40 | } 41 | 42 | printf("%ld\n", result); 43 | return static_cast(result); 44 | } 45 | -------------------------------------------------------------------------------- /apps/sort/.gitignore: -------------------------------------------------------------------------------- 1 | /sort 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/sort/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = sort 2 | OBJS = sort.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/sort/sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char** argv) { 6 | FILE* fp = stdin; 7 | if (argc >= 2) { 8 | fp = fopen(argv[1], "r"); 9 | if (fp == nullptr) { 10 | fprintf(stderr, "failed to open '%s'\n", argv[1]); 11 | return 1; 12 | } 13 | } 14 | 15 | std::vector lines; 16 | char line[1024]; 17 | while (fgets(line, sizeof(line), fp)) { 18 | lines.push_back(line); 19 | } 20 | 21 | auto comp = [](const std::string& a, const std::string& b) { 22 | for (int i = 0; i < std::min(a.length(), b.length()); ++i) { 23 | if (a[i] < b[i]) { 24 | return true; 25 | } else if (a[i] > b[i]) { 26 | return false; 27 | } 28 | } 29 | return a.length() < b.length(); 30 | }; 31 | 32 | std::sort(lines.begin(), lines.end(), comp); 33 | for (auto& line : lines) { 34 | printf("%s", line.c_str()); 35 | } 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /apps/stars/.gitignore: -------------------------------------------------------------------------------- 1 | /stars 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/stars/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = stars 2 | OBJS = stars.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/stars/stars.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../syscall.h" 3 | 4 | static constexpr int kWidth = 100, kHeight = 100; 5 | 6 | void WaitEvent() { 7 | AppEvent events[1]; 8 | while (true) { 9 | auto [ n, err ] = SyscallReadEvent(events, 1); 10 | if (err) { 11 | fprintf(stderr, "ReadEvent failed: %s\n", strerror(err)); 12 | return; 13 | } 14 | if (events[0].type == AppEvent::kQuit) { 15 | return; 16 | } 17 | } 18 | } 19 | 20 | int main(int argc, char** argv) { 21 | auto [layer_id, err_openwin] 22 | = SyscallOpenWindow(kWidth + 8, kHeight + 28, 10, 10, "stars"); 23 | if (err_openwin) { 24 | return err_openwin; 25 | } 26 | 27 | SyscallWinFillRectangle(layer_id | LAYER_NO_REDRAW, 28 | 4, 24, kWidth, kHeight, 0x000000); 29 | 30 | int num_stars = 100; 31 | if (argc >= 2) { 32 | num_stars = atoi(argv[1]); 33 | } 34 | 35 | auto [tick_start, timer_freq] = SyscallGetCurrentTick(); 36 | 37 | std::default_random_engine rand_engine; 38 | std::uniform_int_distribution x_dist(0, kWidth - 2), y_dist(0, kHeight - 2); 39 | for (int i = 0; i < num_stars; ++i) { 40 | int x = x_dist(rand_engine); 41 | int y = y_dist(rand_engine); 42 | SyscallWinFillRectangle(layer_id | LAYER_NO_REDRAW, 43 | 4 + x, 24 + y, 2, 2, 0xfff100); 44 | } 45 | SyscallWinRedraw(layer_id); 46 | 47 | auto tick_end = SyscallGetCurrentTick(); 48 | printf("%d stars in %lu ms.\n", 49 | num_stars, 50 | (tick_end.value - tick_start) * 1000 / timer_freq); 51 | 52 | WaitEvent(); 53 | SyscallCloseWindow(layer_id); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /apps/syscall.asm: -------------------------------------------------------------------------------- 1 | bits 64 2 | section .text 3 | 4 | %macro define_syscall 2 5 | global Syscall%1 6 | Syscall%1: 7 | mov rax, %2 8 | mov r10, rcx 9 | syscall 10 | ret 11 | %endmacro 12 | 13 | define_syscall LogString, 0x80000000 14 | define_syscall PutString, 0x80000001 15 | define_syscall Exit, 0x80000002 16 | define_syscall OpenWindow, 0x80000003 17 | define_syscall WinWriteString, 0x80000004 18 | define_syscall WinFillRectangle, 0x80000005 19 | define_syscall GetCurrentTick, 0x80000006 20 | define_syscall WinRedraw, 0x80000007 21 | define_syscall WinDrawLine, 0x80000008 22 | define_syscall CloseWindow, 0x80000009 23 | define_syscall ReadEvent, 0x8000000a 24 | define_syscall CreateTimer, 0x8000000b 25 | define_syscall OpenFile, 0x8000000c 26 | define_syscall ReadFile, 0x8000000d 27 | define_syscall DemandPages, 0x8000000e 28 | define_syscall MapFile, 0x8000000f 29 | define_syscall IsTerminal, 0x80000010 30 | -------------------------------------------------------------------------------- /apps/syscall.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | #include 3 | #include 4 | 5 | extern "C" { 6 | #else 7 | #include 8 | #include 9 | #endif 10 | 11 | #include "../kernel/logger.hpp" 12 | #include "../kernel/app_event.hpp" 13 | 14 | struct SyscallResult { 15 | uint64_t value; 16 | int error; 17 | }; 18 | 19 | struct SyscallResult SyscallLogString(enum LogLevel level, const char* message); 20 | struct SyscallResult SyscallPutString(int fd, const char* s, size_t len); 21 | void SyscallExit(int exit_code) __attribute__((noreturn)); 22 | struct SyscallResult SyscallOpenWindow(int w, int h, int x, int y, const char* title); 23 | 24 | #define LAYER_NO_REDRAW (0x00000001ull << 32) 25 | struct SyscallResult SyscallWinWriteString( 26 | uint64_t layer_id_flags, int x, int y, uint32_t color, const char* s); 27 | struct SyscallResult SyscallWinFillRectangle( 28 | uint64_t layer_id_flags, int x, int y, int w, int h, uint32_t color); 29 | struct SyscallResult SyscallGetCurrentTick(); 30 | struct SyscallResult SyscallWinRedraw(uint64_t layer_id_flags); 31 | struct SyscallResult SyscallWinDrawLine( 32 | uint64_t layer_id_flags, int x0, int y0, int x1, int y1, uint32_t color); 33 | 34 | struct SyscallResult SyscallCloseWindow(uint64_t layer_id_flags); 35 | struct SyscallResult SyscallReadEvent(struct AppEvent* events, size_t len); 36 | 37 | #define TIMER_ONESHOT_REL 1 38 | #define TIMER_ONESHOT_ABS 0 39 | struct SyscallResult SyscallCreateTimer( 40 | unsigned int type, int timer_value, unsigned long timeout_ms); 41 | 42 | struct SyscallResult SyscallOpenFile(const char* path, int flags); 43 | struct SyscallResult SyscallReadFile(int fd, void* buf, size_t count); 44 | struct SyscallResult SyscallDemandPages(size_t num_pages, int flags); 45 | struct SyscallResult SyscallMapFile(int fd, size_t* file_size, int flags); 46 | struct SyscallResult SyscallIsTerminal(int fd); 47 | 48 | #ifdef __cplusplus 49 | } // extern "C" 50 | #endif 51 | -------------------------------------------------------------------------------- /apps/tedit/.gitignore: -------------------------------------------------------------------------------- 1 | /tedit 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/tedit/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = tedit 2 | OBJS = tedit.o 3 | CXXFLAGS += -DNO_FCLOSE_CHECK 4 | include ../Makefile.elfapp 5 | -------------------------------------------------------------------------------- /apps/timer/.gitignore: -------------------------------------------------------------------------------- 1 | /timer 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/timer/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = timer 2 | OBJS = timer.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/timer/timer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../syscall.h" 5 | 6 | int main(int argc, char** argv) { 7 | if (argc <= 1) { 8 | printf("Usage: timer \n"); 9 | return 1; 10 | } 11 | 12 | const unsigned long duration_ms = atoi(argv[1]); 13 | const auto timeout = SyscallCreateTimer(TIMER_ONESHOT_REL, 1, duration_ms); 14 | printf("timer created. timeout = %lu\n", timeout.value); 15 | 16 | AppEvent events[1]; 17 | while (true) { 18 | SyscallReadEvent(events, 1); 19 | if (events[0].type == AppEvent::kTimerTimeout) { 20 | printf("%lu msecs elapsed!\n", duration_ms); 21 | break; 22 | } else { 23 | printf("unknown event: type = %d\n", events[0].type); 24 | } 25 | } 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /apps/tview/.gitignore: -------------------------------------------------------------------------------- 1 | /tview 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/tview/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = tview 2 | OBJS = tview.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/winhello/.gitignore: -------------------------------------------------------------------------------- 1 | /winhello 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/winhello/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = winhello 2 | OBJS = winhello.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/winhello/winhello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | int main(int argc, char** argv) { 6 | auto [layer_id, err_openwin] 7 | = SyscallOpenWindow(200, 100, 10, 10, "winhello"); 8 | if (err_openwin) { 9 | return err_openwin; 10 | } 11 | 12 | SyscallWinWriteString(layer_id, 7, 24, 0xc00000, "hello world!"); 13 | SyscallWinWriteString(layer_id, 24, 40, 0x00c000, "hello world!"); 14 | SyscallWinWriteString(layer_id, 40, 56, 0x0000c0, "hello world!"); 15 | 16 | AppEvent events[1]; 17 | while (true) { 18 | auto [ n, err ] = SyscallReadEvent(events, 1); 19 | if (err) { 20 | printf("ReadEvent failed: %s\n", strerror(err)); 21 | break; 22 | } 23 | if (events[0].type == AppEvent::kQuit) { 24 | break; 25 | } else if (events[0].type == AppEvent::kMouseMove || 26 | events[0].type == AppEvent::kMouseButton || 27 | events[0].type == AppEvent::kKeyPush) { 28 | // ignore 29 | } else { 30 | printf("unknown event: type = %d\n", events[0].type); 31 | } 32 | } 33 | SyscallCloseWindow(layer_id); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /apps/winjpn/.gitignore: -------------------------------------------------------------------------------- 1 | /winjpn 2 | /*.o 3 | -------------------------------------------------------------------------------- /apps/winjpn/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = winjpn 2 | OBJS = winjpn.o 3 | include ../Makefile.elfapp 4 | -------------------------------------------------------------------------------- /apps/winjpn/winjpn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../syscall.h" 4 | 5 | int main(int argc, char** argv) { 6 | auto [layer_id, err_openwin] 7 | = SyscallOpenWindow(200, 100, 10, 10, u8"こんにちは"); 8 | if (err_openwin) { 9 | return err_openwin; 10 | } 11 | 12 | SyscallWinWriteString(layer_id, 7, 24, 0xc00000, u8"おはよう 世界!"); 13 | SyscallWinWriteString(layer_id, 24, 40, 0x00c000, u8"こんにちは 世界!"); 14 | SyscallWinWriteString(layer_id, 40, 56, 0x0000c0, u8"こんばんは 世界!"); 15 | 16 | AppEvent events[1]; 17 | while (true) { 18 | auto [ n, err ] = SyscallReadEvent(events, 1); 19 | if (err) { 20 | printf("ReadEvent failed: %s\n", strerror(err)); 21 | break; 22 | } 23 | if (events[0].type == AppEvent::kQuit) { 24 | break; 25 | } else if (events[0].type == AppEvent::kMouseMove || 26 | events[0].type == AppEvent::kMouseButton || 27 | events[0].type == AppEvent::kKeyPush) { 28 | // ignore 29 | } else { 30 | printf("unknown event: type = %d\n", events[0].type); 31 | } 32 | } 33 | SyscallCloseWindow(layer_id); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eu 2 | 3 | make ${MAKE_OPTS:-} -C kernel kernel.elf 4 | 5 | for MK in $(ls apps/*/Makefile) 6 | do 7 | APP_DIR=$(dirname $MK) 8 | APP=$(basename $APP_DIR) 9 | make ${MAKE_OPTS:-} -C $APP_DIR $APP 10 | done 11 | 12 | DISK_IMG=./disk.img MIKANOS_DIR=$PWD $HOME/osbook/devenv/make_mikanos_image.sh 13 | 14 | if [ "${1:-}" = "run" ] 15 | then 16 | $HOME/osbook/devenv/run_image.sh ./disk.img 17 | fi 18 | -------------------------------------------------------------------------------- /docs/how-to-connect-USB-device-to-MikanOS.md: -------------------------------------------------------------------------------- 1 | # USB デバイスを MikanOS に接続する方法 2 | 3 | この文書では、ホスト OS に接続された USB デバイスを MikanOS に接続する方法を説明 4 | します。Windows 11 上の WSL2 で動作する Ubuntu 20.04 を前提としますが、他のバー 5 | ジョンでも参考になると思います。 6 | 7 | ## Windows に接続された USB デバイスを Ubuntu に認識させる方法 8 | 9 | IP ネットワークで USB デバイスを共有する USB/IP という仕組みを用いると、Windows 10 | (ホスト OS)に接続された USB デバイスを Ubuntu on WSL から使えるようにできます。 11 | 詳しい方法は [USB デバイスを接続する | Microsoft Docs][usbipd] を参照してくださ 12 | い。 13 | 14 | [usbipd]: https://docs.microsoft.com/ja-jp/windows/wsl/connect-usb 15 | 16 | 以降では usbipd コマンドが使用可能になっている前提で説明します。WSL を使わず、実 17 | 機で Ubuntu を実行している場合はこの手順をスキップして次に進んでください。 18 | 19 | tools/attach_usbdev.sh は usbipd コマンドを使用して USB デバイスを Ubuntu に接続 20 | するためのスクリプトです。引数無しで実行すると、Windows に接続されている USB デ 21 | バイスのリストが表示されます。 22 | 23 | $ tools/attach_usbdev.sh 24 | Please specify a bus id to be attached 25 | BUSID DEVICE STATE 26 | ... 27 | 2-4 USB シリアル デバイス Not attached 28 | 29 | 例えば BUSID = 2-4 が希望する USB デバイスだとします。希望するデバイスがどれか分 30 | からない場合、USB デバイスを抜いた状態と挿した状態それぞれで attach_usbdev.sh を 31 | 実行すれば判明すると思います。 32 | 33 | Ubuntu に接続したい USB デバイスの BUSID が分かったら、それを引数としてもう一度 34 | スクリプトを実行します。 35 | 36 | $ tools/attach_usbdev.sh 2-4 37 | Attaching a specified device: BUS_ID = 2-4 38 | 39 | 実行すると、「このアプリがデバイスに変更を加えることを許可しますか?」という 40 | Windows のユーザーアカウント制御ダイアログが出ますので「はい」を選んでください。 41 | 次に「[sudo] password for ユーザー名:」のようなプロンプトが表示されると思います。 42 | ここには Ubuntu のログインパスワードを入力してください(sudo コマンドの実行時に 43 | 入力するパスワードです)。 44 | 45 | これで指定した USB デバイスが Ubuntu に接続されたはずです。attach_usbdev.sh を実 46 | 行してきちんと接続されたことを確認してください。STATE が Attached になっていれば 47 | 成功です。 48 | 49 | $ tools/attach_usbdev.sh 50 | Please specify a bus id to be attached 51 | BUSID DEVICE STATE 52 | ... 53 | 2-4 USB シリアル デバイス Attached - Ubuntu-20.04 54 | 55 | ## MikanOS に USB デバイスを接続する 56 | 57 | Ubuntu に USB デバイスが接続されたら、lsusb コマンドでバス番号とデバイス番号を確 58 | 認します。 59 | 60 | $ lsusb 61 | Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub 62 | Bus 001 Device 003: ID 04d8:000a Microchip Technology, Inc. CDC RS-232 Emulation Demo 63 | Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub 64 | 65 | 実機で Ubuntu を実行している場合はもっとたくさんのデバイスが表示されると思います。 66 | 上記の例は Ubuntu on WSL なので、最低限のデバイスしか見えていませんね。「Linux 67 | Foundation」と書いてあるデバイスは Ubuntu に最初から接続されているものです。ここ 68 | では「Microchip Technology, Inc.」と書いてあるデバイスを MikanOS に接続するとし 69 | て説明を進めます。 70 | 71 | MikanOS を動かす QEMU がこの USB デバイスを読み書きできるようにするため、デバイ 72 | スファイルのパーミッションを調整します。lsusb の出力で Bus と Device の番号を調 73 | べ(ここでは Bus 001、Device 003)、デバイスファイルを探します。この USB デバイ 74 | スに対応するデバイスファイルは /dev/bus/usb// にあります。 75 | 76 | $ ls -l /dev/bus/usb/001/003 77 | crw------- 1 root root 189, 2 Jul 6 17:46 /dev/bus/usb/001/003 78 | 79 | 初期状態では、パーミッションが 600(rw-------)になっていることが分かります。一 80 | 般ユーザーが読み書きできるよう、666(rw-rw-rw-)に変更します。 81 | 82 | $ sudo chmod 666 /dev/bus/usb/001/003 83 | 84 | ここまできたら、QEMU に USB デバイスを接続するオプションを渡して MikanOS を起動 85 | すれば、MikanOS に USB デバイスを接続することができます。QEMU のオプションは 86 | `-device usb-host,hostbus=,hostaddr=` という形式です。Bus=1、Device 87 | =3 ですので、最終的に次のコマンドとなります。 88 | 89 | QEMU_OPTS="-device usb-host,hostbus=1,hostaddr=3" ./run.sh 90 | 91 | run.sh は MikanOS を手軽に実行するためのスクリプトです。QEMU_OPTS 変数に指定した 92 | 文字列は QEMU のオプションとして渡されます。 93 | 94 | ## USB シリアル変換の速度設定 95 | 96 | MikanOS は現在、CDC ACM という USB クラスを試験的にサポートしています。CDC ACM 97 | は通信を主目的とする USB デバイスのためのクラスで、USB シリアル変換やイーサネッ 98 | トなどの通信デバイスが該当します。先の例で接続した「CDC RS-232 Emulation Demo」 99 | は USB とシリアル通信(RS-232)を変換するデバイスです。 100 | 101 | シリアル通信は通信速度を相手側デバイスと合わせなければ正しく通信できません。 102 | MikanOS にはまだ CDC ACM にしたがって通信速度を設定する機能がありませんので、外 103 | 部から通信速度を設定する必要があります。CDC ACM 機器が Linux に接続されると、初 104 | 期値として 9600bps に設定されるようです。それ以外の速度に変更するためには、QEMU 105 | を起動する前に次のコマンドを実行します。 106 | 107 | $ sudo stty -F /dev/ttyACM0 115200 108 | 109 | `/dev/ttyACM0` は実際のデバイスファイル名に書き換えてください。`115200` はお好み 110 | の通信速度(ボーレート)に変えてください。 111 | 112 | どうやら QEMU を終了すると設定が 9600 のデフォルト値に戻ってしまうようですので、 113 | QEMU を起動する前に毎回上記のコマンドを実行する必要があります。 114 | -------------------------------------------------------------------------------- /docs/how-to-send-pull-request.md: -------------------------------------------------------------------------------- 1 | # プルリクエストの送り方 2 | 3 | GitHub で MikanOS に対してプルリクエストを送る方法を説明します。 4 | 5 | ## プルリクエストとは 6 | 7 | プルリクエストとは、あるリポジトリに対し、変更を提案することです。Git のプル操作を、リポジトリ所有者に対して要請(リクエスト)することから、このような名前になっています。 8 | 9 | MikanOS のバグを修正、あるいは新機能を追加するような場合に、プルリクエストを送ることができます。 10 | リポジトリ所有者(uchan-nos)は、プルリクエストを受け取るとその変更提案が妥当かどうかをチェックし、妥当であれば変更を受け入れます。 11 | 12 | Issues でバグの報告や新機能の要望をするだけの場合に比べて、プルリクエストでは具体的なソースコードの変更提案を行うことができるため、迅速にバグの修正や新機能の追加ができる可能性が高くなります。 13 | また、自分のソースコードが MikanOS に取り込まれることで、晴れて MikanOS 作者の 1 人となります。 14 | 15 | ## プルリクエストの出し方 16 | 17 | ここで説明するのは、MikanOS に限らず、GitHub で一般に通用する方法です。 18 | 他の OSS に対して修正提案をする場合でも役立つでしょう。 19 | 20 | プルリクエストを出す大まかな流れは次の通りです。 21 | 22 | 1. mikanos リポジトリを自分のアカウントへ Fork する 23 | 2. Fork したリポジトリに対して変更をコミットする 24 | 3. Fork したリポジトリから、元のリポジトリへのプルリクエストを作る 25 | 26 | 1 と 3 の手順は、uchan-nos/mikanos リポジトリへの書き込み権限を持たない開発者が mikanos リポジトリを変更するために必要な手順です。 27 | uchan-nos/mikanos リポジトリは一部の開発者にのみ書き込み権限が付与されており、その他の開発者は読み込み権限しかありません。 28 | そのため、mikanos リポジトリに変更を加えるためには、いったん、mikanos リポジトリを自分のアカウント配下へと Fork する必要があるのです。 29 | 30 | 2 の手順で、mikanos リポジトリに対し必要な修正を行います。 31 | 修正を行う際は、意味のまとまりが綺麗になるようにコミットを適切に分割するなど、一般的な開発マナーを守ってください。 32 | 修正が完了したら、3 の手順によりプルリクエストを作成し、提出します。 33 | 34 | ### mikanos リポジトリを自分のアカウントへ Fork する 35 | 36 | mikanos リポジトリを GitHub で開くと、「Fork」と書かれたボタンがあるはずです。 37 | 38 | ![GitHub Fork Button](./images/github-fork-button.png) 39 | 40 | ここをクリックして、自分のアカウントへ Fork してください。 41 | Fork 先に複数の選択肢が出るかもしれませんが、特にこだわりが無ければ自分のアカウントへ Fork すれば OK です。 42 | 43 | Fork は通常、数秒だけ時間がかかります。Fork 中は次のような表示になりますから、終わるまで待ちましょう。 44 | 45 | ![GitHub Fork Progress](./images/github-fork-progress.png) 46 | 47 | Fork が完了すると、自分のアカウントに mikanos リポジトリが複製され、「forked from uchan-nos/mikanos」と Fork 元が表示されます。 48 | 49 | ![GitHub Fork Finished](./images/github-forked-from.png) 50 | 51 | (画像は sudabon さんが、ご自身のリポジトリへ Fork したときの表示です。sudabon さん、画像提供ありがとうございました!) 52 | 53 | ### Fork したリポジトリに対して変更をコミットする 54 | 55 | この手順は、通常、手元のマシンに mikanos リポジトリを git clone して行います。 56 | 普通は uchan-nos/mikanos を git clone して手元でビルドし、開発しているかと思います。 57 | Fork したリポジトリに対して変更をコミットするには、「あなたのアカウント/mikanos」リポジトリを git clone しなければなりません。 58 | 59 | git clone のやり方はウェブ上に記事が多数ありますので、詳しくはそれらの記事に譲るとして、以下で概略を説明します。 60 | 61 | 「あなたのアカウント/mikanos」を git clone するには、「あなたのアカウント/mikanos」を GitHub で開き、「Code」ボタンをクリックします。 62 | 63 | ![GitHub Clone Repository](./images/github-clone-repository.png) 64 | 65 | Code ボタンをクリックすると「Clone」という項目の中に、HTTPS や SSH といった表示が出ます。 66 | お好みの方法を選択し、表示された URL をコピーします。 67 | 手元のマシンのターミナルで `git clone <コピーしたURL>` というコマンドを実行すると、あなたのアカウントに Fork したリポジトリを git clone することができます。 68 | 69 | clone したリポジトリに対し、修正を加え、デバッグし、コミットします。 70 | 一連の修正が完了したら `git push` により一連のコミットを GitHub 上へ送ります。 71 | Fork 元のリポジトリは uchan-nos しか書き込めませんが、今 push しようとしているのは Fork した **あなたの** リポジトリですから、push できるのです。 72 | 73 | ### Fork したリポジトリから、元のリポジトリへのプルリクエストを作る 74 | 75 | 変更を push すると、GitHub のリポジトリのトップページに、次のような「Pull Request」リンクが出現します。 76 | 「This branch is 1 commit ahead of osdev-jp:master」を日本語にすると「このブランチは osdev-jp:master より1コミット進んでいます」という意味です。 77 | Fork 元(osdev-jp:master)よりも 1 コミット新しくなっていますが、Pull Request を作成しませんか、と提案されているわけですね。 78 | 79 | ![GitHub Pull Request Suggestion](./images/github-pull-request-suggestion.png) 80 | 81 | 「Pull Request」リンクをクリックすると次のような画面になります。 82 | 83 | ![GitHub Create Pull Request Button](./images/github-create-pull-request-button.png) 84 | 85 | この画面では、プルリクエストに含まれることになる差分が表示されます。 86 | 差分が正しいことを確認して、OK なら「Create Pull Request」をクリックします。 87 | 88 | ![GitHub Create Pull Request Button](./images/github-describe-pull-request.png) 89 | 90 | 出てきた画面で、プルリクエストのタイトル、および内容を書き、「Create Pull Request」をクリックします。 91 | これでプルリクエストが Fork 元リポジトリに提出されます。 92 | 93 | プルリクエストは、すぐにマージされず、質問や議論のためにコメントが付くかもしれません。 94 | コメントが付いたら返信し、マージを目指します。 95 | -------------------------------------------------------------------------------- /docs/images/github-clone-repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-clone-repository.png -------------------------------------------------------------------------------- /docs/images/github-create-pull-request-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-create-pull-request-button.png -------------------------------------------------------------------------------- /docs/images/github-describe-pull-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-describe-pull-request.png -------------------------------------------------------------------------------- /docs/images/github-fork-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-fork-button.png -------------------------------------------------------------------------------- /docs/images/github-fork-progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-fork-progress.png -------------------------------------------------------------------------------- /docs/images/github-forked-from.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-forked-from.png -------------------------------------------------------------------------------- /docs/images/github-pull-request-suggestion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/docs/images/github-pull-request-suggestion.png -------------------------------------------------------------------------------- /kernel/.gitignore: -------------------------------------------------------------------------------- 1 | /kernel.elf 2 | /hankaku.bin 3 | *.o 4 | .*.d 5 | *.swp 6 | 7 | /html 8 | /ipxe_root 9 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = kernel.elf 2 | OBJS = main.o graphics.o mouse.o font.o hankaku.o newlib_support.o console.o \ 3 | pci.o asmfunc.o libcxx_support.o logger.o interrupt.o segment.o paging.o memory_manager.o \ 4 | window.o layer.o timer.o frame_buffer.o acpi.o keyboard.o task.o terminal.o \ 5 | fat.o syscall.o file.o \ 6 | usb/memory.o usb/device.o usb/xhci/ring.o usb/xhci/trb.o usb/xhci/xhci.o \ 7 | usb/xhci/port.o usb/xhci/device.o usb/xhci/devmgr.o usb/xhci/registers.o \ 8 | usb/classdriver/base.o usb/classdriver/hid.o usb/classdriver/keyboard.o \ 9 | usb/classdriver/mouse.o usb/classdriver/cdc.o 10 | DEPENDS = $(join $(dir $(OBJS)),$(addprefix .,$(notdir $(OBJS:.o=.d)))) 11 | 12 | CPPFLAGS += -I. 13 | CFLAGS += -O2 -Wall -g --target=x86_64-elf -ffreestanding -mno-red-zone -fshort-wchar 14 | CXXFLAGS += -O2 -Wall -g --target=x86_64-elf -ffreestanding -mno-red-zone -fshort-wchar \ 15 | -fno-exceptions -fno-rtti -std=c++17 16 | LDFLAGS += --entry KernelMain -z norelro --image-base 0x100000 --static 17 | 18 | 19 | .PHONY: all 20 | all: $(TARGET) 21 | 22 | .PHONY: clean 23 | clean: 24 | rm -rf *.o 25 | 26 | kernel.elf: $(OBJS) Makefile 27 | ld.lld $(LDFLAGS) -o kernel.elf $(OBJS) -lc -lc++ -lc++abi -lm \ 28 | -lfreetype 29 | 30 | %.o: %.cpp Makefile 31 | clang++ $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ 32 | 33 | .%.d: %.cpp 34 | clang++ $(CPPFLAGS) $(CXXFLAGS) -MM $< > $@ 35 | $(eval OBJ = $(<:.cpp=.o)) 36 | sed --in-place 's|$(notdir $(OBJ))|$(OBJ)|' $@ 37 | 38 | %.o: %.c Makefile 39 | clang $(CPPFLAGS) $(CFLAGS) -c $< -o $@ 40 | 41 | .%.d: %.c 42 | clang $(CPPFLAGS) $(CFLAGS) -MM $< > $@ 43 | $(eval OBJ = $(<:.c=.o)) 44 | sed --in-place 's|$(notdir $(OBJ))|$(OBJ)|' $@ 45 | 46 | %.o: %.asm Makefile 47 | nasm -f elf64 -o $@ $< 48 | 49 | hankaku.bin: hankaku.txt 50 | ../tools/makefont.py -o $@ $< 51 | 52 | hankaku.o: hankaku.bin 53 | objcopy -I binary -O elf64-x86-64 -B i386:x86-64 $< $@ 54 | 55 | .%.d: %.bin 56 | touch $@ 57 | 58 | .PHONY: depends 59 | depends: 60 | $(MAKE) $(DEPENDS) 61 | 62 | -include $(DEPENDS) 63 | -------------------------------------------------------------------------------- /kernel/acpi.cpp: -------------------------------------------------------------------------------- 1 | #include "acpi.hpp" 2 | 3 | #include 4 | #include 5 | #include "asmfunc.h" 6 | #include "logger.hpp" 7 | 8 | namespace { 9 | 10 | template 11 | uint8_t SumBytes(const T* data, size_t bytes) { 12 | return SumBytes(reinterpret_cast(data), bytes); 13 | } 14 | 15 | template <> 16 | uint8_t SumBytes(const uint8_t* data, size_t bytes) { 17 | uint8_t sum = 0; 18 | for (size_t i = 0; i < bytes; ++i) { 19 | sum += data[i]; 20 | } 21 | return sum; 22 | } 23 | 24 | } // namespace 25 | 26 | namespace acpi { 27 | 28 | bool RSDP::IsValid() const { 29 | if (strncmp(this->signature, "RSD PTR ", 8) != 0) { 30 | Log(kDebug, "invalid signature: %.8s\n", this->signature); 31 | return false; 32 | } 33 | if (this->revision != 2) { 34 | Log(kDebug, "ACPI revision must be 2: %d\n", this->revision); 35 | return false; 36 | } 37 | if (auto sum = SumBytes(this, 20); sum != 0) { 38 | Log(kDebug, "sum of 20 bytes must be 0: %d\n", sum); 39 | return false; 40 | } 41 | if (auto sum = SumBytes(this, 36); sum != 0) { 42 | Log(kDebug, "sum of 36 bytes must be 0: %d\n", sum); 43 | return false; 44 | } 45 | return true; 46 | } 47 | 48 | bool DescriptionHeader::IsValid(const char* expected_signature) const { 49 | if (strncmp(this->signature, expected_signature, 4) != 0) { 50 | Log(kDebug, "invalid signature: %.4s\n", this->signature); 51 | return false; 52 | } 53 | if (auto sum = SumBytes(this, this->length); sum != 0) { 54 | Log(kDebug, "sum of %u bytes must be 0: %d\n", this->length, sum); 55 | return false; 56 | } 57 | return true; 58 | } 59 | 60 | const DescriptionHeader& XSDT::operator[](size_t i) const { 61 | auto entries = reinterpret_cast(&this->header + 1); 62 | return *reinterpret_cast(entries[i]); 63 | } 64 | 65 | size_t XSDT::Count() const { 66 | return (this->header.length - sizeof(DescriptionHeader)) / sizeof(uint64_t); 67 | } 68 | 69 | const FADT* fadt; 70 | 71 | void WaitMilliseconds(unsigned long msec) { 72 | const bool pm_timer_32 = (fadt->flags >> 8) & 1; 73 | const uint32_t start = IoIn32(fadt->pm_tmr_blk); 74 | uint32_t end = start + kPMTimerFreq * msec / 1000; 75 | if (!pm_timer_32) { 76 | end &= 0x00ffffffu; 77 | } 78 | 79 | if (end < start) { // overflow 80 | while (IoIn32(fadt->pm_tmr_blk) >= start); 81 | } 82 | while (IoIn32(fadt->pm_tmr_blk) < end); 83 | } 84 | 85 | void Initialize(const RSDP& rsdp) { 86 | if (!rsdp.IsValid()) { 87 | Log(kError, "RSDP is not valid\n"); 88 | exit(1); 89 | } 90 | 91 | const XSDT& xsdt = *reinterpret_cast(rsdp.xsdt_address); 92 | if (!xsdt.header.IsValid("XSDT")) { 93 | Log(kError, "XSDT is not valid\n"); 94 | exit(1); 95 | } 96 | 97 | fadt = nullptr; 98 | for (int i = 0; i < xsdt.Count(); ++i) { 99 | const auto& entry = xsdt[i]; 100 | if (entry.IsValid("FACP")) { // FACP is the signature of FADT 101 | fadt = reinterpret_cast(&entry); 102 | break; 103 | } 104 | } 105 | 106 | if (fadt == nullptr) { 107 | Log(kError, "FADT is not found\n"); 108 | exit(1); 109 | } 110 | } 111 | 112 | } // namespace acpi 113 | -------------------------------------------------------------------------------- /kernel/acpi.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file acpi.hpp 3 | * 4 | * ACPI テーブル定義や操作用プログラムを集めたファイル。 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace acpi { 13 | 14 | struct RSDP { 15 | char signature[8]; 16 | uint8_t checksum; 17 | char oem_id[6]; 18 | uint8_t revision; 19 | uint32_t rsdt_address; 20 | uint32_t length; 21 | uint64_t xsdt_address; 22 | uint8_t extended_checksum; 23 | char reserved[3]; 24 | 25 | bool IsValid() const; 26 | } __attribute__((packed)); 27 | 28 | struct DescriptionHeader { 29 | char signature[4]; 30 | uint32_t length; 31 | uint8_t revision; 32 | uint8_t checksum; 33 | char oem_id[6]; 34 | char oem_table_id[8]; 35 | uint32_t oem_revision; 36 | uint32_t creator_id; 37 | uint32_t creator_revision; 38 | 39 | bool IsValid(const char* expected_signature) const; 40 | } __attribute__((packed)); 41 | 42 | struct XSDT { 43 | DescriptionHeader header; 44 | 45 | const DescriptionHeader& operator[](size_t i) const; 46 | size_t Count() const; 47 | } __attribute__((packed)); 48 | 49 | struct FADT { 50 | DescriptionHeader header; 51 | 52 | char reserved1[76 - sizeof(header)]; 53 | uint32_t pm_tmr_blk; 54 | char reserved2[112 - 80]; 55 | uint32_t flags; 56 | char reserved3[276 - 116]; 57 | } __attribute__((packed)); 58 | 59 | extern const FADT* fadt; 60 | const int kPMTimerFreq = 3579545; 61 | 62 | void WaitMilliseconds(unsigned long msec); 63 | void Initialize(const RSDP& rsdp); 64 | 65 | } // namespace acpi 66 | -------------------------------------------------------------------------------- /kernel/app_event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | struct AppEvent { 8 | enum Type { 9 | kQuit, 10 | kMouseMove, 11 | kMouseButton, 12 | kTimerTimeout, 13 | kKeyPush, 14 | } type; 15 | 16 | union { 17 | struct { 18 | int x, y; 19 | int dx, dy; 20 | uint8_t buttons; 21 | } mouse_move; 22 | 23 | struct { 24 | int x, y; 25 | int press; // 1: press, 0: release 26 | int button; 27 | } mouse_button; 28 | 29 | struct { 30 | unsigned long timeout; 31 | int value; 32 | } timer; 33 | 34 | struct { 35 | uint8_t modifier; 36 | uint8_t keycode; 37 | char ascii; 38 | int press; // 1: press, 0: release 39 | } keypush; 40 | } arg; 41 | }; 42 | 43 | #ifdef __cplusplus 44 | } // extern "C" 45 | #endif 46 | -------------------------------------------------------------------------------- /kernel/asmfunc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern "C" { 6 | void IoOut32(uint16_t addr, uint32_t data); 7 | uint32_t IoIn32(uint16_t addr); 8 | uint16_t GetCS(void); 9 | void LoadIDT(uint16_t limit, uint64_t offset); 10 | void LoadGDT(uint16_t limit, uint64_t offset); 11 | void SetCSSS(uint16_t cs, uint16_t ss); 12 | void SetDSAll(uint16_t value); 13 | uint64_t GetCR0(); 14 | void SetCR0(uint64_t value); 15 | uint64_t GetCR2(); 16 | void SetCR3(uint64_t value); 17 | uint64_t GetCR3(); 18 | void SwitchContext(void* next_ctx, void* current_ctx); 19 | void RestoreContext(void* ctx); 20 | int CallApp(int argc, char** argv, uint16_t ss, uint64_t rip, uint64_t rsp, uint64_t* os_stack_ptr); 21 | void IntHandlerLAPICTimer(); 22 | void LoadTR(uint16_t sel); 23 | void WriteMSR(uint32_t msr, uint64_t value); 24 | void SyscallEntry(void); 25 | void ExitApp(uint64_t rsp, int32_t ret_val); 26 | void InvalidateTLB(uint64_t addr); 27 | } 28 | -------------------------------------------------------------------------------- /kernel/compile_flags.txt: -------------------------------------------------------------------------------- 1 | -I/home/uchan/osbook/devenv/x86_64-elf/include/c++/v1 2 | -I/home/uchan/osbook/devenv/x86_64-elf/include 3 | -nostdlibinc 4 | -D__ELF__ 5 | -D_LDBL_EQ_DBL 6 | -D_GNU_SOURCE 7 | -D_POSIX_TIMERS 8 | -I. 9 | -O2 10 | -Wall 11 | -g 12 | --target=x86_64-elf 13 | -fno-exceptions 14 | -ffreestanding 15 | -fno-rtti 16 | -std=c++17 17 | -------------------------------------------------------------------------------- /kernel/console.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file console.cpp 3 | * 4 | * コンソール描画のプログラムを集めたファイル. 5 | */ 6 | 7 | #include "console.hpp" 8 | 9 | #include 10 | #include "font.hpp" 11 | #include "layer.hpp" 12 | 13 | Console::Console(const PixelColor& fg_color, const PixelColor& bg_color) 14 | : writer_{nullptr}, window_{}, fg_color_{fg_color}, bg_color_{bg_color}, 15 | buffer_{}, cursor_row_{0}, cursor_column_{0}, layer_id_{0} { 16 | } 17 | 18 | void Console::PutString(const char* s) { 19 | while (*s) { 20 | if (*s == '\n') { 21 | Newline(); 22 | } else if (cursor_column_ < kColumns - 1) { 23 | WriteAscii(*writer_, Vector2D{8 * cursor_column_, 16 * cursor_row_}, *s, fg_color_); 24 | buffer_[cursor_row_][cursor_column_] = *s; 25 | ++cursor_column_; 26 | } 27 | ++s; 28 | } 29 | if (layer_manager) { 30 | layer_manager->Draw(layer_id_); 31 | } 32 | } 33 | 34 | void Console::SetWriter(PixelWriter* writer) { 35 | if (writer == writer_) { 36 | return; 37 | } 38 | writer_ = writer; 39 | window_.reset(); 40 | Refresh(); 41 | } 42 | 43 | void Console::SetWindow(const std::shared_ptr& window) { 44 | if (window == window_) { 45 | return; 46 | } 47 | window_ = window; 48 | writer_ = window->Writer(); 49 | Refresh(); 50 | } 51 | 52 | void Console::SetLayerID(unsigned int layer_id) { 53 | layer_id_ = layer_id; 54 | } 55 | 56 | unsigned int Console::LayerID() const { 57 | return layer_id_; 58 | } 59 | 60 | void Console::Newline() { 61 | cursor_column_ = 0; 62 | if (cursor_row_ < kRows - 1) { 63 | ++cursor_row_; 64 | return; 65 | } 66 | 67 | if (window_) { 68 | Rectangle move_src{{0, 16}, {8 * kColumns, 16 * (kRows - 1)}}; 69 | window_->Move({0, 0}, move_src); 70 | FillRectangle(*writer_, {0, 16 * (kRows - 1)}, {8 * kColumns, 16}, bg_color_); 71 | } else { 72 | FillRectangle(*writer_, {0, 0}, {8 * kColumns, 16 * kRows}, bg_color_); 73 | for (int row = 0; row < kRows - 1; ++row) { 74 | memcpy(buffer_[row], buffer_[row + 1], kColumns + 1); 75 | WriteString(*writer_, Vector2D{0, 16 * row}, buffer_[row], fg_color_); 76 | } 77 | memset(buffer_[kRows - 1], 0, kColumns + 1); 78 | } 79 | } 80 | 81 | void Console::Refresh() { 82 | FillRectangle(*writer_, {0, 0}, {8 * kColumns, 16 * kRows}, bg_color_); 83 | for (int row = 0; row < kRows; ++row) { 84 | WriteString(*writer_, Vector2D{0, 16 * row}, buffer_[row], fg_color_); 85 | } 86 | } 87 | 88 | Console* console; 89 | 90 | namespace { 91 | char console_buf[sizeof(Console)]; 92 | } 93 | 94 | void InitializeConsole() { 95 | console = new(console_buf) Console{ 96 | kDesktopFGColor, kDesktopBGColor 97 | }; 98 | console->SetWriter(screen_writer); 99 | } 100 | -------------------------------------------------------------------------------- /kernel/console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "graphics.hpp" 5 | #include "window.hpp" 6 | 7 | class Console { 8 | public: 9 | static const int kRows = 25, kColumns = 80; 10 | 11 | Console(const PixelColor& fg_color, const PixelColor& bg_color); 12 | void PutString(const char* s); 13 | void SetWriter(PixelWriter* writer); 14 | void SetWindow(const std::shared_ptr& window); 15 | void SetLayerID(unsigned int layer_id); 16 | unsigned int LayerID() const; 17 | 18 | private: 19 | void Newline(); 20 | void Refresh(); 21 | 22 | PixelWriter* writer_; 23 | std::shared_ptr window_; 24 | const PixelColor fg_color_, bg_color_; 25 | char buffer_[kRows][kColumns + 1]; 26 | int cursor_row_, cursor_column_; 27 | unsigned int layer_id_; 28 | }; 29 | 30 | extern Console* console; 31 | 32 | void InitializeConsole(); 33 | -------------------------------------------------------------------------------- /kernel/elf.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef uintptr_t Elf64_Addr; 6 | typedef uint64_t Elf64_Off; 7 | typedef uint16_t Elf64_Half; 8 | typedef uint32_t Elf64_Word; 9 | typedef int32_t Elf64_Sword; 10 | typedef uint64_t Elf64_Xword; 11 | typedef int64_t Elf64_Sxword; 12 | 13 | #define EI_NIDENT 16 14 | 15 | typedef struct { 16 | unsigned char e_ident[EI_NIDENT]; 17 | Elf64_Half e_type; 18 | Elf64_Half e_machine; 19 | Elf64_Word e_version; 20 | Elf64_Addr e_entry; 21 | Elf64_Off e_phoff; 22 | Elf64_Off e_shoff; 23 | Elf64_Word e_flags; 24 | Elf64_Half e_ehsize; 25 | Elf64_Half e_phentsize; 26 | Elf64_Half e_phnum; 27 | Elf64_Half e_shentsize; 28 | Elf64_Half e_shnum; 29 | Elf64_Half e_shstrndx; 30 | } Elf64_Ehdr; 31 | 32 | #define ET_NONE 0 33 | #define ET_REL 1 34 | #define ET_EXEC 2 35 | #define ET_DYN 3 36 | #define ET_CORE 4 37 | 38 | typedef struct { 39 | Elf64_Word p_type; 40 | Elf64_Word p_flags; 41 | Elf64_Off p_offset; 42 | Elf64_Addr p_vaddr; 43 | Elf64_Addr p_paddr; 44 | Elf64_Xword p_filesz; 45 | Elf64_Xword p_memsz; 46 | Elf64_Xword p_align; 47 | } Elf64_Phdr; 48 | 49 | #define PT_NULL 0 50 | #define PT_LOAD 1 51 | #define PT_DYNAMIC 2 52 | #define PT_INTERP 3 53 | #define PT_NOTE 4 54 | #define PT_SHLIB 5 55 | #define PT_PHDR 6 56 | #define PT_TLS 7 57 | 58 | typedef struct { 59 | Elf64_Sxword d_tag; 60 | union { 61 | Elf64_Xword d_val; 62 | Elf64_Addr d_ptr; 63 | } d_un; 64 | } Elf64_Dyn; 65 | 66 | #define DT_NULL 0 67 | #define DT_RELA 7 68 | #define DT_RELASZ 8 69 | #define DT_RELAENT 9 70 | 71 | typedef struct { 72 | Elf64_Addr r_offset; 73 | Elf64_Xword r_info; 74 | Elf64_Sxword r_addend; 75 | } Elf64_Rela; 76 | 77 | #define ELF64_R_SYM(i) ((i)>>32) 78 | #define ELF64_R_TYPE(i) ((i)&0xffffffffL) 79 | #define ELF64_R_INFO(s,t) (((s)<<32)+((t)&0xffffffffL)) 80 | 81 | #define R_X86_64_RELATIVE 8 82 | -------------------------------------------------------------------------------- /kernel/error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Error { 7 | public: 8 | enum Code { 9 | kSuccess, 10 | kFull, 11 | kEmpty, 12 | kNoEnoughMemory, 13 | kIndexOutOfRange, 14 | kHostControllerNotHalted, 15 | kInvalidSlotID, 16 | kPortNotConnected, 17 | kInvalidEndpointNumber, 18 | kTransferRingNotSet, 19 | kAlreadyAllocated, 20 | kNotImplemented, 21 | kInvalidDescriptor, 22 | kBufferTooSmall, 23 | kUnknownDevice, 24 | kNoCorrespondingSetupStage, 25 | kTransferFailed, 26 | kInvalidPhase, 27 | kUnknownXHCISpeedID, 28 | kNoWaiter, 29 | kNoPCIMSI, 30 | kUnknownPixelFormat, 31 | kNoSuchTask, 32 | kInvalidFormat, 33 | kFrameTooSmall, 34 | kInvalidFile, 35 | kIsDirectory, 36 | kNoSuchEntry, 37 | kFreeTypeError, 38 | kEndpointNotInCharge, 39 | kLastOfCode, // この列挙子は常に最後に配置する 40 | }; 41 | 42 | private: 43 | static constexpr std::array code_names_{ 44 | "kSuccess", 45 | "kFull", 46 | "kEmpty", 47 | "kNoEnoughMemory", 48 | "kIndexOutOfRange", 49 | "kHostControllerNotHalted", 50 | "kInvalidSlotID", 51 | "kPortNotConnected", 52 | "kInvalidEndpointNumber", 53 | "kTransferRingNotSet", 54 | "kAlreadyAllocated", 55 | "kNotImplemented", 56 | "kInvalidDescriptor", 57 | "kBufferTooSmall", 58 | "kUnknownDevice", 59 | "kNoCorrespondingSetupStage", 60 | "kTransferFailed", 61 | "kInvalidPhase", 62 | "kUnknownXHCISpeedID", 63 | "kNoWaiter", 64 | "kNoPCIMSI", 65 | "kUnknownPixelFormat", 66 | "kNoSuchTask", 67 | "kInvalidFormat", 68 | "kFrameTooSmall", 69 | "kInvalidFile", 70 | "kIsDirectory", 71 | "kNoSuchEntry", 72 | "kFreeTypeError", 73 | "kEndpointNotInCharge", 74 | }; 75 | static_assert(Error::Code::kLastOfCode == code_names_.size()); 76 | 77 | public: 78 | Error(Code code, const char* file, int line) : code_{code}, line_{line}, file_{file} {} 79 | 80 | Code Cause() const { 81 | return this->code_; 82 | } 83 | 84 | operator bool() const { 85 | return this->code_ != kSuccess; 86 | } 87 | 88 | const char* Name() const { 89 | return code_names_[static_cast(this->code_)]; 90 | } 91 | 92 | const char* File() const { 93 | return this->file_; 94 | } 95 | 96 | int Line() const { 97 | return this->line_; 98 | } 99 | 100 | private: 101 | Code code_; 102 | int line_; 103 | const char* file_; 104 | }; 105 | 106 | #define MAKE_ERROR(code) Error((code), __FILE__, __LINE__) 107 | 108 | template 109 | struct WithError { 110 | T value; 111 | Error error; 112 | }; 113 | -------------------------------------------------------------------------------- /kernel/file.cpp: -------------------------------------------------------------------------------- 1 | #include "file.hpp" 2 | 3 | #include 4 | #include 5 | 6 | size_t PrintToFD(FileDescriptor& fd, const char* format, ...) { 7 | constexpr int BUFFER_SIZE = 128; 8 | va_list ap; 9 | int result; 10 | char s[BUFFER_SIZE]; 11 | 12 | va_start(ap, format); 13 | result = vsnprintf(s, BUFFER_SIZE, format, ap); 14 | va_end(ap); 15 | if (result < BUFFER_SIZE) { 16 | fd.Write(s, result); 17 | return result; 18 | } 19 | 20 | std::vector s2(result + 1); 21 | va_start(ap, format); 22 | vsnprintf(&s2[0], result + 1, format, ap); 23 | va_end(ap); 24 | fd.Write(&s2[0], result); 25 | return result; 26 | } 27 | 28 | size_t ReadDelim(FileDescriptor& fd, char delim, char* buf, size_t len) { 29 | size_t i = 0; 30 | for (; i < len - 1; ++i) { 31 | if (fd.Read(&buf[i], 1) == 0) { 32 | break; 33 | } 34 | if (buf[i] == delim) { 35 | ++i; 36 | break; 37 | } 38 | } 39 | buf[i] = '\0'; 40 | return i; 41 | } 42 | -------------------------------------------------------------------------------- /kernel/file.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "error.hpp" 5 | 6 | class FileDescriptor { 7 | public: 8 | virtual ~FileDescriptor() = default; 9 | virtual size_t Read(void* buf, size_t len) = 0; 10 | virtual size_t Write(const void* buf, size_t len) = 0; 11 | virtual size_t Size() const = 0; 12 | virtual bool IsTerminal() const { return false; } 13 | 14 | /** @brief Load reads file content without changing internal offset 15 | */ 16 | virtual size_t Load(void* buf, size_t len, size_t offset) = 0; 17 | }; 18 | 19 | size_t PrintToFD(FileDescriptor& fd, const char* format, ...) 20 | __attribute__((format(printf, 2, 3))); 21 | size_t ReadDelim(FileDescriptor& fd, char delim, char* buf, size_t len); 22 | -------------------------------------------------------------------------------- /kernel/font.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include FT_FREETYPE_H 8 | 9 | #include "graphics.hpp" 10 | #include "error.hpp" 11 | 12 | void WriteAscii(PixelWriter& writer, Vector2D pos, char c, const PixelColor& color); 13 | void WriteString(PixelWriter& writer, Vector2D pos, const char* s, const PixelColor& color); 14 | 15 | int CountUTF8Size(uint8_t c); 16 | std::pair ConvertUTF8To32(const char* u8); 17 | bool IsHankaku(char32_t c); 18 | WithError NewFTFace(); 19 | Error WriteUnicode(PixelWriter& writer, Vector2D pos, 20 | char32_t c, const PixelColor& color); 21 | void InitializeFont(); 22 | -------------------------------------------------------------------------------- /kernel/frame_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "frame_buffer.hpp" 2 | 3 | namespace { 4 | int BytesPerPixel(PixelFormat format) { 5 | switch (format) { 6 | case kPixelRGBResv8BitPerColor: return 4; 7 | case kPixelBGRResv8BitPerColor: return 4; 8 | } 9 | return -1; 10 | } 11 | 12 | uint8_t* FrameAddrAt(Vector2D pos, const FrameBufferConfig& config) { 13 | return config.frame_buffer + BytesPerPixel(config.pixel_format) * 14 | (config.pixels_per_scan_line * pos.y + pos.x); 15 | } 16 | 17 | int BytesPerScanLine(const FrameBufferConfig& config) { 18 | return BytesPerPixel(config.pixel_format) * config.pixels_per_scan_line; 19 | } 20 | 21 | Vector2D FrameBufferSize(const FrameBufferConfig& config) { 22 | return {static_cast(config.horizontal_resolution), 23 | static_cast(config.vertical_resolution)}; 24 | } 25 | } 26 | 27 | Error FrameBuffer::Initialize(const FrameBufferConfig& config) { 28 | config_ = config; 29 | 30 | const auto bytes_per_pixel = BytesPerPixel(config_.pixel_format); 31 | if (bytes_per_pixel <= 0) { 32 | return MAKE_ERROR(Error::kUnknownPixelFormat); 33 | } 34 | 35 | if (config_.frame_buffer) { 36 | buffer_.resize(0); 37 | } else { 38 | buffer_.resize( 39 | bytes_per_pixel 40 | * config_.horizontal_resolution * config_.vertical_resolution); 41 | config_.frame_buffer = buffer_.data(); 42 | config_.pixels_per_scan_line = config_.horizontal_resolution; 43 | } 44 | 45 | switch (config_.pixel_format) { 46 | case kPixelRGBResv8BitPerColor: 47 | writer_ = std::make_unique(config_); 48 | break; 49 | case kPixelBGRResv8BitPerColor: 50 | writer_ = std::make_unique(config_); 51 | break; 52 | default: 53 | return MAKE_ERROR(Error::kUnknownPixelFormat); 54 | } 55 | 56 | return MAKE_ERROR(Error::kSuccess); 57 | } 58 | 59 | Error FrameBuffer::Copy(Vector2D dst_pos, const FrameBuffer& src, 60 | const Rectangle& src_area) { 61 | if (config_.pixel_format != src.config_.pixel_format) { 62 | return MAKE_ERROR(Error::kUnknownPixelFormat); 63 | } 64 | 65 | const auto bytes_per_pixel = BytesPerPixel(config_.pixel_format); 66 | if (bytes_per_pixel <= 0) { 67 | return MAKE_ERROR(Error::kUnknownPixelFormat); 68 | } 69 | 70 | const Rectangle src_area_shifted{dst_pos, src_area.size}; 71 | const Rectangle src_outline{dst_pos - src_area.pos, FrameBufferSize(src.config_)}; 72 | const Rectangle dst_outline{{0, 0}, FrameBufferSize(config_)}; 73 | const auto copy_area = dst_outline & src_outline & src_area_shifted; 74 | const auto src_start_pos = copy_area.pos - (dst_pos - src_area.pos); 75 | 76 | uint8_t* dst_buf = FrameAddrAt(copy_area.pos, config_); 77 | const uint8_t* src_buf = FrameAddrAt(src_start_pos, src.config_); 78 | 79 | for (int y = 0; y < copy_area.size.y; ++y) { 80 | memcpy(dst_buf, src_buf, bytes_per_pixel * copy_area.size.x); 81 | dst_buf += BytesPerScanLine(config_); 82 | src_buf += BytesPerScanLine(src.config_); 83 | } 84 | 85 | return MAKE_ERROR(Error::kSuccess); 86 | } 87 | 88 | void FrameBuffer::Move(Vector2D dst_pos, const Rectangle& src) { 89 | const auto bytes_per_pixel = BytesPerPixel(config_.pixel_format); 90 | const auto bytes_per_scan_line = BytesPerScanLine(config_); 91 | 92 | if (dst_pos.y < src.pos.y) { // move up 93 | uint8_t* dst_buf = FrameAddrAt(dst_pos, config_); 94 | const uint8_t* src_buf = FrameAddrAt(src.pos, config_); 95 | for (int y = 0; y < src.size.y; ++y) { 96 | memcpy(dst_buf, src_buf, bytes_per_pixel * src.size.x); 97 | dst_buf += bytes_per_scan_line; 98 | src_buf += bytes_per_scan_line; 99 | } 100 | } else if (dst_pos.y == src.pos.y) { // move left or move right 101 | uint8_t* dst_buf = FrameAddrAt(dst_pos, config_); 102 | const uint8_t* src_buf = FrameAddrAt(src.pos, config_); 103 | for (int y = 0; y < src.size.y; ++y) { 104 | // dst_buf and src_buf may overlap, we must use memmove 105 | memmove(dst_buf, src_buf, bytes_per_pixel * src.size.x); 106 | dst_buf += bytes_per_scan_line; 107 | src_buf += bytes_per_scan_line; 108 | } 109 | } else { // move down 110 | uint8_t* dst_buf = FrameAddrAt(dst_pos + Vector2D{0, src.size.y - 1}, config_); 111 | const uint8_t* src_buf = FrameAddrAt(src.pos + Vector2D{0, src.size.y - 1}, config_); 112 | for (int y = 0; y < src.size.y; ++y) { 113 | memcpy(dst_buf, src_buf, bytes_per_pixel * src.size.x); 114 | dst_buf -= bytes_per_scan_line; 115 | src_buf -= bytes_per_scan_line; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /kernel/frame_buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "frame_buffer_config.hpp" 7 | #include "graphics.hpp" 8 | #include "error.hpp" 9 | 10 | class FrameBuffer { 11 | public: 12 | Error Initialize(const FrameBufferConfig& config); 13 | Error Copy(Vector2D dst_pos, const FrameBuffer& src, const Rectangle& src_area); 14 | void Move(Vector2D dst_pos, const Rectangle& src); 15 | 16 | FrameBufferWriter& Writer() { return *writer_; } 17 | const FrameBufferConfig& Config() const { return config_; } 18 | 19 | private: 20 | FrameBufferConfig config_{}; 21 | std::vector buffer_{}; 22 | std::unique_ptr writer_{}; 23 | }; 24 | -------------------------------------------------------------------------------- /kernel/frame_buffer_config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum PixelFormat { 6 | kPixelRGBResv8BitPerColor, 7 | kPixelBGRResv8BitPerColor, 8 | }; 9 | 10 | struct FrameBufferConfig { 11 | uint8_t* frame_buffer; 12 | uint32_t pixels_per_scan_line; 13 | uint32_t horizontal_resolution; 14 | uint32_t vertical_resolution; 15 | enum PixelFormat pixel_format; 16 | }; 17 | -------------------------------------------------------------------------------- /kernel/graphics.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file graphics.cpp 3 | * 4 | * 画像描画関連のプログラムを集めたファイル. 5 | */ 6 | 7 | #include "graphics.hpp" 8 | 9 | void RGBResv8BitPerColorPixelWriter::Write(Vector2D pos, const PixelColor& c) { 10 | auto p = PixelAt(pos); 11 | p[0] = c.r; 12 | p[1] = c.g; 13 | p[2] = c.b; 14 | } 15 | 16 | void BGRResv8BitPerColorPixelWriter::Write(Vector2D pos, const PixelColor& c) { 17 | auto p = PixelAt(pos); 18 | p[0] = c.b; 19 | p[1] = c.g; 20 | p[2] = c.r; 21 | } 22 | 23 | void DrawRectangle(PixelWriter& writer, const Vector2D& pos, 24 | const Vector2D& size, const PixelColor& c) { 25 | for (int dx = 0; dx < size.x; ++dx) { 26 | writer.Write(pos + Vector2D{dx, 0}, c); 27 | writer.Write(pos + Vector2D{dx, size.y - 1}, c); 28 | } 29 | for (int dy = 1; dy < size.y - 1; ++dy) { 30 | writer.Write(pos + Vector2D{0, dy}, c); 31 | writer.Write(pos + Vector2D{size.x - 1, dy}, c); 32 | } 33 | } 34 | 35 | void FillRectangle(PixelWriter& writer, const Vector2D& pos, 36 | const Vector2D& size, const PixelColor& c) { 37 | for (int dy = 0; dy < size.y; ++dy) { 38 | for (int dx = 0; dx < size.x; ++dx) { 39 | writer.Write(pos + Vector2D{dx, dy}, c); 40 | } 41 | } 42 | } 43 | 44 | void DrawDesktop(PixelWriter& writer) { 45 | const auto width = writer.Width(); 46 | const auto height = writer.Height(); 47 | FillRectangle(writer, 48 | {0, 0}, 49 | {width, height - 50}, 50 | kDesktopBGColor); 51 | FillRectangle(writer, 52 | {0, height - 50}, 53 | {width, 50}, 54 | {1, 8, 17}); 55 | FillRectangle(writer, 56 | {0, height - 50}, 57 | {width / 5, 50}, 58 | {80, 80, 80}); 59 | DrawRectangle(writer, 60 | {10, height - 40}, 61 | {30, 30}, 62 | {160, 160, 160}); 63 | } 64 | 65 | FrameBufferConfig screen_config; 66 | PixelWriter* screen_writer; 67 | 68 | Vector2D ScreenSize() { 69 | return { 70 | static_cast(screen_config.horizontal_resolution), 71 | static_cast(screen_config.vertical_resolution) 72 | }; 73 | } 74 | 75 | namespace { 76 | char pixel_writer_buf[sizeof(RGBResv8BitPerColorPixelWriter)]; 77 | } 78 | 79 | void InitializeGraphics(const FrameBufferConfig& screen_config) { 80 | ::screen_config = screen_config; 81 | 82 | switch (screen_config.pixel_format) { 83 | case kPixelRGBResv8BitPerColor: 84 | ::screen_writer = new(pixel_writer_buf) 85 | RGBResv8BitPerColorPixelWriter{screen_config}; 86 | break; 87 | case kPixelBGRResv8BitPerColor: 88 | ::screen_writer = new(pixel_writer_buf) 89 | BGRResv8BitPerColorPixelWriter{screen_config}; 90 | break; 91 | default: 92 | exit(1); 93 | } 94 | 95 | DrawDesktop(*screen_writer); 96 | } 97 | -------------------------------------------------------------------------------- /kernel/graphics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "frame_buffer_config.hpp" 6 | 7 | struct PixelColor { 8 | uint8_t r, g, b; 9 | }; 10 | 11 | constexpr PixelColor ToColor(uint32_t c) { 12 | return { 13 | static_cast((c >> 16) & 0xff), 14 | static_cast((c >> 8) & 0xff), 15 | static_cast(c & 0xff) 16 | }; 17 | } 18 | 19 | inline bool operator==(const PixelColor& lhs, const PixelColor& rhs) { 20 | return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b; 21 | } 22 | 23 | inline bool operator!=(const PixelColor& lhs, const PixelColor& rhs) { 24 | return !(lhs == rhs); 25 | } 26 | 27 | template 28 | struct Vector2D { 29 | T x, y; 30 | 31 | template 32 | Vector2D& operator +=(const Vector2D& rhs) { 33 | x += rhs.x; 34 | y += rhs.y; 35 | return *this; 36 | } 37 | 38 | template 39 | Vector2D operator +(const Vector2D& rhs) const { 40 | auto tmp = *this; 41 | tmp += rhs; 42 | return tmp; 43 | } 44 | 45 | template 46 | Vector2D& operator -=(const Vector2D& rhs) { 47 | x -= rhs.x; 48 | y -= rhs.y; 49 | return *this; 50 | } 51 | 52 | template 53 | Vector2D operator -(const Vector2D& rhs) const { 54 | auto tmp = *this; 55 | tmp -= rhs; 56 | return tmp; 57 | } 58 | }; 59 | 60 | template 61 | Vector2D ElementMax(const Vector2D& lhs, const Vector2D& rhs) { 62 | return {std::max(lhs.x, rhs.x), std::max(lhs.y, rhs.y)}; 63 | } 64 | 65 | template 66 | Vector2D ElementMin(const Vector2D& lhs, const Vector2D& rhs) { 67 | return {std::min(lhs.x, rhs.x), std::min(lhs.y, rhs.y)}; 68 | } 69 | 70 | template 71 | struct Rectangle { 72 | Vector2D pos, size; 73 | }; 74 | 75 | template 76 | Rectangle operator&(const Rectangle& lhs, const Rectangle& rhs) { 77 | const auto lhs_end = lhs.pos + lhs.size; 78 | const auto rhs_end = rhs.pos + rhs.size; 79 | if (lhs_end.x < rhs.pos.x || lhs_end.y < rhs.pos.y || 80 | rhs_end.x < lhs.pos.x || rhs_end.y < lhs.pos.y) { 81 | return {{0, 0}, {0, 0}}; 82 | } 83 | 84 | auto new_pos = ElementMax(lhs.pos, rhs.pos); 85 | auto new_size = ElementMin(lhs_end, rhs_end) - new_pos; 86 | return {new_pos, new_size}; 87 | } 88 | 89 | class PixelWriter { 90 | public: 91 | virtual ~PixelWriter() = default; 92 | virtual void Write(Vector2D pos, const PixelColor& c) = 0; 93 | virtual int Width() const = 0; 94 | virtual int Height() const = 0; 95 | }; 96 | 97 | class FrameBufferWriter : public PixelWriter { 98 | public: 99 | FrameBufferWriter(const FrameBufferConfig& config) : config_{config} { 100 | } 101 | virtual ~FrameBufferWriter() = default; 102 | virtual int Width() const override { return config_.horizontal_resolution; } 103 | virtual int Height() const override { return config_.vertical_resolution; } 104 | 105 | protected: 106 | uint8_t* PixelAt(Vector2D pos) { 107 | return config_.frame_buffer + 4 * (config_.pixels_per_scan_line * pos.y + pos.x); 108 | } 109 | 110 | private: 111 | const FrameBufferConfig& config_; 112 | }; 113 | 114 | class RGBResv8BitPerColorPixelWriter : public FrameBufferWriter { 115 | public: 116 | using FrameBufferWriter::FrameBufferWriter; 117 | virtual void Write(Vector2D pos, const PixelColor& c) override; 118 | }; 119 | 120 | class BGRResv8BitPerColorPixelWriter : public FrameBufferWriter { 121 | public: 122 | using FrameBufferWriter::FrameBufferWriter; 123 | virtual void Write(Vector2D pos, const PixelColor& c) override; 124 | }; 125 | 126 | void DrawRectangle(PixelWriter& writer, const Vector2D& pos, 127 | const Vector2D& size, const PixelColor& c); 128 | 129 | void FillRectangle(PixelWriter& writer, const Vector2D& pos, 130 | const Vector2D& size, const PixelColor& c); 131 | 132 | const PixelColor kDesktopBGColor{45, 118, 237}; 133 | const PixelColor kDesktopFGColor{255, 255, 255}; 134 | 135 | void DrawDesktop(PixelWriter& writer); 136 | 137 | extern FrameBufferConfig screen_config; 138 | extern PixelWriter* screen_writer; 139 | Vector2D ScreenSize(); 140 | 141 | void InitializeGraphics(const FrameBufferConfig& screen_config); 142 | -------------------------------------------------------------------------------- /kernel/interrupt.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file interrupt.hpp 3 | * 4 | * 割り込み用のプログラムを集めたファイル. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "x86_descriptor.hpp" 14 | #include "message.hpp" 15 | 16 | union InterruptDescriptorAttribute { 17 | uint16_t data; 18 | struct { 19 | uint16_t interrupt_stack_table : 3; 20 | uint16_t : 5; 21 | DescriptorType type : 4; 22 | uint16_t : 1; 23 | uint16_t descriptor_privilege_level : 2; 24 | uint16_t present : 1; 25 | } __attribute__((packed)) bits; 26 | } __attribute__((packed)); 27 | 28 | struct InterruptDescriptor { 29 | uint16_t offset_low; 30 | uint16_t segment_selector; 31 | InterruptDescriptorAttribute attr; 32 | uint16_t offset_middle; 33 | uint32_t offset_high; 34 | uint32_t reserved; 35 | } __attribute__((packed)); 36 | 37 | extern std::array idt; 38 | 39 | constexpr InterruptDescriptorAttribute MakeIDTAttr( 40 | DescriptorType type, 41 | uint8_t descriptor_privilege_level, 42 | bool present = true, 43 | uint8_t interrupt_stack_table = 0) { 44 | InterruptDescriptorAttribute attr{}; 45 | attr.bits.interrupt_stack_table = interrupt_stack_table; 46 | attr.bits.type = type; 47 | attr.bits.descriptor_privilege_level = descriptor_privilege_level; 48 | attr.bits.present = present; 49 | return attr; 50 | } 51 | 52 | const int kISTForTimer = 1; // index of the interrupt stack table 53 | 54 | void SetIDTEntry(InterruptDescriptor& desc, 55 | InterruptDescriptorAttribute attr, 56 | uint64_t offset, 57 | uint16_t segment_selector); 58 | 59 | class InterruptVector { 60 | public: 61 | enum Number { 62 | kXHCI = 0x40, 63 | kLAPICTimer = 0x41, 64 | }; 65 | }; 66 | 67 | struct InterruptFrame { 68 | uint64_t rip; 69 | uint64_t cs; 70 | uint64_t rflags; 71 | uint64_t rsp; 72 | uint64_t ss; 73 | }; 74 | 75 | void NotifyEndOfInterrupt(); 76 | 77 | void InitializeInterrupt(); 78 | -------------------------------------------------------------------------------- /kernel/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "keyboard.hpp" 2 | 3 | #include 4 | #include "usb/classdriver/keyboard.hpp" 5 | #include "task.hpp" 6 | 7 | namespace { 8 | 9 | const char keycode_map[256] = { 10 | 0, 0, 0, 0, 'a', 'b', 'c', 'd', // 0 11 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', // 8 12 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', // 16 13 | 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', // 24 14 | '3', '4', '5', '6', '7', '8', '9', '0', // 32 15 | '\n', '\b', 0x08, '\t', ' ', '-', '=', '[', // 40 16 | ']', '\\', '#', ';', '\'', '`', ',', '.', // 48 17 | '/', 0, 0, 0, 0, 0, 0, 0, // 56 18 | 0, 0, 0, 0, 0, 0, 0, 0, // 64 19 | 0, 0, 0, 0, 0, 0, 0, 0, // 72 20 | 0, 0, 0, 0, '/', '*', '-', '+', // 80 21 | '\n', '1', '2', '3', '4', '5', '6', '7', // 88 22 | '8', '9', '0', '.', '\\', 0, 0, '=', // 96 23 | 0, 0, 0, 0, 0, 0, 0, 0, // 104 24 | 0, 0, 0, 0, 0, 0, 0, 0, // 112 25 | 0, 0, 0, 0, 0, 0, 0, 0, // 120 26 | 0, 0, 0, 0, 0, 0, 0, 0, // 128 27 | 0, '\\', 0, 0, 0, 0, 0, 0, // 136 28 | }; 29 | 30 | const char keycode_map_shifted[256] = { 31 | 0, 0, 0, 0, 'A', 'B', 'C', 'D', // 0 32 | 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', // 8 33 | 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', // 16 34 | 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', // 24 35 | '#', '$', '%', '^', '&', '*', '(', ')', // 32 36 | '\n', '\b', 0x08, '\t', ' ', '_', '+', '{', // 40 37 | '}', '|', '~', ':', '"', '~', '<', '>', // 48 38 | '?', 0, 0, 0, 0, 0, 0, 0, // 56 39 | 0, 0, 0, 0, 0, 0, 0, 0, // 64 40 | 0, 0, 0, 0, 0, 0, 0, 0, // 72 41 | 0, 0, 0, 0, '/', '*', '-', '+', // 80 42 | '\n', '1', '2', '3', '4', '5', '6', '7', // 88 43 | '8', '9', '0', '.', '\\', 0, 0, '=', // 96 44 | 0, 0, 0, 0, 0, 0, 0, 0, // 104 45 | 0, 0, 0, 0, 0, 0, 0, 0, // 112 46 | 0, 0, 0, 0, 0, 0, 0, 0, // 120 47 | 0, 0, 0, 0, 0, 0, 0, 0, // 128 48 | 0, '|', 0, 0, 0, 0, 0, 0, // 136 49 | }; 50 | 51 | } // namespace 52 | 53 | void InitializeKeyboard() { 54 | usb::HIDKeyboardDriver::default_observer = 55 | [](uint8_t modifier, uint8_t keycode, bool press) { 56 | const bool shift = (modifier & (kLShiftBitMask | kRShiftBitMask)) != 0; 57 | char ascii = keycode_map[keycode]; 58 | if (shift) { 59 | ascii = keycode_map_shifted[keycode]; 60 | } 61 | Message msg{Message::kKeyPush}; 62 | msg.arg.keyboard.modifier = modifier; 63 | msg.arg.keyboard.keycode = keycode; 64 | msg.arg.keyboard.ascii = ascii; 65 | msg.arg.keyboard.press = press; 66 | task_manager->SendMessage(1, msg); 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /kernel/keyboard.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file keyboard.hpp 3 | * 4 | * キーボード制御プログラム. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "message.hpp" 11 | 12 | static const int kLControlBitMask = 0b00000001u; 13 | static const int kLShiftBitMask = 0b00000010u; 14 | static const int kLAltBitMask = 0b00000100u; 15 | static const int kLGUIBitMask = 0b00001000u; 16 | static const int kRControlBitMask = 0b00010000u; 17 | static const int kRShiftBitMask = 0b00100000u; 18 | static const int kRAltBitMask = 0b01000000u; 19 | static const int kRGUIBitMask = 0b10000000u; 20 | 21 | void InitializeKeyboard(); 22 | -------------------------------------------------------------------------------- /kernel/layer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file layer.hpp 3 | * 4 | * 重ね合わせ処理を提供する。 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "graphics.hpp" 14 | #include "window.hpp" 15 | #include "message.hpp" 16 | 17 | /** @brief Layer は 1 つの層を表す。 18 | * 19 | * 現状では 1 つのウィンドウしか保持できない設計だが, 20 | * 将来的には複数のウィンドウを持ち得る。 21 | */ 22 | class Layer { 23 | public: 24 | /** @brief 指定された ID を持つレイヤーを生成する。 */ 25 | Layer(unsigned int id = 0); 26 | /** @brief このインスタンスの ID を返す。 */ 27 | unsigned int ID() const; 28 | 29 | /** @brief ウィンドウを設定する。既存のウィンドウはこのレイヤーから外れる。 */ 30 | Layer& SetWindow(const std::shared_ptr& window); 31 | /** @brief 設定されたウィンドウを返す。 */ 32 | std::shared_ptr GetWindow() const; 33 | /** @brief レイヤーの原点座標を取得する。 */ 34 | Vector2D GetPosition() const; 35 | /** @brief true でレイヤーがドラッグ移動可能となる。 */ 36 | Layer& SetDraggable(bool draggable); 37 | /** @brief レイヤーがドラッグ移動可能なら true を返す。 */ 38 | bool IsDraggable() const; 39 | 40 | /** @brief レイヤーの位置情報を指定された絶対座標へと更新する。再描画はしない。 */ 41 | Layer& Move(Vector2D pos); 42 | /** @brief レイヤーの位置情報を指定された相対座標へと更新する。再描画はしない。 */ 43 | Layer& MoveRelative(Vector2D pos_diff); 44 | 45 | /** @brief 指定された描画先にウィンドウの内容を描画する。 */ 46 | void DrawTo(FrameBuffer& screen, const Rectangle& area) const; 47 | 48 | private: 49 | unsigned int id_; 50 | Vector2D pos_{}; 51 | std::shared_ptr window_{}; 52 | bool draggable_{false}; 53 | }; 54 | 55 | /** @brief LayerManager は複数のレイヤーを管理する。 */ 56 | class LayerManager { 57 | public: 58 | /** @brief Draw メソッドなどで描画する際の描画先を設定する。 */ 59 | void SetWriter(FrameBuffer* screen); 60 | /** @brief 新しいレイヤーを生成して参照を返す。 61 | * 62 | * 新しく生成されたレイヤーの実体は LayerManager 内部のコンテナで保持される。 63 | */ 64 | Layer& NewLayer(); 65 | /** @brief 指定されたレイヤーを削除する。 */ 66 | void RemoveLayer(unsigned int id); 67 | 68 | /** @brief 現在表示状態にあるレイヤーを描画する。 */ 69 | void Draw(const Rectangle& area) const; 70 | /** @brief 指定したレイヤーに設定されているウィンドウの描画領域内を再描画する。 */ 71 | void Draw(unsigned int id) const; 72 | /** @brief 指定したレイヤーに設定されているウィンドウ内の指定された範囲を再描画する。 */ 73 | void Draw(unsigned int id, Rectangle area) const; 74 | 75 | /** @brief レイヤーの位置情報を指定された絶対座標へと更新する。再描画する。 */ 76 | void Move(unsigned int id, Vector2D new_pos); 77 | /** @brief レイヤーの位置情報を指定された相対座標へと更新する。再描画する。 */ 78 | void MoveRelative(unsigned int id, Vector2D pos_diff); 79 | 80 | /** @brief レイヤーの高さ方向の位置を指定された位置に移動する。 81 | * 82 | * new_height に負の高さを指定するとレイヤーは非表示となり, 83 | * 0 以上を指定するとその高さとなる。 84 | * 現在のレイヤー数以上の数値を指定した場合は最前面のレイヤーとなる。 85 | * */ 86 | void UpDown(unsigned int id, int new_height); 87 | /** @brief レイヤーを非表示とする。 */ 88 | void Hide(unsigned int id); 89 | 90 | /** @brief 指定された座標にウィンドウを持つ最も上に表示されているレイヤーを探す。 */ 91 | Layer* FindLayerByPosition(Vector2D pos, unsigned int exclude_id) const; 92 | /** @brief 指定された ID を持つレイヤーを返す。 */ 93 | Layer* FindLayer(unsigned int id); 94 | /** @brief 指定されたレイヤーの現在の高さを返す。 */ 95 | int GetHeight(unsigned int id); 96 | 97 | private: 98 | FrameBuffer* screen_{nullptr}; 99 | mutable FrameBuffer back_buffer_{}; 100 | std::vector> layers_{}; 101 | std::vector layer_stack_{}; 102 | unsigned int latest_id_{0}; 103 | }; 104 | 105 | extern LayerManager* layer_manager; 106 | 107 | class ActiveLayer { 108 | public: 109 | ActiveLayer(LayerManager& manager); 110 | void SetMouseLayer(unsigned int mouse_layer); 111 | void Activate(unsigned int layer_id); 112 | unsigned int GetActive() const { return active_layer_; } 113 | 114 | private: 115 | LayerManager& manager_; 116 | unsigned int active_layer_{0}; 117 | unsigned int mouse_layer_{0}; 118 | }; 119 | 120 | extern ActiveLayer* active_layer; 121 | extern std::map* layer_task_map; 122 | 123 | void InitializeLayer(); 124 | void ProcessLayerMessage(const Message& msg); 125 | 126 | constexpr Message MakeLayerMessage( 127 | uint64_t task_id, unsigned int layer_id, 128 | LayerOperation op, const Rectangle& area) { 129 | Message msg{Message::kLayer, task_id}; 130 | msg.arg.layer.layer_id = layer_id; 131 | msg.arg.layer.op = op; 132 | msg.arg.layer.x = area.pos.x; 133 | msg.arg.layer.y = area.pos.y; 134 | msg.arg.layer.w = area.size.x; 135 | msg.arg.layer.h = area.size.y; 136 | return msg; 137 | } 138 | 139 | Error CloseLayer(unsigned int layer_id); 140 | -------------------------------------------------------------------------------- /kernel/libcxx_support.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int printk(const char* format, ...) 5 | __attribute__((format(printf, 1, 2))); 6 | 7 | std::new_handler std::get_new_handler() noexcept { 8 | return [] { 9 | printk("not enough memory\n"); 10 | exit(1); 11 | }; 12 | } 13 | 14 | extern "C" int posix_memalign(void**, size_t, size_t) { 15 | return ENOMEM; 16 | } 17 | -------------------------------------------------------------------------------- /kernel/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "console.hpp" 7 | 8 | namespace { 9 | LogLevel log_level = kWarn; 10 | } 11 | 12 | extern Console* console; 13 | 14 | void SetLogLevel(LogLevel level) { 15 | log_level = level; 16 | } 17 | 18 | int Log(LogLevel level, const char* format, ...) { 19 | if (level > log_level) { 20 | return 0; 21 | } 22 | 23 | va_list ap; 24 | int result; 25 | char s[1024]; 26 | 27 | va_start(ap, format); 28 | result = vsprintf(s, format, ap); 29 | va_end(ap); 30 | 31 | console->PutString(s); 32 | return result; 33 | } 34 | -------------------------------------------------------------------------------- /kernel/logger.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file logger.hpp 3 | * 4 | * カーネルロガーの実装. 5 | */ 6 | 7 | #pragma once 8 | 9 | enum LogLevel { 10 | kError = 3, 11 | kWarn = 4, 12 | kInfo = 6, 13 | kDebug = 7, 14 | }; 15 | 16 | /** @brief グローバルなログ優先度のしきい値を変更する. 17 | * 18 | * グローバルなログ優先度のしきい値を level に設定する. 19 | * 以降の Log の呼び出しでは,ここで設定した優先度以上のログのみ記録される. 20 | */ 21 | void SetLogLevel(enum LogLevel level); 22 | 23 | /** @brief ログを指定された優先度で記録する. 24 | * 25 | * 指定された優先度がしきい値以上ならば記録する. 26 | * 優先度がしきい値未満ならログは捨てられる. 27 | * 28 | * @param level ログの優先度.しきい値以上の優先度のログのみが記録される. 29 | * @param format 書式文字列.printk と互換. 30 | */ 31 | int Log(enum LogLevel level, const char* format, ...) 32 | __attribute__((format(printf, 2, 3))); 33 | -------------------------------------------------------------------------------- /kernel/memory_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "memory_manager.hpp" 2 | 3 | #include 4 | #include "logger.hpp" 5 | 6 | BitmapMemoryManager::BitmapMemoryManager() 7 | : alloc_map_{}, range_begin_{FrameID{0}}, range_end_{FrameID{kFrameCount}} { 8 | } 9 | 10 | WithError BitmapMemoryManager::Allocate(size_t num_frames) { 11 | size_t start_frame_id = range_begin_.ID(); 12 | while (true) { 13 | size_t i = 0; 14 | for (; i < num_frames; ++i) { 15 | if (start_frame_id + i >= range_end_.ID()) { 16 | return {kNullFrame, MAKE_ERROR(Error::kNoEnoughMemory)}; 17 | } 18 | if (GetBit(FrameID{start_frame_id + i})) { 19 | // "start_frame_id + i" にあるフレームは割り当て済み 20 | break; 21 | } 22 | } 23 | if (i == num_frames) { 24 | // num_frames 分の空きが見つかった 25 | MarkAllocated(FrameID{start_frame_id}, num_frames); 26 | return { 27 | FrameID{start_frame_id}, 28 | MAKE_ERROR(Error::kSuccess), 29 | }; 30 | } 31 | // 次のフレームから再検索 32 | start_frame_id += i + 1; 33 | } 34 | } 35 | 36 | Error BitmapMemoryManager::Free(FrameID start_frame, size_t num_frames) { 37 | for (size_t i = 0; i < num_frames; ++i) { 38 | SetBit(FrameID{start_frame.ID() + i}, false); 39 | } 40 | return MAKE_ERROR(Error::kSuccess); 41 | } 42 | 43 | void BitmapMemoryManager::MarkAllocated(FrameID start_frame, size_t num_frames) { 44 | for (size_t i = 0; i < num_frames; ++i) { 45 | SetBit(FrameID{start_frame.ID() + i}, true); 46 | } 47 | } 48 | 49 | void BitmapMemoryManager::SetMemoryRange(FrameID range_begin, FrameID range_end) { 50 | range_begin_ = range_begin; 51 | range_end_ = range_end; 52 | } 53 | 54 | MemoryStat BitmapMemoryManager::Stat() const { 55 | size_t sum = 0; 56 | for (int i = range_begin_.ID() / kBitsPerMapLine; 57 | i < range_end_.ID() / kBitsPerMapLine; ++i) { 58 | sum += std::bitset(alloc_map_[i]).count(); 59 | } 60 | return { sum, range_end_.ID() - range_begin_.ID() }; 61 | } 62 | 63 | bool BitmapMemoryManager::GetBit(FrameID frame) const { 64 | auto line_index = frame.ID() / kBitsPerMapLine; 65 | auto bit_index = frame.ID() % kBitsPerMapLine; 66 | 67 | return (alloc_map_[line_index] & (static_cast(1) << bit_index)) != 0; 68 | } 69 | 70 | void BitmapMemoryManager::SetBit(FrameID frame, bool allocated) { 71 | auto line_index = frame.ID() / kBitsPerMapLine; 72 | auto bit_index = frame.ID() % kBitsPerMapLine; 73 | 74 | if (allocated) { 75 | alloc_map_[line_index] |= (static_cast(1) << bit_index); 76 | } else { 77 | alloc_map_[line_index] &= ~(static_cast(1) << bit_index); 78 | } 79 | } 80 | 81 | extern "C" caddr_t program_break, program_break_end; 82 | 83 | namespace { 84 | char memory_manager_buf[sizeof(BitmapMemoryManager)]; 85 | 86 | Error InitializeHeap(BitmapMemoryManager& memory_manager) { 87 | const int kHeapFrames = 64 * 512; 88 | const auto heap_start = memory_manager.Allocate(kHeapFrames); 89 | if (heap_start.error) { 90 | return heap_start.error; 91 | } 92 | 93 | program_break = reinterpret_cast(heap_start.value.ID() * kBytesPerFrame); 94 | program_break_end = program_break + kHeapFrames * kBytesPerFrame; 95 | return MAKE_ERROR(Error::kSuccess); 96 | } 97 | } 98 | 99 | BitmapMemoryManager* memory_manager; 100 | 101 | void InitializeMemoryManager(const MemoryMap& memory_map) { 102 | ::memory_manager = new(memory_manager_buf) BitmapMemoryManager; 103 | 104 | const auto memory_map_base = reinterpret_cast(memory_map.buffer); 105 | uintptr_t available_end = 0; 106 | for (uintptr_t iter = memory_map_base; 107 | iter < memory_map_base + memory_map.map_size; 108 | iter += memory_map.descriptor_size) { 109 | auto desc = reinterpret_cast(iter); 110 | if (available_end < desc->physical_start) { 111 | memory_manager->MarkAllocated( 112 | FrameID{available_end / kBytesPerFrame}, 113 | (desc->physical_start - available_end) / kBytesPerFrame); 114 | } 115 | 116 | const auto physical_end = 117 | desc->physical_start + desc->number_of_pages * kUEFIPageSize; 118 | if (IsAvailable(static_cast(desc->type))) { 119 | available_end = physical_end; 120 | } else { 121 | memory_manager->MarkAllocated( 122 | FrameID{desc->physical_start / kBytesPerFrame}, 123 | desc->number_of_pages * kUEFIPageSize / kBytesPerFrame); 124 | } 125 | } 126 | memory_manager->SetMemoryRange(FrameID{1}, FrameID{available_end / kBytesPerFrame}); 127 | 128 | if (auto err = InitializeHeap(*memory_manager)) { 129 | Log(kError, "failed to allocate pages: %s at %s:%d\n", 130 | err.Name(), err.File(), err.Line()); 131 | exit(1); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /kernel/memory_manager.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file memory_manager.hpp 3 | * 4 | * メモリ管理クラスと周辺機能を集めたファイル. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "error.hpp" 13 | #include "memory_map.hpp" 14 | 15 | namespace { 16 | constexpr unsigned long long operator""_KiB(unsigned long long kib) { 17 | return kib * 1024; 18 | } 19 | 20 | constexpr unsigned long long operator""_MiB(unsigned long long mib) { 21 | return mib * 1024_KiB; 22 | } 23 | 24 | constexpr unsigned long long operator""_GiB(unsigned long long gib) { 25 | return gib * 1024_MiB; 26 | } 27 | } 28 | 29 | /** @brief 物理メモリフレーム 1 つの大きさ(バイト) */ 30 | static const auto kBytesPerFrame{4_KiB}; 31 | 32 | class FrameID { 33 | public: 34 | explicit FrameID(size_t id) : id_{id} {} 35 | size_t ID() const { return id_; } 36 | void* Frame() const { return reinterpret_cast(id_ * kBytesPerFrame); } 37 | 38 | private: 39 | size_t id_; 40 | }; 41 | 42 | static const FrameID kNullFrame{std::numeric_limits::max()}; 43 | 44 | struct MemoryStat { 45 | size_t allocated_frames; 46 | size_t total_frames; 47 | }; 48 | 49 | /** @brief ビットマップ配列を用いてフレーム単位でメモリ管理するクラス. 50 | * 51 | * 1 ビットを 1 フレームに対応させて,ビットマップにより空きフレームを管理する. 52 | * 配列 alloc_map の各ビットがフレームに対応し,0 なら空き,1 なら使用中. 53 | * alloc_map[n] の m ビット目が対応する物理アドレスは次の式で求まる: 54 | * kFrameBytes * (n * kBitsPerMapLine + m) 55 | */ 56 | class BitmapMemoryManager { 57 | public: 58 | /** @brief このメモリ管理クラスで扱える最大の物理メモリ量(バイト) */ 59 | static const auto kMaxPhysicalMemoryBytes{128_GiB}; 60 | /** @brief kMaxPhysicalMemoryBytes までの物理メモリを扱うために必要なフレーム数 */ 61 | static const auto kFrameCount{kMaxPhysicalMemoryBytes / kBytesPerFrame}; 62 | 63 | /** @brief ビットマップ配列の要素型 */ 64 | using MapLineType = unsigned long; 65 | /** @brief ビットマップ配列の 1 つの要素のビット数 == フレーム数 */ 66 | static const size_t kBitsPerMapLine{8 * sizeof(MapLineType)}; 67 | 68 | /** @brief インスタンスを初期化する. */ 69 | BitmapMemoryManager(); 70 | 71 | /** @brief 要求されたフレーム数の領域を確保して先頭のフレーム ID を返す */ 72 | WithError Allocate(size_t num_frames); 73 | Error Free(FrameID start_frame, size_t num_frames); 74 | void MarkAllocated(FrameID start_frame, size_t num_frames); 75 | 76 | /** @brief このメモリマネージャで扱うメモリ範囲を設定する. 77 | * この呼び出し以降,Allocate によるメモリ割り当ては設定された範囲内でのみ行われる. 78 | * 79 | * @param range_begin_ メモリ範囲の始点 80 | * @param range_end_ メモリ範囲の終点.最終フレームの次のフレーム. 81 | */ 82 | void SetMemoryRange(FrameID range_begin, FrameID range_end); 83 | 84 | /** @brief 空き/総フレームの数を返す 85 | */ 86 | MemoryStat Stat() const; 87 | 88 | private: 89 | std::array alloc_map_; 90 | /** @brief このメモリマネージャで扱うメモリ範囲の始点. */ 91 | FrameID range_begin_; 92 | /** @brief このメモリマネージャで扱うメモリ範囲の終点.最終フレームの次のフレーム. */ 93 | FrameID range_end_; 94 | 95 | bool GetBit(FrameID frame) const; 96 | void SetBit(FrameID frame, bool allocated); 97 | }; 98 | 99 | extern BitmapMemoryManager* memory_manager; 100 | void InitializeMemoryManager(const MemoryMap& memory_map); 101 | -------------------------------------------------------------------------------- /kernel/memory_map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct MemoryMap { 6 | unsigned long long buffer_size; 7 | void* buffer; 8 | unsigned long long map_size; 9 | unsigned long long map_key; 10 | unsigned long long descriptor_size; 11 | uint32_t descriptor_version; 12 | }; 13 | 14 | struct MemoryDescriptor { 15 | uint32_t type; 16 | uintptr_t physical_start; 17 | uintptr_t virtual_start; 18 | uint64_t number_of_pages; 19 | uint64_t attribute; 20 | }; 21 | 22 | #ifdef __cplusplus 23 | enum class MemoryType { 24 | kEfiReservedMemoryType, 25 | kEfiLoaderCode, 26 | kEfiLoaderData, 27 | kEfiBootServicesCode, 28 | kEfiBootServicesData, 29 | kEfiRuntimeServicesCode, 30 | kEfiRuntimeServicesData, 31 | kEfiConventionalMemory, 32 | kEfiUnusableMemory, 33 | kEfiACPIReclaimMemory, 34 | kEfiACPIMemoryNVS, 35 | kEfiMemoryMappedIO, 36 | kEfiMemoryMappedIOPortSpace, 37 | kEfiPalCode, 38 | kEfiPersistentMemory, 39 | kEfiMaxMemoryType 40 | }; 41 | 42 | inline bool operator==(uint32_t lhs, MemoryType rhs) { 43 | return lhs == static_cast(rhs); 44 | } 45 | 46 | inline bool operator==(MemoryType lhs, uint32_t rhs) { 47 | return rhs == lhs; 48 | } 49 | 50 | inline bool IsAvailable(MemoryType memory_type) { 51 | return 52 | memory_type == MemoryType::kEfiBootServicesCode || 53 | memory_type == MemoryType::kEfiBootServicesData || 54 | memory_type == MemoryType::kEfiConventionalMemory; 55 | } 56 | 57 | const int kUEFIPageSize = 4096; 58 | #endif 59 | -------------------------------------------------------------------------------- /kernel/message.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class LayerOperation { 4 | Move, MoveRelative, Draw, DrawArea 5 | }; 6 | struct Message { 7 | enum Type { 8 | kInterruptXHCI, 9 | kTimerTimeout, 10 | kKeyPush, 11 | kLayer, 12 | kLayerFinish, 13 | kMouseMove, 14 | kMouseButton, 15 | kWindowActive, 16 | kPipe, 17 | kWindowClose, 18 | } type; 19 | 20 | uint64_t src_task; 21 | 22 | union { 23 | struct { 24 | unsigned long timeout; 25 | int value; 26 | } timer; 27 | 28 | struct { 29 | uint8_t modifier; 30 | uint8_t keycode; 31 | char ascii; 32 | int press; 33 | } keyboard; 34 | 35 | struct { 36 | LayerOperation op; 37 | unsigned int layer_id; 38 | int x, y; 39 | int w, h; 40 | } layer; 41 | 42 | struct { 43 | int x, y; 44 | int dx, dy; 45 | uint8_t buttons; 46 | } mouse_move; 47 | 48 | struct { 49 | int x, y; 50 | int press; // 1: press, 0: release 51 | int button; 52 | } mouse_button; 53 | 54 | struct { 55 | int activate; // 1: activate, 0: deactivate 56 | } window_active; 57 | 58 | struct { 59 | char data[16]; 60 | uint8_t len; 61 | } pipe; 62 | 63 | struct { 64 | unsigned int layer_id; 65 | } window_close; 66 | } arg; 67 | }; 68 | -------------------------------------------------------------------------------- /kernel/mouse.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mouse.hpp 3 | * 4 | * マウス制御プログラム. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "graphics.hpp" 12 | 13 | const int kMouseCursorWidth = 15; 14 | const int kMouseCursorHeight = 24; 15 | const PixelColor kMouseTransparentColor{0, 0, 1}; 16 | 17 | void DrawMouseCursor(PixelWriter* pixel_writer, Vector2D position); 18 | 19 | class Mouse { 20 | public: 21 | Mouse(unsigned int layer_id); 22 | void OnInterrupt(uint8_t buttons, int8_t displacement_x, int8_t displacement_y); 23 | 24 | unsigned int LayerID() const { return layer_id_; } 25 | void SetPosition(Vector2D position); 26 | Vector2D Position() const { return position_; } 27 | 28 | private: 29 | unsigned int layer_id_; 30 | Vector2D position_{}; 31 | 32 | unsigned int drag_layer_id_{0}; 33 | uint8_t previous_buttons_{0}; 34 | }; 35 | 36 | void InitializeMouse(); 37 | -------------------------------------------------------------------------------- /kernel/msr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static constexpr uint32_t kIA32_EFER = 0xc0000080; 6 | static constexpr uint32_t kIA32_STAR = 0xc0000081; 7 | static constexpr uint32_t kIA32_LSTAR = 0xc0000082; 8 | static constexpr uint32_t kIA32_FMASK = 0xc0000084; 9 | -------------------------------------------------------------------------------- /kernel/newlib_support.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void _exit(void) { 6 | while (1) __asm__("hlt"); 7 | } 8 | 9 | caddr_t program_break, program_break_end; 10 | 11 | caddr_t sbrk(int incr) { 12 | if (program_break == 0 || program_break + incr >= program_break_end) { 13 | errno = ENOMEM; 14 | return (caddr_t)-1; 15 | } 16 | 17 | caddr_t prev_break = program_break; 18 | program_break += incr; 19 | return prev_break; 20 | } 21 | 22 | int getpid(void) { 23 | return 1; 24 | } 25 | 26 | int kill(int pid, int sig) { 27 | errno = EINVAL; 28 | return -1; 29 | } 30 | 31 | int close(int fd) { 32 | errno = EBADF; 33 | return -1; 34 | } 35 | 36 | off_t lseek(int fd, off_t offset, int whence) { 37 | errno = EBADF; 38 | return -1; 39 | } 40 | 41 | int open(const char* path, int flags) { 42 | errno = ENOENT; 43 | return -1; 44 | } 45 | 46 | ssize_t read(int fd, void* buf, size_t count) { 47 | errno = EBADF; 48 | return -1; 49 | } 50 | 51 | ssize_t write(int fd, const void* buf, size_t count) { 52 | errno = EBADF; 53 | return -1; 54 | } 55 | 56 | int fstat(int fd, struct stat* buf) { 57 | errno = EBADF; 58 | return -1; 59 | } 60 | 61 | int isatty(int fd) { 62 | errno = EBADF; 63 | return -1; 64 | } 65 | -------------------------------------------------------------------------------- /kernel/paging.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file paging.hpp 3 | * 4 | * メモリページング用のプログラムを集めたファイル. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "error.hpp" 13 | 14 | /** @brief 静的に確保するページディレクトリの個数 15 | * 16 | * この定数は SetupIdentityPageMap で使用される. 17 | * 1 つのページディレクトリには 512 個の 2MiB ページを設定できるので, 18 | * kPageDirectoryCount x 1GiB の仮想アドレスがマッピングされることになる. 19 | */ 20 | const size_t kPageDirectoryCount = 64; 21 | 22 | /** @brief 仮想アドレス=物理アドレスとなるようにページテーブルを設定する. 23 | * 最終的に CR3 レジスタが正しく設定されたページテーブルを指すようになる. 24 | */ 25 | void SetupIdentityPageTable(); 26 | 27 | void InitializePaging(); 28 | void ResetCR3(); 29 | 30 | union LinearAddress4Level { 31 | uint64_t value; 32 | 33 | struct { 34 | uint64_t offset : 12; 35 | uint64_t page : 9; 36 | uint64_t dir : 9; 37 | uint64_t pdp : 9; 38 | uint64_t pml4 : 9; 39 | uint64_t : 16; 40 | } __attribute__((packed)) parts; 41 | 42 | int Part(int page_map_level) const { 43 | switch (page_map_level) { 44 | case 0: return parts.offset; 45 | case 1: return parts.page; 46 | case 2: return parts.dir; 47 | case 3: return parts.pdp; 48 | case 4: return parts.pml4; 49 | default: return 0; 50 | } 51 | } 52 | 53 | void SetPart(int page_map_level, int value) { 54 | switch (page_map_level) { 55 | case 0: parts.offset = value; break; 56 | case 1: parts.page = value; break; 57 | case 2: parts.dir = value; break; 58 | case 3: parts.pdp = value; break; 59 | case 4: parts.pml4 = value; break; 60 | } 61 | } 62 | }; 63 | 64 | union PageMapEntry { 65 | uint64_t data; 66 | 67 | struct { 68 | uint64_t present : 1; 69 | uint64_t writable : 1; 70 | uint64_t user : 1; 71 | uint64_t write_through : 1; 72 | uint64_t cache_disable : 1; 73 | uint64_t accessed : 1; 74 | uint64_t dirty : 1; 75 | uint64_t huge_page : 1; 76 | uint64_t global : 1; 77 | uint64_t : 3; 78 | 79 | uint64_t addr : 40; 80 | uint64_t : 12; 81 | } __attribute__((packed)) bits; 82 | 83 | PageMapEntry* Pointer() const { 84 | return reinterpret_cast(bits.addr << 12); 85 | } 86 | 87 | void SetPointer(PageMapEntry* p) { 88 | bits.addr = reinterpret_cast(p) >> 12; 89 | } 90 | }; 91 | 92 | WithError NewPageMap(); 93 | Error FreePageMap(PageMapEntry* table); 94 | Error SetupPageMaps(LinearAddress4Level addr, size_t num_4kpages, 95 | bool writable = true); 96 | Error CleanPageMaps(LinearAddress4Level addr); 97 | Error CopyPageMaps(PageMapEntry* dest, PageMapEntry* src, int part, int start); 98 | Error HandlePageFault(uint64_t error_code, uint64_t causal_addr); 99 | -------------------------------------------------------------------------------- /kernel/register.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file register.hpp 3 | * 4 | * メモリマップトレジスタを読み書きする機能を提供する. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | template 13 | struct ArrayLength {}; 14 | 15 | template 16 | struct ArrayLength { 17 | static const size_t value = N; 18 | }; 19 | 20 | /** 21 | * MemMapRegister is a wrapper for a memory mapped register. 22 | * 23 | * MemMapRegister forces user program to read/write the underlying register 24 | * with specific bit width. The bit width will be deduced from the type of 25 | * T::data. T is the template parameter. T::data should be an array. 26 | */ 27 | template 28 | class MemMapRegister { 29 | public: 30 | T Read() const { 31 | T tmp; 32 | for (size_t i = 0; i < len_; ++i) { 33 | tmp.data[i] = value_.data[i]; 34 | } 35 | return tmp; 36 | } 37 | 38 | void Write(const T& value) { 39 | for (size_t i = 0; i < len_; ++i) { 40 | value_.data[i] = value.data[i]; 41 | } 42 | } 43 | 44 | private: 45 | volatile T value_; 46 | static const size_t len_ = ArrayLength::value; 47 | }; 48 | 49 | template 50 | struct DefaultBitmap { 51 | T data[1]; 52 | 53 | DefaultBitmap& operator =(const T& value) { 54 | data[0] = value; 55 | } 56 | operator T() const { return data[0]; } 57 | }; 58 | 59 | /* 60 | * Design: container-like classes. 61 | * 62 | * Container-like classes, such as PortArray and DeviceContextArray, 63 | * should have Size() method and Iterator type. 64 | * Size() should return the number of elements, and iterators 65 | * of that type should iterate all elements. 66 | * 67 | * Each element may have a flag indicating availableness of the element. 68 | * For example each port has "Port Enabled/Disabled" bit. 69 | * Size() and iterators should not skip disabled elements. 70 | */ 71 | 72 | template 73 | class ArrayWrapper { 74 | public: 75 | using ValueType = T; 76 | using Iterator = ValueType*; 77 | using ConstIterator = const ValueType*; 78 | 79 | ArrayWrapper(uintptr_t array_base_addr, size_t size) 80 | : array_(reinterpret_cast(array_base_addr)), 81 | size_(size) {} 82 | 83 | size_t Size() const { return size_; } 84 | 85 | // begin, end, cbegin, cend must be lower case names 86 | // to be used in rage-based for statements. 87 | Iterator begin() { return array_; } 88 | Iterator end() { return array_ + size_; } 89 | ConstIterator cbegin() const { return array_; } 90 | ConstIterator cend() const { return array_ + size_; } 91 | 92 | ValueType& operator [](size_t index) { return array_[index]; } 93 | 94 | private: 95 | ValueType* const array_; 96 | const size_t size_; 97 | }; 98 | -------------------------------------------------------------------------------- /kernel/segment.cpp: -------------------------------------------------------------------------------- 1 | #include "segment.hpp" 2 | 3 | #include "asmfunc.h" 4 | #include "interrupt.hpp" 5 | #include "logger.hpp" 6 | #include "memory_manager.hpp" 7 | 8 | namespace { 9 | std::array gdt; 10 | std::array tss; 11 | 12 | static_assert((kTSS >> 3) + 1 < gdt.size()); 13 | 14 | void SetTSS(int index, uint64_t value) { 15 | tss[index] = value & 0xffffffff; 16 | tss[index + 1] = value >> 32; 17 | } 18 | 19 | uint64_t AllocateStackArea(int num_4kframes) { 20 | auto [ stk, err ] = memory_manager->Allocate(num_4kframes); 21 | if (err) { 22 | Log(kError, "failed to allocate stack area: %s\n", err.Name()); 23 | exit(1); 24 | } 25 | return reinterpret_cast(stk.Frame()) + num_4kframes * 4096; 26 | } 27 | } 28 | 29 | void SetCodeSegment(SegmentDescriptor& desc, 30 | DescriptorType type, 31 | unsigned int descriptor_privilege_level, 32 | uint32_t base, 33 | uint32_t limit) { 34 | desc.data = 0; 35 | 36 | desc.bits.base_low = base & 0xffffu; 37 | desc.bits.base_middle = (base >> 16) & 0xffu; 38 | desc.bits.base_high = (base >> 24) & 0xffu; 39 | 40 | desc.bits.limit_low = limit & 0xffffu; 41 | desc.bits.limit_high = (limit >> 16) & 0xfu; 42 | 43 | desc.bits.type = type; 44 | desc.bits.system_segment = 1; // 1: code & data segment 45 | desc.bits.descriptor_privilege_level = descriptor_privilege_level; 46 | desc.bits.present = 1; 47 | desc.bits.available = 0; 48 | desc.bits.long_mode = 1; 49 | desc.bits.default_operation_size = 0; // should be 0 when long_mode == 1 50 | desc.bits.granularity = 1; 51 | } 52 | 53 | void SetDataSegment(SegmentDescriptor& desc, 54 | DescriptorType type, 55 | unsigned int descriptor_privilege_level, 56 | uint32_t base, 57 | uint32_t limit) { 58 | SetCodeSegment(desc, type, descriptor_privilege_level, base, limit); 59 | desc.bits.long_mode = 0; 60 | desc.bits.default_operation_size = 1; // 32-bit stack segment 61 | } 62 | 63 | void SetSystemSegment(SegmentDescriptor& desc, 64 | DescriptorType type, 65 | unsigned int descriptor_privilege_level, 66 | uint32_t base, 67 | uint32_t limit) { 68 | SetCodeSegment(desc, type, descriptor_privilege_level, base, limit); 69 | desc.bits.system_segment = 0; 70 | desc.bits.long_mode = 0; 71 | } 72 | 73 | void SetupSegments() { 74 | gdt[0].data = 0; 75 | SetCodeSegment(gdt[1], DescriptorType::kExecuteRead, 0, 0, 0xfffff); 76 | SetDataSegment(gdt[2], DescriptorType::kReadWrite, 0, 0, 0xfffff); 77 | SetDataSegment(gdt[3], DescriptorType::kReadWrite, 3, 0, 0xfffff); 78 | SetCodeSegment(gdt[4], DescriptorType::kExecuteRead, 3, 0, 0xfffff); 79 | LoadGDT(sizeof(gdt) - 1, reinterpret_cast(&gdt[0])); 80 | } 81 | 82 | void InitializeSegmentation() { 83 | SetupSegments(); 84 | 85 | SetDSAll(kKernelDS); 86 | SetCSSS(kKernelCS, kKernelSS); 87 | } 88 | 89 | void InitializeTSS() { 90 | SetTSS(1, AllocateStackArea(8)); 91 | SetTSS(7 + 2 * kISTForTimer, AllocateStackArea(8)); 92 | 93 | uint64_t tss_addr = reinterpret_cast(&tss[0]); 94 | SetSystemSegment(gdt[kTSS >> 3], DescriptorType::kTSSAvailable, 0, 95 | tss_addr & 0xffffffff, sizeof(tss)-1); 96 | gdt[(kTSS >> 3) + 1].data = tss_addr >> 32; 97 | 98 | LoadTR(kTSS); 99 | } 100 | -------------------------------------------------------------------------------- /kernel/segment.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file segment.hpp 3 | * 4 | * セグメンテーション用のプログラムを集めたファイル. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "x86_descriptor.hpp" 13 | 14 | union SegmentDescriptor { 15 | uint64_t data; 16 | struct { 17 | uint64_t limit_low : 16; 18 | uint64_t base_low : 16; 19 | uint64_t base_middle : 8; 20 | DescriptorType type : 4; 21 | uint64_t system_segment : 1; 22 | uint64_t descriptor_privilege_level : 2; 23 | uint64_t present : 1; 24 | uint64_t limit_high : 4; 25 | uint64_t available : 1; 26 | uint64_t long_mode : 1; 27 | uint64_t default_operation_size : 1; 28 | uint64_t granularity : 1; 29 | uint64_t base_high : 8; 30 | } __attribute__((packed)) bits; 31 | } __attribute__((packed)); 32 | 33 | void SetCodeSegment(SegmentDescriptor& desc, 34 | DescriptorType type, 35 | unsigned int descriptor_privilege_level, 36 | uint32_t base, 37 | uint32_t limit); 38 | void SetDataSegment(SegmentDescriptor& desc, 39 | DescriptorType type, 40 | unsigned int descriptor_privilege_level, 41 | uint32_t base, 42 | uint32_t limit); 43 | 44 | const uint16_t kKernelCS = 1 << 3; 45 | const uint16_t kKernelSS = 2 << 3; 46 | const uint16_t kKernelDS = 0; 47 | const uint16_t kTSS = 5 << 3; 48 | 49 | void SetupSegments(); 50 | void InitializeSegmentation(); 51 | void InitializeTSS(); 52 | -------------------------------------------------------------------------------- /kernel/syscall.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void InitializeSyscall(); 4 | -------------------------------------------------------------------------------- /kernel/task.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file task.hpp 3 | * 4 | * タスク管理,コンテキスト切り替えのプログラムを集めたファイル。 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "error.hpp" 18 | #include "message.hpp" 19 | #include "paging.hpp" 20 | #include "fat.hpp" 21 | 22 | struct TaskContext { 23 | uint64_t cr3, rip, rflags, reserved1; // offset 0x00 24 | uint64_t cs, ss, fs, gs; // offset 0x20 25 | uint64_t rax, rbx, rcx, rdx, rdi, rsi, rsp, rbp; // offset 0x40 26 | uint64_t r8, r9, r10, r11, r12, r13, r14, r15; // offset 0x80 27 | std::array fxsave_area; // offset 0xc0 28 | } __attribute__((packed)); 29 | 30 | using TaskFunc = void (uint64_t, int64_t); 31 | 32 | class TaskManager; 33 | 34 | struct FileMapping { 35 | int fd; 36 | uint64_t vaddr_begin, vaddr_end; 37 | }; 38 | 39 | class Task { 40 | public: 41 | static const int kDefaultLevel = 1; 42 | static const size_t kDefaultStackBytes = 8 * 4096; 43 | 44 | Task(uint64_t id); 45 | Task& InitContext(TaskFunc* f, int64_t data); 46 | TaskContext& Context(); 47 | uint64_t& OSStackPointer(); 48 | uint64_t ID() const; 49 | Task& Sleep(); 50 | Task& Wakeup(); 51 | void SendMessage(const Message& msg); 52 | std::optional ReceiveMessage(); 53 | std::vector>& Files(); 54 | uint64_t DPagingBegin() const; 55 | void SetDPagingBegin(uint64_t v); 56 | uint64_t DPagingEnd() const; 57 | void SetDPagingEnd(uint64_t v); 58 | uint64_t FileMapEnd() const; 59 | void SetFileMapEnd(uint64_t v); 60 | std::vector& FileMaps(); 61 | 62 | int Level() const { return level_; } 63 | bool Running() const { return running_; } 64 | 65 | private: 66 | uint64_t id_; 67 | std::vector stack_; 68 | alignas(16) TaskContext context_; 69 | uint64_t os_stack_ptr_; 70 | std::deque msgs_; 71 | unsigned int level_{kDefaultLevel}; 72 | bool running_{false}; 73 | std::vector> files_{}; 74 | uint64_t dpaging_begin_{0}, dpaging_end_{0}; 75 | uint64_t file_map_end_{0}; 76 | std::vector file_maps_{}; 77 | 78 | Task& SetLevel(int level) { level_ = level; return *this; } 79 | Task& SetRunning(bool running) { running_ = running; return *this; } 80 | 81 | friend TaskManager; 82 | }; 83 | 84 | class TaskManager { 85 | public: 86 | // level: 0 = lowest, kMaxLevel = highest 87 | static const int kMaxLevel = 3; 88 | 89 | TaskManager(); 90 | Task& NewTask(); 91 | void SwitchTask(const TaskContext& current_ctx); 92 | 93 | void Sleep(Task* task); 94 | Error Sleep(uint64_t id); 95 | void Wakeup(Task* task, int level = -1); 96 | Error Wakeup(uint64_t id, int level = -1); 97 | Error SendMessage(uint64_t id, const Message& msg); 98 | Task& CurrentTask(); 99 | void Finish(int exit_code); 100 | WithError WaitFinish(uint64_t task_id); 101 | 102 | private: 103 | std::vector> tasks_{}; 104 | uint64_t latest_id_{0}; 105 | std::array, kMaxLevel + 1> running_{}; 106 | int current_level_{kMaxLevel}; 107 | bool level_changed_{false}; 108 | std::map finish_tasks_{}; // key: ID of a finished task 109 | std::map finish_waiter_{}; // key: ID of a finished task 110 | 111 | void ChangeLevelRunning(Task* task, int level); 112 | Task* RotateCurrentRunQueue(bool current_sleep); 113 | }; 114 | 115 | extern TaskManager* task_manager; 116 | 117 | void InitializeTask(); 118 | -------------------------------------------------------------------------------- /kernel/terminal.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file terminal.hpp 3 | * 4 | * ターミナルウィンドウを提供する。 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "window.hpp" 15 | #include "task.hpp" 16 | #include "layer.hpp" 17 | #include "fat.hpp" 18 | #include "graphics.hpp" 19 | 20 | struct AppLoadInfo { 21 | uint64_t vaddr_end, entry; 22 | PageMapEntry* pml4; 23 | }; 24 | 25 | extern std::map* app_loads; 26 | 27 | struct TerminalDescriptor { 28 | std::string command_line; 29 | bool exit_after_command; 30 | bool show_window; 31 | std::array, 3> files; 32 | }; 33 | 34 | enum class EscSeqState { 35 | kInit, // エスケープシーケンスに出会ってない状態 36 | kEsc, // ESC を受信した直後の状態 37 | kCSI, // \033[ 38 | kNum, // 数字を 1 文字以上受信した状態 39 | }; 40 | 41 | class Terminal { 42 | public: 43 | static const int kRows = 15, kColumns = 60; 44 | static const int kLineMax = 128; 45 | 46 | Terminal(Task& task, const TerminalDescriptor* term_desc); 47 | unsigned int LayerID() const { return layer_id_; } 48 | Rectangle BlinkCursor(); 49 | Rectangle InputKey(uint8_t modifier, uint8_t keycode, char ascii); 50 | 51 | void Print(const char* s, std::optional len = std::nullopt); 52 | 53 | Task& UnderlyingTask() const { return task_; } 54 | int LastExitCode() const { return last_exit_code_; } 55 | void Redraw(); 56 | 57 | private: 58 | std::shared_ptr window_; 59 | unsigned int layer_id_; 60 | Task& task_; 61 | 62 | Vector2D cursor_{0, 0}; 63 | bool cursor_visible_{false}; 64 | void DrawCursor(bool visible); 65 | Vector2D CalcCursorPos() const; 66 | 67 | int linebuf_index_{0}; 68 | std::array linebuf_{}; 69 | void Scroll1(); 70 | 71 | void ExecuteLine(); 72 | WithError ExecuteFile(fat::DirectoryEntry& file_entry, 73 | char* command, char* first_arg); 74 | void Print(char32_t c); 75 | 76 | std::deque> cmd_history_{}; 77 | int cmd_history_index_{-1}; 78 | Rectangle HistoryUpDown(int direction); 79 | 80 | bool show_window_; 81 | std::array, 3> files_; 82 | int last_exit_code_{0}; 83 | 84 | EscSeqState esc_seq_state_{EscSeqState::kInit}; 85 | int esc_seq_n_{0}; 86 | PixelColor text_color_{255, 255, 255}; 87 | }; 88 | 89 | void TaskTerminal(uint64_t task_id, int64_t data); 90 | 91 | class TerminalFileDescriptor : public FileDescriptor { 92 | public: 93 | explicit TerminalFileDescriptor(Terminal& term); 94 | size_t Read(void* buf, size_t len) override; 95 | size_t Write(const void* buf, size_t len) override; 96 | size_t Size() const override { return 0; } 97 | bool IsTerminal() const override { return true; } 98 | size_t Load(void* buf, size_t len, size_t offset) override; 99 | 100 | private: 101 | Terminal& term_; 102 | }; 103 | 104 | class PipeDescriptor : public FileDescriptor { 105 | public: 106 | explicit PipeDescriptor(Task& task); 107 | size_t Read(void* buf, size_t len) override; 108 | size_t Write(const void* buf, size_t len) override; 109 | size_t Size() const override { return 0; } 110 | size_t Load(void* buf, size_t len, size_t offset) override { return 0; } 111 | 112 | void FinishWrite(); 113 | 114 | private: 115 | Task& task_; 116 | char data_[16]; 117 | size_t len_{0}; 118 | bool closed_{false}; 119 | }; 120 | -------------------------------------------------------------------------------- /kernel/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.hpp" 2 | 3 | #include "acpi.hpp" 4 | #include "interrupt.hpp" 5 | #include "task.hpp" 6 | 7 | namespace { 8 | const uint32_t kCountMax = 0xffffffffu; 9 | volatile uint32_t& lvt_timer = *reinterpret_cast(0xfee00320); 10 | volatile uint32_t& initial_count = *reinterpret_cast(0xfee00380); 11 | volatile uint32_t& current_count = *reinterpret_cast(0xfee00390); 12 | volatile uint32_t& divide_config = *reinterpret_cast(0xfee003e0); 13 | } 14 | 15 | void InitializeLAPICTimer() { 16 | timer_manager = new TimerManager; 17 | 18 | divide_config = 0b1011; // divide 1:1 19 | lvt_timer = 0b001 << 16; // masked, one-shot 20 | 21 | StartLAPICTimer(); 22 | acpi::WaitMilliseconds(100); 23 | const auto elapsed = LAPICTimerElapsed(); 24 | StopLAPICTimer(); 25 | 26 | lapic_timer_freq = static_cast(elapsed) * 10; 27 | 28 | divide_config = 0b1011; // divide 1:1 29 | lvt_timer = (0b010 << 16) | InterruptVector::kLAPICTimer; // not-masked, periodic 30 | initial_count = lapic_timer_freq / kTimerFreq; 31 | } 32 | 33 | void StartLAPICTimer() { 34 | initial_count = kCountMax; 35 | } 36 | 37 | uint32_t LAPICTimerElapsed() { 38 | return kCountMax - current_count; 39 | } 40 | 41 | void StopLAPICTimer() { 42 | initial_count = 0; 43 | } 44 | 45 | Timer::Timer(unsigned long timeout, int value, uint64_t task_id) 46 | : timeout_{timeout}, value_{value}, task_id_{task_id} { 47 | } 48 | 49 | TimerManager::TimerManager() { 50 | timers_.push(Timer{std::numeric_limits::max(), 0, 0}); 51 | } 52 | 53 | void TimerManager::AddTimer(const Timer& timer) { 54 | timers_.push(timer); 55 | } 56 | 57 | bool TimerManager::Tick() { 58 | ++tick_; 59 | 60 | bool task_timer_timeout = false; 61 | while (true) { 62 | const auto& t = timers_.top(); 63 | if (t.Timeout() > tick_) { 64 | break; 65 | } 66 | 67 | if (t.Value() == kTaskTimerValue) { 68 | task_timer_timeout = true; 69 | timers_.pop(); 70 | timers_.push(Timer{tick_ + kTaskTimerPeriod, kTaskTimerValue, 1}); 71 | continue; 72 | } 73 | 74 | Message m{Message::kTimerTimeout}; 75 | m.arg.timer.timeout = t.Timeout(); 76 | m.arg.timer.value = t.Value(); 77 | task_manager->SendMessage(t.TaskID(), m); 78 | 79 | timers_.pop(); 80 | } 81 | 82 | return task_timer_timeout; 83 | } 84 | 85 | TimerManager* timer_manager; 86 | unsigned long lapic_timer_freq; 87 | 88 | extern "C" void LAPICTimerOnInterrupt(const TaskContext& ctx_stack) { 89 | const bool task_timer_timeout = timer_manager->Tick(); 90 | NotifyEndOfInterrupt(); 91 | 92 | if (task_timer_timeout) { 93 | task_manager->SwitchTask(ctx_stack); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /kernel/timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "message.hpp" 8 | 9 | void InitializeLAPICTimer(); 10 | void StartLAPICTimer(); 11 | uint32_t LAPICTimerElapsed(); 12 | void StopLAPICTimer(); 13 | 14 | class Timer { 15 | public: 16 | Timer(unsigned long timeout, int value, uint64_t task_id); 17 | unsigned long Timeout() const { return timeout_; } 18 | int Value() const { return value_; } 19 | uint64_t TaskID() const { return task_id_; } 20 | 21 | private: 22 | unsigned long timeout_; 23 | int value_; 24 | uint64_t task_id_; 25 | }; 26 | 27 | /** @brief タイマー優先度を比較する。タイムアウトが遠いほど優先度低。 */ 28 | inline bool operator<(const Timer& lhs, const Timer& rhs) { 29 | return lhs.Timeout() > rhs.Timeout(); 30 | } 31 | 32 | class TimerManager { 33 | public: 34 | TimerManager(); 35 | void AddTimer(const Timer& timer); 36 | bool Tick(); 37 | unsigned long CurrentTick() const { return tick_; } 38 | 39 | private: 40 | volatile unsigned long tick_{0}; 41 | std::priority_queue timers_{}; 42 | }; 43 | 44 | extern TimerManager* timer_manager; 45 | extern unsigned long lapic_timer_freq; 46 | const int kTimerFreq = 100; 47 | 48 | const int kTaskTimerPeriod = static_cast(kTimerFreq * 0.02); 49 | const int kTaskTimerValue = std::numeric_limits::max(); 50 | -------------------------------------------------------------------------------- /kernel/uefi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef NULL 4 | #undef NULL 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | #undef NULL 11 | #include 12 | 13 | #define EFIAPI __attribute__((ms_abi)) 14 | 15 | inline EFI_RUNTIME_SERVICES* uefi_rt; 16 | -------------------------------------------------------------------------------- /kernel/usb/arraymap.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/arraymap.hpp 3 | * 4 | * 固定長配列を用いた簡易なマップ実装. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace usb { 13 | template 14 | class ArrayMap { 15 | public: 16 | std::optional Get(const K& key) const { 17 | for (int i = 0; i < table_.size(); ++i) { 18 | if (auto opt_k = table_[i].first; opt_k && opt_k.value() == key) { 19 | return table_[i].second; 20 | } 21 | } 22 | return std::nullopt; 23 | } 24 | 25 | void Put(const K& key, const V& value) { 26 | for (int i = 0; i < table_.size(); ++i) { 27 | if (!table_[i].first) { 28 | table_[i].first = key; 29 | table_[i].second = value; 30 | break; 31 | } 32 | } 33 | } 34 | 35 | void Delete(const K& key) { 36 | for (int i = 0; i < table_.size(); ++i) { 37 | if (auto opt_k = table_[i].first; opt_k && opt_k.value() == key) { 38 | table_[i].first = std::nullopt; 39 | break; 40 | } 41 | } 42 | } 43 | 44 | private: 45 | std::array, V>, N> table_{}; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/base.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/base.hpp" 2 | 3 | namespace usb { 4 | ClassDriver::ClassDriver(Device* dev) : dev_{dev} { 5 | } 6 | 7 | ClassDriver::~ClassDriver() { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/base.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/base.hpp 3 | * 4 | * USB デバイスクラス用のドライバのベースクラス. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "error.hpp" 12 | #include "usb/endpoint.hpp" 13 | #include "usb/setupdata.hpp" 14 | 15 | namespace usb { 16 | class Device; 17 | 18 | class ClassDriver { 19 | public: 20 | ClassDriver(Device* dev); 21 | virtual ~ClassDriver(); 22 | 23 | virtual Error Initialize() = 0; 24 | virtual Error SetEndpoint(const std::vector& configs) = 0; 25 | virtual Error OnEndpointsConfigured() = 0; 26 | virtual Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, 27 | const void* buf, int len) = 0; 28 | virtual Error OnNormalCompleted(EndpointID ep_id, const void* buf, int len) = 0; 29 | 30 | /** このクラスドライバを保持する USB デバイスを返す. */ 31 | Device* ParentDevice() const { return dev_; } 32 | 33 | private: 34 | Device* dev_; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/cdc.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/cdc.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "logger.hpp" 8 | #include "usb/device.hpp" 9 | 10 | namespace usb::cdc { 11 | CDCDriver::CDCDriver(Device* dev, const InterfaceDescriptor* if_comm, 12 | const InterfaceDescriptor* if_data) 13 | : ClassDriver{dev}, 14 | if_data_index_{if_data->interface_number} 15 | { 16 | } 17 | 18 | Error CDCDriver::Initialize() { 19 | return MAKE_ERROR(Error::kNotImplemented); 20 | } 21 | 22 | Error CDCDriver::SetEndpoint(const std::vector& configs) { 23 | for (const auto& config : configs) { 24 | if (config.ep_type == EndpointType::kInterrupt && config.ep_id.IsIn()) { 25 | ep_interrupt_in_ = config.ep_id; 26 | } else if (config.ep_type == EndpointType::kBulk && config.ep_id.IsIn()) { 27 | ep_bulk_in_ = config.ep_id; 28 | } else if (config.ep_type == EndpointType::kBulk && !config.ep_id.IsIn()) { 29 | ep_bulk_out_ = config.ep_id; 30 | } 31 | } 32 | return MAKE_ERROR(Error::kSuccess); 33 | } 34 | 35 | Error CDCDriver::OnEndpointsConfigured() { 36 | return MAKE_ERROR(Error::kSuccess); 37 | } 38 | 39 | Error CDCDriver::OnControlCompleted(EndpointID ep_id, SetupData setup_data, 40 | const void* buf, int len) { 41 | Log(kDebug, "CDCDriver::OnControlCompleted: req_type=0x%02x req=0x%02x len=%u\n", 42 | setup_data.request_type.data, setup_data.request, len); 43 | return MAKE_ERROR(Error::kSuccess); 44 | } 45 | 46 | Error CDCDriver::OnNormalCompleted(EndpointID ep_id, const void* buf, int len) { 47 | Log(kDebug, "CDCDriver::OnNormalCompleted: buf='%.*s'\n", len, buf); 48 | auto buf8 = reinterpret_cast(buf); 49 | if (ep_id == ep_bulk_in_) { 50 | std::copy_n(buf8, len, std::back_inserter(receive_buf_)); 51 | } else if (ep_id == ep_bulk_out_) { 52 | } else { 53 | return MAKE_ERROR(Error::kEndpointNotInCharge); 54 | } 55 | delete[] buf8; 56 | return MAKE_ERROR(Error::kSuccess); 57 | } 58 | 59 | Error CDCDriver::SendSerial(const void* buf, int len) { 60 | uint8_t* buf_out = new uint8_t[len]; 61 | memcpy(buf_out, buf, len); 62 | if (auto err = ParentDevice()->NormalOut(ep_bulk_out_, buf_out, len)) { 63 | Log(kError, "%s:%d: NormalOut failed: %s\n", err.File(), err.Line(), err.Name()); 64 | return err; 65 | } 66 | 67 | uint8_t* buf_in = new uint8_t[8]; 68 | if (auto err = ParentDevice()->NormalIn(ep_bulk_in_, buf_in, 8)) { 69 | Log(kError, "%s:%d: NormalIn failed: %s\n", err.File(), err.Line(), err.Name()); 70 | return err; 71 | } 72 | return MAKE_ERROR(Error::kSuccess); 73 | } 74 | 75 | int CDCDriver::ReceiveSerial(void* buf, int len) { 76 | const auto recv_len = std::min(len, static_cast(receive_buf_.size())); 77 | auto buf8 = reinterpret_cast(buf); 78 | for (int i = 0; i < recv_len; ++i) { 79 | buf8[i] = receive_buf_.front(); 80 | receive_buf_.pop_front(); 81 | } 82 | return recv_len; 83 | } 84 | 85 | Error CDCDriver::SetLineCoding(const LineCoding& value) { 86 | SetupData setup_data{}; 87 | setup_data.request_type.bits.direction = request_type::kOut; 88 | setup_data.request_type.bits.type = request_type::kClass; 89 | setup_data.request_type.bits.recipient = request_type::kInterface; 90 | setup_data.request = request::kSetLineCoding; 91 | setup_data.value = 0; 92 | setup_data.index = if_data_index_; 93 | setup_data.length = sizeof(LineCoding); 94 | 95 | line_coding_ = value; 96 | 97 | return ParentDevice()->ControlOut( 98 | kDefaultControlPipeID, setup_data, 99 | &line_coding_, sizeof(LineCoding), this); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/cdc.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/cdc.hpp 3 | * 4 | * CDC class drivers. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "usb/classdriver/base.hpp" 13 | #include "usb/descriptor.hpp" 14 | 15 | namespace usb::cdc { 16 | enum class DescriptorSubType : uint8_t { 17 | kHeader = 0, 18 | kCM = 1, // Call Management 19 | kACM = 2, // Abstract Control Management 20 | kUnion = 6, 21 | }; 22 | 23 | struct FunctionalDescriptor { 24 | static const uint8_t kType = 36; // CS_INTERFACE 25 | 26 | uint8_t length; // offset 0 27 | uint8_t descriptor_type; // offset 1 28 | DescriptorSubType descriptor_subtype; // offset 2 29 | } __attribute__((packed)); 30 | 31 | struct HeaderDescriptor : public FunctionalDescriptor { 32 | static const auto kSubType = DescriptorSubType::kHeader; 33 | 34 | uint16_t cdc; 35 | } __attribute__((packed)); 36 | 37 | struct CMDescriptor : public FunctionalDescriptor { 38 | static const auto kSubType = DescriptorSubType::kCM; 39 | 40 | union { 41 | uint8_t data; 42 | struct { 43 | uint8_t handle_call_management : 1; 44 | uint8_t data_interface_usable : 1; 45 | uint8_t : 6; 46 | } __attribute__((packed)) bits; 47 | } capabilities; 48 | uint8_t data_interface; 49 | } __attribute__((packed)); 50 | 51 | struct ACMDescriptor : public FunctionalDescriptor { 52 | static const auto kSubType = DescriptorSubType::kACM; 53 | 54 | union { 55 | uint8_t data; 56 | struct { 57 | uint8_t comm_feature : 1; 58 | uint8_t hw_handshake : 1; 59 | uint8_t send_break : 1; 60 | uint8_t conn_notification : 1; 61 | uint8_t : 4; 62 | } __attribute__((packed)) bits; 63 | } capabilities; 64 | } __attribute__((packed)); 65 | 66 | struct UnionDescriptor : public FunctionalDescriptor { 67 | static const auto kSubType = DescriptorSubType::kUnion; 68 | 69 | uint8_t control_interface; 70 | uint8_t SubordinateInterface(size_t index) const { 71 | return reinterpret_cast(this)[index + 4]; 72 | } 73 | } __attribute__((packed)); 74 | 75 | template 76 | const T* FuncDescDynamicCast(const uint8_t* desc_data) { 77 | if (desc_data[1] == T::kType && 78 | desc_data[2] == static_cast(T::kSubType)) { 79 | return reinterpret_cast(desc_data); 80 | } 81 | return nullptr; 82 | } 83 | 84 | template 85 | T* FuncDescDynamicCast(uint8_t* desc_data) { 86 | if (FuncDescDynamicCast(desc_data)) { 87 | return reinterpret_cast(desc_data); 88 | } 89 | return nullptr; 90 | } 91 | 92 | enum class CharFormat : uint8_t { 93 | kStopBit1, 94 | kStopBit15, 95 | kStopBit2 96 | }; 97 | 98 | enum class ParityType : uint8_t { 99 | kNone, 100 | kOdd, 101 | kEven, 102 | kMark, 103 | kSpace 104 | }; 105 | 106 | struct LineCoding { 107 | uint32_t dte_rate; 108 | CharFormat char_format; 109 | ParityType parity_type; 110 | uint8_t data_bits; // 5, 6, 7, 8, 16 111 | } __attribute__((packed)); 112 | 113 | class CDCDriver : public ClassDriver { 114 | public: 115 | CDCDriver(Device* dev, const InterfaceDescriptor* if_comm, 116 | const InterfaceDescriptor* if_data); 117 | 118 | Error Initialize() override; 119 | Error SetEndpoint(const std::vector& configs) override; 120 | Error OnEndpointsConfigured() override; 121 | Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, 122 | const void* buf, int len) override; 123 | Error OnNormalCompleted(EndpointID ep_id, const void* buf, int len) override; 124 | 125 | Error SendSerial(const void* buf, int len); 126 | int ReceiveSerial(void* buf, int len); 127 | Error SetLineCoding(const LineCoding& value); 128 | 129 | private: 130 | EndpointID ep_interrupt_in_, ep_bulk_in_, ep_bulk_out_; 131 | std::deque receive_buf_; 132 | uint8_t if_data_index_; 133 | LineCoding line_coding_; 134 | }; 135 | 136 | inline CDCDriver* driver = nullptr; 137 | } 138 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/hid.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/hid.hpp" 2 | 3 | #include 4 | #include "usb/device.hpp" 5 | #include "logger.hpp" 6 | 7 | namespace usb { 8 | HIDBaseDriver::HIDBaseDriver(Device* dev, int interface_index, 9 | int in_packet_size) 10 | : ClassDriver{dev}, interface_index_{interface_index}, 11 | in_packet_size_{in_packet_size} { 12 | } 13 | 14 | Error HIDBaseDriver::Initialize() { 15 | return MAKE_ERROR(Error::kNotImplemented); 16 | } 17 | 18 | Error HIDBaseDriver::SetEndpoint(const std::vector& configs) { 19 | for (const auto& config : configs) { 20 | if (config.ep_type == EndpointType::kInterrupt && config.ep_id.IsIn()) { 21 | ep_interrupt_in_ = config.ep_id; 22 | } else if (config.ep_type == EndpointType::kInterrupt && !config.ep_id.IsIn()) { 23 | ep_interrupt_out_ = config.ep_id; 24 | } 25 | } 26 | return MAKE_ERROR(Error::kSuccess); 27 | } 28 | 29 | Error HIDBaseDriver::OnEndpointsConfigured() { 30 | SetupData setup_data{}; 31 | setup_data.request_type.bits.direction = request_type::kOut; 32 | setup_data.request_type.bits.type = request_type::kClass; 33 | setup_data.request_type.bits.recipient = request_type::kInterface; 34 | setup_data.request = request::kSetProtocol; 35 | setup_data.value = 0; // boot protocol 36 | setup_data.index = interface_index_; 37 | setup_data.length = 0; 38 | 39 | initialize_phase_ = 1; 40 | return ParentDevice()->ControlOut(kDefaultControlPipeID, setup_data, nullptr, 0, this); 41 | } 42 | 43 | Error HIDBaseDriver::OnControlCompleted(EndpointID ep_id, SetupData setup_data, 44 | const void* buf, int len) { 45 | Log(kDebug, "HIDBaseDriver::OnControlCompleted: dev %08lx, phase = %d, len = %d\n", 46 | reinterpret_cast(this), initialize_phase_, len); 47 | if (initialize_phase_ == 1) { 48 | initialize_phase_ = 2; 49 | return ParentDevice()->NormalIn(ep_interrupt_in_, buf_.data(), in_packet_size_); 50 | } 51 | 52 | return MAKE_ERROR(Error::kNotImplemented); 53 | } 54 | 55 | Error HIDBaseDriver::OnNormalCompleted(EndpointID ep_id, const void* buf, int len) { 56 | if (ep_id.IsIn()) { 57 | OnDataReceived(); 58 | std::copy_n(buf_.begin(), len, previous_buf_.begin()); 59 | return ParentDevice()->NormalIn(ep_interrupt_in_, buf_.data(), in_packet_size_); 60 | } 61 | 62 | return MAKE_ERROR(Error::kEndpointNotInCharge); 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/hid.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/hid.hpp 3 | * 4 | * HID base class driver. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "usb/classdriver/base.hpp" 10 | 11 | namespace usb { 12 | class HIDBaseDriver : public ClassDriver { 13 | public: 14 | HIDBaseDriver(Device* dev, int interface_index, int in_packet_size); 15 | Error Initialize() override; 16 | Error SetEndpoint(const std::vector& configs) override; 17 | Error OnEndpointsConfigured() override; 18 | Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, 19 | const void* buf, int len) override; 20 | Error OnNormalCompleted(EndpointID ep_id, const void* buf, int len) override; 21 | 22 | virtual Error OnDataReceived() = 0; 23 | const static size_t kBufferSize = 1024; 24 | const std::array& Buffer() const { return buf_; } 25 | const std::array& PreviousBuffer() const { return previous_buf_; } 26 | 27 | private: 28 | EndpointID ep_interrupt_in_; 29 | EndpointID ep_interrupt_out_; 30 | const int interface_index_; 31 | int in_packet_size_; 32 | int initialize_phase_{0}; 33 | 34 | std::array buf_{}, previous_buf_{}; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/keyboard.hpp" 2 | 3 | #include 4 | #include 5 | #include "usb/memory.hpp" 6 | #include "usb/device.hpp" 7 | 8 | namespace usb { 9 | HIDKeyboardDriver::HIDKeyboardDriver(Device* dev, int interface_index) 10 | : HIDBaseDriver{dev, interface_index, 8} { 11 | } 12 | 13 | Error HIDKeyboardDriver::OnDataReceived() { 14 | std::bitset<256> prev, current; 15 | for (int i = 2; i < 8; ++i) { 16 | prev.set(PreviousBuffer()[i], true); 17 | current.set(Buffer()[i], true); 18 | } 19 | const auto changed = prev ^ current; 20 | const auto pressed = changed & current; 21 | for (int key = 1; key < 256; ++key) { 22 | if (changed.test(key)) { 23 | NotifyKeyPush(Buffer()[0], key, pressed.test(key)); 24 | } 25 | } 26 | return MAKE_ERROR(Error::kSuccess); 27 | } 28 | 29 | void* HIDKeyboardDriver::operator new(size_t size) { 30 | return AllocMem(sizeof(HIDKeyboardDriver), 0, 0); 31 | } 32 | 33 | void HIDKeyboardDriver::operator delete(void* ptr) noexcept { 34 | FreeMem(ptr); 35 | } 36 | 37 | void HIDKeyboardDriver::SubscribeKeyPush( 38 | std::function observer) { 39 | observers_[num_observers_++] = observer; 40 | } 41 | 42 | std::function HIDKeyboardDriver::default_observer; 43 | 44 | void HIDKeyboardDriver::NotifyKeyPush(uint8_t modifier, uint8_t keycode, bool press) { 45 | for (int i = 0; i < num_observers_; ++i) { 46 | observers_[i](modifier, keycode, press); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/keyboard.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/keyboard.hpp 3 | * 4 | * HID keyboard class driver. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "usb/classdriver/hid.hpp" 11 | 12 | namespace usb { 13 | class HIDKeyboardDriver : public HIDBaseDriver { 14 | public: 15 | HIDKeyboardDriver(Device* dev, int interface_index); 16 | 17 | void* operator new(size_t size); 18 | void operator delete(void* ptr) noexcept; 19 | 20 | Error OnDataReceived() override; 21 | 22 | using ObserverType = void (uint8_t modifier, uint8_t keycode, bool press); 23 | void SubscribeKeyPush(std::function observer); 24 | static std::function default_observer; 25 | 26 | private: 27 | std::array, 4> observers_; 28 | int num_observers_ = 0; 29 | 30 | void NotifyKeyPush(uint8_t modifier, uint8_t keycode, bool press); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/mouse.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/classdriver/mouse.hpp" 2 | 3 | #include 4 | #include "usb/memory.hpp" 5 | #include "usb/device.hpp" 6 | #include "logger.hpp" 7 | 8 | namespace usb { 9 | HIDMouseDriver::HIDMouseDriver(Device* dev, int interface_index) 10 | : HIDBaseDriver{dev, interface_index, 3} { 11 | } 12 | 13 | Error HIDMouseDriver::OnDataReceived() { 14 | uint8_t buttons = Buffer()[0]; 15 | int8_t displacement_x = Buffer()[1]; 16 | int8_t displacement_y = Buffer()[2]; 17 | NotifyMouseMove(buttons, displacement_x, displacement_y); 18 | Log(kDebug, "%02x,(%3d,%3d)\n", buttons, displacement_x, displacement_y); 19 | return MAKE_ERROR(Error::kSuccess); 20 | } 21 | 22 | void* HIDMouseDriver::operator new(size_t size) { 23 | return AllocMem(sizeof(HIDMouseDriver), 0, 0); 24 | } 25 | 26 | void HIDMouseDriver::operator delete(void* ptr) noexcept { 27 | FreeMem(ptr); 28 | } 29 | 30 | void HIDMouseDriver::SubscribeMouseMove(std::function observer) { 31 | observers_[num_observers_++] = observer; 32 | } 33 | 34 | std::function HIDMouseDriver::default_observer; 35 | 36 | void HIDMouseDriver::NotifyMouseMove( 37 | uint8_t buttons, int8_t displacement_x, int8_t displacement_y) { 38 | for (int i = 0; i < num_observers_; ++i) { 39 | observers_[i](buttons, displacement_x, displacement_y); 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /kernel/usb/classdriver/mouse.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/classdriver/mouse.hpp 3 | * 4 | * HID mouse class driver. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "usb/classdriver/hid.hpp" 11 | 12 | namespace usb { 13 | class HIDMouseDriver : public HIDBaseDriver { 14 | public: 15 | HIDMouseDriver(Device* dev, int interface_index); 16 | 17 | void* operator new(size_t size); 18 | void operator delete(void* ptr) noexcept; 19 | 20 | Error OnDataReceived() override; 21 | 22 | using ObserverType = void (uint8_t buttons, int8_t displacement_x, int8_t displacement_y); 23 | void SubscribeMouseMove(std::function observer); 24 | static std::function default_observer; 25 | 26 | private: 27 | std::array, 4> observers_; 28 | int num_observers_ = 0; 29 | 30 | void NotifyMouseMove(uint8_t buttons, int8_t displacement_x, int8_t displacement_y); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /kernel/usb/descriptor.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/descriptor.hpp 3 | * 4 | * USB Descriptor の定義集. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | namespace usb { 13 | struct DeviceDescriptor { 14 | static const uint8_t kType = 1; 15 | 16 | uint8_t length; // offset 0 17 | uint8_t descriptor_type; // offset 1 18 | uint16_t usb_release; // offset 2 19 | uint8_t device_class; // offset 4 20 | uint8_t device_sub_class; // offset 5 21 | uint8_t device_protocol; // offset 6 22 | uint8_t max_packet_size; // offset 7 23 | uint16_t vendor_id; // offset 8 24 | uint16_t product_id; // offset 10 25 | uint16_t device_release; // offset 12 26 | uint8_t manufacturer; // offset 14 27 | uint8_t product; // offset 15 28 | uint8_t serial_number; // offset 16 29 | uint8_t num_configurations; // offset 17 30 | } __attribute__((packed)); 31 | 32 | struct ConfigurationDescriptor { 33 | static const uint8_t kType = 2; 34 | 35 | uint8_t length; // offset 0 36 | uint8_t descriptor_type; // offset 1 37 | uint16_t total_length; // offset 2 38 | uint8_t num_interfaces; // offset 4 39 | uint8_t configuration_value;// offset 5 40 | uint8_t configuration_id; // offset 6 41 | uint8_t attributes; // offset 7 42 | uint8_t max_power; // offset 8 43 | } __attribute__((packed)); 44 | 45 | struct InterfaceDescriptor { 46 | static const uint8_t kType = 4; 47 | 48 | uint8_t length; // offset 0 49 | uint8_t descriptor_type; // offset 1 50 | uint8_t interface_number; // offset 2 51 | uint8_t alternate_setting; // offset 3 52 | uint8_t num_endpoints; // offset 4 53 | uint8_t interface_class; // offset 5 54 | uint8_t interface_sub_class;// offset 6 55 | uint8_t interface_protocol; // offset 7 56 | uint8_t interface_id; // offset 8 57 | } __attribute__((packed)); 58 | 59 | struct EndpointDescriptor { 60 | static const uint8_t kType = 5; 61 | 62 | uint8_t length; // offset 0 63 | uint8_t descriptor_type; // offset 1 64 | union { 65 | uint8_t data; 66 | struct { 67 | uint8_t number : 4; 68 | uint8_t : 3; 69 | uint8_t dir_in : 1; 70 | } __attribute__((packed)) bits; 71 | } endpoint_address; // offset 2 72 | union { 73 | uint8_t data; 74 | struct { 75 | uint8_t transfer_type : 2; 76 | uint8_t sync_type : 2; 77 | uint8_t usage_type : 2; 78 | uint8_t : 2; 79 | } __attribute__((packed)) bits; 80 | } attributes; // offset 3 81 | uint16_t max_packet_size; // offset 4 82 | uint8_t interval; // offset 6 83 | } __attribute__((packed)); 84 | 85 | struct HIDDescriptor { 86 | static const uint8_t kType = 33; 87 | 88 | uint8_t length; // offset 0 89 | uint8_t descriptor_type; // offset 1 90 | uint16_t hid_release; // offset 2 91 | uint8_t country_code; // offset 4 92 | uint8_t num_descriptors; // offset 5 93 | 94 | struct ClassDescriptor { 95 | /** @brief クラス特有ディスクリプタのタイプ値. */ 96 | uint8_t descriptor_type; 97 | /** @brief クラス特有ディスクリプタのバイト数. */ 98 | uint16_t descriptor_length; 99 | } __attribute__((packed)); 100 | 101 | /** @brief HID 特有のディスクリプタに関する情報を得る. 102 | * 103 | * HID はクラス特有(class-specific)のディスクリプタを 1 つ以上持つ. 104 | * その数は num_descriptors に記載されている. 105 | * Report ディスクリプタ(type = 34)は HID デバイスであれば必ず存在するため, 106 | * num_descriptors は必ず 1 以上となる. 107 | * 108 | * @param index 取得するディスクリプタの番号.0 <= index < num_descriptors. 109 | * @return index で指定されたディスクリプタの情報.index が範囲外なら nullptr. 110 | */ 111 | ClassDescriptor* GetClassDescriptor(size_t index) const { 112 | if (index >= num_descriptors) { 113 | return nullptr; 114 | } 115 | const auto end_of_struct = 116 | reinterpret_cast(this) + sizeof(HIDDescriptor); 117 | return reinterpret_cast(end_of_struct) + index; 118 | } 119 | } __attribute__((packed)); 120 | 121 | template 122 | T* DescriptorDynamicCast(uint8_t* desc_data) { 123 | if (desc_data[1] == T::kType) { 124 | return reinterpret_cast(desc_data); 125 | } 126 | return nullptr; 127 | } 128 | 129 | template 130 | const T* DescriptorDynamicCast(const uint8_t* desc_data) { 131 | if (desc_data[1] == T::kType) { 132 | return reinterpret_cast(desc_data); 133 | } 134 | return nullptr; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /kernel/usb/device.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/device.hpp 3 | * 4 | * USB デバイスを表すクラスと関連機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "error.hpp" 13 | #include "usb/arraymap.hpp" 14 | #include "usb/classdriver/base.hpp" 15 | #include "usb/descriptor.hpp" 16 | #include "usb/endpoint.hpp" 17 | #include "usb/setupdata.hpp" 18 | 19 | namespace usb { 20 | class Device { 21 | public: 22 | virtual ~Device(); 23 | virtual Error ControlIn(EndpointID ep_id, SetupData setup_data, 24 | void* buf, int len, ClassDriver* issuer); 25 | virtual Error ControlOut(EndpointID ep_id, SetupData setup_data, 26 | const void* buf, int len, ClassDriver* issuer); 27 | virtual Error NormalIn(EndpointID ep_id, void* buf, int len); 28 | virtual Error NormalOut(EndpointID ep_id, const void* buf, int len); 29 | 30 | Error StartInitialize(); 31 | bool IsInitialized() { return is_initialized_; } 32 | auto& EndpointConfigs() const { return ep_configs_; } 33 | Error OnEndpointsConfigured(); 34 | 35 | uint8_t* Buffer() { return buf_.data(); } 36 | const DeviceDescriptor& DeviceDesc() const { return device_desc_; } 37 | 38 | protected: 39 | Error OnControlCompleted(EndpointID ep_id, SetupData setup_data, 40 | const void* buf, int len); 41 | Error OnNormalCompleted(EndpointID ep_id, const void* buf, int len); 42 | 43 | private: 44 | std::vector class_drivers_{}; 45 | 46 | std::array buf_{}; 47 | DeviceDescriptor device_desc_; 48 | 49 | // following fields are used during initialization 50 | uint8_t num_configurations_; 51 | uint8_t config_index_; 52 | 53 | Error OnDeviceDescriptorReceived(const uint8_t* buf, int len); 54 | Error OnConfigurationDescriptorReceived(const uint8_t* buf, int len); 55 | Error OnSetConfigurationCompleted(uint8_t config_value); 56 | 57 | bool is_initialized_ = false; 58 | int initialize_phase_ = 0; 59 | std::vector ep_configs_{}; 60 | Error InitializePhase1(const uint8_t* buf, int len); 61 | Error InitializePhase2(const uint8_t* buf, int len); 62 | Error InitializePhase3(uint8_t config_value); 63 | Error InitializePhase4(); 64 | 65 | /** OnControlCompleted の中で要求の発行元を特定するためのマップ構造. 66 | * ControlOut または ControlIn を発行したときに発行元が登録される. 67 | */ 68 | ArrayMap event_waiters_{}; 69 | }; 70 | 71 | Error GetDescriptor(Device& dev, EndpointID ep_id, 72 | uint8_t desc_type, uint8_t desc_index, 73 | void* buf, int len, bool debug = false); 74 | Error SetConfiguration(Device& dev, EndpointID ep_id, 75 | uint8_t config_value, bool debug = false); 76 | } 77 | -------------------------------------------------------------------------------- /kernel/usb/endpoint.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/endpoint.hpp 3 | * 4 | * エンドポイント設定に関する機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include "error.hpp" 10 | 11 | namespace usb { 12 | enum class EndpointType { 13 | kControl = 0, 14 | kIsochronous = 1, 15 | kBulk = 2, 16 | kInterrupt = 3, 17 | }; 18 | 19 | class EndpointID { 20 | public: 21 | constexpr EndpointID() : addr_{0} {} 22 | constexpr EndpointID(const EndpointID& ep_id) : addr_{ep_id.addr_} {} 23 | explicit constexpr EndpointID(int addr) : addr_{addr} {} 24 | 25 | /** エンドポイント番号と入出力方向から ID を構成する. 26 | * 27 | * ep_num は 0..15 の整数. 28 | * dir_in は Control エンドポイントでは常に true にしなければならない. 29 | */ 30 | constexpr EndpointID(int ep_num, bool dir_in) : addr_{ep_num << 1 | dir_in} {} 31 | 32 | EndpointID& operator =(const EndpointID& rhs) { 33 | addr_ = rhs.addr_; 34 | return *this; 35 | } 36 | 37 | /** エンドポイントアドレス(0..31) */ 38 | int Address() const { return addr_; } 39 | 40 | /** エンドポイント番号(0..15) */ 41 | int Number() const { return addr_ >> 1; } 42 | 43 | /** 入出力方向.Control エンドポイントは true */ 44 | bool IsIn() const { return addr_ & 1; } 45 | 46 | private: 47 | int addr_; 48 | }; 49 | 50 | constexpr EndpointID kDefaultControlPipeID{0, true}; 51 | 52 | struct EndpointConfig { 53 | /** エンドポイント ID */ 54 | EndpointID ep_id; 55 | 56 | /** このエンドポイントの種別 */ 57 | EndpointType ep_type; 58 | 59 | /** このエンドポイントの最大パケットサイズ(バイト) */ 60 | int max_packet_size; 61 | 62 | /** このエンドポイントの制御周期(125*2^(interval-1) マイクロ秒) */ 63 | int interval; 64 | }; 65 | 66 | inline bool operator==(EndpointID lhs, EndpointID rhs) { 67 | return lhs.Address() == rhs.Address(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /kernel/usb/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/memory.hpp" 2 | 3 | #include 4 | 5 | namespace { 6 | template 7 | T Ceil(T value, unsigned int alignment) { 8 | return (value + alignment - 1) & ~static_cast(alignment - 1); 9 | } 10 | 11 | template 12 | T MaskBits(T value, U mask) { 13 | return value & ~static_cast(mask - 1); 14 | } 15 | } 16 | 17 | namespace usb { 18 | alignas(64) uint8_t memory_pool[kMemoryPoolSize]; 19 | uintptr_t alloc_ptr = reinterpret_cast(memory_pool); 20 | 21 | void* AllocMem(size_t size, unsigned int alignment, unsigned int boundary) { 22 | if (alignment > 0) { 23 | alloc_ptr = Ceil(alloc_ptr, alignment); 24 | } 25 | if (boundary > 0) { 26 | auto next_boundary = Ceil(alloc_ptr, boundary); 27 | if (next_boundary < alloc_ptr + size) { 28 | alloc_ptr = next_boundary; 29 | } 30 | } 31 | 32 | if (reinterpret_cast(memory_pool) + kMemoryPoolSize 33 | < alloc_ptr + size) { 34 | return nullptr; 35 | } 36 | 37 | auto p = alloc_ptr; 38 | alloc_ptr += size; 39 | return reinterpret_cast(p); 40 | } 41 | 42 | void FreeMem(void* p) {} 43 | } 44 | -------------------------------------------------------------------------------- /kernel/usb/memory.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/memory.hpp 3 | * 4 | * USB ドライバ用の動的メモリ管理機能 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | namespace usb { 12 | /** @brief 動的メモリ確保のためのメモリプールの最大容量(バイト) */ 13 | static const size_t kMemoryPoolSize = 4096 * 32; 14 | 15 | /** @brief 指定されたバイト数のメモリ領域を確保して先頭ポインタを返す. 16 | * 17 | * 先頭アドレスが alignment に揃ったメモリ領域を確保する. 18 | * size <= boundary ならメモリ領域が boundary を跨がないことを保証する. 19 | * boundary は典型的にはページ境界を跨がないように 4096 を指定する. 20 | * 21 | * @param size 確保するメモリ領域のサイズ(バイト単位) 22 | * @param alignment メモリ領域のアライメント制約.0 なら制約しない. 23 | * @param boundary 確保したメモリ領域が跨いではいけない境界.0 なら制約しない. 24 | * @return 確保できなかった場合は nullptr 25 | */ 26 | void* AllocMem(size_t size, unsigned int alignment, unsigned int boundary); 27 | 28 | template 29 | T* AllocArray(size_t num_obj, unsigned int alignment, unsigned int boundary) { 30 | return reinterpret_cast( 31 | AllocMem(sizeof(T) * num_obj, alignment, boundary)); 32 | } 33 | 34 | /** @brief 指定されたメモリ領域を解放する.本当に解放することは保証されない. */ 35 | void FreeMem(void* p); 36 | 37 | /** @brief 標準コンテナ用のメモリアロケータ */ 38 | template 39 | class Allocator { 40 | public: 41 | using size_type = size_t; 42 | using pointer = T*; 43 | using value_type = T; 44 | 45 | Allocator() noexcept = default; 46 | Allocator(const Allocator&) noexcept = default; 47 | template Allocator(const Allocator&) noexcept {} 48 | ~Allocator() noexcept = default; 49 | Allocator& operator=(const Allocator&) = default; 50 | 51 | pointer allocate(size_type n) { 52 | return AllocArray(n, Alignment, Boundary); 53 | } 54 | 55 | void deallocate(pointer p, size_type num) { 56 | FreeMem(p); 57 | } 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /kernel/usb/setupdata.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace usb { 4 | namespace request_type { 5 | // bmRequestType recipient 6 | const int kDevice = 0; 7 | const int kInterface = 1; 8 | const int kEndpoint = 2; 9 | const int kOther = 3; 10 | 11 | // bmRequestType type 12 | const int kStandard = 0; 13 | const int kClass = 1; 14 | const int kVendor = 2; 15 | 16 | // bmRequestType direction 17 | const int kOut = 0; 18 | const int kIn = 1; 19 | } 20 | 21 | namespace request { 22 | const int kGetStatus = 0; 23 | const int kClearFeature = 1; 24 | const int kSetFeature = 3; 25 | const int kSetAddress = 5; 26 | const int kGetDescriptor = 6; 27 | const int kSetDescriptor = 7; 28 | const int kGetConfiguration = 8; 29 | const int kSetConfiguration = 9; 30 | const int kGetInterface = 10; 31 | const int kSetInterface = 11; 32 | const int kSynchFrame = 12; 33 | const int kSetEncryption = 13; 34 | const int kGetEncryption = 14; 35 | const int kSetHandshake = 15; 36 | const int kGetHandshake = 16; 37 | const int kSetConnection = 17; 38 | const int kSetSecurityData = 18; 39 | const int kGetSecurityData = 19; 40 | const int kSetWUSBData = 20; 41 | const int kLoopbackDataWrite = 21; 42 | const int kLoopbackDataRead = 22; 43 | const int kSetInterfaceDS = 23; 44 | const int kSetSel = 48; 45 | const int kSetIsochDelay = 49; 46 | 47 | // HID class specific request values 48 | const int kGetReport = 1; 49 | const int kSetProtocol = 11; 50 | 51 | // CDC class specific request values 52 | const int kSetLineCoding = 0x20; 53 | const int kGetLineCoding = 0x21; 54 | } 55 | 56 | namespace descriptor_type { 57 | const int kDevice = 1; 58 | const int kConfiguration = 2; 59 | const int kString = 3; 60 | const int kInterface = 4; 61 | const int kEndpoint = 5; 62 | const int kInterfacePower = 8; 63 | const int kOTG = 9; 64 | const int kDebug = 10; 65 | const int kInterfaceAssociation = 11; 66 | const int kBOS = 15; 67 | const int kDeviceCapability = 16; 68 | const int kHID = 33; 69 | const int kSuperspeedUSBEndpointCompanion = 48; 70 | const int kSuperspeedPlusIsochronousEndpointCompanion = 49; 71 | } 72 | 73 | struct SetupData { 74 | union { 75 | uint8_t data; 76 | struct { 77 | uint8_t recipient : 5; 78 | uint8_t type : 2; 79 | uint8_t direction : 1; 80 | } bits; 81 | } request_type; 82 | uint8_t request; 83 | uint16_t value; 84 | uint16_t index; 85 | uint16_t length; 86 | } __attribute__((packed)); 87 | 88 | inline bool operator ==(SetupData lhs, SetupData rhs) { 89 | return 90 | lhs.request_type.data == rhs.request_type.data && 91 | lhs.request == rhs.request && 92 | lhs.value == rhs.value && 93 | lhs.index == rhs.index && 94 | lhs.length == rhs.length; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /kernel/usb/xhci/context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "usb/endpoint.hpp" 4 | 5 | namespace usb::xhci { 6 | class Ring; 7 | union TRB; 8 | 9 | union SlotContext { 10 | uint32_t dwords[8]; 11 | struct { 12 | uint32_t route_string : 20; 13 | uint32_t speed : 4; 14 | uint32_t : 1; // reserved 15 | uint32_t mtt : 1; 16 | uint32_t hub : 1; 17 | uint32_t context_entries : 5; 18 | 19 | uint32_t max_exit_latency : 16; 20 | uint32_t root_hub_port_num : 8; 21 | uint32_t num_ports : 8; 22 | 23 | // TT : Transaction Translator 24 | uint32_t tt_hub_slot_id : 8; 25 | uint32_t tt_port_num : 8; 26 | uint32_t ttt: 2; 27 | uint32_t : 4; // reserved 28 | uint32_t interrupter_target : 10; 29 | 30 | uint32_t usb_device_address : 8; 31 | uint32_t : 19; 32 | uint32_t slot_state : 5; 33 | } __attribute__((packed)) bits; 34 | } __attribute__((packed)); 35 | 36 | union EndpointContext { 37 | uint32_t dwords[8]; 38 | struct { 39 | uint32_t ep_state : 3; 40 | uint32_t : 5; 41 | uint32_t mult : 2; 42 | uint32_t max_primary_streams : 5; 43 | uint32_t linear_stream_array : 1; 44 | uint32_t interval : 8; 45 | uint32_t max_esit_payload_hi : 8; 46 | 47 | uint32_t : 1; 48 | uint32_t error_count : 2; 49 | uint32_t ep_type : 3; 50 | uint32_t : 1; 51 | uint32_t host_initiate_disable : 1; 52 | uint32_t max_burst_size : 8; 53 | uint32_t max_packet_size : 16; 54 | 55 | uint32_t dequeue_cycle_state : 1; 56 | uint32_t : 3; 57 | uint64_t tr_dequeue_pointer : 60; 58 | 59 | uint32_t average_trb_length : 16; 60 | uint32_t max_esit_payload_lo : 16; 61 | } __attribute__((packed)) bits; 62 | 63 | TRB* TransferRingBuffer() const { 64 | return reinterpret_cast(bits.tr_dequeue_pointer << 4); 65 | } 66 | 67 | void SetTransferRingBuffer(TRB* buffer) { 68 | bits.tr_dequeue_pointer = reinterpret_cast(buffer) >> 4; 69 | } 70 | } __attribute__((packed)); 71 | 72 | struct DeviceContextIndex { 73 | int value; 74 | 75 | explicit DeviceContextIndex(int dci) : value{dci} {} 76 | DeviceContextIndex(EndpointID ep_id) : value{ep_id.Address()} {} 77 | 78 | DeviceContextIndex(int ep_num, bool dir_in) 79 | : value{2 * ep_num + (ep_num == 0 ? 1 : dir_in)} {} 80 | 81 | DeviceContextIndex(const DeviceContextIndex& rhs) = default; 82 | DeviceContextIndex& operator =(const DeviceContextIndex& rhs) = default; 83 | }; 84 | 85 | struct DeviceContext { 86 | SlotContext slot_context; 87 | EndpointContext ep_contexts[31]; 88 | } __attribute__((packed)); 89 | 90 | struct InputControlContext { 91 | uint32_t drop_context_flags; 92 | uint32_t add_context_flags; 93 | uint32_t reserved1[5]; 94 | uint8_t configuration_value; 95 | uint8_t interface_number; 96 | uint8_t alternate_setting; 97 | uint8_t reserved2; 98 | } __attribute__((packed)); 99 | 100 | struct InputContext { 101 | InputControlContext input_control_context; 102 | SlotContext slot_context; 103 | EndpointContext ep_contexts[31]; 104 | 105 | /** @brief Enable the slot context. 106 | * 107 | * @return Pointer to the slot context enabled. 108 | */ 109 | SlotContext* EnableSlotContext() { 110 | input_control_context.add_context_flags |= 1; 111 | return &slot_context; 112 | } 113 | 114 | /** @brief Enable an endpoint. 115 | * 116 | * @param dci Device Context Index (1 .. 31) 117 | * @return Pointer to the endpoint context enabled. 118 | */ 119 | EndpointContext* EnableEndpoint(DeviceContextIndex dci) { 120 | input_control_context.add_context_flags |= 1u << dci.value; 121 | return &ep_contexts[dci.value - 1]; 122 | } 123 | } __attribute__((packed)); 124 | } 125 | -------------------------------------------------------------------------------- /kernel/usb/xhci/device.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/device.hpp 3 | * 4 | * USB デバイスを表すクラスと関連機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "error.hpp" 13 | #include "usb/device.hpp" 14 | #include "usb/arraymap.hpp" 15 | #include "usb/xhci/context.hpp" 16 | #include "usb/xhci/trb.hpp" 17 | #include "usb/xhci/registers.hpp" 18 | 19 | namespace usb::xhci { 20 | class Device : public usb::Device { 21 | public: 22 | enum class State { 23 | kInvalid, 24 | kBlank, 25 | kSlotAssigning, 26 | kSlotAssigned 27 | }; 28 | 29 | using OnTransferredCallbackType = void ( 30 | Device* dev, 31 | DeviceContextIndex dci, 32 | int completion_code, 33 | int trb_transfer_length, 34 | TRB* issue_trb); 35 | 36 | Device(uint8_t slot_id, DoorbellRegister* dbreg); 37 | 38 | Error Initialize(); 39 | 40 | DeviceContext* DeviceContext() { return &ctx_; } 41 | InputContext* InputContext() { return &input_ctx_; } 42 | //usb::Device* USBDevice() { return usb_device_; } 43 | //void SetUSBDevice(usb::Device* value) { usb_device_ = value; } 44 | 45 | State State() const { return state_; } 46 | uint8_t SlotID() const { return slot_id_; } 47 | 48 | void SelectForSlotAssignment(); 49 | Ring* AllocTransferRing(DeviceContextIndex index, size_t buf_size); 50 | 51 | Error ControlIn(EndpointID ep_id, SetupData setup_data, 52 | void* buf, int len, ClassDriver* issuer) override; 53 | Error ControlOut(EndpointID ep_id, SetupData setup_data, 54 | const void* buf, int len, ClassDriver* issuer) override; 55 | Error NormalIn(EndpointID ep_id, void* buf, int len) override; 56 | Error NormalOut(EndpointID ep_id, const void* buf, int len) override; 57 | 58 | Error OnTransferEventReceived(const TransferEventTRB& trb); 59 | 60 | private: 61 | alignas(64) struct DeviceContext ctx_; 62 | alignas(64) struct InputContext input_ctx_; 63 | 64 | const uint8_t slot_id_; 65 | DoorbellRegister* const dbreg_; 66 | 67 | enum State state_; 68 | std::array transfer_rings_; // index = dci - 1 69 | 70 | /** コントロール転送が完了した際に DataStageTRB や StatusStageTRB 71 | * から対応する SetupStageTRB を検索するためのマップ. 72 | */ 73 | ArrayMap setup_stage_map_{}; 74 | 75 | Error PushOneTransaction(EndpointID ep_id, const void* buf, int len); 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /kernel/usb/xhci/devmgr.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/devmgr.hpp" 2 | 3 | #include "usb/memory.hpp" 4 | 5 | namespace usb::xhci { 6 | Error DeviceManager::Initialize(size_t max_slots) { 7 | max_slots_ = max_slots; 8 | 9 | devices_ = AllocArray(max_slots_ + 1, 0, 0); 10 | if (devices_ == nullptr) { 11 | return MAKE_ERROR(Error::kNoEnoughMemory); 12 | } 13 | 14 | device_context_pointers_ = AllocArray(max_slots_ + 1, 64, 4096); 15 | if (device_context_pointers_ == nullptr) { 16 | FreeMem(devices_); 17 | return MAKE_ERROR(Error::kNoEnoughMemory); 18 | } 19 | 20 | for (size_t i = 0; i <= max_slots_; ++i) { 21 | devices_[i] = nullptr; 22 | device_context_pointers_[i] = nullptr; 23 | } 24 | 25 | return MAKE_ERROR(Error::kSuccess); 26 | } 27 | 28 | DeviceContext** DeviceManager::DeviceContexts() const { 29 | return device_context_pointers_; 30 | } 31 | 32 | Device* DeviceManager::FindByPort(uint8_t port_num, uint32_t route_string) const { 33 | for (size_t i = 1; i <= max_slots_; ++i) { 34 | auto dev = devices_[i]; 35 | if (dev == nullptr) continue; 36 | if (dev->DeviceContext()->slot_context.bits.root_hub_port_num == port_num) { 37 | return dev; 38 | } 39 | } 40 | return nullptr; 41 | } 42 | 43 | Device* DeviceManager::FindByState(enum Device::State state) const { 44 | for (size_t i = 1; i <= max_slots_; ++i) { 45 | auto dev = devices_[i]; 46 | if (dev == nullptr) continue; 47 | if (dev->State() == state) { 48 | return dev; 49 | } 50 | } 51 | return nullptr; 52 | } 53 | 54 | Device* DeviceManager::FindBySlot(uint8_t slot_id) const { 55 | if (slot_id > max_slots_) { 56 | return nullptr; 57 | } 58 | return devices_[slot_id]; 59 | } 60 | 61 | /* 62 | WithError DeviceManager::Get(uint8_t device_id) const { 63 | if (device_id >= num_devices_) { 64 | return {nullptr, Error::kInvalidDeviceId}; 65 | } 66 | return {&devices_[device_id], Error::kSuccess}; 67 | } 68 | */ 69 | 70 | Error DeviceManager::AllocDevice(uint8_t slot_id, DoorbellRegister* dbreg) { 71 | if (slot_id > max_slots_) { 72 | return MAKE_ERROR(Error::kInvalidSlotID); 73 | } 74 | 75 | if (devices_[slot_id] != nullptr) { 76 | return MAKE_ERROR(Error::kAlreadyAllocated); 77 | } 78 | 79 | devices_[slot_id] = AllocArray(1, 64, 4096); 80 | new(devices_[slot_id]) Device(slot_id, dbreg); 81 | return MAKE_ERROR(Error::kSuccess); 82 | } 83 | 84 | Error DeviceManager::LoadDCBAA(uint8_t slot_id) { 85 | if (slot_id > max_slots_) { 86 | return MAKE_ERROR(Error::kInvalidSlotID); 87 | } 88 | 89 | auto dev = devices_[slot_id]; 90 | device_context_pointers_[slot_id] = dev->DeviceContext(); 91 | return MAKE_ERROR(Error::kSuccess); 92 | } 93 | 94 | Error DeviceManager::Remove(uint8_t slot_id) { 95 | device_context_pointers_[slot_id] = nullptr; 96 | FreeMem(devices_[slot_id]); 97 | devices_[slot_id] = nullptr; 98 | return MAKE_ERROR(Error::kSuccess); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /kernel/usb/xhci/devmgr.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/devmgr.hpp 3 | * 4 | * USB デバイスの管理機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "error.hpp" 13 | #include "usb/xhci/context.hpp" 14 | #include "usb/xhci/device.hpp" 15 | 16 | namespace usb::xhci { 17 | class DeviceManager { 18 | 19 | public: 20 | Error Initialize(size_t max_slots); 21 | DeviceContext** DeviceContexts() const; 22 | Device* FindByPort(uint8_t port_num, uint32_t route_string) const; 23 | Device* FindByState(enum Device::State state) const; 24 | Device* FindBySlot(uint8_t slot_id) const; 25 | //WithError Get(uint8_t device_id) const; 26 | Error AllocDevice(uint8_t slot_id, DoorbellRegister* dbreg); 27 | Error LoadDCBAA(uint8_t slot_id); 28 | Error Remove(uint8_t slot_id); 29 | 30 | private: 31 | // device_context_pointers_ can be used as DCBAAP's value. 32 | // The number of elements is max_slots_ + 1. 33 | DeviceContext** device_context_pointers_; 34 | size_t max_slots_; 35 | 36 | // The number of elements is max_slots_ + 1. 37 | Device** devices_; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /kernel/usb/xhci/port.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/port.hpp" 2 | 3 | #include "usb/xhci/xhci.hpp" 4 | #include "usb/xhci/registers.hpp" 5 | 6 | namespace usb::xhci { 7 | uint8_t Port::Number() const { 8 | return port_num_; 9 | } 10 | 11 | bool Port::IsConnected() const { 12 | return port_reg_set_.PORTSC.Read().bits.current_connect_status; 13 | } 14 | 15 | bool Port::IsEnabled() const { 16 | return port_reg_set_.PORTSC.Read().bits.port_enabled_disabled; 17 | } 18 | 19 | bool Port::IsConnectStatusChanged() const { 20 | return port_reg_set_.PORTSC.Read().bits.connect_status_change; 21 | } 22 | 23 | bool Port::IsPortResetChanged() const { 24 | return port_reg_set_.PORTSC.Read().bits.port_reset_change; 25 | } 26 | 27 | int Port::Speed() const { 28 | return port_reg_set_.PORTSC.Read().bits.port_speed; 29 | } 30 | 31 | Error Port::Reset() { 32 | auto portsc = port_reg_set_.PORTSC.Read(); 33 | portsc.data[0] &= 0x0e00c3e0u; 34 | portsc.data[0] |= 0x00020010u; // Write 1 to PR and CSC 35 | port_reg_set_.PORTSC.Write(portsc); 36 | while (port_reg_set_.PORTSC.Read().bits.port_reset); 37 | return MAKE_ERROR(Error::kSuccess); 38 | } 39 | 40 | Device* Port::Initialize() { 41 | return nullptr; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kernel/usb/xhci/port.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/port.hpp 3 | * 4 | * xHCI の各ポートを表すクラスと周辺機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "error.hpp" 11 | 12 | #include "usb/xhci/registers.hpp" 13 | 14 | #define CLEAR_STATUS_BIT(bitname) \ 15 | [this](){ \ 16 | PORTSC_Bitmap portsc = port_reg_set_.PORTSC.Read(); \ 17 | portsc.data[0] &= 0x0e01c3e0u; \ 18 | portsc.bits.bitname = 1; \ 19 | port_reg_set_.PORTSC.Write(portsc); \ 20 | }() 21 | 22 | namespace usb::xhci { 23 | class Controller; 24 | struct PortRegisterSet; 25 | class Device; 26 | 27 | class Port { 28 | public: 29 | Port(uint8_t port_num, PortRegisterSet& port_reg_set) 30 | : port_num_{port_num}, port_reg_set_{port_reg_set} 31 | {} 32 | 33 | uint8_t Number() const; 34 | bool IsConnected() const; 35 | bool IsEnabled() const; 36 | bool IsConnectStatusChanged() const; 37 | bool IsPortResetChanged() const; 38 | int Speed() const; 39 | Error Reset(); 40 | Device* Initialize(); 41 | 42 | void ClearConnectStatusChanged() const { 43 | CLEAR_STATUS_BIT(connect_status_change); 44 | } 45 | void ClearPortResetChange() const { 46 | CLEAR_STATUS_BIT(port_reset_change); 47 | } 48 | 49 | private: 50 | const uint8_t port_num_; 51 | PortRegisterSet& port_reg_set_; 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /kernel/usb/xhci/registers.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/registers.hpp" 2 | 3 | namespace { 4 | template 5 | Ptr AddOrNull(Ptr p, Disp d) { 6 | return d == 0 ? nullptr : p + d; 7 | } 8 | } 9 | 10 | namespace usb::xhci { 11 | ExtendedRegisterList::Iterator& ExtendedRegisterList::Iterator::operator++() { 12 | if (reg_) { 13 | reg_ = AddOrNull(reg_, reg_->Read().bits.next_pointer); 14 | static_assert(sizeof(*reg_) == 4); 15 | } 16 | return *this; 17 | } 18 | 19 | ExtendedRegisterList::ExtendedRegisterList(uint64_t mmio_base, 20 | HCCPARAMS1_Bitmap hccp) 21 | : first_{AddOrNull(reinterpret_cast(mmio_base), 22 | hccp.bits.xhci_extended_capabilities_pointer)} {} 23 | } 24 | -------------------------------------------------------------------------------- /kernel/usb/xhci/ring.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/ring.hpp" 2 | 3 | #include 4 | #include "usb/memory.hpp" 5 | 6 | namespace usb::xhci { 7 | Ring::~Ring() { 8 | if (buf_ != nullptr) { 9 | FreeMem(buf_); 10 | } 11 | } 12 | 13 | Error Ring::Initialize(size_t buf_size) { 14 | if (buf_ != nullptr) { 15 | FreeMem(buf_); 16 | } 17 | 18 | cycle_bit_ = true; 19 | write_index_ = 0; 20 | buf_size_ = buf_size; 21 | 22 | buf_ = AllocArray(buf_size_, 64, 64 * 1024); 23 | if (buf_ == nullptr) { 24 | return MAKE_ERROR(Error::kNoEnoughMemory); 25 | } 26 | memset(buf_, 0, buf_size_ * sizeof(TRB)); 27 | 28 | return MAKE_ERROR(Error::kSuccess); 29 | } 30 | 31 | void Ring::CopyToLast(const std::array& data) { 32 | for (int i = 0; i < 3; ++i) { 33 | // data[0..2] must be written prior to data[3]. 34 | buf_[write_index_].data[i] = data[i]; 35 | } 36 | buf_[write_index_].data[3] 37 | = (data[3] & 0xfffffffeu) | static_cast(cycle_bit_); 38 | } 39 | 40 | TRB* Ring::Push(const std::array& data) { 41 | auto trb_ptr = &buf_[write_index_]; 42 | CopyToLast(data); 43 | 44 | ++write_index_; 45 | if (write_index_ == buf_size_ - 1) { 46 | LinkTRB link{buf_}; 47 | link.bits.toggle_cycle = true; 48 | CopyToLast(link.data); 49 | 50 | write_index_ = 0; 51 | cycle_bit_ = !cycle_bit_; 52 | } 53 | 54 | return trb_ptr; 55 | } 56 | 57 | Error EventRing::Initialize(size_t buf_size, 58 | InterrupterRegisterSet* interrupter) { 59 | if (buf_ != nullptr) { 60 | FreeMem(buf_); 61 | } 62 | 63 | cycle_bit_ = true; 64 | buf_size_ = buf_size; 65 | interrupter_ = interrupter; 66 | 67 | buf_ = AllocArray(buf_size_, 64, 64 * 1024); 68 | if (buf_ == nullptr) { 69 | return MAKE_ERROR(Error::kNoEnoughMemory); 70 | } 71 | memset(buf_, 0, buf_size_ * sizeof(TRB)); 72 | 73 | erst_ = AllocArray(1, 64, 64 * 1024); 74 | if (erst_ == nullptr) { 75 | FreeMem(buf_); 76 | return MAKE_ERROR(Error::kNoEnoughMemory); 77 | } 78 | memset(erst_, 0, 1 * sizeof(EventRingSegmentTableEntry)); 79 | 80 | erst_[0].bits.ring_segment_base_address = reinterpret_cast(buf_); 81 | erst_[0].bits.ring_segment_size = buf_size_; 82 | 83 | ERSTSZ_Bitmap erstsz = interrupter_->ERSTSZ.Read(); 84 | erstsz.SetSize(1); 85 | interrupter_->ERSTSZ.Write(erstsz); 86 | 87 | WriteDequeuePointer(&buf_[0]); 88 | 89 | ERSTBA_Bitmap erstba = interrupter_->ERSTBA.Read(); 90 | erstba.SetPointer(reinterpret_cast(erst_)); 91 | interrupter_->ERSTBA.Write(erstba); 92 | 93 | return MAKE_ERROR(Error::kSuccess); 94 | } 95 | 96 | void EventRing::WriteDequeuePointer(TRB* p) { 97 | auto erdp = interrupter_->ERDP.Read(); 98 | erdp.SetPointer(reinterpret_cast(p)); 99 | interrupter_->ERDP.Write(erdp); 100 | } 101 | 102 | void EventRing::Pop() { 103 | auto p = ReadDequeuePointer() + 1; 104 | 105 | TRB* segment_begin 106 | = reinterpret_cast(erst_[0].bits.ring_segment_base_address); 107 | TRB* segment_end = segment_begin + erst_[0].bits.ring_segment_size; 108 | 109 | if (p == segment_end) { 110 | p = segment_begin; 111 | cycle_bit_ = !cycle_bit_; 112 | } 113 | 114 | WriteDequeuePointer(p); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /kernel/usb/xhci/ring.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/ring.hpp 3 | * 4 | * Event Ring, Command Ring, Transfer Ring のクラスや関連機能. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | 12 | #include "error.hpp" 13 | #include "usb/memory.hpp" 14 | #include "usb/xhci/registers.hpp" 15 | #include "usb/xhci/trb.hpp" 16 | 17 | namespace usb::xhci { 18 | /** @brief Command/Transfer Ring を表すクラス. */ 19 | class Ring { 20 | public: 21 | Ring() = default; 22 | Ring(const Ring&) = delete; 23 | ~Ring(); 24 | Ring& operator=(const Ring&) = delete; 25 | 26 | /** @brief リングのメモリ領域を割り当て,メンバを初期化する. */ 27 | Error Initialize(size_t buf_size); 28 | 29 | /** @brief TRB に cycle bit を設定した上でリング末尾に追加する. 30 | * 31 | * @return 追加された(リング上の)TRB を指すポインタ. 32 | */ 33 | template 34 | TRB* Push(const TRBType& trb) { 35 | return Push(trb.data); 36 | } 37 | 38 | TRB* Buffer() const { return buf_; } 39 | 40 | private: 41 | TRB* buf_ = nullptr; 42 | size_t buf_size_ = 0; 43 | 44 | /** @brief プロデューサ・サイクル・ステートを表すビット */ 45 | bool cycle_bit_; 46 | /** @brief リング上で次に書き込む位置 */ 47 | size_t write_index_; 48 | 49 | /** @brief TRB に cycle bit を設定した上でリング末尾に書き込む. 50 | * 51 | * write_index_ は変化させない. 52 | */ 53 | void CopyToLast(const std::array& data); 54 | 55 | /** @brief TRB に cycle bit を設定した上でリング末尾に追加する. 56 | * 57 | * write_index_ をインクリメントする.その結果 write_index_ がリング末尾 58 | * に達したら LinkTRB を適切に配置して write_index_ を 0 に戻し, 59 | * cycle bit を反転させる. 60 | * 61 | * @return 追加された(リング上の)TRB を指すポインタ. 62 | */ 63 | TRB* Push(const std::array& data); 64 | }; 65 | 66 | union EventRingSegmentTableEntry { 67 | std::array data; 68 | struct { 69 | uint64_t ring_segment_base_address; // 64 バイトアライメント 70 | 71 | uint32_t ring_segment_size : 16; 72 | uint32_t : 16; 73 | 74 | uint32_t : 32; 75 | } __attribute__((packed)) bits; 76 | }; 77 | 78 | class EventRing { 79 | public: 80 | Error Initialize(size_t buf_size, InterrupterRegisterSet* interrupter); 81 | 82 | TRB* ReadDequeuePointer() const { 83 | return reinterpret_cast(interrupter_->ERDP.Read().Pointer()); 84 | } 85 | 86 | void WriteDequeuePointer(TRB* p); 87 | 88 | bool HasFront() const { 89 | return Front()->bits.cycle_bit == cycle_bit_; 90 | } 91 | 92 | TRB* Front() const { 93 | return ReadDequeuePointer(); 94 | } 95 | 96 | void Pop(); 97 | 98 | private: 99 | TRB* buf_; 100 | size_t buf_size_; 101 | 102 | bool cycle_bit_; 103 | EventRingSegmentTableEntry* erst_; 104 | InterrupterRegisterSet* interrupter_; 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /kernel/usb/xhci/speed.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/speed.hpp 3 | * 4 | * Protocol Speed ID のデフォルト定義.PSIC == 0 のときのみ有効. 5 | */ 6 | 7 | #pragma once 8 | 9 | namespace usb::xhci { 10 | const int kFullSpeed = 1; 11 | const int kLowSpeed = 2; 12 | const int kHighSpeed = 3; 13 | const int kSuperSpeed = 4; 14 | const int kSuperSpeedPlus = 5; 15 | } 16 | -------------------------------------------------------------------------------- /kernel/usb/xhci/trb.cpp: -------------------------------------------------------------------------------- 1 | #include "usb/xhci/trb.hpp" 2 | 3 | namespace usb::xhci { 4 | const std::array kTRBCompletionCodeToName{ 5 | "Invalid", 6 | "Success", 7 | "Data Buffer Error", 8 | "Babble Detected Error", 9 | "USB Transaction Error", 10 | "TRB Error", 11 | "Stall Error", 12 | "Resource Error", 13 | "Bandwidth Error", 14 | "No Slots Available Error", 15 | "Invalid Stream Type Error", 16 | "Slot Not Enabled Error", 17 | "Endpoint Not Enabled Error", 18 | "Short Packet", 19 | "Ring Underrun", 20 | "Ring Overrun", 21 | "VF Event Ring Full Error", 22 | "Parameter Error", 23 | "Bandwidth Overrun Error", 24 | "Context State Error", 25 | "No ping Response Error", 26 | "Event Ring Full Error", 27 | "Incompatible Device Error", 28 | "Missed Service Error", 29 | "Command Ring Stopped", 30 | "Command Aborted", 31 | "Stopped", 32 | "Stopped - Length Invalid", 33 | "Stopped - Short Packet", 34 | "Max Exit Latency Too Large Error", 35 | "Reserved", 36 | "Isoch Buffer Overrun", 37 | "Event Lost Error", 38 | "Undefined Error", 39 | "Invalid Stream ID Error", 40 | "Secondary Bandwidth Error", 41 | "Split Transaction Error", 42 | }; 43 | 44 | const std::array kTRBTypeToName{ 45 | "Reserved", // 0 46 | "Normal", 47 | "Setup Stage", 48 | "Data Stage", 49 | "Status Stage", 50 | "Isoch", 51 | "Link", 52 | "EventData", 53 | "No-Op", // 8 54 | "Enable Slot Command", 55 | "Disable Slot Command", 56 | "Address Device Command", 57 | "Configure Endpoint Command", 58 | "Evaluate Context Command", 59 | "Reset Endpoint Command", 60 | "Stop Endpoint Command", 61 | "Set TR Dequeue Pointer Command", // 16 62 | "Reset Device Command", 63 | "Force Event Command", 64 | "Negotiate Bandwidth Command", 65 | "Set Latency Tolerance Value Command", 66 | "Get Port Bandwidth Command", 67 | "Force Header Command", 68 | "No Op Command", 69 | "Reserved", // 24 70 | "Reserved", 71 | "Reserved", 72 | "Reserved", 73 | "Reserved", 74 | "Reserved", 75 | "Reserved", 76 | "Reserved", 77 | "Transfer Event", // 32 78 | "Command Completion Event", 79 | "Port Status Change Event", 80 | "Bandwidth Request Event", 81 | "Doorbell Event", 82 | "Host Controller Event", 83 | "Device Notification Event", 84 | "MFINDEX Wrap Event", 85 | "Reserved", // 40 86 | "Reserved", 87 | "Reserved", 88 | "Reserved", 89 | "Reserved", 90 | "Reserved", 91 | "Reserved", 92 | "Reserved", 93 | "Vendor Defined", // 48 94 | "Vendor Defined", 95 | "Vendor Defined", 96 | "Vendor Defined", 97 | "Vendor Defined", 98 | "Vendor Defined", 99 | "Vendor Defined", 100 | "Vendor Defined", 101 | "Vendor Defined", // 56 102 | "Vendor Defined", 103 | "Vendor Defined", 104 | "Vendor Defined", 105 | "Vendor Defined", 106 | "Vendor Defined", 107 | "Vendor Defined", 108 | "Vendor Defined", 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /kernel/usb/xhci/xhci.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file usb/xhci/xhci.hpp 3 | * 4 | * xHCI ホストコントローラ制御用クラス. 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "error.hpp" 11 | #include "usb/xhci/registers.hpp" 12 | #include "usb/xhci/context.hpp" 13 | #include "usb/xhci/ring.hpp" 14 | #include "usb/xhci/port.hpp" 15 | #include "usb/xhci/devmgr.hpp" 16 | 17 | namespace usb::xhci { 18 | class Controller { 19 | public: 20 | Controller(uintptr_t mmio_base); 21 | Error Initialize(); 22 | Error Run(); 23 | Ring* CommandRing() { return &cr_; } 24 | EventRing* PrimaryEventRing() { return &er_; } 25 | DoorbellRegister* DoorbellRegisterAt(uint8_t index); 26 | Port PortAt(uint8_t port_num) { 27 | return Port{port_num, PortRegisterSets()[port_num - 1]}; 28 | } 29 | uint8_t MaxPorts() const { return max_ports_; } 30 | DeviceManager* DeviceManager() { return &devmgr_; } 31 | 32 | private: 33 | static const size_t kDeviceSize = 8; 34 | 35 | const uintptr_t mmio_base_; 36 | CapabilityRegisters* const cap_; 37 | OperationalRegisters* const op_; 38 | const uint8_t max_ports_; 39 | 40 | class DeviceManager devmgr_; 41 | Ring cr_; 42 | EventRing er_; 43 | 44 | InterrupterRegisterSetArray InterrupterRegisterSets() const { 45 | return {mmio_base_ + cap_->RTSOFF.Read().Offset() + 0x20u, 1024}; 46 | } 47 | 48 | PortRegisterSetArray PortRegisterSets() const { 49 | return {reinterpret_cast(op_) + 0x400u, max_ports_}; 50 | } 51 | 52 | DoorbellRegisterArray DoorbellRegisters() const { 53 | return {mmio_base_ + cap_->DBOFF.Read().Offset(), 256}; 54 | } 55 | }; 56 | 57 | Error ConfigurePort(Controller& xhc, Port& port); 58 | Error ConfigureEndpoints(Controller& xhc, Device& dev); 59 | 60 | /** @brief イベントリングに登録されたイベントを高々1つ処理する. 61 | * 62 | * xhc のプライマリイベントリングの先頭のイベントを処理する. 63 | * イベントが無ければ即座に Error::kSuccess を返す. 64 | * 65 | * @return イベントを正常に処理できたら Error::kSuccess 66 | */ 67 | Error ProcessEvent(Controller& xhc); 68 | 69 | extern Controller* controller; 70 | void Initialize(); 71 | void ProcessEvents(); 72 | } 73 | -------------------------------------------------------------------------------- /kernel/window.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file window.hpp 3 | * 4 | * 表示領域を表すWindowクラスを提供する。 5 | */ 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include "graphics.hpp" 13 | #include "frame_buffer.hpp" 14 | 15 | enum class WindowRegion { 16 | kTitleBar, 17 | kCloseButton, 18 | kBorder, 19 | kOther, 20 | }; 21 | 22 | /** @brief Window クラスはグラフィックの表示領域を表す。 23 | * 24 | * タイトルやメニューがあるウィンドウだけでなく,マウスカーソルの表示領域なども対象とする。 25 | */ 26 | class Window { 27 | public: 28 | /** @brief WindowWriter は Window と関連付けられた PixelWriter を提供する。 29 | */ 30 | class WindowWriter : public PixelWriter { 31 | public: 32 | WindowWriter(Window& window) : window_{window} {} 33 | /** @brief 指定された位置に指定された色を描く */ 34 | virtual void Write(Vector2D pos, const PixelColor& c) override { 35 | window_.Write(pos, c); 36 | } 37 | /** @brief Width は関連付けられた Window の横幅をピクセル単位で返す。 */ 38 | virtual int Width() const override { return window_.Width(); } 39 | /** @brief Height は関連付けられた Window の高さをピクセル単位で返す。 */ 40 | virtual int Height() const override { return window_.Height(); } 41 | 42 | private: 43 | Window& window_; 44 | }; 45 | 46 | /** @brief 指定されたピクセル数の平面描画領域を作成する。 */ 47 | Window(int width, int height, PixelFormat shadow_format); 48 | virtual ~Window() = default; 49 | Window(const Window& rhs) = delete; 50 | Window& operator=(const Window& rhs) = delete; 51 | 52 | /** @brief 与えられた FrameBuffer にこのウィンドウの表示領域を描画する。 53 | * 54 | * @param dst 描画先 55 | * @param pos dst の左上を基準としたウィンドウの位置 56 | * @param area dst の左上を基準とした描画対象範囲 57 | */ 58 | void DrawTo(FrameBuffer& dst, Vector2D pos, const Rectangle& area); 59 | /** @brief 透過色を設定する。 */ 60 | void SetTransparentColor(std::optional c); 61 | /** @brief このインスタンスに紐付いた WindowWriter を取得する。 */ 62 | WindowWriter* Writer(); 63 | 64 | /** @brief 指定した位置のピクセルを返す。 */ 65 | const PixelColor& At(Vector2D pos) const; 66 | /** @brief 指定した位置にピクセルを書き込む。 */ 67 | void Write(Vector2D pos, PixelColor c); 68 | 69 | /** @brief 平面描画領域の横幅をピクセル単位で返す。 */ 70 | int Width() const; 71 | /** @brief 平面描画領域の高さをピクセル単位で返す。 */ 72 | int Height() const; 73 | /** @brief 平面描画領域のサイズをピクセル単位で返す。 */ 74 | Vector2D Size() const; 75 | 76 | /** @brief このウィンドウの平面描画領域内で,矩形領域を移動する。 77 | * 78 | * @param src_pos 移動元矩形の原点 79 | * @param src_size 移動元矩形の大きさ 80 | * @param dst_pos 移動先の原点 81 | */ 82 | void Move(Vector2D dst_pos, const Rectangle& src); 83 | 84 | virtual void Activate() {} 85 | virtual void Deactivate() {} 86 | virtual WindowRegion GetWindowRegion(Vector2D pos); 87 | 88 | private: 89 | int width_, height_; 90 | std::vector> data_{}; 91 | WindowWriter writer_{*this}; 92 | std::optional transparent_color_{std::nullopt}; 93 | 94 | FrameBuffer shadow_buffer_{}; 95 | }; 96 | 97 | class ToplevelWindow : public Window { 98 | public: 99 | static constexpr Vector2D kTopLeftMargin{4, 24}; 100 | static constexpr Vector2D kBottomRightMargin{4, 4}; 101 | static constexpr int kMarginX = kTopLeftMargin.x + kBottomRightMargin.x; 102 | static constexpr int kMarginY = kTopLeftMargin.y + kBottomRightMargin.y; 103 | 104 | class InnerAreaWriter : public PixelWriter { 105 | public: 106 | InnerAreaWriter(ToplevelWindow& window) : window_{window} {} 107 | virtual void Write(Vector2D pos, const PixelColor& c) override { 108 | window_.Write(pos + kTopLeftMargin, c); 109 | } 110 | virtual int Width() const override { 111 | return window_.Width() - kTopLeftMargin.x - kBottomRightMargin.x; } 112 | virtual int Height() const override { 113 | return window_.Height() - kTopLeftMargin.y - kBottomRightMargin.y; } 114 | 115 | private: 116 | ToplevelWindow& window_; 117 | }; 118 | 119 | ToplevelWindow(int width, int height, PixelFormat shadow_format, 120 | const std::string& title); 121 | 122 | virtual void Activate() override; 123 | virtual void Deactivate() override; 124 | virtual WindowRegion GetWindowRegion(Vector2D pos) override; 125 | 126 | InnerAreaWriter* InnerWriter() { return &inner_writer_; } 127 | Vector2D InnerSize() const; 128 | 129 | private: 130 | std::string title_; 131 | InnerAreaWriter inner_writer_{*this}; 132 | }; 133 | 134 | void DrawWindow(PixelWriter& writer, const char* title); 135 | void DrawTextbox(PixelWriter& writer, Vector2D pos, Vector2D size); 136 | void DrawTerminal(PixelWriter& writer, Vector2D pos, Vector2D size); 137 | void DrawWindowTitle(PixelWriter& writer, const char* title, bool active); 138 | -------------------------------------------------------------------------------- /kernel/x86_descriptor.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file x86_descriptor.hpp 3 | * 4 | * セグメントと割り込みディスクリプタのための共通定義を集めたファイル. 5 | */ 6 | 7 | #pragma once 8 | 9 | enum class DescriptorType { 10 | // system segment & gate descriptor types 11 | kUpper8Bytes = 0, 12 | kLDT = 2, 13 | kTSSAvailable = 9, 14 | kTSSBusy = 11, 15 | kCallGate = 12, 16 | kInterruptGate = 14, 17 | kTrapGate = 15, 18 | 19 | // code & data segment types 20 | kReadWrite = 2, 21 | kExecuteRead = 10, 22 | }; 23 | -------------------------------------------------------------------------------- /mikanos-after30-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/mikanos-after30-photo.png -------------------------------------------------------------------------------- /release_pack/.gitignore: -------------------------------------------------------------------------------- 1 | /mikanos_distrib-v* 2 | -------------------------------------------------------------------------------- /release_pack/README.md: -------------------------------------------------------------------------------- 1 | ## MikanOS 簡易配布セットの使い方 2 | 3 | by uchan (2020-04-30) 4 | 5 | mikanos_distrib-X.tar.gz を解凍すると次のファイルを得られます。 6 | 7 | mikanos_distrib-X/ 8 | BOOTX64.EFI ブートローダー 9 | kernel.elf MikanOS のカーネル本体 10 | apps/ アプリケーション 11 | resource/ リソース 12 | install.sh USB メモリへのインストールスクリプト 13 | disk.img 起動イメージファイル 14 | README.md このファイル 15 | LICENSE MikanOS の利用ライセンス 16 | NOTICE MikanOS の著作権表示 17 | 18 | disk.img を使うと QEMU で起動実験ができます。 19 | 20 | USB メモリへインストールして使うには,条件を満たした USB メモリを用意して 21 | mikanos_distrib-X ディレクトリで install.sh スクリプトを実行します。 22 | 23 | USB メモリが /dev/sdb1 として認識されている場合,次のコマンドを実行します。 24 | USB メモリでないデバイスを指定すると PC が破壊されるので十分注意してください。 25 | 26 | $ cd mikanos_distrib-X 27 | $ sudo umount /dev/sdb1 # まず USB メモリのマウントを解除してから 28 | $ sudo ./install.sh /dev/sdb1 # install.sh を叩く 29 | 30 | mikanos_distrib-X.tar.gz の改変や再配布は LICENSE の条件に従うかぎり可能です。 31 | 32 | 33 | ### USB メモリの条件 34 | 35 | install.sh を使うと中身がフォーマットされてしまいます。 36 | USB メモリは中身が消えても良い物を使ってください。 37 | 38 | ブートローダーの制約で,USB メモリの容量がある程度小さい必要があります。 39 | おすすめは 1GB 程度です。16GB の製品でギリギリだと思います。 40 | 41 | 大きいものしかない場合,第 1 パーティションを小さくするのでも良いです。 42 | Ubuntu なら「GNOME ディスクユーティリティ」, 43 | Windows なら「コンピューターの管理」にある「ディスクの管理」から可能です。 44 | 45 | install.sh を使わず USB メモリにインストールする場合, 46 | 毎回 USB メモリのフォーマットをしてください。 47 | フォーマットせずにファイルをコピーするだけだと,USB メモリ後方のクラスタが 48 | 使用されてしまい,MikanOS がファイルにアクセスできません。 49 | 50 | 51 | ### install.sh が何をしているか 52 | 53 | Ubuntu 以外を使っていたりして install.sh コマンドが使えない場合でも 54 | MikanOS を試せるように,install.sh がやっていることを紹介します。 55 | ($USB は USB メモリのルートディレクトリを表すとします。) 56 | 57 | - USB メモリを FAT でフォーマットする 58 | - USB メモリの内容を次の通りにする 59 | $USB/ 60 | EFI/BOOT/ 61 | BOOTX64.EFI 62 | kernel.elf 63 | apps/ 64 | cube 65 | ... 66 | nihongo.ttf 67 | mikanos.txt 68 | ... (その他 resource/ にあるファイル) 69 | 70 | 71 | ## USB マウス・キーボード 72 | 73 | MikanOS は USB のマウスとキーボードに対応しています。USB ハブには未対応ですので 74 | パソコンの USB 3.0 ポートに直接接続してください。 75 | 76 | MikanOS は PS/2 のマウスとキーボードには対応していません。ノートパソコンの 77 | タッチパッドやキーボードは PS/2 接続の場合がありますので,反応しないなと 78 | 思ったら外付けマウスとキーボードを試してください。 79 | -------------------------------------------------------------------------------- /release_pack/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eu 2 | 3 | if [ $# -lt 1 ] 4 | then 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | USB_FLASH=$1 10 | 11 | mkfs.fat -F 32 -n MIKANOS $USB_FLASH 12 | 13 | mkdir -p mnt 14 | mount $USB_FLASH mnt 15 | mkdir -p mnt/EFI/BOOT 16 | cp BOOTX64.EFI mnt/EFI/BOOT/BOOTX64.EFI 17 | cp kernel.elf mnt/kernel.elf 18 | 19 | cp -r apps mnt/ 20 | cp resource/* mnt/ 21 | 22 | sleep 0.5 23 | umount mnt 24 | -------------------------------------------------------------------------------- /release_pack/make_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -eux 2 | 3 | if [ $# -ne 1 ] 4 | then 5 | echo "Usage: $0 GIT_TAG" 6 | exit 1 7 | fi 8 | 9 | if [ "$(basename $PWD)" != "release_pack" ] 10 | then 11 | echo "Please run this script in 'release_pack' directory." 12 | exit 1 13 | fi 14 | 15 | mikanos_root=".." 16 | disk="$mikanos_root/disk.img" 17 | kernel="$mikanos_root/kernel/kernel.elf" 18 | 19 | if [ ! -f $disk ] || [ ! -f $kernel ] 20 | then 21 | echo "Please build MikanOS in advance." 22 | exit 1 23 | fi 24 | 25 | edk2_dir="$HOME/edk2" 26 | bootloader="$edk2_dir/Build/MikanLoaderX64/DEBUG_CLANG38/X64/Loader.efi" 27 | 28 | if [ ! -f $bootloader ] 29 | then 30 | echo "Please build MikanLoaderPkg in advance." 31 | exit 1 32 | fi 33 | 34 | git_tag=$1 35 | dist_dir="mikanos_distrib-$git_tag" 36 | 37 | if [ -e $dist_dir ] 38 | then 39 | echo "Please delete the distrib directory: $dist_dir" 40 | exit 1 41 | fi 42 | 43 | mkdir $dist_dir 44 | cp $mikanos_root/LICENSE $mikanos_root/NOTICE $dist_dir 45 | cp README.md install.sh $dist_dir 46 | 47 | cp $disk $kernel $dist_dir 48 | cp $bootloader $dist_dir/BOOTX64.EFI 49 | cp -r $mikanos_root/resource $dist_dir 50 | 51 | apps="$mikanos_root/apps" 52 | mkdir $dist_dir/apps 53 | 54 | for app in $(ls $apps) 55 | do 56 | app_dir=$apps/$app 57 | app_bin=$app_dir/$app 58 | if [ -d $app_dir ] && [ -f $app_bin ] 59 | then 60 | cp $app_bin $dist_dir/apps 61 | fi 62 | done 63 | 64 | tar czf $dist_dir.tar.gz $dist_dir 65 | -------------------------------------------------------------------------------- /resource/fujisan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/resource/fujisan.jpg -------------------------------------------------------------------------------- /resource/jpn.txt: -------------------------------------------------------------------------------- 1 | ようこそ,MikanOSへ! 2 | MikanOSは計算機科学の教材となることを目指して作られています。 3 | 4 | リポジトリ→ https://github.com/uchan-nos/mikanos 5 | -------------------------------------------------------------------------------- /resource/mikanos.txt: -------------------------------------------------------------------------------- 1 | # MikanOS 取扱説明書 2 | 3 | ようこそ,MikanOS の世界へ! 4 | 5 | この文書は uchan が作っている教育用 OS "MikanOS" の使い方を記した文書です。最初 6 | に使い始めるときにとっつきやすい機能をピックアップして紹介します。網羅すること 7 | は目指していません。詳しくはソースコードを参照してください。 8 | 9 | ## ターミナルと戯れる 10 | 11 | MikanOS を起動すると黒い背景の画面が 1 つ開かれています。これはターミナルと呼ば 12 | れる画面です。ターミナルは文字を使って OS に指示を出すための入り口です。Linux 13 | や macOS でいうところのターミナルとシェルを組み合わせたようなものです。 14 | 15 | さっそくターミナルを使ってみます。黒い画面のどこかをクリックしてタイトルバーを 16 | 青色にしてください(もしかすると最初から青色かもしれません)。タイトルバーが青 17 | 色のとき,その画面が有効(アクティブ)になっています。キーボードやマウスからの 18 | 入力はアクティブな画面に向けて行われます。 19 | 20 | ターミナル画面がアクティブになったら次のコマンドラインを入力してみましょう。 21 | 22 | >echo hello world 23 | 24 | 先頭の `>` は最初から入力されているので皆さんが入力する必要はありません。echo 25 | から入力します。d まで入力したらエンターキーを押下します。エンターキーを押下す 26 | ることで,入力した 1 行が実行されます。エンターキーを押す前であればバックスペー 27 | スキーを押すことで入力内容を修正することができます。 28 | 29 | エンターキーを押すと,コマンドラインの次の行に "hello world" と表示されるはずで 30 | すね。これが echo コマンドの効果です。echo コマンドは引数を画面に表示するという 31 | シンプルな機能を持った,ターミナル組み込みのコマンドです。 32 | 33 | つぎに,アプリを試してみます。tview アプリでこの文書を表示してみます。 34 | 35 | >tview mikanos.txt 36 | 37 | 大きな画面が表示され,この文書が表示されたでしょうか。きっと文書の全体は表示さ 38 | れていないと思います。tview では上下の矢印キーで文書をスクロールすることができ 39 | ます。もっと早くスクロールするには PageUp,PageDown キーを使うと,画面の半分ず 40 | つスクロールします。 41 | 42 | MikanOS の多くの作業はターミナルから実行します。アプリの起動,文書閲覧,検索, 43 | その他のほとんどの操作の起点となります。 44 | 45 | ## アプリ紹介 46 | 47 | 収録されているアプリをいくつか紹介します。 48 | 49 | blocks ブロック崩しゲーム 50 | cp ファイルコピー 51 | cube 立方体の 3D 表示デモ 52 | eye マウスを追いかける目玉 53 | more ファイルをページ送りで見る 54 | paint マウスでお絵かき 55 | rpn 逆ポーランド記法電卓 56 | sort 行の並べ替え 57 | tview テキストビューア 58 | 59 | ## アプリを終了する 60 | 61 | アプリを終了するにはアプリの画面がアクティブな状態で Ctrl-Q を入力するか,画面 62 | 右上の閉じるボタンをクリックします。 63 | 64 | ## ターミナルを増やす 65 | 66 | F2 キーでターミナルを新規作成できます。ターミナル上でアプリを起動させると,その 67 | アプリが終了するまでターミナルに処理が返ってきません。そんなときはターミナルを 68 | 増やせば好きなだけ同時にアプリを起動させることができます。 69 | 70 | 実は,noterm コマンドを使うとターミナルを増やさずにアプリを起動させることもでき 71 | ます。noterm コマンドは,非表示のターミナルを裏で 1 つ起動させ,その上でアプリ 72 | を動作させるという仕組みになっています。 73 | 74 | >noterm cube 75 | 76 | とすると cube が起動します。そして,cube が終了しなくてもターミナルでの操作を続 77 | けることができます。cube のウィンドウを閉じると,非表示状態で起動したターミナル 78 | は静かに終了します。 79 | -------------------------------------------------------------------------------- /resource/night.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/resource/night.bmp -------------------------------------------------------------------------------- /resource/nihongo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/resource/nihongo.ttf -------------------------------------------------------------------------------- /resource/tokyost.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uchan-nos/mikanos/b5f7740c04002e67a95af16a5c6e073b664bf3f5/resource/tokyost.jpg -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . $HOME/osbook/devenv/buildenv.sh 4 | APPS_DIR=./apps RESOURCE_DIR=./resource ./build.sh run 5 | -------------------------------------------------------------------------------- /tools/attach_usbdev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | attach_dev() { 4 | powershell.exe Start-Process cmd.exe -Verb runas -ArgumentList cmd.exe,/C,usbipd,wsl,attach,--busid,$1 5 | } 6 | 7 | if [ $# -ne 1 ] 8 | then 9 | echo Please specify a bus id to be attached 10 | (cd $(wslpath -u "C:\Windows"); cmd.exe /C "usbipd wsl list") 11 | exit 1 12 | fi 13 | 14 | echo Attaching a specified device: BUS_ID = $1 15 | attach_dev $1 16 | -------------------------------------------------------------------------------- /tools/makefont.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | import collections 5 | import functools 6 | import re 7 | import sys 8 | 9 | 10 | BITMAP_PATTERN = re.compile(r'([.*@]+)') 11 | 12 | 13 | def compile(src: str) -> bytes: 14 | src = src.lstrip() 15 | result = [] 16 | 17 | for line in src.splitlines(): 18 | m = BITMAP_PATTERN.match(line) 19 | if not m: 20 | continue 21 | 22 | bits = [(0 if x == '.' else 1) for x in m.group(1)] 23 | bits_int = functools.reduce(lambda a, b: 2*a + b, bits) 24 | result.append(bits_int.to_bytes(1, byteorder='little')) 25 | 26 | return b''.join(result) 27 | 28 | 29 | def main(): 30 | parser = argparse.ArgumentParser() 31 | parser.add_argument('font', help='path to a font file') 32 | parser.add_argument('-o', help='path to an output file', default='font.out') 33 | ns = parser.parse_args() 34 | 35 | with open(ns.o, 'wb') as out, open(ns.font) as font: 36 | src = font.read() 37 | out.write(compile(src)) 38 | 39 | 40 | if __name__ == '__main__': 41 | main() 42 | --------------------------------------------------------------------------------