OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Apr 18, 2024 8:25 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Serial Communication seems to mess up VGA in QEMU
PostPosted: Mon May 17, 2021 9:42 pm 
Offline
Member
Member

Joined: Wed Feb 24, 2021 8:52 pm
Posts: 25
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:
Attachment:
debug.png
debug.png [ 53.44 KiB | Viewed 1764 times ]


This is the code to set up uart:

Code:
#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]);
   }
}


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:
Code:
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");


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?


Top
 Profile  
 
 Post subject: Re: Serial Communication seems to mess up VGA in QEMU
PostPosted: Mon May 17, 2021 9:58 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5137
dengeltheyounger wrote:
I'm not sure if that's normal for serial communication.

No, of course it's not normal. There is a bug in your code.

dengeltheyounger wrote:
Any idea what's going on?

The only thing that stands out to me is that your code will use a lot of stack space, but it's hard to tell if that's the problem without running it under a debugger. Speaking of which, you can use a debugger to catch whatever is writing to the screen.


Top
 Profile  
 
 Post subject: Re: Serial Communication seems to mess up VGA in QEMU
PostPosted: Tue May 18, 2021 12:12 am 
Offline
Member
Member

Joined: Wed Feb 24, 2021 8:52 pm
Posts: 25
Octocontrabass wrote:
dengeltheyounger wrote:
I'm not sure if that's normal for serial communication.

No, of course it's not normal. There is a bug in your code.

dengeltheyounger wrote:
Any idea what's going on?

The only thing that stands out to me is that your code will use a lot of stack space, but it's hard to tell if that's the problem without running it under a debugger. Speaking of which, you can use a debugger to catch whatever is writing to the screen.


So, I've noticed two different things. The first is that the program prints to console normally if I return immediately before the memset. For that reason, it looks like the memset has caused the write to go into the VGA buffer. The other thing is that I've attached gdb to the kernel. I went ahead and ran
Quote:
info locals
and this was the result:

Quote:
check_flag = <optimized out>
mmap_addr = <optimized out>
mmap_length = <optimized out>
arr_size = <optimized out>
entries = {}
last_address = <optimized out>
blocks = 1048576
bitmap = {<error reading variable>
ndx = <optimized out>
base = <optimized out>
limit = <optimized out>
address = <optimized out>
to_allocate = <optimized out>
bad_alloc = 0
new_location = <optimized out>


When I attempted to get the first element of bitmap
Quote:
print bitmap[0]
I got this:
Quote:
value requires 131072 bytes, which is more than max-value-size


I also discovered that the kernel jumped into the "bound range exceeded" exception. It's looking like the issue (as you mentioned) is that my code is requiring too much stack space, and for that reason is crashing. Because the stack has exploded, the pusha instruction doesn't seem to be executing properly, which is why my exception handler never told me what was going on (at least, that's my reasoning).

It looks like this mystery has been solved. The bitmap was designed to have each bit represent a block of 4096 bytes. Taking the very top address, dividing by 4096, and then dividing by 8 seems to still yield about 130 K. I wanted it set up so that the entire bitmap would be stored locally, and then have my bitmap pointer to point to the locally stored bitmap. The page frame allocator would then use that locally stored bitmap (through the global pointer) in order to find some space in ram to permanently store the bitmap, and then the initializer would reset the global pointer to whatever the page frame allocator returned (solving the allocating memory for the allocator without an allocator problem). However, I'll need to go back to the drawing board and a better way to handle the initialization step for the physical memory manager.

Hopefully this will serve as a lesson to other newbies like me about the need for good data structures and good algorithms when designing a kernel.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: SemrushBot [Bot] and 94 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group