├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── limine.cfg ├── linker.ld └── src ├── drivers ├── ahci.c ├── cmos.c ├── ether │ ├── ne2k.c │ └── rtl8139.c ├── kb.c └── serial.c ├── fb └── fb.c ├── include ├── cpu │ └── regs.h ├── drivers │ ├── ahci.h │ ├── cmos.h │ ├── kb.h │ ├── ne2k.h │ ├── rtl8139.h │ └── serial.h ├── fb │ ├── fb.h │ └── tamsyn8x16.h ├── kernel │ ├── acpi.h │ ├── gdt.h │ ├── idt.h │ ├── irq.h │ ├── isr.h │ ├── panic.h │ ├── pci.h │ ├── ports.h │ └── timer.h ├── math │ └── math.h ├── mem │ ├── kheap.h │ ├── phys.h │ └── virt.h ├── stivale2.h ├── string │ └── string.h └── task │ ├── sched.h │ └── task.h ├── kernel ├── __gdt.asm ├── __int.asm ├── acpi.c ├── gdt.c ├── idt.c ├── irq.c ├── isr.c ├── panic.c ├── pci.c ├── ports.c └── timer.c ├── kmain.c ├── math └── math.c ├── mem ├── kheap.c ├── phys.c └── virt.c ├── string └── string.c └── task ├── __task.asm ├── sched.c └── task.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | *.iso 4 | *.log 5 | limine 6 | mnt 7 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 dacousb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HydrOS 2 | 3 | ## About 4 | 5 | HydrOS aims to be a simple yet well documented kernel for the x86-64 architecture. 6 | 7 | ## Why HydrOS 8 | 9 | Hydros (that's why OS looks so nice there) is one of the Greek deities associated with water (specially in ancient texts), which is cool. But what is even cooler is the fact the Hydrogen is the first element in the periodic table, which means it is the lightest one. 10 | 11 | ## Building 12 | 13 | Depending on your operating system you may need different packages. In case you run `build.sh` with a problem, simply install the package of the failed command. To build the kernel you need *gcc*, *nasm* and the GNU linker (*ld*). Finally, to build the image you will need *xorriso*. 14 | 15 | - Clone the repository with `git clone ` 16 | - Run `build.sh` with the `build` or `run` options (use the `limine` option before that if you are building the kernel for the first time) 17 | 18 | ## TODO 19 | 20 | Kernel: 21 | 22 | - [x] ISRs and IRQs 23 | - [x] Serial driver 24 | - [x] PCI listing 25 | - [x] ACPI 26 | 27 | Memory 28 | - [x] Physical memory 29 | - [x] Basic kheap 30 | - [x] Virtual memory 31 | 32 | Multitasking 33 | - [x] Scheduler 34 | - [x] Kernel tasks 35 | - [ ] User tasks 36 | - [ ] ELF loading 37 | 38 | Drivers and usage: 39 | 40 | - [x] Framebuffer support 41 | - [x] PS/2 Keyboard driver 42 | - [ ] Mouse support 43 | - [x] Basic shutdown (ACPI) 44 | - [x] AHCI drive detection 45 | - [ ] AHCI drive read/write 46 | 47 | Network: 48 | 49 | - [x] Basic NE2000 driver1 50 | - [x] Basic RTL8139 driver1 51 | - [ ] Working network 52 | 53 | [1] Basic init and MAC address 54 | 55 | # License 56 | 57 | MIT -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | # -------------------------------------- 2 | 3 | LD="ld" 4 | CC="gcc" 5 | AS="nasm" 6 | CFLAGS="-std=gnu99 -ffreestanding -fno-stack-protector -mno-red-zone -Wall -Wextra -c -Isrc/include" 7 | AFLAGS="-f elf64" 8 | 9 | linker="linker.ld" 10 | kernel="kernel.bin" 11 | iso="hydros.iso" 12 | 13 | # -------------------------------------- 14 | 15 | clean() { 16 | rm *.o *.bin *.iso *.log 17 | rm -rf mnt 18 | } 19 | 20 | if [[ $1 == "build" || $1 == "run" ]] 21 | then 22 | clean 23 | 24 | # -------------------------------------- 25 | 26 | $CC $CFLAGS $(find src -name "*.c") 27 | 28 | # -------------------------------------- 29 | 30 | $AS $AFLAGS src/kernel/__gdt.asm -o __gdt.o 31 | $AS $AFLAGS src/kernel/__int.asm -o __int.o 32 | $AS $AFLAGS src/task/__task.asm -o __task.o 33 | 34 | # -------------------------------------- 35 | 36 | $LD -n -o $kernel -T $linker $(find . -name "*.o") 37 | 38 | # -------------------------------------- 39 | 40 | mkdir -p mnt 41 | cp -v $kernel limine.cfg limine/limine.sys\ 42 | limine/limine-cd.bin limine/limine-eltorito-efi.bin mnt 43 | 44 | xorriso -as mkisofs -b limine-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table\ 45 | --efi-boot limine-eltorito-efi.bin -efi-boot-part --efi-boot-image --protective-msdos-label\ 46 | mnt -o $iso 47 | 48 | ./limine/limine-install $iso 49 | 50 | # -------------------------------------- 51 | 52 | if [[ $1 == "run" ]] 53 | then 54 | if [[ $2 == "ne2k" ]]; then qemuflags+="-nic user,model=ne2k_pci"; fi 55 | if [[ $2 == "rtl" ]]; then qemuflags+="-nic user,model=rtl8139"; fi 56 | qemu-system-x86_64 -cdrom $iso $qemuflags 57 | fi 58 | 59 | elif [[ $1 == "clean" ]] 60 | then 61 | clean 62 | 63 | elif [[ $1 == "limine" ]] 64 | then 65 | rm -rf limine 66 | git clone https://github.com/limine-bootloader/limine.git --branch=v2.0-branch-binary --depth=1 67 | make -C limine 68 | 69 | else 70 | echo "(build), (run), (limine), (clean)" 71 | echo "(run ne2k) to load the ne2k device" 72 | echo "(run rtl) to load the rtl8139 device" 73 | fi -------------------------------------------------------------------------------- /limine.cfg: -------------------------------------------------------------------------------- 1 | TIMEOUT=0 2 | 3 | :HydrOS 4 | KASLR=no 5 | PROTOCOL=stivale2 6 | KERNEL_PATH=boot:///kernel.bin -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | /* higher half + 2MB */ 6 | . = 0xffffffff80200000; 7 | 8 | .stivale2hdr : ALIGN(4K) { 9 | KEEP(*.stivale2hdr) 10 | } 11 | 12 | .text : ALIGN(4K) { 13 | *(.text*) 14 | } 15 | 16 | .data : ALIGN(4K) { 17 | *(.data*) 18 | } 19 | 20 | .rodata : ALIGN(4K) { 21 | *(.rodata*) 22 | } 23 | 24 | .bss : ALIGN(4K) { 25 | *(COMMON) 26 | *(.bss*) 27 | } 28 | } -------------------------------------------------------------------------------- /src/drivers/ahci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* the whole AHCI explanation can be found in the ahci.h header file, 5 | * the documentation for specific functions can be found here */ 6 | 7 | #define SATA_SIG_ATA 0x00000101 /* SATA drive */ 8 | #define SATA_SIG_ATAPI 0xEB140101 /* SATAPI drive */ 9 | #define SATA_SIG_SEMB 0xC33C0101 /* enclosure management bridge */ 10 | #define SATA_SIG_PM 0x96690101 /* port multiplier */ 11 | 12 | #define AHCI_DEV_NULL 0 13 | #define AHCI_DEV_SATA 1 14 | #define AHCI_DEV_SEMB 2 15 | #define AHCI_DEV_PM 3 16 | #define AHCI_DEV_SATAPI 4 17 | 18 | #define HBA_PORT_IPM_ACTIVE 1 19 | #define HBA_PORT_DET_PRESENT 3 20 | 21 | #define AHCI_BASE 0x400000 /* 4096 bytes */ 22 | 23 | int check_type(hba_port_t *port) 24 | { 25 | uint32_t ssts = port->ssts; /* SATA status */ 26 | 27 | uint8_t ipm = (ssts >> 8) & 0x0F; 28 | uint8_t det = ssts & 0x0F; 29 | 30 | if (det != HBA_PORT_DET_PRESENT) 31 | return AHCI_DEV_NULL; 32 | if (ipm != HBA_PORT_IPM_ACTIVE) 33 | return AHCI_DEV_NULL; 34 | 35 | switch (port->sig) /* signature */ 36 | { 37 | case SATA_SIG_ATAPI: 38 | return AHCI_DEV_SATAPI; 39 | case SATA_SIG_SEMB: 40 | return AHCI_DEV_SEMB; 41 | case SATA_SIG_PM: 42 | return AHCI_DEV_PM; 43 | default: 44 | return AHCI_DEV_SATA; 45 | } 46 | } 47 | 48 | void probe_port(hba_mem_t *hba_addr) 49 | { 50 | /* from https://wiki.osdev.org/AHCI#Detect_attached_SATA_devices 51 | * 52 | * the Port Implemented register (hba_mem.pi) is a 32 bit value 53 | * and each bit represents a port. If the bit is set, the according 54 | * port has a device attached, otherwise the port is free. */ 55 | 56 | uint32_t pi = hba_addr->pi; /* copy the port implemented register */ 57 | for (uint8_t i = 0; i < 32; i++) 58 | { 59 | if (pi & 1) /* test the first bit */ 60 | { 61 | int dt = check_type(&hba_addr->ports[i]); 62 | if (dt == AHCI_DEV_SATA) 63 | kprintf("[AHCI] SATA drive found, port = %u\n", i); 64 | else if (dt == AHCI_DEV_SATAPI) 65 | kprintf("[AHCI] SATAPI drive found, port = %u\n", i); 66 | else if (dt == AHCI_DEV_SEMB) 67 | kprintf("[AHCI] SEMB drive found, port = %u\n", i); 68 | else if (dt == AHCI_DEV_PM) 69 | kprintf("[AHCI] PM drive found, port = %u\n", i); 70 | } 71 | pi >>= 1; /* next port to the first bit */ 72 | } 73 | } 74 | 75 | void start_cmd(hba_port_t *port) 76 | { 77 | /* wait until CR (bit 15) is cleared */ 78 | while (port->cmd & (1 << 15)) 79 | ; 80 | 81 | /* cmd is the command and status register */ 82 | port->cmd |= 1 << 4; /* set FRE */ 83 | port->cmd |= 1; /* set ST */ 84 | } 85 | 86 | void stop_cmd(hba_port_t *port) 87 | { 88 | /* remember that ~ is NOT and & is AND 89 | * 0b101 &= ~0b001 90 | * 0b101 &= 0b110 91 | * being the result 0b100 */ 92 | 93 | /* cmd is the command and status register */ 94 | port->cmd &= ~1; /* clear ST */ 95 | port->cmd &= ~(1 << 4); /* clear FRE */ 96 | 97 | /* wait until FR and CR are cleared */ 98 | for (;;) 99 | { 100 | if (port->cmd & (1 << 14)) 101 | continue; 102 | if (port->cmd & (1 << 15)) 103 | continue; 104 | break; 105 | } 106 | } 107 | 108 | void port_rebase(hba_mem_t *hba_addr) 109 | { 110 | /* (https://wiki.osdev.org/AHCI#AHCI_port_memory_space_initialization) 111 | * 112 | * before rebasing the port memory space, our kernel needs to wait for 113 | * pending commands */ 114 | 115 | uint32_t pi = hba_addr->pi; /* copy the port implemented register */ 116 | for (uint8_t i = 0; i < 32; i++) 117 | { 118 | if (pi & 1) /* test the first bit */ 119 | { 120 | hba_port_t *port = &hba_addr->ports[i]; 121 | stop_cmd(port); /* stop the command engine */ 122 | 123 | /* clb is command list base address 124 | * command list base address: 1K*port */ 125 | port->clb = AHCI_BASE + (i << 10); 126 | port->clbu = 0; 127 | memset((void *)&port->clb, 0, 1024); 128 | 129 | /* fb is FIS base address 130 | * FIS base address: 32K + 256*port */ 131 | port->fb = AHCI_BASE + (32 << 10) + (i << 8); 132 | port->fbu = 0; 133 | memset((void *)&port->fb, 0, 256); 134 | 135 | hba_cmd_header_t *cmd_header = (hba_cmd_header_t *)(uint64_t)port->clb; 136 | for (uint8_t i = 0; i < 32; i++) 137 | { 138 | cmd_header[i].prdtl = 8; 139 | 140 | cmd_header[i].ctba = AHCI_BASE + (40 << 10) + (i << 13) + (i << 8); 141 | cmd_header[i].ctbau = 0; 142 | memset((void *)&cmd_header[i].ctba, 0, 256); 143 | } 144 | 145 | start_cmd(port); /* restart the command engine */ 146 | } 147 | pi >>= 1; /* next port to the first bit */ 148 | } 149 | } 150 | 151 | void init_ahci(uint64_t hba_addr) 152 | { 153 | kprintf("[AHCI] ABAR = 0x%lx, reconfiguring AHCI memory\n", hba_addr); 154 | port_rebase((hba_mem_t *)hba_addr); 155 | kprintf("[AHCI] AHCI memory spaces configured, enumerating devices\n"); 156 | probe_port((hba_mem_t *)hba_addr); 157 | kprintf("[AHCI] drive enumeration completed\n"); 158 | } -------------------------------------------------------------------------------- /src/drivers/cmos.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define CMOS_CONFIG 0x70 8 | #define CMOS_DATA 0x71 9 | 10 | /* we will use two ports to interact with the CMOS 11 | * 0x70, to select the register (each address of the CMOS is called a register) 12 | * 0x71, to read what we selected 13 | */ 14 | 15 | uint8_t second, minute, hour, day, month, year; 16 | 17 | uint8_t cmos_register(int reg) 18 | { 19 | port_byte_out(CMOS_CONFIG, reg); /* select the desired register */ 20 | return port_byte_in(CMOS_DATA); /* read its contents */ 21 | } 22 | 23 | uint8_t update_flag() 24 | { 25 | port_byte_out(CMOS_CONFIG, 0x0A); /* select the Status Register A */ 26 | return port_byte_in(CMOS_DATA) & (1 << 7); /* return its update flag */ 27 | } 28 | 29 | void read_cmos() 30 | { 31 | /* from https://wiki.osdev.org/CMOS 32 | * 33 | * Register Contents Range 34 | * 0x00 Seconds 0–59 35 | * 0x02 Minutes 0–59 36 | * 0x04 Hours 0–23 in 24-hour mode, 37 | * 1–12 in 12-hour mode, highest bit set if pm 38 | * 0x06 Weekday 1–7, where Sunday = 1 39 | * 0x07 Day of Month 1–31 40 | * 0x08 Month 1–12 41 | * 0x09 Year 0–99 42 | * 0x32 Century (maybe) 19–20? 43 | * 0x0A Status Register A 44 | * 0x0B Status Register B 45 | * 46 | * Status Register A (bit 7 is the update flag, we need it to 47 | * be clear before reading the clock) 48 | * 49 | * Status Register B (value = 2 (010), 24/12 hour format 50 | * value = 4 (100), binary/BCD format) 51 | * 52 | * in BCD, 23 seconds is 0x23 and not 0x17 53 | */ 54 | 55 | while (update_flag()) 56 | ; 57 | 58 | second = cmos_register(0x00); 59 | minute = cmos_register(0x02); 60 | hour = cmos_register(0x04); 61 | day = cmos_register(0x07); 62 | month = cmos_register(0x08); 63 | year = cmos_register(0x09); 64 | 65 | uint8_t cmos_reg_b = cmos_register(0x0B); 66 | 67 | /* if the values are in BCD format, convert them into normal ones */ 68 | /* ((val / 16) * 10 + (val & 0xF)) is the way of doing this */ 69 | if (!(cmos_reg_b & 0b100)) /* remember that AND will ignore everything except that bit */ 70 | { 71 | second = ((second / 16) * 10) + (second & 0x0F); 72 | minute = ((minute / 16) * 10) + (minute & 0x0F); 73 | hour = ((((hour & 0x70) / 16) * 10) + (hour & 0x0F)) | (hour & 0x80); 74 | day = (day & 0x0F) + ((day / 16) * 10); 75 | month = ((month / 16) * 10) + (month & 0x0F); 76 | year = ((year / 16) * 10) + (year & 0x0F); 77 | } 78 | 79 | /* in case the format is 12 hour */ 80 | if (!(cmos_reg_b & 0x02) && (hour & 0x80)) 81 | hour = ((hour & 0x7F) + 12) % 24; 82 | } 83 | 84 | void print_cmos() 85 | { 86 | read_cmos(); 87 | kprintf("[DATE] %u-%u-%u %1u:%1u:%1u\n", 88 | day, month, year, 89 | hour, minute, second); 90 | } -------------------------------------------------------------------------------- /src/drivers/ether/ne2k.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void init_ne2k(uint64_t ioaddr) 6 | { 7 | /* refer to https://wiki.osdev.org/Ne2000 for a more extensive 8 | * explanation */ 9 | 10 | /* NE2000 or NE2k is a network card specification, 11 | * this is a simple implementation to initialize it and get 12 | * the MAC address of the device */ 13 | 14 | /* reset the board, this is done by doing a read 15 | * followed by a write to the reset port */ 16 | port_byte_out(ioaddr + 0x1F, port_byte_in(ioaddr + 0x1F)); 17 | 18 | while ((port_byte_in(ioaddr + 0x07) & 0x80) == 0) 19 | ; /* wait the reset to complete */ 20 | 21 | port_byte_out(ioaddr + 0x07, 0xFF); 22 | 23 | port_byte_out(ioaddr + 0x00, (1 << 5) | 1); 24 | port_byte_out(ioaddr + 0x0E, 0x49); 25 | port_byte_out(ioaddr + 0x0A, 0); 26 | port_byte_out(ioaddr + 0x0B, 0); 27 | port_byte_out(ioaddr + 0x0F, 0); 28 | port_byte_out(ioaddr + 0x07, 0xFF); 29 | port_byte_out(ioaddr + 0x0C, 0x20); 30 | port_byte_out(ioaddr + 0x0D, 0x02); 31 | port_byte_out(ioaddr + 0x0A, 32); 32 | port_byte_out(ioaddr + 0x0B, 0); 33 | port_byte_out(ioaddr + 0x08, 0); 34 | port_byte_out(ioaddr + 0x09, 0); 35 | port_byte_out(ioaddr + 0x00, 0x0A); 36 | 37 | uint8_t mac[6]; 38 | for (uint8_t i = 0; i < 6; i++) 39 | mac[i] = port_byte_in(ioaddr + 0x10); 40 | 41 | kprintf("[NE2k] ioaddr = 0x%x, MAC = ", ioaddr); 42 | 43 | for (uint8_t i = 0; i < 6; i++) 44 | kprintf("%1x%s", mac[i], (i < 5) ? ":" : " "); 45 | 46 | /* since this driver just gets the MAC address (for now), 47 | * print this warning */ 48 | kprintf("warning: network is not up\n"); 49 | } -------------------------------------------------------------------------------- /src/drivers/ether/rtl8139.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void init_rtl8139(uint64_t ioaddr) 6 | { 7 | /* refer to https://wiki.osdev.org/RTL8139 for a more extensive 8 | * explanation */ 9 | 10 | /* the registers used for the RTL8139 are 11 | * 12 | * Offset (from IO base) Size Name 13 | * 0x00 6 MAC0-5 14 | * 0x08 8 MAR0-7 15 | * 0x30 4 RBSTART 16 | * 0x37 1 CMD 17 | * 0x3C 2 IMR 18 | * 0x3E 2 ISR 19 | * 20 | * 0x44 RCR 21 | * 0x52 CONFIG_1 22 | */ 23 | 24 | port_byte_out(ioaddr + 0x52, 0x00); /* turn on the device */ 25 | port_byte_out(ioaddr + 0x37, 0x10); /* reset the device */ 26 | while (port_byte_in(ioaddr + 0x37) & (1 << 4)) /* wait till RST bit is clear (reset finished) */ 27 | ; 28 | 29 | /* let's send the chip a pointer to a memory location to use as the receive 30 | * buffer, it is recommended to allocate 8K + 16 bytes + 1500 bytes */ 31 | uint8_t *rx_buffer = 0; /* NOTE kmalloc is needed */ 32 | port_byte_out(ioaddr + 0x30, (uintptr_t)rx_buffer); 33 | 34 | /* now, we need to set the chip to accept TOK and ROK interrupts, that is, 35 | * transmit and receive */ 36 | port_byte_out(ioaddr + 0x3C, 0b101); 37 | 38 | /* (https://wiki.osdev.org/RTL8139#Configuring_receive_buffer_.28RCR.29) 39 | * tell the chip to accept packets 40 | * - 0xf sets the first 4 bits (rules AB, AM, APM, AAP) 41 | * - the seventh bit sets the WRAP (enabling buffer overflow) 42 | * this is why we needed to add 1500 extra bytes to the buffer 43 | */ 44 | port_byte_out(ioaddr + 0x44, 0xf | (1 << 7)); 45 | 46 | /* finally, we need to enable the receive (RX) and transmitter (TX) functions 47 | * set the RE and TE bits */ 48 | port_byte_out(ioaddr + 0x37, 0b1100); 49 | 50 | uint8_t mac[6]; 51 | for (uint8_t i = 0; i < 6; i++) 52 | mac[i] = port_byte_in(ioaddr + i); /* see the MAC0-5 offsets */ 53 | 54 | kprintf("[RTL] ioaddr = 0x%x, MAC = ", ioaddr); 55 | 56 | for (uint8_t i = 0; i < 6; i++) 57 | kprintf("%1x%s", mac[i], (i < 5) ? ":" : " "); 58 | 59 | /* since this driver just gets the MAC address (for now), 60 | * print this warning */ 61 | kprintf("warning: network is not up\n"); 62 | } -------------------------------------------------------------------------------- /src/drivers/kb.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define SCANCODE_MAX 57 9 | 10 | char scancodes[] = { 11 | 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', 12 | '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 13 | '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 14 | 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 15 | '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7', '8', '9', 16 | '-', '4', '5', '6', '+', '1', '2', '3', '0', '.'}; 17 | 18 | static void keyboard_callback() 19 | { 20 | uint8_t scancode = port_byte_in(KEYBOARD_DATA_PORT); 21 | 22 | if (scancode > SCANCODE_MAX) 23 | return; 24 | 25 | putchar(scancodes[scancode]); 26 | } 27 | 28 | void init_kb() 29 | { 30 | irq_set_handler(IRQ1, keyboard_callback); 31 | } -------------------------------------------------------------------------------- /src/drivers/serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int init_serial() 5 | { 6 | /* the COM1 port will be our base port */ 7 | /* we can add an offset (+n) to access other ports, */ 8 | /* each port is mapped to a register */ 9 | 10 | /* refer to https://wiki.osdev.org/Serial_Ports for a better explanation */ 11 | 12 | port_byte_out(COM1 + 1, 0x00); /* +1 is the interrupt register, disable interrupts */ 13 | port_byte_out(COM1 + 3, 0x80); /* +3 is the line control register, holds a bit called DLAB, */ 14 | port_byte_out(COM1 + 0, 0x03); /* ^ it changes how +1 and +0 (COM1) work */ 15 | port_byte_out(COM1 + 1, 0x00); 16 | port_byte_out(COM1 + 3, 0x03); 17 | port_byte_out(COM1 + 2, 0xC7); 18 | port_byte_out(COM1 + 4, 0x0B); 19 | port_byte_out(COM1 + 4, 0x1E); 20 | port_byte_out(COM1 + 0, 0xAE); /* write an arbitrary value to the data register (+0) */ 21 | 22 | if (port_byte_in(COM1 + 0) != 0xAE) /* test if it worked */ 23 | return 1; /* if it is not what we wrote (0xAE) */ 24 | 25 | port_byte_out(COM1 + 4, 0x0F); 26 | return 0; 27 | } 28 | 29 | int serial_received() 30 | { 31 | return port_byte_in(COM1 + 5) & 1; 32 | } 33 | 34 | char read_serial() 35 | { 36 | while (serial_received() == 0) 37 | ; 38 | return port_byte_in(COM1); 39 | } 40 | 41 | int is_transmit_empty() 42 | { 43 | return port_byte_in(COM1 + 5) & 0x20; 44 | } 45 | 46 | void write_serial(char c) 47 | { 48 | while (is_transmit_empty() == 0) 49 | ; 50 | port_byte_out(COM1, c); 51 | } 52 | 53 | void print_serial(const char *s) 54 | { 55 | for (int i = 0; s[i]; i++) 56 | write_serial(s[i]); 57 | } -------------------------------------------------------------------------------- /src/fb/fb.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | uint32_t *fb; 7 | uint16_t fb_height, fb_width; 8 | 9 | static uint32_t fg_color = DEFAULT_FG, bg_color = DEFAULT_BG; 10 | 11 | int print_x = 0, print_y = 0; 12 | 13 | void init_fb(struct stivale2_struct_tag_framebuffer *fb_tag) 14 | { 15 | fb = (uint32_t *)fb_tag->framebuffer_addr; 16 | fb_width = fb_tag->framebuffer_width; 17 | fb_height = fb_tag->framebuffer_height; 18 | 19 | for (size_t i = 0; i < (fb_width * fb_height); i++) 20 | fb[i] = bg_color; /* fills the entire background for the first time */ 21 | } 22 | 23 | void scroll() 24 | { 25 | /* moves every pixel so we can actually scroll */ 26 | for (size_t i = 0; i < (fb_width * fb_height); i++) 27 | fb[i] = (fb + fb_width * FONT_HEIGHT)[i]; 28 | 29 | /* fills the new line with the background color (because it is empty!) */ 30 | for (size_t i = (fb_width * (fb_height - FONT_HEIGHT)); i < (fb_width * fb_height); i++) 31 | fb[i] = bg_color; 32 | } 33 | 34 | void set_pixel(int x, int y, uint32_t color) 35 | { 36 | /* plot a pixel in the framebuffer */ 37 | fb[y * fb_width + x] = color; 38 | } 39 | 40 | void put_literal_char(int x, int y, char c, uint32_t fg_color, uint32_t bg_color) 41 | { 42 | for (int ly = 0; ly < FONT_HEIGHT; ly++) 43 | { 44 | for (int lx = 0; lx < FONT_WIDTH; lx++) 45 | { 46 | uint8_t pixel = tamsyn8x16[(c * FONT_HEIGHT) + ly]; 47 | if ((pixel >> lx) & 1) 48 | fb[x + ((FONT_WIDTH - 1) - lx) + ((y + ly) * fb_width)] = fg_color; 49 | else 50 | fb[x + ((FONT_WIDTH - 1) - lx) + ((y + ly) * fb_width)] = bg_color; 51 | } 52 | } 53 | } 54 | 55 | void set_color(uint32_t fg, uint32_t bg) 56 | { 57 | fg_color = fg, bg_color = bg; 58 | } 59 | 60 | void putchar(char c) 61 | { 62 | switch (c) 63 | { 64 | case '\n': /* new line */ 65 | print_x = 0; 66 | if (print_y + FONT_HEIGHT * 2 > fb_height) 67 | scroll(); 68 | else 69 | print_y += FONT_HEIGHT; 70 | break; 71 | case '\r': /* carriage return */ 72 | print_x = 0; 73 | break; 74 | case '\b': /* back (del) */ 75 | print_x -= FONT_WIDTH; 76 | putchar(' '); 77 | print_x -= FONT_WIDTH; 78 | break; 79 | default: /* any other character */ 80 | if (print_x + FONT_WIDTH > fb_width) 81 | { 82 | print_x = 0; 83 | print_y += FONT_HEIGHT; 84 | } 85 | if (print_y + FONT_HEIGHT > fb_height) 86 | { 87 | scroll(); 88 | print_y -= FONT_HEIGHT; 89 | } 90 | put_literal_char(print_x, print_y, c, fg_color, bg_color); 91 | print_x += FONT_WIDTH; 92 | } 93 | } 94 | 95 | void print(const char *s) 96 | { 97 | for (size_t i = 0; s[i]; i++) 98 | putchar(s[i]); 99 | } -------------------------------------------------------------------------------- /src/include/cpu/regs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct regs 6 | { 7 | uint64_t r15; 8 | uint64_t r14; 9 | uint64_t r13; 10 | uint64_t r12; 11 | uint64_t r11; 12 | uint64_t r10; 13 | uint64_t r9; 14 | uint64_t r8; 15 | 16 | uint64_t rbp; 17 | uint64_t rdi; 18 | uint64_t rsi; 19 | uint64_t rdx; 20 | uint64_t rcx; 21 | uint64_t rbx; 22 | uint64_t rax; 23 | 24 | uint64_t reserved_1; 25 | uint64_t reserved_2; 26 | 27 | uint64_t rip; 28 | uint64_t cs; 29 | uint64_t rflags; 30 | uint64_t rsp; 31 | uint64_t ss; 32 | } __attribute__((packed)) regs_t; -------------------------------------------------------------------------------- /src/include/drivers/ahci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void init_ahci(uint64_t hba_addr); 6 | 7 | /* SATA uses FIS (Frame Information Structure), FISs are packets 8 | * containing a header that identifies itself and the payload data 9 | * (the data that is not metadata) 10 | * 11 | * there are different types of FIS, so we are going to define them 12 | * along with their identification code */ 13 | 14 | #define FIS_TYPE_REG_H2D 0x27 /* Register FIS; host to dev */ 15 | #define FIS_TYPE_REG_D2H 0x34 /* Register FIX; dev to host */ 16 | #define FIS_TYPE_DMA_ACT 0x39 /* DMA activate FIS; dev to host */ 17 | #define FIS_TYPE_DMA_SETUP 0x41 /* DMA setup FIS; bidirectional */ 18 | #define FIS_TYPE_DATA 0x46 /* DATA FIS; bidirectional */ 19 | #define FIS_TYPE_BIST 0x58 /* BIST activate FIS; bidirectional */ 20 | #define FIS_TYPE_PIO_SETUP 0x5F /* PIO setup FIS; dev to host */ 21 | #define FIS_TYPE_DEV_BITS 0xA1 /* Set dev bits FIS; dev to host */ 22 | 23 | /* a host to dev register FIS is used by the host to send a command or control 24 | * to a device, the kernel will send this */ 25 | typedef struct fis_reg_h2d 26 | { 27 | uint8_t fis_type; /* FIS_TYPE_REG_H2D */ 28 | 29 | uint8_t pm_port : 4; /* port multiplier */ 30 | uint8_t reserved_1 : 3; 31 | uint8_t c : 1; /* 1: command, 0: control */ 32 | 33 | uint8_t command; /* command register */ 34 | uint8_t feature_low; /* feature register */ 35 | 36 | uint8_t lba_low; /* LBA low register */ 37 | uint8_t lba_mid; /* LBA mid register */ 38 | uint8_t lba_high; /* LBA high register */ 39 | uint8_t dev; /* dev register */ 40 | 41 | uint8_t lba3; 42 | uint8_t lba4; 43 | uint8_t lba5; 44 | uint8_t feature_high; /* feature register */ 45 | 46 | uint8_t count_low; /* count register */ 47 | uint8_t count_high; /* count register */ 48 | uint8_t icc; /* isochronous command completion */ 49 | uint8_t control; /* control register */ 50 | 51 | uint8_t reserved_2[4]; 52 | } fis_reg_h2d_t; 53 | 54 | /* a dev to host register FIS is used by the device to notify the host 55 | * that some ATA register has changed, it contains data about things 56 | * that have been updated (status, error and other registers), the kernel 57 | * will receive this */ 58 | typedef struct fis_reg_d2h 59 | { 60 | uint8_t fis_type; /* FIS_TYPE_REG_D2H */ 61 | 62 | uint8_t pm_port : 4; /* port multiplier */ 63 | uint8_t reserved_1 : 2; 64 | uint8_t i : 1; /* interrupt bit */ 65 | uint8_t reserved_2 : 1; 66 | 67 | uint8_t status; /* status register */ 68 | uint8_t error; /* error register */ 69 | 70 | uint8_t lba_low; /* LBA low register */ 71 | uint8_t lba_mid; /* LBA mid register */ 72 | uint8_t lba_high; /* LBA high register */ 73 | uint8_t dev; /* dev register */ 74 | 75 | uint8_t lba3; 76 | uint8_t lba4; 77 | uint8_t lba5; 78 | uint8_t reserved_3; 79 | 80 | uint8_t count_low; /* count register */ 81 | uint8_t count_high; /* count register */ 82 | uint8_t reserved_4[2]; 83 | 84 | uint8_t reserved_5[4]; 85 | } fis_reg_d2h_t; 86 | 87 | /* used by the host or the device itself to send data payload */ 88 | typedef struct fis_data 89 | { 90 | uint8_t fis_type; /* FIS_TYPE_DATA */ 91 | 92 | uint8_t pm_port : 4; /* port multiplier */ 93 | uint8_t reserved_1 : 4; 94 | 95 | uint8_t reserved_2[2]; 96 | 97 | uint32_t data[1]; /* payload */ 98 | } fis_data_t; 99 | 100 | /* used by the device to tell the host that it's about to 101 | * send or ready to receive a PIO data payload */ 102 | typedef struct fis_pio_setup 103 | { 104 | uint8_t fis_type; /* FIS_TYPE_PIO_SETUP */ 105 | 106 | uint8_t pm_port : 4; /* port multiplier */ 107 | uint8_t reserved_1 : 1; 108 | uint8_t d : 1; /* data transfer direction, 1: dev to host */ 109 | uint8_t i : 1; /* interrupt bit */ 110 | uint8_t reserved_2 : 1; 111 | 112 | uint8_t status; /* status register */ 113 | uint8_t error; /* error register */ 114 | 115 | uint8_t lba_low; /* LBA low register */ 116 | uint8_t lba_mid; /* LBA mid register */ 117 | uint8_t lba_high; /* LBA high register */ 118 | uint8_t dev; /* dev register */ 119 | 120 | uint8_t lba3; 121 | uint8_t lba4; 122 | uint8_t lba5; 123 | uint8_t reserved_3; 124 | 125 | uint8_t count_low; /* count register */ 126 | uint8_t count_high; /* count register */ 127 | uint8_t reserved_4; 128 | uint8_t e_status; /* new value of status register */ 129 | 130 | uint16_t tc; /* transfer count */ 131 | uint8_t reserved_5[2]; 132 | } fis_pio_setup_t; 133 | 134 | /* Direct memory access (DMA), way faster than PIO */ 135 | typedef struct fis_dma_setup 136 | { 137 | uint8_t fis_type; /* FIS_TYPE_DMA_SETUP */ 138 | 139 | uint8_t pm_port : 4; /* port multiplier */ 140 | uint8_t reserved_1 : 1; 141 | uint8_t d : 1; /* data transfer direction, 1: dev to host */ 142 | uint8_t i : 1; /* interrupt bit */ 143 | uint8_t a : 1; /* specifies if DMA Activate FIS is needed */ 144 | 145 | uint8_t reserved_2[2]; 146 | 147 | uint64_t DMA_buffer_id; /* DMA buffer identifier */ 148 | 149 | uint32_t reserved_3; 150 | 151 | uint32_t DMA_buffer_offset; 152 | 153 | uint32_t transfer_count; /* number of bytes to transfer */ 154 | 155 | uint32_t reserved_4; 156 | } fis_dma_setup_t; 157 | 158 | /* define the port structure for later use in hba_mem */ 159 | typedef volatile struct hba_port 160 | { 161 | uint32_t clb; /* command list base address */ 162 | uint32_t clbu; /* command list base address (upper 32 bits) */ 163 | uint32_t fb; /* FIS base address */ 164 | uint32_t fbu; /* FIS base address (upper 32 bits) */ 165 | uint32_t is; /* interrupt status */ 166 | uint32_t ie; /* interrupt enable */ 167 | uint32_t cmd; /* command and status */ 168 | uint32_t reserved_1; 169 | uint32_t tfd; /* task file data */ 170 | uint32_t sig; /* signature */ 171 | uint32_t ssts; /* SATA status (SCR0:SStatus) */ 172 | uint32_t sctl; /* SATA control (SCR2:SControl) */ 173 | uint32_t serr; /* SATA error (SCR1:SError) */ 174 | uint32_t sact; /* SATA active (SCR3:SActive) */ 175 | uint32_t ci; /* command issue */ 176 | uint32_t sntf; /* SATA notification (SCR4:SNotification) */ 177 | uint32_t fbs; /* FIS-based switch control */ 178 | uint32_t reserved_2[11]; 179 | uint32_t vendor[4]; /* vendor specific */ 180 | } hba_port_t; 181 | 182 | /* https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1-3-1.pdf 183 | * page 15 (onwards) has some useful information for the following structures */ 184 | 185 | /* (https://wiki.osdev.org/AHCI#AHCI_Registers_and_Memory_Structures) 186 | * 187 | * the last BAR (BAR5 or ABAR) of the AHCI controller PCI device, points to 188 | * the AHCI base address 189 | * 190 | * the HBA (host bus adapter) structure can be found at that base address 191 | * and it contains several memory registers 192 | * 193 | * generic host control registers control the behavior of the whole controller, 194 | * while each port owns its own set of port control registers (that is, hba_port) 195 | */ 196 | typedef volatile struct hba_mem 197 | { 198 | /* 0x00 to 0x2C, generic host control memory registers */ 199 | uint32_t cap; /* host capability */ 200 | uint32_t ghc; /* global host control */ 201 | uint32_t is; /* interrupt status */ 202 | uint32_t pi; /* port implemented */ 203 | uint32_t vs; /* version */ 204 | uint32_t ccc_ctl; /* command completion coalescing control */ 205 | uint32_t ccc_pts; /* command completion coalescing ports */ 206 | uint32_t em_loc; /* enclosure management location */ 207 | uint32_t em_ctl; /* enclosure management control */ 208 | uint32_t cap2; /* host capabilities extended */ 209 | uint32_t bohc; /* BIOS/OS handoff control and status */ 210 | 211 | /* 0x2C to 0xA0, reserved */ 212 | uint8_t reserved[0xA0 - 0x2C]; 213 | 214 | /* 0xA0 to 0x100, vendor specific memory registers */ 215 | uint8_t vendor[0x100 - 0xA0]; 216 | 217 | /* 0x100 to 0x1100, port control registers */ 218 | hba_port_t ports[1]; /* [port 0] to port 31 */ 219 | } hba_mem_t; 220 | 221 | /* each port will correspond to one, and only one, SATA device, 222 | * that is, each port can attach one SATA device 223 | * 224 | * the host (the kernel) will send commands to the device using the command 225 | * list structure and the device will deliver data to the kernel using the 226 | * received FIS structure 227 | * 228 | * they are located at hba_port.clb/clbu and hba_port.fb/fbu */ 229 | 230 | typedef struct hba_cmd_header 231 | { 232 | /* to send a command, the host will fill a header like this 233 | * one, and will set the corresponding bit in the hba_port.ci (command issue) 234 | * register 235 | * 236 | * each port has 32 headers like this one (the command list is compound 237 | * of 32 command headers) 238 | * 239 | * each command header describes an ATA or ATAPI command; hba_port.ci 240 | * is a 32-bit register, so there is one bit for each command 241 | * 242 | * the AHCI controller will send the command to the device attached 243 | * to that specific port 244 | * 245 | * if everything worked, the bit in the command issue register will be cleared */ 246 | 247 | uint8_t cfl : 5; /* command FIS len in DWORDS */ 248 | uint8_t a : 1; /* ATAPI */ 249 | uint8_t w : 1; /* write, 1: H2D, 0: D2H */ 250 | uint8_t p : 1; /* prefetchable */ 251 | 252 | uint8_t r : 1; /* reset */ 253 | uint8_t b : 1; /* BIST */ 254 | uint8_t c : 1; /* clear busy upon R_OK */ 255 | uint8_t reserved_1 : 1; 256 | uint8_t pmp : 4; /* port multiplier port */ 257 | 258 | uint16_t prdtl; /* physical region descriptor table length in entries */ 259 | 260 | volatile uint32_t prdbc; /* physical region descriptor byte count transferred */ 261 | 262 | uint32_t ctba; /* command table descriptor base address */ 263 | uint32_t ctbau; /* command table descriptor base address (upper 32 bits) */ 264 | 265 | uint32_t reserved_2[4]; 266 | } hba_cmd_header_t; 267 | 268 | typedef volatile struct hba_fis 269 | { 270 | /* there are 4 types of FIS that may be sent by the device 271 | * to the host (the kernel) 272 | * 273 | * the FIS will be copied into the host memory and a bit 274 | * will be set in hba_port.is */ 275 | fis_dma_setup_t dsfis; /* DMA setup FIS */ 276 | uint8_t pad1[4]; 277 | 278 | fis_pio_setup_t psfis; /* PIO setup FIS */ 279 | uint8_t pad2[12]; 280 | 281 | fis_reg_d2h_t rfis; /* register - dev to host FIS */ 282 | uint8_t pad3[4]; 283 | 284 | uint16_t sdbfis; /* set device bit FIS */ 285 | 286 | uint8_t ufis[64]; 287 | 288 | uint8_t rsv[0x100 - 0xA0]; 289 | } hba_fis_t; 290 | 291 | /* define the PRDT entry structure for later use in hba_cmd_tbl */ 292 | typedef struct hba_prdt_entry 293 | { 294 | uint32_t dba; /* data base address */ 295 | uint32_t dbau; /* data base address (upper 32 bits) */ 296 | uint32_t reserved_1; 297 | 298 | uint32_t dbc : 22; /* byte count */ 299 | uint32_t reserved_2 : 9; 300 | uint32_t i : 1; /* interrupt completion */ 301 | } hba_prdt_entry_t; 302 | 303 | typedef struct hba_cmd_tbl 304 | { 305 | uint8_t cfis[64]; /* command FIS */ 306 | 307 | uint8_t acmd[16]; /* ATAPI command */ 308 | 309 | uint8_t reserved[48]; 310 | 311 | /* physical region descriptor table entries, 0 ~ 65535 */ 312 | hba_prdt_entry_t prdt_entry[1]; 313 | } hba_cmd_tbl_t; -------------------------------------------------------------------------------- /src/include/drivers/cmos.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void print_cmos(); -------------------------------------------------------------------------------- /src/include/drivers/kb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define KEYBOARD_DATA_PORT 0x60 4 | 5 | void init_kb(); -------------------------------------------------------------------------------- /src/include/drivers/ne2k.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void init_ne2k(uint64_t ioaddr); -------------------------------------------------------------------------------- /src/include/drivers/rtl8139.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void init_rtl8139(uint64_t ioaddr); -------------------------------------------------------------------------------- /src/include/drivers/serial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define COM1 0x3F8 4 | 5 | int init_serial(); 6 | void print_serial(const char *s); -------------------------------------------------------------------------------- /src/include/fb/fb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define DEFAULT_FG 0xFFFFFFFF 6 | #define DEFAULT_BG 0x22282A36 7 | #define DEFAULT_GR 0x555AE67D 8 | #define DEFAULT_RD 0xFFFF5555 9 | 10 | void init_fb(struct stivale2_struct_tag_framebuffer *fb_tag); 11 | void set_pixel(int x, int y, uint32_t color); 12 | void put_literal_char(int x, int y, char c, uint32_t fg_color, uint32_t bg_color); 13 | void set_color(uint32_t fg, uint32_t bg); 14 | void putchar(char c); 15 | void print(const char *s); -------------------------------------------------------------------------------- /src/include/fb/tamsyn8x16.h: -------------------------------------------------------------------------------- 1 | /* LICENSE 2 | * Tamsyn font is free. You are hereby granted permission to use, copy, modify, 3 | * and distribute it as you see fit. 4 | * 5 | * Tamsyn font is provided "as is" without any express or implied warranty. 6 | * 7 | * The author makes no representations about the suitability of this font for 8 | * a particular purpose. 9 | * 10 | * In no event will the author be held liable for damages arising from the use 11 | * of this font. 12 | * 13 | * source https://www.fial.com/~scott/tamsyn-font/ 14 | */ 15 | 16 | #define FONT_WIDTH 8 17 | #define FONT_HEIGHT 16 18 | 19 | unsigned char tamsyn8x16[] = { 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 23 | 0x72, 0xb5, 0x4a, 0x86, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 24 | 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x00, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 27 | 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 31 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 32 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 33 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 36 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 37 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 43 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 44 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 45 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 47 | 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x1c, 0x22, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x72, 0x8c, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x24, 0x24, 0x7e, 0x24, 0x24, 0x24, 0x7e, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0x00, 0x08, 0x08, 0x1e, 0x20, 0x20, 0x1c, 0x02, 0x02, 0x3c, 0x08, 0x08, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x30, 0x49, 0x4a, 0x34, 0x08, 0x16, 0x29, 0x49, 0x06, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x30, 0x31, 0x49, 0x46, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 0x00, 0x00, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00, 61 | 0x00, 0x00, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x00, 0x00, 62 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x18, 0x7e, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x08, 0x10, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x08, 0x18, 0x28, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x02, 0x04, 0x08, 0x10, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x7e, 0x04, 0x08, 0x1c, 0x02, 0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x04, 0x0c, 0x14, 0x24, 0x44, 0x7e, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x7c, 0x02, 0x02, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x1c, 0x20, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 75 | 0x00, 0x00, 0x00, 0x7e, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 77 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x04, 0x38, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 79 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x08, 0x08, 0x10, 0x00, 80 | 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x3c, 0x42, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x1c, 0x22, 0x41, 0x4f, 0x51, 0x51, 0x51, 0x53, 0x4d, 0x40, 0x20, 0x1f, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x1e, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x42, 0x42, 0x42, 0x44, 0x78, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 91 | 0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x46, 0x42, 0x42, 0x22, 0x1e, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x42, 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x41, 0x63, 0x55, 0x49, 0x49, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x42, 0x62, 0x52, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 100 | 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 101 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x04, 0x02, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x48, 0x44, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 103 | 0x00, 0x00, 0x00, 0x3e, 0x40, 0x40, 0x20, 0x18, 0x04, 0x02, 0x02, 0x7c, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 106 | 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 107 | 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41, 0x49, 0x49, 0x49, 0x55, 0x63, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 109 | 0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x7e, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x00, 111 | 0x00, 0x00, 0x1e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x00, 0x00, 112 | 0x00, 0x00, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 113 | 0x00, 0x00, 0x78, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, 0x00, 114 | 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 115 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 116 | 0x00, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 117 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0x00, 0x40, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x00, 0x00, 119 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x02, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 122 | 0x00, 0x00, 0x0e, 0x10, 0x10, 0x7e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00, 124 | 0x00, 0x00, 0x40, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 125 | 0x00, 0x00, 0x08, 0x08, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 126 | 0x00, 0x00, 0x04, 0x04, 0x00, 0x1c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x38, 0x00, 127 | 0x00, 0x00, 0x40, 0x40, 0x40, 0x44, 0x48, 0x50, 0x70, 0x48, 0x44, 0x42, 0x00, 0x00, 0x00, 0x00, 128 | 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x00, 0x00, 0x00, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x40, 0x00, 133 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x02, 0x00, 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x40, 0x20, 0x18, 0x04, 0x02, 0x7c, 0x00, 0x00, 0x00, 0x00, 136 | 0x00, 0x00, 0x00, 0x10, 0x10, 0x7e, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x00, 137 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 138 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x24, 0x24, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x49, 0x49, 0x55, 0x63, 0x00, 0x00, 0x00, 0x00, 140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00, 0x00, 141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00, 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x0e, 0x10, 0x10, 0x10, 0x10, 0x10, 0xe0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e, 0x00, 0x00, 144 | 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 145 | 0x00, 0x70, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x70, 0x00, 0x00, 146 | 0x00, 0x00, 0x31, 0x49, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 148 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 150 | 0x00, 0x00, 0x08, 0x08, 0x1c, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1c, 0x08, 0x08, 0x00, 0x00, 0x00, 151 | 0x00, 0x00, 0x00, 0x1c, 0x22, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x72, 0x8c, 0x00, 0x00, 0x00, 0x00, 152 | 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x24, 0x24, 0x24, 0x3c, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, 0x3e, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 | 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x4d, 0x51, 0x51, 0x4d, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 158 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x24, 0x48, 0x24, 0x12, 0x09, 0x00, 0x00, 0x00, 0x00, 160 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 161 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 | 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 167 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x30, 0x00, 168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 170 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x24, 0x12, 0x09, 0x12, 0x24, 0x48, 0x00, 0x00, 0x00, 0x00, 171 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x42, 0x3c, 0x00, 174 | 0x20, 0x10, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 175 | 0x04, 0x08, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 176 | 0x18, 0x24, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 177 | 0x32, 0x4c, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 178 | 0x24, 0x24, 0x00, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 179 | 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x24, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 180 | 0x00, 0x00, 0x00, 0x0f, 0x14, 0x14, 0x24, 0x27, 0x3c, 0x44, 0x44, 0x47, 0x00, 0x00, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x1e, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x1e, 0x08, 0x08, 0x30, 0x00, 182 | 0x20, 0x10, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 183 | 0x04, 0x08, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 184 | 0x18, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 185 | 0x24, 0x24, 0x00, 0x7e, 0x40, 0x40, 0x40, 0x7c, 0x40, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x00, 0x00, 186 | 0x10, 0x08, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 187 | 0x04, 0x08, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 188 | 0x18, 0x24, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 189 | 0x22, 0x22, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 190 | 0x00, 0x00, 0x00, 0x3c, 0x22, 0x21, 0x21, 0x79, 0x21, 0x21, 0x22, 0x3c, 0x00, 0x00, 0x00, 0x00, 191 | 0x32, 0x4c, 0x00, 0x42, 0x62, 0x52, 0x4a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 192 | 0x10, 0x08, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 193 | 0x04, 0x08, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 194 | 0x18, 0x24, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 195 | 0x32, 0x4c, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 196 | 0x24, 0x24, 0x00, 0x1c, 0x22, 0x41, 0x41, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 197 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 198 | 0x00, 0x00, 0x02, 0x3c, 0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x42, 0x3c, 0x40, 0x00, 0x00, 0x00, 199 | 0x20, 0x10, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 200 | 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 201 | 0x18, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 202 | 0x24, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 203 | 0x04, 0x08, 0x00, 0x41, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 204 | 0x00, 0x00, 0x00, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 205 | 0x00, 0x00, 0x00, 0x3c, 0x42, 0x44, 0x4c, 0x42, 0x42, 0x42, 0x44, 0x58, 0x00, 0x00, 0x00, 0x00, 206 | 0x00, 0x00, 0x20, 0x10, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x04, 0x08, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 208 | 0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 209 | 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 210 | 0x00, 0x00, 0x24, 0x24, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 211 | 0x18, 0x24, 0x24, 0x18, 0x00, 0x3c, 0x02, 0x02, 0x3e, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 212 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x09, 0x39, 0x4f, 0x48, 0x48, 0x37, 0x00, 0x00, 0x00, 0x00, 213 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3c, 0x08, 0x08, 0x30, 0x00, 214 | 0x00, 0x00, 0x20, 0x10, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 215 | 0x00, 0x00, 0x04, 0x08, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 216 | 0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 217 | 0x00, 0x00, 0x24, 0x24, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00, 0x00, 218 | 0x00, 0x00, 0x10, 0x08, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 219 | 0x00, 0x00, 0x04, 0x08, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 220 | 0x00, 0x18, 0x24, 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 221 | 0x00, 0x00, 0x24, 0x24, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00, 0x00, 222 | 0x00, 0x09, 0x06, 0x1a, 0x01, 0x1d, 0x23, 0x41, 0x41, 0x41, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x00, 223 | 0x00, 0x32, 0x4c, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00, 0x00, 224 | 0x00, 0x00, 0x10, 0x08, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 225 | 0x00, 0x00, 0x04, 0x08, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 226 | 0x00, 0x18, 0x24, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 227 | 0x00, 0x32, 0x4c, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 228 | 0x00, 0x00, 0x24, 0x24, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00, 0x00, 229 | 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 230 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x3c, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x3c, 0x40, 0x00, 0x00, 0x00, 231 | 0x00, 0x00, 0x20, 0x10, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 232 | 0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 233 | 0x00, 0x18, 0x24, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 234 | 0x00, 0x00, 0x24, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00, 0x00, 235 | 0x00, 0x00, 0x04, 0x08, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00, 236 | 0x00, 0x00, 0x40, 0x40, 0x40, 0x5c, 0x62, 0x41, 0x41, 0x41, 0x62, 0x5c, 0x40, 0x40, 0x40, 0x00, 237 | 0x00, 0x00, 0x24, 0x24, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x3c, 0x00, 238 | 0xff, 0xff, 0xe2, 0x96, 0x92, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xb0, 0xff, 0xff, 0xff, 0xff, 239 | 0xe2, 0x94, 0x98, 0xff, 0xe2, 0x94, 0x90, 0xff, 0xe2, 0x94, 0x8c, 0xff, 0xe2, 0x94, 0x94, 0xff, 240 | 0xe2, 0x94, 0xbc, 0xff, 0xff, 0xff, 0xe2, 0x94, 0x80, 0xff, 0xff, 0xff, 0xe2, 0x94, 0x9c, 0xff, 241 | 0xe2, 0x94, 0xa4, 0xff, 0xe2, 0x94, 0xb4, 0xff, 0xe2, 0x94, 0xac, 0xff, 0xe2, 0x94, 0x82, 0xff, 242 | 0xff, 0xff, 0xff, 0xff, 0xc2, 0xa3, 0xff, 0xff, 0x20, 0xff, 0x21, 0xff, 0x22, 0xff, 0x23, 0xff, 243 | 0x24, 0xff, 0x25, 0xff, 0x26, 0xff, 0x27, 0xff, 0x28, 0xff, 0x29, 0xff, 0x2a, 0xff, 0x2b, 0xff, 244 | 0x2c, 0xff, 0x2d, 0xff, 0x2e, 0xff, 0x2f, 0xff, 0x30, 0xff, 0x31, 0xff, 0x32, 0xff, 0x33, 0xff, 245 | 0x34, 0xff, 0x35, 0xff, 0x36, 0xff, 0x37, 0xff, 0x38, 0xff, 0x39, 0xff, 0x3a, 0xff, 0x3b, 0xff, 246 | 0x3c, 0xff, 0x3d, 0xff, 0x3e, 0xff, 0x3f, 0xff, 0x40, 0xff, 0x41, 0xff, 0x42, 0xff, 0x43, 0xff, 247 | 0x44, 0xff, 0x45, 0xff, 0x46, 0xff, 0x47, 0xff, 0x48, 0xff, 0x49, 0xff, 0x4a, 0xff, 0x4b, 0xff, 248 | 0x4c, 0xff, 0x4d, 0xff, 0x4e, 0xff, 0x4f, 0xff, 0x50, 0xff, 0x51, 0xff, 0x52, 0xff, 0x53, 0xff, 249 | 0x54, 0xff, 0x55, 0xff, 0x56, 0xff, 0x57, 0xff, 0x58, 0xff, 0x59, 0xff, 0x5a, 0xff, 0x5b, 0xff, 250 | 0x5c, 0xff, 0x5d, 0xff, 0x5e, 0xff, 0x5f, 0xff, 0x60, 0xff, 0x61, 0xff, 0x62, 0xff, 0x63, 0xff, 251 | 0x64, 0xff, 0x65, 0xff, 0x66, 0xff, 0x67, 0xff, 0x68, 0xff, 0x69, 0xff, 0x6a, 0xff, 0x6b, 0xff, 252 | 0x6c, 0xff, 0x6d, 0xff, 0x6e, 0xff, 0x6f, 0xff, 0x70, 0xff, 0x71, 0xff, 0x72, 0xff, 0x73, 0xff, 253 | 0x74, 0xff, 0x75, 0xff, 0x76, 0xff, 0x77, 0xff, 0x78, 0xff, 0x79, 0xff, 0x7a, 0xff, 0x7b, 0xff, 254 | 0x7c, 0xff, 0x7d, 0xff, 0x7e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 255 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 256 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xa0, 0xff, 0xc2, 0xa1, 0xff, 0xc2, 0xa2, 0xff, 257 | 0xc2, 0xa3, 0xff, 0xc2, 0xa4, 0xff, 0xc2, 0xa5, 0xff, 0xc2, 0xa6, 0xff, 0xff, 0xc2, 0xa8, 0xff, 258 | 0xc2, 0xa9, 0xff, 0xff, 0xc2, 0xab, 0xff, 0xff, 0xc2, 0xad, 0xff, 0xff, 0xff, 0xc2, 0xb0, 0xff, 259 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc2, 0xb8, 0xff, 0xff, 0xff, 0xc2, 0xbb, 0xff, 0xff, 260 | 0xff, 0xff, 0xc2, 0xbf, 0xff, 0xc3, 0x80, 0xff, 0xc3, 0x81, 0xff, 0xc3, 0x82, 0xff, 0xc3, 0x83, 261 | 0xff, 0xc3, 0x84, 0xff, 0xc3, 0x85, 0xff, 0xc3, 0x86, 0xff, 0xc3, 0x87, 0xff, 0xc3, 0x88, 0xff, 262 | 0xc3, 0x89, 0xff, 0xc3, 0x8a, 0xff, 0xc3, 0x8b, 0xff, 0xc3, 0x8c, 0xff, 0xc3, 0x8d, 0xff, 0xc3, 263 | 0x8e, 0xff, 0xc3, 0x8f, 0xff, 0xc3, 0x90, 0xff, 0xc3, 0x91, 0xff, 0xc3, 0x92, 0xff, 0xc3, 0x93, 264 | 0xff, 0xc3, 0x94, 0xff, 0xc3, 0x95, 0xff, 0xc3, 0x96, 0xff, 0xc3, 0x97, 0xff, 0xc3, 0x98, 0xff, 265 | 0xc3, 0x99, 0xff, 0xc3, 0x9a, 0xff, 0xc3, 0x9b, 0xff, 0xc3, 0x9c, 0xff, 0xc3, 0x9d, 0xff, 0xc3, 266 | 0x9e, 0xff, 0xc3, 0x9f, 0xff, 0xc3, 0xa0, 0xff, 0xc3, 0xa1, 0xff, 0xc3, 0xa2, 0xff, 0xc3, 0xa3, 267 | 0xff, 0xc3, 0xa4, 0xff, 0xc3, 0xa5, 0xff, 0xc3, 0xa6, 0xff, 0xc3, 0xa7, 0xff, 0xc3, 0xa8, 0xff, 268 | 0xc3, 0xa9, 0xff, 0xc3, 0xaa, 0xff, 0xc3, 0xab, 0xff, 0xc3, 0xac, 0xff, 0xc3, 0xad, 0xff, 0xc3, 269 | 0xae, 0xff, 0xc3, 0xaf, 0xff, 0xc3, 0xb0, 0xff, 0xc3, 0xb1, 0xff, 0xc3, 0xb2, 0xff, 0xc3, 0xb3, 270 | 0xff, 0xc3, 0xb4, 0xff, 0xc3, 0xb5, 0xff, 0xc3, 0xb6, 0xff, 0xc3, 0xb7, 0xff, 0xc3, 0xb8, 0xff, 271 | 0xc3, 0xb9, 0xff, 0xc3, 0xba, 0xff, 0xc3, 0xbb, 0xff, 0xc3, 0xbc, 0xff, 0xc3, 0xbd, 0xff, 0xc3, 272 | 0xbe, 0xff, 0xc3, 0xbf, 0xff}; -------------------------------------------------------------------------------- /src/include/kernel/acpi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct rsdp_ptr_1 7 | { 8 | char signature[8]; 9 | uint8_t checksum; 10 | char oemid[6]; 11 | uint8_t revision; 12 | uint32_t rsdt_addr; 13 | } __attribute__((packed)); 14 | 15 | /* all the System Description Tables have the 16 | * following header */ 17 | struct sdt_header 18 | { 19 | char signature[4]; 20 | uint32_t len; 21 | uint8_t revision; 22 | uint8_t checksum; 23 | char oem_id[6]; 24 | char oem_table_id[8]; 25 | uint32_t oem_revision; 26 | uint32_t creator_id; 27 | uint32_t creator_revision; 28 | } __attribute__((packed)); 29 | 30 | struct rsdt_ptr 31 | { 32 | struct sdt_header sdt_head; 33 | uint32_t other_sdt_ptr[]; 34 | } __attribute__((packed)); 35 | 36 | /* ACPI describe registers as following */ 37 | struct gen_addr_struct 38 | { 39 | uint8_t addr_space; 40 | uint8_t bit_width; 41 | uint8_t bit_offset; 42 | uint8_t access_size; 43 | uint64_t addr; 44 | }; 45 | 46 | /* simplified from https://wiki.osdev.org/FADT */ 47 | struct fadt_ptr 48 | { 49 | struct sdt_header sdt_head; 50 | uint32_t firmware_ctrl; 51 | uint32_t dsdt_addr; 52 | 53 | uint8_t reserved_1; 54 | 55 | uint8_t power_man_profile; 56 | uint16_t sci_interrupt; 57 | uint32_t smi_command_port; 58 | uint8_t acpi_enable; 59 | uint8_t acpi_disable; 60 | uint8_t S4BIOS_REQ; 61 | uint8_t PSTATE_control; 62 | uint32_t PM1a_event_block; 63 | uint32_t PM1b_event_block; 64 | uint32_t PM1a_control_block; 65 | uint32_t PM1b_control_block; 66 | uint32_t PM2_control_block; 67 | uint32_t PM_timer_block; 68 | uint32_t GPE0_block; 69 | uint32_t GPE1_block; 70 | uint8_t PM1_event_len; 71 | uint8_t PM1_control_len; 72 | uint8_t PM2_control_len; 73 | uint8_t PM_timer_len; 74 | uint8_t GPE0_len; 75 | uint8_t GPE1_len; 76 | uint8_t GPE1_base; 77 | uint8_t C_state_control; 78 | uint16_t worst_C2_latency; 79 | uint16_t worst_C3_latency; 80 | uint16_t flush_size; 81 | uint16_t flush_stride; 82 | uint8_t duty_offset; 83 | uint8_t duty_width; 84 | 85 | uint8_t day_alarm; 86 | uint8_t month_alarm; 87 | uint8_t century; 88 | 89 | uint16_t reserved_boot_arch_flags; /* reserved in ACPI 1.0 */ 90 | 91 | uint8_t reserved_2; 92 | uint32_t flags; 93 | 94 | struct gen_addr_struct reset_reg; 95 | 96 | uint8_t reset_value; 97 | uint8_t reserved_3[3]; 98 | } __attribute__((packed)); 99 | 100 | void init_acpi(struct stivale2_struct_tag_rsdp *rsdp_tag); 101 | void acpi_shutdown(); -------------------------------------------------------------------------------- /src/include/kernel/gdt.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct gdt_descriptor 4 | { 5 | uint16_t size; 6 | uint64_t base; 7 | } __attribute__((packed)) gdt_descriptor_t; 8 | 9 | typedef struct gdt_entry 10 | { 11 | uint16_t low_limit; 12 | uint16_t low_base; 13 | uint8_t mid_base; 14 | uint8_t access; 15 | uint8_t limit_flags; 16 | uint8_t hig_base; 17 | } __attribute__((packed)) gdt_entry_t; 18 | 19 | typedef struct gdt 20 | { 21 | gdt_entry_t null; 22 | gdt_entry_t kernel_code; 23 | gdt_entry_t kernel_data; 24 | gdt_entry_t user_code; 25 | gdt_entry_t user_data; 26 | } __attribute__((packed)) gdt_t; 27 | 28 | extern void gdt_load(gdt_descriptor_t *gdt_descriptor); 29 | 30 | void init_gdt(); -------------------------------------------------------------------------------- /src/include/kernel/idt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | typedef struct idt_entry 5 | { /* in this case, we can call this an entry or a gate; IDT entries are called gates */ 6 | uint16_t isr_low; 7 | uint16_t kernel_cs; 8 | uint8_t ist; 9 | uint8_t attributes; 10 | uint16_t isr_mid; 11 | uint32_t isr_hig; 12 | uint32_t reserved; 13 | } __attribute__((packed)) idt_entry_t; 14 | 15 | typedef struct 16 | { /* lidt instruction will load this into the IDTR(egister) */ 17 | uint16_t size; 18 | uint64_t base; 19 | } __attribute__((packed)) idtr_t; 20 | 21 | void idt_set_entry(uint16_t n, uint64_t handler); 22 | void init_idt(); -------------------------------------------------------------------------------- /src/include/kernel/irq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PIC1 0x20 /* master PIC */ 6 | #define PIC2 0xA0 /* slave PIC */ 7 | #define PIC1_DATA (PIC1 + 1) 8 | #define PIC2_DATA (PIC2 + 1) 9 | #define PIC_EOI 0x20 /* end of interrupt */ 10 | #define IRQ_BASE 0x20 11 | 12 | #define IRQ0 32 13 | #define IRQ1 33 14 | #define IRQ2 34 15 | 16 | /* see __int.asm */ 17 | extern void irq0(); 18 | extern void irq1(); 19 | extern void irq2(); 20 | 21 | void init_irq(); 22 | void irq_set_handler(uint16_t irq, void(*handler)); -------------------------------------------------------------------------------- /src/include/kernel/isr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* see __int.asm */ 6 | extern void isr0(); 7 | extern void isr1(); 8 | extern void isr2(); 9 | extern void isr3(); 10 | extern void isr4(); 11 | extern void isr5(); 12 | extern void isr6(); 13 | extern void isr7(); 14 | extern void isr8(); 15 | extern void isr9(); 16 | extern void isr10(); 17 | extern void isr11(); 18 | extern void isr12(); 19 | extern void isr13(); 20 | extern void isr14(); 21 | extern void isr15(); 22 | extern void isr16(); 23 | extern void isr17(); 24 | extern void isr18(); 25 | extern void isr19(); 26 | extern void isr20(); 27 | extern void isr21(); 28 | extern void isr22(); 29 | extern void isr23(); 30 | extern void isr24(); 31 | extern void isr25(); 32 | extern void isr26(); 33 | extern void isr27(); 34 | extern void isr28(); 35 | extern void isr29(); 36 | extern void isr30(); 37 | extern void isr31(); 38 | 39 | void init_isr(); -------------------------------------------------------------------------------- /src/include/kernel/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void panic(const char *panic_msg); -------------------------------------------------------------------------------- /src/include/kernel/pci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define CONFIG_ADDRESS 0xCF8 /* these are I/O locations */ 6 | #define CONFIG_DATA 0xCFC 7 | 8 | /* CONFIG_ADDRESS is mapped to a 32-bit register */ 9 | /* with the following structure */ 10 | 11 | /* Bit 31 Bits 30-24 Bits 23-16 Bits 15-11 Bits 10-8 Bits 7-0 */ 12 | /* Enable Bit Reserved Bus Number Device Number Function Number Register Offset */ 13 | 14 | /* brief explanation 15 | * 31. configuration flag 16 | * 23-16. choose an specific bus address 17 | * 15-11. select a device attached to the bus 18 | * 10-8. choose an specific function of the device 19 | */ 20 | 21 | typedef struct dev 22 | { 23 | uint16_t vendor; 24 | uint16_t dev; 25 | uint16_t class; 26 | uint16_t subclass; 27 | uint32_t ioaddr; 28 | uint32_t abar; 29 | } dev_t; 30 | 31 | typedef struct class 32 | { 33 | const char *class_name; 34 | const char *subclass_name; 35 | } class_t; 36 | 37 | void init_pci(); -------------------------------------------------------------------------------- /src/include/kernel/ports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | unsigned char port_byte_in(unsigned short port); 6 | void port_byte_out(unsigned short port, unsigned char data); 7 | unsigned short port_word_in(unsigned short port); 8 | void port_word_out(unsigned short port, unsigned short data); 9 | uint32_t port_long_in(uint32_t port); 10 | void port_long_out(uint32_t port, uint32_t data); -------------------------------------------------------------------------------- /src/include/kernel/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void init_timer(int freq); -------------------------------------------------------------------------------- /src/include/math/math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int pow(int a, int b); -------------------------------------------------------------------------------- /src/include/mem/kheap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void *kmalloc(size_t size); -------------------------------------------------------------------------------- /src/include/mem/phys.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PAGE_SIZE 4096 6 | #define CHECK_PAGE(page) (phys_bitmap[page / 8] & (1 << (page % 8))) 7 | 8 | extern uint8_t *phys_bitmap; 9 | 10 | void init_phys(struct stivale2_struct_tag_memmap *mem_tag); -------------------------------------------------------------------------------- /src/include/mem/virt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define HIGHER_HALF 0xffff800000000000 6 | #define KERNEL_OFFS 0xffffffff80000000 7 | 8 | extern uint64_t *kernel_page_map; 9 | 10 | void virt_load_page_map(uint64_t pml4); 11 | void virt_map_multi(uint64_t *page_map, uint64_t base, uint64_t end, uint64_t offset, uint64_t flags); 12 | void init_virt(); -------------------------------------------------------------------------------- /src/include/stivale2.h: -------------------------------------------------------------------------------- 1 | #ifndef __STIVALE2_H__ 2 | #define __STIVALE2_H__ 3 | 4 | #include 5 | 6 | struct stivale2_tag { 7 | uint64_t identifier; 8 | uint64_t next; 9 | } __attribute__((__packed__)); 10 | 11 | /* --- Header --------------------------------------------------------------- */ 12 | /* Information passed from the kernel to the bootloader */ 13 | 14 | struct stivale2_header { 15 | uint64_t entry_point; 16 | uint64_t stack; 17 | uint64_t flags; 18 | uint64_t tags; 19 | } __attribute__((__packed__)); 20 | 21 | #define STIVALE2_HEADER_TAG_FRAMEBUFFER_ID 0x3ecc1bc43d0f7971 22 | 23 | struct stivale2_header_tag_framebuffer { 24 | struct stivale2_tag tag; 25 | uint16_t framebuffer_width; 26 | uint16_t framebuffer_height; 27 | uint16_t framebuffer_bpp; 28 | } __attribute__((__packed__)); 29 | 30 | #define STIVALE2_HEADER_TAG_FB_MTRR_ID 0x4c7bb07731282e00 31 | 32 | #define STIVALE2_HEADER_TAG_TERMINAL_ID 0xa85d499b1823be72 33 | 34 | struct stivale2_header_tag_terminal { 35 | struct stivale2_tag tag; 36 | uint64_t flags; 37 | } __attribute__((__packed__)); 38 | 39 | #define STIVALE2_HEADER_TAG_SMP_ID 0x1ab015085f3273df 40 | 41 | struct stivale2_header_tag_smp { 42 | struct stivale2_tag tag; 43 | uint64_t flags; 44 | } __attribute__((__packed__)); 45 | 46 | #define STIVALE2_HEADER_TAG_5LV_PAGING_ID 0x932f477032007e8f 47 | 48 | #define STIVALE2_HEADER_TAG_UNMAP_NULL_ID 0x92919432b16fe7e7 49 | 50 | /* --- Struct --------------------------------------------------------------- */ 51 | /* Information passed from the bootloader to the kernel */ 52 | 53 | struct stivale2_struct { 54 | #define STIVALE2_BOOTLOADER_BRAND_SIZE 64 55 | char bootloader_brand[STIVALE2_BOOTLOADER_BRAND_SIZE]; 56 | 57 | #define STIVALE2_BOOTLOADER_VERSION_SIZE 64 58 | char bootloader_version[STIVALE2_BOOTLOADER_VERSION_SIZE]; 59 | 60 | uint64_t tags; 61 | } __attribute__((__packed__)); 62 | 63 | #define STIVALE2_STRUCT_TAG_CMDLINE_ID 0xe5e76a1b4597a781 64 | 65 | struct stivale2_struct_tag_cmdline { 66 | struct stivale2_tag tag; 67 | uint64_t cmdline; 68 | } __attribute__((__packed__)); 69 | 70 | #define STIVALE2_STRUCT_TAG_MEMMAP_ID 0x2187f79e8612de07 71 | 72 | #define STIVALE2_MMAP_USABLE 1 73 | #define STIVALE2_MMAP_RESERVED 2 74 | #define STIVALE2_MMAP_ACPI_RECLAIMABLE 3 75 | #define STIVALE2_MMAP_ACPI_NVS 4 76 | #define STIVALE2_MMAP_BAD_MEMORY 5 77 | #define STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE 0x1000 78 | #define STIVALE2_MMAP_KERNEL_AND_MODULES 0x1001 79 | #define STIVALE2_MMAP_FRAMEBUFFER 0x1002 80 | 81 | struct stivale2_mmap_entry { 82 | uint64_t base; 83 | uint64_t length; 84 | uint32_t type; 85 | uint32_t unused; 86 | } __attribute__((__packed__)); 87 | 88 | struct stivale2_struct_tag_memmap { 89 | struct stivale2_tag tag; 90 | uint64_t entries; 91 | struct stivale2_mmap_entry memmap[]; 92 | } __attribute__((__packed__)); 93 | 94 | #define STIVALE2_STRUCT_TAG_FRAMEBUFFER_ID 0x506461d2950408fa 95 | 96 | #define STIVALE2_FBUF_MMODEL_RGB 1 97 | 98 | struct stivale2_struct_tag_framebuffer { 99 | struct stivale2_tag tag; 100 | uint64_t framebuffer_addr; 101 | uint16_t framebuffer_width; 102 | uint16_t framebuffer_height; 103 | uint16_t framebuffer_pitch; 104 | uint16_t framebuffer_bpp; 105 | uint8_t memory_model; 106 | uint8_t red_mask_size; 107 | uint8_t red_mask_shift; 108 | uint8_t green_mask_size; 109 | uint8_t green_mask_shift; 110 | uint8_t blue_mask_size; 111 | uint8_t blue_mask_shift; 112 | } __attribute__((__packed__)); 113 | 114 | #define STIVALE2_STRUCT_TAG_EDID_ID 0x968609d7af96b845 115 | 116 | struct stivale2_struct_tag_edid { 117 | struct stivale2_tag tag; 118 | uint64_t edid_size; 119 | uint8_t edid_information[]; 120 | } __attribute__((__packed__)); 121 | 122 | #define STIVALE2_STRUCT_TAG_FB_MTRR_ID 0x6bc1a78ebe871172 123 | 124 | #define STIVALE2_STRUCT_TAG_TERMINAL_ID 0xc2b3f4c3233b0974 125 | 126 | struct stivale2_struct_tag_terminal { 127 | struct stivale2_tag tag; 128 | uint32_t flags; 129 | uint16_t cols; 130 | uint16_t rows; 131 | uint64_t term_write; 132 | } __attribute__((__packed__)); 133 | 134 | #define STIVALE2_STRUCT_TAG_MODULES_ID 0x4b6fe466aade04ce 135 | 136 | struct stivale2_module { 137 | uint64_t begin; 138 | uint64_t end; 139 | 140 | #define STIVALE2_MODULE_STRING_SIZE 128 141 | char string[STIVALE2_MODULE_STRING_SIZE]; 142 | } __attribute__((__packed__)); 143 | 144 | struct stivale2_struct_tag_modules { 145 | struct stivale2_tag tag; 146 | uint64_t module_count; 147 | struct stivale2_module modules[]; 148 | } __attribute__((__packed__)); 149 | 150 | #define STIVALE2_STRUCT_TAG_RSDP_ID 0x9e1786930a375e78 151 | 152 | struct stivale2_struct_tag_rsdp { 153 | struct stivale2_tag tag; 154 | uint64_t rsdp; 155 | } __attribute__((__packed__)); 156 | 157 | #define STIVALE2_STRUCT_TAG_EPOCH_ID 0x566a7bed888e1407 158 | 159 | struct stivale2_struct_tag_epoch { 160 | struct stivale2_tag tag; 161 | uint64_t epoch; 162 | } __attribute__((__packed__)); 163 | 164 | #define STIVALE2_STRUCT_TAG_FIRMWARE_ID 0x359d837855e3858c 165 | 166 | #define STIVALE2_FIRMWARE_BIOS (1 << 0) 167 | 168 | struct stivale2_struct_tag_firmware { 169 | struct stivale2_tag tag; 170 | uint64_t flags; 171 | } __attribute__((__packed__)); 172 | 173 | #define STIVALE2_STRUCT_TAG_EFI_SYSTEM_TABLE_ID 0x4bc5ec15845b558e 174 | 175 | struct stivale2_struct_tag_efi_system_table { 176 | struct stivale2_tag tag; 177 | uint64_t system_table; 178 | } __attribute__((__packed__)); 179 | 180 | #define STIVALE2_STRUCT_TAG_KERNEL_FILE_ID 0xe599d90c2975584a 181 | 182 | struct stivale2_struct_tag_kernel_file { 183 | struct stivale2_tag tag; 184 | uint64_t kernel_file; 185 | } __attribute__((__packed__)); 186 | 187 | #define STIVALE2_STRUCT_TAG_KERNEL_SLIDE_ID 0xee80847d01506c57 188 | 189 | struct stivale2_struct_tag_kernel_slide { 190 | struct stivale2_tag tag; 191 | uint64_t kernel_slide; 192 | } __attribute__((packed)); 193 | 194 | #define STIVALE2_STRUCT_TAG_SMBIOS_ID 0x274bd246c62bf7d1 195 | 196 | struct stivale2_struct_tag_smbios { 197 | struct stivale2_tag tag; 198 | uint64_t flags; 199 | uint64_t smbios_entry_32; 200 | uint64_t smbios_entry_64; 201 | } __attribute__((packed)); 202 | 203 | #define STIVALE2_STRUCT_TAG_SMP_ID 0x34d1d96339647025 204 | 205 | struct stivale2_smp_info { 206 | uint32_t processor_id; 207 | uint32_t lapic_id; 208 | uint64_t target_stack; 209 | uint64_t goto_address; 210 | uint64_t extra_argument; 211 | } __attribute__((__packed__)); 212 | 213 | struct stivale2_struct_tag_smp { 214 | struct stivale2_tag tag; 215 | uint64_t flags; 216 | uint32_t bsp_lapic_id; 217 | uint32_t unused; 218 | uint64_t cpu_count; 219 | struct stivale2_smp_info smp_info[]; 220 | } __attribute__((__packed__)); 221 | 222 | #define STIVALE2_STRUCT_TAG_PXE_SERVER_INFO 0x29d1e96239247032 223 | 224 | struct stivale2_struct_tag_pxe_server_info { 225 | struct stivale2_tag tag; 226 | uint32_t server_ip; 227 | } __attribute__((__packed__)); 228 | 229 | #define STIVALE2_STRUCT_TAG_MMIO32_UART 0xb813f9b8dbc78797 230 | 231 | struct stivale2_struct_tag_mmio32_uart { 232 | struct stivale2_tag tag; 233 | uint64_t addr; 234 | } __attribute__((__packed__)); 235 | 236 | #define STIVALE2_STRUCT_TAG_DTB 0xabb29bd49a2833fa 237 | 238 | struct stivale2_struct_tag_dtb { 239 | struct stivale2_tag tag; 240 | uint64_t addr; 241 | uint64_t size; 242 | } __attribute__((__packed__)); 243 | 244 | #define STIVALE2_STRUCT_TAG_VMAP 0xb0ed257db18cb58f 245 | 246 | struct stivale2_struct_vmap { 247 | struct stivale2_tag tag; 248 | uint64_t addr; 249 | } __attribute__((__packed__)); 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /src/include/string/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void kprintf(const char *fmt, ...); 6 | void itoa(char base, unsigned long i); 7 | int strcmp(char *s1, char *s2); 8 | int strncmp(char *s1, char *s2, int n); 9 | void *memset(void *buffptr, int value, size_t size); 10 | void *kmemcpy(void *dstptr, void *srcptr, size_t size); -------------------------------------------------------------------------------- /src/include/task/sched.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void load_task(task_t *task); 6 | void schedule(regs_t *regs); 7 | void init_sched(); -------------------------------------------------------------------------------- /src/include/task/task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define TASK_STOPPED 1 7 | #define TASK_RUNNING 2 8 | #define TASK_NO_INIT 3 9 | 10 | #define FLAG_USER 0b01 11 | #define FLAG_KERN 0b10 12 | 13 | #define STACK_SIZE 0x10000 14 | 15 | /* a task is a process, for now, we are not going to differentiate 16 | * between task and thread 17 | * 18 | * each task ought to have its own pid (process id), its own registers 19 | * (each time we change tasks, we need to load their new registers) and 20 | * its own pagemap in order to have its own virtual space 21 | * 22 | * so as to get several tasks running (ostensibly), what we are going to do is give 23 | * them a CPU time, that is, one task will run for some milliseconds, and 24 | * then we will change to the next one */ 25 | typedef struct task 26 | { 27 | int pid; 28 | int status; 29 | 30 | regs_t regs; 31 | uint64_t *page_map; 32 | uint8_t flags; 33 | } task_t; 34 | 35 | void create_task(uint64_t ip, uint8_t flags); -------------------------------------------------------------------------------- /src/kernel/__gdt.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | global gdt_load 3 | 4 | ; 0x00 : null 5 | ; 0x08 : kernel code 6 | ; 0x10 : kernel data 7 | 8 | gdt_load: 9 | lgdt [rdi] ; load the GDT given as a function parameter 10 | mov ax, 0x10 11 | mov ds, ax 12 | mov es, ax 13 | mov fs, ax 14 | mov gs, ax 15 | mov ss, ax 16 | pop rdi 17 | mov rax, 0x08 18 | push rax 19 | push rdi 20 | retfq -------------------------------------------------------------------------------- /src/kernel/__int.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | ; keep in mind that 4 | ; cli: clears the interrupt flag 5 | ; iret: returns from an interrupt 6 | 7 | extern isr_handler 8 | extern irq_handler 9 | global interrupt 10 | 11 | ; with the IDT we will tell the CPU that we want it to jump 12 | ; to the isr0 (and so on) label when interrupt 0 is fired 13 | 14 | ; the isr0 label (isr%1, where %1 = 0) will push the interrupt 15 | ; id (push %1, where variable %1 = 0) to the stack and call the handler 16 | 17 | ; pushing to the stack is equivalent to passing function parameters 18 | ; see isr.c, the isr handler accepts an id parameter (same for IRQs) 19 | 20 | %macro def_isr 1 21 | global isr%1 22 | isr%1: 23 | cli 24 | push 0 25 | push %1 26 | jmp isr_stub 27 | %endmacro 28 | 29 | %macro def_irq 1 30 | global irq%1 31 | irq%1: 32 | cli 33 | push 0 34 | push (%1 + 32) 35 | jmp irq_stub 36 | %endmacro 37 | 38 | %macro push_regs 0 39 | push rax 40 | push rbx 41 | push rcx 42 | push rdx 43 | push rsi 44 | push rdi 45 | push rbp 46 | push r8 47 | push r9 48 | push r10 49 | push r11 50 | push r12 51 | push r13 52 | push r14 53 | push r15 54 | mov rsi, rsp 55 | 56 | mov rdi, [rsp + 120] 57 | mov rsi, rsp 58 | %endmacro 59 | 60 | %macro pop_regs 0 61 | pop r15 62 | pop r14 63 | pop r13 64 | pop r12 65 | pop r11 66 | pop r10 67 | pop r9 68 | pop r8 69 | pop rbp 70 | pop rdi 71 | pop rsi 72 | pop rdx 73 | pop rcx 74 | pop rbx 75 | pop rax 76 | 77 | add rsp, 16 78 | %endmacro 79 | 80 | isr_stub: 81 | push_regs 82 | call isr_handler 83 | pop_regs 84 | 85 | iretq 86 | 87 | irq_stub: 88 | push_regs 89 | call irq_handler 90 | pop_regs 91 | 92 | iretq 93 | 94 | def_isr 0 95 | def_isr 1 96 | def_isr 2 97 | def_isr 3 98 | def_isr 4 99 | def_isr 5 100 | def_isr 6 101 | def_isr 7 102 | def_isr 8 103 | def_isr 9 104 | def_isr 10 105 | def_isr 11 106 | def_isr 12 107 | def_isr 13 108 | def_isr 14 109 | def_isr 15 110 | def_isr 16 111 | def_isr 17 112 | def_isr 18 113 | def_isr 19 114 | def_isr 20 115 | def_isr 21 116 | def_isr 22 117 | def_isr 23 118 | def_isr 24 119 | def_isr 25 120 | def_isr 26 121 | def_isr 27 122 | def_isr 28 123 | def_isr 29 124 | def_isr 30 125 | def_isr 31 126 | 127 | def_irq 0 ; 32. system timer 128 | def_irq 1 ; 33. keyboard 129 | def_irq 2 ; 34. PIC cascading (never raised) -------------------------------------------------------------------------------- /src/kernel/acpi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | /* ACPI is a power management standard and its information is in the BIOS memory 8 | * 9 | * to begin using ACPI we need to find the RSDP (Root System Description Pointer), 10 | * the RSDP is a data structure where we will find information about 11 | * 12 | * - ACPI version or revision (1.0 or 2.0) that the machine uses 13 | * - the RSDT table address (refer to https://wiki.osdev.org/RSDT) 14 | * - the XSDT table address (only for ACPI 2.0) 15 | * 16 | * the RSDT is a data structure also used in the ACPI that contains pointers to 17 | * the other System Description Tables (that also contain information about the system) 18 | */ 19 | 20 | /* limine, the bootloader we are using, uses the stivale protocol, we can easily 21 | * get the RSDP pointer 22 | * 23 | * from https://github.com/stivale/stivale/blob/master/STIVALE2.md#rsdp-structure-tag 24 | * 25 | * struct stivale2_struct_tag_rsdp { 26 | * struct stivale2_tag tag; // Identifier: 0x9e1786930a375e78 27 | * uint64_t rsdp; // Pointer to the ACPI RSDP structure 28 | * }; 29 | */ 30 | 31 | static struct rsdp_ptr_1 *rsdp = 0; 32 | static struct rsdt_ptr *rsdt = 0; 33 | static struct fadt_ptr *fadt = 0; 34 | 35 | /* the FADT data structure is the ACPI table that contains information 36 | * about fixed register blocks that have to do with power management 37 | * 38 | * as we explained before, the RSDT contains other tables, one of them will 39 | * be the FADT, keep in mind the FADT signature is FACP 40 | */ 41 | struct fadt_ptr *get_fadt() 42 | { 43 | int entries = (rsdt->sdt_head.len - sizeof(rsdt->sdt_head)) / 4; 44 | for (int i = 0; i < entries; i++) 45 | { 46 | struct sdt_header *header = (struct sdt_header *)(uint64_t)rsdt->other_sdt_ptr[i]; 47 | /* the signature seems not to be a NULL terminated string, so we need to compare 48 | * a fixed amount of characters */ 49 | if (!strncmp(header->signature, "FACP", 4)) 50 | return (struct fadt_ptr *)header; 51 | } 52 | return 0; 53 | } 54 | 55 | uint64_t get_SLP_TYPa() 56 | { 57 | /* this is not a good idea at all, since it is a hardcoded version 58 | * of AML (ACPI Machine Language) parsing (see https://wiki.osdev.org/AML) */ 59 | struct sdt_header *dsdt_addr = (struct sdt_header *)(uint64_t)fadt->dsdt_addr; 60 | 61 | /* if the signature is not DSDT */ 62 | if (strncmp(dsdt_addr->signature, "DSDT", 4)) 63 | panic("[ACPI] DSDT header not found\n"); 64 | 65 | char *S5_addr = (char *)(dsdt_addr + sizeof(struct sdt_header)); 66 | int dsdt_len = dsdt_addr->len; 67 | while (dsdt_len-- > 0) 68 | { 69 | if (!strncmp(S5_addr, "_S5_", 4)) 70 | break; /* found it */ 71 | S5_addr++; /* or move the pointer */ 72 | } 73 | if (dsdt_len <= 0) 74 | panic("[ACPI] S5 not present\n"); 75 | 76 | S5_addr += 5; 77 | S5_addr += ((*S5_addr & 0xC0) >> 6) + 2; 78 | if (*S5_addr == 0xA) /* byte prefix */ 79 | S5_addr++; 80 | return *(S5_addr) << 10; 81 | } 82 | 83 | void acpi_shutdown() 84 | { 85 | port_word_out(fadt->PM1a_control_block, (get_SLP_TYPa()) | (1 << 13)); 86 | port_word_out(0xB004, 0x2000); /* Bochs and old versions of QEMU */ 87 | port_word_out(0x604, 0x2000); /* QEMU */ 88 | port_word_out(0x4004, 0x3400); /* VBox */ 89 | 90 | panic("[ACPI] shutdown failed\n"); 91 | } 92 | 93 | void init_acpi(struct stivale2_struct_tag_rsdp *rsdp_tag) 94 | { 95 | rsdp = (struct rsdp_ptr_1 *)rsdp_tag->rsdp; 96 | rsdt = (struct rsdt_ptr *)(uint64_t)rsdp->rsdt_addr; 97 | fadt = get_fadt(); 98 | 99 | if (!rsdp) 100 | panic("[ACPI] rsdp lookup failed\n"); 101 | else if (!rsdt) 102 | panic("[ACPI] rsdt lookup failed\n"); 103 | else if (!fadt) 104 | panic("[ACPI] fadt lookup failed\n"); 105 | 106 | /* print their memory addresses */ 107 | kprintf("[ACPI] rsdp = 0x%x, rsdt = 0x%lx, fadt = 0x%lx\n", rsdp, rsdt, fadt); 108 | } -------------------------------------------------------------------------------- /src/kernel/gdt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* the GDT that we will load (see init_gdt() and __gdt.asm) */ 4 | gdt_t GDT = { 5 | {0, 0, 0, 0x00, 0x00 << 4, 0}, /* gdt_entry null; */ 6 | {0, 0, 0, 0x9A, 0x02 << 4, 0}, /* gdt_entry kernel_code; */ 7 | {0, 0, 0, 0x92, 0x00 << 4, 0}, /* gdt_entry kernel_data; */ 8 | {0, 0, 0, 0xFA, 0x02 << 4, 0}, /* gdt_entry user_code; */ 9 | {0, 0, 0, 0xF2, 0x00 << 4, 0} /* gdt_entry user_data; */ 10 | }; 11 | /* refer to https://wiki.osdev.org/Global_Descriptor_Table#Segment_Descriptor 12 | * for a better diagram of the segment descriptor and a general explanation 13 | * 14 | * GDT.access is a byte with the following structure 15 | * 16 | * | Pr | Privl | S | Ex | DC | RW | Ac | 17 | * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 18 | * 19 | * 0x9A | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 20 | * 0x92 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 21 | * 0xFA | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 22 | * 0xF2 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 23 | * 24 | * where Privl is the privilege level, 00 is 0, thus, ring 0 25 | * 11 is 3, thus, ring 3 (less privilege) 26 | * 27 | * where Ex, if set to 1, defines a code segment, if set to 0, defines a data segment 28 | * 29 | * GDT.(limit_)flags (also granularity) has four usable bits, because it forms 30 | * part of a byte that shares with GDT.limit(_flags), where limit is the high_limit 31 | * (compared to GDT.low_limit) 32 | * ___________________ _______________ 33 | * |_______flags_______|___high limit__| 34 | * | Gr | Sz | L | Res | | 35 | * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 36 | * 37 | * 0x02<<4 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 38 | * 39 | * where L is the Long-mode flag (64-bit code segment) 40 | */ 41 | static gdt_descriptor_t gdt_descriptor; 42 | 43 | void init_gdt() 44 | { 45 | gdt_descriptor.size = sizeof(gdt_t) - 1; 46 | gdt_descriptor.base = (uint64_t)&GDT; /* the base address of our GDT */ 47 | gdt_load(&gdt_descriptor); 48 | } -------------------------------------------------------------------------------- /src/kernel/idt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* remember that IDT stands for Interrupt Descriptor Table 5 | the CPU will use this to know what to do when an interrupt is fired */ 6 | 7 | __attribute__((aligned(0x10))) /* alignment is a matter of performance */ 8 | static idt_entry_t idt[256]; /* define a new IDT with 256 gates/entries */ 9 | static idtr_t idtr; 10 | 11 | /* this function will be called to define the ISRs and IRQs */ 12 | void idt_set_entry(uint16_t n, uint64_t handler) 13 | { 14 | idt[n].isr_low = (uint16_t)handler; 15 | idt[n].isr_mid = (uint16_t)(handler >> 16); 16 | idt[n].isr_hig = (uint32_t)(handler >> 32); 17 | idt[n].kernel_cs = 0x08; /* this is the selector offset, see GDT */ 18 | idt[n].attributes = (1 << 7) /* present */ | (1 << 5) /* ring 0 */ | 0xE /* interrupt */; 19 | } 20 | 21 | void init_idt() 22 | { 23 | idtr.base = (uint64_t)&idt; 24 | idtr.size = 256 * sizeof(idt_entry_t) - 1; 25 | asm("lidt %0" /* see idt.h */ 26 | : 27 | : "m"(idtr)); 28 | } -------------------------------------------------------------------------------- /src/kernel/irq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void (*irq_handlers[256])(); /* different handlers (eg. kb) are stored here */ 7 | 8 | void init_irq() 9 | { 10 | port_byte_out(PIC1, 0x11); 11 | port_byte_out(PIC2, 0x11); 12 | 13 | port_byte_out(PIC1_DATA, IRQ_BASE); 14 | port_byte_out(PIC2_DATA, IRQ_BASE + 8); 15 | 16 | port_byte_out(PIC1_DATA, 0x04); 17 | port_byte_out(PIC2_DATA, 0x02); 18 | 19 | port_byte_out(PIC1_DATA, 0x01); 20 | port_byte_out(PIC2_DATA, 0x01); 21 | 22 | port_byte_out(PIC1_DATA, 0x00); 23 | port_byte_out(PIC2_DATA, 0x00); 24 | 25 | idt_set_entry(32, (uint64_t)irq0); 26 | idt_set_entry(33, (uint64_t)irq1); 27 | idt_set_entry(34, (uint64_t)irq2); 28 | 29 | asm("sti"); /* set the interrupt flag */ 30 | } 31 | 32 | void irq_handler(uint64_t irq, regs_t *regs) 33 | { 34 | if (irq >= 40) 35 | port_byte_out(PIC2, PIC_EOI); 36 | 37 | if (irq == 32) 38 | schedule(regs); 39 | 40 | void (*handler)() = irq_handlers[irq]; 41 | 42 | if (handler) 43 | handler(); 44 | 45 | port_byte_out(PIC1, PIC_EOI); 46 | } 47 | 48 | void irq_set_handler(uint16_t irq, void(*handler)) 49 | { 50 | irq_handlers[irq] = handler; 51 | } -------------------------------------------------------------------------------- /src/kernel/isr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | const char *exception_messages[] = { 8 | "Division By Zero", 9 | "Debug", 10 | "Non Maskable Interrupt", 11 | "Breakpoint", 12 | "Detected Overflow", 13 | "Out of Bounds", 14 | "Invalid Opcode", 15 | "No Coprocessor", 16 | 17 | "Double Fault", 18 | "Coprocessor Segment Overrun", 19 | "Bad TSS", 20 | "Segment Not Present", 21 | "Stack Fault", 22 | "General Protection Fault", 23 | "Page Fault", 24 | "Unknown Interrupt", 25 | 26 | "Coprocessor Fault", 27 | "Alignment Check", 28 | "Machine Check", 29 | "Reserved", 30 | "Reserved", 31 | "Reserved", 32 | "Reserved", 33 | "Reserved", 34 | 35 | "Reserved", 36 | "Reserved", 37 | "Reserved", 38 | "Reserved", 39 | "Reserved", 40 | "Reserved", 41 | "Reserved", 42 | "Reserved"}; 43 | 44 | void init_isr() 45 | { 46 | idt_set_entry(0, (uint64_t)isr0); 47 | idt_set_entry(1, (uint64_t)isr1); 48 | idt_set_entry(2, (uint64_t)isr2); 49 | idt_set_entry(3, (uint64_t)isr3); 50 | idt_set_entry(4, (uint64_t)isr4); 51 | idt_set_entry(5, (uint64_t)isr5); 52 | idt_set_entry(6, (uint64_t)isr6); 53 | idt_set_entry(7, (uint64_t)isr7); 54 | idt_set_entry(8, (uint64_t)isr8); 55 | idt_set_entry(9, (uint64_t)isr9); 56 | idt_set_entry(10, (uint64_t)isr10); 57 | idt_set_entry(11, (uint64_t)isr11); 58 | idt_set_entry(12, (uint64_t)isr12); 59 | idt_set_entry(13, (uint64_t)isr13); 60 | idt_set_entry(14, (uint64_t)isr14); 61 | idt_set_entry(15, (uint64_t)isr15); 62 | idt_set_entry(16, (uint64_t)isr16); 63 | idt_set_entry(17, (uint64_t)isr17); 64 | idt_set_entry(18, (uint64_t)isr18); 65 | idt_set_entry(19, (uint64_t)isr19); 66 | idt_set_entry(20, (uint64_t)isr20); 67 | idt_set_entry(21, (uint64_t)isr21); 68 | idt_set_entry(22, (uint64_t)isr22); 69 | idt_set_entry(23, (uint64_t)isr23); 70 | idt_set_entry(24, (uint64_t)isr24); 71 | idt_set_entry(25, (uint64_t)isr25); 72 | idt_set_entry(26, (uint64_t)isr26); 73 | idt_set_entry(27, (uint64_t)isr27); 74 | idt_set_entry(28, (uint64_t)isr28); 75 | idt_set_entry(29, (uint64_t)isr29); 76 | idt_set_entry(30, (uint64_t)isr30); 77 | idt_set_entry(31, (uint64_t)isr31); 78 | } 79 | 80 | void isr_handler(uint64_t id, regs_t *regs) 81 | { 82 | set_color(DEFAULT_GR, DEFAULT_BG); 83 | kprintf("----------- INTERRUPT -----------\n"); 84 | kprintf("[0x%lx] %s\n", regs->rip, exception_messages[id]); 85 | kprintf("---------------------------------\n"); 86 | for (;;) 87 | asm("sti\nhlt\n"); 88 | } -------------------------------------------------------------------------------- /src/kernel/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void panic(const char *panic_msg) 6 | { 7 | set_color(DEFAULT_RD, DEFAULT_BG); 8 | kprintf("------------- PANIC -------------\n"); 9 | kprintf(panic_msg); 10 | kprintf("---------------------------------\n"); 11 | for (;;) 12 | asm("sti\nhlt\n"); 13 | } -------------------------------------------------------------------------------- /src/kernel/pci.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | uint16_t pci_conf_read_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) 13 | { /* refer to the pci.h figure for more information */ 14 | uint32_t addr; 15 | uint32_t lbus = (uint32_t)bus; 16 | uint32_t ldev = (uint32_t)dev; 17 | uint32_t lfunc = (uint32_t)func; 18 | uint16_t tmp = 0; 19 | 20 | /* remember that the OR operator flips bits without affecting the other ones */ 21 | /* that is, 0101 OR 1010 = 1111 */ 22 | addr = (uint32_t)((lbus << 16) | (ldev << 11) | (lfunc << 8) | (offset & 0xFC) | (1 << 31)); 23 | 24 | port_long_out(CONFIG_ADDRESS, addr); /* write our new config to the config register, as seen in pci.h */ 25 | tmp = (uint16_t)((port_long_in(CONFIG_DATA) >> (offset & 2) * 8) & 0xFFFF); 26 | return tmp; 27 | } 28 | 29 | uint32_t pci_conf_read_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset) 30 | { /* refer to the pci.h figure for more information */ 31 | uint32_t addr; 32 | uint32_t lbus = (uint32_t)bus; 33 | uint32_t ldev = (uint32_t)dev; 34 | uint32_t lfunc = (uint32_t)func; 35 | uint32_t tmp = 0; 36 | 37 | addr = (uint32_t)((lbus << 16) | (ldev << 11) | (lfunc << 8) | (offset & 0xFC) | (1 << 31)); 38 | 39 | port_long_out(CONFIG_ADDRESS, addr); /* write our new config to the config register, as seen in pci.h */ 40 | tmp = (uint32_t)(port_long_in(CONFIG_DATA)); 41 | return tmp; 42 | } 43 | 44 | /* Register Offset | Bits 31-24 | Bits 23-16 | Bits 15-8 | Bits 7-0 45 | * 0x0 0x0 | Device ID | Vendor ID 46 | * 0x2 0x8 | Class code | Subclass | Prog IF | Revision ID 47 | */ 48 | 49 | uint16_t get_vendor_id(uint8_t bus, uint8_t dev, uint8_t func) 50 | { 51 | return pci_conf_read_word(bus, dev, func, 0); 52 | } 53 | 54 | uint16_t get_dev_id(uint8_t bus, uint8_t dev, uint8_t func) 55 | { 56 | return pci_conf_read_word(bus, dev, func, 0x0 + 2); 57 | } 58 | 59 | uint16_t get_class(uint8_t bus, uint8_t dev, uint8_t func) 60 | { 61 | /* this will read bits 31-16, but device class is located 62 | * in bits 31-24, so we will shift everything 8 bits, discarding 63 | * the first byte and getting the second one */ 64 | return pci_conf_read_word(bus, dev, func, 0x8 + 2) >> 8; 65 | } 66 | 67 | uint16_t get_subclass(uint8_t bus, uint8_t dev, uint8_t func) 68 | { 69 | /* this will read bits 31-16, but device subclass is located 70 | * in bits 23-16, so we will AND 0xFF (equivalent to an entire byte) 71 | * to just get bits 23-16 and discard bits 31-24 */ 72 | return pci_conf_read_word(bus, dev, func, 0x8 + 2) & 0xFF; 73 | } 74 | 75 | uint32_t get_io_address(uint8_t bus, uint8_t dev, uint8_t func) 76 | { 77 | /* Base Address Registers (or BARs) are used to hold memory 78 | * addresses used by the device 79 | * 80 | * NOTE keep in mind that this implementation is simplified 81 | * so it just works for some specific devices 82 | */ 83 | uint64_t addr = 0x0; 84 | uint32_t bar0 = pci_conf_read_dword(bus, dev, func, 0x10); 85 | if (bar0 & 1) /* I/O space BAR */ 86 | addr = (bar0 >> 2) << 2; /* all the bits except the first two */ 87 | return addr; 88 | } 89 | 90 | uint32_t get_abar(uint8_t bus, uint8_t dev, uint8_t func) 91 | { 92 | uint64_t addr = 0x0; 93 | uint32_t bar5 = pci_conf_read_dword(bus, dev, func, 0x24); 94 | if (!(bar5 & 1)) /* Memory Space BAR */ 95 | addr = (bar5 >> 4) << 4; /* all bits except the first four */ 96 | return addr; 97 | } 98 | 99 | char *get_vendor_name(uint16_t vendor) 100 | { 101 | switch (vendor) 102 | { 103 | case 0x8086: 104 | return "Intel Corporation"; 105 | case 0x10DE: 106 | return "NVIDIA"; 107 | case 0x1002: 108 | return "Advanced Micro Devices Inc."; 109 | case 0x10EC: 110 | return "Realtek Semiconductor Corp."; 111 | case 0x1234: 112 | return "Qemu"; 113 | case 0x80EE: 114 | return "Oracle Corporation"; 115 | default: 116 | return "Unknown"; 117 | } 118 | } 119 | 120 | /* for a more extensive list, refer to 121 | * https://wiki.osdev.org/PCI 122 | */ 123 | 124 | const char *class_codes[] = { 125 | "Unclassified", 126 | "Mass Storage Controller", 127 | "Network Controller", 128 | "Display Controller", 129 | "Multimedia Controller", 130 | "Memory Controller", 131 | "Bridge", 132 | "Simple Communication Controller", 133 | "Base System Peripheral", 134 | "Input Device Controller", 135 | "Docking Station", 136 | "Processor", 137 | "Serial Bus Controller", 138 | "Wireless Controller"}; 139 | 140 | static struct 141 | { 142 | uint16_t class, subclass; 143 | const char *name; 144 | } subclass_codes[] = { 145 | {0x00, 0x00, "Non-VGA-Compatible Unclassified Device"}, 146 | {0x00, 0x01, "VGA-Compatible Unclassified Device"}, 147 | 148 | {0x01, 0x00, "SCSI Bus Controller"}, 149 | {0x01, 0x01, "IDE Controller"}, 150 | {0x01, 0x02, "Floppy Disk Controller"}, 151 | {0x01, 0x03, "IPI Bus Controller"}, 152 | {0x01, 0x04, "RAID Controller"}, 153 | {0x01, 0x05, "ATA Controller"}, 154 | {0x01, 0x06, "SATA Controller"}, 155 | {0x01, 0x07, "Serial Attached SCSI Controller"}, 156 | {0x01, 0x08, "Non-Volatile Memory Controller"}, 157 | 158 | {0x02, 0x00, "Ethernet Controller"}, 159 | {0x02, 0x01, "Token Ring Controller"}, 160 | {0x02, 0x02, "FDDI Controller"}, 161 | {0x02, 0x03, "ATM Controller"}, 162 | {0x02, 0x04, "ISDN Controller"}, 163 | {0x02, 0x05, "WorldFip Controller"}, 164 | {0x02, 0x06, "PICMG 2.14 Multi Computing"}, 165 | {0x02, 0x07, "Infiniband Controller"}, 166 | 167 | {0x03, 0x00, "VGA Compatible Controller"}, 168 | {0x03, 0x01, "XGA Controller"}, 169 | {0x03, 0x02, "3D Controller"}, 170 | 171 | {0x04, 0x00, "Multimedia Video Controller"}, 172 | {0x04, 0x01, "Multimedia Audio Controller"}, 173 | {0x04, 0x02, "Computer Telephony Device"}, 174 | {0x04, 0x03, "Audio Device"}, 175 | 176 | {0x05, 0x00, "RAM Controller"}, 177 | {0x05, 0x01, "Flash Controller"}, 178 | 179 | {0x06, 0x00, "Host Bridge"}, 180 | {0x06, 0x01, "ISA Bridge"}, 181 | {0x06, 0x02, "EISA Bridge"}, 182 | {0x06, 0x03, "MCA Bridge"}, 183 | {0x06, 0x04, "PCI-to-PCI Bridge"}, 184 | {0x06, 0x05, "PCMCIA Bridge"}, 185 | {0x06, 0x06, "NuBus Bridge"}, 186 | {0x06, 0x07, "CardBus Bridge"}, 187 | {0x06, 0x08, "RACEWay Bridge"}, 188 | {0x06, 0x09, "PCI-to-PCI Bridge"}, 189 | {0x06, 0x0A, "InfiniBand-to-PCI Host Bridge"}, 190 | 191 | {0x07, 0x00, "Serial Controller"}, 192 | {0x07, 0x01, "Parallel Controller"}, 193 | {0x07, 0x02, "Multiport Serial Controller"}, 194 | {0x07, 0x03, "Modem"}, 195 | {0x07, 0x04, "IEEE 488.1/2 (GPIB) Controller"}, 196 | {0x07, 0x05, "Smart Card Controller"}, 197 | 198 | {0x08, 0x00, "PIC"}, 199 | {0x08, 0x01, "DMA Controller"}, 200 | {0x08, 0x02, "Timer"}, 201 | {0x08, 0x03, "RTC Controller"}, 202 | {0x08, 0x04, "PCI Hot-Plug Controller"}, 203 | {0x08, 0x05, "SD Host controller"}, 204 | {0x08, 0x06, "IOMMU"}, 205 | 206 | {0x09, 0x00, "Keyboard Controller"}, 207 | {0x09, 0x01, "Digitizer Pen"}, 208 | {0x09, 0x02, "Mouse Controller"}, 209 | {0x09, 0x03, "Scanner Controller"}, 210 | {0x09, 0x04, "Gameport Controller"}, 211 | 212 | {0x0B, 0x00, "386"}, 213 | {0x0B, 0x01, "486"}, 214 | {0x0B, 0x02, "Pentium"}, 215 | {0x0B, 0x03, "Pentium Pro"}, 216 | {0x0B, 0x10, "Alpha"}, 217 | {0x0B, 0x20, "PowerPC"}, 218 | {0x0B, 0x30, "MIPS"}, 219 | {0x0B, 0x40, "Co-Processor"}, 220 | 221 | {0x0C, 0x00, "FireWire (IEEE 1394) Controller"}, 222 | {0x0C, 0x01, "ACCESS Bus Controller"}, 223 | {0x0C, 0x01, "ACCESS Bus Controller"}, 224 | {0x0C, 0x02, "SSA"}, 225 | {0x0C, 0x03, "USB Controller"}, 226 | {0x0C, 0x04, "Fibre Channel"}, 227 | {0x0C, 0x05, "SMBus Controller"}, 228 | {0x0C, 0x06, "InfiniBand Controller"}, 229 | {0x0C, 0x07, "IPMI Interface"}, 230 | {0x0C, 0x08, "SERCOS Interface (IEC 61491)"}, 231 | {0x0C, 0x09, "CANbus Controller"}, 232 | 233 | {0x0D, 0x00, "iRDA Compatible Controller"}, 234 | {0x0D, 0x01, "Consumer IR Controller"}, 235 | {0x0D, 0x10, "RF Controller"}, 236 | {0x0D, 0x11, "Bluetooth Controller"}, 237 | {0x0D, 0x12, "Broadband Controller"}, 238 | {0x0D, 0x20, "Ethernet Controller (802.1a)"}, 239 | {0x0D, 0x21, "Ethernet Controller (802.1b)"}, 240 | }; 241 | 242 | class_t get_class_name(uint16_t class, uint16_t subclass) 243 | { 244 | class_t temp; 245 | temp.class_name = (class_codes[class]) ? class_codes[class] : "Other"; 246 | temp.subclass_name = NULL; 247 | for (uint8_t i = 0; subclass_codes[i].name; i++) 248 | { 249 | if (subclass_codes[i].class == class && subclass_codes[i].subclass == subclass) 250 | temp.subclass_name = subclass_codes[i].name; 251 | } 252 | if (temp.subclass_name == NULL) /* (subclass_code = 0x80) */ 253 | temp.subclass_name = "Other"; 254 | return temp; 255 | } 256 | 257 | void init_pci() 258 | { 259 | for (uint32_t bus = 0; bus < 256; bus++) 260 | { 261 | for (uint32_t dev = 0; dev < 32; dev++) 262 | { 263 | for (uint32_t func = 0; func < 8; func++) 264 | { 265 | dev_t temp; 266 | if ((temp.vendor = get_vendor_id(bus, dev, func)) == 0xFFFF) /* not a valid vendor */ 267 | continue; 268 | temp.dev = get_dev_id(bus, dev, func); 269 | temp.class = get_class(bus, dev, func); 270 | temp.subclass = get_subclass(bus, dev, func); 271 | 272 | /* if it is a configurable device, configure it, 273 | * otherwise, just print the pertinent information */ 274 | if (temp.dev == 0x8029) /* NE2000 network card */ 275 | { 276 | temp.ioaddr = get_io_address(bus, dev, func); 277 | if (temp.ioaddr) 278 | init_ne2k(temp.ioaddr); 279 | } 280 | else if (temp.dev == 0x8139) /* RTL8139 network card */ 281 | { 282 | temp.ioaddr = get_io_address(bus, dev, func); 283 | if (temp.ioaddr) 284 | init_rtl8139(temp.ioaddr); 285 | } 286 | else if (temp.class == 0x1 && temp.subclass == 0x6) /* AHCI controller */ 287 | { 288 | temp.abar = get_abar(bus, dev, func); 289 | if (temp.abar) 290 | init_ahci(temp.abar + HIGHER_HALF); /* not virtually mapped */ 291 | } 292 | else 293 | { 294 | class_t name_temp = get_class_name(temp.class, temp.subclass); 295 | 296 | kprintf("[PCI] (0x%x) vendor = %s, class = %s, subclass = %s\n", 297 | temp.dev, get_vendor_name(temp.vendor), 298 | name_temp.class_name, 299 | name_temp.subclass_name); 300 | } 301 | } 302 | } 303 | } 304 | } -------------------------------------------------------------------------------- /src/kernel/ports.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* functions receive a value from an I/O location */ 4 | /* functions send a value to an I/O location */ 5 | 6 | unsigned char port_byte_in(unsigned short port) 7 | { 8 | unsigned char result; 9 | asm("in %%dx, %%al" 10 | : "=a"(result) 11 | : "d"(port)); 12 | return result; 13 | } 14 | 15 | void port_byte_out(unsigned short port, unsigned char data) 16 | { 17 | asm("out %%al, %%dx" 18 | : 19 | : "a"(data), "d"(port)); 20 | } 21 | 22 | unsigned short port_word_in(unsigned short port) 23 | { 24 | unsigned short result; 25 | asm("in %%dx, %%ax" 26 | : "=a"(result) 27 | : "d"(port)); 28 | return result; 29 | } 30 | 31 | void port_word_out(unsigned short port, unsigned short data) 32 | { 33 | asm("out %%ax, %%dx" 34 | : 35 | : "a"(data), "d"(port)); 36 | } 37 | 38 | uint32_t port_long_in(uint32_t port) 39 | { 40 | uint32_t result; 41 | asm("inl %1, %0" 42 | : "=a"(result) 43 | : "d"(port)); 44 | return result; 45 | } 46 | 47 | void port_long_out(uint32_t port, uint32_t data) 48 | { 49 | asm("outl %0, %1" 50 | : 51 | : "a"(data), "d"(port)); 52 | } -------------------------------------------------------------------------------- /src/kernel/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | uint32_t tick = 0; 8 | 9 | static void timer_callback() 10 | { 11 | tick++; 12 | } 13 | 14 | void init_timer(int freq) 15 | { 16 | irq_set_handler(IRQ0, timer_callback); 17 | 18 | int divisor = 1193182 / freq; /* PIT clock signal */ 19 | 20 | /* Programmable interval timer */ 21 | 22 | /* PIT I/O locations (ports) are 23 | * 0x40 data 24 | * 0x41 data 25 | * 0x42 data 26 | * 0x43 command register (write only) 27 | */ 28 | 29 | /* 0x36 is the result of 0x00 | 0x06 | 0x30 | 0x00 30 | * 31 | * 0x00 use binary counter values 32 | * 0x06 square wave mode 33 | * 0x30 lobyte/hibyte 34 | * 0x00 no counter flags 35 | * 36 | * refer to https://wiki.osdev.org/PIT#Read_Back_Status_Byte 37 | * for the entire table and its respective binary values 38 | */ 39 | 40 | port_byte_out(0x43, 0x36); /* set the command byte */ 41 | port_byte_out(0x40, divisor & 0xff); 42 | port_byte_out(0x40, divisor >> 8); 43 | } -------------------------------------------------------------------------------- /src/kmain.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | void _start(struct stivale2_struct *stivale2_struct); 26 | 27 | static uint8_t stack[4096]; 28 | 29 | static struct stivale2_header_tag_framebuffer fb_tag = { 30 | .tag = { 31 | .identifier = STIVALE2_HEADER_TAG_FRAMEBUFFER_ID, 32 | .next = 0}, /* there's no tag after this one, so next = 0 */ 33 | .framebuffer_width = 0, /* let the bootloader decide the fb resolution */ 34 | .framebuffer_height = 0, /* */ 35 | .framebuffer_bpp = 0}; /* bits per pixel, see color depth */ 36 | 37 | __attribute__((section(".stivale2hdr"), used)) 38 | 39 | /* the stivale_hdr or stivale_header, refer to the official documentation 40 | * https://github.com/stivale/stivale/blob/master/STIVALE2.md#stivale2-header */ 41 | static struct stivale2_header stivale_hdr = { 42 | .entry_point = (uint64_t)_start, /* tells the bootloader that the entry point is _start */ 43 | .stack = (uintptr_t)stack + sizeof(stack), /* above, static uint8_t stack[4096]; */ 44 | .flags = (1 << 1), /* bit 1 set, all pointers are to be offset to the higher half */ 45 | .tags = (uintptr_t)&fb_tag}; /* points to the first tag of the linked list of header tags */ 46 | 47 | void *stivale2_get_tag(struct stivale2_struct *stivale2_struct, uint64_t id) 48 | { 49 | struct stivale2_tag *current_tag = (void *)stivale2_struct->tags; 50 | for (;;) 51 | { /* iterate over the tags till we find the desired one */ 52 | if (current_tag == NULL) 53 | return NULL; 54 | if (current_tag->identifier == id) 55 | return current_tag; 56 | current_tag = (void *)current_tag->next; 57 | } 58 | } 59 | 60 | void kernel_task2() 61 | { 62 | kprintf("[kernel_task2] Hello world!\n"); 63 | for (;;) 64 | ; 65 | } 66 | 67 | void kernel_task1() 68 | { 69 | kprintf("[kernel_task1] Hello world!\n"); 70 | create_task((uint64_t)kernel_task2, FLAG_KERN); 71 | for (;;) 72 | ; 73 | } 74 | 75 | void _start(struct stivale2_struct *stivale2_struct) 76 | { 77 | /* init the framebuffer */ 78 | struct stivale2_struct_tag_framebuffer *fb_tag = (struct stivale2_struct_tag_framebuffer *) 79 | stivale2_get_tag(stivale2_struct, STIVALE2_STRUCT_TAG_FRAMEBUFFER_ID); 80 | init_fb(fb_tag); 81 | kprintf("[OK] FB %ux%u\n", fb_tag->framebuffer_width, fb_tag->framebuffer_height); 82 | 83 | /* init the serial */ 84 | int serial = init_serial(); 85 | kprintf((serial) ? "[ER] SERIAL\n" : "[OK] SERIAL\n"); 86 | 87 | /* init the GDT (Global Descriptor Table) */ 88 | init_gdt(); 89 | kprintf("[OK] GDT\n"); 90 | 91 | /* init the IDT */ 92 | init_idt(); 93 | kprintf("[OK] IDT\n"); 94 | 95 | /* init the ISRs */ 96 | init_isr(); 97 | kprintf("[OK] ISR\n"); 98 | 99 | /* init the IRQs */ 100 | init_irq(); 101 | kprintf("[OK] IRQ\n"); 102 | 103 | /* init the ACPI */ 104 | init_acpi(stivale2_get_tag(stivale2_struct, STIVALE2_STRUCT_TAG_RSDP_ID)); 105 | 106 | /* init physical memory */ 107 | init_phys(stivale2_get_tag(stivale2_struct, STIVALE2_STRUCT_TAG_MEMMAP_ID)); 108 | 109 | /* init virtual memory */ 110 | init_virt(); 111 | 112 | /* init scheduler */ 113 | init_sched(); 114 | 115 | /* init the PCI */ 116 | init_pci(); 117 | 118 | /* print the hour */ 119 | print_cmos(); 120 | 121 | /* init the keyboard */ 122 | init_kb(); 123 | kprintf("[OK] keyboard\n"); 124 | 125 | create_task((uint64_t)kernel_task1, FLAG_KERN); 126 | 127 | for (;;) 128 | { 129 | } 130 | } -------------------------------------------------------------------------------- /src/math/math.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int pow(int a, int b) 6 | { 7 | for (uint8_t i = 1; i < b; i++) 8 | a *= a; 9 | return a; 10 | } -------------------------------------------------------------------------------- /src/mem/kheap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* for now, we will use a watermark allocator, we will keep track 6 | * of what we have allocated and how much memory we got left 7 | * 8 | * | allocated | free memory | 9 | * ^free_base free_top^ 10 | * 11 | * each time free_base is incremented by one, we are working with one page, 12 | * that is 4096 bytes 13 | */ 14 | uint64_t free_base = 0; /* free_base works as an offset of phys_bitmap */ 15 | uint64_t free_top = 0; 16 | 17 | void *kmalloc(size_t size) 18 | { 19 | while (CHECK_PAGE(free_base)) /* while we do not find free pages */ 20 | { 21 | free_base++; 22 | free_top++; 23 | } 24 | while (CHECK_PAGE(free_top) == 0) /* while we find free pages */ 25 | free_top++; 26 | uint64_t addr = free_base * PAGE_SIZE; 27 | /* since we are working with pages, we need to round up what we have 28 | * allocated (for example, 300 bytes will be 1 page, 5000, 2 pages) */ 29 | free_base += (size + (PAGE_SIZE - 1)) / PAGE_SIZE; 30 | return (void *)(addr); 31 | } -------------------------------------------------------------------------------- /src/mem/phys.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* refer to the official stivale2 specification memory map section to get more information 6 | * https://github.com/stivale/stivale/blob/master/STIVALE2.md#memory-map-structure-tag */ 7 | 8 | /* the physical memory manager task is simple, it needs to split our available physical memory 9 | * into chunks, or pages (each one of 4096 bytes) 10 | * 11 | * the manager must keep an eye on these pages, and know if they are being used or free, so 12 | * when a program or the kernel asks for some memory, the manager can give a pointer to 13 | * a free and usable area (that is, a classic malloc function) 14 | * 15 | * there are several methods of doing this, we are going to use a bitmap 16 | */ 17 | 18 | /* each byte of the bitmap can hold information about eight different pages, that is, each bit 19 | * holds information for one page 20 | * 21 | * some notes for the following macros 22 | * - we want to set page 9, 9/8 [truncated] is 1, so the information of page 9 will be in byte 1 23 | * - we do 9 % 8, getting 1, that means that the information for the page 9 will be in byte 1, bit 1 24 | * - keep in mind that page 0 is the first one, and page 9 the tenth 25 | * 26 | * 0000 0000, 0100 0000 27 | */ 28 | 29 | #define SET_PAGE(page) (phys_bitmap[page / 8] |= (1 << (page % 8))) 30 | #define CLEAR_PAGE(page) (phys_bitmap[page / 8] &= ~(1 << (page % 8))) 31 | 32 | uint8_t *phys_bitmap = {0}; 33 | 34 | void phys_alloc_page(void *addr) 35 | { 36 | SET_PAGE((uint64_t)addr / PAGE_SIZE); 37 | } 38 | 39 | void phys_free_page(void *addr) 40 | { 41 | CLEAR_PAGE((uint64_t)addr / PAGE_SIZE); 42 | } 43 | 44 | void phys_alloc_multi(void *addr, uint64_t n) 45 | { 46 | for (uint64_t i = 0; i < n; i++) 47 | phys_alloc_page(addr + i * PAGE_SIZE); 48 | } 49 | 50 | void phys_free_multi(void *addr, uint64_t n) 51 | { 52 | for (uint64_t i = 0; i < n; i++) 53 | phys_free_page(addr + i * PAGE_SIZE); 54 | } 55 | 56 | void init_phys(struct stivale2_struct_tag_memmap *mem_tag) 57 | { 58 | uint64_t total_memory = 0; 59 | uint64_t available_memory = 0; 60 | 61 | /* the memory map tag contains 62 | * - entries, count of memory map entries 63 | * - memmap[], array of memory map entries 64 | * 65 | * and each memory entry 66 | * - base, physical address of base of the memory section 67 | * - length, length of the section 68 | * - type, keeping in mind the following constants 69 | * 70 | * USABLE = 1, 71 | * RESERVED = 2, 72 | * ACPI_RECLAIMABLE = 3, 73 | * BOOTLOADER_RECLAIMABLE = 0x1000, 74 | * KERNEL_AND_MODULES = 0x1001, 75 | * 76 | * also, from the stivale specification 77 | * - usable and bootloader reclaimable entries are guaranteed to be 4096 byte 78 | * aligned for both base and length 79 | * - usable and bootloader reclaimable entries are guaranteed not to overlap 80 | * with any other entry 81 | */ 82 | 83 | /* the problem with the bitmap approach is that we don't really know how many 84 | * bytes we will need to store our bitmap, and since we didn't initialize the physical 85 | * memory, we cannot allocate it 86 | * 87 | * a solution would be loading the bitmap into some available area, in theory, the entries must 88 | * be sorted by base address, lowest to highest, the stivale protocol says that 89 | * 90 | * [the entries are guaranteed to be sorted by base address, lowest to highest] 91 | * 92 | * thus, the bitmap size in bytes can be calculated with 93 | * 94 | * highest address / pages / 8 pages per bitmap byte 95 | */ 96 | uint32_t bitmap_bytes = 0; 97 | 98 | for (uint8_t i = 0; i < mem_tag->entries; i++) 99 | { 100 | struct stivale2_mmap_entry mem_entry = mem_tag->memmap[i]; 101 | total_memory += mem_entry.length; 102 | if (mem_entry.type == STIVALE2_MMAP_USABLE) 103 | { 104 | kprintf("[PHYS] usable, from 0x%lx to 0x%lx\n", mem_entry.base, mem_entry.length); 105 | available_memory += mem_entry.length; 106 | bitmap_bytes = (mem_entry.base + mem_entry.length) / PAGE_SIZE / 8; 107 | } 108 | } 109 | 110 | for (uint8_t i = 0; i < mem_tag->entries; i++) 111 | { 112 | struct stivale2_mmap_entry *mem_entry = &mem_tag->memmap[i]; 113 | /* if we find an entry were we can store our bitmap, we will 114 | * manually allocate it as following */ 115 | if (mem_entry->type == STIVALE2_MMAP_USABLE && mem_entry->length > bitmap_bytes) 116 | { 117 | phys_bitmap = (uint8_t *)mem_entry->base; 118 | 119 | /* if the bitmap size is less than one page, it will cause problems later, since 120 | * our physical functions will truncate its value, for example, if the bitmap 121 | * size is 2048 bytes, that is, 0.5 pages, it will be treated as 0 pages */ 122 | uint64_t bitmap_bytes_up = ((bitmap_bytes + (PAGE_SIZE - 1)) / PAGE_SIZE); 123 | bitmap_bytes_up *= PAGE_SIZE; /* this always rounds up */ 124 | 125 | mem_entry->base += bitmap_bytes_up; 126 | mem_entry->length -= bitmap_bytes_up; 127 | break; 128 | } 129 | } 130 | 131 | /* now that we got the bitmap correctly initialized, we can set all the pages contained 132 | * in STIVALE2_MMAP_USABLE entries as free 133 | * 134 | * we need to keep in mind that between entries, there is memory that is not usable, 135 | * so, before doing anything, let's mark everything as used (the for loop will 136 | * take care of setting the usable areas as free) 137 | */ 138 | 139 | memset(phys_bitmap, 0xFF, bitmap_bytes); /* 0xFF is equivalent to one entire byte set */ 140 | for (uint8_t i = 0; i < mem_tag->entries; i++) 141 | { 142 | struct stivale2_mmap_entry mem_entry = mem_tag->memmap[i]; 143 | if (mem_entry.type == STIVALE2_MMAP_USABLE) 144 | phys_free_multi((void *)mem_entry.base, mem_entry.length / PAGE_SIZE); 145 | } 146 | 147 | kprintf("[PHYS] %uMB available, %uMB total\n", available_memory / (1024 * 1024), total_memory / (1024 * 1024)); 148 | } -------------------------------------------------------------------------------- /src/mem/virt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* BASIC EXPLANATION 8 | * 9 | * virtual memory is a memory management technique that allows us to map virtual 10 | * addresses to physical addresses 11 | * 12 | * for example, a program wants two bytes of memory; our virtual allocator will give the 13 | * program a memory area that goes from 0x00 to 0x02 14 | * 15 | * 0x00 0x01 0x02 16 | * | | | 17 | * 18 | * but usually, there are several programs; what if one of them also wants two bytes? 19 | * well, we will also give it another memory area that goes from 0x00 to 0x02 20 | * 21 | * physically, that is totally impossible, but not virtually, what is really 22 | * happening is that the programs are receiving 0x00 - 0x02 and 0x02 - 0x04, respectively, 23 | * but both think that their memory space start at 0x00, something that simplifies everything 24 | * 25 | * that means that both memory areas, from the program perspective, are kind of isolated, 26 | * the program gets its own address space 27 | * 28 | * anyways, that is for the userspace, but the basic concept would be that a virtual address 29 | * can be mapped to a physical address [0x0 -> 0xFFFF], in fact several virtual memory spaces 30 | * can be mapped to a single physical address (memory sharing) 31 | */ 32 | 33 | /* REFERENCES 34 | * 35 | * from https://www.amd.com/system/files/TechDocs/24593.pdf 36 | * - 5.3 Long-Mode Page Translation 37 | * - 5.3.1 CR3 register 38 | * - 5.3.3 4-Kbyte Page Translation 39 | * - 5.4 Page-Translation-Table Entry Fields 40 | * - 5.4.1 Field Definitions 41 | */ 42 | 43 | uint64_t *kernel_page_map = 0; /* page_map is a pointer to PML4, this address will be stored in CR3 */ 44 | 45 | void virt_load_page_map(uint64_t pml4) 46 | { 47 | /* in long mode, the CR3 register is used to point to the PML4 base address */ 48 | asm("mov %0, %%cr3" ::"a"(pml4)); 49 | } 50 | 51 | void virt_invlpg(uint64_t virt_addr) 52 | { 53 | /* TLB stands for translation lookaside buffer */ 54 | asm("invlpg (%0)" ::"r"(virt_addr)); /* invlpg flushes/invalidates the specified TLB entry */ 55 | } 56 | 57 | uint64_t *virt_get_table(uint64_t *table, uint16_t offset, uint64_t flags) 58 | { 59 | /* first of all, we need to check if that offset (entry) has any value, 60 | * if it doesn't, we will point it to some new memory allocated with our kheap */ 61 | if (!(table[offset] & 0x01)) 62 | { 63 | /* one table has 512 entries, 64 bits each one, we need to alloc 512*sizeof(64) = 4096 */ 64 | table[offset] = (uint64_t)kmalloc(PAGE_SIZE); 65 | table[offset] |= flags; 66 | } 67 | /* according to the AMD specification, each entry has the following structure (simplified) 68 | * 69 | * | Other | Table base address field | Flags / Other | 70 | * |63 52|51 12|11 0| 71 | * 72 | * a base address size is a 52-bit value, the [Other usage] part ought to be zeros (that's 73 | * why we are using AND, in case there is some bit set there) 74 | * 75 | * NOTE 0xFFF is equivalent to 12 bits (flags size), ~0xFFF, everything else 76 | */ 77 | return (uint64_t *)((table[offset] & ~0xFFF) + HIGHER_HALF); 78 | } 79 | 80 | void virt_map_page(uint64_t *page_map, uint64_t phys_addr, uint64_t virt_addr, uint64_t flags) 81 | { 82 | /* from the virtual address, we can get the different tables offsets 83 | * 84 | * VIRTUAL / LINEAR ADDRESS 85 | * 86 | * | Sign extend | PML4 offset | PDP offset | PD offset | PT offset | Phys page offset | 87 | * |63 48|47 39|38 30|29 21|20 12|11 0| 88 | * 89 | * NOTE 0x1FF is equivalent to 9 bits, the max offset is 512 (2^9), so it fits perfectly */ 90 | uint16_t pml4_offset = (virt_addr >> 39) & 0x1FF; 91 | uint16_t pdp_offset = (virt_addr >> 30) & 0x1FF; 92 | uint16_t pd_offset = (virt_addr >> 21) & 0x1FF; 93 | uint16_t pt_offset = (virt_addr >> 12) & 0x1FF; 94 | 95 | /* now, attending to the following pointer to pointer chain, we can access the different 96 | * tables 97 | * 98 | * CR3 ---> | PML4 | 99 | * |---> | PDP | 100 | * |---> | PD | 101 | * |---> | PT | 102 | * |---> Phys page 103 | * offset (pp) 104 | * 105 | * for example, if we want to know where the PDP table is, we just need to look 106 | * for its address at its offset in the PML4 table, we can do that with something similar 107 | * to pdp = pml4[pml4_offset] 108 | * 109 | * each table has 512 entries, so the offset is pointing to one of these entries 110 | */ 111 | 112 | uint64_t *pdp = virt_get_table(page_map, pml4_offset, flags); 113 | uint64_t *pd = virt_get_table(pdp, pdp_offset, flags); 114 | uint64_t *pt = virt_get_table(pd, pd_offset, flags); 115 | 116 | pt[pt_offset] |= phys_addr | flags | 0x01; /* the physical page offset that we want */ 117 | /* NOTE 0x01 is there to set the page present even if we didn't specify any flags */ 118 | 119 | virt_invlpg(virt_addr); /* flush the TLB */ 120 | } 121 | 122 | void virt_map_multi(uint64_t *page_map, uint64_t base, uint64_t end, uint64_t offset, uint64_t flags) 123 | { 124 | for (uint64_t i = base; i < end; i += PAGE_SIZE) 125 | virt_map_page(page_map, i, i + offset, flags); 126 | } 127 | 128 | void init_virt() 129 | { 130 | /* according to the AMD documentation [5.4.1 Field Definitions], the [Flags / Other] bits 131 | * are the following ones 132 | * 133 | * 11 10 9 8 7 6 5 4 3 2 1 0 134 | * x x x G PS D A PCD PWT U/S R/W P 135 | * 136 | * main ones are described as following 137 | * 138 | * P (present) this bit indicates whether the page-translation table or physical page is loaded 139 | * in physical memory; a page fault will be raised if an attempt is made to access 140 | * a table or page when the bit is 0 141 | * R/W (read/write) controls read/write access 142 | * U/S (user/supervisor) when the U/S bit is 0, access is restricted to supervisor level; when 143 | * the U/S bit is set to 1, access is for both 144 | */ 145 | kernel_page_map = (uint64_t *)kmalloc(PAGE_SIZE); 146 | memset((void *)kernel_page_map, 0, PAGE_SIZE); 147 | kprintf("[VIRT] page_map = 0x%x, remapping memory: ", kernel_page_map); 148 | 149 | /* limine mapped the kernel for us, but let's do it again */ 150 | virt_map_multi(kernel_page_map, 0, 0x8000000, 0, 0b11); 151 | kprintf("[id map]"); 152 | virt_map_multi(kernel_page_map, 0, 0x100000000, HIGHER_HALF, 0b11); 153 | kprintf("[data]"); 154 | virt_map_multi(kernel_page_map, 0, 0x8000000, KERNEL_OFFS, 0b11); 155 | kprintf("[kernel]"); 156 | 157 | virt_load_page_map((uint64_t)kernel_page_map); 158 | 159 | kprintf("[done]\n"); 160 | } -------------------------------------------------------------------------------- /src/string/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void itoa(char base, unsigned long i) 8 | { 9 | if (i == 0) 10 | putchar('0'); 11 | char tmp[21], *tmpr = &tmp[0]; 12 | memset((void *)tmp, 0, 21); 13 | base = (base == 'u') ? 10 : 16; 14 | for (; i > 0; i /= base) 15 | *(++tmpr) = "0123456789ABCDEF"[i % base]; 16 | while (*tmpr != 0) 17 | putchar(*(tmpr--)); 18 | } 19 | 20 | int strcmp(char *s1, char *s2) 21 | { 22 | while (*s1) 23 | { 24 | if (*s1 != *s2) 25 | return 1; 26 | ++s1; 27 | ++s2; 28 | } 29 | return 0; 30 | } 31 | 32 | int strncmp(char *s1, char *s2, int n) 33 | { 34 | while (*s1 && n) 35 | { 36 | if (*s1 != *s2) 37 | return 1; 38 | ++s1; 39 | ++s2; 40 | n--; 41 | } 42 | return 0; 43 | } 44 | 45 | void kprintf(const char *fmt, ...) 46 | { /* this function is a variadic function, it accepts a variable number of arguments */ 47 | va_list lst; 48 | va_start(lst, fmt); /* fmt is the previous argument to the ones we want (...) */ 49 | /* NOTE for some reason, this does not work with llvm, it generates garbage */ 50 | int leading_zero; 51 | 52 | for (size_t i = 0; fmt[i]; i++) 53 | { 54 | if (fmt[i] == '%') 55 | { 56 | i++; 57 | switch (fmt[i]) 58 | { 59 | case 's': 60 | print(va_arg(lst, char *)); 61 | break; 62 | case 'l': 63 | i++; 64 | if (fmt[i] == 'u' || fmt[i] == 'x') 65 | itoa(fmt[i], va_arg(lst, long)); 66 | break; 67 | case 'u': 68 | itoa('u', va_arg(lst, int)); 69 | break; 70 | case 'x': 71 | itoa('x', va_arg(lst, int)); 72 | break; 73 | case '0' ... '9': 74 | leading_zero = fmt[i] - '0'; 75 | i++; 76 | if (fmt[i] == 'u' || fmt[i] == 'x') 77 | { 78 | int tmp = va_arg(lst, int); 79 | if (tmp < pow((fmt[i] == 'u') ? 10 : 16, leading_zero)) 80 | putchar('0'); 81 | itoa(fmt[i], tmp); 82 | } 83 | break; 84 | case '%': 85 | putchar(fmt[i]); 86 | break; 87 | } 88 | } 89 | else 90 | putchar(fmt[i]); 91 | } 92 | 93 | va_end(lst); 94 | } 95 | 96 | void *memset(void *buffptr, int value, size_t size) 97 | { 98 | uint8_t *buff = (uint8_t *)buffptr; 99 | for (size_t i = 0; i < size; i++) 100 | buff[i] = (uint8_t)value; 101 | return buffptr; 102 | } 103 | 104 | void *kmemcpy(void *dstptr, void *srcptr, size_t size) 105 | { 106 | uint8_t *dst = (uint8_t *)dstptr; 107 | uint8_t *src = (uint8_t *)srcptr; 108 | for (size_t i = 0; i < size; i++) 109 | dst[i] = src[i]; 110 | return dstptr; 111 | } -------------------------------------------------------------------------------- /src/task/__task.asm: -------------------------------------------------------------------------------- 1 | global pop_regs 2 | 3 | pop_regs: ; you may also see this as context switching, so keep that in mind 4 | mov rsp, rdi ; point the stack register (rsp) to our own stack (rdi) 5 | pop r15 ; that works because rdi is the first function parameter, thus, our own stack 6 | pop r14 ; with pop, we restore each value of the stack to its very own register 7 | pop r13 8 | pop r12 9 | pop r11 10 | pop r10 11 | pop r9 12 | pop r8 13 | 14 | pop rbp 15 | pop rdi 16 | pop rsi 17 | pop rdx 18 | pop rcx 19 | pop rbx 20 | pop rax 21 | 22 | add rsp, 16 ; move the stack pointer, skip two 64 bits values (see regs_t struct) 23 | 24 | ; the stack will now have what we want in order to switch to the new task, that is 25 | ; uint64_t rip; 26 | ; uint64_t cs; 27 | ; uint64_t rflags; 28 | ; uint64_t rsp; 29 | ; uint64_t ss; 30 | ; the iretq instruction will take care of using these values 31 | iretq -------------------------------------------------------------------------------- /src/task/sched.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAX_TASKS 64 12 | 13 | /* in order to keep the count of the different tasks that are running, we will 14 | * store them in a queue */ 15 | task_t *tasks; 16 | static int task_count = 0; 17 | 18 | /* this function is defined in __task.asm */ 19 | extern void pop_regs(regs_t *regs); 20 | 21 | /* as you can read in task.h, we need to change tasks quickly, so it looks like 22 | * there are several tasks running at the same time 23 | * 24 | * in order to achieve that, we will use the PIT (timer.c), each tick, it will 25 | * call our schedule function, that will take care of changing tasks */ 26 | void schedule(regs_t *regs) 27 | { 28 | if (task_count == 0) /* if the scheduler has not been initialized */ 29 | return; 30 | 31 | /* each time our schedule function gets called, it will search for the last 32 | * running task in the queue, the queue has the followning structure (where 33 | * S is stopped, R is running, and X is no init) 34 | * 35 | * | S | S | X | S | R | X | S | S | X | X | 36 | * (3) (1) (2) 37 | * 38 | * we will iterate over this, find the running task (1) and set it as stopped, 39 | * then, we will change to the next stopped task (2) (no init ones will be ignored) 40 | * 41 | * in case we get to the last initialized task in the queue, we will jump again to 42 | * the first initialized one (3) 43 | */ 44 | 45 | int next = 0; 46 | if (task_count > 1) 47 | { 48 | for (size_t i = 0; i < MAX_TASKS; i++) 49 | { 50 | if (tasks[i].status == TASK_NO_INIT && i == MAX_TASKS - 1) /* finished the iteration without finding anything */ 51 | panic("No available tasks to switch to [i]\n"); /* panic because this should not happen */ 52 | else if (tasks[i].status == TASK_NO_INIT) 53 | continue; 54 | else if (tasks[i].status == TASK_RUNNING) 55 | { 56 | tasks[i].status = TASK_STOPPED; /* set current task as stopped */ 57 | kmemcpy((void *)&tasks[i].regs, regs, sizeof(regs_t)); /* save current task registers into the task struct */ 58 | 59 | /* from that position, we are going to find the next task that we can 60 | * set as running, in order change to that new one */ 61 | for (size_t j = i + 1; j < MAX_TASKS; j++) 62 | { 63 | if (tasks[j].status == TASK_STOPPED) 64 | { 65 | next = j; 66 | goto end; 67 | } 68 | else if (j == MAX_TASKS - 1) 69 | { 70 | /* if there are not any stopped tasks after that one, we completed a cycle 71 | * and we have to start again */ 72 | for (size_t k = 0; k < i + 1; k++) 73 | { 74 | if (tasks[k].status == TASK_STOPPED) 75 | { 76 | next = k; 77 | goto end; 78 | } 79 | else if (k == i) 80 | /* if that didn't work, it means that the scheduler did something wrong, or something 81 | * external broke our queue */ 82 | panic("No available tasks to switch to [k]\n"); 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | end: 90 | if (tasks[next].status != TASK_RUNNING) 91 | { 92 | // kprintf("[SCHED] changing to [number = %u, status = %u, rip = 0x%lx, page_map = 0x%lx]\n", 93 | // next, tasks[next].status, tasks[next].regs.rip, tasks[next].page_map); 94 | tasks[next].status = TASK_RUNNING; 95 | 96 | /* remember that the schedule function was called because an IRQ0 was issued, then, we need to 97 | * mark the interrupt as correctly handled */ 98 | port_byte_out(PIC1, PIC_EOI); 99 | 100 | /* now that we marked the task as running, we need to actually make the switch, before that, 101 | * we need to load the task's pagemap into CR3 */ 102 | virt_load_page_map((uint64_t)tasks[next].page_map); 103 | pop_regs(&tasks[next].regs); /* restore task registers and make the switch */ 104 | } 105 | } 106 | 107 | void load_task(task_t *task) 108 | { 109 | for (size_t i = 0; i < MAX_TASKS; i++) 110 | { 111 | if (tasks[i].status == TASK_NO_INIT) /* if we find an empty task */ 112 | { 113 | kmemcpy(&tasks[i], task, sizeof(task_t)); /* load it into the queue */ 114 | tasks[i].status = TASK_STOPPED; 115 | task_count++; 116 | kprintf("[SCHD] task[%u] loaded into the queue\n", task->pid); 117 | break; 118 | } 119 | else if (i == MAX_TASKS - 1) 120 | panic("Cannot load a new task\n"); 121 | } 122 | } 123 | 124 | void init_sched() 125 | { 126 | tasks = (task_t *)kmalloc(sizeof(task_t) * MAX_TASKS); 127 | for (size_t i = 0; i < MAX_TASKS; i++) /* set all tasks as uninitialized */ 128 | tasks[i].status = TASK_NO_INIT; 129 | kprintf("[SCHD] tasks queue initialized\n"); 130 | 131 | init_timer(50); 132 | kprintf("[SCHD] PIT timer initialized, scheduler initialized\n"); 133 | } -------------------------------------------------------------------------------- /src/task/task.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int pid_count = 0; 9 | 10 | void create_task(uint64_t ip, uint8_t flags) 11 | { 12 | task_t *task = (task_t *)kmalloc(sizeof(task_t)); 13 | task->flags = flags; 14 | 15 | task->pid = pid_count++; 16 | task->regs.rip = ip; /* the ip is the instruction pointer, and will be loaded into the rip register */ 17 | task->regs.rflags = (1 << 9) | (1 << 1); /* rflags reg contains the current CPU state, interrupt enable flag | reserved */ 18 | 19 | if (task->flags & FLAG_USER) /* this task is for a user program */ 20 | { 21 | task->regs.cs = 0x23; /* gdt_entry user_code */ 22 | task->regs.ss = 0x1B; /* gdt_entry user_data */ 23 | 24 | task->page_map = (uint64_t *)kmalloc(PAGE_SIZE); 25 | 26 | /* the pagemap has 4096/8 = 512 entries (each one 64 bits), so, if the task is for 27 | * a user program, we will copy the higher half kernel entries into the task pagemap */ 28 | for (size_t i = 256; i < 512; i++) 29 | task->page_map[i] = kernel_page_map[i]; 30 | 31 | panic("User tasks are not implemented\n"); 32 | } 33 | else /* this task is for the kernel */ 34 | { 35 | task->regs.cs = 0x08; /* [8] -> gdt_entry kernel_code */ 36 | task->regs.ss = 0x10; /* [16] -> gdt_entry kernel_data */ 37 | 38 | task->page_map = kernel_page_map; 39 | 40 | /* the rsp register points to the top of the stack (since the stack grows downwards, we add its size) */ 41 | task->regs.rsp = (uint64_t)kmalloc(STACK_SIZE) + HIGHER_HALF + STACK_SIZE; 42 | } 43 | 44 | load_task(task); /* automatically enqueue the new task */ 45 | } --------------------------------------------------------------------------------