Вместо этого он просто зависает.
Запустите вашу ОС внутри BOCHS, чтобы вы могли использовать встроенный отладчик BOCHS, чтобы точно определить, где он застрял.
Возможность отладки блокировок, в том числе с отключенными прерываниями, вероятно, очень полезна ...
asm volatile("call (%0)" : : "r" (&code));
небезопасен из-за отсутствия клобуферов.
Но, что еще хуже, он загружает новое значение EIP из первых 4 байтов массива вместо установки EIP по этому адресу. (Если загружаемые данные не являются массивом указателей, а не фактическим машинным кодом?)
У вас есть %0
в скобках, так что это режим адресации. Ассемблер предупредит вас о косвенном вызове без *
, но соберет его как call *(%eax)
, с EAX = адрес code[0][0]
. Вы действительно хотите call *%eax
или любой другой регистр, выбранный компилятором, регистр-косвенный, а не память-косвенный.
&code
и code
являются просто указателями на начало массива; &code
не создает объект анонимного указателя, хранящий адрес другого адреса. &code
принимает адрес массива в целом. code
в этом контексте «разлагается» на указатель на первый объект.
https://gcc.gnu.org/wiki/DontUseInlineAsm (для этого).
Вы можете заставить компилятор выдавать инструкцию call
, приведя указатель к указателю на функцию.
__builtin___clear_cache(&code[0][0], &code[30][255]); // don't optimize away stores into the buffer
void (*fptr)(void) = (void*)code; // casting to void* instead of the actual target type is simpler
fptr();
Это скомпилирует (с включенной оптимизацией) что-то вроде lea 16(%esp), %eax
/ call *%eax
для 32-битного x86, потому что ваш буфер code[][]
является массивом в стеке.
Или, чтобы вместо этого он испускал jmp
, сделайте это в конце функции void
, или return funcptr();
в функции без void, чтобы компилятор мог оптимизировать вызов / ret в jmp
tailcall.
Если он не возвращается, вы можете объявить его с помощью __attribute__((noreturn))
.
Убедитесь, что страница / сегмент памяти является исполняемой . (Ваш uint16_t code[];
является локальным, поэтому gcc выделит его в стеке. Это может быть не то, что вам нужно. Размер - это константа времени компиляции, поэтому вы можете сделать это static
, но если вы сделаете это для других массивов в других одноуровневых функциях (не родительских или дочерних), то вы теряете возможность повторно использовать большой кусок памяти стека для разных массивов.)
Это намного лучше, чем ваш небезопасный встроенный ассм. (Вы забыли "memory"
clobber, так что ничто не говорит компилятору, что ваш asm действительно читает указанную память). Кроме того, вы забыли объявить любые взломщики регистров; предположительно, загруженный вами блок кода будет перекрывать некоторые регистры, если он вернется, если только он не записан для сохранения / восстановления всего.
В GNU C вам необходимо использовать __builtin__clear_cache
при приведении указателя данных к указателю на функцию . На x86 он на самом деле не очищает кеш, он сообщает компилятору, что хранилища в этой памяти не мертвы, потому что он будет читаться при выполнении. См. Как работает __builtin___clear_cache?
Без этого gcc может оптимизировать копирование в uint16_t code[sectors][256];
, потому что это похоже на мертвое хранилище. (Точно так же, как с вашим текущим встроенным asm, который запрашивает только указатель в регистре.)
В качестве бонуса эта часть вашей ОС становится переносимой на другие архитектуры, в том числе такие, как ARM, без согласованного кэша команд, где встроенная функция расширяется до реальных инструкций. (На x86 это чисто влияет на оптимизатор).
read(basesector+sector);
Вероятно, для вашей функции read
было бы неплохо взять указатель назначения для чтения, поэтому вам не нужно сбрасывать данные через буфер readOut
.
Кроме того, я не понимаю, почему вы хотите объявить свой код как 2D-массив; сектора - это артефакт того, как вы выполняете дисковый ввод-вывод, не относящийся к использованию кода после его загрузки. Секторный элемент времени должен присутствовать только в коде цикла, который загружает данные, но не виден в других частях вашей программы.
char code[sectors * 512];
было бы хорошо.