From b21842ed7a5579818072a5a6202d9cd1e6500ad6 Mon Sep 17 00:00:00 2001 From: tkchia Date: Wed, 6 Sep 2023 18:41:07 +0800 Subject: [PATCH] [metal] Fix video mode filtering & frame buffer ref-counting (#889) Co-authored-by: tkchia --- libc/runtime/pc.internal.h | 15 +++++++++++++++ libc/vga/rlinit-vesa.S | 10 +++------- libc/vga/vga-init.greg.c | 5 ++--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libc/runtime/pc.internal.h b/libc/runtime/pc.internal.h index 6f815fd11..3089c7474 100644 --- a/libc/runtime/pc.internal.h +++ b/libc/runtime/pc.internal.h @@ -159,6 +159,7 @@ #define PAGE_V /* */ 0b000000000001 #define PAGE_RW /* */ 0b000000000010 #define PAGE_U /* */ 0b000000000100 +#define PAGE_PCD /* */ 0b000000010000 #define PAGE_PS /* */ 0b000010000000 #define PAGE_G /* */ 0b000100000000 #define PAGE_IGN1 /* */ 0b111000000000 @@ -196,6 +197,20 @@ void __ref_page(struct mman *, uint64_t *, uint64_t); void __ref_pages(struct mman *, uint64_t *, uint64_t, uint64_t); void __unref_page(struct mman *, uint64_t *, uint64_t); +/** + * Identity maps an area of physical memory to its negative address and + * marks it as permanently referenced and unreclaimable (so that it will + * never be added to the free list). This is useful for special-purpose + * physical memory regions, such as video frame buffers and memory-mapped + * I/O devices. + */ +forceinline void __invert_and_perm_ref_memory_area(struct mman *mm, + uint64_t *pml4t, uint64_t ps, + uint64_t size, + uint64_t pte_flags) { + __invert_memory_area(mm, pml4t, ps, size, pte_flags | PAGE_REFC); +} + forceinline unsigned char inb(unsigned short port) { unsigned char al; asm volatile("inb\t%1,%0" : "=a"(al) : "dN"(port)); diff --git a/libc/vga/rlinit-vesa.S b/libc/vga/rlinit-vesa.S index 8fe3a14de..21681036b 100644 --- a/libc/vga/rlinit-vesa.S +++ b/libc/vga/rlinit-vesa.S @@ -46,11 +46,7 @@ .set "mi::sizeof",14 // Routine to activate additional VESA functionality if the user holds -// down a magic key, when booting from bare metal. Currently this just -// dumps a list of all usable VESA VBE video modes on the console. -// -// TODO: allow user to select a video mode and switch to it -// TODO: implement character drawing in graphics modes +// down a magic key, when booting from bare metal. // // @return CF = 0 if we decided to set a new video mode, CF = 1 otherwise @@ -372,7 +368,7 @@ _rlinit_vesa: jz .fail3 call .video_type # check if we know about the video jc .fail3 # buffer type, & what exact type it is - mov (%bp),%bx # if we are already tracking too + mov (%bp),%bx # if we are already tracking too many cmp $MAX_VESA_MODES_TO_TRACK,%bx # VESA modes, also skip jnb .fail3 inc %bx # otherwise start noting down mode @@ -435,7 +431,7 @@ _rlinit_vesa: mov $PC_VIDEO_TEXT,%al # if text mode, simply say so test $0b00010000,%cl jz .ok4 - cmpl $6,%es:0x1b(%di) # if graphics mode, check if direct + cmpb $6,%es:0x1b(%di) # if graphics mode, check if direct jnz .fail4 # color; bail out if not mov %es:0x19(%di),%cl # check actual color fields & bits mov %es:0x1f(%di),%ax # per pixel, against a list diff --git a/libc/vga/vga-init.greg.c b/libc/vga/vga-init.greg.c index bdfa3ae7a..7cb8b66f7 100644 --- a/libc/vga/vga-init.greg.c +++ b/libc/vga/vga-init.greg.c @@ -53,9 +53,8 @@ void _vga_reinit(struct Tty *tty, unsigned short starty, unsigned short startx, chr_ht = VGA_ASSUME_CHAR_HEIGHT_PX; chr_wid = VGA_ASSUME_CHAR_WIDTH_PX; /* Make sure the video buffer is mapped into virtual memory. */ - __invert_memory_area(mm, __get_pml4t(), vid_buf_phy, vid_buf_sz, - PAGE_RW | PAGE_XD); - __ref_pages(mm, __get_pml4t(), vid_buf_phy, vid_buf_sz); + __invert_and_perm_ref_memory_area(mm, __get_pml4t(), vid_buf_phy, vid_buf_sz, + PAGE_RW | PAGE_XD); /* * Initialize our tty structure from the current screen geometry, screen * contents, cursor position, & character dimensions.