diff --git a/Makefile b/Makefile index 2be5f8c..733445d 100644 --- a/Makefile +++ b/Makefile @@ -7,11 +7,12 @@ LINKER_SCRIPT ?= linker.ld MYAS ?= gcc OBJ_EXT ?= .o OUT_EXT ?= .img +QEMU ?= qemu-system-i386 RUN ?= bios_hello_world TMP_EXT ?= .tmp -INS := $(wildcard *$(IN_EXT)) -OUTS := $(patsubst %$(IN_EXT),%$(OUT_EXT),$(INS)) +OUTS := $(patsubst %$(IN_EXT),%$(OUT_EXT),$(wildcard *$(IN_EXT))) +RUN_FILE := $(RUN)$(OUT_EXT) .PRECIOUS: %$(OBJ_EXT) .PHONY: all clean run @@ -28,11 +29,18 @@ clean: rm -fr *$(OBJ_EXT) *$(OUT_EXT) *$(TMP_EXT) run: all - qemu-system-i386 '$(RUN)$(OUT_EXT)' + $(QEMU) '$(RUN_FILE)' + +debug: all + $(QEMU) -hda '$(RUN_FILE)' -S -s & + gdb -ex 'target remote localhost:1234' -ex 'break *0x7c00' -ex 'continue' bochs: all + # Supposes size is already multiples of 512. + # `grub-mkrescue` seems to respect that. + CYLINDERS="$$(($$(stat -c '%s' '$(RUN_FILE)') / 512))" && \ bochs -qf /dev/null \ - 'ata0-master: type=disk, path="$(RUN)$(OUT_EXT)", mode=flat, cylinders=1, heads=1, spt=1' \ + 'ata0-master: type=disk, path="$(RUN_FILE)", mode=flat, cylinders='"$$CYLINDERS"', heads=1, spt=1' \ 'boot: disk' \ 'display_library: sdl' \ 'megs: 128' diff --git a/README.md b/README.md index 7c5112c..30ab845 100644 --- a/README.md +++ b/README.md @@ -21,20 +21,22 @@ Hello world programs that run without an operating system. 1. [pixel line](bios_pixel_line.S) 1. [keyboard](bios_keyboard.S) 1. [keyboard loop](bios_keyboard_loop.S) + 1. [disk load](bios_disk_load.S) 1. [reboot](reboot.S) 1. [Not testable in userland](not-testable-in-userland.md) 1. [Segment registers real mode](segment_registers_real_mode.S) 1. [SS (TODO)](ss.S) 1. [Interrupt](interrupt.S) 1. [Interrupt zero divide](interrupt_zero_divide.S) + 1. [Interrupt loop](interrupt_loop.S) 1. in 1. [in keyboard](in_keyboard.S) 1. [in RTC](in_rtc.S) 1. [in PIT (TODO)](in_pit.S) - 1. [in beep (TODO)](in_beep.S) - 1. [in beep_kernel (TODO)](in_beep_kernel.S) - 1. [in beep_illinois (TODO)](in_beep_illinois.S) + 1. [in beep](in_beep.S) + 1. [in beep_illinois](in_beep_illinois.S) 1. [in mouse (TODO)](in_mouse.S) + 1. [Protected mode](protected-mode.S) 1. APM 1. [APM shutdown](apm_shutdown.S) 1. [APM shutdown 2](apm_shutdown2.S) @@ -45,6 +47,7 @@ Hello world programs that run without an operating system. 1. Misc 1. [hajji](hajji/) 1. Theory + 1. [Modes of operation](modes-of-operation.md) 1. [Formats](formats.md) 1. [MBR](mbr.md) 1. [IO](io.md) diff --git a/TODO.md b/TODO.md index 9c24d38..517481c 100644 --- a/TODO.md +++ b/TODO.md @@ -5,32 +5,44 @@ - move to 32 bit mode. Answer http://stackoverflow.com/questions/7130726/writing-a-hello-world-kernel - http://stackoverflow.com/questions/20848412/modes-of-intel-64-cpu -- instructions +- cache: wbinvd - - cache: wbinvd +- inb outb - - inb outb + Answer with bare metal Tetris - Answer with bare metal Tetris + - http://stackoverflow.com/questions/8365746/what-does-outb-in-att-asm-mean + - http://stackoverflow.com/questions/3215878/what-are-in-out-instructions-in-x86-used-for DUPE + - http://stackoverflow.com/questions/3194139/x86-memory-and-i-o-map DUPE + - http://stackoverflow.com/questions/14848645/what-does-this-code-do-in-intel-assembly-language/14848821#14848821 + - http://stackoverflow.com/questions/8894241/real-mode-x86-asm-how-are-the-basics-done?rq=1 + - http://wiki.osdev.org/Text_UI - - http://stackoverflow.com/questions/8365746/what-does-outb-in-att-asm-mean - - http://stackoverflow.com/questions/3215878/what-are-in-out-instructions-in-x86-used-for - - http://wiki.osdev.org/Text_UI + Port 0x80 on Linux kenrnel: - Port 0x80 on Linux kenrnel: https://github.com/torvalds/linux/blob/v4.2/arch/x86/boot/boot.h#L78 http://stackoverflow.com/questions/6793899/what-does-the-0x80-port-address-connects + - https://github.com/torvalds/linux/blob/v4.2/arch/x86/boot/boot.h#L78 + - http://stackoverflow.com/questions/6793899/what-does-the-0x80-port-address-connects - Port 0x61 speaker https://courses.engr.illinois.edu/ece390/books/labmanual/io-devices-speaker.html +- lgdtl - - http://wiki.osdev.org/GUI - - lgdtl, paging http://stackoverflow.com/questions/21128311/the-physical-address-of-global-descriptor-table http://stackoverflow.com/questions/7415515/problem-accessing-control-registers-cr0-cr2-cr3 - - lidtl, interrupts, IDTR - - https://en.wikipedia.org/wiki/Double_fault - - https://en.wikipedia.org/wiki/Triple_fault - - http://stackoverflow.com/questions/1817577/what-does-int-0x80-mean-in-assembly-code - - http://stackoverflow.com/questions/12464329/is-it-possible-to-make-a-custom-interrupt-in-assembly - - WRMSR https://en.wikipedia.org/wiki/Model-specific_register http://x86.renejeschke.de/html/file_module_x86_id_326.html + - http://stackoverflow.com/questions/21128311/the-physical-address-of-global-descriptor-table + - http://stackoverflow.com/questions/7415515/problem-accessing-control-registers-cr0-cr2-cr3 + - http://stackoverflow.com/questions/10671147/how-do-x86-page-tables-work?rq=1 + - http://stackoverflow.com/questions/14354626/how-to-create-two-separate-segments-in-global-descriptor-table -- Segment registers on protected mode. Then try to answer all of: +- lidtl, interrupts, IDTR + + - http://stackoverflow.com/questions/3392831/what-happens-in-an-interrupt-service-routine + - http://stackoverflow.com/questions/1817577/what-does-int-0x80-mean-in-assembly-code + - http://stackoverflow.com/questions/12464329/is-it-possible-to-make-a-custom-interrupt-in-assembly + - https://en.wikipedia.org/wiki/Double_fault + - https://en.wikipedia.org/wiki/Triple_fault + +- paging + +- WRMSR https://en.wikipedia.org/wiki/Model-specific_register http://x86.renejeschke.de/html/file_module_x86_id_326.html + +- Segment registers and protected mode. Then try to answer all of: - http://reverseengineering.stackexchange.com/questions/2006/how-are-the-segment-registers-fs-gs-cs-ss-ds-es-used-in-linux - http://stackoverflow.com/questions/10810203/what-is-the-fs-gs-register-intended-for @@ -49,21 +61,19 @@ - http://stackoverflow.com/questions/6611346/amd64-fs-gs-registers-in-linux - http://stackoverflow.com/questions/7844963/how-to-interpret-segment-register-accesses-on-x86-64?lq=1 - http://stackoverflow.com/questions/928082/why-does-the-mov-instruction-have-to-be-used-this-way?lq=1 + - http://stackoverflow.com/questions/14661916/gdt-segmented-memory + - http://stackoverflow.com/questions/18247106/implementing-gdt-with-basic-kernel + - http://stackoverflow.com/questions/9172837/idt-without-gdt-using-grub + - http://stackoverflow.com/questions/22962251/how-to-enter-64-bit-mode-on-a-x86-64/22963701#22963701 64-bit: + - http://stackoverflow.com/questions/22962251/how-to-enter-64-bit-mode-on-a-x86-64/22963701#22963701 - http://stackoverflow.com/questions/21165678/why-64-bit-mode-long-mode-doesnt-use-segment-registers - http://stackoverflow.com/questions/19502868/meaning-of-cs-and-ss-registers-on-x86-64-linux-in-userland - http://stackoverflow.com/questions/7844963/how-to-interpret-segment-register-accesses-on-x86-64 -- load a stage 2 from disk into memory - - - http://stackoverflow.com/questions/7716427/loading-2nd-stage-of-bootloader-and-starting-it - - http://stackoverflow.com/questions/2065370/how-to-load-second-stage-boot-loader-from-first-stage - -- interrupt - -- timer +- timer, IPT - BIOS @@ -71,7 +81,9 @@ - ACPI -- multithreading: http://stackoverflow.com/questions/7308391/how-is-concurrency-done-in-intel-x86-assembly || http://stackoverflow.com/questions/980999/what-does-multicore-assembly-language-look-like || http://stackoverflow.com/questions/714905/threads-in-x86-assembler-using-the-gnu-assember-as || https://github.com/cirosantilli/oszur11-operating-system-examples/tree/1af6451852887fac3d7206d4d09714c181c81d1e/Chapter_07_Threads +- multithreading: + + http://stackoverflow.com/questions/7308391/how-is-concurrency-done-in-intel-x86-assembly || http://stackoverflow.com/questions/980999/what-does-multicore-assembly-language-look-like || http://stackoverflow.com/questions/714905/threads-in-x86-assembler-using-the-gnu-assember-as || https://github.com/cirosantilli/oszur11-operating-system-examples/tree/1af6451852887fac3d7206d4d09714c181c81d1e/Chapter_07_Threads - play with hardware @@ -83,3 +95,21 @@ - GPU... - POST https://en.wikipedia.org/wiki/Power-on_self-test + +- 2 stage two stage boot: load another part from the disk + + - http://stackoverflow.com/a/28645943/895245 contains: + + [bits 16] + [org 0x7c00] + mov ax, 0201h + mov cx, 0002h + mov dh, 0 + mov bx, 0 + mov es, bx + mov bx, 2000h + int 13h + jmp 0:2000h + + [SECTION signature start=0x7dfe] + dw 0AA55h diff --git a/bibliography.md b/bibliography.md index 1570135..9e8ab2f 100644 --- a/bibliography.md +++ b/bibliography.md @@ -2,29 +2,71 @@ - Information about assembly in general. + README.md explain `make qemu` + - +## Small projects + - tested on Ubuntu 14.04. Just works. Has Multiboot and El Torito. Uses custom linker script. Almost entirely in C `-nostdlib`, with very few inline `asm` commands, and a small assembly entry point. So a good tutorial in how to do the bridge. +The following did not work on my machine out of the box: + +- +- + +## Multi collaborator websites + - osdev.org is a major source for this. - - - -- - - Several examples of increasing complexity. Found at: - -- A more automated / general bare metal compilation system. Untested, but looks promising. +- - Illinois course from 2004 -The following did not work on my machine out of the box: +## Progressive tutorials -- -- +- + + Multiboot based kernels of increasing complexity, one example builds on the last one. Non DRY as a result. + + Source code: + + Well known bugs: That's what happens when you don't use GitHub. + + Good tutorials, author seems to master the subject. + + But he could learn more about version control and build automation: source code inside ugly tar.gz with output files. + +- + + GitHub mirror: + + Several examples of increasing complexity. Found at: + + Just works, but examples are non-minimal, lots of code duplication and blobs. There must be around 20 El Torito blobs in that repo. + +- + +- + +## Actually useful + +These are not meant as learning resources but rather as useful programs: + +- A more automated / general bare metal compilation system. Untested, but looks promising. + +## Other archs + +For when we decide to port this tutorial: + +ARM: + +- diff --git a/bios_disk_load.S b/bios_disk_load.S new file mode 100644 index 0000000..f1c6f6e --- /dev/null +++ b/bios_disk_load.S @@ -0,0 +1,77 @@ +/* +Load one more sector from the disk +besides the first 512 bytes and do something with it.. + +Expected outcome: `@` gets printed to the screen. + +TODO: not working on: + +- Bochs: `BOUND_GdMa: fails bounds test`. +- GRUB `chainloader` through big.img + +Does work on QEMU and Thinkpad T400. + +Bibliography: + +- http://wiki.osdev.org/ATA_in_x86_RealMode_%28BIOS%29 +- https://en.wikipedia.org/wiki/INT_13H + +TODO answer: + +- https://thiscouldbebetter.wordpress.com/2011/03/15/creating-a-bootable-program-in-assembly-language/ +- http://stackoverflow.com/questions/7716427/loading-2nd-stage-of-bootloader-and-starting-it +- http://stackoverflow.com/questions/2065370/how-to-load-second-stage-boot-loader-from-first-stage +- http://stackoverflow.com/questions/19381434/cannot-read-disk-sectors-in-assembly-language +- http://stackoverflow.com/questions/15497842/read-a-sector-from-hard-drive-with-int-13h +- http://stackoverflow.com/questions/9899577/example-for-int-13-ah-03h-interupt-assembly +*/ + +#include "common.h" +BEGIN + CLEAR + + /* + Reset disk. TODO is this really needed? + Was suggested in one tutorial. + */ + /* + mov $0, %ah + mov $0x80, %dl + int $0x13 + */ + + /* Read sectors into memory */ + mov $2, %ah + /* Number of sectors to read. */ + mov $1, %al + /* Drive number. Starts at 0x80, second one is 0x81. TODO why not from 0? */ + mov $0x80, %dl + /* cylinder number */ + mov $0, %ch + /* Head number */ + mov $0, %dh + /* Starting sector number. 2 because 1 was already loaded. */ + mov $2, %cl + /* Where to load to. Must coincide with our stage2 for the linking to work. */ + mov $stage2, %bx + int $0x13 + + jmp stage2 + +/* +Our linker script will put this section on the right place in memory: +just after the magic bytes. +*/ +.section .stage2 +stage2: + PUTC($0x0E40) + hlt + /* + Fill up the second sector to 512 byte. + + This should be on the linker script as well in decent programs, + but let's just put it here to keep our other images smaller. + */ + .org 512 +END + diff --git a/common.h b/common.h index 8a64e36..89bd3a7 100644 --- a/common.h +++ b/common.h @@ -7,7 +7,7 @@ The big ones do bloat the executable. .altmacro #define BEGIN \ - .code16;\ + .code16 ;\ cli ;\ xor %ax, %ax ;\ /* We must zero %ds for any data access.. */ \ diff --git a/grub/introduction.md b/grub/introduction.md index c14bb92..aed276f 100644 --- a/grub/introduction.md +++ b/grub/introduction.md @@ -283,6 +283,16 @@ You can then check that they've appeared under `cat /proc/cmdline`. - `syslinux`: Linux specific. Used by default by the kernel, e.g. on 4.2 `make isoimage`. - LILO: old popular bootloader, largely replaced by GRUB now. +## Legacy + +Documentation: + +### kernel + +Directive used to boot *both* multiboot and Linux. + +Got split up more or less into `multiboot` and `linux` directives. + ## Bibliography - diff --git a/in.md b/in.md index 8ba05e7..f4da07f 100644 --- a/in.md +++ b/in.md @@ -6,6 +6,16 @@ IO operations. Do not work with immediates: only registers or memory locations. +## Memory mapped vs Port mapped IO + + + +`in` and `out` are used for port mapped IO. + +In memory mapped IO, IO is done just like RAM IO. + +TODO: see some circuit schematics. + ## Bit 0x80 TODO http://wiki.osdev.org/CMOS#Getting_Current_Date_and_Time_from_RTC says: @@ -14,3 +24,7 @@ TODO http://wiki.osdev.org/CMOS#Getting_Current_Date_and_Time_from_RTC says: out 0x70, al What does it mean? + +## Linux kernel + + man outb diff --git a/in_beep.S b/in_beep.S index cb4d586..eca7f62 100644 --- a/in_beep.S +++ b/in_beep.S @@ -1,27 +1,65 @@ /* -The beep circuit is linked to the PIT. +TODO not working on QEMU, but does produce some horrible sound on real hard. +Maybe because I cannot get the beep working on my Ubuntu host? +http://askubuntu.com/questions/19906/beep-in-shell-script-not-working -I think it is possible to make a beep after a certain amount of time passes. +It looks like the beep (port 0x61) just uses +the PIT Channel 2 to generate the frequency, so understand the PIT first. -http://fly.srk.fer.hr/GDM/articles/sndmus/speaker1.html +Extracted from: +https://github.com/torvalds/linux/blob/v4.2/arch/x86/realmode/rm/wakemain.c#L38 +The kernel has a Morse code encoder with it! + +Not using io_delay here, maybe would sound better with it? + +See also: + +- http://fly.srk.fer.hr/GDM/articles/sndmus/speaker1.html + +## Port 0x61 + +http://wiki.osdev.org/PC_Speaker + +Speaker specifics are there. +The 0x4X IO are the PIT. */ #include "common.h" -.equ div, 1193181 / 1000; - BEGIN -start: - PUTC($0x61) + /* Chanel 2, square wave, load TODO?, binary */ mov $0xb6, %al - out %al, $0x43 /* Ctr 2, squarewave, load, binary */ - mov div, %al - out %al, $0x42 /* LSB of counter */ - mov div, %al - shr $8, %al - out %al, $0x42 /* MSB of counter */ - in $0x61, %al /* Dummy read of System Control Port B */ + out %al, $0x43 + + /* Set frequency of Channel 2. */ + .equ div, 1193181 / 1000 + mov div, %ax + out %al, $0x42 + mov %ah, %al + out %al, $0x42 + + /* Dummy read of System Control Port B. TODO why? */ + in $0x61, %al + + /* + Enable timer 2 output to speaker. + + THIS is where the sound begins. + */ mov $0x03, %al - out %al, $0x61 /* Enable timer 2 output to speaker */ - jmp start + out %al, $0x61 + + /* Loop forever to keep hearing it. */ +loop: + nop + jmp loop + + /* + This is how a sound can be stopped. + + This code never runs in this example. + */ + in $0x61, %al + mov $0x00, %al + out %al, $0x61 END diff --git a/in_beep_illinois.S b/in_beep_illinois.S index 15083e9..e13dd8b 100644 --- a/in_beep_illinois.S +++ b/in_beep_illinois.S @@ -1,7 +1,7 @@ /* -https://courses.engr.illinois.edu/ece390/books/labmanual/io-devices-speaker.html +Originally from: https://courses.engr.illinois.edu/ece390/books/labmanual/io-devices-speaker.html -TODO not working on QEMU +Same as the kernel version. */ #include "common.h" @@ -9,29 +9,40 @@ BEGIN start: PUTC($0x61) - movb $182,%al # Prepare the speaker for the - outb %al, $0x43 # note. - movw $4560,%ax # Frequency number (in decimal) - # for middle C. - outb %al, $0x42 # Output low byte. - movb %ah,%al # Output high byte. - outb %al, $0x42 - inb $0x61,%al # Turn on note (get value from - # port 61h). - orb $0b00000011,%al # Set bits 1 and 0. - outb %al, $0x61 # Send new value. - movw $25,%bx # Pause for duration of note. + mov $0xb6, %al + out %al, $0x43 + + mov $4560, %ax + out %al, $0x42 + + mov %ah, %al + out %al, $0x42 + + in $0x61, %al + + /* TODO why or, while Linux kernel sets it to 3? */ + or $0b00000011, %al + out %al, $0x61 + + /* + Pause for duration of note. + + Busy loop of `25 * 2 ^ 16 - 1` + */ + mov $25, %bx .pause1: - movw $65535,%cx + mov $65535, %cx .pause2: - decw %cx - jne .pause2 - decw %bx - jne .pause1 - inb $0x61,%al # Turn off note (get value from - # port 61h). - andb $0b11111100,%al # Reset bits 1 and 0. - outb %al, $0x61 # Send new value. + dec %cx + jne .pause2 + dec %bx + jne .pause1 + + in $0x61, %al + + /* TODO why Reset bits 1 and 0. */ + and $0b11111100, %al + out %al, $0x61 jmp start END diff --git a/in_beep_kernel.S b/in_beep_kernel.S deleted file mode 100644 index 32d85f0..0000000 --- a/in_beep_kernel.S +++ /dev/null @@ -1,25 +0,0 @@ -/* -Extracted from: https://github.com/torvalds/linux/blob/v4.2/arch/x86/realmode/rm/wakemain.c#L38 - -TODO not working on QEMU -*/ - -#include "common.h" - -.equ div, 1193181 / 1000; - -BEGIN -start: - PUTC($0x61) - mov $0xb6, %al - out %al, $0x43 /* Ctr 2, squarewave, load, binary */ - mov div, %al - out %al, $0x42 /* LSB of counter */ - mov div, %al - shr $8, %al - out %al, $0x42 /* MSB of counter */ - in $0x61, %al /* Dummy read of System Control Port B */ - mov $0x03, %al - out %al, $0x61 /* Enable timer 2 output to speaker */ - jmp start -END diff --git a/in_mouse.S b/in_mouse.S index 714b407..0e7bc96 100644 --- a/in_mouse.S +++ b/in_mouse.S @@ -1,5 +1,10 @@ /* -TODO http://wiki.osdev.org/Mouse_Input +TODO get working + +Bibliography: + +- http://wiki.osdev.org/Mouse_Input +- https://courses.engr.illinois.edu/ece390/books/labmanual/io-devices-mouse.html I am so going to make a pixel drawing program with this. */ diff --git a/in_pit.S b/in_pit.S index e595f1b..b34db0b 100644 --- a/in_pit.S +++ b/in_pit.S @@ -3,15 +3,52 @@ TODO get working -TODO answer with example http://stackoverflow.com/questions/13950264/does-using-tsc-as-clock-source-improve-timer-and-scheduling-granularity +TODO answer with example: + +- http://stackoverflow.com/questions/13950264/does-using-tsc-as-clock-source-improve-timer-and-scheduling-granularity +- http://stackoverflow.com/questions/26853905/use-of-irq0-pit-in-x86-linux +- http://stackoverflow.com/questions/12229644/time-sources-in-x86-processors +- http://stackoverflow.com/questions/26321531/pit-not-sending-interrupts-to-irq0 + +TODO where is it documented? Programmable interrupt timer. +Read this *now*: http://wiki.osdev.org/PIT + Can generate periodic interrupts, or sounds. -The kernel has a Morse code encoder with it. https://github.com/torvalds/linux/blob/v4.2/arch/x86/realmode/rm/wakemain.c#L38 +Has 3 channels that can generate 3 independent signals -Going to use in 0x40 +- channel 0 at port 0x40: generates interrupts +- channel 1 at port 0x41: not to be used for some reason +- channel 2 at port 0x42: linked to the speaker to generate sounds + +Port 0x43 is used to control signal properties except frequency +(which goes in the channel ports) for the 3 channels. +See osdev article for details. + +## PIC + +Programmable interrupt controller: + +- http://wiki.osdev.org/PIC +- http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html + +Controls interrupts. Ports: + +- 0x0020: Master PIC command +- 0x0021: Master PIC data +- 0x00A0: Slave PIC command +- 0x00A1: Slave PIC Data + +### EOI + +End of interrupt. + +We must tell the PIC that we are at the end. TODO otherwise what? + +https://en.wikipedia.org/wiki/End_of_interrupt ## 1193181 @@ -19,28 +56,63 @@ Magic number that is the frequency of the oscillator. http://f.osdev.org/viewtopic.php?f=1&t=15503 +Frequency divisor. + +Produce 1000Hz signal. + +1193181 is the source frequency. + +This is an electronics concept: +https://en.wikipedia.org/wiki/Frequency_divider + +2 occurrences on Linux 4.2. + ## Bibliography - https://en.wikipedia.org/wiki/Intel_8253 -- http://wiki.osdev.org/PIT - http://kernelx.weebly.com/programmable-interval-timer.html */ #include "common.h" -#define PIT_CHANNEL0 $0x40 -#define PIT_CHANNEL1 $0x41 -#define PIT_CHANNEL2 $0x42 -#define PIT_CMDREG $0x43 - BEGIN -start: - #u16 div = 1193181/hz; - # Linux kernel - #outb(0xb6, 0x43); /* Ctr 2, squarewave, load, binary */ - #outb(div, 0x42); /* LSB of counter */ - #outb(div >> 8, 0x42); /* MSB of counter */ - #enable = 0x03; /* Turn on speaker */ - #inb(0x61); /* Dummy read of System Control Port B */ - #outb(enable, 0x61); /* Enable timer 2 output to speaker */ + /* Setup interrupt handler 0. */ + movw $handler, 0x00 + mov %cs, 0x02 + + /* + Define the properties of the wave: + + - Channel: 0 + - access mode: lobyte/hibyte + - operating mode: rate generator + - BCD/binary: binary + */ + mov $0b00110100, %al + out %al, $0x43 + + /* + Set frequency of Channel 0. + + We have to split the 2 ax bytes, + as we can only communicate one byte at a time here. + */ + .equ div, 1193181 / 1000 + mov div, %ax + out %al, $0x40 + mov %ah, %al + out %al, $0x40 + + PUTC($0x61) + sti + +loop: + nop + jmp loop + +handler: + PUTC($0x61) + iret + + /* TODO turn off. */ END diff --git a/interrupt.S b/interrupt.S index 1907bce..e672a75 100644 --- a/interrupt.S +++ b/interrupt.S @@ -13,13 +13,24 @@ TODO: interrupt priority: order looks like: 0, 1, 2, 8, 9, 10, 11, 12, 13, 14, 1 Returns to the next instruction to be executed before the interrupt came in. + +TODO I think this is mandatory, e.g. a `jmp` wouldn't be enough. +Otherwise what? + +## ISR + +## Interrupt service routines + +Fancy name for the handler. + +http://wiki.osdev.org/Interrupt_Service_Routines */ #include "common.h" BEGIN CLEAR movw $handler, 0x00 - movw %cs, 0x02 + mov %cs, 0x02 int $0 PUTC($0x62) jmp end diff --git a/interrupt_loop.S b/interrupt_loop.S new file mode 100644 index 0000000..18f6c6d --- /dev/null +++ b/interrupt_loop.S @@ -0,0 +1,13 @@ +/* +*/ + +#include "common.h" +BEGIN + CLEAR + movw $handler, 0x00 + mov %cs, 0x02 + int $0 +handler: + PUTC($0x61) + int $0 +END diff --git a/linker.ld b/linker.ld index b23c52c..eaffd56 100644 --- a/linker.ld +++ b/linker.ld @@ -8,13 +8,34 @@ SECTIONS . = 0x7c00; .text : { - *(*) + /* + We are going to stuff everything + into a text segment for now, including data. + Who cares? Other segments only exist to appease C compilers. + */ + *(.text) + /* Magic bytes. 0x1FE == 510. - We could add this on each Gas file. + We could add this on each Gas file separately with `.word`, + but this is the perfect place to DRY that out. */ . = 0x1FE; SHORT(0xAA55) + + /* + This is only needed if we are going to use a 2 stage boot process, + e.g. by reading more disk than the default 512 bytes with BIOS `int 0x13`. + */ + *(.stage2) } } + +/* +The linux kernel 4.2 uses linker scripts like: + +- https://github.com/torvalds/linux/blob/v4.2/arch/x86/boot/setup.ld + +The kernel also uses the `.lds` extension for its scripts. +*/ diff --git a/modes-of-operation.md b/modes-of-operation.md new file mode 100644 index 0000000..25d10ea --- /dev/null +++ b/modes-of-operation.md @@ -0,0 +1,32 @@ +# Modes of operation + +Covered on Intel Volume 3. Specially useful is the "Transitions Among the Processor’s Operating Modes" diagram. + +- Protected +- Real address +- System management +- IA-32e. Has two sub modes: + - Compatibility + - 64-bit + +## Real mode + +CPU starts here for backwards compatibility. + +## IA-32e + +Wikipedia seems to call it *long mode*: + +Contains two sub-modes: 64-bit and compatibility. + +64-bit is the major mode of operation, compatibility mode emulates IA-32. This is where systems run most of the time. + +The other mode is legacy mode, which as the name implies, should not be used on new programs. + +### Compatibility mode + +Controlled by the `CS.L` bit of the segment descriptor. + +It appears that it is possible for user programs to modify that during execution: + + diff --git a/protected_mode.S b/protected_mode.S new file mode 100644 index 0000000..5abbba8 --- /dev/null +++ b/protected_mode.S @@ -0,0 +1,64 @@ +/* +# Protected mode + +TODO get working. + +- http://wiki.osdev.org/Journey_To_The_Protected_Land +- http://wiki.osdev.org/Protected_Mode +- http://stackoverflow.com/questions/28645439/how-do-i-enter-32-bit-protected-mode-in-nasm-assembly +- https://github.com/chrisdew/xv6/blob/master/bootasm.S +- https://thiscouldbebetter.wordpress.com/2011/03/17/entering-protected-mode-from-assembly/ FASM based. Did not word on first try, but looks real clean. + +## GDT + +Table in memory that gives properties of segment registers. + +Segment registers in protected mode point to entries of that table. + +The GDT modifies every memory access of a given segment by adding an offset to it. + +GDT is used as soon as we enter protected mode, so that's why we have to deal with it, but the preferred way of managing program memory spaces is paging. + +## GDTR + +## GDT register + +In 32-bit, a 6 byte register that holds: + +- 2 byte length of the GDT (TODO in bytes or number of entries?) +- 4 byte address of the GDT in memory + +In 64 bit, makes 10 bytes, with the address having 8 bytes + +GRUB seems to setup one for you: http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html +*/ + +#include "common.h" +BEGIN +cli + + /* Set the GDT register with start address of Global Descriptor Table */ + lgdt gdt + mov %cr0, %eax + /* Set PE (Protection Enable) bit in CR0 (Control Register 0) */ + or $1, %al + mov %eax, %cr0 + + /* + TODO why 8? + + Perform far jump to selector 08h (offset into GDT, + pointing at a 32bit PM code segment descriptor) + to load CS with proper PM32 descriptor). + */ + ljmp $0x08, $PModeMain + + PModeMain: + /* TODO load DS, ES, FS, GS, SS, ESP. */ + + hlt + + gdt: + .word 0x1234 + .long 0x12345678 +END diff --git a/segment_registers_real_mode.S b/segment_registers_real_mode.S index 1e347a1..25d1043 100644 --- a/segment_registers_real_mode.S +++ b/segment_registers_real_mode.S @@ -9,14 +9,18 @@ On failure, trash is shows. I think their goal was to implement process virtualization in the past. -In protected mode, they do very different things: +FS and GS are general purpose: they don't generate or are affected implicitly by instructions. + +The special semantics of other registers will be covered in other files. + +## Vs protected mode + +In protected mode, the segment registers point to entries on the GDT: - http://reverseengineering.stackexchange.com/questions/2006/how-are-the-segment-registers-fs-gs-cs-ss-ds-es-used-in-linux - http://wiki.osdev.org/Segmentation -FS and GS are general purpose: they don't generate or are affected implicitly by instructions. - -The special semantics of other registers will be covered in other files. +Those entries still contain an analogous offset to the real mode offset, but also much more data, like segment width and permission. ## How they work