Serial Communication seems to mess up VGA in QEMU
Posted: Mon May 17, 2021 9:42 pm
So, I'm in the process of working on page frame allocation. I decided to set up serial communication in order to make it a little bit easier to debug (right now, there's no file IO, and I don't have keyboard input set up, which makes having an internal kernel buffer difficult to work with). Using qemu, I'm doing the option -serial file:serial.log, which seems to work well. The only problem is that the whole screen is being filled with weird characters (Ideally, I'd like to also print to console for some cases, and then to have qemu write to file for other cases). I'm not sure if that's normal for serial communication.
This is what the screen looks: This is the code to set up uart:
Currently, it is set up with interrupts disabled. I'm not sure if that explains why the screen ends up looking like that. The com port that is written to is always COM1.
This is the code for the memory map initializer:
printf_serial, is literally the same as my printf implementation, except it calls the serial_write type functions in instead of terminal_write. It specifically writes to COM1 (I'm not really at the point of writing a generalized implementation that works out the addresses of all of the COM ports and allows them all to be set up in their unique ways).
As best I can tell, it is either a bug with the serial console, or it is a bug with the physical memory initializer. Any idea what's going on?
This is what the screen looks: This is the code to set up uart:
Code: Select all
#include <i386/uart.h>
int init_com_port(uint16_t port) {
// Disable interrupts
outb(port + 1, 0x00);
// enable DLAB
outb(port + 3, 0x80);
// Set divisor to 3, 38400 buad
outb(port + 0, 0x03);
// hi byte
outb(port + 1, 0x00);
// 8 bits, no parity, one stop bit
outb(port + 3, 0x03);
// enable FIFO, clear them, with 14-byte threshold
outb(port + 2, 0xC7);
// IRQs enabled RTS/DSR set
outb(port + 4, 0x0B);
// Set in loopback mode, test serial chhip
outb(port + 4, 0x1E);
// Test serial chip
outb(port + 0, 0xAE);
// Check if serial is faulty
if (inb(port + 0) != 0xAE)
return 0;
// if serial is not faulty set it in normal operation mode
outb(port + 4, 0x0F);
return 1;
}
int is_transmit_empty(uint16_t port) {
return inb(port + 5) & 0x20;
}
void serial_putchar(uint16_t port, char a) {
while (!is_transmit_empty(port));
outb(port, a);
}
void serial_writestring(uint16_t port, const char *string) {
serial_write(port, string, strlen(string));
}
void serial_write(uint16_t port, const char *string, size_t n) {
for (size_t i = 0; i < n; ++i) {
serial_putchar(port, string[i]);
}
}
This is the code for the memory map initializer:
Code: Select all
int init_memory_map(multiboot_info_t *mbd) {
/* We only actually use the mmap_t here, and so we're going
* to go ahead and define it within the function
*/
struct mmap_t {
uint32_t size;
uint64_t base;
uint64_t len;
uint32_t type;
} __attribute((packed));
unsigned char check_flag = (1 << 0) & mbd->flags;
// Check flags to make sure memory mapping is sane
if (!check_flag) {
printf("Flag is not set at bit 0!\n");
return 0;
}
check_flag = (1 << 6) & mbd->flags;
if (!check_flag) {
printf("Flag is not set at bit 6!\n");
return 0;
}
// Get the address and length to set up
struct mmap_t *mmap_addr = (struct mmap_t *) mbd->mmap_addr;
uint64_t mmap_length = (uint64_t) mbd->mmap_length;
// Get the number of entries
uint32_t arr_size = mmap_length / sizeof(struct mmap_t);
// TODO: Remove this and just use mbd->mmap_addr
struct mmap_t entries[arr_size];
// Copy the multiboot info into entries
memcpy(entries, mmap_addr, mmap_length);
// TODO: Remove this code later
for (uint32_t i = 0; i < arr_size; ++i) {
printf_serial("Size: %u\n", entries[i].size);
printf_serial("Base: 0x%llx\n", entries[i].base);
printf_serial("Size: %llu\n", entries[i].len);
printf_serial("Type: 0x%lx\n\n", entries[i].type);
}
/* Get the base + length to determine total "usable"
* ram
*/
uint64_t last_address = entries[arr_size-1].base +
entries[arr_size-1].len;
printf_serial("Last address: 0x%llx\n", last_address);
// Get the number of pages
uint32_t blocks = last_address / 4096;
printf_serial("Number of blocks: %u\n", blocks);
// Get the number of bytes for the bitmap, each bit is 1 page
bitmap_size = blocks / 8;
printf_serial("Bitmap size: %u\n", bitmap_size);
uint8_t bitmap[bitmap_size];
printf_serial("Preparing to memset bitmap...\n");
// Set all bits to 1
memset(bitmap, 0xFF, bitmap_size);
// It never gets passed this point
printf_serial("Finished memsetting bitmap...\n");
As best I can tell, it is either a bug with the serial console, or it is a bug with the physical memory initializer. Any idea what's going on?