O projeto doslinux permite a execução de binários Linux dentro de um sistema DOS. Este guia explora os detalhes de implementação do processo de inicialização e da simulação de chamadas de sistema que tornam isso possível.
Sequência de Inicialização
A inicialização configura o ambiente raiz necessário. O ponto de entrada é a função main() no arquivo init/init.c.
Preparação do Sistema de Arquivos
Primeiramente, o sistema de arquivos raiz é remontado em modo leitura-escrita com sincronização, garantindo que as escritas do Linux sejam visíveis no DOS:
if (mount(NULL, "/", "vfat", MS_REMOUNT | MS_SYNCHRONOUS, NULL)) {
perror_fatal("Erro ao remontar raiz");
}
Em seguida, um sistema de arquivos temporário (ramfs) é criado e montado, e o sistema de arquivos virtual proc é configurado:
if (mount(NULL, "/mnt/doslinux_root", "ramfs", 0, NULL)) {
perror_fatal("Erro ao montar ramfs");
}
// Montagem do /proc
if (mkdir("/mnt/doslinux_root/proc", 0755) ||
mount(NULL, "/mnt/doslinux_root/proc", "proc", 0, NULL)) {
perror_fatal("Falha ao configurar /proc");
}
Instalação de Ferramentas Básicas
O utilitário BusyBox é copiado para a nova hierarquia de diretórios, e links simbólicos são criados para os comandos comuns:
copiar_arquivo("/bin/busybox", "/mnt/doslinux_root/bin/busybox");
criar_diretorios_essenciais("/mnt/doslinux_root"); // Cria /bin, /sbin, etc.
instalar_applets_busybox();
Criação de Nós de Dispositivo
Nós de disposiitvo essenciais são criados para permitir o acesso a hardware básico:
mknod("/mnt/doslinux_root/dev/mem", S_IFCHR | 0600, makedev(1, 1));
mknod("/mnt/doslinux_root/dev/tty", S_IFCHR | 0600, makedev(5, 0));
Troca do Sistema de Arquivos Raiz
Por fim, o sistema muda para o novo ambiente de raiz usando a chamada de sistema pivot_root:
if (syscall(SYS_pivot_root, "/mnt/doslinux_root", "/mnt/doslinux_root/old_root")) {
perror_fatal("Falha na troca de raiz (pivot_root)");
}
Simulação de Chamadas de Sistema
O núcleo do doslinux é um emulador de chamadas de sistema Linux. A lógica central reside no manipulador handle_syscall() dentro de init/vm86.c.
Dispatching de Chamadas
O manipulador examina o registrador AH para determinar a operação solicitada:
void handle_syscall(cpu_state_t *cpu) {
uint8_t syscall_num = (cpu->eax >> 8) & 0xFF;
switch(syscall_num) {
case SYS_NUM_EXEC:
execute_program(cpu);
break;
case SYS_NUM_GETPID:
cpu->eax = get_simulated_pid();
break;
// ... outros handlers ...
default:
cpu->eax = -ENOSYS; // Não implementado
}
}
Execução de Programas
A operação de execução extrai o comando do segmento de memória do programa e o delega ao interpretador de comandos:
case SYS_NUM_EXEC: {
uint16_t segment = cpu->cs;
uint8_t *program_segment = (uint8_t*)(segment << 4);
uint8_t cmd_length = program_segment[0x80];
char command[256];
memcpy(command, &program_segment[0x81], cmd_length);
command[cmd_length] = '\0';
int result = shell_execute(command);
cpu->eax = result;
break;
}
Entrada no Modo Virtual 8086 (VM86)
Para executar o código DOS real, o sistema entra no modo VM86. A configuração e o controle deste modo são feitos via uma chamada de sistema especializada:
void iniciar_monitor_virtual(vm86_config_t *config) {
// Mapeia a memória baixa necessária
void *mem_dos = mmap(NULL, DOS_MEMORY_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// Configura a estrutura de estado do VM86
inicializar_estado_vm86(config, mem_dos);
// Entra no loop de emulação
int status = syscall(SYS_vm86, VM86_ENTER, config);
if (status < 0) {
perror("Erro na emulação VM86");
}
}
Gerenciamento de Tarefas Simuladas
Após a inicialização, o processo primário cria processos filhos para isolar o shell de controle do monitor de máquina virtual.
int main(int argc, char *argv[]) {
inicializar_ambiente();
pid_t pid_shell = fork();
if (pid_shell == 0) {
executar_shell_interativo();
exit(EXIT_FAILURE);
}
pid_t pid_vmmon = fork();
if (pid_vmmon == 0) {
vm86_config_t config;
configurar_vm86(&config);
iniciar_monitor_virtual(&config);
exit(EXIT_FAILURE);
}
// Processo pai aguarda a finalização dos filhos
while (wait(NULL) > 0);
return 0;
}