/*----------------------------------------------------------------------------------------------------- SISTEMA OPERACIONAL - carregar execut vel ---------------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "so.h" /*** declara‡”es de vari veis globais ***/ dt idt, gdt; // interrupt table e global descriptor table // endere‡os l¢gicos do TSS do S.O. e do processo ativo /* Eles armazenam os respetivos seletores e permitem saltos de troca de contexto usando endere‡amento direto "jmp []" */ logAddr so; logAddr ativo; // endere‡o logico do programa a ser carregado // e executado segment *base_ldt_atual; // endere‡o do inicio da LDT atual // fila de processos prontos fila prontos; /*** declara‡”es de referˆncias externas e internas ***/ extern void cadastrar(int matricula); extern void inicializar(int modo); // v¡deo gr fico void videotext() { // v¡deo em modo texto asm mov ax,3 asm int 0x10 } //*** Recuperamos alguns valores de registradores de sistema //*** da CPU para ficar em vari veis globais, facilitando o acesso a eles. void get_system_regs() { asm sidt [idt] // armazena base da IDT asm sgdt [gdt] // armazena base da GDT asm str ax // pegue o Task Register so.selector = _EAX; // isso vai permir de passar para o S.O. com jmp [so] } /*** rotinas para leitura de descritores ***/ segment *seg_do_sel(word sel) { // As v ri veis globais gdt e base_ldt_atual precisam // estar setados corretamente. selector s = eq(selector)sel; if(s.ldt) return( base_ldt_atual + s.index ); else return( gdt.base.seg + s.index ); } void *base_do_seg(segment *segp) { return( (void *)(segp->a.base&0xFF000000 | segp->b.base&0x00FFFFFF ) ); } void *base_do_sel(word sel) { return( base_do_seg(seg_do_sel(sel)) ); } dword size_do_sel(word sel) { return( ( seg_do_sel(sel)->a.limit&0xFFFF ) + 1 ); } tss *base_do_tss(word sel) { selector s = eq(selector)sel; access acc = eq(access)gdt.base.seg[s.index].c.access; if(!(acc.s==SPECIAL && (acc.type==TSS_AVAILABLE || acc.type==TSS_BUSY ))) { fprintf(stderr,"Error: tssBase: selector %X is not a TSS\n",sel); exit(2); } return(base_do_sel(sel)); } void set_ldt_atual() { // settar variavel global antes de chamar seg_do_sel base_ldt_atual = base_do_sel(base_do_tss(ativo.selector)->ldtr); } /*** rotinas para preenchimento de descritores ***/ // preenchimento de descritor de interrupt gate, call gate ou task gate void faz_gate(gate *desc, logAddr sr, byte params, byte access) { desc->b.offset = desc->a.offset = sr.offset; //call Address; desc->b.selector = sr.selector; //call Selector; desc->a.access = access; desc->a.params = params; } // preenchimento de um descritor de segmento para size<2^16 void faz_segment(segment *desc, dword size, byte acesso) { static byte *linear = USER_SEG_MEM_INI; access acc = eq(access)acesso; desc->a.limit = size-1; desc->b.base = desc->a.base = linear; desc->c.access = acesso; desc->c.bitsize = 0x40; // trata-se de uma LDT ? if(acc.s==SPECIAL && acc.type==LDT) base_ldt_atual = linear; linear += size; } // preenchimento de um novo descritor de segmento na GDT word faz_gdt_segment(dword size, byte acesso) { static word livre = USER_SEG_INDEX_INI; // index livre na GDT selector s; access acc = eq(access)acesso; faz_segment(&gdt.base.seg[livre], size, acesso); // constroi seletor para o descritor criado s.index = livre; s.ldt = 0; s.pl = acc.dpl; // RPL = DPL livre++; // passa para a proxima posicao livre na GDT return(eq(word)s); } /* rotina especial para criar o segmento de v¡deo */ void descVideo() { dword *desc = (dword *)(gdt.base.seg + VIDEO_INDEX); desc[1] = 0xE04BF200; // base = 0xE0000000, size=1024*768-1 desc[0] = 0x0000FFFF; // tipo = 0xF2 (s=NORNAL type=DATA_READ_WRITE pl=USERPL) } // preenchimento de novo PCB, incluindo LDT word new_task(userSize *user) { selector sel; access acc; word pcbSel; // seletor da nova TSS na GDT tss *pcb; // PCB do novo processo a ser instanciado segment *ldt; // LDT do novo processo acc.p = 1; // descritor presente - vale para todos // ****** primeiro os descritores especiais com privil‚gio sistema acc.dpl = SYSTEMPL; acc.s = SPECIAL; // Task State Segment acc.type = TSS_AVAILABLE; pcbSel = faz_gdt_segment(sizeof(tss), eq(byte)acc); pcb = base_do_tss(pcbSel); pcb->status = novo; pcb->cr3 = _CR3; // a mesma pagina‡„o para todos pcb->eflags = 0x202; // settar interrupt flag, permitindo interrup‡”es pcb->iopb = sizeof(tss); // prote‡„o total de I/O pcb->eip = 0; // 'main' do usu rio ‚ no in¡cio // Local Desciptor Table acc.type = LDT; pcb->ldtr = faz_gdt_segment(6*sizeof(segment), eq(byte)acc); ldt = base_do_sel(pcb->ldtr); // Pilha para system calls e interrup‡”es do processo acc.s = NORMAL; acc.type = DATA_READ_WRITE; pcb->ss0 = faz_gdt_segment(USER_STACK0_SIZE, eq(byte)acc); pcb->esp0 = USER_STACK0_SIZE; // *** a partir d'aqui segmentos de privilegio usuario na LDT sel.ldt = 1; sel.pl = USERPL; acc.dpl = USERPL; // segmento de dados constantes acc.type = DATA_READ_ONLY; faz_segment(ldt + (sel.index=1), user->datasize, eq(byte)acc); pcb->es = eq(word)sel; // Segmento de c¢digo acc.type = CODE_EXECUTE_ONLY; faz_segment(ldt + (sel.index=2), user->codesize, eq(byte)acc); pcb->cs = eq(word)sel; // Segmento de vari veis acc.type = DATA_READ_WRITE; faz_segment(ldt + (sel.index=3), user->varsize, eq(byte)acc); pcb->ds = eq(word)sel; // Pilha do processo acc.type = DATA_READ_WRITE; faz_segment(ldt + (sel.index=4), user->stacksize, eq(byte)acc); pcb->ss = eq(word)sel; pcb->esp = user->stacksize; return(pcbSel); } /* carregar um arquivo execut vel */ word carregar(char exechar) { static char exename[] = "c:us ."; // nome do arquivo (com prefixo) static userSize header; // cabe‡alho do execut vel static unsigned int exehandle, result; word pcbSel; // seletor do novo PCB tss *pcb; // apontador para o novo PCB word save_ES = _ES; // Esta rotina cont‚m chamadas … libc _ES = _DS; // as quais devem ser usadas com ES apropriado. exename[strlen(exename)-2] = exechar; // colocar exechar no final do nome if (_dos_open(exename, O_RDONLY, &exehandle) ) { videotext(); puts("Execut vel "); puts(exename); puts(" ausente.\n"); exit(1); } // Cabe‡alho if (_dos_read(exehandle, &header, sizeof(header), &result) || result!=sizeof(header)) { videotext(); puts("N„o consigo ler o cabe‡alho do execut vel.\n"); exit(1); } pcbSel = new_task(&header); pcb = base_do_sel(pcbSel); // Segmento de dados constantes if (_dos_read(exehandle, base_do_sel(pcb->es), header.datasize, &result) || result!=header.datasize) { videotext(); puts("N„o consigo ler os dados do execut vel.\n"); exit(1); } // Segmento de c¢digo if (_dos_read(exehandle, base_do_sel(pcb->cs), header.codesize, &result) || result!=header.codesize) { videotext(); puts("N„o consigo ler os dados do execut vel.\n"); exit(1); } debug(base_do_sel(pcb->cs), exename); // RPC para o depurador _dos_close(exehandle); _ES = save_ES; // Coloque valor original em ES. return(pcbSel); } //*** rotinas de atendimento a system calls e interrup‡”es _loadds dword _far _pascal systemcall (char comando, dword argumento) { set_ldt_atual(); switch(comando) { case 'T': // marcar processo como "terminado" base_do_tss(ativo.selector)->status = terminado; asm jmp [so] ; // d'aqui n„o tem volta } return 0; } // anexar processo ao fim da fila void anexar_fila(word processo, fila *fila) { selector s; access *acesso; if(fila->fim==0) // se fila est  vazia // o novo processo anexado ser  tambem o primeiro na fila fila->ini = processo; else // se n„o est  vazia, linkar novo processo no ultimo processo da fila base_do_tss(fila->fim)->link = processo; // o processo agora ‚ o novo fim da fila fila->fim = processo; // marcar TSS como "available" s = eq(selector)processo; acesso = &gdt.base.seg[s.index].c.access; acesso->type = TSS_AVAILABLE; } // tirar processo do inicio da fila word tirar_fila(fila *fila) { word processo = 0; // se fila estiver vazia retorne 0 if(fila->ini!=0) { // se fila n„o est  vazia processo = fila->ini; // pegar o primeiro processo da fila // atualizar inicio da fila fila->ini = base_do_tss(processo)->link; if(fila->ini==0) // se fila agora estiver vazia fila->fim=0; // o fim da fila tambem deve estar em zero // zerar campo "link" do processo retirado base_do_tss(processo)->link = 0; } return(processo); } // a tecla "espa‡o" p ra a tarefa no depurador _interrupt void tecla (dword edi, dword esi, dword ebp, dword esp, dword ebx, dword edx, dword ecx, dword eax, dword gs, dword fs, dword es, dword eip, dword cs, dword eflags) { inportb(0x64); // read keyboard status if( inportb(0x60) == 0x39 ) // tecla == "espa‡o" ? eflags |= 0x100; // se sim, settar trap flag EOI1; } // cria descritores de call gate, interrupt gate, etc. void faz_system_gates () { access acc; // campo de acesso para os descritores logAddr sr; // endere‡o l¢gico de uma rotina de atendimento acc.p = 1; acc.dpl = USERPL; acc.s = SPECIAL; // Call Gate para chamadas ao S.O. acc.type=CALL_GATE; sr.offset = &systemcall; sr.selector = _CS; faz_gate(gdt.base.gate+SYSTEM_CALL_INDEX, sr, 2, eq(byte)acc); asm cli // desabilita interrupcoes para poder mexer na IDT // Interrupt Gate para o teclado (somente para depura‡„o) acc.type=INTERRUPT_GATE; sr.offset = &tecla; sr.selector = _CS; faz_gate(idt.base.gate+KEYBOARD, sr, 0, eq(byte)acc); // Task Gate para o timer acc.type=TASK_GATE; faz_gate(idt.base.gate+TIMER, so, 0, eq(byte)acc); } int main() { char c; cadastrar(20123456); //    COLOQUE AQUI SUA MATRICULA SEM 'g'    //******* agora vamos dar boot no NOSSO get_system_regs(); faz_system_gates(); // Segmento de v¡deo descVideo(); inicializar(5); // cria tres processos de usu rio e coloca na fila de prontos anexar_fila( carregar('a'), &prontos); anexar_fila( carregar('b'), &prontos); anexar_fila( carregar('c'), &prontos); c = getchar(); while(ativo.selector = tirar_fila(&prontos)) { asm jmp [ativo] EOI1; anexar_fila(ativo.selector,&prontos); } c = getchar(); videotext(); fprintf(stderr,"NOSSO shut down.\n"); c = getchar(); return(0); }