What happens when you first switches on the computer?
What code should we put in the bootsector?
Implement a BL (Lab1 files: bootstart.S and mpos-boot.c)
void bootmain() { for (i = 1; i < 20; i++) { read_ide_sector(i, 0x100000 + (i - 1) * 512); } //inline aseembly to set up kernel stack at 0x200000 //and jump to 0x100000; } void read_ide_sector(uint32_t sect, void *dst) { // wait for disk to be ready waitdisk(); outb(0x1F2, 1); // count = 1 // secno is an integer (4 bytes) but with outb you can only write one byte at a time // therefore we write secno byte by byte to ports 0x1F3 to 0x1F6 outb(0x1F3, sect); outb(0x1F4, sect >> 8); outb(0x1F5, sect >> 16); outb(0x1F6, (sect >> 24) | 0xE0); outb(0x1F7, 0x20); // cmd 0x20 - read sectors // wait for disk to be ready waitdisk(); // use x86's string instruction to repeatedly read SECTORSIZE/4 4-byte doublewords from device insl(0x1F0, dst, SECTORSIZE/4); }
Let's continue to finish Alice's kernel
void kernelstart(void) { int sec = 20; int nwords = 0; char buf[SECTORSIZE]; while (1) { //read Alice' file from sec20 onwards read_ide_sector(sec, &buf[0]); for (int j = 0; j < 512; j++) { if (buf[j] == '\0') goto done: if (buf[j] == ' ') //assume every word is separated by exactly one space character nwords++; } s++; } //print output on screen uint16_t *screen = 0xB8014; /* Screen memory address */ while (nwords) { *screen = (nwords % 10) + ‘0’; /*ASCII math to convert digits to printable values */ nwords /= 10; screen--; } }
To address this critique
void kernelstart(void) { while (1) { //print a menu to prompt Alice to load her application //find out which sector no the application resides on program_loader(0x200000, alice_program); //load the program in the appropriate physical memory region //set up program stack and jump to the program's entry point //what happens when the program returns? } } //in a different file called alice_wordcount.c void main() { int sec = 20; int nwords = 0; char buf[SECTORSIZE]; ///etc. etc. }
To address this critiquie, the kernel should abstract away h/w details
//Alice's wordcount program void main() { char c; int nwords = 0, n; char buf[100]; while (1) { n = read_file("/home/alice/wordfile", offset, &buf[0], 100); //sys_call, more sophisticated than a normal function call offset += n; ... } print_console("The number of words is %d\n", nwords); }The OS Kernel implements a file service and a display service module (Draw on board)
int sys_read_file(char *filename, int offset, char *buffer, int bytes) { //lookup to decide which secno on which the file resides ... while (buffer not filled) { read_ide_sector(secno, buffer[offset]); offset+=SECTORSIZE } return num_of_bytes_read; }Even better, we can make reading a block from the device modular
int sys_read_file(char *filename, char *buffer, int bytes) { //lookup to decide which disk and which secno on which the file resides ... while (buffer not filled) { read_block(diskno, secno, buffer[offset]); offset+=SECTORSIZE; } } void read_block(int diskno, int addr, char *buffer) { //find disk associated with diskno switch(disk_type){ case IDE: read_ide_sector(sectorno, addr); break; case FLASH: read_flash_sector(sectorno, addr); break; ...etc... } }What have we learned from the above modifications?
Virtualization
Abastraction
void kernelstart(void) { ... program_loader(0x200000, alice_program); program_loader(0x300000, bob_program); current = alice; switch_to(current); ... } void switch_to(alice_program); { //restore saved %eax,%ebx,%esp etc. from memory //jump to saved %eip } void sys_yield_to_another_program() { //save current program's %eax,%ebx,...,%esp,%eip in memory if (current == alice) switch_to(bob); else switch_to(alice); } //in alice's or bob's program void main() { while(1) { ... yield_to_another_program(); .. } }
//stupid alice crashes kernel void main() { char *p = 0x100000; bzero(p, 100000); //!!!!! nothing good will come out of this! } //stupid alice crashes bob's program void main() { char *p = 0x300000; bzero(p, 100000); //wipe out bob's memory, hahaha! }