Update README with link to UEFI tutorial
[rrq/jonasforth.git] / README.md
index 2ed38d52851dd3679fa2236efc9bb14a795d45c1..572e58a31e56ff4b10f45345896c7d6fc1a7475f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,27 +1,65 @@
 # Building and running
 
-Create the executable:
+You can run JONASFORTH inside QEMU or on real hardware. If you want to run
+inside QEMU, you should have the following dependencies installed (assuming
+Arch Linux):
 
-    $ make main
+    $ pacman -S qemu ovmf
 
-The `sys.f` file contains code that defines some of the usual words that you
-would expect in a Forth distribution. To run this code and then read from
-standard input, run:
+Then, to run a UEFI shell inside QEMU, run:
 
-    $ cat sys.f - | ./main
+    $ make qemu
 
-The `example.f` file contains an example that you can run with:
+JONASFORTH will be available as `main` on `FS0:`. Thus, to run it, you can run
+the following command inside the UEFI shell:
 
-    $ cat sys.f example.f | ./main
+    Shell> fs0:main
+    Ready.
+    S" Hello, World!" TELL
+    Hello World!
+
+(Try typing in the code in `example.f` for something a little more
+interesting.)
+
+## Running on real hardware
+
+Making the program run on physical hardware is pretty easy. Just create a
+FAT32-formatted USB drive, and copy `out/main` to it. Then, you can execute the
+program in the same way that you did from inside QEMU, assuming your system
+comes with a UEFI shell built-in.
+
+If your system doesn't have a UEFI shell, then you can copy the executable to
+`\EFI\BOOT\BOOTx64.EFI` on the USB drive. Then, the system should be able to
+boot from the USB drive and directly into JONASFORTH. The way to do this is a
+little bit different depending on the exact firmware, but most firmwares will
+have some way to enter a boot menu where you can select the USB drive. You may
+need to disable Secure Boot first.
+
+To format a USB drive as FAT32, you can run
+
+    # mkfs.vfat -F32 /dev/sdx
+
+with `/dev/sdx` replaced by the path of your USB drive. Then mount the drive,
+and copy `out/main` to `\EFI\BOOT\BOOTx64.EFI`:
+
+    $ mkdir mnt
+    # mount /dev/sdx mnt
+    $ mkdir -p mnt/EFI/BOOT
+    $ make out/main
+    $ cp out/main mnt/EFI/BOOT/BOOTx64.EFI
+
+Now you should be able to boot directly from the USB drive.
 
 # Notes on implementation
 
-This is my summary of the most important parts of
-https://raw.githubusercontent.com/nornagon/jonesforth/master/jonesforth.S.
+The implementation is based on
+[JONESFORTH](https://raw.githubusercontent.com/nornagon/jonesforth/master/jonesforth.S).
+This is my summary of the most important parts.
 
 ## Dictionary
 
-In Forth, words are stored in a dictionary. The dictionary is a linked list whose entries look like this:
+In Forth, words are stored in a dictionary. The dictionary is a linked list
+whose entries look like this:
 
     +------------------------+--------+---------- - - - - +----------- - - - -
     | LINK POINTER           | LENGTH/| NAME              | DEFINITION
@@ -112,11 +150,11 @@ and then manipulates ESI to skip over the literal value.
 
 ## Built-in variables
 
-* **STATE** -- Is the interpreter executing code (0) or compiling a word (non-zero)?
-* **LATEST** -- Points to the latest (most recently defined) word in the dictionary.
-* **HERE** -- Points to the next free byte of memory.  When compiling, compiled words go here.
-* **S0** -- Stores the address of the top of the parameter stack.
-* **BASE** -- The current base for printing and reading numbers.
+- **STATE** -- Is the interpreter executing code (0) or compiling a word (non-zero)?
+- **LATEST** -- Points to the latest (most recently defined) word in the dictionary.
+- **HERE** -- Points to the next free byte of memory. When compiling, compiled words go here.
+- **S0** -- Stores the address of the top of the parameter stack.
+- **BASE** -- The current base for printing and reading numbers.
 
 ## Input and lookup
 
@@ -155,3 +193,131 @@ then switches back to immediate mode.
 These words rely on `,` to append words to the currently compiling definition.
 This word simply appends some literal value to `HERE` and moves the `HERE`
 pointer forward.
+
+# Notes on UEFI
+
+`JONASFORTH` is runs without an operating system, instead using the facilities
+provided by UEFI by running as a UEFI application. (Or rather, in the future it
+hopefully will. Right now, it uses Linux.) This section contains some notes
+about how this functionality is implemented.
+
+I also wrote an entire tutorial that descirbes how to write and compile a
+"Hello, World!" UEFI application, including how to run it on real hardware,
+which you can find here: [Getting started with bare-metal
+assembly](https://johv.dk/blog/bare-metal-assembly-tutorial.html).
+
+## Packaging and testing the image
+
+UEFI expects a UEFI application to be stored in a FAT32 file system on a
+GPT-partitioned disk.
+
+Luckily, QEMU has a convenient way of making a subdirectory availabe as a
+FAT-formatted disk (see [the relevant section in the QEMU User
+Documentation](https://qemu.weilnetz.de/doc/qemu-doc.html#disk_005fimages_005ffat_005fimages)
+for more information):
+
+    $ qemu-sytem-x86_64 ... -hda fat:/some/directory
+
+We use this to easily test the image in QEMU; see the Makefile for more
+information, or just run the `qemu` target to run the program inside of QEMU
+(of course, you must have QEMU installed for this to work):
+
+    $ make qemu
+
+## Interfacing with UEFI
+
+From [OSDev Wiki](https://wiki.osdev.org/UEFI#How_to_use_UEFI):
+
+> Traditional operating systems like Windows and Linux have an existing software
+> architecture and a large code base to perform system configuration and device
+> discovery. With their sophisticated layers of abstraction they don't directly
+> benefit from UEFI. As a result, their UEFI bootloaders do little but prepare
+> the environment for them to run.
+>
+> An independent developer may find more value in using UEFI to write
+> feature-full UEFI applications, rather than viewing UEFI as a temporary
+> start-up environment to be jettisoned during the boot process. Unlike legacy
+> bootloaders, which typically interact with BIOS only enough to bring up the OS,
+> a UEFI application can implement sophisticated behavior with the help of UEFI.
+> In other words, an independent developer shouldn't be in a rush to leave
+> "UEFI-land".
+
+For `JONASFORTH`, I have decided to run as a UEFI application, taking advantage
+of UEFI's features, including its text I/O features and general graphical device
+drivers. Eventually, we would like to add some basic graphical drawing
+capabilities to `JONASFORTH`, and it's my impression that this would be possible
+using what is provided to us by UEFI.
+
+A UEFI images is basically a windows EXE without symbol tables. There are three
+types of UEFI images; we use the EFI application, which has subsystem `10`. It
+is an x68-64 image, which has value `0x8664`.
+
+UEFI applications use [Microsoft's 64-bit calling
+convention](https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention)
+for x68-64 functions. See the linked article for a full description. Here is
+the short version:
+
+- Integer or pointer arguments are given in RCX, RDX, R8 and R9.
+- Additional arguments are pushed onto the stack from right to left.
+- Integer or pointer values are returned in RAX.
+- An integer-sized struct is passed directly; non-integer-sized structs are passed as pointers.
+- The caller must allocate 32 bytes of "shadow space" on the stack immediately
+  before calling the function, regardless of the number of parameters used, and
+  the caller is responsible for popping the stack afterwards.
+- The following registers are volatile (caller-saved): RAX, RCX, RDX, R8, R9, R10, R11
+- The following registers are nonvolatile (callee-saved): RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15
+
+When the application is loaded, RCX contains a firmware allocated `EFI_HANDLE`
+for the UEFI image, RDX contains a `EFI_SYSTEM_TABLE*` pointer to the EFI system
+table and RSP contains the return address. For more infromation about how a UEFI
+application is entered, see "4 - EFI System Table" in [the latest UEFI
+specification as of March 2020 (PDF)](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf).
+
+**Sources:**
+
+- [UEFI applications in detail - OSDev Wiki](https://wiki.osdev.org/UEFI#UEFI_applications_in_detail)
+- [Microsoft x64 calling convention](https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention)
+- [UEFI Specifications](https://uefi.org/specifications)
+
+### UEFI with FASM
+
+We might want to consider using something like this: https://wiki.osdev.org/Uefi.inc)
+
+FASM can generate UEFI application binaries by default. Use the following
+template to output a 64-bit UEFI application:
+
+    format pe64 dll efi
+    entry main
+
+    section '.text' code executable readable
+
+    main:
+       ;; ...
+       ret
+
+    section '.data' data readable writable
+
+    ;; ...
+
+Use `objdump -x` to inspect the assembled application binary.
+
+### UEFI documentation
+
+- [Latest specification as of March 2020 (PDF)](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_A_Feb14.pdf)
+
+Notable sections:
+
+- 2\. Overview (14)
+- 4\. EFI System Table (89)
+- 7\. Services - Boot Services (140)
+- 8\. Services - Runtime Services (228)
+- 12\. Protocols - Console Support (429)
+- 13\. Protocols - Media Access (493)
+- Appendix B - Console (2201)
+- Appendix D - Status Codes (2211)
+
+## Resources
+
+- [UEFI - OSDev Wiki](https://wiki.osdev.org/UEFI)
+- [Unified Extensible Firmware Interface (Wikipedia)](https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface)
+- [UEFI Specifications](https://uefi.org/specifications)