Biscuit Code Reading
Go Conference 2018 Autumn
Learning System Programming with Golang (21)��Hashtag: #gocon_a
Yoshiki Shibukawa
Future Corporation
@shibu_jp
Today’s Content
Who are You?
01
What is Biscuit?
Code Structure
Boot Sequence of Std Go Application
Boot Sequence of Biscuit
Pickup Some Code
02
03
04
05
06
Who Are You?
SECTION ONE
Who Are You?
Yoshiki Shibukawa
Job:
Honda R&D: - Dec.2010
DeNA: - Aug.2017
Future Corporation: Sep.2017-
Family:
Wife and three girls
Books:
つまみぐい勉強法, Real World HTTP, Mithil, � Goならわかるシステムプログラミング
Translation:
Expert Python Programming
Software development with SCRUM
Pomodoro Technique Illustrated
The Art of Community etc
Favorite Languages:
((Type)|(Java))Script / Go / Python
Other hobbies:
Inline skating
Account:
github.com/shibukawa
twitter.com/shibu_jp
フューチャーをざっくり言うと
2016年4月1日に持株会社体制に移行
Most favorite OSS in Future is...
https://github.com/future-architect/vuls
6
Cheetah Grid
https://github.com/future-architect/cheetah-grid
7
We Are Hiring!
Important Information
Gopher icon is designed by Renée French | CC-BY 3.0
This Session Level: Easy
Even If...
{
What is Biscuit?
SECTION TWO
Who Made it?
�
Biscuit is a monolithic, POSIX-subset operating system kernel in Go for x86-64 CPUs.
from README of Biscuit
Features
from README of Biscuit
Build and Run Biscuit
Code Structure
SECTION THREE
Code Structure
Code Structure: biscuit/src
Code Structure: biscuit/src
Where is good entry point to read?
Why Biscuit team bundles whole Golang code?
From The benefits and costs of writing a POSIX kernel in a high-level language
Boot Sequence of Standard Go Application
SECTION FOUR
Executable File Format
Surueña [GFDL or CC BY-SA 3.0], from Wikimedia Commons
Before main() (1)
Before main() (2)
Boot Sequence of Biscuit
SECTION FIVE
Biscuit Boot Sequence for amd64 system
Boot Sequence of Biscuit
BIOS
Bootloader
Biscuit Kernel
/bin/init
/bin/lsh
Runtime Bootstrap
Main code
Bootloader : Summary
Boot Sequence of Biscuit
Get Memory Layout �(BIOS e820)
Switch to Protect Mode (32 bit)
Read kernel image �from storage
Switch to Long Mode (64 bit)
Start running Biscuit kernel
Read remained part of Boot Loader
Boot Loader : Get Memory Layout
e820:� movw $start, %sp� xorl %ebx, %ebx� pushl %ebx� movw $e820m, %di�e820.1:� movl $0xe820, %eax� movl $20, %ecx� movl $0x534d4150, %edx� int $0x15
Boot Loader : Read remained part of Boot Loader
movw $0x7c00 + SECTSIZE, %ax�movw %ax, dap_dest_off�movw $0, dap_dest_seg�movw $1, dap_sectoff_lo��// 13h 42h�// Extended Read Sectors From // Drive�movb $0x42, %ah�movb diskid, %dl�movw $dap, %si�int $0x13
Boot Loader: Switch to Protect Mode (32 bit)
Mode | Bits | Virtual Memory | Ring Protection |
Real Mode | 16 | | |
Protect Mode | 32 | ✔︎ | ✔︎ |
Long Mode | 64 | ✔︎ | ✔︎ |
gdt:� SEG_NULL # 0 - null seg� SEG(STA_X|STA_R, 0x0, 0xffffffff) # 1 - code seg� SEG(STA_W, 0x0, 0xffffffff) # 2 - data seg� SEG64(STA_X|STA_R, 0x0, 0xffffffff) # 3 - 64bit code seg� RSEG(STA_X|STA_R, 0, 0xffff) # 4 - real mode code� RSEG(STA_W, 0, 0xffff) # 5 - real mode data��gdtdesc:� .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1� .long gdt # gdt address
Boot Loader : Read kernel image from storage
Bootloader : Switch to Long Mode (64 bit)
// enter long mode�enable_pae();�enable_global();�lcr3(pgdir);�uint64_t efer = rdmsr(IA32_EFER);�wrmsr(IA32_EFER, efer | IA32_EFER_LME);�enable_paging_wp();
| Control Register | What to do? |
enable_pae() | CR4 | Extend physical address (over 4GB) |
enable_global() | CR4 | Skip TLB reset if the global flag is set |
lcr3() | CR4 | Set Page Directory |
wrmsr | MSR | Set Long Mode Enable flag Year! |
enable_paging_wp() | CR0 | Enable paging and write protection |
Bootloader: Launch Biscuit’s Kernel
// Long Jump into Kernel’s entry point!
#define CODE64 3�ljmp(CODE64, entry, (uint32_t)pgdir, firstfree, NEWSTACK);
Kernel: Entrypoint
Kernel: hackmode flag
TEXT runtime·sysMmap(SB),NOSPLIT,$0� // Jump to mmap_skip if runtime.hackmode is 0� MOVQ runtime·hackmode(SB), DI� TESTQ DI, DI� JZ mmap_skip� JMP ·hack_mmap(SB)�mmap_skip:� :� MOVL $SYS_mmap, AX� SYSCALL� CMPQ AX, $0xfffffffffffff001� JLS ok� :� RET�ok:� MOVQ AX, p+32(FP)� MOVQ $0, err+40(FP)� RET
Kernel: Before main()
Kernel: After main() 1/3
Kernel: After main() 2/3
Kernel: After main() 3/3
Pickup Some Code
SECTION SIX
Hardware Access: Summary
Pickup Some Code
Hardware Access: Port mapped I/O
func Inb(uint16) uint�func Inl(int) int�func Insl(uint16, unsafe.Pointer, uint)�func Outb(uint16, uint8)�func Outl(int, int)�func Outsl(int, unsafe.Pointer, int)�func Outw(int, int)
Hardware Access: Memory mapped I/O
CPU just receive�interrupt when�transporting task�is finished
lapaddr := 0xfee00000�lap := (*[mem.PGSIZE / 4]uint32)� (unsafe.Pointer(uintptr(lapaddr)))�icrh := 0x310 / 4�icrl := 0x300 / 4�icrw := func(hi uint32, low uint32) {� // use sync to guarantee order� atomic.StoreUint32(&lap[icrh], hi)� atomic.StoreUint32(&lap[icrl], low)� ipisent := uint32(1 << 12)� for atomic.LoadUint32(&lap[icrl])&ipisent != 0 {� }�}
Hardware Access: Interrupt
System Call: as an entry point
func (s *syscall_t) Syscall(...) int {� sysno := int(tf[defs.TF_RAX])� :� a1 := int(tf[defs.TF_RDI])� a2 := int(tf[defs.TF_RSI])� a3 := int(tf[defs.TF_RDX])� a4 := int(tf[defs.TF_RCX])� a5 := int(tf[defs.TF_R8])� var ret int� switch sysno {� case defs.SYS_READ:� ret = sys_read(p, a1, a2, a3)� case defs.SYS_WRITE:� ret = sys_write(p, a1, a2, a3)� :� }�}
System Call: MaxLive
lim := _sysbounds[sysno]�if !res.Resadd(lim) {� return int(-defs.ENOHEAP)�}
Conclusion
Conclusion
Conclusion
In addition to that, we can read the OS implementation in Go
(It is under real MIT’s real MIT license)