/////////////////////////////////////////////////////////////////////////////////// // File : bdv_driver.c // Date : 23/05/2013 // Author : alain greiner // Maintainer: cesar fuguet // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // Implementation notes: // 1. In order to share code, the two _bdv_read() and _bdv_write() functions // call the same _bdv_access() function. // 2. All accesses to BDV registers are done by the two // _bdv_set_register() and _bdv_get_register() low-level functions, // that are handling virtual / physical extended addressing. /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // BDV global variables /////////////////////////////////////////////////////////////////////////////// spin_lock_t _bdv_lock __attribute__((aligned(64))); unsigned int _bdv_status; unsigned int _bdv_gtid; /////////////////////////////////////////////////////////////////////////////// // This low_level function returns the value contained in register (index). /////////////////////////////////////////////////////////////////////////////// unsigned int _bdv_get_register( unsigned int index ) { unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index; return _io_extended_read( vaddr ); } /////////////////////////////////////////////////////////////////////////////// // This low-level function set a new value in register (index). /////////////////////////////////////////////////////////////////////////////// void _bdv_set_register( unsigned int index, unsigned int value ) { unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index; _io_extended_write( vaddr, value ); } /////////////////////////////////////////////////////////////////////////////// // This function transfer data between a memory buffer and the block device. // The buffer lentgth is (count*block_size) bytes. // Arguments are: // - to_mem : from external storage to memory when non 0. // - mode : BOOT / KERNEL / USER // - lba : first block index on the external storage. // - buf_paddr : physical base address of the memory buffer. // - count : number of blocks to be transfered. // Returns 0 if success, > 0 if error. /////////////////////////////////////////////////////////////////////////////// static unsigned int _bdv_access( unsigned int to_mem, unsigned int mode, unsigned int lba, unsigned long long buf_paddr, unsigned int count) { unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1<>32) ); _bdv_set_register( BLOCK_DEVICE_COUNT , count ); _bdv_set_register( BLOCK_DEVICE_LBA , lba ); // In BOOT mode, we launch transfer, and poll the BDV_STATUS // register because IRQs are masked. if ( mode == IOC_BOOT_MODE ) { // Launch transfert if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE ); else _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ ); #if GIET_DEBUG_IOC_DRIVER _puts("\n[BDV DEBUG] _bdv_access() : P["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( p ); _puts("] launch transfer in polling mode\n"); #endif unsigned int status; do { status = _bdv_get_register( BLOCK_DEVICE_STATUS ); #if GIET_DEBUG_IOC_DRIVER _puts("\n[BDV DEBUG] _bdv_access() : P["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( p ); _puts("] wait on BDV_STATUS register ...\n"); #endif } while( (status != BLOCK_DEVICE_READ_SUCCESS) && (status != BLOCK_DEVICE_READ_ERROR) && (status != BLOCK_DEVICE_WRITE_SUCCESS) && (status != BLOCK_DEVICE_WRITE_ERROR) ); // busy waiting // analyse status error = ( (status == BLOCK_DEVICE_READ_ERROR) || (status == BLOCK_DEVICE_WRITE_ERROR) ); // release lock _spin_lock_release( &_bdv_lock ); } // in USER or KERNEL mode, we deschedule the task. // When the task is rescheduled, we check the _bdv_status variable, // and release the lock. // We need a critical section, because we must reset the RUN bit // before to launch the transfer, and we don't want to be descheduled // between these two operations. else { unsigned int save_sr; unsigned int ltid = _get_current_task_id(); // activates BDV interrupts _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 1 ); // set the _bdv_status variable _bdv_status = BLOCK_DEVICE_BUSY; // enters critical section _it_disable( &save_sr ); // set _bdv_gtid and reset runnable _bdv_gtid = (procid<<16) + ltid; _set_task_slot( x, y, p, ltid, CTX_RUN_ID, 0 ); // launch transfer if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE ); else _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ ); #if GIET_DEBUG_IOC_DRIVER _puts("\n[BDV DEBUG] _bdv_access() : P["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( p ); _puts("] launch transfer in nterrupt mode\n"); #endif // deschedule task _ctx_switch(); #if GIET_DEBUG_IOC_DRIVER _puts("\n[BDV DEBUG] _bdv_access() : P["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( p ); _puts("] resume execution after descheduling\n"); #endif // restore SR _it_restore( &save_sr ); // analyse status error = ( (_bdv_status == BLOCK_DEVICE_READ_ERROR) || (_bdv_status == BLOCK_DEVICE_WRITE_ERROR) ); // reset _bdv_status and release lock _bdv_status = BLOCK_DEVICE_IDLE; _spin_lock_release( &_bdv_lock ); } #if GIET_DEBUG_IOC_DRIVER _puts("\n[BDV DEBUG] _bdv_access() : P["); _putd( x ); _puts(","); _putd( y ); _puts(","); _putd( p ); _puts("] exit at cycle "); _putd( _get_proctime() ); _puts(" / error = "); _putd( error ); _puts("\n"); #endif return error; } // end _bdv_access() /////////////////////////////////////////////////////////////////////////////// // External functions /////////////////////////////////////////////////////////////////////////////// //////////////////////// unsigned int _bdv_init() { if ( _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ) != 512 ) { _puts("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n"); return 1; } _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 0 ); return 0; } //////////////////////////////////////////////// unsigned int _bdv_read( unsigned int mode, unsigned int lba, unsigned long long buffer, unsigned int count) { return _bdv_access( 1, // read access mode, lba, buffer, count ); } ///////////////////////////////////////////////// unsigned int _bdv_write( unsigned int mode, unsigned int lba, unsigned long long buffer, unsigned int count ) { return _bdv_access( 0, // write access mode, lba, buffer, count ); } ////////////////////////////// unsigned int _bdv_get_status() { return _bdv_get_register( BLOCK_DEVICE_STATUS ); } ////////////////////////////////// unsigned int _bdv_get_block_size() { return _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ); } ///////////////////////////////////// void _bdv_isr( unsigned int irq_type, // HWI / WTI unsigned int irq_id, // index returned by ICU unsigned int channel ) // unused { // get BDV status (and reset IRQ) unsigned int status = _bdv_get_register( BLOCK_DEVICE_STATUS ); // check status: does nothing if IDLE or BUSY if ( (status == BLOCK_DEVICE_IDLE) || (status == BLOCK_DEVICE_BUSY) ) return; // save status in kernel buffer _bdv_status _bdv_status = status; // identify task waiting on BDV unsigned int remote_procid = _bdv_gtid>>16; unsigned int ltid = _bdv_gtid & 0xFFFF; unsigned int remote_cluster = remote_procid >> P_WIDTH; unsigned int remote_x = remote_cluster >> Y_WIDTH; unsigned int remote_y = remote_cluster & ((1<> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1<