diff --git a/.editorconfig b/.editorconfig index 055607d..329880a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,4 +9,8 @@ trim_trailing_whitespace = false insert_final_newline = false [*.asm] -indent_style = space \ No newline at end of file +indent_style = space + +[*.yml] +indent_size = space +indent_size = 2 \ No newline at end of file diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..8f82dbb --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,82 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + typos: + name: "Typos" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check typos + uses: crate-ci/typos@master + with: + config: ./tools/typos.toml + + clippy: + name: "Clippy" + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: "Install Rust" + run: | + sudo apt update + DEBIAN_FRONTEND=noninteractive \ + sudo apt install --yes \ + rustup + + - name: Run clippy + working-directory: ./tetros + run: cargo clippy --all-targets --all-features + + build: + name: "Build" + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + + - name: "Install Rust" + run: | + sudo apt update + DEBIAN_FRONTEND=noninteractive \ + sudo apt install --yes \ + rustup nasm python3-requests + + - name: Build + run: make + + # Upload build output + - name: "Save output" + uses: actions/upload-artifact@v3 + with: + name: "Build output" + path: "build/*" + retention-days: 7 + + - name: "Publish package (hash)" + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + run: | + PUBLISH_USER="${{ secrets.PUBLISH_USER }}" \ + PUBLISH_KEY="${{ secrets.PUBLISH_KEY }}" \ + VERSION="${{ github.sha }}" \ + PACKAGE="${{ vars.PACKAGE }}" \ + python tools/scripts/publish.py + + - name: "Publish package (latest)" + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + run: | + PUBLISH_USER="${{ secrets.PUBLISH_USER }}" \ + PUBLISH_KEY="${{ secrets.PUBLISH_KEY }}" \ + VERSION="latest" \ + PACKAGE="${{ vars.PACKAGE }}" \ + python tools/scripts/publish.py diff --git a/bios/gdt.asm b/bios/gdt.asm index 0322a53..f58dc99 100644 --- a/bios/gdt.asm +++ b/bios/gdt.asm @@ -1,6 +1,6 @@ SECTION .text ; cannot use .data -struc GDTEntry +struc GDTEntry ; spell:disable-line .limitl resw 1 .basel resw 1 .basem resb 1 @@ -26,21 +26,21 @@ gdt_attr: .accessed equ 1 << 0 ;system ; legacy - .tssAvailabe16 equ 0x1 + .tssAvailabe16 equ 0x1 ; spell:disable-line .ldt equ 0x2 .tssBusy16 equ 0x3 .call16 equ 0x4 .task equ 0x5 .interrupt16 equ 0x6 .trap16 equ 0x7 - .tssAvailabe32 equ 0x9 + .tssAvailabe32 equ 0x9 ; spell:disable-line .tssBusy32 equ 0xB .call32 equ 0xC .interrupt32 equ 0xE .trap32 equ 0xF ; long mode .ldt32 equ 0x2 - .tssAvailabe64 equ 0x9 + .tssAvailabe64 equ 0x9 ; spell:disable-line .tssBusy64 equ 0xB .call64 equ 0xC .interrupt64 equ 0xE diff --git a/tetros/src/idt/entry.rs b/tetros/src/idt/entry.rs index a8056d5..9b3ff91 100644 --- a/tetros/src/idt/entry.rs +++ b/tetros/src/idt/entry.rs @@ -61,6 +61,7 @@ impl fmt::Debug for Entry { } } +// spell:off impl Entry { /// Create a valid non-present IDT entry. #[inline] @@ -111,7 +112,9 @@ impl Entry { )) } } +// spell:on +// spell:off impl Entry { /// Sets the handler address for the IDT entry and sets the following defaults: /// - The code selector is the code segment currently active in the CPU @@ -125,3 +128,4 @@ impl Entry { unsafe { self.set_handler_addr(handler.to_virt_addr()) } } } +// spell:on diff --git a/tetros/src/idt/handler.rs b/tetros/src/idt/handler.rs index 66fe96c..9044899 100644 --- a/tetros/src/idt/handler.rs +++ b/tetros/src/idt/handler.rs @@ -50,9 +50,9 @@ bitflags! { } } -/// -/// MARK: types -/// +// +// MARK: types +// /// A handler function for an interrupt or an exception without error code. pub type HandlerFunc = extern "x86-interrupt" fn(InterruptStackFrame); @@ -84,6 +84,7 @@ pub unsafe trait HandlerFuncType { unsafe impl HandlerFuncType for HandlerFunc { #[inline] fn to_virt_addr(self) -> VirtAddr { + #[expect(clippy::fn_to_numeric_cast_with_truncation)] VirtAddr::new(self as u32) } } @@ -91,6 +92,7 @@ unsafe impl HandlerFuncType for HandlerFunc { unsafe impl HandlerFuncType for HandlerFuncWithErrCode { #[inline] fn to_virt_addr(self) -> VirtAddr { + #[expect(clippy::fn_to_numeric_cast_with_truncation)] VirtAddr::new(self as u32) } } @@ -98,6 +100,7 @@ unsafe impl HandlerFuncType for HandlerFuncWithErrCode { unsafe impl HandlerFuncType for DivergingHandlerFunc { #[inline] fn to_virt_addr(self) -> VirtAddr { + #[expect(clippy::fn_to_numeric_cast_with_truncation)] VirtAddr::new(self as u32) } } @@ -105,6 +108,7 @@ unsafe impl HandlerFuncType for DivergingHandlerFunc { unsafe impl HandlerFuncType for DivergingHandlerFuncWithErrCode { #[inline] fn to_virt_addr(self) -> VirtAddr { + #[expect(clippy::fn_to_numeric_cast_with_truncation)] VirtAddr::new(self as u32) } } @@ -112,6 +116,7 @@ unsafe impl HandlerFuncType for DivergingHandlerFuncWithErrCode { unsafe impl HandlerFuncType for PageFaultHandlerFunc { #[inline] fn to_virt_addr(self) -> VirtAddr { + #[expect(clippy::fn_to_numeric_cast_with_truncation)] VirtAddr::new(self as u32) } } diff --git a/tetros/src/idt/table.rs b/tetros/src/idt/table.rs index 92b7478..7f31e87 100644 --- a/tetros/src/idt/table.rs +++ b/tetros/src/idt/table.rs @@ -7,7 +7,7 @@ use super::{ // TODO: comments #[repr(C, packed(2))] -struct IDTR { +struct Idtr { size: u16, offset: u32, } @@ -16,6 +16,7 @@ struct IDTR { // MARK: idt // +// spell:off #[derive(Clone, Debug)] #[repr(C)] #[repr(align(8))] @@ -410,6 +411,7 @@ pub struct InterruptDescriptorTable { /// instruction pointer points to the instruction after the INTn. interrupts: [Entry; 256 - 32], } +// spell:on // // MARK: impl @@ -469,7 +471,7 @@ impl InterruptDescriptorTable { #[inline] pub unsafe fn load_unsafe(&self) { let idtr = { - IDTR { + Idtr { size: (size_of::() - 1) as u16, offset: self as *const _ as u32, } diff --git a/tetros/src/os/panic.rs b/tetros/src/os/panic.rs index fea6607..f9de158 100644 --- a/tetros/src/os/panic.rs +++ b/tetros/src/os/panic.rs @@ -21,7 +21,6 @@ pub fn rust_begin_unwind(info: &PanicInfo<'_>) -> ! { } } -#[allow(non_snake_case)] #[no_mangle] /// Required to handle panics pub extern "C" fn _Unwind_Resume() -> ! { diff --git a/tetros/src/os/thunk.rs b/tetros/src/os/thunk.rs index 9e26ab7..e960d60 100644 --- a/tetros/src/os/thunk.rs +++ b/tetros/src/os/thunk.rs @@ -2,7 +2,6 @@ use core::ptr; use super::THUNK_STACK_ADDR; -#[allow(dead_code)] #[derive(Clone, Copy, Debug)] #[repr(C, packed)] pub struct ThunkData { diff --git a/tetros/targets/x86-unknown-none.json b/tetros/targets/x86-unknown-none.json index b3efe7b..777017d 100644 --- a/tetros/targets/x86-unknown-none.json +++ b/tetros/targets/x86-unknown-none.json @@ -1,28 +1,28 @@ { - "llvm-target": "i686-unknown-none", - "target-endian": "little", - "target-pointer-width": "32", - "target-c-int-width": "32", - "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", - "arch": "x86", - "os": "none", - "env": "", - "vendor": "unknown", - "linker-flavor": "gcc", - "panic-strategy": "abort", - "pre-link-args": { - "gcc": ["-m32", "-nostdlib", "-static"] - }, - "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", - "dynamic-linking": false, - "executables": false, - "relocation-model": "static", - "code-model": "large", - "disable-redzone": true, - "frame-pointer": "always", - "exe-suffix": "", - "has-rpath": false, - "no-default-libraries": true, - "position-independent-executables": false, - "tls-model": "global-dynamic" + "llvm-target": "i686-unknown-none", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128", + "arch": "x86", + "os": "none", + "env": "", + "vendor": "unknown", + "linker-flavor": "gcc", + "panic-strategy": "abort", + "pre-link-args": { + "gcc": ["-m32", "-nostdlib", "-static"] + }, + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float", + "dynamic-linking": false, + "executables": false, + "relocation-model": "static", + "code-model": "large", + "disable-redzone": true, + "frame-pointer": "always", + "exe-suffix": "", + "has-rpath": false, + "no-default-libraries": true, + "position-independent-executables": false, + "tls-model": "global-dynamic" } diff --git a/tools/scripts/publish.py b/tools/scripts/publish.py new file mode 100644 index 0000000..3304fa4 --- /dev/null +++ b/tools/scripts/publish.py @@ -0,0 +1,63 @@ +# Publish the output of `build.py` +# as a Gitea package. + +from pathlib import Path +import requests +import os +import re + +URL = "https://git.betalupi.com" +USER = os.environ["PUBLISH_USER"] +PACKAGE = os.environ["PACKAGE"] +VERSION = os.environ["VERSION"] +AUTH = requests.auth.HTTPBasicAuth(USER, os.environ["PUBLISH_KEY"]) + +ROOT: Path = Path(os.getcwd()) + + +def log(msg): + print(f"[PUBLISH.PY] {msg}") + + +log(f"Version is {VERSION}") +log(f"Package is {PACKAGE}") +log(f"Running in {ROOT}") +if not ROOT.is_dir(): + log("Root is not a directory, cannot continue") + exit(1) + + +def del_package(): + log(f"Deleting package {PACKAGE}/{VERSION}") + res = requests.delete( + f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}", + auth=AUTH, + ) + if res.status_code != 204 and res.status_code != 404: + log(f"Deletion failed with code {res.status_code}") + + +# Delete if already exists +# (important for the `latest` package) +del_package() + + +def upload(data, target: str): + target = re.sub("[^A-Za-z0-9_. -]+", "", target) + + res = requests.put( + f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}/{target}", + auth=AUTH, + data=data, + ) + + if res.status_code != 201: + log(f"Upload failed with code {res.status_code}") + del_package() # Do not keep partial package if upload fails + exit(1) + + return f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}/{target}" + + +log("Uploading disk.img") +upload(Path("./build/disk.img").open("rb").read(), "disk.img") diff --git a/tools/scripts/ruff.toml b/tools/scripts/ruff.toml new file mode 100644 index 0000000..c8f72c4 --- /dev/null +++ b/tools/scripts/ruff.toml @@ -0,0 +1,18 @@ +exclude = ["venv"] +line-length = 88 +indent-width = 4 +target-version = "py39" +include = ["scripts/**/*.py"] + +[lint] +select = ["E4", "E7", "E9", "F"] +ignore = [] +fixable = ["ALL"] +unfixable = [] +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[format] +quote-style = "double" +indent-style = "tab" +skip-magic-trailing-comma = false +line-ending = "lf" diff --git a/tools/typos.toml b/tools/typos.toml new file mode 100644 index 0000000..eed47b0 --- /dev/null +++ b/tools/typos.toml @@ -0,0 +1,8 @@ +[default] +extend-ignore-re = [ + # spell:disable-line + "(?Rm)^.*(%|#|//|;)\\s*spell:disable-line$", + + # spell: + "(?s)(%|#|//|;)\\s*spell:off.*?\\n\\s*(%|#|//)\\s*spell:on", +]