As Rust's documentation explains "... Under the hood, both a copy and a move can result in bits being copied in memory, although this is sometimes optimized away"
What's important about Copy is that, since you said you want to permit Copy here, it's OK if Rust keeps both identical bit patterns alive after the bits are copied. Just keeping both alive is obviously better for trivial types like u32, but on modern hardware it's also true for somewhat more complicated types like &str (which is a pointer and a count) or SocketAddrV6 (an IPv6 address, a port number, flow label and so on).
Thus, in your example, v is being bit copied (at least notionally) in both cases to become the parameter of this foo function, but because v is Copy, we can nevertheless use v again on the next line, the bits in v are still alive. If v was not Copy (e.g. an owned String) then foo(v) consumes v, it's moved into foo and now it's gone, we can't access v on the next line.
The fact that foo(v) consumes v, is why core::mem::drop() is so trivial: https://doc.rust-lang.org/core/mem/fn.drop.html