There's been some discussion about using libuv but no consensus: https://gitlab.haskell.org/ghc/ghc/issues/8400. It's available as a library: https://haskell-stdio.github.io/stdio/
I didn't re-read the papers but IIRC GHC just spawns an extra OS thread any time a possibly-blocking function is called, as it doesn't follow a strict M:N model. There's a thread pool to reduce overhead but it's probably not as efficient as Go's method.
¹ It's possible the JVM, being the highly optimized workhorse VM it is, has specialized optimizations for I/O and does indeed skip over JNI and libc in these cases.
AFAIK Go does syscalls itself on any platform but Windows and macOS, this includes all BSDs. And even for macOS despite that having never been officially supported it took multiple breakages a few years back.
The first thread here mentions the issues that causes for openbsd.