From 71e33e3a8b23ea618b9f251ccd564c080fde55e8 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Fri, 5 Aug 2016 15:10:39 +0200 Subject: [PATCH] Start reading the disk --- init/src/boot_32.cpp | 176 ++++++++++++++++++++++++++++++-- kernel/include/kernel_utils.hpp | 16 +-- 2 files changed, 176 insertions(+), 16 deletions(-) diff --git a/init/src/boot_32.cpp b/init/src/boot_32.cpp index 196c4618..8de63849 100644 --- a/init/src/boot_32.cpp +++ b/init/src/boot_32.cpp @@ -14,6 +14,12 @@ #include "kernel.hpp" #include "early_memory.hpp" #include "virtual_debug.hpp" +#include "ata_constants.hpp" + +static_assert(sizeof(uint8_t) == 1, "uint8_t must be 1 byte long"); +static_assert(sizeof(uint16_t) == 2, "uint16_t must be 2 bytes long"); +static_assert(sizeof(uint32_t) == 4, "uint32_t must be 4 bytes long"); +static_assert(sizeof(uint64_t) == 8, "uint64_t must be 8 bytes long"); namespace { @@ -31,15 +37,34 @@ void early_log(const char* s){ early::early_logs_count(c + 1); } -typedef unsigned int uint8_t __attribute__((__mode__(__QI__))); -typedef unsigned int uint16_t __attribute__ ((__mode__ (__HI__))); -typedef unsigned int uint32_t __attribute__ ((__mode__ (__SI__))); -typedef unsigned int uint64_t __attribute__ ((__mode__ (__DI__))); +void virtual_debug(const uint32_t& value){ + char buffer[32]; -static_assert(sizeof(uint8_t) == 1, "uint8_t must be 1 byte long"); -static_assert(sizeof(uint16_t) == 2, "uint16_t must be 2 bytes long"); -static_assert(sizeof(uint32_t) == 4, "uint32_t must be 4 bytes long"); -static_assert(sizeof(uint64_t) == 8, "uint64_t must be 8 bytes long"); + if(value == 0){ + ::virtual_debug("0"); + return; + } + + char int_buffer[20]; + int i = 0; + auto rem = value; + + while(rem != 0){ + int_buffer[i++] = '0' + rem % 10; + rem /= 10; + } + + --i; + + size_t j = 0; + for(; i >= 0; --i){ + buffer[j++] = int_buffer[i]; + } + + buffer[j] = '\0'; + + ::virtual_debug(buffer); +} const uint32_t PML4T = 0x70000; @@ -145,6 +170,139 @@ void serial_init() { out_byte(COM1_PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set } +inline void ata_400ns_delay(uint16_t controller){ + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); + in_byte(controller + ATA_STATUS); +} + +uint8_t wait_for_controller(uint16_t controller, uint8_t mask, uint8_t value, uint16_t timeout){ + uint8_t status; + do { + // Sleep at least 400ns before reading the status register + ata_400ns_delay(controller); + + // Final read of the controller status + status = in_byte(controller + ATA_STATUS); + } while ((status & mask) != value && --timeout); + + return timeout; +} + +bool ata_select_device(){ + auto wait_mask = ATA_STATUS_BSY | ATA_STATUS_DRQ; + + if(!wait_for_controller(ATA_PRIMARY, wait_mask, 0, 10000)){ + return false; + } + + //Indicate the selected device + out_byte(ATA_PRIMARY + ATA_DRV_HEAD, 0xA0 | (MASTER_BIT << 4)); + + if(!wait_for_controller(ATA_PRIMARY, wait_mask, 0, 10000)){ + return false; + } + + return true; +} + +bool read_sector(uint64_t start, void* data){ + //Select the device + if(!ata_select_device()){ + return false; + } + + uint8_t sc = start & 0xFF; + uint8_t cl = (start >> 8) & 0xFF; + uint8_t ch = (start >> 16) & 0xFF; + uint8_t hd = (start >> 24) & 0x0F; + + //Process the command + out_byte(ATA_PRIMARY + ATA_NSECTOR, 1); + out_byte(ATA_PRIMARY + ATA_SECTOR, sc); + out_byte(ATA_PRIMARY + ATA_LCYL, cl); + out_byte(ATA_PRIMARY + ATA_HCYL, ch); + out_byte(ATA_PRIMARY + ATA_DRV_HEAD, (1 << 6) | (MASTER_BIT << 4) | hd); + out_byte(ATA_PRIMARY + ATA_COMMAND, ATA_READ_BLOCK); + + //Wait at most 30 seconds for BSY flag to be cleared + if(!wait_for_controller(ATA_PRIMARY, ATA_STATUS_BSY, 0, 30000)){ + return false; + } + + //Verify if there are errors + if(in_byte(ATA_PRIMARY + ATA_STATUS) & ATA_STATUS_ERR){ + return false; + } + + // Polling + + while(true){ + ata_400ns_delay(ATA_PRIMARY); + + auto status = in_byte(ATA_PRIMARY + ATA_STATUS); + + if(status & ATA_STATUS_BSY){ + continue; + } + + if(status & ATA_STATUS_ERR || status & ATA_STATUS_DF){ + return false; + } + + if(status & ATA_STATUS_DRQ){ + break; + } + } + + //Read the disk sectors + uint16_t* buffer = reinterpret_cast(data); + for(int i = 0; i < 256; ++i){ + *buffer++ = in_word(ATA_PRIMARY + ATA_DATA); + } + + return true; +} + +void suspend_init(const char* message){ + early_log(message); + asm volatile("cli; hlt"); + __builtin_unreachable(); +} + +void detect_disks(){ + // Disable all interrupts + out_byte(ATA_PRIMARY + ATA_DEV_CTL, ATA_CTL_nIEN); + out_byte(ATA_SECONDARY + ATA_DEV_CTL, ATA_CTL_nIEN); + + // Make sure the primary controller is enabled + + out_byte(ATA_PRIMARY + ATA_NSECTOR, 0xAB); + if(in_byte(ATA_PRIMARY + ATA_NSECTOR) != 0xAB){ + suspend_init("The primary ATA controller is not enabled"); + } + + char block_buffer[512]; + + // Read the MBR + if(!read_sector(0, block_buffer)){ + suspend_init("Unable to read MBR"); + } + + // Extract the start of the partition + uint16_t partition_start = *reinterpret_cast(block_buffer + 0x1be); + + // Reset interrupt status + out_byte(ATA_PRIMARY + ATA_DEV_CTL, 0); + out_byte(ATA_SECONDARY + ATA_DEV_CTL, 0); +} + } //end of anonymous namespace bool serial_is_transmit_buffer_empty() { @@ -184,6 +342,8 @@ void pm_main(){ //Enable paging enable_paging(); + detect_disks(); + asm volatile("cli; hlt"); __builtin_unreachable(); diff --git a/kernel/include/kernel_utils.hpp b/kernel/include/kernel_utils.hpp index 443d549b..a0085fca 100644 --- a/kernel/include/kernel_utils.hpp +++ b/kernel/include/kernel_utils.hpp @@ -28,8 +28,6 @@ inline void out_byte (uint16_t _port, uint8_t _data){ : [port] "dN" (_port), [data] "a" (_data)); } -#ifndef THOR_INIT - inline uint16_t in_word(uint16_t _port){ uint16_t rv; @@ -40,6 +38,14 @@ inline uint16_t in_word(uint16_t _port){ return rv; } +inline void out_word(uint16_t _port, uint16_t _data){ + asm volatile ("out %[port], %[data]" + : /* No outputs */ + : [port] "dN" (_port), [data] "a" (_data)); +} + +#ifndef THOR_INIT + inline uint32_t in_dword(uint16_t _port){ uint32_t rv; @@ -60,12 +66,6 @@ inline uint64_t in_qword(uint16_t _port){ return rv; } -inline void out_word(uint16_t _port, uint16_t _data){ - asm volatile ("out %[port], %[data]" - : /* No outputs */ - : [port] "dN" (_port), [data] "a" (_data)); -} - inline void out_dword(uint16_t _port, uint32_t _data){ asm volatile ("out %[port], %[data]" : /* No outputs */