├── COPYING ├── Makefile ├── csum8.c ├── design.txt ├── rom16.ld ├── sgabios.S └── sgabios.h /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2010 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # $Id$ 16 | 17 | BUILD_DATE = \"$(shell date -u)\" 18 | BUILD_SHORT_DATE = \"$(shell date -u +%D)\" 19 | BUILD_HOST = \"$(shell hostname)\" 20 | BUILD_USER = \"$(shell whoami)\" 21 | 22 | CFLAGS := -Wall -Os -m32 -nostdlib 23 | 24 | ASFLAGS := $(CFLAGS) 25 | ASFLAGS += -DBUILD_DATE="$(BUILD_DATE)" 26 | ASFLAGS += -DBUILD_SHORT_DATE="$(BUILD_SHORT_DATE)" 27 | ASFLAGS += -DBUILD_HOST="$(BUILD_HOST)" 28 | ASFLAGS += -DBUILD_USER="$(BUILD_USER)" 29 | 30 | LDSCRIPT := rom16.ld 31 | LDFLAGS := -T $(LDSCRIPT) -nostdlib 32 | OBJCOPY := objcopy 33 | HOSTCC := $(CC) 34 | 35 | ASRCS = sgabios.S 36 | 37 | CSRCS = 38 | 39 | SRCS = $(CSRCS) $(ASRCS) 40 | 41 | OBJS = ${CSRCS:.c=.o} ${ASRCS:.S=.o} 42 | INCS = ${CSRCS:.c=.h} ${ASRCS:.S=.h} 43 | 44 | PROGS = sgabios.bin csum8 45 | 46 | .SUFFIXES: .bin .elf 47 | .PHONY: buildinfo 48 | 49 | all: $(PROGS) 50 | 51 | sgabios.bin: sgabios.elf 52 | $(OBJCOPY) -O binary $< $@ 53 | ./csum8 $@ 54 | 55 | sgabios.elf: .depend $(OBJS) $(LDSCRIPT) csum8 56 | $(LD) $(LDFLAGS) $(OBJS) -o $@ 57 | 58 | csum8: csum8.c 59 | $(HOSTCC) -Wall -O2 -o $@ $< 60 | 61 | sgabios.o: buildinfo 62 | 63 | 64 | buildinfo: 65 | touch sgabios.S 66 | clean: 67 | $(RM) $(PROGS) $(OBJS) *.elf *.srec *.com version.h 68 | 69 | .depend:: $(INCS) $(SRCS) Makefile 70 | $(RM) .depend 71 | $(CPP) -M $(CFLAGS) $(SRCS) >.tmpdepend && mv .tmpdepend .depend 72 | 73 | ifeq (.depend, $(wildcard .depend)) 74 | include .depend 75 | else 76 | # if no .depend file existed, add a make clean to the end of building .depend 77 | .depend:: 78 | $(MAKE) clean 79 | endif 80 | -------------------------------------------------------------------------------- /csum8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * compute rom checksum byte 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define MAX_SIZE 65536 26 | unsigned char buf[MAX_SIZE]; 27 | 28 | int main(int argc, char **argv) 29 | { 30 | ssize_t fsize; 31 | int i, sum, fd; 32 | unsigned char csum; 33 | 34 | if (argc < 2) { 35 | fprintf(stderr, "usage: %s filename\n", argv[0]); 36 | exit(1); 37 | } 38 | if ((fd = open(argv[1], O_RDWR)) < 0) { 39 | perror(argv[1]); 40 | exit(1); 41 | } 42 | if ((fsize = read(fd, buf, MAX_SIZE)) < 0) { 43 | perror(argv[1]); 44 | exit(1); 45 | } 46 | if (fsize >= MAX_SIZE && read(fd, &buf[MAX_SIZE - 1], 1) > 0) { 47 | fprintf(stderr, "FAIL: %s is larger than %d bytes\n", argv[1], MAX_SIZE); 48 | exit(1); 49 | } 50 | i = fsize - 2048 * (fsize / 2048); 51 | if (i != 2047) { 52 | fprintf(stderr, "FAIL: %s is %zd bytes, need 2K pad-1\n", argv[1], fsize); 53 | exit(1); 54 | } 55 | for (i = sum = 0; i < fsize; i++) { 56 | sum += buf[i]; 57 | } 58 | sum &= 0xff; 59 | csum = -sum & 0xff; 60 | write(fd, &csum, 1); 61 | close(fd); 62 | fprintf(stderr, "%s: sum = 0x%02x, wrote byte 0x%02x\n", argv[1], sum, csum); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /design.txt: -------------------------------------------------------------------------------- 1 | ============================================= 2 | Google Serial Graphics Adapter BIOS (SGABIOS) 3 | 4 | Copyright 2007 Google Inc. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | ============================================= 18 | Status: Implemented (as of 2007-08-08) 19 | 20 | Nathan Laredo 21 | Modified: 2008-02-14 13:45 PDT 22 | 23 | 24 | Objective 25 | --------- 26 | 27 | The Google Serial Graphics Adapter BIOS or SGABIOS provides a means 28 | for legacy pc software to communicate with an attached serial console 29 | as if a vga card is attached. 30 | 31 | Background 32 | ---------- 33 | 34 | The headless server problem 35 | 36 | When building a lot of systems for data center use, it makes 37 | no sense to install hardware that will rarely if ever be used. 38 | Graphics adapters are not very useful even if they are installed 39 | in a data center environment since often the person interested in 40 | seeing the output is separated from the device by tens to thousands 41 | of miles. 42 | 43 | While it's possible to use remote management hardware that provides 44 | a remotely accessible display and keyboard, this hardware is much 45 | more expensive than the hardware that it replaces, and often this 46 | hardware sends only images of the display rather than something 47 | suitable for logging. 48 | 49 | Since most systems already have a serial port, it's an obvious 50 | target as a replacement for the primary display and keyboard. 51 | The problem is that while an operating system like Linux can 52 | support this arrangement, all of the output that would normally 53 | appear on a graphics adapter before Linux boots is lost on modern 54 | x86 hardware without modifications to the system firmware. 55 | 56 | While some vendors provide firmware that enables the serial port to 57 | be used as the primary display, this is usually a "premium" option 58 | and isn't universally available for all x86 platforms. Often such 59 | services aren't implemented in a way that is friendly to saving logs 60 | of boot activity. One particularly ugly implementation might send 61 | the same text hundreds of times as it tries to refresh the entire 62 | display each timer tick. Others have ansi control sequences 63 | between every single character output which, while readable in a 64 | terminal, is almost unusable when referring to serial log files. 65 | Behavior like this slows down the serial output by up to fifteen 66 | times in some cases, using sometimes that many extra characters 67 | of control sequences for each character output. 68 | 69 | The need for detailed system logs 70 | 71 | None of the vendor-supplied serial redirection implementations 72 | include facilities for logging boot message for later capture by 73 | an operating system. Being able to refer to the boot messages 74 | after an operating system has loaded, or having a history of such 75 | messages can be a useful debug, analysis, and management feature. 76 | 77 | Even on systems with graphics adapters attached, once the display 78 | is scrolled or refreshed with enough new text, the old messages 79 | are only available in the user's own brain, which often isn't 80 | very good at accurately recalling more than two or three items 81 | that aren't grammatically meaningful in the user's native language. 82 | 83 | Overview 84 | --------- 85 | SGABIOS is designed to be inserted into a bios as an option rom 86 | to provide over a serial port the display and input capabilites 87 | normally handled by a VGA adapter and a keyboard, and additionally 88 | provide hooks for logging displayed characters for later collection 89 | after an operating system boots. 90 | 91 | It is designed to handle all text mode output sent to the legacy 92 | bios int 10h service routine. Int 10h is the most common method 93 | for displaying characters in 16-bit legacy x86 code. 94 | 95 | Occasionally some code may write directly to the vga memory in 96 | the interest of "speed," and this output will be missed, but 97 | it's rather uncommon for any code involved in booting a system 98 | to be concerned with the speed of display output. SGABIOS is not 99 | designed to handle these cases since those applications that make 100 | such assumptions generally write to an area of memory that typically 101 | already in use for system management mode and unusable outside of 102 | that mode. Paging tricks could be used to capture such output, 103 | but enabling paging requires protected mode to be enabled which 104 | instantly breaks all segment loads in legacy 16-bit real- mode code 105 | (which is the traditional boot environment). 106 | 107 | Detailed Design 108 | ---------------- 109 | 110 | VGA BIOS int 10h is hooked and chained to any existing handler or 111 | the default handler that the BIOS previously setup. 112 | 113 | During initialization, the option rom also probes the serial port 114 | for reply from an attached terminal. If the terminal replies to 115 | a specific sequence, the terminal size is recorded and used for 116 | all future display calculations. If a VGA card is attached at 117 | the same time, the width of the terminal is limited to 80 columns 118 | in order to have sensible output on both the VGA card and on the 119 | serial console. If no reply comes from the serial terminal within 120 | a very short timeout of about 8 milliseconds (or more accurately, 121 | 65536 reads of the serial status port), a default size of 80x24 122 | is used. The detected size is displayed at the end of option rom 123 | init to the serial console. 124 | 125 | Because of the way the cursor is updated, if the cursor is never 126 | moved upwards or more than one line down by int 10h calls, output 127 | will still be appear completely appropriate for whatever sized 128 | terminal is attached but failed to get detected. 129 | 130 | Whenever int 10h is invoked, SGABIOS gets control first and decides 131 | whether to act based on register state. With the exception of 132 | functions for getting current mode info or the current cursor 133 | position, whether it acts or not, register state is ultimately 134 | restored to the state on entry and a far jump is made to the 135 | original handler. 136 | 137 | SGABIOS maintains two active cursor positions. One contains the 138 | traditional VGA cursor position at the traditional location in 139 | the BIOS Data Area, while the other maintains the position the 140 | serial console's cursor is located. The serial cursor position 141 | is located in a BDA location that traditionally contains the 142 | base io port address for LPT3, but since builtin printer ports are 143 | disappearing over time, this location is reused. These two values 144 | will often differ since serial terminal output will always move 145 | the cursor to the next position on the screen while many VGA 146 | operations don't update the cursor position at all, or some only 147 | at the start of the string, but leave the old value at the end. 148 | Keeping track of two active cursor positions means that SGABIOS 149 | can collapse a string of "set cursor" calls into perhaps a single 150 | one or none if the serial console cursor already happens to be at 151 | the target location. Cursor movements are further optimized 152 | by sending newline characters to move the cursor down one row, 153 | carriage return characters to move the cursor back to column 0, 154 | and backspace characters to send the cursor back one or two spaces. 155 | 156 | To avoid problems when a video card is connected, any Bios Data 157 | Area location that would be updated by a VGA card is left alone 158 | to be updated by the VGA card. SGABIOS will update the cursor 159 | position as usual, but just before chaining to an existing vga 160 | card's handler, it will restore the values to those on entry, 161 | and for those functions that return data, it will defer completely 162 | to the chained routines rather than taking those over as it does 163 | when no video card is detected. 164 | 165 | Cursor position updates to serial console are deferred until the 166 | next character of terminal output is available. This collapses 167 | the cases where the cursor is updated more than one time between 168 | each character output (this is surprisingly common). 169 | 170 | The goal of tracking the cursor so closely and minimizing the number 171 | of characters required to update the cursor position is to both to 172 | make the display of output as efficient and fast as possible and 173 | to allow one to grep a raw log of serial console output for text 174 | (which without such optimization may be impossible or extremely 175 | difficult with movement escape sequences between every character). 176 | 177 | In the same way cursor position is tracked, vga character attributes 178 | are tracked so that it's possible to minimize the number of times 179 | an attribute change escape sequence is sent to the serial console. 180 | 181 | A BIOS Data Area location traditionally used for storing the 182 | current palette value is used to store the last attribute sent to 183 | the serial console. As SGABIOS processes new calls, if the value 184 | is the same, after masking off bright background colors which 185 | aren't supported in ansi escape codes, then no attribute update 186 | is sent to the serial console, else an escape sequence is sent 187 | that gives the new background and foreground colors and whether 188 | the foreground is bold or not. 189 | 190 | Data communication 191 | 192 | Whenever the call is made to output text, SGABIOS first updates 193 | the serial terminal cursor to match the current position of 194 | the vga cursor (if necessary), outputs any attribute change if 195 | applicable to the particular int 10h call made, and finally sends 196 | the text character (or characters) out to the serial port, and then 197 | updates its own view of where the serial console cursor is located. 198 | After the text is sent, a logging routine is called to store that 199 | text in a private area of memory allocated at option rom init. 200 | 201 | For keyboard/terminal input, SGABIOS hooks bios int 16h which is 202 | typically called to poll for a keypress. Before passing the call 203 | along, SGABIOS looks for any pending input on the serial port and 204 | stuffs the keyboard buffer with any pending byte after translating 205 | it to a compatible keyboard scancode. If the character received 206 | is an escape, SGABIOS will continue to poll for up to four extra 207 | characters of input for several milliseconds in order to detect 208 | ANSI/VT100/xterm/etc cursor keys and function keys, looking up 209 | appropriate scancodes in a table of escape sequences for all 210 | known non-conflicting terminal types. 211 | 212 | SGABIOS also hooks the serial port interrupts, and on receiving 213 | an interrupt blocks out interrupts, calls the same polling 214 | routines as above, following the same processing of multi-byte 215 | input as well, stuffing the keyboard buffer as appropriate, 216 | and finally acknowledging the interrupt and returning from the 217 | handler. [ serial port interrupts are now DISABLED ] 218 | 219 | Optionally the serial port input/output can be replaced with 220 | a SMI trigger that calls into an EFI BIOS in order to tie into 221 | its own console input and output routines rather than directly 222 | hitting the serial port. In this particular case it's assumed 223 | that all logging is handled in the EFI module that will be called. 224 | BIOS int 15h, ax = 0d042h is used to trigger SMI. The parameters 225 | passed will need to be changed to be specific to the EFI or SMI 226 | handler put in place. In the example in SMBIOS, for output, 227 | ebx = 0xf00d0000 | (char << 8), and for input, ebx = 0xfeed0000, 228 | with the character, if any, returned in the eax register with ZF 229 | set and eax=0 if no character was available. 230 | 231 | Summary of new enhancements 232 | --------------------------- 233 | SGABIOS now keeps a log of the last 256 characters written to 234 | the screen and where they were written in the event an application 235 | like lilo asks for the current character under the cursor. These 236 | are currently stored in a 1KB EBDA allocation which can be expanded 237 | as needed. This method avoids having to store a 64KB buffer for 238 | the largest possible serial terminal supported (255x255). 239 | 240 | When lilo 22.6 is detected, SGABIOS now knows how to disable 241 | lilo's serial output in favor of its own. This avoids having 242 | double character output from both serial and VGABIOS interleaved. 243 | 244 | Possible future enhancements 245 | ---------------------------- 246 | Previous future ideas have now been implemented. 247 | 248 | Known Bugs 249 | ---------- 250 | With some versions of DOS, only the last character of every line 251 | is displayed once dos boots since DOS will use direct access to 252 | the VGA framebuffer until the end of line is reached, at which 253 | point it will start using int 10h. Dual cursor tracking might 254 | fix this issue by maintaining positions for dos that look like 255 | the end of line and another for internal use to know where to 256 | output next. 257 | 258 | Caveats 259 | ------- 260 | It may be possible for someone to construct a terminal reply for 261 | the terminal sizing code that is completely invalid and attempts 262 | to either setup variables to overrun buffers or else overruns 263 | the input buffer itself. This situation is currently handled 264 | by limiting the reply to between eight and fourteen characters 265 | and ignoring any values outside the range from ten to two hundred 266 | fifty-five for both the number of rows and the number of columns. 267 | In these situations a default size of 80x24 is used (unless a 268 | video card is present, in which case its size is used). If the 269 | resize code detects several unexpected characters during the 270 | terminal size detection, it currently assumes that someone has 271 | left a loopback device plugged into the serial port and redirects 272 | the serial input and output to the fourth serial port at 0x2e8. 273 | 274 | 275 | Security considerations 276 | ----------------------- 277 | None. This is already 16-bit real-mode x86 code. The entire 278 | system may be crashed or bent to do anyone's bidding at any time 279 | by any other running code outside of SGABIOS. 280 | 281 | 282 | Opensource Plan 283 | --------------- 284 | This source code was approved for release to the public for use under 285 | the Apache License, Version 2.0 on http://code.google.com/p/sgabios 286 | 287 | 288 | Document History 289 | ---------------- 290 | Date Author Description 291 | 2008-02-14 nil fix for release 292 | 2007-10-04 nil new features 293 | 2007-08-31 nil sga+vga fixes 294 | 2007-08-08 nil Initial version 295 | 296 | $Id$ 297 | -------------------------------------------------------------------------------- /rom16.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Linker script for ROM16 binaries 3 | */ 4 | 5 | /* Script for -z combreloc: combine and sort reloc sections */ 6 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", 7 | "elf32-i386") 8 | OUTPUT_ARCH(i386) 9 | EXTERN(_start) 10 | ENTRY(_start) 11 | SECTIONS 12 | { 13 | /* Read-only sections, merged into text segment: */ 14 | . = 0x0; 15 | PROVIDE (__executable_start = .); 16 | 17 | .init : 18 | { 19 | KEEP (*(.init)) 20 | } =0x90909090 21 | .text : 22 | { 23 | *(.text .stub .text.* .gnu.linkonce.t.*) 24 | /* .gnu.warning sections are handled specially by elf32.em. */ 25 | *(.gnu.warning) 26 | } =0x90909090 27 | .fini : 28 | { 29 | KEEP (*(.fini)) 30 | } =0x90909090 31 | PROVIDE (__etext = .); 32 | PROVIDE (_etext = .); 33 | PROVIDE (etext = .); 34 | .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } 35 | .rodata1 : { *(.rodata1) } 36 | 37 | /* Ensure the __preinit_array_start label is properly aligned. We 38 | could instead move the label definition inside the section, but 39 | the linker would then create the section even if it turns out to 40 | be empty, which isn't pretty. */ 41 | . = ALIGN(4); 42 | PROVIDE (__preinit_array_start = .); 43 | .preinit_array : { *(.preinit_array) } 44 | PROVIDE (__preinit_array_end = .); 45 | PROVIDE (__init_array_start = .); 46 | .init_array : { *(.init_array) } 47 | PROVIDE (__init_array_end = .); 48 | PROVIDE (__fini_array_start = .); 49 | .fini_array : { *(.fini_array) } 50 | PROVIDE (__fini_array_end = .); 51 | PROVIDE (__ctors_start = .); 52 | .ctors : 53 | { 54 | KEEP (*(SORT(.ctors.*))) 55 | KEEP (*(.ctors)) 56 | } 57 | PROVIDE (__ctors_end = .); 58 | PROVIDE (__dtors_start = .); 59 | .dtors : 60 | { 61 | KEEP (*(SORT(.dtors.*))) 62 | KEEP (*(.dtors)) 63 | } 64 | PROVIDE (__dtors_end = .); 65 | 66 | /* Adjust the address for the data segment. Avoid mixing code and 67 | data within same 128-byte chunk. */ 68 | . = ALIGN(128); 69 | 70 | .data : 71 | { 72 | *(.data .data.* .gnu.linkonce.d.*) 73 | SORT(CONSTRUCTORS) 74 | } 75 | .data1 : { *(.data1) } 76 | _edata = .; 77 | PROVIDE (edata = .); 78 | __bss_start = .; 79 | .bss : 80 | { 81 | *(.dynbss) 82 | *(.bss .bss.* .gnu.linkonce.b.*) 83 | *(COMMON) 84 | /* Align here to ensure that the .bss section occupies space up to 85 | _end. Align after .bss to ensure correct alignment even if the 86 | .bss section disappears because there are no input sections. */ 87 | . = ALIGN(32 / 8); 88 | } 89 | .csum : 90 | { 91 | . = ALIGN(2048) - 5; 92 | LONG(0xff4c494e); 93 | /* BYTE(_rom_size_byte); */ 94 | } =0xffffffff 95 | _end = .; 96 | PROVIDE (end = .); 97 | PROVIDE(_rom_size_byte = (511 + end) / 512); 98 | 99 | /* Stabs debugging sections. */ 100 | .stab 0 : { *(.stab) } 101 | .stabstr 0 : { *(.stabstr) } 102 | .stab.excl 0 : { *(.stab.excl) } 103 | .stab.exclstr 0 : { *(.stab.exclstr) } 104 | .stab.index 0 : { *(.stab.index) } 105 | .stab.indexstr 0 : { *(.stab.indexstr) } 106 | .comment 0 : { *(.comment) } 107 | /* DWARF debug sections. 108 | Symbols in the DWARF debugging sections are relative to the beginning 109 | of the section so we begin them at 0. */ 110 | /* DWARF 1 */ 111 | .debug 0 : { *(.debug) } 112 | .line 0 : { *(.line) } 113 | /* GNU DWARF 1 extensions */ 114 | .debug_srcinfo 0 : { *(.debug_srcinfo) } 115 | .debug_sfnames 0 : { *(.debug_sfnames) } 116 | /* DWARF 1.1 and DWARF 2 */ 117 | .debug_aranges 0 : { *(.debug_aranges) } 118 | .debug_pubnames 0 : { *(.debug_pubnames) } 119 | /* DWARF 2 */ 120 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } 121 | .debug_abbrev 0 : { *(.debug_abbrev) } 122 | .debug_line 0 : { *(.debug_line) } 123 | .debug_frame 0 : { *(.debug_frame) } 124 | .debug_str 0 : { *(.debug_str) } 125 | .debug_loc 0 : { *(.debug_loc) } 126 | .debug_macinfo 0 : { *(.debug_macinfo) } 127 | /* SGI/MIPS DWARF 2 extensions */ 128 | .debug_weaknames 0 : { *(.debug_weaknames) } 129 | .debug_funcnames 0 : { *(.debug_funcnames) } 130 | .debug_typenames 0 : { *(.debug_typenames) } 131 | .debug_varnames 0 : { *(.debug_varnames) } 132 | /DISCARD/ : { *(.note.GNU-stack) } 133 | } 134 | -------------------------------------------------------------------------------- /sgabios.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "sgabios.h" 18 | #define BUILD_CL "$Id$" 19 | 20 | .code16 21 | .text 22 | .section ".init","ax" 23 | .globl _start 24 | .type _start,@object 25 | _start: 26 | /* option rom header */ 27 | .byte 0x55 28 | .byte 0xaa 29 | .byte _rom_size_byte 30 | .size _start, .-_start 31 | 32 | .globl legacy_entry 33 | .type legacy_entry,@function 34 | legacy_entry: 35 | jmp sga_init 36 | /* pnp entry here to avoid changing PnP table as code moves */ 37 | pnp_init: 38 | jmp pnp_sga_init 39 | 40 | /* 41 | * do_old_int10h 42 | * 43 | * Patched at option rom init to be a far jump to old int 10h isr 44 | * 45 | */ 46 | do_old_int10h: 47 | .byte 0xea /* jmp absolute segment:offset */ 48 | old_int10h: /* store what was at offset 0x40 */ 49 | .word 0xf065 /* placeholder for chained ISR offset */ 50 | /* if the chained segment is detected as 0xc000, use 80 cols only */ 51 | /* since it's assumed that a vga card is attached and 80 cols max */ 52 | old_int10h_seg: 53 | .word 0xf000 /* placeholder for chained ISR segment */ 54 | /* 55 | * do_old_int16h 56 | * 57 | * Patched at option rom init to be a far jump to old int 16h isr 58 | * 59 | */ 60 | do_old_int16h: 61 | .byte 0xea /* jmp absolute segment:offset */ 62 | old_int16h: /* store what was at offset 0x58 */ 63 | .word 0xe82e /* placeholder for chained ISR offset */ 64 | .word 0xf000 /* placeholder for chained ISR segment */ 65 | .org 0x18 66 | .word 0 /* offset to PCI data, 0 = none */ 67 | .word pnp_table /* offset to PnP expansion header */ 68 | .org 0x20 69 | pnp_table: 70 | /* FIXME: **** PnP header currently disabled by PoO **** */ 71 | /* legacy entry only called once, PnP entry called multiple times */ 72 | /* The code isn't yet written to deal with multiple inits properly */ 73 | .ascii "$PoO" /* PnP expansion header signature */ 74 | .byte 1 /* structure revision */ 75 | .byte 2 /* length in 16-byte increments */ 76 | .word 0 /* offset of next header, 0 if none */ 77 | .byte 0 /* reserved */ 78 | .byte 0x52 /* checksum - update manually! FIXME */ 79 | .long 0 /* device identifier */ 80 | .word mfg_string /* pointer to manufacturer string */ 81 | .word prod_string /* pointer to product name string */ 82 | .byte 3, 0x80, 0x80 /* device type code = other display */ 83 | .byte 0xe3 /* device indicators, kbd/display dev */ 84 | .word 0 /* boot connection vector, 0 if none */ 85 | .word 0 /* disconnect vector, 0 if none */ 86 | .word pnp_init /* bootstrap entry vector */ 87 | .word 0 /* reserved */ 88 | .word 0 /* static resource information vector */ 89 | 90 | /* WARNING: changing mfg_string / prod_string locations will */ 91 | /* affect pnp table above -- recalculate checksum manually! */ 92 | mfg_string: 93 | .asciz "Google, Inc." 94 | prod_string: 95 | .ascii "Serial Graphics Adapter " 96 | build_date: 97 | .asciz BUILD_SHORT_DATE 98 | long_version: 99 | .ascii "SGABIOS " 100 | .ascii BUILD_CL 101 | .ascii " (" 102 | .ascii BUILD_USER 103 | .ascii "@" 104 | .ascii BUILD_HOST 105 | .ascii ") " 106 | .asciz BUILD_DATE 107 | term_cols: 108 | .byte 80 /* overwritten at rom init with detected value */ 109 | term_rows: 110 | .byte 24 /* overwritten at rom init with detected value */ 111 | term_init_string: /* terminal reply: \033[n;mR n=row, m=col */ 112 | .asciz "\033[1;256r\033[256;256H\033[6n" 113 | /* reset the scroll, move to col 256, row 256, ask current position */ 114 | /* bios cursor positions >255 rows or cols can't be used anyway */ 115 | term_info: 116 | .asciz "Term: " 117 | ebda_info: 118 | .asciz "EBDA: " 119 | 120 | /* 121 | * do_old_irq3 - exception 0x0b, int 0x0a 122 | * 123 | * Patched at option rom init to be a far jump to old irq 3 isr 124 | * 125 | */ 126 | do_old_irq3: 127 | .byte 0xea /* jmp absolute segment:offset */ 128 | old_irq3: /* store what was at offset 0x28 */ 129 | .word 0xeef3 /* placeholder for chained ISR offset */ 130 | .word 0xf000 /* placeholder for chained ISR segment */ 131 | 132 | /* 133 | * do_old_irq4 - exception 0x0c, int 0x0b 134 | * 135 | * Patched at option rom init to be a far jump to old irq 4 isr 136 | * 137 | */ 138 | do_old_irq4: 139 | .byte 0xea /* jmp absolute segment:offset */ 140 | old_irq4: /* store what was at offset 0x2c */ 141 | .word 0xeef3 /* placeholder for chained ISR offset */ 142 | .word 0xf000 /* placeholder for chained ISR segment */ 143 | 144 | /* 145 | * do_old_int14h 146 | * 147 | * Patched at option rom init to be a far jump to old int 14h isr 148 | * 149 | */ 150 | do_old_int14h: 151 | .byte 0xea /* jmp absolute segment:offset */ 152 | old_int14h: /* store what was at offset 0x50 */ 153 | .word 0xe739 /* placeholder for chained ISR offset */ 154 | .word 0xf000 /* placeholder for chained ISR segment */ 155 | 156 | .align 16, 0xff /* aligning this table only makes hexdump prettier */ 157 | /* ascii -> scancode, bit 7=shifted, char < 32 = +ctrl */ 158 | /* except chars 8, 9, 13, 27 (bs, tab, enter, esc) */ 159 | /* most int16h consumers will probably never use */ 160 | ascii2scan: 161 | /*00*/ .byte 0x00, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22 162 | /*08*/ .byte 0x0e, 0x17, 0x24, 0x25, 0x26, 0x1c, 0x31, 0x18 163 | /*10*/ .byte 0x19, 0x0f, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11 164 | /*18*/ .byte 0x2d, 0x15, 0x2c, 0x01, 0x2b, 0x1b, 0x87, 0x8c 165 | /*20*/ .byte 0x39, 0x82, 0xa8, 0x84, 0x85, 0x86, 0x88, 0x28 166 | /*28*/ .byte 0x8a, 0x8b, 0x89, 0x8d, 0x33, 0x0c, 0x34, 0x35 167 | /*30*/ .byte 0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 168 | /*38*/ .byte 0x09, 0x0a, 0xa7, 0x27, 0xb3, 0x0d, 0x34, 0xb5 169 | /*40*/ .byte 0x83, 0x9e, 0xb0, 0xae, 0xa0, 0x92, 0xa1, 0xa2 170 | /*48*/ .byte 0xa3, 0x97, 0xa4, 0xa5, 0xa6, 0xb2, 0xb1, 0x98 171 | /*50*/ .byte 0x99, 0x90, 0x93, 0x9f, 0x94, 0x96, 0xaf, 0x91 172 | /*58*/ .byte 0xad, 0x95, 0xac, 0x1a, 0x2b, 0x1b, 0x87, 0x8c 173 | /*60*/ .byte 0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22 174 | /*68*/ .byte 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18 175 | /*70*/ .byte 0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11 176 | /*78*/ .byte 0x2d, 0x15, 0x2c, 0x9a, 0xab, 0x9b, 0xa9, 0x0e 177 | 178 | /* TABLES FOR NON-ASCII VGA CHARACTERS (CP437) TO ASCII */ 179 | /* Unicode at: http://en.wikipedia.org/wiki/Code_page_437 */ 180 | 181 | ctrl2ascii: 182 | /* translate vga (CP437) first 32 characters to ascii */ 183 | /* for char 0, update the cursor position, but output nothing */ 184 | /* lilo uses this "trick" for a background attribute update */ 185 | .ascii "\0@@v***........*><|!PS-|^v>###||||++||+++++" 192 | /*c0*/ .ascii "+--|-+||++--|-+----++++++++#-||-" 193 | /*e0*/ .ascii "abgpesut00osiye^=+><||-=...vn2* " 194 | 195 | colortable: 196 | /* vga text color is IRGB, ansi color is BGR */ 197 | /* this table is effectively a nibble bit-reverse */ 198 | .byte 0, 4, 2, 6, 1, 5, 3, 7 199 | 200 | serial_port_base_address: 201 | .word COM_BASE_ADDR 202 | 203 | /* in-memory console log 204 | * 205 | * It's expected that the EBDA contains a magic signature 206 | * like 0xdeadbabe, followed by a byte of flags, followed 207 | * by a 32-bit buffer pointer, followed by a 16-bit start 208 | * index, followed by a 16-bit end index, followed by 16- 209 | * bit logged character count, followed by an 8-bit flag. 210 | */ 211 | 212 | #define MEMCONSOLE_BUFFER_SIZE 32768 213 | #define MEMCONSOLE_SIGNATURE 0xdeadbabe 214 | #define MEMCONSOLE_ENDINDEX_OFF 0x0b 215 | #define SGABIOS_EBDA_SIGNATURE 0x00414753 216 | 217 | memconsole_buffer_start: /* pulled from ebda struct */ 218 | .long 0x00000000 /* 0 = not found/no logging */ 219 | memconsole_ebda_deadbabe_offset: /* bytes from start of ebda */ 220 | .word 0x0000 /* 40:0e contains ebda seg */ 221 | sgabios_ebda_logbuf_offset: /* bytes from start of ebda */ 222 | .word 0x0000 /* 40:0e contains ebda seg */ 223 | 224 | /* 225 | * setup_memconsole 226 | * 227 | * Initialize the option rom variables associated with logging 228 | * of the legacy console output 229 | * 230 | * If these variables are left at zero, no logging will occur 231 | * 232 | * There are no parameters 233 | * All registers except flags should be preserved 234 | */ 235 | 236 | setup_memconsole: 237 | pushaw 238 | pushw %ds 239 | pushw %es 240 | pushw $BDA_SEG 241 | popw %ds /* ds = 0x40 */ 242 | pushw BDA_EBDA /* push word at 0x0e */ 243 | popw %es /* es = EBDA_SEG */ 244 | /* search for memconsole signature in ebda */ 245 | movl $MEMCONSOLE_SIGNATURE, %eax 246 | xorw %di, %di /* start at zero */ 247 | movzbw %es:(%di), %cx /* cx = size of EBDA in KB */ 248 | shlw $8, %cx /* cx = (cx * 1024) / 4 */ 249 | cld 250 | repnz 251 | scasl /* search until sig found */ 252 | subw $4, %di /* scasl always increments di, undo */ 253 | cmpl %eax, %es:(%di) /* is signature here? */ 254 | jnz setup_memconsole_end /* bail if so */ 255 | movw %di, %cs:memconsole_ebda_deadbabe_offset /* save offset */ 256 | movl %es:5(%di), %eax /* get 32-bit buffer base address */ 257 | movl %eax, %cs:memconsole_buffer_start 258 | setup_memconsole_end: 259 | popw %es 260 | popw %ds 261 | popaw 262 | ret 263 | 264 | /* 265 | * memconsole_log_char 266 | * 267 | * Log the character passed in %al to the next available memory 268 | * console log position, if any. 269 | * 270 | * If memconsole_buffer_start is zero, no logging will occur 271 | * 272 | * %al = character to be logged 273 | * All registers except flags should be preserved 274 | */ 275 | 276 | memconsole_log_char: 277 | pushaw 278 | pushw %ds 279 | pushw %es 280 | pushw %fs 281 | pushw $BDA_SEG 282 | popw %ds /* ds = 0x40 */ 283 | pushw BDA_EBDA /* push word at 0x0e */ 284 | popw %es /* es = EBDA_SEG */ 285 | movw %ax, %si /* %si = %al = byte to write */ 286 | movl %cs:memconsole_buffer_start, %ebp 287 | movw %cs:memconsole_ebda_deadbabe_offset, %di 288 | addw $MEMCONSOLE_ENDINDEX_OFF, %di /* %di points to char pos */ 289 | orl %ebp, %ebp 290 | jz memconsole_log_tail /* bufptr==0, no logging */ 291 | movw %es:(%di), %bx /* bx = current position in buffer */ 292 | cmpw $MEMCONSOLE_BUFFER_SIZE, %bx /* at end of buffer? */ 293 | jnc memconsole_log_tail /* don't log any more if so */ 294 | cmpb $0xd, %al /* is the char CR? */ 295 | jz memconsole_log_tail /* if so, ignore it */ 296 | cmpb $0x8, %al /* is the char backspace? */ 297 | jnz memconsole_update_fsbase /* if not, log char as usual... */ 298 | orw %bx, %bx /* make sure ptr isn't already zero */ 299 | jz memconsole_log_tail /* if so, bail */ 300 | decw %bx /* else point to previous character */ 301 | jmp memconsole_update_end_ptr /* and go directly to save it */ 302 | memconsole_update_fsbase: 303 | movl $0xc0000100, %ecx /* ecx = IA32_FS_BASE (AMD64+) */ 304 | rdmsr /* read what was there before */ 305 | pushl %eax /* save away previous FS_BASE eax */ 306 | pushl %edx /* save away previous FS_BASE edx */ 307 | xorl %edx, %edx /* clear high 32 bits */ 308 | movl %ebp, %eax /* eax = memconsole buffer start */ 309 | wrmsr /* fs_base = memconsole buffer start */ 310 | movw %si, %ax /* %ax = saved value on entry */ 311 | movb %al, %fs:(%bx) /* log character */ 312 | popl %edx /* restore previous FS_BASE edx */ 313 | popl %eax /* restore previous FS_BASE eax */ 314 | wrmsr /* write what was there before */ 315 | incw %bx /* update character count */ 316 | memconsole_update_end_ptr: 317 | movw %bx, %es:(%di) /* save new end pointer */ 318 | addw $2, %di /* numchars stored at next word */ 319 | movw %bx, %es:(%di) /* save new numchar value */ 320 | memconsole_log_tail: 321 | popw %fs 322 | popw %es 323 | popw %ds 324 | popaw 325 | ret 326 | 327 | /* sgabioslog_setup_ebda 328 | * 329 | * SGABIOS makes its own 1KB EBDA allocation to save non- 330 | * translated characters with associated cursor positions 331 | * for the last 256 characters output. This is organized 332 | * with 256 bytes reserved for houskeeping, 256 bytes for 333 | * the raw character codes, and 512 bytes of 16bit cursor 334 | * positions to record the associated position for each. 335 | * 336 | * The first 4 bytes contain "SGA\0" followed by a 16-bit 337 | * size of the allocation in bytes, followed by a 16-bit 338 | * index indicating the next spot to be overwritten. 339 | * 340 | * There are no parameters 341 | * All registers should be preserved 342 | */ 343 | 344 | sgabioslog_setup_ebda: 345 | pushf 346 | pushaw 347 | pushw %ds 348 | pushw %es 349 | pushw $BDA_SEG 350 | popw %ds /* ds = 0x40 */ 351 | movw BDA_EBDA, %ax /* ax = old ebda segment from 0x0e */ 352 | subw $SGABIOS_EBDA_DELTA, %ax 353 | movw %ax, %es /* es = new EBDA segment start */ 354 | cmpw $EBDA_MIN_SEG, %ax /* is there room for the allocation? */ 355 | jc sgabioslog_setup_ebda_tail /* if not, don't change anything */ 356 | cli /* paranoid in case irq uses EBDA */ 357 | movw %ax, BDA_EBDA /* save new EBDA segment start */ 358 | subw $SGABIOS_EBDA_KB, BDA_MEM_SIZE /* subtract extra allocation */ 359 | movw %ax, %ds /* ds = new EBDA segment start */ 360 | movw $SGABIOS_EBDA_BYTES, %si /* si = offset of first byte to move */ 361 | movzbw (%si), %cx /* cx = number of KB in EBDA */ 362 | addb $SGABIOS_EBDA_KB, (%si) /* update EBDA size in kb */ 363 | shlw $10, %cx /* cx = KB * 1024 = bytes in EBDA */ 364 | movw %cx, %cs:sgabios_ebda_logbuf_offset /* new ebda space */ 365 | xorw %di, %di /* di = new EBDA start */ 366 | cld 367 | rep 368 | movsb /* move ebda by SGABIOS_EBDA_BYTES */ 369 | movw %cs:sgabios_ebda_logbuf_offset, %bx /* bx = new buffer */ 370 | movl $SGABIOS_EBDA_SIGNATURE, (%bx) /* setup signature */ 371 | movw $SGABIOS_EBDA_BYTES, 4(%bx) /* bytes in new ebda buffer */ 372 | movw $0, 6(%bx) /* next log index, new ebda buffer */ 373 | sgabioslog_setup_ebda_tail: 374 | popw %es 375 | popw %ds 376 | popaw 377 | popf 378 | ret 379 | 380 | /* 381 | * sgabioslog_save_char 382 | * 383 | * Like memconsole_log_char, except the original, untranslated 384 | * character is expected to be given in the %al register. 385 | * 386 | * The original character and its corresponding cursor position 387 | * are logged to the sgabios ebda memory allocation. 388 | * 389 | * %al = character to be logged 390 | * All registers except flags should be preserved 391 | */ 392 | 393 | sgabioslog_save_char: 394 | pushaw 395 | pushw %ds 396 | pushw %es 397 | pushw $BDA_SEG 398 | popw %ds /* ds = 0x40 */ 399 | pushw BDA_EBDA /* push word at 0x0e */ 400 | popw %es /* es = EBDA_SEG */ 401 | movw %cs:sgabios_ebda_logbuf_offset, %di 402 | orw %di, %di /* is offset zero? */ 403 | jz sgabioslog_save_tail /* if so, bail */ 404 | cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di) 405 | jnz sgabioslog_save_tail /* bail if magic not found */ 406 | movw %es:6(%di), %bx /* bx = index of next char output */ 407 | movb %al, %es:SGABIOS_EBDA_LOG_START(%bx,%di) /* store character */ 408 | movzbw %bl, %ax /* %ax = next cursor buffer index */ 409 | shlw $1, %ax /* %ax = offset to cursor storage */ 410 | call get_current_cursor /* %dh = row, %dl = column */ 411 | addw $SGABIOS_EBDA_POS_START, %di /* cursor storage */ 412 | addw %ax, %di /* %di = next cursor storage offset */ 413 | movw %dx, %es:(%di) /* save position for logged char */ 414 | incw %bx /* point to next char to log */ 415 | cmpw $SGABIOS_EBDA_LOG_SIZE, %bx 416 | jnz sgabioslog_save_index 417 | xorw %bx, %bx /* wrap around to start */ 418 | sgabioslog_save_index: 419 | movw %cs:sgabios_ebda_logbuf_offset, %di 420 | movw %bx, %es:6(%di) /* save new index */ 421 | sgabioslog_save_tail: 422 | popw %es 423 | popw %ds 424 | popaw 425 | ret 426 | 427 | /* 428 | * sgabioslog_get_char 429 | * 430 | * Return the character at current cursor position, last recorded 431 | * to sgabios ebda allocation, if available. 432 | * 433 | * If the current cursor postition contains one of the last 256 characters 434 | * written to the ebda buffer, return that character, else return 0. 435 | * 436 | * If sgabios_ebdda_logbuf_offset is zero, %al will be 0 and zf set 437 | * 438 | * All registers except flags and %al should be preserved 439 | */ 440 | 441 | sgabioslog_get_char: 442 | pushaw 443 | movw %sp, %bp 444 | movb $0, 14(%bp) /* %al on stack = 0 */ 445 | pushw %ds 446 | pushw %es 447 | pushw $BDA_SEG 448 | popw %ds /* ds = 0x40 */ 449 | pushw BDA_EBDA /* push word at 0x0e */ 450 | popw %es /* es = EBDA_SEG */ 451 | movw %cs:sgabios_ebda_logbuf_offset, %di 452 | orw %di, %di 453 | jz sgabioslog_get_tail /* offset==0, no logging */ 454 | cmpl $SGABIOS_EBDA_SIGNATURE, %es:(%di) 455 | jnz sgabioslog_get_tail /* bail if magic not found */ 456 | call get_current_cursor /* dh = row, dl = col */ 457 | std /* scan backwards in mem */ 458 | movw %es:6(%di), %bx /* bx = index of next char output */ 459 | decw %bx /* %bx = offset of last char in buf */ 460 | jnc sgabioslog_got_pos 461 | addw $SGABIOS_EBDA_LOG_SIZE, %bx /* bx position wrap around */ 462 | sgabioslog_got_pos: 463 | movw %bx, %ax /* %ax = last cursor pos written */ 464 | shlw $1, %ax /* %ax = offset of last cursor pos */ 465 | addw $SGABIOS_EBDA_POS_START, %di /* %di = first cursor position */ 466 | addw %ax, %di /* %di = offset in ebda */ 467 | movw %dx, %ax /* %ax = cursor pos to compare */ 468 | movw %bx, %cx /* %cx = positions before wrap */ 469 | jcxz sgabioslog_cmp_wrap /* if zero, try from end next */ 470 | repnz 471 | scasw /* search until position match */ 472 | addw $2, %di /* scasd always decrements di, undo */ 473 | cmpw %ax, %es:(%di) /* did it really match? */ 474 | jz sgabioslog_cursor_match /* if so, do something */ 475 | sgabioslog_cmp_wrap: 476 | movw %cs:sgabios_ebda_logbuf_offset, %di 477 | addw $SGABIOS_EBDA_POS_LAST, %di /* %di = last cursor storage */ 478 | movw $SGABIOS_EBDA_LOG_SIZE, %cx /* %cx = compare all positions */ 479 | repnz 480 | scasw /* search until position match */ 481 | addw $2, %di /* scasd always decrements di, undo */ 482 | cmpw %ax, %es:(%di) /* did it really match? */ 483 | jnz sgabioslog_get_tail /* if not, bail */ 484 | sgabioslog_cursor_match: 485 | /* %di contains the EBDA offset of the matching position */ 486 | /* convert this into a memconsole offset */ 487 | subw $512, %di /* take off the storage offset */ 488 | subw %cs:sgabios_ebda_logbuf_offset, %di /* and ebda offset */ 489 | shrw $1, %di /* %di = char position index */ 490 | addw %cs:sgabios_ebda_logbuf_offset, %di /* add back ebda offset */ 491 | addw $SGABIOS_EBDA_LOG_START, %di /* and add back log offset */ 492 | movb %es:(%di), %al /* get related saved character */ 493 | movb %al, 14(%bp) /* %al on stack = logged char */ 494 | sgabioslog_get_tail: 495 | popw %es 496 | popw %ds 497 | popaw 498 | ret 499 | 500 | /* 501 | * multibyteinput 502 | * 503 | * When an escape key is detected, the input routines will attempt to 504 | * capture as many characters as arrive up until a timeout, or six, 505 | * whichever is less. 506 | * 507 | * This table is intended to decide what the characters after the 508 | * initial escape key translate to in terms of high and low bytes 509 | * that go into the keyboard buffer the high byte is the scancode, 510 | * the low byte is ascii, but for special keys this is usually 0xe0 511 | * or 0x00. 512 | * 513 | * This table is formatted so that the first word is a scancode + 514 | * ascii pair (as returned by int 16h, ah = 10h or 11h). Immediately 515 | * following is a nul-terminated ascii string to match in order to 516 | * use the corresponding scancode+ascii word. 517 | * 518 | * The search through this table is terminated by a match or finding 519 | * a 0 scancode+ascii word. 520 | * 521 | * FIXME: all the low bytes are now zero, get rid of them? 522 | */ 523 | multibyteinput: 524 | .byte 0x3b /* F1 */ 525 | .asciz "[[A" /* F1/screen */ 526 | 527 | .byte 0x3b /* F1 */ 528 | .asciz "OP" /* F1/xterm/ansi */ 529 | 530 | .byte 0x3b /* F1 */ 531 | .asciz "[11~" /* F1/vt400 */ 532 | 533 | .byte 0x3c /* F2 */ 534 | .asciz "[[B" /* F2/screen */ 535 | 536 | .byte 0x3c /* F2 */ 537 | .asciz "OQ" /* F2/xterm/ansi */ 538 | 539 | .byte 0x3c /* F2 */ 540 | .asciz "[12~" /* F2/vt400 */ 541 | 542 | .byte 0x3d /* F3 */ 543 | .asciz "[[C" /* F3/screen */ 544 | 545 | .byte 0x3d /* F3 */ 546 | .asciz "OR" /* F3/xterm/ansi */ 547 | 548 | .byte 0x3d /* F3 */ 549 | .asciz "[13~" /* F3/vt400 */ 550 | 551 | .byte 0x3e /* F4 */ 552 | .asciz "[[D" /* F4/screen */ 553 | 554 | .byte 0x3e /* F4 */ 555 | .asciz "OS" /* F4/xterm/ansi */ 556 | 557 | .byte 0x3e /* F4 */ 558 | .asciz "[14~" /* F4/vt400 */ 559 | 560 | .byte 0x3f /* F5 */ 561 | .asciz "[[E" /* F5/screen */ 562 | 563 | .byte 0x3f /* F5 */ 564 | .asciz "[15~" /* F5/xterm */ 565 | 566 | .byte 0x3f /* F5 */ 567 | .asciz "OT" /* F5/ansi */ 568 | 569 | .byte 0x40 /* F6 */ 570 | .asciz "[17~" /* F6/screen/vt220/xterm/vt400 */ 571 | 572 | .byte 0x40 /* F6 */ 573 | .asciz "OU" /* F6/ansi */ 574 | 575 | .byte 0x41 /* F7 */ 576 | .asciz "[18~" /* F7/screen/vt220/xterm/vt400 */ 577 | 578 | .byte 0x41 /* F7 */ 579 | .asciz "OV" /* F7/ansi */ 580 | 581 | .byte 0x42 /* F8 */ 582 | .asciz "[19~" /* F8/screen/vt220/xterm/vt400 */ 583 | 584 | .byte 0x42 /* F8 */ 585 | .asciz "OW" /* F8/ansi */ 586 | 587 | .byte 0x43 /* F9 */ 588 | .asciz "[20~" /* F9/screen/vt220/xterm/vt400 */ 589 | 590 | .byte 0x43 /* F9 */ 591 | .asciz "OX" /* F9/ansi */ 592 | 593 | .byte 0x44 /* F10 */ 594 | .asciz "[21~" /* F10/screen/vt220/xterm/vt400 */ 595 | 596 | .byte 0x44 /* F10 */ 597 | .asciz "OY" /* F10/ansi */ 598 | 599 | .byte 0x85 /* F11 */ 600 | .asciz "[23~" /* F11/screen/xterm/vt400 */ 601 | 602 | .byte 0x85 /* F11 */ 603 | .asciz "OZ" /* F11/ansi */ 604 | 605 | .byte 0x86 /* F12 */ 606 | .asciz "[24~" /* F12/screen/xterm/vt400 */ 607 | 608 | .byte 0x52 /* Insert */ 609 | .asciz "[2~" /* Insert/screen/vt102/xterm */ 610 | 611 | .byte 0x53 /* Delete */ 612 | .asciz "[3~" /* Delete/screen/vt102/xterm */ 613 | 614 | .byte 0x4b /* Left */ 615 | .asciz "OD" /* Left/screen/vt102 */ 616 | 617 | .byte 0x4b /* Left */ 618 | .asciz "[D" /* Left/xterm */ 619 | 620 | .byte 0x47 /* Home */ 621 | .asciz "[1~" /* Home/screen/vt102 */ 622 | 623 | .byte 0x47 /* Home */ 624 | .asciz "[H" /* Home/xterm */ 625 | 626 | .byte 0x4f /* End */ 627 | .asciz "[4~" /* End/screen/vt102 */ 628 | 629 | .byte 0x4f /* End */ 630 | .asciz "[F" /* End/xterm */ 631 | 632 | .byte 0x48 /* Up */ 633 | .asciz "OA" /* Up/screen/vt102 app */ 634 | 635 | .byte 0x48 /* Up */ 636 | .asciz "[A" /* Up/xterm/vt102 ansi */ 637 | 638 | .byte 0x50 /* Down */ 639 | .asciz "OB" /* Down/screen/vt102 app */ 640 | 641 | .byte 0x50 /* Down */ 642 | .asciz "[B" /* Down/xterm/vt102 ansi */ 643 | 644 | .byte 0x49 /* PageUp */ 645 | .asciz "[5~" /* PageUp/screen/vt102/xterm */ 646 | 647 | .byte 0x51 /* PageDown */ 648 | .asciz "[6~" /* PageDown/screen/vt102/xterm */ 649 | 650 | .byte 0x4d /* Right */ 651 | .asciz "OC" /* Right/screen/vt102 app */ 652 | 653 | .byte 0x4d /* Right */ 654 | .asciz "[C" /* Right/xterm/vt102 ansi */ 655 | 656 | .byte 0 /* end of table marker */ 657 | 658 | /* init_serial_port 659 | * 660 | * Initialize serial port to 115200,8n1 661 | * Serial interrupts disabled 662 | * 663 | * All registers except flags preserved 664 | */ 665 | 666 | init_serial_port: 667 | pushw %ax 668 | pushw %dx 669 | pushw %bx 670 | movw %cs:serial_port_base_address, %dx 671 | addw $IER_OFFSET, %dx 672 | xorb %al, %al 673 | outb %al, %dx /* disable all serial interrupts */ 674 | addw $(LCR_OFFSET - IER_OFFSET), %dx /* LCR */ 675 | movb $(LCR_VALUE|LCR_DLAB), %al 676 | outb %al, %dx /* enable divisor access */ 677 | movw %cs:serial_port_base_address, %dx 678 | movw $(PORT_DIVISOR/PORT_SPEED), %bx 679 | movb %bl, %al /* al = lsb of divisor */ 680 | outb %al, %dx /* set divisor latch lsb */ 681 | movb %bh, %al /* al = msb of divisor */ 682 | incw %dx 683 | outb %al, %dx /* set divisor latch msb */ 684 | movw %cs:serial_port_base_address, %dx 685 | addw $LCR_OFFSET, %dx 686 | movb $LCR_VALUE, %al 687 | outb %al, %dx /* disable divisor access */ 688 | addw $(MCR_OFFSET - LCR_OFFSET), %dx /* MCR */ 689 | movb $MCR_DTRRTS, %al 690 | outb %al, %dx /* enable DTR + RTS */ 691 | movw %cs:serial_port_base_address, %dx 692 | addw $FCR_OFFSET, %dx 693 | movb $FCR_FIFO_ENABLE, %al 694 | outb %al, %dx /* enable FIFOs */ 695 | popw %bx 696 | popw %dx 697 | popw %ax 698 | ret 699 | 700 | 701 | /* get_serial_lsr 702 | * 703 | * return serial line status register in %al 704 | * return offset to serial port line status register io port in %dx 705 | * all other registers except flags unchanged 706 | * 707 | * if status == 0xff return ZF=1, else return ZF=0 708 | */ 709 | 710 | get_serial_lsr: 711 | movw %cs:serial_port_base_address, %dx 712 | addw $LSR_OFFSET, %dx 713 | inb %dx, %al 714 | cmpb $0xff, %al 715 | ret 716 | 717 | /* 718 | * get_byte 719 | * 720 | * get serial byte in %al, scancode in %ah [FIXME: EFI console input] 721 | * 722 | * all registers except %ax preserved 723 | * 724 | */ 725 | 726 | get_byte: 727 | pushw %dx 728 | pushw %bx 729 | next_serial_char: 730 | call get_serial_lsr /* get serial lsr in %al */ 731 | jz get_byte_tail /* no port present... */ 732 | testb $1, %al /* bit 0 of LSR = 1 = data available */ 733 | jz get_byte_tail /* no input waiting */ 734 | /* new character found on serial port */ 735 | /* convert it to a scancode */ 736 | movw %cs:serial_port_base_address, %dx 737 | inb %dx, %al /* al = serial input char */ 738 | testb $0x80, %al /* non-ascii char received? */ 739 | jnz next_serial_char /* throw char away */ 740 | movb %al, %dl /* dl = character read */ 741 | pushw %ds 742 | pushw %cs 743 | popw %ds /* ds = cs */ 744 | movw $ascii2scan, %bx /* table to translate ascii->scan */ 745 | xlatb /* translate char to scancode */ 746 | popw %ds 747 | /* shift status is ignored at this point, may be used later */ 748 | andb $0x7f, %al /* strip shift status from table */ 749 | movb %al, %ah /* scancode goes in high byte */ 750 | movb %dl, %al /* "translated" ascii in lower byte */ 751 | cmpb $0x7f, %al /* Did the user transmit ascii DEL? */ 752 | jnz get_byte_not_del /* if not, don't do anything to al */ 753 | movb $0x08, %al /* else delete becomes backspace */ 754 | get_byte_not_del: 755 | testw %ax, %ax /* clear zero flag */ 756 | get_byte_tail: 757 | popw %bx 758 | popw %dx 759 | ret 760 | 761 | /* 762 | * poll_byte 763 | * 764 | * get serial byte in %al, scancode in %ah [FIXME: EFI console input] 765 | * retry up to 65536 times for an expected input byte 766 | * 767 | * all registers except %ax preserved 768 | * 769 | */ 770 | 771 | poll_byte: 772 | pushw %cx 773 | xorw %cx, %cx 774 | poll_byte_retry: 775 | inb $0xed, %al 776 | call get_byte 777 | loopz poll_byte_retry /* repeat while zf set or cx != 0 */ 778 | popw %cx 779 | ret 780 | 781 | /* 782 | * get_multibyte 783 | * 784 | * after an escape character, poll for terminal keys that generate 785 | * an escape code plus multiple bytes (up to four). 786 | * 787 | * if no byte is waiting, all registers preserved except flags 788 | * if more bytes are waiting, all registers preserved except %ax and flags 789 | * 790 | */ 791 | get_multibyte: 792 | pushw %bp /* bp points to temp buffer on stack */ 793 | pushw %bx /* bx points to multibyteinput table */ 794 | pushw %cx /* cx will count chars */ 795 | pushw %ax /* ax will receive chars */ 796 | pushl $0 /* make space on stack for 4 chars */ 797 | xorw %cx, %cx /* cx = 0 */ 798 | movw %sp, %bp /* point bp at temp data */ 799 | call poll_byte /* is a character waiting? */ 800 | jz get_multibyte_tail /* if not, bail */ 801 | get_multibyte_store: 802 | movb %al, (%bp) /* store char received */ 803 | incb %cl /* mark one char received */ 804 | incw %bp /* point to next char */ 805 | cmpb $4, %cl /* got enough chars? */ 806 | jz got_multibyte /* no strings longer than 4 chars */ 807 | call poll_byte /* is another char waiting? */ 808 | jnz get_multibyte_store /* store a new one if it's there */ 809 | got_multibyte: 810 | movw $multibyteinput, %bx /* point to first scancode */ 811 | got_multibyte_findkey: 812 | movw %sp, %bp /* bp = start of buffer */ 813 | movb %cs:(%bx), %ah /* ah = scancode */ 814 | incw %bx /* bx = start of test string */ 815 | orb %ah, %ah /* is it zero? */ 816 | jz get_multibyte_tail /* if so, bail, key not found */ 817 | got_multibyte_nextchar: 818 | movb %cs:(%bx), %ch /* ch = test char to compare */ 819 | incw %bx /* point to next char */ 820 | orb %ch, %ch /* is char to compare NUL? */ 821 | jz got_multibyte_key /* matched to end of a string! */ 822 | cmpb %ch, (%bp) /* input tmp buf equal to test char? */ 823 | jnz got_multibyte_try_next_key 824 | /* note: expected that test string will be nul before input string */ 825 | /* no attempt is made to ensure no more than 4 bytes stack read */ 826 | incw %bp /* point to next input */ 827 | jmp got_multibyte_nextchar 828 | got_multibyte_try_next_key: /* align to next scancode/ascii pair */ 829 | movb %cs:(%bx), %ch /* ch = test char to compare */ 830 | incw %bx /* point to next char */ 831 | orb %ch, %ch /* is char to compare NUL? */ 832 | jnz got_multibyte_try_next_key 833 | jmp got_multibyte_findkey 834 | got_multibyte_key: 835 | xorb %al, %al /* ascii value = 0 for special keys */ 836 | movw %sp, %bp 837 | movw %ax, 4(%bp) /* overwrite old %ax value with key */ 838 | get_multibyte_tail: 839 | addw $4, %sp /* pop temp space */ 840 | popw %ax 841 | popw %cx 842 | popw %bx 843 | popw %bp 844 | ret 845 | 846 | /* 847 | * send_byte 848 | * 849 | * send character in %al to serial port [FIXME: EFI console out] 850 | * 851 | * all registers preserved except flags 852 | * 853 | */ 854 | 855 | send_byte: 856 | pushw %ax 857 | pushw %dx 858 | pushw %cx 859 | testb $0x80, %al /* don't send non-ascii chars */ 860 | jnz send_tail /* these should be translated earlier */ 861 | movb %al, %ah /* save char to output in %ah */ 862 | movw $0xFFF0, %cx /* only retry 65520 times */ 863 | serial_ready_test: 864 | call get_serial_lsr /* get serial lsr in %al */ 865 | testb $TRANSMIT_READY_BIT, %al 866 | loopz serial_ready_test /* if !tx ready, loop while cx!=0 */ 867 | movb %ah, %al 868 | movw %cs:serial_port_base_address, %dx 869 | outb %al, %dx 870 | send_tail: 871 | popw %cx 872 | popw %dx 873 | popw %ax 874 | ret 875 | 876 | /* 877 | * translate_char 878 | * 879 | * translate vga character in %al to ascii 880 | * 881 | * returns: 882 | * al = translated character 883 | * 884 | * all registers except %al preserved 885 | * 886 | */ 887 | 888 | translate_char: 889 | pushw %bx 890 | pushw %ds 891 | pushw %cs 892 | popw %ds /* ds = cs */ 893 | testb $0x80, %al 894 | jz translate_char_ctrl 895 | andb $0x7f, %al 896 | movw $high2ascii, %bx 897 | xlatb 898 | translate_char_ctrl: 899 | cmpb $0x20, %al 900 | jnc translate_char_tail 901 | movw $ctrl2ascii, %bx 902 | xlatb 903 | translate_char_tail: 904 | popw %ds 905 | popw %bx 906 | ret 907 | 908 | /* 909 | * translate_char_tty 910 | * 911 | * translate vga character in %al to ascii 912 | * unless %al == 7, 8, 10, or 13 (bell, bs, lf, cr) 913 | * 914 | * returns: 915 | * al = translated character 916 | * 917 | * all registers except %al preserved 918 | * 919 | */ 920 | 921 | translate_char_tty: 922 | cmpb $0x07, %al /* bell */ 923 | jz translate_char_tty_tail 924 | cmpb $0x08, %al /* backspace */ 925 | jz translate_char_tty_tail 926 | cmpb $0x0a, %al /* LF */ 927 | jz translate_char_tty_tail 928 | cmpb $0x0d, %al /* CR */ 929 | jz translate_char_tty_tail 930 | call translate_char 931 | translate_char_tty_tail: 932 | ret 933 | 934 | /* 935 | * send_char 936 | * 937 | * send character 0 - 255 in %al out through serial port 938 | * increment cursor position without control processing 939 | * 940 | * send_byte is used for data that isn't tracked 941 | * 942 | * send_char is used for text that should be tracked 943 | * send_char outputs all characters as non-control chars 944 | * 945 | * returns: 946 | * al = translated character 947 | * 948 | * all registers except %al preserved 949 | * 950 | */ 951 | 952 | send_char: 953 | call sgabioslog_save_char /* save original char+pos */ 954 | call translate_char 955 | jmp send_char_tty_out 956 | /* after ctrl translation, same as send_char_tty */ 957 | 958 | /* 959 | * send_char_tty 960 | * 961 | * send character 0 - 255 in %al out through serial port 962 | * increment cursor position *with* control processing 963 | * for bell, linefeed, cr, and backspace (others all printable) 964 | * 965 | * send_byte is used for data that isn't tracked 966 | * 967 | * send_char_tty is used for text that should be tracked 968 | * 969 | * returns: 970 | * al = translated character 971 | * 972 | * all registers except %al preserved 973 | * 974 | */ 975 | 976 | 977 | /* send character 0 - 255 in %al out through serial port */ 978 | /* increment cursor position with CR/LF/Backspace processing */ 979 | send_char_tty: 980 | call sgabioslog_save_char /* save original char+pos */ 981 | call translate_char_tty 982 | send_char_tty_out: 983 | pushw %dx 984 | call update_serial_cursor 985 | call get_current_cursor /* vga cursor in %dx */ 986 | cmpb $0x0d, %al /* CR */ 987 | jnz send_char_tty_nul /* if not CR, check for NUL */ 988 | orb %dl, %dl /* already at col 0? */ 989 | jz send_char_tty_tail /* no need to re-send CR */ 990 | send_char_tty_nul: 991 | orb %al, %al /* %al == 0 ? (nul) */ 992 | /* more than likely, we have NUL at this point because the caller */ 993 | /* tried to read a char using int $0x10, %ah=8, and is trying */ 994 | /* to re-output it with different attributes - for now send nothing */ 995 | jz send_char_tty_tail 996 | send_char_tty_write: 997 | call memconsole_log_char /* log character sent */ 998 | call send_byte 999 | cmpb $0x07, %al /* bell */ 1000 | jz send_char_tty_tail /* no cursor update for bell */ 1001 | cmpb $0x08, %al /* backspace */ 1002 | jz send_char_tty_backspace 1003 | cmpb $0x0a, %al /* LF */ 1004 | jz send_char_tty_lf 1005 | cmpb $0x0d, %al /* CR */ 1006 | jz send_char_tty_cr 1007 | incb %dl 1008 | jmp send_char_tty_tail 1009 | send_char_tty_backspace: 1010 | orb %dl, %dl 1011 | jz send_char_tty_tail 1012 | decb %dl 1013 | jmp send_char_tty_tail 1014 | send_char_tty_lf: 1015 | incb %dh 1016 | jmp send_char_tty_tail 1017 | send_char_tty_cr: 1018 | xorb %dl, %dl 1019 | send_char_tty_tail: 1020 | cmpb %cs:term_cols, %dl 1021 | jc send_char_tty_check_rows 1022 | movb %cs:term_cols, %dl 1023 | decb %dl /* dl = cols - 1 */ 1024 | send_char_tty_check_rows: 1025 | cmpb %cs:term_rows, %dh 1026 | jc send_char_tty_save_cursor 1027 | movb %cs:term_rows, %dh 1028 | decb %dh /* dh = rows - 1 */ 1029 | send_char_tty_save_cursor: 1030 | call set_current_cursor 1031 | pushw %ds 1032 | pushw $BDA_SEG 1033 | popw %ds 1034 | /* save current position as the serial terminal position */ 1035 | /* since a character was just output at that position */ 1036 | movw %dx, BDA_SERIAL_POS 1037 | popw %ds 1038 | popw %dx 1039 | ret 1040 | 1041 | /* 1042 | * send_asciz_out 1043 | * 1044 | * send nul terminated string pointed to by %ds:%si 1045 | * to serial port without text tracking 1046 | * 1047 | * indended to be used for multi-byte send_byte 1048 | * 1049 | * all registers preserved except flags 1050 | */ 1051 | 1052 | send_asciz_out: 1053 | pushw %ax 1054 | pushw %si 1055 | cld 1056 | send_asciz_loop: 1057 | lodsb 1058 | test %al,%al 1059 | jz send_asciz_end 1060 | call send_byte 1061 | jmp send_asciz_loop 1062 | send_asciz_end: 1063 | popw %si 1064 | popw %ax 1065 | ret 1066 | 1067 | /* 1068 | * send_string 1069 | * 1070 | * send cx chars in string pointed to by %ds:%si 1071 | * to serial port with tty tracking 1072 | * 1073 | * indended to be used for multi-byte send_char_tty 1074 | * 1075 | * all registers preserved except flags 1076 | */ 1077 | 1078 | send_string: 1079 | pushw %ax 1080 | pushw %si 1081 | cld 1082 | send_string_loop: 1083 | lodsb 1084 | call send_char_tty 1085 | loop send_string_loop 1086 | popw %si 1087 | popw %ax 1088 | ret 1089 | 1090 | /* 1091 | * send_string 1092 | * 1093 | * send cx chars in string pointed to by %ds:%si 1094 | * with interleaved attribute data 1095 | * 1096 | * indended to be used for multi-byte send_char_tty 1097 | * with interleaved vga attribute updates 1098 | * 1099 | * all registers preserved except flags 1100 | */ 1101 | 1102 | send_attr_string: 1103 | pushw %ax 1104 | pushw %bx 1105 | pushw %si 1106 | cld 1107 | send_attr_string_loop: 1108 | lodsb 1109 | call send_char_tty 1110 | lodsb 1111 | movb %al, %bl 1112 | call send_attribute /* send attribute in %bl */ 1113 | loop send_attr_string_loop 1114 | popw %si 1115 | popw %bx 1116 | popw %ax 1117 | ret 1118 | 1119 | /* 1120 | * send_number 1121 | * 1122 | * send ascii version of number in %al to serial port 1123 | * 1124 | * intended for ansi cursor positions and attributes, 1125 | * so cursor position is not tracked/updated 1126 | * 1127 | * all registers preserved except flags 1128 | */ 1129 | 1130 | send_number: 1131 | pushw %ax 1132 | pushw %bx 1133 | aam /* ah = al/10, al = al mod 10 */ 1134 | movw %ax, %bx /* bh = al/10, bl = al mod 10 */ 1135 | movb %bh, %al 1136 | aam /* ah = bh/10, al = bh mod 10 */ 1137 | movb %al, %bh /* bh = 10s digit, bl = 1s digit */ 1138 | movb %ah, %al /* ah = al = 100s digit */ 1139 | testb %al, %al /* is there a 100s digit? */ 1140 | jz send_tens /* move to tens if not */ 1141 | orb $0x30, %al /* al = ascii value of digit */ 1142 | call send_byte 1143 | send_tens: 1144 | orb %bh, %ah /* bh = 10s, ah = 100s digits */ 1145 | jz send_ones /* non-zero = must send tens */ 1146 | movb %bh, %al /* al = bh = 10s digit */ 1147 | orb $0x30, %al /* al = ascii value of digit */ 1148 | call send_byte 1149 | send_ones: 1150 | movb %bl, %al /* al = bl = 1s digit */ 1151 | orb $0x30, %al /* al = ascii value of digit */ 1152 | call send_byte 1153 | popw %bx 1154 | popw %ax 1155 | ret 1156 | 1157 | /* 1158 | * send_crlf 1159 | * 1160 | * send CRLF to serial port 1161 | * 1162 | * FIXME: used at vga init and for scrolling terminal 1163 | * so position is not tracked. Callers of this routine 1164 | * predate the code that does smart tty/cursor output. 1165 | * 1166 | * Callers should probably be changed to use those 1167 | * routines or send_crlf changed to use them and 1168 | * terminal scrolling fixed to use linefeed only. 1169 | * 1170 | * all registers preserved except flags 1171 | */ 1172 | 1173 | send_crlf: 1174 | pushw %ax 1175 | movb $0x0d, %al 1176 | call send_byte 1177 | movb $0x0a, %al 1178 | call send_byte 1179 | popw %ax 1180 | ret 1181 | /* 1182 | * send_ansi_csi 1183 | * 1184 | * send ESCAPE [ to serial port 1185 | * 1186 | * output is not tracked since these are control sequences 1187 | * 1188 | * all registers preserved except flags 1189 | */ 1190 | 1191 | send_ansi_csi: /* transmit ESC [ */ 1192 | pushw %ax 1193 | movb $0x1b, %al /* escape */ 1194 | call send_byte 1195 | movb $0x5b, %al /* [ */ 1196 | call send_byte 1197 | popw %ax 1198 | ret 1199 | /* 1200 | * send_ansi_csi_2num 1201 | * 1202 | * send ESC [ %dh ; %dl to serial port 1203 | * 1204 | * since both position and attribute updates generally have 1205 | * two parameters, this function converts values in dx to 1206 | * two ascii numbers. It's expected that the caller will 1207 | * output the final trailing H or m or whatever is required. 1208 | * 1209 | * output is not tracked since these are control sequences 1210 | * 1211 | * all registers preserved except flags 1212 | */ 1213 | 1214 | send_ansi_csi_2num: 1215 | /* send ESC [ %dh ; %dl */ 1216 | pushw %ax 1217 | call send_ansi_csi /* esc [ */ 1218 | movb %dh, %al 1219 | call send_number 1220 | movb $0x3b, %al /* semicolon */ 1221 | call send_byte 1222 | movb %dl, %al 1223 | call send_number 1224 | popw %ax 1225 | ret 1226 | 1227 | /* 1228 | * send_ansi_cursor_pos 1229 | * 1230 | * send ESC [ %dh+1 ; %dl+1 to serial port to position 1231 | * cursor 1232 | * 1233 | * since both position and attribute updates generally have 1234 | * two parameters, this function converts values in dx to 1235 | * two ascii numbers, after adding 1 to both dh and dl. 1236 | * 1237 | * output is not tracked since this is a control sequence 1238 | * 1239 | * all registers preserved except flags 1240 | */ 1241 | 1242 | send_ansi_cursor_pos: 1243 | pushw %ax 1244 | pushw %dx 1245 | addw $0x0101, %dx /* dh += 1, dl += 1 */ 1246 | call send_ansi_csi_2num /* send esc [ %dh+1;%dl+1 */ 1247 | movb $0x48, %al /* H */ 1248 | call send_byte 1249 | popw %dx 1250 | popw %ax 1251 | ret 1252 | 1253 | /* 1254 | * send_attribute 1255 | * 1256 | * send ansi attribute change ESC [ 4x ; 3y ; (1|22)m 1257 | * if the attribute has changed since last sent (stored in bda) 1258 | * 1259 | * output is not tracked since this is a control sequence 1260 | * 1261 | * all registers preserved except flags 1262 | */ 1263 | 1264 | send_attribute: 1265 | andb $0x7f, %bl /* ansi has no bright bg */ 1266 | pushw %ds 1267 | pushw %es 1268 | pushw %ax 1269 | pushw %bx 1270 | pushw %dx 1271 | pushw $BDA_SEG 1272 | popw %es /* es = 0x40 */ 1273 | pushw %cs 1274 | popw %ds /* ds = cs */ 1275 | cmpb %es:BDA_COLOR_VAL, %bl 1276 | jz send_attribute_tail 1277 | cmpb $0x07, %bl /* is it white on black? */ 1278 | jnz send_attribute_color 1279 | /* for white on black, send esc [ m */ 1280 | call send_ansi_csi 1281 | jmp send_attribute_m /* send the m, return */ 1282 | send_attribute_color: 1283 | movb %bl, %ah /* ah = attribute */ 1284 | movw $colortable, %bx 1285 | movb %ah, %al 1286 | andb $7, %al /* al = fg attr */ 1287 | xlatb /* al = fg ansi num */ 1288 | movb %al, %dl /* dl = fg ansi num */ 1289 | movb %ah, %al 1290 | shrb $4, %al /* al = bg attr */ 1291 | xlatb /* al = bg ansi num */ 1292 | movb %al, %dh /* dh = bg ansi num */ 1293 | addw $0x281e, %dx /* 3x=setfg, 4x=setbg */ 1294 | call send_ansi_csi_2num 1295 | movb $0x3b, %al /* semicolon */ 1296 | call send_byte 1297 | shlb $4, %ah /* bright text? */ 1298 | sets %al /* if bit 7, al = 1 */ 1299 | js send_attribute_intensity 1300 | movb $22, %al /* 22 = normal intensity */ 1301 | send_attribute_intensity: 1302 | call send_number /* either 22 or 1 */ 1303 | send_attribute_m: 1304 | movb $0x6d, %al /* m */ 1305 | call send_byte 1306 | send_attribute_tail: 1307 | popw %dx 1308 | popw %bx 1309 | /* mark attribute in %bl the current one */ 1310 | movb %bl, %es:BDA_COLOR_VAL 1311 | popw %ax 1312 | popw %es 1313 | popw %ds 1314 | ret 1315 | 1316 | /* 1317 | * serial_get_input 1318 | * 1319 | * common code for both interrupt-driven and non-interrupt 1320 | * driven serial input. Called only when LSR bit 1 is set. 1321 | * 1322 | * No parameters, no return values 1323 | * 1324 | * Preserves all registers 1325 | */ 1326 | 1327 | serial_get_input: 1328 | pushf 1329 | /* be paranoid about int 9h happening during update */ 1330 | cli 1331 | pushaw 1332 | pushw %ds 1333 | /* next char input buffer is at 0x40:0x1c */ 1334 | pushw $BDA_SEG 1335 | popw %ds /* es = 0x40 */ 1336 | call get_byte /* next scancode/byte in %ax */ 1337 | cmpb $0x1b, %al /* look for escape */ 1338 | jnz serial_gotkey /* not escape, don't look for more bytes */ 1339 | call get_multibyte /* look for any chars after escape */ 1340 | serial_gotkey: 1341 | movw KBD_TAIL, %bx /* bx = keyboard tail pointer */ 1342 | movw %ax, (%bx) /* store key in buffer */ 1343 | addw $2, %bx /* point to next location */ 1344 | cmpw $KBD_BUF_END, %bx /* did the buffer wrap? */ 1345 | jb kbd_buf_no_wrap 1346 | movw $KBD_BUF_START, %bx 1347 | kbd_buf_no_wrap: 1348 | movw %bx, KBD_TAIL /* update tail pointer to show key */ 1349 | popw %ds 1350 | popaw 1351 | popf 1352 | ret 1353 | 1354 | /* 1355 | * irq3_isr 1356 | * 1357 | * entry point for irq 3 / int 0x0b / exception 11 1358 | * 1359 | * Called when COM2 or COM4 have characters pending 1360 | * 1361 | * The segment not present exception should never happen 1362 | * in real mode 16-bit code like this, but just to be safe, 1363 | * if this interrupt is invoked and no characters are 1364 | * pending on the port found in serial_port_base_address, 1365 | * this routine will chain to the original handler. 1366 | * 1367 | * If characters are found pending, they will be processed 1368 | * and control returned via iret. 1369 | */ 1370 | 1371 | irq3_isr: 1372 | #if 0 1373 | pushw %ax 1374 | pushw %dx 1375 | /* placeholder, this shouldn't ever happen */ 1376 | /* no interrupts are configured outside COM1 */ 1377 | call get_serial_lsr /* get serial lsr in %al */ 1378 | jz chain_irq3 /* no port present... */ 1379 | testb $1, %al /* bit 0 of LSR = 1 = data available */ 1380 | jz chain_irq3 /* no input waiting */ 1381 | call serial_get_input /* get input and stuff kbd buffer */ 1382 | movb $0x20, %al 1383 | outb %al, $0x20 /* send non-specific EOI */ 1384 | popw %dx 1385 | popw %ax 1386 | iret 1387 | chain_irq3: 1388 | popw %dx 1389 | popw %ax 1390 | #endif 1391 | jmp do_old_irq3 1392 | 1393 | /* 1394 | * irq4_isr 1395 | * 1396 | * entry point for irq 4 / int 0x0c / exception 12 1397 | * 1398 | * Called when COM1 or COM3 have characters pending 1399 | * 1400 | * The stack fault exception may occur if code attempts to 1401 | * read from sp:0xffff, so if this interrupt is invoked and 1402 | * no characters are pending on the port found in 1403 | * serial_port_base_address, this routine will chain to the 1404 | * original handler. 1405 | * 1406 | * If characters are found pending, they will be processed 1407 | * and control returned via iret. 1408 | */ 1409 | 1410 | irq4_isr: 1411 | #if 0 1412 | pushw %ax 1413 | pushw %dx 1414 | call get_serial_lsr /* get serial lsr in %al */ 1415 | jz chain_irq4 /* no port present... */ 1416 | testb $1, %al /* bit 0 of LSR = 1 = data available */ 1417 | jz chain_irq4 /* no input waiting */ 1418 | call serial_get_input /* get input and stuff kbd buffer */ 1419 | movb $0x20, %al 1420 | outb %al, $0x20 /* send non-specific EOI */ 1421 | popw %dx 1422 | popw %ax 1423 | iret 1424 | chain_irq4: 1425 | popw %dx 1426 | popw %ax 1427 | #endif 1428 | jmp do_old_irq4 1429 | 1430 | /* 1431 | * int14h_isr 1432 | * 1433 | * entry point for int 14h 1434 | * 1435 | */ 1436 | int14h_isr: 1437 | pushaw 1438 | movw %sp, %bp 1439 | addw $16, %bp /* bp points to return address */ 1440 | orb %ah, %ah /* fn 0x00, initialize port */ 1441 | jz int14h_init_port 1442 | cmpb $0x04, %ah /* fn 0x04, extended intialize */ 1443 | jnz chain_isr14h 1444 | int14h_init_port: 1445 | /* check for init port = current port */ 1446 | pushw %ds 1447 | pushw $BDA_SEG 1448 | popw %ds /* ds = 0x40 */ 1449 | movw %dx, %bx /* bx = port number */ 1450 | shlw $1, %bx /* bx = port number * 2 */ 1451 | andw $7, %bx /* bx = bda offset of serial io addr */ 1452 | movw (%bx), %cx /* cx = io address of port to init */ 1453 | popw %ds /* restore original ds */ 1454 | cmpw %cx, %cs:serial_port_base_address 1455 | jnz chain_isr14h /* if different, don't get in the way */ 1456 | /* init port == current port */ 1457 | pushw %ds 1458 | /* LILO 22.6 HACK STARTS HERE */ 1459 | movw (%bp), %bx /* return address for int 14h call */ 1460 | movw 2(%bp), %ds /* return segment for int 14h call */ 1461 | cmpl $0x4f4c494c, 0x06 /* does segment have lilo signature? */ 1462 | jnz int14h_init_tail /* not lilo, bail on hack */ 1463 | cmpw $0x0616, 0x0a /* does version match lilo 22.6? */ 1464 | jnz int14h_init_tail /* unknown lilo release, bail on hack */ 1465 | movb $0, 0x12 /* set lilo com port = 0 */ 1466 | movl $0x90c3585a, (%bx) /* return code= pop dx;pop ax;ret;nop */ 1467 | /* now lilo 22.6's own serial out is permanently disabled */ 1468 | /* this prevents double-character output from int10h + serial */ 1469 | /* this also prevents lilo from stealing serial input chars */ 1470 | /* END LILO 22.6 HACK */ 1471 | int14h_init_tail: 1472 | popw %ds 1473 | popaw 1474 | pushw %dx /* get_serial_lsr trashes %dx */ 1475 | call get_serial_lsr /* return serial status in %al */ 1476 | xorb %ah, %ah /* return serial status in %ax */ 1477 | popw %dx /* restore %dx */ 1478 | iret 1479 | chain_isr14h: 1480 | popaw 1481 | jmp do_old_int14h 1482 | 1483 | /* 1484 | * int16h_isr 1485 | * 1486 | * entry point for int 16h 1487 | * 1488 | * keyboard characters are usually retrieved by calling 1489 | * int 16h, generally placed in the keyboard buffer by 1490 | * irq 1 (int 9h). Poll serial port for new data before 1491 | * chaining to int 16h to fake irq 1 behavior 1492 | * 1493 | * all registers preserved except flags (later iret will restore) 1494 | * bda updated with a new keypress if available 1495 | * 1496 | * FIXME: handle multi-byte keypresses like cursor up/down 1497 | * to send proper scancodes for navigating lilo menus 1498 | */ 1499 | 1500 | int16h_isr: 1501 | pushw %ax 1502 | pushw %dx 1503 | /* each time int 16h is invoked, fake an int 9h */ 1504 | /* except read the serial input buffer */ 1505 | /* then chain to the original int 16h for processing */ 1506 | call get_serial_lsr 1507 | jz chain_isr16h /* no port present... */ 1508 | testb $1, %al /* bit 0 of LSR = 1 = data available */ 1509 | jz chain_isr16h /* no input waiting */ 1510 | call serial_get_input /* get input and stuff kbd buffer */ 1511 | /* for now, leave remaining chars pending in serial fifo */ 1512 | /* int 16h callers only get one char at a time anyway */ 1513 | chain_isr16h: 1514 | popw %dx 1515 | popw %ax 1516 | jmp do_old_int16h 1517 | 1518 | /* 1519 | * update serial_cursor 1520 | * 1521 | * figure out where the cursor was, and where it's going 1522 | * use the minimal amount of serial output to get it there 1523 | * input: vga cursor and serial cursor positions stored in BDA 1524 | * 1525 | * all registers preserved except flags 1526 | * bda updated with new position for serial console cursor 1527 | */ 1528 | update_serial_cursor: 1529 | pushw %ax 1530 | pushw %bx 1531 | pushw %dx 1532 | pushw %ds 1533 | pushw $BDA_SEG 1534 | popw %ds /* ds = 0x40 */ 1535 | call get_current_cursor /* dh = row, dl = col */ 1536 | movw BDA_SERIAL_POS, %bx /* bh = row, bl = col */ 1537 | subb %dl, %bl /* -col update */ 1538 | negb %bl /* col update */ 1539 | subb %dh, %bh /* -row update */ 1540 | negb %bh /* row update */ 1541 | /* handle a few special movement cases */ 1542 | /* cr, lf, bs, bs+bs, space, else send full ansi position */ 1543 | orb %dl, %dl /* column zero? */ 1544 | jnz update_serial_cursor_lf 1545 | movb $0x0d, %al /* CR */ 1546 | call send_byte 1547 | xorb %bl, %bl /* mark no diff in col */ 1548 | update_serial_cursor_lf: 1549 | cmpb $1, %bh /* +1 row? */ 1550 | jnz update_serial_cursor_bs 1551 | movb $0x0a, %al /* LF */ 1552 | call send_byte 1553 | xorb %bh, %bh /* mark no diff in row */ 1554 | update_serial_cursor_bs: 1555 | cmpb $-1, %bl /* one char back */ 1556 | jz update_serial_cursor_one_bs 1557 | cmpb $-2, %bl /* two chars back */ 1558 | jnz update_serial_cursor_space /* check for space */ 1559 | movb $0x08, %al /* BS */ 1560 | call send_byte 1561 | update_serial_cursor_one_bs: 1562 | movb $0x08, %al /* BS */ 1563 | call send_byte 1564 | xorb %bl, %bl /* mark no diff in col */ 1565 | update_serial_cursor_space: 1566 | cmpb $1, %bl /* one char forward */ 1567 | jnz update_serial_cursor_up 1568 | movb $0x20, %al /* space */ 1569 | call send_byte 1570 | xorb %bl, %bl /* mark no diff in col */ 1571 | update_serial_cursor_up: 1572 | cmpb $-1, %bh /* -1 row? */ 1573 | jnz update_serial_cursor_full /* do full ansi pos update */ 1574 | call send_ansi_csi /* send ESC [ A (cursor up) */ 1575 | movb $0x41, %al /* A */ 1576 | call send_byte 1577 | xorb %bh, %bh /* mark no diff in row */ 1578 | update_serial_cursor_full: 1579 | orw %bx, %bx /* diff = 0? */ 1580 | jz update_serial_cursor_done 1581 | call send_ansi_cursor_pos /* set cursor pos from dh,dl */ 1582 | update_serial_cursor_done: 1583 | movw %dx, BDA_SERIAL_POS 1584 | popw %ds 1585 | popw %dx 1586 | popw %bx 1587 | popw %ax 1588 | ret 1589 | 1590 | /* 1591 | * write_teletype 1592 | * 1593 | * handle int 10h, function 0eh 1594 | * 1595 | * ah = 0x0e write teletype character 1596 | * al = character ascii code 1597 | * bh = display page number 1598 | * 1599 | * all registers except %al preserved 1600 | * caller will restore all registers 1601 | */ 1602 | 1603 | write_teletype: 1604 | pushw %bx 1605 | movb $0x07, %bl /* black bg, white fg */ 1606 | call send_attribute 1607 | popw %bx 1608 | call send_char_tty 1609 | ret 1610 | 1611 | /* 1612 | * write_attr_char 1613 | * 1614 | * handle int 10h, function 09h 1615 | * 1616 | * ah = 0x09 write attribute/character at current cursor position 1617 | * al = character ascii code 1618 | * bh = display page number 1619 | * bl = character attribute 1620 | * cx = repetition count 1621 | * 1622 | * does not update cursor position 1623 | * all registers except %cx and %al preserved 1624 | * caller will restore all registers 1625 | */ 1626 | 1627 | write_attr_char: 1628 | call send_attribute /* send attribute in %bl */ 1629 | jmp write_char_common 1630 | 1631 | /* 1632 | * write_char 1633 | * 1634 | * handle int 10h, function 0ah 1635 | * 1636 | * ah = 0x0a write character at current cursor position 1637 | * al = character ascii code 1638 | * bh = display page number 1639 | * cx = repetition count 1640 | * 1641 | * does not update cursor position 1642 | * all registers except %cx and %al preserved 1643 | * caller will restore all registers 1644 | */ 1645 | 1646 | write_char: 1647 | pushw %bx 1648 | movb $0x07, %bl /* black bg, white fg */ 1649 | call send_attribute 1650 | popw %bx 1651 | write_char_common: 1652 | call get_current_cursor 1653 | call send_char 1654 | /* make cx=0 and cx=1 only output one char */ 1655 | cmpw $1, %cx 1656 | jbe write_char_tail 1657 | decw %cx 1658 | jmp write_char 1659 | write_char_tail: 1660 | /* put cursor back where it was on entry */ 1661 | call set_current_cursor 1662 | ret 1663 | 1664 | /* 1665 | * write_string 1666 | * 1667 | * handle int 10h, function 13h 1668 | * 1669 | * ah = 0x13 write character at current cursor position 1670 | * al = 0, data = char, ..., no cursor update 1671 | * al = 1, data = char, ..., cursor at end of string 1672 | * al = 2, data = char+attr, ..., no cursor update 1673 | * al = 3, data = char+attr, ..., cursor at end of string 1674 | * bh = display page number 1675 | * bl = character attribute for all chars (if al = 0 or 1) 1676 | * cx = characters in string (attributes don't count) 1677 | * dh = cursor row start 1678 | * dl = cursor column start 1679 | * es:bp = pointer to source text string in memory 1680 | * 1681 | * all registers preserved except flags 1682 | * caller will restore all registers 1683 | */ 1684 | write_string: 1685 | call set_cursor_position 1686 | pushw %ds 1687 | pushw %es 1688 | pushw %es 1689 | popw %ds /* ds = es */ 1690 | movw %bp, %si /* si = bp */ 1691 | testb $2, %al 1692 | jnz write_attr_string 1693 | call send_attribute /* send attribute in %bl */ 1694 | test %cx, %cx 1695 | jz write_string_empty 1696 | call send_string /* plaintext out */ 1697 | write_string_empty: 1698 | jmp write_string_update_cursor 1699 | write_attr_string: 1700 | call send_attr_string /* text+attrib out */ 1701 | write_string_update_cursor: 1702 | testb $1, %al /* cursor update? */ 1703 | jnz write_string_tail /* yes? already happened */ 1704 | /* restore entry cursor position if no update */ 1705 | call set_cursor_position 1706 | write_string_tail: 1707 | popw %es 1708 | popw %ds 1709 | ret 1710 | 1711 | /* 1712 | * set_cursor_position 1713 | * 1714 | * handle int 10h, function 02h 1715 | * 1716 | * ah = 0x02 set cursor position 1717 | * bh = display page number 1718 | * dh = cursor row 1719 | * dl = cursor column 1720 | * 1721 | * update bda cursor position with value in %dx 1722 | * serial console cursor only updated on text output 1723 | * this routine also called by set_current_cursor 1724 | * which won't bother setting ah = 2 1725 | * 1726 | * all registers preserved except flags 1727 | */ 1728 | 1729 | set_cursor_position: 1730 | pushw %ax 1731 | pushw %bx 1732 | pushw %ds 1733 | pushw $BDA_SEG 1734 | popw %ds /* ds = 0x40 */ 1735 | movzbw %bh, %ax /* ax = page number */ 1736 | andb $0x07, %al /* prevent invalid page number */ 1737 | shlb $1, %al /* calculate word offset */ 1738 | addb $BDA_CURSOR_BUF, %al /* ax = cursor save offset */ 1739 | movw %ax, %bx /* bx = cursor save offset */ 1740 | movw %dx, (%bx) /* save new cursor value */ 1741 | popw %ds 1742 | popw %bx 1743 | popw %ax 1744 | ret 1745 | 1746 | /* 1747 | * set_current_cursor 1748 | * 1749 | * get current display page number and call set_cursor_positon 1750 | * to store the row/column value in dx to the bda 1751 | * 1752 | * all registers preserved except flags 1753 | */ 1754 | 1755 | set_current_cursor: 1756 | pushw %ds 1757 | pushw %bx 1758 | pushw $BDA_SEG 1759 | popw %ds /* ds = 0x40 */ 1760 | movb BDA_ACTIVE_PAGE, %bh 1761 | call set_cursor_position 1762 | popw %bx 1763 | popw %ds 1764 | ret 1765 | 1766 | /* 1767 | * get_cursor_common 1768 | * 1769 | * read cursor position for page %bh from bda into %dx 1770 | * 1771 | * returns: 1772 | * dh = cursor row 1773 | * dl = cursor column 1774 | * ch = cursor start scanline 1775 | * cl = cursor end scanline 1776 | * 1777 | * all registers except %dx, %cx preserved 1778 | */ 1779 | get_cursor_common: 1780 | pushw %bx 1781 | pushw %ds 1782 | pushw $BDA_SEG 1783 | popw %ds /* ds = 0x40 */ 1784 | movzbw %bh, %bx /* dx = current page */ 1785 | andb $7, %bl 1786 | shlb $1, %bl 1787 | addb $BDA_CURSOR_BUF, %bl 1788 | movw (%bx), %dx /* get cursor pos */ 1789 | movw BDA_CURSOR_SCAN, %cx 1790 | popw %ds 1791 | popw %bx 1792 | ret 1793 | 1794 | /* 1795 | * get_current_cursor 1796 | * 1797 | * read cursor position for current page from bda into %dx 1798 | * 1799 | * returns: 1800 | * dh = cursor row 1801 | * dl = cursor column 1802 | * 1803 | * all registers except %dx preserved 1804 | */ 1805 | 1806 | get_current_cursor: 1807 | pushw %ds 1808 | pushw %bx 1809 | pushw %cx 1810 | pushw $BDA_SEG 1811 | popw %ds /* ds = 0x40 */ 1812 | movb BDA_ACTIVE_PAGE, %bh 1813 | call get_cursor_common 1814 | popw %cx 1815 | popw %bx 1816 | popw %ds 1817 | ret 1818 | 1819 | /* 1820 | * get_cursor_position 1821 | * 1822 | * handle int 10h, function 03h 1823 | * 1824 | * ah = 0x02 get cursor position 1825 | * bh = display page number 1826 | * 1827 | * returns: 1828 | * ax = 0 1829 | * ch = cursor start scanline 1830 | * ch = cursor end scanline 1831 | * dh = cursor row 1832 | * dl = cursor column 1833 | * 1834 | * all registers except %ax, %cx, %dx preserved 1835 | */ 1836 | 1837 | get_cursor_position: 1838 | call bail_if_vga_attached /* does not return if vga attached */ 1839 | popw %ax /* not chaining, pop fake return address */ 1840 | popw %dx /* not chaining, pop saved cursor position */ 1841 | popaw /* not chaining to old int 10h, pop saved state */ 1842 | call get_cursor_common 1843 | xorw %ax, %ax 1844 | iret 1845 | 1846 | /* 1847 | * return_current_video_state 1848 | * 1849 | * handle int 10h, function 0fh 1850 | * 1851 | * ah = 0x0f return current video state 1852 | * 1853 | * returns: 1854 | * ah = number of columns on screen (from 40:4a) 1855 | * al = current video mode setting (from 40:49) 1856 | * bh = active display page number (from 40:62) 1857 | * 1858 | * all registers except %ax and %bh preserved 1859 | */ 1860 | 1861 | read_current_video_state: 1862 | call bail_if_vga_attached /* does not return if vga attached */ 1863 | popw %ax /* not chaining, pop fake return address */ 1864 | popw %dx /* not chaining, pop saved cursor position */ 1865 | popaw /* not chaining to old int 10h, pop saved state */ 1866 | pushw %ds 1867 | pushw $BDA_SEG 1868 | popw %ds /* ds = 0x40 */ 1869 | movb BDA_COLS, %ah 1870 | movb BDA_MODE_NUM, %al 1871 | movb BDA_ACTIVE_PAGE, %bh 1872 | popw %ds 1873 | iret 1874 | 1875 | /* 1876 | * read_attr_char 1877 | * 1878 | * handle int 10h, function 08h 1879 | * 1880 | * ah = 0x08 read character/attribute from screen 1881 | * 1882 | * returns: 1883 | * ah = attribute at current cursor position 1884 | * al = character read from current cursor position 1885 | * 1886 | * all registers preserved except %ax and flags 1887 | */ 1888 | 1889 | read_attr_char: 1890 | call bail_if_vga_attached /* does not return if vga attached */ 1891 | popw %ax /* not chaining, pop fake return address */ 1892 | popw %dx /* not chaining, pop saved cursor position */ 1893 | popaw /* not chaining to old int 10h, pop saved state */ 1894 | pushw %ds 1895 | pushw $BDA_SEG 1896 | popw %ds /* ds = 0x40 */ 1897 | movb BDA_COLOR_VAL, %ah /* return last color value */ 1898 | call sgabioslog_get_char 1899 | popw %ds 1900 | iret 1901 | 1902 | /* 1903 | * set_video_mode 1904 | * 1905 | * handle int 10h, function 00h 1906 | * 1907 | * ah = 0x00 set video mode 1908 | * al = video mode 1909 | * 1910 | * unless bit 7 of al = 1, setting mode clears screen 1911 | * 1912 | * all registers preserved except %bh, %dx, flags 1913 | */ 1914 | 1915 | set_video_mode: 1916 | testb $0x80, %al /* preserve screen flag? */ 1917 | jnz set_video_mode_tail 1918 | call send_ansi_csi 1919 | movb $0x32, %al /* 2 */ 1920 | call send_byte 1921 | movb $0x4a, %al /* J */ 1922 | call send_byte 1923 | set_video_mode_tail: 1924 | movb $0x07, %bl /* white on black text */ 1925 | call send_attribute /* send attribute in %bl */ 1926 | /* set cursor position to 0,0 */ 1927 | xorb %bh, %bh /* page 0 */ 1928 | xorw %dx, %dx 1929 | jmp set_cursor_position 1930 | 1931 | /* 1932 | * scroll_page_up 1933 | * 1934 | * handle int 10h, function 06h 1935 | * 1936 | * ah = 0x06 scroll current page up 1937 | * al = scroll distance in character rows (0 blanks entire area) 1938 | * bh = attribute to used on blanked lines 1939 | * ch = top row (upper left corner) of window 1940 | * cl = left-most column (upper left corner) of window 1941 | * dh = bottom row (lower right corner) of window 1942 | * dl = right-most column (lower right corner) of window 1943 | * 1944 | * all registers preserved except flags 1945 | */ 1946 | 1947 | scroll_page_up: 1948 | pushw %si 1949 | pushw %dx 1950 | call get_current_cursor /* save current cursor */ 1951 | movw %dx, %si /* si = vga cursor pos */ 1952 | popw %dx 1953 | cmpb $0, %al /* al = 0 = clear window */ 1954 | jz scroll_common_clear 1955 | pushw %ax 1956 | call send_ansi_csi /* CSI [ %al S */ 1957 | call send_number 1958 | movb $0x53, %al /* S */ 1959 | call send_byte 1960 | popw %dx 1961 | popw %si 1962 | ret 1963 | 1964 | /* 1965 | * scroll_common_clear 1966 | * 1967 | * common tail for up/down scrolls to clear window specified 1968 | * in %cx and %dx. 1969 | * 1970 | * stack should contain saved copy of si 1971 | * si = original vga cursor position on service entry 1972 | * 1973 | * bh = attribute to used on blanked lines 1974 | * ch = top row (upper left corner) of window 1975 | * cl = left-most column (upper left corner) of window 1976 | * dh = bottom row (lower right corner) of window 1977 | * dl = right-most column (lower right corner) of window 1978 | */ 1979 | scroll_common_clear: 1980 | pushw %ax 1981 | xchgb %bl, %bh /* bl = attribute, bh = old bl */ 1982 | call send_attribute /* send attribute in %bl */ 1983 | xchgb %bl, %bh /* restore bx */ 1984 | pushw %ds 1985 | pushw $BDA_SEG 1986 | popw %ds /* ds = 0x40 */ 1987 | /* check to see if region is full screen, and attribute default */ 1988 | orw %cx, %cx /* is top left 0,0? */ 1989 | jnz scroll_common_window /* no, handle window */ 1990 | cmpb $0x07, %bh /* is attribute white on black? */ 1991 | jnz scroll_common_window /* no, must write spaces */ 1992 | #ifdef LILO_CLEAR_WORKAROUND_NOT_REQUIRED 1993 | cmpb %cs:term_cols, %dl /* is right less than cols ? */ 1994 | jc scroll_common_window /* if so, handle window */ 1995 | cmpb %cs:term_rows, %dh /* is bottom less than rows ? */ 1996 | jc scroll_common_window /* if so, handle window */ 1997 | #endif 1998 | /* safe to send standard clear screen sequence */ 1999 | call send_ansi_csi /* send ESC [ */ 2000 | movb $0x32, %al /* 2 */ 2001 | call send_byte 2002 | movb $0x4a, %al /* J */ 2003 | call send_byte 2004 | jmp scroll_common_tail 2005 | scroll_common_window: 2006 | pushw %dx 2007 | movw %cx, %dx /* dx = upper right */ 2008 | call set_current_cursor 2009 | popw %dx 2010 | pushw %cx 2011 | /* setup cx with count of chars to clear per row */ 2012 | xorb %ch, %ch 2013 | negb %cl 2014 | addb %dl, %cl /* cl = dl - cl */ 2015 | incb %cl /* start = end col = clear 1 col */ 2016 | cmpb %cs:term_cols, %cl /* is count < cols? */ 2017 | jc scroll_common_row_ok /* if so then skip limit */ 2018 | movb %cs:term_cols, %cl /* limit count to cols */ 2019 | scroll_common_row_ok: 2020 | jz scroll_common_row_done /* count == 0 ? */ 2021 | movb $0x20, %al /* space */ 2022 | scroll_common_space_loop: 2023 | call send_char 2024 | loop scroll_common_space_loop /* send cx spaces */ 2025 | scroll_common_row_done: 2026 | popw %cx 2027 | incb %ch /* top left now next row */ 2028 | cmpb %dh, %ch 2029 | jbe scroll_common_window /* do next row */ 2030 | scroll_common_tail: 2031 | popw %ds 2032 | popw %ax 2033 | pushw %dx 2034 | movw %si, %dx /* dx = saved vga cursor pos */ 2035 | call set_current_cursor /* restore saved cursor */ 2036 | popw %dx 2037 | popw %si 2038 | ret 2039 | 2040 | /* 2041 | * scroll_page_down 2042 | * 2043 | * handle int 10h, function 07h 2044 | * 2045 | * ah = 0x07 scroll current page down 2046 | * al = scroll distance in character rows (0 blanks entire area) 2047 | * bh = attribute to used on blanked lines 2048 | * ch = top row (upper left corner) of window 2049 | * cl = left-most column (upper left corner) of window 2050 | * dh = bottom row (lower right corner) of window 2051 | * dl = right-most column (lower right corner) of window 2052 | * 2053 | * FIXME: this routine doesn't handle windowing, it currently 2054 | * only handles one line screen scrolls and erasing entire screen 2055 | * 2056 | * all registers preserved except flags 2057 | */ 2058 | 2059 | scroll_page_down: 2060 | pushw %si 2061 | pushw %dx 2062 | call get_current_cursor /* save current cursor */ 2063 | movw %dx, %si /* si = vga cursor pos */ 2064 | popw %dx 2065 | cmpb $0, %al /* al = 0 = clear window */ 2066 | jz scroll_common_clear 2067 | pushw %ax 2068 | call send_ansi_csi /* CSI [ %al T */ 2069 | call send_number 2070 | movb $0x54, %al /* T */ 2071 | call send_byte 2072 | popw %dx 2073 | popw %si 2074 | ret 2075 | 2076 | /* 2077 | * bail_if_vga_attached 2078 | * 2079 | * Check for vga installed, if not, return to caller. 2080 | * If so, pop return address, return to chain_isr_10h 2081 | * 2082 | * expected that routine calling this one has chain_isr_10h 2083 | * as the next item on the stack 2084 | * 2085 | * all registers except flags and sp preserved 2086 | */ 2087 | 2088 | bail_if_vga_attached: 2089 | cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ 2090 | jnz bail_tail /* if not, don't modify stack */ 2091 | addw $2, %sp /* else drop first return address */ 2092 | bail_tail: 2093 | ret /* return to caller or chain_isr_10h */ 2094 | 2095 | /* 2096 | * int10h_isr 2097 | * 2098 | * entry point for int 10h 2099 | * 2100 | * save all registers, force return to chain to previous int10h isr 2101 | * decide which function in ah needs to be dispatched 2102 | * 2103 | * ah = 0x00 set mode 2104 | * ah = 0x01 set cursor type 2105 | * ah = 0x02 set cursor position 2106 | * ah = 0x03 read cursor position 2107 | * ah = 0x04 read light pen position 2108 | * ah = 0x05 set active display page 2109 | * ah = 0x06 scroll active page up 2110 | * ah = 0x07 scroll active page down 2111 | * ah = 0x08 read attribute/character at cursor 2112 | * ah = 0x09 write attribute/character at cursor 2113 | * ah = 0x0a write character at cursor position 2114 | * ah = 0x0b set color palette 2115 | * ah = 0x0c write pixel 2116 | * ah = 0x0d read pixel 2117 | * ah = 0x0e write teletype 2118 | * ah = 0x0f read current video state 2119 | * ah = 0x10 set individual palette registers 2120 | * ah = 0x11 character generation (font control/info) 2121 | * ah = 0x12 alternate select (video control/info) 2122 | * ah = 0x13 write string 2123 | * ah = 0x1a read/write display combination code 2124 | * ah = 0x1b return functionality/state information 2125 | * ah = 0x1c save/restore video state 2126 | * ah = 0x4f vesa bios calls 2127 | * all registers preserved except flags (later iret will restore) 2128 | */ 2129 | 2130 | int10h_isr: 2131 | pushaw 2132 | call get_current_cursor 2133 | pushw %dx /* save current cursor */ 2134 | pushw %bp /* need bp for indexing off stack */ 2135 | movw %sp, %bp /* bp = sp */ 2136 | movw 14(%bp), %dx /* restore dx from earlier pushaw */ 2137 | popw %bp /* restore old bp */ 2138 | pushw $chain_isr10h /* force return to chain_isr10h */ 2139 | testb %ah, %ah 2140 | jnz int10h_02 2141 | jmp set_video_mode 2142 | int10h_02: 2143 | cmpb $0x02, %ah 2144 | jnz int10h_03 2145 | jmp set_cursor_position 2146 | int10h_03: 2147 | cmpb $0x03, %ah 2148 | jnz int10h_06 2149 | jmp get_cursor_position 2150 | int10h_06: 2151 | cmpb $0x06, %ah 2152 | jnz int10h_07 2153 | jmp scroll_page_up 2154 | int10h_07: 2155 | cmpb $0x07, %ah 2156 | jnz int10h_08 2157 | jmp scroll_page_down 2158 | int10h_08: 2159 | cmpb $0x08, %ah 2160 | jnz int10h_09 2161 | jmp read_attr_char 2162 | int10h_09: 2163 | cmpb $0x09, %ah 2164 | jnz int10h_0a 2165 | jmp write_attr_char 2166 | int10h_0a: 2167 | cmpb $0x0a, %ah 2168 | jnz int10h_0e 2169 | jmp write_char 2170 | int10h_0e: 2171 | cmpb $0x0e, %ah 2172 | jnz int10h_0f 2173 | jmp write_teletype 2174 | int10h_0f: 2175 | cmpb $0x0f, %ah 2176 | jnz int10h_13 2177 | jmp read_current_video_state 2178 | int10h_13: 2179 | cmpb $0x13, %ah 2180 | jnz int10h_default 2181 | jmp write_string 2182 | int10h_default: 2183 | popw %ax /* pop chain_isr10h return address */ 2184 | chain_isr10h: 2185 | popw %dx /* pop saved cursor */ 2186 | cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ 2187 | jnz chain_post_cursor /* if not, don't restore the cursor */ 2188 | call set_current_cursor /* restore cursor if vga attached */ 2189 | chain_post_cursor: 2190 | popaw 2191 | jmp do_old_int10h 2192 | 2193 | /* 2194 | * pnp_sga_init 2195 | * 2196 | * handle PnP initialization of option rom 2197 | * 2198 | * es:di = pointer to PnP structure 2199 | * ax = indication as to which vectors should be hooked 2200 | * by specifying th type of boot device this has 2201 | * been selected as 2202 | * bit 7..3= reserved(0) 2203 | * bit 2 = 1 = connect as IPL (int 13h) 2204 | * bit 1 = 1 = connect as primary video (int 10h) 2205 | * bit 0 = 1 = connect as primary input (int 9h) 2206 | * bx = card select number (probably 0xffff) 2207 | * dx = read data port address (probably 0xffff) 2208 | * 2209 | * return: 2210 | * ax = initialization status 2211 | * bit 8 = 1 = IPL device supports int 13h block dev format 2212 | * bit 7 = 1 = Output device supports int 10h char output 2213 | * bit 6 = 1 = Input device supports int 9h char input 2214 | * bit 5..4 = 00 = no IPL device attached 2215 | * 01 = unknown whether or not IPL device attached 2216 | * 10 = IPL device attached (RPL devices have connection) 2217 | * 11 = reserved 2218 | * bit 3..2 = 00 = no display device attached 2219 | * 01 = unknown whether or not display device attached 2220 | * 10 = display device attached 2221 | * 11 = reserved 2222 | * bit 1..0 = 00 = no input device attached 2223 | * 01 = unknown whether or not input device attached 2224 | * 10 = input device attached 2225 | * 11 = reserved 2226 | * 2227 | * all registers preserved except %ax 2228 | */ 2229 | 2230 | pnp_sga_init: 2231 | /* FIXME: this is *wrong* -- init only what bios says to init */ 2232 | movw $0xca, %ax /* 0xca = attached int 10h, 9h display, input */ 2233 | 2234 | /* 2235 | * sga_init 2236 | * 2237 | * legacy option rom entry point 2238 | * 2239 | * all registers preserved 2240 | */ 2241 | 2242 | sga_init: 2243 | /* this is probably paranoid about register preservation */ 2244 | pushfw 2245 | cli /* more paranoia */ 2246 | pushaw 2247 | pushw %ds 2248 | pushw %es 2249 | pushw $0 2250 | popw %es /* es = 0 */ 2251 | pushw %cs 2252 | popw %ds /* ds = cs */ 2253 | /* get original ISR */ 2254 | movl %es:0x28, %eax /* eax = old irq 3/int 0bh */ 2255 | movl %eax, old_irq3 /* save away old irq 4/int 0bh */ 2256 | movl %es:0x2c, %eax /* eax = old irq 4/int 0ch */ 2257 | movl %eax, old_irq4 /* save away old irq 4/int 0ch */ 2258 | movl %es:0x40, %eax /* eax = old int 10h */ 2259 | movl %eax, old_int10h /* save away old int 10h */ 2260 | movl %es:0x50, %eax /* eax = old int 14h */ 2261 | movl %eax, old_int14h /* save away old int 14h */ 2262 | movl %es:0x58, %eax /* eax = old int 16h */ 2263 | movl %eax, old_int16h /* save away old int 16h */ 2264 | movw $irq3_isr, %es:0x28 /* new irq 3 offset */ 2265 | movw %cs, %es:0x2a /* write new irq 3 seg */ 2266 | movw $irq4_isr, %es:0x2c /* new irq 4 offset */ 2267 | movw %cs, %es:0x2e /* write new irq 4 seg */ 2268 | movw $int10h_isr, %es:0x40 /* new int 10h offset */ 2269 | movw %cs, %es:0x42 /* write new int10h seg */ 2270 | movw $int14h_isr, %es:0x50 /* new int 14h offset */ 2271 | movw %cs, %es:0x52 /* write new int14h seg */ 2272 | movw $int16h_isr, %es:0x58 /* new int 16h offset */ 2273 | movw %cs, %es:0x5a /* write new int16h seg */ 2274 | /* empty input buffer to prepare for terminal sizing */ 2275 | call init_serial_port 2276 | input_clear_loop: 2277 | call get_byte 2278 | jnz input_clear_loop 2279 | movw $term_init_string, %si 2280 | call send_asciz_out 2281 | push $BDA_SEG 2282 | push $BDA_SEG 2283 | popw %ds /* ds = 0x40 */ 2284 | popw %es /* es = 0x40 */ 2285 | movw $BDA_CURSOR_BUF, %di 2286 | input_timeout_loop: 2287 | /* get input from terminal until timeout found */ 2288 | /* store input at 40:50 - 40:5e (cursor pos) */ 2289 | call poll_byte 2290 | jz input_timeout 2291 | stosb /* es:di */ 2292 | cmpw $0x5f, %di /* 14 characters max */ 2293 | jnz input_timeout_loop /* good for more data */ 2294 | input_timeout: 2295 | xorb %al, %al /* nul terminate input */ 2296 | stosb 2297 | cmpw $0x58, %di /* less than 8 chars? */ 2298 | jc resize_end /* too small to have valid data */ 2299 | movw $BDA_CURSOR_BUF, %si /* point to start */ 2300 | lodsw /* ax = first 2 chars */ 2301 | cmpw $0x5b1b, %ax /* was it "ESC[" ? */ 2302 | jnz resize_end /* reply starts ESC[row;colR */ 2303 | xorb %bl, %bl /* bl = ascii->int conversion */ 2304 | input_first_number: 2305 | lodsb /* al = next char */ 2306 | cmpb $0x30, %al 2307 | jc resize_end /* char < 0x30 invalid */ 2308 | cmpb $0x3a, %al /* is char < 0x3a */ 2309 | jnc input_semicolon 2310 | andb $0x0f, %al /* al = 0 - 9 */ 2311 | movb %bl, %ah /* ah = last conversion */ 2312 | aad /* ax = (al + ah * 10) & 0xff */ 2313 | movb %al, %bl /* bl = row ascii->int conversion */ 2314 | jmp input_first_number 2315 | input_semicolon: 2316 | /* at this point bl should contain rows, al = ; */ 2317 | /* sanity check, bail if invalid */ 2318 | cmpb $0x3b, %al 2319 | jnz resize_end /* invalid input found */ 2320 | cmpb $0x0a, %bl /* less than 10 rows? */ 2321 | jc suspect_loopback /* consider input invalid */ 2322 | xorb %bh, %bh /* bh = col ascii->int conversion */ 2323 | input_second_number: 2324 | lodsb /* al = next char */ 2325 | cmpb $0x30, %al 2326 | jc resize_end /* char < 0x30 invalid */ 2327 | cmpb $0x3a, %al /* is char < 0x3a */ 2328 | jnc input_final_r 2329 | andb $0x0f, %al /* al = 0 - 9 */ 2330 | movb %bh, %ah /* ah = last conversion */ 2331 | aad /* ax = (al + ah * 10) & 0xff */ 2332 | movb %al, %bh /* bh = ascii->int conversion */ 2333 | jmp input_second_number 2334 | input_final_r: 2335 | cmpb $0x52, %al /* is al = 'R' ? */ 2336 | jnz suspect_loopback /* invalid input found */ 2337 | movb %bl, %cs:term_rows /* save away bl rows value */ 2338 | cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ 2339 | jz resize_end /* if so, leave term_cols at 80 */ 2340 | movb %bh, %cs:term_cols /* save away bh cols value */ 2341 | jmp resize_end 2342 | suspect_loopback: 2343 | /* 2344 | * characters were received that look like what we sent out 2345 | * at this point, assume that a loopback device was plugged in 2346 | * and disable any future serial port reads or writes, by pointing 2347 | * output to port 0x2e8 (COM4) instead of 0x3f8 -- it's expected 2348 | * that this is safe since a real port responds correctly and a 2349 | * missing port will respond with 0xff which will terminate the 2350 | * loop that waits for the "right" status on the port. 2351 | */ 2352 | movw $0x2e8, %cs:serial_port_base_address 2353 | resize_end: 2354 | /* clear (hopefully) overwritten cursor position buffer */ 2355 | xorb %al, %al 2356 | movw $BDA_CURSOR_BUF, %di 2357 | movw $0x10, %cx 2358 | cld 2359 | rep 2360 | stosb /* fill 40:50 - 40:5f with 0 */ 2361 | pushw %cs 2362 | popw %ds /* ds = cs */ 2363 | call get_byte /* flush any remaining "wrong" input */ 2364 | jnz resize_end 2365 | call send_crlf /* place cursor on start of last line */ 2366 | movw $mfg_string, %si 2367 | call send_asciz_out 2368 | call send_crlf 2369 | movw $prod_string, %si 2370 | call send_asciz_out 2371 | call send_crlf 2372 | movw $long_version, %si 2373 | call send_asciz_out 2374 | call send_crlf 2375 | /* if vga attached, skip terminal message and bda setup... */ 2376 | cmpw $0xc000, %cs:old_int10h_seg /* vga attached? */ 2377 | jz post_bda_init_tail /* if so, don't modify BDA */ 2378 | /* show detected terminal size, or default if none detected */ 2379 | movw $term_info, %si 2380 | call send_asciz_out 2381 | pushw $BDA_SEG 2382 | popw %ds /* ds = 0x40 */ 2383 | movb %cs:term_cols, %al 2384 | movb %al, BDA_COLS /* 40:4a = number of character cols */ 2385 | movb $0, BDA_CURSOR_COL /* 40:51 = cursor0 col */ 2386 | call send_number 2387 | movb $0x78, %al /* x */ 2388 | call send_byte 2389 | movb %cs:term_rows, %al 2390 | movb %al, %ah 2391 | decb %ah /* ah = rows-1 */ 2392 | movb %ah, BDA_ROWS /* 40:84 = num character rows - 1 */ 2393 | movb %ah, BDA_CURSOR_ROW /* 40:50 = cursor0 row */ 2394 | call send_number 2395 | call send_crlf 2396 | movb $3, BDA_MODE_NUM 2397 | movb $0x29, BDA_MODE_SEL 2398 | movw $VGA_IO_BASE, BDA_6845_ADDR 2399 | movw $0x4000, BDA_PAGE_SIZE /* 16KB per video page */ 2400 | /* to avoid ansi colors every character, store last attribute */ 2401 | movb $0x07, BDA_COLOR_VAL /* 07 = black bg, white fg */ 2402 | movw %cs, %ax 2403 | movw $_start, BDA_ROM_OFF 2404 | movw %ax, BDA_ROM_SEG 2405 | post_bda_init_tail: 2406 | /* copy BDA rows/cols to sgabios location... */ 2407 | /* if vga card is installed, reuse those values... */ 2408 | /* if no vga card is installed, this shouldn't change anything */ 2409 | pushw $BDA_SEG 2410 | popw %ds /* ds = 0x40 */ 2411 | movb BDA_ROWS, %al 2412 | incb %al /* bda holds rows-1 */ 2413 | movb %al, %cs:term_rows /* sgabios rows */ 2414 | movb BDA_COLS, %ah 2415 | movb %ah, %cs:term_cols /* sgabios cols */ 2416 | /* setup in-memory logging of console if desired... */ 2417 | call setup_memconsole 2418 | /* setup logging of last 256 characters output, if ebda has room */ 2419 | call sgabioslog_setup_ebda 2420 | movw $ebda_info, %si 2421 | call send_asciz_out 2422 | movw %cs:sgabios_ebda_logbuf_offset, %ax 2423 | xchgb %ah, %al 2424 | call send_number 2425 | movb $0x20, %al 2426 | call send_byte 2427 | movb %ah, %al 2428 | call send_number 2429 | call send_crlf 2430 | popw %es 2431 | popw %ds 2432 | popaw 2433 | popf 2434 | lret 2435 | 2436 | _end_sgabios: 2437 | -------------------------------------------------------------------------------- /sgabios.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* don't allocate ebda if new value at 0x40e will be less than this */ 18 | #define EBDA_MIN_SEG 0x9800 19 | #define SGABIOS_EBDA_KB 1 20 | /* note: no testing has yet been done logging other than 256 bytes */ 21 | #define SGABIOS_EBDA_BYTES (SGABIOS_EBDA_KB*1024) 22 | #define SGABIOS_EBDA_DELTA (SGABIOS_EBDA_BYTES/16) 23 | #define SGABIOS_EBDA_LOG_START 256 24 | #define SGABIOS_EBDA_LOG_SIZE 256 25 | #define SGABIOS_EBDA_POS_START (SGABIOS_EBDA_LOG_START+SGABIOS_EBDA_LOG_SIZE) 26 | #define SGABIOS_EBDA_POS_LAST (SGABIOS_EBDA_POS_START+(SGABIOS_EBDA_LOG_SIZE*2)-2) 27 | 28 | /* serial costants that may require modification */ 29 | #define COM_BASE_ADDR 0x3f8 30 | #define PORT_SPEED 115200 31 | #define LCR_VALUE 0x13 /* 8n1 */ 32 | 33 | /* serial constants below shouldn't require modification */ 34 | #define IER_OFFSET 0x01 35 | #define FCR_OFFSET 0x02 36 | #define LCR_OFFSET 0x03 37 | #define MCR_OFFSET 0x04 38 | #define LSR_OFFSET 0x05 39 | #define MSR_OFFSET 0x06 40 | #define SCR_OFFSET 0x07 41 | #define LCR_DLAB 0x80 42 | #define MCR_DTRRTS 0x03 43 | #define FCR_FIFO_ENABLE 0x01 44 | #define PORT_DIVISOR 115200 45 | #define TRANSMIT_READY_BIT 0x20 46 | #define BIOS_BUILD_VERSION "$Id: sgabios.S 7 2009-11-13 00:21:26Z smiles@google.com $" 47 | 48 | #define KBD_HEAD 0x1a 49 | #define KBD_TAIL 0x1c 50 | #define KBD_BUF_START 0x1e 51 | #define KBD_BUF_END 0x3e 52 | 53 | #define VGA_IO_BASE 0x3d4 54 | #define BDA_SEG 0x40 55 | #define BDA_EBDA 0x0e 56 | #define BDA_MEM_SIZE 0x13 57 | #define BDA_MODE_NUM 0x49 58 | #define BDA_COLS 0x4a 59 | #define BDA_PAGE_SIZE 0x4c 60 | /* BDA word 40:0c traditionally holds the LPT3 io port address... */ 61 | /* Reuse it for tracking where the serial console cursor was left */ 62 | /* Don't send ansi cursor pos update without text ready to output */ 63 | /* Some operations don't update cursor position, but next int 10h */ 64 | /* call is often one that might update to where cursor already is */ 65 | #define BDA_SERIAL_POS 0x0c 66 | #define BDA_CURSOR_BUF 0x50 67 | #define BDA_CURSOR_COL 0x50 68 | #define BDA_CURSOR_ROW 0x51 69 | #define BDA_CURSOR_SCAN 0x60 70 | #define BDA_ACTIVE_PAGE 0x62 71 | #define BDA_6845_ADDR 0x63 72 | #define BDA_MODE_SEL 0x65 73 | #define BDA_COLOR_VAL 0x66 74 | #define BDA_ROM_OFF 0x67 75 | #define BDA_ROM_SEG 0x69 76 | #define BDA_ROWS 0x84 77 | --------------------------------------------------------------------------------