/*----------------------------------------------------------------------------------------------------- SISTEMA OPERACIONAL - carregar e fork ---------------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include "so.h" /*** declara‡”es de vari veis globais ***/ dt idt, gdt; // interrupt table e global descriptor table segment *base_ldt_atual; // endere‡o do inicio da LDT atual // 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, ativo; // fila de processos prontos fila prontos; //*** 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.params = params; desc->a.access = access; } // 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); } word segment_copy(word original) { dword size = size_do_sel(original); selector orig_sel = eq(selector)original; if (orig_sel.ldt) { // O endere‡o do descritor do segmento original pode ser encontrado // na LDT do processo ativo. segment *origp = (segment *)base_do_sel( base_do_tss(ativo.selector)->ldtr ) + orig_sel.index; // O endere‡o do descritor do segmento novo se encontra na LDT corrente. segment *novop = base_ldt_atual + orig_sel.index; faz_segment(novop, size, origp->c.access); memcpy(base_do_seg(novop), base_do_seg(origp), size); // Sempre copiamos para uma mesma posi‡„o (seletor), // mas entre LDTs diferentes. return(original); } else { // selector_original ‚ da GDT word novo = faz_gdt_segment(size, gdt.base.seg[orig_sel.index].c.access); memcpy(base_do_sel(novo), base_do_sel(original), size); return (novo); } } /* 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->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; memset(base_do_sel(pcb->ss0), 0x55, USER_STACK0_SIZE); // *** a partir d'aqui segmentos de privilegio usuario na LDT sel.ldt = 1; sel.pl = USERPL; acc.dpl = USERPL; acc.s = NORMAL; // segmento de dados constantes acc.type = DATA_READ_ONLY; faz_segment(ldt + (sel.index=1), user->datasize, eq(byte)acc); pcb->ds = 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->es = 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; pcb->status = novo; return(pcbSel); } /* cria uma c¢pia de um PCB */ word task_copy(word paiSel) { selector sel; word filhoSel; tss *pai; tss *filho; pai = base_do_tss(paiSel); // c¢pia do pr¢prio TSS filhoSel = segment_copy(paiSel); filho = base_do_sel(filhoSel); // c¢pia da LDT filho->ldtr = segment_copy(pai->ldtr); // Segmento de vari veis filho->es = segment_copy(pai->es); // Pilha de privil‚gio sistema para system calls e interrup‡”es do processo filho->ss0 = segment_copy(pai->ss0); /* Se esta rotina tinha sido chamado em consequˆncia de um system call, o pai est  com SS de privilegio sistema. */ sel = eq(selector)pai->ss; if(sel.pl==SYSTEMPL) { /* No caso de um system call, o £ltimo elemento da pilha de privil‚gio sistema ‚ o SS de privil‚gio usu rio. Aqui estamos confiando que a pilha de privil‚gio usu rio est  na LDT, ou seja, os seletores para pai e filho s„o iguais. N„o podemos mudar filho->ss agora porque aqui est  o SS de privil‚gio sistema, necess rio para retomar a tarefa filho de dentro da chamada ao S.O. O SS de privil‚gio usu rio ser  usado pelo filho normalmente quando fizer retf da chamada ao S.O. */ // copie o segmento cujo seletor est  na base da pilha dword *base_pilha = (byte *)base_do_sel(pai->ss) + USER_STACK0_SIZE; segment_copy(base_pilha[-1]); // por enquanto, a pilha do filho e a pilha de privil‚gio sistema filho->ss = filho->ss0; } else { /* Se o PL e diferente de 0, esta rotina n„o tinha sido chamado de um system call, simplesmente copie a pilha do pai. */ filho->ss = segment_copy(pai->ss); } /* os valores de retorno da chamada ao 'fork' */ pai ->eax = (dword)filhoSel; filho->eax = 0; filho->status = novo; return(filhoSel); } /* carregar um arquivo execut vel */ word carregar(char exechar) { static char exename[] = "us ."; // nome do arquivo (com prefixo) static userSize header; // cabe‡alho do execut vel FILE *exefile; 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 exename[strlen(exename)-2] = exechar; // colocar exechar no final do nome if((exefile=fopen(exename,"rb"))==NULL) { fprintf(stderr,"Execut vel %s ausente.\n", exename); exit(1); } if(fread(&header, sizeof(header), 1, exefile)!=1) { fprintf(stderr,"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(fread(base_do_sel(pcb->ds), header.datasize, 1, exefile)!=1) { fprintf(stderr,"N„o consigo ler os dados do execut vel.\n"); exit(1); } /* Segmento de c¢digo */ if(fread(base_do_sel(pcb->cs), header.codesize, 1, exefile)!=1) { fprintf(stderr,"N„o consigo ler o c¢digo do execut vel.\n"); exit(1); } debug(base_do_sel(pcb->cs), exename); // enviar mensagem ao depurador _dos_close(exehandle); _ES = save_ES; // Coloque valor original em ES. return(pcbSel); } // 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); } //*** rotinas de atendimento a system calls e interrup‡”es _loadds _far dword _pascal systemcall (char comando, dword argumento) { logAddr processo; set_ldt_atual(); switch(comando) { case 'C': // carregar filho // system call para carregar um processo partir do //arquivo fonto. //O nome do arquivo fonte ‚ formado a partir //do prefixo "us" concatenado com o argumento passado na //subrotina concatenado com a extensÆo.("us"+argumento+".c") // O retorno desse system call ‚ o n£mero da TSS criada. { word pcb = carregar((char)argumento); base_do_tss(pcb)->status = pronto; anexar_fila(pcb, &prontos); return pcb; } case 'F': // fork // System call para criar um processo filho a partir da //c¢pia do processo pai. O processo ativo tem seu status //alterado para forking para que a clonagem seja feita //no scheduler do SO. base_do_tss(ativo.selector)->status = forking; asm jmp [so] // a clonagem deve ser realizada no scheduler return _EAX; // pai ou filho voltou do scheduler case 'T': // marcar processo como "terminado" e volta para o SO. // Depois de marcar o processo como terminado, nao tem mais //volta, ou seja, o processo ir  encerrar sua execu‡Æo. base_do_tss(ativo.selector)->status = terminado; asm jmp [so]; // d'aqui n„o tem volta // System call wait. // O valor do sem faro passado como parametro ‚ decrementado. // Verifica-se entÆo se este valor ‚ menor que zero, ou seja, // existe uma fila no sem foro. Caso exista fila no sem foro(valor<0) // o processo corrente ‚ colocado na fila do sem foro e seu status // ‚ alterado para "esperando" e depois retorna para o so. // Caso nao exista fila no sem foro, // retorna sem fazer nada para que o processo seja atendido. case 'W': sema[argumento].valor--; if(sema[argumento].valor < 0) { anexar_fila(ativo.selector, &sema[argumento].espera); base_do_tss(ativo.selector)->status = esperando; asm jmp [so]; } break; // System call signal. // O valor do sem foro passado como parametro ‚ incrementado. // Verifica-se entÆo se este valor ‚ menor ou igual a zero, ou seja, // existe uma fila no sem foro. Caso exista fila no sem foro // o processo ‚ retirado da fila do sem foro, seu status ‚ // alterado para pronto e entÆo ele ‚ colocado na fila de prontos // para que seja executado no scheduler no SO. // Caso nÆo exista fila no sem foro, retorna do sytem call // sem fazer nada. case 'S': sema[argumento].valor++; if(sema[argumento].valor <= 0) { processo.selector = tirar_fila(&sema[argumento].espera); base_do_tss(processo.selector)->status = pronto; anexar_fila(processo.selector, &prontos); } break; } return 0; } // 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 EOI; } // 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 interrup‡”es 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); // Interrupt Gate para o teclado (somente para depura‡„o) acc.type=TASK_GATE; faz_gate(idt.base.gate+TIMER, so, 0, eq(byte)acc); outportb(0x43, 0x26); // bit 7,6 = (00) timer counter 0 // bit 5,4 = (10) write MSB only // bit 3-1 = (011) generate square wave // bit 0 = (0) binary counter // prep PIT, interrupts when counter decrements to 0 outportb(0x40, 5 MS); // set MSB of timer count (max 51 ms) } int main(int argc, char *argv[]) { char c; int i; //Cadastra o aluno a partir de sua matr¡cula. cadastrar(20021034); //    COLOQUE AQUI SUA MATRICULA SEM 'g'    //******* agora vamos dar boot no NOSSO get_system_regs(); // Segmento de v¡deo //Cria o segmento de v¡deo e a //inicializa‡Æo do modo v¡deo gr fico. descVideo(); initializar(5); { // cria o primeiro processo de usu rio atrav‚s do system call carrega. // Esta subrotina ir  carregar o arquivo us5.c. // Ela ler o arquivo fonte(us5.c), // Cria a TSS, // Carrega os segmentos de c¢digo, dados, vari veis, pilha e // Retornar o n£mero da TSS criada. word pcb = carregar('5'); //Muda o status da base da TSS para pronto, //indicando que o processo //est  pronto para se executado. base_do_tss(pcb)->status = pronto; //Depois o processo carregado ‚ anexado … fila de pronto. Essa fila //mant‚m os processos que estÆo prontos para serem executados. anexar_fila(pcb, &prontos); } c = getchar(); faz_system_gates(); //Inicializa‡Æo dos sem foros de cada fil¢sofo. // Existe um array de sem foro, um para cada fil¢sofo. // Um sem foro ‚ uma estrutura(struct) que possui um valor, //para armazernar a quantidade de processos esperando(negativo quando //tiver processo na fila) e //uma lista para armazernar os processos que estÆo esperando. for(i=0;i<2000;i++) sema[i].valor=1; // dispararmos os processos um atr s do outro //Aqui ‚ feito o escalonamento de processos while((ativo.selector = tirar_fila(&prontos))) { // Da um pulo para a TSS do processo ativo. asm jmp [ativo] EOI; // Se o status do processo ativo ‚ "forking", entÆo ser  feita // uma c¢pia do processo pai, ou seja, o processo filho vai ser criado // a partir do pai, ter  seu status alterado para "pronto" e // ser  colocado na fila de pronto antes do processo pai //que foi interrompido. if(base_do_tss(ativo.selector)->status == forking) { word filho = task_copy(ativo.selector); base_do_tss(filho)->status = pronto; anexar_fila(filho, &prontos); // filho vai na fila antes do pai base_do_tss(ativo.selector)->status = pronto; // pai vai em seguido } // colocar o processo interompido de volta na fila de prontos // se ele n„o tiver sido terminado if(base_do_tss(ativo.selector)->status == pronto) anexar_fila(ativo.selector,&prontos); } c = getchar(); //Fim da execu‡Æo do programa. Imprime na tela uma mensagem //indicando que o programa terminou sua execu‡Æo. fprintf(stderr,"NOSSO shut down.\n"); return(0); }