It seems like goroutines in Go don't do as much asynchronous stuff as Haskell's I/O manager and green threads do. Looking at Go sources, there is some async stuff w/epoll in the net library with sockets, but file.Read is indeed just a plain syscall.
It makes me wonder, what happens to a goroutine when a system call blocks. Go is supposed to mux many goroutines to a smaller number of OS threads, but what happens when one of those threads has been blocked in a system call?
There are plenty of vague descriptions on how goroutines work but none of the ones I found quickly explained what happens on a blocking system call that allows another goroutine to run, apart from hand-waving about "another goroutine running".
The Go runtime keeps track of the number of goroutines that are currently executing and ensures that this doesn't go above GOMAXPROCS. As soon as a goroutine enters a system call it is excluded from this count. If there aren't enough threads to run GOMAXPROCS goroutines the runtime may launch a new one.
See pkg/runtime/proc.c (entersyscall, ready, and matchmg).