├── LICENSE ├── README ├── acpi_shutdown_hack.c ├── acpi_shutdown_hack.h └── test ├── .gitignore ├── GNUmakefile ├── kernel ├── .gitignore ├── GNUmakefile ├── linker.ld └── src │ ├── acpi.c │ ├── acpi.h │ ├── acpi_shutdown_hack.c │ ├── acpi_shutdown_hack.h │ ├── builtins.c │ ├── builtins.h │ └── main.c └── limine.cfg /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-2023 mintsuki and contributors. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ACPI shutdown hack 2 | ================== 3 | 4 | This is a *hack* to support shutdown in OSes where full support for ACPI is 5 | lacking. 6 | 7 | To use the hack, integrate the acpi_shutdown_hack.{c,h} files in your codebase. 8 | 9 | The interface to shutdown is extremely simple and self documenting. 10 | See acpi_shutdown_hack.h. 11 | -------------------------------------------------------------------------------- /acpi_shutdown_hack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct facp { 6 | char signature[4]; 7 | uint32_t length; 8 | uint8_t unneeded1[40 - 8]; 9 | uint32_t dsdt; 10 | uint8_t unneeded2[48 - 44]; 11 | uint32_t SMI_CMD; 12 | uint8_t ACPI_ENABLE; 13 | uint8_t ACPI_DISABLE; 14 | uint8_t unneeded3[64 - 54]; 15 | uint32_t PM1a_CNT_BLK; 16 | uint32_t PM1b_CNT_BLK; 17 | uint8_t unneeded4[89 - 72]; 18 | uint8_t PM1_CNT_LEN; 19 | }; 20 | 21 | static inline uint8_t parse_integer(uint8_t* s5_addr, uint64_t* value) { 22 | uint8_t op = *s5_addr++; 23 | if (op == 0x0) { // ZeroOp 24 | *value = 0; 25 | return 1; // 1 Op Byte 26 | } else if (op == 0x1) { // OneOp 27 | *value = 1; 28 | return 1; // 1 Op Byte 29 | } else if (op == 0xFF) { // OnesOp 30 | *value = ~0; 31 | return 1; // 1 Op Byte 32 | } else if (op == 0xA) { // ByteConst 33 | *value = s5_addr[0]; 34 | return 2; // 1 Type Byte, 1 Data Byte 35 | } else if (op == 0xB) { // WordConst 36 | *value = s5_addr[0] | ((uint16_t)s5_addr[1] << 8); 37 | return 3; // 1 Type Byte, 3 Data Bytes 38 | } else if (op == 0xC) { // DWordConst 39 | *value = s5_addr[0] | ((uint32_t)s5_addr[1] << 8) | ((uint32_t)s5_addr[2] << 16) | ((uint32_t)s5_addr[3] << 24); 40 | return 5; // 1 Type Byte, 4 Data Bytes 41 | } else if (op == 0xE) { // QWordConst 42 | *value = s5_addr[0] | ((uint64_t)s5_addr[1] << 8) | ((uint64_t)s5_addr[2] << 16) | ((uint64_t)s5_addr[3] << 24) \ 43 | | ((uint64_t)s5_addr[4] << 32) | ((uint64_t)s5_addr[5] << 40) | ((uint64_t)s5_addr[6] << 48) | ((uint64_t)s5_addr[7] << 56); 44 | return 9; // 1 Type Byte, 8 Data Bytes 45 | } else { 46 | return 0; // No Integer, so something weird 47 | } 48 | } 49 | 50 | int acpi_shutdown_hack( 51 | uintptr_t direct_map_base, 52 | void *(*find_sdt)(const char *signature, size_t index), 53 | uint8_t (*inb)(uint16_t port), 54 | uint16_t (*inw)(uint16_t port), 55 | void (*outb)(uint16_t port, uint8_t value), 56 | void (*outw)(uint16_t port, uint16_t value) 57 | ) { 58 | struct facp *facp = find_sdt("FACP", 0); 59 | 60 | uint8_t *dsdt_ptr = (uint8_t *)(uintptr_t)facp->dsdt + 36 + direct_map_base; 61 | size_t dsdt_len = *((uint32_t *)((uintptr_t)facp->dsdt + 4 + direct_map_base)) - 36; 62 | 63 | uint8_t *s5_addr = 0; 64 | for (size_t i = 0; i < dsdt_len; i++) { 65 | if ((dsdt_ptr + i)[0] == '_' 66 | && (dsdt_ptr + i)[1] == 'S' 67 | && (dsdt_ptr + i)[2] == '5' 68 | && (dsdt_ptr + i)[3] == '_') { 69 | s5_addr = dsdt_ptr + i; 70 | goto s5_found; 71 | } 72 | } 73 | return -1; 74 | 75 | s5_found: 76 | s5_addr += 4; // Skip last part of NameSeg, the earlier segments of the NameString were already tackled by the search loop 77 | if (*s5_addr++ != 0x12) // Assert that it is a PackageOp, if its a Method or something there's not much we can do with such a basic parser 78 | return -1; 79 | s5_addr += ((*s5_addr & 0xc0) >> 6) + 1; // Skip PkgLength 80 | if (*s5_addr++ < 2) // Make sure there are at least 2 elements, which we need, normally there are 4 81 | return -1; 82 | 83 | uint64_t value = 0; 84 | uint8_t size = parse_integer(s5_addr, &value); 85 | if (size == 0) // Wasn't able to parse it 86 | return -1; 87 | 88 | uint16_t SLP_TYPa = value << 10; 89 | s5_addr += size; 90 | 91 | 92 | size = parse_integer(s5_addr, &value); 93 | if (size == 0) // Wasn't able to parse it 94 | return -1; 95 | 96 | uint16_t SLP_TYPb = value << 10; 97 | s5_addr += size; 98 | 99 | if(facp->SMI_CMD != 0 && facp->ACPI_ENABLE != 0) { // This PC has SMM and we need to enable ACPI mode first 100 | outb(facp->SMI_CMD, facp->ACPI_ENABLE); 101 | for (int i = 0; i < 100; i++) 102 | inb(0x80); 103 | 104 | while (!inw(facp->PM1a_CNT_BLK) & (1 << 0)) 105 | ; 106 | } 107 | 108 | 109 | outw(facp->PM1a_CNT_BLK, SLP_TYPa | (1 << 13)); 110 | if (facp->PM1b_CNT_BLK) 111 | outw(facp->PM1b_CNT_BLK, SLP_TYPb | (1 << 13)); 112 | 113 | for (int i = 0; i < 100; i++) 114 | inb(0x80); 115 | 116 | return -1; 117 | } 118 | -------------------------------------------------------------------------------- /acpi_shutdown_hack.h: -------------------------------------------------------------------------------- 1 | #ifndef _ACPI_SHUTDOWN_HACK_H 2 | #define _ACPI_SHUTDOWN_HACK_H 3 | 4 | #include 5 | #include 6 | 7 | int acpi_shutdown_hack( 8 | uintptr_t direct_map_base, 9 | void *(*find_sdt)(const char *signature, size_t index), 10 | uint8_t (*inb)(uint16_t port), 11 | uint16_t (*inw)(uint16_t port), 12 | void (*outb)(uint16_t port, uint8_t value), 13 | void (*outw)(uint16_t port, uint16_t value) 14 | ); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | /limine 2 | /ovmf 3 | *.iso 4 | *.hdd 5 | -------------------------------------------------------------------------------- /test/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Nuke built-in rules and variables. 2 | override MAKEFLAGS += -rR 3 | 4 | override IMAGE_NAME := test 5 | 6 | # Convenience macro to reliably declare user overridable variables. 7 | define DEFAULT_VAR = 8 | ifeq ($(origin $1),default) 9 | override $(1) := $(2) 10 | endif 11 | ifeq ($(origin $1),undefined) 12 | override $(1) := $(2) 13 | endif 14 | endef 15 | 16 | # Toolchain for building the 'limine' executable for the host. 17 | override DEFAULT_HOST_CC := cc 18 | $(eval $(call DEFAULT_VAR,HOST_CC,$(DEFAULT_HOST_CC))) 19 | override DEFAULT_HOST_CFLAGS := -g -O2 -pipe 20 | $(eval $(call DEFAULT_VAR,HOST_CFLAGS,$(DEFAULT_HOST_CFLAGS))) 21 | override DEFAULT_HOST_CPPFLAGS := 22 | $(eval $(call DEFAULT_VAR,HOST_CPPFLAGS,$(DEFAULT_HOST_CPPFLAGS))) 23 | override DEFAULT_HOST_LDFLAGS := 24 | $(eval $(call DEFAULT_VAR,HOST_LDFLAGS,$(DEFAULT_HOST_LDFLAGS))) 25 | override DEFAULT_HOST_LIBS := 26 | $(eval $(call DEFAULT_VAR,HOST_LIBS,$(DEFAULT_HOST_LIBS))) 27 | 28 | .PHONY: all 29 | all: $(IMAGE_NAME).iso 30 | 31 | .PHONY: all-hdd 32 | all-hdd: $(IMAGE_NAME).hdd 33 | 34 | .PHONY: run 35 | run: $(IMAGE_NAME).iso 36 | qemu-system-x86_64 -M q35 -m 2G -cdrom $(IMAGE_NAME).iso -boot d 37 | 38 | .PHONY: run-uefi 39 | run-uefi: ovmf $(IMAGE_NAME).iso 40 | qemu-system-x86_64 -M q35 -m 2G -bios ovmf/OVMF.fd -cdrom $(IMAGE_NAME).iso -boot d 41 | 42 | .PHONY: run-hdd 43 | run-hdd: $(IMAGE_NAME).hdd 44 | qemu-system-x86_64 -M q35 -m 2G -hda $(IMAGE_NAME).hdd 45 | 46 | .PHONY: run-hdd-uefi 47 | run-hdd-uefi: ovmf $(IMAGE_NAME).hdd 48 | qemu-system-x86_64 -M q35 -m 2G -bios ovmf/OVMF.fd -hda $(IMAGE_NAME).hdd 49 | 50 | ovmf: 51 | mkdir -p ovmf 52 | cd ovmf && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd 53 | 54 | limine: 55 | git clone https://github.com/limine-bootloader/limine.git --branch=v6.x-branch-binary --depth=1 56 | $(MAKE) -C limine \ 57 | CC="$(HOST_CC)" \ 58 | CFLAGS="$(HOST_CFLAGS)" \ 59 | CPPFLAGS="$(HOST_CPPFLAGS)" \ 60 | LDFLAGS="$(HOST_LDFLAGS)" \ 61 | LIBS="$(HOST_LIBS)" 62 | 63 | .PHONY: kernel 64 | kernel: 65 | $(MAKE) -C kernel 66 | 67 | $(IMAGE_NAME).iso: limine kernel 68 | rm -rf iso_root 69 | mkdir -p iso_root 70 | cp -v kernel/bin/kernel \ 71 | limine.cfg limine/limine-bios.sys limine/limine-bios-cd.bin limine/limine-uefi-cd.bin iso_root/ 72 | mkdir -p iso_root/EFI/BOOT 73 | cp -v limine/BOOTX64.EFI iso_root/EFI/BOOT/ 74 | cp -v limine/BOOTIA32.EFI iso_root/EFI/BOOT/ 75 | xorriso -as mkisofs -b limine-bios-cd.bin \ 76 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 77 | --efi-boot limine-uefi-cd.bin \ 78 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 79 | iso_root -o $(IMAGE_NAME).iso 80 | ./limine/limine bios-install $(IMAGE_NAME).iso 81 | rm -rf iso_root 82 | 83 | $(IMAGE_NAME).hdd: limine kernel 84 | rm -f $(IMAGE_NAME).hdd 85 | dd if=/dev/zero bs=1M count=0 seek=64 of=$(IMAGE_NAME).hdd 86 | sgdisk $(IMAGE_NAME).hdd -n 1:2048 -t 1:ef00 87 | ./limine/limine bios-install $(IMAGE_NAME).hdd 88 | mformat -i $(IMAGE_NAME).hdd@@1M 89 | mmd -i $(IMAGE_NAME).hdd@@1M ::/EFI ::/EFI/BOOT 90 | mcopy -i $(IMAGE_NAME).hdd@@1M kernel/bin/kernel limine.cfg limine/limine-bios.sys ::/ 91 | mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTX64.EFI ::/EFI/BOOT 92 | mcopy -i $(IMAGE_NAME).hdd@@1M limine/BOOTIA32.EFI ::/EFI/BOOT 93 | 94 | .PHONY: clean 95 | clean: 96 | rm -rf iso_root $(IMAGE_NAME).iso $(IMAGE_NAME).hdd 97 | $(MAKE) -C kernel clean 98 | 99 | .PHONY: distclean 100 | distclean: clean 101 | rm -rf limine ovmf 102 | $(MAKE) -C kernel distclean 103 | -------------------------------------------------------------------------------- /test/kernel/.gitignore: -------------------------------------------------------------------------------- 1 | /src/limine.h 2 | /bin 3 | /obj 4 | -------------------------------------------------------------------------------- /test/kernel/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Nuke built-in rules and variables. 2 | override MAKEFLAGS += -rR 3 | 4 | # This is the name that our final kernel executable will have. 5 | # Change as needed. 6 | override KERNEL := kernel 7 | 8 | # Convenience macro to reliably declare user overridable variables. 9 | define DEFAULT_VAR = 10 | ifeq ($(origin $1),default) 11 | override $(1) := $(2) 12 | endif 13 | ifeq ($(origin $1),undefined) 14 | override $(1) := $(2) 15 | endif 16 | endef 17 | 18 | # It is suggested to use a custom built cross toolchain to build a kernel. 19 | # We are using the standard "cc" here, it may work by using 20 | # the host system's toolchain, but this is not guaranteed. 21 | override DEFAULT_CC := cc 22 | $(eval $(call DEFAULT_VAR,CC,$(DEFAULT_CC))) 23 | 24 | # Same thing for "ld" (the linker). 25 | override DEFAULT_LD := ld 26 | $(eval $(call DEFAULT_VAR,LD,$(DEFAULT_LD))) 27 | 28 | # User controllable C flags. 29 | override DEFAULT_CFLAGS := -g -O2 -pipe 30 | $(eval $(call DEFAULT_VAR,CFLAGS,$(DEFAULT_CFLAGS))) 31 | 32 | # User controllable C preprocessor flags. We set none by default. 33 | override DEFAULT_CPPFLAGS := 34 | $(eval $(call DEFAULT_VAR,CPPFLAGS,$(DEFAULT_CPPFLAGS))) 35 | 36 | # User controllable nasm flags. 37 | override DEFAULT_NASMFLAGS := -F dwarf -g 38 | $(eval $(call DEFAULT_VAR,NASMFLAGS,$(DEFAULT_NASMFLAGS))) 39 | 40 | # User controllable linker flags. We set none by default. 41 | override DEFAULT_LDFLAGS := 42 | $(eval $(call DEFAULT_VAR,LDFLAGS,$(DEFAULT_LDFLAGS))) 43 | 44 | # Internal C flags that should not be changed by the user. 45 | override CFLAGS += \ 46 | -Wall \ 47 | -Wextra \ 48 | -std=gnu11 \ 49 | -ffreestanding \ 50 | -fno-stack-protector \ 51 | -fno-stack-check \ 52 | -fno-lto \ 53 | -fPIE \ 54 | -m64 \ 55 | -march=x86-64 \ 56 | -mno-80387 \ 57 | -mno-mmx \ 58 | -mno-sse \ 59 | -mno-sse2 \ 60 | -mno-red-zone 61 | 62 | # Internal C preprocessor flags that should not be changed by the user. 63 | override CPPFLAGS := \ 64 | -I src \ 65 | $(CPPFLAGS) \ 66 | -MMD \ 67 | -MP 68 | 69 | # Internal linker flags that should not be changed by the user. 70 | override LDFLAGS += \ 71 | -m elf_x86_64 \ 72 | -nostdlib \ 73 | -static \ 74 | -pie \ 75 | --no-dynamic-linker \ 76 | -z text \ 77 | -z max-page-size=0x1000 \ 78 | -T linker.ld 79 | 80 | # Internal nasm flags that should not be changed by the user. 81 | override NASMFLAGS += \ 82 | -Wall \ 83 | -f elf64 84 | 85 | # Use "find" to glob all *.c, *.S, and *.asm files in the tree and obtain the 86 | # object and header dependency file names. 87 | override CFILES := $(shell cd src && find -L * -type f -name '*.c') 88 | override ASFILES := $(shell cd src && find -L * -type f -name '*.S') 89 | override NASMFILES := $(shell cd src && find -L * -type f -name '*.asm') 90 | override OBJ := $(addprefix obj/,$(CFILES:.c=.c.o) $(ASFILES:.S=.S.o) $(NASMFILES:.asm=.asm.o)) 91 | override HEADER_DEPS := $(addprefix obj/,$(CFILES:.c=.c.d) $(ASFILES:.S=.S.d)) 92 | 93 | # Default target. 94 | .PHONY: all 95 | all: bin/$(KERNEL) 96 | 97 | src/limine.h: 98 | curl -Lo $@ https://github.com/limine-bootloader/limine/raw/trunk/limine.h 99 | 100 | # Link rules for the final kernel executable. 101 | bin/$(KERNEL): GNUmakefile linker.ld $(OBJ) 102 | mkdir -p "$$(dirname $@)" 103 | $(LD) $(OBJ) $(LDFLAGS) -o $@ 104 | 105 | # Include header dependencies. 106 | -include $(HEADER_DEPS) 107 | 108 | # Compilation rules for *.c files. 109 | obj/%.c.o: src/%.c GNUmakefile src/limine.h 110 | mkdir -p "$$(dirname $@)" 111 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ 112 | 113 | # Compilation rules for *.S files. 114 | obj/%.S.o: src/%.S GNUmakefile 115 | mkdir -p "$$(dirname $@)" 116 | $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ 117 | 118 | # Compilation rules for *.asm (nasm) files. 119 | obj/%.asm.o: src/%.asm GNUmakefile 120 | mkdir -p "$$(dirname $@)" 121 | nasm $(NASMFLAGS) $< -o $@ 122 | 123 | # Remove object files and the final executable. 124 | .PHONY: clean 125 | clean: 126 | rm -rf bin obj 127 | 128 | .PHONY: distclean 129 | distclean: clean 130 | rm -f src/limine.h 131 | -------------------------------------------------------------------------------- /test/kernel/linker.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an x86_64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-x86-64) 3 | OUTPUT_ARCH(i386:x86-64) 4 | 5 | /* We want the symbol _start to be our entry point */ 6 | ENTRY(_start) 7 | 8 | /* Define the program headers we want so the bootloader gives us the right */ 9 | /* MMU permissions */ 10 | PHDRS 11 | { 12 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 13 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 14 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 15 | dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic PHDR for relocations */ 16 | } 17 | 18 | SECTIONS 19 | { 20 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 21 | /* and because that is what the Limine spec mandates. */ 22 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 23 | /* that is the beginning of the region. */ 24 | . = 0xffffffff80000000; 25 | 26 | .text : { 27 | *(.text .text.*) 28 | } :text 29 | 30 | /* Move to the next memory page for .rodata */ 31 | . += CONSTANT(MAXPAGESIZE); 32 | 33 | .rodata : { 34 | *(.rodata .rodata.*) 35 | } :rodata 36 | 37 | /* Move to the next memory page for .data */ 38 | . += CONSTANT(MAXPAGESIZE); 39 | 40 | .data : { 41 | *(.data .data.*) 42 | } :data 43 | 44 | /* Dynamic section for relocations, both in its own PHDR and inside data PHDR */ 45 | .dynamic : { 46 | *(.dynamic) 47 | } :data :dynamic 48 | 49 | /* NOTE: .bss needs to be the last thing mapped to :data, otherwise lots of */ 50 | /* unnecessary zeros will be written to the binary. */ 51 | /* If you need, for example, .init_array and .fini_array, those should be placed */ 52 | /* above this. */ 53 | .bss : { 54 | *(.bss .bss.*) 55 | *(COMMON) 56 | } :data 57 | 58 | /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ 59 | /DISCARD/ : { 60 | *(.eh_frame) 61 | *(.note .note.*) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/kernel/src/acpi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct rsdp { 9 | char signature[8]; 10 | uint8_t checksum; 11 | char oem_id[6]; 12 | uint8_t rev; 13 | uint32_t rsdt_addr; 14 | // ver 2.0 only 15 | uint32_t length; 16 | uint64_t xsdt_addr; 17 | uint8_t ext_checksum; 18 | uint8_t reserved[3]; 19 | } __attribute__((packed)); 20 | 21 | struct rsdt { 22 | struct sdt sdt; 23 | symbol ptrs_start; 24 | } __attribute__((packed)); 25 | 26 | static bool use_xsdt; 27 | static struct rsdt *rsdt; 28 | 29 | struct limine_rsdp_request rsdp_req = { 30 | LIMINE_RSDP_REQUEST, 0, NULL 31 | }; 32 | 33 | extern struct limine_hhdm_request hhdm_req; 34 | 35 | /* This function should look for all the ACPI tables and index them for 36 | later use */ 37 | void acpi_init(void) { 38 | struct rsdp *rsdp = rsdp_req.response->address; 39 | 40 | if (rsdp->rev >= 2 && rsdp->xsdt_addr) { 41 | use_xsdt = true; 42 | rsdt = (struct rsdt *)((uintptr_t)rsdp->xsdt_addr + hhdm_req.response->offset); 43 | } else { 44 | use_xsdt = false; 45 | rsdt = (struct rsdt *)((uintptr_t)rsdp->rsdt_addr + hhdm_req.response->offset); 46 | } 47 | } 48 | 49 | /* Find SDT by signature */ 50 | void *acpi_find_sdt(const char *signature, size_t index) { 51 | size_t cnt = 0; 52 | 53 | for (size_t i = 0; i < rsdt->sdt.length - sizeof(struct sdt); i++) { 54 | struct sdt *ptr; 55 | if (use_xsdt) 56 | ptr = (struct sdt *)((uintptr_t)((uint64_t *)rsdt->ptrs_start)[i] + hhdm_req.response->offset); 57 | else 58 | ptr = (struct sdt *)((uintptr_t)((uint32_t *)rsdt->ptrs_start)[i] + hhdm_req.response->offset); 59 | 60 | if (!strncmp(ptr->signature, signature, 4) && cnt++ == index) { 61 | return (void *)ptr; 62 | } 63 | } 64 | 65 | return NULL; 66 | } 67 | -------------------------------------------------------------------------------- /test/kernel/src/acpi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct sdt { 7 | char signature[4]; 8 | uint32_t length; 9 | uint8_t rev; 10 | uint8_t checksum; 11 | char oem_id[6]; 12 | char oem_table_id[8]; 13 | uint32_t oem_rev; 14 | uint32_t creator_id; 15 | uint32_t creator_rev; 16 | } __attribute__((packed)); 17 | 18 | void acpi_init(void); 19 | void *acpi_find_sdt(const char *signature, size_t index); 20 | -------------------------------------------------------------------------------- /test/kernel/src/acpi_shutdown_hack.c: -------------------------------------------------------------------------------- 1 | ../../../acpi_shutdown_hack.c -------------------------------------------------------------------------------- /test/kernel/src/acpi_shutdown_hack.h: -------------------------------------------------------------------------------- 1 | ../../../acpi_shutdown_hack.h -------------------------------------------------------------------------------- /test/kernel/src/builtins.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void *memcpy(void *dest, const void *src, size_t n) { 6 | uint8_t *pdest = (uint8_t *)dest; 7 | const uint8_t *psrc = (const uint8_t *)src; 8 | 9 | for (size_t i = 0; i < n; i++) { 10 | pdest[i] = psrc[i]; 11 | } 12 | 13 | return dest; 14 | } 15 | 16 | void *memset(void *s, int c, size_t n) { 17 | uint8_t *p = (uint8_t *)s; 18 | 19 | for (size_t i = 0; i < n; i++) { 20 | p[i] = (uint8_t)c; 21 | } 22 | 23 | return s; 24 | } 25 | 26 | void *memmove(void *dest, const void *src, size_t n) { 27 | uint8_t *pdest = (uint8_t *)dest; 28 | const uint8_t *psrc = (const uint8_t *)src; 29 | 30 | if (src > dest) { 31 | for (size_t i = 0; i < n; i++) { 32 | pdest[i] = psrc[i]; 33 | } 34 | } else if (src < dest) { 35 | for (size_t i = n; i > 0; i--) { 36 | pdest[i-1] = psrc[i-1]; 37 | } 38 | } 39 | 40 | return dest; 41 | } 42 | 43 | int memcmp(const void *s1, const void *s2, size_t n) { 44 | const uint8_t *p1 = (const uint8_t *)s1; 45 | const uint8_t *p2 = (const uint8_t *)s2; 46 | 47 | for (size_t i = 0; i < n; i++) { 48 | if (p1[i] != p2[i]) 49 | return p1[i] < p2[i] ? -1 : 1; 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | char *strcpy(char *dest, const char *src) { 56 | size_t i; 57 | 58 | for (i = 0; src[i]; i++) 59 | dest[i] = src[i]; 60 | 61 | dest[i] = 0; 62 | 63 | return dest; 64 | } 65 | 66 | char *strncpy(char *dest, const char *src, size_t n) { 67 | size_t i; 68 | 69 | for (i = 0; i < n && src[i]; i++) 70 | dest[i] = src[i]; 71 | for ( ; i < n; i++) 72 | dest[i] = 0; 73 | 74 | return dest; 75 | } 76 | 77 | int strcmp(const char *s1, const char *s2) { 78 | for (size_t i = 0; ; i++) { 79 | char c1 = s1[i], c2 = s2[i]; 80 | if (c1 != c2) 81 | return c1 - c2; 82 | if (!c1) 83 | return 0; 84 | } 85 | } 86 | 87 | int strncmp(const char *s1, const char *s2, size_t n) { 88 | for (size_t i = 0; i < n; i++) { 89 | char c1 = s1[i], c2 = s2[i]; 90 | if (c1 != c2) 91 | return c1 - c2; 92 | if (!c1) 93 | return 0; 94 | } 95 | 96 | return 0; 97 | } 98 | 99 | size_t strlen(const char *str) { 100 | size_t len; 101 | 102 | for (len = 0; str[len]; len++); 103 | 104 | return len; 105 | } 106 | -------------------------------------------------------------------------------- /test/kernel/src/builtins.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef char symbol[]; 4 | 5 | void *memcpy(void *dest, const void *src, size_t n); 6 | void *memset(void *s, int c, size_t n); 7 | void *memmove(void *dest, const void *src, size_t n); 8 | int memcmp(const void *s1, const void *s2, size_t n); 9 | 10 | char *strcpy(char *dest, const char *src); 11 | char *strncpy(char *dest, const char *src, size_t n); 12 | int strcmp(const char *s1, const char *s2); 13 | int strncmp(const char *s1, const char *s2, size_t n); 14 | size_t strlen(const char *str); 15 | -------------------------------------------------------------------------------- /test/kernel/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | LIMINE_BASE_REVISION(1) 9 | 10 | static uint8_t inb(uint16_t port) { 11 | uint8_t ret; 12 | asm volatile ( 13 | "inb %1, %0\n\t" 14 | : "=a"(ret) 15 | : "d"(port) 16 | : "memory" 17 | ); 18 | return ret; 19 | } 20 | 21 | static uint16_t inw(uint16_t port) { 22 | uint16_t ret; 23 | asm volatile ( 24 | "inw %1, %0\n\t" 25 | : "=a"(ret) 26 | : "d"(port) 27 | : "memory" 28 | ); 29 | return ret; 30 | } 31 | 32 | static void outb(uint16_t port, uint8_t value) { 33 | asm volatile ( 34 | "outb %1, %0\n\t" 35 | : 36 | : "d"(port), "a"(value) 37 | : "memory" 38 | ); 39 | } 40 | 41 | static void outw(uint16_t port, uint16_t value) { 42 | asm volatile ( 43 | "outw %1, %0\n\t" 44 | : 45 | : "d"(port), "a"(value) 46 | : "memory" 47 | ); 48 | } 49 | 50 | struct limine_hhdm_request hhdm_req = { 51 | LIMINE_HHDM_REQUEST, 0, NULL 52 | }; 53 | 54 | void _start(void) { 55 | if (LIMINE_BASE_REVISION_SUPPORTED == false) { 56 | for (;;) { 57 | asm ("hlt"); 58 | } 59 | } 60 | 61 | acpi_init(); 62 | 63 | for (size_t i = 0; i < 10000000; i++) 64 | inb(0x80); 65 | 66 | acpi_shutdown_hack(hhdm_req.response->offset, 67 | acpi_find_sdt, inb, inw, outb, outw); 68 | 69 | for (;;) { 70 | asm ("hlt"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/limine.cfg: -------------------------------------------------------------------------------- 1 | # Timeout in seconds that Limine will use before automatically booting. 2 | TIMEOUT=3 3 | 4 | # The entry name that will be displayed in the boot menu. 5 | :ACPI shutdown hack test (KASLR on) 6 | # We use the Limine boot protocol. 7 | PROTOCOL=limine 8 | 9 | # Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located. 10 | KERNEL_PATH=boot:///kernel 11 | 12 | # Same thing, but without KASLR. 13 | :ACPI shutdown hack test (KASLR off) 14 | PROTOCOL=limine 15 | 16 | # Disable KASLR (it is enabled by default for relocatable kernels) 17 | KASLR=no 18 | 19 | KERNEL_PATH=boot:///kernel 20 | --------------------------------------------------------------------------------