Как GRUB это делает?
В GRUB загрузочный сектор 512 байт не загружает ядро. Вместо этого он загружает остальную часть загрузчика, который намного больше, чем 512 байт. Именно этот загрузчик второго уровня загружает ядро. Вам придется сделать что-то подобное.
Код для загрузки этого второго этапа может быть намного проще, чем код для загрузки полного ядра - он в основном загружает несколько секторов непосредственно в фиксированный адрес памяти (в нехватке памяти - на данный момент он все еще находится в реальном режиме) и переходит на фиксированный адрес памяти.
Этот второй этап в основном может быть написан на C. Вам нужно всего лишь немного настроек в сборке (вход в защищенный режим, настройка стека и несколько других битов низкоуровневого процессора), прежде чем перейти к функции C, выполнить оставшиеся настройки.
Ядро Linux делало что-то подобное в прошлом. Вы можете скопировать сырое ядро прямо на дискету. Первые 512 байт были загрузочным сектором дискеты, который загружал следующие несколько секторов (все еще в реальном режиме) по фиксированному адресу в нехватке памяти. Эти следующие несколько секторов имели код (все еще в сборке) для загрузки остальной части ядра в фиксированный адрес памяти и перехода к его реальной точке входа. В настоящее время IIRC большая часть этого кода была удалена, и ядро Linux теперь зависит от внешних загрузчиков.