Emulator for representing hardware and operating systems
That's not what friends are for. Friends are for tightly integrated classes which need to be able to play with each other's private data. OS and Hardware are not tightly related; they interact through a specifically designed interface.

If you don't want other classes besides OS to call the hardware functions, don't write code in those other classes which call the hardware functions. Better yet, simply only give the Hardware pointer to the OS class so only it can call the hardware functions.

For callbacks have a look at the boost::function library. It will allow you to create callbacks to the OS methods. You can create pointers to methods but you need both the method pointer and the object point for it to work. Boost::function hides all of this behind the scenes. 

pImpl is workaround because C++ is such a silly language. Its purpose is to make it so that you do not have to recompile the header file whenever the private members of the class change. More modern languages essentially do this automatically for all classes. pImpl makes sense only when your compile times get too large. A class project probably doesn't get large enough for that to be a big concern.