Skip to content
Snippets Groups Projects
  • Eugeniy Paltsev's avatar
    c27814be
    ARC: Flush & invalidate D$ with a single command · c27814be
    Eugeniy Paltsev authored
    
    We don't implement separate flush_dcache_all() intentionally as
    entire data cache invalidation is dangerous operation even if we flush
    data cache right before invalidation.
    
    There is the real example:
    We may get stuck in the following code if we store any context (like
    BLINK register) on stack in invalidate_dcache_all() function.
    
    BLINK register is the register where return address is automatically saved
    when we do function call with instructions like 'bl'.
    
    void flush_dcache_all() {
    	__dc_entire_op(OP_FLUSH);
    	// Other code //
    }
    
    void invalidate_dcache_all() {
    	__dc_entire_op(OP_INV);
    	// Other code //
    }
    
    void foo(void) {
    	flush_dcache_all();
    	invalidate_dcache_all();
    }
    
    Now let's see what really happens during that code execution:
    
    foo()
      |->> call flush_dcache_all
      	[return address is saved to BLINK register]
      	[push BLINK] (save to stack)              ![point 1]
      	|->> call __dc_entire_op(OP_FLUSH)
      		[return address is saved to BLINK register]
      		[flush L1 D$]
      		return [jump to BLINK]
      	<<------
      	[other flush_dcache_all code]
      	[pop BLINK] (get from stack)
      	return [jump to BLINK]
      <<------
      |->> call invalidate_dcache_all
      	[return address is saved to BLINK register]
      	[push BLINK] (save to stack)               ![point 2]
      	|->> call __dc_entire_op(OP_FLUSH)
      		[return address is saved to BLINK register]
      		[invalidate L1 D$]                 ![point 3]
      		// Oops!!!
      		// We lose return address from invalidate_dcache_all function:
      		// we save it to stack and invalidate L1 D$ after that!
      		return [jump to BLINK]
      	<<------
      	[other invalidate_dcache_all code]
      	[pop BLINK] (get from stack)
      	// we don't have this data in L1 dcache as we invalidated it in [point 3]
      	// so we get it from next memory level (for example DDR memory)
      	// but in the memory we have value which we save in [point 1], which
      	// is return address from flush_dcache_all function (instead of
      	// address from current invalidate_dcache_all function which we
      	// saved in [point 2] !)
      	return [jump to BLINK]
      <<------
      // As BLINK points to invalidate_dcache_all, we call it again and
      // loop forever.
    
    Fortunately we may do flush and invalidation of D$ with a single one
    instruction which automatically mitigates a situation described above.
    
    And because invalidate_dcache_all() isn't used in common U-Boot code we
    implement "flush and invalidate dcache all" instead.
    
    Signed-off-by: default avatarEugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
    Signed-off-by: default avatarAlexey Brodkin <abrodkin@synopsys.com>
    c27814be
    History
    ARC: Flush & invalidate D$ with a single command
    Eugeniy Paltsev authored
    
    We don't implement separate flush_dcache_all() intentionally as
    entire data cache invalidation is dangerous operation even if we flush
    data cache right before invalidation.
    
    There is the real example:
    We may get stuck in the following code if we store any context (like
    BLINK register) on stack in invalidate_dcache_all() function.
    
    BLINK register is the register where return address is automatically saved
    when we do function call with instructions like 'bl'.
    
    void flush_dcache_all() {
    	__dc_entire_op(OP_FLUSH);
    	// Other code //
    }
    
    void invalidate_dcache_all() {
    	__dc_entire_op(OP_INV);
    	// Other code //
    }
    
    void foo(void) {
    	flush_dcache_all();
    	invalidate_dcache_all();
    }
    
    Now let's see what really happens during that code execution:
    
    foo()
      |->> call flush_dcache_all
      	[return address is saved to BLINK register]
      	[push BLINK] (save to stack)              ![point 1]
      	|->> call __dc_entire_op(OP_FLUSH)
      		[return address is saved to BLINK register]
      		[flush L1 D$]
      		return [jump to BLINK]
      	<<------
      	[other flush_dcache_all code]
      	[pop BLINK] (get from stack)
      	return [jump to BLINK]
      <<------
      |->> call invalidate_dcache_all
      	[return address is saved to BLINK register]
      	[push BLINK] (save to stack)               ![point 2]
      	|->> call __dc_entire_op(OP_FLUSH)
      		[return address is saved to BLINK register]
      		[invalidate L1 D$]                 ![point 3]
      		// Oops!!!
      		// We lose return address from invalidate_dcache_all function:
      		// we save it to stack and invalidate L1 D$ after that!
      		return [jump to BLINK]
      	<<------
      	[other invalidate_dcache_all code]
      	[pop BLINK] (get from stack)
      	// we don't have this data in L1 dcache as we invalidated it in [point 3]
      	// so we get it from next memory level (for example DDR memory)
      	// but in the memory we have value which we save in [point 1], which
      	// is return address from flush_dcache_all function (instead of
      	// address from current invalidate_dcache_all function which we
      	// saved in [point 2] !)
      	return [jump to BLINK]
      <<------
      // As BLINK points to invalidate_dcache_all, we call it again and
      // loop forever.
    
    Fortunately we may do flush and invalidation of D$ with a single one
    instruction which automatically mitigates a situation described above.
    
    And because invalidate_dcache_all() isn't used in common U-Boot code we
    implement "flush and invalidate dcache all" instead.
    
    Signed-off-by: default avatarEugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
    Signed-off-by: default avatarAlexey Brodkin <abrodkin@synopsys.com>