/* * ioc_ata.c - ATA driver implementation * * Copyright (c) 2017 Maxime Villard * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #define PIO_ATA_CBR_BASE 0x1F0 # define ATA_DATA 0x000 # define ATA_ERRFEAT 0x001 /* two regs */ # define ATA_SCR 0x002 # define ATA_SNR 0x003 /* lba0 */ # define ATA_CLR 0x004 /* lba1 */ # define ATA_CHR 0x005 /* lba2 */ # define ATA_DHR 0x006 /* drive | lba3 */ # define ATA_SR 0x007 # define ATA_SR_ERR 0x01 # define ATA_SR_IDX 0x02 # define ATA_SR_CORR 0x04 # define ATA_SR_DRQ 0x08 # define ATA_SR_DSC 0x10 # define ATA_SR_DF 0x20 # define ATA_SR_DRDY 0x40 # define ATA_SR_BSY 0x80 # define ATA_CR 0x007 /* two regs */ #define ATA_CMD_READ_SECTORS_RETRY 0x20 #define ATA_CMD_READ_SECTORS_NORETRY 0x21 #define ATA_CMD_WRITE_SECTORS_RETRY 0x30 #define ATA_CMD_WRITE_SECTORS_NORETRY 0x31 #define ATA_CMD_IDENTIFY 0xEC static inline uint16_t ata_data_read( void ) { return in16(PIO_ATA_CBR_BASE + ATA_DATA); } static inline void ata_data_write(uint16_t val) { out16(PIO_ATA_CBR_BASE + ATA_DATA, val); } static inline uint8_t ata_cbr_read(uint32_t reg) { return in8(PIO_ATA_CBR_BASE + reg); } static inline void ata_cbr_write(uint32_t reg, uint8_t val) { out8(PIO_ATA_CBR_BASE + reg, val); } static inline int ata_wait( void ) { uint8_t status; while (1) { status = ata_cbr_read(ATA_SR); if (status & ATA_SR_DRQ) break; } return ((status & ATA_SR_ERR) ? -1 : 0); } static void ata_prepare(uint8_t slave, uint32_t lba, uint8_t count) { ata_cbr_write(ATA_ERRFEAT, 0x00); /* NULL byte to port 0x1F1 */ ata_cbr_write(ATA_SCR, count); /* sector count */ /* set the lba */ ata_cbr_write(ATA_SNR, (lba >> 0) & 0xFF); ata_cbr_write(ATA_CLR, (lba >> 8) & 0xFF); ata_cbr_write(ATA_CHR, (lba >> 16)& 0xFF); /* set the drive and lba3 */ ata_cbr_write(ATA_DHR, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); } static int ata_read(uint8_t count, char *buf) { uint16_t tmpword; size_t idx, n; for (n = 0; n < count; n++) { /* wait for the drive to signal that it's ready */ if (ata_wait() == -1) x86_panic("ata_wait"); /* read one block */ for (idx = 0; idx < 256; idx++) { tmpword = ata_data_read(); buf[n * 512 + idx * 2] = (uint8_t)(tmpword & 0xFF); buf[n * 512 + idx * 2 + 1] = (uint8_t)(tmpword >> 8); } } return 0; } static int ata_write(uint8_t count, char *buf) { uint16_t tmpword; size_t idx, n; for (n = 0; n < count; n++) { /* wait for the drive to signal that it's ready */ if (ata_wait() == -1) x86_panic("ata_wait"); /* write one block */ for (idx = 0; idx < 256; idx++) { tmpword = (buf[n * 512 + idx * 2 + 1] << 8) | buf[n * 512 + idx * 2]; ata_data_write(tmpword); } } return 0; } static uint16_t bswap16(uint16_t x) { return ((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF); } static void ata_init( void ) { uint8_t data[512]; uint16_t tmpw, *p; size_t idx; char *model; int ret; ata_prepare(0, 0, 0); ata_cbr_write(ATA_CR, ATA_CMD_IDENTIFY); /* wait for the drive to signal that it's ready */ ret = ata_wait(); if (ret == -1) x86_panic("-> unable to identify ATA\n"); /* * Read the first sector, swap it, and print the disk model. */ for (idx = 0; idx < 256; idx++) { tmpw = ata_data_read(); data[idx * 2] = (uint8_t)((tmpw >> 0) & 0xFF); data[idx * 2 + 1] = (uint8_t)((tmpw >> 8) & 0xFF); } for (idx = 27*2; idx < 46*2; idx += 2) { p = (uint16_t *)(&data[idx]); *p = bswap16(*p); } model = (char *)&data[27*2]; data[46*2] = '\0'; x86_printf("-> ATA model: '%s'\n", model); } /* -------------------------------------------------------------------------- */ static void ioc_ata_cmd(xptr_t th_xp); extern void x86_ioapic_ata0( void ); void ioc_ata_init(chdev_t *chdev) { chdev->cmd = &ioc_ata_cmd; chdev->isr = &x86_ioapic_ata0; ata_init(); } static void ioc_ata_cmd(xptr_t th_xp) { uint32_t cmd_type; // IOC_READ / IOC_WRITE / IOC_SYNC_READ uint32_t lba; // command argument uint32_t count; // command argument xptr_t buf_xp; // command argument // get client thread cluster and local pointer cxy_t th_cxy = GET_CXY( th_xp ); thread_t * th_ptr = (thread_t *)GET_PTR( th_xp ); // get command arguments and extended pointer on IOC device cmd_type = hal_remote_l32 ( XPTR( th_cxy , &th_ptr->ioc_cmd.type ) ); lba = hal_remote_l32 ( XPTR( th_cxy , &th_ptr->ioc_cmd.lba ) ); count = hal_remote_l32 ( XPTR( th_cxy , &th_ptr->ioc_cmd.count ) ); buf_xp = (xptr_t)hal_remote_l64( XPTR( th_cxy , &th_ptr->ioc_cmd.buf_xp ) ); /* execute operation */ ata_prepare(0, lba, count); if (cmd_type == IOC_WRITE) { ata_cbr_write(ATA_CR, ATA_CMD_WRITE_SECTORS_RETRY); } else { ata_cbr_write(ATA_CR, ATA_CMD_READ_SECTORS_RETRY); } /* waiting policy depends on the command type */ if (cmd_type == IOC_SYNC_READ) { ata_read(count, (char *)buf_xp); } else { x86_panic("!IOC_SYNC_READ not supported"); } } void ioc_ata_isr(hal_trapframe_t *ctx) { x86_printf("rip = %Z\n", ctx->tf_rip); x86_panic((char *)__func__); }