Running a program in kernel mode forbids using standard C library because the only thing your program linked to is kernel itself. So I'm allowed to use functions defined in kernel. But kernel itself is a program written in C and compiled for some particular architecture. And it shouldn't use C standard library, but it also shouldn't use any drivers since drivers are loadable modules. So my question is what actual C functions are used when writing a kernel? How can you interact with hardware not through kernel? Don't say me to look at sources it's too next level for me, TY.
The Unix kernel has traditionally included some assembly language code. I haven’t looked at its source code recently, but I suspect that that’s still true.
See How does a driver actually communicate with a hardware device? for an overview of that topic. The answers to that question discuss two kinds of computer architecture. On a system that uses port-mapped I/O (PMIO), the kernel must be written partly in assembly language — although you may be able to get by with a couple very short routines. On a system that uses memory-mapped I/O (MMIO), even device drivers can be written entirely in C. All they need to do is declare a pointer, set it equal to the virtual address of the device, and then use it to manipulate the device as if it were accessing memory.
The kernel reproduces a whole bunch of the functionality provided by libc statically, within itself.
Just like in user-mode C programming, a function can be defined in one compilation unit and another unit can simply reference it (usually via a header file), the compiler will make it an undefined reference, and the linker will link it with the compilation unit that actually provides the symbol.